修复评估维度配置中dataSources和questions字段返回null的问题

- 添加autoResultMap=true到EvaluationDimensionDO的@TableName注解
- 确保JacksonTypeHandler能正确处理dataSources字段的JSON序列化
This commit is contained in:
tangweijie 2026-01-19 22:19:09 +08:00
parent f6b0410fda
commit 877e691792
22 changed files with 569 additions and 46 deletions

View File

@ -12,6 +12,7 @@ import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.util.stream.Collectors;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@ -28,6 +29,9 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.prison.controller.admin.evaluationreport.vo.*;
import cn.iocoder.yudao.module.prison.dal.dataobject.evaluationreport.*;
import cn.iocoder.yudao.module.prison.service.evaluationreport.EvaluationReportService;
import cn.iocoder.yudao.module.prison.service.evaluationreport.dto.DimensionDataSourcesRespDTO;
import cn.iocoder.yudao.module.prison.enums.EvaluationTemplateTypeEnum;
import cn.iocoder.yudao.module.prison.enums.EvaluationCycleEnum;
@Tag(name = "管理后台 - 评估报告")
@RestController
@ -87,7 +91,34 @@ public class EvaluationReportController {
@PreAuthorize("@ss.hasPermission('prison:evaluation-report:template:query')")
public CommonResult<PageResult<EvaluationTemplateRespVO>> getTemplatePage(@Valid EvaluationTemplatePageReqVO pageReqVO) {
PageResult<EvaluationTemplateDO> pageResult = evaluationReportService.getTemplatePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, EvaluationTemplateRespVO.class));
// 转换为 VO 并填充枚举名称
List<EvaluationTemplateRespVO> voList = pageResult.getList().stream().map(template -> {
EvaluationTemplateRespVO vo = BeanUtils.toBean(template, EvaluationTemplateRespVO.class);
// 填充模板类型名称
if (template.getType() != null) {
EvaluationTemplateTypeEnum typeEnum = EvaluationTemplateTypeEnum.fromValue(template.getType());
if (typeEnum != null) {
vo.setTypeName(typeEnum.getName());
}
}
// 填充评估周期名称
if (template.getEvaluationCycle() != null) {
EvaluationCycleEnum cycleEnum = EvaluationCycleEnum.fromValue(template.getEvaluationCycle());
if (cycleEnum != null) {
vo.setEvaluationCycleName(cycleEnum.getName());
}
}
// 填充状态名称
if (template.getStatus() != null) {
vo.setStatusName(template.getStatus() == 1 ? "启用" : "禁用");
}
// 填充AI允许名称
if (template.getAiEnabled() != null) {
vo.setAiEnabledName(template.getAiEnabled() == 1 ? "" : "");
}
return vo;
}).collect(Collectors.toList());
return success(new PageResult<>(voList, pageResult.getTotal()));
}
@GetMapping("/template/list-enabled")
@ -171,6 +202,18 @@ public class EvaluationReportController {
return success(BeanUtils.toBean(list, EvaluationDimensionRespVO.class));
}
@GetMapping("/dimension/data-sources")
@Operation(summary = "获取维度数据源")
@Parameter(name = "dimensionId", description = "维度ID", required = true)
@Parameter(name = "prisonerId", description = "罪犯ID", required = true)
@PreAuthorize("@ss.hasPermission('prison:evaluation-report:dimension:query')")
public CommonResult<DimensionDataSourcesRespDTO> getDimensionDataSources(
@RequestParam("dimensionId") Long dimensionId,
@RequestParam("prisonerId") Long prisonerId) {
DimensionDataSourcesRespDTO data = evaluationReportService.getDimensionDataSources(dimensionId, prisonerId);
return success(data);
}
// ========== 评估报告管理 ==========
@PostMapping("/report/create")

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.prison.controller.admin.evaluationreport.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
@ -73,6 +74,7 @@ public class EvaluationDimensionDataRespVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createTime;
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.prison.controller.admin.evaluationreport.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
@ -57,13 +58,36 @@ public class EvaluationDimensionRespVO {
@ExcelProperty("及格分值")
private BigDecimal passScore;
@Schema(description = "评估方式1-问卷测评 2-量表评分 3-行为观察 4-AI分析 5-综合评定", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "评估方式1-问卷测评 2-量表评分 3-行为观察 4-AI分析 5-综合评定")
@ExcelProperty("评估方式")
private Integer evaluationMethod;
@Schema(description = "AI提示词")
@ExcelProperty("AI提示词")
private String aiPrompt;
@Schema(description = "输出格式1-文本 2-数值 3-评分 4-等级 5-综合")
@ExcelProperty("输出格式")
private Integer outputFormat;
@Schema(description = "是否启用AI0-否 1-是")
@ExcelProperty("是否启用AI")
private Integer aiEnabled;
@Schema(description = "编辑器类型1-富文本 2-Markdown 3-纯文本 4-表单编辑")
@ExcelProperty("编辑器类型")
private Integer editorType;
@Schema(description = "是否必填0-否 1-是")
@ExcelProperty("是否必填")
private Integer isRequired;
@Schema(description = "问题列表JSON格式")
private String questions;
@Schema(description = "数据源绑定prisoner-罪犯档案 consumption-消费记录 score-计分考核 questionnaire-问卷测评 risk-风险评估 violation-违规记录 reward-奖励记录 visit-会见记录 labor-劳动数据 family-家庭帮教 psychology-心理测评")
private List<String> dataSources;
@Schema(description = "排序")
@ExcelProperty("排序")
private Integer sort;
@ -74,6 +98,7 @@ public class EvaluationDimensionRespVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createTime;
}

View File

@ -13,57 +13,65 @@ public class EvaluationDimensionSaveReqVO {
@Schema(description = "维度ID")
private Long id;
@Schema(description = "模板ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "模板ID不能为空")
@Schema(description = "模板ID")
private Long templateId;
@Schema(description = "维度名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "维度名称不能为空")
@Schema(description = "维度名称")
private String name;
@Schema(description = "维度编码", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "维度编码不能为空")
@Schema(description = "维度编码")
private String code;
@Schema(description = "维度描述")
private String description;
@Schema(description = "维度类型1-心理测评 2-行为表现 3-教育改造 4-劳动表现 5-人际交往 6-自评/他评", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "维度类型不能为空")
@Schema(description = "维度类型1-心理测评 2-行为表现 3-教育改造 4-劳动表现 5-人际交往 6-自评/他评")
private Integer dimensionType;
@Schema(description = "权重(百分比)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "权重不能为空")
@Schema(description = "权重(百分比)")
private BigDecimal weight;
@Schema(description = "评分规则1-分值越高越好 2-分值越低越好 3-区间评分", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "评分规则不能为空")
@Schema(description = "评分规则1-分值越高越好 2-分值越低越好 3-区间评分")
private Integer scoreRule;
@Schema(description = "最大分值", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "最大分值不能为空")
@Schema(description = "最大分值")
private BigDecimal maxScore;
@Schema(description = "最小分值", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "最小分值不能为空")
@Schema(description = "最小分值")
private BigDecimal minScore;
@Schema(description = "及格分值")
private BigDecimal passScore;
@Schema(description = "评估方式1-问卷测评 2-量表评分 3-行为观察 4-AI分析 5-综合评定", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "评估方式不能为空")
@Schema(description = "评估方式1-问卷测评 2-量表评分 3-行为观察 4-AI分析 5-综合评定")
private Integer evaluationMethod;
@Schema(description = "AI提示词")
private String aiPrompt;
@Schema(description = "输出格式1-文本 2-数值 3-评分 4-等级 5-综合")
private Integer outputFormat;
@Schema(description = "是否启用AI0-否 1-是")
private Integer aiEnabled;
@Schema(description = "编辑器类型1-富文本 2-Markdown 3-纯文本 4-表单编辑")
private Integer editorType;
@Schema(description = "是否必填0-否 1-是")
private Integer isRequired;
@Schema(description = "问题列表JSON格式")
private String questions;
@Schema(description = "数据源绑定prisoner-罪犯档案 consumption-消费记录 score-计分考核 questionnaire-问卷测评 risk-风险评估 violation-违规记录 reward-奖励记录 visit-会见记录 labor-劳动数据 family-家庭帮教 psychology-心理测评")
private List<String> dataSources;
@Schema(description = "排序")
@Min(value = 0, message = "排序不能为负数")
private Integer sort;
@Schema(description = "状态1-启用 2-禁用", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "状态不能为空")
@Schema(description = "状态1-启用 2-禁用")
private Integer status;
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.prison.controller.admin.evaluationreport.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
@ -51,6 +52,7 @@ public class EvaluationReportRespVO {
@Schema(description = "评估日期")
@ExcelProperty("评估日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime evaluationDate;
@Schema(description = "评估人员ID")
@ -106,6 +108,7 @@ public class EvaluationReportRespVO {
@Schema(description = "审核时间")
@ExcelProperty("审核时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime auditTime;
@Schema(description = "审核意见")
@ -129,6 +132,7 @@ public class EvaluationReportRespVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}

View File

@ -5,6 +5,7 @@ import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.*;
@Schema(description = "管理后台 - 评估模板 Response VO")
@ -28,6 +29,10 @@ public class EvaluationTemplateRespVO {
@ExcelProperty("模板类型")
private Integer type;
@Schema(description = "模板类型名称")
@ExcelProperty("模板类型名称")
private String typeName;
@Schema(description = "描述")
@ExcelProperty("描述")
private String description;
@ -40,14 +45,26 @@ public class EvaluationTemplateRespVO {
@ExcelProperty("评估周期")
private Integer evaluationCycle;
@Schema(description = "评估周期名称")
@ExcelProperty("评估周期名称")
private String evaluationCycleName;
@Schema(description = "状态1-启用 2-禁用", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("状态")
private Integer status;
@Schema(description = "状态名称")
@ExcelProperty("状态名称")
private String statusName;
@Schema(description = "是否允许AI生成1-是 2-否")
@ExcelProperty("允许AI生成")
private Integer aiEnabled;
@Schema(description = "是否允许AI生成名称")
@ExcelProperty("允许AI生成名称")
private String aiEnabledName;
@Schema(description = "AI提示词")
private String aiPrompt;
@ -62,8 +79,20 @@ public class EvaluationTemplateRespVO {
@ExcelProperty("备注")
private String remark;
@Schema(description = "创建人")
@ExcelProperty("创建人")
private String creator;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createTime;
@Schema(description = "更新人")
private String updater;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime updateTime;
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.prison.controller.admin.evaluationreport.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
@ -58,6 +59,7 @@ public class ReportCommentRespVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createTime;
}

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.prison.dal.dataobject.evaluationreport.*;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.ArrayList;
import java.util.List;
/**
@ -28,8 +29,42 @@ public interface EvaluationReportConvert {
// ========== 维度 ==========
EvaluationDimensionDO convert(EvaluationDimensionSaveReqVO bean);
EvaluationDimensionRespVO convert(EvaluationDimensionDO bean);
List<EvaluationDimensionRespVO> convertDimensionList(List<EvaluationDimensionDO> list);
default EvaluationDimensionRespVO convert(EvaluationDimensionDO bean) {
if (bean == null) {
return null;
}
EvaluationDimensionRespVO vo = new EvaluationDimensionRespVO();
vo.setId(bean.getId());
vo.setTemplateId(bean.getTemplateId());
vo.setName(bean.getName());
vo.setCode(bean.getCode());
vo.setDescription(bean.getDescription());
vo.setDimensionType(bean.getDimensionType());
vo.setWeight(bean.getWeight());
vo.setScoreRule(bean.getScoreRule());
vo.setMaxScore(bean.getMaxScore());
vo.setMinScore(bean.getMinScore());
vo.setPassScore(bean.getPassScore());
vo.setEvaluationMethod(bean.getEvaluationMethod());
vo.setAiPrompt(bean.getAiPrompt());
vo.setOutputFormat(bean.getOutputFormat());
vo.setAiEnabled(bean.getAiEnabled());
vo.setEditorType(bean.getEditorType());
vo.setIsRequired(bean.getIsRequired());
vo.setQuestions(bean.getQuestions());
// null 转为空数组
vo.setDataSources(bean.getDataSources() != null ? bean.getDataSources() : new ArrayList<>());
vo.setSort(bean.getSort());
vo.setStatus(bean.getStatus());
vo.setCreateTime(bean.getCreateTime());
return vo;
}
default List<EvaluationDimensionRespVO> convertDimensionList(List<EvaluationDimensionDO> list) {
return list.stream().map(this::convert).toList();
}
PageResult<EvaluationDimensionRespVO> convertDimensionPage(PageResult<EvaluationDimensionDO> page);
// ========== 报告 ==========

View File

@ -6,13 +6,14 @@ import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
/**
* 评估维度配置 DO
*
* @author xlcp
*/
@TableName("prison_evaluation_dimension")
@TableName(value = "prison_evaluation_dimension", autoResultMap = true)
@KeySequence("prison_evaluation_dimension_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ -71,10 +72,35 @@ public class EvaluationDimensionDO extends BaseDO {
* 评估方式1-问卷测评 2-量表评分 3-行为观察 4-AI分析 5-综合评定
*/
private Integer evaluationMethod;
/**
* AI提示词
*/
private String aiPrompt;
/**
* 输出格式1-文本 2-数值 3-评分 4-等级 5-综合
*/
private Integer outputFormat;
/**
* 是否启用AI0- 1-
*/
private Integer aiEnabled;
/**
* 编辑器类型1-富文本 2-Markdown 3-纯文本 4-表单编辑
*/
private Integer editorType;
/**
* 是否必填0- 1-
*/
private Integer isRequired;
/**
* 问题列表JSON格式
*/
private String questions;
/**
* 数据源绑定prisoner-罪犯档案 consumption-消费记录 score-计分考核 questionnaire-问卷测评 risk-风险评估 violation-违规记录 reward-奖励记录 visit-会见记录 labor-劳动数据 family-家庭帮教 psychology-心理测评
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> dataSources;
/**
* 排序
*/

View File

@ -82,6 +82,5 @@ public class ErrorCodeConstants {
public static final ErrorCode EVALUATION_DIMENSION_DATA_NOT_EXISTS = new ErrorCode(13_000_005, "维度数据不存在");
public static final ErrorCode REPORT_COMMENT_NOT_EXISTS = new ErrorCode(13_000_006, "快捷评语不存在");
public static final ErrorCode PRISON_REPORT_TEMPLATE_NOT_EXISTS = new ErrorCode(13_000_007, "评估报告模板不存在");
public static final ErrorCode PRISON_REPORT_NOT_EXISTS = new ErrorCode(13_000_008, "评估报告不存在");
}

View File

@ -4,6 +4,7 @@ import java.util.*;
import jakarta.validation.*;
import cn.iocoder.yudao.module.prison.controller.admin.evaluationreport.vo.*;
import cn.iocoder.yudao.module.prison.dal.dataobject.evaluationreport.*;
import cn.iocoder.yudao.module.prison.service.evaluationreport.dto.DimensionDataSourcesRespDTO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@ -88,6 +89,11 @@ public interface EvaluationReportService {
*/
List<EvaluationDimensionDO> getDimensionsByTemplateId(Long templateId);
/**
* 获取维度数据源根据维度配置聚合数据
*/
DimensionDataSourcesRespDTO getDimensionDataSources(Long dimensionId, Long prisonerId);
// ========== 评估报告管理 ==========
/**

View File

@ -15,6 +15,7 @@ import java.util.stream.Collectors;
import cn.iocoder.yudao.module.prison.controller.admin.evaluationreport.vo.*;
import cn.iocoder.yudao.module.prison.dal.dataobject.evaluationreport.*;
import cn.iocoder.yudao.module.prison.service.evaluationreport.dto.DimensionDataSourcesRespDTO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@ -26,10 +27,29 @@ import cn.iocoder.yudao.module.prison.dal.mysql.evaluationreport.EvaluationRepor
import cn.iocoder.yudao.module.prison.dal.mysql.evaluationreport.EvaluationDimensionDataMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.evaluationreport.ReportCommentMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.PrisonerMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.area.AreaMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.consumption.ConsumptionMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.score.ScoreMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.questionnairerecord.QuestionnaireRecordMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.riskassessment.RiskAssessmentMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.reporttemplate.ReportTemplateMapper;
import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.area.AreaDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.score.ScoreDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO;
import cn.iocoder.yudao.module.prison.dal.dataobject.riskassessment.RiskAssessmentDO;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.prison.enums.EvaluationReportStatusEnum.DRAFT;
import static cn.iocoder.yudao.module.prison.enums.EvaluationReportStatusEnum.PENDING_AUDIT;
import static cn.iocoder.yudao.module.prison.enums.EvaluationReportStatusEnum.VOID;
import static cn.iocoder.yudao.module.prison.enums.EvaluationAiStatusEnum.PENDING;
/**
* 评估报告 Service 实现类
@ -55,6 +75,24 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
@Resource
private ReportCommentMapper commentMapper;
@Resource
private PrisonerMapper prisonerMapper;
@Resource
private AreaMapper areaMapper;
@Resource
private ConsumptionMapper consumptionMapper;
@Resource
private ScoreMapper scoreMapper;
@Resource
private QuestionnaireRecordMapper questionnaireRecordMapper;
@Resource
private RiskAssessmentMapper riskAssessmentMapper;
// ========== 模板管理 ==========
@Override
@ -161,6 +199,150 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
return dimensionMapper.selectListByTemplateId(templateId);
}
@Override
public DimensionDataSourcesRespDTO getDimensionDataSources(Long dimensionId, Long prisonerId) {
// 获取维度配置
EvaluationDimensionDO dimension = dimensionMapper.selectById(dimensionId);
if (dimension == null) {
throw exception(EVALUATION_DIMENSION_NOT_EXISTS);
}
DimensionDataSourcesRespDTO result = new DimensionDataSourcesRespDTO();
result.setDimensionId(dimensionId);
result.setPrisonerId(prisonerId);
// 获取数据源配置
List<String> dataSources = dimension.getDataSources();
if (CollUtil.isEmpty(dataSources)) {
return result;
}
Set<String> sourceSet = new HashSet<>(dataSources);
// 根据数据源类型查询数据
Map<String, Object> dataMap = new HashMap<>();
if (sourceSet.contains("prisoner")) {
PrisonerDO prisoner = prisonerMapper.selectById(prisonerId);
if (prisoner != null) {
Map<String, Object> prisonerInfo = new HashMap<>();
prisonerInfo.put("id", prisoner.getId());
prisonerInfo.put("name", prisoner.getName());
prisonerInfo.put("prisonerNo", prisoner.getPrisonerNo());
prisonerInfo.put("gender", prisoner.getGender() != null ? prisoner.getGender().getName() : null);
prisonerInfo.put("crime", prisoner.getCrime());
prisonerInfo.put("imprisonmentDate", prisoner.getImprisonmentDate());
prisonerInfo.put("status", prisoner.getStatus() != null ? prisoner.getStatus().getName() : null);
dataMap.put("prisoner", prisonerInfo);
}
}
if (sourceSet.contains("consumption")) {
List<ConsumptionDO> consumptions = consumptionMapper.selectList(new LambdaQueryWrapperX<ConsumptionDO>()
.eq(ConsumptionDO::getPrisonerId, prisonerId));
if (CollUtil.isNotEmpty(consumptions)) {
dataMap.put("consumptionRecords", consumptions.stream().map(c -> {
Map<String, Object> item = new HashMap<>();
item.put("id", c.getId());
item.put("month", c.getTradeTime() != null ? c.getTradeTime().toLocalDate().getMonth().getValue() + "" : null);
item.put("totalAmount", c.getTotalAmount());
return item;
}).collect(Collectors.toList()));
// 计算汇总信息
BigDecimal totalAmount = consumptions.stream()
.map(ConsumptionDO::getTotalAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Map<String, Object> summary = new HashMap<>();
summary.put("totalAmount", totalAmount);
summary.put("recordCount", consumptions.size());
dataMap.put("consumptionSummary", summary);
}
}
if (sourceSet.contains("score")) {
List<ScoreDO> scores = scoreMapper.selectList(new LambdaQueryWrapperX<ScoreDO>()
.eq(ScoreDO::getPrisonerId, prisonerId));
if (CollUtil.isNotEmpty(scores)) {
dataMap.put("scoreRecords", scores.stream().map(s -> {
Map<String, Object> item = new HashMap<>();
item.put("id", s.getId());
item.put("year", s.getYear());
item.put("month", s.getMonth());
item.put("totalScore", s.getTotalScore());
item.put("level", s.getLevel());
return item;
}).collect(Collectors.toList()));
// 计算汇总信息
BigDecimal totalScore = scores.stream()
.map(ScoreDO::getTotalScore)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Map<String, Object> summary = new HashMap<>();
summary.put("totalScore", totalScore);
summary.put("recordCount", scores.size());
summary.put("level", calculateAverageLevel(scores));
dataMap.put("scoreSummary", summary);
}
}
if (sourceSet.contains("questionnaire")) {
List<QuestionnaireRecordDO> records = questionnaireRecordMapper.selectList(new LambdaQueryWrapperX<QuestionnaireRecordDO>()
.eq(QuestionnaireRecordDO::getPrisonerId, prisonerId));
if (CollUtil.isNotEmpty(records)) {
dataMap.put("questionnaireRecords", records.stream().map(r -> {
Map<String, Object> item = new HashMap<>();
item.put("id", r.getId());
item.put("questionnaireName", r.getQuestionnaireName());
item.put("totalScore", r.getTotalScore());
item.put("status", r.getStatus());
return item;
}).collect(Collectors.toList()));
}
}
if (sourceSet.contains("risk")) {
List<RiskAssessmentDO> riskList = riskAssessmentMapper.selectList(new LambdaQueryWrapperX<RiskAssessmentDO>()
.eq(RiskAssessmentDO::getPrisonerId, prisonerId));
if (CollUtil.isNotEmpty(riskList)) {
// 取最新的风险评估
RiskAssessmentDO latestRisk = riskList.get(0);
Map<String, Object> riskInfo = new HashMap<>();
riskInfo.put("id", latestRisk.getId());
riskInfo.put("riskLevel", latestRisk.getRiskLevel());
riskInfo.put("riskFactors", latestRisk.getRiskFactors());
riskInfo.put("suggestions", latestRisk.getSuggestions());
riskInfo.put("totalScore", latestRisk.getTotalScore());
riskInfo.put("assessmentDate", latestRisk.getAssessmentDate());
dataMap.put("riskAssessment", riskInfo);
}
}
result.setData(dataMap);
return result;
}
/**
* 计算平均等级
*/
private String calculateAverageLevel(List<ScoreDO> scores) {
if (CollUtil.isEmpty(scores)) {
return "未知";
}
Map<String, Long> levelCount = scores.stream()
.filter(s -> s.getLevel() != null)
.map(s -> String.valueOf(s.getLevel()))
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
return levelCount.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse("未知");
}
private void validateDimensionExists(Long id) {
if (dimensionMapper.selectById(id) == null) {
throw exception(EVALUATION_DIMENSION_NOT_EXISTS);
@ -177,11 +359,11 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
report.setReportNo(generateReportNo());
// 初始状态为草稿
if (report.getStatus() == null) {
report.setStatus(1);
report.setStatus(DRAFT.getValue());
}
// AI生成状态初始为待生成
if (report.getAiStatus() == null) {
report.setAiStatus(1);
report.setAiStatus(PENDING.getValue());
}
evaluationReportMapper.insert(report);
return report.getId();
@ -210,7 +392,33 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
@Override
public EvaluationReportDO getReport(Long id) {
return evaluationReportMapper.selectById(id);
EvaluationReportDO report = evaluationReportMapper.selectById(id);
if (report == null) {
return null;
}
// 填充罪犯信息
if (report.getPrisonerId() != null) {
PrisonerDO prisoner = prisonerMapper.selectById(report.getPrisonerId());
if (prisoner != null) {
report.setPrisonerNo(prisoner.getPrisonerNo());
report.setPrisonerName(prisoner.getName());
}
}
// 填充模板信息
if (report.getTemplateId() != null) {
EvaluationTemplateDO template = templateMapper.selectById(report.getTemplateId());
if (template != null) {
report.setTemplateName(template.getName());
}
}
// 填充监区信息
if (report.getAreaId() != null) {
AreaDO area = areaMapper.selectById(report.getAreaId());
if (area != null) {
report.setAreaName(area.getName());
}
}
return report;
}
@Override
@ -232,12 +440,12 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
@Transactional(rollbackFor = Exception.class)
public void submitReport(Long id) {
EvaluationReportDO report = validateReportExists(id);
if (!report.getStatus().equals(1)) {
if (!report.getStatus().equals(DRAFT.getValue())) {
throw exception(EVALUATION_REPORT_STATUS_ERROR);
}
EvaluationReportDO updateObj = new EvaluationReportDO();
updateObj.setId(id);
updateObj.setStatus(2); // 待审核
updateObj.setStatus(PENDING_AUDIT.getValue()); // 待审核
evaluationReportMapper.updateById(updateObj);
}
@ -245,7 +453,7 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
@Transactional(rollbackFor = Exception.class)
public void auditReport(EvaluationReportAuditReqVO auditReqVO) {
EvaluationReportDO report = validateReportExists(auditReqVO.getId());
if (!report.getStatus().equals(2)) {
if (!report.getStatus().equals(PENDING_AUDIT.getValue())) {
throw exception(EVALUATION_REPORT_STATUS_ERROR);
}
EvaluationReportDO updateObj = new EvaluationReportDO();
@ -261,10 +469,16 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
EvaluationReportDO report = validateReportExists(id);
EvaluationReportDO updateObj = new EvaluationReportDO();
updateObj.setId(id);
updateObj.setStatus(4); // 已作废
updateObj.setStatus(VOID.getValue()); // 已作废
evaluationReportMapper.updateById(updateObj);
}
/**
* 校验报告是否存在
* @param id 报告ID
* @return 报告DO对象
* @throws ServiceException 如果报告不存在
*/
private EvaluationReportDO validateReportExists(Long id) {
EvaluationReportDO report = evaluationReportMapper.selectById(id);
if (report == null) {

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.prison.service.evaluationreport.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* 维度数据源响应DTO
*
* @author xlcp
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DimensionDataSourcesRespDTO {
/**
* 维度ID
*/
private Long dimensionId;
/**
* 罪犯ID
*/
private Long prisonerId;
/**
* 数据源配置
*/
private String dataSources;
/**
* 聚合的数据
* key: 数据源类型 (prisoner, consumption, score, questionnaire, risk, etc.)
* value: 数据对象
*/
private Map<String, Object> data;
}

View File

@ -68,7 +68,7 @@ public class ReportServiceImpl implements ReportService {
private void validateReportExists(Long id) {
if (reportMapper.selectById(id) == null) {
throw exception(PRISON_REPORT_NOT_EXISTS);
throw exception(EVALUATION_REPORT_NOT_EXISTS);
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.prison.dal.mysql.evaluationreport.EvaluationDimensionMapper">
<resultMap id="BaseResultMap" type="cn.iocoder.yudao.module.prison.dal.dataobject.evaluationreport.EvaluationDimensionDO" autoMapping="true">
<id property="id" column="id"/>
<result property="templateId" column="template_id"/>
<result property="name" column="name"/>
<result property="code" column="code"/>
<result property="description" column="description"/>
<result property="dimensionType" column="dimension_type"/>
<result property="weight" column="weight"/>
<result property="scoreRule" column="score_rule"/>
<result property="maxScore" column="max_score"/>
<result property="minScore" column="min_score"/>
<result property="passScore" column="pass_score"/>
<result property="evaluationMethod" column="evaluation_method"/>
<result property="aiPrompt" column="ai_prompt"/>
<result property="outputFormat" column="output_format"/>
<result property="aiEnabled" column="ai_enabled"/>
<result property="editorType" column="editor_type"/>
<result property="isRequired" column="is_required"/>
<result property="questions" column="questions"/>
<result property="dataSources" column="data_sources" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
<result property="sort" column="sort"/>
<result property="status" column="status"/>
<result property="creator" column="creator"/>
<result property="createTime" column="create_time"/>
<result property="updater" column="updater"/>
<result property="updateTime" column="update_time"/>
<result property="deleted" column="deleted"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,46 @@
-- ============================================================
-- 评估维度配置表 - 添加缺失字段
-- 执行日期2026-01-19
-- ============================================================
-- 添加维度编码
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `code` varchar(100) DEFAULT NULL COMMENT '维度编码' AFTER `name`;
-- 添加维度描述
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `description` varchar(500) DEFAULT NULL COMMENT '维度描述' AFTER `code`;
-- 添加权重
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `weight` decimal(5,2) DEFAULT NULL COMMENT '权重(百分比)' AFTER `dimension_type`;
-- 添加评分规则
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `score_rule` tinyint DEFAULT NULL COMMENT '评分规则1-分值越高越好 2-分值越低越好 3-区间评分' AFTER `weight`;
-- 添加最大分值
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `max_score` decimal(10,2) DEFAULT NULL COMMENT '最大分值' AFTER `score_rule`;
-- 添加最小分值
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `min_score` decimal(10,2) DEFAULT NULL COMMENT '最小分值' AFTER `max_score`;
-- 添加及格分值
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `pass_score` decimal(10,2) DEFAULT NULL COMMENT '及格分值' AFTER `min_score`;
-- 添加评估方式
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `evaluation_method` tinyint DEFAULT NULL COMMENT '评估方式1-问卷测评 2-量表评分 3-行为观察 4-AI分析 5-综合评定' AFTER `pass_score`;
-- 添加问题列表
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `questions` text COMMENT '问题列表JSON格式' AFTER `evaluation_method`;
-- 添加状态
ALTER TABLE `prison_evaluation_dimension`
ADD COLUMN `status` tinyint(1) DEFAULT 1 COMMENT '状态0-停用 1-启用' AFTER `is_required`;
SELECT '评估维度表字段添加完成!' AS RESULT;

View File

@ -16,6 +16,7 @@ class EducationEnumTest {
@Test
@DisplayName("测试所有枚举值存在")
void testAllEnumValuesExist() {
assertNotNull(EducationEnum.UNKNOWN);
assertNotNull(EducationEnum.ILLITERATE);
assertNotNull(EducationEnum.PRIMARY_SCHOOL);
assertNotNull(EducationEnum.MIDDLE_SCHOOL);
@ -77,6 +78,6 @@ class EducationEnumTest {
@Test
@DisplayName("测试枚举数量")
void testEnumCount() {
assertEquals(7, EducationEnum.values().length);
assertEquals(8, EducationEnum.values().length);
}
}

View File

@ -16,6 +16,7 @@ class GenderEnumTest {
@Test
@DisplayName("测试所有枚举值存在")
void testAllEnumValuesExist() {
assertNotNull(GenderEnum.UNKNOWN);
assertNotNull(GenderEnum.MALE);
assertNotNull(GenderEnum.FEMALE);
}
@ -37,6 +38,6 @@ class GenderEnumTest {
@Test
@DisplayName("测试枚举数量")
void testEnumCount() {
assertEquals(2, GenderEnum.values().length);
assertEquals(3, GenderEnum.values().length);
}
}

View File

@ -16,6 +16,7 @@ class PrisonerStatusEnumTest {
@Test
@DisplayName("测试所有枚举值存在")
void testAllEnumValuesExist() {
assertNotNull(PrisonerStatusEnum.UNKNOWN);
assertNotNull(PrisonerStatusEnum.IMPRISONED);
assertNotNull(PrisonerStatusEnum.PAROLED);
assertNotNull(PrisonerStatusEnum.OUTSIDE_EXECUTION);
@ -70,6 +71,6 @@ class PrisonerStatusEnumTest {
@Test
@DisplayName("测试枚举数量")
void testEnumCount() {
assertEquals(5, PrisonerStatusEnum.values().length);
assertEquals(6, PrisonerStatusEnum.values().length);
}
}

View File

@ -16,15 +16,17 @@ class RiskLevelEnumTest {
@Test
@DisplayName("测试所有枚举值存在")
void testAllEnumValuesExist() {
assertNotNull(RiskLevelEnum.HIGH);
assertNotNull(RiskLevelEnum.MEDIUM);
assertNotNull(RiskLevelEnum.UNKNOWN);
assertNotNull(RiskLevelEnum.LOW);
assertNotNull(RiskLevelEnum.MEDIUM);
assertNotNull(RiskLevelEnum.HIGH);
assertNotNull(RiskLevelEnum.EXTREME);
}
@Test
@DisplayName("测试高风险枚举值")
void testHighRiskValue() {
assertEquals(1, RiskLevelEnum.HIGH.getValue());
assertEquals(3, RiskLevelEnum.HIGH.getValue());
assertEquals("高风险", RiskLevelEnum.HIGH.getName());
}
@ -38,13 +40,13 @@ class RiskLevelEnumTest {
@Test
@DisplayName("测试低风险枚举值")
void testLowRiskValue() {
assertEquals(3, RiskLevelEnum.LOW.getValue());
assertEquals(1, RiskLevelEnum.LOW.getValue());
assertEquals("低风险", RiskLevelEnum.LOW.getName());
}
@Test
@DisplayName("测试枚举数量")
void testEnumCount() {
assertEquals(3, RiskLevelEnum.values().length);
assertEquals(5, RiskLevelEnum.values().length);
}
}

View File

@ -16,6 +16,7 @@ class SupervisionLevelEnumTest {
@Test
@DisplayName("测试所有枚举值存在")
void testAllEnumValuesExist() {
assertNotNull(SupervisionLevelEnum.UNKNOWN);
assertNotNull(SupervisionLevelEnum.STRICT);
assertNotNull(SupervisionLevelEnum.NORMAL);
assertNotNull(SupervisionLevelEnum.RELAXED);
@ -45,6 +46,6 @@ class SupervisionLevelEnumTest {
@Test
@DisplayName("测试枚举数量")
void testEnumCount() {
assertEquals(3, SupervisionLevelEnum.values().length);
assertEquals(4, SupervisionLevelEnum.values().length);
}
}

View File

@ -284,7 +284,7 @@ llm:
local:
base-url: ${LLM_BASE_URL:https://oneapi.gongjulian.cn/v1} # OneAPI服务地址
api-key: ${LLM_API_KEY:sk-lB2Fc9ssY5UuwmiV5dD441F997364d29Be547e008dF5Cf41} # API密钥建议通过环境变量配置
model: ${LLM_MODEL:deepseek-ai/deepseek-v3.2} # 使用的模型
model: ${LLM_MODEL:minimaxai/minimax-m2.1} # 使用的模型deepseek-ai/deepseek-v3.2 暂时不可用)
timeout-seconds: ${LLM_TIMEOUT:120} # 请求超时时间
# Claude可选需要时取消注释
# claude: