user_obj = Db::name('user'); $this->captcha_obj = Db::name('captcha'); $this->user_act_obj = Db::name('user_act'); $this->admin_obj = Db::name('admin'); $this->article_obj = Db::name('article'); $this->user_reviewer_obj = Db::name('user_reviewer_apply'); $this->journal_obj = Db::name('journal'); $this->reviewer_major_obj = Db::name('reviewer_major'); $this->reviewer_to_journal_obj = Db::name('reviewer_to_journal'); $this->user_reviewer_info_obj = Db::name('user_reviewer_info'); $this->user_msg_obj = Db::name('user_msg'); $this->country_obj = Db::name('country'); $this->article_author_obj = Db::name('article_author'); $this->article_file_obj = Db::name('article_file'); $this->article_msg_obj = Db::name('article_msg'); $this->user_log_obj = Db::name('user_log'); $this->major_obj = Db::name("major"); $this->major_to_journal_obj = Db::name('major_to_journal'); $this->user_black_obj = Db::name('user_black'); $this->user_to_special_obj = Db::name('user_to_special'); $this->chief_to_journal_obj = Db::name('chief_to_journal'); $this->article_reviewer_obj = Db::name('article_reviewer'); $this->user_reviewer_recommend_obj = Db::name('user_reviewer_recommend'); $this->company_top_obj = Db::name('company_top'); } /** * 注册功能 */ public function register() { $data = $this->request->post(); //检测是否用户名和密码已经占用 $account = $data['username']; $email = $data['email']; $res_once = $this->user_obj->where("account='$account' or email = '$email'")->find(); if ($res_once != null) { return json('existence'); } //验证验证码 // if (!$this->my_checkcaptcha($data['code'], $data['random_num'])) { // return json('errcaptcha'); // } //存入数据 $inser_data['account'] = trim($account); $inser_data['password'] = md5($data['password']); $inser_data['email'] = $email; $inser_data['phone'] = $data['phone']; $inser_data['realname'] = $data['realname']; $inser_data['ctime'] = time(); $id = $this->user_obj->insertGetId($inser_data); //发送注册成功邮件 $tt = "Dear author,You have successfully registered

"; $content = $tt . '

Username:' . $account . '
Password:' . $data['password'] . '

'; $sendUser = [ 'title' => 'Dear ' . $data['realname'], // 邮件标题 'content' => $content, //邮件内容 'user_id' => $id, //收件人ID 'email' => $email, // 收件人邮箱 'journal_id' => 0, // 期刊ID 'sendEmail' => Env::get('email.send_email'), // 期刊邮箱 'sendPassword' => Env::get('email.send_email_password'), // 期刊密码 'from_name' => 'TMR' ]; // Queue::push('app\api\job\domail@fire',$sendUser,'domail'); sendEmail($email, 'Dear ' . $data['realname'], 'TMR', $content); return json($inser_data); } /** * 登录功能 * @return type */ public function checkLogin() { $data = $this->request->post(); //判断是否管理员登录 if ($data['username'] == 'superadmin' || $data['username'] == 'wuxiongzhi2') { $where_admin['account'] = $data['username']; $where_admin['password'] = md5($data['password']); $admin_info = $this->admin_obj->where($where_admin)->find(); if ($admin_info == null) { return json(['code' => 1]); } else { $up_admin['last_login_time'] = time(); $up_admin['last_login_ip'] = $this->request->ip(); $this->admin_obj->where('admin_id = ' . $admin_info['admin_id'])->update($up_admin); return json(['code' => 0, 'userinfo' => $admin_info]); } } else { //用户登录 // $where['account'] = $data['username']; // $where['password'] = md5($data['password']); $user_info = $this->user_obj->where('account|email', trim($data['username']))->where('password', md5($data['password']))->find(); if ($user_info == null) { //登陆失败 return json(['code' => 1]); } //黑名单验证 $blackCheck = $this->user_black_obj->where('user_id', $user_info['user_id'])->where('black_state', 0)->find(); if ($blackCheck) { return jsonError("Your account has been blocked. Please contact the publisher for details: publisher@tmrjournals.com."); } $up_data['last_login_time'] = time(); $up_data['last_login_ip'] = $this->request->ip(); $this->user_obj->where('user_id = ' . $user_info['user_id'])->update($up_data); return json(['code' => 0, 'userinfo' => $user_info]); } } /** * 获取文章 */ public function getArticles() { $data = $this->request->post(); $list = $this->article_obj ->where('special_num', $data['special_id']) ->where('user_id', $data['user_id']) ->where('state', 0)->select(); $re['articles'] = $list; return json(['code' => 0, 'data' => ['articles' => $re]]); } /** * 获取期刊的专刊列表 */ public function getSpecialByIssn() { $data = $this->request->post(); $rule = new Validate([ 'journal_issn' => 'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $base_url = Env::get('journal.base_url'); $res = object_to_array(json_decode(myPost($base_url . "/master/Special/getSpecialByIssn", ['journal_issn' => $data['journal_issn']]))); $specials = $res['data']['specials']; foreach ($specials as $k => $v) { unset($specials[$k]['journal_id']); } $re['specials'] = $specials; return jsonSuccess($re); } /** * 获取客座 */ public function getSpecial(){ $data = $this->request->post(); $rule = new Validate([ 'journal_id' => 'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $journal_info = $this->journal_obj->where('journal_id',$data['journal_id'])->find(); $base_url = Env::get('journal.base_url'); $res = object_to_array(json_decode(myPost($base_url . "/master/Special/getSpecialByIssn", ['journal_issn' => $journal_info['issn']]))); $specials = isset($res['data']['specials'])?$res['data']['specials']:[]; foreach ($specials as $k => $v) { unset($specials[$k]['journal_id']); } $re['specials'] = $specials; return jsonSuccess($re); } /** * 更改文章客座 */ public function editArticleSpecial(){ $data = $this->request->post(); $rule = new Validate([ 'article_id' => 'require', 'special_num' => 'require', 'special_title' =>'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $article_info = $this->article_obj->where('article_id',$data['article_id'])->find(); if($article_info['special_num']>0){ return jsonError('Failed! The attached special issue exists.'); } $updata['special_num'] = $data['special_num']; $updata['special_title'] = $data['special_title']; $this->article_obj->where('article_id',$data['article_id'])->update($updata); return jsonSuccess([]); } /** * 获取客座审稿人文章列表 */ public function getSpecialArticles() { $data = $this->request->post(); $rule = new Validate([ 'user_id' => 'require', 'pageIndex' => 'require', 'pageSize' => 'require', 'special_id' => 'require', 'state' => 'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } //构造查询条件 $specialIds = $this->user_to_special_obj->where('user_id', $data['user_id'])->where('uts_state', 0)->column('special_id'); $where['t_article.special_num'] = ['in', $specialIds]; if ($data['special_id'] != 0) { $where['special_num'] = $data['special_id']; } if ($data['state'] > -1) { $where['t_article.state'] = $data['state']; } if (isset($data['titleKey']) && $data['titleKey'] != '') { $where['t_article.title'] = ['like', '%' . trim($data['titleKey']) . '%']; } if (isset($data['sn']) && $data['sn'] != '') { $where['t_article.accept_sn'] = $data['sn']; } //分页查询数据 $limit_start = ($data['pageIndex'] - 1) * $data['pageSize']; $res = $this->article_obj ->field('t_article.*,t_journal.title journalname,t_user.email,t_user.realname,t_user.phone') ->join('t_journal', 't_journal.journal_id = t_article.journal_id', 'LEFT') ->join('t_user', "t_user.user_id = t_article.user_id", 'left') ->where($where) ->order('article_id desc') ->limit($limit_start, $data['pageSize'])->select(); //添加国家信息 foreach ($res as $k => $v) { $cache_author_list = $this->article_author_obj->where('article_id', $v['article_id'])->select(); $cache_country = []; foreach ($cache_author_list as $key => $val) { if ($val['country'] != '' && !in_array($val['country'], $cache_country)) { $cache_country[] = $val['country']; } } $res[$k]['countrys'] = $cache_country; } $count = $this->article_obj->where($where)->count(); $re['articles'] = $res; $re['count'] = $count; return jsonSuccess($re); } /** * 更改文章状态客座编辑 */ public function editArticle() { $data = $this->request->post(); $rule = new Validate([ 'editname' => 'require', 'articleId' => 'require', 'state' => 'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $editor_info = $this->user_obj->where('account', $data['editname'])->find(); $article_info = $this->article_obj->where('article_id', $data['articleId'])->find(); $journal_info = $this->journal_obj->where('journal_id', $article_info['journal_id'])->find(); $user_info = $this->user_obj->where(['user_id' => $article_info['user_id']])->find(); //添加文章状态信息(如果状态未更新可做通话用,并结束操作) $insert_data['article_id'] = $data['articleId']; $insert_data['content'] = ''; $insert_data['state_from'] = $article_info['state']; $insert_data['state_to'] = $data['state']; $insert_data['ctime'] = time(); $this->article_msg_obj->insert($insert_data); $update_data['state'] = $data['state']; $update_data['editor_act'] = 1; //更新文章状态 if ($data['state'] == 4) { $update_data['ttime'] = time(); } if ($data['state'] == 3) { $update_data['rstime'] = time(); } $this->article_obj->where('article_id', $data['articleId'])->update($update_data); //拒稿或者录用 - 发送审稿意见 $articlecontroller = new articlecontroller(); if ($article_info['journal_id'] == 1 && ($data['state'] == 3 || $data['state'] == 5)) { $articlecontroller->sendEmailToReviewer($data['articleId'], $data['state']); } //发送文章状态更改邮件 if ($data['state'] == 3) { //拒稿 $tt = '"' . $article_info['title'] . '"
'; $tt .= $article_info['accept_sn'] . '
'; $tt .= 'journal:' . $journal_info['title'] . '
'; $tt .= 'Dear Dr. ' . ($user_info['realname'] == '' ? $user_info['account'] : $user_info['realname']) . ',
'; $tt .= 'Thank you for submitting your paper to ' . $journal_info['title'] . '. Your manuscript has undergone review.
'; $tt .= 'Unfortunately the editors feel that ' . $journal_info['title'] . ' is not the appropriate venue for your manuscript,' . ' and we are returning your manuscript to you so that you can submit it to another journal without delay. ' . '

Yours sincerely,

'; $tt .= 'Sincerely,
Editorial Office
'; } else if ($data['state'] == 5) { //录用 //录用后更新录用时间 $this->article_obj->where('article_id', $article_info['article_id'])->update(['rtime' => time()]); $tt = 'Manuscript ID: ' . $article_info['accept_sn'] . '
'; $tt .= 'Manuscript Title: ' . $article_info['title'] . '
'; $tt .= 'Authors’ Name: ' . $articlecontroller->getArticleAuthors($article_info['article_id']) . '

'; $tt .= 'Dear Dr. ' . ($user_info['realname'] == '' ? $user_info['account'] : $user_info['realname']) . ',
It is a distinct pleasure to inform you that your manuscript has been accepted for publication in ' . $journal_info['title'] . ' (ISSN ' . $journal_info['issn'] . ').
The editor will contact you further by email soon.


'; $tt .= 'Sincerely,
Editorial Office
'; } else if ($data['state'] == 4) { //退修 $tt = $article_info['accept_sn'] . '
'; $tt .= 'Dear Dr. ' . ($user_info['realname'] == '' ? $user_info['account'] : $user_info['realname']) . ',
'; $tt .= 'Thank you for submitting the manuscript to ' . $journal_info['title'] . '.
'; $tt .= 'Please find the new comments in the "Author Center", Please submit your revised manuscript within two weeks.

'; $tt .= 'If you need more time to revise, you can send E-mial to tell us.
'; $tt .= 'Sincerely,
Editorial Office
'; } else if ($data['state'] == 6) { //终审 $tt = 'Dear Dr. ' . ($user_info['realname'] == '' ? $user_info['account'] : $user_info['realname']) . ',
'; $tt .= 'Manuscript status: Your manuscript "' . $article_info['title'] . '" is under reviewing by editorial member team of ' . $journal_info['title'] . '.'; } else { $tt = '"' . $article_info['title'] . '"
'; $tt .= $article_info['accept_sn'] . '
'; $tt .= 'journal:' . $journal_info['title'] . '

'; $tt .= 'Dear Dr. ' . ($user_info['realname'] == '' ? $user_info['account'] : $user_info['realname']) . ',
Please check the new status of your manuscript online.

'; } $tt .= $journal_info['title'] . '
'; $tt .= 'Subscribe to this journal
'; $tt .= 'Email: ' . $journal_info['email'] . '
'; $tt .= 'Website: ' . $journal_info['website']; if ($data['state'] != 5 || $journal_info['journal_id'] != 9) { $sendUser['content'] = $tt; // sendEmail($user_info['email'], $journal_info['title'], $journal_info['title'], $tt, $journal_info['email'], $journal_info['epassword']); } if ($data['state'] == 6) { //进入终审,通知主编邮件 $chiefs = $this->chief_to_journal_obj->join('t_user', 't_user.user_id = t_chief_to_journal.user_id', 'left')->where('t_chief_to_journal.journal_id', $journal_info['journal_id'])->where('t_chief_to_journal.state', 0)->select(); foreach ($chiefs as $v) { $tts = 'Dear Dr. ' . ($v['realname'] == '' ? $v['account'] : $v['realname']) . ',

'; $tts .= 'The manuscript entitled “' . $article_info['title'] . '” has been peer-reviewed, revised and about to be published in ' . $journal_info['title'] . '.

'; $tts .= 'If you want to review this article, you could use it Submission System (Plese click here).
'; $tts .= 'Your username: ' . $v['account'] . '
'; $tts .= 'Password: 123456qwe (Original password)

'; $tts .= 'If you are unable to review it now, you may provide your comments at a later time at your convenience. Then ,there is no need to reply to this email.

'; $tts .= 'Any comments you make will be valued by the editorial board. Please bring into our knowledge if there is any potential Conflict of Interest.



'; $tts .= 'Sincerely,
Editorial Office
' . $journal_info['title'] . '
'; $tts .= 'Email: ' . $journal_info['email'] . '
'; $tts .= 'Website:' . $journal_info['website'] . ''; $sendUser['user_id'] = $v['user_id']; $sendUser['email'] = $v['email']; $sendUser['content'] = $tts; // sendEmail($v['email'], $journal_info['title'], $journal_info['title'], $tts, $journal_info['email'], $journal_info['epassword']); } } //增加用户操作log $log_data['user_id'] = $editor_info['user_id']; $log_data['type'] = 1; $log_data['content'] = $editor_info['account'] . "(" . $editor_info['realname'] . "),更改了一篇文章:(" . $article_info['title'] . ")的状态,更改时间是:" . date('Y-m-d H:i:s', time()); $log_data['ctime'] = time(); $this->user_log_obj->insert($log_data); //增加usermsg add_usermsg($article_info['user_id'], 'Your manuscript has new process: ' . $article_info['title'], '/articleDetail?id=' . $article_info['article_id']); return json(['code' => 0]); } /** * 添加专刊文章(作者) */ public function addArticle() { //接受参数,查询信息 $data = $this->request->post(); // $data['authorList'] = [ // [ // 'address'=>'111', // 'company'=>'111', // 'country'=>'china', // 'department'=>'', // 'email'=>'6541654@qq.com', // 'firstname'=>'2222222', // 'isReport'=>'true', // 'isSuper'=>'false', // 'lastname'=>'2222222', // 'title'=>'Ph.D.' // ], // [ // 'address'=>'', // 'company'=>'112221', // 'country'=>'china', // 'department'=>'', // 'email'=>'2254@qq.com', // 'firstname'=>'22212222', // 'isReport'=>'false', // 'isSuper'=>'true', // 'lastname'=>'2222222', // 'title'=>'Ph.D.' // ] // ]; // $data['abstrart'] = '1111'; // $data['coverLetter'] = ''; // $data['fund'] = ''; // $data['journal_issn'] = '2538-015X'; // $data['keyWords'] = '111,1111,1111'; // $data['title'] = "111"; // $data['major'] = '1'; // $data['cmajor'] = '26'; // $data['type'] = 'A'; // $data['approval'] = 'true'; // $data['username'] = "user1"; // $data['coverLetter'] = ''; // $data['picturesAndTables'] = ''; // $data['totalpage'] = ''; // $data['manuscirpt'] = '20210122/e08e82edcabe5dc9d9409d1947fccc82.png'; $user_res = $this->user_obj->where('account', $data['username'])->find(); $journal_info = $this->journal_obj->where('issn', $data['journal_issn'])->find(); $check_article = $this->article_obj->where('title',$data['title'])->find(); if($check_article){ return json(['code' => 1, 'msg' => 'A manuscript with the same title already exists. Please revise the title and try again.']); } if(isset($data['approval']) && $data['approval']==1 &&(!isset($data['approval_file'])||!$data['approval_file'])){ return json(['code' => 1, 'msg' => 'To help us proceed with your submission, please kindly upload the ethics approval document.']); }else if((!isset($data['approval'])||$data['approval']!=1) && (!isset($data['approval_content']) || !$data['approval_content'])){ return json(['code' => 1, 'msg' => 'If an ethics approval document is not required, please kindly provide a brief explanation.']); } Db::startTrans(); //添加文章基础信息 $inset_data['user_id'] = $user_res['user_id']; $inset_data['journal_id'] = $journal_info['journal_id']; $inset_data['editor_id'] = $journal_info['editor_id']; $inset_data['title'] = $data['title']; $inset_data['keywords'] = $data['keyWords']; $inset_data['fund'] = $data['fund']; $inset_data['special_num'] = $data['special_id']; $inset_data['special_title'] = $data['special_title']; $inset_data['accept_sn'] = getArticleSN($journal_info['abbr'], $data['type']); $inset_data['type'] = $data['type']; $inset_data['major_id'] = $data['major']; $inset_data['cmajor_id'] = $data['cmajor']; $inset_data['approval'] = $data['approval'] == 'true' ? 1 : 0; $inset_data['abstrart'] = $data['abstrart']; $inset_data['author_act'] = 1; if(isset($data['approval']))$inset_data['approval'] = $data['approval']; if(isset($data['approval_file']))$inset_data['approval_file'] = $data['approval_file']; if(isset($data['approval_content']))$inset_data['approval_content'] = $data['approval_content']; $inset_data['ctime'] = time(); $res = $this->article_obj->insertGetId($inset_data); //上传文章作者信息 $authors = []; $res_add_user = true; foreach ($data['authorList'] as $v) { if ($v['firstname'] == '') { continue; } $i['article_id'] = $res; $i['firstname'] = $v['firstname']; $i['lastname'] = $v['lastname']; $i['company'] = $v['company']; $i['department'] = $v['department']; $i['author_title'] = $v['title']; $i['country'] = $v['country']; $i['orcid'] = $v['orcid']; $i['email'] = $v['email']; $i['address'] = $v['address']; $i['is_super'] = $v['isSuper'] == 'true' ? 1 : 0; $i['is_report'] = $v['isReport'] == 'true' ? 1 : 0; $authors[] = $i; //通讯作者用户添加 if($v['isReport']=="true"){ $re_user_check = $this->user_obj->where('account|email',trim($v['email']))->find(); if(!$re_user_check){ $password = getRandPassword(); $inser_data['account'] = trim($v['email']); $inser_data['password'] = md5($password); $inser_data['email'] = trim($v['email']); $inser_data['realname'] = trim($v['firstname']).' '.trim($v['lastname']); $inser_data['ctime'] = time(); $c_res_add_user=$this->user_obj->insertGetId($inser_data); if($res_add_user){ $res_add_user = $c_res_add_user; } //发送提示邮件给通讯作者 $report_tt = "Dear ".$inser_data['realname'].',

'; $report_tt .= "We are delighted to welcome you as a new author for our journal, ".$journal_info['title'].". We have received your submission and are excited to review it for potential publication.

"; $report_tt .= "As a next step, we have created an account for you on our journal's website. Your account is [Username: ".trim($v['email'])." password:$password]"; $report_tt .= "and you can access your account by visiting ".$journal_info['website']." and logging in.

"; $report_tt .= "If you have any questions or need assistance with accessing your account, please don't hesitate to contact us. We are here to support you throughout the submission and review process.

"; $report_tt .= "Thank you for choosing to submit your work to our journal. We look forward to working with you.

"; $report_tt .= "Best regards,
".$inser_data['realname'].'
'.$journal_info['title']; $maidata['email'] = trim($v['email']); $maidata['title'] = $journal_info['title']; $maidata['content'] = $report_tt; $maidata['tmail'] = $journal_info['email']; $maidata['tpassword'] = $journal_info['epassword']; Queue::push('app\api\job\mail@fire', $maidata, "tmail"); } } } $res_author = $this->article_author_obj->insertAll($authors); //增加转投信息 $transr = true; // if($data['istransfer']=='true'){ // foreach ($data['checkedjours'] as $val){ // $trans_insert['article_id'] = $res; // $trans_insert['journal_id'] = $val; // $trans_insert['ctime'] = time(); // $transr = $transr?$this->article_transfer_obj->insert($trans_insert):false; // } // } //增加articlefile表的信息 $res_file1 = self::save_article_file($res, $user_res['user_id'], $user_res['account'], $data['coverLetter'], 'coverLetter'); $res_file2 = self::save_article_file($res, $user_res['user_id'], $user_res['account'], $data['picturesAndTables'], 'picturesAndTables'); // $res_file4 = self::save_article_file($res, $user_res['user_id'], $user_res['account'], $data['totalpage'], 'totalpage'); $res_file3 = self::save_article_file($res, $user_res['user_id'], $user_res['account'], $data['manuscirpt'], 'manuscirpt'); //发送邮件到编辑,提醒有待审文章 $editor_info = $this->user_obj->where('user_id', $journal_info['editor_id'])->find(); $tt = 'Dear editor,
'; $tt .= 'Please check the new manuscript in the submission system.'; sendEmail($editor_info['email'], $journal_info['title'], $journal_info['title'], $tt, $journal_info['email'], $journal_info['epassword']); //获取客座编辑 $iJournalSpecialId = empty($data['special_id']) ? 0 : $data['special_id']; $aSpecialEditor = $this->getJournalSpecialEditor(['journal_special_id' => $iJournalSpecialId]); $aSpecialEditorData = empty($aSpecialEditor['data']) ? [] : $aSpecialEditor['data']; $aSpecialEditorData = empty($aSpecialEditorData['editors']) ? [] : $aSpecialEditorData['editors']; if(!empty($aSpecialEditorData)){ foreach($aSpecialEditorData as $k => $v){ if(empty($v['email'])){ continue; } sendEmail($v['email'], $journal_info['title'], $journal_info['title'], $tt, $journal_info['email'], $journal_info['epassword']); } } // $sendEditor = [ // 'title' => $journal_info['title'], // 邮件标题 // 'content' => $tt, //邮件内容 // 'user_id' => $journal_info['editor_id'], //收件人ID // 'email' => $editor_info['email'], // 收件人邮箱 // 'journal_id' => $journal_info['journal_id'], // 期刊ID // 'sendEmail' => $journal_info['email'], // 期刊邮箱 // 'sendPassword' => $journal_info['epassword'], // 期刊密码 // 'from_name' => $journal_info['title'] // ]; // Queue::push('app\api\job\domail@fire',$sendEditor,'domail'); $iJournalFee = empty($journal_info['fee']) ? 0 : $journal_info['fee']; $iNowFee = intval($iJournalFee * 100); //收费地址 $sApc = empty($journal_info['apc_url']) ? '' : $journal_info['apc_url']; //稿件号 $sArticleSn = $inset_data['accept_sn']; //邮件主题 $sSubject = 'Manuscript Submission Confirmation – '.'['.$sArticleSn.'] '.$journal_info['title']; if ($iNowFee > 0) { $tt1 = 'Thank you for submitting your manuscript entitled "' . $data['title'] . '". Your submission has been assigned the following tracking number: ' . $sArticleSn . '. We will be in touch again as soon as we have reached a decision. You may check on the status of this manuscript in the Submission System. Please quote the tracking number in any communication.

'; $tt1 .= 'The following information was acknowledged during the submission process: '.''.$journal_info['title'].''.' is an open access journal that charges a publication fee of '.$iJournalFee.' USD for accepted manuscripts (click here for details).

'; $tt1 .= 'This e-mail simply acknowledges receipt of your submission. If the editors decide for editorial reasons that the paper is unsuitable for publication in '.''.$journal_info['title'].''.', you will be informed as soon as possible.

'; $tt1 .= 'If you encounter any problems, please contact '.$journal_info['email'].'.

Thank you for choosing to submit your manuscript to '.''.$journal_info['title'].''.'.


'; $tt1 .= 'Sincerely,
Editorial Office
'; $tt1 .= ''.$journal_info['title'].'' . '
'; $tt1 .= 'Email: ' . $journal_info['email'] . '
'; $tt1 .= 'Website: ' . $journal_info['website'] . '
'; $tt1 .= '
If you have any questions, please contact us:
'; $tt1 .= 'Head of publication ethics
Dr. Dan Chen
TMR Publishing Group Limited Company, Auckland, New Zealand
Email: publisher@tmrjournals.com'; } else { $tt1 = 'Thank you for submitting your manuscript entitled "' . $data['title'] . '". Your submission has been assigned the following tracking number:' . $sArticleSn . '. We will be in touch again as soon as we have reached a decision. Please quote the tracking number in any communication. This e-mail simply acknowledges receipt of your submission. If the editors decide for editorial reasons that the paper is unsuitable for publication in ' . ''.$journal_info['title'].'' . ', you will be informed as soon as possible.


'; $tt1 .= 'You may check on the status of this manuscript in the Submission System. If you encounter any problems, please contact ' . $journal_info['email'] . '.

'; $tt1 .= 'Thank you for choosing to submit your manuscript to ' . ''.$journal_info['title'].'' . '.


'; $tt1 .= 'Sincerely,
Editorial Office
'; $tt1 .= 'Subscribe to this journal
'; $tt1 .= ''.$journal_info['title'].'' . '
'; $tt1 .= 'Email: ' . $journal_info['email'] . '
'; $tt1 .= 'Website: ' . $journal_info['website'] . '
'; $tt1 .= '
If you have any questions, please contact us:
'; $tt1 .= 'Head of publication ethics
Dr. Dan Chen
TMR Publishing Group Limited Company, Auckland, New Zealand
Email: publisher@tmrjournals.com'; } foreach ($data['authorList'] as $v) { $cache_str = 'Dear Dr. ' . $v['firstname'] ." ".$v['lastname'] . ',

'; $maidata['email'] = $v['email']; $maidata['title'] = $journal_info['title']; $maidata['content'] = $cache_str . $tt1; $maidata['temail'] = $journal_info['email']; $maidata['tpassword'] = $journal_info['epassword']; //给文章作者发送邮件主题调整 20250105 start $maidata['subject'] = empty($sSubject) ? '' : $sSubject;//邮件主题 $maidata['article_id'] = empty($res) ? 0 : $res;//文章ID // Queue::push('app\api\job\mail@fire', $maidata, "tmail"); Queue::push('app\api\job\SendAuthorEmail@fire', $maidata, "SendAuthorEmail"); //给文章作者发送邮件主题调整 20250105 end } //增加用户操作log $log_data['user_id'] = $user_res['user_id']; $log_data['type'] = 0; $log_data['content'] = $user_res['account'] . "(" . $user_res['realname'] . "),上传了一篇文章:" . $data['title'] . ",上传时间是:" . date('Y-m-d H:i:s', time()); $log_data['ctime'] = time(); $res_log = $this->user_log_obj->insert($log_data); //增加usermsg $res_msg = add_usermsg($journal_info['editor_id'], 'New manuscript', '/articleDetailEditor?id=' . $res); if ($res && $res_author && $transr &&$res_add_user && $res_file1 && $res_file2 && $res_file3 && $res_log && $res_msg) { Db::commit(); $this->ai_scor($res); return json(['code' => 0]); } else { Db::rollback(); return json(['code' => 1]); } } /** * 人工智能打分 */ public function ai_scor($article_id) { $article_info = $this->article_obj->where('article_id', $article_id)->find(); $files = $this->article_file_obj->where('article_id', $article_id)->where('type_name', 'manuscirpt') ->order('ctime desc') ->limit(1) ->select(); $authors = $this->article_author_obj->where('article_id', $article_id)->where('is_report', 1)->where('state', 0)->select(); $user_id = $article_info['user_id']; $g_index = 0; $google_time = 0; foreach ($authors as $v) { $c_user = $this->user_obj->where('email', $v['email'])->find(); if ($google_time < $c_user['google_time']) {//应对h指数为0的用户文章,先取是否填写过h指数为0的情况 $google_time = $c_user['google_time']; } if ($c_user['google_index'] >= $g_index) { $user_id = $c_user['user_id']; $g_index = $c_user['google_index']; } } $user_info = $this->user_obj->where('user_id', $user_id)->find(); $fen = 0; //h指数分数 $h_fen = 0; if ($user_info['google_index'] >= 20) { $h_fen = 4; } elseif ($user_info['google_index'] >= 6) { $h_fen = 3; } elseif ($user_info['google_index'] >= 2) { $h_fen = 1; } else { $h_fen = 0; } $fen += $h_fen; //图表 $b_fen = 0; $p_num = 0; $url = $this->ts_base_url."api/typeset/readPic"; $program['fileRoute'] = "https://submission.tmrjournals.com/public/" . $files[0]['file_url']; $res = object_to_array(json_decode(myPost($url, $program))); $wp = isset($res['data']['pic']) ? $res['data']['pic'] : 0; $tp = isset($res['data']['table']) ? $res['data']['table'] : 0; $pics = $this->article_file_obj->where('article_id', $article_id)->where('type_name', 'picturesAndTables')->select(); $np = 0; foreach ($pics as $v) { $ext = substr(strrchr($v['file_url'], '.'), 1); if ($ext == 'zip') { $cn = $this->readZip(ROOT_PATH . "public" . DS . $v['file_url']); $np += $cn; } else { $np++; } } $p_num = $wp > $np ? $wp : $np; $all_num = $p_num + $tp; if ($all_num > 3) { $b_fen = 2; } elseif ($all_num >= 1) { $b_fen = 1; } else { $b_fen = 0; } //当h指数为0 并且是编辑填写的 if ($user_info['google_index'] == 0 && $google_time > 0) { if ($all_num >= 8) { $b_fen = 4; } elseif ($all_num == 7) { $b_fen = 3.5; } elseif ($all_num == 6) { $b_fen = 3; } elseif ($all_num == 5) { $b_fen = 2; } elseif ($all_num == 4) { $b_fen = 1.5; } elseif ($all_num >= 1) { $b_fen = 1; } else { $b_fen = 0; } } $fen += $b_fen; //图表扣分环节 if ($article_info['journal_id'] == 1) { $jan_fen = 0; if ($article_info['type'] == "A") { if ($all_num < 1) { $jan_fen = 2; } elseif ($all_num < 2) { $jan_fen = 1; } elseif ($all_num < 3) { $jan_fen = 0.5; } } elseif ($article_info['type'] == "B") { if ($all_num == 0) { $jan_fen = 1; } } $b_fen = $b_fen - $jan_fen; $fen = $fen - $jan_fen; } else { $jan_fen1 = 0; if ($article_info['type'] == "A") { if ($all_num < 1) { $jan_fen1 = 1; } if ($all_num < 2) { $jan_fen1 = 0.5; } } elseif ($article_info['type'] == "B") { if ($all_num == 0) { $jan_fen1 = 0.5; } } $b_fen = $b_fen - $jan_fen1; $fen = $fen - $jan_fen1; } //国家 $c_fen = 0; $author = $this->article_author_obj->where('article_id', $article_id)->where('is_report', 1)->find(); if ($author['country'] != "") { $coun = $this->country_obj->where('en_name', $author['country'])->find(); if ($coun && $coun['is_hot'] == 1) { $c_fen = 1; $fen += 1; } elseif ($coun && $coun['en_name'] != "China" && $coun['en_name'] != "India") { $c_fen = 0.5; $fen += 0.5; } else { $c_fen = 0; $fen += 0; } } //单位 $dw_fen = 0; $report_author = $this->article_author_obj->where('article_id', $article_id)->where('email', $user_info['email'])->find(); if ($report_author) { $ca_res = $this->company_top_obj->where('title', $report_author['company'])->find(); if ($ca_res) { if ($ca_res['company_id'] <= 100 || ($ca_res['company_id'] > 201 && $ca_res['company_id'] <= 301)) { $dw_fen = 1.5; $fen += 1.5; } else { $dw_fen = 1; $fen += 1; } } } //领域 $ly_fen = 0; $m = $this->major_obj->where('major_id', $article_info['major_id'])->find(); if ($m['is_hot'] == 1) { $ly_fen = 1; $fen += 1; } //基金 $jj_fen = 0; if (strlen($article_info['fund']) > 10) { $jj_fen = 0.5; $fen += 0.5; } // return $fen; $updata['scoring'] = $fen; $updata['h_fen'] = $h_fen; $updata['b_fen'] = $b_fen; $updata['c_fen'] = $c_fen; $updata['dw_fen'] = $dw_fen; $updata['ly_fen'] = $ly_fen; $updata['jj_fen'] = $jj_fen; $this->article_obj->where('article_id', $article_id)->update($updata); return jsonSuccess([]); } private function readZip($file){ $zip = new \ZipArchive(); if($zip->open($file)===true){ return $zip->numFiles; }else{ return 0; } } /** * 客座编辑添加审稿人 */ public function addReviewerToArticleForSpecial() { $data = $this->request->post(); $rule = new Validate([ 'editor_id' => 'require', 'email' => 'require|email', 'realname' => 'require', 'article_id' => 'require', 'major' => 'require', 'country' => 'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $article_info = $this->article_obj->where('article_id', $data['article_id'])->find(); $journal_info = $this->journal_obj->where('journal_id', $article_info['journal_id'])->find(); Db::startTrans(); //判断此用户是否存在 $reviewer_info = $this->user_obj->where('email', trim($data['email']))->where('state', 0)->find(); if ($reviewer_info == null) { //添加用户 $insert_user['account'] = $data['email']; $insert_user['password'] = md5('123456qwe'); $insert_user['email'] = $data['email']; $insert_user['realname'] = $data['realname']; $insert_user['ctime'] = time(); $i_id = $this->user_obj->insertGetId($insert_user); $reviewer_info = $this->user_obj->where('user_id', $i_id)->find(); } //检验审稿实例是否存在 $check_repi = $this->article_reviewer_obj->where('article_id', $data['article_id'])->where('reviewer_id', $reviewer_info['user_id'])->find(); if ($check_repi != null) { return jsonError('Cannot be added repeatedly'); } //判断是否是此期刊审稿人 $rtj = $this->reviewer_to_journal_obj->where('journal_id', $article_info['journal_id'])->where('reviewer_id', $reviewer_info['user_id'])->where('state', 0)->find(); $res2 = true; $res3 = true; if ($rtj == null) { //判断是否具有审稿人身份,并具有审稿人资料 $uri = $this->user_reviewer_info_obj->where('reviewer_id', $reviewer_info['user_id'])->where('state', 0)->find(); if ($uri == null) { //添加审稿人信息 $insert_reviewer_info['reviewer_id'] = $reviewer_info['user_id']; $insert_reviewer_info['major'] = $data['major']; // $insert_reviewer_info['cmajor'] = $data['cmajor']; $insert_reviewer_info['country'] = $data['country']; $insert_reviewer_info['test_from'] = "addReviewerToArticleForSpecial"; $res2 = $this->user_reviewer_info_obj->insert($insert_reviewer_info); } $insert_rtj['reviewer_id'] = $reviewer_info['user_id']; $insert_rtj['journal_id'] = $journal_info['journal_id']; $insert_rtj['account'] = $reviewer_info['account']; $insert_rtj['journal_title'] = $journal_info['title']; $insert_rtj['ctime'] = time(); $res3 = $this->reviewer_to_journal_obj->insert($insert_rtj); } //创建审稿实例 $insert_article_reviewer['reviewer_id'] = $reviewer_info['user_id']; $insert_article_reviewer['article_id'] = $data['article_id']; $insert_article_reviewer['editor_act'] = 1; $insert_article_reviewer['ctime'] = time(); $arid = $this->article_reviewer_obj->insertGetId($insert_article_reviewer); //修改文章状态->审稿中 $this->article_obj->where('article_id',$data['article_id'])->update(['state'=>2]); //添加推荐审稿人信息 $insert_recommend['reviewer_id'] = $reviewer_info['user_id']; $insert_recommend['recommend_user_id'] = $data['editor_id']; $insert_recommend['urr_type'] = 1; $insert_recommend['urr_ctime'] = time(); $res_recommend = $this->user_reviewer_recommend_obj->insert($insert_recommend); if ($res2 && $res3 && $arid && $res_recommend) { Db::commit(); $articlecontroller = new articlecontroller(); //发送email提醒审稿员 $tt = $article_info['accept_sn'] . '
'; $tt .= 'Dear Dr. ' . ($reviewer_info['realname'] == "" ? $reviewer_info['account'] : $reviewer_info['realname']) . '

'; $tt .= 'The manuscript entitled “' . $article_info['title'] . '” has' . ' been submitted to the journal ' . $journal_info['title'] . '. The Editor-in-Chief would' . ' be most grateful if you could offer an opinion regarding its suitability for publication' . ' in the journal ' . $journal_info['title'] . '.
'; $tt .= 'Please bring into our knowledge if there is any potential Conflict of Interest. If you agree to review this manuscript, we ask you to complete your review and submit it by submission system within 10 days of receipt of the manuscript.

'; $tt .= 'Thank you for your consideration.
Look forward for your reply.
'; $tt .= 'Click here to review the article
'; $tt .= 'Click on the link to reject the review of this manuscript
'; $tt .= 'Your username:' . $reviewer_info['account'] . '

'; $tt .= 'Your original password:123456qwe, if you have reset the password, please login with the new one or click the "forgot password".
'; $tt .= 'Sincerely,
Editorial Office
'; $tt .= 'Subscribe to this journal
'; $tt .= $journal_info['title'] . '
'; $tt .= 'Email:' . $journal_info['email'] . '
'; $tt .= 'Website:' . $journal_info['website']; sendEmail($reviewer_info['email'], $journal_info['title'], $journal_info['title'], $tt, $journal_info['email'], $journal_info['epassword']); return jsonSuccess([]); } else { Db::rollback(); return jsonError("system error"); } } /** * 上传文章的文件 * coverLetter / picturesAndTables / manuscirpt 限制不超过 20M */ public function up_file($type) { $file = request()->file($type); if ($file) { $limitTypes = ['coverLetter', 'picturesAndTables', 'manuscirpt']; if (in_array($type, $limitTypes, true)) { $maxSize = 20 * 1024 * 1024; $fileInfo = $file->getInfo(); $fileSize = isset($fileInfo['size']) ? intval($fileInfo['size']) : 0; if ($fileSize > $maxSize) { return json(['code' => 1, 'msg' => 'File size cannot exceed 20MB']); } } $info = $file->move(ROOT_PATH . 'public' . DS . $type); if ($info) { return json(['code' => 0, 'upurl' => str_replace("\\", "/", $info->getSaveName())]); } else { return json(['code' => 1, 'msg' => $file->getError()]); } } } /** * 获取领域分类 */ public function getMajor() { $majors = $this->reviewer_major_obj->where('pid', 0)->select(); return json(['code' => 0, 'data' => $majors]); } /** * 获取major子项目 */ public function majorChild() { $majorid = $this->request->post('majorid'); $ds = $this->reviewer_major_obj->where('pid', $majorid)->select(); return json(['code' => 0, 'data' => $ds]); } /** * 获取城市 */ public function getCountrys() { $res = $this->country_obj->order('en_name')->select(); return json($res); } public function test() { echo strtotime('2021-1-1'); } /** * 存储article文件历史信息 */ private function save_article_file($article_id, $user_id, $username, $url, $type_name) { //首先确定数据库里面是否存在此数据 $res = $this->article_file_obj->where(['file_url' => $url])->find(); if ($res) { return true; } else if ($type_name == 'picturesAndTables' && trim($url) == '') { return true; } else if ($type_name == 'coverLetter' && trim($url) == '') { return true; } else if ($type_name == 'totalpage' && trim($url) == '') { return true; } $insert_data['article_id'] = $article_id; $insert_data['user_id'] = $user_id; $insert_data['username'] = $username; $insert_data['file_url'] = $url; $insert_data['type_name'] = $type_name; $insert_data['ctime'] = time(); return $this->article_file_obj->insert($insert_data); } /** * 获取期刊客座编辑 */ private function getJournalSpecialEditor($aParam = []){ $iJournalSpecialId = empty($aParam['journal_special_id']) ? 0 : $aParam['journal_special_id']; if(empty($iJournalSpecialId)){ return ['code' => 2,'msg' => 'journal_special_id is null']; } $sJournalUrl = 'http://journalapi.tmrjournals.com/public/index.php/';//'http://zmzm.journal.dev.com/'; $sUrl = $sJournalUrl."master/Special/getSpecialDetail"; $aResult = object_to_array(json_decode(myPost1($sUrl,$aParam))); return $aResult; } /** * 获取期刊的专刊列表 */ public function getSpecialList(){ $data = $this->request->post(); $rule = new Validate([ 'journal_id' => 'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } // 分页参数:pageIndex=页码,pageSize=每页条数 $iPage = isset($data['pageIndex']) ? intval($data['pageIndex']) : 1; $iSize = isset($data['pageSize']) ? intval($data['pageSize']) : 15; if ($iPage < 1) { $iPage = 1; } if ($iSize < 1) { $iSize = 15; } $journal_id = $data['journal_id']; $base_url = Env::get('journal.base_url'); $journal_image_url = Env::get('journal.journal_image_url'); $state = isset($data['state'])?$data['state']:-1; $gwData = [ 'journal_id' => $journal_id, 'pageIndex' => $iPage, 'pageSize' => $iSize, 'state' => $state, ]; $res = object_to_array(json_decode(myPost($base_url . "/master/Special/getSpecialList", $gwData))); // master 接口通常直接返回 specials/count,也兼容 data 包裹结构 $specials = isset($res['specials']) ? $res['specials'] : (isset($res['data']['specials']) ? $res['data']['specials'] : []); foreach ($specials as $k => $v) { unset($specials[$k]['abstract']); $specials[$k]['icon'] = $specials[$k]['icon']?$journal_image_url."journalicon/".$specials[$k]['icon']:""; } $re['specials'] = $specials; $re['count'] = isset($res['data']['count']) ? intval($res['data']['count']) : 0; $re['pageIndex'] = $iPage; $re['pageSize'] = $iSize; return jsonSuccess($re); } /** * 获取期刊的专刊列表 */ public function getSpecialDetail(){ $data = $this->request->post(); $rule = new Validate([ 'journal_special_id' => 'require' ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $journal_special_id = $data['journal_special_id']; $base_url = Env::get('journal.base_url'); $res = object_to_array(json_decode(myPost($base_url . "/master/Special/getSpecialDetail", ['journal_special_id' => $journal_special_id]))); $specialInfo = isset($res['data'])?$res['data']:[]; $editorsGw = isset($specialInfo['editors']) && is_array($specialInfo['editors']) ? $specialInfo['editors'] : []; // 客座编辑信息以投稿系统本地数据为准 $localEditors = $this->user_to_special_obj ->alias('uts') ->field('u.user_id,u.account,u.email,u.realname,u.icon,u.orcid,u.g_website,r.firstname,r.lastname,r.address,r.introduction,r.website,r.interests,r.company,r.department,r.country') ->join('t_user u', 'u.user_id = uts.user_id', 'left') ->join('t_user_reviewer_info r', 'r.reviewer_id = u.user_id AND r.state = 0', 'left') ->where('uts.special_id', intval($journal_special_id)) ->where('uts.uts_state', 0) ->where('u.state', 0) ->select(); $specialInfo['editors'] = empty($localEditors) ? [] : $localEditors; // editors_gw:去掉与本地 editors 邮箱相同的项,并去除邮箱字段 $localEmails = []; foreach ($specialInfo['editors'] as $localEditor) { $email = trim((string)(is_array($localEditor) ? ($localEditor['email'] ?? '') : ($localEditor['email'] ?? ''))); if ($email !== '') { $localEmails[strtolower($email)] = true; } } $filteredGw = []; foreach ($editorsGw as $gwEditor) { if (!is_array($gwEditor)) { continue; } $gwEmail = trim((string)($gwEditor['email'] ?? '')); if ($gwEmail !== '' && isset($localEmails[strtolower($gwEmail)])) { continue; } unset($gwEditor['email']); $filteredGw[] = $gwEditor; } $specialInfo['editors_gw'] = $filteredGw; unset($specialInfo['applys']); return jsonSuccess($specialInfo); } /** * 添加专刊 * 投稿系统:一专刊最多一个客座编辑(hasEditor=1 时绑定一个 user_id) * 官网:可存在多个客座编辑,hasEditor=1 时会同步创建/关联一个 */ public function addSpecial(){ $data = $this->request->post(); $rule = new Validate([ 'journal_id' => 'require', 'title' => 'require', 'abstract' => 'require', // 'intro' => 'require', 'keywords' => 'require', 'deadline' => 'require', ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $hasEditor = $this->parseHasEditor(isset($data['hasEditor']) ? $data['hasEditor'] : 0); if ($hasEditor) { $editorRule = new Validate([ 'user_id' => 'require|number', /*'email' => 'require', 'first_name' => 'require', 'last_name' => 'require', 'address' => 'require', 'intro' => 'require',*/ ]); if (!$editorRule->check($data)) { return jsonError($editorRule->getError()); } } $base_url = Env::get('journal.base_url'); // 注意:官网 journal 的 /api/Special/addSpecial 在 hasEditor=1 时会同时创建专刊+客座编辑+关系 $remoteData = [ 'journal_id' => $data['journal_id'], 'title' => $data['title'], // 专刊封面(由前端先调 up_icon_file 拿到路径后传入) 'specialIcon' => isset($data['specialIcon']) ? trim((string)$data['specialIcon']) : '', 'abstract' => $data['abstract'], 'intro' => $data['intro'], 'keywords' => $data['keywords'], 'deadline' => $data['deadline'], // 同步 hasEditor 标记到官网;非 1 时不传/不存客座编辑信息 'hasEditor' => $hasEditor ? 1 : 0, ]; $userId = 0; if ($hasEditor) { $userId = intval($data['user_id']); // t_user + t_user_reviewer_info 联查,组装官网客座编辑 $user = $this->user_obj ->alias('u') ->field('u.user_id,u.email,u.realname,u.icon,u.orcid,u.g_website,r.company,r.country,r.website,r.field,r.address,r.introduction,r.department,r.firstname,r.lastname,r.interests') ->join('t_user_reviewer_info r', 'r.reviewer_id = u.user_id AND r.state = 0', 'LEFT') ->where('u.user_id', $userId) ->where('u.state', 0) ->find(); if (!$user) { return jsonError('guest editor user not found'); } $realname = trim(isset($user['realname']) ? $user['realname'] : ''); $nameParts = preg_split('/\s+/', $realname, 2); $addressParts = array_filter([ trim(isset($user['company']) ? $user['company'] : ''), trim(isset($user['department']) ? $user['department'] : ''), trim(isset($user['country']) ? $user['country'] : ''), ]); $specialIcon = ''; // 客座编辑头像 $iconPath = trim(isset($user['icon']) ? $user['icon'] : ''); if ($iconPath !== '') { $normalized = $this->normalizeJournalSpecialIconPath($iconPath); if ($normalized !== '') { $specialIcon = $normalized; } else { $iconSync = $this->uploadGuestEditorIconToJournal($iconPath, $base_url); if (!$iconSync['ok']) { return jsonError('sync guest editor icon failed: ' . $iconSync['msg']); } $specialIcon = $iconSync['special_icon']; } } $remoteData['email'] = trim(isset($user['email']) ? $user['email'] : ''); $remoteData['first_name'] = !empty($user['firstname']) ? $user['firstname'] : (isset($nameParts[0]) ? $nameParts[0] : ''); $remoteData['last_name'] = !empty($user['lastname']) ? $user['lastname'] : (isset($nameParts[1]) ? $nameParts[1] : ''); $remoteData['address'] = !empty($user['address']) ? $user['address'] : (!empty($addressParts) ? implode(', ', $addressParts) : '-'); $remoteData['interests'] = isset($user['interests']) ? trim($user['interests']) : ''; $remoteData['website'] = trim(!empty($user['website']) ? $user['website'] : (isset($user['g_website']) ? $user['g_website'] : '')); $remoteData['orcid'] = trim(isset($user['orcid']) ? $user['orcid'] : ''); // 客座编辑头像 $remoteData['icon'] = $specialIcon; } $raw = myPost($base_url . "/api/Special/addSpecial", $remoteData); $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : ('add special failed: ' . (is_string($raw) ? $raw : 'remote empty')); return jsonError($msg); } $specialInfo = isset($res['data']) ? $res['data'] : []; $specialInfo['guest_editor_bound'] = 0; $specialInfo['guest_editor_synced'] = 0; $specialInfo['hasEditor'] = $hasEditor ? 1 : 0; $specialId = isset($specialInfo['journal_special_id']) ? intval($specialInfo['journal_special_id']) : 0; if ($specialId <= 0) { $specialId = $this->resolveCreatedSpecialIdFromJournal($base_url, $data); } if ($specialId <= 0) { return jsonError('add special success but missing journal_special_id'); } $specialInfo['special_id'] = $specialId; // hasEditor=1:同步 tougao 客座身份(官网侧 addSpecial 已完成编辑与关系写入) if ($hasEditor) { $check = $this->user_to_special_obj ->where('user_id', $userId) ->where('special_id', $specialId) ->where('uts_state', 0) ->find(); if (!$check) { $insertId = $this->user_to_special_obj->insertGetId([ 'user_id' => $userId, 'special_id' => $specialId, 'uts_ctime' => time(), 'uts_state' => 0, ]); if (!$insertId) { return jsonError('bind guest editor to special failed'); } } $specialInfo['guest_editor_synced'] = 1; $specialInfo['guest_editor_bound'] = 1; $specialInfo['user_id'] = $userId; } return jsonSuccess($specialInfo); } /** * 解析前端“是否有客座编辑”参数 */ private function parseHasEditor($value) { if (is_bool($value)) { return $value; } if (is_numeric($value)) { return intval($value) === 1; } $value = strtolower(trim(strval($value))); return in_array($value, ['1', 'true', 'yes', 'y'], true); } /** * 将投稿系统的用户头像上传到官网 specialIcon 目录 * @return array{ok:bool,msg:string,special_icon:string} */ private function uploadGuestEditorIconToJournal($iconPath, $baseUrl) { $localPath = $this->resolveGuestEditorIconLocalPath($iconPath); if ($localPath === '' || !is_file($localPath)) { return ['ok' => false, 'msg' => 'icon file not found: ' . $iconPath, 'special_icon' => '']; } $mime = function_exists('mime_content_type') ? mime_content_type($localPath) : ''; if ($mime === '' || $mime === false) { $mime = 'application/octet-stream'; } $originName = basename($localPath); $url = rtrim($baseUrl, '/') . '/api/Special/up_icon_file'; $postFields = [ 'specialIcon' => new \CURLFile($localPath, $mime, $originName), ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $raw = curl_exec($ch); $err = curl_error($ch); curl_close($ch); if ($raw === false || $raw === '') { return ['ok' => false, 'msg' => ($err !== '' ? $err : 'upload icon failed'), 'special_icon' => '']; } $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : 'upload icon failed'; return ['ok' => false, 'msg' => $msg, 'special_icon' => '']; } $upurl = trim(isset($res['upurl']) ? $res['upurl'] : ''); if ($upurl === '') { return ['ok' => false, 'msg' => 'journal up_icon_file returned empty upurl', 'special_icon' => '']; } $upurl = ltrim(str_replace('\\', '/', $upurl), '/'); return ['ok' => true, 'msg' => 'success', 'special_icon' => 'specialIcon/' . $upurl]; } /** * 解析投稿系统头像相对路径/URL到本地文件路径 */ private function resolveGuestEditorIconLocalPath($iconPath) { $iconPath = trim((string)$iconPath); if ($iconPath === '') { return ''; } // 绝对路径 if (preg_match('/^([a-zA-Z]:[\\\\\/]|\/)/', $iconPath) && is_file($iconPath)) { return $iconPath; } // URL 场景:尝试取 path 映射到本地 ROOT/public if (preg_match('#^https?://#i', $iconPath)) { $path = parse_url($iconPath, PHP_URL_PATH); if (!empty($path)) { $candidate = rtrim(ROOT_PATH, '/\\') . ltrim($path, '/\\'); if (is_file($candidate)) { return $candidate; } $candidate = rtrim(ROOT_PATH, '/\\') . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . ltrim($path, '/\\'); if (is_file($candidate)) { return $candidate; } } } // 相对路径场景:优先按 public 目录解析 $relative = ltrim($iconPath, '/\\'); $relativeWithoutPublic = preg_replace('#^public[\/\\\\]#i', '', $relative); $relativeWithoutUsericon = preg_replace('#^usericon[\/\\\\]#i', '', $relativeWithoutPublic); $candidates = [ rtrim(ROOT_PATH, '/\\') . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'usericon' . DIRECTORY_SEPARATOR . $relativeWithoutUsericon, rtrim(ROOT_PATH, '/\\') . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . $relativeWithoutPublic, rtrim(ROOT_PATH, '/\\') . DIRECTORY_SEPARATOR . $relative, ]; foreach ($candidates as $candidate) { if (is_file($candidate)) { return $candidate; } } return ''; } /** * 若入参已是官网 specialIcon 路径,直接标准化返回;否则返回空字符串 */ private function normalizeJournalSpecialIconPath($iconPath) { $iconPath = trim((string)$iconPath); if ($iconPath === '') { return ''; } $path = str_replace('\\', '/', $iconPath); // 已是相对路径 specialIcon/xxx if (stripos($path, 'specialIcon/') === 0) { return 'specialIcon/' . ltrim(substr($path, strlen('specialIcon/')), '/'); } // 可能是官网完整 URL,提取 /specialIcon/ 后半段 if (preg_match('#/specialIcon/(.+)$#i', $path, $m)) { return 'specialIcon/' . ltrim($m[1], '/'); } return ''; } /** * 官网 addSpecial 成功但未返回 journal_special_id 时,按条件反查 */ private function resolveCreatedSpecialIdFromJournal($baseUrl, array $data) { $journalId = intval(isset($data['journal_id']) ? $data['journal_id'] : 0); $title = trim(isset($data['title']) ? $data['title'] : ''); $deadline = trim(isset($data['deadline']) ? $data['deadline'] : ''); if ($journalId <= 0 || $title === '') { return 0; } // 1) 优先走精确查询接口(不受 state=2 过滤影响) $rawUnique = myPost(rtrim($baseUrl, '/') . '/api/Special/getSpecialIdByUnique', [ 'journal_id' => $journalId, 'title' => $title, 'deadline' => $deadline, ]); $resUnique = object_to_array(json_decode($rawUnique)); if (isset($resUnique['code']) && intval($resUnique['code']) === 0) { $id = intval(isset($resUnique['data']['journal_special_id']) ? $resUnique['data']['journal_special_id'] : 0); if ($id > 0) { return $id; } } // 2) API 端(仅 state=2) $raw = myPost(rtrim($baseUrl, '/') . '/api/Special/getSpecialsNew', [ 'journal_id' => $journalId, 'pageIndex' => 1, 'pageSize' => 30, ]); $res = object_to_array(json_decode($raw)); $specials = isset($res['data']['specials']) && is_array($res['data']['specials']) ? $res['data']['specials'] : []; $id = $this->pickSpecialIdByTitleDeadline($specials, $title, $deadline); if ($id > 0) { return $id; } // 3) Master 端(state<>1),覆盖“刚创建未到 state=2”场景 $rawMaster = myPost(rtrim($baseUrl, '/') . '/master/Special/getSpecialList', [ 'journal_id' => $journalId, 'state' => -1, 'pageIndex' => 1, 'pageSize' => 50, ]); $resMaster = object_to_array(json_decode($rawMaster)); $specialsMaster = isset($resMaster['specials']) && is_array($resMaster['specials']) ? $resMaster['specials'] : []; if (empty($specialsMaster) && isset($resMaster['data']['specials']) && is_array($resMaster['data']['specials'])) { $specialsMaster = $resMaster['data']['specials']; } $id = $this->pickSpecialIdByTitleDeadline($specialsMaster, $title, $deadline); if ($id > 0) { return $id; } return 0; } /** * 按 title/deadline 从列表里挑选 journal_special_id */ private function pickSpecialIdByTitleDeadline(array $specials, $title, $deadline) { if (empty($specials)) { return 0; } foreach ($specials as $sp) { if (trim((string)($sp['title'] ?? '')) === $title && trim((string)($sp['deadline'] ?? '')) === $deadline ) { return intval($sp['journal_special_id'] ?? 0); } } foreach ($specials as $sp) { if (trim((string)($sp['title'] ?? '')) === $title) { return intval($sp['journal_special_id'] ?? 0); } } return 0; } /** * 获取已开通的专刊列表(代理 journal getSpecials,state=2) * 入参为投稿系统 journal_id:先按 ISSN 换官网 journal_id,再调官网接口 */ public function getSpecials() { $data = $this->request->post(); $rule = new Validate([ 'journal_id' => 'require|number', ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $localJournalId = intval($data['journal_id']); $journalInfo = $this->journal_obj->where('journal_id', $localJournalId)->find(); if (!$journalInfo) { return jsonError('journal not found'); } $issn = trim(isset($journalInfo['issn']) ? (string)$journalInfo['issn'] : ''); if ($issn === '') { return jsonError('journal issn is empty'); } $resolved = $this->resolveSiteJournalIdByIssn($issn); if (!$resolved['ok']) { return jsonError($resolved['msg']); } $siteJournalId = intval($resolved['journal_id']); $base_url = Env::get('journal.base_url'); $res = object_to_array(json_decode(myPost(rtrim($base_url, '/') . '/api/Special/getSpecials', [ 'journal_id' => $siteJournalId, ]))); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : 'get specials failed'; return jsonError($msg); } $re = isset($res['data']) ? $res['data'] : []; if (!isset($re['specials'])) { $re['specials'] = []; } if (!isset($re['is_show'])) { $re['is_show'] = count($re['specials']) > 0 ? 'true' : 'false'; } return jsonSuccess($re); } /** * 编辑专刊基础信息(代理 journal master/Special/editSpecialBasic) */ public function editSpecialBasic() { $data = $this->request->post(); $rule = new Validate([ 'journal_special_id' => 'require|number', 'title' => 'require', 'intro' => 'require', 'abstract' => 'require', 'keywords' => 'require', 'deadline' => 'require', ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $base_url = Env::get('journal.base_url'); $postData = [ 'journal_special_id' => intval($data['journal_special_id']), 'title' => trim((string)$data['title']), 'intro' => trim((string)$data['intro']), // 专刊封面 'icon' => isset($data['specialIcon']) ? trim((string)$data['specialIcon']) : '', 'abstract' => trim((string)$data['abstract']), 'keywords' => trim((string)$data['keywords']), 'deadline' => trim((string)$data['deadline']), ]; $raw = myPost($base_url . "/master/Special/editSpecialBasic", $postData); $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : 'edit special basic failed'; return jsonError($msg); } return jsonSuccess([]); } /** * 修改专刊信息(同步官网)+ 客座编辑信息(仅更新投稿系统本地) * 专刊:调用 journal master/Special/editSpecialBasic * 投稿系统一专刊仅一个客座编辑:hasEditor=1 时若已有其他绑定则先解绑旧编辑(本地+官网),再绑定传入 user_id * 官网允许一专刊多编辑,替换时只处理被替换的那条关系 * hasEditor=1 时 user_id 必填;hasEditor 改为 0 时解除本地该专刊全部绑定,并调官网 delAllSpecialToEditor 清空关系 */ public function editSpecialAndEditorLocal() { $data = $this->request->post(); $hasEditor = $this->parseHasEditor(isset($data['hasEditor']) ? $data['hasEditor'] : 0); $ruleFields = [ 'journal_special_id' => 'require|number', 'title' => 'require', // 'intro' => 'require', 'abstract' => 'require', 'keywords' => 'require', 'deadline' => 'require', // 必填:漏传会被当成无客座编辑,导致误解绑 'hasEditor' => 'require', ]; if ($hasEditor) { $ruleFields['user_id'] = 'require|number'; } $rule = new Validate($ruleFields); if (!$rule->check($data)) { return jsonError($rule->getError()); } $specialId = intval($data['journal_special_id']); $userId = isset($data['user_id']) ? intval($data['user_id']) : 0; $base_url = Env::get('journal.base_url'); // 1) 专刊信息同步官网(含 hasEditor 标记) $remoteData = [ 'journal_special_id' => $specialId, 'title' => trim((string)$data['title']), 'intro' => trim((string)$data['intro']), // 专刊封面 'icon' => isset($data['specialIcon']) ? trim((string)$data['specialIcon']) : '', 'abstract' => trim((string)$data['abstract']), 'keywords' => trim((string)$data['keywords']), 'deadline' => trim((string)$data['deadline']), 'hasEditor' => $hasEditor ? 1 : 0, ]; $raw = myPost($base_url . "/master/Special/editSpecialBasic", $remoteData); $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : 'sync special to journal failed'; return jsonError($msg); } // hasEditor 非 1:专刊无客座编辑,解除官网全部关系 + 本地全部绑定 if (!$hasEditor) { $rawDelAll = myPost($base_url . "/master/Special/delAllSpecialToEditor", [ 'journal_special_id' => $specialId, ]); $delAllRes = object_to_array(json_decode($rawDelAll)); if (!isset($delAllRes['code']) || intval($delAllRes['code']) !== 0) { $msg = isset($delAllRes['msg']) ? $delAllRes['msg'] : 'delete all remote relations failed'; return jsonError($msg); } $localUnbound = $this->user_to_special_obj ->where('special_id', $specialId) ->where('uts_state', 0) ->update(['uts_state' => 1]); if ($localUnbound === false) { return jsonError('delete local relation failed'); } return jsonSuccess([ 'journal_special_id' => $specialId, 'hasEditor' => 0, 'special_synced' => 1, 'editor_local_updated' => 0, 'local_unbound' => intval($localUnbound), 'remote_unbound' => 1, ]); } // 2) 客座编辑信息只更新投稿系统本地 Db::startTrans(); try { $firstname = trim((string)$data['firstname']); $lastname = trim((string)$data['lastname']); $userUpdate = [ 'realname' => trim($firstname . ' ' . $lastname), ]; if (isset($data['orcid'])) { $userUpdate['orcid'] = trim((string)$data['orcid']); } if (isset($data['phone'])) { $userUpdate['phone'] = trim((string)$data['phone']); } if (isset($data['icon'])) { $userUpdate['icon'] = trim((string)$data['icon']); } $upUser = $this->user_obj->where('user_id', $userId)->where('state', 0)->update($userUpdate); if ($upUser === false) { throw new \Exception('update user failed'); } $reviewerInfo = $this->user_reviewer_info_obj ->where('reviewer_id', $userId) ->where('state', 0) ->find(); $reviewerUpdate = [ 'firstname' => $firstname, 'lastname' => $lastname, ]; if (isset($data['website'])) { $reviewerUpdate['website'] = trim((string)$data['website']); } if (isset($data['address'])) { $reviewerUpdate['address'] = trim((string)$data['address']); } if (isset($data['interests'])) { $reviewerUpdate['interests'] = trim((string)$data['interests']); } if ($reviewerInfo) { $upReviewer = $this->user_reviewer_info_obj ->where('reviewer_id', $userId) ->where('state', 0) ->update($reviewerUpdate); if ($upReviewer === false) { throw new \Exception('update user_reviewer_info failed'); } } else { $reviewerUpdate['reviewer_id'] = $userId; $reviewerUpdate['test_from'] = 'kzeditor'; $insReviewer = $this->user_reviewer_info_obj->insert($reviewerUpdate); if (!$insReviewer) { throw new \Exception('insert user_reviewer_info failed'); } } Db::commit(); } catch (\Exception $e) { Db::rollback(); return jsonError($e->getMessage()); } // 3) 一专刊一个客座编辑:先解绑其他本地绑定(并解除对应官网关系),再绑定当前 user_id $editorReplaced = 0; $unbindOthers = $this->unbindGuestEditorsOfSpecial($specialId, $base_url, $userId); if (!$unbindOthers['ok']) { return jsonError($unbindOthers['msg']); } if ($unbindOthers['local_unbound'] > 0) { $editorReplaced = 1; } $guestBound = 0; $guestSynced = 0; $check = $this->user_to_special_obj ->where('user_id', $userId) ->where('special_id', $specialId) ->where('uts_state', 0) ->find(); if ($check) { $guestBound = 1; } else { $bindResult = $this->bindGuestEditorToSpecial($specialId, $userId, $base_url); if (!$bindResult['ok']) { return jsonError($bindResult['msg']); } $guestBound = 1; $guestSynced = intval($bindResult['remote_bound']); } return jsonSuccess([ 'journal_special_id' => $specialId, 'user_id' => $userId, 'hasEditor' => 1, 'special_synced' => 1, 'editor_local_updated' => 1, 'editor_replaced' => $editorReplaced, 'local_unbound' => $unbindOthers['local_unbound'], 'remote_unbound' => $unbindOthers['remote_unbound'], 'guest_editor_bound' => $guestBound, 'guest_editor_synced' => $guestSynced, ]); } /** * 删除专刊的客座编辑 * 默认会同步删除官网专刊-编辑关系(可通过 sync_remote=0 关闭) */ public function delSpecialEditor() { $data = $this->request->post(); $rule = new Validate([ 'journal_special_id' => 'require|number', 'user_id' => 'require|number', ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $specialId = intval($data['journal_special_id']); $userId = intval($data['user_id']); $syncRemote = !isset($data['sync_remote']) || intval($data['sync_remote']) === 1; $bind = $this->user_to_special_obj ->where('special_id', $specialId) ->where('user_id', $userId) ->where('uts_state', 0) ->find(); if (!$bind) { return jsonSuccess([ 'journal_special_id' => $specialId, 'user_id' => $userId, 'local_deleted' => 0, 'remote_deleted' => 0, 'msg' => 'already deleted or not bound', ]); } $remoteDeleted = 0; if ($syncRemote) { $base_url = Env::get('journal.base_url'); $user = $this->user_obj->where('user_id', $userId)->find(); $email = trim(isset($user['email']) ? $user['email'] : ''); if ($email !== '') { $raw = myPost($base_url . "/master/Special/getSpecialDetail", [ 'journal_special_id' => $specialId ]); $res = object_to_array(json_decode($raw)); $editors = isset($res['editors']) ? $res['editors'] : (isset($res['data']['editors']) ? $res['data']['editors'] : []); if (!is_array($editors)) { $editors = []; } $journalSpecialEditorId = 0; foreach ($editors as $editor) { if (trim((string)($editor['email'] ?? '')) === $email) { $journalSpecialEditorId = intval($editor['journal_special_editor_id'] ?? 0); if ($journalSpecialEditorId > 0) { break; } } } if ($journalSpecialEditorId > 0) { $rawDel = myPost($base_url . "/master/Special/delSpecialToEditor", [ 'journal_special_id' => $specialId, 'journal_special_editor_id' => $journalSpecialEditorId, ]); $delRes = object_to_array(json_decode($rawDel)); if (!isset($delRes['code']) || intval($delRes['code']) !== 0) { $msg = isset($delRes['msg']) ? $delRes['msg'] : 'delete remote relation failed'; return jsonError($msg); } $remoteDeleted = 1; } } } $up = $this->user_to_special_obj ->where('special_id', $specialId) ->where('user_id', $userId) ->where('uts_state', 0) ->update(['uts_state' => 1]); if ($up === false) { return jsonError('delete local relation failed'); } return jsonSuccess([ 'journal_special_id' => $specialId, 'user_id' => $userId, 'local_deleted' => $up > 0 ? 1 : 0, 'remote_deleted' => $remoteDeleted, ]); } /** * hasEditor=1 且传入 user_id 无有效绑定时: * 1) 本地把该 user_id 绑定到专刊 * 2) 同步官网:邮箱已存在则跳过 addSpecialEditor;关系已存在则跳过 addSpecialToEditor * @return array{ok:bool,msg:string,remote_bound:int} */ private function bindGuestEditorToSpecial($specialId, $userId, $baseUrl) { $specialId = intval($specialId); $userId = intval($userId); $baseUrl = rtrim($baseUrl, '/'); $user = $this->user_obj ->alias('u') ->field('u.user_id,u.email,u.realname,u.icon,u.orcid,u.g_website,r.company,r.country,r.website,r.address,r.department,r.firstname,r.lastname,r.interests') ->join('t_user_reviewer_info r', 'r.reviewer_id = u.user_id AND r.state = 0', 'LEFT') ->where('u.user_id', $userId) ->where('u.state', 0) ->find(); if (!$user) { return ['ok' => false, 'msg' => 'guest editor user not found', 'remote_bound' => 0]; } $email = trim(isset($user['email']) ? $user['email'] : ''); if ($email === '') { return ['ok' => false, 'msg' => 'guest editor email is empty', 'remote_bound' => 0]; } $raw = myPost($baseUrl . "/master/Special/getSpecialDetail", [ 'journal_special_id' => $specialId, ]); $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : 'get special detail failed'; return ['ok' => false, 'msg' => $msg, 'remote_bound' => 0]; } $payload = isset($res['data']) && is_array($res['data']) ? $res['data'] : $res; $special = isset($payload['special']) ? $payload['special'] : []; $journalId = intval(isset($special['journal_id']) ? $special['journal_id'] : 0); if ($journalId <= 0) { return ['ok' => false, 'msg' => 'journal_id missing from special detail', 'remote_bound' => 0]; } $editors = isset($payload['editors']) ? $payload['editors'] : []; if (!is_array($editors)) { $editors = []; } // 专刊下已建立有效关系:按邮箱匹配,跳过 addSpecialEditor / addSpecialToEditor $journalSpecialEditorId = 0; $alreadyRelated = false; $emailLower = strtolower($email); foreach ($editors as $editor) { if ($emailLower !== '' && strtolower(trim((string)($editor['email'] ?? ''))) === $emailLower) { $journalSpecialEditorId = intval($editor['journal_special_editor_id'] ?? 0); if ($journalSpecialEditorId > 0) { $alreadyRelated = true; break; } } } if (!$alreadyRelated) { // 官网是否已有该邮箱编辑档案 if ($journalSpecialEditorId <= 0) { $rawFind = myPost($baseUrl . "/master/Special/getSpecialEditorByEmail", [ 'journal_id' => $journalId, 'email' => $email, ]); $findRes = object_to_array(json_decode($rawFind)); if (isset($findRes['code']) && intval($findRes['code']) === 0) { $findData = isset($findRes['data']) && is_array($findRes['data']) ? $findRes['data'] : $findRes; $journalSpecialEditorId = intval(isset($findData['journal_special_editor_id']) ? $findData['journal_special_editor_id'] : 0); } } // 邮箱不存在才创建编辑档案 if ($journalSpecialEditorId <= 0) { $realname = trim(isset($user['realname']) ? $user['realname'] : ''); $nameParts = preg_split('/\s+/', $realname, 2); $firstname = !empty($user['firstname']) ? $user['firstname'] : (isset($nameParts[0]) ? $nameParts[0] : ''); $lastname = !empty($user['lastname']) ? $user['lastname'] : (isset($nameParts[1]) ? $nameParts[1] : ''); $addressParts = array_filter([ trim(isset($user['company']) ? $user['company'] : ''), trim(isset($user['department']) ? $user['department'] : ''), trim(isset($user['country']) ? $user['country'] : ''), ]); $address = !empty($user['address']) ? $user['address'] : (!empty($addressParts) ? implode(', ', $addressParts) : '-'); $specialIcon = ''; $iconPath = trim(isset($user['icon']) ? $user['icon'] : ''); if ($iconPath !== '') { $normalized = $this->normalizeJournalSpecialIconPath($iconPath); if ($normalized !== '') { $specialIcon = $normalized; } else { $iconSync = $this->uploadGuestEditorIconToJournal($iconPath, $baseUrl); if ($iconSync['ok']) { $specialIcon = $iconSync['special_icon']; } } } $rawAddEditor = myPost($baseUrl . "/master/Special/addSpecialEditor", [ 'journal_id' => $journalId, 'email' => $email, 'first_name' => $firstname !== '' ? $firstname : '-', 'last_name' => $lastname !== '' ? $lastname : '-', 'address' => $address !== '' ? $address : '-', 'interests' => isset($user['interests']) ? trim($user['interests']) : '', 'website' => trim(!empty($user['website']) ? $user['website'] : (isset($user['g_website']) ? $user['g_website'] : '')), 'orcid' => trim(isset($user['orcid']) ? $user['orcid'] : ''), 'specialIcon' => $specialIcon, ]); $addEditorRes = object_to_array(json_decode($rawAddEditor)); if (!isset($addEditorRes['code']) || intval($addEditorRes['code']) !== 0) { $msg = isset($addEditorRes['msg']) ? $addEditorRes['msg'] : 'add special editor failed'; return ['ok' => false, 'msg' => $msg, 'remote_bound' => 0]; } $journalSpecialEditorId = intval(isset($addEditorRes['data']['journal_special_editor_id']) ? $addEditorRes['data']['journal_special_editor_id'] : (isset($addEditorRes['journal_special_editor_id']) ? $addEditorRes['journal_special_editor_id'] : 0)); if ($journalSpecialEditorId <= 0) { return ['ok' => false, 'msg' => 'add special editor success but missing journal_special_editor_id', 'remote_bound' => 0]; } } // 未建立关系:调用 addSpecialToEditor $rawBind = myPost($baseUrl . "/master/Special/addSpecialToEditor", [ 'journal_id' => $journalId, 'journal_special_id' => $specialId, 'journal_special_editor_id' => $journalSpecialEditorId, ]); $bindRes = object_to_array(json_decode($rawBind)); if (!isset($bindRes['code']) || intval($bindRes['code']) !== 0) { $msg = isset($bindRes['msg']) ? $bindRes['msg'] : 'add special to editor failed'; return ['ok' => false, 'msg' => $msg, 'remote_bound' => 0]; } } // 本地绑定传入的 user_id:优先恢复软删,否则新建 $softBind = $this->user_to_special_obj ->where('user_id', $userId) ->where('special_id', $specialId) ->where('uts_state', 1) ->find(); if ($softBind) { $up = $this->user_to_special_obj ->where('user_id', $userId) ->where('special_id', $specialId) ->where('uts_state', 1) ->update(['uts_state' => 0]); if ($up === false) { return ['ok' => false, 'msg' => 'restore local bind failed', 'remote_bound' => $alreadyRelated ? 0 : 1]; } } else { $insertId = $this->user_to_special_obj->insertGetId([ 'user_id' => $userId, 'special_id' => $specialId, 'uts_ctime' => time(), 'uts_state' => 0, ]); if (!$insertId) { return ['ok' => false, 'msg' => 'bind guest editor to special failed', 'remote_bound' => $alreadyRelated ? 0 : 1]; } } return ['ok' => true, 'msg' => '', 'remote_bound' => $alreadyRelated ? 0 : 1]; } /** * 解除专刊下本地客座绑定,并同步官网 delSpecialToEditor * 投稿系统一专刊仅一个客座编辑;exceptUserId>0 时保留该 user_id(替换场景) * 官网可有多编辑:只解除投稿侧曾绑定编辑的 delSpecialToEditor,不清理其他官网编辑 * @return array{ok:bool,msg:string,local_unbound:int,remote_unbound:int} */ private function unbindGuestEditorsOfSpecial($specialId, $baseUrl, $exceptUserId = 0) { $specialId = intval($specialId); $baseUrl = rtrim($baseUrl, '/'); $query = $this->user_to_special_obj ->where('special_id', $specialId) ->where('uts_state', 0); if (intval($exceptUserId) > 0) { $query->where('user_id', '<>', intval($exceptUserId)); } $oldUserIds = $query->column('user_id'); if (empty($oldUserIds)) { return ['ok' => true, 'msg' => '', 'local_unbound' => 0, 'remote_unbound' => 0]; } // 旧 user_id → 邮箱 → 官网编辑关系 $emails = []; foreach ($this->user_obj->where('user_id', 'in', $oldUserIds)->column('email') as $email) { $email = strtolower(trim((string)$email)); if ($email !== '') { $emails[$email] = true; } } $remoteUnbound = 0; foreach ($this->fetchJournalSpecialEditors($specialId, $baseUrl) as $editor) { $editorId = intval($editor['journal_special_editor_id'] ?? 0); if ($editorId <= 0 || !isset($emails[strtolower(trim((string)($editor['email'] ?? '')))])) { continue; } $delRes = object_to_array(json_decode(myPost($baseUrl . "/master/Special/delSpecialToEditor", [ 'journal_special_id' => $specialId, 'journal_special_editor_id' => $editorId, ]))); if (!isset($delRes['code']) || intval($delRes['code']) !== 0) { $msg = isset($delRes['msg']) ? $delRes['msg'] : 'delete remote relation failed'; return ['ok' => false, 'msg' => $msg, 'local_unbound' => 0, 'remote_unbound' => $remoteUnbound]; } $remoteUnbound++; } $localUnbound = $this->user_to_special_obj ->where('special_id', $specialId) ->where('user_id', 'in', $oldUserIds) ->where('uts_state', 0) ->update(['uts_state' => 1]); if ($localUnbound === false) { return ['ok' => false, 'msg' => 'delete local relation failed', 'local_unbound' => 0, 'remote_unbound' => $remoteUnbound]; } return [ 'ok' => true, 'msg' => '', 'local_unbound' => intval($localUnbound), 'remote_unbound' => $remoteUnbound, ]; } /** * 获取官网专刊已关联的客座编辑列表 */ private function fetchJournalSpecialEditors($specialId, $baseUrl) { $res = object_to_array(json_decode(myPost(rtrim($baseUrl, '/') . "/master/Special/getSpecialDetail", [ 'journal_special_id' => intval($specialId), ]))); $payload = isset($res['data']) && is_array($res['data']) ? $res['data'] : $res; return isset($payload['editors']) && is_array($payload['editors']) ? $payload['editors'] : []; } /** * 上传专刊封面图(转发至 journal master/Journal/up_file) * 入参表单字段兼容 specialIcon / journalicon;转发官网时使用 journalicon */ public function up_icon_file() { $file = request()->file('specialIcon'); if (!$file) { $file = request()->file('journalicon'); } if (!$file) { return jsonError('specialIcon is required'); } $info = $file->getInfo(); $tmpPath = isset($info['tmp_name']) ? $info['tmp_name'] : ''; $originName = isset($info['name']) ? $info['name'] : 'journalicon'; $mime = isset($info['type']) && $info['type'] !== '' ? $info['type'] : 'application/octet-stream'; if ($tmpPath === '' || !is_file($tmpPath)) { return jsonError('upload file invalid'); } $base_url = Env::get('journal.base_url'); $url = rtrim($base_url, '/') . '/master/Journal/up_file'; $postFields = [ 'journalicon' => new \CURLFile(realpath($tmpPath), $mime, $originName), ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $raw = curl_exec($ch); $err = curl_error($ch); curl_close($ch); if ($raw === false || $raw === '') { return jsonError($err !== '' ? $err : 'upload to journal failed'); } $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : 'upload icon failed'; return jsonError($msg); } // 与 journal 返回保持一致,便于前端直接用 upurl return json([ 'code' => 0, 'msg' => 'success', 'upurl' => isset($res['upurl']) ? $res['upurl'] : '', ]); } /** * 更改专刊状态(代理 journal master/Special/changeSpecialState) * state: 0初始 1拒绝 2通过 */ public function changeSpecialState() { $data = $this->request->post(); $rule = new Validate([ 'journal_special_id' => 'require|number', 'state' => 'require|number|in:0,1,2', ]); if (!$rule->check($data)) { return jsonError($rule->getError()); } $specialId = intval($data['journal_special_id']); $state = intval($data['state']); $base_url = Env::get('journal.base_url'); $raw = myPost($base_url . "/master/Special/changeSpecialState", [ 'journal_special_id' => $specialId, 'state' => $state, ]); $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : 'change special state failed'; return jsonError($msg); } return jsonSuccess([ 'journal_special_id' => $specialId, 'state' => $state, ]); } /** * 根据 ISSN 获取官网期刊 ID(代理 journal master/Special/getJournalIdByIssn) * 入参兼容 journal_issn / issn;转发官网时使用字段名 issn */ public function getJournalIdByIssn() { $data = $this->request->post(); $issn = ''; if (isset($data['journal_issn'])) { $issn = trim((string)$data['journal_issn']); } if ($issn === '' && isset($data['issn'])) { $issn = trim((string)$data['issn']); } if ($issn === '') { return jsonError('issn不能为空'); } $resolved = $this->resolveSiteJournalIdByIssn($issn); if (!$resolved['ok']) { return jsonError($resolved['msg']); } return jsonSuccess([ 'journal_issn' => $issn, 'journal_id' => intval($resolved['journal_id']), ]); } /** * 按 ISSN 向官网解析 journal_id * @return array{ok:bool,msg:string,journal_id:int} */ private function resolveSiteJournalIdByIssn($issn) { $issn = trim((string)$issn); if ($issn === '') { return ['ok' => false, 'msg' => 'issn不能为空', 'journal_id' => 0]; } $base_url = Env::get('journal.base_url'); $raw = myPost(rtrim($base_url, '/') . '/master/Special/getJournalIdByIssn', [ 'issn' => $issn, ]); $res = object_to_array(json_decode($raw)); if (!isset($res['code']) || intval($res['code']) !== 0) { $msg = isset($res['msg']) ? $res['msg'] : ('get journal id by issn failed: ' . (is_string($raw) ? $raw : 'remote empty')); return ['ok' => false, 'msg' => $msg, 'journal_id' => 0]; } $payload = isset($res['data']) ? $res['data'] : []; $journalId = 0; if (is_array($payload)) { $journalId = intval(isset($payload['journal_id']) ? $payload['journal_id'] : 0); } elseif (is_numeric($payload)) { $journalId = intval($payload); } if ($journalId <= 0 && isset($res['journal_id'])) { $journalId = intval($res['journal_id']); } if ($journalId <= 0) { return ['ok' => false, 'msg' => 'journal id not found for issn', 'journal_id' => 0]; } return ['ok' => true, 'msg' => '', 'journal_id' => $journalId]; } public function up_approval_file() { $file = request()->file('articleApproval'); if ($file) { $info = $file->move(ROOT_PATH . 'public' . DS . 'articleApproval'); if ($info) { return json(['code' => 0, 'upurl' => str_replace("\\", "/", $info->getSaveName())]); } else { return json(['code' => 1, 'msg' => $file->getError()]); } } } }