package com.supervision.police.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.deepoove.poi.XWPFTemplate; import com.supervision.common.domain.R; import com.supervision.common.utils.StringUtils; import com.supervision.constant.JudgeResultEnum; import com.supervision.constant.ScoreEnum; import com.supervision.neo4j.dto.ResultDTO; import com.supervision.neo4j.utils.Neo4jUtils; import com.supervision.police.domain.*; import com.supervision.police.dto.AnalyseCaseDTO; import com.supervision.police.dto.AtomicData; import com.supervision.police.dto.CaseStatus; import com.supervision.police.dto.JudgeLogic; import com.supervision.police.dto.caseScore.CaseScoreDetailBuilder; import com.supervision.police.dto.caseScore.CaseScoreDetailDTO; import com.supervision.police.mapper.*; import com.supervision.police.mybatis.RowSqlMapper; import com.supervision.police.service.CaseStatusManageService; import com.supervision.police.service.ModelIndexService; import com.supervision.police.service.ModelService; import com.supervision.utils.SqlParserUtil; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.neo4j.driver.Driver; import org.neo4j.driver.Result; import org.neo4j.driver.Session; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Service; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; @Slf4j @Service @RequiredArgsConstructor public class ModelServiceImpl implements ModelService { private final ResourceLoader resourceLoader; private final Driver driver; private final ModelCaseMapper modelCaseMapper; private final ModelAtomicIndexMapper modelAtomicIndexMapper; private final ModelAtomicResultMapper modelAtomicResultMapper; private final ModelIndexMapper modelIndexMapper; private final ModelIndexResultMapper modelIndexResultMapper; private final CasePersonMapper casePersonMapper; private final RowSqlMapper rowSqlMapper; private final ModelIndexService modelIndexService; private final CaseStatusManageService caseStatusManageService; @Override public R analyseCase(AnalyseCaseDTO analyseCaseDTO) { CaseStatus caseDateStatus = this.getCaseDateStatus(analyseCaseDTO.getCaseId()); String check = caseDateStatus.check(); Assert.isTrue(StrUtil.isEmpty(check), check); ModelCase modelCase = modelCaseMapper.selectById(analyseCaseDTO.getCaseId()); // 获取案件行为人ID CasePerson casePerson = casePersonMapper.selectOne(new LambdaQueryWrapper() .eq(CasePerson::getCaseId, analyseCaseDTO.getCaseId()) .eq(CasePerson::getCaseActorFlag, 1) .eq(CasePerson::getRoleCode, "1") .eq(CasePerson::getName, analyseCaseDTO.getLawActorName())); if (ObjectUtil.isEmpty(casePerson)) { throw new RuntimeException("未找到的行为人" + analyseCaseDTO.getLawActorName()); } // 首先将原先的值进行赋值,设置到pre_result modelIndexResultMapper.updatePreResult(analyseCaseDTO.getCaseId()); //原子指标 List atomicIndices = modelAtomicIndexMapper.selectByCaseType(modelCase.getCaseType()); Map atomicResultMap = new HashMap<>(); for (ModelAtomicIndex atomicIndex : atomicIndices) { //原子指标结果 ModelAtomicResult result = new ModelAtomicResult(); result.setCasePersonId(casePerson.getId()); result.setCaseId(analyseCaseDTO.getCaseId()); result.setAtomicId(atomicIndex.getId()); //查询语句 String ql = atomicIndex.getQueryLang(); //原子指标结果表 try { // index_source==1 // List list = analyseCaseDTO.getAtomicIndexList(); //index_source==3 //查询图谱 index_source: 1人工定义 2数据库查询 3图谱生成 4大模型 if ("1".equals(atomicIndex.getIndexSource())) { // list manuallyDefinedCase(analyseCaseDTO, result, atomicIndex); } else if ("2".equals(atomicIndex.getIndexSource()) && StringUtils.isNotEmpty(ql)) { // analyseDataBaseCase(analyseCaseDTO, result, ql); } else if ("3".equals(atomicIndex.getIndexSource()) && StringUtils.isNotEmpty(ql)) { // 使用知识图谱进行计算 analyseGraphCase(analyseCaseDTO, result, ql); } else if ("4".equals(atomicIndex.getIndexSource())) { // } } catch (Exception e) { log.error(e.getMessage(), e); } // 非人工填写的,需要进行更新结果,人工填写的不更新(人工填写的编码,需要进行特殊处理) if (!"1".equals(atomicIndex.getIndexSource())) { //保存原子指标结果表 ModelAtomicResult exist = modelAtomicResultMapper.selectByCaseIdAndAtomicId(analyseCaseDTO.getCaseId(), casePerson.getId(), atomicIndex.getId()); if (exist == null) { modelAtomicResultMapper.insert(result); } else { result.setId(exist.getId()); modelAtomicResultMapper.updateById(result); } } // 所有原子指标id,判断结果 Set checkAtomicResult = Set.of("-1", "0", "1"); if (StrUtil.isNotBlank(result.getAtomicResult()) && !checkAtomicResult.contains(result.getAtomicResult())) { log.error("TEST:不被支持的校核结果,需排查BUG,atomicId:" + result.getAtomicId() + " result:" + result.getAtomicResult()); } atomicResultMap.put(result.getAtomicId(), result.getAtomicResult()); } // 最终计算得分 calculateFinalScore(analyseCaseDTO, modelCase, atomicResultMap); caseStatusManageService.whenAnalyseCaseSuccess(analyseCaseDTO.getCaseId(), modelCase.getTotalScore()); return R.ok(); } /** * 手动定义原子指标处理 * * @param analyseCaseDTO * @param result * @param atomicIndex */ private void manuallyDefinedCase(AnalyseCaseDTO analyseCaseDTO, ModelAtomicResult result, ModelAtomicIndex atomicIndex) { List modelAtomicResults = modelAtomicResultMapper.selectList( new LambdaQueryWrapper().eq(ModelAtomicResult::getCaseId, analyseCaseDTO.getCaseId()) .eq(ModelAtomicResult::getAtomicId, atomicIndex.getId())); if (CollUtil.isEmpty(modelAtomicResults)) { log.info("manuallyDefinedCase:根据caseId:{},auomicId:{}未找到原子指标结果", analyseCaseDTO.getCaseId(), atomicIndex.getId()); return; } if (CollUtil.size(modelAtomicResults) > 0) { log.warn("manuallyDefinedCase:根据caseId:{},auomicId:{}找到多个原子指标结果", analyseCaseDTO.getCaseId(), atomicIndex.getId()); } ModelAtomicResult modelAtomicResult = CollUtil.getFirst(modelAtomicResults); // 对结果进行映射操作,前端填的是1-5,我们这里应该是-1,0,1 JudgeResultEnum instance = JudgeResultEnum.getInstance(modelAtomicResult.getAtomicResult()); result.setAtomicResult(instance.getTranslateResult()); } @Override public CaseScoreDetailDTO caseScoreDetail(String caseId) { Assert.notEmpty(caseId, "案件id不能为空"); CaseScoreDetailBuilder caseScoreDetailBuilder = new CaseScoreDetailBuilder() .setOllamaChatClient(null) .setMapper(modelCaseMapper, casePersonMapper, modelAtomicIndexMapper, modelIndexResultMapper, modelIndexMapper, modelAtomicResultMapper); caseScoreDetailBuilder.loadCaseScoreDetail(caseId); return caseScoreDetailBuilder.build(); } @Override public CaseStatus getCaseDateStatus(String caseId) { Assert.notEmpty(caseId, "案件id不能为空"); ModelCase modelCase = modelCaseMapper.selectById(caseId); Assert.notNull(modelCase, "案件不存在"); return new CaseStatus(modelCase.getCaseDataStatus(), modelCase.getCaseAnalysisStatus()); } @Override public void exportCaseScoreDetail(String caseId, HttpServletResponse response) { CaseScoreDetailDTO caseScoreDetailDTO = caseScoreDetail(caseId); caseScoreDetailDTO.buildDesc(); // 加载模板 Resource resource = resourceLoader.getResource("classpath:template/case_detail.docx"); InputStream inputStream = null; try { response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(caseScoreDetailDTO.getCaseName() + ".docx", StandardCharsets.UTF_8)); inputStream = resource.getInputStream(); XWPFTemplate template = XWPFTemplate.compile(inputStream).render(caseScoreDetailDTO); template.writeAndClose(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.error("exportCaseScoreDetail: inputStream.close() error", e); } } } } /** * 最终计算得分 */ private void calculateFinalScore(AnalyseCaseDTO analyseCaseDTO, ModelCase modelCase, Map atomicResultMap) { // 计算指标结果 int score = 0; // 根据案件类型获取所有的指标 List modelIndices = modelIndexMapper.selectByCaseType(modelCase.getCaseType()); for (ModelIndex modelIndex : modelIndices) { ModelIndexResult result = new ModelIndexResult(); result.setCaseId(analyseCaseDTO.getCaseId()); result.setIndexId(modelIndex.getId()); Set atomicIds = new HashSet<>(); // 判断逻辑是否为空,如果不为空,就根据判断逻辑进行判断 if (StringUtils.isNotEmpty(modelIndex.getJudgeLogic())) { List judgeLogics = JSONUtil.toList(modelIndex.getJudgeLogic(), JudgeLogic.class); if (CollUtil.isNotEmpty(judgeLogics)) { boolean finalJudgeResult = false; // 遍历组 for (int i = 0; i < judgeLogics.size(); i++) { // 组内结果 boolean innerGroupJudge = false; JudgeLogic logic = judgeLogics.get(i); // 获取组之间的的判断逻辑 String rowLogic = logic.getRowLogic(); // 首先对组内进行判断,判断组内的结果 List atomicData = logic.getAtomicData(); for (int j = 0; j < atomicData.size(); j++) { AtomicData data = atomicData.get(j); atomicIds.add(data.getAtomicIndex()); // 这里可能不存在,如果未找到,就默认为false String atomicIndexResult = atomicResultMap.getOrDefault(data.getAtomicIndex(), "-1"); String relationalSymbol = data.getRelationalSymbol(); JudgeResultEnum instance = JudgeResultEnum.getInstance(relationalSymbol); boolean ato = StrUtil.equals(atomicIndexResult, instance.getTranslateResult()); if (j == 0) { innerGroupJudge = ato; } else { if ("1".equals(rowLogic)) { innerGroupJudge = innerGroupJudge && ato; } else if ("2".equals(rowLogic)) { innerGroupJudge = innerGroupJudge || ato; } } } // 组间进行判断 String groupLogic = logic.getGroupLogic(); if (i == 0) { finalJudgeResult = innerGroupJudge; } else { // 如果组间判断为1 与,则进行与操作 if ("1".equals(groupLogic)) { finalJudgeResult = finalJudgeResult && innerGroupJudge; // 如果组间判断为或,则进行或操作 } else if ("2".equals(groupLogic)) { finalJudgeResult = finalJudgeResult || innerGroupJudge; } } } result.setIndexResult(finalJudgeResult ? "true" : "false"); result.setAtomicIds(StringUtils.join(atomicIds, ",")); } } // 最后保存结果 ModelIndexResult exist = modelIndexResultMapper.selectByCaseIdAndIndexId(analyseCaseDTO.getCaseId(), modelIndex.getId()); if (exist == null) { modelIndexResultMapper.insert(result); } else { result.setId(exist.getId()); modelIndexResultMapper.updateById(result); } if ("true".equals(result.getIndexResult())) { score = score + modelIndex.getIndexScore(); } } modelCase.setTotalScore(score); modelCaseMapper.updateById(modelCase); } private void analyseGraphCase(AnalyseCaseDTO analyseCaseDTO, ModelAtomicResult result, String ql) { Session session = driver.session(); //图谱 int i = 1; Map params = new HashMap<>(); // 行为人 params.put("lawActor", analyseCaseDTO.getLawActorName()); // 案号 params.put("caseId", analyseCaseDTO.getCaseId()); // 参数中是否传了受害人 String lawPartys = analyseCaseDTO.getLawParty(); // 如果有受害人的话就进行分割 String[] split = StringUtils.isEmpty(lawPartys) ? new String[0] : lawPartys.split(","); if (ql.contains("$lawParty") && split.length > 1) { i = split.length; } // 只有有符合的,就认为符合,不进行遍历了 judge: for (int j = 0; j < i; j++) { if (split.length > 0) { params.put("lawParty", split[j]); } Result run; try { run = session.run(ql, params); } catch (Exception e) { result.setAtomicResult("-1"); log.error("图数据库查询出现错误,查询语句{},参数{}", ql, JSONUtil.toJsonStr(params), e); return; } List res = Neo4jUtils.getResultDTOList(run); if (res.isEmpty()) { result.setAtomicResult("-1"); } else { // 设置为0,不存在 result.setAtomicResult("0"); // 进行遍历,如果有存在的,就设置为有 for (ResultDTO resultDTO : res) { if (StringUtils.isNotEmpty(resultDTO.getRelId())) { //存在关系 result.setAtomicResult("1"); result.setRecordSplitId(resultDTO.getRecordSplitId()); result.setRecordId(resultDTO.getRecordId()); // 如果只要存在,就不进行校验了,直接跳出循环 break judge; } } } } } /** * 分析数据库类型的原子信息 * * @param analyseCaseDTO * @param result * @param sql */ private void analyseDataBaseCase(AnalyseCaseDTO analyseCaseDTO, ModelAtomicResult result, String sql) { Map params = new HashMap<>(); params.put("caseId", result.getCaseId()); params.put("evidenceName", analyseCaseDTO.getEvidenceName()); params.put("provider", null); params.put("party_a", analyseCaseDTO.getLawActorName()); params.put("party_b", analyseCaseDTO.getLawParty()); boolean success = false; if (modelIndexService.checkSql(sql)) { success = parseResult(rowSqlMapper.selectList(sql, params, Map.class)); } result.setAtomicResult(success ? "1" : "0"); } private boolean checkSql(String sql, List allowedTables) { if (StringUtils.isEmpty(sql)) { return false; } if (CollUtil.isEmpty(allowedTables)) { log.info("checkSql:未配置允许的表"); return false; } MySqlStatementParser parser = new MySqlStatementParser(sql); SQLStatement sqlStatement = SqlParserUtil.parseStatement(parser); if (Objects.isNull(sqlStatement)) { log.warn("checkSql sql:{}语句解析失败", sql); return false; } String sqlType = SqlParserUtil.detectSQLType(sqlStatement); if (!"SELECT".equals(sqlType)) { log.warn("checkSql:只支持查询类型语句"); return false; } List tableList = SqlParserUtil.extractTableNames(sqlStatement); if (CollUtil.isEmpty(tableList)) { log.warn("checkSql:未检测到表"); return false; } long count = tableList.stream().filter(table -> !allowedTables.contains(table)).count(); if (count > 0) { log.warn("checkSql:表{}不在允许的表列表中", tableList); return false; } return true; } /** * 执行结果分析: * 1. 如果查询出的结果只有一行,判断列数是否大于1,如果大于1,返回真,如果=1,继续判断值是否大于0,如果大于0,返回真,如果=0,返回假 * 2. 如果查询出的结果大于一行,则返回真 * * @param mapList 查询结果 * @return */ private boolean parseResult(List mapList) { if (CollUtil.isEmpty(mapList)) { return false; } if (CollUtil.size(mapList) > 1) { return true; } Map firstRow = CollUtil.getFirst(mapList); if (firstRow.size() == 1 && CollUtil.size(firstRow.values()) == 1) { Object first = CollUtil.getFirst(firstRow.values()); if (NumberUtil.isNumber(first.toString())) { return NumberUtil.parseInt(first.toString()) > 0; } } return true; } }