自动查重
This commit is contained in:
@@ -2817,7 +2817,28 @@ class EmailClient extends Base
|
||||
break;
|
||||
|
||||
case 1: // 主编(预留,本期不实现)
|
||||
break;
|
||||
case 4: // 作者(预留)
|
||||
Db::name("article_author")->alias('aa')
|
||||
->join('t_user u', 'u.email = aa.email', 'inner')
|
||||
->join("t_article a","a.article_id = aa.article_id","left")
|
||||
->join('t_user_reviewer_info uri', 'uri.reviewer_id = u.user_id', 'left')
|
||||
->where('a.journal_id', $journalId)
|
||||
->where('u.email', '<>', '')
|
||||
->where('u.unsubscribed', 0);
|
||||
break;
|
||||
case 6: //获取往期的青年编委2025年以前的,中国人
|
||||
$now = strtotime('2025-01-01');
|
||||
$query = Db::name('user_to_yboard')->alias('y')
|
||||
->join('t_user u', 'u.user_id = y.user_id', 'inner')
|
||||
->join('t_user_reviewer_info uri', 'uri.reviewer_id = u.user_id', 'left')
|
||||
->where('y.journal_id', $journalId)
|
||||
->where('y.state', 0)
|
||||
->where('y.start_date', '<=', $now)
|
||||
->where('uri.country', 'China')
|
||||
->where('u.email', '<>', '')
|
||||
->where('u.unsubscribed', 0);
|
||||
break;//
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use think\Validate;
|
||||
* 论文查重(Turnitin / Crossref Similarity Check)控制器。
|
||||
*
|
||||
* 触发方式:纯手工(编辑后台点"查重"按钮)。
|
||||
* 报告策略:在线 viewer URL 临时签名 + PDF 永久落盘 runtime/plagiarism/。
|
||||
* 报告策略:PDF 在 poll 完成时落盘;在线 viewer URL 通过 getReportUrl 按需生成(临时签名)。
|
||||
*
|
||||
* 主要接口:
|
||||
* POST submit 触发查重
|
||||
@@ -37,12 +37,14 @@ class Plagiarism extends Base
|
||||
* article_id 必填
|
||||
* file_url 选填;不传则按 article_id 在 t_article_file 找 manuscirpt
|
||||
* editor_id 选填;触发人 user_id(前端拿不到也可以传 0)
|
||||
* check_type 选填;full(默认全文)| body_only(正文)| both(各提交一条)
|
||||
*/
|
||||
public function submit()
|
||||
{
|
||||
$articleId = intval($this->request->param('article_id', 0));
|
||||
$fileUrl = trim($this->request->param('file_url', ''));
|
||||
$editorId = intval($this->request->param('editor_id', 0));
|
||||
$checkType = trim($this->request->param('check_type', 'full'));
|
||||
|
||||
if ($articleId <= 0) {
|
||||
return jsonError('article_id required');
|
||||
@@ -53,8 +55,12 @@ class Plagiarism extends Base
|
||||
$localPath = $fileUrl !== ''
|
||||
? $svc->resolveFileUrlToLocal($fileUrl)
|
||||
: $svc->locateArticleManuscript($articleId);
|
||||
$checkId = $svc->submit($articleId, $localPath, $editorId, 'manual');
|
||||
return jsonSuccess(['check_id' => $checkId]);
|
||||
if (strtolower($checkType) === 'both') {
|
||||
$ids = $svc->submitBoth($articleId, $localPath, $editorId, 'manual');
|
||||
return jsonSuccess($ids);
|
||||
}
|
||||
$checkId = $svc->submit($articleId, $localPath, $editorId, 'manual', $checkType);
|
||||
return jsonSuccess(['check_id' => $checkId, 'check_type' => strtolower($checkType) ?: 'full']);
|
||||
} catch (\Throwable $e) {
|
||||
return jsonError($e->getMessage());
|
||||
}
|
||||
@@ -257,10 +263,14 @@ class Plagiarism extends Base
|
||||
'similarity_score' => floatval($r['similarity_score']),
|
||||
'tii_report_status' => (string)$r['tii_report_status'],
|
||||
'has_pdf' => !empty($r['pdf_local_path']),
|
||||
'local_pdf_url' => $r['pdf_local_path'],
|
||||
'has_viewer_url' => !empty($r['view_only_url']) && intval($r['view_only_url_expire']) > time(),
|
||||
'attempts' => intval($r['attempts']),
|
||||
'error_msg' => (string)$r['error_msg'],
|
||||
'source_file_name' => (string)$r['source_file_name'],
|
||||
'check_type' => (string)($r['check_type'] ?? 'full'),
|
||||
'check_type_label' => $this->checkTypeLabel($r['check_type'] ?? 'full'),
|
||||
'derived_file_path'=> (string)($r['derived_file_path'] ?? ''),
|
||||
'trigger_source' => (string)$r['trigger_source'],
|
||||
'triggered_by' => intval($r['triggered_by']),
|
||||
'ctime' => intval($r['ctime']),
|
||||
@@ -268,6 +278,15 @@ class Plagiarism extends Base
|
||||
];
|
||||
}
|
||||
|
||||
private function checkTypeLabel($checkType)
|
||||
{
|
||||
$t = strtolower(trim((string) $checkType));
|
||||
if ($t === 'body_only' || $t === 'body') {
|
||||
return '正文查重';
|
||||
}
|
||||
return '全文查重';
|
||||
}
|
||||
|
||||
private function stateLabel($state)
|
||||
{
|
||||
$map = [
|
||||
|
||||
92
application/api/controller/UserFieldAi.php
Normal file
92
application/api/controller/UserFieldAi.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
|
||||
use think\Db;
|
||||
use think\Validate;
|
||||
use app\common\UserFieldAiService;
|
||||
|
||||
/**
|
||||
* 用户主领域 AI 总结(写入 t_user_reviewer_info.field_ai)。
|
||||
*
|
||||
* POST startChain 启动链式队列(扫描全部符合条件的用户)
|
||||
* POST processOne 同步处理单个 user_id(调试)
|
||||
* GET preview 预览某用户是否 eligible 及上下文摘要
|
||||
*/
|
||||
class UserFieldAi extends Base
|
||||
{
|
||||
/**
|
||||
* 启动链式处理。需 worker: php think queue:work --queue UserFieldAi
|
||||
*/
|
||||
public function startChain()
|
||||
{
|
||||
$force = intval($this->request->param('force', 0)) === 1;
|
||||
$delay = max(0, intval($this->request->param('delay', 1)));
|
||||
|
||||
$svc = new UserFieldAiService();
|
||||
$started = $svc->startChain($force, $delay);
|
||||
|
||||
return jsonSuccess([
|
||||
'started' => $started,
|
||||
'queue' => UserFieldAiService::QUEUE_NAME,
|
||||
'force' => $force,
|
||||
'msg' => $started ? 'chain enqueued' : 'no pending users',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步处理单个用户(不调队列)。
|
||||
*/
|
||||
public function processOne()
|
||||
{
|
||||
$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 UserFieldAiService();
|
||||
$result = $svc->processUser($userId, $force);
|
||||
if (empty($result['ok'])) {
|
||||
return jsonError(isset($result['error']) ? $result['error'] : 'failed');
|
||||
}
|
||||
return jsonSuccess($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览:是否满足条件、当前 field_ai 状态。
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
$userId = intval($this->request->param('user_id', 0));
|
||||
if ($userId <= 0) {
|
||||
return jsonError('user_id required');
|
||||
}
|
||||
|
||||
$svc = new UserFieldAiService();
|
||||
$svc->ensureReviewerInfoRow($userId);
|
||||
$uri = Db::name('user_reviewer_info')->where('reviewer_id', $userId)->find();
|
||||
|
||||
return jsonSuccess([
|
||||
'user_id' => $userId,
|
||||
'has_articles' => $svc->hasSubmittedArticles($userId),
|
||||
'profile_complete' => $svc->isReviewerProfileComplete($uri),
|
||||
'eligible' => $svc->isEligible($userId, $uri),
|
||||
'field_ai' => $uri ? (string) $uri['field_ai'] : '',
|
||||
'field_ai_status' => $uri ? intval($uri['field_ai_status']) : 0,
|
||||
'field_ai_utime' => $uri ? intval($uri['field_ai_utime']) : 0,
|
||||
'field_ai_status_text' => $this->statusLabel($uri ? intval($uri['field_ai_status']) : 0),
|
||||
]);
|
||||
}
|
||||
|
||||
private function statusLabel($status)
|
||||
{
|
||||
$map = [
|
||||
UserFieldAiService::STATUS_PENDING => 'pending',
|
||||
UserFieldAiService::STATUS_DONE => 'done',
|
||||
UserFieldAiService::STATUS_INSUFFICIENT => 'insufficient',
|
||||
UserFieldAiService::STATUS_FAILED => 'failed',
|
||||
];
|
||||
return isset($map[$status]) ? $map[$status] : 'unknown';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user