AI对话功能优化
This commit is contained in:
@@ -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' => []]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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群公告
|
||||
});
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user