好友切换

This commit is contained in:
wong
2026-01-13 10:20:33 +08:00
parent 9eeaaa70c4
commit 3e8b607948
5 changed files with 333 additions and 29 deletions

View File

@@ -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,
'账号不在线,自动迁移到在线账号'
);
// 迁移结果已记录在服务中,这里不需要额外处理
}
}

View File

@@ -47,4 +47,7 @@ return [
// 统一任务调度器
'scheduler:run' => 'app\command\TaskSchedulerCommand', // 统一任务调度器,支持多进程并发执行
// 检查未读/未回复消息并自动迁移好友
'check:unread-message' => 'app\command\CheckUnreadMessageCommand', // 检查未读/未回复消息并自动迁移好友
];

View File

@@ -0,0 +1,57 @@
<?php
namespace app\command;
use app\common\service\FriendTransferService;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Log;
/**
* 检查未读/未回复消息并自动迁移好友命令
*
* 功能:
* 1. 检查消息未读超过30分钟的好友
* 2. 检查消息未回复超过30分钟的好友
* 3. 自动迁移这些好友到其他在线账号
*/
class CheckUnreadMessageCommand extends Command
{
protected function configure()
{
$this->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("<error>{$errorMsg}</error>");
Log::error($errorMsg);
}
}
}

View File

@@ -0,0 +1,254 @@
<?php
namespace app\common\service;
use app\api\controller\AutomaticAssign;
use app\api\controller\AccountController;
use think\Db;
use think\facade\Log;
/**
* 好友迁移服务类
* 负责处理好友在不同账号之间的迁移逻辑
*/
class FriendTransferService
{
/**
* 迁移好友到其他账号
* @param int $wechatFriendId 微信好友ID
* @param int $currentAccountId 当前账号ID
* @param string $reason 迁移原因
* @return array ['success' => 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
];
}
}
}