['media_id' => 'Cn8zlXvVB5DwjcA9h40z9eW0lwKRJjo_skJ0AmRAwthNP62sxfHGXoC1uIBqzhtc','url' => 'http://mmbiz.qpic.cn/mmbiz_png/QHFVW13lONaQJxK9QbHU9CtrvTS2ModZC7TJuqGRGX6dBIzou8E4d2vfgib3hiaJC5Vv8cI5BTN78FAMqXXL6PmQ/0?wx_fmt=png'], 'blackground.png' => ['media_id' => 'Cn8zlXvVB5DwjcA9h40z9ZL-R__Pwz7qdbJfxSHmuBjNbphyBFlanm-sBQ_nWUYC','url' => 'http://mmbiz.qpic.cn/mmbiz_png/QHFVW13lONaQJxK9QbHU9CtrvTS2ModZfssPs0wTQYhX6ctSiaY1Uck0WTJ1xXZsZlhU05sOjUqFRmLWamH3XKQ/0?wx_fmt=png'], 'dongtu.gif' => ['media_id' => 'Cn8zlXvVB5DwjcA9h40z9b7p9bSZhAeYdRRstPiBXjqSRboKJcxeHKz6VsG5H8ZG','url' => 'http://mmbiz.qpic.cn/mmbiz_gif/QHFVW13lONaQJxK9QbHU9CtrvTS2ModZK6wx6OQLlIiaDRcp0sgxXkZQgwWfDcoM7KzErBE6Nvq4IVf4iaX2icrUQ/0?wx_fmt=gif'], 'weixin.jpeg' => ['media_id' => 'Cn8zlXvVB5DwjcA9h40z9VDBX-gpcy4OA13l_atzNZ8NLWvSV7YA82IlpP6Q1672','url' => 'http://mmbiz.qpic.cn/mmbiz_jpg/QHFVW13lONaQJxK9QbHU9CtrvTS2ModZknGn1cCteibInoxTDzD0EGt5mL7S170R99QGv7ibwWnQFJRZFLN7iaD5g/0?wx_fmt=jpeg'], ]; protected $aLogo = ['media_id' => 'Cn8zlXvVB5DwjcA9h40z9fprHDoc3Jqv97SwrInpmyYiilkeRdKvpD63cWqTYHfz','url' => 'http://mmbiz.qpic.cn/mmbiz_jpg/QHFVW13lONaQJxK9QbHU9CtrvTS2ModZnUyeAvuVN67t8XP85DxVJwDJf2YxCTalrsr17jS080xM6xQv5yGiaEQ/0?wx_fmt=jpeg'];//默认头像 protected $sAppID = 'wx03cb871b66e34e10'; protected $sAppSecret = 'f59ccaf00383dcfab489292c4697620a'; protected $sWechatAccessToken = 'wechat_access_token'; protected $proxy; protected $sUrl; protected $curl; protected $sResponesData; protected $sError; protected $timeout = 60; protected $aAiFileds = ['article_id','title_english','title_chinese','journal_id','cite','covered','digest','research_result','content','highlights','discussion','prospect','journal_content','research_background','journal_topic']; 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 => ' **核心要求** 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高被引文献的对比(用"与A研究的X结论不同..."句式) 机制解释的3级证据链(如:基因敲除→通路抑制→表型逆转) 临床转化的可行性路径(如:基于RECIST标准的ORR提升)研究局限性(分方法学/样本量/随访维度) 5 针对稿件内容进行展望撰写 现有方法的可扩展性(如空间组学升级计划) 6 按点总结归纳研究结果并做简要阐述【3点以上】 7 总结归纳亮点【3点以上】 8 禁用清单 直接复制讨论部分的原文 未达到统计学显著性的趋势描述 稿件内未提及的内容一定不要总结归纳出来 "可能""或许"等不确定性词汇 单纯古籍原文翻译(需结合现代数据解读) 未经验证的因果断言(如"证明XX药治愈癌症") 9 请将标题翻译成中文 {#title_chinese#} 10 请将稿件内容翻译成中文 {#content#} 11 请将稿件所属期刊简介翻译 {#journal_content#} **稿件关键信息** - 稿件简介:{#abstract#} - 稿件内容:{#content#} **输出格式** 中文格式[英文简写忽略首字母大写] 格式内容 ```json { "title_chinese": "", "content": "", "journal_content": "", "covered": "【总字数<=100】", "digest": "【总字数<=500】", "research_background": "", "research_result": "", "highlights": "", "prospect": "", "discussion": "" }' ]; //"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'] ?? ''); $aSearch['{#journal_content#}'] = trim(trim($aSearch['{#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' => []]; //获取参数 $aParam = $this->request->post(); //主键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; $bStatus = true; $sFiled = ''; $aUpdateParam = []; foreach($aFields as $val){ if(!isset($aParam[$val])){ continue; } if(empty($aParam[$val])){ $bStatus = false; $sFiled = $val; break; } $aUpdateParam[$val] = addslashes(strip_tags($aParam[$val])); } if($bStatus == false){ return json_encode(['status' => 2,'msg' => $sFiled.'cannot be empty']); } if(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; //更新作者信息 $aAuthorList = empty($aParam['author_list']) ? []: $aParam['author_list']; $aAuthorList = is_array($aParam['author_list']) ? $aAuthorList: json_decode($aAuthorList,true); if(!empty($aAuthorList)){ foreach ($aAuthorList as $key => $value) { if(empty($value['email'])){ continue; } $aWhere = ['article_id' => $iArticleId,'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 * @param $iIsSync 是否同步素材到微信 1是2否 */ public function getTemplateContent($iArticleId = '',$iTemplateId = '',$iIsSync = 2){ //获取参数 $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生成文章内容 $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']); } //模版ID $iTemplateId = empty($iTemplateId) ? $aAiArticle['template_id'] : $iTemplateId; //获取文章内容 $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']; //文章图片 $sArticleIcon = trim($this->sJournalUsx,'/').'/public/articleicon/'.$aArticle['article_icon']; //期刊图片 $sJournalIcon = trim($this->sJournalUsx,'/').'/public/journalicon/'.$aJournal['journal_icon']; //获取期刊图片二维码 $aJournalQrCode = $this->createJournalQrCode($aJournal); $sJournalCode = empty($aJournalQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aJournalQrCode['qrcode_url']; $aAiArticle['journal_qrcode'] = $sJournalCode; //文章图片二维码 $aArticleQrCode = $this->createArticleQrCode($aArticle); $sArticleCode = empty($aArticleQrCode['qrcode_url']) ? '' : $this->sSubmissionUrl.'public/qrcode/'.$aArticleQrCode['qrcode_url']; $aAiArticle['article_qrcode'] = $sArticleCode; // //上传素材到微信 if($iIsSync == 1){ //文章图片上传 $aUpload = ['article_id' => $iArticleId,'icon' => $sArticleIcon]; $aArticleResult = json_decode($this->_addArticleMaterial($aUpload),true); $aArticleData = empty($aArticleResult['data']) ? [] : $aArticleResult['data']; $aAiArticle['article_icon'] = empty($aArticleData['media_url']) ? $sArticleIcon : $aArticleData['media_url']; //期刊图片上传 $aUpload = ['journal_id' => $aAiArticle['journal_id'],'icon' => $sJournalIcon]; $aJournalResult = json_decode($this->_addJournalMaterial($aUpload),true); $aJournalData = empty($aJournalResult['data']) ? [] : $aJournalResult['data']; $aAiArticle['journal_icon'] = empty($aJournalData['media_url']) ? $sJournalIcon : $aJournalData['media_url']; //期刊二维码图片上传 $aUpload = ['journal_id' => $aAiArticle['journal_id']]; $aJournalQrCodeResult = json_decode($this->_updateJournalQrcode($aUpload),true); $aJournalQrCodeData = empty($aJournalQrCodeResult['data']) ? [] : $aJournalQrCodeResult['data']; $aAiArticle['journal_qrcode'] = empty($aJournalQrCodeData['media_url']) ? $sJournalIcon : $aJournalQrCodeData['media_url']; //文章二维码图片上传 $aUpload = ['article_id' => $iArticleId]; $aArticleQrCodeResult = json_decode($this->_updateArticleQrcode($aUpload),true); $aArticleQrCodeData = empty($aArticleQrCodeResult['data']) ? [] : $aArticleQrCodeResult['data']; $aAiArticle['article_qrcode'] = empty($aArticleQrCodeData['media_url']) ? $sArticleIcon : $aArticleQrCodeData['media_url']; //作者图片上传 if(!empty($aAiAuthor)){ foreach ($aAiAuthor as $key => $value) { if(empty($value['icon'])){ continue; } //文章二维码图片上传 $aUpload = ['email' => $value['email'],'icon' => $value['icon']]; $aAuthorResult = json_decode($this->_updateArticleAuthor($aUpload),true); $aAuthorData = empty($aAuthorResult['data']) ? [] : $aAuthorResult['data']; $aAiAuthor[$key]['icon'] = empty($aAuthorData['media_url']) ? $value['icon'] : $aAuthorData['media_url']; } } }else{ //处理文章图片地址数据 $aAiArticle['article_icon'] = $sArticleIcon; //处理文章期刊图片地址数据 $aAiArticle['journal_icon'] = $sJournalIcon; } //期刊访问地址 $journal_usx = trim($this->sJournalUsx,'/').'/'; if(!empty($aJournal['journal_usx'])){ //地址 $journal_usx .= $aJournal['journal_usx']??''.'/'; } $aAiArticle['journal_usx_url'] = trim($journal_usx); $aAiArticle += $aJournal; //获取模版信息 $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(strip_tags($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; } //模版替换变量 if(empty($aSearch)){ return json_encode(['status' => 6, 'msg' => '模版内容不能为空']); } $aSearch['{###jabbr###}'] = trim(trim($aSearch['{###jabbr###}'],'.'),'。'); $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 = ''; $aLogo = $this->aLogo; foreach ($aAuthor as $key => $value) { $aAuthorSerach = []; foreach ($value as $k => $val) { if($k == 'icon'){ if($val == $aLogo['url']){ $aAuthorSerach['{###'.$k.'###}'] = $val; }else{ $aAuthorSerach['{###'.$k.'###}'] = trim($this->sSubmissionUrl,'/').$val; } $aAuthorSerach['{###'.$k.'###}'] = trim($aAuthorSerach['{###'.$k.'###}'],'/'); }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_delete' => 2]; $aRecommend = Db::name('ai_article')->field('article_id')->where($aWhere)->select(); $aSearchInfo = []; if(!empty($aRecommend)){ $aId = array_column($aRecommend, 'article_id'); //查询是否推送到微信公众号并且已发布 $aWhere['template_id'] = $iTemplateId;//,'is_publish' => 1 $aWechatArticle = Db::name('ai_wechat_article')->where($aWhere)->column('article_id'); if(!empty($aWechatArticle)){ return json_encode(['status' => 6, 'msg' => 'No article published on WeChat official account was found']); } //查询文章图片 $aWhere = ['w_article_id' => ['in',$aId]]; $aProductionArticle = Db::name('production_article')->where($aWhere)->column('w_article_id,icon'); foreach ($aRecommend as $key => $value) { if(!in_array($value['article_id'], $aWechatArticle)){ continue; } $aSerach['{###wechat_url###}'] = $value['wechat_url']; $sIcon = empty($aProductionArticle[$value['article_id']]) ? '' : trim($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('ai_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 = trim($this->sJournalUsx,'/').'/'.$journal_usx.'/'; //二维码中间图片 $sLogo = empty($aJournal['journal_icon']) ? '' : $aJournal['journal_icon']; if(empty($sLogo)){ $sImg = QrCodeImage::generate($sUrl,500,$sQrCodeUrl); }else{ $sLogo = trim($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('ai_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('ai_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 = trim($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 = trim($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('ai_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]); } /** * 文章同步到微信公众号草稿箱 * @param article_id 文章ID */ public function syncWechat(){ //获取参数 $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']); } //获取模版生成内容 $aResult = json_decode($this->getTemplateContent($iArticleId,$iTemplateId,1),true); $iStatus = empty($aResult['status']) ? 0 : $aResult['status'];//状态 $sMsg = empty($aResult['msg']) ? 'The content is empty' : $aResult['msg']; if($iStatus != 1){ return json_encode(['status' => 3, 'msg' => $sMsg]); } $aData = empty($aResult['data']) ? [] : $aResult['data']; if(empty($aData['template'])){ return json_encode(['status' => 4, 'msg' => $sMsg]); } //请求微信公众号接口推送 $aParam['template_content'] = $aData['template']; $aParam['template_id'] = $iTemplateId; $aResult = $this->addDraft($aParam); return $aResult; } /** * CURL 发送请求获取Token * @param $appid AppID(小程序ID) * @param $secret AppSecret(小程序密钥) */ private function getAccessToken() { //Token缓存值 $sToken = Cache::get($this->sWechatAccessToken); //判断缓存是否存在 if (!$sToken) { //不存在 //组装接口地址 $sUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $this->sAppID . "&secret=" . $this->sAppSecret; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $sUrl); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout); $result = curl_exec($curl); //请求失败 if (curl_errno($curl)){ $this->sError = curl_errno($curl); curl_close($curl); return FALSE; } $result = json_decode($result, true); if (!empty($result['access_token'])) { $sToken = $result['access_token']; Cache::set($this->sWechatAccessToken, $sToken, 7000); // 有效期7200秒 } curl_close($curl); } return $sToken; } /** * CURL 发送请求上传永久素材 * @param $sToken Token * @param $sPath 图片地址 */ public function uploadMaterial($sPath = '') { $aParam = $this->request->post(); $sPath = empty($sPath) ? $aParam['path'] : $sPath; // $sPath = ROOT_PATH.'static/img/'.$sPath; // return json_encode(['status' => 1,'data' => ['media_id' => '1111111','url' => '22222']]); //返回结果数组 $aResult = ['status' => 1,'msg' => '上传素材成功','data' => []]; $sPath = empty($sPath) ? $aParam['image_path']?? '' : $sPath;//文件地址 $sType = empty($aParam['type']) ? 'image' : $aParam['type'];//文件类型 // 验证文件有效性 if (!file_exists($sPath)) { return json_encode(['status' => 2,'msg' => '素材不存在'.$sPath]); } if (!is_readable($sPath)) { return json_encode(['status' => 3,'msg' => '素材不可读']); } if (filesize($sPath) > 10 * 1024 * 1024) { return json_encode(['status' => 4,'msg' => '素材不能超过10MB']); } //获取Token $sToken = $this->getAccessToken(); if (empty($sToken)) { return json_encode(['status' => 5, 'msg' => '获取access_token失败']); } //CURL 请求接口 //文件参数 $data = [ 'media' => new \CURLFile( realpath($sPath), mime_content_type($sPath), // 自动检测MIME类型 basename($sPath) // 上传后的文件名 ), // 关键:使用CURLFile对象 ]; $sUrl = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={$sToken}&type={$sType}"; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $sUrl); curl_setopt($curl, CURLOPT_POST, true); //设置为POST方式 curl_setopt($curl, CURLOPT_POSTFIELDS, $data); curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE) ; // 获取数据返回 curl_setopt($curl, CURLOPT_PROXY,$this->proxy); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER,true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,false); //执行 $response = curl_exec($curl); //请求失败 if (curl_errno($curl)){ curl_close($curl); return json_encode(['status' => 6,'msg' => "网络请求失败: " . curl_errno($curl)]); } curl_close($curl);//关闭请求 //获取返回结果 $response = json_decode($response, true); if (!empty($response['errcode']) && $response['errcode'] != 0) { return json_encode(['status' => 7,'msg' => "微信接口错误: [{$response['errcode']}] {$response['errmsg']}"]); } //插入数据库 if(empty($response['media_id'])){ return json_encode(['status' => 8,'msg' => "未获取到上传素材"]); } return json_encode(['status' => 1,'msg' => 'success','data' => $response]); } /** * 添加文章素材 * @param article_id 文章ID * @param media_id 微信公众号素材media_id * @param media_url 微信公众号素材地址 * @param sType select 查询|add 新增 * @param $sPath 图片地址 */ private function _addArticleMaterial($aParam = []){ //文章ID $iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id']; //文章图片 $sIcon = empty($aParam['icon']) ? '' : $aParam['icon']; //必填验证 if(empty($iArticleId) || empty($sIcon)){ return json_encode(['status' => 2,'msg' => 'article_id or article_image is empty']); } //查询是否添加素材 $aWhere = ['article_id' => $iArticleId,'is_delete' => 2]; $aMaterial = Db::name('ai_article_material')->field('media_id,media_url')->where($aWhere)->find(); if(!empty($aMaterial)){ return json_encode(['status' => 1,'msg' => 'added','data' => $aMaterial]); } //处理图片地址 $aImageInfo = pathinfo($sIcon); //图片后缀名 $sExtension = empty($aImageInfo['extension']) ? 'jpg' : $aImageInfo['extension']; $sImagePath = 'article/imgae_'.$iArticleId.'.'.$sExtension; $sImagePath = ROOT_PATH.'public/qrcode/'.$sImagePath; if (!file_exists($sImagePath)) {//下载图片 $aImageInfo = json_decode($this->getImage($sIcon,$iArticleId,'article'),true); if(empty($aImageInfo['data'])){ return json_encode(['status' => 3,'msg' => 'Image download failed']); } } //调用微信新增素材接口上传 $aUploadResult = json_decode($this->uploadMaterial($sImagePath),true); $iStatus = empty($aUploadResult['status']) ? 0 : $aUploadResult['status']; $sMsg = empty($aUploadResult['msg']) ? 'Failed to upload materials' : $aUploadResult['msg']; if($iStatus != 1){ return json_encode(['status' => 4,'msg' => $sMsg]); } $aData = empty($aUploadResult['data']) ? [] : $aUploadResult['data']; if(empty($aData['url'])){ return json_encode(['status' => 5,'msg' => 'Material not obtained']); } //处理入库数据 $sMediaId = empty($aData['media_id']) ? [] : $aData['media_id']; $sMediaUrl = empty($aData['url']) ? [] : $aData['url']; $iTime = empty($aUploadResult['created_at']) ? time() : $aUploadResult['created_at']; //入库数据组装 $aInsert = ['type' => 'image','media_id' => $sMediaId,'create_time' => $iTime,'article_id' => $iArticleId,'media_url' => $sMediaUrl]; $response = Db::name('ai_article_material')->insert($aInsert); if($response === false){ return json_encode(['status' => 6,'msg' => 'Add fail']); } return json_encode(['status' => 1,'msg' => 'success','data' => $aInsert]); } /** * 添加期刊素材 * @param $sToken Token * @param $sPath 图片地址 */ private function _addJournalMaterial($aParam = []){ //期刊ID $iJournalId = empty($aParam['journal_id']) ? '' : $aParam['journal_id']; //期刊图片 $sIcon = empty($aParam['icon']) ? '' : $aParam['icon']; if(empty($iJournalId) || empty($sIcon)){ return json_encode(['status' => 2,'msg' => 'journal_id or image is empty']); } //查询是否添加素材 $aWhere = ['journal_id' => $iJournalId,'is_delete' => 2]; $aMaterial = Db::name('ai_journal_material')->field('media_id,media_url')->where($aWhere)->find(); if(!empty($aMaterial)){ return json_encode(['status' => 1,'msg' => 'success','data' => $aMaterial]); } //处理图片地址 $aImageInfo = pathinfo($sIcon); //图片后缀名 $sExtension = empty($aImageInfo['extension']) ? 'jpg' : $aImageInfo['extension']; $sImagePath = 'journal/imgae_'.$iJournalId.'.'.$sExtension; $sImagePath = ROOT_PATH.'public/qrcode/'.$sImagePath; if (!file_exists($sImagePath)) {//下载图片 $aImageInfo = json_decode($this->getImage($sIcon,$iJournalId,'journal'),true); if(empty($aImageInfo['data'])){ return json_encode(['status' => 3,'msg' => 'Image download failed']); } } //调用微信新增素材接口上传 $aUploadResult = json_decode($this->uploadMaterial($sImagePath),true); $iStatus = empty($aUploadResult['status']) ? 0 : $aUploadResult['status']; $sMsg = empty($aUploadResult['msg']) ? 'Failed to upload materials' : $aUploadResult['msg']; if($iStatus != 1){ return json_encode(['status' => 4,'msg' => $sMsg]); } $aData = empty($aUploadResult['data']) ? [] : $aUploadResult['data']; if(empty($aData['url'])){ return json_encode(['status' => 5,'msg' => 'Material not obtained']); } //处理入库数据 $sMediaId = empty($aData['media_id']) ? [] : $aData['media_id']; $sMediaUrl = empty($aData['url']) ? [] : $aData['url']; $iTime = empty($aUploadResult['created_at']) ? time() : $aUploadResult['created_at']; //入库数据组装 $aInsert = ['type' => 'image','media_id' => $sMediaId,'create_time' => $iTime,'journal_id' => $iJournalId,'media_url' => $sMediaUrl]; $response = Db::name('ai_journal_material')->insert($aInsert); if($response === false){ return json_encode(['status' => 6,'msg' => 'Add fail']); } return json_encode(['status' => 1,'msg' => 'success','data' => $aInsert]); } /** * 更新期刊二维码关联微信公众号素材 */ private function _updateJournalQrcode($aParam = []){ //期刊ID $iJournalId = empty($aParam['journal_id']) ? '' : $aParam['journal_id']; if(empty($iJournalId)){ return json_encode(['status' => 2,'msg' => 'journal_id or image is empty']); } //查询是否生成二维码 $aWhere = ['journal_id' => $iJournalId,'is_delete' => 2]; $aMaterial = Db::name('ai_journal_qrcode')->field('qrcode_url,media_id,media_url')->where($aWhere)->find(); if(empty($aMaterial)){ return json_encode(['status' => 3,'msg' => 'data is null']); } if(!empty($aMaterial['media_url'])){ return json_encode(['status' => 1,'msg' => 'success','data' => $aMaterial]); } //处理图片地址 $sImagePath = ROOT_PATH.'public/qrcode/'.$aMaterial['qrcode_url']; //调用微信新增素材接口上传 $aUploadResult = json_decode($this->uploadMaterial($sImagePath),true); $iStatus = empty($aUploadResult['status']) ? 0 : $aUploadResult['status']; $sMsg = empty($aUploadResult['msg']) ? 'Failed to upload materials' : $aUploadResult['msg']; if($iStatus != 1){ return json_encode(['status' => 4,'msg' => $sMsg]); } $aData = empty($aUploadResult['data']) ? [] : $aUploadResult['data']; if(empty($aData['url'])){ return json_encode(['status' => 5,'msg' => 'Material not obtained']); } $sMediaId = empty($aData['media_id']) ? [] : $aData['media_id']; $sMediaUrl = empty($aData['url']) ? [] : $aData['url']; $iTime = empty($aUploadResult['created_at']) ? time() : $aUploadResult['created_at']; $aUpdate = ['media_id' => $sMediaId,'media_url' => $sMediaUrl,'update_time' => $iTime]; $response = Db::name('ai_journal_qrcode')->where('journal_id',$iJournalId)->limit(1)->update($aUpdate); if($response === false){ return json_encode(['status' => 3,'msg' => 'fail']); } return json_encode(['status' => 1,'msg' => 'success','data' => $aUpdate]); } /** * 更新文章二维码关联微信公众号素材 */ private function _updateArticleQrcode($aParam = []){ $iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id']; if(empty($iArticleId)){ return json_encode(['status' => 2,'msg' => 'article_id is empty']); } //查询是否添加素材 $aWhere = ['article_id' => $iArticleId,'is_delete' => 2]; $aMaterial = Db::name('ai_article_qrcode')->field('qrcode_url,media_id,media_url')->where($aWhere)->find(); if(empty($aMaterial)){ return json_encode(['status' => 3,'msg' => 'data is null']); } if(!empty($aMaterial['media_url'])){ return json_encode(['status' => 1,'msg' => 'success','data' => $aMaterial]); } //处理图片地址 $sImagePath = ROOT_PATH.'public/qrcode/'.$aMaterial['qrcode_url']; //调用微信新增素材接口上传 $aUploadResult = json_decode($this->uploadMaterial($sImagePath),true); $iStatus = empty($aUploadResult['status']) ? 0 : $aUploadResult['status']; $sMsg = empty($aUploadResult['msg']) ? 'Failed to upload materials' : $aUploadResult['msg']; if($iStatus != 1){ return json_encode(['status' => 4,'msg' => $sMsg]); } $aData = empty($aUploadResult['data']) ? [] : $aUploadResult['data']; if(empty($aData['url'])){ return json_encode(['status' => 5,'msg' => 'Material not obtained']); } //更新 $sMediaId = empty($aData['media_id']) ? [] : $aData['media_id']; $sMediaUrl = empty($aData['url']) ? [] : $aData['url']; $iTime = empty($aUploadResult['created_at']) ? time() : $aUploadResult['created_at']; $aUpdate = ['media_id' => $sMediaId,'media_url' => $sMediaUrl,'update_time' => $iTime]; $response = Db::name('ai_article_qrcode')->where('article_id',$iArticleId)->limit(1)->update($aUpdate); if($response === false){ return json_encode(['status' => 3,'msg' => 'update fail']); } return json_encode(['status' => 1,'msg' => 'success','data' => $aUpdate]); } /** * 更新通讯作者关联微信公众号素材 */ private function _updateArticleAuthor($aParam = []){ $sEmail = empty($aParam['email']) ? '' : $aParam['email']; //文章图片 $sIcon = empty($aParam['icon']) ? '' : $aParam['icon']; if(empty($sEmail) || empty($sIcon)){ return json_encode(['status' => 2,'msg' => 'email is empty']); } //查询是否添加素材 $aWhere = ['email' => $sEmail]; $aMaterial = Db::name('ai_article_author')->field('media_id,media_url')->where($aWhere)->find(); if(empty($aMaterial)){ return json_encode(['status' => 3,'msg' => 'data is null']); } if(!empty($aMaterial['media_url'])){ return json_encode(['status' => 1,'msg' => 'success','data' => $aMaterial]); } //处理图片地址 $sImagePath = ROOT_PATH.trim($sIcon,'/'); if(trim($sIcon) != '/static/img/userImg.f3d9bc3b.jpg'){//非默认头像 //调用微信新增素材接口上传 $aUploadResult = json_decode($this->uploadMaterial($sImagePath),true); $iStatus = empty($aUploadResult['status']) ? 0 : $aUploadResult['status']; $sMsg = empty($aUploadResult['msg']) ? 'Failed to upload materials' : $aUploadResult['msg']; if($iStatus != 1){ return json_encode(['status' => 4,'msg' => $sMsg]); } $aData = empty($aUploadResult['data']) ? [] : $aUploadResult['data']; if(empty($aData['url'])){ return json_encode(['status' => 5,'msg' => 'Material not obtained']); } } if(trim($sIcon) == '/static/img/userImg.f3d9bc3b.jpg'){//默认头像 $aData = $this->aLogo; } //更新 $sMediaId = empty($aData['media_id']) ? [] : $aData['media_id']; $sMediaUrl = empty($aData['url']) ? [] : $aData['url']; $iTime = empty($aUploadResult['created_at']) ? time() : $aUploadResult['created_at']; $aUpdate = ['media_id' => $sMediaId,'media_url' => $sMediaUrl,'update_time' => $iTime]; $response = Db::name('ai_article_author')->where('email',$sEmail)->limit(1)->update($aUpdate); if($response === false){ return json_encode(['status' => 3,'msg' => 'fail']); } return json_encode(['status' => 1,'msg' => 'success','data' => $aUpdate]); } /** * CURL 添加文章到草稿箱 * @param $sToken Token * @param $sPath 图片地址 */ public function addDraft($aParam = []){ //返回结果数组 $aResult = ['status' => 1,'msg' => 'Upload draft box successfully','data' => []]; //文章ID $iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id']; //模版ID $iTemplateId = empty($aParam['template_id']) ? '' : $aParam['template_id']; //生成模版内容 $sContent = empty($aParam['template_content']) ? '' : $aParam['template_content']; if(empty($iArticleId) || empty($sContent)){ return json_encode(['status' => 2,'msg' => 'article_id OR template_content is empty']); } //查询文章是否存在 $aWhere = ['article_id' => $iArticleId,'is_delete' => 2]; $aAiArticle = Db::name('ai_article')->field('title_chinese as title,author,journal_id')->where($aWhere)->find(); if(empty($aAiArticle)){ return json_encode(['status' => 3, 'msg' => 'The article does not exist']); } //查询该模版是否推送到微信公众号 $aWhere['template_id'] = $iTemplateId; $aWechatArticle = Db::name('ai_wechat_article')->where($aWhere)->find(); if(!empty($aWechatArticle)){ return json_encode(['status' => 4, 'msg' => 'Already uploaded to draft box']); } //查询文章封面 $aMaterial = Db::name('ai_article_material')->field('media_id as thumb_media_id')->where('article_id',$iArticleId)->find(); if(!empty($aMaterial)){ $aAiArticle += $aMaterial; } $aAiArticle['content'] = $sContent; unset($aAiArticle['journal_id']); //获取Token $sToken = $this->getAccessToken(); if (empty($sToken)) { return json_encode(['status' => 5, 'msg' => 'Failed to obtain access_token']); } //发送CURL请求 $sUrl = "https://api.weixin.qq.com/cgi-bin/draft/add?access_token={$sToken}"; //参数组装 $sJsonData = json_encode([ 'articles' => [$aAiArticle] ], JSON_UNESCAPED_UNICODE); $aCurlResult = $this->curlWechatApi($sUrl,$sJsonData); if($aCurlResult['status'] != 1){ return $aCurlResult; } $response = empty($aCurlResult['data']) ? [] : $aCurlResult['data']; //判断是否推送成功 if(empty($response['media_id'])){ return json_encode(['status' => 5,'msg' => "Unable to obtain the uploaded draft"]); } //判断文章类型 $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"]); } //是否打开评论,0不打开(默认),1打开 $need_open_comment = empty($aParam['need_open_comment']) ? 0 : $aParam['need_open_comment']; //是否粉丝才可评论,0所有人可评论(默认),1粉丝才可评论 $only_fans_can_comment = empty($aParam['only_fans_can_comment']) ? 0 : $aParam['only_fans_can_comment']; //插入文章表 $aParam = ['media_id' => $response['media_id'],'update_time' => time(),'template_id' => $iTemplateId,'template_content' => $aParam['template_content'],'article_id' => $iArticleId,'article_type' => $article_type,'need_open_comment' => $need_open_comment,'only_fans_can_comment' => $only_fans_can_comment,'template_id' => $iTemplateId,'create_time' => time()]; $result = Db::name('ai_wechat_article')->insert($aParam); if($result === false){ return json_encode(['status' => 8,'msg' => "Article data insertion failed"]); } return json_encode($aResult); } /** * CURL 获取草稿箱的文章 * @param $sToken Token * @param article_id int 文章ID */ public function getDraft($sMediaId = ''){ //返回结果数组 $aResult = ['status' => 1,'msg' => 'Successfully obtained draft','data' => []]; //获取参数 $aParam = $this->request->post(); $sMediaId = empty($sMediaId) ? $aParam['media_id'] : $sMediaId; if(empty($sMediaId)){ return json_encode(['status' => 2, 'msg' => 'Parameter: media_id cannot be empty']); } //查询关联文章信息 $aArticle = Db::name('ai_article')->field('media_id')->where(['media_id'=>$sMediaId])->find(); if(empty($aArticle)){ return json_encode(['status' => 3,'msg' => 'The article does not exist']); } //获取Token $sToken = $this->getAccessToken(); if (empty($sToken)) { return json_encode(['status' => 4, 'msg' => '获取access_token失败']); } //CURL请求 $sUrl = "https://api.weixin.qq.com/cgi-bin/draft/get?access_token={$sToken}"; //参数组装 $sJsonData = json_encode([ 'media_id' => $aArticle['media_id'] ], JSON_UNESCAPED_UNICODE); //发送CURL请求 $aCurlResult = $this->curlWechatApi($sUrl,$sJsonData); if($aCurlResult['status'] != 1){ return json_encode(['status' => 5,'msg' => 'Acquisition failed']); } $response = empty($aCurlResult['data']) ? [] : $aCurlResult['data']; $aResult['data'] = $response['news_item'] ?? []; return json_encode($aResult); } /** * CURL 发送请求到 微信公众号 * @param $messages 内容 * @param $model 模型类型 */ private function curlWechatApi($sUrl = '',$sJsonData = ''){ //发送CURL请求 $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $sUrl); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $sJsonData); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Content-Length: ' . strlen($sJsonData) ]); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); // 关闭SSL验证(生产环境建议开启) $response = curl_exec($curl); //请求失败 if (curl_errno($curl)){ curl_close($curl); return ['status' => 2,'msg' => "网络请求失败: " . curl_errno($curl)]; } curl_close($curl);//关闭请求 // 数据处理 $response = json_decode($response, true); if (!empty($response['errcode'])) { return ['status' => 3, 'msg' => '接口请求异常'.$response['errmsg'] ?? $response['errcode']]; } return ['status' => 1,'msg' => '接口请求成功','data' => $response]; } }