Merge remote-tracking branch 'origin/master'

This commit is contained in:
wangjinlei
2025-04-01 13:20:51 +08:00
2 changed files with 284 additions and 2 deletions

View File

@@ -0,0 +1,243 @@
<?php
namespace app\api\controller;
use app\api\controller\Base;
use think\Db;
/**
* @title AI审核文章
* @description 对接OPENAI接口
*/
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审核文章
* @param article_id 文章ID
* @param abstrart 摘要
* @param keywords 关键词
* @param model 接口模型
* @param stream 是否流式输出 true是false否
*/
public function review(){
//获取参数
$aParam = $this->request->post();
if(empty($aParam['article_id'])){
exit(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();
if(empty($aArticle)){
exit(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();
if(!empty($aAiReview)){
exit(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查询期刊信息
$aJournal = Db::table('t_journal')->field('zname')->where('journal_id',$aArticle['journal_id'])->find();
if(empty($aJournal)){
exit(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)));
}
//处理返回信息
$data = $this->sResponesData;
if(!is_object($data)){
exit(json_encode(array('status' => 5,'msg' => 'There is a misunderstanding in the data returned by the interface')));
}
$data = object_to_array($data);
$aChoices = $data['choices'] ?? [];
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 = [];
$param['content'] = addslashes($aMessage['content']);
$param['article_id'] = $aParam['article_id'];
$aResult = $this->addAiReview($param);
exit(json_encode($aResult));
}
/**
* @title AI审核内容入库
* @param article_id 文章ID
* @param content 内容
*/
protected function addAiReview($aParam = array()){
//返回数组
$aResult = ['status' => 1,'msg' => 'AI review successful','data' => $aParam];
//必填参数验证
$aFields = ['article_id','content'];
$bStatus = true;
foreach($aFields as $val){
if(empty($aParam[$val])){
$aResult = ['status' => 2,'msg' => $val.'cannot be empty'];
$bStatus = false;
break;
}
}
if($bStatus == false){
return $aResult;
}
//执行入库
$aParam['create_time'] = date('Y-m-d H:i:s');
if(!Db::name('article_ai_review')->insert($aParam)){
$aResult = ['status' => 2,'msg' => 'Failed to add AI audit content'];
}
return $aResult;
}
/**
* @title 文章AI审核内容查询
* @param article_id 文章ID
*/
public function get(){
//获取参数
$aParam = $this->request->post();
if(empty($aParam['article_id'])){
exit(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();
if(empty($aArticle)){
exit(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();
exit(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;
}
}

View File

@@ -106,7 +106,6 @@ class Article extends Base
->limit($limit_start, $data['pageSize'])->select();
$count = $this->article_obj->where($where)->count();
foreach ($res as $key => $val) {
//查询建议转投详情
$transfer_info = $this->article_transfer_obj
@@ -346,6 +345,15 @@ class Article extends Base
->order('t_article.article_id desc')
->limit($limit_start, $data['pageSize'])
->select();
//查询AI审核内容 chengxiaoling 20250328 start
$aAiReview = array();
if(!empty($res)){
$aArticleId = array_column($res, 'article_id');
$aAiReview = Db::table('t_article_ai_review')->field('article_id,content')->whereIn('article_id',$aArticleId)->column('article_id,content');
}
//查询AI审核内容 chengxiaoling 20250328 end
//增加审稿意见信息
foreach ($res as $key => $val) {
$cache_review = $this->article_reviewer_obj
@@ -383,6 +391,10 @@ class Article extends Base
$file_frag[$v['type_name']][] = $v;
}
$res[$key]['file'] = $file_frag;
//返回AI审核内容 chengxiaoling 20250328 start
$res[$key]['ai_review'] = empty($aAiReview[$val['article_id']]) ? '' : $aAiReview[$val['article_id']];
//返回AI审核内容 chengxiaoling 20250328 end
}
//添加国家信息
@@ -616,6 +628,13 @@ class Article extends Base
$user_res = $this->user_obj->where(['account' => $username])->find();
$article_old_info = $this->article_obj->where('article_id', $data['articleId'])->find();
//判断简介字符串长度是否不低于200 chengxiaoling 20250327 start
if(!$this->checkMinChars($data['abstrart'],200)){
return jsonError("The abstract should not be less than 200 Chinese characters or English words!");
}
//判断简介字符串长度是否不低于200 chengxiaoling 20250327 end
Db::startTrans();
//更新文章信息
@@ -2485,7 +2504,6 @@ class Article extends Base
//接受参数,查询信息
$data = $this->request->post();
$user_res = $this->user_obj->where('account', $data['username'])->find();
//确定用户是否属于黑名单
@@ -2919,6 +2937,12 @@ class Article extends Base
if (!$rule->check($data)) {
return jsonError($rule->getError());
}
//判断简介字符串长度是否不低于200 chengxiaoling 20250327 start
if(!$this->checkMinChars($data['abstrart'],200)){
return jsonError("The abstract should not be less than 200 Chinese characters or English words!");
}
//判断简介字符串长度是否不低于200 chengxiaoling 20250327 end
$user_info = $this->user_obj->where('user_id', $data['user_id'])->find();
$journal_info = $this->journal_obj->where('journal_id', $data['journal'])->find();
@@ -4257,4 +4281,19 @@ class Article extends Base
$this->user_msg_obj->where("user_id", $data['editor_id'])->update(["state" => 1]);
return jsonSuccess([]);
}
/**
* 验证字符长度
* @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;
}
}