Query whereIn 合并 legacy 场景;karuoAi 120s 超时,避免误降级 doubao 欠费。 Co-authored-by: Cursor <cursoragent@cursor.com>
174 lines
5.6 KiB
PHP
174 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace app\cunkebao\controller;
|
|
|
|
use app\cunkebao\service\AiAssistantService;
|
|
use library\ResponseHelper;
|
|
use think\facade\Log;
|
|
|
|
/**
|
|
* 存客宝 · AI 智能助手
|
|
* 自然语言配置获客计划 / 查询设备微信 / 算力扣费
|
|
*/
|
|
class AiAssistantController extends BaseController
|
|
{
|
|
protected function service(): AiAssistantService
|
|
{
|
|
return new AiAssistantService($this->getUserInfo());
|
|
}
|
|
|
|
/**
|
|
* GET /v1/ai-assistant/chat-history
|
|
*/
|
|
public function chatHistory()
|
|
{
|
|
try {
|
|
$sessionId = trim((string)$this->request->param('sessionId', ''));
|
|
if ($sessionId === '') {
|
|
$sessionId = $this->service()->newSessionId();
|
|
}
|
|
$list = $this->service()->getChatHistory($sessionId);
|
|
return ResponseHelper::success([
|
|
'sessionId' => $sessionId,
|
|
'list' => $list,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$code = (int)$e->getCode();
|
|
if ($code === 401) {
|
|
return ResponseHelper::unauthorized($e->getMessage());
|
|
}
|
|
return ResponseHelper::error($e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /v1/ai-assistant/send-message
|
|
*/
|
|
public function sendMessage()
|
|
{
|
|
try {
|
|
$message = trim((string)$this->request->param('message', ''));
|
|
$sessionId = trim((string)$this->request->param('sessionId', ''));
|
|
$result = $this->service()->sendMessage($sessionId, $message);
|
|
|
|
if (!empty($result['needRecharge'])) {
|
|
return ResponseHelper::error($result['msg'] ?? '算力余额不足', 402, [
|
|
'needRecharge' => true,
|
|
'rechargePath' => $result['rechargePath'] ?? '/recharge?tab=buy',
|
|
]);
|
|
}
|
|
|
|
return ResponseHelper::success($result, 'ok');
|
|
} catch (\Exception $e) {
|
|
Log::error('AiAssistant sendMessage: ' . $e->getMessage());
|
|
$code = (int)$e->getCode();
|
|
if ($code === 401) {
|
|
return ResponseHelper::unauthorized($e->getMessage());
|
|
}
|
|
return ResponseHelper::error($e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /v1/ai-assistant/send-message-stream
|
|
* SSE: session | delta | done | error
|
|
*/
|
|
public function sendMessageStream()
|
|
{
|
|
$message = trim((string)$this->request->param('message', ''));
|
|
$sessionId = trim((string)$this->request->param('sessionId', ''));
|
|
|
|
header('Content-Type: text/event-stream; charset=utf-8');
|
|
header('Cache-Control: no-cache, no-transform');
|
|
header('Connection: keep-alive');
|
|
header('X-Accel-Buffering: no');
|
|
|
|
if (function_exists('apache_setenv')) {
|
|
@apache_setenv('no-gzip', '1');
|
|
}
|
|
@ini_set('zlib.output_compression', '0');
|
|
@ini_set('output_buffering', 'off');
|
|
@ini_set('implicit_flush', '1');
|
|
while (ob_get_level() > 0) {
|
|
ob_end_flush();
|
|
}
|
|
|
|
$emit = function (string $event, array $data) {
|
|
echo 'event: ' . $event . "\n";
|
|
echo 'data: ' . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n";
|
|
if (ob_get_level() > 0) {
|
|
ob_flush();
|
|
}
|
|
flush();
|
|
};
|
|
|
|
try {
|
|
$this->service()->sendMessageStream($sessionId, $message, $emit);
|
|
} catch (\Exception $e) {
|
|
Log::error('AiAssistant sendMessageStream: ' . $e->getMessage());
|
|
$code = (int)$e->getCode();
|
|
if ($code === 401) {
|
|
$emit('error', ['code' => 401, 'msg' => $e->getMessage()]);
|
|
} else {
|
|
$emit('error', ['code' => 500, 'msg' => $e->getMessage()]);
|
|
}
|
|
}
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* POST /v1/ai-assistant/confirm-actions
|
|
*/
|
|
public function confirmActions()
|
|
{
|
|
try {
|
|
$sessionId = trim((string)$this->request->param('sessionId', ''));
|
|
$messageId = trim((string)$this->request->param('messageId', ''));
|
|
$actionIds = $this->request->param('actionIds/a', []);
|
|
if (!is_array($actionIds)) {
|
|
$actionIds = [];
|
|
}
|
|
|
|
$result = $this->service()->confirmActions($sessionId, $messageId, $actionIds);
|
|
if (!empty($result['needRecharge'])) {
|
|
return ResponseHelper::error($result['msg'] ?? '算力余额不足', 402, [
|
|
'needRecharge' => true,
|
|
'rechargePath' => $result['rechargePath'] ?? '/recharge?tab=buy',
|
|
]);
|
|
}
|
|
return ResponseHelper::success($result, '配置已应用');
|
|
} catch (\Exception $e) {
|
|
return ResponseHelper::error($e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /v1/ai-assistant/analysis-report
|
|
*/
|
|
public function analysisReport()
|
|
{
|
|
try {
|
|
$data = $this->service()->buildAnalysisReport();
|
|
return ResponseHelper::success($data);
|
|
} catch (\Exception $e) {
|
|
return ResponseHelper::error($e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /v1/ai-assistant/tools
|
|
*/
|
|
public function listTools()
|
|
{
|
|
try {
|
|
$tools = (new \app\cunkebao\service\AiAssistantToolService($this->getUserInfo()))->toolCatalog();
|
|
return ResponseHelper::success([
|
|
'tools' => $tools,
|
|
'apiCatalog' => \app\cunkebao\service\AiAssistantApiCatalog::all(),
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return ResponseHelper::error($e->getMessage());
|
|
}
|
|
}
|
|
}
|