WIN-2308041133\Administrator 1 неделя назад
Родитель
Сommit
b5440f366f
2 измененных файлов с 110 добавлено и 47 удалено
  1. 100 36
      app/services/workerman/chat/ChatHandle.php
  2. 10 11
      socket.php

+ 100 - 36
app/services/workerman/chat/ChatHandle.php

@@ -1,9 +1,12 @@
 <?php
 // +----------------------------------------------------------------------
-// | Chat 消息处理类
+// | Chat 消息处理类(参照熙思研模式:在 WS handler 内直接保存聊天记录)
 // +----------------------------------------------------------------------
 namespace app\services\workerman\chat;
 
+use app\model\api\ChatRecord;
+use app\model\api\ChatUserRelation;
+use app\model\api\User;
 use app\services\workerman\Response;
 use Workerman\Connection\TcpConnection;
 
@@ -26,9 +29,6 @@ class ChatHandle
         }
 
         // TODO: 根据你的系统实现客服token验证
-        // 示例:$kefuInfo = your_kefu_auth_service::parseToken($token);
-        
-        // 临时示例
         $kefuInfo = [
             'uid' => $res['data']['uid'] ?? 1,
             'name' => $res['data']['name'] ?? '客服',
@@ -50,70 +50,134 @@ class ChatHandle
             return $response->close(['msg' => '授权失败!']);
         }
 
-        // TODO: 根据你的系统实现用户token验证
-        // 示例:$authInfo = your_user_auth_service::parseToken($token);
-        
-        // 临时示例
-        $userInfo = [
-            'uid' => $res['data']['uid'] ?? 1,
-            'nickname' => $res['data']['nickname'] ?? '用户',
-            'avatar' => $res['data']['avatar'] ?? '',
-        ];
+        // 通过 token 查询用户
+        $userInfo = User::where('token', $token)->find();
+        if (!$userInfo) {
+            return $response->close(['msg' => '用户不存在']);
+        }
 
-        $connection->user = (object)$userInfo;
-        
-        // 从 URL 参数获取聊天对象 UID(在 socket.php onWebSocketConnect 中设置)
+        $connection->user = (object)$userInfo->toArray();
         $connection->chatToUid = $connection->to_uid ?? 0;
-        
         $this->service->setUser($connection);
 
         return $response->success([
             'msg' => '登录成功',
-            'to_uid' => $connection->chatToUid,  // 返回当前聊天对象
+            'to_uid' => $connection->chatToUid,
         ]);
     }
 
     /**
-     * 发送消息
+     * 用户切换到与某人聊天(参照熙思研 to_chat)
+     * 前端打开某用户聊天窗口时调用,用于标记消息已读、重置未读数
+     */
+    public function to_chat(TcpConnection &$connection, array $res, Response $response)
+    {
+        $to_uid = $res['data']['id'] ?? $res['data']['to_uid'] ?? 0;
+        $connection->chatToUid = $to_uid;
+
+        if (isset($connection->user) && $connection->chatToUid) {
+            $uid = $connection->user->uid;
+            // 标记对方发给我的消息为已读
+            ChatRecord::markAsRead($uid, $connection->chatToUid);
+            $response->send('mssage_num', [
+                'uid' => $connection->chatToUid,
+                'num' => 0,
+                'recored' => (object)[],
+            ]);
+        }
+    }
+
+    /**
+     * 发送消息(参照熙思研模式:在 WS handler 内直接保存聊天记录到数据库)
      */
     public function chat(TcpConnection &$connection, array $res, Response $response)
     {
-        // 检查用户是否已登录
         if (!isset($connection->user) || !$connection->user) {
             return $response->send('err_tip', ['msg' => '请先登录']);
         }
 
-        // 优先从消息数据获取 to_uid,其次从 URL 参数获取(聊天对象UID)
         $to_uid = $res['data']['to_uid'] ?? $connection->chatToUid ?? 0;
+        $msn_type = $res['data']['type'] ?? ChatRecord::TYPE_TEXT;
         $msn = $res['data']['msn'] ?? '';
-        $msn_type = $res['data']['type'] ?? 1; // 1=文字, 2=语音, 3=图片
+        $form_type = $res['form_type'] ?? $connection->form_type ?? ChatRecord::FROM_PC;
+        $uid = $connection->user->uid ?? 0;
 
         if (!$to_uid) {
             return $response->send('err_tip', ['msg' => '用户不存在']);
         }
-
-        if ($to_uid == ($connection->user->uid ?? 0)) {
+        if ($to_uid == $uid) {
             return $response->send('err_tip', ['msg' => '不能和自己聊天']);
         }
+        if (!in_array($msn_type, [ChatRecord::TYPE_TEXT, ChatRecord::TYPE_VOICE, ChatRecord::TYPE_IMAGE])) {
+            return $response->send('err_tip', ['msg' => '格式错误']);
+        }
 
-        $uid = $connection->user->uid ?? 0;
-        
+        // 清洗消息内容
+        $msn = trim(strip_tags(str_replace(["\n", "\t", "\r", "&nbsp;"], '', htmlspecialchars_decode($msn))));
+
+        // 判断对方是否在线且正在与当前用户聊天
+        $connections = $this->service->user();
+        $online = isset($connections[$to_uid]) && isset($connections[$to_uid]->chatToUid) && $connections[$to_uid]->chatToUid == $uid;
+
+        // ========== 关键:在 WS handler 内直接保存聊天记录 ==========
+        $record = ChatRecord::saveRecord($uid, $to_uid, $msn, $msn_type, $form_type);
+
+        // 如果对方在线且正在聊天,直接标记已读
+        if ($online) {
+            ChatRecord::where('id', $record->id)->update(['is_read' => ChatRecord::READ]);
+            $record->is_read = ChatRecord::READ;
+        }
+
+        // 更新会话关系
+        $relation = ChatUserRelation::getOrCreate($uid, $to_uid);
+        if ($relation) {
+            $relation->update_time = time();
+            $relation->save();
+        }
+
+        // 获取发送者用户信息
+        $user = User::find($uid);
+        $nickname = $user->nickname ?? $connection->user->nickname ?? '用户';
+        $avatar = $user->avatar ?? $connection->user->avatar ?? '';
+
+        // 未读消息计数
+        $unreadCount = ChatRecord::getUnreadCount($to_uid, $uid);
+
+        // 构造返回数据
         $data = [
-            'from_uid' => $uid,
-            'to_uid' => $to_uid,
-            'msn' => $msn,
-            'type' => $msn_type,
-            'add_time' => time(),
-            'nickname' => $connection->user->nickname ?? '用户',
-            'avatar' => $connection->user->avatar ?? '',
+            'record_id'    => $record->id,
+            'user_id'      => $uid,
+            'to_user_id'   => $to_uid,
+            'content'      => $msn,
+            'type'         => $msn_type,
+            'type_text'    => ChatRecord::getTypeText($msn_type),
+            'form_type'    => $form_type,
+            'is_read'      => $online ? ChatRecord::READ : ChatRecord::UNREAD,
+            'create_time'  => $record->create_time,
+            'nickname'     => $nickname,
+            'avatar'       => $avatar,
         ];
 
         // 给自己回复
         $response->send('chat', $data);
 
-        // 发送给目标用户
-        if (isset($this->service->user()[$to_uid])) {
-            $response->connection($this->service->user()[$to_uid])->send('reply', $data);
+        // 发送给对方
+        if ($online) {
+            // 对方在线且正在聊天:直接推送消息
+            $replyData = $data;
+            $replyData['is_read'] = ChatRecord::READ;
+            $response->connection($this->service->user()[$to_uid])->send('reply', $replyData);
+        } elseif (isset($connections[$to_uid])) {
+            // 对方在线但没在聊天:推送未读消息提醒
+            $response->connection($this->service->user()[$to_uid])->send('mssage_num', [
+                'uid' => $uid,
+                'num' => $unreadCount,
+                'recored' => [
+                    'nickname' => $nickname,
+                    'avatar'   => $avatar,
+                ],
+            ]);
         }
+        // 对方不在线:什么都不做,等对方上线后通过 HTTP API 拉取
     }
 }

+ 10 - 11
socket.php

@@ -15,6 +15,9 @@
 
 require __DIR__ . '/vendor/autoload.php';
 
+// 引导 ThinkPHP 框架(使模型、数据库等可用)
+(new \think\App())->initialize();
+
 use Workerman\Worker;
 use Workerman\Connection\TcpConnection;
 
@@ -86,18 +89,14 @@ $ws_worker->onWebSocketConnect = function(TcpConnection $connection) use ($ws_wo
     $connection->form_type = $query['form_type'] ?? 3;
     $connection->to_uid    = isset($query['to_uid']) ? intval($query['to_uid']) : 0;  // 聊天对象UID
     
-    // 自动登录:根据 URL 参数设置用户信息
+    // 自动登录:通过 token 验证用户
     if ($connection->token) {
-        // TODO: 实际项目中应验证 token 有效性
-        // 从 URL 参数获取用户 UID(from 或 uid 参数)
-        $uid = intval($query['from'] ?? $query['uid'] ?? $connection->form_type ?? 0);
-        $connection->user = (object)[
-            'uid'      => $uid,
-            'nickname' => $query['nickname'] ?? '用户',
-            'avatar'   => $query['avatar'] ?? '',
-        ];
-        $connection->chatToUid = $connection->to_uid;
-        $ws_worker->chatService->setUser($connection);
+        $userInfo = \app\model\api\User::where('token', $connection->token)->find();
+        if ($userInfo) {
+            $connection->user = (object)$userInfo->toArray();
+            $connection->chatToUid = $connection->to_uid;
+            $ws_worker->chatService->setUser($connection);
+        }
     }
     
     echo "[WebSocket Connect] type={$connection->type}, uid={$connection->user->uid ?? 0}, to_uid={$connection->to_uid}\n";