Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -58,10 +58,20 @@ class Aiarticle extends Base
|
||||
public function create($aParam = []){
|
||||
//获取参数
|
||||
$aParam = empty($aParam) ? $this->request->post() : $aParam;
|
||||
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
|
||||
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
|
||||
if(empty($iArticleId)){
|
||||
return json_encode(array('status' => 2,'msg' => 'Please select an article'.json_encode($aParam) ));
|
||||
}
|
||||
$iPArticleId = empty($aParam['p_article_id']) ? 0 : $aParam['p_article_id'];
|
||||
if(!empty($iPArticleId)){
|
||||
//获取官网ID
|
||||
$aWhere = ['p_article_id' => $iPArticleId];
|
||||
$aProductionArticle = Db::name('production_article')->field('w_article_id')->where($aWhere)->find();
|
||||
if(empty($aProductionArticle['w_article_id'])){
|
||||
return json_encode(['status' => 2,'msg' => 'The article was not successfully pushed']);
|
||||
}
|
||||
$iArticleId = $aProductionArticle['w_article_id'];
|
||||
}
|
||||
//媒体类型
|
||||
$iMediaType = empty($aParam['media_type']) ? 1 : $aParam['media_type'];
|
||||
//获取文章是否生成AI内容
|
||||
@@ -1094,6 +1104,7 @@ class Aiarticle extends Base
|
||||
|
||||
if(!empty($aAiArticle)){
|
||||
|
||||
$iStatus = 2;
|
||||
$aMsg = [1 => 'The article has been pushed to the draft box',2 => 'Not pushed to the draft box',3 => 'Material upload in progress',4 => 'Material upload completed'];
|
||||
|
||||
//判断是否上传素材
|
||||
@@ -1115,6 +1126,7 @@ class Aiarticle extends Base
|
||||
if(!empty($aLog) && $aLog['status_name'] == 'finish'){
|
||||
$iStatus = 4;//素材上传完成
|
||||
}
|
||||
|
||||
//判断是否推送到草稿箱
|
||||
if(!empty($aLog) && $iStatus == 4){
|
||||
$aDraft = Db::name('ai_wechat_article')->field('is_publish,publish_status,article_id,template_id,wechat_id')->where($aWhere)->find();
|
||||
|
||||
@@ -140,7 +140,6 @@ class Aireview extends Base
|
||||
$sContent = $oHelperFunction->filterAllTags($sContent);
|
||||
//将文章内容拆分参考文献
|
||||
$aDealContent = $this->dealContent($sContent);
|
||||
|
||||
$sBefore= empty($aDealContent['before']) ? '' : $aDealContent['before'];
|
||||
$sReference = empty($aDealContent['after']) ? '' : $aDealContent['after'];
|
||||
if(in_array($sQuestionFields, ['attribute'])){//科学性和创新性
|
||||
@@ -193,50 +192,72 @@ class Aireview extends Base
|
||||
* @param sContent 文章内容
|
||||
*/
|
||||
private function dealContent($sContent = '',$regex = null){
|
||||
if(empty($sContent)){
|
||||
if (empty($sContent)) {
|
||||
return ['before' => '', 'after' => ''];
|
||||
}
|
||||
|
||||
// 1. 限制匹配范围(末尾30%)
|
||||
$contentLength = strlen($sContent);
|
||||
$searchStart = $contentLength > 5000 ? (int)($contentLength * 0.7) : 0;
|
||||
$searchContent = substr($sContent, $searchStart);
|
||||
// 1. 优化字符串长度计算
|
||||
$contentLength = mb_strlen($sContent);
|
||||
$searchStart = $contentLength > 5000 ? (int)($contentLength * 0.6) : 0;
|
||||
|
||||
// 2. 正则模式优化
|
||||
// 2. 截取搜索区域
|
||||
$searchContent = $searchStart > 0
|
||||
? mb_substr($sContent, $searchStart, null, 'UTF-8')
|
||||
: $sContent;
|
||||
|
||||
// 3. 正则模式优化 - 重点处理拼写错误和特殊格式
|
||||
if ($regex === null) {
|
||||
// 关键词列表增加容错处理
|
||||
$keywords = [
|
||||
'references?', 'bibliograph(?:y|ies)',
|
||||
'works? cited', 'citation(?:s)?'
|
||||
'r?reference[s]?', // 允许关键词前多一个r(处理Rreferences这类拼写错误)
|
||||
'bibliograph(?:y|ies)',
|
||||
'works? cited',
|
||||
'citation[s]?',
|
||||
'literature cited',
|
||||
'reference list'
|
||||
];
|
||||
|
||||
// 正则模式优化:
|
||||
// 1. 允许关键词与前面内容直接连接(解决"article.Rreferences"这种没有空格的情况)
|
||||
// 2. 增强对冒号和方括号的支持(匹配"References:[1]"这种格式)
|
||||
$pattern = sprintf(
|
||||
'/(?:^|\s)\s*#*\s*(%s)\s*[:\-–]?\s*(?:$|\s)/i',
|
||||
'/(?:^|\s|[(\[{<"\']|[\p{P}])?\s*\#*[*_]*\s*(%s)\s*[*_]*\s*[:\-–=.]?\s*(?:$|\s|[)\]}>"\'.|[\d{[])*/i',
|
||||
implode('|', $keywords)
|
||||
);
|
||||
$regex = $pattern;
|
||||
}
|
||||
|
||||
// 3. 匹配并处理结果
|
||||
if (preg_match_all($regex, $searchContent, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
$lastMatch = end($matches[0]);
|
||||
$refPosition = $searchStart + $lastMatch[1];
|
||||
// 4. 高效匹配 - 从字符串末尾开始搜索最后一个匹配项
|
||||
// 使用带偏移量的循环匹配找到最后一个符合条件的关键词
|
||||
$lastPos = 0;
|
||||
$matchFound = false;
|
||||
$offset = 0;
|
||||
$searchLen = mb_strlen($searchContent);
|
||||
|
||||
// 4. 合并字符串处理
|
||||
$before = substr($sContent, 0, $refPosition);
|
||||
$after = substr($sContent, $refPosition);
|
||||
// 从字符串末尾向前搜索,提高长文本效率
|
||||
while ($offset < $searchLen && preg_match($regex, $searchContent, $match, PREG_OFFSET_CAPTURE, $offset)) {
|
||||
$lastPos = $match[0][1];
|
||||
$matchFound = true;
|
||||
// 移动偏移量继续查找下一个匹配
|
||||
$offset = $lastPos + mb_strlen($match[0][0]);
|
||||
}
|
||||
|
||||
if ($matchFound) {
|
||||
$refPosition = $searchStart + $lastPos;
|
||||
|
||||
// 一次性处理空白和换行
|
||||
$process = function($str) {
|
||||
return str_replace("\n", '', trim($str));
|
||||
$process = static function($str) {
|
||||
return trim(str_replace("\n", '', $str));
|
||||
};
|
||||
|
||||
return [
|
||||
'before' => $process($before),
|
||||
'after' => $process($after)
|
||||
'before' => $process(mb_substr($sContent, 0, $refPosition, 'UTF-8')),
|
||||
'after' => $process(mb_substr($sContent, $refPosition, null, 'UTF-8'))
|
||||
];
|
||||
}
|
||||
|
||||
// 未匹配时处理
|
||||
return [
|
||||
'before' => str_replace("\n", '', trim($sContent)),
|
||||
'before' => trim(str_replace("\n", '', $sContent)),
|
||||
'after' => ''
|
||||
];
|
||||
// $lastPos = strrpos($sContent, 'Reference');
|
||||
|
||||
@@ -418,7 +418,7 @@ class Article extends Base
|
||||
foreach ($state_num as $item) {
|
||||
$num_arr[$item['state']] = $item['num'];
|
||||
}
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
for ($i = 0; $i <= 8; $i++) {
|
||||
$num_frag[$i] = isset($num_arr[$i]) ? $num_arr[$i] : 0;
|
||||
}
|
||||
|
||||
@@ -552,7 +552,23 @@ class Article extends Base
|
||||
$up_data['editor_act'] = 0;
|
||||
}
|
||||
$this->article_obj->where('article_id', $data['articleId'])->update($up_data);
|
||||
return json(['article' => $article_res, 'msg' => $article_msg, 'authors' => $author_res, 'suggest' => $suggest, 'transfer' => $transfer_res, 'transinfo' => $transfer_info, "major" => $major]);
|
||||
|
||||
//查询终审意见 chengxiaoling 20250828 start
|
||||
$aFinal = [];
|
||||
$iArticleId = empty($data['articleId']) ? 0 : $data['articleId'];
|
||||
if(!empty($iArticleId)){
|
||||
$aWhere = ['article_id' => $iArticleId,'state' => ['between',[1,3]]];
|
||||
$aFinal = Db::name('article_reviewer_final')->field('id,reviewer_id,state,suggest_for_editor,suggest_for_author,review_time')->where($aWhere)->select();
|
||||
if(!empty($aFinal)){
|
||||
//数据处理
|
||||
foreach ($aFinal as $key => $value) {
|
||||
$aFinal[$key]['review_time'] = empty($value['review_time']) ? '' : date('Y-m-d H:i:s',$value['review_time']);
|
||||
$aFinal[$key]['realname'] = empty($aUser[$value['reviewer_id']]) ? '' : $aUser[$value['reviewer_id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
//查询终审意见 chengxiaoling 20250828 end
|
||||
return json(['article' => $article_res, 'msg' => $article_msg, 'authors' => $author_res, 'suggest' => $suggest, 'transfer' => $transfer_res, 'transinfo' => $transfer_info, "major" => $major,'suggest_final' => $aFinal]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1221,6 +1237,62 @@ class Article extends Base
|
||||
return jsonError("Status cannot be changed");
|
||||
}
|
||||
|
||||
//终审判断[3个审稿人审稿意见为:同意/1个审稿人审稿意见为:不同意] chengxiaoling 20250825 start
|
||||
if(isset($data['state']) && $data['state'] == 8){
|
||||
if($article_info['state'] != 1){
|
||||
return jsonError("The article status is not with editor");
|
||||
}
|
||||
$iId = empty($data['articleId']) ? 0 : $data['articleId'];//文章ID
|
||||
if(empty($iId)){
|
||||
return jsonError("Please select an article");
|
||||
}
|
||||
//判断是否有审稿记录
|
||||
$aReviewerParam = ['article_id' => $iId,'state' => ['between',[1,3]]];
|
||||
$aReviewLists = Db::name('article_reviewer')->where($aReviewerParam)->column('state');
|
||||
if(empty($aReviewLists)){
|
||||
return jsonError("Reviewer did not conduct a review");
|
||||
}
|
||||
//判断审稿记录是否有拒稿
|
||||
if(!in_array(2, $aReviewLists)){//拒稿
|
||||
if(count($aReviewLists) < 3){
|
||||
return jsonError("The number of reviewers is less than 3");
|
||||
}
|
||||
}
|
||||
|
||||
//更新文章状态
|
||||
$aWhere = ['article_id' => $iId,'state' => 1];
|
||||
$sUpateResult = $this->article_obj->where($aWhere)->limit(1)->update(['state' => 8]);
|
||||
if($sUpateResult === false){
|
||||
return jsonError("Status update failed");
|
||||
}
|
||||
//添加文章状态信息(如果状态未更新可做通话用,并结束操作)
|
||||
$insert_data['article_id'] = $iId;
|
||||
$insert_data['content'] = 'The article enters the final review stage';
|
||||
$insert_data['state_from'] = $article_info['state'];
|
||||
$insert_data['state_to'] = 8;
|
||||
$insert_data['ctime'] = time();
|
||||
if(!$this->article_msg_obj->insert($insert_data)){
|
||||
return jsonError("article_msg insertion failed");
|
||||
}
|
||||
|
||||
//增加用户操作log
|
||||
$iEditorId = empty($editor_info['user_id']) ? 0 : $editor_info['user_id'];
|
||||
$sEditorAccount = empty($editor_info['account']) ? '' : $editor_info['account'];
|
||||
$sRealName = empty($editor_info['realname']) ? '' : $editor_info['realname'];
|
||||
$sTitle = empty($article_info['title']) ? '' : $article_info['title'];
|
||||
$iArticleUserId = empty($article_info['user_id']) ? 0 : $article_info['user_id'];
|
||||
$log_data['user_id'] = $iEditorId;
|
||||
$log_data['type'] = 1;
|
||||
$log_data['content'] = $sEditorAccount . "(" . $sRealName . "),更改了一篇文章:(" . $sTitle . ")的状态,更改时间是:" . date('Y-m-d H:i:s', time());
|
||||
$log_data['ctime'] = time();
|
||||
$this->user_log_obj->insert($log_data);
|
||||
|
||||
//增加usermsg
|
||||
add_usermsg($iArticleUserId, 'Your manuscript has new process: ' . $sTitle, '/articleDetail?id=' . $iId);
|
||||
return json(['code' => 0]);
|
||||
}
|
||||
//终审判断[3个审稿人审稿意见为:同意/1个审稿人审稿意见为:不同意] chengxiaoling 20250825 end
|
||||
|
||||
//判断文章的h指数是否添加
|
||||
$authors = $this->article_author_obj->where('article_id', $data['articleId'])->where('is_report', 1)->select();
|
||||
// $h_check = false;
|
||||
|
||||
970
application/api/controller/Finalreview.php
Normal file
970
application/api/controller/Finalreview.php
Normal file
@@ -0,0 +1,970 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\controller;
|
||||
use app\api\controller\Base;
|
||||
use think\Db;
|
||||
/**
|
||||
* @title 终审
|
||||
* @description 对接OPENAI接口
|
||||
*/
|
||||
class Finalreview extends Base
|
||||
{
|
||||
|
||||
|
||||
//邮件模版配置
|
||||
private $aEmailConfig = [
|
||||
|
||||
'reviewer' => [
|
||||
'email_subject' => 'Invitation for Final Review of Manuscript - {journal_title} [{accept_sn}]',
|
||||
'email_content' => '
|
||||
Dear Prof. {realname},<br><br>
|
||||
I hope this message finds you well.<br><br>
|
||||
We would like to invite you to conduct the final review of the manuscript detailed below:<br>
|
||||
Title: [{article_title}]<br>
|
||||
Manuscript ID: [{accept_sn}]<br><br>
|
||||
The manuscript has passed the peer review, and revisions have been made based on the external reviewer\'s comments. We would be honored if you could conduct the final review of the manuscript. Your expert opinion is crucial in ensuring the academic quality of the manuscript and determining its final acceptance for publication.<br><br>
|
||||
<a href="{accept_url}">Click here to review the article and kindly submit your final comments within 5 days</a><br>
|
||||
<a href="{reject_url}">Click here to reject the review of this manuscript</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 truly appreciate your time and expertise, and we look forward to receiving your valuable feedback.<br><br>
|
||||
Sincerely,<br>Editorial Office<br>
|
||||
<a href="https://www.tmrjournals.com/draw_up.html?issn={journal_issn}">{journal_title}</a><br>
|
||||
Email: {journal_email}<br>
|
||||
Website: {website}'
|
||||
],
|
||||
];
|
||||
//投稿系统地址
|
||||
private $sTouGaoUrl = "https://submission.tmrjournals.com/";
|
||||
|
||||
|
||||
public function __construct(\think\Request $request = null) {
|
||||
|
||||
parent::__construct($request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @title 终审列表-审稿人【 编委/主编/副主编】
|
||||
* @param article_id 文章ID
|
||||
*/
|
||||
public function lists(){
|
||||
|
||||
//获取参数
|
||||
$aParam = $this->request->post();
|
||||
|
||||
//参数验证-审稿人ID
|
||||
$iReviewerId = empty($aParam['reviewer_id']) ? 0 : $aParam['reviewer_id'];
|
||||
if(empty($iReviewerId)){
|
||||
return json_encode(['status' => 2,'msg' => 'Please select a reviewer']);
|
||||
}
|
||||
|
||||
//获取分页相关参数
|
||||
$iSize = empty($aParam['size']) ? 15 : $aParam['size'];//每页显示条数
|
||||
$iPage = empty($aParam['page']) ? 1 : $aParam['page'];// 当前页码
|
||||
|
||||
//参数组装
|
||||
$aWhere = ['reviewer_id' => $iReviewerId];
|
||||
if(isset($aParam['state'])){
|
||||
$aWhere['state'] = $aParam['state'];
|
||||
}
|
||||
|
||||
//组装sql
|
||||
$sFinalQuery = Db::name('article_reviewer_final')->where($aWhere)->buildSql();
|
||||
|
||||
//参数组装
|
||||
$aWhere = [];
|
||||
//accept_sn
|
||||
if(!empty($aParam['accept_sn'])){
|
||||
$aWhere['t_article.accept_sn'] = $aParam['accept_sn'];
|
||||
}
|
||||
//title
|
||||
if(!empty($aParam['title'])){
|
||||
$aWhere['t_article.title'] = ['like',"%".$aParam['title']."%"];
|
||||
}
|
||||
//查询审稿人相关的文章-统计数量
|
||||
$iCount = Db::table("({$sFinalQuery}) finalquery")
|
||||
->join('t_article', 'finalquery.article_id = t_article.article_id')->where($aWhere)->count();
|
||||
if(empty($iCount)){
|
||||
return json_encode(['status' => 1,'msg' => 'No final review records related to the reviewer were found','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
//判断页数是否超过最大分页限制
|
||||
$iPageNum = ceil($iCount/$iSize);
|
||||
if($iPage > $iPageNum){
|
||||
return json_encode(['status' => 1,'msg' => 'The number of pages has exceeded the limit, maximum page number:'.$iPageNum,'data' => ['total' => $iCount,'lists' => []]]);
|
||||
}
|
||||
|
||||
//查询记录
|
||||
$sOrder = empty($aParam['order']) ? 'id desc' : $aParam['order'];//排序
|
||||
$aLists = Db::table("({$sFinalQuery}) finalquery")
|
||||
->join('t_article', 'finalquery.article_id = t_article.article_id')
|
||||
->field('finalquery.*,t_article.accept_sn,t_article.title,t_article.type as article_type,t_article.keywords,t_article.scoring,t_article.manuscirpt_url,t_article.state as article_state,t_article.journal_id')
|
||||
->where($aWhere)
|
||||
->order($sOrder)
|
||||
->page($iPage, $iSize)
|
||||
->select();
|
||||
if(!empty($aLists)){
|
||||
$aJournalId = array_unique(array_column($aLists, 'journal_id'));
|
||||
if(!empty($aJournalId)){
|
||||
$aWhere = ['journal_id' => ['in',$aJournalId],'state' => 0];
|
||||
$aJournal = Db::name('journal')->where($aWhere)->column('journal_id,title');
|
||||
}
|
||||
foreach ($aLists as $key => $value) {
|
||||
$aLists[$key]['article_type_name'] = empty($value['article_type']) ? '' : translateType($value['article_type']);
|
||||
$aLists[$key]['journal_name'] = empty($aJournal[$value['journal_id']]) ? '' : $aJournal[$value['journal_id']];
|
||||
}
|
||||
}
|
||||
return json_encode(['status' => 1,'msg' => 'success','data' => ['total' => $iCount,'lists' => $aLists]]);
|
||||
}
|
||||
/**
|
||||
* @title 终审人员列表【编委/主编/副主编】
|
||||
* @param id 主键ID
|
||||
*/
|
||||
public function boardLists(){
|
||||
|
||||
//获取参数
|
||||
$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']);
|
||||
}
|
||||
|
||||
//获取分页相关参数
|
||||
$iSize = empty($aParam['size']) ? 15 : $aParam['size'];//每页显示条数
|
||||
$iPage = empty($aParam['page']) ? 1 : $aParam['page'];// 当前页码
|
||||
|
||||
//查询文章
|
||||
$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']);
|
||||
}
|
||||
//获取期刊ID
|
||||
$iJournalId = empty($aArticle['journal_id']) ? '' : $aArticle['journal_id'];
|
||||
if(empty($iJournalId)){
|
||||
return json_encode(['status' => 3,'msg' => 'The article did not select a journal']);
|
||||
}
|
||||
//判断文章状态是否进入终审
|
||||
$iArticleState = !isset($aArticle['state']) ? '' : $aArticle['state'];
|
||||
if($iArticleState != 8){
|
||||
return json_encode(['status' => 4,'msg' => 'The article has not entered the final review stage']);
|
||||
}
|
||||
|
||||
//查询期刊编委
|
||||
$aWhere = ['journal_id' => $iJournalId,'state' => 0];
|
||||
//编委类型0主编1副主编2编委成员
|
||||
if(isset($aParam['board_type'])){
|
||||
$aWhere['type'] = $aParam['board_type'];
|
||||
}
|
||||
if(!empty($aParam['research_areas'])){
|
||||
$aWhere['sQuery.research_areas'] = ['like',"%".$aParam['research_areas']."%"];
|
||||
}
|
||||
//组装sql
|
||||
$sQuery = Db::name('board_to_journal')->where($aWhere)->buildSql();
|
||||
|
||||
//email
|
||||
$aWhere = [];
|
||||
if(!empty($aParam['email'])){
|
||||
$aWhere['t_user.email'] = ['like', "%".$aParam["email"] . "%"];
|
||||
}
|
||||
|
||||
//统计数量
|
||||
$iCount = Db::table("({$sQuery}) sQuery")
|
||||
->join('t_user', 'sQuery.user_id = t_user.user_id')->where($aWhere)->count();
|
||||
if(empty($iCount)){
|
||||
return json_encode(['status' => 1,'msg' => 'Reviewer not found','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
|
||||
//判断页数是否超过最大分页限制
|
||||
$iPageNum = ceil($iCount/$iSize);
|
||||
if($iPage > $iPageNum){
|
||||
return json_encode(['status' => 1,'msg' => 'The number of pages has exceeded the limit, maximum page number:'.$iPageNum,'data' => ['total' => $iCount,'lists' => []]]);
|
||||
}
|
||||
|
||||
//查询记录
|
||||
$sOrder = empty($aParam['order']) ? 'type desc,user_id desc' : $aParam['order'];//排序
|
||||
$aLists = Db::table("({$sQuery}) sQuery")
|
||||
->join('t_user', 'sQuery.user_id = t_user.user_id')
|
||||
->field('sQuery.user_id,sQuery.type,sQuery.research_areas,t_user.account,t_user.email,t_user.realname,t_user.localname')
|
||||
->where($aWhere)
|
||||
->order($sOrder)
|
||||
->page($iPage, $iSize)
|
||||
->select();
|
||||
return json_encode(['status' => 1,'msg' => 'success','data' => ['total' => $iCount,'lists' => $aLists]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 邀请审稿人
|
||||
* @param article_id 文章ID
|
||||
* @param reviewer_id 审稿人ID
|
||||
*/
|
||||
public function invite(){
|
||||
//获取参数
|
||||
$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
|
||||
$iReviewerId = empty($aParam['reviewer_id']) ? 0 : $aParam['reviewer_id'];
|
||||
if(empty($iReviewerId)){
|
||||
return json_encode(['status' => 2,'msg' => 'Please select a reviewer']);
|
||||
}
|
||||
|
||||
//查询文章
|
||||
$aWhere = ['article_id' => $iArticleId];
|
||||
$aArticle = Db::name('article')->field('journal_id,state,accept_sn,title,abstrart')->where($aWhere)->find();
|
||||
if(empty($aArticle)){
|
||||
return json_encode(['status' => 3,'msg' => 'The query article does not exist']);
|
||||
}
|
||||
//获取期刊ID
|
||||
$iJournalId = empty($aArticle['journal_id']) ? '' : $aArticle['journal_id'];
|
||||
if(empty($iJournalId)){
|
||||
return json_encode(['status' => 3,'msg' => 'The article did not select a journal']);
|
||||
}
|
||||
//判断文章状态是否进入终审
|
||||
$iArticleState = empty($aArticle['state']) ? '' : $aArticle['state'];
|
||||
if($iArticleState != 8){
|
||||
return json_encode(['status' => 4,'msg' => 'The article has not entered the final review stage']);
|
||||
}
|
||||
|
||||
//判断审稿人是否已邀请
|
||||
$aWhere = ['article_id' => $iArticleId,'reviewer_id' => $iReviewerId,'state' => ['in',[0,5]]];
|
||||
$aReviewerFinal = Db::name('article_reviewer_final')->where($aWhere)->find();
|
||||
if(!empty($aReviewerFinal)){
|
||||
return json_encode(['status' => 5,'msg' => 'The reviewer has been invited, please be patient and wait for the review results']);
|
||||
}
|
||||
|
||||
//查询审稿人是否存在
|
||||
$aWhere = ['journal_id' => $iJournalId,'state' => 0,'user_id' => $iReviewerId];
|
||||
$aBoard = Db::name('board_to_journal')->field('user_id,type')->where($aWhere)->find();
|
||||
if(empty($aBoard)){
|
||||
return json_encode(['status' => 6,'msg' => 'Reviewer information not found']);
|
||||
}
|
||||
//查询审稿人的邮箱
|
||||
$aWhere = ['user_id' => $iReviewerId,'state' => 0];
|
||||
$aUser = Db::name('user')->field('email,realname,account')->where($aWhere)->find();
|
||||
if(empty($aUser['email'])){
|
||||
return json_encode(['status' => 7,'msg' => "Reviewer's email is empty"]);
|
||||
}
|
||||
|
||||
//插入审稿记录
|
||||
$aInsert = ['article_id' => $iArticleId,'reviewer_id' => $iReviewerId,'reviewer_type' => $aBoard['type'],'state' => 5,'invited_time' => time(),'update_time' => time()];
|
||||
$iId = DB::name('article_reviewer_final')->insertGetId($aInsert);
|
||||
if(!$iId){
|
||||
return json_encode(['status' => 8,'msg' => "Review record insertion failed"]);
|
||||
}
|
||||
|
||||
|
||||
//邮件发送
|
||||
//数据准备-查询期刊信息
|
||||
$aWhere = ['journal_id' => $iJournalId,'state' => 0];
|
||||
$aJournal = Db::name('journal')->field('title,issn,editorinchief,zname,abbr,alias,email,epassword,website')->where($aWhere)->find();
|
||||
//数据准备-获取邮件模版
|
||||
$aEmailConfig= empty($this->aEmailConfig['reviewer']) ? [] : $this->aEmailConfig['reviewer'];
|
||||
//数据准备-邮件内容替换
|
||||
$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'],
|
||||
];
|
||||
|
||||
//邮箱
|
||||
$email = 'tmr@tmrjournals.com';//$aUser['email'];
|
||||
//用户名
|
||||
$realname = empty($aUser['account']) ? '' : $aUser['account'];
|
||||
$realname = empty($aUser['realname']) ? $realname : $aUser['realname'];
|
||||
$aSearch['{realname}'] = $realname;
|
||||
//用户账号
|
||||
$aSearch['{account}'] = empty($aUser['account']) ? '' : $aUser['account'];
|
||||
//审稿链接
|
||||
$aSearch['{accept_url}'] = $this->createReviewUrl($iReviewerId,$iId,1);
|
||||
$aSearch['{reject_url}'] = $this->createReviewUrl($iReviewerId,$iId,2);
|
||||
|
||||
//邮件标题
|
||||
$title = str_replace(array_keys($aSearch), array_values($aSearch),$aEmailConfig['email_subject']);
|
||||
//邮件内容变量替换
|
||||
$content = str_replace(array_keys($aSearch), array_values($aSearch), $aEmailConfig['email_content']);
|
||||
//带模版的邮件内容
|
||||
$pre = \think\Env::get('emailtemplete.pre');
|
||||
$net = \think\Env::get('emailtemplete.net');
|
||||
$net1 = str_replace("{{email}}",trim($email),$net);
|
||||
$content=$pre.$content.$net1;
|
||||
//发送邮件邮箱配置
|
||||
$memail = empty($aJournal['email']) ? '' : $aJournal['email'];
|
||||
$mpassword = empty($aJournal['epassword']) ? '' : $aJournal['epassword'];
|
||||
//期刊标题
|
||||
$from_name = empty($aJournal['title']) ? '' : $aJournal['title'];
|
||||
|
||||
//发送邮件
|
||||
$aResult = sendEmail($email,$title,$from_name,$content,$memail,$mpassword);
|
||||
$iStatus = empty($aResult['status']) ? 1 : $aResult['status'];
|
||||
$iIsSuccess = $iStatus == 1 ? 1 : 2;
|
||||
$sMsg = empty($aResult['data']) ? $iIsSuccess : $aResult['data'];
|
||||
|
||||
//添加邮件发送日志
|
||||
$aEmailLog = ['article_id' => $iArticleId,'art_rev_id' => $iId,'reviewer_id' => $iReviewerId,'type' => 4,'email' => $email,'content' => $content,'create_time' => time(),'is_success' => $iIsSuccess,'msg' => $sMsg];
|
||||
$oReviewer = new \app\common\Reviewer;
|
||||
$iId = $oReviewer->addLog($aEmailLog);
|
||||
|
||||
//返回结果
|
||||
return json_encode(['status' => 1,'msg' => "Invitation successful"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 更新审稿状态
|
||||
* @param article_id 文章ID
|
||||
* @param reviewer_id 审稿人ID
|
||||
* @param record_id 记录ID
|
||||
* @param state 状态
|
||||
*/
|
||||
public function review(){
|
||||
//获取参数
|
||||
$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
|
||||
$iReviewerId = empty($aParam['reviewer_id']) ? 0 : $aParam['reviewer_id'];
|
||||
if(empty($iReviewerId)){
|
||||
return json_encode(['status' => 2,'msg' => 'Please select a reviewer']);
|
||||
}
|
||||
//主键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 = isset($aParam['state']) ? intval($aParam['state']) : '-1';
|
||||
if(!in_array($iState, [0,1,2,3,4])){
|
||||
return json_encode(['status' => 2,'msg' => 'Illegal review status']);
|
||||
}
|
||||
|
||||
//判断审稿记录
|
||||
$aWhere = ['reviewer_id' => $iReviewerId,'article_id' => $iArticleId,'id' => $iId];
|
||||
$aReviewerFinal = Db::name('article_reviewer_final')->field('state')->where($aWhere)->find();
|
||||
if(empty($aReviewerFinal)){
|
||||
return json_encode(['status' => 3,'msg' => 'Review record does not exist']);
|
||||
}
|
||||
//判断记录状态
|
||||
if(!in_array($aReviewerFinal['state'], [0,5])){
|
||||
return json_encode(['status' => 4,'msg' => 'Reviewer processed']);
|
||||
}
|
||||
if($aReviewerFinal['state'] == 5){//邀请审稿:0同意4拒绝
|
||||
if(!in_array($iState, [0,4])){
|
||||
return json_encode(['status' => 5,'msg' => 'Illegal review status']);
|
||||
}
|
||||
$aUpdate = ['state' => $iState,'update_time' => time()];
|
||||
if($iState == 0){
|
||||
$aUpdate['agree_review_time'] = time();
|
||||
}
|
||||
if($iState == 4){
|
||||
$aUpdate['refuse_review_time'] = time();
|
||||
}
|
||||
}
|
||||
if($aReviewerFinal['state'] == 0){//同意审稿之后更新状态1接收2拒绝3退修
|
||||
if(!in_array($iState, [1,2,3])){
|
||||
return json_encode(['status' => 6,'msg' => 'Illegal review status']);
|
||||
}
|
||||
$suggest_for_editor = empty($aParam['suggest_for_editor']) ? '' : $aParam['suggest_for_editor'];
|
||||
$suggest_for_author = empty($aParam['suggest_for_author']) ? '' : $aParam['suggest_for_author'];
|
||||
$aUpdate = ['state' => $iState,'update_time' => time(),'review_time' => time(),'suggest_for_editor' => $suggest_for_editor,'suggest_for_author' => $suggest_for_author];
|
||||
}
|
||||
|
||||
if(empty($aUpdate)){
|
||||
return json_encode(['status' => 7,'msg' => 'Illegal request']);
|
||||
}
|
||||
|
||||
//数据库更新
|
||||
$aWhere = ['id' => $iId];
|
||||
$result = Db::name('article_reviewer_final')->where($aWhere)->limit(1)->update($aUpdate);
|
||||
if(!$result){
|
||||
json_encode(['status' => 8,'msg' => "Review failed"]);
|
||||
}
|
||||
//返回结果
|
||||
return json_encode(['status' => 1,'msg' => "Reviewed successfully"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 获取审稿信息
|
||||
* @param record_id 记录ID
|
||||
* @param state 状态
|
||||
*/
|
||||
public function get(){
|
||||
//获取参数
|
||||
$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,'state' => 0];
|
||||
$aReviewerFinal = Db::name('article_reviewer_final')->field('article_id')->where($aWhere)->find();
|
||||
if(empty($aReviewerFinal)){
|
||||
return json_encode(['status' => 3,'msg' => 'Review record does not exist or review has been completed']);
|
||||
}
|
||||
//查询文章相关信息
|
||||
$aWhere = ['article_id' => $aReviewerFinal['article_id'],'journal_id' => $aBoard,'query' => ['review','repeat','final','response']];
|
||||
return $this->getReviewRecord($aWhere);
|
||||
}
|
||||
/**
|
||||
* @title 查看审稿记录
|
||||
* @param record_id 记录ID
|
||||
* @param state 状态
|
||||
*/
|
||||
public function view(){
|
||||
//获取参数
|
||||
$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']);
|
||||
}
|
||||
//查询审稿记录
|
||||
$aWhere = ['id' => $iId];
|
||||
$aReviewerFinal = Db::name('article_reviewer_final')->field('article_id,state,suggest_for_editor,suggest_for_author,update_time,reviewer_id')->where($aWhere)->find();
|
||||
if(empty($aReviewerFinal)){
|
||||
return json_encode(['status' => 3,'msg' => 'No review records found']);
|
||||
}
|
||||
$aReviewerFinal['update_time'] = date('Y-m-d H:i:s',$aReviewerFinal['update_time']);
|
||||
//查询审稿人信息
|
||||
$aWhere = ['user_id' => $aReviewerFinal['reviewer_id']];
|
||||
$aUser = Db::name('user')->field('email,realname,localname')->where($aWhere)->find();
|
||||
if(!empty($aUser)){
|
||||
$aReviewerFinal += $aUser;
|
||||
}
|
||||
//查询文章相关信息
|
||||
$aWhere = ['article_id' => $aReviewerFinal['article_id'],'query' => ['review','repeat','response']];
|
||||
$aResult = json_decode($this->getReviewRecord($aWhere),true);
|
||||
$aArticle = empty($aResult['data']) ? [] : $aResult['data'];
|
||||
if(empty($aArticle)){
|
||||
return json_encode($aResult);
|
||||
}
|
||||
$aArticle['article_final'] = $aReviewerFinal;
|
||||
return json_encode(['status' => 1,'msg' => 'success','data' => $aArticle]);
|
||||
}
|
||||
/**
|
||||
* @title 获取文章信息及审核信息
|
||||
* @param article_id 文章ID
|
||||
* @param reviewer_id 审稿人ID
|
||||
* @param record_id 记录ID
|
||||
* @param state 状态
|
||||
*/
|
||||
public function getReviewRecord($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']);
|
||||
}
|
||||
//查询内容
|
||||
$sQuery = empty($aParam['query']) ? ['review'] : $aParam['query'];
|
||||
//查询文章
|
||||
$aWhere = ['article_id' => $iArticleId];
|
||||
$aArticle = Db::name('article')->field('article_id,journal_id,state,accept_sn,title,abstrart,keywords,type,ctime')->where($aWhere)->find();
|
||||
if(empty($aArticle)){
|
||||
return json_encode(['status' => 3,'msg' => 'The query article does not exist']);
|
||||
}
|
||||
//文章类型
|
||||
$aArticle['atype'] = empty($aArticle['type']) ? '' : translateType($aArticle['type']);
|
||||
$aArticle['ctime'] = empty($aArticle['ctime']) ? '' : date('Y-m-d H:i:s',$aArticle['ctime']);
|
||||
//获取文章领域
|
||||
$oArticle = new \app\common\Article;
|
||||
$aMajor = $oArticle->getArticleField($aParam);
|
||||
$aMajor = empty($aMajor['data']) ? [] : array_unique(array_column($aMajor['data'], 'major_id'));
|
||||
if(!empty($aMajor)){
|
||||
foreach ($aMajor as $key => $value) {
|
||||
$aMajorData[] = getMajorStr($value);
|
||||
}
|
||||
}
|
||||
$aArticle['major'] = empty($aMajorData) ? [] : $aMajorData;
|
||||
//判断期刊ID是否符合
|
||||
if(!empty($aParam['journal_id']) && !in_array($aArticle['journal_id'], $aParam['journal_id'])){
|
||||
return json_encode(['status' => 4,'msg' => 'The journal to which the reviewer belongs does not match the article. Please confirm']);
|
||||
}
|
||||
//判断文章状态是否进入终审
|
||||
$iArticleState = !isset($aArticle['state']) ? '' : $aArticle['state'];
|
||||
if($iArticleState != 8){
|
||||
return json_encode(['status' => 5,'msg' => 'The article has not entered the final review stage']);
|
||||
}
|
||||
|
||||
//查询文章审稿记录
|
||||
$aWhere = ['article_id' => $iArticleId,'state' => ['between',[1,3]]];
|
||||
$aArticleReviewer = Db::name('article_reviewer')->field('art_rev_id,reviewer_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')->where($aWhere)->order('ctime asc')->select();
|
||||
$aQuestion = empty($aQuestion) ? [] : array_column($aQuestion, null,'art_rev_id');
|
||||
|
||||
//查询复审
|
||||
if(in_array('repeat', $sQuery)){
|
||||
$aWhere = ['art_rev_id' => ['in',$aArtRevId],'recommend' => ['between',[1,3]]];
|
||||
$aReviewerRepeat = Db::name('article_reviewer_repeat')->field('art_rev_id,recommend,content,ctime,stime')->where($aWhere)->select();
|
||||
$aReviewerRepeat = empty($aReviewerRepeat) ? [] : array_column($aReviewerRepeat, null,'art_rev_id');
|
||||
}
|
||||
|
||||
//查询用户信息
|
||||
$aUserId = array_column($aArticleReviewer, 'reviewer_id');
|
||||
$aWhere = ['user_id' => ['in',$aUserId],'state' => 0];
|
||||
$aUser = Db::name('user')->field('user_id,realname')->where($aWhere)->select();
|
||||
$aUser = empty($aUser) ? [] : array_column($aUser, 'realname','user_id');
|
||||
}
|
||||
//数据处理
|
||||
$aReviewerData = $aReviewerRepeatData = [];
|
||||
foreach ($aArticleReviewer as $key => $value) {
|
||||
//初审答卷
|
||||
$aQuestionInfo = empty($aQuestion[$value['art_rev_id']]) ? [] : $aQuestion[$value['art_rev_id']];
|
||||
$value['realname'] = empty($aUser[$value['reviewer_id']]) ? '' : $aUser[$value['reviewer_id']];
|
||||
$value['ctime'] = empty($aQuestionInfo['ctime']) ? date('Y-m-d H:i:s',$value['ctime']) : date('Y-m-d H:i:s',$aQuestionInfo['ctime']);
|
||||
$value['is_anonymous'] = empty($aQuestionInfo['is_anonymous']) ? 0 : $aQuestionInfo['is_anonymous'];
|
||||
$value['question'] = $aQuestionInfo;
|
||||
$aReviewerData[] = $value;
|
||||
|
||||
//复审
|
||||
$aReviewerRepeatInfo = empty($aReviewerRepeat[$value['art_rev_id']]) ? [] : $aReviewerRepeat[$value['art_rev_id']];
|
||||
if(empty($aReviewerRepeatInfo)){
|
||||
continue;
|
||||
}
|
||||
if(!empty($aReviewerRepeatInfo['ctime'])){
|
||||
$aReviewerRepeatInfo['ctime'] = date('Y-m-d H:i:s',$aReviewerRepeatInfo['ctime']);
|
||||
}
|
||||
if(!empty($aReviewerRepeatInfo['stime'])){
|
||||
$aReviewerRepeatInfo['stime'] = date('Y-m-d H:i:s',$aReviewerRepeatInfo['stime']);
|
||||
}
|
||||
unset($value['ctime']);
|
||||
$aReviewerRepeatData[] = array_merge($aReviewerRepeatInfo,$value);
|
||||
}
|
||||
|
||||
//查询作者回复
|
||||
if(in_array('response', $sQuery)){
|
||||
$aWhere = ['article_id' => $iArticleId,'artr_state' => 0];
|
||||
$aResponse = Db::name('article_response_to_reviewer')->field('file_url,artr_ctime')->where($aWhere)->select();
|
||||
if(!empty($aResponse)){
|
||||
foreach ($aResponse as $key => $value) {
|
||||
$sFileUrl = empty($value['file_url']) ? '' : $value['file_url'];
|
||||
if(empty($sFileUrl)){
|
||||
continue;
|
||||
}
|
||||
$value['file_url'] = trim($this->sTouGaoUrl,'/').'/public/'.$sFileUrl;
|
||||
$value['artr_ctime'] = empty($value['artr_ctime']) ? '' : date('Y-m-d',$value['artr_ctime']);
|
||||
$aResponse[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//查询终审记录
|
||||
if(in_array('final', $sQuery)){
|
||||
$aWhere = ['article_id' => $iArticleId,'state' => ['between',[1,3]]];
|
||||
$aFinal = Db::name('article_reviewer_final')->field('id,reviewer_id,state,suggest_for_editor,suggest_for_author,review_time')->where($aWhere)->select();
|
||||
if(!empty($aFinal)){
|
||||
//查询用户信息
|
||||
$aUserId = array_column($aFinal, 'reviewer_id');
|
||||
$aWhere = ['user_id' => ['in',$aUserId],'state' => 0];
|
||||
$aUser = Db::name('user')->field('user_id,realname')->where($aWhere)->select();
|
||||
$aUser = empty($aUser) ? [] : array_column($aUser, 'realname','user_id');
|
||||
//数据处理
|
||||
foreach ($aFinal as $key => $value) {
|
||||
$aFinal[$key]['review_time'] = empty($value['review_time']) ? '' : date('Y-m-d H:i:s',$value['review_time']);
|
||||
$aFinal[$key]['realname'] = empty($aUser[$value['reviewer_id']]) ? '' : $aUser[$value['reviewer_id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
return json_encode(['status' => 1,'msg' => 'success','data' => ['article_review' => $aReviewerData,'article_review_repeat' => empty($aReviewerRepeatData) ? [] : $aReviewerRepeatData,'article_response' => empty($aResponse) ? [] : $aResponse,'article_final' => empty($aFinal) ? [] : $aFinal,'article' => $aArticle]]);
|
||||
}
|
||||
/**
|
||||
* @title 获取终审文章列表
|
||||
* @param article_id 文章ID
|
||||
*/
|
||||
public function articleLists(){
|
||||
|
||||
//接受参数
|
||||
$aParam = $this->request->post();
|
||||
|
||||
//获取登录账号信息
|
||||
$sUserName = empty($aParam['username']) ? '' : $aParam['username'];
|
||||
if(empty($sUserName)){
|
||||
return json_encode(['status' => 1,'msg' => 'Please log in to your account','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
|
||||
//查询条件数组
|
||||
$aWhere = [];
|
||||
//期刊ID
|
||||
$iJournalId = empty($aParam['journal_id']) ? 0 : $aParam['journal_id'];
|
||||
if (empty($iJournalId)) { //全部期刊
|
||||
$aUserInfo = Db::name('user')->field('user_id')->where(['account' => $sUserName])->find();
|
||||
if(empty($aUserInfo)){
|
||||
return json_encode(['status' => 1,'msg' => 'User does not exist','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
//获取管理期刊信息
|
||||
$aJournalId = Db::name('journal')->where(['editor_id' => $aUserInfo['user_id']])->column('journal_id');
|
||||
if(empty($aJournalId)){
|
||||
return json_encode(['status' => 1,'msg' => 'No journals managed by this account were found','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
$aWhere['journal_id'] = ['in', $aJournalId];
|
||||
$aParam['journal_id'] = $aJournalId;
|
||||
}else {
|
||||
$aWhere['journal_id'] = $iJournalId;
|
||||
}
|
||||
//受理流水号
|
||||
$sAcceptSn = empty($aParam['accept_sn']) ? '' : trim($aParam['accept_sn']);
|
||||
if(!empty($sAcceptSn)){
|
||||
$aWhere['accept_sn'] = $sAcceptSn;
|
||||
}
|
||||
//状态
|
||||
if(isset($aParam['state'])){
|
||||
$aWhere['state'] = $aParam['state'];
|
||||
}
|
||||
//标题
|
||||
$sTitle = empty($aParam['title']) ? '' : $aParam['title'];
|
||||
if (!empty($sTitle)) {
|
||||
$aWhere['title'] = ['like',"%".$sTitle."%"];
|
||||
}
|
||||
|
||||
//获取分页相关参数
|
||||
$iSize = empty($aParam['size']) ? 15 : $aParam['size'];//每页显示条数
|
||||
$iPage = empty($aParam['page']) ? 1 : $aParam['page'];// 当前页码
|
||||
|
||||
//获取总条数
|
||||
$iCount = Db::name('article')->where($aWhere)->count();
|
||||
if(empty($iCount)){
|
||||
return json_encode(['status' => 1,'msg' => 'No articles found that meet the criteria','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
//判断页数是否超过最大分页限制
|
||||
$iPageNum = ceil($iCount/$iSize);
|
||||
if($iPage > $iPageNum){
|
||||
return json_encode(['status' => 1,'msg' => 'The number of pages has exceeded the limit, maximum page number:'.$iPageNum,'data' => ['total' => $iCount,'lists' => []]]);
|
||||
}
|
||||
|
||||
//获取列表数据
|
||||
$aArticle = Db::name('article')
|
||||
->field('article_id,user_id,journal_id,accept_sn,title,type,keywords,abstrart,manuscirpt_url,special_num,remarks,state,scoring')
|
||||
->where($aWhere)
|
||||
->order('article_id desc')
|
||||
->page($iPage, $iSize)
|
||||
->select();
|
||||
if(empty($aArticle)){
|
||||
return json_encode(['status' => 1,'msg' => 'No articles found that meet the criteria','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
|
||||
//查询作者信息
|
||||
$aUserId = array_column($aArticle, 'user_id');
|
||||
$aWhere = ['user_id' => ['in',$aUserId]];
|
||||
$aUser = Db::name('user')->field('user_id,email,realname,phone')->where($aWhere)->select();
|
||||
$aUser = empty($aUser) ? [] : array_column($aUser, null,'user_id');
|
||||
|
||||
//查询期刊信息
|
||||
$aJournalId = array_column($aArticle, 'journal_id');
|
||||
$aWhere = ['journal_id' => ['in',$aJournalId]];
|
||||
$aJournal = Db::name('journal')->where($aWhere)->column('journal_id,title');
|
||||
|
||||
//AI审核内容
|
||||
$aArticleId = array_column($aArticle, 'article_id');
|
||||
$aWhere = ['article_id' => ['in',$aArticleId]];
|
||||
$aAiReview = Db::name('article_ai_review')->field('article_id,content')->where($aWhere)->column('article_id,content');
|
||||
|
||||
//查询终审信息
|
||||
$aFinal = Db::name('article_reviewer_final')->field('id,article_id,reviewer_id,state,reviewer_type')->where($aWhere)->select();
|
||||
$aUserId = empty($aFinal) ? [] : array_unique(array_column($aFinal, 'reviewer_id'));
|
||||
|
||||
//查询作者信息
|
||||
$aUserId += array_column($aArticle, 'user_id');
|
||||
$aWhere = ['user_id' => ['in',$aUserId]];
|
||||
$aUser = Db::name('user')->field('user_id,email,realname,phone,google_index')->where($aWhere)->select();
|
||||
$aUser = empty($aUser) ? [] : array_column($aUser, null,'user_id');
|
||||
|
||||
//终审数据处理
|
||||
$aFinalData = [];
|
||||
if(!empty($aFinal)){
|
||||
foreach ($aFinal as $key => $value) {
|
||||
$value['realname'] = empty($aUser[$value['reviewer_id']]['realname']) ? '' : $aUser[$value['reviewer_id']]['realname'];
|
||||
$aFinalData[$value['article_id']][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
//查询通讯作者信息
|
||||
$aReportData = $aAuthorData = $aCountryData = [];
|
||||
$aWhere = ['article_id' => ['in',$aArticleId],'state' => 0];
|
||||
$aAuthor = Db::name('article_author')->field('art_aut_id,article_id,firstname,lastname,country,email,is_report')->where($aWhere)->select();
|
||||
if(!empty($aAuthor)){
|
||||
$aEmail = array_unique(array_column($aAuthor, 'email'));
|
||||
$aWhere = ['email' => ['in',$aEmail]];
|
||||
$aUserData = Db::name('user')->field('user_id,email,realname,phone,google_index')->where($aWhere)->select();
|
||||
$aUserData = empty($aUserData) ? [] : array_column($aUserData, null,'email');
|
||||
|
||||
//数据处理
|
||||
foreach ($aAuthor as $key => $value) {
|
||||
if($value['is_report'] == 1){
|
||||
$aUserInfo = empty($aUserData[$value['email']]) ? [] : $aUserData[$value['email']];
|
||||
if(!empty($aUserInfo)){
|
||||
$value['author_account'] = $aUserInfo;
|
||||
$aReportData[$value['article_id']][] = $value;
|
||||
}
|
||||
}
|
||||
$aAuthorData[$value['article_id']][] = $value;
|
||||
$aCountryData[$value['article_id']][] = $value['country'];
|
||||
}
|
||||
}
|
||||
|
||||
//查询文件
|
||||
$aWhere = ['article_id' => ['in',$aArticleId],'state' => 0];
|
||||
$aFile = Db::name('article_file')->field('file_id,article_id,file_url,type_name,ctime')->where($aWhere)->select();
|
||||
$aFileData = [];
|
||||
if(!empty($aFile)){
|
||||
foreach ($aFile as $key => $value) {
|
||||
$value['ctime'] = date('Y-m-d');
|
||||
$aFileData[$value['article_id']][$value['type_name']][] = $value;
|
||||
}
|
||||
}
|
||||
//数据处理
|
||||
$aArticleLists = [];
|
||||
foreach ($aArticle as $key => $value) {
|
||||
|
||||
//终审
|
||||
$value['final'] = empty($aFinalData[$value['article_id']]) ? [] : $aFinalData[$value['article_id']];
|
||||
//作者信息
|
||||
if(!empty($aUser[$value['user_id']])){
|
||||
$value += $aUser[$value['user_id']];
|
||||
}
|
||||
//AI审稿
|
||||
$value['ai_review'] = empty($aAiReview[$value['article_id']]) ? '' : $aAiReview[$value['article_id']];
|
||||
//期刊名
|
||||
$value['journalname'] = empty($aJournal[$value['journal_id']]) ? '' : $aJournal[$value['journal_id']];
|
||||
|
||||
//查询作者信息
|
||||
$value['author'] = empty($aAuthorData[$value['article_id']]) ? [] : $aAuthorData[$value['article_id']];
|
||||
|
||||
//查询通讯作者信息
|
||||
$value['reports'] = empty($aReportData[$value['article_id']]) ? [] : $aReportData[$value['article_id']];
|
||||
|
||||
//国家
|
||||
$value['country'] = empty($aCountryData[$value['article_id']]) ? [] : array_unique($aCountryData[$value['article_id']]);
|
||||
|
||||
//文件
|
||||
$value['file'] = empty($aFileData[$value['article_id']]) ? [] : $aFileData[$value['article_id']];
|
||||
|
||||
|
||||
//H指数
|
||||
$iGindex = 0;
|
||||
$iUserId = empty($value['user_id']) ? 0 : $value['user_id'];
|
||||
$sEmail = '';
|
||||
if(!empty($value['reports'])){
|
||||
foreach ($value['reports'] as $val) {
|
||||
if ($val['author_account']['google_index'] >= $iGindex) {
|
||||
$iUserId = empty($val['author_account']['user_id']) ? $iUserId : $val['author_account']['user_id'];
|
||||
$iGindex = empty($val['author_account']['google_index']) ? $iGindex : $val['author_account']['google_index'];
|
||||
$sEmail = empty($val['author_account']['email']) ? $iGindex : $val['author_account']['email'];
|
||||
}
|
||||
}
|
||||
}
|
||||
//查询H指数添加人信息
|
||||
if(!empty($sEmail)){
|
||||
$value['H'] = empty($aUserData[$sEmail]) ? [] : $aUserData[$sEmail];
|
||||
}else{
|
||||
$value['H'] = empty($aUser[$iUserId]) ? [] : $aUser[$iUserId];
|
||||
}
|
||||
|
||||
$aArticleLists[] = $value;
|
||||
}
|
||||
|
||||
//统计各个状态下的数量
|
||||
$aWhere = ['state' => ['between',[0,7]]];
|
||||
if(!empty($aParam['journal_id'])){
|
||||
$aWhere['journal_id'] = ['in',$aParam['journal_id']];
|
||||
}
|
||||
$aCountNum = Db::name('article')->field("state,count(*) as num")->where($aWhere)->group("state")->select();
|
||||
$aCountNum = empty($aCountNum) ? [] : array_column($aCountNum, 'num','state');
|
||||
$aCountNumData = [];
|
||||
for ($i = 0; $i <= 7; $i++) {
|
||||
$aCountNumData[$i] = empty($aCountNum[$i]) ? 0 : $aCountNum[$i];
|
||||
}
|
||||
return json_encode(['status' => 1,'msg' => 'success','data' => ['total' => $iCount,'lists' => $aArticleLists,'count_num' => $aCountNumData]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 获取已邀请的审稿人
|
||||
* @param article_id 文章ID
|
||||
*/
|
||||
public function inviteBoardLists(){
|
||||
|
||||
//获取参数
|
||||
$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']);
|
||||
}
|
||||
|
||||
//获取分页相关参数
|
||||
$iSize = empty($aParam['size']) ? 15 : $aParam['size'];//每页显示条数
|
||||
$iPage = empty($aParam['page']) ? 1 : $aParam['page'];// 当前页码
|
||||
|
||||
//查询文章
|
||||
$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']);
|
||||
}
|
||||
//获取期刊ID
|
||||
$iJournalId = empty($aArticle['journal_id']) ? '' : $aArticle['journal_id'];
|
||||
if(empty($iJournalId)){
|
||||
return json_encode(['status' => 3,'msg' => 'The article did not select a journal']);
|
||||
}
|
||||
//判断文章状态是否进入终审
|
||||
$iArticleState = !isset($aArticle['state']) ? '' : $aArticle['state'];
|
||||
if($iArticleState != 8){
|
||||
return json_encode(['status' => 4,'msg' => 'The article has not entered the final review stage']);
|
||||
}
|
||||
|
||||
//编委类型0主编1副主编2编委成员
|
||||
if(isset($aParam['reviewer_type'])){
|
||||
$aWhere['sQuery.reviewer_type'] = $aParam['reviewer_type'];
|
||||
}
|
||||
//组装sql
|
||||
$sQuery = Db::name('article_reviewer_final')->where($aWhere)->buildSql();
|
||||
|
||||
//email
|
||||
$aWhere = [];
|
||||
if(!empty($aParam['email'])){
|
||||
$aWhere['t_user.email'] = ['like', "%".$aParam["email"] . "%"];
|
||||
}
|
||||
|
||||
//统计数量
|
||||
$iCount = Db::table("({$sQuery}) sQuery")
|
||||
->join('t_user', 'sQuery.reviewer_id = t_user.user_id')->where($aWhere)->count();
|
||||
if(empty($iCount)){
|
||||
return json_encode(['status' => 1,'msg' => 'Reviewer not found','data' => ['total' => 0,'lists' => []]]);
|
||||
}
|
||||
|
||||
//判断页数是否超过最大分页限制
|
||||
$iPageNum = ceil($iCount/$iSize);
|
||||
if($iPage > $iPageNum){
|
||||
return json_encode(['status' => 1,'msg' => 'The number of pages has exceeded the limit, maximum page number:'.$iPageNum,'data' => ['total' => $iCount,'lists' => []]]);
|
||||
}
|
||||
|
||||
//查询记录
|
||||
$sOrder = empty($aParam['order']) ? 'type desc,user_id desc' : $aParam['order'];//排序
|
||||
$aLists = Db::table("({$sQuery}) sQuery")
|
||||
->join('t_user', 'sQuery.reviewer_id = t_user.user_id')
|
||||
->field('sQuery.id,sQuery.reviewer_id,sQuery.reviewer_type,sQuery.state,sQuery.suggest_for_author,sQuery.suggest_for_editor,sQuery.update_time,t_user.account,t_user.email,t_user.realname,t_user.localname')
|
||||
->where($aWhere)
|
||||
->order($sOrder)
|
||||
->page($iPage, $iSize)
|
||||
->select();
|
||||
return json_encode(['status' => 1,'msg' => 'success','data' => ['total' => $iCount,'lists' => $aLists]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @title 获取文章文件
|
||||
* @param article_id 文章ID
|
||||
*/
|
||||
public function getArticleFile(){
|
||||
|
||||
//获取参数
|
||||
$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']);
|
||||
}
|
||||
|
||||
//查询审稿记录
|
||||
$aWhere = ['id' => $iId];
|
||||
$aReviewerFinal = Db::name('article_reviewer_final')->field('article_id,state')->where($aWhere)->find();
|
||||
if(empty($aReviewerFinal)){
|
||||
return json_encode(['status' => 3,'msg' => 'No review records found']);
|
||||
}
|
||||
//文章已审核
|
||||
if($aReviewerFinal['state'] != 0){
|
||||
return json_encode(['status' => 4,'msg' => 'The article has been reviewed']);
|
||||
}
|
||||
//查询文章信息
|
||||
$iArticleId = empty($aReviewerFinal['article_id']) ? 0 : $aReviewerFinal['article_id'];
|
||||
$aWhere = ['article_id' => $iArticleId];
|
||||
$aArticle = Db::name('article')->field('article_id')->where($aWhere)->find();
|
||||
if(empty($aArticle)){
|
||||
return json_encode(['status' => 3,'msg' => 'The query article does not exist']);
|
||||
}
|
||||
// 定义需要查询的文件类型
|
||||
$aType = ['manuscirpt', 'picturesAndTables', 'supplementary'];
|
||||
|
||||
// 初始化结果数组,确保每个类型都有默认空数组
|
||||
$aData = array_fill_keys($aType, []);
|
||||
|
||||
// 获取每个类型的最大file_id(最新记录)
|
||||
$aWhere = ['state' => 0,'type_name' => ['in',$aType],'article_id' => $iArticleId];
|
||||
$sQuery = Db::name('article_file')
|
||||
->field('type_name, MAX(file_id) AS max_file_id')
|
||||
->where($aWhere)
|
||||
->group('type_name')
|
||||
->buildSql();
|
||||
|
||||
//关联查询获取完整信息
|
||||
$aFileList = Db::name('article_file')
|
||||
->alias('article_file')
|
||||
->join([$sQuery => 'sQuery'], 'article_file.file_id = sQuery.max_file_id', 'INNER')
|
||||
->field('article_file.type_name, article_file.file_id, article_file.file_url, article_file.ctime')
|
||||
->select();
|
||||
|
||||
// 数据处理
|
||||
if (!empty($aFileList)) {
|
||||
foreach ($aFileList as $value) {
|
||||
$type = empty($value['type_name']) ? '' : $value['type_name'];
|
||||
$aData[$type] = [
|
||||
'file_id' => $value['file_id'],
|
||||
'file_url' => $value['file_url'],
|
||||
'ctime' => empty($value['ctime']) ? '' : date('Y-m-d',$value['ctime'])
|
||||
];
|
||||
}
|
||||
}
|
||||
return json_encode(['status' => 1,'msg' => 'success','data' => $aData]);
|
||||
}
|
||||
/**
|
||||
* @title 审稿链接创建
|
||||
* @param reviewer_id 审稿人ID
|
||||
* @param record_id 审稿记录主键ID
|
||||
* @param is_agree 是否同意1是2否
|
||||
*/
|
||||
private function createReviewUrl($reviewer_id = 0, $record_id = 0, $is_agree = 2)
|
||||
{
|
||||
if(empty($reviewer_id) || empty($record_id)){
|
||||
return '';
|
||||
}
|
||||
$code = md5(time() . rand(1000, 9999) . 'thinkphp');
|
||||
|
||||
//插入自动登录记录
|
||||
$aInsert = ['user_id' => $reviewer_id,'code' => $code,'ctime' => time()];
|
||||
$result = Db::name('login_auto')->insertGetId($aInsert);
|
||||
|
||||
//返回链接
|
||||
$sJumpUrl = 'edit_per_text_yq';//拒绝
|
||||
return trim($this->sTouGaoUrl,'/').'/'.$sJumpUrl.'?r_id=' . $record_id . '&act=' . $code;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -626,6 +626,19 @@ class Reviewer extends Base
|
||||
//文章类型转译
|
||||
$res['atype'] = translateType($res['atype']);
|
||||
|
||||
//查询文章状态是否为终审7 chengxiaoling 20250828 start
|
||||
$iArticleId = empty($res['article_id']) ? 0 : $res['article_id'];
|
||||
if(!empty($iArticleId)){
|
||||
//查询文章
|
||||
$aWhere = ['article_id' => $iArticleId,'state' => 8];
|
||||
$aArticle = Db::name('article')->field('article_id,state')->where($aWhere)->find();
|
||||
if(!empty($aArticle)){
|
||||
$res['can_repeat'] = 0;
|
||||
return json($res);
|
||||
}
|
||||
}
|
||||
//查询文章状态是否为终审7 chengxiaoling 20250828 end
|
||||
|
||||
//复审相关
|
||||
$repeats = $this->article_reviewer_repeat_obj->where('art_rev_id',$data['revid'])->select();
|
||||
if($repeats){
|
||||
@@ -1338,7 +1351,8 @@ class Reviewer extends Base
|
||||
$insert['code'] = $code;
|
||||
$insert['ctime'] = time();
|
||||
$this->login_auto_obj->insert($insert);
|
||||
return 'https://submission.tmrjournals.com/per_text?Art_id=' . $article_id . '&act=' . $code;
|
||||
$url = 'https://submission.tmrjournals.com/per_text?Art_id=' . $article_id . '&act=' . $code;
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1957,6 +1957,19 @@ class User extends Base
|
||||
$board_res = $this->board_to_journal_obj->where('user_id', $user_info['user_id'])->where('state', 0)->find();
|
||||
if ($board_res != null) {
|
||||
$roles[] = 'board';
|
||||
|
||||
//判断用户角色 0主编1副主编2编委成员 chengxiaoling 20250827 start
|
||||
$iType = isset($board_res['type']) ? $board_res['type'] : '-1';
|
||||
if($iType == 0){
|
||||
$roles[] = 'chief_editor';
|
||||
}
|
||||
if($iType == 1){
|
||||
$roles[] = 'deputy_editor';
|
||||
}
|
||||
if($iType == 2){
|
||||
$roles[] = 'editor_board';
|
||||
}
|
||||
//判断用户角色 0主编1副主编2编委成员 chengxiaoling 20250827 end
|
||||
}
|
||||
$special_res = $this->user_to_special_obj->where('user_id', $user_info['user_id'])->where("uts_state", 0)->find();
|
||||
if ($special_res != null) {
|
||||
|
||||
@@ -37,11 +37,12 @@ class ArticleAiCreateContent
|
||||
$job->delete();
|
||||
return;
|
||||
}
|
||||
$iPArticleId = empty($data['p_article_id']) ? 0 : $data['p_article_id'];
|
||||
try {
|
||||
|
||||
// 生成Redis键并尝试获取锁
|
||||
$sClassName = get_class($this);
|
||||
$sRedisKey = "queue_job:{$sClassName}:{$iArticleId}";
|
||||
$sRedisKey = "queue_job:{$sClassName}:{$iArticleId}:{$iPArticleId}";
|
||||
$sRedisValue = uniqid() . '_' . getmypid();
|
||||
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
|
||||
return; // 未获取到锁,已处理
|
||||
|
||||
@@ -32,6 +32,10 @@ class ArticleReview
|
||||
|
||||
// 获取文章ID
|
||||
$iArticleId = empty($data['article_id']) ? 0 : $data['article_id'];
|
||||
//问题等级
|
||||
$sQuestionLevel = empty($data['question_level']) ? 'A' : $data['question_level'];
|
||||
//需要处理的AI问题字段
|
||||
$sQuestionFields = empty($data['queue_fields']) ? 'journal_scope' : $data['queue_fields'];
|
||||
if (empty($iArticleId)) {
|
||||
$this->oQueueJob->log("无效的article_id,删除任务");
|
||||
$job->delete();
|
||||
@@ -41,7 +45,7 @@ class ArticleReview
|
||||
|
||||
// 生成Redis键并尝试获取锁
|
||||
$sClassName = get_class($this);
|
||||
$sRedisKey = "queue_job:{$sClassName}:{$iArticleId}";
|
||||
$sRedisKey = "queue_job:{$sClassName}:{$iArticleId}_{$sQuestionLevel}_{$sQuestionFields}";
|
||||
$sRedisValue = uniqid() . '_' . getmypid();
|
||||
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
|
||||
return; // 未获取到锁,已处理
|
||||
|
||||
@@ -40,10 +40,10 @@ class ArticleReviewForQueueChunk
|
||||
$job->delete();
|
||||
return;
|
||||
}
|
||||
|
||||
$sQuestionFields = empty($data['queue_fields']) ? 0 : $data['queue_fields'];
|
||||
// 生成Redis键并尝试获取锁
|
||||
$sClassName = get_class($this);
|
||||
$sRedisKey = "queue_job:{$sClassName}:{$iRedisId}:{$sChunkIndex}";
|
||||
$sRedisKey = "queue_job:{$sClassName}:{$iRedisId}:{$sQuestionFields}:{$sChunkIndex}";
|
||||
$sRedisValue = uniqid() . '_' . getmypid();
|
||||
if (!$this->oQueueJob->acquireLock($sRedisKey, $sRedisValue, $job)) {
|
||||
return; // 未获取到锁,已处理
|
||||
|
||||
@@ -574,12 +574,13 @@ class Material
|
||||
//记录处理开始
|
||||
$iNum = count($aUploadStep);
|
||||
$sRedisKey = 'queue_material_job:upload:'.$iArticleId;
|
||||
$result = $this->oQueueRedis->recordProcessingStart($sRedisKey,$iNum);
|
||||
$result = $this->oQueueRedis->recordQuestionProcessingStart($sRedisKey,$iNum);
|
||||
$result = empty($result) ? 0 : $result;
|
||||
if($result == 1){
|
||||
foreach ($aUploadStep as $key => $value) {
|
||||
$aParam = empty($value['params']) ? [] : $value['params'];
|
||||
$aParam += ['chunkIndex' => $key+1,'func_name' => empty($value['func_name']) ? '' : $value['func_name']];
|
||||
$aParam['key_name'] = 'queue_1_completed';
|
||||
Queue::push('app\api\job\uploadMaterialStep@fire', $aParam, 'uploadMaterialStep');
|
||||
}
|
||||
//返回结果
|
||||
@@ -610,15 +611,21 @@ class Material
|
||||
return json_encode(['status' => 2, 'msg' => 'The execution method is null']);
|
||||
}
|
||||
|
||||
//更新处理进度
|
||||
$iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex'];
|
||||
$sRedisKey = 'queue_material_job:upload:'.$iId;
|
||||
$sChunkProgress = $this->oQueueRedis->getChunkProgress($sRedisKey, $iIndex);
|
||||
if(!empty($sChunkProgress)){
|
||||
return json_encode(['status' => 3, 'msg' => $sRedisKey.'_'.$iIndex.' Uploaded']);
|
||||
}
|
||||
//请求方法上传
|
||||
$aResult = json_decode($this->$sFuncName($aParam),true);
|
||||
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
|
||||
$sMsg = empty($aResult['msg']) ? 'upload fail' : $aResult['msg'];
|
||||
|
||||
//更新处理进度
|
||||
$iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex'];
|
||||
$sRedisKey = 'queue_material_job:upload:'.$iId;
|
||||
$iProgress = $this->oQueueRedis->updateProcessingProgress($sRedisKey,$iIndex);
|
||||
$sKeyName = empty($aParam['key_name']) ? 'queue_1_completed' : $aParam['key_name'];
|
||||
$iProgress = $this->oQueueRedis->updateQuestionProcessingProgress($sRedisKey,$sKeyName);
|
||||
//保存内容
|
||||
$sRedisKey = 'queue_job:article_material_upload_progress:'.$iId;
|
||||
$this->oQueueRedis->saveChunkProgress($sRedisKey, $iIndex,json_encode($aResult));
|
||||
@@ -628,7 +635,7 @@ class Material
|
||||
$aLog = json_decode($this->getWechatLog($aWhere),true);
|
||||
$aLog = empty($aLog['data']) ? [] : $aLog['data'];
|
||||
if(!empty($aLog)){
|
||||
$sMsg .= empty($aLog['msg']) ? '' : ';'.$aLog['msg'];
|
||||
$sMsg .= empty($aLog['msg']) ? '' : "\n".$aLog['msg'];
|
||||
}
|
||||
$sStatusName = 'processing';
|
||||
if($iStatus != 1) {
|
||||
@@ -649,6 +656,10 @@ class Material
|
||||
$aWhere['log_id'] = $aLog['log_id'];
|
||||
$result = Db::name('wechat_api_log')->where($aWhere)->limit(1)->update($aUpdate);
|
||||
}
|
||||
if($iProgress >= 100){//上传素材完成后【1分钟后推送草稿箱】
|
||||
$iDelaySeconds = 60;// 1分钟的秒数
|
||||
Queue::later($iDelaySeconds,'app\api\job\WechatDraft@fire', ['article_id' => $iId], 'WechatDraft')
|
||||
}
|
||||
return json_encode($aResult);
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ class OpenAi
|
||||
"discussion" => "[针对讨论进行简单总结,采用连贯的段落形式,注意内容不要和文章内容有严重重复,总字数超过450字]",
|
||||
"research_method" => "[针对研究方法进行简单总结,采用连贯的段落形式,注意内容不要和文章内容有严重重复,总字数超过300字]",
|
||||
"discussion" => "[针对讨论进行简单总结,采用连贯的段落形式,注意内容不要和文章内容有严重重复,总字数超过450字]",
|
||||
"conclusion" => "[针对结论进行简单总结,采用连贯的段落形式,注意内容不要和文章内容有严重重复,总字数超过450字]",
|
||||
"conclusion" => "[将结论直接翻译成中文,无需针对内容总结翻译]",
|
||||
],
|
||||
'review' => [
|
||||
"overview" => "按照学术规范翻译并提炼文章概述,整体内容应大于1200字,其中应包含文章背景(不少于400字),其他内容提炼更强调逻辑性、科学术语准确性和表达的严谨性,注意内容不要和文章内容有严重重复,采用连贯的段落形式",
|
||||
@@ -54,9 +54,9 @@ class OpenAi
|
||||
],
|
||||
'B' => [
|
||||
'ai_ethics' => '结合标题:title摘要:abstrart分析',
|
||||
'ethics' => '结合标题:title摘要:abstrart内容:content分析',
|
||||
'ethics' => '结合标题:title摘要:abstrart文章内容:content分析',
|
||||
'ai_registration' => '结合标题:title摘要:abstrart分析',
|
||||
'registration' => '标题:title摘要:abstrart内容content分析',
|
||||
'registration' => '标题:title摘要:abstrart文章内容content分析',
|
||||
'academic' => '分析content是否存在学术不端问题【包括但不限于:1. 抄袭:是否存在未经引用的重复内容2. 数据作假:数据是否矛盾、不合理或无来源3. 图片伪造:是否提及可疑的图片/图表(如无原始数据支持)】',
|
||||
'contradiction' => '分析content是否存在前后矛盾或逻辑不一致的问题【包括但不限于1. 性别与疾病不匹配(如女性疾病出现男性患者、男性疾病出现女性患者)2. 数量不一致(如病人数量、动物数量在不同位置描述冲突)3. 种类不一致(如实验动物种类前后描述不同)4. 其他明显的逻辑矛盾(如时间线冲突、因果关系矛盾)】',
|
||||
'fund_number' => '分析content',
|
||||
@@ -82,16 +82,17 @@ class OpenAi
|
||||
'ethics_explanation' => "[结合标题和摘要解释说明是否需要伦理号][返回格式字符串]",
|
||||
],
|
||||
'ethics' => [
|
||||
'ethics_assessment' => "结合标题和摘要判断内容是否缺失伦理号或存在明显伦理问题[返回是/否]",
|
||||
'ethics_explanation' => "逐个回答以下问题【1.[解释说明是否需要伦理号]2.[结合标题和摘要判断内容是否存在伦理号缺失解释说明(无需解释摘要内容)]3.[结合标题和摘要判断内容是否存在明显伦理问题]】[返回格式字符串]",
|
||||
// 'ethics_assessment' => "结合标题和摘要判断文章内容是否存在伦理号缺失[返回是/否]",
|
||||
'ethics_explanation' => "逐个回答以下问题【1.[解释说明是否需要伦理号]2.[结合标题和摘要判断文章内容是否存在伦理号缺失解释说明(无需解释摘要内容)]3.[结合标题和摘要判断内容是否存在明显伦理问题]4.[解释说明内容是否存在伦理号]】[返回格式字符串]",
|
||||
'ethics_assessment' => "针对字段ethics_explanation里的四个问题答案分析文章内容否存在伦理号缺失或存在明显伦理问题[返回是/否]",
|
||||
],
|
||||
'ai_registration' => [
|
||||
'registration_assessment' => "结合标题和摘要判断是否需要临床注册号和知情同意书?[返回是/否]",
|
||||
'registration_explanation' => "[结合标题和摘要解释说明是否需要临床注册号和知情同意书][返回格式字符串]",
|
||||
'registration_assessment' => "结合标题和摘要判断是否需要临床注册号和知情同意书?[返回是/否]",
|
||||
],
|
||||
'registration' => [
|
||||
'registration_assessment' => "结合标题摘要判断内容是否缺失临床注册号和知情同意书[返回是/否]",
|
||||
'registration_explanation' => "逐个回答以下问题【1.[解释说明是否需要临床注册号和知情同意书]2.[分析内容是否存在临床注册号和知情同意书]】[返回格式字符串]",
|
||||
'registration_assessment' => "针对字段registration_assessment里的两个问题答案分析文章内容是否存在缺失临床注册号或知情同意书[返回是/否]",
|
||||
],
|
||||
'academic' => [
|
||||
'academic_assessment' => "是/否",
|
||||
@@ -306,7 +307,7 @@ class OpenAi
|
||||
$aMessage = [];
|
||||
|
||||
//特殊标题强制转译
|
||||
$aField = ['introduction' => 'research_background','background' => 'research_background','methods' => 'research_method','materials_and_methods' => 'research_method'];
|
||||
$aField = ['introduction' => 'research_background','background' => 'research_background','methods' => 'research_method','materials_and_methods' => 'research_method','Conclusions' => 'conclusion','conclusions' => 'conclusion','result' => 'results'];
|
||||
|
||||
//组装问题
|
||||
foreach ($aParam as $key => $value) {
|
||||
@@ -385,7 +386,7 @@ class OpenAi
|
||||
$aMessage = [];
|
||||
|
||||
//特殊标题强制转译
|
||||
$aField = ['introduction' => 'research_background','background' => 'research_background','methods' => 'research_method','materials_and_methods' => 'research_method'];
|
||||
$aField = ['introduction' => 'research_background','background' => 'research_background','methods' => 'research_method','materials_and_methods' => 'research_method','Conclusions' => 'conclusion','conclusions' => 'conclusion','result' => 'results'];
|
||||
|
||||
//组装问题
|
||||
foreach ($aParam as $key => $value) {
|
||||
@@ -749,13 +750,19 @@ class OpenAi
|
||||
if (empty($aMessage)) {
|
||||
return json_encode(['status' => 2, 'msg' => 'AI Q&A content not obtained']);
|
||||
}
|
||||
|
||||
//判断是否执行
|
||||
$iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex'];
|
||||
$sRedisKey = 'queue_job:ai_create_article:'.$iId;
|
||||
$sChunkProgress = $this->oQueueRedis->getChunkProgress($sRedisKey, $iIndex);
|
||||
if(!empty($sChunkProgress)){
|
||||
return json_encode(['status' => 3, 'msg' => $sRedisKey.'_'.$iIndex.' Generated']);
|
||||
}
|
||||
//请求OPENAI
|
||||
$aParam['temperature'] = '0.6';
|
||||
$aParam['top_p'] = '0.8';
|
||||
$aResult = $this->curlOpenAIStream($aParam);
|
||||
//更新处理进度
|
||||
$iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex'];
|
||||
$sRedisKey = 'queue_job:ai_create_article:'.$iId;
|
||||
$sKeyName = empty($aParam['key_name']) ? 'queue_1_completed' : $aParam['key_name'];
|
||||
$iProgress = $this->oQueueRedis->updateQuestionProcessingProgress($sRedisKey,$sKeyName);
|
||||
//保存内容
|
||||
@@ -1135,14 +1142,19 @@ class OpenAi
|
||||
//问题等级
|
||||
$sQuestionLevel = empty($aParam['question_level']) ? '' : $aParam['question_level'];
|
||||
|
||||
//判断是否执行
|
||||
$iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex'];
|
||||
$sRedisKey = 'queue_job:review_chunk:'.$iArticleId.'_'.$iJournalId.'_'.$sFieldsName;
|
||||
$sChunkProgress = $this->oQueueRedis->getChunkProgress($sRedisKey, $iIndex);
|
||||
if(!empty($sChunkProgress)){
|
||||
return json_encode(['status' => 3, 'msg' => $sRedisKey.'_'.$iIndex.' AI Reviewed']);
|
||||
}
|
||||
//请求OPENAI
|
||||
$aParam['temperature'] = '0.1';
|
||||
$aParam['top_p'] = '0.9';
|
||||
$aResult = $this->curlOpenAIStream($aParam);
|
||||
|
||||
//更新处理进度
|
||||
$iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex'];
|
||||
$sRedisKey = 'queue_job:review_chunk:'.$iArticleId.'_'.$iJournalId.'_'.$sFieldsName;
|
||||
$sKeyName = empty($aParam['key_name']) ? 'queue_1_completed' : $aParam['key_name'];
|
||||
$iProgress = $this->oQueueRedis->updateQuestionProcessingProgress($sRedisKey,$sKeyName);
|
||||
//保存内容
|
||||
|
||||
@@ -347,11 +347,11 @@ LUA;
|
||||
}
|
||||
|
||||
// 获取分块进度
|
||||
public function getChunkProgress($key, $chunkIndex = '')
|
||||
public function getChunkProgress($key, $chunkIndex = '-1')
|
||||
{
|
||||
$redis = $this->connect();
|
||||
|
||||
if(empty($chunkIndex)){
|
||||
if($chunkIndex == '-1'){
|
||||
|
||||
$aChunkValue = $redis->hGetAll($key);
|
||||
}else{
|
||||
|
||||
263
application/common/traits/QueueDbHATraitBak.php
Normal file
263
application/common/traits/QueueDbHATraitBak.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
namespace app\common\traits;
|
||||
|
||||
use think\Db;
|
||||
use think\Exception;
|
||||
use think\db\PDOException;
|
||||
use think\facade\Log;
|
||||
|
||||
trait QueueDbHATraitBak
|
||||
{
|
||||
// 可配置参数(针对队列场景优化)
|
||||
private $dbConfig = [
|
||||
'check_interval' => 20, // 缩短检查间隔,提升连接有效性
|
||||
'max_attempts' => 4, // 增加重试次数,应对连接波动
|
||||
'base_wait' => 1, // 基础等待时间(秒)
|
||||
'reconnect_threshold' => 3, // 连续失败告警阈值
|
||||
'fatal_error_codes' => [2006, 2013, 1053], // 致命错误码(含服务中断)
|
||||
];
|
||||
|
||||
// 进程内状态记录(避免跨进程干扰)
|
||||
private $consecutiveFailures = [];
|
||||
private $lastCheckTime = [];
|
||||
|
||||
/**
|
||||
* 队列任务执行前的连接检查(核心入口)
|
||||
* @param bool $force 是否强制检查
|
||||
* @return bool 连接是否可用
|
||||
*/
|
||||
public function checkDbConnectionTrait($force = false)
|
||||
{
|
||||
$pid = getmypid();
|
||||
$this->initConsecutiveFailures($pid);
|
||||
|
||||
// 非强制检查且未到间隔,直接返回
|
||||
if (!$force && isset($this->lastCheckTime[$pid])
|
||||
&& (time() - $this->lastCheckTime[$pid] < $this->dbConfig['check_interval'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$attempt = 0;
|
||||
$maxAttempts = $this->dbConfig['max_attempts'];
|
||||
$baseWait = $this->dbConfig['base_wait'];
|
||||
|
||||
while ($attempt < $maxAttempts) {
|
||||
try {
|
||||
// 执行轻量查询验证(使用框架Db方法,确保与业务代码一致)
|
||||
$result = $this->safeQuery('SELECT 1 FROM DUAL', 2);
|
||||
if ($this->isValidResult($result)) {
|
||||
$this->resetConsecutiveFailures($pid);
|
||||
$this->lastCheckTime[$pid] = time();
|
||||
$this->log("进程[{$pid}]数据库连接有效", 'info');
|
||||
return true;
|
||||
}
|
||||
throw new Exception("查询结果无效");
|
||||
} catch (PDOException $e) {
|
||||
// 致命错误加速重试
|
||||
if (in_array($e->getCode(), $this->dbConfig['fatal_error_codes'])) {
|
||||
$this->log("进程[{$pid}]致命错误({$e->getCode()}):{$e->getMessage()}", 'error');
|
||||
$attempt = $maxAttempts - 1;
|
||||
}
|
||||
$this->handleConnectionError($e, $pid, $attempt, $maxAttempts, $baseWait);
|
||||
} catch (Exception $e) {
|
||||
$this->handleConnectionError($e, $pid, $attempt, $maxAttempts, $baseWait);
|
||||
} finally {
|
||||
$attempt++;
|
||||
}
|
||||
}
|
||||
|
||||
// 达到最大重试次数
|
||||
$this->incrementConsecutiveFailures($pid);
|
||||
$this->log("进程[{$pid}]连接异常,已达最大重试次数({$maxAttempts})", 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理连接错误(确保后续能正常使用Db::insert())
|
||||
*/
|
||||
private function handleConnectionError($e, $pid, &$attempt, $maxAttempts, $baseWait)
|
||||
{
|
||||
$errorMsg = $e->getMessage() ?: '未知错误';
|
||||
$errorCode = $e->getCode() ?: 0;
|
||||
$this->log("进程[{$pid}]连接失败(尝试{$attempt}/{$maxAttempts}):{$errorMsg}(码:{$errorCode})", 'warning');
|
||||
|
||||
// 强制清理当前进程的连接缓存(关键:确保重建的连接能被Db::insert()使用)
|
||||
$this->cleanupConnections();
|
||||
|
||||
// 最后一次尝试无需等待
|
||||
if ($attempt + 1 >= $maxAttempts) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 差异化等待策略
|
||||
$waitTime = $this->calculateWaitTime($errorMsg, $attempt, $baseWait);
|
||||
$this->log("进程[{$pid}]将在{$waitTime}秒后重试", 'info');
|
||||
$this->safeSleep($waitTime);
|
||||
|
||||
// 重建连接并验证(使用框架方法,确保与业务代码兼容)
|
||||
try {
|
||||
// 强制重建框架连接,确保Db::insert()能使用新连接
|
||||
Db::connect(config('database'), true);
|
||||
$result = $this->safeQuery('SELECT 1 FROM DUAL', 2);
|
||||
if ($this->isValidResult($result)) {
|
||||
$this->resetConsecutiveFailures($pid);
|
||||
$this->lastCheckTime[$pid] = time();
|
||||
$this->log("进程[{$pid}]连接已重建(尝试{$attempt}/{$maxAttempts})", 'info');
|
||||
$attempt = $maxAttempts; // 退出循环
|
||||
} else {
|
||||
throw new Exception("重建连接后查询无效");
|
||||
}
|
||||
} catch (Exception $e2) {
|
||||
$this->log("进程[{$pid}]重连失败:{$e2->getMessage()}", 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全执行查询(兼容框架Db方法,带超时控制)
|
||||
*/
|
||||
private function safeQuery($sql, $timeout)
|
||||
{
|
||||
$start = microtime(true);
|
||||
// 使用框架Db::query(),确保与业务中Db::insert()使用相同的连接机制
|
||||
$result = Db::query($sql);
|
||||
|
||||
// 代码层面控制超时,不依赖database.php配置
|
||||
if (microtime(true) - $start > $timeout) {
|
||||
throw new Exception("查询超时({$timeout}秒)");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理连接资源(仅影响当前进程,不干扰现有系统)
|
||||
*/
|
||||
private function cleanupConnections()
|
||||
{
|
||||
// 关闭当前进程的框架连接(不影响其他进程)
|
||||
Db::close();
|
||||
// 清除Db类静态缓存(仅当前进程)
|
||||
$this->clearDbInstanceCache();
|
||||
// 保留系统缓存,避免影响现有业务
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除Db类实例缓存(确保新连接能被正确创建)
|
||||
*/
|
||||
private function clearDbInstanceCache()
|
||||
{
|
||||
static $reflection = null;
|
||||
static $instanceProp = null;
|
||||
|
||||
if (!$reflection) {
|
||||
try {
|
||||
$reflection = new \ReflectionClass('\think\Db');
|
||||
$instanceProp = $reflection->getProperty('instance');
|
||||
$instanceProp->setAccessible(true);
|
||||
} catch (\ReflectionException $e) {
|
||||
$this->log("反射初始化失败:{$e->getMessage()}", 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// 仅清空当前进程的Db实例缓存,不影响其他进程(如Web请求)
|
||||
$instanceProp->setValue(null, []);
|
||||
} catch (\ReflectionException $e) {
|
||||
$this->log("清除Db缓存失败:{$e->getMessage()}", 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算等待时间(针对队列优化)
|
||||
*/
|
||||
private function calculateWaitTime($errorMsg, $attempt, $baseWait)
|
||||
{
|
||||
$isGoneAway = stripos($errorMsg, 'MySQL server has gone away') !== false;
|
||||
$isTimeout = stripos($errorMsg, 'timeout') !== false;
|
||||
|
||||
if ($isGoneAway) {
|
||||
return $baseWait * pow(2, $attempt); // 致命错误:1→2→4秒
|
||||
} elseif ($isTimeout) {
|
||||
return $baseWait * pow(1.5, $attempt); // 超时错误:1→1.5→2.25秒
|
||||
} else {
|
||||
return $baseWait * pow(1.2, $attempt); // 普通错误:1→1.2→1.44秒
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证查询结果有效性
|
||||
*/
|
||||
private function isValidResult($result)
|
||||
{
|
||||
return is_array($result) && !empty($result)
|
||||
&& isset(current($result)['1']) && current($result)['1'] == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连续失败计数管理
|
||||
*/
|
||||
private function initConsecutiveFailures($pid)
|
||||
{
|
||||
if (!isset($this->consecutiveFailures[$pid])) {
|
||||
$this->consecutiveFailures[$pid] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private function incrementConsecutiveFailures($pid)
|
||||
{
|
||||
$this->consecutiveFailures[$pid]++;
|
||||
if ($this->consecutiveFailures[$pid] >= $this->dbConfig['reconnect_threshold']) {
|
||||
$this->alert("进程[{$pid}]连续连接失败{$this->consecutiveFailures[$pid]}次,可能存在数据库隐患");
|
||||
}
|
||||
}
|
||||
|
||||
private function resetConsecutiveFailures($pid)
|
||||
{
|
||||
$this->consecutiveFailures[$pid] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志记录(仅输出到队列控制台,不干扰系统日志)
|
||||
*/
|
||||
private function log($message, $level = 'info')
|
||||
{
|
||||
$logTime = date('Y-m-d H:i:s');
|
||||
$content = "[{$logTime}] [{$level}] {$message}";
|
||||
// 仅在队列Worker控制台输出,不写入系统日志文件
|
||||
echo $content . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 告警通知(独立日志文件,不干扰现有系统)
|
||||
*/
|
||||
private function alert($message)
|
||||
{
|
||||
$alertFile = RUNTIME_PATH . "log/queue_db_alert_" . date('Ymd') . ".log";
|
||||
file_put_contents($alertFile, "[".date('Y-m-d H:i:s')."] ALERT: {$message}\n", FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全睡眠(支持Worker正常终止)
|
||||
*/
|
||||
private function safeSleep($seconds)
|
||||
{
|
||||
$interval = 1;
|
||||
while ($seconds > 0) {
|
||||
if (connection_aborted() || $this->isWorkerStopped()) {
|
||||
throw new Exception("队列Worker已终止,中断睡眠");
|
||||
}
|
||||
$sleep = min($interval, $seconds);
|
||||
sleep($sleep);
|
||||
$seconds -= $sleep;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测Worker是否已停止(兼容TP5.0机制)
|
||||
*/
|
||||
private function isWorkerStopped()
|
||||
{
|
||||
$stopFile = RUNTIME_PATH . 'queue/stop_worker';
|
||||
return file_exists($stopFile);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user