From 4e3882a8880d272bdbe52adcb258c036785ed389 Mon Sep 17 00:00:00 2001 From: chengxl Date: Thu, 8 May 2025 14:06:50 +0800 Subject: [PATCH] =?UTF-8?q?ai=E7=94=9F=E6=88=90=E5=BE=AE=E4=BF=A1=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/api/controller/Aiarticle.php | 1053 ++++++++++++++++++++++ 1 file changed, 1053 insertions(+) create mode 100644 application/api/controller/Aiarticle.php diff --git a/application/api/controller/Aiarticle.php b/application/api/controller/Aiarticle.php new file mode 100644 index 0000000..636b960 --- /dev/null +++ b/application/api/controller/Aiarticle.php @@ -0,0 +1,1053 @@ + ' + **核心要求** + 1️ 请将姓名翻译成中文 + {#author_name#} + 2 请将单位翻译成中文 + {#company#} + 3 请将简介翻译成中文 + {#introduction#} + 4 请将职称翻译成中文 + {#technical#} + + **输出格式** + 格式内容 + ```json + { + "author_name": "", + "company": "", + "introduction": "", + "technical": "" + } + ', + 2 => ' + **核心要求** + 1️ ​内容涵盖哪些学科及方法请罗列 + 2 结构化摘要生成【四要素模版】 + 3 研究背景提炼【三段式结构】 + 发病率+当前治疗缺口(如5年OS<20%) + 引用2-3篇高被引论文的矛盾结论(如Nature vs Cell观点分歧) + 基于团队前期工作(如预实验发现X调控Y) + 4 结论与展望撰写 + 结论部分:与TOP3高被引文献的对比 + 展望部分: + 现有方法的可扩展性(如空间组学升级计划) + + 5 按点总结归纳研究结果并做简要阐述【3点以上】 + 6 总结归纳亮点【3点以上】 + 7 禁用清单 + 直接复制讨论部分的原文 + 未达到统计学显著性的趋势描述 + 稿件内未提及的内容一定不要总结归纳出来 + "可能""或许"等不确定性词汇 + 单纯古籍原文翻译(需结合现代数据解读) + 未经验证的因果断言(如"证明XX药治愈癌症") + 8 请将标题翻译成中文 + {#title_chinese#} + 9 请将稿件内容翻译成中文 + {#content#} + 10 请将稿件所属期刊简介翻译 + {#journal_content#} + + **稿件关键信息** + - 稿件简介:{#abstract#} + - 稿件内容:{#content#} + **输出格式** + + 中文格式[英文简写忽略首字母大写] + 格式内容 + ```json + { + "title_chinese": "", + "content": "", + "journal_content": "", + "covered": "【总字数<=100】", + "digest": "【总字数<=500】", + "research_background": "", + "discussion_prospect": "", + "research_result": "", + "highlights": "", + }' + ]; + //"highlights": { + // "theory_innovation": "【中西医理论结合点】", + // "methodology": "【关键技术+量化指标】", + // "data_strength": "【数据规模/古籍分析深度】" + // } + + public function __construct(\think\Request $request = null) { + // 从配置读取敏感信息(非硬编码) + $this->sApiKey = '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'; + $this->proxy = ''; + $this->sUrl = 'https://api.openai.com/v1/chat/completions'; + + parent::__construct($request); + } + + /** + * CURL 发送请求到 OpenAI + * @param $messages 内容 + * @param $model 模型类型 + */ + protected function curlOpenAI($messages, $model = 'gpt-4o'){ + + $sUrl = $this->sUrl; + + $data = [ + 'model' => $model, + 'messages' => $messages, + 'temperature' => 0.7 + ]; + + $this->curl = curl_init(); + + + // 通用配置 + curl_setopt($this->curl, CURLOPT_URL, $sUrl); + // 设置头信息 + curl_setopt($this->curl, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $this->sApiKey + ]); + curl_setopt($this->curl, CURLOPT_PROXY,$this->proxy); + curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER,true); + curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST,2); + curl_setopt($this->curl, CURLOPT_POST, true); //设置为POST方式 + curl_setopt($this->curl, CURLOPT_POSTFIELDS,json_encode($data)); + curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, TRUE) ; // 获取数据返回 + // curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->timeout); + + $result = curl_exec($this->curl); + + //请求失败 + if (curl_errno($this->curl)){ + $this->sError = curl_errno($this->curl); + curl_close($this->curl); + return FALSE; + } + $this->sResponesData = json_decode($result); + curl_close($this->curl); + return TRUE; + } + /** + * @title 请求OPENAI + * @param aSearch array 模版内容 + * @param iTemplateId 模版ID + */ + private function _createContentForOpenAI($aSearch = [],$iTemplateId = 0){ + + if(empty($aSearch) || empty($iTemplateId)){ + return json_encode(array('status' => 2,'msg' => 'Please select a template or enter the content you want to consult'.$this->sError)); + } + //组织参数 + $sTemplate = $this->aOpenAiAsk[$iTemplateId]; + $sTemplate = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplate); + $messages = [ + [ + 'role' => 'user', //角色:platform:平台;developer:开发者;user:用户;guideline:模型规范“指南”部分:连接:https://model-spec.openai.com/2025-02-12.html#chain_of_command + 'content' => $sTemplate + ] + ]; + //请求接口 + $sModel = $aParam['api_model'] ?? 'gpt-4o'; + $result = $this->curlOpenAI($messages,$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)); + } + + /** + * @title AI生成稿件文章 + * @param article_id 文章ID + * @param model 接口模型 + * @param stream 是否流式输出 true是false否 + */ + public function create(){ + + + //获取参数 + $aParam = $this->request->post(); + $iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id']; + if(empty($iArticleId)){ + return json_encode(array('status' => 2,'msg' => 'Please select an article' )); + } + + //获取文章是否生成AI内容 + $aResult = json_decode($this->getArticle($iArticleId),true); + if($aResult['status'] != 1){ + return json_encode($aResult); + } + //获取数据 + $aArticleContent = empty($aResult['data']) ? [] : $aResult['data']; + + //文章数据 + $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']; + + + //生成期刊图片二维码 + $aJournalQrCode = $this->createJournalQrCode($aJournal); + if(empty($aJournalQrCode['qrcode_url'])){ + return json_encode(['status' => 3,'msg' => 'Journal QR code not generated']); + } + + //生成文章图片二维码 + $aArticleQrCode = $this->createArticleQrCode($aArticle); + if(empty($aArticleQrCode['qrcode_url'])){ + return json_encode(['status' => 4,'msg' => 'Article QR code not generated']); + } + + //查询文章内容 + $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'] ?? ''); + $aResult = $this->_createContentForOpenAI($aSearch,2); + $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_id'] = $aArticle['journal_id']; + //文章引用 + $aContent['cite'] = $this->_cite($aArticle,$aJournal,$aJournalStage); + if(!empty($aJournal['journal_topic'])){ + $aContent['journal_topic'] = $aJournal['journal_topic']; + } + + //获取AI生成表里的数据判断是新增或更新 + $aAiContent = json_decode($this->getAiArticle($iArticleId),true); + $aAiContent = empty($aAiContent['data']) ? [] : $aAiContent['data']; + $aAiArticle = empty($aAiContent['ai_article']) ? [] : $aAiContent['ai_article']; + $aAiAuthor = empty($aAiContent['ai_article_autho']) ? [] : $aAiContent['ai_article_autho']; + if(empty($aAiArticle)){//新增 + //执行数据入库 + $aResult = $this->addAiArticle($aContent,$aAuthor); + } + if(!empty($aAiArticle)){//更新 + if(empty($aAiArticle['ai_article_id'])){ + return json_encode(array('status' => 8,'msg' => 'No AI article found to update')); + } + //执行数据入库 + $aContent['ai_article_id'] = $aAiArticle['ai_article_id']; + $aContent['author_list'] = $aAuthor; + $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'] . '. ' . choiseJabbr($aArticle['article_id'], $jabbr) . '. ' . $stage_year . ';' . $stage_vol . $no . $aArticle['npp'] . '. doi:' . $aArticle['doi']; + } + return $sCite; + } + + /** + * @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')->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['realname'] : $sName; + $sName = empty($sName) ? $value['localname'] : $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']; + + //请求AI翻译 + // $aSearch['{#author_name#}'] = $sName; + // $aSearch['{#company#}'] = $sCompany; + // $aSearch['{#technical#}'] = $sTechnical; + // $aSearch['{#introduction#}'] = $sIntroduction; + // $aResult = $this->_createContentForOpenAI($aSearch,1); + // $aResult = json_decode($aResult,true); + // if($aResult['status'] == 1){ + // $aContent = empty($aResult['data']) ? [] : $aResult['data']; + // $sCompany = empty($aContent['company']) ? $sCompany : $aContent['company']; + // $sTechnical = empty($aContent['technical']) ? $sTechnical : $aContent['technical']; + // $sIntroduction = empty($aContent['introduction']) ? $sIntroduction : $aContent['introduction']; + // } + $aInfo[] = ['email' => $value['email'],'author_name' => $sName,'technical' => $sTechnical,'introduction' => $sIntroduction,'company' => $sCompany,'article_id' => $iArticleId,'create_time' => time()]; + + } + return $aInfo; + } + + + /** + * 接口请求获取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 + */ + public function getAiArticle($iArticleId = 0){ + + $aParam = $this->request->post(); + $iArticleId = empty($iArticleId) ? $aParam['article_id'] : $iArticleId; + //查询AI生成的文章内容 + $aParam['article_id'] = $iArticleId; + $aParam['is_delete'] = 2; + $aAiArticle = Db::name('ai_article')->where($aParam)->find(); + if(empty($aAiArticle)){ + return json_encode(['status' => 1,'msg' => 'data is null','data' => ['ai_article' => []]]); + } + //查询文章通讯作者 + if($aAuthorInfo = Db::name('ai_article_author')->where(['article_id' => $iArticleId])->select()){ + //查询头像 + $aEmail = array_column($aAuthorInfo, 'email'); + $aUser = Db::name('user')->where(['email' => ['in',$aEmail]])->column('email,icon'); + foreach ($aAuthorInfo as $key => $value) { + $aAuthorInfo[$key]['icon'] = empty($aUser[$value['email']]) ? '/static/img/userImg.f3d9bc3b.jpg' : '/public/usericon/'.$aUser[$value['email']]; + } + } + return json_encode(['status' => 1,'msg' => 'success','data' => ['ai_article' => $aAiArticle,'ai_article_author' => $aAuthorInfo]]); + } + /** + * 基础HTML过滤 + * @param string $html + * @return string + */ + private function basic_html_filter($html) { + // 移除所有HTML标签及PHP标签 + $text = 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 内容 + */ + protected function addAiArticle($aParam = array(),$aAuthor = []){ + + + //返回数组 + $aResult = ['status' => 1,'msg' => 'AI article added successfully','data' => []]; + //必填参数验证 + $aFields = $this->aAiFileds; + $bStatus = true; + $sFiled = ''; + $aInsertParam = []; + foreach($aFields as $val){ + if(empty($aParam[$val])){ + $bStatus = false; + $sFiled = $val; + break; + } + if($val == 'article_id'){ + $aInsertParam[$val] = $aParam[$val]; + continue; + } + $aInsertParam[$val] = addslashes(strip_tags($aParam[$val])); + } + if($bStatus == false){ + return json_encode(['status' => 2,'msg' => $sFiled.'cannot be empty']); + } + //执行入库 + $aInsertParam['create_time'] = time(); + + //作者判断空值 + $aInsertParam['author'] = empty($aInsertParam['author']) ? 'TMR编辑部' : $aInsertParam['author']; + Db::startTrans(); + if(!$iId = Db::name('ai_article')->insertGetId($aInsertParam)){ + return ['status' => 3,'msg' => 'Adding AI article failed']; + } + $aResult['data'] = array_merge($aInsertParam,['ai_article_id' => $iId]); + if(!empty($aAuthor)){ + $aInfo = $this->_dealAuthor($aAuthor); + + if(!empty($aInfo)){ + + foreach ($aInfo as $key => $value) { + //请求AI翻译 + $aSearch['{#author_name#}'] = $value['author_name']; + $aSearch['{#company#}'] = $value['company']; + $aSearch['{#technical#}'] = $value['technical']; + $aSearch['{#introduction#}'] = $value['introduction']; + $aResultInfo = $this->_createContentForOpenAI($aSearch,1); + $aResultInfo = json_decode($aResultInfo,true); + if($aResultInfo['status'] == 1){ + $aContent = empty($aResultInfo['data']) ? [] : $aResultInfo['data']; + $aInfo[$key]['company'] = empty($aContent['company']) ? $value['company'] : $aContent['company']; + $aInfo[$key]['technical'] = empty($aContent['technical']) ? $value['technical'] : $aContent['technical']; + $aInfo[$key]['introduction'] = empty($aContent['introduction']) ? $value['introduction'] : $aContent['introduction']; + } + } + Db::name('ai_article_author')->insertAll($aInfo); + } + } + Db::commit(); + return json_encode($aResult); + } + + /** + * @title AI生成稿件内容入库 + * @param article_id 文章ID + * @param content 内容 + */ + public function updateAiArticle($aParam = array()){ + //返回数组 + $aResult = ['status' => 1,'msg' => 'AI article updated successfully','data' => []]; + //必填参数验证 + $aFields = array_merge(['ai_article_id'],$this->aAiFileds); + $bStatus = true; + $sFiled = ''; + $aUpdateParam = []; + $iAiArticleId = ''; + foreach($aFields as $val){ + if(empty($aParam[$val])){ + $bStatus = false; + $sFiled = $val; + break; + } + if($val == 'article_id'){ + continue; + } + if($val == 'ai_article_id'){ + $iAiArticleId = $aParam[$val]; + continue; + } + $aUpdateParam[$val] = addslashes(strip_tags($aParam[$val])); + } + if($bStatus == false){ + return json_encode(['status' => 2,'msg' => $sFiled.'cannot be empty']); + } + + if(empty($iAiArticleId) || empty($aUpdateParam)){ + return json_encode(['status' => 1,'msg' => 'No data currently being processed']); + } + + Db::startTrans(); + //执行入库 + $aUpdateParam['update_time'] = time(); + $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; + + //更新作者信息 + if(!empty($aParam['author_list'])){ + foreach ($aParam['author_list'] as $key => $value) { + $aWhere = ['article_id' => $value['article_id'],'email' => $value['email']]; + unset($value['article_id']); + unset($value['email']); + $value['update_time'] = time(); + Db::name('ai_article_author')->where($aWhere)->limit(1)->update($value); + } + } + Db::commit(); + return json_encode($aResult); + } + + + /** + * 文章内容选择模版生成数据 + * @param article_id 文章ID + * @param template_id 模版ID + */ + public function getTemplateContent($iArticleId = '',$iTemplateId = ''){ + + //获取参数 + $aParam = $this->request->post(); + + //文章ID + $iArticleId = empty($iArticleId) ? $aParam['article_id'] ?? '' : $iArticleId; + //模版ID + $iTemplateId = empty($iTemplateId) ? $aParam['template_id'] ?? '' : $iTemplateId; + //必填参数验证 + if(empty($iArticleId)){ + return json_encode(['status' => 2, 'msg' => 'Please select an article']); + } + if(empty($iTemplateId)){ + return json_encode(['status' => 2, 'msg' => 'Please select a template']); + } + + //获取文章是否生成AI内容 + $aResult = json_decode($this->getArticle($iArticleId),true); + if($aResult['status'] != 1){ + return json_encode($aResult); + } + //获取数据 + $aArticleContent = empty($aResult['data']) ? [] : $aResult['data']; + + //文章数据 + $aArticle = empty($aArticleContent['article']) ? [] : $aArticleContent['article']; + + //期刊数据 + $aJournal = empty($aArticleContent['journal']) ? [] : $aArticleContent['journal']; + + //子期刊数据 + $aJournalStage = empty($aArticleContent['journal_stage']) ? [] : $aArticleContent['journal_stage']; + + //获取AI生成文章内容 + $aAiContent = json_decode($this->getAiArticle($iArticleId),true); + $aAiContent = empty($aAiContent['data']) ? [] : $aAiContent['data']; + $aAiArticle = empty($aAiContent['ai_article']) ? [] : $aAiContent['ai_article']; + //AI通讯作者 + $aAiAuthor = empty($aAiContent['ai_article_author']) ? [] : $aAiContent['ai_article_author']; + + //判断是否生成AI内容 + if(empty($aAiArticle)){ + return json_encode(['status' => 3, 'msg' => 'The article content of WeChat official account has not been generated']); + } + + //期刊访问地址 + $journal_usx = $this->sJournalUsx; + if(!empty($aJournal['journal_usx'])){ + //地址 + $journal_usx .= $aJournal['journal_usx']??''.'/'; + } + $aAiArticle['journal_usx_url'] = trim($journal_usx); + $aAiArticle += $aJournal; + + + //获取期刊图片二维码 + $aJournalQrCode = $this->createJournalQrCode($aJournal); + $aAiArticle['journal_qrcode'] = empty($aJournalQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aJournalQrCode['qrcode_url']; + //获取期刊图片二维码 + $aArticleQrCode = $this->createArticleQrCode($aArticle); + $aAiArticle['article_qrcode'] = empty($aArticleQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aArticleQrCode['qrcode_url']; + + //获取模版信息 + $sTemplatePath = ROOT_PATH."public/wechatTemplate/".$iTemplateId.'.html'; + // 验证文件有效性 + if (!file_exists($sTemplatePath)) { + return json_encode(['status' => 4, 'msg' => 'Article template does not exist']); + } + if (!is_readable($sTemplatePath)) { + return json_encode(['status' => 5, 'msg' => 'The article template is unreadable']); + } + //获取模版内容 + $sTemplate = file_get_contents($sTemplatePath); + + //数据处理 + $aSearch = []; + foreach ($aAiArticle as $key => $value) { + $aSearch['{###'.$key.'###}'] = htmlspecialchars(trim(trim(trim($value),'【'),'】')); + } + + //处理通讯作者信息数据 + $aAuthorInfo = json_decode($this->dealTemplateAuthor($aAiAuthor,$iTemplateId),true); + $aSearch['{###author_info###}'] = empty($aAuthorInfo['data']['author_info']) ? ' ' : implode("\n", $aAuthorInfo['data']['author_info']); + $aSearch['{###author###}'] = empty($aAuthorInfo['data']['author']) ? ' ' : $aAuthorInfo['data']['author']; + + // //处理公众号推荐 + // $aRecommend = json_decode($this->dealTemplateWechatRecommend($aArticle['journal_id'],$iTemplateId),true); + // $aSearch['{###wechat_recommend###}'] = empty($aRecommend['data']) ? ' ' : implode("\n", $aRecommend['data']); + + //处理往期推荐数据 + $aPreviousRecommend = json_decode($this->dealTemplatePreviousRecommend($aArticle,$iTemplateId),true); + if(empty($aPreviousRecommend['data'])){ + $aSearch['{###previous_recommend_summary###}'] = ''; + }else{ + $sSummaryTemplatePath = ROOT_PATH."public/wechatTemplate/".$iTemplateId.'_previous_recommend_summary.html'; + $sSummaryTemplate = file_get_contents($sSummaryTemplatePath); + $aSearchInfo['{###previous_recommend###}'] = empty($aPreviousRecommend['data']) ? ' ' : implode("\n", $aPreviousRecommend['data']); + $sSummaryTemplate = str_replace(array_keys($aSearchInfo), array_values($aSearchInfo), $sSummaryTemplate); + $aSearch['{###previous_recommend_summary###}'] = $sSummaryTemplate; + } + + + //处理期刊主题数据 + $aTopic = json_decode($this->dealTemplateTopic($aAiArticle['journal_topic'],$iTemplateId),true); + if(empty($aTopic['data'])){ + $aSearch['{###topic_name_summary###}'] = ''; + }else{ + + $sSummaryTemplatePath = ROOT_PATH."public/wechatTemplate/".$iTemplateId.'_topic_summary.html'; + $sSummaryTemplate = file_get_contents($sSummaryTemplatePath); + $aSearchInfo['{###topic_name###}'] = empty($aTopic['data']) ? ' ' : implode("\n", $aTopic['data']); + $sSummaryTemplate = str_replace(array_keys($aSearchInfo), array_values($aSearchInfo), $sSummaryTemplate); + $aSearch['{###topic_name_summary###}'] = $sSummaryTemplate; + } + + //处理文章图片地址数据 + $aSearch['{###article_icon###}'] = $this->sJournalUsx.'/public/articleicon/'.$aArticle['article_icon']??''; + //处理文章期刊图片地址数据 + $aSearch['{###journal_icon###}'] = $this->sJournalUsx.'/public/journalicon/'.$aJournal['journal_icon']??''; + //模版替换变量 + if(empty($aSearch)){ + return json_encode(['status' => 6, 'msg' => '模版内容不能为空']); + } + $sTemplate = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplate); + file_put_contents(ROOT_PATH.'public/template_info.html', $sTemplate); + return json_encode(['status' => 1, 'msg' => '模版生成成功','data' => ['template' => $sTemplate]]); + } + + /** + * 处理模版通讯作者 + * @param article_id 文章ID + */ + private function dealTemplateAuthor($aAuthor = [],$iTemplateId = 0){ + + //必填字段判断 + if(empty($aAuthor) || empty($iTemplateId)){ + return json_encode(['status' => 2,'msg' => 'Corresponding author or template does not exist']); + } + + //获取通讯作者模版 + $sAuthorTemplatePath = ROOT_PATH."public/wechatTemplate/".$iTemplateId.'_author.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); + + + //处理模版变量 + $aAuthorSerachInfo = []; + $sAuthorInfo = ''; + foreach ($aAuthor as $key => $value) { + $aAuthorSerach = []; + foreach ($value as $k => $val) { + if($k == 'icon'){ + $aAuthorSerach['{###'.$k.'###}'] = $this->sSubmissionUrl.trim($val); + }else{ + $aAuthorSerach['{###'.$k.'###}'] = trim($val); + } + } + $aAuthorSerachInfo[] = str_replace(array_keys($aAuthorSerach), array_values($aAuthorSerach), $sAuthorTemplate); + $sAuthorInfo = $value['company'].' '.$value['author_name'].';'; + } + return json_encode(['status' => 1,'msg' => 'success','data' => ['author_info' => $aAuthorSerachInfo,'author' => trim($sAuthorInfo,';')]]); + } + + // /** + // * 处理公众号推荐 + // * @param article_id 文章ID + // */ + // private function dealTemplateWechatRecommend($iJournalId = 0,$iTemplateId = 0){ + + // if(empty($iTemplateId)){ + // return json_encode(['status' => 2,'msg' => 'template does not exist']); + // } + // //获取通讯作者 + // $sTemplatePath = ROOT_PATH."public/wechatTemplate/".$iTemplateId.'_wechat_recommend.html'; + // if (!file_exists($sTemplatePath)) { + // return json_encode(['status' => 4, 'msg' => 'Corresponding author template does not exist','data' => '']); + // } + // if (!is_readable($sTemplatePath)) { + // return json_encode(['status' => 5, 'msg' => 'The corresponding author template is unreadable','data' => '']); + // } + // $sTemplate = file_get_contents($sTemplatePath); + + // //查询公众号推荐 + // $aRecommend = Db::name('ai_article')->field('article_id,title_chinese,covered,digest')->where('journal_id',$iJournalId)->select(); + + // $aSearchInfo = []; + // if(!empty($aRecommend)){ + // foreach ($aRecommend as $key => $value) { + // $aSerach = []; + // foreach ($value as $k => $val) { + // $aSerach['{###'.$k.'###}'] = trim(trim(trim($val),'【'),'】'); + // } + // $aSearchInfo[] = str_replace(array_keys($aSerach), array_values($aSerach), $sTemplate); + // } + // } + // return json_encode(['status' => 1,'msg' => 'success','data' => $aSearchInfo]); + // } + + /** + * 处理模版主题 + * @param article_id 文章ID + */ + private function dealTemplateTopic($sTopic = '',$iTemplateId = 0){ + + if(empty($iTemplateId)){ + return json_encode(['status' => 2,'template does not exist']); + } + if(empty($sTopic)){ + return json_encode(['status' => 2,'topic does not exist']); + } + + //获取主题模版 + $sTemplatePath = ROOT_PATH."public/wechatTemplate/".$iTemplateId.'_topic.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); + + //数据处理 + $sTopic = trim($sTopic); + $aTopic = empty($sTopic) ? [] : explode(',', $sTopic); + $aSearchInfo = []; + if(!empty($aTopic)){ + foreach ($aTopic as $key => $value) { + $aSerach['{###topic_name###}'] = trim(trim(trim($value),'【'),'】'); + $aSearchInfo[] = str_replace(array_keys($aSerach), array_values($aSerach), $sTemplate); + } + } + return json_encode(['status' => 1,'msg' => 'success','data' => $aSearchInfo]); + } + + /** + * 处理往期推荐 + * @param article_id 文章ID + */ + private function dealTemplatePreviousRecommend($aArticle=[],$iTemplateId = 0){ + + if(empty($iTemplateId)){ + return json_encode(['status' => 2,'template does not exist']); + } + //获取往期推荐模版 + $sTemplatePath = ROOT_PATH."public/wechatTemplate/".$iTemplateId.'_previous_recommend.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); + + //判断是否关联文章 + if(empty($aArticle['related'])){ + return json_encode(['status' => 6, 'msg' => 'Unrelated articles']); + } + + //获取关联文章信息 + $aWhere = ['article_id' => ['in',json_decode($aArticle['related'],true)],'is_publish' => 1,'is_delete' => 2]; + $aRecommend = Db::name('ai_article')->field('article_id,media_id,wechat_url')->where($aWhere)->select(); + $aSearchInfo = []; + if(!empty($aRecommend)){ + + //查询文章图片 + $aId = array_column($aRecommend, 'article_id'); + $aWhere = ['w_article_id' => ['in',$aId]]; + $aProductionArticle = Db::name('production_article')->where($aWhere)->column('w_article_id,icon'); + foreach ($aRecommend as $key => $value) { + $aSerach['{###wechat_url###}'] = $value['wechat_url']; + $sIcon = empty($aProductionArticle[$value['article_id']]) ? '' : $this->sJournalUsx.'/public/articleicon/'.$aProductionArticle[$value['article_id']]; + $aSerach['{###wechat_media_url###}'] = empty($value['wechat_media_url']) ? $sIcon : $value['wechat_media_url']; + $aSearchInfo[] = str_replace(array_keys($aSerach), array_values($aSerach), $sTemplate); + } + } + return json_encode(['status' => 1,'msg' => 'success','data' => $aSearchInfo]); + } + + /** + * @title 文章AI生成稿件内容查询 + * @param aJournal 期刊信息 + */ + private function createJournalQrCode($aJournal = []){ + $iJournalId = empty($aJournal['journal_id']) ? 0 : $aJournal['journal_id']; + //判断期刊二维码是否生成 + $aWhere = ['journal_id' => $iJournalId]; + $aQrCode = Db::name('journal_qrcode')->where($aWhere)->find(); + if(!empty($aQrCode)){ + return $aQrCode; + } + //创建二维码 + //生成图片地址 + $sImagePath = 'journal/'.$iJournalId.'.jpg'; + $sQrCodeUrl = ROOT_PATH.'public/qrcode/'.$sImagePath; + // 验证图片是否存在 + if (file_exists($sQrCodeUrl)) { + return ['qrcode_url' => $sImagePath]; + } + + //地址 + $journal_usx = empty($aJournal['journal_usx']) ? '' : $aJournal['journal_usx']; + $sUrl = $this->sJournalUsx.'/'.$journal_usx.'/'; + + //二维码中间图片 + $sLogo = empty($aJournal['journal_icon']) ? '' : $aJournal['journal_icon']; + if(empty($sLogo)){ + $sImg = QrCodeImage::generate($sUrl,500,$sQrCodeUrl); + }else{ + $sLogo = $this->sJournalUsx.'/public/journalicon/'.$sLogo; + $aImageInfo = json_decode($this->getImage($sLogo,$iJournalId,'journal'),true); + if(empty($aImageInfo['data'])){ + $sImg = QrCodeImage::generate($sUrl,500,$sQrCodeUrl); + } + $sImg = QrCodeImage::withLogo($sUrl,$aImageInfo['data'],500,100, $sQrCodeUrl); + } + + //插入期刊二维码表 + $aParam = ['journal_id' => $iJournalId,'qrcode_url' => $sImagePath,'create_time' => time()]; + $result = Db::name('journal_qrcode')->insertGetId($aParam); + return $aParam; + } + + /** + * @title 文章AI生成稿件内容查询 + * @param aArticle 文章信息 + */ + private function createArticleQrCode($aArticle = []){ + + $iArticleId = empty($aArticle['article_id']) ? 0 : $aArticle['article_id']; + //判断期刊二维码是否生成 + $aWhere = ['article_id' => $iArticleId]; + $aQrCode = Db::name('article_qrcode')->where($aWhere)->find(); + if(!empty($aQrCode)){ + return $aQrCode; + } + + //创建二维码 + //生成图片地址 + $sImagePath = 'article/'.$iArticleId.'.jpg'; + $sQrCodeUrl = ROOT_PATH.'public/qrcode/'.$sImagePath; + // 验证图片是否存在 + if (file_exists($sQrCodeUrl)) { + return ['qrcode_url' => $sImagePath]; + } + //地址 + $sUrl = $this->sJournalUsx.'/'.'article.html?J_num='.$aArticle['journal_id'].'&a_id='.$iArticleId; + + //二维码中间图片 + $sLogo = $aArticle['article_icon']; + if(empty($sLogo)){ + $sImg = QrCodeImage::generate($sUrl,500,$sQrCodeUrl); + }else{ + $sLogo = $this->sJournalUsx.'/public/articleicon/'.$sLogo; + $aImageInfo = json_decode($this->getImage($sLogo,$iArticleId,'article'),true); + if(empty($aImageInfo['data'])){ + $sImg = QrCodeImage::generate($sUrl,500,$sQrCodeUrl); + } + $sImg = QrCodeImage::withLogo($sUrl,$aImageInfo['data'],500,100, $sQrCodeUrl); + } + + //插入期刊二维码表 + $aParam = ['article_id' => $iArticleId,'qrcode_url' => $sImagePath,'create_time' => time()]; + $result = Db::name('article_qrcode')->insertGetId($aParam); + return $aParam; + } + + /** + * @title 请求OPENAI翻译内容 + * @param article_id 文章ID + */ + public function translate($param = [],$url=''){ + + if (!is_array($param)) { + + throw new Exception("参数必须为array"); + } + + $httph = curl_init($url); + + // curl_setopt($httph, CURLOPT_SSL_VERIFYPEER, 0); + // curl_setopt($httph, CURLOPT_SSL_VERIFYHOST, 1); + + curl_setopt($httph, CURLOPT_RETURNTRANSFER, 1); + + curl_setopt($httph, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"); + + curl_setopt($httph, CURLOPT_POST, 1); //设置为POST方式 + + curl_setopt($httph, CURLOPT_POSTFIELDS, $param); + + curl_setopt($httph, CURLOPT_SSL_VERIFYPEER, false); + + // curl_setopt($httph, CURLOPT_RETURNTRANSFER,0); + // curl_setopt($httph, CURLOPT_HEADER,1); + + $rst = curl_exec($httph); + + curl_close($httph); + + return json_decode($rst,true); + } + + /** + * @title curl 请求获取图片保存到本地 + * @param sPath 图片链接地址 + */ + public function getImage($sPath = '',$iId = 0,$sType = 'journal'){ + //判断参数 + if(empty($sPath)){ + return json_encode(['status' => 2,'msg' => 'url is empty','data' => '']); + } + + //获取图片名称 + $aImageInfo = pathinfo($sPath); + + //图片后缀名 + $sExtension = empty($aImageInfo['extension']) ? 'jpg' : $aImageInfo['extension']; + + //图片地址 + $sImagePath = $sType.'/imgae_'.$iId.'.'.$sExtension; + $sImagePath = ROOT_PATH.'public/qrcode/'.$sImagePath; + if (file_exists($sImagePath)) { + return json_encode(['status' => 1,'msg' => 'success','data' => $sImagePath]); + } + //curl 请求 + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL,$sPath); // 设置请求URL + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 设置返回数据而不是直接输出 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 禁用SSL验证 + $response = curl_exec($ch); + if (curl_errno($ch)) { + return json_encode(['status' => 3,'msg' => 'Error:' . curl_error($ch),'data' => $sImagePath]); + } + //保存图片 + file_put_contents($sImagePath, $response); + curl_close($ch); + return json_encode(['status' => 1,'msg' => 'success','data' => $sImagePath]); + + } + +}