Merge remote-tracking branch 'origin/master'

This commit is contained in:
wangjinlei
2025-10-13 17:36:11 +08:00
17 changed files with 2382 additions and 93 deletions

View File

@@ -264,7 +264,7 @@ class Aiarticle extends Base
//是否查询作者 1是2否
$iSelectAuthor = empty($aParam['is_select_author']) ? 2 : $aParam['is_select_author'];
//查询AI生成的文章内容
$aWhere = ['is_delete' => 2,'is_generate' => 1];
$aWhere = ['is_delete' => 2];//,'is_generate' => 1
if(!empty($iArticleId)){
$aWhere['article_id'] = $iArticleId;
}
@@ -292,7 +292,10 @@ class Aiarticle extends Base
}
}
return json_encode(['status' => 1,'msg' => 'success','data' => ['ai_article' => $aAiArticle,'ai_article_author' => $aAiAuthor]]);
//查询结论
$aWhere = ['article_id' => $iArticleId,'is_delete' => 2];
$aArticleResult = DB::name('ai_article_results')->field('id,title,content')->where($aWhere)->select();
return json_encode(['status' => 1,'msg' => 'success','data' => ['ai_article' => $aAiArticle,'ai_article_author' => $aAiAuthor,'ai_article_results' => $aArticleResult]]);
}
/**
* @title 处理作者数据
@@ -376,7 +379,14 @@ class Aiarticle extends Base
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//文章ID
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
//查询内容是否存在
$aWhere = ['is_delete' => 2];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select the article to be modified']);
}
//更新AI文章内容
$oArticle = new \app\common\Article;
$aResult = json_decode($oArticle->updateAiArticle($aParam),true);
@@ -385,66 +395,101 @@ class Aiarticle extends Base
return json_encode($aResult);
}
//结论信息
$aResultInfo = empty($aParam['results']) ? [] : $aParam['results'];
$aResultInfo = is_array( $aResultInfo) ? $aResultInfo : json_decode($aResultInfo,true);
//更新作者信息
$aAuthorList = empty($aParam['author_list']) ? []: $aParam['author_list'];
$aAuthorList = is_array( $aAuthorList) ? $aAuthorList: json_decode($aAuthorList,true);
if(empty($aAuthorList)){
if(empty($aAuthorList) && empty($aResultInfo)){
return json_encode($aResult);
}
//根据邮箱查询作者信息
$aAuthorList = array_column($aAuthorList, null,'email');
$aEmail = array_keys($aAuthorList);
if(empty($aEmail)){
return json_encode($aResult);
}
$aUserParam = ['email' => ['in',$aEmail]];
$aUserInfo = Db::name('user')->field('email,user_id')->where($aUserParam)->select();
if(empty($aUserInfo)){
return json_encode($aResult);
}
if(!empty($aAuthorList)){
$aAuthorList = array_column($aAuthorList, null,'email');
$aEmail = array_keys($aAuthorList);
if(empty($aEmail)){
return json_encode($aResult);
}
$aUserParam = ['email' => ['in',$aEmail]];
$aUserInfo = Db::name('user')->field('email,user_id')->where($aUserParam)->select();
if(empty($aUserInfo)){
return json_encode($aResult);
}
//查询用户附表
$aUserId = array_column($aUserInfo, 'user_id');
$aWhere = ['reviewer_id' => ['in',$aUserId]];
$aUserReviewer = Db::name('user_reviewer_info')->where($aWhere)->column('reviewer_id');
//更新用户信息
Db::startTrans();
foreach ($aUserInfo as $key => $value) {
//查询用户附表
$aUserId = array_column($aUserInfo, 'user_id');
$aWhere = ['reviewer_id' => ['in',$aUserId]];
$aUserReviewer = Db::name('user_reviewer_info')->where($aWhere)->column('reviewer_id');
//更新用户信息
Db::startTrans();
foreach ($aUserInfo as $key => $value) {
$aUser = empty($aAuthorList[$value['email']]) ? [] : $aAuthorList[$value['email']];
if(empty($aUser)){
continue;
}
//更新作者名字
if(isset($aUser['author_name'])){
$aUpdate = ['localname' => $aUser['author_name']];
Db::name('user')->where('user_id',$value['user_id'])->update($aUpdate);
}
//更新作者简介和单位
$aUpdateReviewer = [];
if(isset($aUser['technical'])){
$aUpdateReviewer['technical'] = $aUser['technical'];
}
if(isset($aUser['introduction'])){
$aUpdateReviewer['introduction'] = $aUser['introduction'];
}
if(isset($aUser['company'])){
$aUpdateReviewer['company'] = $aUser['company'];
}
if(isset($aUpdateReviewer)){
if(in_array($value['user_id'], $aUserReviewer)){//更新
Db::name('user_reviewer_info')->where('reviewer_id',$value['user_id'])->limit(1)->update($aUpdateReviewer);
$aUser = empty($aAuthorList[$value['email']]) ? [] : $aAuthorList[$value['email']];
if(empty($aUser)){
continue;
}
if(!in_array($value['user_id'], $aUserReviewer)){//插入
$aUpdateReviewer['reviewer_id'] = $value['user_id'];
Db::name('user_reviewer_info')->insert($aUpdateReviewer);
//更新作者名字
if(isset($aUser['author_name'])){
$aUpdate = ['localname' => $aUser['author_name']];
Db::name('user')->where('user_id',$value['user_id'])->update($aUpdate);
}
//更新作者简介和单位
$aUpdateReviewer = [];
if(isset($aUser['technical'])){
$aUpdateReviewer['technical'] = $aUser['technical'];
}
if(isset($aUser['introduction'])){
$aUpdateReviewer['introduction'] = $aUser['introduction'];
}
if(isset($aUser['company'])){
$aUpdateReviewer['company'] = $aUser['company'];
}
if(isset($aUpdateReviewer)){
if(in_array($value['user_id'], $aUserReviewer)){//更新
Db::name('user_reviewer_info')->where('reviewer_id',$value['user_id'])->limit(1)->update($aUpdateReviewer);
}
if(!in_array($value['user_id'], $aUserReviewer)){//插入
$aUpdateReviewer['reviewer_id'] = $value['user_id'];
Db::name('user_reviewer_info')->insert($aUpdateReviewer);
}
}
}
Db::commit();
}
Db::commit();
$aResult['data'] = $aParam;
//更新结论
if(!empty($aResultInfo)){
Db::startTrans();
foreach ($aResultInfo as $key => $value) {
//记录ID
$iRecordId = empty($value['record_id']) ? 0 : $value['record_id'];
if(empty($iRecordId)){
continue;
}
//标题
$sTitle = empty($value['title']) ? '' : $value['title'];
//内容
$sContent = empty($value['content']) ? '' : $value['content'];
if(empty($sTitle) && empty($sContent)){
continue;
}
if(!empty($sTitle)){
$aUpdate['title'] = $sTitle;
}
if(!empty($sContent)){
$aUpdate['content'] = $sContent;
}
if(!empty($aUpdate)){
$aUpdate['update_time'] = time();
$aWhere = ['article_id' => $iArticleId,'id' => $iRecordId];
$result = DB::name('ai_article_results')->where($aWhere)->limit(1)->update($aUpdate);
}
}
Db::commit();
}
// $aResult['data'] = $aParam;
return json_encode($aResult);
}

View File

@@ -536,6 +536,22 @@ class Article extends Base
$article_res['majors'] = $majors;
//查询文章作者信息
$author_res = $this->article_author_obj->where('article_id', $data['articleId'])->where('state', 0)->select();
//查询作者scopus chengxiaoling 20260924 start
if(!empty($author_res)){
$aEmail = array_unique(array_column($author_res, 'email'));
$aWhere = ['email' => ['in',$aEmail]];
$aUserData = Db::name('user')->field('user_id,email,scopus_index,scopus_website,google_index,wos_index')->where($aWhere)->select();
$aUserData = empty($aUserData) ? [] : array_column($aUserData, null,'email');
foreach ($author_res as $key => $value) {
$aUserInfo = empty($aUserData[$value['email']]) ? [] : $aUserData[$value['email']];
$author_res[$key]['scopus_index'] = isset($aUserInfo['scopus_index']) ? $aUserInfo['scopus_index'] : '';
$author_res[$key]['scopus_website'] = empty($aUserInfo['scopus_website']) ? '' : $aUserInfo['scopus_website'];
$author_res[$key]['google_index'] = isset($aUserInfo['google_index']) ? $aUserInfo['google_index'] : '';
$author_res[$key]['wos_index'] = isset($aUserInfo['wos_index']) ? $aUserInfo['wos_index'] : '';
$author_res[$key]['user_id'] = empty($aUserInfo['user_id']) ? 0 : $aUserInfo['user_id'];
}
}
//查询作者scopus chengxiaoling 20260924 end
//查询转投信息
$transfer_res = $this->article_transfer_obj->where('article_id', $data['articleId'])->select();
//查询建议转投详情

View File

@@ -76,6 +76,7 @@ class Cronmonitor extends Controller
$aParam['date'] = $sDate;
$aResult = object_to_array(json_decode(myPost($sUrl, $aParam)));
$aContent = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aContent)){
$this->showMessage($aResult['msg'] ?? '接口异常',2);
exit;
@@ -91,23 +92,20 @@ class Cronmonitor extends Controller
foreach ($aArticleCite as $key => $value) {
$aCite[$value['article_id']][] = $value;
}
//获取数据-文章通讯作者数据
$aArticleAuthor = empty($aContent['article_author']) ? [] : $aContent['article_author'];
if(empty($aArticleAuthor)){
$this->showMessage('未查询到引用文章通讯作者数据:'.$sDate,2);
exit;
}
$aArticleId = array_unique(array_column($aArticleAuthor, 'article_id'));
//获取数据-文章数据
$aArticle = empty($aContent['article']) ? [] : $aContent['article'];
//期刊数据
$aJournal = empty($aContent['journal']) ? [] : $aContent['journal'];
//查询发送人信息
$aEmail = array_column($aArticleAuthor, 'email');
$aWhere = ['email' => ['in',$aEmail]];
$aUser = Db::name('user')->where($aWhere)->column('email,realname,localname');
//查询邮件发送日志
$aArticleCiteId = array_column($aArticleCite, 'article_cite_id');
$aWhere = ['article_cite_id' => ['in',$aArticleCiteId],'is_success' => 1];
@@ -123,28 +121,32 @@ class Cronmonitor extends Controller
$aEmailCite= $this->aEmailConfig['cite'];
//邮件发送日志记录数组
$aEmailLog = [];
$aSendEmail = [];//['2101' => ['mohammadalipour_z@yahoo.com']];
foreach ($aArticleAuthor as $key => $value) {
if(empty($value['email'])){//邮箱为空
//作者邮箱-发送信息
$email = empty($value['email']) ? '' : $value['email'];
if(empty($email)){
continue;
}
//获取引用文章信息
$aArticleCite = empty($aCite[$value['article_id']]) ? [] : $aCite[$value['article_id']];
if(empty($aArticleCite)){
$this->showMessage('未查询到文章引用信息为空,文章ID:'.$value['article_id']."\n\n",2);
continue;
}
//数据组装-接收邮箱
// $email = '1172937051@qq.com';//$value['email'];
$email = $value['email'];
//判断同一个邮箱是否重复发送
if(!empty($aSendEmail[$value['article_id']]) && in_array($email, $aSendEmail[$value['article_id']])){
continue;
}
//用户信息
$aUserInfo = empty($aUser[$value['email']]) ? [] : $aUser[$value['email']];
$realname = empty($aUserInfo['realname']) ? '' : $aUserInfo['realname'];
$localname = empty($aUserInfo['localname']) ? '' : $aUserInfo['localname'];
$realname = empty($realname) ? $localname : $realname;
$realname = empty($value['author_name']) ? $realname : $value['author_name'];
$realname = empty($value['author_name']) ? $email : $value['author_name'];
//邮件日志
$aEmailLogInfo = empty($aLog[$email]) ? [] : $aLog[$email];
foreach ($aArticleCite as $val) {
if(in_array($val['article_cite_id'], $aEmailLogInfo)){
$this->showMessage('文章标题为:'.$val['article_name'].'邮箱账号为:'.$email."已发送过邮件\n\n",2);
@@ -194,13 +196,14 @@ class Cronmonitor extends Controller
$aResult = sendEmail($email,$title,$from_name,$content,$memail,$mpassword);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$iIsSuccess = 2;
$sMsg = empty($aResult['data']) ? '失败' : $aResult['data'];
$sMsg = empty($aResult['msg']) ? '未知' : $aResult['msg'];
if($iStatus == 1){
$iIsSuccess = 1;
$sMsg = '成功';
}
$this->showMessage('给邮箱:'.$email.'发送邮件-'.$sMsg."\n\n",2);
$aEmailLog[] = ['article_id' => $val['article_id'],'article_cite_id' => $val['article_cite_id'],'email' => $email,'content' => $content,'create_tiem' => time(),'is_success' => $iIsSuccess,'journal_id' => $val['journal_id'],'journal_issn' => $aJournalInfo['issn'],'msg' => $sMsg];
$aSendEmail[$value['article_id']][] = $email;
}
}

View File

@@ -1040,7 +1040,7 @@ class Finalreview extends Base
}
/**
* @title 获取文章的审意见
* @title 获取文章的审稿意见
* @param article_id
*/
public function getArticleFinalReview($aParam = []){
@@ -1052,18 +1052,81 @@ class Finalreview extends Base
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select a article']);
}
//查询审稿记录
//查询文章审稿记录
$aWhere = ['article_id' => $iArticleId,'state' => ['between',[1,3]]];
$aArticleReviewer = Db::name('article_reviewer')->field('art_rev_id,state,ctime')->where($aWhere)->select();
if(!empty($aArticleReviewer)){
$aArtRevId = array_column($aArticleReviewer, 'art_rev_id');
$aWhere = ['art_rev_id' => ['in',$aArtRevId],'state' => 0];
//查询初审问卷
$aQuestion = Db::name('article_reviewer_question')->field('art_rev_id,ctime,score,rated')->where($aWhere)->order('ctime asc')->select();
$aQuestion = empty($aQuestion) ? [] : array_column($aQuestion, null,'art_rev_id');
//查询复审
$aReviewerRepeatLists = [];
$aWhere = ['art_rev_id' => ['in',$aArtRevId],'recommend' => ['between',[1,3]]];
$aReviewerRepeat = Db::name('article_reviewer_repeat')->field('art_rev_rep_id,art_rev_id,recommend,ctime,stime')->where($aWhere)->select();
if(!empty($aReviewerRepeat)){
foreach ($aReviewerRepeat as $key => $value) {
$aReviewerRepeatLists[$value['art_rev_id']][] = $value;
}
}
foreach ($aArticleReviewer as $key => $value) {
$aQuestionData = empty($aQuestion[$value['art_rev_id']]) ? [] : $aQuestion[$value['art_rev_id']];
$value['ctime'] = empty($aQuestionData['ctime']) ? $value['ctime'] : $aQuestionData['ctime'];
$value['score'] = empty($aQuestionData['score']) ? 0 : $aQuestionData['score'];
$value['repeat'] = empty($aReviewerRepeatLists[$value['art_rev_id']]) ? [] : $aReviewerRepeatLists[$value['art_rev_id']];
$value['rated'] = empty($aQuestionData['rated']) ? 0 : $aQuestionData['rated'];
$aArticleReviewer[$key] = $value;
}
}
//查询终审-审稿记录
$aWhere = ['article_id' => $iArticleId,'state' => ['in',[1,2,3]]];
$aReviewerFinal = Db::name('article_reviewer_final')->field('id,state,suggest_for_editor,suggest_for_author,update_time,reviewer_id,is_anonymous')->where($aWhere)->select();
if(!empty($aReviewerFinal)){
//查询作者信息
$aUserId = array_unique(array_column($aReviewerFinal, 'user_id'));
$aUserId = array_unique(array_column($aReviewerFinal, 'reviewer_id'));
$aWhere = ['user_id' => ['in',$aUserId],'state' => 0];
$aUser = Db::name('user')->where($aWhere)->column('user_id,realname');
foreach ($aReviewerFinal as $key => $value) {
$aReviewerFinal[$key]['realname'] = empty($aUser[$value['reviewer_id']]) ? '' : $aUser[$value['reviewer_id']];
}
}
$aData = ['review' => $aArticleReviewer,'final_review' => $aReviewerFinal];
return json_encode(['status' => 1,'msg' => 'success','data' => $aData]);
}
/**
* @title 查询终审状态
* @param record_id 记录ID
* @param state 状态
*/
public function getById(){
//获取参数
$aParam = $this->request->post();
//主键ID
$iId = empty($aParam['record_id']) ? 0 : $aParam['record_id'];
if(empty($iId)){
return json_encode(['status' => 2,'msg' => 'Please select the review record']);
}
//参数验证-审稿人ID
$iReviewerId = empty($aParam['reviewer_id']) ? 0 : $aParam['reviewer_id'];
if(empty($iReviewerId)){
return json_encode(['status' => 2,'msg' => 'Please select a reviewer']);
}
//稿件状态
//判断审稿人是否是编委/主编/副主编
$aWhere = ['user_id' => $iReviewerId,'state' => 0];
$aBoard = Db::name('board_to_journal')->where($aWhere)->column('journal_id');
if(empty($aBoard)){
return json_encode(['status' => 2,'msg' => 'The reviewer role does not meet the review requirements']);
}
//查询审稿记录
$aWhere = ['reviewer_id' => $iReviewerId,'id' => $iId];
$aReviewerFinal = Db::name('article_reviewer_final')->field('id,article_id,state,reviewer_id')->where($aWhere)->find();
if(empty($aReviewerFinal)){
return json_encode(['status' => 3,'msg' => 'Review record does not exist or review has been completed']);
}
return json_encode(['status' => 1,'msg' => 'success','data' => $aReviewerFinal]);
}
}

View File

@@ -1606,6 +1606,23 @@ class Production extends Base
return jsonError($rule->getError());
}
$old_article_author_info = $this->production_article_author_obj->where('p_article_author_id', $data['p_article_author_id'])->find();
//判断邮箱是否重复 chengxiaoling 20250926 start
if(empty($old_article_author_info)){
return jsonError('Author information does not exist');
}
$iPArticleId = empty($old_article_author_info['p_article_id']) ? 0 : $old_article_author_info['p_article_id'];
$sEmail = empty($data['email']) ? '' : $data['email'];
$iAuthorId = empty($old_article_author_info['p_article_author_id']) ? 0 : $old_article_author_info['p_article_author_id'];
if(!empty($sEmail) && !empty($iPArticleId)){
$aWhere = ['p_article_id' => $iPArticleId,'email' => $sEmail,'state' => 0,'p_article_author_id' => ['<>',$iAuthorId]];
$aAuthor = $this->production_article_author_obj->field('p_article_author_id')->where($aWhere)->find();
if(!empty($aAuthor)){
return jsonError("Email has been bound by another author");
}
}
//判断邮箱是否重复 chengxiaoling 20250926 end
$article_info = $this->article_obj->where('article_id', $old_article_author_info['article_id'])->find();
$updata['author_name'] = $article_info['journal_id'] == 21 ? trim($data['last_name']) . trim($data['first_name']) : trim($data['first_name']) . ' ' . trim($data['last_name']);
$updata['first_name'] = trim($data['first_name']);
@@ -1695,6 +1712,19 @@ class Production extends Base
$p_info = $this->production_article_obj->where('p_article_id', $data['p_article_id'])->find();
$article_info = $this->article_obj->where('article_id', $p_info['article_id'])->find();
//判断邮箱是否重复 chengxiaoling 20250926 start
$iPArticleId = empty($data['p_article_id']) ? 0 : $data['p_article_id'];
$sEmail = empty($data['email']) ? '' : $data['email'];
if(!empty($sEmail) && !empty($iPArticleId)){
$aWhere = ['p_article_id' => $iPArticleId,'email' => $sEmail,'state' => 0];
$aAuthor = $this->production_article_author_obj->field('p_article_author_id')->where($aWhere)->find();
if(!empty($aAuthor)){
return jsonError("Email has been bound by another author");
}
}
//判断邮箱是否重复 chengxiaoling 20250926 end
$insert['p_article_id'] = $data['p_article_id'];
$insert['article_id'] = $p_info['article_id'];
$insert['author_name'] = $article_info['journal_id'] == 21 ? trim($data['last_name']) . trim($data['first_name']) : trim($data['first_name']) . ' ' . trim($data['last_name']);

View File

@@ -0,0 +1,503 @@
<?php
namespace app\api\controller;
use app\api\controller\Base;
use think\Db;
use app\common\OpenAi;
use app\common\Article;
/**
* @title 文章校对记录
* @description 对接OPENAI接口
*/
class Proofread extends Base
{
/**
* @title AI文章校对
* @param article_id 文章ID
*/
public function proofRead($aParam = []){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(array('status' => 2,'msg' => 'Please select an article' ));
}
//行号
$iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id'];
//查询文章
$aWhere = ['article_id' => $iArticleId];
$oArticle = new Article;
$aArticle = json_decode($oArticle->get($aWhere),true);
$aArticle = empty($aArticle['data']) ? [] : $aArticle['data'];
if(empty($aArticle)){
return json_encode(array('status' => 3,'msg' => 'No articles requiring review were found' ));
}
if($aArticle['state'] != 6){
return json_encode(array('status' => 4,'msg' => 'The article has not entered the proofreading stage'));
}
//查询是否进行过校对
$aProofReadWhere = ['article_id' => $iArticleId,'state' => 2];
if(!empty($iAmId)){
$aProofReadWhere['am_id'] = $iAmId;
}
$iCount = Db::name('article_proofread')->where($aProofReadWhere)->count();
if(!empty($iCount)){
return json_encode(array('status' => 5,'msg' => 'The article or paragraph has been proofread'));
}
//查询文章内容
$aWhere['type'] = 0;
$aWhere['content'] = ['<>',''];
$aWhere['state'] = 0;
if(!empty($iAmId)){
$aWhere['am_id'] = $iAmId;
}
$aArticleMain = Db::table('t_article_main')->field('am_id,content,type,is_h1,is_h2,is_h3,ami_id,amt_id')->where($aWhere)->select();
if(empty($aArticleMain)){
return json_encode(array('status' => 5,'msg' => 'The content of the article is empty'));
}
//实例化公共方法
$oHelperFunction = new \app\common\HelperFunction;
$oProofReadService = new \app\common\ProofReadService;
//数据处理
$aH = $aTable = [];
foreach ($aArticleMain as $key => $value) {
if(empty($oHelperFunction->filterAllTags($value['content']))){
continue;
}
$aResult = $oProofReadService->proofread($value['content']);
if(empty($aResult)){
continue;
}
$aResult['am_id'] = $value['am_id'];
$aError[] = $aResult;
}
if(empty($aError)){
return json_encode(array('status' => 1,'msg' => 'No errors found'));
}
//数据处理
foreach ($aError as $key => $value) {
if(empty($value['errors'])){
continue;
}
foreach ($value['errors'] as $k => $val) {
$val['am_id'] = $value['am_id'];
$val['article_id'] = $iArticleId;
$val['proof_before'] = empty($value['proof_before']) ? '' : $value['proof_before'];
$val['proof_after'] = empty($value['proof_after']) ? '' : $value['proof_after'];
$aData[] = $val;
}
}
if(empty($aData)){
return json_encode(array('status' => 1,'msg' => 'Data processing failed'));
}
//插入
$result = Db::name('article_proofread')->insertAll($aData);
if(!$result){
return json_encode(array('status' => 6,'msg' => 'No errors found'));
}
//查询参考文献
$aWhere = ['state' => ['in',[0,2]],'article_id' => $iArticleId];
return json_encode(['status' => 1,'msg' => 'success']);
}
/**
* @title 获取每行校对记录
* @param article_id 文章ID
* @param am_id 行号
* @param state 状态1已执行2未执行3删除
*/
public function get($aParam = []){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//参数验证-文章ID
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select a article']);
}
//行号
$iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id'];
//查询文章
$aWhere = ['article_id' => $iArticleId];
$aArticle = Db::name('article')->field('journal_id,state')->where($aWhere)->find();
if(empty($aArticle)){
return json_encode(['status' => 3,'msg' => 'The query article does not exist']);
}
if($aArticle['state'] < 5 || $aArticle['state'] == 8){
return json_encode(array('status' => 4,'msg' => 'The article has not entered the proofreading stage'));
}
//查询文章内容
$aWhere['type'] = 0;
$aWhere['content'] = ['<>',''];
$aWhere['state'] = 0;
if(!empty($iAmId)){
$aWhere['am_id'] = $iAmId;
}
$aArticleMain = Db::name('article_main')->field('am_id,content')->where($aWhere)->select();
if(empty($aArticleMain)){
return json_encode(array('status' => 5,'msg' => 'The content of the article is empty'));
}
//查询校对内容
$aAmId = array_column($aArticleMain, 'am_id');
$aWhere = ['am_id' => ['in',$aAmId]];
$iState = empty($aParam['state']) ? 0 : $aParam['state'];
if(!empty($iState)){
$aWhere['state'] = ['in',$iState];
}
$aProofRead = Db::name('article_proofread')->field('id,am_id,verbatim_texts,revised_content,explanation,state')->where($aWhere)->select();
if(empty($aProofRead)){
return json_encode(array('status' => 1,'msg' => 'Proofreading record is empty'));
}
//数据处理
$aData = [];
$aArticleMain = array_column($aArticleMain, 'content','am_id');
foreach ($aProofRead as $key => $value) {
$aData[$value['am_id']][] = $value;
}
// 存储每个文章的最新处理内容
// 样式定义:标红+下划线
// 定义标红+下划线样式
// $style = 'color: red; text-decoration: underline;';
// // 存储最终处理后的文章内容
// $aFinalContent = [];
// // 外层循环:遍历每个文章的错误分组($key为am_id
// foreach ($aData as $amId => $errors) {
// // 获取原始文章内容(若不存在则跳过)
// $sContent = empty($aArticleMain[$amId]) ? '' : $aArticleMain[$amId];
// if (empty($sContent)) {
// continue;
// }
// // 初始化当前内容为原始内容,后续替换将基于此更新
// $currentContent = $sContent;
// // 内层循环:处理当前文章的每条错误记录
// foreach ($errors as $val) {
// // 只处理状态为2的错误
// if ($val['state'] != 2) {
// continue;
// }
// // 获取错误内容(待标红的文本)
// $verbatim_texts = trim($val['verbatim_texts'] ?? '');
// if (empty($verbatim_texts)) {
// continue; // 空错误内容跳过
// }
// // 构建带样式的替换内容用span标签包裹添加CSS样式
// $revised_content = '<span style="' . $style . '">' . htmlspecialchars($verbatim_texts, ENT_QUOTES, 'UTF-8') . '</span>';
// // 定位错误内容在当前内容中的位置(支持多字节字符,如中文)
// $startPos = mb_strpos($currentContent, $verbatim_texts, 0, 'UTF-8');
// if ($startPos === false) {
// continue; // 未找到错误内容,跳过
// }
// // 计算错误内容的字节长度(用于替换)
// $byteLength = strlen($verbatim_texts);
// // 转换字符起始位置为字节位置适配substr_replace的字节处理
// $byteStart = 0;
// for ($i = 0; $i < $startPos; $i++) {
// $byteStart += strlen(mb_substr($currentContent, $i, 1, 'UTF-8'));
// }
// // 执行替换:用带样式的内容替换原始错误内容
// $currentContent = substr_replace($currentContent, $revised_content, $byteStart, $byteLength);
// }
// // 保存当前文章处理后的最终内容
// $aFinalContent[$amId] = $currentContent;
// }
return json_encode(['status' => 1,'msg' => 'success','data' => ['record' => $aData]]);//,'record_style' => $aFinalContent
}
/**
* @title 更新状态
* @param article_id 文章ID
* @param am_id 行号
* @param record_id 记录ID
* @param state 1已执行2未执行3撤销
*/
public function change(){
//获取参数
$aParam = $this->request->post();
//参数验证-文章ID
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select a article']);
}
//参数验证-行号ID
$iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id'];
if(empty($iAmId)){
return json_encode(['status' => 2,'msg' => 'Please select the proofreading content']);
}
//主键ID
$iId = empty($aParam['record_id']) ? 0 : $aParam['record_id'];
if(empty($iId)){
return json_encode(['status' => 2,'msg' => 'Please select the review record']);
}
//状态
$iState = empty($aParam['state']) ? 0 : intval($aParam['state']);
if(!in_array($iState, [1,2,3])){
return json_encode(['status' => 2,'msg' => 'Illegal review status']);
}
//修改内容
$sContent = empty($aParam['content']) ? '' : $aParam['content'];
if(in_array($iState, [1,2]) && empty($sContent)){
return json_encode(['status' => 2,'msg' => 'The operation content cannot be empty']);
}
//判断校对记录
$aWhere = ['am_id' => $iAmId,'article_id' => $iArticleId,'id' => $iId];
$aProofRead = Db::name('article_proofread')->where($aWhere)->find();
if(empty($aProofRead)){
return json_encode(['status' => 3,'msg' => 'Proofreading record is empty']);
}
if($aProofRead['state'] == 3){
return json_encode(['status' => 4,'msg' => 'Record deleted']);
}
//状态一致
if($iState == $aProofRead['state']){
return json_encode(['status' => 5,'msg' => 'Consistent status without modification']);
}
//判断记录是否执行
if($iState == 3 && $aProofRead['state'] == 1){
return json_encode(['status' => 6,'msg' => 'This record has been executed and cannot be deleted']);
}
$sData = $sUpdateContent = '';
if($iState == 1){ //执行替换操作
$aProofRead['content'] = $sContent;
$aResult = $this->replaceError($aProofRead);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
if($iStatus != 1){
return json_encode($aResult);
}
//获取内容
$sUpdateContent = empty($aResult['data']) ? '' : $aResult['data'];
if(empty($sUpdateContent)){
return json_encode(['status' => 5,'msg' => 'Content processing failed']);
}
// $aDealData = json_decode($this->get(['am_id' => $iAmId,'article_id' => $iArticleId]),true);
// $aDealData = empty($aDealData['data']) ? [] : $aDealData['data'];
// $sData = empty($aDealData['record_style']) ? '' : $aDealData['record_style'];
$sData = $sUpdateContent;
}
if($iState == 2){ //执行替换操作
$aProofRead['content'] = $sContent;
$aResult = $this->removeReplaceError($aProofRead);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
if($iStatus != 1){
return json_encode($aResult);
}
//获取内容
$sUpdateContent = empty($aResult['data']) ? '' : $aResult['data'];
if(empty($sUpdateContent)){
return json_encode(['status' => 5,'msg' => 'Content processing failed']);
}
// $aDealData = json_decode($this->get(['am_id' => $iAmId,'article_id' => $iArticleId]),true);
// $aDealData = empty($aDealData['data']) ? [] : $aDealData['data'];
// $sData = empty($aDealData['record_style']) ? '' : $aDealData['record_style'];
$sData = $sUpdateContent;
}
Db::startTrans();
//更新原始内容
if(!empty($sUpdateContent)){
$aWhere = ['am_id' => $iAmId,'state' => 0,'type' => 0];
$aUpdate = ['content' => $sUpdateContent];
$result_main = Db::name('article_main')->where($aWhere)->limit(1)->update($aUpdate);
}
//判断更新参数
$aUpdate = ['state' => $iState,'update_time' => time()];
//数据库更新
$aWhere = ['id' => $iId];
$result_proofread= Db::name('article_proofread')->where($aWhere)->limit(1)->update($aUpdate);
if(!$result_proofread || !$result_main){
return json_encode(['status' => 7,'msg' => "Update failed"]);
}
Db::commit();
//返回结果
return json_encode(['status' => 1,'msg' => "Update successful",'data' => $sData]);
}
/**
* 替换错误
* @param string $errorId 错误ID
* @return bool 替换是否成功
*/
private function replaceError($aParam = [])
{
if(empty($aParam)){
return ['status' => 2,'msg' => 'The content is empty'];
}
//原始内容
$sContent = empty($aParam['content']) ? '' : $aParam['content'];
//错误内容
$verbatim_texts = empty($aParam['verbatim_texts']) ? '' : $aParam['verbatim_texts'];
//正确内容
$revised_content = empty($aParam['revised_content']) ? 0 : $aParam['revised_content'];
$iLength = strlen($verbatim_texts);
//内容替换
$sContent = $this->replaceByLengthAndKeyword($sContent,$iLength,$verbatim_texts,$revised_content);
return ['status' => 1,'msg' => 'success','data' => $sContent];
}
/**
* 撤回替换错误
* @param string $errorId 错误ID
* @return bool 替换是否成功
*/
private function removeReplaceError($aParam = [])
{
if(empty($aParam)){
return ['status' => 2,'msg' => 'The content is empty'];
}
//原始内容
$sContent = empty($aParam['content']) ? '' : $aParam['content'];
//错误内容
$verbatim_texts = empty($aParam['verbatim_texts']) ? '' : $aParam['verbatim_texts'];
//正确内容
$revised_content = empty($aParam['revised_content']) ? '' : $aParam['revised_content'];
$iLength = strlen($revised_content);
//内容替换
$sContent = $this->replaceByLengthAndKeyword($sContent,$iLength,$revised_content,$verbatim_texts);
return ['status' => 1,'msg' => 'success','data' => $sContent];
}
/**
* 按长度和内容特征快速定位并替换字符串
* @param string $str 原始字符串
* @param int $targetLength 目标子串长度(字节数,多字节字符需注意)
* @param string $keyword 筛选关键词(目标子串需包含此关键词)
* @param string $replace 替换内容
* @param string $encoding 字符编码默认UTF-8处理多字节字符
* @return string 替换后的字符串
*/
private function replaceByLengthAndKeyword($str, $targetLength, $keyword, $replace, $encoding = 'UTF-8') {
// 边界校验:避免无效参数导致错误
if (empty($str) || $targetLength <= 0 || empty($keyword)) {
return '';
}
$strLength = strlen($str);
if ($targetLength > $strLength) {
return ''; // 目标长度超过原始字符串,无需处理
}
$result = $str;
$targets = []; // 存储目标子串的起始位置(字节索引)
// 遍历字符串,提取符合长度且包含关键词的子串位置(一次遍历完成筛选,减少内存占用)
for ($i = 0; $i <= $strLength - $targetLength; $i++) {
// 截取目标长度的子串
$substr = substr($result, $i, $targetLength);
// 检查子串是否包含关键词(多字节安全)
if (mb_strpos($substr, $keyword, 0, $encoding) !== false) {
$targets[] = $i; // 只存起始位置,减少内存占用
}
}
// 若没有匹配目标,直接返回原始字符串
if (empty($targets)) {
return '';
}
// 从后往前替换(避免前面替换导致后面位置偏移)
// 倒序遍历数组,无需额外排序,效率更高
for ($k = count($targets) - 1; $k >= 0; $k--) {
$start = $targets[$k];
// 二次校验:替换前确认子串仍符合条件(防止重复替换或中间修改导致的偏差)
$currentSubstr = substr($result, $start, $targetLength);
if (mb_strpos($currentSubstr, $keyword, 0, $encoding) !== false) {
$result = substr_replace($result, $replace, $start, $targetLength);
}
}
return $result;
}
/**
* @title 更新内容
* @param article_id 文章ID
* @param am_id 行号
* @param record_id 记录ID
* @param revised_content 修改内容
* @param is_update_all 是否更新所有1是2否
*/
public function modify(){
//获取参数
$aParam = $this->request->post();
//参数验证-文章ID
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select a article']);
}
//参数验证-行号ID
$iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id'];
if(empty($iAmId)){
return json_encode(['status' => 2,'msg' => 'Please select the proofreading content']);
}
//主键ID
$iId = empty($aParam['record_id']) ? 0 : $aParam['record_id'];
if(empty($iId)){
return json_encode(['status' => 2,'msg' => 'Please select the review record']);
}
//修改后的内容
$sRevisedContent = empty($aParam['revised_content']) ? '' : $aParam['revised_content'];
if(empty($sRevisedContent)){
return json_encode(['status' => 2,'msg' => 'Please enter the modification content']);
}
//解释说明
$sExplanation = empty($aParam['explanation']) ? '' : $aParam['explanation'];
//是否更新所有1是2否
$iIsUpdateAll = empty($aParam['is_update_all']) ? 2 : $aParam['is_update_all'];
//查询内容是否存在
$aWhere = ['am_id' => $iAmId,'article_id' => $iArticleId,'id' => $iId];
$aProofRead = Db::name('article_proofread')->field('verbatim_texts,revised_content,explanation,state')->where($aWhere)->find();
if(empty($aProofRead)){
return json_encode(['status' => 3,'msg' => 'Proofreading record is empty']);
}
if($aProofRead['state'] == 3){
return json_encode(['status' => 4,'msg' => 'Record deleted']);
}
if($aProofRead['state'] == 1){
return json_encode(['status' => 5,'msg' => 'Record executed']);
}
//判断更新参数
$aUpdate = ['revised_content' => $sRevisedContent,'update_time' => time()];
if(!empty($sExplanation)){
$aUpdate['explanation'] = $sExplanation;
}
//数据库更新
$aWhere = ['id' => $iId];
if($iIsUpdateAll == 1){
if(empty($aProofRead['verbatim_texts']) || empty($aProofRead['revised_content'])){
return json_encode(['status' => 6,'msg' => 'AI proofreading content is empty']);
}
$aWhere = ['verbatim_texts' => $aProofRead['verbatim_texts'],'revised_content' => $aProofRead['revised_content'],'article_id' => $iArticleId,'state' => 2];
}
$result = Db::name('article_proofread')->where($aWhere)->update($aUpdate);
if(!$result){
return json_encode(['status' => 7,'msg' => "Update failed"]);
}
//返回结果
return json_encode(['status' => 1,'msg' => "Update successful"]);
}
}

View File

@@ -421,6 +421,13 @@ class Publish extends Base
$pra['journal_stage_id'] = $data['journal_stage_id'];
$res = object_to_array(json_decode(myPost($url, $pra)));
$res1 = object_to_array(json_decode(myPost($url1, $pra)));
//同步分期下的文章PDF文件到ftp.portico.org chengxiaoling 20250925 start
if(!empty($data['journal_stage_id'])){
$sQueueId = \think\Queue::push('app\api\job\SyncArticleData@fire', ['journal_stage_id' => $data['journal_stage_id']], 'SyncArticleData');
}
//同步分期下的文章PDF文件到ftp.portico.org chengxiaoling 20250925 end
return jsonSuccess([]);
}

View File

@@ -31,28 +31,40 @@ class Supplementary extends Base
if(empty($sIssn)){
return json_encode(['status' => 2,'msg' => 'Please select an journal']);
}
if(is_string($sIssn)){
$sIssn = explode(',', $sIssn);
}
//根据期刊issn查询期刊ID
$aWhere = ['state' => 0,'issn' => $sIssn];
$aJournal = Db::name('journal')->field('journal_id')->where($aWhere)->find();
$aWhere = ['state' => 0,'issn' => ['in',$sIssn]];
$aJournal = Db::name('journal')->where($aWhere)->column('journal_id,issn');
if(empty($aJournal)){
return json_encode(['status' => 3,'msg' => 'No journal information found']);
}
//查询期刊编辑信息
$aWhere = ['state' => 0,'journal_id' => $aJournal['journal_id']];
$aWhere = ['state' => 0,'journal_id' => ['in',array_keys($aJournal)]];
if(isset($aParam['type'])){//编辑类型
$aWhere['type'] = $aParam['type'];
}
$aJournalBoard = Db::name('board_to_journal')->where($aWhere)->column('user_id');
$aJournalBoard = Db::name('board_to_journal')->field('journal_id,user_id')->where($aWhere)->select();
if(empty($aJournalBoard)){
return json_encode(['status' => 4,'msg' => 'No editorial information was found for the journal']);
}
//查询编辑详情
$aWhere = ['state' => 0,'user_id' => ['in',$aJournalBoard]];
$aUser = Db::name('user')->field('user_id,realname')->where($aWhere)->select();
return json_encode(['status' => 1,'msg' => 'success','data' => $aUser]);
$aUserId = array_column($aJournalBoard, 'user_id');
$aWhere = ['state' => 0,'user_id' => ['in',$aUserId]];
$aUser = Db::name('user')->where($aWhere)->column('user_id,realname');
$aUserData = [];
foreach ($aJournalBoard as $key => $value) {
$sIssn = empty($aJournal[$value['journal_id']]) ? '' : $aJournal[$value['journal_id']];
if(empty($sIssn)){
continue;
}
$sRealName = empty($aUser[$value['user_id']]) ? '' : $aUser[$value['user_id']];
$aUserData[$sIssn][] = $sRealName;
}
return json_encode(['status' => 1,'msg' => 'success','data' => $aUserData]);
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace app\api\controller;
namespace app\api\controller;
use app\api\controller\Base;
use think\Controller;
use think\Db;
use think\Queue;
/**
* @title 服务器同步数据
* @description 服务器同步数据
* @group 服务器同步数据
*/
class Syncdata extends Base
{
protected $sJournalUrl = 'http://journalapi.tmrjournals.com/public/index.php/';//'http://zmzm.journal.dev.com/';//
public function __construct(\think\Request $request = null) {
parent::__construct($request);
}
/**
* @title 查询期刊下的子刊
* @param journal_id 期刊ID
* @param journal_stage_id 子刊ID
*/
public function getJournalStage(){
//获取数据
$aParam = $this->request->post();
//期刊ID
$iJournalId = empty($aParam['journal_id']) ? 0 : $aParam['journal_id'];
if(empty($iJournalId)){
return json_encode(['code' => 2, 'msg' => 'Please select a journal']);
}
//获取期刊下的子刊
$sUrl = $this->sJournalUrl."api/Syncdata/getJournalStage";
$aResult = object_to_array(json_decode(myPost1($sUrl,$aParam),true));
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? 'Illegal operation-getJournalStage' : $aResult['msg'];
if($iStatus != 1){
return json_encode(['code' => 3, 'msg' => $sMsg]);
}
//获取期刊下的子刊
$aJournalStage = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aJournalStage)){
return json_encode(['code' => 4, 'msg' => 'Journal data is empty']);
}
//数据处理
foreach ($aJournalStage as $key => $value) {
// $aArticle = json_decode($this->getJournalStageArticle(['journal_stage_id' => $value['journal_stage_id']]),true);
// var_dump($aArticle);exit;
//写入查询期刊文章队列
$sQueue = Queue::push('app\api\job\SyncArticleData@fire', ['journal_stage_id' => $value['journal_stage_id']], 'SyncArticleData');
}
return json_encode(['code' => 1, 'msg' => 'Synchronization queue addition completed','data' => $sQueue]);
}
/**
* @title 获取期刊下的文章
* @param journal_id 期刊ID
* @param journal_stage_id 子刊ID
*/
public function getJournalStageArticle($aParam = []){
//获取数据
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//期刊ID
$iJournalStageId = empty($aParam['journal_stage_id']) ? 0 : $aParam['journal_stage_id'];
if(empty($iJournalStageId)){
return json_encode(['code' => 2, 'msg' => 'Please select a sub issue under the journal']);
}
//获取期刊下的子刊
$sUrl = $this->sJournalUrl."api/Syncdata/getJournalStageArticle";
$aResult = object_to_array(json_decode(myPost1($sUrl,$aParam),true));
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? 'Illegal operation-getJournalStageArticle' : $aResult['msg'];
if($iStatus != 1){
return json_encode(['code' => 3, 'msg' => $sMsg]);
}
//获取文章
$aArticle = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aArticle)){
return json_encode(['code' => 4, 'msg' => 'Article data is empty']);
}
//写入上传文章队列
$sQueue = Queue::push('app\api\job\SyncArticleUpload@fire', $aArticle, 'SyncArticleUpload');
// $aResult = json_decode($this->uploadArticle($aArticle),true);
return json_encode(['code' => 1, 'msg' => 'Joined the upload file queue','data' => $sQueue]);
}
/**
* @title 同步文章数据到服务器
* @param file 文章数据
* @param journal_stage_id 子刊ID
*/
public function uploadArticle($aParam = []){
//获取数据
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//期刊ID
$iJournalStageId = empty($aParam['journal_stage_id']) ? 0 : $aParam['journal_stage_id'];
if(empty($iJournalStageId)){
return json_encode(['code' => 2, 'msg' => 'Please select a sub issue under the journal']);
}
//文章数据
$aArticle = empty($aParam['article']) ? [] : $aParam['article'];
if(empty($aArticle)){
return json_encode(['code' => 2, 'msg' => 'Article data is empty']);
}
//获取期刊下的子刊
$sUrl = $this->sJournalUrl."api/Syncdata/uploadArticle";
$aResult = object_to_array(json_decode(myPost1($sUrl,$aParam),true));
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? 'Illegal operation-uploadArticle' : $aResult['msg'];
if($iStatus != 1){
return json_encode(['code' => 3, 'msg' => $sMsg]);
}
$aResult = empty($aResult['data']) ? [] : $aResult['data'];
return json_encode(['code' => 1, 'msg' => $sMsg,'data' => $aResult]);
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace app\api\job;
use think\queue\Job;
use app\common\QueueJob;
use app\common\QueueRedis;
use app\api\controller\Proofread;
class ArticleProofRead
{
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);
// 获取 Redis 任务的原始数据
$rawBody = empty($job->getRawBody()) ? '' : $job->getRawBody();
$jobData = empty($rawBody) ? [] : json_decode($rawBody, true);
$jobId = empty($jobData['id']) ? 'unknown' : $jobData['id'];
$this->oQueueJob->log("-----------队列任务开始-----------");
$this->oQueueJob->log("当前任务ID: {$jobId}, 尝试次数: {$job->attempts()}");
// 获取文章ID
$iArticleId = empty($data['article_id']) ? 0 : $data['article_id'];
if (empty($iArticleId)) {
$this->oQueueJob->log("无效的article_id删除任务");
$job->delete();
return;
}
try {
// 生成Redis键并尝试获取锁
$sClassName = get_class($this);
$sRedisKey = "queue_job:{$sClassName}:{$iArticleId}";
$sRedisValue = uniqid() . '_' . getmypid();
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
return; // 未获取到锁,已处理
}
//生成内容
$oProofRead = new Proofread;
$response = $oProofRead->proofRead($data);
// 验证API响应
if (empty($response)) {
throw new \RuntimeException("OpenAI API返回空结果");
}
// 检查JSON解析错误
$aResult = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException("解析OpenAI响应失败: " . json_last_error_msg() . " | 原始响应: {$response}");
}
$sMsg = empty($aResult['msg']) ? 'success' : $aResult['msg'];
//更新完成标识
$this->QueueRedis->finishJob($sRedisKey, 'completed', $this->completedExprie,$sRedisValue);
$job->delete();
$this->oQueueJob->log("任务执行成功 | 日志ID: {$sRedisKey} | 执行日志:{$sMsg}");
} 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();
}
}
}

View File

@@ -70,7 +70,7 @@ class SendRelatedArticleEmail
// 执行核心任务
//查询是否发送过邮件
$oJournalArticle = new JournalArticle;
$aLog = json_decode($oJournalArticle::getLog(['article_id' => $iArticleId,'article_author_id' => $article_author_id,'related_article_id' => $related_article_id,'is_success' => 1]),true);
$aLog = json_decode($oJournalArticle::getLog(['article_id' => $iArticleId,'email' => $email,'related_article_id' => $related_article_id,'is_success' => 1]),true);
$sMsg = '邮件已发送';
if(empty($aLog['data'])){
$aResult = sendEmail($email,$title,$from_name,$content,$memail,$mpassword);

View File

@@ -0,0 +1,78 @@
<?php
namespace app\api\job;
use think\queue\Job;
use app\common\QueueJob;
use app\common\QueueRedis;
use app\api\controller\Syncdata;
class SyncArticleData
{
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);
// 获取 Redis 任务的原始数据
$rawBody = empty($job->getRawBody()) ? '' : $job->getRawBody();
$jobData = empty($rawBody) ? [] : json_decode($rawBody, true);
$jobId = empty($jobData['id']) ? 'unknown' : $jobData['id'];
$this->oQueueJob->log("-----------队列任务开始-----------");
$this->oQueueJob->log("当前任务ID: {$jobId}, 尝试次数: {$job->attempts()}");
// 获取文章ID
$iJournalStageId = empty($data['journal_stage_id']) ? 0 : $data['journal_stage_id'];
if (empty($iJournalStageId)) {
$this->oQueueJob->log("无效的journal_stage_id删除任务");
$job->delete();
return;
}
try {
// 生成Redis键并尝试获取锁
$sClassName = get_class($this);
$sRedisKey = "queue_job:{$sClassName}:{$iJournalStageId}";
$sRedisValue = uniqid() . '_' . getmypid();
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
return; // 未获取到锁,已处理
}
//生成内容
$oAireview = new Syncdata;
$response = $oAireview->getJournalStageArticle($data);
// 验证API响应
if (empty($response)) {
throw new \RuntimeException("OpenAI API返回空结果");
}
// 检查JSON解析错误
$aResult = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException("解析OpenAI响应失败: " . json_last_error_msg() . " | 原始响应: {$response}");
}
$sMsg = empty($aResult['msg']) ? 'success' : $aResult['msg'];
//更新完成标识
$this->QueueRedis->finishJob($sRedisKey, 'completed', $this->completedExprie,$sRedisValue);
$job->delete();
$this->oQueueJob->log("任务执行成功 | 日志ID: {$sRedisKey} | 执行日志:{$sMsg}");
} 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();
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace app\api\job;
use think\queue\Job;
use app\common\QueueJob;
use app\common\QueueRedis;
use app\api\controller\Syncdata;
class SyncArticleUpload
{
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);
// 获取 Redis 任务的原始数据
$rawBody = empty($job->getRawBody()) ? '' : $job->getRawBody();
$jobData = empty($rawBody) ? [] : json_decode($rawBody, true);
$jobId = empty($jobData['id']) ? 'unknown' : $jobData['id'];
$this->oQueueJob->log("-----------队列任务开始-----------");
$this->oQueueJob->log("当前任务ID: {$jobId}, 尝试次数: {$job->attempts()}");
// 获取文章ID
$iJournalStageId = empty($data['journal_stage_id']) ? 0 : $data['journal_stage_id'];
if (empty($iJournalStageId)) {
$this->oQueueJob->log("无效的journal_stage_id删除任务");
$job->delete();
return;
}
try {
// 生成Redis键并尝试获取锁
$sClassName = get_class($this);
$sRedisKey = "queue_job:{$sClassName}:{$iJournalStageId}";
$sRedisValue = uniqid() . '_' . getmypid();
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
return; // 未获取到锁,已处理
}
//生成内容
$oAireview = new Syncdata;
$response = $oAireview->uploadArticle($data);
// 验证API响应
if (empty($response)) {
throw new \RuntimeException("OpenAI API返回空结果");
}
// 检查JSON解析错误
$aResult = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException("解析OpenAI响应失败: " . json_last_error_msg() . " | 原始响应: {$response}");
}
$sMsg = empty($aResult['msg']) ? 'success' : $aResult['msg'];
$sData = empty($aResult['data']) ? '' : json_encode($aResult['data']);
//更新完成标识
$this->QueueRedis->finishJob($sRedisKey, 'completed', $this->completedExprie,$sRedisValue);
$job->delete();
$this->oQueueJob->log("任务执行成功 | 日志ID: {$sRedisKey} | 执行日志:{$sMsg} | 数据:{$sData}");
} 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();
}
}
}

View File

@@ -84,7 +84,7 @@ class Article
}
$iAiArticleId = $aAiArticle['ai_article_id'];
//必填参数验证
$aFields = ['article_id','article_type','media_type','journal_id','journal_issn','title_english','title_chinese','covered','research_method','digest','research_background','overview','summary','conclusion','is_generate'];
$aFields = ['article_id','article_type','media_type','journal_id','journal_issn','title_english','covered','title_chinese','digest','research_background','discussion','research_method','conclusion','overview','summary','is_generate'];
$sFiled = '';
$aUpdateParam = [];

View File

@@ -7,7 +7,7 @@ class JournalArticle
{
//官网接口地址
protected static $sApiUrl = 'http://journalapi.tmrjournals.com/public/index.php/';//'http://zmzm.journal.dev.com/';
protected static $sApiUrl = 'http://journalapi.tmrjournals.com/public/index.php/';//'http://zmzm.journal.dev.com/';//
//期刊官网
protected static $sJournalUsx = 'https://www.tmrjournals.com/';
@@ -85,19 +85,20 @@ class JournalArticle
if(empty($aJournal)){
return json_encode(['status' => 5,'msg' => 'The journal to which the article belongs does not exist']);
}
//文章作者
//获取数据-文章通讯作者数据
$aAuthor = empty($aData['author']) ? [] : $aData['author'];
if(empty($aAuthor)){
return json_encode(['status' => 6,'msg' => 'No author information found for the article']);
return json_encode(['status' => 5,'msg' => '未查询到引用文章作者数据']);
}
//查询邮件发送日志
$aWhere = ['article_id' => $iArticleId,'is_success' => 1];
$aEmailLog = Db::name('email_related_article')->field('related_article_id,article_author_id')->where($aWhere)->select();
$aEmailLog = Db::name('email_related_article')->field('related_article_id,email')->where($aWhere)->select();
$aLog = [];
if(!empty($aEmailLog)){
foreach ($aEmailLog as $key => $value) {
$aLog[$value['related_article_id']][] = $value['article_author_id'];
$aLog[$value['related_article_id']][] = $value['email'];
}
}
@@ -106,18 +107,26 @@ class JournalArticle
//数据处理
$aEmailLog = [];
$sErrorMsg = '';
foreach ($aAuthor as $key => $value) {
$aSendEmail = [];//['2101' => ['mohammadalipour_z@yahoo.com']];
foreach ($aAuthor as $key => $value) {
if(empty($value['is_report']) || (!empty($value['is_report']) && $value['is_report'] != 1)){
continue;
}
//作者邮箱-发送信息
$email = empty($value['email']) ? '' : $value['email'];
$email = empty($value['email']) ? '' : $value['email'];//'tmr@tmrjournals.com';//'1172937051@qq.com';//
if(empty($email)){
continue;
}
$email = $value['email'];//'tmr@tmrjournals.com';//'1172937051@qq.com';//
//判断同一个邮箱是否重复发送
if(!empty($aSendEmail[$value['article_id']]) && in_array($email, $aSendEmail[$value['article_id']])){
continue;
}
//判断是否发送过邮件
//邮件日志
$aEmailLogInfo = empty($aLog[$value['article_id']]) ? [] : $aLog[$value['article_id']];
if(in_array($value['article_author_id'], $aEmailLogInfo)){
$aEmailLogInfo = empty($aLog[$value['article_id']]) ? [] : $aLog[$value['article_id']];//邮件日志
if(in_array($value['email'], $aEmailLogInfo)){
continue;
}
@@ -186,9 +195,12 @@ class JournalArticle
$iIsSuccess = 1;
$sMsg = '成功';
}
$aEmaiParam = ['article_id' => $iArticleId,'article_author_id' =>$value['article_author_id'],'related_article_id' => $value['article_id'],'email' => $email,'content' => $content,'create_time' => time(),'journal_id' => $aJournalInfo['journal_id'],'journal_issn' => $aJournalInfo['issn'],'email' => $email,'title' => $title,'from_name' => $from_name,'content' => $content,'memail' => $memail,'mpassword' => $mpassword];
$aEmailParam = ['article_id' => $iArticleId,'article_author_id' =>$value['article_author_id'],'related_article_id' => $value['article_id'],'email' => $email,'content' => $content,'create_time' => time(),'journal_id' => $aJournalInfo['journal_id'],'journal_issn' => $aJournalInfo['issn'],'title' => $title,'from_name' => $from_name,'memail' => $memail,'mpassword' => $mpassword];
//邮件发送记录
$aSendEmail[$value['article_id']][] = $email;
//调用邮件发送队列
Queue::push('app\api\job\SendRelatedArticleEmail@fire', $aEmaiParam, 'SendRelatedArticleEmail');
Queue::push('app\api\job\SendRelatedArticleEmail@fire', $aEmailParam, 'SendRelatedArticleEmail');
}
return json_encode(['status' => 1,'msg' => 'Added to email sending queue']);
}
@@ -221,9 +233,8 @@ class JournalArticle
* @return void
*/
static function getLog($aParam = []) {
//查询条件判断
if(empty($aParam['article_id']) || empty($aParam['article_author_id']) || empty($aParam['related_article_id'])){
if(empty($aParam['article_id']) || empty($aParam['email']) || empty($aParam['related_article_id'])){
return json_encode(['status' => 2,'msg' => 'Missing parameter']);
}

View File

@@ -103,7 +103,7 @@ class OpenAi
'contradiction_explanation' => "解释说明",
],
'fund_number' => [
'fund_number' => "1.[内容是否有基金号]2.[解释说明][返回格式字符串]"
'fund_number' => "1.[内容是否有基金号]2.[列出基金号]3.[解释说明][返回格式字符串]"
],
'attribute' => [
'attribute_assessment' => "内容是否有科学性和创新性[包括但不限于科学性(结论是否科学、参考文献是否新颖等);创新性(结论与当前研究水平相比是否有明显突破、参考文献的时间)][返回是/否]",

File diff suppressed because it is too large Load Diff