Files
tougao/application/common/OpenAi.php
2025-07-22 16:40:27 +08:00

599 lines
29 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\common;
use think\Cache;
use think\Db;
use think\Queue;
use app\common\Article;
use app\common\QueueRedis;
class OpenAi
{
protected $sApiKey = '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';
protected $proxy = '';
protected $sUrl = 'http://chat.taimed.cn/v1/chat/completions';//'https://api.openai.com/v1/chat/completions';
protected $curl;
protected $sResponesData;
protected $sError;
protected $timeout = 300;
//JAVA接口
protected $sJavaUrl = "http://ts.tmrjournals.com/";
//官网文件地址
protected $sFileUrl = "https://submission.tmrjournals.com/public/";
//官网接口地址
protected $sTmrUrl = "http://journalapi.tmrjournals.com/public/index.php";//"http://zmzm.journal.dev.com/";//;
protected $aArticleImportantPrompt = [
"journal_scope" => [
'system' => '你是一位资深的学术评审专家,负责严谨、客观地评估学术文章。
请针对问题提供客观、专业的评估,并给出简要的理由。请返回中文解释!返回格式必须严格遵循以下JSON结构{
"journal_scope": {
"assessment": "是/否",
"explanation": "请详细解释说明"
}
}',
"criteria" => "根据文章的标题:{title};摘要:{abstrart}以及期刊范围:{scope}来判断文章是否符合目标期刊{journal_name}"
],
"attribute" => [
'system' => '你是一位资深的学术评审专家,负责严谨、客观地评估学术文章。
请针对问题提供客观、专业的评估,并给出简要的理由。请返回中文解释!返回格式必须严格遵循以下JSON结构{
"attribute": {
"assessment": "是/否",
"explanation": "请总结归纳分析"
}
}',
"criteria" => "请结合以下几点【研究内容的原创性论文中的研究内容是否与已有的研究重复是否在同样的领域提出了类似的结论但在方法或结果上有所创新如果有作者是否清楚地解释了如何与之前的研究不同或者如何在原有基础上进行扩展或改进如果是综述文章汇总并综合最新的研究成果尤其是近几年内的重要发现展示领域内最新的进展成果。作者可以识别出未被充分讨论的问题或提出新的研究问题而不是简单文献堆砌。文章中的图表创新能否将信息的清晰呈现方便读者理解复杂研究问题。论文方法创新性评估要点是否采用了新的实验模型或创新的实验设计能有效解决当前研究中的难点或空白是否有合理的对照组和多组实验设计确保研究结果的可靠性是否使用了当前前沿的技术如高通量测序、CRISPR基因编辑等提高了实验精度或数据分析能力是否结合了跨学科的方法如生物信息学、人工智能等是否应用了多种验证手段或统计方法确保结果的可信度是否通过细胞实验、动物模型等多重验证确保实验结果的可靠性结论与数据的创新性研究结论是否提出了新观点或新见解是否提供了新的实验数据或观察结果能够突破当前的研究局限例如发现了新的生物标志物或对已知生物通路的作用机制提供了全新的解释】评估文章内容{content}是否有科学前沿性和创新性?"
]
//,
// "contradiction" => [
// 'system' => '你是一位资深的学术评审专家,负责严谨、客观地评估学术文章。
// 请针对问题提供客观、专业的评估,并给出简要的理由。请返回中文解释!返回格式必须严格遵循以下JSON结构{
// "contradiction": {
// "assessment": "是/否",
// "explanation": "请引用具体段落说明矛盾之处"
// }
// }',
// "criteria" => "根据文章内容{content}分析是否前后矛盾或存在逻辑不一致的问题?"
// ],
// "unreasonable" => [
// 'system' => '你是一位资深的学术评审专家,负责严谨、客观地评估学术文章。
// 请针对问题提供客观、专业的评估,并给出简要的理由。请返回中文解释!返回格式必须严格遵循以下JSON结构{
// "unreasonable": {
// "assessment": "是/否",
// "explanation": "包括实验设计、数据分析、结论推导等方面的问题"
// }
// }',
// "criteria" => "根据文章内容{content}分析是否有明显的不合理之处?"
// ]
];
//定义redis连接
private $redis;
private $oQueueRedis;
public function __construct()
{
// 初始化 Redis 连接
$config = \think\Config::get('queue');
$this->redis = new \Redis();
$this->redis->connect($config['host'], $config['port']);
if (!empty($config['password'])) {
$this->redis->auth($config['password']);
}
$this->redis->select($config['select']);
$this->oQueueRedis = QueueRedis::getInstance();
}
/**
* 构建公微模版-处理提示词【默认】
*/
public function buildDefaultPrompt($aSearch = []){
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '
你是一位专业的医学学术翻译与分析专家,擅长将复杂的医学研究论文转化为适合微信公众号推送的专业科普内容。请根据提供的医学论文信息,请返回中文解释!返回格式必须严格遵循以下JSON结构';
$aQuestion = [
"covered" => "[列出文章涵盖的学科及研究方法总字数不超过100字学科和方法之间用逗号分隔例如肿瘤学,分子生物学,基因组测序,生物信息学分析]",
"digest" => "[学术规范翻译并提炼摘要强调逻辑性、科学术语准确性和表达严谨性采用段落形式总字数不超过500字]",
"research_background" => "[提炼研究背景采用连贯的段落形式总字数超过200字]",
"discussion_results" => "[针对文章简单总结讨论和结果采用连贯的段落形式总字数超过450字]",
"research_method" => "[总结文章的研究方法采用连贯的段落形式总字数超过300字]",
"prospect" => "[针对稿件内容进行展望撰写,采用连贯的段落形式]",
"highlights" => "[总结归纳亮点至少3点每点用分号分隔]",
"title_chinese" => "[将标题翻译成中文:内容需自然流畅、口语化、连贯性、学术性]"
// ,
// "content" => "将内容翻译成中文,需自然流畅、口语化、连贯性、学术性,保留原文的章节结构和图表编号"
];
//问题处理
$aMessage = [];
foreach($aQuestion as $key => $value){
//修改当前内容
$sInfo = json_encode([$key => $value],JSON_UNESCAPED_UNICODE);
$sSysMessagePromptInfo = $sSysMessagePrompt.$sInfo;
if($key == "title_chinese"){
$sUserPrompt = '{#title_chinese#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
}
if($key == "content"){
$sUserPrompt = '{#content#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
}
if(!in_array($key,["title_chinese","content"])){
$sUserPrompt = '标题:{#title_chinese#} 摘要: {#abstract#} 内容: {#content#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
}
$aMessage[] = [
['role' => 'system', 'content' => $sSysMessagePromptInfo],
['role' => 'user', 'content' => $sUserPrompt]
];
}
return $aMessage;
}
/**
* 构建公微模版-处理提示词【Review】
*/
public function buildReviewPrompt($aSearch = []){
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '
你是一位专业的医学学术翻译与分析专家,擅长将复杂的医学研究论文转化为适合微信公众号推送的专业科普内容。请根据提供的医学论文信息,按照以下严格格式生成结构化输出[中文]:';
$aQuestion = [
"covered" => "列出文章涵盖的学科及研究方法总字数不超过100字学科和方法之间用逗号分隔例如肿瘤学,分子生物学,基因组测序,生物信息学分析",
"overview" => "按照学术规范翻译并提炼文章概述整体内容应大于1200字其中应包含文章背景不少于400字其他内容提炼更强调逻辑性、科学术语准确性和表达的严谨性注意内容不要有严重重复采用连贯的段落形式",
"summary" => "针对文章结论生成一个简单总结内容不要和文章概述重复字数150以内",
"title_chinese" => "将标题翻译成中文:内容需自然流畅、口语化、连贯性、学术性"
// ,
// "content" => "将内容翻译成中文,需自然流畅、口语化、连贯性、学术性,保留原文的章节结构和图表编号"
];
//问题处理
$aMessage = [];
foreach($aQuestion as $key => $value){
$sInfo = json_encode([$key => $value],JSON_UNESCAPED_UNICODE);
$sSysMessagePromptInfo = $sSysMessagePrompt.$sInfo;
if($key == "title_chinese"){
$sUserPrompt = '{#title_chinese#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
}
if($key == "content"){
$sUserPrompt = '{#content#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
}
if(!in_array($key,["title_chinese","content"])){
$sUserPrompt = '标题:{#title_chinese#} 摘要: {#abstract#} 内容: {#content#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
}
$aMessage[] = [
['role' => 'system', 'content' => $sSysMessagePromptInfo],
['role' => 'user', 'content' => $sUserPrompt]
];
}
return $aMessage;
}
/**
* 构建AI翻译-处理提示词
*/
public function buildTranslatePrompt($aSearch = []){
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '你是一位专业的医学翻译专家,请将用户提供的内容准确、流畅地翻译成中文。翻译需自然流畅、口语化、连贯性、学术性,保留原文的专业术语和逻辑结构';
$sUserPrompt = '"将以下内容翻译为中文,仅返回翻译结果,不要解释:\n {#content#}"';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
return [
['role' => 'user', 'content' => $sUserPrompt]
];
}
/**
* 构建AI审稿-处理提示词【重要的多次请求问题初始化】
*/
public function buildReviewPromptImportant($aSearch = [],$aValue = []) {
//必填验证
if(empty($aSearch) || empty($aValue)){
return [];
}
//组装问题
$sUserPrompt = empty($aValue['criteria']) ? '' : $aValue['criteria'];
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
$aMessage = [
['role' => 'system', 'content' => $aValue['system']],
['role' => 'user', 'content' => $sUserPrompt]
];
//模型版本
$model = empty($aSearch['model']) ? 'gpt-4.1' : $aSearch['model'];
$aMessage = [
'model' => $model,
'messages' => $aMessage,
'temperature' => 0.2,// 降低随机性0-10为最确定
];
return $aMessage;
}
/**
* 构建AI审稿-处理提示词【非重要的一次请求回答】
*/
public function buildReviewPromptUnimportant($aSearch = [])
{
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '
你是一位资深的学术评审专家,负责严谨、客观地评估学术文章。请根据提供的信息,对以下问题进行专业评审
```json
{
"ethics": "根据文章内容分析是否存在伦理号缺失或明显伦理问题?",
"academic": "根据文章内容分析是否存在学术不端问题【抄袭、数据作假、图片伪造等】?",
"conclusion": "根据文章内容判断文章结论科学性和可靠性?",
"fund_number":"根据文章内容分析有无基金号?请详细说明",
"hotspot":"根据文章内容分析有哪些符合目标期刊当下的热点话题",
"submit_direction":"根据文章内容总结文章送审方向",
"references_num":"根据文章内容统计文章参考文献的数量",
"references_past_three":"统计文章内容里近3年的参考文献的数量及所占比例",
"references_past_five":"统计文章内容里近5年的参考文献的数量及所占比例",
"references_ratio_JCR1":"根据2024JCR最新分区分析文章内容里的文献判断属于JCR1还是JCR2,统计属于JCR 1区的数量及比例",
"references_ratio_JCR2":"根据2024JCR最新分区评估文章内容里的文献判断属于JCR1还是JCR2,统计属于JCR 2区的数量及比例",
"registration_assessment":"根据文章内容分析是否存在临床注册号和知情同意书?解释说明",
"cite_rate" => "根据文章内容分析文章发表后被引用的概率"
}请针对每个问题提供客观、专业的评估,并给出简要的理由。请返回中文解释!返回格式必须严格遵循以下 JSON 结构:{
"ethics": {
"assessment": "是/否",
"explanation": "请针对文章伦理号及伦理进行分析及说明"
},
"academic": {
"assessment": "是/否",
"explanation": "请进行相似度分析并指出具体可疑部分"
},
"conclusion": {
"assessment": "是/否",
"explanation": "是否基于充分证据得出"
},
"fund_number": "",
"hotspot": "",
"submit_direction": "",
"references_num":"",
"references_past_three": "",
"references_past_five": "",
"references_ratio_JCR1": "",
"references_ratio_JCR2": "",
"registration_assessment":"",
"cite_rate" => ""
}';
$sUserPrompt = '文章内容{content};目标期刊{journal_name}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
return [
['role' => 'system', 'content' => $sSysMessagePrompt], ['role' => 'user', 'content' => $sUserPrompt]
];
}
/**
* CURL 发送请求到 OpenAI【单独】
* @param $messages 内容
* @param $model 模型类型
*/
public function curlOpenAI($aParam = []){
//询问AI信息
$aMessage = empty($aParam['messages']) ? [] : $aParam['messages'];
if(empty($aMessage)){
return json_encode(['status' => 2,'msg' => 'AI Q&A content not obtained']);
}
//模型版本
$model = empty($aParam['model']) ? 'gpt-4.1' : $aParam['model'];
//接口地址
$sUrl = $this->sUrl;
//组装数据
$data = [
'model' => $model,
'messages' => $aMessage,
'temperature' => 0.2,// 降低随机性0-10为最确定
];
$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 json_encode(['status' => 3,'OPENAI Error:'.$this->sError]);
}
$aResult = json_decode($result,true);
//处理返回信息
$aData = empty($aResult['choices']) ? [] : $aResult['choices'];
if(empty($aData)){
return json_encode(['status' => 5,'msg' => 'OPENAI returns empty content']);
}
$aData = empty($aData[0]) ? [] : $aData[0];
if(empty($aData)){
return json_encode(array('status' => 6,'msg' => 'OPENAI did not return data'));
}
$aData = empty($aData['message']) ? [] : $aData['message'];
$aData = empty($aData['content']) ? [] : $aData['content'];
if(empty($aData)){
return json_encode(array('status' => 7,'msg' => 'OPENAI did not return data'));
}
//数据转换
$aData = $this->extractAndParse($aData);
$aContent = empty($aData['data']) ? [] : $aData['data'];
$sMsg = empty($aData['msg']) ? 'OPENAI did not return data' : $aData['msg'];
if(empty($aContent)){
return json_encode(array('status' => 8,'msg' => $sMsg));
}
curl_close($this->curl);
return json_encode(['status' => 1,'msg' => 'success','data' => $aContent]);
}
/**
* CURL 发送请求到 OpenAI【流式】
* @param $messages 内容
* @param $model 模型类型
*/
public function curlOpenAIStream($aParam = []){
//询问AI信息
$aMessage = empty($aParam['messages']) ? [] : $aParam['messages'];
if(empty($aMessage)){
return json_encode(['status' => 2,'msg' => 'AI Q&A content not obtained']);
}
//模型
$model = empty($aParam['model']) ? 'gpt-4' : $aParam['model'];
//超时设置
$timeout = empty($aParam['timeout']) ? 300 : $aParam['timeout'];
//接口地址
$sUrl = empty($aParam['url']) ? $this->sUrl : $aParam['url'];
//组装数据
$data = [
'model' => $model,
'messages' => $aMessage,
'temperature' => 0.2,// 降低随机性0-10为最确定
'stream' => true // 关键:启用流式传输,避免超时
];
// Curl通用配置
$this->curl = curl_init();
curl_setopt($this->curl, CURLOPT_URL, $this->sUrl);
curl_setopt($this->curl, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->sApiKey
]);
// 代理与SSL配置根据你的服务器环境调整
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);
curl_setopt($this->curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($this->curl, CURLOPT_TIMEOUT, $timeout); // CURL超时
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, 30); // 连接超时
// === 5. 流式响应处理(核心避免超时) ===
$streamContent = ''; // 累积流式返回的内容
// 回调函数:每收到一块数据就处理并保存,避免整段等待
curl_setopt($this->curl, CURLOPT_WRITEFUNCTION, function ($curl, $data) use (&$streamContent) {
$streamContent .= $data;
return strlen($data); // 必须返回数据长度否则CURL会中断
});
//执行请求
$result = curl_exec($this->curl);
//获取错误信息
$curlErrno = curl_errno($this->curl);
//获取Code码
$httpCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
//关闭连接
curl_close($this->curl);
//错误处理
if (!empty($curlErrno)) {
// 超时但已有部分数据:保存进度,下次从该块重试
if ($curlErrno == CURLE_OPERATION_TIMEDOUT && !empty($streamContent)) {
return json_encode([
'status' => 3,
'msg' => "处理超时,已保存进度",
]);
}
// 其他错误(如网络问题)
return json_encode([
'status' => 4,
'msg' => "OPENAI Error:".curl_error($this->curl)
]);
}
//处理流式结果
$sStreamResponse = $this->parseMedicalStreamResponse($streamContent);
return json_encode(['status' => 1,'msg' => 'success','data' => $sStreamResponse]);
}
/**
* 解析流式响应
*/
private function parseMedicalStreamResponse($streamContent){
$fullContent = '';
$lines = explode("\n", $streamContent);
foreach ($lines as $line) {
$line = trim($line);
if (strpos($line, 'data: ') === 0 && $line !== 'data: [DONE]') {
$jsonStr = substr($line, 6); // 去掉"data: "前缀
$jsonData = json_decode($jsonStr, true);
$fullContent .= $jsonData['choices'][0]['delta']['content'] ?? '';
}
}
return $fullContent;
}
/**
* 微信公众号-生成公微内容(CURL)
*/
public function createWechatContent($aParam = []){
//主键ID
$iId = empty($aParam['redis_id']) ? 0 : $aParam['redis_id'];
if(empty($iId)){
return json_encode(['status' => 2, 'msg' => 'Please select an article']);
}
//提问信息
$aMessage = empty($aParam['messages']) ? [] : $aParam['messages'];
if (empty($aMessage)) {
return json_encode(['status' => 2, 'msg' => 'AI Q&A content not obtained']);
}
//记录处理开始
$iNum = count($aMessage);
$sRedisKey = 'ai_create_article_'.$iId;
$this->oQueueRedis->recordProcessingStart($sRedisKey,$iNum);
//定义空数组
$aChunkResult = $aFail = [];
foreach ($aMessage as $key => $value) {
$aParam['messages'] = $value;
$aParam['chunkIndex'] = $key;
$aParam['count_num'] = $iNum;
Queue::push('app\api\job\createFieldForQueue@fire', $aParam, 'createFieldForQueue');
}
return json_encode(['status' => 1, 'msg' => 'createFieldForQueue success']);
}
/**
* 微信公众号-生成内容队列形式
*/
public function createFieldForQueue($aParam = []){
//主键ID
$iId = empty($aParam['redis_id']) ? 0 : $aParam['redis_id'];
if(empty($iId)){
return json_encode(['status' => 2, 'msg' => 'Please select an article']);
}
//提问信息
$aMessage = empty($aParam['messages']) ? [] : $aParam['messages'];
if (empty($aMessage)) {
return json_encode(['status' => 2, 'msg' => 'AI Q&A content not obtained']);
}
//最大执行数
$iMaxNum = empty($aParam['count_num']) ? 0 : $aParam['count_num'];
//请求OPENAI
$aResult = $this->curlOpenAIStream($aParam);
//更新处理进度
$iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex'];
$sRedisKey = 'ai_create_article_'.$iId;
$iProgress = $this->oQueueRedis->updateProcessingProgress($sRedisKey,$iIndex + 1);
//保存内容
$sRedisKey = 'ai_create_article_progress_'.$iId;
$this->oQueueRedis->saveChunkProgress($sRedisKey, $iIndex,$aResult);
//更新入库
$aReturnData = json_decode($aResult,true);
$aDataInfo =empty($aReturnData['data']) ? [] : $aReturnData['data'];
$aData = empty($aDataInfo) ? [] : $this->extractAndParse($aDataInfo);
$aData = empty($aData['data']) ? [] : $aData['data'];
if(!empty($aData)){//更新AI审稿记录表
if($iProgress >= 100){
$aData['is_generate'] = 1;
}
$aData['article_id'] = $iId;
$this->updateAiContent($aData);
}
return $aResult;
}
/**
* 微信公众号-更新AI生成内容
*/
private function updateAiContent($aParam = []){
//文章ID
$iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select the article to be modified']);
}
//更新生成状态
$oArticle = new Article;
$aResult = json_decode($oArticle->updateAiArticle($aParam),true);
$iStatus = empty($aResult['status']) ? 0 : $aResult['status'];
$sMsg = empty($aResult['msg']) ? '更新状态失败' : $aResult['msg'];
//是否生成
$is_generate = empty($aParam['is_generate']) ? 2 : $aParam['is_generate'];
//内容生成完成推送上传素材队列
if($is_generate == 1){
if($iStatus == 1){
//四小时后推送上传素材并推送草稿箱
$iDelaySeconds = 4 * 3600; // 4小时的秒数
Queue::later($iDelaySeconds,'app\api\job\WechatMaterial@fire', ['article_id' => $iArticleId], 'WechatMaterial');
$sMsg = '文章AI内容生成成功';
}else{
$iStatus = 2;
}
//插入日志记录
$oMaterial = new Material;
$aLogInfo = ['article_id' => $iArticleId,'type' => 5,'msg' =>$sMsg,'status' => $iStatus,'create_time' => time()];
$result = $oMaterial->addWechatLog($aLogInfo);
}
return json_encode($aResult);
}
/**
* 添加接口访问日志
*/
private function addLog($aParam = []){
$aField = ['open_ai_id','log_data','empty_data'];
$aInsert = [];
foreach ($aField as $key => $value) {
if(isset($aParam[$value])){
$aInsert[$value] = is_array($aParam[$value]) ? json_encode($aParam[$value]) : $aParam[$value];
}
}
$result = 0;
if(empty($aInsert)){
return true;
}
$aInsert['create_time'] = time();
return DB::name('openapi_log')->insertGetId($aInsert);
}
/**
* 从文本中提取被```json```和```包裹的JSON内容并解析
* @param string $text 包含JSON代码块的文本
* @param bool $assoc 是否返回关联数组默认true
* @return array|object 解析后的JSON数据失败时返回null
*/
public function extractAndParse($text, $assoc = true){
// 使用正则表达式提取JSON代码块
preg_match('/```json\s*(\{.*?\})\s*```/s', $text, $matches);
$jsonContent = empty($matches[1]) ? '' : $matches[1];
if (empty($jsonContent)) {
// 尝试宽松匹配允许没有json标记
preg_match('/```\s*(\{.*?\})\s*```/s', $text, $matches);
$jsonContent = empty($matches[1]) ? $text : $matches[1];
}
// 解析JSON
$aData = json_decode($jsonContent, $assoc);
// 检查解析是否成功
if (json_last_error() !== JSON_ERROR_NONE) {
return ['status' => 2,'msg' => "API返回无效JSON: " . json_last_error_msg()];
}
return ['status' => 1,'msg' => 'success','data' => $aData];
}
}