You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fu-hsi-service/src/main/java/com/supervision/police/service/impl/ModelIndexServiceImpl.java

456 lines
22 KiB
Java

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<ModelIndexMapper, ModelIndex> 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<String> allowedTables;
@Override
@Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class)
public R<?> selectAll(ModelIndexReqVO modelIndex, Integer page, Integer size) {
// 构建查询条件
IPage<ModelIndex> iPage = new Page<>(page, size);
LambdaQueryWrapper<ModelIndex> 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<ModelIndexAtomicRelationDTO> modelIndexAtomicDTOS = modelIndexAtomicRelationService.listByAtomicIndexName(modelIndex.getAtomicIndexName());
List<String> 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<ModelIndex> records = iPage.getRecords();
List<ComDictionary> 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<String> ids = new ArrayList<>();
if (StringUtils.isNotEmpty(judgeLogic)) {
List<JudgeLogic> logic = JSONUtil.toList(judgeLogic, JudgeLogic.class);
for (JudgeLogic judge : logic) {
List<AtomicData> atomicData = judge.getAtomicData();
for (AtomicData atomic : atomicData) {
ids.add(atomic.getAtomicIndex());
}
}
List<ModelAtomicIndex> 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<ModelAtomicIndex> iPage = new Page<>(page, size);
iPage = modelAtomicIndexService.selectAll(iPage, modelAtomicIndex);
List<ModelAtomicIndex> records = iPage.getRecords();
List<ComDictionary> 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<CaseAtomicIndexDTO> 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<CasePerson> 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<ModelIndex> 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<String> 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<ModelAtomicIndex> modelAtomicIndexList = modelAtomicIndexService.listCaseAtomicIndex(atomicIndexIds, caseType, indexSource);
if (CollUtil.isEmpty(modelAtomicIndexList)) {
return new ArrayList<>(1);
}
// 查询判定结果数据
List<ModelAtomicResult> modelAtomicResults = modelAtomicResultMapper.selectList(
Wrappers.lambdaQuery(ModelAtomicResult.class).eq(ModelAtomicResult::getCaseId, caseId)
.eq(ModelAtomicResult::getCasePersonId, actorId)
.in(ModelAtomicResult::getAtomicId, atomicIndexIds));
Map<String, Map<String, List<ModelAtomicResult>>> modelAtomicResultGroup = modelAtomicResults.stream()
.filter(modelAtomicResult -> StrUtil.isNotEmpty(modelAtomicResult.getAtomicId()))
.collect(Collectors.groupingBy(ModelAtomicResult::getIndexId, Collectors.groupingBy(ModelAtomicResult::getAtomicId)));
Map<String, List<ModelIndex>> modelIndexMapAtomic = groupModelIndexByAtomicIndexId(modelIndexList);
// 以原子指标为基准,组装数据
return modelAtomicIndexList.stream().flatMap(atomicIndex ->
modelIndexMapAtomic.get(atomicIndex.getId()).stream().map(modelIndex ->{
Map<String, List<ModelAtomicResult>> 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<String> 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<String,List<ValueCalculateScopeDTO>> listAtomicIndexAttributeScope(List<String> atomicIndexIds) {
if (CollUtil.isEmpty(atomicIndexIds)){
return Map.of();
}
Map<String, List<ValueCalculateScopeDTO>> result = atomicIndexIds.stream().collect(Collectors.toMap(i -> i, i->List.of()));
List<ModelAtomicIndex> atomicIndexList = modelAtomicIndexService.listByIds(atomicIndexIds);
if (CollUtil.isEmpty(atomicIndexList)){
return result;
}
List<String> promptIds = atomicIndexList.stream().map(ModelAtomicIndex::getPromptId).filter(StrUtil::isNotEmpty).toList();
if (CollUtil.isEmpty(promptIds)){
return result;
}
List<NotePrompt> notePrompts = notePromptService.listByIds(promptIds);
Map<String, String> dicMap = comDictionaryService.getDictionaryMapReverse("prompt_attribute_valuetype");
for (Map.Entry<String, List<ValueCalculateScopeDTO>> entry : result.entrySet()) {
List<ValueCalculateScopeDTO> scopeDTOList = makeScopes(entry.getKey(), atomicIndexList, notePrompts, dicMap);
if (scopeDTOList != null){
entry.setValue(scopeDTOList);
}
}
return result;
}
private List<ValueCalculateScopeDTO> makeScopes(String atomicIndexId, List<ModelAtomicIndex> atomicIndexList, List<NotePrompt> notePrompts, Map<String, String> 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<NotePromptExtractAttributesDto> 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<String, String> 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<ModelAtomicIndex> modelAtomicIndexList = modelAtomicIndexService.listCaseAtomicIndex(null, caseType, indexSource);
List<String> 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<String, List<ModelIndex>> groupModelIndexByAtomicIndexId(List<ModelIndex> modelIndexList) {
Map<String, List<ModelIndex>> groupMap = new HashMap<>();
if (CollUtil.isEmpty(modelIndexList)) {
return groupMap;
}
for (ModelIndex modelIndex : modelIndexList) {
if (StrUtil.isEmpty(modelIndex.getJudgeLogic())) {
continue;
}
String judgeLogic = modelIndex.getJudgeLogic();
List<JudgeLogic> judgeLogicList = JSONUtil.toList(judgeLogic, JudgeLogic.class);
for (JudgeLogic logic : judgeLogicList) {
List<AtomicData> atomicData = logic.getAtomicData();
if (CollUtil.isEmpty(atomicData)) {
continue;
}
for (AtomicData atomic : atomicData) {
List<ModelIndex> modelIndexs = groupMap.getOrDefault(atomic.getAtomicIndex(), new ArrayList<>());
modelIndexs.add(modelIndex);
groupMap.put(atomic.getAtomicIndex(), modelIndexs);
}
}
}
return groupMap;
}
}