Merge remote-tracking branch 'origin/master'

# Conflicts:
#	application/api/controller/Preaccept.php
#	application/api/controller/Production.php
#	application/api/controller/References.php
#	application/api/controller/Reviewer.php
#	application/api/controller/Workbench.php
#	application/common/ArticleParserService.php
#	application/common/ProductionArticleRefer.php
#	application/common/Reviewer.php
This commit is contained in:
wangjinlei
2026-06-15 16:44:38 +08:00
7 changed files with 724 additions and 0 deletions

View File

@@ -3476,6 +3476,17 @@ class Article extends Base
return jsonError("Submission failed");
}
$article_id = $data['article_id'];
//查询标题是否存在
$oArticle = new \app\common\Article;
$aCheckTitle = $oArticle->checkTitle(['title' => $data['title'],'article_id' => $article_id,'user_id' => $data['user_id']]);
$iStatus = empty($aCheckTitle['status']) ? -1 : $aCheckTitle['status'];
$sMsg = empty($aCheckTitle['msg']) ? '': $aCheckTitle['msg'];
$iDraftId = empty($aCheckTitle['draft_id']) ? 0 : $aCheckTitle['draft_id'];
if($iStatus != 1){
return json(['code' => $iStatus, 'msg' => $sMsg,'draft_id' => $iDraftId]);
}
//添加文章基础信息
if ($data['article_id'] == 0) {
$checkArticle = $this->article_obj->where("title", trim($data['title']))->select();
@@ -5489,6 +5500,17 @@ class Article extends Base
}
}
//查询标题是否存在
$oArticle = new \app\common\Article;
$aCheckTitle = $oArticle->checkTitle(['title' => $aArticle['title'],'article_id' => $iArticleId,'user_id' => $iUserId]);
$iStatus = empty($aCheckTitle['status']) ? -1 : $aCheckTitle['status'];
$sMsg = empty($aCheckTitle['msg']) ? '': $aCheckTitle['msg'];
$iDraftId = empty($aCheckTitle['draft_id']) ? 0 : $aCheckTitle['draft_id'];
if($iStatus != 1){
$iFirstStatus = 2;
$sFirstMsg = 'Step 1: '.$sMsg;
}
//判断伦理
if(!empty($aArticle['approval']) && $aArticle['approval'] == 1 && empty($aArticle['approval_file'])){
$iFirstStatus = 2;

View File

@@ -0,0 +1,407 @@
<?php
namespace app\api\controller;
use think\Controller;
use think\Db;
class Cronreview extends Controller
{
//定义邮件模版
protected $aEmailConfig = [
'three' => [
'email_subject' => 'Invitation to Review Manuscript for [{accept_sn}]-Reminder',
'email_content' => '
Dear Dr. {realname},<br><br>
I hope this email finds you well.<br><br>
On {invite_time}, we sent you the following review request for <i>{journal_title}</i>, <br>
Manuscript ID:{accept_sn}<br>
Title:{article_title}<br><br>
We have not yet received a response from you, and we understand that the original invitation may not have reached you. We would greatly appreciate it if you could kindly inform us whether you are available to undertake this review.<br><br>
For your convenience, please find the relevant links below:<br>
<a href="{creatLoginUrlForreviewer}">Accept the review invitation</a><br>
<a href="{creatRejectUrlForReviewer}">Reject the review invitation</a><br>
Your username: {account}<br>
Your original password:123456qwe, if you have reset the password, please sign in with the new one or click the "<a href="https://submission.tmrjournals.com/retrieve">forgot password</a>".<br><br>
Thank you once again for considering our invitation. Your input is invaluable to us, and we truly appreciate your time and effort.<br><br>
Please feel free to reply to this email or contact me directly with any questions.<br><br>
Sincerely,<br>
Editorial Office<br>
<a href="https://www.tmrjournals.com/draw_up.html?issn={journal_issn}">Subscribe to this journal</a><br><i>{journal_title}</i><br>
Email: {journal_email}<br>
Website: {website}'
],
'five' => [
'email_subject' => 'Gentle Reminder: Review Invitation for Manuscript {accept_sn}',
'email_content' => '
Dear Dr. {realname},<br><br>
This is a brief follow-up regarding our review invitation sent on {invite_time} for the following manuscript submitted to <i>{journal_title}</i>:<br><br>
Manuscript ID:{accept_sn}<br>
Title:{article_title}<br><br>
We would appreciate it if you could inform us whether you are able to undertake this review. If you are unavailable or require additional time, please feel free to let us know so that we may make appropriate arrangements.<br><br>
For your convenience, please find the relevant links below:<br>
<a href="{creatLoginUrlForreviewer}">Accept the review invitation</a><br>
<a href="{creatRejectUrlForReviewer}">Reject the review invitation</a><br>
Your username: {account}<br>
Your original password:123456qwe, if you have reset the password, please sign in with the new one or click the "<a href="https://submission.tmrjournals.com/retrieve">forgot password</a>".<br><br>
Thank you very much for your time and consideration.<br><br>
Sincerely,<br>
Editorial Office<br>
<a href="https://www.tmrjournals.com/draw_up.html?issn={journal_issn}">Subscribe to this journal</a><br><i>{journal_title}</i><br>
Email: {journal_email}<br>
Website: {website}'
],
'ten' => [
'email_subject' => 'Reminder: Review Report for Manuscript [{accept_sn}]',
'email_content' => '
Dear Dr. {realname},<br><br>
I hope this message finds you well.<br><br>
I am writing to kindly follow up regarding the review report for the following manuscript, for which you were so kind to agree to serve as a reviewer for <i>{journal_title}</i>.<br>
Manuscript ID:{accept_sn}<br>
Title:{article_title}<br><br>
We sincerely appreciate the time and expertise you are dedicating to this review. We fully understand that academic and professional commitments can be demanding, and should you require additional time or experience any difficulty in accessing the manuscript, please do not hesitate to let us know. We would be more than happy to accommodate your schedule or provide any assistance needed.<br><br>
We would greatly appreciate your expert feedback at your earliest convenience, as it will help us proceed smoothly with the editorial process. For your convenience, please find the relevant link below:<br>
<a href="{creatLoginUrlForreviewer}">Click here to submit the review report</a><br>
Your username: {account}<br>
Your original password:123456qwe, if you have reset the password, please sign in with the new one or click the "<a href="https://submission.tmrjournals.com/retrieve">forgot password</a>".<br><br>
Thank you once again for your valued contribution to <i>{journal_title}</i> and for your continued support of our peer-review process.<br><br>
Sincerely,<br>
Editorial Office<br>
<a href="https://www.tmrjournals.com/draw_up.html?issn={journal_issn}">Subscribe to this journal</a><br><i>{journal_title}</i><br>
Email: {journal_email}<br>
Website: {website}'
],
'twelve' => [
'email_subject' => 'Gentle Reminder: Review Report for Manuscript [{accept_sn}]',
'email_content' => '
Dear Dr. {realname},<br><br>
This is a gentle reminder regarding the review report for the manuscript listed below, for which you kindly agreed to serve as a reviewer:<br><br>
Manuscript ID:{accept_sn}<br>
Title:{article_title}<br><br>
For your convenience, please find the relevant links below:<br>
<a href="{creatLoginUrlForreviewer}">Click here to submit review report</a><br>
Your username: {account}<br>
Your original password:123456qwe, if you have reset the password, please login with the new one or click the "<a href="https://submission.tmrjournals.com/retrieve">forgot password</a>".<br><br>
We would greatly appreciate it if you could submit your review at your convenience before <b>{agree_deadline}</b>. If you require additional time or encounter any difficulties, please feel free to let us know.<br><br>
Thank you very much for your valuable time and contribution to the peer-review process.<br><br>
Sincerely,<br>
Editorial Office<br>
<a href="https://www.tmrjournals.com/draw_up.html?issn={journal_issn}">Subscribe to this journal</a><br><i>{journal_title}</i><br>
Email: {journal_email}<br>
Website: {website}'
],
];
private $iDayTime = 86400;//一天秒数
/**
* 文章审稿阶段-邀请审稿超过三天/五天发送提醒邮件
* @return void
*/
public function inviteReminder(){
//获取当前时间
$sCurrentDate = date('Y-m-d', time());
$iTime = $this->iDayTime;
//获取文章信息
$aResult = $this->getArticle();
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? 'No data obtained' : $aResult['msg'];
if($iStatus != 1){
$this->showMessage($sMsg,2);
exit;
}
//数据处理
$aArticle = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aArticle)){
$this->showMessage('Article or journal information is empty',2);
exit;
}
//查询文章邀请的审稿人
$aArticleId = array_column($aArticle, 'article_id');
$aWhere = ['article_id' => ['in',$aArticleId],'state' => 5];
$aReviewer = Db::name('article_reviewer')->field('art_rev_id,reviewer_id,article_id,ctime')->where($aWhere)->order('article_id desc')->select();
if(empty($aReviewer)){
$this->showMessage('No qualified reviewers were found',2);
exit;
}
//数据处理
foreach ($aReviewer as $key => $value) {
if(empty($value['ctime'])){
continue;
}
//时间处理
$sTargetDate = date('Y-m-d', $value['ctime']);
//日期转时间戳
$iTargetTime = strtotime($sTargetDate);//邀请时间戳
$iCurrentTime = strtotime($sCurrentDate);//当前时间戳
$iThreeCtime = intval($iTargetTime + (3 * $iTime));//三天
$iFiveCtime = intval($iTargetTime + (5 * $iTime));//五天
//对比
if($iCurrentTime != $iThreeCtime && $iCurrentTime != $iFiveCtime){
continue;
}
if($iThreeCtime == $iCurrentTime){ //超过三天
$value['email_type'] = 'three';
}
if($iFiveCtime == $iCurrentTime){ //超过五天
$value['email_type'] = 'five';
}
\think\Queue::push('app\api\job\ReminderEmailToReviewer@fire',$value, 'ReminderEmailToReviewer');
}
$this->showMessage('邀请审稿超过三天/五天发送提醒邮件处理完成',1);
}
/**
* 文章审稿阶段-同意审稿超过十天/十二天发送提醒邮件
* @return void
*/
public function agreeReminder(){
//获取当前时间
$sCurrentDate = date('Y-m-d', time());
$iTime = $this->iDayTime;
//获取文章信息
$aResult = $this->getArticle();
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? 'No data obtained' : $aResult['msg'];
if($iStatus != 1){
$this->showMessage($sMsg,2);
exit;
}
//数据处理
$aArticle = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aArticle)){
$this->showMessage('Article or journal information is empty',2);
exit;
}
//查询文章邀请的审稿人
$aArticleId = array_column($aArticle, 'article_id');
$aWhere = ['article_id' => ['in',$aArticleId],'state' => 0];
$aReviewer = Db::name('article_reviewer')->field('art_rev_id,reviewer_id,article_id,agree_review_time')->where($aWhere)->order('article_id desc')->select();
if(empty($aReviewer)){
$this->showMessage('No qualified reviewers were found',2);
exit;
}
//数据处理
foreach ($aReviewer as $key => $value) {
if(empty($value['agree_review_time'])){
continue;
}
//时间处理
$sTargetDate = date('Y-m-d', $value['agree_review_time']);
//日期转时间戳
$iTargetTime = strtotime($sTargetDate);//邀请时间戳
$iCurrentTime = strtotime($sCurrentDate);//当前时间戳
$iTenCtime = intval($iTargetTime + (10 * $iTime));//十天
$iTwelveCtime = intval($iTargetTime + (12 * $iTime));//十二天
//对比
if($iCurrentTime != $iTenCtime && $iCurrentTime != $iTwelveCtime){
continue;
}
if($iTenCtime == $iCurrentTime){ //超过十天
$value['email_type'] = 'ten';
}
if($iTwelveCtime == $iCurrentTime){ //超过十二天
$value['email_type'] = 'twelve';
}
\think\Queue::push('app\api\job\ReminderEmailToReviewer@fire',$value, 'ReminderEmailToReviewer');
}
$this->showMessage('同意审稿超过十天/十二天发送提醒邮件处理完成',1);
}
/**
* 发送邮件提醒
* @return void
*/
public function reminder($aParam = []){
//文章ID
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select the article to query']);
}
//审稿人ID
$iReviewerId = empty($aParam['reviewer_id']) ? 0 : $aParam['reviewer_id'];
if(empty($iReviewerId)){
return json_encode(['status' => 2,'msg' => 'Reviewers who meet the criteria of the article were not selected']);
}
//邮件类型
$sEmailType = empty($aParam['email_type']) ? '' : $aParam['email_type'];
if(empty($sEmailType)){
return json_encode(['status' => 2,'msg' => 'Email type cannot be empty']);
}
//判断文章是否送审
$aWhere = ['state' => 2,'article_id' => $iArticleId];
$aArticle = Db::name('article')->field('article_id,user_id,journal_id,accept_sn,title,abstrart')->where($aWhere)->find();
if(empty($aArticle)){
return json_encode(array('status' => 3,'msg' => 'No articles requiring review were found' ));
}
//查询是否存在审稿记录
$aState = ['three' => 5,'five' => 5,'ten' => 0,'twelve' => 0];
$iState = isset($aState[$sEmailType]) ? $aState[$sEmailType] : -1 ;
$aWhere = ['reviewer_id' => $iReviewerId,'article_id' => $iArticleId,'state' => $iState];
$aReviewer = Db::name('article_reviewer')->field('art_rev_id,reviewer_id,ctime,agree_review_time,state')->where($aWhere)->find();
if(empty($aReviewer)){
return json_encode(['status' => 4,'msg' => 'No qualified reviewers were found']);
}
//查询期刊信息
if(empty($aArticle['journal_id'])){
return json_encode(array('status' => 5,'msg' => 'The article is not associated with a journal' ));
}
$aWhere = ['state' => 0,'journal_id' => $aArticle['journal_id']];
$aJournal = Db::name('journal')->where($aWhere)->find();
if(empty($aJournal)){
return json_encode(array('status' => 6,'msg' => 'No journal information found' ));
}
//查询审稿人邮箱
$aWhere = ['user_id' => $iReviewerId,'state' => 0,'email' => ['<>','']];
$aUser = Db::name('user')->field('user_id,email,realname,account')->where($aWhere)->find();
//收件人
$sEmail = empty($aUser['email']) ? '' : $aUser['email'];//'1172937051@qq.com';//'publisher@tmrjournals.com';//'1172937051@qq.com';//
if(empty($sEmail)){
return json_encode(['status' => 7,'msg' => "Reviewer's email information not found"]);
}
//处理发邮件
//获取邮件模版
$aEmailConfig= empty($this->aEmailConfig[$sEmailType]) ? [] : $this->aEmailConfig[$sEmailType];
if(empty($aEmailConfig)){
return json_encode(['status' => 8,'msg' => "Email template not obtained"]);
}
//邮件内容
$aSearch = [
'{accept_sn}' => empty($aArticle['accept_sn']) ? '' : $aArticle['accept_sn'],//accept_sn
'{article_title}' => empty($aArticle['title']) ? '' : $aArticle['title'],//文章标题
'{abstrart}' => empty($aArticle['abstrart']) ? '' : $aArticle['abstrart'],//文章摘要
'{journal_title}' => empty($aJournal['title']) ? '' : $aJournal['title'],//期刊名
'{journal_issn}' => empty($aJournal['issn']) ? '' : $aJournal['issn'],
'{journal_email}' => empty($aJournal['email']) ? '' : $aJournal['email'],
'{website}' => empty($aJournal['website']) ? '' : $aJournal['website'],
];
//用户名
$realname = empty($aUser['account']) ? '' : $aUser['account'];
$realname = empty($aUser['realname']) ? $realname : $aUser['realname'];
$aSearch['{realname}'] = $realname;
//用户账号
$aSearch['{account}'] = empty($aUser['account']) ? '' : $aUser['account'];
//审稿链接
$oArticle = new \app\api\controller\Article;
$aSearch['{creatLoginUrlForreviewer}'] = $oArticle->creatLoginUrlForreviewer(['user_id' => $iReviewerId],$aReviewer['art_rev_id']);
if($aReviewer['state'] == 5){
$aSearch['{creatRejectUrlForReviewer}'] = $oArticle->creatRejectUrlForReviewer(['user_id' => $iReviewerId],$aReviewer['art_rev_id']);
}
//邀请时间
$aSearch['{invite_time}'] = empty($aReviewer['ctime']) ? '' : $this->timestampToEnglishDate($aReviewer['ctime']);
//同意审稿截止时间
$iAgreeTime = empty($aReviewer['agree_review_time']) ? 0 : $aReviewer['agree_review_time'];
$iAgreeTime = empty($iAgreeTime) ? '' : intval($iAgreeTime + (14 * $this->iDayTime));//十四天
$aSearch['{agree_deadline}'] = empty($iAgreeTime) ? '' : $this->timestampToEnglishDate($iAgreeTime);
//邮件标题
$title = str_replace(array_keys($aSearch), array_values($aSearch),$aEmailConfig['email_subject']);
//邮件内容变量替换
$content = str_replace(array_keys($aSearch), array_values($aSearch), $aEmailConfig['email_content']);
//判断标题和内容是否为空
if(empty($title) || empty($content)){
return json_encode(['status' => 9,'msg' => "The email content and title are empty"]);
}
//拼接样式
$pre = \think\Env::get('emailtemplete.pre');
$net = \think\Env::get('emailtemplete.net');
$net1 = str_replace("{{email}}",trim($sEmail),$net);
$content=$pre.$content.$net1;
//发送邮件
$memail = empty($aJournal['email']) ? '' : $aJournal['email'];
$mpassword = empty($aJournal['epassword']) ? '' : $aJournal['epassword'];
//期刊标题
$from_name = empty($aJournal['title']) ? '' : $aJournal['title'];
//查询是否发送过邮件
$aStateType = ['three' => 10,'five' => 11,'ten' => 12,'twelve' => 13];
//邮件日志类型
$iLogType = empty($aStateType[$sEmailType]) ? 0 : $aStateType[$sEmailType];
$oReviewer = new \app\common\Reviewer;
$aEmailLog = ['article_id' => $iArticleId,'art_rev_id' => $aReviewer['art_rev_id'],'reviewer_id' => $iReviewerId,'type' => $iLogType,'is_success' => 1];
$aLog = DB::name('email_reviewer')->field('id')->where($aEmailLog)->find();
$sMsg = '邮件已发送';
if(empty($aLog)){
$aResult = sendEmail($sEmail,$title,$from_name,$content,$memail,$mpassword);
$iStatus = empty($aResult['status']) ? 1 : $aResult['status'];
$iIsSuccess = 2;
$sMsg = empty($aResult['data']) ? '失败' : $aResult['data'];
if($iStatus == 1){
$iIsSuccess = 1;
$sMsg = '成功';
}
//记录邮件发送日志
$aEmailLog['email'] = $sEmail;
$aEmailLog['content'] = $content;
$aEmailLog['create_time'] = time();
$aEmailLog['is_success'] = $iIsSuccess;
$aEmailLog['msg'] = $sMsg;
//添加邮件发送日志
$iId = $oReviewer->addLog($aEmailLog);
}
return json_encode(['status' => 1,'msg' => $sMsg]);
}
/**
* 获取审稿状态的文章
* @return void
*/
private function getArticle(){
//查询送审中的文章
$aWhere = ['state' => 2];
$aArticle = Db::name('article')->field('article_id')->where($aWhere)->select();
if(empty($aArticle)){
return ['status' => 2,'msg' => 'No articles requiring review were found'];
}
//数据返回
return ['status' => 1,'msg' => 'Successfully obtained information','data' => $aArticle];
}
private function timestampToEnglishDate($timestamp) {
//验证时间戳有效性
if (!is_numeric($timestamp) || $timestamp < 0) {
return '';
}
//设置本地化确保月份为英文全称兼容Linux/Windows
$locale = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'
? 'English_United States.1252'
: 'en_US.UTF-8';
setlocale(LC_TIME, $locale);
//格式化d=日无前导零、F=英文月份全称、Y=四位年 strftime的%e在Linux下是无前导零的日Windows用%#d
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$dateStr = strftime('%#d %B %Y', $timestamp); // Windows
} else {
$dateStr = strftime('%e %B %Y', $timestamp); // Linux/Mac
}
return trim($dateStr);
}
/**
*
* 格式化信息输出
*
* @access public
* @return void
* @date 2018.09.28
* @param $[message] [<显示信息>]
* @param $[status] [<输出信息1成功2失败>]
*/
private function showMessage($message, $status = 1) {
if ($status == 1) {
echo "[SUCCESS]";
} else {
echo "[ERROR]";
}
echo date("Y-m-d H:i:s") . " " . $message . "\n";
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace app\api\job;
use think\queue\Job;
use app\common\QueueJob;
use app\common\QueueRedis;
use think\Db;
class AiCheckRefer
{
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
$iPArticleId = empty($data['p_article_id']) ? 0 : $data['p_article_id'];
if (empty($iPArticleId)) {
$this->oQueueJob->log("无效的p_article_id删除任务");
$job->delete();
return;
}
try {
// 生成Redis键并尝试获取锁
$sClassName = get_class($this);
$sRedisKey = "queue_job:{$sClassName}:{$iPArticleId}";
$sRedisValue = uniqid() . '_' . getmypid();
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
return; // 未获取到锁,已处理
}
//生成内容
$oProductionArticleRefer = new \app\api\controller\References;
$response = $oProductionArticleRefer->checkByAi($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,82 @@
<?php
namespace app\api\job;
use think\queue\Job;
use app\common\QueueJob;
use app\common\QueueRedis;
use app\common\ProofRead;
class ProofReadQueue
{
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'];
$sChunkIndex = empty($data['chunkIndex']) ? 0 : $data['chunkIndex'];
$sPrompt = empty($data['prompt']) ? '' : $data['prompt'];
if (empty($iArticleId)) {
$this->oQueueJob->log("无效的article_id删除任务");
$job->delete();
return;
}
try {
// 生成Redis键并尝试获取锁
$sClassName = get_class($this);
$sRedisKey = "queue_job:{$sClassName}:{$iArticleId}:{$sPrompt}_{$sChunkIndex}";
$sRedisValue = uniqid() . '_' . getmypid();
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
return; // 未获取到锁,已处理
}
//生成内容
$oAireview = new ProofRead;
$response = $oAireview->proofReadQueue($data);
// 验证API响应
if (empty($response)) {
throw new \RuntimeException("OpenAI API返回空结果");
}
// 检查JSON解析错误
$aResult = json_decode($response, true);
echo '<pre>';var_dump($aResult);
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,89 @@
<?php
namespace app\api\job;
use think\queue\Job;
use app\common\QueueJob;
use app\common\QueueRedis;
class SendAuthorEmail
{
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()}");
try {
// 验证任务数据完整性
// 获取文章ID
$iArticleId = empty($data['article_id']) ? 0 : $data['article_id'];
//作者邮箱
$email = empty($data['email']) ? '' : $data['email'];
//邮件主题
$subject = empty($data['subject']) ? '' : $data['subject'];
//发送来源
$title = empty($data['title']) ? '' : $data['title'];
$subject = empty($subject) ? $title : $subject;
//邮件内容
$content = empty($data['content']) ? '' : $data['content'];
//邮箱
$temail = empty($data['temail']) ? '' : $data['temail'];
//密码
$tpassword = empty($data['tpassword']) ? '' : $data['tpassword'];
//邮件类型
$type = empty($data['type']) ? 1 : $data['type'];
if (empty($iArticleId) || empty($email)) {
$this->oQueueJob->log("无效的article_id/email删除任务");
$job->delete();
return;
}
// 生成唯一任务标识
$sClassName = get_class($this);
$sRedisKey = "queue_job:{$sClassName}:{$iArticleId}:{$email}";
$sRedisValue = uniqid() . '_' . getmypid();
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
return; // 未获取到锁,已处理
}
// 执行核心任务-发送邮件
$aResult = sendEmail($email,$subject,$title,$content,$temail,$tpassword);
$iStatus = empty($aResult['status']) ? 1 : $aResult['status'];
$iIsSuccess = 2;
$sMsg = empty($aResult['data']) ? '失败' : $aResult['data'];
if($iStatus == 1){
$iIsSuccess = 1;
$sMsg = '成功';
}
// 更新完成标识
$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,23 @@
#!/bin/bash
# agreeReviewReminder.sh
# 批量处理文章审稿阶段-同意审稿超过十天/十二天发送提醒邮件
# 调用接口获取需要提醒的审稿人记录
# 此文件需要在crontab中配置每天【凌晨0:30】运行一次
# @author chengxiaoling
# @date 2025-12-29
# 基础配置
DOMAIN="http://api.tmrjournals.com/public/index.php/" # 项目域名
# DOMAIN="http://zmzm.tougao.dev.com" # 项目域名
ROUTE="/api/Cronreview/agreeReminder" # 控制器路由
BASE_PATH=$(cd `dirname $0`; pwd)
# 如果日志目录不存在则创建
logDir=${BASE_PATH}/log/$(date "+%Y")/$(date "+%m")
if [ ! -d $logDir ];then
mkdir -p $logDir
fi
# 执行请求并记录日志
curl "${DOMAIN}${ROUTE}" >> ${logDir}/agreeReminder_$(date "+%Y%m%d").log 2>&1
# 添加时间戳
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 定时任务已执行" >> ${logDir}/agreeReminder_$(date "+%Y%m%d").log

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# inviteReviewReminder.sh
# 批量处理文章审稿阶段-邀请审稿超过三天/五天发送提醒邮件
# 调用接口获取需要提醒的审稿人记录
# 此文件需要在crontab中配置每天【凌晨0:30】运行一次
# @author chengxiaoling
# @date 2025-12-29
# 基础配置
DOMAIN="http://api.tmrjournals.com/public/index.php/" # 项目域名
# DOMAIN="http://zmzm.tougao.dev.com" # 项目域名
ROUTE="/api/Cronreview/inviteReminder" # 控制器路由
BASE_PATH=$(cd `dirname $0`; pwd)
# 如果日志目录不存在则创建
logDir=${BASE_PATH}/log/$(date "+%Y")/$(date "+%m")
if [ ! -d $logDir ];then
mkdir -p $logDir
fi
# 执行请求并记录日志
curl "${DOMAIN}${ROUTE}" >> ${logDir}/inviteReminder_$(date "+%Y%m%d").log 2>&1
# 添加时间戳
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 定时任务已执行" >> ${logDir}/inviteReminder_$(date "+%Y%m%d").log