修改自动推广的相关任务
This commit is contained in:
217
application/api/controller/Plagiarism.php
Normal file
217
application/api/controller/Plagiarism.php
Normal 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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user