AI对话功能优化

This commit is contained in:
wong
2025-10-28 09:25:39 +08:00
parent 51c60c55b0
commit 1644e478c7
6 changed files with 488 additions and 48 deletions

View File

@@ -76,7 +76,7 @@ class CozeAI extends Controller
$result = requestCurl($this->apiUrl . '/v1/bot/create', $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '创建成功', 'data' => $result['data']]);
@@ -136,9 +136,9 @@ class CozeAI extends Controller
$result = requestCurl($this->apiUrl . '/v1/bot/update', $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '获取成功']);
return json_encode(['code' => 200, 'msg' => '更新成功', 'data' => []]);
}
@@ -162,9 +162,9 @@ class CozeAI extends Controller
$result = requestCurl($this->apiUrl . '/v1/bot/publish', $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '发布成功']);
return json_encode(['code' => 200, 'msg' => '发布成功', 'data' => []]);
}
@@ -191,7 +191,7 @@ class CozeAI extends Controller
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]);
}
@@ -231,7 +231,7 @@ class CozeAI extends Controller
$result = requestCurl($this->apiUrl . '/open_api/knowledge/document/create', $params, 'POST', $headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['document_infos']]);
}
@@ -254,9 +254,9 @@ class CozeAI extends Controller
$result = requestCurl($this->apiUrl . '/open_api/knowledge/document/delete', $params, 'POST', $headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '删除成功']);
return json_encode(['code' => 200, 'msg' => '删除成功', 'data' => []]);
}
@@ -285,7 +285,7 @@ class CozeAI extends Controller
$result = requestCurl($this->apiUrl . '/v1/conversation/create', $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]);
}
@@ -306,15 +306,15 @@ class CozeAI extends Controller
if(empty($bot_id)){
return errorJson('智能体ID不能为空');
return json_encode(['code' => 500, 'msg' => '智能体ID不能为空', 'data' => []]);
}
if(empty($conversation_id)){
return errorJson('会话ID不能为空');
return json_encode(['code' => 500, 'msg' => '会话ID不能为空', 'data' => []]);
}
if(empty($question)){
return errorJson('问题不能为空');
return json_encode(['code' => 500, 'msg' => '问题不能为空', 'data' => []]);
}
// 构建请求数据
@@ -330,16 +330,21 @@ class CozeAI extends Controller
$result = requestCurl($url, $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]);
} catch (\Exception $e) {
return errorJson('创建对话失败:' . $e->getMessage());
return json_encode(['code' => 500, 'msg' => '创建对话失败:' . $e->getMessage(), 'data' => []]);
}
}
/**
* 查看对话详情
* @param $data
* @return false|string|\think\response\Json
*/
public function getConversationChat($data = [])
{
$conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : '';
@@ -348,12 +353,17 @@ class CozeAI extends Controller
$result = requestCurl($url, [], 'GET', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]);
return json_encode(['code' => 200, 'msg' => '获取成功','data' => $result['data']]);
}
/**
* 查看对话消息详情
* @param $data
* @return false|string|\think\response\Json
*/
public function listConversationMessage($data = [])
{
$conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : '';
@@ -362,9 +372,35 @@ class CozeAI extends Controller
$result = requestCurl($url, [], 'GET', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]);
return json_encode(['code' => 200, 'msg' => '获取成功','data' => $result['data']]);
}
/**
* 取消进行中的对话
* @param $data
* @return false|string|\think\response\Json
*/
public function cancelConversationChat($data = [])
{
$conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : '';
$chat_id = !empty($data['chat_id']) ? $data['chat_id'] : '';
// 构建请求数据
$params = [
'conversation_id' => (string) $conversation_id,
'chat_id' => (string) $chat_id
];
$url = $this->apiUrl . '/v3/chat/cancel';
$result = requestCurl($url, $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
if ($result['code'] != 0) {
return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]);
}
return json_encode(['code' => 200, 'msg' => '取消成功', 'data' => []]);
}
}

View File

@@ -12,10 +12,12 @@ Route::group('v1/', function () {
//好友相关
Route::group('wechatFriend/', function () {
Route::get('list', 'app\chukebao\controller\WechatFriendController@getList'); // 获取好友列表
Route::get('detail', 'app\chukebao\controller\WechatFriendController@getDetail'); // 获取好友详情
});
//群相关
Route::group('wechatChatroom/', function () {
Route::get('list', 'app\chukebao\controller\WechatChatroomController@getList'); // 获取好友列表
Route::get('detail', 'app\chukebao\controller\WechatChatroomController@getDetail'); // 获取群详情
Route::post('aiAnnouncement', 'app\chukebao\controller\WechatChatroomController@aiAnnouncement'); // AI群公告
});

View File

@@ -11,6 +11,9 @@ use app\chukebao\model\FriendSettings;
use app\chukebao\model\TokensCompany;
use library\ResponseHelper;
use think\Db;
use think\facade\Cache;
use think\facade\Log;
/**
* AI聊天控制器
@@ -27,8 +30,16 @@ class AiChatController extends BaseController
const STATUS_CANCELED = 'canceled'; // 对话已取消
// 轮询配置
const MAX_RETRY_TIMES = 30; // 最大重试次数
const RETRY_INTERVAL = 2; // 重试间隔(秒)
const MAX_RETRY_TIMES = 1000; // 最大重试次数
const RETRY_INTERVAL = 500000; // 重试间隔(微秒即500毫秒)
// 并发控制
const CACHE_EXPIRE = 30; // 缓存过期时间(秒)
// 请求唯一标识符
private $requestKey = '';
private $requestId = '';
private $currentStep = 0;
/**
* AI聊天主入口
@@ -40,59 +51,219 @@ class AiChatController extends BaseController
try {
// 1. 参数验证和初始化
$params = $this->validateAndInitParams();
if ($params === false) {
return ResponseHelper::error('参数验证失败');
}
// 并发控制:检查并处理同一用户的重复请求
$this->requestKey = "aichat_{$params['friendId']}_{$params['wechatAccountId']}";
$this->requestId = uniqid('req_', true);
$concurrentCheck = $this->handleConcurrentRequest($params);
if ($concurrentCheck !== true) {
return $concurrentCheck; // 返回错误响应
}
$this->currentStep = 1;
// 2. 验证Tokens余额
if (!$this->checkTokensBalance($params['companyId'])) {
$this->updateRequestStep(2);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$hasBalance = $this->checkTokensBalance($params['companyId']);
if (!$hasBalance) {
$this->clearRequestCache();
return ResponseHelper::error('Tokens余额不足请充值后再试');
}
// 3. 获取AI配置
$this->updateRequestStep(3);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$setting = $this->getAiSettings($params['companyId']);
if (!$setting) {
$this->clearRequestCache();
return ResponseHelper::error('未找到AI配置信息请先配置AI策略');
}
// 4. 获取好友AI设置
$this->updateRequestStep(4);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$friendSettings = $this->getFriendSettings($params['companyId'], $params['friendId']);
if (!$friendSettings) {
$this->clearRequestCache();
return ResponseHelper::error('该好友未配置或未开启AI功能');
}
// 5. 确保会话存在
$this->updateRequestStep(5);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$conversationId = $this->ensureConversation($friendSettings, $setting, $params);
if (!$conversationId) {
$this->clearRequestCache();
return ResponseHelper::error('创建会话失败');
}
// 6. 获取历史消息
$this->updateRequestStep(6);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$msgData = $this->getHistoryMessages($params['friendId'], $friendSettings);
// 7. 创建AI对话
// 7. 创建AI对话从这步开始需要保存对话ID以便取消
$this->updateRequestStep(7);
if ($this->isRequestCanceled($conversationId, null)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$chatId = $this->createAiChat($setting, $friendSettings, $msgData);
if (!$chatId) {
$this->clearRequestCache();
return ResponseHelper::error('创建对话失败');
}
// 保存对话ID到缓存以便新请求可以取消
$this->updateRequestStep(7, $conversationId, $chatId);
// 8. 等待AI处理完成轮询
$chatResult = $this->waitForChatCompletion($conversationId, $chatId);
if (!$chatResult) {
return ResponseHelper::error('AI处理超时或失败');
$this->updateRequestStep(8, $conversationId, $chatId);
if ($this->isRequestCanceled($conversationId, $chatId)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$chatResult = $this->waitForChatCompletion($conversationId, $chatId);
if (!$chatResult['success']) {
$this->clearRequestCache();
return ResponseHelper::error($chatResult['error']);
}
$chatResult = $chatResult['data'];
// 9. 扣除Tokens
$this->updateRequestStep(9, $conversationId, $chatId);
if ($this->isRequestCanceled($conversationId, $chatId)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$this->consumeTokens($chatResult, $params, $friendSettings);
// 10. 获取对话消息
$this->updateRequestStep(10, $conversationId, $chatId);
if ($this->isRequestCanceled($conversationId, $chatId)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$messages = $this->getChatMessages($conversationId, $chatId);
if (!$messages) {
return ResponseHelper::error('获取对话消息失败');
}
return ResponseHelper::success($messages[1]['content'], '对话成功');
// 筛选type为answer的消息AI回复的内容
$answerContent = '';
foreach ($messages as $msg) {
if (isset($msg['type']) && $msg['type'] === 'answer') {
$answerContent = $msg['content'] ?? '';
break;
}
}
if (empty($answerContent)) {
Log::warning('未找到AI回复内容messages: ' . json_encode($messages));
return ResponseHelper::error('未获取到AI回复内容');
}
// 清理请求缓存
$this->clearRequestCache();
// 返回结果
return ResponseHelper::success(['content' => $answerContent], '对话成功');
} catch (\Exception $e) {
\think\facade\Log::error('AI聊天异常' . $e->getMessage());
Log::error('AI聊天异常' . $e->getMessage());
// 清理请求缓存
$this->clearRequestCache();
return ResponseHelper::error('系统异常:' . $e->getMessage());
}
}
/**
* 取消AI对话
* 取消当前正在进行的AI对话请求
*
* @return \think\response\Json
*/
public function cancel()
{
try {
// 获取参数
$friendId = $this->request->param('friendId', '');
$wechatAccountId = $this->request->param('wechatAccountId', '');
if (empty($wechatAccountId) || empty($friendId)) {
return ResponseHelper::error('参数缺失');
}
// 生成缓存键
$requestKey = "aichat_{$friendId}_{$wechatAccountId}";
// 获取缓存数据
$cacheData = Cache::get($requestKey);
if (!$cacheData) {
return ResponseHelper::error('当前没有正在进行的AI对话');
}
$requestId = $cacheData['request_id'] ?? '';
$step = $cacheData['step'] ?? 0;
$conversationId = $cacheData['conversation_id'] ?? '';
$chatId = $cacheData['chat_id'] ?? '';
Log::info("手动取消AI对话 - 请求ID: {$requestId}, 步骤: {$step}");
// 如果已经到达步骤7或之后需要调用取消API
if ($step >= 7 && !empty($conversationId) && !empty($chatId)) {
try {
$cozeAI = new CozeAI();
$cancelResult = $cozeAI->cancelConversationChat([
'conversation_id' => $conversationId,
'chat_id' => $chatId,
]);
$result = json_decode($cancelResult, true);
if ($result['code'] != 200) {
Log::error("调用取消API失败 - conversation_id: {$conversationId}, chat_id: {$chatId}, 错误: " . ($result['msg'] ?? '未知错误'));
} else {
Log::info("成功调用取消API - conversation_id: {$conversationId}, chat_id: {$chatId}");
}
} catch (\Exception $e) {
Log::error("调用取消API异常" . $e->getMessage());
}
}
// 清理缓存
Cache::rm($requestKey);
Log::info("已清理AI对话缓存 - 请求ID: {$requestId}");
return ResponseHelper::success([
'canceled_request_id' => $requestId,
'step' => $step
], 'AI对话已取消');
} catch (\Exception $e) {
Log::error('取消AI对话异常' . $e->getMessage());
return ResponseHelper::error('系统异常:' . $e->getMessage());
}
}
@@ -194,7 +365,7 @@ class AiChatController extends BaseController
$res = json_decode($res, true);
if ($res['code'] != 200) {
\think\facade\Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误'));
Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误'));
return null;
}
@@ -292,7 +463,7 @@ class AiChatController extends BaseController
$res = json_decode($res, true);
if ($res['code'] != 200) {
\think\facade\Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误'));
Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误'));
return null;
}
@@ -304,7 +475,7 @@ class AiChatController extends BaseController
*
* @param string $conversationId 会话ID
* @param string $chatId 对话ID
* @return array|null
* @return array ['success' => bool, 'data' => array|null, 'error' => string]
*/
private function waitForChatCompletion($conversationId, $chatId)
{
@@ -320,8 +491,9 @@ class AiChatController extends BaseController
$res = json_decode($res, true);
if ($res['code'] != 200) {
\think\facade\Log::error('获取对话状态失败:' . ($res['msg'] ?? '未知错误'));
return null;
$errorMsg = 'AI接口调用失败:' . ($res['msg'] ?? '未知错误');
Log::error($errorMsg);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
}
$status = $res['data']['status'] ?? '';
@@ -330,36 +502,41 @@ class AiChatController extends BaseController
switch ($status) {
case self::STATUS_COMPLETED:
// 对话完成,返回结果
return $res['data'];
return ['success' => true, 'data' => $res['data'], 'error' => ''];
case self::STATUS_IN_PROGRESS:
case self::STATUS_CREATED:
// 继续等待
$retryCount++;
sleep(self::RETRY_INTERVAL);
usleep(self::RETRY_INTERVAL);
break;
case self::STATUS_FAILED:
\think\facade\Log::error('对话失败chat_id: ' . $chatId);
return null;
$errorMsg = 'AI对话处理失败';
Log::error($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
case self::STATUS_CANCELED:
\think\facade\Log::error('对话已取消chat_id: ' . $chatId);
return null;
$errorMsg = 'AI对话已取消';
Log::error($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
case self::STATUS_REQUIRES_ACTION:
\think\facade\Log::warning('对话需要进一步处理chat_id: ' . $chatId);
return null;
$errorMsg = 'AI对话需要进一步处理';
Log::warning($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
default:
\think\facade\Log::error('未知状态:' . $status);
return null;
$errorMsg = 'AI返回未知状态:' . $status;
Log::error($errorMsg);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
}
}
// 超时
\think\facade\Log::error('对话处理超时chat_id: ' . $chatId);
return null;
$errorMsg = 'AI对话处理超时已等待' . (self::MAX_RETRY_TIMES * self::RETRY_INTERVAL / 1000000) . '秒';
Log::error($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
}
/**
@@ -412,13 +589,154 @@ class AiChatController extends BaseController
$res = json_decode($res, true);
if ($res['code'] != 200) {
\think\facade\Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误'));
Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误'));
return null;
}
return $res['data'] ?? [];
}
/**
* 处理并发请求
* 检查是否有同一用户的旧请求正在处理,如果有则取消旧请求
*
* @param array $params 请求参数
* @return true|\think\response\Json true表示可以继续否则返回错误响应
*/
private function handleConcurrentRequest($params)
{
$cacheData = Cache::get($this->requestKey);
if ($cacheData) {
// 有旧请求正在处理
$oldRequestId = $cacheData['request_id'] ?? '';
$oldStep = $cacheData['step'] ?? 0;
$oldConversationId = $cacheData['conversation_id'] ?? '';
$oldChatId = $cacheData['chat_id'] ?? '';
Log::info("检测到并发请求 - 旧请求: {$oldRequestId} (步骤{$oldStep}), 新请求: {$this->requestId}");
// 如果旧请求已经到达步骤7或之后需要调用取消API
if ($oldStep >= 7 && !empty($oldConversationId) && !empty($oldChatId)) {
try {
$cozeAI = new CozeAI();
$cancelResult = $cozeAI->cancelConversationChat([
'conversation_id' => $oldConversationId,
'chat_id' => $oldChatId,
]);
Log::info("已调用取消API取消旧请求的对话 - conversation_id: {$oldConversationId}, chat_id: {$oldChatId}");
} catch (\Exception $e) {
Log::error("取消旧请求对话失败:" . $e->getMessage());
}
}
// 标记旧请求为已取消(通过更新缓存的 canceled 标志)
$cacheData['canceled'] = true;
$cacheData['canceled_by'] = $this->requestId;
Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE);
}
// 设置当前请求为活动请求
$newCacheData = [
'request_id' => $this->requestId,
'step' => 1,
'start_time' => time(),
'canceled' => false,
'conversation_id' => '',
'chat_id' => '',
];
Cache::set($this->requestKey, $newCacheData, self::CACHE_EXPIRE);
return true;
}
/**
* 检查当前请求是否被新请求取消
*
* @param string $conversationId 会话ID可选用于取消对话
* @param string $chatId 对话ID可选用于取消对话
* @return bool
*/
private function isRequestCanceled($conversationId = '', $chatId = '')
{
$cacheData = Cache::get($this->requestKey);
if (!$cacheData) {
// 缓存不存在,说明被清理或过期,视为被取消
return true;
}
$currentRequestId = $cacheData['request_id'] ?? '';
$isCanceled = $cacheData['canceled'] ?? false;
// 如果缓存中的请求ID与当前请求ID不一致或者被标记为取消
if ($currentRequestId !== $this->requestId || $isCanceled) {
Log::info("当前请求已被取消 - 请求ID: {$this->requestId}, 缓存请求ID: {$currentRequestId}, 取消标志: " . ($isCanceled ? 'true' : 'false'));
// 如果提供了对话ID尝试取消对话
if (!empty($conversationId) && !empty($chatId) && $this->currentStep >= 7) {
try {
$cozeAI = new CozeAI();
$cancelResult = $cozeAI->cancelConversationChat([
'conversation_id' => $conversationId,
'chat_id' => $chatId,
]);
Log::info("已取消当前请求的对话 - conversation_id: {$conversationId}, chat_id: {$chatId}");
} catch (\Exception $e) {
Log::error("取消当前请求对话失败:" . $e->getMessage());
}
}
return true;
}
return false;
}
/**
* 更新请求步骤
*
* @param int $step 当前步骤
* @param string $conversationId 会话ID可选
* @param string $chatId 对话ID可选
*/
private function updateRequestStep($step, $conversationId = '', $chatId = '')
{
$this->currentStep = $step;
$cacheData = Cache::get($this->requestKey);
if ($cacheData && $cacheData['request_id'] === $this->requestId) {
$cacheData['step'] = $step;
$cacheData['update_time'] = time();
if (!empty($conversationId)) {
$cacheData['conversation_id'] = $conversationId;
}
if (!empty($chatId)) {
$cacheData['chat_id'] = $chatId;
}
Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE);
}
}
/**
* 清理请求缓存
*/
private function clearRequestCache()
{
if (!empty($this->requestKey)) {
$cacheData = Cache::get($this->requestKey);
// 只有当前请求才能清理自己的缓存
if ($cacheData && isset($cacheData['request_id']) && $cacheData['request_id'] === $this->requestId) {
Cache::rm($this->requestKey);
Log::info("已清理请求缓存 - 请求ID: {$this->requestId}");
}
}
}
public function index2222()
{

View File

@@ -30,7 +30,7 @@ class MessageController extends BaseController
// 优化后的查询使用MySQL兼容的查询方式
$unionQuery = "
(SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 2 as msgType, wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId
(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
FROM s2_wechat_chatroom wc
INNER JOIN s2_wechat_message m ON wc.id = m.wechatChatroomId AND m.type = 2
INNER JOIN (
@@ -42,7 +42,7 @@ class MessageController extends BaseController
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
(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
FROM s2_wechat_message m
INNER JOIN (
SELECT wechatFriendId, MAX(wechatTime) as maxTime, MAX(id) as maxId

View File

@@ -83,7 +83,51 @@ class WechatChatroomController extends BaseController
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
}
public function getDetail(){
$id = input('id', 0);
if (!$id) {
return ResponseHelper::error('聊天室ID不能为空');
}
$accountId = $this->getUserInfo('s2_accountId');
if (empty($accountId)){
return ResponseHelper::error('请先登录');
}
$detail = Db::table('s2_wechat_chatroom')
->where(['accountId' => $accountId, 'id' => $id, 'isDeleted' => 0])
->find();
if (!$detail) {
return ResponseHelper::error('聊天室不存在或无权限访问');
}
// 处理时间格式
$detail['createTime'] = !empty($detail['createTime']) ? date('Y-m-d H:i:s', $detail['createTime']) : '';
$detail['updateTime'] = !empty($detail['updateTime']) ? date('Y-m-d H:i:s', $detail['updateTime']) : '';
// 查询未读消息数量
$unreadCount = Db::table('s2_wechat_message')
->where('wechatChatroomId', $id)
->where('isRead', 0)
->count();
// 查询最新消息
$latestMessage = Db::table('s2_wechat_message')
->where('wechatChatroomId', $id)
->order('id desc')
->find();
$config = [
'unreadCount' => $unreadCount,
'chat' => !empty($latestMessage),
'msgTime' => isset($latestMessage['wechatTime']) ? $latestMessage['wechatTime'] : 0
];
$detail['config'] = $config;
return ResponseHelper::success($detail);
}
public function aiAnnouncement()
{

View File

@@ -101,4 +101,44 @@ class WechatFriendController extends BaseController
return ResponseHelper::success(['list' => $list, 'total' => $total]);
}
/**
* 获取单个好友详情
* @return \think\response\Json
*/
public function getDetail()
{
$friendId = $this->request->param('id');
$accountId = $this->getUserInfo('s2_accountId');
if (empty($accountId)) {
return ResponseHelper::error('请先登录');
}
if (empty($friendId)) {
return ResponseHelper::error('好友ID不能为空');
}
// 查询好友详情
$friend = Db::table('s2_wechat_friend')
->where(['id' => $friendId, 'isDeleted' => 0])
->find();
if (empty($friend)) {
return ResponseHelper::error('好友不存在');
}
// 处理好友数据
$friend['labels'] = json_decode($friend['labels'], true);
$friend['siteLabels'] = json_decode($friend['siteLabels'], true);
$friend['createTime'] = !empty($friend['createTime']) ? date('Y-m-d H:i:s', $friend['createTime']) : '';
$friend['updateTime'] = !empty($friend['updateTime']) ? date('Y-m-d H:i:s', $friend['updateTime']) : '';
$friend['passTime'] = !empty($friend['passTime']) ? date('Y-m-d H:i:s', $friend['passTime']) : '';
// 获取AI类型设置
$aiTypeSetting = FriendSettings::where('friendId', $friendId)->find();
$friend['aiType'] = $aiTypeSetting ? $aiTypeSetting['type'] : 0;
return ResponseHelper::success(['detail' => $friend]);
}
}