Files
chatroom/app/Http/Controllers/FriendController.php
lkddi 3c2038e8fe 优化:好友通知弹窗根据互相状态显示不同内容
FriendAdded 事件:
- 新增 hasAddedBack 字段(B 是否已回加 A)
- Toast:已互相好友 → '你们现在互为好友 🎉'
- Toast:未回加 → '但你还没有添加对方为好友' + [ 回加] 一键操作按钮

FriendRemoved 事件:
- 新增 hadAddedBack 字段(之前是否互相好友)
- Toast:之前互相好友 → 提示 + [🗑️ 同步移除] 一键操作按钮
- Toast:单向好友 → 简单通知,无操作按钮

Toast 改进:
- 右上角 × 关闭按钮
- 快捷操作按钮支持 fetch 直接请求
- 完成后显示结果并自动关闭,延时改为 8 秒
2026-03-01 00:54:10 +08:00

245 lines
7.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 文件功能:好友系统控制器
*
* 处理聊天室内的好友关系管理:
* 1. 添加好友addFriend
* 2. 删除好友removeFriend
* 3. 查询与指定用户的好友关系status
* 4. 查询当前用户的好友列表index
*
* 好友关系模型:单向存储,互相添加才构成双向好友。
* 使用原版 friend_requests 表字段who / towho / sub_time
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Http\Controllers;
use App\Events\FriendAdded;
use App\Events\FriendRemoved;
use App\Models\FriendRequest;
use App\Models\User;
use App\Services\ChatStateService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class FriendController extends Controller
{
/**
* 注入 Redis 状态服务,用于推送悄悄话通知。
*/
public function __construct(
private readonly ChatStateService $chatState,
) {}
/**
* 查询当前用户与目标用户的好友关系状态。
*
* 返回:
* - is_friend: 当前用户是否已将对方加为好友
* - mutual: 是否互相添加(双向好友)
*
* @param string $username 目标用户名
*/
public function status(string $username): JsonResponse
{
$me = Auth::user();
// 我是否已将对方加为好友
$iAdded = FriendRequest::where('who', $me->username)
->where('towho', $username)
->exists();
// 对方是否也将我加为好友
$theyAdded = FriendRequest::where('who', $username)
->where('towho', $me->username)
->exists();
return response()->json([
'is_friend' => $iAdded,
'mutual' => $iAdded && $theyAdded,
]);
}
/**
* 添加好友。
*
* 流程:
* 1. 校验目标用户存在、且不是自己
* 2. 检查是否已经添加过
* 3. 写入 friend_requests 记录
* 4. 广播 FriendAdded 事件通知对方
* 5. 若对方当前在线Redis向对方发送悄悄话
*
* @param string $username 目标用户名
*/
public function addFriend(Request $request, string $username): JsonResponse
{
$me = Auth::user();
// 不能加自己
if ($me->username === $username) {
return response()->json(['status' => 'error', 'message' => '不能将自己加为好友'], 422);
}
// 检查目标用户是否存在
$target = User::where('username', $username)->first();
if (! $target) {
return response()->json(['status' => 'error', 'message' => '用户不存在'], 404);
}
// 是否已添加
$exists = FriendRequest::where('who', $me->username)->where('towho', $username)->exists();
if ($exists) {
return response()->json(['status' => 'error', 'message' => '已是好友,无需重复添加'], 422);
}
// 写入好友关系
FriendRequest::create([
'who' => $me->username,
'towho' => $username,
'sub_time' => now(),
]);
// 检查 B 是否已将 A 加为好友(互相好友判断)
$hasAddedBack = FriendRequest::where('who', $username)
->where('towho', $me->username)
->exists();
// 广播给对方(仅对方可见),携带是否已回加的状态
broadcast(new FriendAdded($me->username, $username, $hasAddedBack));
// 若对方在线,推送聊天区悄悄话
$this->notifyOnlineUser($username, $me->username, 'added', $request->input('room_id'));
return response()->json([
'status' => 'success',
'message' => '已成功添加 '.$username.' 为好友 🎉',
]);
}
/**
* 删除好友。
*
* 流程:
* 1. 删除 friend_requests 中「我 → 对方」的记录
* 2. 广播 FriendRemoved 事件通知对方
* 3. 若对方在线,向对方发送悄悄话
*
* @param string $username 目标用户名
*/
public function removeFriend(Request $request, string $username): JsonResponse
{
$me = Auth::user();
$deleted = FriendRequest::where('who', $me->username)
->where('towho', $username)
->delete();
if (! $deleted) {
return response()->json(['status' => 'error', 'message' => '好友关系不存在'], 404);
}
// 检查 B 之前是否也将 A 加为好友(删除前的互相状态)
$hadAddedBack = FriendRequest::where('who', $username)
->where('towho', $me->username)
->exists();
// 广播给对方,携带之前的互相好友状态
broadcast(new FriendRemoved($me->username, $username, $hadAddedBack));
// 若对方在线,推送聊天区悄悄话
$this->notifyOnlineUser($username, $me->username, 'removed', $request->input('room_id'));
return response()->json([
'status' => 'success',
'message' => '已将 '.$username.' 从好友列表移除',
]);
}
/**
* 获取当前用户的好友列表(我添加的 + 对方也添加我的 = 双向好友标记)。
*/
public function index(): JsonResponse
{
$me = Auth::user();
// 我添加的所有人
$myAdded = FriendRequest::where('who', $me->username)->pluck('towho');
// 也把我加了的
$addedMe = FriendRequest::where('towho', $me->username)->pluck('who');
$friends = User::whereIn('username', $myAdded)->get(['username', 'usersf', 'user_level', 'sex'])->map(function ($u) use ($addedMe) {
return [
'username' => $u->username,
'headface' => $u->headface,
'user_level' => $u->user_level,
'sex' => $u->sex,
'mutual' => $addedMe->contains($u->username), // 是否互相添加
];
});
return response()->json(['status' => 'success', 'friends' => $friends]);
}
/**
* 若目标用户在线,向其发送系统悄悄话通知。
*
* 好友上线/下线使用此方法,不公开广播,只有本人可见。
*
* @param string $targetUsername 接收通知的用户名
* @param string $fromUsername 发起操作的用户名
* @param string $action 'added' | 'removed' | 'online'
* @param int|null $roomId 当前房间 ID用于推送到对应房间频道
*/
private function notifyOnlineUser(
string $targetUsername,
string $fromUsername,
string $action,
?int $roomId = null
): void {
if (! $roomId) {
return;
}
// 检查对方是否在该房间在线
$onlineUsers = $this->chatState->getRoomUsers($roomId);
if (! isset($onlineUsers[$targetUsername])) {
return;
}
$content = match ($action) {
'added' => "💚 <b>{$fromUsername}</b> 将你加为好友了!你们现在是好友了 🎉",
'removed' => "💔 <b>{$fromUsername}</b> 已将你从好友列表移除。",
'online' => "🟢 你的好友 <b>{$fromUsername}</b> 上线啦!",
default => '',
};
if (! $content) {
return;
}
// 构建系统悄悄话消息
$msg = [
'id' => $this->chatState->nextMessageId($roomId),
'room_id' => $roomId,
'from_user' => '系统',
'to_user' => $targetUsername,
'content' => $content,
'is_secret' => true,
'font_color' => '#16a34a',
'action' => '',
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($roomId, $msg);
broadcast(new \App\Events\MessageSent($roomId, $msg));
}
}