Files
tougao/application/api/controller/ExpertFieldAi.php
2026-06-05 15:50:48 +08:00

204 lines
6.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\api\controller;
use think\Db;
use app\common\ExpertFieldAiService;
/**
* Expert 领域 AI 总结(方案 C少量 user 关联 + 主流程 AI
*
* POST startChain 启动链式队列(关联 + AI主入口
* POST processOne 同步处理单个 expert_id
* POST processBatch 同步批量处理
* POST linkOne 仅 user 关联(调试)
* POST syncByUser user 有 field_ai 后同步到 expert
* GET preview 预览可关联 / 可 AI 总结 / 上下文
* GET statistics 覆盖统计
*/
class ExpertFieldAi extends Base
{
/**
* 启动链式处理(主入口)
* Worker: php think queue:work --queue ExpertFieldAi
*/
public function startChain()
{
$force = intval($this->request->param('force', 0)) === 1;
$delay = max(0, intval($this->request->param('delay', 1)));
$svc = new ExpertFieldAiService();
$started = $svc->startChain($force, $delay);
return jsonSuccess([
'started' => $started,
'queue' => ExpertFieldAiService::QUEUE_NAME,
'force' => $force,
'msg' => $started ? 'chain enqueued' : 'no pending experts',
]);
}
/** 兼容旧接口名 */
public function startLinkChain()
{
return $this->startChain();
}
/**
* 同步处理单个 expert关联 + AI
*/
public function processOne()
{
$expertId = intval($this->request->param('expert_id', 0));
$force = intval($this->request->param('force', 0)) === 1;
if ($expertId <= 0) {
return jsonError('expert_id required');
}
$svc = new ExpertFieldAiService();
$result = $svc->processExpert($expertId, $force);
if (empty($result['ok'])) {
return jsonError(isset($result['error']) ? $result['error'] : 'failed');
}
return jsonSuccess($result);
}
/**
* 同步批量处理
* expert_ids 逗号分隔,或 limit 扫描待处理前 N 条
*/
public function processBatch()
{
$force = intval($this->request->param('force', 0)) === 1;
$ids = $this->resolveExpertIds();
if (empty($ids)) {
return jsonError('expert_ids 或 limit 必填');
}
$svc = new ExpertFieldAiService();
return jsonSuccess($svc->batchProcess($ids, $force));
}
/**
* 仅 user 关联(不 AI
*/
public function linkOne()
{
$expertId = intval($this->request->param('expert_id', 0));
$force = intval($this->request->param('force', 0)) === 1;
if ($expertId <= 0) {
return jsonError('expert_id required');
}
$svc = new ExpertFieldAiService();
$result = $svc->linkFromUser($expertId, $force);
if (empty($result['ok'])) {
return jsonError(isset($result['error']) ? $result['error'] : 'failed');
}
return jsonSuccess($result);
}
public function linkBatch()
{
$force = intval($this->request->param('force', 0)) === 1;
$ids = $this->resolveExpertIds(true);
if (empty($ids)) {
return jsonError('expert_ids 或 limit 必填');
}
$svc = new ExpertFieldAiService();
return jsonSuccess($svc->batchLinkFromUser($ids, $force));
}
public function syncByUser()
{
$userId = intval($this->request->param('user_id', 0));
$force = intval($this->request->param('force', 0)) === 1;
if ($userId <= 0) {
return jsonError('user_id required');
}
$svc = new ExpertFieldAiService();
$result = $svc->syncExpertsByUserId($userId, $force);
if (empty($result['ok'])) {
return jsonError(isset($result['error']) ? $result['error'] : 'failed');
}
return jsonSuccess($result);
}
/**
* 预览:是否可 user 关联、是否可 AI、上下文摘要
*/
public function preview()
{
$expertId = intval($this->request->param('expert_id', 0));
if ($expertId <= 0) {
return jsonError('expert_id required');
}
$svc = new ExpertFieldAiService();
$result = $svc->preview($expertId);
if (empty($result['ok'])) {
return jsonError(isset($result['error']) ? $result['error'] : 'failed');
}
$result['field_ai_status_text'] = $svc->statusLabel(intval($result['expert_field_ai_status']));
return jsonSuccess($result);
}
public function statistics()
{
$base = Db::name('expert')->where('state', '<>', 5);
$total = (clone $base)->count();
$done = (clone $base)->where('field_ai_status', ExpertFieldAiService::STATUS_DONE)->count();
$userLink = (clone $base)->where('field_ai_source', ExpertFieldAiService::SOURCE_USER_LINK)->count();
$aiDone = (clone $base)->where('field_ai_source', ExpertFieldAiService::SOURCE_AI)->count();
$pending = (clone $base)->where('field_ai_status', ExpertFieldAiService::STATUS_PENDING)->count();
$noUserLink = (clone $base)->where('field_ai_status', ExpertFieldAiService::STATUS_NO_USER_LINK)->count();
$insufficient = (clone $base)->where('field_ai_status', ExpertFieldAiService::STATUS_INSUFFICIENT)->count();
$failed = (clone $base)->where('field_ai_status', ExpertFieldAiService::STATUS_FAILED)->count();
return jsonSuccess([
'total' => $total,
'done' => $done,
'user_link' => $userLink,
'ai_done' => $aiDone,
'pending' => $pending,
'no_user_link' => $noUserLink,
'insufficient' => $insufficient,
'failed' => $failed,
'coverage_rate' => $total > 0 ? round($done / $total * 100, 2) . '%' : '0%',
]);
}
private function resolveExpertIds($linkOnly = false)
{
$idsRaw = trim((string)$this->request->param('expert_ids', ''));
$limit = min(max(intval($this->request->param('limit', 0)), 0), 200);
if ($idsRaw !== '') {
return array_filter(array_map('intval', explode(',', $idsRaw)));
}
if ($limit <= 0) {
return [];
}
$query = Db::name('expert')->where('state', '<>', 5);
if ($linkOnly) {
$query->where(function ($q) {
$q->where('field_ai_status', ExpertFieldAiService::STATUS_PENDING)
->whereOr('field_ai_status', ExpertFieldAiService::STATUS_FAILED);
});
} else {
$query->where(function ($q) {
$q->where('field_ai_status', ExpertFieldAiService::STATUS_PENDING)
->whereOr('field_ai_status', ExpertFieldAiService::STATUS_FAILED)
->whereOr('field_ai_status', ExpertFieldAiService::STATUS_NO_USER_LINK);
});
}
return $query->order('expert_id asc')->limit($limit)->column('expert_id');
}
}