package com.supervision.police.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; 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.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.supervision.common.domain.R; import com.supervision.common.utils.IPages; import com.supervision.common.utils.StringUtils; import com.supervision.police.domain.*; import com.supervision.police.dto.AtomicData; import com.supervision.police.dto.CaseAtomicIndexDTO; import com.supervision.police.dto.CaseAtomicResultWrapper; import com.supervision.police.dto.JudgeLogic; import com.supervision.police.mapper.CasePersonMapper; import com.supervision.police.mapper.ModelAtomicResultMapper; import com.supervision.police.mapper.ModelIndexMapper; import com.supervision.police.service.*; import com.supervision.utils.SqlParserUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.stream.Collectors; /** * 指标表(ModelIndex)表服务实现类 * * @author qmy * @since 2024-07-05 09:20:10 */ @Slf4j @Service @RequiredArgsConstructor public class ModelIndexServiceImpl extends ServiceImpl implements ModelIndexService { private final ComDictionaryService comDictionaryService; private final ModelIndexMapper modelIndexMapper; private final ModelAtomicIndexService modelAtomicIndexService; private final ModelCaseService modelCaseService; private final ModelAtomicResultMapper modelAtomicResultMapper; private final CasePersonMapper casePersonMapper; private final CaseStatusManageService caseStatusManageService; @Value("${case.evidence.table}") private List allowedTables; @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R selectAll(ModelIndex modelIndex, Integer page, Integer size) { IPage iPage = new Page<>(page, size); LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.like(modelIndex.getName() != null, ModelIndex::getName, modelIndex.getName()) .like(modelIndex.getShortName() != null, ModelIndex::getShortName, modelIndex.getShortName()) .like(modelIndex.getRemark() != null, ModelIndex::getRemark, modelIndex.getRemark()) .eq(StringUtils.isNotEmpty(modelIndex.getIndexType()), ModelIndex::getIndexType, modelIndex.getIndexType()) .eq(StringUtils.isNotEmpty(modelIndex.getCaseType()), ModelIndex::getCaseType, modelIndex.getCaseType()) .eq(ModelIndex::getDataStatus, "1"); iPage = modelIndexMapper.selectPage(iPage, wrapper); List records = iPage.getRecords(); List dicts = comDictionaryService.list(); for (ModelIndex index : records) { index.setIndexTypeName(comDictionaryService.getName(dicts, "index_type", index.getIndexType())); index.setCaseTypeName(comDictionaryService.getName(dicts, "case_type", index.getCaseType())); //原子指标 String judgeLogic = index.getJudgeLogic(); List ids = new ArrayList<>(); if (StringUtils.isNotEmpty(judgeLogic)) { List logic = JSONUtil.toList(judgeLogic, JudgeLogic.class); for (JudgeLogic judge : logic) { List atomicData = judge.getAtomicData(); for (AtomicData atomic : atomicData) { ids.add(atomic.getAtomicIndex()); } } List atomicIndexList = modelAtomicIndexService.selectBatchIds(ids); index.setAtomicIndexList(atomicIndexList); } index.setAtomicIndexNum(ids.size()); } iPage.setRecords(records); return R.ok(IPages.buildDataMap(iPage)); } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R addOrUpd(ModelIndex modelIndex) { int i = 0; if (StringUtils.isEmpty(modelIndex.getId())) { i = modelIndexMapper.insert(modelIndex); } else { i = modelIndexMapper.updateById(modelIndex); } if (i > 0) { return R.okMsg("保存成功"); } else { return R.fail("保存失败"); } } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R del(String id) { ModelIndex index = modelIndexMapper.selectById(id); index.setDataStatus(StringUtils.getUUID()); int i = modelIndexMapper.updateById(index); if (i > 0) { return R.okMsg("删除成功"); } else { return R.fail("删除失败"); } } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R selectAllAtomic(ModelAtomicIndex modelAtomicIndex, Integer page, Integer size) { IPage iPage = new Page<>(page, size); iPage = modelAtomicIndexService.selectAll(iPage, modelAtomicIndex); List records = iPage.getRecords(); List dicts = comDictionaryService.list(); for (ModelAtomicIndex index : records) { index.setCaseTypeName(comDictionaryService.getName(dicts, "case_type", index.getCaseType())); index.setIndexSourceName(comDictionaryService.getName(dicts, "index_source", index.getIndexSource())); index.setRecordTypeName(comDictionaryService.getName(dicts, "record_type", index.getRecordType())); } iPage.setRecords(records); return R.ok(IPages.buildDataMap(iPage)); } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R addOrUpdAtomic(ModelAtomicIndex modelAtomicIndex) { int i = 0; if (StringUtils.equals("2", modelAtomicIndex.getIndexSource())) { // 如果查询类型为数据查询,则校验查询语句 Assert.notEmpty(modelAtomicIndex.getQueryLang(), "查询语言不能为空"); Assert.isTrue(checkSql(modelAtomicIndex.getQueryLang()), "查询语句不合法"); } // 校验是否已经存在了相同名称的原子指标 ModelAtomicIndex existIndex = modelAtomicIndexService.lambdaQuery().eq(ModelAtomicIndex::getName, modelAtomicIndex.getName()).last("limit 1").one(); if (ObjectUtil.isNotEmpty(existIndex) && !StringUtils.equals(modelAtomicIndex.getId(), existIndex.getId())){ throw new RuntimeException("已存在相同名称的原子指标"); } if (StringUtils.isEmpty(modelAtomicIndex.getId())) { i = modelAtomicIndexService.getMapper().insert(modelAtomicIndex); } else { i = modelAtomicIndexService.getMapper().updateById(modelAtomicIndex); } if (i > 0) { return R.okMsg("保存成功"); } else { return R.fail("保存失败"); } } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R delAtomic(String id) { ModelAtomicIndex index = modelAtomicIndexService.getMapper().selectById(id); index.setDataStatus(StringUtils.getUUID()); int i = modelAtomicIndexService.getMapper().updateById(index); if (i > 0) { return R.okMsg("删除成功"); } else { return R.fail("删除失败"); } } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public List listCaseAtomicIndex(String caseId, String indexSource) { Assert.notEmpty(caseId, "案件id不能为空"); ModelCase modelCase = modelCaseService.getById(caseId); Assert.notNull(modelCase, "案件不存在"); String caseType = modelCase.getCaseType(); Assert.notEmpty(caseType, "案件类型不能为空"); List casePersonList = casePersonMapper.selectList(Wrappers.lambdaQuery(CasePerson.class) .eq(CasePerson::getCaseId, caseId).eq(CasePerson::getRoleCode, "1")); Assert.notEmpty(casePersonList, "案件行为人不能为空!"); String actorId = CollUtil.getFirst(casePersonList).getId(); // 获取案件类型对应的指标 List modelIndexList = modelIndexMapper.selectList( Wrappers.lambdaQuery(ModelIndex.class) .eq(ModelIndex::getCaseType, caseType).eq(ModelIndex::getDataStatus, "1")); if (CollUtil.isEmpty(modelIndexList)) { return new ArrayList<>(1); } // 从指标中计算出所有原子指标id List automicIndexIds = modelIndexList.stream().filter(modelIndex -> StrUtil.isNotEmpty(modelIndex.getJudgeLogic())) .map(modelIndex -> pickAtomicIndexIds(modelIndex.getJudgeLogic())) .flatMap(Collection::stream).distinct().toList(); if (CollUtil.isEmpty(automicIndexIds)) { return new ArrayList<>(1); } // 查询原子指标相关信息 List modelAtomicIndexList = modelAtomicIndexService.listCaseAtomicIndex(automicIndexIds, caseType, indexSource); if (CollUtil.isEmpty(modelAtomicIndexList)) { return new ArrayList<>(1); } Map modelAtomicIndexMap = modelAtomicIndexList.stream().collect(Collectors.toMap(ModelAtomicIndex::getId, v -> v, (v1, v2) -> v1)); // 查询判定结果数据 List modelAtomicResults = modelAtomicResultMapper.selectList( Wrappers.lambdaQuery(ModelAtomicResult.class).eq(ModelAtomicResult::getCaseId, caseId) .eq(ModelAtomicResult::getCasePersonId, actorId) .in(ModelAtomicResult::getAtomicId, automicIndexIds)); Map modelAtomicResultMap = modelAtomicResults.stream() .filter(modelAtomicResult -> StrUtil.isNotEmpty(modelAtomicResult.getAtomicId())).collect(Collectors.toMap(ModelAtomicResult::getAtomicId, v -> v, (v1, v2) -> v1)); Map> modelIndexMapAtomic = groupModelIndexByAtomicIndexId(modelIndexList); // 以原子指标为基准,组装数据 return modelAtomicIndexList.stream().flatMap(atomicIndex -> modelIndexMapAtomic.get(atomicIndex.getId()).stream().map(modelIndex -> new CaseAtomicIndexDTO(atomicIndex, modelIndex, modelAtomicResultMap.get(atomicIndex.getId()))) ).toList(); } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public Boolean saveCaseAtomicResult(CaseAtomicResultWrapper caseAtomicResultWrapper) { Assert.notEmpty(caseAtomicResultWrapper.getCaseId(), "案件id不能为空!"); ModelCase modelCase = modelCaseService.getById(caseAtomicResultWrapper.getCaseId()); Assert.notNull(modelCase, "案件不存在!"); // 设置行为人id List casePersonList = casePersonMapper.selectList(Wrappers.lambdaQuery(CasePerson.class) .eq(CasePerson::getCaseId, modelCase.getId()).eq(CasePerson::getRoleCode, "1")); Assert.notEmpty(casePersonList, "案件行为人不能为空!"); caseAtomicResultWrapper.setActorId(CollUtil.getFirst(casePersonList).getId()); //清空人工评估结果 removeCaseAtomicResult(caseAtomicResultWrapper.getCaseId(), modelCase.getCaseType(), caseAtomicResultWrapper.getActorId(), "1"); // 保存原子评估结果 caseAtomicResultWrapper.getCaseAtomicIndexList().stream().map(caseAtomicIndexDTO -> caseAtomicIndexDTO.toModelAtomicResult( caseAtomicResultWrapper.getCaseId(), caseAtomicResultWrapper.getActorId())).toList() .forEach(modelAtomicResultMapper::insert); caseStatusManageService.whenUpdateIndexResult(caseAtomicResultWrapper.getCaseId()); return true; } @Override public boolean checkSql(String sql) { log.info("checkSql:{}", sql); if (StringUtils.isEmpty(sql)) { return false; } if (CollUtil.isEmpty(this.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 -> !this.allowedTables.contains(table)).count(); if (count > 0) { log.warn("checkSql:表{}不在允许的表列表中", tableList); return false; } return true; } /** * 清空案件下的评估结果 * * @param caseId 案件id * @param caseType 案件类型 * @param actorId 行为人id * @param indexSource 原子指标来源 */ private void removeCaseAtomicResult(String caseId, String caseType, String actorId, String indexSource) { List modelAtomicIndexList = modelAtomicIndexService.listCaseAtomicIndex(null, caseType, indexSource); List automicIndexIds = modelAtomicIndexList.stream().map(ModelAtomicIndex::getId).distinct().toList(); modelAtomicResultMapper.delete(Wrappers.lambdaQuery(ModelAtomicResult.class) .eq(ModelAtomicResult::getCaseId, caseId) .eq(ModelAtomicResult::getCasePersonId, actorId) .in(CollUtil.isNotEmpty(automicIndexIds), ModelAtomicResult::getAtomicId, automicIndexIds)); } /** * 从逻辑中获取原子指标id * * @param judgeLogic 判断逻辑json字符串 * @return 原子指标id(不重复) */ private List pickAtomicIndexIds(String judgeLogic) { List ids = new ArrayList<>(); List logic = JSONUtil.toList(judgeLogic, JudgeLogic.class); for (JudgeLogic judge : logic) { List atomicData = judge.getAtomicData(); for (AtomicData atomic : atomicData) { if (!ids.contains(atomic.getAtomicIndex())) { ids.add(atomic.getAtomicIndex()); } } } return ids; } private Map> groupModelIndexByAtomicIndexId(List modelIndexList) { Map> groupMap = new HashMap<>(); if (CollUtil.isEmpty(modelIndexList)) { return groupMap; } for (ModelIndex modelIndex : modelIndexList) { if (StrUtil.isEmpty(modelIndex.getJudgeLogic())) { continue; } String judgeLogic = modelIndex.getJudgeLogic(); List judgeLogicList = JSONUtil.toList(judgeLogic, JudgeLogic.class); for (JudgeLogic logic : judgeLogicList) { List atomicData = logic.getAtomicData(); if (CollUtil.isEmpty(atomicData)) { continue; } for (AtomicData atomic : atomicData) { List modelIndexs = groupMap.getOrDefault(atomic.getAtomicIndex(), new ArrayList<>()); modelIndexs.add(modelIndex); groupMap.put(atomic.getAtomicIndex(), modelIndexs); } } } return groupMap; } }