修改自动推广的相关任务

This commit is contained in:
wangjinlei
2026-05-07 11:45:55 +08:00
parent a68742d2c2
commit b1e978ed73
7 changed files with 1221 additions and 0 deletions

View File

@@ -0,0 +1,217 @@
<?php
namespace app\api\controller;
use think\Db;
use think\Response;
use app\common\PlagiarismService;
/**
* 论文查重Turnitin / Crossref Similarity Check控制器。
*
* 触发方式:纯手工(编辑后台点"查重"按钮)。
* 报告策略:在线 viewer URL 临时签名 + PDF 永久落盘 runtime/plagiarism/。
*
* 主要接口:
* POST submit 触发查重
* GET getStatus 轮询单条查重状态(前端 ajax
* GET getList 列出某 article 的全部查重记录
* GET getReportUrl 获取/刷新在线查看 URL
* GET downloadReport 下载本地 PDF
* POST retry 重新触发(创建新行)
* GET features 探活(开发调试用)
*/
class Plagiarism extends Base
{
public function __construct(\think\Request $request = null)
{
parent::__construct($request);
}
/**
* 触发查重
*
* 入参:
* article_id 必填
* file_url 选填;不传则按 article_id 在 t_article_file 找 manuscirpt
* editor_id 选填;触发人 user_id前端拿不到也可以传 0
*/
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));
if ($articleId <= 0) {
return jsonError('article_id required');
}
try {
$svc = new PlagiarismService();
$localPath = $fileUrl !== ''
? $svc->resolveFileUrlToLocal($fileUrl)
: $svc->locateArticleManuscript($articleId);
$checkId = $svc->submit($articleId, $localPath, $editorId, 'manual');
return jsonSuccess(['check_id' => $checkId]);
} catch (\Throwable $e) {
return jsonError($e->getMessage());
}
}
/**
* 重试 = 提交一次新查重(保留历史)
*/
public function retry()
{
return $this->submit();
}
/**
* 取单条查重状态
*/
public function getStatus()
{
$checkId = intval($this->request->param('check_id', 0));
if ($checkId <= 0) {
return jsonError('check_id required');
}
$row = Db::name('plagiarism_check')->where('check_id', $checkId)->find();
if (!$row) {
return jsonError('not found');
}
return jsonSuccess($this->formatRow($row));
}
/**
* 列出某 article 的全部查重记录(按时间倒序)
*/
public function getList()
{
$articleId = intval($this->request->param('article_id', 0));
if ($articleId <= 0) {
return jsonError('article_id required');
}
$rows = Db::name('plagiarism_check')
->where('article_id', $articleId)
->order('check_id desc')
->select();
$out = [];
foreach ($rows as $r) {
$out[] = $this->formatRow($r);
}
return jsonSuccess(['list' => $out]);
}
/**
* 取在线查看 URL过期则自动刷新
*/
public function getReportUrl()
{
$checkId = intval($this->request->param('check_id', 0));
if ($checkId <= 0) {
return jsonError('check_id required');
}
try {
$row = Db::name('plagiarism_check')->where('check_id', $checkId)->find();
if (!$row) {
return jsonError('not found');
}
if ($row['state'] != 3) {
return jsonError('check not completed yet, state=' . $row['state']);
}
$needRefresh = empty($row['view_only_url'])
|| intval($row['view_only_url_expire']) < time() + 60;
if ($needRefresh) {
$svc = new PlagiarismService();
$info = $svc->refreshViewerUrlFor($checkId);
return jsonSuccess([
'view_only_url' => $info['url'],
'expire' => $info['expire'],
]);
}
return jsonSuccess([
'view_only_url' => $row['view_only_url'],
'expire' => intval($row['view_only_url_expire']),
]);
} catch (\Throwable $e) {
return jsonError($e->getMessage());
}
}
/**
* 直接吐 PDF 二进制流给浏览器下载
*/
public function downloadReport()
{
$checkId = intval($this->request->param('check_id', 0));
if ($checkId <= 0) {
return jsonError('check_id required');
}
$row = Db::name('plagiarism_check')->where('check_id', $checkId)->find();
if (!$row || empty($row['pdf_local_path'])) {
return jsonError('report not available');
}
$rootDir = ROOT_PATH ?: dirname(dirname(dirname(__DIR__)));
$abs = rtrim($rootDir, '/\\') . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $row['pdf_local_path']);
if (!is_file($abs)) {
return jsonError('pdf file missing on disk: ' . $row['pdf_local_path']);
}
$filename = sprintf('plagiarism_check_%d_article_%d.pdf', $row['check_id'], $row['article_id']);
return Response::create(file_get_contents($abs), 'html', 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
'Content-Length' => (string)filesize($abs),
]);
}
/**
* Turnitin 探活(开发调试用)
*/
public function features()
{
try {
$tii = new \app\common\TurnitinService();
return jsonSuccess($tii->featuresEnabled());
} catch (\Throwable $e) {
return jsonError($e->getMessage());
}
}
// ---------- 内部 ----------
private function formatRow($r)
{
return [
'check_id' => intval($r['check_id']),
'article_id' => intval($r['article_id']),
'journal_id' => intval($r['journal_id']),
'state' => intval($r['state']),
'state_label' => $this->stateLabel($r['state']),
'similarity_score' => floatval($r['similarity_score']),
'tii_report_status' => (string)$r['tii_report_status'],
'has_pdf' => !empty($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'],
'trigger_source' => (string)$r['trigger_source'],
'triggered_by' => intval($r['triggered_by']),
'ctime' => intval($r['ctime']),
'utime' => intval($r['utime']),
];
}
private function stateLabel($state)
{
$map = [
0 => '待上传',
1 => '上传中',
2 => '比对中',
3 => '完成',
4 => '失败',
];
return isset($map[$state]) ? $map[$state] : 'unknown';
}
}