AI审稿功能开发
This commit is contained in:
@@ -4,7 +4,7 @@ namespace app\api\controller;
|
|||||||
|
|
||||||
use app\api\controller\Base;
|
use app\api\controller\Base;
|
||||||
use think\Db;
|
use think\Db;
|
||||||
|
use app\common\OpenAi;
|
||||||
/**
|
/**
|
||||||
* @title AI审核文章
|
* @title AI审核文章
|
||||||
* @description 对接OPENAI接口
|
* @description 对接OPENAI接口
|
||||||
@@ -12,69 +12,6 @@ use think\Db;
|
|||||||
class Aireview extends Base
|
class Aireview extends Base
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $sApiKey;
|
|
||||||
protected $proxy;
|
|
||||||
protected $sUrl;
|
|
||||||
protected $curl;
|
|
||||||
protected $sResponesData;
|
|
||||||
protected $sError;
|
|
||||||
protected $timeout = 60;
|
|
||||||
|
|
||||||
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 AI审核文章
|
* @title AI审核文章
|
||||||
* @param article_id 文章ID
|
* @param article_id 文章ID
|
||||||
@@ -83,82 +20,72 @@ class Aireview extends Base
|
|||||||
* @param model 接口模型
|
* @param model 接口模型
|
||||||
* @param stream 是否流式输出 true是false否
|
* @param stream 是否流式输出 true是false否
|
||||||
*/
|
*/
|
||||||
public function review(){
|
public function review(){
|
||||||
|
|
||||||
|
|
||||||
//获取参数
|
//获取参数
|
||||||
$aParam = $this->request->post();
|
$aParam = $this->request->post();
|
||||||
if(empty($aParam['article_id'])){
|
$iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id'];
|
||||||
exit(json_encode(array('status' => 2,'msg' => 'Please select an article' )));
|
if(empty($iArticleId)){
|
||||||
|
return json_encode(array('status' => 2,'msg' => 'Please select an article' ));
|
||||||
}
|
}
|
||||||
// if(empty($aParam['abstrart'])){
|
|
||||||
// exit(json_encode(array('status' => 2,'msg' => 'abstrart cannot be empty' )));
|
|
||||||
// }
|
|
||||||
// if(empty($aParam['keywords'])){
|
|
||||||
// exit(json_encode(array('status' => 2,'msg' => 'keywords cannot be empty' )));
|
|
||||||
// }
|
|
||||||
|
|
||||||
//查询文章
|
//查询文章
|
||||||
$aArticle = Db::table('t_article')->field('article_id,abstrart,keywords,journal_id')->where('article_id',$aParam['article_id'])->find();
|
$aWhere = ['article_id' => $aParam['article_id']];
|
||||||
|
$aArticle = Db::name('article')->field('article_id,abstrart,keywords,journal_id')->where('article_id',$aParam['article_id'])->find();
|
||||||
if(empty($aArticle)){
|
if(empty($aArticle)){
|
||||||
exit(json_encode(array('status' => 3,'msg' => 'No articles requiring review were found' )));
|
return json_encode(array('status' => 3,'msg' => 'No articles requiring review were found' ));
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取文章评测内容
|
//获取文章评测内容
|
||||||
$aAiReview = Db::table('t_article_ai_review')->field('article_id,content')->where('article_id',$aParam['article_id'])->find();
|
$aAiReview = Db::table('t_article_ai_review')->where('article_id',$aParam['article_id'])->find();
|
||||||
if(!empty($aAiReview)){
|
if(!empty($aAiReview)){
|
||||||
exit(json_encode(array('status' => 1,'msg' => 'AI has been reviewed','data' => $aAiReview)));
|
return json_encode(array('status' => 1,'msg' => 'AI has been reviewed','data' => $aAiReview));
|
||||||
}
|
}
|
||||||
|
|
||||||
$aParam['abstrart'] = empty($aParam['abstrart']) ? $aArticle['abstrart'] : $aParam['abstrart'];//简介
|
|
||||||
$aParam['keywords'] = empty($aParam['keywords']) ? $aArticle['keywords'] : $aParam['keywords'];//关键词
|
|
||||||
|
|
||||||
|
|
||||||
//根据期刊ID查询期刊信息
|
//根据期刊ID查询期刊信息
|
||||||
$aJournal = Db::table('t_journal')->field('zname')->where('journal_id',$aArticle['journal_id'])->find();
|
$aJournal = Db::table('t_journal')->field('zname')->where('journal_id',$aArticle['journal_id'])->find();
|
||||||
if(empty($aJournal)){
|
if(empty($aJournal)){
|
||||||
exit(json_encode(array('status' => 4,'msg' => 'This article is not associated with a journal' )));
|
return json_encode(array('status' => 4,'msg' => 'This article is not associated with a journal' ));
|
||||||
}
|
|
||||||
//组织参数
|
|
||||||
$sContent = '';
|
|
||||||
$sContent .= '摘要:'.$aParam['abstrart'];
|
|
||||||
$sContent .= '关键词:'.$aParam['keywords'];
|
|
||||||
$sContent .= '以上这篇文章是否符合'.$aJournal['zname'].'?是否具有科学前沿性和创新性?请返回中文解释';
|
|
||||||
$messages = [
|
|
||||||
[
|
|
||||||
'role' => 'user', //角色:platform:平台;developer:开发者;user:用户;guideline:模型规范“指南”部分:连接:https://model-spec.openai.com/2025-02-12.html#chain_of_command
|
|
||||||
'content' => $sContent
|
|
||||||
]
|
|
||||||
];
|
|
||||||
//请求接口
|
|
||||||
$sModel = $aParam['api_model'] ?? 'gpt-4o';
|
|
||||||
$result = $this->curlOpenAI($messages,$sModel);
|
|
||||||
if($result == FALSE){
|
|
||||||
exit(json_encode(array('status' => 4,'msg' => 'Interface request failed'.$this->sError)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//查询文章内容
|
||||||
|
$aWhere['type'] = 0;
|
||||||
|
$aWhere['content'] = ['<>',''];
|
||||||
|
$aWhere['state'] = 0;
|
||||||
|
$aArticleMain = Db::table('t_article_main')->where($aWhere)->column('content');
|
||||||
|
|
||||||
|
//获取提问AI的内容
|
||||||
|
$aSearch = [];
|
||||||
|
$aSearch['{abstrart}'] = empty($aArticle['abstrart']) ? '' : $aArticle['abstrart'];//简介
|
||||||
|
$aSearch['{keywords}'] = empty($aArticle['keywords']) ? '' : $aArticle['keywords'];//关键词
|
||||||
|
$aSearch['{content}'] = empty($aArticleMain) ? '' : strip_tags(implode('', array_unique($aArticleMain)));//文章内容
|
||||||
|
$aSearch['{journal_name}'] = empty($aJournal['zname']) ? '' : $aJournal['zname'];//期刊名
|
||||||
|
//获取问答内容
|
||||||
|
$oOpenAi = new OpenAi;
|
||||||
|
$aMessage = $oOpenAi->buildReviewArticlePrompt($aSearch);
|
||||||
|
if(empty($aMessage)){
|
||||||
|
return json_encode(['status' => 5,'msg' => 'AI Q&A content not obtained']);
|
||||||
|
}
|
||||||
|
|
||||||
|
//请求OPENAI接口
|
||||||
|
$aParam = ['messages' => $aMessage,'model' => empty($aParam['api_model']) ? 'gpt-4.1' : $aParam['api_model']];
|
||||||
|
$aResult = json_decode($oOpenAi->curlOpenAI($aParam),true);
|
||||||
|
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
|
||||||
|
if($iStatus != 1){
|
||||||
|
return json_encode($aResult);
|
||||||
|
}
|
||||||
//处理返回信息
|
//处理返回信息
|
||||||
$data = $this->sResponesData;
|
$aData = empty($aResult['data']) ? [] : $aResult['data'];
|
||||||
if(!is_object($data)){
|
if(empty($aData)){
|
||||||
exit(json_encode(array('status' => 5,'msg' => 'There is a misunderstanding in the data returned by the interface')));
|
return json_encode(['status' => 6,'msg' => 'OPENAI returns empty content']);
|
||||||
}
|
}
|
||||||
$data = object_to_array($data);
|
if(empty($aData)){
|
||||||
$aChoices = $data['choices'] ?? [];
|
return json_encode(array('status' => 10,'msg' => 'OPENAI did not return data'));
|
||||||
if(empty($aChoices)){
|
|
||||||
exit(json_encode(array('status' => 6,'msg' => 'OPENAI did not return data')));
|
|
||||||
}
|
}
|
||||||
$aChoicesInfo = $aChoices[0] ?? [];
|
|
||||||
$aMessage = $aChoicesInfo['message'] ?? [];
|
|
||||||
if(empty($aMessage['content'])){
|
|
||||||
exit(json_encode(array('status' => 7,'msg' => 'OPENAI returns empty data')));
|
|
||||||
}
|
|
||||||
|
|
||||||
//执行数据入库
|
//执行数据入库
|
||||||
$param = [];
|
$aData['article_id'] = $iArticleId;
|
||||||
$param['content'] = addslashes($aMessage['content']);
|
$aResult = $this->addAiReview($aData);
|
||||||
$param['article_id'] = $aParam['article_id'];
|
return json_encode($aResult);
|
||||||
$aResult = $this->addAiReview($param);
|
|
||||||
exit(json_encode($aResult));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -171,25 +98,40 @@ class Aireview extends Base
|
|||||||
protected function addAiReview($aParam = array()){
|
protected function addAiReview($aParam = array()){
|
||||||
|
|
||||||
//返回数组
|
//返回数组
|
||||||
$aResult = ['status' => 1,'msg' => 'AI review successful','data' => $aParam];
|
$aResult = ['status' => 1,'msg' => 'AI review successful'];
|
||||||
//必填参数验证
|
//必填参数验证
|
||||||
$aFields = ['article_id','content'];
|
$aFields = ['journal_scope','attribute','contradiction','unreasonable','ethics','academic','conclusion','fund_number','hotspot','submit_direction','overall_evaluation','article_id','references_past_three','references_past_five','references_ratio_JCR1','references_ratio_JCR2','registration_assessment','cite_rate'];
|
||||||
$bStatus = true;
|
$aInsert = ['create_time' => date('Y-m-d H:i:s')];
|
||||||
foreach($aFields as $val){
|
foreach($aFields as $val){
|
||||||
if(empty($aParam[$val])){
|
$aValue = empty($aParam[$val]) ? [] : $aParam[$val];
|
||||||
$aResult = ['status' => 2,'msg' => $val.'cannot be empty'];
|
if(!isset($aValue)){
|
||||||
$bStatus = false;
|
continue;
|
||||||
break;
|
}
|
||||||
|
if(is_array($aValue)){
|
||||||
|
foreach ($aValue as $key => $value) {
|
||||||
|
$sField = $val.'_'.$key;
|
||||||
|
$aInsert[$sField] = empty($value) ? '' : addslashes($value);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$aInsert[$val] = empty($aValue) ? '' : addslashes($aValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if($bStatus == false){
|
|
||||||
return $aResult;
|
|
||||||
}
|
|
||||||
//执行入库
|
//执行入库
|
||||||
$aParam['create_time'] = date('Y-m-d H:i:s');
|
if(empty($aInsert['article_id'])){
|
||||||
if(!Db::name('article_ai_review')->insert($aParam)){
|
return ['status' => 2,'msg' => 'Please select the article to be reviewed'];
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断是否生成
|
||||||
|
$aAiReview = Db::table('t_article_ai_review')->where('article_id',$aInsert['article_id'])->find();
|
||||||
|
if(!empty($aAiReview)){
|
||||||
|
return json_encode(array('status' => 1,'msg' => 'AI has been reviewed','data' => $aAiReview));
|
||||||
|
}
|
||||||
|
|
||||||
|
$aInsert['content'] = $aInsert['article_id'];
|
||||||
|
if(!Db::name('article_ai_review')->insert($aInsert)){
|
||||||
$aResult = ['status' => 2,'msg' => 'Failed to add AI audit content'];
|
$aResult = ['status' => 2,'msg' => 'Failed to add AI audit content'];
|
||||||
}
|
}
|
||||||
|
$aResult['data'] = $aInsert;
|
||||||
return $aResult;
|
return $aResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,42 +144,16 @@ class Aireview extends Base
|
|||||||
//获取参数
|
//获取参数
|
||||||
$aParam = $this->request->post();
|
$aParam = $this->request->post();
|
||||||
if(empty($aParam['article_id'])){
|
if(empty($aParam['article_id'])){
|
||||||
exit(json_encode(array('status' => 2,'msg' => 'Please select an article' )));
|
return json_encode(array('status' => 2,'msg' => 'Please select an article' ));
|
||||||
}
|
}
|
||||||
|
|
||||||
//查询文章
|
//查询文章
|
||||||
$aArticle = Db::table('t_article')->field('article_id')->where('article_id',$aParam['article_id'])->find();
|
$aArticle = Db::table('t_article')->field('article_id')->where('article_id',$aParam['article_id'])->find();
|
||||||
if(empty($aArticle)){
|
if(empty($aArticle)){
|
||||||
exit(json_encode(array('status' => 3,'msg' => 'No articles requiring review were found' )));
|
return json_encode(array('status' => 3,'msg' => 'No articles requiring review were found' ));
|
||||||
}
|
}
|
||||||
//查询文章审核内容
|
//查询文章审核内容
|
||||||
$aAiReview = Db::table('t_article_ai_review')->field('content')->where('article_id',$aParam['article_id'])->find();
|
$aAiReview = Db::table('t_article_ai_review')->where('article_id',$aParam['article_id'])->find();
|
||||||
exit(json_encode(array('status' => 1,'msg' => 'Successfully obtained article review content','data' => $aAiReview)));
|
return json_encode(array('status' => 1,'msg' => 'Successfully obtained article review content','data' => $aAiReview));
|
||||||
}
|
|
||||||
|
|
||||||
public function test(){
|
|
||||||
//获取参数
|
|
||||||
$aParam = $this->request->post();
|
|
||||||
if(empty($aParam['content'])){
|
|
||||||
exit(json_encode(array('status' => 2,'msg' => '请输入需要验证的字符串' )));
|
|
||||||
}
|
|
||||||
if(!$this->checkMinChars($aParam['content'],200)){
|
|
||||||
exit(json_encode(array('status' => 3,'msg' => '字符串长度未满足配置' )));
|
|
||||||
}
|
|
||||||
exit(json_encode(array('status' => 1,'msg' => '验证成功' )));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 验证字符长度
|
|
||||||
* @param $str 字符串
|
|
||||||
* @param $num 字符长度
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function checkMinChars($str = '',$num = 200) {
|
|
||||||
// 过滤非汉字和英文的字符(保留汉字、大小写字母)
|
|
||||||
$filteredStr = preg_replace('/[^\x{4e00}-\x{9fa5}a-zA-Z0-9]/u', '', $str);
|
|
||||||
|
|
||||||
// 计算有效字符总数(汉字按1个字符计,英文和数字同理)
|
|
||||||
$total = mb_strlen($filteredStr, 'UTF-8');
|
|
||||||
return $total >= $num;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user