package com.supervision.police.service.impl; import cn.hutool.cache.CacheUtil; import cn.hutool.cache.impl.TimedCache; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.supervision.constant.EvidencePropertyName; import com.supervision.constant.OcrStatusEnum; import com.supervision.police.domain.EvidenceFile; import com.supervision.police.domain.FileEvidenceProperty; import com.supervision.police.domain.FileOcrProcess; import com.supervision.police.dto.*; import com.supervision.police.service.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.aop.framework.AopContext; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.stream.Collectors; @Slf4j @Service @RequiredArgsConstructor public class OCREvidenceServiceImpl implements OCREvidenceService { private final FileOcrProcessService ocrProcessService; private final OCRService ocrService; private final FileEvidencePropertyService fileEvidencePropertyService; private final EvidenceFileService evidenceFileService; private final CaseEvidenceService caseEvidenceService; private final OcrExtractService ocrExtractService; private final TimedCache timedCache = CacheUtil.newTimedCache(5*60*1000); @Override public Boolean submitOrcTask(String fileId) { Assert.notEmpty(fileId, "文件id不能为空"); ((OCREvidenceService)AopContext.currentProxy()).asyncDoOrcTask(null, fileId); return true; } /** * 识别文件信息 由于识别文件标题需要通过组文件进行上下文分析,所以废弃单个文件识别的方式 * @param evidenceId * @param fileId * @return */ @Deprecated @Override @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager") public List doOrcTask(String evidenceId,String fileId) { List evidencePropertyList = new ArrayList<>(); // 识别文件信息 List fileOcrProcesses = ocrProcessService.syncSubmitOCR(CollUtil.newArrayList(fileId)); if (CollUtil.isEmpty(fileOcrProcesses)){ log.warn("subOrcTask:文件id:{} 识别失败", fileId); return evidencePropertyList; } for (FileOcrProcess fileOcrProcess : fileOcrProcesses) { // 调用提取属性接口 fileOcrProcess = ocrProcessService.getById(fileOcrProcess.getId()); if (StrUtil.isEmpty(fileOcrProcess.getOcrText())){ log.warn("subOrcTask:文件id:{}, 对应的ocrText属性为空,跳过属性识别...", fileId); continue; } log.debug("subOrcTask:远程接口retrieve开始识别文本...:{}", fileOcrProcess.getOcrText()); RetrieveResDTO retrieve = ocrService.retrieve(new RetrieveReqDTO(fileOcrProcess.getOcrText())); log.info("subOrcTask:文件id:{} 识别结果:{}", fileId, JSONUtil.toJsonStr(retrieve)); if (Integer.valueOf(0).equals(retrieve.getStatus())){ // 保存属性信息 String title = retrieve.getTitle(); if (StrUtil.isNotEmpty(title)){ evidencePropertyList.addAll(this.saveEvidenceProperty(evidenceId, fileId, Map.of(EvidencePropertyName.TITLE.getName(), retrieve.getTitle()))); if (StrUtil.isNotEmpty(evidenceId)){ evidenceFileService.save(new EvidenceFile(evidenceId,fileId)); } }else { log.warn("subOrcTask:文件id:{} 识别结果为空", fileId); } } } return evidencePropertyList; } @Async @Override public void asyncDoOrcTask(String evidenceId, String fileId) { ocrProcessService.syncSubmitOCR(CollUtil.newArrayList(fileId)); } @Override @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager") public List saveEvidenceProperty(String evidenceId,String fileId ,Map propertyMap) { // 先清空数据 fileEvidencePropertyService.removeByEvidenceAndFileId(evidenceId,fileId); List fileEvidence = toFileEvidence(evidenceId, fileId, propertyMap); for (FileEvidenceProperty property : fileEvidence) { fileEvidencePropertyService.save(property); } return fileEvidence; } @Override public List queryFileList(List fileIdList) { if (CollUtil.isEmpty(fileIdList)){ return new ArrayList<>(1); } List ocrEvidencePropertyDTOS = fileEvidencePropertyService.listPrewByFileIdList(fileIdList); List collect = ocrEvidencePropertyDTOS.stream() .collect(Collectors.groupingBy(OCREvidencePropertyDTO::getFileId)) .values().stream() .map(EvidenceFileOCRDTO::new).peek(evidenceFileOCRDTO -> { if (StrUtil.isEmpty(evidenceFileOCRDTO.getEvidenceType())){ evidenceFileOCRDTO.setEvidenceType("1");// 默认为书证 } evidenceFileOCRDTO.setEvidenceNameValue(); }) .collect(Collectors.toList()); return sortByIdOrder(fileIdList, collect); } @Override @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager") public String saveEvidence(EvidenceFileOCRDTO evidenceFileOCRDTO) { Assert.notEmpty(evidenceFileOCRDTO.getFileId(), "文件id不能为空"); Assert.notEmpty(evidenceFileOCRDTO.getCaseId(), "案件id不能为空"); Assert.notEmpty(evidenceFileOCRDTO.getEvidenceName(), "案件证据名称不能为空"); Assert.notEmpty(evidenceFileOCRDTO.getEvidenceType(), "案件证据类型不能为空"); //保存证据信息 String evidenceId = caseEvidenceService.saveEvidence(evidenceFileOCRDTO.toCaseEvidenceDTO()); // 保存证据属性 List evidencePropertyList = saveEvidenceProperty(evidenceId, evidenceFileOCRDTO.getFileId(), evidenceFileOCRDTO.getProperty()); if (CollUtil.isEmpty(evidencePropertyList)){ return null; } return CollUtil.getFirst(evidencePropertyList).getEvidenceId(); } @Override public Boolean retrieveTitle(List fileIdList) { if (CollUtil.isEmpty(fileIdList)){ return false; } ((OCREvidenceService)(AopContext.currentProxy())).asyncRetrieveTitle(fileIdList); return true; } @Override public Boolean retrieveTitleProcess(List fileIdList) { if (CollUtil.isEmpty(fileIdList)){ return true; } boolean timeOut = processTimeOut(fileIdList); if (timeOut){ log.info("retrieveTitleProcess:文件识别超时,跳过等待..."); return true; } List properties = fileEvidencePropertyService.lambdaQuery().in(FileEvidenceProperty::getFileId, fileIdList).list(); for (String fileId : fileIdList) { if (!evidencePropertiesExist(fileId, properties)){ return false; } } return true; } private boolean processTimeOut(List fileIdList) { String join = StrUtil.join(",", fileIdList); if (!timedCache.containsKey(join)){ timedCache.put(join, DateUtil.date()); return false; } DateTime dateTime = timedCache.get(join); return DateUtil.between(dateTime, DateUtil.date(), DateUnit.SECOND) > fileIdList.size() * 20L; } @Override @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager") public List batchSaveEvidence(List evidenceFileOCRDTOS) { List evidenceList = new ArrayList<>(); if (CollUtil.isEmpty(evidenceFileOCRDTOS)){ return evidenceList; } for (EvidenceFileOCRDTO evidenceFileOCRDTO : evidenceFileOCRDTOS) { String evidence = saveEvidence(evidenceFileOCRDTO); evidenceList.add(evidence); } return evidenceList; } @Async @Override @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager") public void asyncRetrieveTitle(List fileIdList) { doRetrieveTitle(fileIdList); } @Override @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager") public void doRetrieveTitle(List fileIdList) { List recordFileDTOS = waitingOCRFinish(fileIdList, 30 * fileIdList.size()); if (CollUtil.isEmpty(recordFileDTOS)){ log.warn("doRetrieveTitle:文件id:{} 未查询到文件信息...", StrUtil.join(",", fileIdList)); return; } List dtoList = recordFileDTOS.stream().map(recordFileDTO -> { OcrExtractDto dto = new OcrExtractDto(); dto.setId(recordFileDTO.getFileId()); dto.setText(recordFileDTO.getOcrText()); return dto; }).toList(); log.info("doRetrieveTitle,extractTitle参数是:{}", JSONUtil.toJsonStr(dtoList)); List result = ocrExtractService.extractTitle(dtoList); log.info("doRetrieveTitle,extractTitle返回结果是:{}", JSONUtil.toJsonStr(result)); if (CollUtil.isEmpty(result)){ log.warn("retrieveTitle:文件id:{} 提取标题失败", StrUtil.join(",", fileIdList)); } for (OcrExtractDto dto : result) { this.saveEvidenceProperty(null, dto.getId(), Map.of(EvidencePropertyName.TITLE.getName(), dto.getTitle())); } } @Override @Transactional(transactionManager = "dataSourceTransactionManager",rollbackFor = Exception.class) public CaseEvidenceDetailDTO queryEvidenceDetail(String evidenceId) { CaseEvidenceDetailDTO caseEvidenceDetailDTO = caseEvidenceService.queryEvidenceDetail(evidenceId); if (caseEvidenceDetailDTO == null){ return null; } List list = fileEvidencePropertyService.lambdaQuery().eq(FileEvidenceProperty::getEvidenceId, evidenceId).list(); caseEvidenceDetailDTO.setPropertyValue(list); return caseEvidenceDetailDTO; } private List waitingOCRFinish(List fileIdList, int timeout){ DateTime date = DateUtil.date(); while (true){ List recordFileDTOS = ocrProcessService.queryFileListWithIdSort(fileIdList); boolean match = recordFileDTOS.stream().allMatch(recordFileDTO -> StrUtil.equals(recordFileDTO.getOcrStatus(), OcrStatusEnum.FAIL.getCode()) || StrUtil.equals(recordFileDTO.getOcrStatus(), OcrStatusEnum.SUCCESS.getCode())); if (match){ return recordFileDTOS; } log.info("waitingOCRFinish:等待文件id:{} 识别完成中...", StrUtil.join(",", fileIdList)); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } if (DateUtil.between(date, DateUtil.date(), DateUnit.SECOND) > timeout){ return null; } } } private boolean evidencePropertiesExist(String fileId, List properties){ for (FileEvidenceProperty property : properties) { if (StrUtil.equals(fileId, property.getFileId())){ return true; } } return false; } private List sortByIdOrder(List fileIdList, List evidenceFileOCRDTOS){ Map fileOCRDTOMap = evidenceFileOCRDTOS.stream() .collect(Collectors.toMap(EvidenceFileOCRDTO::getFileId, recordFileDTO -> recordFileDTO, (k1, k2) -> k1)); return fileIdList.stream().map(fileId -> { EvidenceFileOCRDTO evidenceFileOCRDTO = fileOCRDTOMap.get(fileId); if (Objects.isNull(evidenceFileOCRDTO)) { evidenceFileOCRDTO = new EvidenceFileOCRDTO(); evidenceFileOCRDTO.setFileId(fileId); evidenceFileOCRDTO.setOcrStatus(-1); } return evidenceFileOCRDTO; } ).collect(Collectors.toList()); } private List toFileEvidence(String evidenceId,String fileId ,Map propertyMap){ if (StrUtil.isAllEmpty(evidenceId,fileId) || CollUtil.isEmpty(propertyMap)){ log.warn("saveEvidenceProperty:参数为空"); return new ArrayList<>(1); } return EvidencePropertyName.getByNames(propertyMap) .stream().map(evidence -> new FileEvidenceProperty(evidenceId, fileId, evidence.getName(), propertyMap.get(evidence.getName()))) .toList(); } }