Files
cunkebao_v3/Server/application/chukebao/controller/AiChatController.php

832 lines
30 KiB
PHP
Raw Normal View History

2025-09-19 16:49:25 +08:00
<?php
namespace app\chukebao\controller;
2025-10-25 17:41:20 +08:00
use app\ai\controller\CozeAI;
2025-09-19 16:49:25 +08:00
use app\ai\controller\DouBaoAI;
2025-10-25 17:41:20 +08:00
use app\api\model\WechatFriendModel;
2025-09-19 16:49:25 +08:00
use app\chukebao\controller\TokensRecordController as tokensRecord;
2025-10-25 17:41:20 +08:00
use app\chukebao\model\AiSettings;
use app\chukebao\model\FriendSettings;
2025-09-30 16:10:43 +08:00
use app\chukebao\model\TokensCompany;
2025-09-19 16:49:25 +08:00
use library\ResponseHelper;
use think\Db;
2025-10-28 09:25:39 +08:00
use think\facade\Cache;
use think\facade\Log;
2025-09-19 16:49:25 +08:00
2025-10-25 17:41:20 +08:00
/**
* AI聊天控制器
* 负责处理与好友的AI对话功能
*/
2025-09-19 16:49:25 +08:00
class AiChatController extends BaseController
{
2025-10-25 17:41:20 +08:00
// 对话状态常量
const STATUS_CREATED = 'created'; // 对话已创建
const STATUS_IN_PROGRESS = 'in_progress'; // 智能体正在处理中
const STATUS_COMPLETED = 'completed'; // 智能体已完成处理
const STATUS_FAILED = 'failed'; // 对话失败
const STATUS_REQUIRES_ACTION = 'requires_action'; // 对话中断,需要进一步处理
const STATUS_CANCELED = 'canceled'; // 对话已取消
// 轮询配置
2025-10-28 09:25:39 +08:00
const MAX_RETRY_TIMES = 1000; // 最大重试次数
const RETRY_INTERVAL = 500000; // 重试间隔微秒即500毫秒
// 并发控制
const CACHE_EXPIRE = 30; // 缓存过期时间(秒)
// 请求唯一标识符
private $requestKey = '';
private $requestId = '';
private $currentStep = 0;
2025-10-25 17:41:20 +08:00
/**
* AI聊天主入口
*
* @return \think\response\Json
*/
public function index()
{
try {
// 1. 参数验证和初始化
$params = $this->validateAndInitParams();
2025-10-28 09:25:39 +08:00
2025-10-25 17:41:20 +08:00
if ($params === false) {
return ResponseHelper::error('参数验证失败');
}
2025-10-28 09:25:39 +08:00
// 并发控制:检查并处理同一用户的重复请求
$this->requestKey = "aichat_{$params['friendId']}_{$params['wechatAccountId']}";
$this->requestId = uniqid('req_', true);
$concurrentCheck = $this->handleConcurrentRequest($params);
if ($concurrentCheck !== true) {
return $concurrentCheck; // 返回错误响应
}
$this->currentStep = 1;
2025-10-25 17:41:20 +08:00
// 2. 验证Tokens余额
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(2);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
$hasBalance = $this->checkTokensBalance($params['companyId']);
if (!$hasBalance) {
$this->clearRequestCache();
2025-10-25 17:41:20 +08:00
return ResponseHelper::error('Tokens余额不足请充值后再试');
}
// 3. 获取AI配置
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(3);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$setting = $this->getAiSettings($params['companyId']);
2025-10-28 09:25:39 +08:00
2025-10-25 17:41:20 +08:00
if (!$setting) {
2025-10-28 09:25:39 +08:00
$this->clearRequestCache();
2025-10-25 17:41:20 +08:00
return ResponseHelper::error('未找到AI配置信息请先配置AI策略');
}
// 4. 获取好友AI设置
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(4);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$friendSettings = $this->getFriendSettings($params['companyId'], $params['friendId']);
2025-10-28 09:25:39 +08:00
2025-10-25 17:41:20 +08:00
if (!$friendSettings) {
2025-10-28 09:25:39 +08:00
$this->clearRequestCache();
2025-10-25 17:41:20 +08:00
return ResponseHelper::error('该好友未配置或未开启AI功能');
}
// 5. 确保会话存在
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(5);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$conversationId = $this->ensureConversation($friendSettings, $setting, $params);
2025-10-29 15:31:43 +08:00
if (empty($conversationId)) {
2025-10-28 09:25:39 +08:00
$this->clearRequestCache();
2025-10-25 17:41:20 +08:00
return ResponseHelper::error('创建会话失败');
}
// 6. 获取历史消息
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(6);
if ($this->isRequestCanceled()) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$msgData = $this->getHistoryMessages($params['friendId'], $friendSettings);
2025-10-28 09:25:39 +08:00
// 7. 创建AI对话从这步开始需要保存对话ID以便取消
$this->updateRequestStep(7);
if ($this->isRequestCanceled($conversationId, null)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$chatId = $this->createAiChat($setting, $friendSettings, $msgData);
2025-10-29 15:48:48 +08:00
if (empty($chatId)) {
2025-10-28 09:25:39 +08:00
$this->clearRequestCache();
2025-10-25 17:41:20 +08:00
return ResponseHelper::error('创建对话失败');
}
2025-10-28 09:25:39 +08:00
// 保存对话ID到缓存以便新请求可以取消
$this->updateRequestStep(7, $conversationId, $chatId);
2025-10-25 17:41:20 +08:00
// 8. 等待AI处理完成轮询
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(8, $conversationId, $chatId);
if ($this->isRequestCanceled($conversationId, $chatId)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$chatResult = $this->waitForChatCompletion($conversationId, $chatId);
2025-10-28 09:25:39 +08:00
if (!$chatResult['success']) {
$this->clearRequestCache();
return ResponseHelper::error($chatResult['error']);
2025-10-25 17:41:20 +08:00
}
2025-10-28 09:25:39 +08:00
$chatResult = $chatResult['data'];
2025-10-25 17:41:20 +08:00
// 9. 扣除Tokens
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(9, $conversationId, $chatId);
if ($this->isRequestCanceled($conversationId, $chatId)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$this->consumeTokens($chatResult, $params, $friendSettings);
// 10. 获取对话消息
2025-10-28 09:25:39 +08:00
$this->updateRequestStep(10, $conversationId, $chatId);
if ($this->isRequestCanceled($conversationId, $chatId)) {
return ResponseHelper::error('该好友有新的AI对话请求正在处理中当前请求已被取消');
}
2025-10-25 17:41:20 +08:00
$messages = $this->getChatMessages($conversationId, $chatId);
2025-10-28 09:25:39 +08:00
2025-10-25 17:41:20 +08:00
if (!$messages) {
return ResponseHelper::error('获取对话消息失败');
}
2025-10-28 09:25:39 +08:00
// 筛选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], '对话成功');
2025-10-25 17:41:20 +08:00
} catch (\Exception $e) {
2025-10-28 09:25:39 +08:00
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());
2025-10-25 17:41:20 +08:00
return ResponseHelper::error('系统异常:' . $e->getMessage());
}
}
/**
* 验证和初始化参数
*
* @return array|false
*/
private function validateAndInitParams()
{
$userId = $this->getUserInfo('id');
$companyId = $this->getUserInfo('companyId');
$friendId = $this->request->param('friendId', '');
$wechatAccountId = $this->request->param('wechatAccountId', '');
if (empty($wechatAccountId) || empty($friendId)) {
return false;
}
return [
'userId' => $userId,
'companyId' => $companyId,
'friendId' => $friendId,
'wechatAccountId' => $wechatAccountId
];
}
/**
* 检查Tokens余额
*
* @param int $companyId 公司ID
* @return bool
*/
private function checkTokensBalance($companyId)
{
$tokens = TokensCompany::where(['companyId' => $companyId])->value('tokens');
return !empty($tokens) && $tokens > 1000;
}
/**
* 获取AI配置
*
* @param int $companyId 公司ID
* @return AiSettings|null
*/
private function getAiSettings($companyId)
{
return AiSettings::where(['companyId' => $companyId])->find();
}
/**
* 获取好友AI设置
*
* @param int $companyId 公司ID
* @param string $friendId 好友ID
* @return FriendSettings|null
*/
private function getFriendSettings($companyId, $friendId)
{
$friendSettings = FriendSettings::where([
'companyId' => $companyId,
'friendId' => $friendId
])->find();
if (empty($friendSettings) || $friendSettings->type == 0) {
return null;
}
return $friendSettings;
}
/**
* 确保会话存在
*
* @param FriendSettings $friendSettings 好友设置
* @param AiSettings $setting AI设置
* @param array $params 参数
* @return string|null 会话ID
*/
private function ensureConversation($friendSettings, $setting, $params)
{
if (!empty($friendSettings->conversationId)) {
return $friendSettings->conversationId;
}
// 创建新会话
$cozeAI = new CozeAI();
$data = [
'bot_id' => $setting->botId,
'name' => '与好友' . $params['friendId'] . '的对话',
'meta_data' => [
'friendId' => (string)$friendSettings->friendId,
'wechatAccountId' => (string)$params['wechatAccountId'],
],
];
$res = $cozeAI->createConversation($data);
$res = json_decode($res, true);
if ($res['code'] != 200) {
2025-10-28 09:25:39 +08:00
Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误'));
2025-10-25 17:41:20 +08:00
return null;
}
// 保存会话ID
$conversationId = $res['data']['id'];
$friendSettings->conversationId = $conversationId;
$friendSettings->conversationTime = time();
$friendSettings->save();
return $conversationId;
}
/**
* 获取历史消息
*
* @param string $friendId 好友ID
* @param FriendSettings $friendSettings 好友设置
* @return array
*/
private function getHistoryMessages($friendId, $friendSettings)
{
$msgData = [];
// 会话创建时间小于1分钟加载最近10条消息
if ($friendSettings->conversationTime >= time() - 60) {
$messages = Db::table('s2_wechat_message')
->where('wechatFriendId', $friendId)
->where('msgType', '<', 50)
->order('wechatTime desc')
->field('id,content,msgType,isSend,wechatTime')
->limit(10)
->select();
// 按时间正序排列
usort($messages, function ($a, $b) {
return $a['wechatTime'] <=> $b['wechatTime'];
});
// 处理聊天数据
foreach ($messages as $val) {
if (empty($val['content'])) {
continue;
}
$msg = [
'role' => empty($val['isSend']) ? 'user' : 'assistant',
'content' => $val['content'],
'type' => empty($val['isSend']) ? 'question' : 'answer',
'content_type' => 'text'
];
$msgData[] = $msg;
}
} else {
// 只加载最新一条用户消息
$message = Db::table('s2_wechat_message')
->where('wechatFriendId', $friendId)
->where('msgType', '<', 50)
->where('isSend', 0)
->order('wechatTime desc')
->field('id,content,msgType,isSend,wechatTime')
->find();
if (!empty($message) && !empty($message['content'])) {
$msgData[] = [
'role' => 'user',
'content' => $message['content'],
'type' => 'question',
'content_type' => 'text'
];
}
}
return $msgData;
}
/**
* 创建AI对话
*
* @param AiSettings $setting AI设置
* @param FriendSettings $friendSettings 好友设置
* @param array $msgData 消息数据
* @return string|null 对话ID
*/
private function createAiChat($setting, $friendSettings, $msgData)
{
$cozeAI = new CozeAI();
$data = [
'bot_id' => $setting->botId,
'uid' => $friendSettings->friendId,
'conversation_id' => $friendSettings->conversationId,
'question' => $msgData,
];
$res = $cozeAI->createChat($data);
$res = json_decode($res, true);
if ($res['code'] != 200) {
2025-10-28 09:25:39 +08:00
Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误'));
2025-10-25 17:41:20 +08:00
return null;
}
return $res['data']['id'];
}
/**
* 等待AI处理完成轮询机制
*
* @param string $conversationId 会话ID
* @param string $chatId 对话ID
2025-10-28 09:25:39 +08:00
* @return array ['success' => bool, 'data' => array|null, 'error' => string]
2025-10-25 17:41:20 +08:00
*/
private function waitForChatCompletion($conversationId, $chatId)
{
$cozeAI = new CozeAI();
$retryCount = 0;
while ($retryCount < self::MAX_RETRY_TIMES) {
// 获取对话状态
$res = $cozeAI->getConversationChat([
'conversation_id' => $conversationId,
'chat_id' => $chatId,
]);
$res = json_decode($res, true);
if ($res['code'] != 200) {
2025-10-28 09:25:39 +08:00
$errorMsg = 'AI接口调用失败' . ($res['msg'] ?? '未知错误');
Log::error($errorMsg);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
2025-10-25 17:41:20 +08:00
}
$status = $res['data']['status'] ?? '';
// 处理不同的状态
switch ($status) {
case self::STATUS_COMPLETED:
// 对话完成,返回结果
2025-10-28 09:25:39 +08:00
return ['success' => true, 'data' => $res['data'], 'error' => ''];
2025-10-25 17:41:20 +08:00
case self::STATUS_IN_PROGRESS:
case self::STATUS_CREATED:
// 继续等待
$retryCount++;
2025-10-28 09:25:39 +08:00
usleep(self::RETRY_INTERVAL);
2025-10-25 17:41:20 +08:00
break;
case self::STATUS_FAILED:
2025-10-28 09:25:39 +08:00
$errorMsg = 'AI对话处理失败';
Log::error($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
2025-10-25 17:41:20 +08:00
case self::STATUS_CANCELED:
2025-10-28 09:25:39 +08:00
$errorMsg = 'AI对话已被取消';
Log::error($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
2025-10-25 17:41:20 +08:00
case self::STATUS_REQUIRES_ACTION:
2025-10-28 09:25:39 +08:00
$errorMsg = 'AI对话需要进一步处理';
Log::warning($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
2025-10-25 17:41:20 +08:00
default:
2025-10-28 09:25:39 +08:00
$errorMsg = 'AI返回未知状态' . $status;
Log::error($errorMsg);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
2025-10-25 17:41:20 +08:00
}
}
// 超时
2025-10-28 09:25:39 +08:00
$errorMsg = 'AI对话处理超时已等待' . (self::MAX_RETRY_TIMES * self::RETRY_INTERVAL / 1000000) . '秒';
Log::error($errorMsg . 'chat_id: ' . $chatId);
return ['success' => false, 'data' => null, 'error' => $errorMsg];
2025-10-25 17:41:20 +08:00
}
/**
* 扣除Tokens
*
* @param array $chatResult 对话结果
* @param array $params 参数
* @param FriendSettings $friendSettings 好友设置
*/
private function consumeTokens($chatResult, $params, $friendSettings)
{
$tokenCount = $chatResult['usage']['token_count'] ?? 0;
if (empty($tokenCount)) {
return;
}
// 获取好友昵称
$nickname = WechatFriendModel::where('id', $friendSettings->friendId)->value('nickname');
$remarks = !empty($nickname) ? '与好友【' . $nickname . '】聊天' : '与好友聊天';
// 扣除Tokens
$tokensRecord = new tokensRecord();
$data = [
'tokens' => $tokenCount * 20,
'type' => 0,
2025-12-09 15:01:38 +08:00
'form' => 13,
2025-10-25 17:41:20 +08:00
'wechatAccountId' => $params['wechatAccountId'],
'friendIdOrGroupId' => $params['friendId'],
'remarks' => $remarks,
];
$tokensRecord->consumeTokens($data);
}
/**
* 获取对话消息
*
* @param string $conversationId 会话ID
* @param string $chatId 对话ID
* @return array|null
*/
private function getChatMessages($conversationId, $chatId)
{
$cozeAI = new CozeAI();
$res = $cozeAI->listConversationMessage([
'conversation_id' => $conversationId,
'chat_id' => $chatId,
]);
$res = json_decode($res, true);
if ($res['code'] != 200) {
2025-10-28 09:25:39 +08:00
Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误'));
2025-10-25 17:41:20 +08:00
return null;
}
return $res['data'] ?? [];
}
2025-10-28 09:25:39 +08:00
/**
* 处理并发请求
* 检查是否有同一用户的旧请求正在处理,如果有则取消旧请求
*
* @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}");
}
}
}
2025-10-25 17:41:20 +08:00
public function index2222()
{
2025-09-19 16:49:25 +08:00
$userId = $this->getUserInfo('id');
$companyId = $this->getUserInfo('companyId');
2025-10-25 17:41:20 +08:00
$friendId = $this->request->param('friendId', '');
$wechatAccountId = $this->request->param('wechatAccountId', '');
$content = $this->request->param('content', '');
2025-09-19 16:49:25 +08:00
2025-10-25 17:41:20 +08:00
if (empty($wechatAccountId) || empty($friendId)) {
2025-09-19 16:49:25 +08:00
return ResponseHelper::error('参数缺失');
}
2025-09-30 16:10:43 +08:00
$tokens = TokensCompany::where(['companyId' => $companyId])->value('tokens');
2025-10-25 17:41:20 +08:00
if (empty($tokens) || $tokens <= 0) {
2025-09-19 16:49:25 +08:00
return ResponseHelper::error('用户Tokens余额不足');
}
//读取AI配置
2025-10-25 17:41:20 +08:00
$setting = Db::name('ai_settings')->where(['companyId' => $companyId, 'userId' => $userId])->find();
if (empty($setting)) {
2025-09-19 16:49:25 +08:00
return ResponseHelper::error('未找到配置信息请先配置AI策略');
}
2025-10-25 17:41:20 +08:00
$config = json_decode($setting['config'], true);
2025-09-19 16:49:25 +08:00
$modelSetting = $config['modelSetting'];
$round = isset($config['round']) ? $config['round'] : 10;
// 导出聊天
$messages = Db::table('s2_wechat_message')
->where('wechatFriendId', $friendId)
->order('wechatTime desc')
->field('id,content,msgType,isSend,wechatTime')
->limit($round)
->select();
2025-10-25 17:41:20 +08:00
usort($messages, function ($a, $b) {
2025-09-19 16:49:25 +08:00
return $a['wechatTime'] <=> $b['wechatTime'];
});
//处理聊天数据
$msg = [];
2025-10-25 17:41:20 +08:00
foreach ($messages as $val) {
if (empty($val['content'])) {
2025-09-19 16:49:25 +08:00
continue;
}
2025-10-25 17:41:20 +08:00
if (!empty($val['isSend'])) {
$msg[] = '客服:' . $val['content'];
} else {
$msg[] = '用户:' . $val['content'];
2025-09-19 16:49:25 +08:00
}
}
$content = implode("\n", $msg);
$params = [
'model' => 'doubao-1-5-pro-32k-250115',
'messages' => [
// ['role' => 'system', 'content' => '请完成跟客户的对话'],
['role' => 'system', 'content' => '角色设定:' . $modelSetting['role']],
['role' => 'system', 'content' => '公司背景:' . $modelSetting['businessBackground']],
['role' => 'system', 'content' => '对话风格:' . $modelSetting['dialogueStyle']],
['role' => 'user', 'content' => $content],
],
];
//AI处理
$ai = new DouBaoAI();
$res = $ai->text($params);
2025-10-25 17:41:20 +08:00
$res = json_decode($res, true);
2025-09-19 16:49:25 +08:00
if ($res['code'] == 200) {
//扣除Tokens
$tokensRecord = new tokensRecord();
$nickname = Db::table('s2_wechat_friend')->where(['id' => $friendId])->value('nickname');
2025-10-25 17:41:20 +08:00
$remarks = !empty($nickname) ? '与好友【' . $nickname . '】聊天' : '与好友聊天';
2025-09-19 16:49:25 +08:00
$data = [
'tokens' => $res['data']['token'],
'type' => 0,
2025-12-09 15:01:38 +08:00
'form' => 13,
2025-09-19 16:49:25 +08:00
'wechatAccountId' => $wechatAccountId,
'friendIdOrGroupId' => $friendId,
'remarks' => $remarks,
];
$tokensRecord->consumeTokens($data);
return ResponseHelper::success($res['data']['content']);
2025-10-25 17:41:20 +08:00
} else {
2025-09-19 16:49:25 +08:00
return ResponseHelper::error($res['msg']);
}
}
}