- 任命/撤销事件增加 type 字段区分类型 - 任命:全屏礼花 + 紫色弹窗 + 紫色系统消息 - 撤销:灰色弹窗 + 灰色系统消息,无礼花 - 消息分发:操作者/被操作者显示在私聊面板,其他人显示在公屏 - 系统消息加随机鼓励语(各5条轮换) - ChatStateService 修复 Redis key 前缀扫描问题(getAllActiveRoomIds) - 用户名片折叠优化:管理员视野、职务履历均可折叠 - 管理操作 + 职务操作合并为「🔧 管理操作」折叠区 - 悄悄话改为「🎁 送礼物」按钮,礼物面板内联展开
148 lines
5.2 KiB
PHP
148 lines
5.2 KiB
PHP
<?php
|
||
|
||
/**
|
||
* 文件功能:聊天室内快速任命/撤销控制器
|
||
* 供有职务的管理员在聊天室用户名片弹窗中快速任命或撤销目标用户的职务。
|
||
* 权限校验委托给 AppointmentService,本控制器只做请求解析和返回 JSON。
|
||
*
|
||
* @author ChatRoom Laravel
|
||
*
|
||
* @version 1.0.0
|
||
*/
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use App\Events\AppointmentAnnounced;
|
||
use App\Models\Position;
|
||
use App\Models\User;
|
||
use App\Services\AppointmentService;
|
||
use App\Services\ChatStateService;
|
||
use Illuminate\Http\JsonResponse;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Auth;
|
||
|
||
class ChatAppointmentController extends Controller
|
||
{
|
||
/**
|
||
* 注入任命服务
|
||
*/
|
||
public function __construct(
|
||
private readonly AppointmentService $appointmentService,
|
||
private readonly ChatStateService $chatState,
|
||
) {}
|
||
|
||
/**
|
||
* 获取可用职务列表(供名片弹窗下拉选择)
|
||
* 返回操作人有权限任命的职务
|
||
*/
|
||
public function positions(): JsonResponse
|
||
{
|
||
$operator = Auth::user();
|
||
$superLevel = (int) \App\Models\Sysparam::getValue('superlevel', '100');
|
||
$operatorPosition = $operator->activePosition?->position;
|
||
|
||
$query = Position::query()
|
||
->with('department')
|
||
->orderByDesc('rank');
|
||
|
||
// 仅有具体职务(非 superlevel 直通)的操作人才限制 rank 范围
|
||
if ($operatorPosition && $operator->user_level < $superLevel) {
|
||
$query->where('rank', '<', $operatorPosition->rank);
|
||
}
|
||
|
||
$positions = $query->get()->map(fn ($p) => [
|
||
'id' => $p->id,
|
||
'name' => $p->name,
|
||
'icon' => $p->icon,
|
||
'rank' => $p->rank,
|
||
'department' => $p->department?->name,
|
||
]);
|
||
|
||
return response()->json(['status' => 'success', 'positions' => $positions]);
|
||
}
|
||
|
||
/**
|
||
* 快速任命:将目标用户任命为指定职务
|
||
* 成功后向操作人所在聊天室广播任命公告
|
||
*/
|
||
public function appoint(Request $request): JsonResponse
|
||
{
|
||
$request->validate([
|
||
'username' => 'required|string|exists:users,username',
|
||
'position_id' => 'required|exists:positions,id',
|
||
'remark' => 'nullable|string|max:100',
|
||
'room_id' => 'nullable|integer|exists:rooms,id',
|
||
]);
|
||
|
||
$operator = Auth::user();
|
||
$target = User::where('username', $request->username)->firstOrFail();
|
||
$position = Position::with('department')->findOrFail($request->position_id);
|
||
|
||
$result = $this->appointmentService->appoint($operator, $target, $position, $request->remark);
|
||
|
||
// 任命成功后广播礼花通知:优先用前端传来的 room_id,否则从 Redis 查操作人所在房间
|
||
if ($result['ok']) {
|
||
$roomId = $request->integer('room_id') ?: ($this->chatState->getUserRooms($operator->username)[0] ?? null);
|
||
if ($roomId) {
|
||
broadcast(new AppointmentAnnounced(
|
||
roomId: (int) $roomId,
|
||
targetUsername: $target->username,
|
||
positionIcon: $position->icon ?? '🎖️',
|
||
positionName: $position->name,
|
||
departmentName: $position->department?->name ?? '',
|
||
operatorName: $operator->username,
|
||
));
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'status' => $result['ok'] ? 'success' : 'error',
|
||
'message' => $result['message'],
|
||
], $result['ok'] ? 200 : 422);
|
||
}
|
||
|
||
/**
|
||
* 快速撤销:撤销目标用户当前的职务
|
||
*/
|
||
public function revoke(Request $request): JsonResponse
|
||
{
|
||
$request->validate([
|
||
'username' => 'required|string|exists:users,username',
|
||
'remark' => 'nullable|string|max:100',
|
||
'room_id' => 'nullable|integer|exists:rooms,id',
|
||
]);
|
||
|
||
$operator = Auth::user();
|
||
$target = User::where('username', $request->username)->firstOrFail();
|
||
|
||
// 撤销前先取目标当前职务信息(撤销后就查不到了)
|
||
$activeUp = $target->activePosition?->load('position.department');
|
||
$posIcon = $activeUp?->position?->icon ?? '🎖️';
|
||
$posName = $activeUp?->position?->name ?? '';
|
||
$deptName = $activeUp?->position?->department?->name ?? '';
|
||
|
||
$result = $this->appointmentService->revoke($operator, $target, $request->remark);
|
||
|
||
// 撤销成功后广播通知到聊天室
|
||
if ($result['ok'] && $posName) {
|
||
$roomId = $request->integer('room_id') ?: ($this->chatState->getUserRooms($operator->username)[0] ?? null);
|
||
if ($roomId) {
|
||
broadcast(new AppointmentAnnounced(
|
||
roomId: (int) $roomId,
|
||
targetUsername: $target->username,
|
||
positionIcon: $posIcon,
|
||
positionName: $posName,
|
||
departmentName: $deptName,
|
||
operatorName: $operator->username,
|
||
type: 'revoke',
|
||
));
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'status' => $result['ok'] ? 'success' : 'error',
|
||
'message' => $result['message'],
|
||
], $result['ok'] ? 200 : 422);
|
||
}
|
||
}
|