Chat.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. declare (strict_types = 1);
  3. namespace app\api\controller;
  4. use app\model\api\ChatRecord;
  5. use app\model\api\ChatUserRelation;
  6. use app\model\api\User;
  7. use app\services\chat\ChatBalanceService;
  8. use library\JwtAuth;
  9. use library\LoginSession;
  10. use think\Request;
  11. use think\Response;
  12. /**
  13. * 聊天控制器
  14. */
  15. class Chat extends JwtAuth
  16. {
  17. protected $noNeedLogin = ['recordList'];
  18. /**
  19. * 获取会话列表
  20. * @return Response
  21. */
  22. public function sessionList(): Response
  23. {
  24. $sessions = ChatUserRelation::getUserSessions($this->userId);
  25. return $this->success('获取成功', [
  26. 'list' => $sessions,
  27. ]);
  28. }
  29. /**
  30. * 初始化会话(首次聊天扣次数)
  31. * @return Response
  32. */
  33. public function initSession(): Response
  34. {
  35. $data = $this->request->post();
  36. $toUserId = (int)($data['to_user_id'] ?? 0);
  37. if ($toUserId <= 0) {
  38. return $this->fail('对方用户ID错误');
  39. }
  40. if ($toUserId == $this->userId) {
  41. return $this->fail('不能和自己聊天');
  42. }
  43. // 检查对方用户是否存在
  44. $toUser = User::find($toUserId);
  45. if (!$toUser) {
  46. return $this->fail('用户不存在');
  47. }
  48. // 创建或获取会话关系
  49. $relation = ChatUserRelation::getOrCreate($this->userId, $toUserId);
  50. // 如果未扣过次数,则扣除
  51. if ($relation && $relation->is_deduct == ChatUserRelation::NOT_DEDUCT) {
  52. $result = ChatBalanceService::checkAndDeductFirstChat($this->userId, $toUserId);
  53. if (!$result['success']) {
  54. return $this->fail($result['message']);
  55. }
  56. }
  57. $balance = ChatBalanceService::getBalance($this->userId);
  58. return $this->success('会话创建成功', [
  59. 'to_user_id' => $toUserId,
  60. 'to_nickname' => $toUser->nickname ?? '',
  61. 'to_avatar' => $toUser->avatar ?? '',
  62. 'balance' => $balance,
  63. ]);
  64. }
  65. /**
  66. * 发送消息
  67. * @return Response
  68. */
  69. public function send(): Response
  70. {
  71. $data = $this->request->post();
  72. $toUserId = (int)($data['to_user_id'] ?? 0);
  73. $content = trim($data['content'] ?? '');
  74. $type = (int)($data['type'] ?? ChatRecord::TYPE_TEXT);
  75. $formType = (int)($data['form_type'] ?? ChatRecord::FROM_PC);
  76. // 验证 form_type 有效值
  77. if (!in_array($formType, [ChatRecord::FROM_PC, ChatRecord::FROM_WECHAT, ChatRecord::FROM_MINIAPP, ChatRecord::FROM_H5])) {
  78. $formType = ChatRecord::FROM_PC;
  79. }
  80. if ($toUserId <= 0) {
  81. return $this->fail('对方用户ID错误');
  82. }
  83. if (empty($content) && $type == ChatRecord::TYPE_TEXT) {
  84. return $this->fail('消息内容不能为空');
  85. }
  86. if ($toUserId == $this->userId) {
  87. return $this->fail('不能和自己聊天');
  88. }
  89. // 检查对方用户是否存在
  90. $toUser = User::find($toUserId);
  91. if (!$toUser) {
  92. return $this->fail('用户不存在');
  93. }
  94. // 检查会话关系是否存在,不存在则创建
  95. $relation = ChatUserRelation::getOrCreate($this->userId, $toUserId);
  96. // 如果未扣过次数,则扣除
  97. if ($relation && $relation->is_deduct == ChatUserRelation::NOT_DEDUCT) {
  98. $result = ChatBalanceService::checkAndDeductFirstChat($this->userId, $toUserId);
  99. if (!$result['success']) {
  100. return $this->fail($result['message']);
  101. }
  102. }
  103. // 保存聊天记录(包含来源类型)
  104. $record = ChatRecord::saveRecord($this->userId, $toUserId, $content, $type, $formType);
  105. // 更新会话关系时间
  106. ChatUserRelation::where('id', $relation->id)->update(['update_time' => time()]);
  107. // 广播消息给在线用户
  108. self::broadcastMessage($toUserId, [
  109. 'type' => 'new_message',
  110. 'data' => [
  111. 'id' => $record->id,
  112. 'from_user_id' => $this->userId,
  113. 'to_user_id' => $toUserId,
  114. 'content' => $content,
  115. 'msg_type' => $type,
  116. 'form_type' => $formType,
  117. 'create_time' => $record->create_time,
  118. ],
  119. ]);
  120. return $this->success('发送成功', [
  121. 'record_id' => $record->id,
  122. 'create_time' => $record->create_time,
  123. ]);
  124. }
  125. /**
  126. * 获取聊天记录
  127. * @return Response
  128. */
  129. public function recordList(): Response
  130. {
  131. $data = $this->request->get();
  132. $toUserId = (int)($data['to_user_id'] ?? 0);
  133. $page = (int)($data['page'] ?? 1);
  134. $limit = (int)($data['limit'] ?? 20);
  135. if ($toUserId <= 0) {
  136. return $this->fail('对方用户ID错误');
  137. }
  138. $result = ChatRecord::getChatRecords($this->userId, $toUserId, $page, $limit);
  139. // 处理消息类型文本
  140. foreach ($result['list'] as &$item) {
  141. $item['type_text'] = ChatRecord::getTypeText($item['type']);
  142. $item['form_type_text'] = ChatRecord::getFormTypeText($item['form_type'] ?? 0);
  143. // 判断是否是自己发送的
  144. $item['is_mine'] = $item['user_id'] == $this->userId;
  145. }
  146. return $this->success('获取成功', $result);
  147. }
  148. /**
  149. * 标记消息已读
  150. * @return Response
  151. */
  152. public function markRead(): Response
  153. {
  154. $data = $this->request->post();
  155. $fromUserId = (int)($data['from_user_id'] ?? 0);
  156. if ($fromUserId <= 0) {
  157. return $this->fail('用户ID错误');
  158. }
  159. ChatRecord::markAsRead($this->userId, $fromUserId);
  160. return $this->success('标记成功');
  161. }
  162. /**
  163. * 获取未读消息数
  164. * @return Response
  165. */
  166. public function unreadCount(): Response
  167. {
  168. $data = $this->request->get();
  169. $fromUserId = $data['from_user_id'] ?? null;
  170. if ($fromUserId !== null) {
  171. $count = ChatRecord::getUnreadCount($this->userId, (int)$fromUserId);
  172. } else {
  173. $count = ChatRecord::getTotalUnreadCount($this->userId);
  174. }
  175. return $this->success('获取成功', [
  176. 'count' => $count,
  177. ]);
  178. }
  179. /**
  180. * 删除会话
  181. * @return Response
  182. */
  183. public function deleteSession(): Response
  184. {
  185. $data = $this->request->post();
  186. $toUserId = (int)($data['to_user_id'] ?? 0);
  187. if ($toUserId <= 0) {
  188. return $this->fail('对方用户ID错误');
  189. }
  190. $result = ChatUserRelation::deleteSession($this->userId, $toUserId);
  191. if ($result) {
  192. return $this->success('删除成功');
  193. }
  194. return $this->fail('删除失败');
  195. }
  196. /**
  197. * 获取用户信息
  198. * @return Response
  199. */
  200. public function getUserInfo(): Response
  201. {
  202. $data = $this->request->get();
  203. $userId = (int)($data['user_id'] ?? 0);
  204. if ($userId <= 0) {
  205. return $this->fail('用户ID错误');
  206. }
  207. $user = User::find($userId);
  208. if (!$user) {
  209. return $this->fail('用户不存在');
  210. }
  211. return $this->success('获取成功', [
  212. 'user_id' => $user->id,
  213. 'nickname' => $user->nickname ?? '',
  214. 'avatar' => $user->avatar ?? '',
  215. ]);
  216. }
  217. /**
  218. * 广播消息给指定用户
  219. * @param int $userId 用户ID
  220. * @param array $data 消息数据
  221. */
  222. protected static function broadcastMessage(int $userId, array $data): void
  223. {
  224. try {
  225. \Channel\Client::getInstance()->send('muyinjie_chat_' . $userId, $data);
  226. } catch (\Throwable $e) {
  227. // 忽略广播失败
  228. }
  229. }
  230. }