From c877eba03539fc63fcc641a73c60e6450d67e950 Mon Sep 17 00:00:00 2001 From: chengxl Date: Mon, 13 Oct 2025 16:16:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=A1=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/api/controller/Proofread.php | 503 +++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 application/api/controller/Proofread.php diff --git a/application/api/controller/Proofread.php b/application/api/controller/Proofread.php new file mode 100644 index 0000000..7d55dcd --- /dev/null +++ b/application/api/controller/Proofread.php @@ -0,0 +1,503 @@ +request->post() : $aParam; + $iArticleId = empty($aParam['article_id']) ? '' : $aParam['article_id']; + if(empty($iArticleId)){ + return json_encode(array('status' => 2,'msg' => 'Please select an article' )); + } + //行号 + $iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id']; + //查询文章 + $aWhere = ['article_id' => $iArticleId]; + $oArticle = new Article; + $aArticle = json_decode($oArticle->get($aWhere),true); + $aArticle = empty($aArticle['data']) ? [] : $aArticle['data']; + if(empty($aArticle)){ + return json_encode(array('status' => 3,'msg' => 'No articles requiring review were found' )); + } + if($aArticle['state'] != 6){ + return json_encode(array('status' => 4,'msg' => 'The article has not entered the proofreading stage')); + } + + //查询是否进行过校对 + $aProofReadWhere = ['article_id' => $iArticleId,'state' => 2]; + if(!empty($iAmId)){ + $aProofReadWhere['am_id'] = $iAmId; + } + $iCount = Db::name('article_proofread')->where($aProofReadWhere)->count(); + if(!empty($iCount)){ + return json_encode(array('status' => 5,'msg' => 'The article or paragraph has been proofread')); + } + + //查询文章内容 + $aWhere['type'] = 0; + $aWhere['content'] = ['<>','']; + $aWhere['state'] = 0; + if(!empty($iAmId)){ + $aWhere['am_id'] = $iAmId; + } + $aArticleMain = Db::table('t_article_main')->field('am_id,content,type,is_h1,is_h2,is_h3,ami_id,amt_id')->where($aWhere)->select(); + if(empty($aArticleMain)){ + return json_encode(array('status' => 5,'msg' => 'The content of the article is empty')); + } + + //实例化公共方法 + $oHelperFunction = new \app\common\HelperFunction; + $oProofReadService = new \app\common\ProofReadService; + //数据处理 + $aH = $aTable = []; + foreach ($aArticleMain as $key => $value) { + if(empty($oHelperFunction->filterAllTags($value['content']))){ + continue; + } + $aResult = $oProofReadService->proofread($value['content']); + if(empty($aResult)){ + continue; + } + $aResult['am_id'] = $value['am_id']; + $aError[] = $aResult; + } + if(empty($aError)){ + return json_encode(array('status' => 1,'msg' => 'No errors found')); + } + //数据处理 + foreach ($aError as $key => $value) { + if(empty($value['errors'])){ + continue; + } + foreach ($value['errors'] as $k => $val) { + $val['am_id'] = $value['am_id']; + $val['article_id'] = $iArticleId; + $val['proof_before'] = empty($value['proof_before']) ? '' : $value['proof_before']; + $val['proof_after'] = empty($value['proof_after']) ? '' : $value['proof_after']; + $aData[] = $val; + } + } + if(empty($aData)){ + return json_encode(array('status' => 1,'msg' => 'Data processing failed')); + } + //插入 + $result = Db::name('article_proofread')->insertAll($aData); + if(!$result){ + return json_encode(array('status' => 6,'msg' => 'No errors found')); + } + + //查询参考文献 + $aWhere = ['state' => ['in',[0,2]],'article_id' => $iArticleId]; + return json_encode(['status' => 1,'msg' => 'success']); + } + /** + * @title 获取每行校对记录 + * @param article_id 文章ID + * @param am_id 行号 + * @param state 状态1已执行2未执行3删除 + */ + public function get($aParam = []){ + + //获取参数 + $aParam = empty($aParam) ? $this->request->post() : $aParam; + //参数验证-文章ID + $iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id']; + if(empty($iArticleId)){ + return json_encode(['status' => 2,'msg' => 'Please select a article']); + } + //行号 + $iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id']; + + //查询文章 + $aWhere = ['article_id' => $iArticleId]; + $aArticle = Db::name('article')->field('journal_id,state')->where($aWhere)->find(); + if(empty($aArticle)){ + return json_encode(['status' => 3,'msg' => 'The query article does not exist']); + } + if($aArticle['state'] < 5 || $aArticle['state'] == 8){ + return json_encode(array('status' => 4,'msg' => 'The article has not entered the proofreading stage')); + } + + //查询文章内容 + $aWhere['type'] = 0; + $aWhere['content'] = ['<>','']; + $aWhere['state'] = 0; + if(!empty($iAmId)){ + $aWhere['am_id'] = $iAmId; + } + $aArticleMain = Db::name('article_main')->field('am_id,content')->where($aWhere)->select(); + if(empty($aArticleMain)){ + return json_encode(array('status' => 5,'msg' => 'The content of the article is empty')); + } + + //查询校对内容 + $aAmId = array_column($aArticleMain, 'am_id'); + $aWhere = ['am_id' => ['in',$aAmId]]; + $iState = empty($aParam['state']) ? 0 : $aParam['state']; + if(!empty($iState)){ + $aWhere['state'] = ['in',$iState]; + } + $aProofRead = Db::name('article_proofread')->field('id,am_id,verbatim_texts,revised_content,explanation,state')->where($aWhere)->select(); + if(empty($aProofRead)){ + return json_encode(array('status' => 1,'msg' => 'Proofreading record is empty')); + } + //数据处理 + $aData = []; + $aArticleMain = array_column($aArticleMain, 'content','am_id'); + foreach ($aProofRead as $key => $value) { + $aData[$value['am_id']][] = $value; + } + + // 存储每个文章的最新处理内容 + // 样式定义:标红+下划线 + // 定义标红+下划线样式 + // $style = 'color: red; text-decoration: underline;'; + + // // 存储最终处理后的文章内容 + // $aFinalContent = []; + + // // 外层循环:遍历每个文章的错误分组($key为am_id) + // foreach ($aData as $amId => $errors) { + // // 获取原始文章内容(若不存在则跳过) + // $sContent = empty($aArticleMain[$amId]) ? '' : $aArticleMain[$amId]; + // if (empty($sContent)) { + // continue; + // } + + // // 初始化当前内容为原始内容,后续替换将基于此更新 + // $currentContent = $sContent; + + // // 内层循环:处理当前文章的每条错误记录 + // foreach ($errors as $val) { + // // 只处理状态为2的错误 + // if ($val['state'] != 2) { + // continue; + // } + + // // 获取错误内容(待标红的文本) + // $verbatim_texts = trim($val['verbatim_texts'] ?? ''); + // if (empty($verbatim_texts)) { + // continue; // 空错误内容跳过 + // } + + // // 构建带样式的替换内容(用span标签包裹,添加CSS样式) + // $revised_content = '' . htmlspecialchars($verbatim_texts, ENT_QUOTES, 'UTF-8') . ''; + + // // 定位错误内容在当前内容中的位置(支持多字节字符,如中文) + // $startPos = mb_strpos($currentContent, $verbatim_texts, 0, 'UTF-8'); + // if ($startPos === false) { + // continue; // 未找到错误内容,跳过 + // } + + // // 计算错误内容的字节长度(用于替换) + // $byteLength = strlen($verbatim_texts); + // // 转换字符起始位置为字节位置(适配substr_replace的字节处理) + // $byteStart = 0; + // for ($i = 0; $i < $startPos; $i++) { + // $byteStart += strlen(mb_substr($currentContent, $i, 1, 'UTF-8')); + // } + + // // 执行替换:用带样式的内容替换原始错误内容 + // $currentContent = substr_replace($currentContent, $revised_content, $byteStart, $byteLength); + // } + + // // 保存当前文章处理后的最终内容 + // $aFinalContent[$amId] = $currentContent; + // } + return json_encode(['status' => 1,'msg' => 'success','data' => ['record' => $aData]]);//,'record_style' => $aFinalContent + } + + /** + * @title 更新状态 + * @param article_id 文章ID + * @param am_id 行号 + * @param record_id 记录ID + * @param state 1已执行2未执行3撤销 + */ + public function change(){ + //获取参数 + $aParam = $this->request->post(); + //参数验证-文章ID + $iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id']; + if(empty($iArticleId)){ + return json_encode(['status' => 2,'msg' => 'Please select a article']); + } + //参数验证-行号ID + $iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id']; + if(empty($iAmId)){ + return json_encode(['status' => 2,'msg' => 'Please select the proofreading content']); + } + //主键ID + $iId = empty($aParam['record_id']) ? 0 : $aParam['record_id']; + if(empty($iId)){ + return json_encode(['status' => 2,'msg' => 'Please select the review record']); + } + //状态 + $iState = empty($aParam['state']) ? 0 : intval($aParam['state']); + if(!in_array($iState, [1,2,3])){ + return json_encode(['status' => 2,'msg' => 'Illegal review status']); + } + + //修改内容 + $sContent = empty($aParam['content']) ? '' : $aParam['content']; + if(in_array($iState, [1,2]) && empty($sContent)){ + return json_encode(['status' => 2,'msg' => 'The operation content cannot be empty']); + } + + //判断校对记录 + $aWhere = ['am_id' => $iAmId,'article_id' => $iArticleId,'id' => $iId]; + $aProofRead = Db::name('article_proofread')->where($aWhere)->find(); + + if(empty($aProofRead)){ + return json_encode(['status' => 3,'msg' => 'Proofreading record is empty']); + } + if($aProofRead['state'] == 3){ + return json_encode(['status' => 4,'msg' => 'Record deleted']); + } + + //状态一致 + if($iState == $aProofRead['state']){ + return json_encode(['status' => 5,'msg' => 'Consistent status without modification']); + } + + //判断记录是否执行 + if($iState == 3 && $aProofRead['state'] == 1){ + return json_encode(['status' => 6,'msg' => 'This record has been executed and cannot be deleted']); + } + $sData = $sUpdateContent = ''; + if($iState == 1){ //执行替换操作 + $aProofRead['content'] = $sContent; + $aResult = $this->replaceError($aProofRead); + $iStatus = empty($aResult['status']) ? 0 : $aResult['status']; + if($iStatus != 1){ + return json_encode($aResult); + } + //获取内容 + $sUpdateContent = empty($aResult['data']) ? '' : $aResult['data']; + if(empty($sUpdateContent)){ + return json_encode(['status' => 5,'msg' => 'Content processing failed']); + } + // $aDealData = json_decode($this->get(['am_id' => $iAmId,'article_id' => $iArticleId]),true); + // $aDealData = empty($aDealData['data']) ? [] : $aDealData['data']; + // $sData = empty($aDealData['record_style']) ? '' : $aDealData['record_style']; + $sData = $sUpdateContent; + } + if($iState == 2){ //执行替换操作 + $aProofRead['content'] = $sContent; + $aResult = $this->removeReplaceError($aProofRead); + $iStatus = empty($aResult['status']) ? 0 : $aResult['status']; + if($iStatus != 1){ + return json_encode($aResult); + } + //获取内容 + $sUpdateContent = empty($aResult['data']) ? '' : $aResult['data']; + if(empty($sUpdateContent)){ + return json_encode(['status' => 5,'msg' => 'Content processing failed']); + } + // $aDealData = json_decode($this->get(['am_id' => $iAmId,'article_id' => $iArticleId]),true); + // $aDealData = empty($aDealData['data']) ? [] : $aDealData['data']; + // $sData = empty($aDealData['record_style']) ? '' : $aDealData['record_style']; + $sData = $sUpdateContent; + } + Db::startTrans(); + //更新原始内容 + if(!empty($sUpdateContent)){ + $aWhere = ['am_id' => $iAmId,'state' => 0,'type' => 0]; + $aUpdate = ['content' => $sUpdateContent]; + $result_main = Db::name('article_main')->where($aWhere)->limit(1)->update($aUpdate); + } + //判断更新参数 + $aUpdate = ['state' => $iState,'update_time' => time()]; + //数据库更新 + $aWhere = ['id' => $iId]; + $result_proofread= Db::name('article_proofread')->where($aWhere)->limit(1)->update($aUpdate); + if(!$result_proofread || !$result_main){ + return json_encode(['status' => 7,'msg' => "Update failed"]); + } + Db::commit(); + //返回结果 + return json_encode(['status' => 1,'msg' => "Update successful",'data' => $sData]); + } + + /** + * 替换错误 + * @param string $errorId 错误ID + * @return bool 替换是否成功 + */ + private function replaceError($aParam = []) + { + if(empty($aParam)){ + return ['status' => 2,'msg' => 'The content is empty']; + } + //原始内容 + $sContent = empty($aParam['content']) ? '' : $aParam['content']; + //错误内容 + $verbatim_texts = empty($aParam['verbatim_texts']) ? '' : $aParam['verbatim_texts']; + //正确内容 + $revised_content = empty($aParam['revised_content']) ? 0 : $aParam['revised_content']; + + $iLength = strlen($verbatim_texts); + //内容替换 + $sContent = $this->replaceByLengthAndKeyword($sContent,$iLength,$verbatim_texts,$revised_content); + + return ['status' => 1,'msg' => 'success','data' => $sContent]; + } + + /** + * 撤回替换错误 + * @param string $errorId 错误ID + * @return bool 替换是否成功 + */ + private function removeReplaceError($aParam = []) + { + if(empty($aParam)){ + return ['status' => 2,'msg' => 'The content is empty']; + } + //原始内容 + $sContent = empty($aParam['content']) ? '' : $aParam['content']; + //错误内容 + $verbatim_texts = empty($aParam['verbatim_texts']) ? '' : $aParam['verbatim_texts']; + //正确内容 + $revised_content = empty($aParam['revised_content']) ? '' : $aParam['revised_content']; + + $iLength = strlen($revised_content); + //内容替换 + $sContent = $this->replaceByLengthAndKeyword($sContent,$iLength,$revised_content,$verbatim_texts); + + return ['status' => 1,'msg' => 'success','data' => $sContent]; + } + /** + * 按长度和内容特征快速定位并替换字符串 + * @param string $str 原始字符串 + * @param int $targetLength 目标子串长度(字节数,多字节字符需注意) + * @param string $keyword 筛选关键词(目标子串需包含此关键词) + * @param string $replace 替换内容 + * @param string $encoding 字符编码(默认UTF-8,处理多字节字符) + * @return string 替换后的字符串 + */ + private function replaceByLengthAndKeyword($str, $targetLength, $keyword, $replace, $encoding = 'UTF-8') { + // 边界校验:避免无效参数导致错误 + if (empty($str) || $targetLength <= 0 || empty($keyword)) { + return ''; + } + $strLength = strlen($str); + if ($targetLength > $strLength) { + return ''; // 目标长度超过原始字符串,无需处理 + } + + $result = $str; + $targets = []; // 存储目标子串的起始位置(字节索引) + + // 遍历字符串,提取符合长度且包含关键词的子串位置(一次遍历完成筛选,减少内存占用) + for ($i = 0; $i <= $strLength - $targetLength; $i++) { + // 截取目标长度的子串 + $substr = substr($result, $i, $targetLength); + // 检查子串是否包含关键词(多字节安全) + if (mb_strpos($substr, $keyword, 0, $encoding) !== false) { + $targets[] = $i; // 只存起始位置,减少内存占用 + } + } + + // 若没有匹配目标,直接返回原始字符串 + if (empty($targets)) { + return ''; + } + + // 从后往前替换(避免前面替换导致后面位置偏移) + // 倒序遍历数组,无需额外排序,效率更高 + for ($k = count($targets) - 1; $k >= 0; $k--) { + $start = $targets[$k]; + // 二次校验:替换前确认子串仍符合条件(防止重复替换或中间修改导致的偏差) + $currentSubstr = substr($result, $start, $targetLength); + if (mb_strpos($currentSubstr, $keyword, 0, $encoding) !== false) { + $result = substr_replace($result, $replace, $start, $targetLength); + } + } + + return $result; + } + + /** + * @title 更新内容 + * @param article_id 文章ID + * @param am_id 行号 + * @param record_id 记录ID + * @param revised_content 修改内容 + * @param is_update_all 是否更新所有1是2否 + */ + public function modify(){ + //获取参数 + $aParam = $this->request->post(); + //参数验证-文章ID + $iArticleId = empty($aParam['article_id']) ? 0 : $aParam['article_id']; + if(empty($iArticleId)){ + return json_encode(['status' => 2,'msg' => 'Please select a article']); + } + //参数验证-行号ID + $iAmId = empty($aParam['am_id']) ? 0 : $aParam['am_id']; + if(empty($iAmId)){ + return json_encode(['status' => 2,'msg' => 'Please select the proofreading content']); + } + //主键ID + $iId = empty($aParam['record_id']) ? 0 : $aParam['record_id']; + if(empty($iId)){ + return json_encode(['status' => 2,'msg' => 'Please select the review record']); + } + //修改后的内容 + $sRevisedContent = empty($aParam['revised_content']) ? '' : $aParam['revised_content']; + if(empty($sRevisedContent)){ + return json_encode(['status' => 2,'msg' => 'Please enter the modification content']); + } + //解释说明 + $sExplanation = empty($aParam['explanation']) ? '' : $aParam['explanation']; + //是否更新所有1是2否 + $iIsUpdateAll = empty($aParam['is_update_all']) ? 2 : $aParam['is_update_all']; + + //查询内容是否存在 + $aWhere = ['am_id' => $iAmId,'article_id' => $iArticleId,'id' => $iId]; + $aProofRead = Db::name('article_proofread')->field('verbatim_texts,revised_content,explanation,state')->where($aWhere)->find(); + if(empty($aProofRead)){ + return json_encode(['status' => 3,'msg' => 'Proofreading record is empty']); + } + if($aProofRead['state'] == 3){ + return json_encode(['status' => 4,'msg' => 'Record deleted']); + } + if($aProofRead['state'] == 1){ + return json_encode(['status' => 5,'msg' => 'Record executed']); + } + //判断更新参数 + $aUpdate = ['revised_content' => $sRevisedContent,'update_time' => time()]; + if(!empty($sExplanation)){ + $aUpdate['explanation'] = $sExplanation; + } + //数据库更新 + $aWhere = ['id' => $iId]; + if($iIsUpdateAll == 1){ + if(empty($aProofRead['verbatim_texts']) || empty($aProofRead['revised_content'])){ + return json_encode(['status' => 6,'msg' => 'AI proofreading content is empty']); + } + $aWhere = ['verbatim_texts' => $aProofRead['verbatim_texts'],'revised_content' => $aProofRead['revised_content'],'article_id' => $iArticleId,'state' => 2]; + } + $result = Db::name('article_proofread')->where($aWhere)->update($aUpdate); + if(!$result){ + return json_encode(['status' => 7,'msg' => "Update failed"]); + } + //返回结果 + return json_encode(['status' => 1,'msg' => "Update successful"]); + } +}