Changes
This commit is contained in:
@@ -1426,11 +1426,16 @@ class References extends Base
|
||||
*
|
||||
* POST/GET: p_article_id(必填)
|
||||
*
|
||||
* 返回 list 中每项含:reference_no、p_refer_id、status(数值)、
|
||||
* 返回 list 中每项含:reference_no、p_refer_id、progress_status(数值)、
|
||||
* total、pending、done、failed、pass、is_pass、last_updated_at、records
|
||||
*
|
||||
* status 数值含义:
|
||||
* 分组状态字段 progress_status 数值含义(生命周期顺序):
|
||||
* 0 = 待校验 1 = 校对中 2 = 校对完成 3 = 校对失败
|
||||
*
|
||||
* records[i].status 与分组同一套数值含义(但 record 不会出现 1=校对中):
|
||||
* 0 = 待校验 2 = 校对完成 3 = 校对失败
|
||||
*
|
||||
* summary 用字符串键:pending / checking / completed / failed
|
||||
*/
|
||||
public function referenceCheckProgressAI()
|
||||
{
|
||||
@@ -1467,8 +1472,8 @@ class References extends Base
|
||||
* 返回字段:p_article_id、status、total、pending、done、failed、progress_percent
|
||||
* total —— 参考文献条数
|
||||
* pending —— 该条参考文献仍有未跑完明细的数量(含"部分跑完")
|
||||
* done —— 该条参考文献所有明细都 status=1 的数量
|
||||
* failed —— 该条参考文献全部跑完且至少 1 条 status=2 的数量
|
||||
* done —— 该条参考文献所有明细都 status=2(完成) 的数量
|
||||
* failed —— 该条参考文献全部跑完且至少 1 条 status=3(失败) 的数量
|
||||
* pending + done + failed = total;progress_percent = (done+failed)/total
|
||||
*
|
||||
* 分组明细请走 referenceCheckProgressAI。
|
||||
|
||||
@@ -49,7 +49,7 @@ class ReferenceCheck
|
||||
return;
|
||||
}
|
||||
|
||||
if (intval($row['status']) === 1) {
|
||||
if (intval($row['status']) === ReferenceCheckService::RECORD_COMPLETED) {
|
||||
$job->delete();
|
||||
return;
|
||||
}
|
||||
@@ -100,7 +100,7 @@ class ReferenceCheck
|
||||
$row = Db::name('article_reference_check_result')->where('id', $checkId)->find();
|
||||
try {
|
||||
(new ReferenceCheckService())->updateCheckResult($checkId, [
|
||||
'status' => 2,
|
||||
'status' => ReferenceCheckService::RECORD_FAILED,
|
||||
'error_msg' => $msg,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -58,7 +58,7 @@ class ReferenceCheckTwo
|
||||
return;
|
||||
}
|
||||
|
||||
// if (intval($row['status']) === 1) {
|
||||
// if (intval($row['status']) === ReferenceCheckService::RECORD_COMPLETED) {
|
||||
// $job->delete();
|
||||
// return;
|
||||
// }
|
||||
@@ -95,12 +95,12 @@ class ReferenceCheckTwo
|
||||
: '[Crossref复核-无摘要]';
|
||||
$reason = $tag . ' ' . (isset($llmResult['reason']) ? $llmResult['reason'] : '');
|
||||
|
||||
// LLM 通讯失败:写 status=2 并抛异常触发队列重试
|
||||
// LLM 通讯失败:写 status=RECORD_FAILED(3) 并抛异常触发队列重试
|
||||
if ($requestFailed) {
|
||||
$svc->updateCheckResult($checkId, [
|
||||
'confidence' => floatval($llmResult['confidence']),
|
||||
'reason' => $reason,
|
||||
'status' => 2,
|
||||
'status' => ReferenceCheckService::RECORD_FAILED,
|
||||
'error_msg' => isset($llmResult['reason']) ? $llmResult['reason'] : 'LLM request failed',
|
||||
]);
|
||||
throw new \RuntimeException(isset($llmResult['reason']) ? $llmResult['reason'] : 'LLM request failed');
|
||||
@@ -111,7 +111,7 @@ class ReferenceCheckTwo
|
||||
'is_match' => $canSupport ? 1 : 0,
|
||||
'confidence' => floatval($llmResult['confidence']),
|
||||
'reason' => $reason,
|
||||
'status' => 1,
|
||||
'status' => ReferenceCheckService::RECORD_COMPLETED,
|
||||
'error_msg' => '',
|
||||
]);
|
||||
$this->oQueueJob->log("Crossref复核写入 id={$checkId} affected={$affected} can_support=" . ($canSupport ? 1 : 0) . " confidence=" . floatval($llmResult['confidence']));
|
||||
@@ -148,7 +148,7 @@ class ReferenceCheckTwo
|
||||
$row = Db::name('article_reference_check_result')->where('id', $checkId)->find();
|
||||
try {
|
||||
(new ReferenceCheckService())->updateCheckResult($checkId, [
|
||||
'status' => 2,
|
||||
'status' => ReferenceCheckService::RECORD_FAILED,
|
||||
'error_msg' => $msg,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
@@ -21,28 +21,32 @@ class ReferenceCheckService
|
||||
const AM_STATUS_FAIL = 2;
|
||||
const AM_STATUS_RUNNING = 3;
|
||||
|
||||
/** 引用校对进度(按 reference_no 分组聚合后的对外状态) */
|
||||
const PROGRESS_PENDING = 0; // 待校验:分组内全部明细 status=0
|
||||
const PROGRESS_CHECKING = 1; // 校对中:分组内部分明细已结束、部分仍为 0
|
||||
const PROGRESS_COMPLETED = 2; // 校对完成:分组内全部明细 status=1
|
||||
const PROGRESS_FAILED = 3; // 校对失败:分组内全部明细已结束,且至少 1 条 status=2
|
||||
/**
|
||||
* 引用校对状态(生命周期顺序:0→1→2→3 = 待→进行→完成→失败)
|
||||
*
|
||||
* 这套常量在两个维度共用:
|
||||
* - 单条明细(article_reference_check_result.status)只会取 {0, 2, 3} —— 明细不会出现"校对中"
|
||||
* - 分组(按 reference_no 聚合后的 progress_status)四个值都会用 —— 1=部分跑完、部分仍为 0
|
||||
*/
|
||||
const PROGRESS_PENDING = 0; // 待校验:明细 status=0;分组内全部明细 status=0
|
||||
const PROGRESS_CHECKING = 1; // 校对中:仅分组层 —— 部分明细已结束、部分仍为 0
|
||||
const PROGRESS_COMPLETED = 2; // 校对完成:明细 status=2;分组内全部明细 status=2
|
||||
const PROGRESS_FAILED = 3; // 校对失败:明细 status=3;分组内全部跑完、≥1 条 status=3
|
||||
|
||||
/** 整篇文章的引用校对状态(对外整体状态,用于"开始/重置"按钮分流) */
|
||||
const ARTICLE_PROGRESS_NONE = 0; // 还没有任何校对记录
|
||||
const ARTICLE_PROGRESS_RUNNING = 1; // 至少 1 条 status=0(队列里还有未跑完的)
|
||||
const ARTICLE_PROGRESS_RUNNING = 1; // 至少 1 条明细 status=0(队列里还有未跑完的)
|
||||
const ARTICLE_PROGRESS_COMPLETED = 2; // 所有明细 status != 0(全部已完成或失败)
|
||||
|
||||
/**
|
||||
* 单条校对明细的对外状态(getProgressByPArticleId 返回的 records[i].status)
|
||||
* 单条校对明细的状态(DB 字段 article_reference_check_result.status)
|
||||
*
|
||||
* DB 里 article_reference_check_result.status 只有 0/1/2 三种值;
|
||||
* RECORD_PROCESSING 是基于 Redis 队列锁 :status='processing' 的瞬时态,
|
||||
* 并不持久化。worker 进入 LLM 调用期间 DB.status 仍是 0,需要靠队列锁识别。
|
||||
* 这里只列实际写入 DB 的三种值。"校对中"(值 1)是分组层专用,明细不会出现。
|
||||
* 数值与 PROGRESS_* 对齐(同一套语义),方便前端/后端混用。
|
||||
*/
|
||||
const RECORD_PENDING = 0; // 待校对,已入队但还没被 worker 拾起
|
||||
const RECORD_COMPLETED = 1; // 校对完成
|
||||
const RECORD_FAILED = 2; // 校对失败
|
||||
const RECORD_PROCESSING = 3; // 处理中:worker 正在跑 LLM(Redis :status='processing')
|
||||
const RECORD_COMPLETED = 2; // 校对完成
|
||||
const RECORD_FAILED = 3; // 校对失败
|
||||
|
||||
/** LLM 评分(confidence)通过阈值:>= 该值视为"通过" */
|
||||
const PASS_CONFIDENCE_THRESHOLD = 0.65;
|
||||
@@ -187,7 +191,7 @@ class ReferenceCheckService
|
||||
|
||||
$rows = Db::name('article_reference_check_result')
|
||||
->where('article_id', $articleId)
|
||||
->where('status', 1)
|
||||
->where('status', self::RECORD_COMPLETED)
|
||||
->where('confidence', '<=', 0.65)
|
||||
->orderRaw('rand()')
|
||||
->limit(2)
|
||||
@@ -399,14 +403,14 @@ class ReferenceCheckService
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$st = intval($row['status']);
|
||||
if ($st === 0) {
|
||||
if ($st === self::RECORD_PENDING) {
|
||||
$pending++;
|
||||
continue;
|
||||
}
|
||||
if ($st === 2 || ($st === 1 && intval($row['is_match']) === 0)) {
|
||||
if ($st === self::RECORD_FAILED || ($st === self::RECORD_COMPLETED && intval($row['is_match']) === 0)) {
|
||||
$hasFail = true;
|
||||
}
|
||||
if ($st === 1) {
|
||||
if ($st === self::RECORD_COMPLETED) {
|
||||
$done++;
|
||||
}
|
||||
}
|
||||
@@ -595,8 +599,8 @@ class ReferenceCheckService
|
||||
*
|
||||
* 每条参考文献按其明细 status 分布落桶(互斥):
|
||||
* pending —— 组内任一明细 status=0(含部分跑完的"校对中"也归此桶)
|
||||
* done —— 组内全部明细 status=1
|
||||
* failed —— 组内全部明细已结束、至少 1 条 status=2
|
||||
* done —— 组内全部明细 status=2(完成)
|
||||
* failed —— 组内全部明细已结束、至少 1 条 status=3(失败)
|
||||
*
|
||||
* pending + done + failed = total;progress_percent = (done + failed) / total。
|
||||
* 分组明细请走 getProgressByPArticleId(控制器 referenceCheckProgressAI)。
|
||||
@@ -614,8 +618,8 @@ class ReferenceCheckService
|
||||
// 50 条参考文献 → 返回 50 行,PHP 走一次循环分桶即可
|
||||
$rows = Db::name('article_reference_check_result')
|
||||
->field('reference_no'
|
||||
. ', SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS pending_cnt'
|
||||
. ', SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) AS failed_cnt')
|
||||
. ', SUM(CASE WHEN status = ' . self::RECORD_PENDING . ' THEN 1 ELSE 0 END) AS pending_cnt'
|
||||
. ', SUM(CASE WHEN status = ' . self::RECORD_FAILED . ' THEN 1 ELSE 0 END) AS failed_cnt')
|
||||
->where('p_article_id', $pArticleId)
|
||||
->group('reference_no')
|
||||
->select();
|
||||
@@ -668,19 +672,19 @@ class ReferenceCheckService
|
||||
/**
|
||||
* 按 p_article_id 查整篇引用校对进度,按 reference_no 分组聚合状态,并展开每条明细。
|
||||
*
|
||||
* 单条 article_reference_check_result.status:
|
||||
* 0 = 待校验 1 = 校对完成 2 = 校对失败
|
||||
* 状态映射统一遵循"生命周期顺序"(PROGRESS_* / RECORD_* 取值一致):
|
||||
* 0 = 待校验 1 = 校对中(仅分组层) 2 = 校对完成 3 = 校对失败
|
||||
*
|
||||
* 分组(reference_no)状态(返回字段 status,数值类型):
|
||||
* 0 = PROGRESS_PENDING 待校验 :分组内全部明细 status=0
|
||||
* 1 = PROGRESS_CHECKING 校对中 :分组内部分明细已结束、部分仍为 0
|
||||
* 2 = PROGRESS_COMPLETED 校对完成:分组内全部明细 status=1
|
||||
* 3 = PROGRESS_FAILED 校对失败:分组内全部明细已结束,且至少 1 条 status=2
|
||||
* 分组(reference_no)状态返回字段 progress_status:
|
||||
* - 0 = PROGRESS_PENDING 分组内全部明细 status=0
|
||||
* - 1 = PROGRESS_CHECKING 分组内部分明细已结束、部分仍为 0(明细不会出现此值)
|
||||
* - 2 = PROGRESS_COMPLETED 分组内全部明细 status=2
|
||||
* - 3 = PROGRESS_FAILED 分组内全部明细已结束、且至少 1 条 status=3
|
||||
*
|
||||
* 每个分组还会展开 records 子数组,给出该 reference_no 下每条 check 明细的:
|
||||
* - status(同上 0/1/2)
|
||||
* - confidence 评分
|
||||
* - is_pass(confidence >= PASS_CONFIDENCE_THRESHOLD 视为通过)
|
||||
* records[i] 字段:
|
||||
* - status 0=待校验 2=完成 3=失败(与分组同一套数值含义,不会出现 1)
|
||||
* - confidence LLM 评分
|
||||
* - is_pass confidence >= PASS_CONFIDENCE_THRESHOLD 视为通过
|
||||
*
|
||||
* @return array{p_article_id:int, total_groups:int, summary:array, list:array}
|
||||
*/
|
||||
@@ -697,12 +701,12 @@ class ReferenceCheckService
|
||||
->order('reference_no asc, id asc')
|
||||
->select();
|
||||
|
||||
// summary 用数值键,0/1/2/3 对应 PROGRESS_* 常量
|
||||
// summary 用字符串键,避免数值下标看不出含义;同时保留数值键和 PROGRESS_* 常量对照
|
||||
$summary = [
|
||||
self::PROGRESS_PENDING => 0,
|
||||
self::PROGRESS_CHECKING => 0,
|
||||
self::PROGRESS_COMPLETED => 0,
|
||||
self::PROGRESS_FAILED => 0,
|
||||
'pending' => 0, // PROGRESS_PENDING = 0
|
||||
'checking' => 0, // PROGRESS_CHECKING = 1
|
||||
'completed' => 0, // PROGRESS_COMPLETED = 2
|
||||
'failed' => 0, // PROGRESS_FAILED = 3
|
||||
];
|
||||
if (empty($rows)) {
|
||||
return [
|
||||
@@ -737,11 +741,12 @@ class ReferenceCheckService
|
||||
|
||||
$groups[$refNo]['total']++;
|
||||
$st = intval($this->arrGet($row, 'status', 0));
|
||||
if ($st === 0) {
|
||||
// record 仅存 {0=待校验, 2=完成, 3=失败};不会出现 1(校对中)
|
||||
if ($st === self::RECORD_PENDING) {
|
||||
$groups[$refNo]['pending']++;
|
||||
} elseif ($st === 1) {
|
||||
} elseif ($st === self::RECORD_COMPLETED) {
|
||||
$groups[$refNo]['done']++;
|
||||
} elseif ($st === 2) {
|
||||
} elseif ($st === self::RECORD_FAILED) {
|
||||
$groups[$refNo]['failed']++;
|
||||
}
|
||||
|
||||
@@ -778,22 +783,27 @@ class ReferenceCheckService
|
||||
$pass = $g['pass'];
|
||||
|
||||
if ($pending === $total) {
|
||||
$status = self::PROGRESS_PENDING;
|
||||
$progressStatus = self::PROGRESS_PENDING;
|
||||
} elseif ($pending === 0) {
|
||||
$status = $failed > 0 ? self::PROGRESS_FAILED : self::PROGRESS_COMPLETED;
|
||||
$progressStatus = $failed > 0 ? self::PROGRESS_FAILED : self::PROGRESS_COMPLETED;
|
||||
} else {
|
||||
$status = self::PROGRESS_CHECKING;
|
||||
$progressStatus = self::PROGRESS_CHECKING;
|
||||
}
|
||||
|
||||
// 整体通过校验:分组已全部完成(无 pending、无 failed),且每条 confidence >= 0.65
|
||||
$g['is_pass'] = (
|
||||
$status === self::PROGRESS_COMPLETED
|
||||
$progressStatus === self::PROGRESS_COMPLETED
|
||||
&& $total > 0
|
||||
&& $pass === $total
|
||||
);
|
||||
|
||||
$summary[$status]++;
|
||||
$g['status'] = $status;
|
||||
switch ($progressStatus) {
|
||||
case self::PROGRESS_PENDING: $summary['pending']++; break;
|
||||
case self::PROGRESS_CHECKING: $summary['checking']++; break;
|
||||
case self::PROGRESS_COMPLETED: $summary['completed']++; break;
|
||||
case self::PROGRESS_FAILED: $summary['failed']++; break;
|
||||
}
|
||||
$g['progress_status'] = $progressStatus;
|
||||
$list[] = $g;
|
||||
}
|
||||
|
||||
@@ -988,7 +998,7 @@ class ReferenceCheckService
|
||||
$q->where('status', $status);
|
||||
}
|
||||
if ($onlyMismatch) {
|
||||
$q->where('status', 1)->where('is_match', 0);
|
||||
$q->where('status', self::RECORD_COMPLETED)->where('is_match', 0);
|
||||
}
|
||||
return $q->order('am_id asc, cite_tag_start asc, reference_no asc')->select();
|
||||
}
|
||||
@@ -1018,7 +1028,7 @@ class ReferenceCheckService
|
||||
|
||||
foreach ($this->listByArticle($articleId, -1) as $r) {
|
||||
$stats['total']++;
|
||||
if (intval($r['status']) === 0) {
|
||||
if (intval($r['status']) === self::RECORD_PENDING) {
|
||||
$stats['pending']++;
|
||||
} elseif (intval($r['is_match']) === 1) {
|
||||
$stats['match']++;
|
||||
@@ -1078,14 +1088,14 @@ class ReferenceCheckService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $rows status=1 的检测结果
|
||||
* @param array $rows 已校对完成(status=RECORD_COMPLETED)但 is_match=0 的检测结果
|
||||
* @return array<int, array> am_id => indexed bad map
|
||||
*/
|
||||
private function indexBadResults($rows)
|
||||
{
|
||||
$byAm = [];
|
||||
foreach ($rows as $row) {
|
||||
if (intval($row['status']) !== 1 || intval($row['is_match']) === 1) {
|
||||
if (intval($row['status']) !== self::RECORD_COMPLETED || intval($row['is_match']) === 1) {
|
||||
continue;
|
||||
}
|
||||
$amId = intval($row['am_id']);
|
||||
@@ -1589,7 +1599,7 @@ class ReferenceCheckService
|
||||
|
||||
if ($contentA === '' || $contentB === '') {
|
||||
$this->updateCheckResult($checkId, [
|
||||
'status' => 2,
|
||||
'status' => self::RECORD_FAILED,
|
||||
'error_msg' => 'Missing article_main.content or refer_text',
|
||||
]);
|
||||
throw new \RuntimeException('Missing article_main.content or refer_text');
|
||||
@@ -1601,13 +1611,13 @@ class ReferenceCheckService
|
||||
$confidence = floatval(isset($llmResult['confidence']) ? $llmResult['confidence'] : 0);
|
||||
$reason = isset($llmResult['reason']) ? $llmResult['reason'] : '';
|
||||
|
||||
// LLM 通讯失败:写 status=2(校对失败) + error_msg,抛异常让队列 worker 走 release(30) 重试;
|
||||
// 重试 3 次后 ReferenceCheck::markFailed 会保持 status=2 收尾
|
||||
// LLM 通讯失败:写 status=RECORD_FAILED(3) + error_msg,抛异常让队列 worker 走 release(30) 重试;
|
||||
// 重试 3 次后 ReferenceCheck::markFailed 会保持 status=3 收尾
|
||||
if ($requestFailed) {
|
||||
$this->updateCheckResult($checkId, [
|
||||
'confidence' => $confidence,
|
||||
'reason' => $reason,
|
||||
'status' => 2,
|
||||
'status' => self::RECORD_FAILED,
|
||||
'error_msg' => $reason,
|
||||
]);
|
||||
$this->clearReferenceCheckQueueLock($checkId);
|
||||
@@ -1619,7 +1629,7 @@ class ReferenceCheckService
|
||||
'is_match' => $canSupport ? 1 : 0,
|
||||
'confidence' => $confidence,
|
||||
'reason' => $reason,
|
||||
'status' => 1,
|
||||
'status' => self::RECORD_COMPLETED,
|
||||
'error_msg' => '',
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user