package com.supervision.police.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Pair; 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.constant.IndexRuleConstants; import com.supervision.common.constant.NotePromptConstants; import com.supervision.common.domain.R; import com.supervision.common.utils.IPages; import com.supervision.common.utils.StringUtils; import com.supervision.constant.DataStatus; import com.supervision.police.domain.*; import com.supervision.police.dto.*; 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.police.vo.ModelIndexReqVO; import com.supervision.utils.JudgeLogicUtil; 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; private final ModelIndexAtomicRelationService modelIndexAtomicRelationService; private final NotePromptService notePromptService; private final EvidenceCategoryService evidenceCategoryService; @Value("${case.evidence.table}") private List allowedTables; @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R selectAll(ModelIndexReqVO 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"); if (StrUtil.isNotEmpty(modelIndex.getAtomicIndexName())){ // 如果查询条件中有原子指标名称,则根据原子指标名称过滤关联的指标id List modelIndexAtomicDTOS = modelIndexAtomicRelationService.listByAtomicIndexName(modelIndex.getAtomicIndexName()); List indexIdList = modelIndexAtomicDTOS.stream().map(ModelIndexAtomicRelationDTO::getModelIndexId).distinct().toList(); if (CollUtil.isEmpty(indexIdList)){ return R.ok(IPages.buildDataMap(iPage)); } wrapper.in(CollUtil.isNotEmpty(indexIdList),ModelIndex::getId, indexIdList); } wrapper.orderBy(true, false, ModelIndex::getUpdateTime); 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); modelIndexAtomicRelationService.saveByModelIndex(modelIndex); } else { ModelIndex dbModelIndex = super.getById(modelIndex.getId()); modelIndex.setJudgeLogic(dbModelIndex.getJudgeLogic());// 保持原来判断逻辑不变 i = modelIndexMapper.updateById(modelIndex); modelIndexAtomicRelationService.updateByModelIndex(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(DataStatus.NOT_AVAILABLE.getCode()); int i = modelIndexMapper.updateById(index); if (i > 0) { modelIndexAtomicRelationService.deleteByModelIndex(id); 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(); String caseType = StrUtil.isNotEmpty(modelAtomicIndex.getCaseType()) ? modelAtomicIndex.getCaseType() : NotePromptConstants.CASE_TYPE_ENGINEERING_CONTRACT_FRAUD; EvidenceCategoryDTO rootCategory = new EvidenceCategoryDTO(evidenceCategoryService.listCategoryTree(caseType)); 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())); index.setCategoryIdPath(rootCategory.listCategoryIdPath(index.getCategoryId())); } iPage.setRecords(records); return R.ok(IPages.buildDataMap(iPage)); } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R addOrUpdAtomic(ModelAtomicIndex modelAtomicIndex) { saveAtomicIndexPreDo(modelAtomicIndex); // 校验是否已经存在了相同名称的原子指标 ModelAtomicIndex existIndex = modelAtomicIndexService.lambdaQuery().eq(ModelAtomicIndex::getName, modelAtomicIndex.getName()) .eq(ModelAtomicIndex::getDataStatus, DataStatus.AVAILABLE.getCode()).last("limit 1").one(); if (ObjectUtil.isNotEmpty(existIndex) && !StringUtils.equals(modelAtomicIndex.getId(), existIndex.getId())){ throw new RuntimeException("已存在相同名称的原子指标"); } boolean success = modelAtomicIndexService.saveOrUpdate(modelAtomicIndex); return success ? R.okMsg("保存成功") : R.fail("保存失败"); } /** * 原子指标保存前置处理 * @param modelAtomicIndex */ private void saveAtomicIndexPreDo(ModelAtomicIndex modelAtomicIndex) { if (StringUtils.equals(IndexRuleConstants.OPERAND_TYPE_DB, modelAtomicIndex.getIndexSource())) { Assert.notEmpty(modelAtomicIndex.getCategoryId(), "分类不能为空"); // 如果查询类型为数据查询,则校验查询语句 Assert.notEmpty(modelAtomicIndex.getQueryLang(), "查询语言不能为空"); Assert.isTrue(checkSql(modelAtomicIndex.getQueryLang()), "查询语句不合法"); }else if (StringUtils.equals(IndexRuleConstants.OPERAND_TYPE_LLM, modelAtomicIndex.getIndexSource())){ Assert.notEmpty(modelAtomicIndex.getPromptId(), "promptId不能为空"); NotePrompt notePrompt = notePromptService.getBaseMapper().selectById(modelAtomicIndex.getPromptId()); Assert.notNull(notePrompt, "提示词信息不存在"); Assert.notEmpty(notePrompt.getEvidenceCategoryId(), "提示词分类为空"); modelAtomicIndex.setCategoryId(notePrompt.getEvidenceCategoryId()); } } @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R delAtomic(String id) { ModelAtomicIndex index = modelAtomicIndexService.getMapper().selectById(id); index.setDataStatus(DataStatus.NOT_AVAILABLE.getCode()); int i = modelAtomicIndexService.getMapper().updateById(index); if (i > 0) { modelAtomicIndexService.whenDeleteAtomicIndex(index.getCaseType(), id); 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 atomicIndexIds = modelIndexList.stream().filter(modelIndex -> StrUtil.isNotEmpty(modelIndex.getJudgeLogic())) .map(modelIndex -> JudgeLogicUtil.pickAtomicIndexIds(modelIndex.getJudgeLogic())) .flatMap(Collection::stream).distinct().toList(); if (CollUtil.isEmpty(atomicIndexIds)) { return new ArrayList<>(1); } // 查询原子指标相关信息 List modelAtomicIndexList = modelAtomicIndexService.listCaseAtomicIndex(atomicIndexIds, caseType, indexSource); if (CollUtil.isEmpty(modelAtomicIndexList)) { return new ArrayList<>(1); } // 查询判定结果数据 List modelAtomicResults = modelAtomicResultMapper.selectList( Wrappers.lambdaQuery(ModelAtomicResult.class).eq(ModelAtomicResult::getCaseId, caseId) .eq(ModelAtomicResult::getCasePersonId, actorId) .in(ModelAtomicResult::getAtomicId, atomicIndexIds)); Map>> modelAtomicResultGroup = modelAtomicResults.stream() .filter(modelAtomicResult -> StrUtil.isNotEmpty(modelAtomicResult.getAtomicId())) .collect(Collectors.groupingBy(ModelAtomicResult::getIndexId, Collectors.groupingBy(ModelAtomicResult::getAtomicId))); Map> modelIndexMapAtomic = groupModelIndexByAtomicIndexId(modelIndexList); // 以原子指标为基准,组装数据 return modelAtomicIndexList.stream().flatMap(atomicIndex -> modelIndexMapAtomic.get(atomicIndex.getId()).stream().map(modelIndex ->{ Map> map = modelAtomicResultGroup.getOrDefault(modelIndex.getId(),new HashMap<>(1)); return new CaseAtomicIndexDTO(atomicIndex, modelIndex,CollUtil.getFirst(map.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 CasePerson casePerson = casePersonMapper.selectOne(Wrappers.lambdaQuery(CasePerson.class) .eq(CasePerson::getCaseId, modelCase.getId()).eq(CasePerson::getRoleCode, "1").eq(CasePerson::getCaseActorFlag, 1)); caseAtomicResultWrapper.setActorId(casePerson.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; } @Override public Map> listAtomicIndexAttributeScope(List atomicIndexIds) { if (CollUtil.isEmpty(atomicIndexIds)){ return Map.of(); } Map> result = atomicIndexIds.stream().collect(Collectors.toMap(i -> i, i->List.of())); List atomicIndexList = modelAtomicIndexService.listByIds(atomicIndexIds); if (CollUtil.isEmpty(atomicIndexList)){ return result; } List promptIds = atomicIndexList.stream().map(ModelAtomicIndex::getPromptId).filter(StrUtil::isNotEmpty).toList(); if (CollUtil.isEmpty(promptIds)){ return result; } List notePrompts = notePromptService.listByIds(promptIds); Map dicMap = comDictionaryService.getDictionaryMapReverse("prompt_attribute_valuetype"); for (Map.Entry> entry : result.entrySet()) { List scopeDTOList = makeScopes(entry.getKey(), atomicIndexList, notePrompts, dicMap); if (scopeDTOList != null){ entry.setValue(scopeDTOList); } } return result; } private List makeScopes(String atomicIndexId, List atomicIndexList, List notePrompts, Map dicMap) { ModelAtomicIndex modelAtomicIndex = atomicIndexList.stream().filter(i -> StrUtil.equals(atomicIndexId, i.getId())).findFirst().orElse(null); if (null == modelAtomicIndex || StrUtil.isEmpty(modelAtomicIndex.getPromptId())){ return null; } String properties = modelAtomicIndex.getProperties(); if (StrUtil.isEmpty(properties)){ return null; } NotePrompt notePromptDTO = notePrompts.stream().filter(i -> StrUtil.equals(modelAtomicIndex.getPromptId(), i.getId())).findFirst().orElse(null); if (null == notePromptDTO){ return null; } List extractAttributes = notePromptDTO.getExtractAttributes(); if (CollUtil.isNotEmpty(extractAttributes)) { return extractAttributes.stream().map(extractAttribute -> { ValueCalculateScopeDTO valueCalculateScope = new ValueCalculateScopeDTO(); valueCalculateScope.setValue(extractAttribute.getAttrName()); valueCalculateScope.setValueType(dicMap.get(extractAttribute.getAttrValueType())); valueCalculateScope.setValueTypeDesc(extractAttribute.getAttrValueType()); valueCalculateScope.setOperatorList( IndexRuleConstants.VALUE_TYPE_OPERATOR_MAPPING.get(valueCalculateScope.getValueType())); Map map = IndexRuleConstants.VALUE_TYPE_AGGREGATE_MAPPING.get(valueCalculateScope.getValueType()); valueCalculateScope.setAggregateList(map.entrySet().stream().map(entry -> new Pair<>(entry.getKey(), entry.getValue())).toList()); return valueCalculateScope; }).toList(); } return null; } /** * 清空案件下的评估结果 * * @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 atomicIndexIds = modelAtomicIndexList.stream().map(ModelAtomicIndex::getId).distinct().toList(); modelAtomicResultMapper.delete(Wrappers.lambdaQuery(ModelAtomicResult.class) .eq(ModelAtomicResult::getCaseId, caseId) .eq(ModelAtomicResult::getCasePersonId, actorId) .in(CollUtil.isNotEmpty(atomicIndexIds), ModelAtomicResult::getAtomicId, atomicIndexIds)); } 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; } }