diff --git a/bonus-admin/src/main/java/com/bonus/web/controller/analysis/AnalysisController.java b/bonus-admin/src/main/java/com/bonus/web/controller/analysis/AnalysisController.java index 6497981..44bb421 100644 --- a/bonus-admin/src/main/java/com/bonus/web/controller/analysis/AnalysisController.java +++ b/bonus-admin/src/main/java/com/bonus/web/controller/analysis/AnalysisController.java @@ -107,6 +107,14 @@ public class AnalysisController extends BaseController { return analysisService.saveBidData(dto); } + @ApiOperation(value = "招标解析", notes = "查看解析数据") + @GetMapping("getAnalysisData") + @SysLog(title = "招标解析", module = "招标解析->查看解析数据", businessType = OperaType.QUERY, details = "查看解析数据", logType = 1) + @RequiresPermissions("analysis:analysis:query") + public AjaxResult getAnalysisData(AnalysisDto dto) { + return analysisService.getAnalysisData(dto); + } + @ApiOperation(value = "测试mq异步消息", notes = "测试mq异步消息") @GetMapping("/testAsyncMq2") public AjaxResult testAsyncMq2() { diff --git a/bonus-admin/src/main/java/com/bonus/web/service/analysis/AnalysisService.java b/bonus-admin/src/main/java/com/bonus/web/service/analysis/AnalysisService.java index 37a2c85..ad21003 100644 --- a/bonus-admin/src/main/java/com/bonus/web/service/analysis/AnalysisService.java +++ b/bonus-admin/src/main/java/com/bonus/web/service/analysis/AnalysisService.java @@ -10,6 +10,7 @@ import com.bonus.common.domain.analysis.dto.AnalysisProDto; import com.bonus.common.domain.analysis.po.ProComposition; import com.bonus.common.domain.analysis.vo.AnalysisBidVo; import com.bonus.common.domain.analysis.vo.AnalysisVo; +import com.bonus.common.domain.analysis.vo.ProBidAnalysisResultVo; import com.bonus.common.domain.file.vo.ResourceFileVo; import com.bonus.common.domain.file.vo.SysFile; import com.bonus.common.domain.ocr.dto.OcrRequest; @@ -24,6 +25,7 @@ import com.bonus.ocr.service.WordConvertPdfService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.units.qual.A; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; @@ -33,6 +35,7 @@ import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.Executor; +import java.util.stream.Collectors; /** @@ -112,7 +115,7 @@ public class AnalysisService { }) .join(); // 确保每个消息发送完成后再进行下一个 }*/ - List> list = new ArrayList<>(); + List> list = new ArrayList<>(); for (int i = 0; i < 2; i++) { Map map = new HashMap<>(); String taskId = UUID.randomUUID().toString(); @@ -128,6 +131,7 @@ public class AnalysisService { /** * 招标解析->查询列表 + * * @param dto * @return List * @author cwchen @@ -139,6 +143,7 @@ public class AnalysisService { /** * 招标解析->新建项目 + * * @param dto * @return AjaxResult * @author cwchen @@ -159,7 +164,7 @@ public class AnalysisService { List compositions = new ArrayList<>(); for (String uploadType : dto.getUploadType()) { - ProComposition vo = createVo(dto.getProId(), uploadType,"1"); + ProComposition vo = createVo(dto.getProId(), uploadType, "1"); compositions.add(vo); } // 保存项目的文件组成数据 @@ -178,6 +183,7 @@ public class AnalysisService { msg.setProId(dto.getProId()); msg.setTemplateId(dto.getTemplateId()); msg.setAnalysisLabelId(dto.getAnalysisLabelId()); + msg.setProCompositionId(id); msg.setCompositionType(1); msg.setTaskName("OCR_PROCESS"); asyncTaskList.add(msg); @@ -187,13 +193,13 @@ public class AnalysisService { AsyncManager.me().executeSendRabbitMqMessage(asyncTaskList); return AjaxResult.success(); } catch (Exception e) { - log.error(e.toString(),e); + log.error(e.toString(), e); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return AjaxResult.error(); } } - public ProComposition createVo(Long proId,String value,String compositionType){ + public ProComposition createVo(Long proId, String value, String compositionType) { ProComposition proComposition = new ProComposition(); proComposition.setProId(proId); proComposition.setCompositionFileName(value); @@ -203,6 +209,7 @@ public class AnalysisService { /** * 招标解析->查看详情 + * * @param dto * @return AjaxResult * @author cwchen @@ -219,15 +226,16 @@ public class AnalysisService { try { WordConvertPdfResponse wordConvertPdfResponse = wordConvertPdfService.convertWordToPdf(ocrRequest); String pdfBase64 = wordConvertPdfResponse.getPdfBase64(); - Base64ToPdfConverter.convertToFile(pdfBase64,"C:\\Users\\10488\\Desktop\\test12121212.pdf"); + Base64ToPdfConverter.convertToFile(pdfBase64, "C:\\Users\\10488\\Desktop\\test12121212.pdf"); } catch (IOException e) { - log.error(e.toString(),e); + log.error(e.toString(), e); } return null; } /** * 招标解析->查看项目详情 + * * @param dto * @return AjaxResult * @author cwchen @@ -239,7 +247,7 @@ public class AnalysisService { List bidCompositions = analysisService.getBidCompositionByBid(analysisVo); analysisVo.setBidCompositions(bidCompositions); // 查询类型为2时查询文件 - if(dto.getQueryType() == 2){ + if (dto.getQueryType() == 2) { // 查询项目组成文件 dto.setCompositionType("1"); List compositions = analysisService.getProComposition(dto); @@ -248,10 +256,10 @@ public class AnalysisService { for (ProComposition composition : compositions) { List fileVoList = sourceFileService.getFilesByTable(composition.getId(), TableConstants.TB_PRO_COMPOSITION); // 4.取minio中的文件访问路径 - if(CollectionUtils.isNotEmpty(fileVoList)){ + if (CollectionUtils.isNotEmpty(fileVoList)) { for (ResourceFileVo file : fileVoList) { SysFile sysFile = fileUploadService.getFile(file.getFilePath()); - if(Objects.nonNull(sysFile)){ + if (Objects.nonNull(sysFile)) { file.setLsFilePath(sysFile.getUrl()); } } @@ -265,6 +273,7 @@ public class AnalysisService { /** * 招标解析->更新项目数据 + * * @param dto * @return AjaxResult * @author cwchen @@ -282,7 +291,7 @@ public class AnalysisService { analysisService.editProData(dto); return AjaxResult.success(); } catch (Exception e) { - log.error(e.toString(),e); + log.error(e.toString(), e); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return AjaxResult.error(); } @@ -300,7 +309,7 @@ public class AnalysisService { analysisService.editBidData(dto); return AjaxResult.success(); } catch (Exception e) { - log.error(e.toString(),e); + log.error(e.toString(), e); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return AjaxResult.error(); } @@ -308,6 +317,7 @@ public class AnalysisService { /** * 删除项目数据 + * * @param dto * @return AjaxResult * @author cwchen @@ -327,9 +337,9 @@ public class AnalysisService { analysisService.delBidData(dto); // 删除项目组成文件 List compositions = analysisService.getProComposition(dto); - if(CollectionUtils.isNotEmpty(compositions)){ + if (CollectionUtils.isNotEmpty(compositions)) { for (ProComposition composition : compositions) { - sourceFileService.delResourceFileByTable(composition.getId(),TableConstants.TB_PRO_COMPOSITION); + sourceFileService.delResourceFileByTable(composition.getId(), TableConstants.TB_PRO_COMPOSITION); } } // 删除模板组成数据 @@ -338,7 +348,7 @@ public class AnalysisService { analysisService.delProBidAnalysisResult(dto); return AjaxResult.success(); } catch (Exception e) { - log.error(e.toString(),e); + log.error(e.toString(), e); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return AjaxResult.error(); } @@ -346,6 +356,7 @@ public class AnalysisService { /** * 招标解析->标段提交文件解析 + * * @param dto * @return AjaxResult * @author cwchen @@ -365,7 +376,7 @@ public class AnalysisService { List compositions = new ArrayList<>(); for (String uploadType : dto.getUploadType()) { - ProComposition vo = createVo(dto.getBidId(), uploadType,"2"); + ProComposition vo = createVo(dto.getBidId(), uploadType, "2"); compositions.add(vo); } // 保存标段的文件组成数据 @@ -391,10 +402,106 @@ public class AnalysisService { AsyncManager.me().executeSendRabbitMqMessage(asyncTaskList); return AjaxResult.success(); } catch (Exception e) { - log.error(e.toString(),e); + log.error(e.toString(), e); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return AjaxResult.error(); } } + + /** + * 查看标段解析数据 + * + * @param dto + * @return AjaxResult + * @author cwchen + * @date 2025/12/6 13:41 + */ + public AjaxResult getAnalysisData(AnalysisDto dto) { + // 查询工程和标段的解析数据 + List proList = analysisService.getProBidAnalysisResult(dto, 1); + List bidList = analysisService.getProBidAnalysisResult(dto, 2); + // 2. 判空处理,防止空指针 + /*if (CollectionUtils.isNotEmpty(proList) && CollectionUtils.isNotEmpty(bidList)) { + // 3. 将 bidList 转换为 Map + // Key: analysisLabelItemId, Value: analysisContent + // (v1, v2) -> v1 是为了防止 bidList 中有重复 ID 报错,取第一个找到的值 + Map bidContentMap = bidList.stream() + .filter(item -> item.getAnalysisLabelItemId() != null && item.getAnalysisContent() != null) + .collect(Collectors.toMap( + ProBidAnalysisResultVo::getAnalysisLabelItemId, + ProBidAnalysisResultVo::getAnalysisContent, + (v1, v2) -> v1 + )); + + // 4. 遍历 proList 进行合并 + for (ProBidAnalysisResultVo proItem : proList) { + // 逻辑:如果 pro 的内容为空(null 或 空字符串),才去尝试合并 + if (proItem.getAnalysisContent() == null || proItem.getAnalysisContent().trim().isEmpty()) { + // 根据 ID 从 Map 中获取内容 + String contentFromBid = bidContentMap.get(proItem.getAnalysisLabelItemId()); + // 如果 bid 中有值,则赋值 + if (contentFromBid != null) { + proItem.setAnalysisContent(contentFromBid); + } + } + } + }*/ + List tree = buildTree(proList, "0"); + return AjaxResult.success("查询成功", tree); + } + + /** + * 将扁平 List 转换为树形结构,并按 analysisSort 排序。 + * + * @param flatList 扁平的 ProBidAnalysisResultVo 列表 + * @param rootId 根节点的 parentId (通常是 null, "0", 或其他特定值) + * @return 排序后的树形结构根节点列表 + */ + public static List buildTree( + List flatList, + String rootId) { + + if (flatList == null || flatList.isEmpty()) { + return new ArrayList<>(); + } + + // 1. 按 parentId 分组:Map> + // 这一步是性能优化的关键,将 O(N^2) 降低到 O(N) + Map> map = flatList.stream() + .filter(item -> item.getParentId() != null) // 过滤掉 parentId 为 null 的情况 + .collect(Collectors.groupingBy(ProBidAnalysisResultVo::getParentId)); + + // 2. 递归构建树 + return buildTreeRecursive(rootId, map); + } + + /** + * 递归构建树的核心逻辑 + */ + private static List buildTreeRecursive( + String parentId, + Map> map) { + + // 1. 从 Map 中取出当前 parentId 的所有子节点 + List childrenList = map.getOrDefault(parentId, Collections.emptyList()); + + // 2. 对当前层级的子节点列表进行排序 (根据 analysisSort 升序) + // 注意:analysisSort 可能是 Integer,需要处理 null 值,通常放在最后或最前 + childrenList.sort(Comparator.comparing( + ProBidAnalysisResultVo::getAnalysisSort, + Comparator.nullsLast(Comparator.naturalOrder()) + )); + + // 3. 递归处理每个子节点 + for (ProBidAnalysisResultVo node : childrenList) { + // 递归调用,以当前节点的 analysisLabelItemId 作为新的 parentId + List grandChildren = buildTreeRecursive(node.getAnalysisLabelItemId(), map); + + // 设置子节点 + node.setChildren(grandChildren); + } + // 4. 返回当前层级已排序且完成子节点挂载的列表 + return childrenList; + } } diff --git a/bonus-analysis/src/main/java/com/bonus/analysis/mapper/IASAnalysisMapper.java b/bonus-analysis/src/main/java/com/bonus/analysis/mapper/IASAnalysisMapper.java index 3f5da9e..d1832bd 100644 --- a/bonus-analysis/src/main/java/com/bonus/analysis/mapper/IASAnalysisMapper.java +++ b/bonus-analysis/src/main/java/com/bonus/analysis/mapper/IASAnalysisMapper.java @@ -7,8 +7,11 @@ import com.bonus.common.domain.analysis.po.ProComposition; import com.bonus.common.domain.analysis.vo.AnalysisBidVo; import com.bonus.common.domain.analysis.vo.AnalysisLabelItemOcrVo; import com.bonus.common.domain.analysis.vo.AnalysisVo; +import com.bonus.common.domain.analysis.vo.ProBidAnalysisResultVo; +import com.bonus.common.domain.ocr.vo.TwoAnalysisResponse; import com.bonus.common.domain.rabbitmq.dto.RabbitMqMessage; import org.apache.ibatis.annotations.Param; +import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Repository; import java.util.List; @@ -96,12 +99,12 @@ public interface IASAnalysisMapper { /** * 获取解析标签项 - * @param analysisLabelId + * @param message * @return List * @author cwchen * @date 2025/11/29 14:49 */ - List getAnalysisLabels(@Param("analysisLabelId") Long analysisLabelId, @Param("templateId") Long templateId); + List getAnalysisLabels(RabbitMqMessage message); /** * 删除项目数据 @@ -174,4 +177,52 @@ public interface IASAnalysisMapper { * @date 2025/11/30 15:03 */ List getProCompositionState(RabbitMqMessage message); + + /** + * isLabelExist + * @param message + * @return int + * @author cwchen + * @date 2025/12/6 11:07 + */ + int isLabelExist(RabbitMqMessage message); + + /** + * 从解析内容中读取标签配置 + * @param message + * @return List + * @author cwchen + * @date 2025/12/6 11:13 + */ + List getAnalysisLabelsByProOrBid(RabbitMqMessage message); + + /** + * 保存解析标签数据 + * @param message + * @param labelItemVoList + * @return void + * @author cwchen + * @date 2025/12/6 12:26 + */ + void saveProBidAnalysisData(@Param("params") RabbitMqMessage message, @Param("list") List labelItemVoList); + + /** + * 更新解析标签数据 + * @param message + * @param analysisDataList + * @return void + * @author cwchen + * @date 2025/12/6 13:17 + */ + void updateProBidAnalysisData(@Param("params") RabbitMqMessage message, @Param("list")List analysisDataList); + + /** + * 查询工程和标段的解析数据 + * @param dto + * @param type + * @return List + * @author cwchen + * @date 2025/12/6 13:47 + */ + List getProBidAnalysisResult(@Param("params") AnalysisDto dto,@Param("type") int type); } diff --git a/bonus-analysis/src/main/java/com/bonus/analysis/service/IASAnalysisService.java b/bonus-analysis/src/main/java/com/bonus/analysis/service/IASAnalysisService.java index 7801b6e..89b8e96 100644 --- a/bonus-analysis/src/main/java/com/bonus/analysis/service/IASAnalysisService.java +++ b/bonus-analysis/src/main/java/com/bonus/analysis/service/IASAnalysisService.java @@ -7,6 +7,8 @@ import com.bonus.common.domain.analysis.po.ProComposition; import com.bonus.common.domain.analysis.vo.AnalysisBidVo; import com.bonus.common.domain.analysis.vo.AnalysisLabelItemOcrVo; import com.bonus.common.domain.analysis.vo.AnalysisVo; +import com.bonus.common.domain.analysis.vo.ProBidAnalysisResultVo; +import com.bonus.common.domain.ocr.vo.TwoAnalysisResponse; import com.bonus.common.domain.rabbitmq.dto.RabbitMqMessage; import java.util.List; @@ -22,6 +24,7 @@ public interface IASAnalysisService { /** * 招标解析->查询列表 + * * @param dto * @return List * @author cwchen @@ -31,6 +34,7 @@ public interface IASAnalysisService { /** * 保存项目数据 + * * @param dto * @return void * @author cwchen @@ -40,6 +44,7 @@ public interface IASAnalysisService { /** * 招标解析->查看详情 + * * @param dto * @return List * @author cwchen @@ -49,6 +54,7 @@ public interface IASAnalysisService { /** * 招标解析->查看项目详情 + * * @param dto * @return AnalysisVo * @author cwchen @@ -58,6 +64,7 @@ public interface IASAnalysisService { /** * 更新项目数据 + * * @param dto * @return void * @author cwchen @@ -67,6 +74,7 @@ public interface IASAnalysisService { /** * 更新标段数据 + * * @param dto * @return void * @author cwchen @@ -76,6 +84,7 @@ public interface IASAnalysisService { /** * 保存项目的文件组成 + * * @param compositions * @return void * @author cwchen @@ -85,6 +94,7 @@ public interface IASAnalysisService { /** * 查询项目组成文件 + * * @return List * @author cwchen * @date 2025/11/26 15:54 @@ -94,15 +104,17 @@ public interface IASAnalysisService { /** * 获取解析标签项 - * @param analysisLabelId + * + * @param message * @return List * @author cwchen * @date 2025/11/29 14:48 */ - List getAnalysisLabels(Long analysisLabelId, Long templateId); + List getAnalysisLabels(RabbitMqMessage message); /** * 删除项目数据 + * * @param dto * @return void * @author cwchen @@ -112,6 +124,7 @@ public interface IASAnalysisService { /** * 删除标段数据 + * * @param dto * @return void * @author cwchen @@ -121,6 +134,7 @@ public interface IASAnalysisService { /** * 删除模板组成数据 + * * @param dto * @return void * @author cwchen @@ -130,6 +144,7 @@ public interface IASAnalysisService { /** * 删除项目/标段解析数据 + * * @param dto * @return void * @author cwchen @@ -139,6 +154,7 @@ public interface IASAnalysisService { /** * 查询标段文件的文件组成 + * * @param analysisVo * @return List * @author cwchen @@ -148,6 +164,7 @@ public interface IASAnalysisService { /** * 更新项目或者标段的文件组成的解析状态 + * * @param message * @return void * @author cwchen @@ -157,6 +174,7 @@ public interface IASAnalysisService { /** * 更新项目或者标段的解析状态 + * * @param message * @return void * @author cwchen @@ -166,10 +184,61 @@ public interface IASAnalysisService { /** * 查询项目或标段的组成文件的解析状态 + * * @param message * @return List * @author cwchen * @date 2025/11/30 15:02 */ List getProCompositionState(RabbitMqMessage message); + + /** + * 查询项目标签是否已经保存 + * + * @param message + * @return int + * @author cwchen + * @date 2025/12/6 11:06 + */ + int isLabelExist(RabbitMqMessage message); + + /** + * 从解析结果中读取标签项配置 + * + * @param message + * @return List + * @author cwchen + * @date 2025/12/6 11:11 + */ + List getAnalysisLabelsByProOrBid(RabbitMqMessage message); + + /** + * 保存解析标签数据 + * @param message + * @param labelItemVoList + * @return void + * @author cwchen + * @date 2025/12/6 12:26 + */ + void saveProBidAnalysisData(RabbitMqMessage message, List labelItemVoList); + + /** + * 更新解析标签数据 + * @param message + * @param analysisDataList + * @return void + * @author cwchen + * @date 2025/12/6 13:16 + */ + void updateProBidAnalysisData(RabbitMqMessage message, List analysisDataList); + + /** + * 查询工程和标段的解析数据 + * @param dto + * @param type + * @return List + * @author cwchen + * @date 2025/12/6 13:46 + */ + List getProBidAnalysisResult(AnalysisDto dto, int type); } diff --git a/bonus-analysis/src/main/java/com/bonus/analysis/service/impl/ASAnalysisServiceImpl.java b/bonus-analysis/src/main/java/com/bonus/analysis/service/impl/ASAnalysisServiceImpl.java index 3964ee3..c9fb893 100644 --- a/bonus-analysis/src/main/java/com/bonus/analysis/service/impl/ASAnalysisServiceImpl.java +++ b/bonus-analysis/src/main/java/com/bonus/analysis/service/impl/ASAnalysisServiceImpl.java @@ -9,6 +9,8 @@ import com.bonus.common.domain.analysis.po.ProComposition; import com.bonus.common.domain.analysis.vo.AnalysisBidVo; import com.bonus.common.domain.analysis.vo.AnalysisLabelItemOcrVo; import com.bonus.common.domain.analysis.vo.AnalysisVo; +import com.bonus.common.domain.analysis.vo.ProBidAnalysisResultVo; +import com.bonus.common.domain.ocr.vo.TwoAnalysisResponse; import com.bonus.common.domain.rabbitmq.dto.RabbitMqMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -82,9 +84,9 @@ public class ASAnalysisServiceImpl implements IASAnalysisService { } @Override - public List getAnalysisLabels(Long analysisLabelId, Long templateId) { + public List getAnalysisLabels(RabbitMqMessage message) { try { - return Optional.ofNullable(analysisMapper.getAnalysisLabels(analysisLabelId,templateId)).orElse(new ArrayList()); + return Optional.ofNullable(analysisMapper.getAnalysisLabels(message)).orElse(new ArrayList()); } catch (Exception e) { return new ArrayList<>(); } @@ -135,4 +137,29 @@ public class ASAnalysisServiceImpl implements IASAnalysisService { public List getProCompositionState(RabbitMqMessage message) { return analysisMapper.getProCompositionState(message); } + + @Override + public int isLabelExist(RabbitMqMessage message) { + return analysisMapper.isLabelExist(message); + } + + @Override + public List getAnalysisLabelsByProOrBid(RabbitMqMessage message) { + return analysisMapper.getAnalysisLabelsByProOrBid(message); + } + + @Override + public void saveProBidAnalysisData(RabbitMqMessage message, List labelItemVoList) { + analysisMapper.saveProBidAnalysisData(message,labelItemVoList); + } + + @Override + public void updateProBidAnalysisData(RabbitMqMessage message, List analysisDataList) { + analysisMapper.updateProBidAnalysisData(message,analysisDataList); + } + + @Override + public List getProBidAnalysisResult(AnalysisDto dto, int type) { + return analysisMapper.getProBidAnalysisResult(dto,type); + } } diff --git a/bonus-analysis/src/main/resources/mapper/AnalysisMapper.xml b/bonus-analysis/src/main/resources/mapper/AnalysisMapper.xml index d90a7b8..2a467aa 100644 --- a/bonus-analysis/src/main/resources/mapper/AnalysisMapper.xml +++ b/bonus-analysis/src/main/resources/mapper/AnalysisMapper.xml @@ -112,6 +112,34 @@ ) + + + INSERT INTO tb_pro_bid_analysis_result + (id,pro_id,analysis_label_item_id,analysis_name, + analysis_code,parent_id,analysis_level,analysis_sort, + extract_content,analysis_type + ) + VALUES + + ( + + #{params.proId}, + + + #{params.bidId}, + + #{params.proId}, + #{item.id}, + #{item.searchKeyword}, + #{item.targetField}, + #{item.parentId}, + #{item.analysisLevel}, + #{item.analysisSort}, + #{item.description}, + #{params.compositionType} + ) + + - + + + + + + @@ -175,12 +252,7 @@ - - UPDATE tb_pro_composition SET analysis_state = #{analysisState} WHERE pro_id = #{proId} AND composition_type = '1' - - - UPDATE tb_pro_composition SET analysis_state = #{analysisState} WHERE pro_id = #{bidId} AND composition_type = '2' - + UPDATE tb_pro_composition SET analysis_state = #{analysisState} WHERE id = #{proCompositionId} @@ -191,4 +263,16 @@ UPDATE tb_pro_bid SET parsing_state = #{analysisState} WHERE bid_id = #{bidId} + + + + UPDATE tb_pro_bid_analysis_result SET analysis_content = #{item.jsonStr} WHERE analysis_label_item_id = #{item.id} + + AND id = #{params.proId} + + + AND id = #{params.bidId} + + + diff --git a/bonus-common/src/main/java/com/bonus/common/domain/analysis/vo/ProBidAnalysisResultVo.java b/bonus-common/src/main/java/com/bonus/common/domain/analysis/vo/ProBidAnalysisResultVo.java new file mode 100644 index 0000000..abf5d05 --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/domain/analysis/vo/ProBidAnalysisResultVo.java @@ -0,0 +1,64 @@ +package com.bonus.common.domain.analysis.vo; + +import lombok.Data; + +import java.util.List; + +/** + * @className:ProBidAnalysisResultVo + * @author:cwchen + * @date:2025-12-06-13:43 + * @version:1.0 + * @description: + */ +@Data +public class ProBidAnalysisResultVo { + + /** + * 标段id / 项目id + */ + private Long id; + + /** + * 项目id + */ + private Long proId; + + /** + * 配置id + */ + private String analysisLabelItemId; + + /** + * 解析名称 + */ + private String analysisName; + + /** + * 解析编码 + */ + private String analysisCode; + + /** + * 父节点 + */ + private String parentId; + + /** + * 层级 + */ + private Integer analysisLevel; + + /** + * 排序 + */ + private Integer analysisSort; + + /** + * 解析内容 + */ + private String analysisContent; + + private List children; + +} diff --git a/bonus-common/src/main/java/com/bonus/common/domain/ocr/vo/TwoAnalysisResponse.java b/bonus-common/src/main/java/com/bonus/common/domain/ocr/vo/TwoAnalysisResponse.java new file mode 100644 index 0000000..cff8cd5 --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/domain/ocr/vo/TwoAnalysisResponse.java @@ -0,0 +1,22 @@ +package com.bonus.common.domain.ocr.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.Map; +import java.util.Objects; + +/** + * @className:OcrResponse + * @author:cwchen + * @date:2025-10-16-9:53 + * @version:1.0 + * @description: 招标解析响应结果 + */ +@Data +public class TwoAnalysisResponse { + + private Long id; + + private String jsonStr; +} diff --git a/bonus-common/src/main/java/com/bonus/common/domain/rabbitmq/dto/RabbitMqMessage.java b/bonus-common/src/main/java/com/bonus/common/domain/rabbitmq/dto/RabbitMqMessage.java index 1155d00..61017df 100644 --- a/bonus-common/src/main/java/com/bonus/common/domain/rabbitmq/dto/RabbitMqMessage.java +++ b/bonus-common/src/main/java/com/bonus/common/domain/rabbitmq/dto/RabbitMqMessage.java @@ -16,7 +16,7 @@ public class RabbitMqMessage implements Serializable { private static final long serialVersionUID = 1L; /** - * 业务id + * 项目id * */ private Long proId; @@ -25,6 +25,11 @@ public class RabbitMqMessage implements Serializable { * */ private Long bidId; + /** + * 文件组成id + * */ + private Long proCompositionId; + /** * 标签组id * */ @@ -70,4 +75,6 @@ public class RabbitMqMessage implements Serializable { * */ private String analysisState; + + } diff --git a/bonus-ocr/src/main/java/com/bonus/ocr/service/AnalysisOcrService.java b/bonus-ocr/src/main/java/com/bonus/ocr/service/AnalysisOcrService.java index 5287520..1046526 100644 --- a/bonus-ocr/src/main/java/com/bonus/ocr/service/AnalysisOcrService.java +++ b/bonus-ocr/src/main/java/com/bonus/ocr/service/AnalysisOcrService.java @@ -153,10 +153,6 @@ public class AnalysisOcrService { // 将请求对象转换为JSON字符串 String jsonRequest = convertToJson(analysisOcrRequest); - // 打印请求参数 - log.info("OCR请求URL: {}", extractInfoUrl); - log.info("OCR请求参数: {}", jsonRequest); - // 设置请求体为JSON StringEntity entity = new StringEntity(jsonRequest, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); diff --git a/bonus-rabbitmq/src/main/java/com/bonus/rabbitmq/consumer/RabbitMQConsumerService.java b/bonus-rabbitmq/src/main/java/com/bonus/rabbitmq/consumer/RabbitMQConsumerService.java index 63e5d48..ed8bd4b 100644 --- a/bonus-rabbitmq/src/main/java/com/bonus/rabbitmq/consumer/RabbitMQConsumerService.java +++ b/bonus-rabbitmq/src/main/java/com/bonus/rabbitmq/consumer/RabbitMQConsumerService.java @@ -5,6 +5,7 @@ import com.bonus.common.domain.analysis.po.ProComposition; import com.bonus.common.domain.analysis.vo.AnalysisLabelItemOcrVo; import com.bonus.common.domain.ocr.dto.AnalysisOcrRequest; import com.bonus.common.domain.ocr.vo.AnalysisResponse; +import com.bonus.common.domain.ocr.vo.TwoAnalysisResponse; import com.bonus.common.domain.rabbitmq.dto.RabbitMqMessage; import com.bonus.common.utils.FileUtil; import com.bonus.file.config.MinioConfig; @@ -17,14 +18,16 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionAspectSupport; import javax.annotation.Resource; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.sql.Array; +import java.util.*; +import java.util.stream.Collectors; /** * @className:RabbitMQConsumerService @@ -52,7 +55,6 @@ public class RabbitMQConsumerService { private IASAnalysisService analysisService; - @RabbitListener( queues = "myQueue", containerFactory = "multiConsumerFactory" // 使用上面配置的工厂,保证按顺序消费 @@ -102,41 +104,188 @@ public class RabbitMQConsumerService { /** * 处理招标解析算法服务业务 + * * @param message * @return void * @author cwchen * @date 2025/11/29 13:25 */ - private void processBusiness(RabbitMqMessage message) { - // 更新项目或者标段的解析状态 - message.setAnalysisState("0"); - analysisService.updateProOrBidState(message); - // 更新项目或者标段的文件组成的解析状态 - message.setAnalysisState("0"); - analysisService.updateProCompositionState(message); - // 查询项目或标段的组成文件的解析状态 - List proCompositionList = analysisService.getProCompositionState(message); - // 招标解析执行一次处理 - String uploadPath = message.getUploadPath(); - File fileFromMinio = getFileFromMinio(uploadPath); - AnalysisResponse ocrResponse = performAnalysisRecognition(fileFromMinio); - String folderPath = Optional.ofNullable(ocrResponse) - .map(AnalysisResponse::getData) - .map(data -> data.get("folder_path")) - .filter(Objects::nonNull) - .map(Object::toString) - .orElse(null); - if(StringUtils.isNotBlank(folderPath)) { - // 招标解析执行二次处理 - performAnalysisRecognition2(message, folderPath); - }else{ - // 解析失败 + public void processBusiness(RabbitMqMessage message) { + try { + // 开始解析 更新项目或者标段的解析状态 && 项目或者标段的文件组成的解析状态 + message.setAnalysisState("0"); + analysisService.updateProOrBidState(message); + analysisService.updateProCompositionState(message); + + // 招标解析执行一次处理 + String uploadPath = message.getUploadPath(); + File fileFromMinio = getFileFromMinio(uploadPath); + AnalysisResponse ocrResponse = performAnalysisRecognition(fileFromMinio); + String folderPath = Optional.ofNullable(ocrResponse) + .map(AnalysisResponse::getData) + .map(data -> data.get("folder_path")) + .filter(Objects::nonNull) + .map(Object::toString) + .orElse(null); + if (StringUtils.isNotBlank(folderPath)) { + // 招标解析执行二次处理 + performAnalysisRecognition2(message, folderPath); + // 更新文件组成的解析状态 + message.setAnalysisState("1"); + analysisService.updateProCompositionState(message); + // 查询项目或标段的组成文件的解析状态 + List proCompositionList = analysisService.getProCompositionState(message); + Integer analysisState = determineAnalysisState(proCompositionList); + if (analysisState != null) { + message.setAnalysisState(analysisState.toString()); + analysisService.updateProOrBidState(message); + } + } else { + // 第一次解析失败 + message.setAnalysisState("2"); + analysisService.updateProOrBidState(message); + analysisService.updateProCompositionState(message); + } + } catch (Exception e) { message.setAnalysisState("2"); + analysisService.updateProOrBidState(message); + analysisService.updateProCompositionState(message); + throw new RuntimeException(e); + } + } + + + /** + * 解析一次处理 + * + * @param file + * @return AnalysisResponse + * @author cwchen + * @date 2025/11/29 13:30 + */ + private AnalysisResponse performAnalysisRecognition(File file) { + try { + AnalysisOcrRequest analysisOcrRequest = buildOcrRequest(file); + AnalysisResponse ocrResponse = analysisOcrService.callOcrService(analysisOcrRequest); + // 修复:检查 招标解析算法服务 响应是否为 null + if (Objects.isNull(ocrResponse)) { + throw new RuntimeException("招标解析算法服务返回结果为空"); + } + log.info("OCR识别成功 - 数据: {}", ocrResponse.getData()); + return ocrResponse; + } catch (Exception e) { + log.error("OCR识别失败", e); + throw new RuntimeException("OCR识别失败: " + e.getMessage(), e); } } + /** + * 解析二次处理 + * + * @param message + * @param folderPath + * @return void + * @author cwchen + * @date 2025/12/6 13:21 + */ + public boolean performAnalysisRecognition2(RabbitMqMessage message, String folderPath) { + try { + // 查询项目/标段解析标签是否已经保存,未保存则从配置中读取 已保存则从项目/标段解析结果中读取 + int result = analysisService.isLabelExist(message); + List labelItemVoList = null; + if (result == 0) { + // 保存解析标签数据 + labelItemVoList = analysisService.getAnalysisLabels(message); + analysisService.saveProBidAnalysisData(message, labelItemVoList); + } else { + labelItemVoList = analysisService.getAnalysisLabelsByProOrBid(message); + } + AnalysisOcrRequest analysisOcrRequest = buildOcrRequest2(folderPath, labelItemVoList); + AnalysisResponse ocrResponse2 = analysisOcrService.callOcrService(analysisOcrRequest); + // 修复:检查 招标解析算法服务 响应是否为 null + if (Objects.isNull(ocrResponse2)) { + throw new RuntimeException("招标解析算法服务返回结果为空"); + } + log.info("OCR识别成功 - 数据: {}", ocrResponse2.getData()); + Map data = ocrResponse2.getData(); + List analysisDataList = new ArrayList<>(); + // 更新解析标签数据 + analysisService.updateProBidAnalysisData(message, analysisDataList); + return true; + } catch (IOException e) { + log.error("OCR识别失败", e); + throw new RuntimeException("OCR识别失败: " + e.getMessage(), e); + } + } + + /** + * 构建招标解析算法服务请求 - 一次处理 + * + * @param file + * @return OcrRequest + * @author cwchen + * @date 2025/11/29 13:29 + */ + private AnalysisOcrRequest buildOcrRequest(File file) { + AnalysisOcrRequest ocrRequest = new AnalysisOcrRequest(); + ocrRequest.setFile(file); + ocrRequest.setType(FileUtil.getMimeTypeByFilename(file.getName())); + ocrRequest.setAnalysisType("1"); + return ocrRequest; + } + + /** + * 构建招标解析算法服务请求 - 二次处理 + * + * @return OcrRequest + * @author cwchen + * @date 2025/11/29 13:29 + */ + private AnalysisOcrRequest buildOcrRequest2(String filePath, List list) { + AnalysisOcrRequest ocrRequest = new AnalysisOcrRequest(); + ocrRequest.setExtraction_items(list); + ocrRequest.setDoc_folder_path(filePath); + ocrRequest.setAnalysisType("2"); + return ocrRequest; + } + + /** + * 处理数据 + * + * @param proCompositionList + * @return Integer + * @author cwchen + * @date 2025/12/6 12:59 + */ + public Integer determineAnalysisState(List proCompositionList) { + // 1. 判空处理 + if (proCompositionList == null || proCompositionList.isEmpty()) { + return null; + } + + // 2. 提取所有的 analysisState 并过滤掉 null + List states = proCompositionList.stream() + .map(ProComposition::getAnalysisState) + .filter(Objects::nonNull) // 需要 import java.util.Objects; + .collect(Collectors.toList()); + + // 3. 根据规则判断 + if (states.isEmpty()) { + return null; // 规则: 都是 null (或列表为空) + } + if (states.contains("2")) { + return 2; // 规则: 存在一个 2 + } + if (states.contains("0")) { + return 0; // 规则: 存在至少一个 0 (且不存在 2) + } + + return 1; // 规则: 剩下的情况也就是都是 1 了 + } + /** * 从minio中获取文件 + * * @param uploadPath * @return File * @author cwchen @@ -153,81 +302,4 @@ public class RabbitMQConsumerService { throw new RuntimeException("获取Minio文件失败: " + uploadPath, e); } } - - /** - * 调用算法服务 - * @param file - * @return AnalysisResponse - * @author cwchen - * @date 2025/11/29 13:30 - */ - private AnalysisResponse performAnalysisRecognition(File file) { - try { - AnalysisOcrRequest analysisOcrRequest = buildOcrRequest(file); - AnalysisResponse ocrResponse = analysisOcrService.callOcrService(analysisOcrRequest); - // 修复:检查 招标解析算法服务 响应是否为 null - if (Objects.isNull(ocrResponse)) { - throw new RuntimeException("招标解析算法服务返回结果为空"); - } - log.info("OCR识别成功 - 数据: {}", ocrResponse.getData()); - return ocrResponse; - - } catch (Exception e) { - log.error("OCR识别失败", e); - throw new RuntimeException("OCR识别失败: " + e.getMessage(), e); - } - } - - public void performAnalysisRecognition2(RabbitMqMessage message,String folderPath){ - try { - - if(message.getBidId() == null){ - // 1.项目解析 - - }else { - // 2.标段解析 - } - - List labelItemVoList = analysisService.getAnalysisLabels(message.getAnalysisLabelId(),message.getTemplateId()); - AnalysisOcrRequest analysisOcrRequest = buildOcrRequest2(folderPath, labelItemVoList); - AnalysisResponse ocrResponse2 = analysisOcrService.callOcrService(analysisOcrRequest); - // 修复:检查 招标解析算法服务 响应是否为 null - if (Objects.isNull(ocrResponse2)) { - throw new RuntimeException("招标解析算法服务返回结果为空"); - } - log.info("OCR识别成功 - 数据: {}", ocrResponse2.getData()); - } catch (IOException e) { - log.error("OCR识别失败", e); - throw new RuntimeException("OCR识别失败: " + e.getMessage(), e); - } - } - - /** - * 构建招标解析算法服务请求 - 一次处理 - * @param file - * @return OcrRequest - * @author cwchen - * @date 2025/11/29 13:29 - */ - private AnalysisOcrRequest buildOcrRequest(File file) { - AnalysisOcrRequest ocrRequest = new AnalysisOcrRequest(); - ocrRequest.setFile(file); - ocrRequest.setType(FileUtil.getMimeTypeByFilename(file.getName())); - ocrRequest.setAnalysisType("1"); - return ocrRequest; - } - - /** - * 构建招标解析算法服务请求 - 二次处理 - * @return OcrRequest - * @author cwchen - * @date 2025/11/29 13:29 - */ - private AnalysisOcrRequest buildOcrRequest2(String filePath,List list) { - AnalysisOcrRequest ocrRequest = new AnalysisOcrRequest(); - ocrRequest.setExtraction_items(list); - ocrRequest.setDoc_folder_path(filePath); - ocrRequest.setAnalysisType("2"); - return ocrRequest; - } } \ No newline at end of file