From 3e8b6079481f189a548e681f90d011a186fc1181 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 13 Jan 2026 10:20:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A5=BD=E5=8F=8B=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/MessageController.php | 38 +-- Server/application/command.php | 3 + .../command/CheckUnreadMessageCommand.php | 57 ++++ .../common/service/FriendTransferService.php | 254 ++++++++++++++++++ Server/config/task_scheduler.php | 10 + 5 files changed, 333 insertions(+), 29 deletions(-) create mode 100644 Server/application/command/CheckUnreadMessageCommand.php create mode 100644 Server/application/common/service/FriendTransferService.php diff --git a/Server/application/api/controller/MessageController.php b/Server/application/api/controller/MessageController.php index daedae1a..34b79b5b 100644 --- a/Server/application/api/controller/MessageController.php +++ b/Server/application/api/controller/MessageController.php @@ -3,6 +3,7 @@ namespace app\api\controller; use app\api\model\WechatMessageModel; +use app\common\service\FriendTransferService; use think\Db; use think\facade\Request; @@ -389,38 +390,17 @@ class MessageController extends BaseController if ($item['msgType'] == 10000 && strpos($item['content'],'开启了朋友验证') !== false) { Db::table('s2_wechat_friend')->where('id',$item['wechatFriendId'])->update(['isDeleted'=> 1,'deleteTime' => $wechatTime]); }else{ - //优先分配在线客服 + //优先分配在线客服 - 使用新的好友迁移服务 $friend = Db::table('s2_wechat_friend')->where('id',$item['wechatFriendId'])->find(); if (!empty($friend)){ $accountId = $item['accountId']; - $accountData = Db::table('s2_company_account')->where('id',$accountId)->find(); - if (!empty($accountData)){ - $account = new AccountController(); - $account->getlist(['pageIndex' => 0,'pageSize' => 100,'departmentId' => $accountData['departmentId']]); - $accountIds = Db::table('s2_company_account')->where(['departmentId' => $accountData['departmentId'],'alive' => 1])->column('id'); - if (!empty($accountIds)){ - if (!in_array($friend['accountId'],$accountIds)){ - // 执行切换好友命令 - $randomKey = array_rand($accountIds, 1); - $toAccountId = $accountIds[$randomKey]; - $toAccountData = Db::table('s2_company_account')->where('id',$toAccountId)->find(); - $automaticAssign = new AutomaticAssign(); - $automaticAssign->allotWechatFriend([ - 'wechatFriendId' => $friend['id'], - 'toAccountId' => $toAccountId - ], true); - Db::table('s2_wechat_friend') - ->where('id',$friend['id']) - ->update([ - 'accountId' => $toAccountId, - 'accountUserName' => $toAccountData['userName'], - 'accountRealName' => $toAccountData['realName'], - 'accountNickname' => $toAccountData['nickname'], - ]); - } - } - - } + $friendTransferService = new FriendTransferService(); + $result = $friendTransferService->transferFriend( + $item['wechatFriendId'], + $accountId, + '账号不在线,自动迁移到在线账号' + ); + // 迁移结果已记录在服务中,这里不需要额外处理 } } diff --git a/Server/application/command.php b/Server/application/command.php index 2bf23db7..8fc4a2e9 100644 --- a/Server/application/command.php +++ b/Server/application/command.php @@ -47,4 +47,7 @@ return [ // 统一任务调度器 'scheduler:run' => 'app\command\TaskSchedulerCommand', // 统一任务调度器,支持多进程并发执行 + + // 检查未读/未回复消息并自动迁移好友 + 'check:unread-message' => 'app\command\CheckUnreadMessageCommand', // 检查未读/未回复消息并自动迁移好友 ]; diff --git a/Server/application/command/CheckUnreadMessageCommand.php b/Server/application/command/CheckUnreadMessageCommand.php new file mode 100644 index 00000000..3d47b95e --- /dev/null +++ b/Server/application/command/CheckUnreadMessageCommand.php @@ -0,0 +1,57 @@ +setName('check:unread-message') + ->setDescription('检查未读/未回复消息并自动迁移好友') + ->addOption('minutes', 'm', \think\console\input\Option::VALUE_OPTIONAL, '未读/未回复分钟数,默认30分钟', 30); + } + + protected function execute(Input $input, Output $output) + { + $minutes = intval($input->getOption('minutes')); + if ($minutes <= 0) { + $minutes = 30; + } + + $output->writeln("开始检查未读/未回复消息(超过{$minutes}分钟)..."); + + try { + $friendTransferService = new FriendTransferService(); + $result = $friendTransferService->checkAndTransferUnreadOrUnrepliedFriends($minutes); + + $output->writeln("检查完成:"); + $output->writeln(" 总计需要迁移的好友数:{$result['total']}"); + $output->writeln(" 成功迁移的好友数:{$result['transferred']}"); + $output->writeln(" 迁移失败的好友数:{$result['failed']}"); + + if ($result['total'] > 0) { + Log::info("未读/未回复消息检查完成:总计{$result['total']},成功{$result['transferred']},失败{$result['failed']}"); + } + + } catch (\Exception $e) { + $errorMsg = "检查未读/未回复消息异常:" . $e->getMessage(); + $output->writeln("{$errorMsg}"); + Log::error($errorMsg); + } + } +} + diff --git a/Server/application/common/service/FriendTransferService.php b/Server/application/common/service/FriendTransferService.php new file mode 100644 index 00000000..4382a656 --- /dev/null +++ b/Server/application/common/service/FriendTransferService.php @@ -0,0 +1,254 @@ + bool, 'message' => string, 'toAccountId' => int|null] + */ + public function transferFriend($wechatFriendId, $currentAccountId, $reason = '') + { + try { + // 获取好友信息 + $friend = Db::table('s2_wechat_friend')->where('id', $wechatFriendId)->find(); + if (empty($friend)) { + return [ + 'success' => false, + 'message' => '好友不存在', + 'toAccountId' => null + ]; + } + + // 获取当前账号的部门信息 + $accountData = Db::table('s2_company_account')->where('id', $currentAccountId)->find(); + if (empty($accountData)) { + return [ + 'success' => false, + 'message' => '当前账号不存在', + 'toAccountId' => null + ]; + } + + // 获取同部门的在线账号列表 + $accountController = new AccountController(); + $accountController->getlist([ + 'pageIndex' => 0, + 'pageSize' => 100, + 'departmentId' => $accountData['departmentId'] + ]); + + $accountIds = Db::table('s2_company_account') + ->where([ + 'departmentId' => $accountData['departmentId'], + 'alive' => 1 + ]) + ->column('id'); + + if (empty($accountIds)) { + return [ + 'success' => false, + 'message' => '没有可用的在线账号', + 'toAccountId' => null + ]; + } + + // 如果好友当前账号不在可用账号列表中,或者需要迁移到其他账号 + $needTransfer = !in_array($friend['accountId'], $accountIds); + + // 如果需要迁移,选择目标账号 + if ($needTransfer || $currentAccountId != $friend['accountId']) { + // 排除当前账号,选择其他账号 + $availableAccountIds = array_filter($accountIds, function($id) use ($currentAccountId) { + return $id != $currentAccountId; + }); + + if (empty($availableAccountIds)) { + return [ + 'success' => false, + 'message' => '没有其他可用的在线账号', + 'toAccountId' => null + ]; + } + + // 随机选择一个账号 + $availableAccountIds = array_values($availableAccountIds); + $randomKey = array_rand($availableAccountIds, 1); + $toAccountId = $availableAccountIds[$randomKey]; + + // 获取目标账号信息 + $toAccountData = Db::table('s2_company_account')->where('id', $toAccountId)->find(); + if (empty($toAccountData)) { + return [ + 'success' => false, + 'message' => '目标账号不存在', + 'toAccountId' => null + ]; + } + + // 执行迁移 + $automaticAssign = new AutomaticAssign(); + $result = $automaticAssign->allotWechatFriend([ + 'wechatFriendId' => $wechatFriendId, + 'toAccountId' => $toAccountId + ], true); + + $resultData = json_decode($result, true); + + if (isset($resultData['code']) && $resultData['code'] == 200) { + // 更新好友的账号信息 + Db::table('s2_wechat_friend') + ->where('id', $wechatFriendId) + ->update([ + 'accountId' => $toAccountId, + 'accountUserName' => $toAccountData['userName'], + 'accountRealName' => $toAccountData['realName'], + 'accountNickname' => $toAccountData['nickname'], + ]); + + $logMessage = "好友迁移成功:好友ID={$wechatFriendId},从账号{$currentAccountId}迁移到账号{$toAccountId}"; + if (!empty($reason)) { + $logMessage .= ",原因:{$reason}"; + } + Log::info($logMessage); + + return [ + 'success' => true, + 'message' => '好友迁移成功', + 'toAccountId' => $toAccountId + ]; + } else { + $errorMsg = isset($resultData['msg']) ? $resultData['msg'] : '迁移失败'; + Log::error("好友迁移失败:好友ID={$wechatFriendId},错误:{$errorMsg}"); + return [ + 'success' => false, + 'message' => $errorMsg, + 'toAccountId' => null + ]; + } + } + + return [ + 'success' => true, + 'message' => '好友已在正确的账号上,无需迁移', + 'toAccountId' => $friend['accountId'] + ]; + + } catch (\Exception $e) { + Log::error("好友迁移异常:好友ID={$wechatFriendId},错误:" . $e->getMessage()); + return [ + 'success' => false, + 'message' => '迁移异常:' . $e->getMessage(), + 'toAccountId' => null + ]; + } + } + + /** + * 检查并迁移未读或未回复的好友 + * @param int $unreadMinutes 未读分钟数,默认30分钟 + * @return array ['total' => int, 'transferred' => int, 'failed' => int] + */ + public function checkAndTransferUnreadOrUnrepliedFriends($unreadMinutes = 30) + { + $total = 0; + $transferred = 0; + $failed = 0; + + try { + $timeThreshold = time() - ($unreadMinutes * 60); + + // 查询需要迁移的好友 + // 条件:最后一条消息是用户发送的消息(isSend=0),且超过指定分钟数,且客服在这之后没有回复 + // 即:用户发送了消息,但客服超过30分钟没有回复,需要迁移给其他客服处理 + + // 使用子查询找到每个好友的最后一条消息 + // SQL逻辑说明: + // 1. 找到每个好友的最后一条消息(通过MAX(id)) + // 2. 最后一条消息必须是用户发送的(isSend=0,即客服接收的消息) + // 3. 这条消息的时间超过30分钟前(wm.wechatTime <= timeThreshold) + // 4. 在这条用户消息之后,客服没有发送任何回复(NOT EXISTS isSend=1的消息) + // 5. 满足以上条件的好友,说明客服超过30分钟未回复,需要迁移给其他客服 + $sql = " + SELECT DISTINCT + wf.id as friendId, + wf.accountId, + wm.wechatAccountId, + wm.wechatTime, + wm.id as lastMessageId + FROM s2_wechat_friend wf + INNER JOIN ( + SELECT wechatFriendId, MAX(id) as maxId + FROM s2_wechat_message + WHERE type = 1 + GROUP BY wechatFriendId + ) last_msg ON wf.id = last_msg.wechatFriendId + INNER JOIN s2_wechat_message wm ON wm.id = last_msg.maxId + WHERE wf.isDeleted = 0 + AND wm.type = 1 + AND wm.isSend = 0 -- 最后一条消息是用户发送的(客服接收的) + AND wm.wechatTime <= ? -- 超过指定时间(默认30分钟) + AND NOT EXISTS ( + -- 检查在这条用户消息之后,是否有客服的回复 + SELECT 1 + FROM s2_wechat_message + WHERE wechatFriendId = wf.id + AND type = 1 + AND isSend = 1 -- 客服发送的消息 + AND wechatTime > wm.wechatTime -- 在用户消息之后 + ) + AND wf.accountId IS NOT NULL + "; + + $friends = Db::query($sql, [$timeThreshold]); + $total = count($friends); + + Log::info("开始检查未读/未回复好友,共找到 {$total} 个需要迁移的好友"); + + foreach ($friends as $friend) { + $result = $this->transferFriend( + $friend['friendId'], + $friend['accountId'], + "消息未读或未回复超过{$unreadMinutes}分钟" + ); + + if ($result['success']) { + $transferred++; + } else { + $failed++; + Log::warning("好友迁移失败:好友ID={$friend['friendId']},原因:{$result['message']}"); + } + } + + Log::info("未读/未回复好友迁移完成:总计{$total},成功{$transferred},失败{$failed}"); + + return [ + 'total' => $total, + 'transferred' => $transferred, + 'failed' => $failed + ]; + + } catch (\Exception $e) { + Log::error("检查未读/未回复好友异常:" . $e->getMessage()); + return [ + 'total' => $total, + 'transferred' => $transferred, + 'failed' => $failed + ]; + } + } +} + diff --git a/Server/config/task_scheduler.php b/Server/config/task_scheduler.php index 7fa7ea37..6b6a4940 100644 --- a/Server/config/task_scheduler.php +++ b/Server/config/task_scheduler.php @@ -289,6 +289,16 @@ return [ 'log_file' => 'all_friends.log', ], + // 检查未读/未回复消息并自动迁移好友(每5分钟执行一次) + 'check_unread_message' => [ + 'command' => 'check:unread-message', + 'schedule' => '*/5 * * * *', // 每5分钟 + 'options' => ['--minutes=30'], // 30分钟未读/未回复 + 'enabled' => true, + 'max_concurrent' => 1, + 'log_file' => 'check_unread_message.log', + ], + // 已禁用的任务(注释掉的任务) // 'workbench_group_push' => [ // 'command' => 'workbench:groupPush',