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 )); } } // ================= 系统事件 ================= /** * 认证 */ 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 ($payload['server']['HTTP_ORIGIN'] != 'https://wstool.js.org') { // 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); // 响应 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; } } /** * 系统心跳 * * @param string|null $traceId */ public function systemPing(?string $traceId) { Gateway::sendToClient($this->clientId, WsResponse::success( 'system.pong', ['time' => time()], $traceId )); } // ================= 消息通知事件 ================= /** * 管理端主动推送通知 * data = [ * 'to_uids' => [uid1, uid2], // 可选,为空表示全员 * 'title' => '标题', * 'content' => '内容', * ] */ 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', 400, 'title and content are required', $traceId )); return; } $msgId = rand(1000, 9999); // 生成消息ID,可用数据库存储 $payload = WsResponse::success('notify.push', [ '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 )); } /** * 客户端标记通知已读 * data = ['msg_ids' => [1,2,3]] */ public function notifyRead(?string $traceId) { $msgIds = $this->eventData['msg_ids'] ?? []; if (empty($msgIds)) { Gateway::sendToClient($this->clientId, WsResponse::error( 'notify.read', 400, 'msg_ids required', $traceId )); return; } Gateway::sendToClient($this->clientId, WsResponse::success( 'notify.read.result', ['msg_ids' => $msgIds], $traceId )); } // ================= 辅助方法 ================= protected function eventToMethod(string $event): string { $parts = explode('.', $event); $method = array_shift($parts); foreach ($parts as $part) { $method .= ucfirst($part); } return $method; } }