ChatRecord.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. declare (strict_types = 1);
  3. namespace app\model\api;
  4. use library\basic\BaseModel;
  5. use library\traits\ModelTrait;
  6. use think\facade\Db;
  7. use think\Model;
  8. /**
  9. * 聊天记录模型
  10. * @mixin \think\Model
  11. */
  12. class ChatRecord extends BaseModel
  13. {
  14. use ModelTrait;
  15. protected $name = 'chat_record';
  16. // 消息类型
  17. const TYPE_TEXT = 1; // 文字
  18. const TYPE_VOICE = 2; // 语音
  19. const TYPE_IMAGE = 3; // 图片
  20. // 未读
  21. const UNREAD = 0;
  22. // 已读
  23. const READ = 1;
  24. // 来源类型
  25. const FROM_PC = 0; // PC端
  26. const FROM_WECHAT = 1; // 微信
  27. const FROM_MINIAPP = 2; // 小程序
  28. const FROM_H5 = 3; // H5
  29. /**
  30. * 保存聊天记录
  31. * @param int $userId 发送者ID
  32. * @param int $toUserId 接收者ID
  33. * @param string $content 消息内容
  34. * @param int $type 消息类型
  35. * @param int $formType 来源类型 0=PC, 1=微信, 2=小程序, 3=H5
  36. * @return self
  37. */
  38. public static function saveRecord(int $userId, int $toUserId, string $content, int $type = self::TYPE_TEXT, int $formType = self::FROM_PC): self
  39. {
  40. return self::create([
  41. 'user_id' => $userId,
  42. 'to_user_id' => $toUserId,
  43. 'content' => $content,
  44. 'type' => $type,
  45. 'form_type' => $formType,
  46. 'is_read' => self::UNREAD,
  47. 'create_time' => time(),
  48. ]);
  49. }
  50. /**
  51. * 获取与某用户的聊天记录(分页,按时间正序返回)
  52. * @param int $userId 当前用户ID
  53. * @param int $toUserId 对方用户ID
  54. * @param int $page 页码,第1页为最新消息
  55. * @param int $limit 每页数量
  56. * @return array
  57. */
  58. public static function getChatRecords(int $userId, int $toUserId, int $page = 1, int $limit = 20): array
  59. {
  60. $db = \think\facade\Db::name('chat_record');
  61. $where = function ($query) use ($userId, $toUserId) {
  62. $query->where(function ($q) use ($userId, $toUserId) {
  63. $q->where('user_id', $userId)->where('to_user_id', $toUserId);
  64. })->whereOr(function ($q) use ($userId, $toUserId) {
  65. $q->where('user_id', $toUserId)->where('to_user_id', $userId);
  66. });
  67. };
  68. // 总记录数
  69. $total = $db->where($where)->count();
  70. // 分页参数矫正
  71. $page = max(1, $page);
  72. $limit = max(1, min($limit, 100)); // 限制每页最大100条
  73. // 按时间倒序查最新的一批,拿到后在 PHP 层面反转成正序返回给前端
  74. $data = $db->where($where)
  75. ->order('create_time desc')
  76. ->page($page, $limit)
  77. ->select();
  78. $list = $data ? array_reverse($data->toArray()) : [];
  79. // 标记为已读
  80. self::markAsRead($userId, $toUserId);
  81. $totalPages = (int)ceil($total / $limit);
  82. $hasMore = $page < $totalPages;
  83. return [
  84. 'list' => $list,
  85. 'total' => $total,
  86. 'page' => $page,
  87. 'limit' => $limit,
  88. 'total_pages' => $totalPages,
  89. 'has_more' => $hasMore,
  90. ];
  91. }
  92. /**
  93. * 标记消息为已读
  94. * @param int $userId 当前用户ID
  95. * @param int $fromUserId 发送者ID
  96. * @return bool
  97. */
  98. public static function markAsRead(int $userId, int $fromUserId): bool
  99. {
  100. $affected = Db::name('chat_record')
  101. ->where('user_id', $fromUserId)
  102. ->where('to_user_id', $userId)
  103. ->where('is_read', 0)
  104. ->update(['is_read' => 1]);
  105. // @file_put_contents('quanju.txt', $affected."-何意啊333\r\n", 8);
  106. // 调试:记录执行的SQL和影响行数
  107. // $sql = Db::name('chat_record')->getLastSql();
  108. // trace('【markAsRead】SQL: ' . $sql . ' | 影响行数: ' . $affected, 'chat_debug');
  109. //
  110. return $affected > 0;
  111. }
  112. /**
  113. * 获取未读消息数
  114. * @param int $userId 用户ID
  115. * @param int|null $fromUserId 指定发送者(可选)
  116. * @return int
  117. */
  118. public static function getUnreadCount(int $userId, ?int $fromUserId = null): int
  119. {
  120. $query = Db::name('chat_record')
  121. ->where('to_user_id', $userId)
  122. ->where('is_read', 0);
  123. if ($fromUserId !== null && $fromUserId > 0) {
  124. $query->where('user_id', $fromUserId);
  125. }
  126. $count = $query->count();
  127. // // 调试:记录执行的SQL和结果
  128. // $sql = Db::name('chat_record')->getLastSql();
  129. // trace('【getUnreadCount】SQL: ' . $sql . ' | 结果: ' . $count, 'chat_debug');
  130. //
  131. return $count;
  132. }
  133. /**
  134. * 获取所有未读消息数
  135. * @param int $userId 用户ID
  136. * @return int
  137. */
  138. public static function getTotalUnreadCount(int $userId): int
  139. {
  140. $count = Db::name('chat_record')
  141. ->where('to_user_id', $userId)
  142. ->where('is_read', 0)
  143. ->count();
  144. // // 调试:记录执行的SQL和结果
  145. // $sql = Db::name('chat_record')->getLastSql();
  146. // trace('【getTotalUnreadCount】SQL: ' . $sql . ' | 结果: ' . $count, 'chat_debug');
  147. //
  148. return $count;
  149. }
  150. /**
  151. * 获取消息类型文本
  152. * @param int $type 类型
  153. * @return string
  154. */
  155. public static function getTypeText(int $type): string
  156. {
  157. $typeMap = [
  158. self::TYPE_TEXT => '文字',
  159. self::TYPE_VOICE => '语音',
  160. self::TYPE_IMAGE => '图片',
  161. ];
  162. return $typeMap[$type] ?? '未知';
  163. }
  164. /**
  165. * 获取来源类型文本
  166. * @param int $formType 来源类型
  167. * @return string
  168. */
  169. public static function getFormTypeText(int $formType): string
  170. {
  171. $formTypeMap = [
  172. self::FROM_PC => 'PC端',
  173. self::FROM_WECHAT => '微信',
  174. self::FROM_MINIAPP => '小程序',
  175. self::FROM_H5 => 'H5',
  176. ];
  177. return $formTypeMap[$formType] ?? '未知';
  178. }
  179. }