From 2a89361d3ec75047d90348041aaae5de73ca42fd Mon Sep 17 00:00:00 2001 From: ASUS <515617283@qq.com> Date: Wed, 19 Nov 2025 15:34:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=B3=E6=97=B6=E6=B6=88=E6=81=AF=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AE=8C=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/chat/controller/GroupController.php | 18 ++ app/chat/controller/MessageController.php | 201 +++++++--------------- app/chat/model/ChatMessage.php | 44 +++++ app/functions.php | 48 +++++- config/route.php | 3 - 5 files changed, 167 insertions(+), 147 deletions(-) diff --git a/app/chat/controller/GroupController.php b/app/chat/controller/GroupController.php index 5d761e1..a90b08c 100644 --- a/app/chat/controller/GroupController.php +++ b/app/chat/controller/GroupController.php @@ -95,6 +95,14 @@ class GroupController extends ApiController return $this->error('已在群内'); } + + // 再次加入时 + $z_exists = Db::name('chat_group_member')->where([ + 'group_id' => $group_id, + 'user_id' => $this->uid, + 'is_kicked' => 0 + ])->where('quit_time', 'not null')->find(); + // 被踢用户不能重新加入(需群主邀请) $isKicked = Db::name('chat_group_member')->where([ 'group_id' => $group_id, @@ -113,6 +121,8 @@ class GroupController extends ApiController 'join_time' => d(), ]; $is_insert = true; + + if($invite) { // 验证邀请参数是否正常 $decryptedText = simple_decrypt($invite, config('cons.sercer_key')); @@ -125,6 +135,11 @@ class GroupController extends ApiController $insert_arr['group_id'] = $decryptedText_arr['group_id']; $group_id = $decryptedText_arr['group_id']; } + // 如果是再次加入 + if($z_exists) { + $insert_arr['quit_time'] = null; + $is_insert = false; + } try { Db::startTrans(); // 插入用户信息 @@ -258,6 +273,9 @@ class GroupController extends ApiController 'quit_time' => null, 'role' => 1 ])->update(['user_id' => $object_user_id, 'object_user_id' => $this->uid]); + Db::name('chat_group')->where([ + 'id' => $group_id, + ])->update(['owner_id' => $object_user_id]); if($res) { return $this->success(); } diff --git a/app/chat/controller/MessageController.php b/app/chat/controller/MessageController.php index e55661f..8bb9893 100644 --- a/app/chat/controller/MessageController.php +++ b/app/chat/controller/MessageController.php @@ -8,6 +8,7 @@ use app\chat\model\ChatUser; use app\chat\model\ChatGroup; use app\common\controller\ApiController; use app\utils\Session; +use support\Redis; use support\Request; use support\Response; use support\think\Db; @@ -48,10 +49,11 @@ class MessageController extends ApiController // 分页查询(倒序取,再正序返回) $total = $query->count(); - $messages = $query->order('send_time', 'desc') + $messages = $query->alias('msg')->order('send_time', 'desc') + ->leftJoin('chat_user user', 'msg.from_id = user.user_id') + ->field('msg.id,from_id,to_id,chat_type,msg_type,content,image_url,order_id,is_read,send_time,session_id,nick_name,user_id,avatar,type') ->page($page, $size) ->select()->toArray(); - $messages = array_reverse($messages); // 单聊自动标记已读 if ($chatType == 1) { @@ -67,7 +69,9 @@ class MessageController extends ApiController 'session_id' => $session_id ])->update(['count' => 0, 'updated_time' => d()]); } - + foreach ($messages as $k => &$v) { + $v['send_time'] = formatWeChatTime($v['send_time']); + } return $this->success([ 'list' => $messages, 'page' => $page, @@ -80,42 +84,33 @@ class MessageController extends ApiController */ public function markRead(Request $request): Response { - $uid = $request->uid; - $msgIds = $request->post('msg_ids', []); - - if (empty($msgIds) || !is_array($msgIds)) { - return json(['code' => 400, 'msg' => '请传入有效消息ID数组']); + $msgIds = $request->post('msg_ids'); + if (empty($msgIds) || !is_string($msgIds)) { + return $this->error('请传入有效消息ID'); } - + $msgIds = explode(',', $msgIds); + $query = Db::name('chat_message')->whereIn('id', $msgIds)->where('to_id', $this->uid) + ->where('is_read', 0); // 标记已读(仅自己接收的消息) - $messages = ChatMessage::whereIn('id', $msgIds) - ->where('to_id', $uid) - ->where('is_read', 0) - ->get(); - - if (empty($messages)) { - return json(['code' => 200, 'msg' => '无未读消息可标记']); + $count = $query->count(); + if (empty($count)) { + return $this->error('无未读消息可标记'); } - - // 批量更新 - $sessionIds = $messages->pluck('session_id')->unique()->toArray(); - ChatMessage::whereIn('id', $msgIds)->where('to_id', $uid)->update(['is_read' => 1]); - - // 更新未读计数 - foreach ($sessionIds as $sessionId) { - $reduceNum = ChatMessage::whereIn('id', $msgIds) - ->where('session_id', $sessionId) - ->count(); - $unread = ChatUnreadCount::where(['user_id' => $uid, 'session_id' => $sessionId])->first(); - if ($unread) { - $newCount = max(0, $unread->count - $reduceNum); - $unread->count = $newCount; - $unread->updated_at = time(); - $unread->save(); + try { + Db::startTrans(); + $sessionIds = $query->group('session_id')->column('session_id'); + // 更新未读计数 + foreach ($sessionIds as $sessionId) { + Db::name('chat_unread_count')->where(['user_id' => $this->uid, 'session_id' => $sessionId])->update(['count' => 0, 'updated_time' => d()]); } + // 批量更新 + $query->update(['is_read' => 1]); + Db::commit(); + return $this->success(); + }catch (\Throwable $exception) { + Db::rollback(); + return $this->error($exception->getMessage()); } - - return json(['code' => 200, 'msg' => '标记已读成功']); } /** @@ -123,27 +118,27 @@ class MessageController extends ApiController */ public function markReadAll(Request $request): Response { - $uid = $request->uid; - $sessionId = $request->post('session_id', ''); - - if (empty($sessionId)) { - return json(['code' => 400, 'msg' => '缺少session_id']); + $msgIds = $request->post('session_ids'); + if (empty($msgIds) || !is_string($msgIds)) { + return $this->error('请传入有效会话ID'); + } + $msgIds = explode(',', $msgIds); + $query = Db::name('chat_message')->whereIn('session_id', $msgIds)->where('to_id', $this->uid) + ->where('is_read', 0); + try { + Db::startTrans(); + // 更新未读计数 + foreach ($msgIds as $sessionId) { + Db::name('chat_unread_count')->where(['user_id' => $this->uid, 'session_id' => $sessionId])->update(['count' => 0, 'updated_time' => d()]); + } + // 批量更新 + $query->update(['is_read' => 1]); + Db::commit(); + return $this->success(); + }catch (\Throwable $exception) { + Db::rollback(); + return $this->error($exception->getMessage()); } - - // 标记该会话所有未读消息已读 - ChatMessage::where([ - 'session_id' => $sessionId, - 'to_id' => $uid, - 'is_read' => 0 - ])->update(['is_read' => 1]); - - // 重置未读计数 - ChatUnreadCount::where([ - 'user_id' => $uid, - 'session_id' => $sessionId - ])->update(['count' => 0, 'updated_at' => time()]); - - return json(['code' => 200, 'msg' => '标记全部已读成功']); } /** @@ -151,10 +146,8 @@ class MessageController extends ApiController */ public function getUnreadCount(Request $request): Response { - $uid = $request->uid; - $total = ChatUnreadCount::where('user_id', $uid)->sum('count'); - - return json(['code' => 200, 'msg' => 'success', 'data' => ['total' => $total]]); + $total = Db::name('chat_unread_count')->where('user_id', $this->uid)->sum('count'); + return $this->success(['total' => $total]); } /** @@ -162,93 +155,17 @@ class MessageController extends ApiController */ public function getSessionList(Request $request): Response { - $uid = $request->uid; - // 获取所有会话ID - $sessionIds = ChatMessage::where(function ($query) use ($uid) { - $query->where('from_id', $uid)->orWhere('to_id', $uid); - })->groupBy('session_id')->pluck('session_id')->toArray(); - + $sessionIds = Db::name('chat_message')->where('from_id', $this->uid)->WhereOr('to_id', $this->uid) + ->group('session_id')->column('session_id'); if (empty($sessionIds)) { - return json(['code' => 200, 'msg' => 'success', 'data' => ['list' => []]]); + return $this->success(); } - - $sessionList = []; - foreach ($sessionIds as $sessionId) { - // 置顶状态 - $top = ChatTop::where(['user_id' => $uid, 'session_id' => $sessionId])->first(); - $isTop = $top && $top->status == 1 ? 1 : 0; - $sort = $top ? $top->sort : 0; - - // 未读计数 - $unread = ChatUnreadCount::where(['user_id' => $uid, 'session_id' => $sessionId])->first(); - $unreadCount = $unread ? $unread->count : 0; - - // 最后一条消息 - $lastMsg = ChatMessage::where('session_id', $sessionId) - ->orderBy('send_time', 'desc') - ->first([ - 'id', 'from_id', 'msg_type', 'content', 'image_url', - 'order_id', 'send_time', 'is_read' - ]); - - if (!$lastMsg) continue; - - // 会话类型(单聊/群聊) - $chatType = strpos($sessionId, 'group_') === 0 ? 2 : 1; - $targetInfo = []; - - if ($chatType == 1) { - // 单聊:解析对方ID - list($minId, $maxId) = explode('_', $sessionId); - $targetUid = $uid == $minId ? $maxId : $minId; - $targetUser = ChatUser::find($targetUid); - if ($targetUser) { - $targetInfo = [ - 'id' => $targetUser->id, - 'name' => $targetUser->username, - 'avatar' => $targetUser->avatar, - 'type' => $targetUser->type - ]; - } - } else { - // 群聊:解析群ID - $groupId = str_replace('group_', '', $sessionId); - $group = ChatGroup::find($groupId); - if ($group) { - $targetInfo = [ - 'id' => $group->id, - 'name' => $group->name, - 'avatar' => $group->avatar, - 'owner_id' => $group->owner_id - ]; - } - } - - $sessionList[] = [ - 'session_id' => $sessionId, - 'chat_type' => $chatType, - 'is_top' => $isTop, - 'sort' => $sort, - 'unread_count' => $unreadCount, - 'last_msg' => $lastMsg ? $lastMsg->toArray() : [], - 'target_info' => $targetInfo - ]; + $list = Redis::get('usermsg:list:' . $this->user_type . ':' . $this->uid); + if($list) { + return $this->success(['list' => json_decode($list, true)]); + }else { + return $this->success(['list' => ChatMessage::getconverlist($this->uid, $this->user_type)]); } - - // 排序:置顶(按sort降序)→ 非置顶(按最后消息时间降序) - usort($sessionList, function ($a, $b) { - if ($a['is_top'] != $b['is_top']) { - return $b['is_top'] - $a['is_top']; - } - if ($a['is_top'] == 1) { - return $b['sort'] - $a['sort']; - } - $aTime = $a['last_msg']['send_time'] ?? 0; - $bTime = $b['last_msg']['send_time'] ?? 0; - return $bTime - $aTime; - }); - - return json(['code' => 200, 'msg' => 'success', 'data' => ['list' => $sessionList]]); } } diff --git a/app/chat/model/ChatMessage.php b/app/chat/model/ChatMessage.php index e2e79dc..43cac9c 100644 --- a/app/chat/model/ChatMessage.php +++ b/app/chat/model/ChatMessage.php @@ -2,7 +2,51 @@ namespace app\chat\model; +use support\Redis; +use support\think\Db; + class ChatMessage extends BaseModel { public $tabla_name = 'chat_message'; + + public static function getconverlist($uid, $user_type) + { + // 获取所有会话ID + $sessionIds = Db::name('chat_message')->where('from_id', $uid)->WhereOr('to_id', $uid) + ->group('session_id')->column('session_id'); + $data = []; + $i = 0; + foreach ($sessionIds as $k => $session_id) { + // 未读计数 + $unreadCount = Db::name('chat_unread_count')->where(['user_id' => $uid, 'session_id' => $session_id])->value('count')?:0; + $data[$i]['unread_count'] = $unreadCount; + // 最后一条消息 + $lastMsg = Db::name('chat_message') + ->where('session_id', $session_id) + ->order('send_time', 'desc') + ->find(); + if($lastMsg) { + $chat_user = Db::name('chat_user')->where(['user_id' => $lastMsg['from_id']])->field('nick_name,avatar')->find(); + // 如果是自己发的不显示发送人 + if($lastMsg['from_id'] == $uid) { + $data[$i]['msg'] = $lastMsg['content']; + }else { + // 如果是别人发的显示昵称 + $data[$i]['msg'] = $chat_user['nick_name'] . ':' . $lastMsg['content']; + } + $data[$i]['avatar'] = $chat_user['avatar']; + $data[$i]['chat_type'] = $lastMsg['chat_type']; + }else { + $data[$i][] = ''; + } + $data[$i]['send_time'] = formatWeChatTime($lastMsg['send_time']); + $data[$i]['session_id'] = $session_id; + $i ++; + } + if(Redis::set('usermsg:list:' . $user_type . ':' . $uid, json_encode($data))) { + return $data; + } + } + + } \ No newline at end of file diff --git a/app/functions.php b/app/functions.php index 400b833..463f9d1 100644 --- a/app/functions.php +++ b/app/functions.php @@ -141,16 +141,60 @@ function simple_decrypt(string $ciphertext, string $key): string|false } +/** + * 微信风格时间格式化(仅用原生 date/strtotime 函数,周一为一周起点) + * @param string $inputTime 输入时间字符串(支持:Y-m-d H:i:s、timestamp、Y-m-d 等) + * @param string $timezone 时区(默认北京时间,原生函数依赖服务器时区,建议提前配置) + * @return string 格式化结果(如:09:11、昨天 15:30、周一 10:20、11-16 08:05、2024-05-20 14:30) + */ +function formatWeChatTime(string $inputTime, string $timezone = 'Asia/Shanghai'): string +{ + // 1. 配置时区(原生函数需手动设置,避免服务器时区差异) + date_default_timezone_set($timezone); + // 2. 转换目标时间为时间戳(兼容多种输入格式) + $targetTimestamp = strtotime($inputTime); + if ($targetTimestamp === false) { + error_log('无效时间格式:' . $inputTime); + return $inputTime; // 无效时间返回原始值 + } + // 3. 计算关键时间边界的时间戳(原生函数核心) + $nowTimestamp = time(); // 当前时间戳 + $todayStart = strtotime('today'); // 今天 00:00:00(等价于 mktime(0,0,0,date('m'),date('d'),date('Y'))) + $yesterdayStart = strtotime('yesterday'); // 昨天 00:00:00(today - 86400) + $currentWeekDay = date('N', $todayStart); // 今天是本周第几天(1=周一,7=周日) + $mondayStart = strtotime("-$currentWeekDay days +1 day", $todayStart); // 本周一 00:00:00 + $yearStart = strtotime(date('Y') . '-01-01 00:00:00'); // 今年 1月1日 00:00:00 + // 4. 周几映射(date('N') 返回 1-7 → 对应周一-周日) + $weekMap = [1 => '周一', 2 => '周二', 3 => '周三', 4 => '周四', 5 => '周五', 6 => '周六', 7 => '周日']; + // 5. 按优先级判断场景(从近到远) + // 场景1:今天(>= 今天0点) + if ($targetTimestamp >= $todayStart) { + return date('H:i', $targetTimestamp); + } + // 场景2:昨天(>= 昨天0点 且 < 今天0点) + if ($targetTimestamp >= $yesterdayStart && $targetTimestamp < $todayStart) { + return '昨天 ' . date('H:i', $targetTimestamp); + } + // 场景3:本周内(>= 本周一0点 且 < 今天0点)→ 显示周几+时间 + if ($targetTimestamp >= $mondayStart && $targetTimestamp < $todayStart) { + $targetWeekDay = date('N', $targetTimestamp); // 目标时间的周几 + return $weekMap[$targetWeekDay] . ' ' . date('H:i', $targetTimestamp); + } + // 场景4:今年内(>= 今年1月1日 且 < 本周一0点)→ 显示月-日+时间 + if ($targetTimestamp >= $yearStart && $targetTimestamp < $mondayStart) { + return date('m-d H:i', $targetTimestamp); + } - - + // 场景5:去年及以前(< 今年1月1日)→ 显示完整日期+时间 + return date('Y-m-d H:i', $targetTimestamp); +} /** * 多维数组去重并重新索引 diff --git a/config/route.php b/config/route.php index 5e177e5..f9ceb99 100644 --- a/config/route.php +++ b/config/route.php @@ -48,9 +48,6 @@ Route::group('/api/chat', function () { Route::post('/unread-count', app\chat\controller\MessageController::class . '@getUnreadCount'); // 未读总数 Route::post('/session-list', app\chat\controller\MessageController::class . '@getSessionList'); // 会话列表 })->middleware($jwt_middleware); - - // 图片上传(相册/拍照) - Route::post('/upload/image', app\chat\controller\UploadController::class . '@image')->middleware($jwt_middleware); });