Merge remote-tracking branch 'remotes/origin/checkrefer'
This commit is contained in:
@@ -271,6 +271,14 @@ class Base extends Controller
|
|||||||
}
|
}
|
||||||
$this->production_article_refer_obj->where('p_article_id', $refer_info['p_article_id'])->where('index', ">", $refer_info['index'])->where('state', 0)->setDec('index');
|
$this->production_article_refer_obj->where('p_article_id', $refer_info['p_article_id'])->where('index', ">", $refer_info['index'])->where('state', 0)->setDec('index');
|
||||||
$this->production_article_refer_obj->where('p_refer_id', $p_refer_id)->update(['state' => 1]);
|
$this->production_article_refer_obj->where('p_refer_id', $p_refer_id)->update(['state' => 1]);
|
||||||
|
|
||||||
|
// 文献集合已变更,原校对结果的 reference_no 已全部错位,整篇标记为未校对
|
||||||
|
try {
|
||||||
|
(new \app\common\ReferenceCheckService())
|
||||||
|
->clearArticleChecksByPArticleId(intval($refer_info['p_article_id']));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\think\Log::error('delOneRefer clearArticleChecksByPArticleId p_refer_id=' . $p_refer_id . ' ' . $e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use think\Env;
|
|||||||
use think\Queue;
|
use think\Queue;
|
||||||
use think\Validate;
|
use think\Validate;
|
||||||
use app\common\CrossrefService;
|
use app\common\CrossrefService;
|
||||||
|
use app\common\ReferenceCheckService;
|
||||||
|
|
||||||
class Preaccept extends Base
|
class Preaccept extends Base
|
||||||
{
|
{
|
||||||
@@ -15,6 +16,26 @@ class Preaccept extends Base
|
|||||||
parent::__construct($request);
|
parent::__construct($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增/修改导致文献集合改变后,清空整篇校对明细,使文章状态回到"未校对"。
|
||||||
|
* 失败仅记日志,不阻塞主流程。
|
||||||
|
*/
|
||||||
|
private function resetArticleChecksOnReferChange($pArticleId, $sourceTag = '')
|
||||||
|
{
|
||||||
|
$pArticleId = intval($pArticleId);
|
||||||
|
if ($pArticleId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
(new ReferenceCheckService())->clearArticleChecksByPArticleId($pArticleId);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\think\Log::error(
|
||||||
|
'resetArticleChecksOnReferChange[' . $sourceTag . '] p_article_id='
|
||||||
|
. $pArticleId . ' ' . $e->getMessage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**获取文章参考文献列表
|
/**获取文章参考文献列表
|
||||||
* @return \think\response\Json
|
* @return \think\response\Json
|
||||||
@@ -92,6 +113,7 @@ class Preaccept extends Base
|
|||||||
return jsonError($rule->getError());
|
return jsonError($rule->getError());
|
||||||
}
|
}
|
||||||
$this->production_article_refer_obj->where('p_article_id',$data['p_article_id'])->update(["state"=>1]);
|
$this->production_article_refer_obj->where('p_article_id',$data['p_article_id'])->update(["state"=>1]);
|
||||||
|
$this->resetArticleChecksOnReferChange(intval($data['p_article_id']), 'discardRefersByParticleid');
|
||||||
return jsonSuccess([]);
|
return jsonSuccess([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +164,7 @@ class Preaccept extends Base
|
|||||||
}
|
}
|
||||||
$adId= $this->production_article_refer_obj->insertGetId($insert);
|
$adId= $this->production_article_refer_obj->insertGetId($insert);
|
||||||
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
||||||
|
$this->resetArticleChecksOnReferChange(intval($p_info['p_article_id']), 'addRefer');
|
||||||
return jsonSuccess([]);
|
return jsonSuccess([]);
|
||||||
|
|
||||||
|
|
||||||
@@ -198,6 +221,7 @@ class Preaccept extends Base
|
|||||||
}
|
}
|
||||||
$adId= $this->production_article_refer_obj->insertGetId($insert);
|
$adId= $this->production_article_refer_obj->insertGetId($insert);
|
||||||
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
||||||
|
$this->resetArticleChecksOnReferChange(intval($p_info['p_article_id']), 'addReferByParticleid');
|
||||||
return jsonSuccess([]);
|
return jsonSuccess([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +257,7 @@ class Preaccept extends Base
|
|||||||
$insert['cs'] = 1;
|
$insert['cs'] = 1;
|
||||||
$adId = $this->production_article_refer_obj->insertGetId($insert);
|
$adId = $this->production_article_refer_obj->insertGetId($insert);
|
||||||
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
$this->production_article_refer_obj->where('p_article_id', $p_info['p_article_id'])->where("p_refer_id", "<>", $adId)->where("index", ">", $pre_refer['index'])->where('state', 0)->setInc('index');
|
||||||
|
$this->resetArticleChecksOnReferChange(intval($p_info['p_article_id']), 'addReferNotdoi');
|
||||||
return jsonSuccess([]);
|
return jsonSuccess([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +487,17 @@ class Preaccept extends Base
|
|||||||
// }
|
// }
|
||||||
// $this->production_article_refer_obj->where('p_refer_id', $data['p_refer_id'])->update(['refer_doi' => $data['doi']]);
|
// $this->production_article_refer_obj->where('p_refer_id', $data['p_refer_id'])->update(['refer_doi' => $data['doi']]);
|
||||||
// my_doiToFrag2($this->production_article_refer_obj->where('p_refer_id', $data['p_refer_id'])->find());
|
// my_doiToFrag2($this->production_article_refer_obj->where('p_refer_id', $data['p_refer_id'])->find());
|
||||||
|
|
||||||
|
//文献内容更新成功后异步重检该文献对应的全部校对明细(失败不阻塞主流程)
|
||||||
|
try {
|
||||||
|
(new ReferenceCheckService())->enqueueRecheckByPReferId(
|
||||||
|
intval($data['p_refer_id']),
|
||||||
|
intval($old_refer_info['p_article_id'])
|
||||||
|
);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\think\Log::error('editRefer enqueueRecheckByPReferId p_refer_id=' . $data['p_refer_id'] . ' ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
return jsonSuccess([]);
|
return jsonSuccess([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1453,6 +1489,7 @@ class Preaccept extends Base
|
|||||||
return jsonError($rule->getError());
|
return jsonError($rule->getError());
|
||||||
}
|
}
|
||||||
$refer_info = $this->production_article_refer_obj->where('p_refer_id', $data['p_refer_id'])->find();
|
$refer_info = $this->production_article_refer_obj->where('p_refer_id', $data['p_refer_id'])->find();
|
||||||
|
$sibling_p_refer_id = 0;
|
||||||
if ($data['act'] == "up") {
|
if ($data['act'] == "up") {
|
||||||
$up_info = $this->production_article_refer_obj->where('p_article_id', $refer_info['p_article_id'])->where('index', $refer_info['index'] - 1)->where('state', 0)->find();
|
$up_info = $this->production_article_refer_obj->where('p_article_id', $refer_info['p_article_id'])->where('index', $refer_info['index'] - 1)->where('state', 0)->find();
|
||||||
if (!$up_info) {
|
if (!$up_info) {
|
||||||
@@ -1460,6 +1497,7 @@ class Preaccept extends Base
|
|||||||
}
|
}
|
||||||
$this->production_article_refer_obj->where('p_refer_id', $up_info['p_refer_id'])->setInc("index");
|
$this->production_article_refer_obj->where('p_refer_id', $up_info['p_refer_id'])->setInc("index");
|
||||||
$this->production_article_refer_obj->where('p_refer_id', $refer_info['p_refer_id'])->setDec("index");
|
$this->production_article_refer_obj->where('p_refer_id', $refer_info['p_refer_id'])->setDec("index");
|
||||||
|
$sibling_p_refer_id = intval($up_info['p_refer_id']);
|
||||||
} else {
|
} else {
|
||||||
$down_info = $this->production_article_refer_obj->where('p_article_id', $refer_info['p_article_id'])->where('index', $refer_info['index'] + 1)->where('state', 0)->find();
|
$down_info = $this->production_article_refer_obj->where('p_article_id', $refer_info['p_article_id'])->where('index', $refer_info['index'] + 1)->where('state', 0)->find();
|
||||||
if (!$down_info) {
|
if (!$down_info) {
|
||||||
@@ -1467,7 +1505,19 @@ class Preaccept extends Base
|
|||||||
}
|
}
|
||||||
$this->production_article_refer_obj->where('p_refer_id', $refer_info['p_refer_id'])->setInc("index");
|
$this->production_article_refer_obj->where('p_refer_id', $refer_info['p_refer_id'])->setInc("index");
|
||||||
$this->production_article_refer_obj->where('p_refer_id', $down_info['p_refer_id'])->setDec("index");
|
$this->production_article_refer_obj->where('p_refer_id', $down_info['p_refer_id'])->setDec("index");
|
||||||
|
$sibling_p_refer_id = intval($down_info['p_refer_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 仅同步本次交换的两条 p_refer_id 对应的校对明细 reference_no / refer_index
|
||||||
|
try {
|
||||||
|
(new ReferenceCheckService())->syncReferenceNoByPReferIds(
|
||||||
|
[intval($refer_info['p_refer_id']), $sibling_p_refer_id],
|
||||||
|
intval($refer_info['p_article_id'])
|
||||||
|
);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\think\Log::error('sortRefer syncReferenceNoByPReferIds: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
return jsonSuccess([]);
|
return jsonSuccess([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1308,4 +1308,231 @@ class References extends Base
|
|||||||
}
|
}
|
||||||
return json_encode(['status' => 8,'msg' => 'fail']);
|
return json_encode(['status' => 8,'msg' => 'fail']);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 参考文献第一次校对
|
||||||
|
* @return \think\response\Json
|
||||||
|
*/
|
||||||
|
public function allReferenceCheckAI(){
|
||||||
|
//获取参数
|
||||||
|
$aParam = empty($aParam) ? $this->request->post() : $aParam;
|
||||||
|
|
||||||
|
//必填值验证
|
||||||
|
$iPArticleId = empty($aParam['p_article_id']) ? '' : $aParam['p_article_id'];
|
||||||
|
if(empty($iPArticleId)){
|
||||||
|
return json_encode(array('status' => 2,'msg' => 'Please select an article' ));
|
||||||
|
}
|
||||||
|
//查询文章(p_article_id 与 article_id 都要带,下游服务方法两者都用)
|
||||||
|
$aWhere = ['p_article_id' => $iPArticleId,'state' => ['in',[0,2]]];
|
||||||
|
$aProductionArticle = Db::name('production_article')->field('p_article_id,article_id')->where($aWhere)->find();
|
||||||
|
if(empty($aProductionArticle)){
|
||||||
|
return json_encode(array('status' => 3,'msg' => 'No articles found' ));
|
||||||
|
}
|
||||||
|
if($this->checkReferStatus($iPArticleId)==0){
|
||||||
|
return jsonError('请修正完文献内容再进行校对。');
|
||||||
|
}
|
||||||
|
//已存在校对记录则禁止重复执行第一次校对,提示走重置接口
|
||||||
|
$iExisting = Db::name('article_reference_check_result')
|
||||||
|
->where('p_article_id', $iPArticleId)
|
||||||
|
->count();
|
||||||
|
if(intval($iExisting) > 0){
|
||||||
|
return jsonError('该文章已存在校对记录,请使用"重置校对"接口重新校对。');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$svc = new ReferenceCheckService();
|
||||||
|
$result = $svc->enqueueByPArticle($aProductionArticle);
|
||||||
|
return jsonSuccess($result);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return jsonError($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 文献校对重置:删除该文章已有的全部校对明细,并重新入队整篇校对
|
||||||
|
* POST/GET: article_id(必填)
|
||||||
|
* @url /api/Article/referenceCheckReset
|
||||||
|
*/
|
||||||
|
public function referenceCheckResetAI()
|
||||||
|
{
|
||||||
|
//获取参数
|
||||||
|
$aParam = empty($aParam) ? $this->request->post() : $aParam;
|
||||||
|
|
||||||
|
//必填值验证
|
||||||
|
$iPArticleId = empty($aParam['p_article_id']) ? '' : $aParam['p_article_id'];
|
||||||
|
if(empty($iPArticleId)){
|
||||||
|
return json_encode(array('status' => 2,'msg' => 'Please select an article' ));
|
||||||
|
}
|
||||||
|
//查询文章(p_article_id 与 article_id 都要带,下游服务方法两者都用)
|
||||||
|
$aWhere = ['p_article_id' => $iPArticleId,'state' => ['in',[0,2]]];
|
||||||
|
$aProductionArticle = Db::name('production_article')->field('p_article_id,article_id')->where($aWhere)->find();
|
||||||
|
if(empty($aProductionArticle)){
|
||||||
|
return json_encode(array('status' => 3,'msg' => 'No articles found' ));
|
||||||
|
}
|
||||||
|
if($this->checkReferStatus($iPArticleId)==0){
|
||||||
|
return jsonError('请修正完文献内容再进行校对。');
|
||||||
|
}
|
||||||
|
$iArticleId = empty($aProductionArticle['article_id']) ? 0 : $aProductionArticle['article_id'];
|
||||||
|
if(empty($iArticleId)){
|
||||||
|
return json_encode(array('status' => 4,'msg' => 'Unbound article' ));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$result = (new ReferenceCheckService())->resetAndRecheckByArticle($aProductionArticle);
|
||||||
|
return jsonSuccess($result);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return jsonError($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空某篇文章下的全部参考文献校对记录(不重新入队)
|
||||||
|
*
|
||||||
|
* 与 referenceCheckResetAI 的区别:reset 是「清空 + 重新校对」,
|
||||||
|
* 这里只做「清空」一步,校对状态回到未校对,等待用户手动再触发。
|
||||||
|
*
|
||||||
|
* POST/GET: p_article_id(必填)
|
||||||
|
*/
|
||||||
|
public function referenceCheckClearAI()
|
||||||
|
{
|
||||||
|
$aParam = $this->request->post();
|
||||||
|
if (empty($aParam)) {
|
||||||
|
$aParam = $this->request->param();
|
||||||
|
}
|
||||||
|
|
||||||
|
$iPArticleId = empty($aParam['p_article_id']) ? 0 : intval($aParam['p_article_id']);
|
||||||
|
if ($iPArticleId <= 0) {
|
||||||
|
return json_encode(array('status' => 2, 'msg' => 'Please select an article'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验文章存在(与其它校对接口口径一致:state in [0,2])
|
||||||
|
$aProductionArticle = Db::name('production_article')
|
||||||
|
->field('p_article_id,article_id')
|
||||||
|
->where(['p_article_id' => $iPArticleId, 'state' => ['in', [0, 2]]])
|
||||||
|
->find();
|
||||||
|
if (empty($aProductionArticle)) {
|
||||||
|
return json_encode(array('status' => 3, 'msg' => 'No articles found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$deleted = (new ReferenceCheckService())->clearArticleChecksByPArticleId($iPArticleId);
|
||||||
|
return jsonSuccess([
|
||||||
|
'p_article_id' => $iPArticleId,
|
||||||
|
'deleted' => intval($deleted),
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return jsonError($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 p_article_id 查整篇引用校对进度(按 reference_no 分组聚合)
|
||||||
|
*
|
||||||
|
* POST/GET: p_article_id(必填)
|
||||||
|
*
|
||||||
|
* 返回 list 中每项含:reference_no、p_refer_id、status(数值)、
|
||||||
|
* total、pending、done、failed、pass、is_pass、last_updated_at、records
|
||||||
|
*
|
||||||
|
* status 数值含义:
|
||||||
|
* 0 = 待校验 1 = 校对中 2 = 校对完成 3 = 校对失败
|
||||||
|
*/
|
||||||
|
public function referenceCheckProgressAI()
|
||||||
|
{
|
||||||
|
$aParam = $this->request->post();
|
||||||
|
if (empty($aParam)) {
|
||||||
|
$aParam = $this->request->param();
|
||||||
|
}
|
||||||
|
|
||||||
|
$iPArticleId = empty($aParam['p_article_id']) ? 0 : intval($aParam['p_article_id']);
|
||||||
|
if ($iPArticleId <= 0) {
|
||||||
|
return json_encode(array('status' => 2, 'msg' => 'Please select an article'));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$result = (new ReferenceCheckService())->getProgressByPArticleId($iPArticleId);
|
||||||
|
return jsonSuccess($result);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return jsonError($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 p_article_id 查整篇文章引用校对总状态(用于前端按钮分流)
|
||||||
|
*
|
||||||
|
* POST/GET: p_article_id(必填)
|
||||||
|
*
|
||||||
|
* 计数维度是「参考文献」(按 reference_no 分组),不是单条校对明细行。
|
||||||
|
* 例:50 条参考文献、底层 111 条校对明细时,total = 50。
|
||||||
|
*
|
||||||
|
* 返回 status 数值含义(整篇):
|
||||||
|
* 0 = 未校对(一条记录都没有)
|
||||||
|
* 1 = 校对中(至少 1 条参考文献仍有未跑完的明细)
|
||||||
|
* 2 = 校对完成(所有参考文献全部明细已结束)
|
||||||
|
*
|
||||||
|
* 返回字段:p_article_id、status、total、pending、done、failed、progress_percent
|
||||||
|
* total —— 参考文献条数
|
||||||
|
* pending —— 该条参考文献仍有未跑完明细的数量(含"部分跑完")
|
||||||
|
* done —— 该条参考文献所有明细都 status=1 的数量
|
||||||
|
* failed —— 该条参考文献全部跑完且至少 1 条 status=2 的数量
|
||||||
|
* pending + done + failed = total;progress_percent = (done+failed)/total
|
||||||
|
*
|
||||||
|
* 分组明细请走 referenceCheckProgressAI。
|
||||||
|
*/
|
||||||
|
public function referenceCheckArticleStatusAI()
|
||||||
|
{
|
||||||
|
$aParam = $this->request->post();
|
||||||
|
if (empty($aParam)) {
|
||||||
|
$aParam = $this->request->param();
|
||||||
|
}
|
||||||
|
|
||||||
|
$iPArticleId = empty($aParam['p_article_id']) ? 0 : intval($aParam['p_article_id']);
|
||||||
|
if ($iPArticleId <= 0) {
|
||||||
|
return json_encode(array('status' => 2, 'msg' => 'Please select an article'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = (new ReferenceCheckService())->getArticleProgressStatusByPArticleId($iPArticleId);
|
||||||
|
return jsonSuccess($result);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return jsonError($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 p_refer_id 查单条参考文献的校对明细
|
||||||
|
*
|
||||||
|
* POST/GET: p_refer_id(必填)
|
||||||
|
*
|
||||||
|
* 返回 list 中每项含:am_id、confidence、reason、is_match、is_pass
|
||||||
|
* 同时附带上下文:p_refer_id、p_article_id、reference_no、total
|
||||||
|
*/
|
||||||
|
public function referenceCheckDetailsAI()
|
||||||
|
{
|
||||||
|
$aParam = $this->request->post();
|
||||||
|
if (empty($aParam)) {
|
||||||
|
$aParam = $this->request->param();
|
||||||
|
}
|
||||||
|
|
||||||
|
$iPReferId = empty($aParam['p_refer_id']) ? 0 : intval($aParam['p_refer_id']);
|
||||||
|
if ($iPReferId <= 0) {
|
||||||
|
return json_encode(array('status' => 2, 'msg' => 'Please select a reference'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = (new ReferenceCheckService())->getCheckDetailsByPReferId($iPReferId);
|
||||||
|
return jsonSuccess($result);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return jsonError($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkReferStatus($p_article_id){
|
||||||
|
$list = $this->production_article_refer_obj->where('p_article_id', $p_article_id)->where('state', 0)->select();
|
||||||
|
if (!$list) {
|
||||||
|
return jsonError('references error');
|
||||||
|
}
|
||||||
|
$frag = 1;
|
||||||
|
foreach ($list as $v) {
|
||||||
|
if ($v['cs'] == 0) {
|
||||||
|
$frag = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $frag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
114
application/api/job/ReferenceCheck.php
Normal file
114
application/api/job/ReferenceCheck.php
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
namespace app\api\job;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\queue\Job;
|
||||||
|
use app\common\QueueJob;
|
||||||
|
use app\common\QueueRedis;
|
||||||
|
use app\common\ReferenceCheckService;
|
||||||
|
|
||||||
|
class ReferenceCheck
|
||||||
|
{
|
||||||
|
private $oQueueJob;
|
||||||
|
private $QueueRedis;
|
||||||
|
private $completedExprie = 3600;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->oQueueJob = new QueueJob();
|
||||||
|
$this->QueueRedis = QueueRedis::getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fire(Job $job, $data)
|
||||||
|
{
|
||||||
|
$this->oQueueJob->init($job);
|
||||||
|
|
||||||
|
$rawBody = empty($job->getRawBody()) ? '' : $job->getRawBody();
|
||||||
|
$jobData = empty($rawBody) ? [] : json_decode($rawBody, true);
|
||||||
|
$jobId = empty($jobData['id']) ? 'unknown' : $jobData['id'];
|
||||||
|
|
||||||
|
$sRedisKey = '';
|
||||||
|
$sRedisValue = '';
|
||||||
|
|
||||||
|
$this->oQueueJob->log("-----------队列任务开始-----------");
|
||||||
|
$this->oQueueJob->log("当前任务ID: {$jobId}, 尝试次数: {$job->attempts()}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$checkId = intval(isset($data['check_id']) ? $data['check_id'] : 0);
|
||||||
|
if ($checkId <= 0 && !empty($jobData['data']['check_id'])) {
|
||||||
|
$checkId = intval($jobData['data']['check_id']);
|
||||||
|
}
|
||||||
|
if ($checkId <= 0) {
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = Db::name('article_reference_check_result')->where('id', $checkId)->find();
|
||||||
|
if (empty($row)) {
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intval($row['status']) === 1) {
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sClassName = get_class($this);
|
||||||
|
$sRedisKey = "queue_job:{$sClassName}:{$checkId}";
|
||||||
|
$sRedisValue = uniqid() . '_' . getmypid();
|
||||||
|
|
||||||
|
$svc = new ReferenceCheckService();
|
||||||
|
$svc->clearReferenceCheckQueueLock($checkId);
|
||||||
|
|
||||||
|
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$svc->runReferenceCheckOnce($checkId);
|
||||||
|
|
||||||
|
$amId = intval(isset($row['am_id']) ? $row['am_id'] : 0);
|
||||||
|
if ($amId > 0) {
|
||||||
|
$svc->syncAmRefCheckStatus($amId);
|
||||||
|
}
|
||||||
|
$this->QueueRedis->finishJob($sRedisKey, 'completed', $this->completedExprie, $sRedisValue);
|
||||||
|
$job->delete();
|
||||||
|
$this->oQueueJob->log("任务执行成功 | 日志ID: {$sRedisKey}");
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->oQueueJob->log('ReferenceCheck error: ' . $e->getMessage());
|
||||||
|
if ($job->attempts() >= 3) {
|
||||||
|
$this->markFailed($checkId, $e->getMessage());
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$job->release(30);
|
||||||
|
}
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
$this->oQueueJob->handleRetryableException($e, $sRedisKey, $sRedisValue, $job);
|
||||||
|
} catch (\LogicException $e) {
|
||||||
|
$this->oQueueJob->handleNonRetryableException($e, $sRedisKey, $sRedisValue, $job);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->oQueueJob->handleRetryableException($e, $sRedisKey, $sRedisValue, $job);
|
||||||
|
} finally {
|
||||||
|
$this->oQueueJob->finnal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function markFailed($checkId, $msg)
|
||||||
|
{
|
||||||
|
$row = Db::name('article_reference_check_result')->where('id', $checkId)->find();
|
||||||
|
try {
|
||||||
|
(new ReferenceCheckService())->updateCheckResult($checkId, [
|
||||||
|
'status' => 2,
|
||||||
|
'error_msg' => $msg,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\think\Log::error('ReferenceCheck markFailed: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
$amId = empty($row) ? 0 : intval(isset($row['am_id']) ? $row['am_id'] : 0);
|
||||||
|
if ($amId > 0) {
|
||||||
|
(new ReferenceCheckService())->syncAmRefCheckStatus($amId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
162
application/api/job/ReferenceCheckTwo.php
Normal file
162
application/api/job/ReferenceCheckTwo.php
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
namespace app\api\job;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\queue\Job;
|
||||||
|
use app\common\QueueJob;
|
||||||
|
use app\common\QueueRedis;
|
||||||
|
use app\common\ReferenceCheckService;
|
||||||
|
use app\common\service\LLMService;
|
||||||
|
|
||||||
|
class ReferenceCheckTwo
|
||||||
|
{
|
||||||
|
private $oQueueJob;
|
||||||
|
private $QueueRedis;
|
||||||
|
private $completedExprie = 3600;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->oQueueJob = new QueueJob();
|
||||||
|
$this->QueueRedis = QueueRedis::getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fire(Job $job, $data)
|
||||||
|
{
|
||||||
|
$this->oQueueJob->init($job);
|
||||||
|
|
||||||
|
$rawBody = empty($job->getRawBody()) ? '' : $job->getRawBody();
|
||||||
|
$jobData = empty($rawBody) ? [] : json_decode($rawBody, true);
|
||||||
|
$jobId = empty($jobData['id']) ? 'unknown' : $jobData['id'];
|
||||||
|
|
||||||
|
$sRedisKey = '';
|
||||||
|
$sRedisValue = '';
|
||||||
|
|
||||||
|
$this->oQueueJob->log("-----------队列任务开始-----------");
|
||||||
|
$this->oQueueJob->log("当前任务ID: {$jobId}, 尝试次数: {$job->attempts()}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$checkId = intval(isset($data['check_id']) ? $data['check_id'] : 0);
|
||||||
|
if ($checkId <= 0 && !empty($jobData['data']['check_id'])) {
|
||||||
|
$checkId = intval($jobData['data']['check_id']);
|
||||||
|
}
|
||||||
|
$sClassName = get_class($this);
|
||||||
|
$sRedisKey = "queue_job_two:{$sClassName}:{$checkId}";
|
||||||
|
$sRedisValue = uniqid() . '_' . getmypid();
|
||||||
|
|
||||||
|
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($checkId <= 0) {
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = Db::name('article_reference_check_result')->where('id', $checkId)->find();
|
||||||
|
if (empty($row)) {
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (intval($row['status']) === 1) {
|
||||||
|
// $job->delete();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
try {
|
||||||
|
$svc = new ReferenceCheckService();
|
||||||
|
|
||||||
|
$contentA = $svc->resolveMainContentForJob($row);
|
||||||
|
$referText = trim((string)(isset($row['refer_text']) ? $row['refer_text'] : ''));
|
||||||
|
$refer = null;
|
||||||
|
|
||||||
|
if (intval($row['p_refer_id']) > 0) {
|
||||||
|
$refer = Db::name('production_article_refer')
|
||||||
|
->where('p_refer_id', intval($row['p_refer_id']))
|
||||||
|
->where('state', 0)
|
||||||
|
->find();
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $svc->prepareRecheckPayload(is_array($refer) ? $refer : [], $referText);
|
||||||
|
$doiBlock = $payload['doi_block'];
|
||||||
|
|
||||||
|
if ($contentA === '' || $referText === '') {
|
||||||
|
$this->markFailed($checkId, 'Missing article_main.content or refer_text');
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$llm = new LLMService();
|
||||||
|
$llmResult = $llm->checkReference($contentA, $referText, true, $doiBlock);
|
||||||
|
|
||||||
|
$requestFailed = !empty($llmResult['request_failed']);
|
||||||
|
$canSupport = $svc->parseLlmCanSupport($llmResult);
|
||||||
|
$tag = $payload['has_abstract']
|
||||||
|
? ('[Crossref复核' . ($payload['doi_used'] !== '' ? ' ' . $payload['doi_used'] : '') . ']')
|
||||||
|
: '[Crossref复核-无摘要]';
|
||||||
|
$reason = $tag . ' ' . (isset($llmResult['reason']) ? $llmResult['reason'] : '');
|
||||||
|
|
||||||
|
// LLM 通讯失败:写 status=2 并抛异常触发队列重试
|
||||||
|
if ($requestFailed) {
|
||||||
|
$svc->updateCheckResult($checkId, [
|
||||||
|
'confidence' => floatval($llmResult['confidence']),
|
||||||
|
'reason' => $reason,
|
||||||
|
'status' => 2,
|
||||||
|
'error_msg' => isset($llmResult['reason']) ? $llmResult['reason'] : 'LLM request failed',
|
||||||
|
]);
|
||||||
|
throw new \RuntimeException(isset($llmResult['reason']) ? $llmResult['reason'] : 'LLM request failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$affected = $svc->updateCheckResult($checkId, [
|
||||||
|
'can_support' => $canSupport ? 1 : 0,
|
||||||
|
'is_match' => $canSupport ? 1 : 0,
|
||||||
|
'confidence' => floatval($llmResult['confidence']),
|
||||||
|
'reason' => $reason,
|
||||||
|
'status' => 1,
|
||||||
|
'error_msg' => '',
|
||||||
|
]);
|
||||||
|
$this->oQueueJob->log("Crossref复核写入 id={$checkId} affected={$affected} can_support=" . ($canSupport ? 1 : 0) . " confidence=" . floatval($llmResult['confidence']));
|
||||||
|
|
||||||
|
$amId = intval(isset($row['am_id']) ? $row['am_id'] : 0);
|
||||||
|
if ($amId > 0) {
|
||||||
|
$svc->syncAmRefCheckStatus($amId);
|
||||||
|
}
|
||||||
|
$this->QueueRedis->finishJob($sRedisKey, 'completed', $this->completedExprie, $sRedisValue);
|
||||||
|
$job->delete();
|
||||||
|
$this->oQueueJob->log("任务执行成功 | 日志ID: {$sRedisKey}");
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->oQueueJob->log('ReferenceCheckTwo error: ' . $e->getMessage());
|
||||||
|
if ($job->attempts() >= 3) {
|
||||||
|
$this->markFailed($checkId, $e->getMessage());
|
||||||
|
$job->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$job->release(30);
|
||||||
|
}
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
$this->oQueueJob->handleRetryableException($e, $sRedisKey, $sRedisValue, $job);
|
||||||
|
} catch (\LogicException $e) {
|
||||||
|
$this->oQueueJob->handleNonRetryableException($e, $sRedisKey, $sRedisValue, $job);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->oQueueJob->handleRetryableException($e, $sRedisKey, $sRedisValue, $job);
|
||||||
|
} finally {
|
||||||
|
$this->oQueueJob->finnal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function markFailed($checkId, $msg)
|
||||||
|
{
|
||||||
|
$row = Db::name('article_reference_check_result')->where('id', $checkId)->find();
|
||||||
|
try {
|
||||||
|
(new ReferenceCheckService())->updateCheckResult($checkId, [
|
||||||
|
'status' => 2,
|
||||||
|
'error_msg' => $msg,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\think\Log::error('ReferenceCheckTwo markFailed: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
$amId = empty($row) ? 0 : intval(isset($row['am_id']) ? $row['am_id'] : 0);
|
||||||
|
if ($amId > 0) {
|
||||||
|
(new ReferenceCheckService())->syncAmRefCheckStatus($amId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,6 +80,25 @@ class QueueRedis
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一个或多个 Redis 键(用于重检前清除队列任务 completed 标记)
|
||||||
|
*/
|
||||||
|
public function deleteRedisKeys(array $keys)
|
||||||
|
{
|
||||||
|
$keys = array_values(array_filter($keys, function ($k) {
|
||||||
|
return $k !== null && $k !== '';
|
||||||
|
}));
|
||||||
|
if (empty($keys)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$this->connect()->del(...$keys);
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 安全释放锁(仅当值匹配时删除)
|
// 安全释放锁(仅当值匹配时删除)
|
||||||
public function releaseRedisLock($key, $value)
|
public function releaseRedisLock($key, $value)
|
||||||
|
|||||||
2578
application/common/ReferenceCheckService.php
Normal file
2578
application/common/ReferenceCheckService.php
Normal file
File diff suppressed because it is too large
Load Diff
1271
application/common/service/LLMService.php
Normal file
1271
application/common/service/LLMService.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user