统一设备/微信解析与 BFF 发消息正规流程(Resolver、142-P1、sendStatus 判定),并补齐超管映射、触客宝乐观 UI 与验收文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
1148 lines
47 KiB
PHP
1148 lines
47 KiB
PHP
<?php
|
||
|
||
namespace app\chukebao\controller;
|
||
|
||
use app\api\model\WechatMessageModel;
|
||
use app\chukebao\model\FriendSettings;
|
||
use app\chukebao\service\CompanyWechatScopeService;
|
||
use app\common\service\workphone\WorkphoneMessageDeviceResolver;
|
||
use app\common\util\WorkPhoneSDK;
|
||
use library\ResponseHelper;
|
||
use think\Db;
|
||
use think\facade\Env;
|
||
use think\facade\Log;
|
||
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();
|
||
}
|
||
|
||
private function resolveConversationWechatAccountId(
|
||
int $accountId,
|
||
int $wechatFriendId,
|
||
int $wechatChatroomId
|
||
): int {
|
||
$companyId = (int)$this->getUserInfo('companyId');
|
||
if ($wechatChatroomId > 0) {
|
||
$row = Db::table('s2_wechat_chatroom')
|
||
->where('id', $wechatChatroomId)
|
||
->where('isDeleted', 0)
|
||
->field('wechatAccountId,accountId')
|
||
->find();
|
||
if (empty($row)) {
|
||
return 0;
|
||
}
|
||
$wechatAccountId = (int)($row['wechatAccountId'] ?? 0);
|
||
if ($wechatAccountId <= 0) {
|
||
return 0;
|
||
}
|
||
if ((int)($row['accountId'] ?? 0) === $accountId) {
|
||
return $wechatAccountId;
|
||
}
|
||
return $this->isWechatAccountInKefuScope($companyId, $accountId, $wechatAccountId)
|
||
? $wechatAccountId
|
||
: 0;
|
||
}
|
||
if ($wechatFriendId > 0) {
|
||
$friend = Db::table('s2_wechat_friend')
|
||
->where('id', $wechatFriendId)
|
||
->where('isDeleted', 0)
|
||
->field('wechatAccountId,accountId')
|
||
->find();
|
||
if (empty($friend)) {
|
||
return 0;
|
||
}
|
||
$wechatAccountId = (int)($friend['wechatAccountId'] ?? 0);
|
||
if ($wechatAccountId <= 0) {
|
||
return 0;
|
||
}
|
||
if ((int)($friend['accountId'] ?? 0) === $accountId) {
|
||
return $wechatAccountId;
|
||
}
|
||
return $this->isWechatAccountInKefuScope($companyId, $accountId, $wechatAccountId)
|
||
? $wechatAccountId
|
||
: 0;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/** 与 ensure-session 侧栏口径一致:本项目设备链微信号可跨 accountId 代发 */
|
||
private function isWechatAccountInKefuScope(int $companyId, int $accountId, int $wechatAccountId): bool
|
||
{
|
||
if ($wechatAccountId <= 0 || $companyId <= 0) {
|
||
return false;
|
||
}
|
||
$scopeIds = CompanyWechatScopeService::resolveKefuSidebarAccountIds($companyId, $accountId);
|
||
return !empty($scopeIds) && in_array($wechatAccountId, $scopeIds, true);
|
||
}
|
||
|
||
/**
|
||
* POST /v1/kefu/message/ensure-session(CKB-141 · 141.4 进入聊天链路)
|
||
*
|
||
* 好友管理「进入聊天」前置:权限校验 → 会话补建 → 返回定位上下文。
|
||
* body: { friendId, wechatAccountId? }
|
||
* 失败原因明确返回:NOT_FOUND / NOT_IN_COMPANY / OWNED_BY_OTHER。
|
||
*/
|
||
public function ensureSession()
|
||
{
|
||
try {
|
||
$accountId = (int)$this->getUserInfo('s2_accountId');
|
||
$companyId = (int)$this->getUserInfo('companyId');
|
||
if ($accountId <= 0) {
|
||
return ResponseHelper::error('请先登录', 401);
|
||
}
|
||
$friendId = (int)$this->request->param('friendId', 0);
|
||
if ($friendId <= 0) {
|
||
return ResponseHelper::error('缺少 friendId', 400);
|
||
}
|
||
|
||
$friend = Db::table('s2_wechat_friend')
|
||
->where('id', $friendId)
|
||
->where('isDeleted', 0)
|
||
->find();
|
||
if (!$friend) {
|
||
return ResponseHelper::error('好友不存在或已删除(NOT_FOUND)', 404);
|
||
}
|
||
|
||
$friendWechatAccountId = (int)($friend['wechatAccountId'] ?? 0);
|
||
|
||
// 权限校验:与好友管理页同一口径(项目设备链微信号,无则个人 IM 号兜底)(141.4 步骤 2)
|
||
$scopeAccountIds = CompanyWechatScopeService::resolveKefuSidebarAccountIds($companyId, $accountId);
|
||
if (!empty($scopeAccountIds) && !in_array($friendWechatAccountId, $scopeAccountIds, true)) {
|
||
return ResponseHelper::error('好友不属于本项目微信号,无聊天权限(NOT_IN_COMPANY)', 403);
|
||
}
|
||
|
||
// 归属判定(141.4 步骤 6:失败原因明确;141.3 管理员可触达全部)
|
||
$isAdmin = (int)$this->getUserInfo('isAdmin') === 1;
|
||
$ownerAccountId = (int)($friend['accountId'] ?? 0);
|
||
$ownedByOther = $ownerAccountId > 0 && $ownerAccountId !== $accountId;
|
||
$ownerName = '';
|
||
if ($ownedByOther) {
|
||
try {
|
||
$owner = Db::name('users')
|
||
->where('s2_accountId', $ownerAccountId)
|
||
->where('deleteTime', 0)
|
||
->field('account,username')
|
||
->find();
|
||
$ownerName = $owner ? (string)($owner['username'] ?: $owner['account']) : '';
|
||
} catch (\Throwable $e) {
|
||
}
|
||
}
|
||
|
||
// 初始分配兜底:未分配好友领取到当前客服(141.2 初始分配 / V5)
|
||
$assigned = false;
|
||
if ($ownerAccountId <= 0) {
|
||
Db::table('s2_wechat_friend')->where('id', $friendId)->update(['accountId' => $accountId]);
|
||
$friend['accountId'] = $accountId;
|
||
$assigned = true;
|
||
Log::info('[ensure-session] friend auto-assigned', [
|
||
'friendId' => $friendId, 'toAccountId' => $accountId, 'companyId' => $companyId,
|
||
]);
|
||
}
|
||
|
||
$blocked = $ownedByOther && !$isAdmin;
|
||
|
||
// 会话补建:无任何消息时插入一条系统消息,使 message/list 聚合出该会话(141.4 步骤 3 / V6)
|
||
$sessionCreated = false;
|
||
if (!$blocked) {
|
||
$hasMsg = Db::table('s2_wechat_message')
|
||
->where('type', 1)
|
||
->where('wechatFriendId', $friendId)
|
||
->limit(1)
|
||
->value('id');
|
||
if (!$hasMsg) {
|
||
$now = time();
|
||
// s2_wechat_message.id 为同步主键(非自增),手工取 MAX+1
|
||
$nextId = (int)Db::table('s2_wechat_message')->max('id') + 1;
|
||
Db::table('s2_wechat_message')->insert([
|
||
'id' => $nextId,
|
||
'type' => 1,
|
||
'wechatFriendId' => $friendId,
|
||
'wechatChatroomId' => 0,
|
||
'wechatAccountId' => $friendWechatAccountId,
|
||
'accountId' => (int)$friend['accountId'],
|
||
'tenantId' => (int)($friend['tenantId'] ?? 0),
|
||
'content' => '[系统] 会话已建立,可直接发起聊天',
|
||
'originalContent' => '[系统] 会话已建立,可直接发起聊天',
|
||
'msgType' => 1,
|
||
'msgSubType' => 0,
|
||
'isSend' => 1,
|
||
'isRead' => 1,
|
||
'createTime' => $now,
|
||
'wechatTime' => $now,
|
||
'sendStatus' => 1,
|
||
]);
|
||
$sessionCreated = true;
|
||
}
|
||
}
|
||
|
||
return ResponseHelper::success([
|
||
'friendId' => $friendId,
|
||
'wechatAccountId' => $friendWechatAccountId,
|
||
'wechatId' => (string)($friend['wechatId'] ?? ''),
|
||
'nickname' => (string)($friend['nickname'] ?? ''),
|
||
'conRemark' => (string)($friend['conRemark'] ?? ''),
|
||
'avatar' => (string)($friend['avatar'] ?? ''),
|
||
'canChat' => !$blocked,
|
||
'ownedByOther' => $ownedByOther,
|
||
'ownerAccountId' => $ownerAccountId,
|
||
'ownerName' => $ownerName,
|
||
'assigned' => $assigned,
|
||
'sessionCreated' => $sessionCreated,
|
||
'reason' => $blocked ? 'OWNED_BY_OTHER' : '',
|
||
]);
|
||
} catch (\Throwable $e) {
|
||
return ResponseHelper::error('进入聊天预检失败:' . $e->getMessage(), 500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* POST /v1/kefu/message/send
|
||
*
|
||
* 触客宝专用发送适配层:只做权限、对象解析、SDK 调用与本地消息落库。
|
||
* 工作手机能力仍由 WorkPhoneSDK / /v1/workphone/* 负责,避免业务域耦合。
|
||
*/
|
||
public function send()
|
||
{
|
||
try {
|
||
$accountId = (int)$this->getUserInfo('s2_accountId');
|
||
if ($accountId <= 0) {
|
||
return ResponseHelper::error('请先登录', 401);
|
||
}
|
||
|
||
$wechatFriendId = (int)$this->request->param('wechatFriendId/d', 0);
|
||
$wechatChatroomId = (int)$this->request->param('wechatChatroomId/d', 0);
|
||
$content = trim((string)$this->request->param('content', ''));
|
||
$msgType = (int)$this->request->param('msgType/d', 1);
|
||
$seq = (int)$this->request->param('seq/d', 0);
|
||
$channel = trim((string)$this->request->param('channel', ''));
|
||
|
||
if ($wechatFriendId <= 0 && $wechatChatroomId <= 0) {
|
||
return ResponseHelper::error('wechatFriendId 或 wechatChatroomId 至少传一个', 400);
|
||
}
|
||
if ($content === '') {
|
||
return ResponseHelper::error('消息内容不能为空', 400);
|
||
}
|
||
|
||
$wechatAccountId = $this->resolveConversationWechatAccountId($accountId, $wechatFriendId, $wechatChatroomId);
|
||
if ($wechatAccountId <= 0) {
|
||
return ResponseHelper::error('会话不存在或无权限', 404);
|
||
}
|
||
|
||
$target = $this->resolveSendTarget($accountId, $wechatAccountId, $wechatFriendId, $wechatChatroomId);
|
||
if (empty($target['toId'])) {
|
||
return ResponseHelper::error('未找到接收方微信标识', 404);
|
||
}
|
||
|
||
$companyId = (int)$this->getUserInfo('companyId');
|
||
$resolved = WorkphoneMessageDeviceResolver::resolveForWechatAccount($companyId, $wechatAccountId);
|
||
$deviceId = (string)($resolved['deviceId'] ?? '');
|
||
if ($deviceId === '') {
|
||
$hint = trim((string)($resolved['reason'] ?? ''));
|
||
return ResponseHelper::error(
|
||
$hint !== '' ? $hint : '未找到工作手机设备标识,请先在设备管理绑定该微信号',
|
||
422
|
||
);
|
||
}
|
||
// 安全门禁:禁止客户端 device_id 绕过 Resolver 跨设备代发
|
||
$requestedDeviceId = trim((string)$this->request->param('device_id', $this->request->param('deviceId', '')));
|
||
if ($requestedDeviceId !== '' && strcasecmp($requestedDeviceId, $deviceId) !== 0) {
|
||
Log::warning('[TouchkebaoMessageSend] device_id mismatch blocked', [
|
||
'companyId' => $companyId,
|
||
'wechatAccountId' => $wechatAccountId,
|
||
'requested' => $requestedDeviceId,
|
||
'resolved' => $deviceId,
|
||
'accountId' => $accountId,
|
||
]);
|
||
return ResponseHelper::error('设备标识与项目解析结果不一致,禁止跨设备代发', 403);
|
||
}
|
||
|
||
// 142-P1:发消息前校验会话微信号 == 工作手机当前 Hook 登录微信
|
||
$isAdmin = (int)$this->getUserInfo('isAdmin') === 1;
|
||
$skipAccountCheck = $isAdmin && (int)$this->request->param('skipAccountCheck/d', 0) === 1;
|
||
if (!$skipAccountCheck) {
|
||
$check = $this->verifyWechatAccountOnDevice($wechatAccountId, $deviceId);
|
||
if ($check['checked'] && !$check['matched']) {
|
||
return ResponseHelper::error(
|
||
'工作手机当前登录微信为「' . $check['hookNickname'] . '」,与会话归属微信「' . $check['expectNickname'] . '」不一致,'
|
||
. '请先在真机切换对应微信后再发送',
|
||
409
|
||
);
|
||
}
|
||
}
|
||
|
||
$messageId = $seq > 0 ? $seq : (int)floor(microtime(true) * 1000);
|
||
$sdkResp = WorkPhoneSDK::getInstance()->sendMessage(
|
||
$deviceId,
|
||
'wechat',
|
||
(string)$target['toId'],
|
||
$content,
|
||
$this->mapWorkPhoneMsgType($msgType),
|
||
$channel !== '' ? $channel : null
|
||
);
|
||
|
||
$sdkOk = $this->isWorkphoneSendSuccessful($sdkResp);
|
||
$sendStatus = $sdkOk ? 1 : 2;
|
||
$this->upsertLocalOutgoingMessage([
|
||
'id' => $messageId,
|
||
'type' => $wechatChatroomId > 0 ? 2 : 1,
|
||
'accountId' => $accountId,
|
||
'wechatAccountId' => $wechatAccountId,
|
||
'wechatFriendId' => $wechatFriendId,
|
||
'wechatChatroomId' => $wechatChatroomId,
|
||
'content' => $content,
|
||
'msgType' => $msgType,
|
||
'sendStatus' => $sendStatus,
|
||
]);
|
||
|
||
return ResponseHelper::success([
|
||
'messageId' => $messageId,
|
||
'seq' => $messageId,
|
||
'sendStatus' => $sendStatus,
|
||
'deviceId' => $deviceId,
|
||
'toId' => $target['toId'],
|
||
'targetName' => $target['name'],
|
||
'wechatAccountId' => $wechatAccountId,
|
||
'sdk' => $sdkResp,
|
||
], $sdkOk ? '消息已提交工作手机,等待微信回执' : '工作手机返回失败,已记录失败消息');
|
||
} catch (\Throwable $e) {
|
||
Log::error('[TouchkebaoMessageSend] ' . $e->getMessage());
|
||
return ResponseHelper::error($e->getMessage(), 500);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 142-P1:校验工作手机当前 Hook 登录微信是否等于会话归属微信。
|
||
* Hook 探测失败/超时不阻断发送(checked=false);命中缓存 60s 降低真机 RPC 频次。
|
||
*
|
||
* @return array{checked:bool,matched:bool,hookNickname:string,expectNickname:string}
|
||
*/
|
||
private function verifyWechatAccountOnDevice(int $wechatAccountId, string $deviceId): array
|
||
{
|
||
$result = ['checked' => false, 'matched' => true, 'hookNickname' => '', 'expectNickname' => ''];
|
||
try {
|
||
$expect = Db::table('s2_wechat_account')
|
||
->where('id', $wechatAccountId)
|
||
->field('wechatId,nickname,alias')
|
||
->find();
|
||
if (empty($expect) || empty($expect['wechatId'])) {
|
||
return $result;
|
||
}
|
||
$result['expectNickname'] = (string)($expect['nickname'] ?: $expect['wechatId']);
|
||
|
||
$cacheKey = 'tkb:hook_wxid:' . strtolower($deviceId);
|
||
$hook = \think\facade\Cache::get($cacheKey);
|
||
if (!is_array($hook)) {
|
||
$resp = WorkPhoneSDK::getInstance()->getProfile($deviceId, 'wechat');
|
||
$profile = $resp['data']['profile']
|
||
?? $resp['data']['data']['profile']
|
||
?? $resp['profile']
|
||
?? [];
|
||
if (!is_array($profile)) {
|
||
$profile = [];
|
||
}
|
||
$hook = [
|
||
'wxid' => trim((string)($profile['wxid'] ?? ($profile['wechat_id'] ?? ''))),
|
||
'alias' => trim((string)($profile['alias'] ?? '')),
|
||
'nickname' => trim((string)($profile['nickname'] ?? '')),
|
||
];
|
||
\think\facade\Cache::set($cacheKey, $hook, 60);
|
||
}
|
||
|
||
if ($hook['wxid'] === '' && $hook['alias'] === '') {
|
||
// Hook 未返回当前微信(未登录/通道不可用)→ 不阻断
|
||
return $result;
|
||
}
|
||
|
||
$result['checked'] = true;
|
||
$result['hookNickname'] = $hook['nickname'] !== '' ? $hook['nickname'] : ($hook['wxid'] ?: $hook['alias']);
|
||
$expectIds = array_filter([
|
||
strtolower((string)$expect['wechatId']),
|
||
strtolower((string)($expect['alias'] ?? '')),
|
||
]);
|
||
$hookIds = array_filter([strtolower($hook['wxid']), strtolower($hook['alias'])]);
|
||
$result['matched'] = count(array_intersect($expectIds, $hookIds)) > 0;
|
||
} catch (\Throwable $e) {
|
||
// 探测异常不阻断发送
|
||
}
|
||
return $result;
|
||
}
|
||
|
||
private function resolveSendTarget(int $accountId, int $wechatAccountId, int $wechatFriendId, int $wechatChatroomId): array
|
||
{
|
||
if ($wechatChatroomId > 0) {
|
||
$row = Db::table('s2_wechat_chatroom')
|
||
->where([
|
||
'id' => $wechatChatroomId,
|
||
'accountId' => $accountId,
|
||
'wechatAccountId' => $wechatAccountId,
|
||
'isDeleted' => 0,
|
||
])
|
||
->field('id,nickname,chatroomId')
|
||
->find();
|
||
return [
|
||
'toId' => $row['chatroomId'] ?? '',
|
||
'name' => $row['nickname'] ?? '',
|
||
];
|
||
}
|
||
|
||
$row = Db::table('s2_wechat_friend')
|
||
->where([
|
||
'id' => $wechatFriendId,
|
||
'wechatAccountId' => $wechatAccountId,
|
||
'isDeleted' => 0,
|
||
])
|
||
->field('id,nickname,conRemark,wechatId,alias')
|
||
->find();
|
||
$displayName = trim((string)($row['conRemark'] ?? ''));
|
||
if ($displayName === '') {
|
||
$displayName = trim((string)($row['nickname'] ?? ''));
|
||
}
|
||
return [
|
||
'toId' => $row['wechatId'] ?? ($row['alias'] ?? ''),
|
||
'name' => $displayName,
|
||
];
|
||
}
|
||
|
||
private function resolveWorkPhoneDeviceId(int $wechatAccountId): string
|
||
{
|
||
$account = Db::table('s2_wechat_account')
|
||
->where('id', $wechatAccountId)
|
||
->field('id,wechatId,currentDeviceId,imei,deviceAccountId')
|
||
->find();
|
||
if (empty($account)) {
|
||
return '';
|
||
}
|
||
|
||
$candidates = [];
|
||
foreach (['currentDeviceId', 'deviceAccountId', 'imei'] as $key) {
|
||
if (!empty($account[$key])) {
|
||
$candidates[] = (string)$account[$key];
|
||
}
|
||
}
|
||
|
||
if (!empty($account['wechatId'])) {
|
||
$login = Db::name('device_wechat_login')->alias('l')
|
||
->join('device d', 'd.id = l.deviceId', 'left')
|
||
->where('l.wechatId', $account['wechatId'])
|
||
->where('d.deleteTime', 0)
|
||
->field('l.deviceId,d.imei,d.deviceImei,d.extra')
|
||
->order('l.alive desc,l.updateTime desc')
|
||
->find();
|
||
if (!empty($login)) {
|
||
foreach (['deviceId', 'imei', 'deviceImei'] as $key) {
|
||
if (!empty($login[$key])) {
|
||
$candidates[] = (string)$login[$key];
|
||
}
|
||
}
|
||
$candidates = array_merge($candidates, $this->extractDeviceIdentifiers($login['extra'] ?? ''));
|
||
}
|
||
}
|
||
|
||
$device = Db::name('device')
|
||
->where(function ($q) use ($account) {
|
||
if (!empty($account['imei'])) {
|
||
$q->where('imei', $account['imei'])->whereOr('deviceImei', $account['imei']);
|
||
} else {
|
||
$q->where('id', (int)($account['currentDeviceId'] ?? 0));
|
||
}
|
||
})
|
||
->where('deleteTime', 0)
|
||
->field('id,imei,deviceImei,extra')
|
||
->find();
|
||
if (!empty($device)) {
|
||
foreach (['id', 'imei', 'deviceImei'] as $key) {
|
||
if (!empty($device[$key])) {
|
||
$candidates[] = (string)$device[$key];
|
||
}
|
||
}
|
||
$candidates = array_merge($candidates, $this->extractDeviceIdentifiers($device['extra'] ?? ''));
|
||
}
|
||
|
||
$resolved = $this->matchSdkDeviceIdentifier($candidates);
|
||
return $resolved !== '' ? $resolved : (string)($candidates[0] ?? '');
|
||
}
|
||
|
||
private function extractDeviceIdentifiers($extra): array
|
||
{
|
||
$decoded = is_array($extra) ? $extra : json_decode((string)$extra, true);
|
||
if (!is_array($decoded)) {
|
||
return [];
|
||
}
|
||
$ids = [];
|
||
foreach (['device_id', 'deviceId', 'device_id_md5', 'deviceIdMd5', 'serial', 'adb_serial', 'adbSerial', 'ws_id'] as $key) {
|
||
if (!empty($decoded[$key])) {
|
||
$ids[] = (string)$decoded[$key];
|
||
}
|
||
}
|
||
return $ids;
|
||
}
|
||
|
||
private function matchSdkDeviceIdentifier(array $candidates): string
|
||
{
|
||
$candidateMap = [];
|
||
foreach ($candidates as $candidate) {
|
||
$key = strtolower(trim((string)$candidate));
|
||
if ($key !== '') {
|
||
$candidateMap[$key] = (string)$candidate;
|
||
}
|
||
}
|
||
if (empty($candidateMap)) {
|
||
return '';
|
||
}
|
||
|
||
try {
|
||
$resp = WorkPhoneSDK::getInstance()->getDevices();
|
||
$devices = is_array($resp['data'] ?? null) ? $resp['data'] : [];
|
||
foreach ($devices as $device) {
|
||
if (!is_array($device)) {
|
||
continue;
|
||
}
|
||
$status = strtolower((string)($device['status'] ?? ''));
|
||
foreach (['device_id', 'id', 'deviceId', 'device_id_md5', 'deviceIdMd5', 'serial', 'adb_serial', 'imei'] as $key) {
|
||
$value = strtolower(trim((string)($device[$key] ?? '')));
|
||
if ($value !== '' && isset($candidateMap[$value])) {
|
||
return (string)($device['device_id'] ?? $device['id'] ?? $candidateMap[$value]);
|
||
}
|
||
}
|
||
}
|
||
} catch (\Throwable $e) {
|
||
return '';
|
||
}
|
||
|
||
return '';
|
||
}
|
||
|
||
/**
|
||
* 判定工作手机 SDK 是否真正发送成功(禁止仅凭 HTTP 200 误判)。
|
||
*/
|
||
private function isWorkphoneSendSuccessful(array $sdkResp): bool
|
||
{
|
||
if (!empty($sdkResp['success']) && empty($sdkResp['data']['success']) && empty($sdkResp['data']['error'])) {
|
||
return true;
|
||
}
|
||
$payload = $sdkResp['data'] ?? [];
|
||
if (is_array($payload)) {
|
||
if (array_key_exists('success', $payload)) {
|
||
return (bool)$payload['success'];
|
||
}
|
||
$inner = $payload['data'] ?? null;
|
||
if (is_array($inner) && array_key_exists('success', $inner)) {
|
||
return (bool)$inner['success'];
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private function mapWorkPhoneMsgType(int $msgType): string
|
||
{
|
||
if ($msgType === 3) {
|
||
return 'image';
|
||
}
|
||
if ($msgType === 43) {
|
||
return 'video';
|
||
}
|
||
if ($msgType === 49) {
|
||
return 'file';
|
||
}
|
||
return 'text';
|
||
}
|
||
|
||
private function upsertLocalOutgoingMessage(array $data): void
|
||
{
|
||
$now = time();
|
||
$row = [
|
||
'id' => (int)$data['id'],
|
||
'type' => (int)$data['type'],
|
||
'wechatFriendId' => (int)$data['wechatFriendId'],
|
||
'wechatChatroomId' => (int)$data['wechatChatroomId'],
|
||
'wechatAccountId' => (int)$data['wechatAccountId'],
|
||
'tenantId' => 0,
|
||
'accountId' => (int)$data['accountId'],
|
||
'synergyAccountId' => 0,
|
||
'content' => (string)$data['content'],
|
||
'originalContent' => (string)$data['content'],
|
||
'msgType' => (int)$data['msgType'],
|
||
'msgSubType' => 0,
|
||
'msgSvrId' => '',
|
||
'isSend' => 1,
|
||
'createTime' => $now,
|
||
'isDeleted' => 0,
|
||
'deleteTime' => 0,
|
||
'sendStatus' => (int)$data['sendStatus'],
|
||
'wechatTime' => $now,
|
||
'origin' => 0,
|
||
'msgId' => 0,
|
||
'recallId' => 0,
|
||
'isRead' => 1,
|
||
];
|
||
|
||
$exists = Db::table('s2_wechat_message')
|
||
->where('id', $row['id'])
|
||
->where('type', $row['type'])
|
||
->find();
|
||
if ($exists) {
|
||
Db::table('s2_wechat_message')
|
||
->where('id', $row['id'])
|
||
->where('type', $row['type'])
|
||
->update($row);
|
||
return;
|
||
}
|
||
Db::table('s2_wechat_message')->insert($row);
|
||
}
|
||
|
||
public function getList()
|
||
{
|
||
$page = $this->request->param('page', 1);
|
||
$limit = $this->request->param('limit', 10);
|
||
$wechatAccountId = (int)$this->request->param('wechatAccountId/d', 0);
|
||
$accountId = (int)$this->getUserInfo('s2_accountId');
|
||
$companyId = (int)$this->getUserInfo('companyId');
|
||
if ($accountId <= 0) {
|
||
return ResponseHelper::error('请先登录');
|
||
}
|
||
|
||
if ($wechatAccountId > 0) {
|
||
$scopeIds = CompanyWechatScopeService::resolveKefuSidebarAccountIds($companyId, $accountId);
|
||
if (!empty($scopeIds) && !in_array($wechatAccountId, $scopeIds, true)) {
|
||
return ResponseHelper::error('会话不属于本项目微信号(NOT_IN_COMPANY)', 403);
|
||
}
|
||
$friendWhere = ['wechatAccountId' => $wechatAccountId, 'isDeleted' => 0];
|
||
$chatroomWhere = ['wechatAccountId' => $wechatAccountId, 'isDeleted' => 0];
|
||
} else {
|
||
$friendWhere = ['accountId' => $accountId, 'isDeleted' => 0];
|
||
$chatroomWhere = ['accountId' => $accountId, 'isDeleted' => 0];
|
||
}
|
||
|
||
// 直接查询好友ID列表
|
||
$ids = Db::table('s2_wechat_friend')
|
||
->where($friendWhere)
|
||
->column('id');
|
||
$friendIds = empty($ids) ? [0] : $ids; // 避免 IN 查询为空
|
||
|
||
// 直接查询好友信息
|
||
$friends = Db::table('s2_wechat_friend')
|
||
->where($friendWhere)
|
||
->column('id,nickname,avatar,conRemark,labels,groupId,wechatAccountId,wechatId,extendFields,phone,region,isTop');
|
||
|
||
// 直接查询群聊信息
|
||
$chatrooms = Db::table('s2_wechat_chatroom')
|
||
->where($chatroomWhere)
|
||
->column('id,nickname,chatroomAvatar,chatroomId,isTop');
|
||
|
||
// 获取群聊ID列表
|
||
$chatroomIds = array_keys($chatrooms);
|
||
if (empty($chatroomIds)) {
|
||
$chatroomIds = [0];
|
||
}
|
||
|
||
// 1. 查询群聊最新消息
|
||
$chatroomMessages = [];
|
||
if (!empty($chatroomIds) && $chatroomIds[0] != 0) {
|
||
$chatroomIdsStr = implode(',', array_map('intval', $chatroomIds));
|
||
$chatroomLatestQuery = "
|
||
SELECT wc.id as chatroomId, m.id, m.content, m.wechatChatroomId, m.createTime, m.wechatTime, m.wechatAccountId,
|
||
wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId, wc.isTop, 2 as msgType
|
||
FROM s2_wechat_chatroom wc
|
||
INNER JOIN (
|
||
SELECT wechatChatroomId, MAX(wechatTime) as maxTime, MAX(id) as maxId
|
||
FROM s2_wechat_message
|
||
WHERE type = 2 AND wechatChatroomId IN ({$chatroomIdsStr})
|
||
GROUP BY wechatChatroomId
|
||
) latest ON wc.id = latest.wechatChatroomId
|
||
INNER JOIN s2_wechat_message m ON m.wechatChatroomId = latest.wechatChatroomId
|
||
AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||
WHERE wc.accountId = {$accountId} AND wc.isDeleted = 0
|
||
";
|
||
$chatroomMessages = Db::query($chatroomLatestQuery);
|
||
}
|
||
|
||
// 2. 查询好友最新消息
|
||
$friendMessages = [];
|
||
if (!empty($friendIds) && $friendIds[0] != 0) {
|
||
$friendIdsStr = implode(',', array_map('intval', $friendIds));
|
||
$friendLatestQuery = "
|
||
SELECT m.wechatFriendId, m.id, m.content, m.createTime, m.wechatTime,
|
||
f.wechatAccountId, 1 as msgType, 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 ({$friendIdsStr})
|
||
GROUP BY wechatFriendId
|
||
) latest ON m.wechatFriendId = latest.wechatFriendId
|
||
AND m.wechatTime = latest.maxTime AND m.id = latest.maxId
|
||
INNER JOIN s2_wechat_friend f ON f.id = m.wechatFriendId
|
||
WHERE m.type = 1 AND m.wechatFriendId IN ({$friendIdsStr})
|
||
";
|
||
$friendMessages = Db::query($friendLatestQuery);
|
||
}
|
||
|
||
// 合并结果并排序
|
||
$allMessages = array_merge($chatroomMessages, $friendMessages);
|
||
usort($allMessages, function ($a, $b) {
|
||
return $b['wechatTime'] <=> $a['wechatTime'];
|
||
});
|
||
|
||
// 计算总数
|
||
$totalCount = count($allMessages);
|
||
|
||
// 分页处理
|
||
$list = array_slice($allMessages, ($page - 1) * $limit, $limit);
|
||
|
||
// 收集需要查询的ID
|
||
$queryFriendIds = [];
|
||
$queryChatroomIds = [];
|
||
foreach ($list as $row) {
|
||
if (!empty($row['wechatFriendId'])) {
|
||
$queryFriendIds[] = $row['wechatFriendId'];
|
||
}
|
||
if (!empty($row['wechatChatroomId'])) {
|
||
$queryChatroomIds[] = $row['wechatChatroomId'];
|
||
}
|
||
}
|
||
$queryFriendIds = array_unique($queryFriendIds);
|
||
$queryChatroomIds = array_unique($queryChatroomIds);
|
||
|
||
// 批量查询未读数量(优化:合并查询)
|
||
$unreadMap = [];
|
||
if (!empty($queryFriendIds)) {
|
||
$friendUnreads = Db::table('s2_wechat_message')
|
||
->where(['isRead' => 0, 'type' => 1])
|
||
->whereIn('wechatFriendId', $queryFriendIds)
|
||
->field('wechatFriendId, COUNT(*) as cnt')
|
||
->group('wechatFriendId')
|
||
->select();
|
||
foreach ($friendUnreads as $item) {
|
||
$unreadMap['friend_' . $item['wechatFriendId']] = (int)$item['cnt'];
|
||
}
|
||
}
|
||
|
||
if (!empty($queryChatroomIds)) {
|
||
$chatroomUnreads = Db::table('s2_wechat_message')
|
||
->where(['isRead' => 0, 'type' => 2])
|
||
->whereIn('wechatChatroomId', $queryChatroomIds)
|
||
->field('wechatChatroomId, COUNT(*) as cnt')
|
||
->group('wechatChatroomId')
|
||
->select();
|
||
foreach ($chatroomUnreads as $item) {
|
||
$unreadMap['chatroom_' . $item['wechatChatroomId']] = (int)$item['cnt'];
|
||
}
|
||
}
|
||
|
||
// 批量查询AI类型
|
||
$aiTypeData = [];
|
||
if (!empty($queryFriendIds)) {
|
||
$aiTypeData = FriendSettings::where('friendId', 'in', $queryFriendIds)->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'])) {
|
||
// 好友消息
|
||
$friendId = $v['wechatFriendId'];
|
||
$friend = $friends[$friendId] ?? null;
|
||
|
||
$v['nickname'] = $friend['nickname'] ?? '';
|
||
$v['avatar'] = $friend['avatar'] ?? '';
|
||
$v['conRemark'] = $friend['conRemark'] ?? '';
|
||
$v['groupId'] = $friend['groupId'] ?? '';
|
||
$v['wechatAccountId'] = $friend['wechatAccountId'] ?? '';
|
||
$v['wechatId'] = $friend['wechatId'] ?? '';
|
||
$v['extendFields'] = $friend['extendFields'] ?? [];
|
||
$v['region'] = $friend['region'] ?? '';
|
||
$v['phone'] = $friend['phone'] ?? '';
|
||
$v['isTop'] = $friend['isTop'] ?? 0;
|
||
$v['labels'] = !empty($friend['labels']) ? json_decode($friend['labels'], true) : [];
|
||
|
||
$unreadCount = $unreadMap['friend_' . $friendId] ?? 0;
|
||
$v['aiType'] = $aiTypeData[$friendId] ?? 0;
|
||
$v['id'] = $friendId;
|
||
unset($v['chatroomId']);
|
||
} elseif (!empty($v['wechatChatroomId'])) {
|
||
// 群聊消息
|
||
$chatroomId = $v['wechatChatroomId'];
|
||
$chatroom = $chatrooms[$chatroomId] ?? null;
|
||
|
||
$v['nickname'] = $chatroom['nickname'] ?? '';
|
||
$v['avatar'] = $chatroom['chatroomAvatar'] ?? '';
|
||
$v['conRemark'] = '';
|
||
$v['isTop'] = $chatroom['isTop'] ?? 0;
|
||
$v['chatroomId'] = $chatroom['chatroomId'] ?? '';
|
||
|
||
$unreadCount = $unreadMap['chatroom_' . $chatroomId] ?? 0;
|
||
$v['id'] = $chatroomId;
|
||
unset($v['wechatFriendId']);
|
||
}
|
||
|
||
$v['config'] = [
|
||
'top' => !empty($v['isTop']) ? true : false,
|
||
'unreadCount' => $unreadCount,
|
||
'chat' => true,
|
||
'msgTime' => $wechatTime,
|
||
];
|
||
$v['createTime'] = $createTime;
|
||
$v['lastUpdateTime'] = $wechatTime;
|
||
$v['latestMessage'] = [
|
||
'content' => $v['content'] ?? '',
|
||
'wechatTime' => $wechatTime
|
||
];
|
||
|
||
unset($v['wechatChatroomId'], $v['isTop'], $v['msgType']);
|
||
}
|
||
unset($v);
|
||
|
||
return ResponseHelper::success(['list' => $list, 'total' => $totalCount]);
|
||
}
|
||
|
||
|
||
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('参数缺失');
|
||
}
|
||
$conversationAccountId = $this->resolveConversationWechatAccountId(
|
||
(int)$accountId,
|
||
(int)$wechatFriendId,
|
||
(int)$wechatChatroomId
|
||
);
|
||
if ($conversationAccountId <= 0) {
|
||
return ResponseHelper::error('会话不存在或无权限', 404);
|
||
}
|
||
|
||
$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('消息类型不能为空');
|
||
}
|
||
$conversationAccountId = $this->resolveConversationWechatAccountId(
|
||
(int)$accountId,
|
||
(int)$wechatFriendId,
|
||
(int)$wechatChatroomId
|
||
);
|
||
if ($conversationAccountId <= 0) {
|
||
return ResponseHelper::error('会话不存在或无权限', 404);
|
||
}
|
||
$wechatAccountId = $conversationAccountId;
|
||
|
||
// 查询单条消息的基本信息(只需要发送状态相关字段)
|
||
$messageQuery = Db::table('s2_wechat_message')->where('id', $messageId);
|
||
if (!empty($wechatChatroomId)) {
|
||
$messageQuery->where('wechatChatroomId', $wechatChatroomId);
|
||
} else {
|
||
$messageQuery->where('wechatFriendId', $wechatFriendId);
|
||
}
|
||
$message = $messageQuery
|
||
->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', $this->request->param('from', ''));
|
||
$to = $this->request->param('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('参数缺失');
|
||
}
|
||
$conversationAccountId = $this->resolveConversationWechatAccountId(
|
||
(int)$accountId,
|
||
(int)$wechatFriendId,
|
||
(int)$wechatChatroomId
|
||
);
|
||
if ($conversationAccountId <= 0) {
|
||
return ResponseHelper::error('会话不存在或无权限', 404);
|
||
}
|
||
$wechatAccountId = $conversationAccountId;
|
||
|
||
$where = [];
|
||
if (!empty($wechatChatroomId)) {
|
||
$where[] = ['wechatChatroomId', '=', $wechatChatroomId];
|
||
}
|
||
|
||
if (!empty($wechatFriendId)) {
|
||
$where[] = ['wechatFriendId', '=', $wechatFriendId];
|
||
}
|
||
|
||
if ($from !== '' && $to !== '' && is_numeric($from) && is_numeric($to)) {
|
||
$where[] = ['wechatTime', 'between', [(int)$from, (int)$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());
|
||
}
|
||
}
|
||
|
||
}
|