Files
chatroom/app/Http/Controllers/FriendController.php
lkddi 700ab9def4 feat: 好友系统全实现
后端:
- FriendController:add/remove/status/index 四个接口
- FriendAdded / FriendRemoved 广播事件(私有频道)
- channels.php 注册 user.{username} 私有频道鉴权
- routes/web.php 注册好友路由
- ChatController::init() 修复 DutyLog 在 return 后执行的 bug
- ChatController::notifyFriendsOnline() 上线时悄悄话通知好友

前端:
- user-actions:写私信 → 加好友/删好友按钮(动态状态)
- toggleFriend() 方法 + fetchUser 后加载好友状态
- scripts:监听私有频道 FriendAdded/FriendRemoved
- showFriendToast() 右下角浮窗通知(5秒自动消失)
- global-dialog 加 fdSlideIn 动画
2026-03-01 00:48:51 +08:00

235 lines
7.3 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(),
]);
// 广播给对方(仅对方可见)
broadcast(new FriendAdded($me->username, $username));
// 若对方在线,推送聊天区悄悄话
$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);
}
// 广播给对方
broadcast(new FriendRemoved($me->username, $username));
// 若对方在线,推送聊天区悄悄话
$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));
}
}