507 lines
21 KiB
PHP
507 lines
21 KiB
PHP
<?php
|
||
|
||
namespace app\chukebao\controller;
|
||
|
||
use app\api\model\WechatMessageModel;
|
||
use app\chukebao\model\FriendSettings;
|
||
use library\ResponseHelper;
|
||
use think\Db;
|
||
use think\facade\Env;
|
||
use app\common\service\AuthService;
|
||
|
||
class MessageController extends BaseController
|
||
{
|
||
protected $baseUrl;
|
||
protected $authorization;
|
||
|
||
public function __construct()
|
||
{
|
||
parent::__construct();
|
||
$this->baseUrl = Env::get('api.wechat_url');
|
||
$this->authorization = AuthService::getSystemAuthorization();
|
||
}
|
||
|
||
public function getList()
|
||
{
|
||
$page = $this->request->param('page', 1);
|
||
$limit = $this->request->param('limit', 10);
|
||
$accountId = $this->getUserInfo('s2_accountId');
|
||
if (empty($accountId)) {
|
||
return ResponseHelper::error('请先登录');
|
||
}
|
||
|
||
$friends = Db::table('s2_wechat_friend')
|
||
->where(['accountId' => $accountId, 'isDeleted' => 0])
|
||
->column('id,nickname,avatar,conRemark,labels,groupId,wechatAccountId,wechatId,extendFields,phone,region,isTop');
|
||
|
||
|
||
// 构建好友子查询
|
||
$friendSubQuery = Db::table('s2_wechat_friend')
|
||
->where(['accountId' => $accountId, 'isDeleted' => 0])
|
||
->field('id')
|
||
->buildSql();
|
||
|
||
// 计算总数:群聊数量 + 好友数量
|
||
// 使用与 UNION 查询相同的逻辑来计算总数
|
||
$countQuery = "
|
||
SELECT COUNT(*) as total FROM (
|
||
(SELECT DISTINCT wc.id
|
||
FROM s2_wechat_chatroom wc
|
||
INNER JOIN s2_wechat_message m ON wc.id = m.wechatChatroomId AND m.type = 2
|
||
INNER JOIN (
|
||
SELECT wechatChatroomId, MAX(wechatTime) as maxTime, MAX(id) as maxId
|
||
FROM s2_wechat_message
|
||
WHERE type = 2
|
||
GROUP BY wechatChatroomId
|
||
) latest ON m.wechatChatroomId = latest.wechatChatroomId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||
WHERE wc.accountId = {$accountId} AND wc.isDeleted = 0
|
||
)
|
||
UNION ALL
|
||
(SELECT DISTINCT m.wechatFriendId as id
|
||
FROM s2_wechat_message m
|
||
INNER JOIN (
|
||
SELECT wechatFriendId, MAX(wechatTime) as maxTime, MAX(id) as maxId
|
||
FROM s2_wechat_message
|
||
WHERE type = 1 AND wechatFriendId IN {$friendSubQuery}
|
||
GROUP BY wechatFriendId
|
||
) latest ON m.wechatFriendId = latest.wechatFriendId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||
WHERE m.type = 1 AND m.wechatFriendId IN {$friendSubQuery}
|
||
)
|
||
) as combined
|
||
";
|
||
$totalResult = Db::query($countQuery);
|
||
$total = !empty($totalResult) ? (int)$totalResult[0]['total'] : 0;
|
||
|
||
// 优化后的查询:使用MySQL兼容的查询方式
|
||
$unionQuery = "
|
||
(SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime,m.wechatAccountId, 2 as msgType, wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId, wc.isTop
|
||
FROM s2_wechat_chatroom wc
|
||
INNER JOIN s2_wechat_message m ON wc.id = m.wechatChatroomId AND m.type = 2
|
||
INNER JOIN (
|
||
SELECT wechatChatroomId, MAX(wechatTime) as maxTime, MAX(id) as maxId
|
||
FROM s2_wechat_message
|
||
WHERE type = 2
|
||
GROUP BY wechatChatroomId
|
||
) latest ON m.wechatChatroomId = latest.wechatChatroomId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||
WHERE wc.accountId = {$accountId} AND wc.isDeleted = 0
|
||
)
|
||
UNION ALL
|
||
(SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 1 as msgType, 1 as nickname, 1 as avatar, 1 as chatroomId, 1 as wechatAccountId, 0 as isTop
|
||
FROM s2_wechat_message m
|
||
INNER JOIN (
|
||
SELECT wechatFriendId, MAX(wechatTime) as maxTime, MAX(id) as maxId
|
||
FROM s2_wechat_message
|
||
WHERE type = 1 AND wechatFriendId IN {$friendSubQuery}
|
||
GROUP BY wechatFriendId
|
||
) latest ON m.wechatFriendId = latest.wechatFriendId AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||
WHERE m.type = 1 AND m.wechatFriendId IN {$friendSubQuery}
|
||
)
|
||
ORDER BY wechatTime DESC
|
||
LIMIT " . (($page - 1) * $limit) . ", {$limit}
|
||
";
|
||
|
||
$list = Db::query($unionQuery);
|
||
|
||
// 对分页后的结果进行排序(按wechatTime降序)
|
||
usort($list, function ($a, $b) {
|
||
return $b['wechatTime'] <=> $a['wechatTime'];
|
||
});
|
||
|
||
// 批量统计未读数量(isRead=0),按好友/群聊分别聚合
|
||
$friendIds = [];
|
||
$chatroomIds = [];
|
||
foreach ($list as $row) {
|
||
if (!empty($row['wechatFriendId'])) {
|
||
$friendIds[] = $row['wechatFriendId'];
|
||
}
|
||
if (!empty($row['wechatChatroomId'])) {
|
||
$chatroomIds[] = $row['wechatChatroomId'];
|
||
}
|
||
}
|
||
$friendIds = array_values(array_unique(array_filter($friendIds)));
|
||
$chatroomIds = array_values(array_unique(array_filter($chatroomIds)));
|
||
|
||
$friendUnreadMap = [];
|
||
if (!empty($friendIds)) {
|
||
// 获取未读消息数量
|
||
$friendUnreadMap = Db::table('s2_wechat_message')
|
||
->where(['isRead' => 0])
|
||
->whereIn('wechatFriendId', $friendIds)
|
||
->group('wechatFriendId')
|
||
->column('COUNT(*) AS cnt', 'wechatFriendId');
|
||
}
|
||
|
||
$chatroomUnreadMap = [];
|
||
if (!empty($chatroomIds)) {
|
||
// 获取未读消息数量
|
||
$chatroomUnreadMap = Db::table('s2_wechat_message')
|
||
->where(['isRead' => 0])
|
||
->whereIn('wechatChatroomId', $chatroomIds)
|
||
->group('wechatChatroomId')
|
||
->column('COUNT(*) AS cnt', 'wechatChatroomId');
|
||
}
|
||
|
||
$aiTypeData = [];
|
||
if (!empty($friendIds)) {
|
||
$aiTypeData = FriendSettings::where('friendId', 'in', $friendIds)->column('friendId,type');
|
||
}
|
||
|
||
foreach ($list as $k => &$v) {
|
||
|
||
$createTime = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||
$wechatTime = !empty($v['wechatTime']) ? date('Y-m-d H:i:s', $v['wechatTime']) : '';
|
||
|
||
|
||
$unreadCount = 0;
|
||
$v['aiType'] = 0;
|
||
if (!empty($v['wechatFriendId'])) {
|
||
$v['nickname'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['nickname'] : '';
|
||
$v['avatar'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['avatar'] : '';
|
||
$v['conRemark'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['conRemark'] : '';
|
||
$v['groupId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['groupId'] : '';
|
||
$v['wechatAccountId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['wechatAccountId'] : '';
|
||
$v['wechatId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['wechatId'] : '';
|
||
$v['extendFields'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['extendFields'] : [];
|
||
$v['region'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['region'] : '';
|
||
$v['phone'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['phone'] : '';
|
||
$v['isTop'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['isTop'] : 0;
|
||
$v['labels'] = !empty($friends[$v['wechatFriendId']]) ? json_decode($friends[$v['wechatFriendId']]['labels'], true) : [];
|
||
|
||
$unreadCount = isset($friendUnreadMap[$v['wechatFriendId']]) ? (int)$friendUnreadMap[$v['wechatFriendId']] : 0;
|
||
$v['aiType'] = isset($aiTypeData[$v['wechatFriendId']]) ? $aiTypeData[$v['wechatFriendId']] : 0;
|
||
unset($v['chatroomId']);
|
||
}
|
||
|
||
if (!empty($v['wechatChatroomId'])) {
|
||
$v['conRemark'] = '';
|
||
$unreadCount = isset($chatroomUnreadMap[$v['wechatChatroomId']]) ? (int)$chatroomUnreadMap[$v['wechatChatroomId']] : 0;
|
||
}
|
||
|
||
$v['id'] = !empty($v['wechatFriendId']) ? $v['wechatFriendId'] : $v['wechatChatroomId'];
|
||
$v['config'] = [
|
||
'top' => !empty($v['isTop']) ? true : false,
|
||
'unreadCount' => $unreadCount,
|
||
'chat' => true,
|
||
'msgTime' => $v['wechatTime'],
|
||
];
|
||
$v['createTime'] = $createTime;
|
||
$v['lastUpdateTime'] = $wechatTime;
|
||
|
||
// 最新消息内容已经在UNION查询中获取,直接使用
|
||
$v['latestMessage'] = [
|
||
'content' => $v['content'],
|
||
'wechatTime' => $wechatTime
|
||
];
|
||
|
||
unset($v['wechatFriendId'], $v['wechatChatroomId'],$v['isTop']);
|
||
|
||
}
|
||
unset($v);
|
||
return ResponseHelper::success(['list' => $list, 'total' => $total]);
|
||
}
|
||
|
||
|
||
public function readMessage()
|
||
{
|
||
$wechatFriendId = $this->request->param('wechatFriendId', '');
|
||
$wechatChatroomId = $this->request->param('wechatChatroomId', '');
|
||
$accountId = $this->getUserInfo('s2_accountId');
|
||
if (empty($accountId)) {
|
||
return ResponseHelper::error('请先登录');
|
||
}
|
||
if (empty($wechatChatroomId) && empty($wechatFriendId)) {
|
||
return ResponseHelper::error('参数缺失');
|
||
}
|
||
|
||
$where = [];
|
||
if (!empty($wechatChatroomId)) {
|
||
$where[] = ['wechatChatroomId', '=', $wechatChatroomId];
|
||
}
|
||
|
||
if (!empty($wechatFriendId)) {
|
||
$where[] = ['wechatFriendId', '=', $wechatFriendId];
|
||
}
|
||
|
||
Db::table('s2_wechat_message')->where($where)->update(['isRead' => 1]);
|
||
return ResponseHelper::success([]);
|
||
}
|
||
|
||
|
||
/**
|
||
* 获取单条消息发送状态(带轮询功能)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function getMessageStatus()
|
||
{
|
||
$messageId = $this->request->param('messageId', 0);
|
||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||
$accountId = $this->getUserInfo('s2_accountId');
|
||
$wechatFriendId = $this->request->param('wechatFriendId', '');
|
||
$wechatChatroomId = $this->request->param('wechatChatroomId', '');
|
||
|
||
if (empty($accountId)) {
|
||
return ResponseHelper::error('请先登录');
|
||
}
|
||
|
||
if (empty($messageId)) {
|
||
return ResponseHelper::error('消息ID不能为空');
|
||
}
|
||
|
||
if(empty($wechatFriendId) && empty($wechatChatroomId)) {
|
||
return ResponseHelper::error('消息类型不能为空');
|
||
}
|
||
|
||
// 查询单条消息的基本信息(只需要发送状态相关字段)
|
||
$message = Db::table('s2_wechat_message')
|
||
->where('id', $messageId)
|
||
->field('id,wechatAccountId,wechatFriendId,wechatChatroomId,sendStatus')
|
||
->find();
|
||
|
||
if (empty($message)) {
|
||
$message = [
|
||
'id' => $messageId,
|
||
'wechatAccountId' => $wechatAccountId,
|
||
'wechatFriendId' => $wechatFriendId,
|
||
'wechatChatroomId' => $wechatChatroomId,
|
||
'sendStatus' => 0,
|
||
];
|
||
}
|
||
|
||
$sendStatus = isset($message['sendStatus']) ? (int)$message['sendStatus'] : 0;
|
||
$isUpdated = false;
|
||
$pollCount = 0;
|
||
$maxPollCount = 10; // 最多轮询10次
|
||
|
||
// 如果sendStatus不为0,开始轮询
|
||
if ($sendStatus != 0) {
|
||
$messageRequest = [
|
||
'id' => $message['id'],
|
||
'wechatAccountId' => !empty($wechatAccountId) ? $wechatAccountId : $message['wechatAccountId'],
|
||
'wechatFriendId' => !empty($message['wechatFriendId']) ? $message['wechatFriendId'] : '',
|
||
'wechatChatroomId' => !empty($message['wechatChatroomId']) ? $message['wechatChatroomId'] : '',
|
||
'from' => '',
|
||
'to' => '',
|
||
];
|
||
|
||
|
||
// 轮询逻辑:最多10次
|
||
while ($pollCount < $maxPollCount && $sendStatus != 0) {
|
||
$pollCount++;
|
||
|
||
// 请求线上接口获取最新状态
|
||
$newData = $this->fetchLatestMessageFromApi($messageRequest);
|
||
|
||
if (!empty($newData)) {
|
||
// 重新查询消息状态(可能已更新)
|
||
$updatedMessage = Db::table('s2_wechat_message')
|
||
->where('id', $messageId)
|
||
->field('sendStatus')
|
||
->find();
|
||
|
||
if (!empty($updatedMessage)) {
|
||
$newSendStatus = isset($updatedMessage['sendStatus']) ? (int)$updatedMessage['sendStatus'] : 0;
|
||
|
||
// 如果状态已更新为0(已发送),停止轮询
|
||
if ($newSendStatus == 0) {
|
||
$sendStatus = 0;
|
||
$isUpdated = true;
|
||
break;
|
||
}
|
||
|
||
// 如果状态仍然是1,继续轮询(但需要等待一下,避免请求过快)
|
||
if ($newSendStatus != 0 && $pollCount < $maxPollCount) {
|
||
// 每次轮询间隔500毫秒(0.5秒)
|
||
usleep(500000);
|
||
}
|
||
}
|
||
} else {
|
||
// 如果请求失败,等待后继续尝试
|
||
if ($pollCount < $maxPollCount) {
|
||
usleep(500000);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 返回发送状态信息
|
||
return ResponseHelper::success([
|
||
'messageId' => $messageId,
|
||
'sendStatus' => $sendStatus,
|
||
'statusText' => $sendStatus == 0 ? '已发送' : '发送中'
|
||
]);
|
||
}
|
||
|
||
public function details()
|
||
{
|
||
$wechatFriendId = $this->request->param('wechatFriendId', '');
|
||
$wechatChatroomId = $this->request->param('wechatChatroomId', '');
|
||
$wechatAccountId = $this->request->param('wechatAccountId', '');
|
||
$page = $this->request->param('page', 1);
|
||
$limit = $this->request->param('limit', 10);
|
||
$from = $this->request->param('From', '');
|
||
$to = $this->request->param('To', '');
|
||
$olderData = $this->request->param('olderData', false);
|
||
$accountId = $this->getUserInfo('s2_accountId');
|
||
if (empty($accountId)) {
|
||
return ResponseHelper::error('请先登录');
|
||
}
|
||
if (empty($wechatChatroomId) && empty($wechatFriendId)) {
|
||
return ResponseHelper::error('参数缺失');
|
||
}
|
||
|
||
$where = [];
|
||
if (!empty($wechatChatroomId)) {
|
||
$where[] = ['wechatChatroomId', '=', $wechatChatroomId];
|
||
}
|
||
|
||
if (!empty($wechatFriendId)) {
|
||
$where[] = ['wechatFriendId', '=', $wechatFriendId];
|
||
}
|
||
|
||
if (!empty($From) && !empty($To)) {
|
||
$where[] = ['wechatTime', 'between', [$from, $to]];
|
||
}
|
||
|
||
$total = Db::table('s2_wechat_message')->where($where)->count();
|
||
$list = Db::table('s2_wechat_message')->where($where)->page($page, $limit)->order('id DESC')->select();
|
||
|
||
// 检查消息是否有sendStatus字段,如果有且不为0,则请求线上最新接口
|
||
foreach ($list as $k => &$item) {
|
||
// 检查是否存在sendStatus字段且不为0(0表示已发送成功)
|
||
if (isset($item['sendStatus']) && $item['sendStatus'] != 0) {
|
||
// 需要请求新的数据
|
||
$messageRequest = [
|
||
'id' => $item['id'],
|
||
'wechatAccountId' => $wechatAccountId,
|
||
'wechatFriendId' => $wechatFriendId,
|
||
'wechatChatroomId' => $wechatChatroomId,
|
||
'from' => '',
|
||
'to' => '',
|
||
];
|
||
$newData = $this->fetchLatestMessageFromApi($messageRequest);
|
||
if (!empty($newData)){
|
||
$item['sendStatus'] = 0;
|
||
}
|
||
}
|
||
// 格式化时间
|
||
$item['wechatTime'] = !empty($item['wechatTime']) ? date('Y-m-d H:i:s', $item['wechatTime']) : '';
|
||
}
|
||
unset($item);
|
||
|
||
|
||
return ResponseHelper::success(['total' => $total, 'list' => $list]);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/**
|
||
* 从线上接口获取最新消息
|
||
* @param array $messageRequest 消息项(包含wechatAccountId、wechatFriendId或wechatChatroomId、id等)
|
||
* @return array|null 最新消息数据,失败返回null
|
||
*/
|
||
private function fetchLatestMessageFromApi($messageRequest)
|
||
{
|
||
if (empty($this->baseUrl) || empty($this->authorization)) {
|
||
return null;
|
||
}
|
||
|
||
try {
|
||
// 设置请求头
|
||
$headerData = ['client:system'];
|
||
$header = setHeader($headerData, $this->authorization, 'json');
|
||
|
||
// 判断是好友消息还是群聊消息
|
||
if (!empty($messageRequest['wechatFriendId'])) {
|
||
// 好友消息接口
|
||
$params = [
|
||
'keyword' => '',
|
||
'msgType' => '',
|
||
'accountId' => '',
|
||
'count' => 20, // 获取多条消息以便找到对应的消息
|
||
'messageId' => isset($messageRequest['id']) ? $messageRequest['id'] : '',
|
||
'olderData' => true,
|
||
'wechatAccountId' => $messageRequest['wechatAccountId'],
|
||
'wechatFriendId' => $messageRequest['wechatFriendId'],
|
||
'from' => $messageRequest['from'],
|
||
'to' => $messageRequest['to'],
|
||
'searchFrom' => 'admin'
|
||
];
|
||
$result = requestCurl($this->baseUrl . 'api/FriendMessage/searchMessage', $params, 'GET', $header, 'json');
|
||
$response = handleApiResponse($result);
|
||
// 查找对应的消息
|
||
if (!empty($response) && is_array($response)) {
|
||
$data = $response[0];
|
||
if ($data['sendStatus'] == 0){
|
||
WechatMessageModel::where(['id' => $data['id']])->update(['sendStatus' => 0]);
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
} elseif (!empty($messageRequest['wechatChatroomId'])) {
|
||
// 群聊消息接口
|
||
$params = [
|
||
'keyword' => '',
|
||
'msgType' => '',
|
||
'accountId' => '',
|
||
'count' => 20, // 获取多条消息以便找到对应的消息
|
||
'messageId' => isset($messageRequest['id']) ? $messageRequest['id'] : '',
|
||
'olderData' => true,
|
||
'wechatId' => '',
|
||
'wechatAccountId' => $messageRequest['wechatAccountId'],
|
||
'wechatChatroomId' => $messageRequest['wechatChatroomId'],
|
||
'from' => $messageRequest['from'],
|
||
'to' => $messageRequest['to'],
|
||
'searchFrom' => 'admin'
|
||
];
|
||
|
||
$result = requestCurl($this->baseUrl . 'api/ChatroomMessage/searchMessage', $params, 'GET', $header, 'json');
|
||
$response = handleApiResponse($result);
|
||
|
||
// 查找对应的消息
|
||
if (!empty($response) && is_array($response)) {
|
||
$data = $response[0];
|
||
if ($data['sendStatus'] == 0){
|
||
WechatMessageModel::where(['id' => $data['id']])->update(['sendStatus' => 0]);
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
} catch (\Exception $e) {
|
||
// 记录错误日志,但不影响主流程
|
||
\think\facade\Log::error('获取线上最新消息失败:' . $e->getMessage());
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 更新数据库中的消息
|
||
* @param array $latestMessage 线上获取的最新消息
|
||
* @param array $oldMessage 旧消息数据
|
||
*/
|
||
private function updateMessageInDatabase($latestMessage, $oldMessage)
|
||
{
|
||
try {
|
||
// 使用API模块的MessageController来保存消息
|
||
$apiMessageController = new \app\api\controller\MessageController();
|
||
|
||
// 判断是好友消息还是群聊消息
|
||
if (!empty($oldMessage['wechatFriendId'])) {
|
||
// 保存好友消息
|
||
$apiMessageController->saveMessage($latestMessage);
|
||
} elseif (!empty($oldMessage['wechatChatroomId'])) {
|
||
// 保存群聊消息
|
||
$apiMessageController->saveChatroomMessage($latestMessage);
|
||
}
|
||
} catch (\Exception $e) {
|
||
// 记录错误日志,但不影响主流程
|
||
\think\facade\Log::error('更新数据库消息失败:' . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
} |