Files
tougao/application/api/controller/Aiarticle.php
2025-06-25 17:03:26 +08:00

1660 lines
76 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\api\controller;
use app\api\controller\Base;
use think\Controller;
use think\Db;
use app\common\QrCodeImage;
use app\common\Wechat;
use app\common\Material;
use think\Cache;
use think\Queue;
/**
* @title 数据库接口
* @description 数据库接口
* @group 数据库接口
*/
class Aiarticle extends Base
{
protected $aLogo = ['media_id' => 'Cn8zlXvVB5DwjcA9h40z9fprHDoc3Jqv97SwrInpmyYiilkeRdKvpD63cWqTYHfz','url' => 'http://mmbiz.qpic.cn/mmbiz_jpg/QHFVW13lONaQJxK9QbHU9CtrvTS2ModZnUyeAvuVN67t8XP85DxVJwDJf2YxCTalrsr17jS080xM6xQv5yGiaEQ/0?wx_fmt=jpeg'];//默认头像
/**OPENAI相关配置----------start**/
//OPENAI接口地址
protected $sUrl = 'https://api.openai.com/v1/chat/completions';
//OPENAI接口key
protected $sApiKey = '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';
protected $proxy = '';
//接口返回对象
protected $sResponesData;
//接口返回错误信息
protected $sError;
//接口超时请求
protected $timeout = 60;
/**OPENAI相关配置----------end**/
//数据表必填字段[ai_article]
protected $aAiFileds = ['article_id','title_english','title_chinese','journal_issn','covered','digest','research_result','content','highlights','discussion','prospect','research_background','discussion_results','research_method','overview','summary'];
//期刊接口地址
protected $sJournalUrl = 'http://journalapi.tmrjournals.com/public/index.php';//'http://zmzm.journal.dev.com'; //
//期刊官网
protected $sJournalUsx = 'https://www.tmrjournals.com';
//投稿系统
protected $sSubmissionUrl = 'https://submission.tmrjournals.com/';
protected $aOpenAiAsk = [
1 => '"将以下内容翻译为中文,仅返回翻译结果,不要解释:\n {#content#}"',
'default' => '
**核心要求**
1 内容涵盖哪些学科及方法请罗列
2 学术规范翻译并提炼摘要更强调逻辑性、科学术语准确性和表达的严谨性并且不需要分点展示字数小于200字以便更方便读者阅读
3 研究背景提炼,大于500字
4 针对文章简单总结讨论和结果, 大于450字
5 简单总结文章的研究方法, 大于300字
6 针对稿件内容进行展望撰写
7 总结归纳亮点【3点以上】
8 你是一名资深医学翻译专家,请将标题翻译成中文【内容需自然流畅、口语化、连贯性、学术性】
{#title_chinese#}
9 你是一名资深医学翻译专家,请将文章内容翻译成中文【内容需自然流畅、口语化、连贯性、学术性】
{#content#}
**稿件关键信息**
- 稿件简介:{#abstract#}
- 稿件内容:{#content#}
**输出格式**
中文格式[英文简写忽略首字母大写]
格式内容
```json{"covered": "【总字数<=100】", "digest": "【总字数<=500】", "research_background": "【总字数>200】", "discussion_results": "【总字数>450】","research_method" => "【总字数>300】", "prospect": "", "highlights": "", "title_chinese": "", "content": ""}',
'Review' => '
**核心要求**
1 内容涵盖哪些学科及方法请罗列
2 翻译好的内容将以公众号的形式推送给读者按学术规范翻译并提炼文章概述整体内容应大于1200字其中应包函文章背景不少于400字内容其他内容提炼更强调逻辑性、科学术语准确性和表达的严谨性注意内容不要有严重重复不需要分点展示,大于1000字
3 针对文章结论生成一个简单总结内容不要和文章概述重复字数150以内
4 请将标题翻译成中文【内容需自然流畅、口语化、连贯性、学术性】
{#title_chinese#}
5 请将文章内容翻译成中文【内容需自然流畅、口语化、连贯性、学术性】
{#content#}
**稿件关键信息**
- 稿件简介:{#abstract#}
- 稿件内容:{#content#}
**输出格式**
中文格式[英文简写忽略首字母大写]
格式内容
```json{"covered": "【总字数<=100】", "overview": "【总字数>=1000】", "summary": "【总字数>=300】", "title_chinese": "", "content": ""}'
];
//文章图片icon地址
protected $sArticleIcon = '/public/articleicon/';
//期刊图片icon地址
protected $sJournalIcon = '/public/journalicon/';
//期刊编辑二维码地址
protected $sJournalEditorQrcode = '/public/journaleditorqrcode/';
//作者头像地址
protected $sUserIcon = '/public/usericon/';
//默认头像
protected $sDefaultUserIcon = '/static/img/userImg.f3d9bc3b.jpg';
//默认公众号配置
protected $aWechatConfig = [
'issn' => 'default',
'wechat_name' => 'TMR Publishing Group',
'wechat_app_id' => 'wxda4cc30fe32e6313',
'wechat_app_secret' => 'd5e6002b8b48de46f64dc9a02312f944'
];
//生成AI文章状态
protected $sGenerateStatusName = 'generate_status_article_id';
public function __construct(\think\Request $request = null) {
$this->proxy = '';
parent::__construct($request);
}
/**
* CURL 发送请求到 OpenAI
* @param $messages 内容
* @param $model 模型类型
*/
protected function curlOpenAI($messages, $model = 'gpt-4o'){
$sUrl = $this->sUrl;
$data = [
'model' => $model,
// 'messages' => $messages,
'messages' => [
// [
// 'role' => 'system',
// 'content' => '你是一名资深医学期刊编辑,需遵循医学原则回答以下问题。回答的内容会以微信公众号的形式推送给学术专家,要提高内容的连贯性,逻辑性,科学性,避免让学术专家产生质疑'
// ],
[
'role' => 'user',
'content' => $messages
]
],
'temperature' => 0.3,// 降低随机性0-10为最确定
// 'top_p' => 9, // 控制生成多样性
// 'max_tokens' => 1500, // 防止答案截断
// 'frequency_penalty' => 0.5, // 减少重复内容
// 'presence_penalty' => 0.2 // 鼓励提及新概念
];
$oCurl = curl_init();
// 通用配置
curl_setopt($oCurl, CURLOPT_URL, $sUrl);
// 设置头信息
curl_setopt($oCurl, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->sApiKey
]);
curl_setopt($oCurl, CURLOPT_PROXY,$this->proxy);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER,true);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST,2);
curl_setopt($oCurl, CURLOPT_POST, true); //设置为POST方式
curl_setopt($oCurl, CURLOPT_POSTFIELDS,json_encode($data));
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, TRUE) ; // 获取数据返回
// curl_setopt($oCurl, CURLOPT_TIMEOUT, $this->timeout);
$result = curl_exec($oCurl);
//请求失败
if (curl_errno($oCurl)){
$this->sError = curl_errno($oCurl);
curl_close($oCurl);
return FALSE;
}
$this->sResponesData = json_decode($result);
curl_close($oCurl);
return TRUE;
}
/**
* @title 请求OPENAI
* @param aSearch array 模版内容
* @param iTemplateId 模版ID
*/
private function _createContentForOpenAI($aSearch = [],$sArticleType = 'default',$isTranslate = 2){
if(empty($aSearch) || empty($sArticleType)){
return json_encode(array('status' => 2,'msg' => 'Please select a template or enter the content you want to consult'.$this->sError));
}
//组织参数
if(in_array($sArticleType, ['Mini Review','Review'])){
$sArticleType = 'Review';
}else{
$sArticleType = 'default';
}
if($isTranslate == 1){
$sArticleType = 1;
}
$sTemplate = $this->aOpenAiAsk[$sArticleType];
$sTemplate = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplate);
//请求接口
$sModel = empty($aParam['api_model']) ? 'gpt-4o' : $aParam['api_model'];
$result = $this->curlOpenAI($sTemplate,$sModel);
if($result == FALSE){
return json_encode(array('status' => 2,'msg' => 'Interface request failed'.$this->sError));
}
//处理返回信息
$data = $this->sResponesData;
if(!is_object($data)){
return json_encode(array('status' => 3,'msg' => 'There is a misunderstanding in the data returned by the interface'));
}
$data = object_to_array($data);
$aChoices = $data['choices'] ?? [];
if(empty($aChoices)){
return json_encode(array('status' => 4,'msg' => 'OPENAI did not return data'));
}
$aChoicesInfo = $aChoices[0] ?? [];
$aMessage = $aChoicesInfo['message'] ?? [];
if(empty($aMessage['content'])){
return json_encode(array('status' => 5,'msg' => 'OPENAI returns empty data'));
}
$sContent = trim(trim($aMessage['content'],'```json'),'```');
$aContent = json_decode($sContent,true);
return json_encode(array('status' => 1,'msg' => 'OPENAI successfully generated content','data' => $aContent));
}
public function create($aParam = []){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(array('status' => 2,'msg' => 'Please select an article'.json_encode($aParam) ));
}
//媒体类型
$iMediaType = empty($aParam['media_type']) ? 1 : $aParam['media_type'];
//获取文章是否生成AI内容
$aResult = json_decode($this->getArticle($iArticleId),true);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
if($iStatus != 1){
return json_encode($aResult);
}
//获取数据
$aArticleContent = empty($aResult['data']) ? [] : $aResult['data'];
//文章数据
$aArticle = empty($aArticleContent['article']) ? [] : $aArticleContent['article'];
if(empty($aArticle)){
return json_encode(['status' => 3,'msg' => 'The content of the article is empty']);
}
//期刊数据
$aJournal = empty($aArticleContent['journal']) ? [] : $aArticleContent['journal'];
if(empty($aJournal)){
return json_encode(['status' => 3,'msg' => 'The journal to which the article belongs does not exist or has been closed']);
}
//查询AI内容是否生成
$aAiArticle = json_decode($this->getAiArticle(['article_id' => $iArticleId]),true);
$aAiArticleContent = empty($aAiArticle['data']) ? [] : $aAiArticle['data'];
$aAiArticle = empty($aAiArticleContent['ai_article']) ? [] : $aAiArticleContent['ai_article'];
$iId = empty($aAiArticle['ai_article_id']) ? 0 : $aAiArticle['ai_article_id'];
if(empty($aAiArticle)){
//插入t_ai_article数据
$sIssn = empty($aJournal['issn']) ? '' : $aJournal['issn'];
$aInsert = ['title_english' => $aArticle['title'],'article_id' => $iArticleId,'create_time' => time(),'journal_issn' => $sIssn,'journal_id' => $aArticle['journal_id'],'article_type' => $aArticle['type'],'media_type' => $iMediaType,'journal_issn' => empty($aJournal['issn']) ? '' :$aJournal['issn']];
$iId = Db::name('ai_article')->insertGetId($aInsert);
if($iId === false){
return json_encode(['status' => 4,'msg' => 'Data insertion failed']);
}
$aAiArticle = array_merge(['ai_article_id' => $iId,'article_id' => $iArticleId,'is_generate' => 2],$aInsert);
}
//判断是否生成
if(!empty($aAiArticle['is_generate']) && $aAiArticle['is_generate'] == 1){
return json_encode(['status' => 5,'msg' => 'The data has been generated, please proceed with the next steps']);
}
//请求OPENAI
$aResult = json_decode($this->createForOpenAi($aArticleContent,$aAiArticle),true);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? '生成失败' : $aResult['msg'];
if($iStatus == 1){
//四小时后推送上传素材并推送草稿箱
$iDelaySeconds = 4 * 3600; // 4小时的秒数
Queue::later($iDelaySeconds,'app\api\job\WechatMaterial@fire', ['article_id' => $iArticleId], 'WechatMaterial');
$sMsg = '文章AI内容生成成功';
}else{
$iStatus = 2;
}
//插入日志记录
$oMaterial = new Material;
$aLogInfo = ['article_id' => $iArticleId,'type' => 5,'msg' =>$sMsg,'status' => $iStatus,'create_time' => time()];
$result = json_decode($oMaterial->addWechatLog($aLogInfo),true);
return json_encode($aResult);
}
/**
* @title 请求OPENAI生成内容
* @param article_id 文章ID
* @param model 接口模型
* @param stream 是否流式输出 true是false否
*/
public function createForOpenAi($aArticleContent = [],$aAiArticle = []){
$iId = empty($aAiArticle['ai_article_id']) ? 0 : $aAiArticle['ai_article_id'];
$iArticleId = empty($aAiArticle['article_id']) ? 0 : $aAiArticle['article_id'];
//文章数据
$aArticle = empty($aArticleContent['article']) ? [] : $aArticleContent['article'];
//期刊数据
$aJournal = empty($aArticleContent['journal']) ? [] : $aArticleContent['journal'];
//子期刊数据
$aJournalStage = empty($aArticleContent['journal_stage']) ? [] : $aArticleContent['journal_stage'];
//通讯作者
$aAuthor = empty($aArticleContent['author']) ? [] : $aArticleContent['author'];
//查询文章内容
$aMain = empty($aArticleContent['main']) ? [] : $aArticleContent['main'];;
$sContent = empty($aMain) ? '' : implode(',', $aMain);
//请求OPENAI生成微信公众号文章内容
$aSearch = [];
$aSearch['{#content#}'] = $this->basic_html_filter($sContent);
$aSearch['{#abstract#}'] = $this->basic_html_filter($aArticle['abstract']);
$aSearch['{#title_chinese#}'] = $this->basic_html_filter($aArticle['title']);
$aSearch['{#journal_content#}'] = $this->basic_html_filter($aJournal['journal_content'] ?? '');
$aSearch['{#journal_content#}'] = trim(trim($aSearch['{#journal_content#}'],'.'),'。');
//写入Redis 标识已经开始请求OPENAI处理
$sArticleType = empty($aArticle['type']) ? 'default' : $aArticle['type'];
$aResult = $this->_createContentForOpenAI($aSearch,$sArticleType);
$aResult = json_decode($aResult,true);
if($aResult['status'] != 1){
return json_encode($aResult);
}
$aContent = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aContent)){
return json_encode(['status' => 4,'msg' => 'Data is empty']);
}
//组装数据
$aContent['article_id'] = $iArticleId;
$aContent['title_english'] = $aArticle['title'];
$aContent['journal_issn'] = empty($aJournal['issn']) ? '' : $aJournal['issn'];
//更新数据库
$aContent['ai_article_id'] = $iId;
$aContent['author'] = empty($aJournal['publish_author']) ? 'TMR编辑部' : $aJournal['publish_author'];
$aResult = $this->updateAiArticle($aContent);
return $aResult;
}
/**
* 处理文章引用
* @param string $html
* @return string
*/
private function _cite($aArticle = [],$aJournal = [],$aJournalStage = []){
$no = empty($aJournalStage['stage_no']) ? ':' : '(' . $aJournalStage['stage_no'] . '):';
$jabbr = empty($aJournal['jabbr']) ? '' : $aJournal['jabbr'];
$stage_year = empty($aJournalStage['stage_year']) ? '' : $aJournalStage['stage_year'];
$stage_vol = empty($aJournalStage['stage_vol']) ? '' : $aJournalStage['stage_vol'];
$sCite = '';
if ($aArticle['journal_id'] == 22) {
$sCite = $aArticle['abbr'] . '. ' . $aArticle['title'] . '[J]. ' . choiseJabbr($aArticle['article_id'],$jabbr) . ',' . $stage_year . ',' . $stage_vol . $no . $aArticle['npp'] . '. doi:' . $aArticle['doi'];
} else {
$sCite = $aArticle['abbr'] . '. ' . $aArticle['title'] . '. <i>' . choiseJabbr($aArticle['article_id'], $jabbr) . '</i>. ' . $stage_year . ';' . $stage_vol . $no . $aArticle['npp'] . '. doi:' . $aArticle['doi'];
}
return $sCite;
}
/**
* 接口请求获取Journal数据库文章信息
* @param article_id 文章ID
*/
public function getArticle($iArticleId = ''){
$aParam = $this->request->post();
$iArticleId = empty($iArticleId) ? $aParam['article_id'] : $iArticleId;
//接口请求地址
$sUrl = $this->sJournalUrl.'/wechat/Article/getArticle';
//请求接口
$aParam['article_id'] = $iArticleId;
$aResult = object_to_array(json_decode(myPost($sUrl, $aParam)));
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
if($iStatus != 1){
return json_encode(['status' => 0,'msg' => $aResult['msg'] ?? '操作异常']);
}
$aContent = empty($aResult['data']) ? [] : $aResult['data'];
if(!empty($aContent['journal'])){
$aJournal = $aContent['journal'];
//查询期刊主题
$aWhere['issn'] = $aJournal['issn'];
$aJournalInfo = Db::name('journal')->field('journal_topic')->where($aWhere)->find();
$aContent['journal']+=$aJournalInfo;
}
return json_encode(['status' => 1,'msg' => 'success','data' => $aContent]);
}
/**
* 获取Ai生成文章信息
* @param article_id 文章ID
* @param $iSelectAuthor 是否查询作者 1是2否
*/
public function getAiArticle($aParam = []){
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//文章ID
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select the article to be generated']);
}
//是否查询作者 1是2否
$iSelectAuthor = empty($aParam['is_select_author']) ? 2 : $aParam['is_select_author'];
//查询AI生成的文章内容
$aWhere = ['is_delete' => 2];
if(!empty($iArticleId)){
$aWhere['article_id'] = $iArticleId;
}
$aAiArticle = Db::name('ai_article')->where($aWhere)->find();
if(empty($aAiArticle)){
return json_encode(['status' => 1,'msg' => 'data is null','data' => ['ai_article' => []]]);
}
$iArticleId = empty($aAiArticle['article_id']) ? 0 : $aAiArticle['article_id'];
//查询文章通讯作者
$aAiAuthor = [];
if($iSelectAuthor == 2){
$aResult = json_decode($this->getArticle($iArticleId),true);
//获取数据
$aArticleContent = empty($aResult['data']) ? [] : $aResult['data'];
//通讯作者
$aAuthor = empty($aArticleContent['author']) ? [] : $aArticleContent['author'];
if(!empty($aAuthor)){
$aAiAuthor = $this->_dealAuthor($aAuthor);
if(!empty($aAiAuthor)){
foreach ($aAiAuthor as $key => $value) {
$aAiAuthor[$key]['icon_path'] = empty($value['icon']) ? $this->sDefaultUserIcon : $this->sUserIcon.$value['icon'];
}
}
}
}
return json_encode(['status' => 1,'msg' => 'success','data' => ['ai_article' => $aAiArticle,'ai_article_author' => $aAiAuthor]]);
}
/**
* @title 处理作者数据
* @param article_id 文章ID
* @param content 内容
*/
protected function _dealAuthor($aAuthor = array()){
//返回数组
if(empty($aAuthor)){
return [];
}
//根据邮箱查询作者信息
$aArticleId = array_unique(array_column($aAuthor, 'article_id'));
$iArticleId = $aArticleId[0];
$aEmail = array_unique(array_column($aAuthor, 'email'));
$aWhere = ['email' => ['in',$aEmail]];
$aUser = Db::name('user')->field('user_id,realname,email,localname,icon')->where($aWhere)->select();
if(empty($aUser)){
return [];
}
//查询作者详情
$aUserId = array_column($aUser, 'user_id');
$aWhere = ['reviewer_id' => ['in',$aUserId]];
$aUserInfo = DB::name('user_reviewer_info')->field('reviewer_id,technical,introduction,company')->where($aWhere)->select();
if(empty($aUserInfo)){
return [];
}
//数据处理
$aUserInfo = array_column($aUserInfo, null,'reviewer_id');
$aAuthor = array_column($aAuthor, null,'email');
$aInfo = [];
foreach ($aUser as $key => $value) {
//姓名
$sName = empty($aAuthor[$value['email']]['author_name']) ? '' : $aAuthor[$value['email']]['author_name'];
$sName = empty($sName) ? $value['localname'] : $sName;
$sName = empty($sName) ? $value['realname'] : $sName;
//单位
$sCompany = empty($aAuthor[$value['email']]['company']) ? '' : $aAuthor[$value['email']]['company'];
$sCompany1 = empty($aUserInfo[$value['user_id']]['company']) ? '' : $aUserInfo[$value['user_id']]['company'];
$sCompany = empty($sCompany) ? $sCompany1 : $sCompany;
//职称
$sTechnical = empty($aUserInfo[$value['user_id']]['technical']) ? '' : $aUserInfo[$value['user_id']]['technical'];
//简介
$sIntroduction = empty($aUserInfo[$value['user_id']]['introduction']) ? '' : $aUserInfo[$value['user_id']]['introduction'];
$aInfo[] = ['user_id' => $value['user_id'],'email' => $value['email'],'author_name' => $sName,'technical' => $sTechnical,'introduction' => $sIntroduction,'company' => $sCompany,'article_id' => $iArticleId,'create_time' => time(),'icon' => $value['icon']];
}
return $aInfo;
}
/**
* 基础HTML过滤
* @param string $html
* @return string
*/
private function basic_html_filter($html) {
// 移除所有HTML标签及PHP标签
$text = empty($html) ? '' : strip_tags($html);
// 转换HTML实体
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// 合并连续空白字符
$text = preg_replace('/\s+/u', ' ', $text);
return trim($text);
}
/**
* @title AI生成稿件内容入库
* @param article_id 文章ID
* @param content 内容
*/
public function updateAiArticle($aParam = array()){
//返回数组
$aResult = ['status' => 1,'msg' => 'AI article updated successfully','data' => []];
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//主键ID
$iAiArticleId = empty($aParam['ai_article_id']) ? 0 : $aParam['ai_article_id'];
//文章ID
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
//查询内容是否存在
$aWhere = ['is_delete' => 2];
if(empty($iArticleId) && empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select the article to be modified']);
}
if(!empty($iArticleId)){
$aWhere['article_id'] = $iArticleId;
}
if(!empty($iAiArticleId)){
$aWhere['ai_article_id'] = $iAiArticleId;
}
$aAiArticle = Db::name('ai_article')->where($aWhere)->find();
if(empty($aAiArticle)){
return json_encode(['status' => 3,'msg' => 'he article content of WeChat official account has not been generated']);
}
//必填参数验证
$aFields = $this->aAiFileds;
$sFiled = '';
$aUpdateParam = [];
foreach($aFields as $val){
if(!isset($aParam[$val])){
continue;
}
if(is_array($aParam[$val])){
$aParam[$val] = implode(";",$aParam[$val]);
}
$aUpdateParam[$val] = empty($aParam[$val]) ? '' : addslashes(strip_tags($aParam[$val]));
}
if(empty($aUpdateParam)){
return json_encode(['status' => 1,'msg' => 'No data currently being processed']);
}
Db::startTrans();
//执行入库
$aUpdateParam['update_time'] = time();
$aUpdateParam['is_generate'] = 1;
$result = Db::name('ai_article')->where('ai_article_id',$iAiArticleId)->limit(1)->update($aUpdateParam);
if($result === false){
$aResult = json_encode(['status' => 3,'msg' => 'UPDATEING AI article failed']);
}
$aResult['data'] = $aUpdateParam;
//更新作者信息
$aAuthorList = empty($aParam['author_list']) ? []: $aParam['author_list'];
$aAuthorList = is_array( $aAuthorList) ? $aAuthorList: json_decode($aAuthorList,true);
if(!empty($aAuthorList)){
$aAuthorList = array_column($aAuthorList, null,'email');
//根据邮箱查询作者信息
$aEmail = array_keys($aAuthorList);
$aUserInfo = $aUserReviewer = [];
if(!empty($aEmail)){
$aUserParam = ['email' => ['in',$aEmail]];
$aUserInfo = Db::name('user')->field('email,user_id')->where($aUserParam)->select();
if(!empty($aUserInfo)){
$aUserId = array_column($aUserInfo, 'user_id');
//查询用户附表
$aWhere = ['reviewer_id' => ['in',$aUserId]];
$aUserReviewer = Db::name('user_reviewer_info')->where($aWhere)->column('reviewer_id');
}
}
//更新用户信息
if(!empty($aUserInfo)){
foreach ($aUserInfo as $key => $value) {
$aUser = empty($aAuthorList[$value['email']]) ? [] : $aAuthorList[$value['email']];
if(empty($aUser)){
continue;
}
//更新作者名字
if(isset($aUser['author_name'])){
$aUpdate = ['localname' => $aUser['author_name']];
Db::name('user')->where('user_id',$value['user_id'])->update($aUpdate);
}
//更新作者简介和单位
$aUpdateReviewer = [];
if(isset($aUser['technical'])){
$aUpdateReviewer['technical'] = $aUser['technical'];
}
if(isset($aUser['introduction'])){
$aUpdateReviewer['introduction'] = $aUser['introduction'];
}
if(isset($aUser['company'])){
$aUpdateReviewer['company'] = $aUser['company'];
}
if(isset($aUpdateReviewer)){
if(in_array($value['user_id'], $aUserReviewer)){//更新
Db::name('user_reviewer_info')->where('reviewer_id',$value['user_id'])->limit(1)->update($aUpdateReviewer);
}
if(!in_array($value['user_id'], $aUserReviewer)){//插入
$aUpdateReviewer['reviewer_id'] = $value['user_id'];
Db::name('user_reviewer_info')->insert($aUpdateReviewer);
}
}
}
}
}
Db::commit();
$aResult['data'] = $aUpdateParam;
return json_encode($aResult);
}
/**
* 文章内容选择模版生成数据
* @param article_id 文章ID
* @param template_id 模版ID
* @param $iIsSync 是否同步素材到微信 1是2否
*/
public function getTemplateContent($aParam = [],$iIsSync = 2){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//文章ID
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
//模版ID
$iIsSync = empty($aParam['is_sync']) ? 2 : $aParam['is_sync'];
//必填参数验证
if(empty($iArticleId)){
return json_encode(['status' => 2, 'msg' => 'Please select an article']);
}
//获取AI生成文章内容
$aAiContent = json_decode($this->getAiArticle(['article_id' => $iArticleId,'is_select_author' => 1]),true);
$aAiContent = empty($aAiContent['data']) ? [] : $aAiContent['data'];
$aAiArticle = empty($aAiContent['ai_article']) ? [] : $aAiContent['ai_article'];
//判断是否生成AI内容
if(empty($aAiArticle)){
return json_encode(['status' => 3, 'msg' => 'The article content of WeChat official account has not been generated']);
}
//获取文章内容
$aResult = json_decode($this->getArticle($iArticleId),true);
//获取数据
$aArticleContent = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aArticleContent)){
return json_encode($aResult);
}
//文章数据
$aArticle = empty($aArticleContent['article']) ? [] : $aArticleContent['article'];
if(empty($aArticle)){
return json_encode(['status' => 4,'msg' => 'Article data is empty']);
}
//期刊数据
$aJournal = empty($aArticleContent['journal']) ? [] : $aArticleContent['journal'];
if(empty($aJournal)){
return json_encode(['status' => 4,'msg' => 'Journal data is empty']);
}
//子期刊数据
$aJournalStage = empty($aArticleContent['journal_stage']) ? [] : $aArticleContent['journal_stage'];
//通讯作者
$aAuthor = empty($aArticleContent['author']) ? [] : $aArticleContent['author'];
//文章图片
$sArticleIcon = trim($this->sJournalUsx,'/').$this->sArticleIcon.$aArticle['article_icon'];
//期刊图片
$sJournalStageIcon = empty($aJournalStage['stage_icon']) ? '' : '/public/'.$aJournalStage['stage_icon'];
$sJournalIcon = empty($aJournal['journal_icon']) ? '' : $this->sJournalIcon.$aJournal['journal_icon'];
$sJournalIcon = empty($sJournalStageIcon) ? $sJournalIcon : $sJournalStageIcon;
$sJournalIcon = trim($this->sJournalUsx,'/').$sJournalIcon;
//期刊编辑二维码
$sEditorQrcode = empty($aJournal['editor_qrcode']) ? '' : $aJournal['editor_qrcode'];
$sEditorQrcode = trim($this->sSubmissionUrl,'/').$this->sJournalEditorQrcode.$sEditorQrcode;
//获取期刊二维码
$oMaterial = new Material;
$aJournal['icon'] = $sJournalIcon;
$aJournal['journal_stage_id'] = empty($aArticle['journal_stage_id']) ? 0 : $aArticle['journal_stage_id'];
$aJournalQrCode = $oMaterial->createJournalQrCode($aJournal);
$sJournalCode = empty($aJournalQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aJournalQrCode['qrcode_url'];
$aAiArticle['journal_qrcode'] = $sJournalCode;
//文章二维码
$aArticleQrCode = $oMaterial->createArticleQrCode($aArticle);
$sArticleCode = empty($aArticleQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aArticleQrCode['qrcode_url'];
$aAiArticle['article_qrcode'] = $sArticleCode;
//文章cite
$aAiArticle['cite'] = $this->_cite($aArticle,$aJournal,$aJournalStage);
// //上传素材到微信
if($iIsSync == 1){
//查询参数组装
$aQueryParam = ['article_id' => $iArticleId,'journal_id' => $aArticle['journal_id']];
//组装作者数据
if(!empty($aAuthor)){
$aAuthor = $this->_dealAuthor($aAuthor);
$aQueryParam['author_info'] = $aAuthor;
}
//获取素材信息
$oMaterial = new Material;
$aReturnDataResult = json_decode($oMaterial->getMaterial($aQueryParam),true);
$aReturnData = empty($aReturnDataResult['data']) ? [] : $aReturnDataResult['data'];
//文章图片
$aDataInfo = empty($aReturnData['ai_article_material']) ? [] : $aReturnData['ai_article_material'];
$aAiArticle['article_icon'] = empty($aDataInfo['media_url']) ? $sArticleIcon : $aDataInfo['media_url'];
//文章二维码图片
$aDataInfo = empty($aReturnData['ai_article_qrcode']) ? [] : $aReturnData['ai_article_qrcode'];
$aAiArticle['article_qrcode'] = empty($aDataInfo['media_url']) ? $sArticleIcon : $aDataInfo['media_url'];
//期刊图片
$aDataInfo = empty($aReturnData['ai_journal_material']) ? [] : $aReturnData['ai_journal_material'];
$aAiArticle['journal_icon'] = empty($aDataInfo['media_url']) ? $sJournalIcon : $aDataInfo['media_url'];
//期刊二维码图片
$aDataInfo = empty($aReturnData['ai_journal_qrcode']) ? [] : $aReturnData['ai_journal_qrcode'];
$aAiArticle['journal_qrcode'] = empty($aDataInfo['media_url']) ? $sJournalIcon : $aDataInfo['media_url'];
//期刊编辑二维码
$aDataInfo = empty($aReturnData['ai_journal_editor_material']) ? [] : $aReturnData['ai_journal_editor_material'];
$aAiArticle['editor_qrcode'] = empty($aDataInfo['media_url']) ? $sEditorQrcode : $aDataInfo['media_url'];
//作者图片上传
$aDataInfo = empty($aReturnData['ai_author_material']) ? [] : $aReturnData['ai_author_material'];
if(!empty($aDataInfo) && $aAuthor){
$aDataInfo = array_column($aDataInfo, null,'email');
foreach ($aAuthor as $key => $value) {
$aAuthorData = empty($aDataInfo[$value['email']]) ? [] : $aDataInfo[$value['email']];
if(empty($aAuthorData)){
continue;
}
$aAuthor[$key]['icon'] = empty($aAuthorData['media_url']) ? $value['icon'] : $aAuthorData['media_url'];
}
}
}else{
//处理文章图片地址数据
$aAiArticle['article_icon'] = $sArticleIcon;
//处理文章期刊图片地址数据
$aAiArticle['journal_icon'] = $sJournalIcon;
//期刊编辑二维码图片地址数据
$aAiArticle['editor_qrcode'] = $sEditorQrcode;
//作者信息处理
if(!empty($aAuthor)){
$aAuthor = $this->_dealAuthor($aAuthor);
}
}
//期刊访问地址
$journal_usx = trim($this->sJournalUsx,'/').'/';
if(!empty($aJournal['journal_usx'])){
//地址
$journal_usx .= $aJournal['journal_usx']??''.'/';
}
$aAiArticle['journal_usx_url'] = trim($journal_usx);
//处理期刊内容
if(empty($aJournal['journal_content'])){
$aJournal['journal_content'] = empty($aJournal['journal_content']) ? $aJournal['journal_content_english'] : $aJournal['journal_content'];
}
$aAiArticle += $aJournal;
//获取模版信息
$sJournalIssn = empty($aJournal['issn']) ? 'default' : $aJournal['issn'];//期刊issn
$sArticleType = empty($aArticle['type']) ? 'default' : $aArticle['type'];//文章类型
if(in_array($sArticleType, ['review','Mini Review'])){
$sArticleType = "Review";
}
$sTemplatePath = ROOT_PATH."public/ArticleTemplate/".$sJournalIssn."/".$sArticleType;
if (!is_dir($sTemplatePath)) {//默认模版信息
$sTemplatePath = ROOT_PATH."public/ArticleTemplate/default/".$sArticleType;
}
//index模版目录
$sTemplateIndexPath = $sTemplatePath.'/index.html';
// 验证文件有效性
if (!file_exists($sTemplateIndexPath)) {
return json_encode(['status' => 4, 'msg' => 'Article template does not exist']);
}
if (!is_readable($sTemplateIndexPath)) {
return json_encode(['status' => 5, 'msg' => 'The article template is unreadable']);
}
//获取模版内容
$sTemplate = file_get_contents($sTemplateIndexPath);
//数据处理
$aSearch = [];
foreach ($aAiArticle as $key => $value) {
if(is_array($value)){
continue;
}
$aSearch['{###'.$key.'###}'] = empty($value) ? '' : htmlspecialchars(strip_tags($value));
}
//处理通讯作者信息数据
$aAuthorInfo = json_decode($this->dealTemplateAuthor($aAuthor,$sTemplatePath),true);
$aAuthorInfo = empty($aAuthorInfo['data']) ? [] : $aAuthorInfo['data'];
$aSearch['{###author_summary###}'] = empty($aAuthorInfo['author_info']) ? '' : $aAuthorInfo['author_info'];
$aSearch['{###author_string###}'] = empty($aAuthorInfo['author_string']) ? '' : ',通讯作者为'.$aAuthorInfo['author_string'];
//处理往期推荐数据
$aPreviousRecommend = json_decode($this->dealTemplatePreviousRecommend($aArticle,$sTemplatePath),true);
$aSearch['{###previous_recommend_summary###}'] = empty($aPreviousRecommend['data']) ? '' : $aPreviousRecommend['data'];
//处理期刊主题数据
$sJournalTopic = empty($aJournal['journal_topic']) ? '' : $aJournal['journal_topic'];
$aTopic = json_decode($this->dealTemplateTopic($sJournalTopic,$sTemplatePath),true);
$aSearch['{###topic_name_summary###}'] = empty($aTopic['data']) ? '' : $aTopic['data'];
//公众号名字处理
$aSearch['{###wechat_name###}'] = empty($aSearch['{###wechat_name###}']) ? '“'.$aSearch['{###journal_title###}'].'“' : '“'.$aSearch['{###wechat_name###}'].'“';
//是否显示文章来源
//查询app_id和app_secret
$sIssn = empty($aJournal['issn']) ? '' : $aJournal['issn'];
$aTougaoJournal = Db::name('journal')->field('wechat_app_id,wechat_app_secret')->where('issn',$sIssn)->find();
if(!empty($aJournal)){
$wechat_app_id = empty($aTougaoJournal['wechat_app_id']) ? '' : $aTougaoJournal['wechat_app_id'];
$aJournal['wechat_app_id'] = empty($aJournal['wechat_app_id']) ? $wechat_app_id : $aJournal['wechat_app_id'];
$wechat_app_secret = empty($aTougaoJournal['wechat_app_secret']) ? '' : $aTougaoJournal['wechat_app_secret'];
$aJournal['wechat_app_secret'] = empty($aJournal['wechat_app_secret']) ? $wechat_app_secret : $aJournal['wechat_app_secret'];
}
$aSearch['{###article_from###}'] = '';
if(empty($aJournal['wechat_app_id']) || empty($aJournal['wechat_app_secret'])){//获取默认推送公众号
$sTemplateFromPath = $sTemplatePath.'/article_from.html';
//获取默认推送公众号配置
$aWechatConfig = $this->aWechatConfig;
if (file_exists($sTemplateFromPath) && is_readable($sTemplateFromPath)) {
$sTemplateFromPath = file_get_contents($sTemplateFromPath);
$aSearch['{###article_from###}'] = str_replace('{###journal_title###}', $aJournal['wechat_name']??$aWechatConfig['wechat_name'], $sTemplateFromPath);
}
$aSearch['{###wechat_name###}'] = '“'.$aWechatConfig['wechat_name'].'“';
}
//模版替换变量
if(empty($aSearch)){
return json_encode(['status' => 6, 'msg' => '模版内容不能为空']);
}
$aSearch['{###jabbr###}'] = trim(trim($aSearch['{###jabbr###}'],'.'),'。');
// $aSearch['{###free_year###}'] = date('Y');
// $aSearch['{###covered###}'] = '内容涵盖'.str_replace('研究涵盖', '', $aSearch['{###covered###}']);
if(!empty($aSearch['{###covered###}']) && strpos($aSearch['{###covered###}'], '涵盖') === false){
$aSearch['{###covered###}'] = '内容涵盖'.str_replace('研究涵盖', '', $aSearch['{###covered###}']);
}
//期刊封面
$aSearch['{###journal_icon_info###}'] = $aSearch['{###journal_icon_details###}'] = '';
if(!empty($sJournalIcon)){
$sTemplateJournalInfo = $sTemplatePath.'/journal_icon_info.html';
if(file_exists($sTemplateJournalInfo) && is_readable($sTemplateJournalInfo)) {
$sTemplateJournalInfo = file_get_contents($sTemplateJournalInfo);
$aSearch['{###journal_icon_info###}'] = str_replace('{###journal_icon###}', $aAiArticle['journal_icon']??'', $sTemplateJournalInfo);
}
$sTemplateJournalInfo = $sTemplatePath.'/journal_icon_details.html';
if(file_exists($sTemplateJournalInfo) && is_readable($sTemplateJournalInfo)) {
$sTemplateJournalInfo = file_get_contents($sTemplateJournalInfo);
$aSearch['{###journal_icon_details###}'] = str_replace('{###journal_icon###}', $aAiArticle['journal_icon']??'', $sTemplateJournalInfo);
}
}
$sTemplate = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplate);
// $this->writeFile($sTemplate,ROOT_PATH.'public/template_info.html');
return json_encode(['status' => 1, 'msg' => '模版生成成功','data' => ['template' => $sTemplate]]);
}
/**
* 处理模版通讯作者
* @param article_id 文章ID
*/
private function dealTemplateAuthor($aAuthor = [],$sTemplatePath = ''){
//必填字段判断
if(empty($aAuthor) || empty($sTemplatePath)){
return json_encode(['status' => 2,'msg' => 'Corresponding author or template does not exist']);
}
//获取通讯作者模版
$sAuthorTemplatePathAuthor = $sTemplatePath.'/author.html';
if (!file_exists($sAuthorTemplatePathAuthor)) {
return json_encode(['status' => 3, 'msg' => 'Corresponding author template does not exist','data' => '']);
}
if (!is_readable($sAuthorTemplatePathAuthor)) {
return json_encode(['status' => 4, 'msg' => 'The corresponding author template is unreadable','data' => '']);
}
$sAuthorTemplate = file_get_contents($sAuthorTemplatePathAuthor);
//处理模版变量
$aAuthorSerachInfo = [];
$sAuthorInfo = '';
$aLogo = $this->aLogo;
//请求AI翻译
$sContent = '';
$aSearch['{#content#}'] = json_encode($aAuthor);
$aResult = json_decode($this->_createContentForOpenAI($aSearch,1,1),true);
$aContent = empty($aResult['data']) ? [] : array_column($aResult['data'], null,'email');
foreach ($aAuthor as $key => $value) {
//请求AI翻译
$aInfo = empty($aContent[$value['email']]) ? [] : $aContent[$value['email']];
//替换模版
$aAuthorSerach = [];
//所属单位处理
$value['company'] = empty($aInfo['company']) ? $value['company'] : $aInfo['company'];
if(!empty($value['company'])){
$value['company'] =str_replace('', ',', $value['company']);
$aCompany = explode(',', $value['company']);
}
$value['company'] = empty($aCompany[0]) ? $value['company'] : trim($aCompany[0],',');
//简介处理
$value['introduction'] = empty($aInfo['introduction']) ? $value['introduction'] : $aInfo['introduction'];
//职称处理
$value['technical'] = empty($aInfo['technical']) ? $value['technical'] : $aInfo['technical'];
if(empty($value['introduction'])){//简介为空不处理
$sTechnical = empty($value['technical']) ? '老师' : $value['technical'];
$sAuthorInfo .= $value['company'].$value['author_name'].$sTechnical.',';
continue;
}
//缓缓处理进行模版替换
foreach ($value as $k => $val) {
if($k == 'icon'){//头像拼接处理
$sIconInfo = empty($val) ? $aLogo['url'] : $val;
$aParsedUrl = parse_url($sIconInfo);
if(empty($aParsedUrl['scheme'])){
$aAuthorSerach['{###icon_path###}'] = trim($this->sSubmissionUrl,'/').$this->sUserIcon.$val;
}else{
$aAuthorSerach['{###icon_path###}'] = $val;
}
$aAuthorSerach['{###icon_path###}'] = trim($aAuthorSerach['{###icon_path###}'],'/');
}else{
$aAuthorSerach['{###'.$k.'###}'] = trim($val,'/');
}
}
$aAuthorSerachInfo[] = str_replace(array_keys($aAuthorSerach), array_values($aAuthorSerach), $sAuthorTemplate);
}
//通讯作者列表
//获取通讯作者汇总模版
$sAuthorTemplatePath = $sTemplatePath.'/author_summary.html';
if (!file_exists($sAuthorTemplatePath)) {
return json_encode(['status' => 3, 'msg' => 'Corresponding author template does not exist','data' => '']);
}
if (!is_readable($sAuthorTemplatePath)) {
return json_encode(['status' => 4, 'msg' => 'The corresponding author template is unreadable','data' => '']);
}
$sAuthorTemplate = file_get_contents($sAuthorTemplatePath);
//数据处理内容替换
$sInfo = empty($aAuthorSerachInfo) ? '' : implode("\n", $aAuthorSerachInfo);
$sAuthorTemplate = empty($sInfo) ? '' : str_replace('{###author_info###}', $sInfo, $sAuthorTemplate);
//通讯作者字符串处理
$sAuthorInfo = trim($sAuthorInfo,',');
return json_encode(['status' => 1,'msg' => 'success','data' => ['author_info' => $sAuthorTemplate,'author_string' => trim($sAuthorInfo,';')]]);
}
/**
* 处理期刊主题模版
* @param article_id 文章ID
*/
private function dealTemplateTopic($sTopic = '',$sTemplatePath = ''){
if(empty($sTemplatePath)){
return json_encode(['status' => 2,'template does not exist']);
}
if(empty($sTopic)){
return json_encode(['status' => 2,'topic does not exist']);
}
//获取主题模版
$sTemplatePathTopic = $sTemplatePath.'/topic.html';
if (!file_exists($sTemplatePathTopic)) {
return json_encode(['status' => 4, 'msg' => 'Corresponding author template does not exist']);
}
if (!is_readable($sTemplatePathTopic)) {
return json_encode(['status' => 5, 'msg' => 'The corresponding author template is unreadable']);
}
$sTemplate = file_get_contents($sTemplatePathTopic);
//数据处理
$sTopic = trim($sTopic);
$aTopic = empty($sTopic) ? [] : explode(',', $sTopic);
$aSearchInfo = [];
if(!empty($aTopic)){
foreach ($aTopic as $key => $value) {
$aSerach['{###topic_name###}'] = empty($value) ? '' : strip_tags($value);
$aSearchInfo[] = str_replace(array_keys($aSerach), array_values($aSerach), $sTemplate);
}
}
//获取期刊主题汇总模版
$sTemplatePath = $sTemplatePath.'/topic_summary.html';
if (!file_exists($sTemplatePath)) {
return json_encode(['status' => 4, 'msg' => 'Corresponding author template does not exist']);
}
if (!is_readable($sTemplatePath)) {
return json_encode(['status' => 5, 'msg' => 'The corresponding author template is unreadable']);
}
$sTemplate = file_get_contents($sTemplatePath);
//模版数据替换
$sTopicName = empty($aSearchInfo) ? '' : implode("\n", $aSearchInfo);
$sTemplate = empty($sTopicName) ? '' : str_replace('{###topic_name###}', $sTopicName, $sTemplate);
return json_encode(['status' => 1,'msg' => 'success','data' => $sTemplate]);
}
/**
* 处理往期推荐
* @param article_id 文章ID
*/
private function dealTemplatePreviousRecommend($aArticle=[],$sTemplatePath = ''){
if(empty($sTemplatePath)){
return json_encode(['status' => 2,'template does not exist']);
}
//获取往期推荐模版
$sTemplatePathRecommend = $sTemplatePath.'/previous_recommend.html';
if (!file_exists($sTemplatePathRecommend)) {
return json_encode(['status' => 3, 'msg' => 'Corresponding author template does not exist']);
}
if (!is_readable($sTemplatePathRecommend)) {
return json_encode(['status' => 4, 'msg' => 'The corresponding author template is unreadable']);
}
$sTemplate = file_get_contents($sTemplatePathRecommend);
//判断是否关联文章
if(empty($aArticle['related'])){
return json_encode(['status' => 6, 'msg' => 'Unrelated articles']);
}
//获取关联文章信息
$aWhere = ['article_id' => ['in',json_decode($aArticle['related'],true)],'is_delete' => 2];
$aRecommend = Db::name('ai_article')->field('article_id')->where($aWhere)->select();
$aSearchInfo = [];
if(!empty($aRecommend)){
$aId = array_column($aRecommend, 'article_id');
//查询是否推送到微信公众号并且已发布
$aWhere['is_publish'] = 1;
$aWechatArticle = Db::name('ai_wechat_article')->field('article_id,media_url')->where($aWhere)->select();
if(empty($aWechatArticle)){
return json_encode(['status' => 6, 'msg' => 'No article published on WeChat official account was found']);
}
//查询文章图片
$aWhere = ['article_id' => ['in',$aId],'is_delete' => 2];
$aArticleMaterial = Db::name('ai_article_material')->where($aWhere)->column('article_id,media_url');
foreach ($aWechatArticle as $key => $value) {
$aSearch['{###wechat_url###}'] = $value['media_url'];
$sIcon = empty($aArticleMaterial[$value['article_id']]) ? '' : $aArticleMaterial[$value['article_id']];
$aSearch['{###wechat_media_url###}'] = $sIcon;
$aSearchInfo[] = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplate);
}
}
//获取往期推荐汇总模版
$sTemplatePath = $sTemplatePath.'/previous_recommend_summary.html';
if (!file_exists($sTemplatePath)) {
return json_encode(['status' => 3, 'msg' => 'Corresponding author template does not exist']);
}
if (!is_readable($sTemplatePath)) {
return json_encode(['status' => 4, 'msg' => 'The corresponding author template is unreadable']);
}
$sTemplate = file_get_contents($sTemplatePath);
//数据处理内容替换
$sInfo = empty($aSearchInfo) ? '' : implode("\n", $aSearchInfo);
$sTemplate = empty($sInfo) ? '' : str_replace('{###previous_recommend###}', $sInfo, $sTemplate);
return json_encode(['status' => 1,'msg' => 'success','data' => $sTemplate]);
}
/**
* 文章同步到微信公众号草稿箱
* @param article_id 文章ID
*/
public function syncWechat($aParam = []){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//文章ID
$iArticleId = empty($aParam['article_id'] ) ? '' : $aParam['article_id'];
//必填参数验证
if(empty($iArticleId)){
return json_encode(['status' => 2, 'msg' => 'Please select an article']);
}
//判断文章类型
$article_type = empty($aParam['article_type']) ? 'news' : $aParam['article_type'];
if(!in_array($article_type, ['news','newspic'])){
return json_encode(['status' => 6,'msg' => "The article type does not match"]);
}
//查询文章是否存在
$aWhere = ['article_id' => $iArticleId,'is_delete' => 2];
$aAiArticle = Db::name('ai_article')->field('ai_article_id,title_chinese as title,journal_issn,digest,article_type')->where($aWhere)->find();
if(empty($aAiArticle)){
return json_encode(['status' => 3, 'msg' => 'The article does not exist']);
}
$sWechatId = empty($aAiArticle['journal_issn']) ? '' : $aAiArticle['journal_issn'];
if(empty($sWechatId)){
return json_encode(['status' => 2, 'msg' => 'Please select the WeChat official account to push']);
}
//获取模版
$sTemplate = empty($aAiArticle['article_type']) ? 'default' : $aAiArticle['article_type'];//文章类型
if(in_array($sTemplate, ['review','Mini Review'])){
$sTemplate = "Review";
}
//查询期刊微信公众号配置
$aJournalInfo = Db::name('journal')->field('wechat_app_id,wechat_app_secret')->where('issn',$sWechatId)->find();
if(empty($aJournalInfo['wechat_app_id']) || empty($aJournalInfo['wechat_app_secret'])){
//获取默认配置
$aJournalInfo = $this->aWechatConfig;
$sWechatId = $aJournalInfo['issn'] ?? 'default';
}
//查询该模版是否推送到微信公众号
$aWhere['wechat_id'] = $sWechatId;
$aWhere['template_id'] = $sTemplate;
$aWhere['wechat_id'] = $aAiArticle['journal_issn'];
$aWechatArticle = Db::name('ai_wechat_article')->where($aWhere)->find();
if(!empty($aWechatArticle)){
return json_encode(['status' => 4, 'msg' => 'Already uploaded to draft box']);
}
//数据处理-标题
$sTitle = empty($aAiArticle['title']) ? '' : $aAiArticle['title'];
$sTitle = mb_convert_encoding($sTitle, 'UTF-8', 'auto');
$aAiArticle['title'] = $this->truncateByBytes($sTitle);
//数据处理-摘要
$sDigest = empty($aAiArticle['digest']) ? '' : $aAiArticle['digest'];
$sDigest = mb_convert_encoding($sDigest, 'UTF-8', 'auto');
$aAiArticle['digest'] = $this->truncateByBytes($sDigest, 120);
//是否打开评论0不打开(默认)1打开
$aParam['need_open_comment'] = empty($aParam['need_open_comment']) ? 1 : $aParam['need_open_comment'];
//是否粉丝才可评论0所有人可评论(默认)1粉丝才可评论
$aParam['only_fans_can_comment'] = empty($aParam['only_fans_can_comment']) ? 1 : $aParam['only_fans_can_comment'];
//查询文章封面
$aMaterial = Db::name('ai_article_material')->field('media_id as thumb_media_id')->where('article_id',$iArticleId)->find();
if(!empty($aMaterial)){
$aAiArticle += $aMaterial;
}
$aParam += $aAiArticle;
//获取模版生成内容
$aTemplateParam = ['article_id' => $iArticleId,'template_id' => $sTemplate,'is_sync' => 1,'wechat_id' => $sWechatId];
$aTemplateParam += $aJournalInfo;
$aResult = json_decode($this->getTemplateContent($aTemplateParam,1),true);
$sMsg = empty($aResult['msg']) ? 'The content is empty' : $aResult['msg'];
$aData = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aData['template'])){
return json_encode(['status' => 4, 'msg' => $sMsg]);
}
//请求微信公众号接口推送
$aParam['content'] = $aData['template'];
$aParam += $aJournalInfo;
$oWechat = new Wechat;
$aResult = json_decode($oWechat->addDraft($aParam),true);
$aData = empty($aResult['data']) ? [] : $aResult['data'];
//插入操作日志
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? '' : $aResult['msg'];
if ($iStatus != 1) {
$iStatus = 2;
$sMsg = empty($sMsg) ? '草稿箱推送失败' : $sMsg;
}
//插入日志记录
$oMaterial = new Material;
$aLogInfo = ['article_id' => $iArticleId,'type' => 2,'msg' =>$sMsg,'status' => $iStatus,'create_time' => time()];
$result = json_decode($oMaterial->addWechatLog($aLogInfo),true);
if(empty($aData)){
return json_encode($aResult);
}
//插入记录表
$aParam = ['media_id' => $aData['media_id'],'update_time' => time(),'template_id' => $sTemplate,'template_content' => $aParam['content'],'article_id' => $iArticleId,'article_type' => $article_type,'need_open_comment' => $aParam['need_open_comment'],'only_fans_can_comment' => $aParam['only_fans_can_comment'],'create_time' => time(),'wechat_id' => $sWechatId,'ai_article_id' => $aAiArticle['ai_article_id']];
$result = Db::name('ai_wechat_article')->insert($aParam);
if($result === false){
return json_encode(['status' => 5,'msg' => "Article data insertion failed"]);
}
//写入发布队列
// Queue::later(60,'app\api\job\WechatPublishDraft@fire', ['article_id' => $iArticleId], 'WechatDraftPublish');
return json_encode(['status' => 1,'msg' => 'Upload draft box successfully','data' => []]);
}
/**
* 字符串处理
* @param article_id 文章ID
*/
private function truncateByBytes($str, $maxLength = 64) {
$totalChars = mb_strlen($str, 'UTF-8');
if($totalChars >= $maxLength){
return mb_substr($str,0,$maxLength);
}
return $str;
}
/**
* 获取Ai生成文章状态
* @param article_id 文章ID
*/
public function getAiArticleStatus(){
$aParam = $this->request->post();
//AI生成文章ID
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select the article to be generated']);
}
//返回数据定义
$aResult = ['generate_status' => 3,'draft_status' => 2,'publish_status' => -1];
//查询AI生成的文章内容
$aWhere = ['is_delete' => 2];
if(!empty($iArticleId)){
$aWhere['article_id'] = $iArticleId;
}
$aAiArticle = Db::name('ai_article')->field('is_generate')->where($aWhere)->find();
// 生成状态
$aMsg = [1 => 'The data has been generated, please proceed with the next steps',2 => 'AI content generation in progress',3 => 'AI content not generated',4 => 'The article has been pushed to the draft box',6 => 'The article has been pushed to the draft box'];
$iStatus = empty($aAiArticle['is_generate']) ? 3 : $aAiArticle['is_generate'];
$sMsg = empty($aMsg[$iStatus]) ? 'illegal request' : $aMsg[$iStatus];
$aResult['generate_status'] = $iStatus;
if(!empty($aAiArticle)){
$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'];
//获取Redis内容判断是否上传素材成功
$oMaterial = new Material;
$sUploadInfo = $oMaterial->getStepForRedis($iArticleId);
$sErrorInfo = '';
if(!empty($sUploadInfo) && $sUploadInfo != 'finish'){
$iStatus = 3;//素材上传中
$sErrorInfo = $oMaterial->getErrorForRedis($iArticleId);
if(!empty($sErrorInfo)){
$iStatus = 5;
return json_encode(['status' => 5,'msg' => $sErrorInfo]);
}
}
if(!empty($sUploadInfo) && $sUploadInfo == 'finish'){
$iStatus = 4;//素材上传完成
}
//判断是否推送到草稿箱
if(empty($sUploadInfo) || $iStatus == 4){
if(!empty($aParam['template_id'])){
$aWhere['template_id'] = $aParam['template_id'];
}
$aDraft = Db::name('ai_wechat_article')->field('is_publish,publish_status,article_id,template_id,wechat_id')->where($aWhere)->find();
$iStatus = empty($aDraft) ? 2 : 1;
}
$aResult['draft_status'] = $iStatus;
if(!empty($aDraft)){
$aMsg = [0 => 'Article successfully published', 1 => 'Article is being published',2 => 'Original article creation failed', 3 => 'Article conventional failure', 4 => 'WeChat official account platform review failed', 5 => 'After success, the user deletes all articles', 6 => 'After success, the system will ban all articles','-1' => 'Draft box article not published'];
//查询微信接口获取发布状态
if($aDraft['publish_status'] == 1){
$aReturnData = json_decode($this->queryStatus($aDraft),true);
$aData = empty($aReturnData['data']['data']) ? [] : $aReturnData['data']['data'];
}
$iStatus = empty($aData['publish_status']) ? $aDraft['publish_status'] : $aData['publish_status'];
$aResult['publish_status'] = $iStatus;
}
}
$sMsg = empty($aMsg[$iStatus]) ? 'illegal request' : $aMsg[$iStatus];
return json_encode(['status' => 1,'msg' => $sMsg,'data' => $aResult]);
}
/**
* 获取公众号信息
*/
public function getWechatLists(){
$aWhere = ['state' => 0,'wechat_app_id' => ['<>',''],'wechat_app_secret' => ['<>','']];
$aJournal = Db::name('journal')->field('issn,wechat_name')->where($aWhere)->select();
return json_encode(['status' => 1,'msg' => 'success','data' => $aJournal]);
}
/**
* 公众号文章发布
*/
public function publishDraft($aParam = []){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//文章ID
$iArticleId = empty($aParam['article_id'] ) ? '' : $aParam['article_id'];
//必填参数验证
if(empty($iArticleId)){
return json_encode(['status' => 2, 'msg' => 'Please select an article']);
}
//查询文章是否存在
$aWhere = ['article_id' => $iArticleId,'is_delete' => 2];
$aAiArticle = Db::name('ai_article')->field('article_type')->where($aWhere)->find();
if(empty($aAiArticle)){
return json_encode(['status' => 3, 'msg' => 'The article does not exist']);
}
//查询是否上传到草稿箱
$aWhere = ['article_id' => $iArticleId,'template_id' => $aAiArticle['article_type']];
$aWechatArticle = Db::name('ai_wechat_article')->field('id,is_publish,media_id,publish_status,wechat_id')->where($aWhere)->find();
$sMediaId = empty($aWechatArticle['media_id']) ? '' : $aWechatArticle['media_id'];
if(empty($sMediaId)){
return json_encode(['status' => 3, 'msg' => 'The article was not found in the draft box of WeChat official account']);
}
$iId = empty($aWechatArticle['id']) ? 0 : $aWechatArticle['id'];
//推送公众号Id
$sWechatId = empty($aWechatArticle['wechat_id']) ? '' : $aWechatArticle['wechat_id'];
//判断是否发布
if($aWechatArticle['publish_status'] != '-1'){
return json_encode(['status' => 5, 'msg' => 'The article has been published, please confirm']);
}
//查询期刊微信公众号配置
$aJournalInfo = Db::name('journal')->field('wechat_app_id,wechat_app_secret')->where('issn',$sWechatId)->find();
if(empty($aJournalInfo['wechat_app_id']) || empty($aJournalInfo['wechat_app_secret'])){
//获取默认配置
$aJournalInfo = $this->aWechatConfig;
}
$aJournalInfo['media_id'] = $sMediaId;
//调用发布接口
$oWechat = new Wechat;
$aResult = json_decode($oWechat->publishDraft($aJournalInfo),true);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? '' : $aResult['msg'];
if ($iStatus != 1) {
$iStatus = 2;
$sMsg = empty($sMsg) ? '发布失败' : $sMsg;
}
//插入日志记录
$oMaterial = new Material;
$aLogInfo = ['article_id' => $iArticleId,'type' => 3,'msg' =>$sMsg,'status' => $iStatus,'create_time' => time()];
$result = json_decode($oMaterial->addWechatLog($aLogInfo),true);
if($iStatus != 1){
return json_encode($aResult);
}
//更新数据表
//更新文章表publish_id
$aData = empty($aResult['data']) ? [] : $aResult['data'];
$aUpdate = ['update_time' => time(),'is_publish' => 1,'publish_id' => $aData['publish_id'],'publish_status' => 1];
$result = Db::name('ai_wechat_article')->where('id',$iId)->limit(1)->update($aUpdate);
if($result === false){
return json_encode(['status' => 5,'msg' => "Failed to update the status of the published article"]);
}
//发布成功查询状态
$iDelaySeconds = 30;//4 * 3600; // 30秒数
Queue::later($iDelaySeconds,'app\api\job\WechatQueryStatus@fire', ['article_id' => $iArticleId], 'WechatQueryStatus');
return json_encode(['status' => 1,'msg' => 'Draft box article successfully published','data' => []]);
}
/**
* CURL 查询文章的发布状态
* @param $sToken Token
* @param article_id array 文章ID
*/
public function queryStatus($aParam = []){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//查询条件
$aWhere = ['is_publish' => 1,'publish_id' => ['<>',''],'publish_status' => 1,'is_delete' => 2];
//文章ID
if(!empty($aParam['article_id'])){
$aWhere['article_id'] = $aParam['article_id'];
}
//模版ID
if(!empty($aParam['template_id'])){
$aWhere['template_id'] = $aParam['template_id'];
}
//推送公众号Id
if(!empty($aParam['wechat_id'])){
$aWhere['wechat_id'] = $aParam['wechat_id'];
}
//查询待发布文章信息
$aWechatArticle = Db::name('ai_wechat_article')->where($aWhere)->field('id,article_id,template_id,wechat_id,publish_id')->select();
if(empty($aWechatArticle)){
return json_encode(['status' => 2,'msg' => 'No articles requiring synchronization status were found'],true);
}
//查询期刊微信公众号配置
$aIssn = array_unique(array_column($aWechatArticle, 'wechat_id'));
if(empty($aIssn)){
return json_encode(['status' => 3,'msg' => 'Official account information is empty']);
}
$aWhere = ['issn' => ['in',$aIssn]];
$aJournalInfo = Db::name('journal')->field('issn,wechat_app_id,wechat_app_secret')->where($aWhere)->select();
if(!empty($aJournalInfo)){
$aJournalInfo = array_column($aJournalInfo, null,'issn');
}
//循环处理
$oWechat = new Wechat;
$aInsert = [];
Db::startTrans();
foreach ($aWechatArticle as $key => $value) {
if(empty($value['wechat_id']) || empty($value['publish_id'])){
continue;
}
//账号信息
$aAccount = empty($aJournalInfo[$value['wechat_id']]) ? [] : $aJournalInfo[$value['wechat_id']];
if(empty($aAccount['wechat_app_id']) || empty($aAccount['wechat_app_secret'])){
$aAccount = $this->aWechatConfig;
}
$aAccount['publish_id'] = $value['publish_id'];
$aResult = json_decode($oWechat->queryStatus($aAccount),true);
$aData = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aData)){
continue;
}
//日志记录
$aData = $this->dealStatusData($aData);
$aLog = empty($aData['log']) ? [] : $aData['log'];
if(!empty($aLog)){
$aInsert[] = $aData['log'];
}
//更新状态
$aDetail = empty($aData['article_detail']) ? [] : $aData['article_detail'];
$iStatus = isset($aLog['publish_status']) ? $aLog['publish_status'] : $value['publish_status'];
//微信文章链接
$sUrl = empty($aDetail[0]['article_url']) ? '' : $aDetail[0]['article_url'];
$updateResult = Db::name('ai_wechat_article')->where('id',$value['id'])->limit(1)->update(['publish_status' => $iStatus,'wechat_article_url' => $sUrl,'update_time' => time()]);
}
//插入日志表
if(!empty($aInsert)){
$insertResult = Db::name('wechat_article_publish_log')->insertAll($aInsert);
}
Db::commit();
return json_encode(['status' => 1,'msg' => 'success']);
}
/**
* 发布日志数据处理
* @param $sToken Token
* @param article_id array 文章ID
*/
private function dealStatusData($aParam = []){
//log日志需要参数
$aField = ['publish_id' => '','publish_status' => '' ,'article_id' => '' ,'article_detail' => '' ,'fail_idx' => ''];
$aDetail = [];
foreach ($aParam as $key => $value) {
if($key == 'article_detail'){
$aDetail = empty($value) ? [] : $value;
$aDetail = empty($aDetail['item']) ? [] : $aDetail['item'];
}
$aField[$key] = empty($value) ? '' : $value;
$aField[$key] = is_array($aField[$key]) ? json_encode($aField[$key]) : $aField[$key];
}
$aField['create_time'] = time();
return ['article_detail' => $aDetail,'log' => $aField];
}
/**
* 上传素材
* @param $sToken Token
* @param article_id array 文章ID
*/
public function uploadMaterial($aParam = []){
//获取参数
$aParam = empty($aParam) ? $this->request->post() : $aParam;
//文章ID
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
//必填参数验证
if(empty($iArticleId)){
return json_encode(['status' => 2, 'msg' => 'Please select an article']);
}
//判断素材是否上传
$oMaterial = new Material;
$aLog = json_decode($oMaterial->getWechatLog(['article_id' => $iArticleId,'type' => 1,'status' =>1]),true);
if(!empty($aLog['data'])){
$aLog = json_decode($oMaterial->getWechatLog(['article_id' => $iArticleId,'type' => 2,'status' =>1]),true);
if(empty($aLog['data'])){ //推送到草稿箱
Queue::push('app\api\job\WechatDraft@fire', ['article_id' => $iArticleId], 'WechatDraft');
return json_encode(['status' => 1,'msg' => 'The article material has been pushed into the draft box queue','data' => []]);
}
return json_encode(['status' => 1,'msg' => 'The material has been uploaded','data' => []]);
}
//获取AI生成文章内容
$aAiContent = json_decode($this->getAiArticle(['article_id' => $iArticleId,'is_select_author' => 1]),true);
$aAiContent = empty($aAiContent['data']) ? [] : $aAiContent['data'];
$aAiArticle = empty($aAiContent['ai_article']) ? [] : $aAiContent['ai_article'];
//判断是否生成AI内容
if(empty($aAiArticle)){
return json_encode(['status' => 3, 'msg' => 'The article content of WeChat official account has not been generated']);
}
//获取文章内容
$aResult = json_decode($this->getArticle($iArticleId),true);
//获取数据
$aArticleContent = empty($aResult['data']) ? [] : $aResult['data'];
if(empty($aArticleContent)){
return json_encode($aResult);
}
//文章数据
$aArticle = empty($aArticleContent['article']) ? [] : $aArticleContent['article'];
if(empty($aArticle)){
return json_encode(['status' => 4,'msg' => 'Article data is empty']);
}
//期刊数据
$aJournal = empty($aArticleContent['journal']) ? [] : $aArticleContent['journal'];
if(empty($aJournal)){
return json_encode(['status' => 4,'msg' => 'Journal data is empty']);
}
//通讯作者
$aAuthor = empty($aArticleContent['author']) ? [] : $aArticleContent['author'];
//文章图片
$sArticleIcon = trim($this->sJournalUsx,'/').$this->sArticleIcon.$aArticle['article_icon'];
//子期刊数据
$aJournalStage = empty($aArticleContent['journal_stage']) ? [] : $aArticleContent['journal_stage'];
//期刊图片
$sJournalStageIcon = empty($aJournalStage['stage_icon']) ? '' : '/public/'.$aJournalStage['stage_icon'];
$sJournalIcon = empty($aJournal['journal_icon']) ? '' : $this->sJournalIcon.$aJournal['journal_icon'];
$sJournalIcon = empty($sJournalStageIcon) ? $sJournalIcon : $sJournalStageIcon;
$sJournalIcon = trim($this->sJournalUsx,'/').$sJournalIcon;
//期刊编辑二维码
$editor_qrcode = empty($aJournal['editor_qrcode']) ? '' : $aJournal['editor_qrcode'];
$sEditorQrcode = trim($this->sSubmissionUrl,'/').$this->sJournalEditorQrcode.$editor_qrcode;
//获取期刊二维码
$oMaterial = new Material;
$aJournal['journal_stage_id'] = empty($aArticle['journal_stage_id']) ? 0 : $aArticle['journal_stage_id'];
$aJournalQrCode = $oMaterial->createJournalQrCode($aJournal);
$sJournalCode = empty($aJournalQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aJournalQrCode['qrcode_url'];
$aAiArticle['journal_qrcode'] = $sJournalCode;
//文章二维码
$aArticleQrCode = $oMaterial->createArticleQrCode($aArticle);
$sArticleCode = empty($aArticleQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aArticleQrCode['qrcode_url'];
$aAiArticle['article_qrcode'] = $sArticleCode;
// //上传素材到微信
//组装文章数据
$aWechatConfig = $this->aWechatConfig;
$sWechatAppId = empty($aJournal['wechat_app_id']) ? $aWechatConfig['wechat_app_id'] : $aJournal['wechat_app_id'];
$sWechatAppSecret = empty($aJournal['wechat_app_secret']) ? $aWechatConfig['wechat_app_secret'] : $aJournal['wechat_app_secret'];
$aUpload['article_info'] = ['icon' => $sArticleIcon];
//组装期刊数据
$aUpload['journal_info'] = ['icon' => $sJournalIcon,'editor_qrcode' => $editor_qrcode,'journal_stage_id' => $aArticle['journal_stage_id']];
//组装作者数据
if(!empty($aAuthor)){
$aUpload['author_info'] = $this->_dealAuthor($aAuthor);
}
//上传微信公众号用到的素材
$aUpload += ['article_id' => $iArticleId,'journal_id' => $aArticle['journal_id'],'wechat_app_id' => $sWechatAppId,'wechat_app_secret' => $sWechatAppSecret];
$aResult = json_decode($oMaterial->uploadMaterial($aUpload),true);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? '' : $aResult['msg'];
if ($iStatus != 1) {
$iStatus = 2;
$sMsg = empty($sMsg) ? '素材上传失败' : $sMsg;
}
//插入日志记录
$aLogInfo = ['article_id' => $iArticleId,'type' => 1,'msg' =>$sMsg,'status' => $iStatus,'create_time' => time()];
$result = json_decode($oMaterial->addWechatLog($aLogInfo),true);
//素材上传成功调用上传草稿箱的任务
if($iStatus == 1){
//1分钟后推送草稿箱
$iDelaySeconds = 60;//4 * 3600; // 1分钟的秒数
Queue::later($iDelaySeconds,'app\api\job\WechatDraft@fire', ['article_id' => $iArticleId], 'WechatDraft');
}
return json_encode(['status' => $iStatus,'msg' => $sMsg,'data' => $aLogInfo]);
}
/**
* 获取文章对应模版
*/
private function writeFile($sContent,$sPath = ''){
$chunkSize = 1024 * 1024; // 例如每次写入1MB
$file = fopen($sPath, "w");
for ($i = 0; $i < strlen($sContent); $i += $chunkSize) {
$chunk = substr($sContent, $i, $chunkSize);
fwrite($file, $chunk);
}
fclose($file);
}
}