registerAddress; } /** * 监听消息接收 */ public function message($client_id, $message) { $this->clientId = $client_id; $payload = json_decode($message, true); $event = $payload['event']; $this->eventData = $payload['data'] ?? []; $traceId = $payload['trace_id'] ?? null; $method = $this->eventToMethod($event); if (method_exists($this, $method)) { try { $this->$method($traceId); } catch (\Throwable $th) { Gateway::sendToClient($client_id, WsResponse::error( $event, 500, $th->getMessage(), $traceId )); } } else { Gateway::sendToClient($client_id, WsResponse::error( $event, 404, "Event handler not found: $method", $traceId )); } } protected function eventToMethod(string $event): string { $parts = explode('.', $event); $method = array_shift($parts); foreach ($parts as $part) { $method .= ucfirst($part); } return $method; } // ================= 系统事件 ================= /** * 认证 */ public function auth($client_id, $payload) { try { // 检查来源 if (!isset($payload['server']['HTTP_ORIGIN'])) { flg('[x] [TOKEN认证] 未定义HTTP_ORIGIN', false); Gateway::sendToClient($client_id, WsResponse::error( 'system.auth', 1001, '[x] [TOKEN认证] 未定义HTTP_ORIGIN' )); Gateway::closeClient($client_id); return; } // 连接是否满足条件 if (getenv('ENVIRONMENT') === 'master' && $payload['server']['HTTP_ORIGIN'] === getenv('MAIN_DOMAIN')) { flg('[x] [TOKEN认证] HTTP_ORIGIN不满足条件', false); Gateway::sendToClient($client_id, WsResponse::error( 'system.auth', 1001, '[x] [TOKEN认证] HTTP_ORIGIN不满足条件' )); Gateway::closeClient($client_id); return; } // 签名检查 if (!isset($payload['get']['token'])) { flg('[x] [TOKEN认证] 未携带TOKEN参数', false); Gateway::sendToClient($client_id, WsResponse::error( 'system.auth', 1001, '[x] [TOKEN认证] 未携带TOKEN参数' )); Gateway::closeClient($client_id); return; } // 解析TOKEN $user = (new Jwt())->verifyToken($payload['get']['token']); // 绑定用户ID Gateway::bindUid($client_id, $user->uid); // 绑定SESSION $_SESSION['uid'] = $user->uid; // $_SESSION['name'] = $user->uname; // 响应 Gateway::sendToClient($client_id, WsResponse::success( 'system.auth', ['uid' => $user->uid] )); } catch (\Throwable $th) { flg('[x] [TOKEN认证] TOKEN认证失败:'.$th->getMessage(), false); Gateway::sendToClient($client_id, WsResponse::error( 'system.auth', 1001, '[x] [TOKEN认证] TOKEN认证失败:'.$th->getMessage() )); Gateway::closeClient($client_id); return; } } public function logout(string $client_id) { // var_dump('====== ====== ====== start ====== ====== ======'); // var_dump('关闭客户端ID:'.$client_id); // if ($_SESSION !== null) { // var_dump($_SESSION); // } // var_dump('====== ====== ====== end ====== ====== ======'); } /** * 系统心跳 * * @param string|null $traceId */ public function systemPing(?string $traceId) { Gateway::sendToClient($this->clientId, WsResponse::success( 'system.pong', ['time' => time()], $traceId )); } public function systemConnect(?string $traceId) { // 响应 Gateway::sendToClient($this->clientId, WsResponse::success( 'system.connect.receive', ['time' => time()], $traceId )); // 获取审批待办数量 $num = (new \app\common\utils\approval\service\ApprovalFlowService())->getInitTodoCount($_SESSION['uid']); $this->todoCounts($_SESSION['uid'], $num); // 绑定检查 $initData = $this->initCheck($_SESSION['uid']); Gateway::sendToClient($this->clientId, WsResponse::success( 'system.init.check', $initData, $traceId )); } // ================= 消息通知事件 ================= /** * 管理端主动推送通知 */ public function notifyPush(?string $traceId) { $title = $this->eventData['title'] ?? null; $content = $this->eventData['content'] ?? null; $toUids = $this->eventData['to_uids'] ?? []; if (!$title || !$content) { Gateway::sendToClient($this->clientId, WsResponse::error( 'notify.push.result', 400, '标题和内容不能为空', $traceId )); return; } // 写消息 $msgService = new MessageNotificationsService(); $msgId = $msgService->createMessage([ 'sender_id' => $this->eventData['sender_id'] ?? 0, 'sender_type' => $this->eventData['sender_type'] ?? 'system', 'title' => $title, 'content' => $content, 'scene' => $this->eventData['scene'] ?? 0, 'recipient' => $this->eventData['to_uids'] ? implode(',', $this->eventData['to_uids']) : '', 'push_method' => isset($this->eventData['push_method']) ? $this->eventData['push_method'] : 0 ]); // 写用户状态 $uids = !empty($toUids) ? $toUids : $msgService->getAllUserIds(); $msgService->addUserStatus($msgId, $uids); $payload = WsResponse::success('notify.receive', [ 'msg_id' => $msgId, 'title' => $title, 'content' => $content, 'created_at' => time() ], $traceId); if (!empty($toUids)) { // 推送给指定用户 foreach ($toUids as $uid) { Gateway::sendToUid($uid, $payload); } } else { // 推送给全体在线用户 Gateway::sendToAll($payload); } // 回复发送者确认 Gateway::sendToClient($this->clientId, WsResponse::success( 'notify.push.result', ['msg_id' => $msgId], $traceId, '消息发送成功' )); } /** * 客户端标记通知已读 */ public function notifyRead(?string $traceId) { $msgIds = $this->eventData['msg_ids']; if (!is_array($msgIds) && $msgIds !== 'all') { Gateway::sendToClient($this->clientId, WsResponse::error( 'notify.read.result', 400, '消息ID不能为空', $traceId )); return; } // 更新消息为已读 $msgService = new MessageNotificationsService(); $msgService->markAsRead($msgIds, Gateway::getUidByClientId($this->clientId)); Gateway::sendToClient($this->clientId, WsResponse::success( 'notify.read.result', ['msg_ids' => $msgIds], $traceId )); } /* approval.todo.counts 审批待办数量 */ // ================= 主动推送 ================= public function pushWebsocket(string $event, int|string $uid, array $data) { return match (gettype($uid)) { 'integer' => Gateway::sendToUid($uid, WsResponse::success($event, $data)), 'string' => Gateway::sendToClient($uid, WsResponse::success($event, $data)), default => throw new \InvalidArgumentException('UID must be integer or string'), }; } // ================= 业务处理 ================= /** * 初始化检查 */ public function initCheck(int $userId) { $data = [ // 'is_report_to' => false, // 'is_office_clerk' => false, 'is_sign' => false ]; $user = \app\common\model\User::find($userId); // if ($user->report_to > 0) { // $data['is_report_to'] = true; // } if ($user->sign_img !== null && $user->sign_img !== '') { $data['is_sign'] = true; } // $ug = \app\common\model\UserGroupMembers::with(['group' => function($query) { // $query->where('scenario_code', 'stock_orders'); // }])->where('user_id', $userId)->first(); // if ($ug !== null) { // $data['is_office_clerk'] = true; // } if ($data['is_sign']) { Redis::hSet('user:check:sign'.$userId, 'status', 1); } return $data; } // 审批待办数量 public function todoCounts(int $userId, int $num = 0) { $ly = [ 1 => 186, 3 => 224, ]; if ($num === 0) { $num = (new \app\common\utils\approval\service\ApprovalFlowService())->getInitTodoCount($userId); } $systemIds = \app\common\model\UserSystems::where('user_id', $userId)->pluck('system_id')->toArray(); if (count($systemIds) > 0) { foreach ($systemIds as $systemId) { if (isset($ly[$systemId])) { $chain = \app\common\service\MenuBadgeService::buildBadgeChain(menuId: $ly[$systemId], leafCount: $num); $this->pushWebsocket('approval.todo.counts', $userId, $chain); } } } } }