feat: 更新评估报告服务和相关模块

- 计分考核模块新增监区、监室字段
- 问卷答题记录添加关联字段
- 危险评估优化服务逻辑
- 评估报告服务重构数据源处理
- 新增数据库升级脚本

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tangweijie 2026-01-19 23:13:07 +08:00
parent 877e691792
commit 0d46e00ba7
13 changed files with 222 additions and 44 deletions

View File

@ -0,0 +1,15 @@
-- 升级脚本:为 prison_score 表添加 prison_area_id 和 prison_cell_id 字段
-- 执行时间2026-01-19
-- 为 prison_score 表添加 prison_area_id 字段
ALTER TABLE `prison_score`
ADD COLUMN `prison_area_id` bigint DEFAULT NULL COMMENT '监区ID' AFTER `remark`;
-- 为 prison_score 表添加 prison_cell_id 字段
ALTER TABLE `prison_score`
ADD COLUMN `prison_cell_id` bigint DEFAULT NULL COMMENT '监室ID' AFTER `prison_area_id`;
-- 添加索引(可选,如果查询频繁需要的话)
ALTER TABLE `prison_score`
ADD KEY `idx_prison_score_prison_area_id` (`prison_area_id`),
ADD KEY `idx_prison_score_prison_cell_id` (`prison_cell_id`);

View File

@ -39,7 +39,7 @@ public class QuestionnaireRecordPageReqVO extends PageParam {
@Schema(description = "及格状态1-及格 2-不及格 3-待评阅", example = "1")
private Integer passStatus;
@Schema(description = "风险等级1-高风险 2-中风险 3-低风险", example = "3")
@Schema(description = "风险等级1-低风险 2-中风险 3-高风险 4-极高风险", example = "3")
private Integer riskLevel;
@Schema(description = "总分")
@ -49,6 +49,10 @@ public class QuestionnaireRecordPageReqVO extends PageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] startTime;
@Schema(description = "测评结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] endTime;
@Schema(description = "测评截止时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] deadline;

View File

@ -54,6 +54,9 @@ public class QuestionnaireRecordSaveReqVO {
@Schema(description = "结束时间")
private LocalDateTime endTime;
@Schema(description = "答题时间")
private LocalDateTime answerTime;
@Schema(description = "截止日期")
private LocalDateTime deadline;
@ -74,7 +77,7 @@ public class QuestionnaireRecordSaveReqVO {
@Schema(description = "及格状态1-及格 2-不及格 3-待评阅", example = "2")
private Integer passStatus;
@Schema(description = "风险等级1-高风险 2-中风险 3-低风险", example = "3")
@Schema(description = "风险等级1-低风险 2-中风险 3-高风险 4-极高风险", example = "3")
private Integer riskLevel;
// ==================== 评阅信息 ====================

View File

@ -52,11 +52,7 @@ public class RiskAssessmentSaveReqVO {
@Schema(description = "管控建议")
private String suggestions;
@Schema(description = "评估人ID", example = "11993")
private Long assessorId;
@Schema(description = "评估人姓名", example = "赵六")
private String assessorName;
// 评估人ID和评估人姓名由后端自动从登录上下文获取不从前端传递
@Schema(description = "下次评估日期")
private LocalDate nextAssessmentDate;

View File

@ -21,6 +21,9 @@ public class ScoreSaveReqVO {
@NotEmpty(message = "罪犯编号不能为空")
private String prisonerNo;
@Schema(description = "罪犯姓名", example = "张三")
private String prisonerName;
@Schema(description = "考核年份", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "考核年份不能为空")
private Integer year;

View File

@ -83,6 +83,14 @@ public class ScoreDO extends BaseDO {
* 备注
*/
private String remark;
/**
* 监区ID
*/
private Long prisonAreaId;
/**
* 监室ID
*/
private Long prisonCellId;
}

View File

@ -90,10 +90,11 @@ public class AreaServiceImpl implements AreaService {
List<AreaDO> list = pageResult.getList();
for (AreaDO area : list) {
if (area.getParentId() == null || area.getParentId() == 0) {
// 只为顶级监区查询子监区
// 只为顶级监区查询子监区添加status=1筛选
List<AreaDO> children = areaMapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<AreaDO>()
.eq(AreaDO::getParentId, area.getId())
.eq(AreaDO::getDeleted, false)
.eq(AreaDO::getStatus, 1) // 只返回启用状态的子监区
.orderByAsc(AreaDO::getSort));
area.setChildren(children);
}
@ -187,6 +188,7 @@ public class AreaServiceImpl implements AreaService {
return areaMapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<AreaDO>()
.eq(AreaDO::getParentId, parentId)
.eq(AreaDO::getDeleted, false)
.eq(AreaDO::getStatus, 1) // 只返回启用状态的子监区
.orderByAsc(AreaDO::getSort));
}
@ -195,6 +197,7 @@ public class AreaServiceImpl implements AreaService {
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<AreaDO> queryWrapper =
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<AreaDO>()
.eq(AreaDO::getDeleted, false)
.eq(AreaDO::getStatus, 1) // 只返回启用状态的父级监区
.eq(level != null, AreaDO::getLevel, level)
.orderByAsc(AreaDO::getSort);
return areaMapper.selectList(queryWrapper);

View File

@ -219,9 +219,7 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
Set<String> sourceSet = new HashSet<>(dataSources);
// 根据数据源类型查询数据
Map<String, Object> dataMap = new HashMap<>();
// 根据数据源类型查询数据直接设置到 DTO 字段展平结构
if (sourceSet.contains("prisoner")) {
PrisonerDO prisoner = prisonerMapper.selectById(prisonerId);
if (prisoner != null) {
@ -233,7 +231,7 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
prisonerInfo.put("crime", prisoner.getCrime());
prisonerInfo.put("imprisonmentDate", prisoner.getImprisonmentDate());
prisonerInfo.put("status", prisoner.getStatus() != null ? prisoner.getStatus().getName() : null);
dataMap.put("prisoner", prisonerInfo);
result.setPrisoner(prisonerInfo);
}
}
@ -241,13 +239,14 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
List<ConsumptionDO> consumptions = consumptionMapper.selectList(new LambdaQueryWrapperX<ConsumptionDO>()
.eq(ConsumptionDO::getPrisonerId, prisonerId));
if (CollUtil.isNotEmpty(consumptions)) {
dataMap.put("consumptionRecords", consumptions.stream().map(c -> {
List<Map<String, Object>> 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()));
}).collect(Collectors.toList());
result.setConsumptionRecords(consumptionRecords);
// 计算汇总信息
BigDecimal totalAmount = consumptions.stream()
@ -258,7 +257,7 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
Map<String, Object> summary = new HashMap<>();
summary.put("totalAmount", totalAmount);
summary.put("recordCount", consumptions.size());
dataMap.put("consumptionSummary", summary);
result.setConsumptionSummary(summary);
}
}
@ -266,7 +265,7 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
List<ScoreDO> scores = scoreMapper.selectList(new LambdaQueryWrapperX<ScoreDO>()
.eq(ScoreDO::getPrisonerId, prisonerId));
if (CollUtil.isNotEmpty(scores)) {
dataMap.put("scoreRecords", scores.stream().map(s -> {
List<Map<String, Object>> scoreRecords = scores.stream().map(s -> {
Map<String, Object> item = new HashMap<>();
item.put("id", s.getId());
item.put("year", s.getYear());
@ -274,7 +273,8 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
item.put("totalScore", s.getTotalScore());
item.put("level", s.getLevel());
return item;
}).collect(Collectors.toList()));
}).collect(Collectors.toList());
result.setScoreRecords(scoreRecords);
// 计算汇总信息
BigDecimal totalScore = scores.stream()
@ -286,7 +286,7 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
summary.put("totalScore", totalScore);
summary.put("recordCount", scores.size());
summary.put("level", calculateAverageLevel(scores));
dataMap.put("scoreSummary", summary);
result.setScoreSummary(summary);
}
}
@ -294,14 +294,15 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
List<QuestionnaireRecordDO> records = questionnaireRecordMapper.selectList(new LambdaQueryWrapperX<QuestionnaireRecordDO>()
.eq(QuestionnaireRecordDO::getPrisonerId, prisonerId));
if (CollUtil.isNotEmpty(records)) {
dataMap.put("questionnaireRecords", records.stream().map(r -> {
List<Map<String, Object>> 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()));
}).collect(Collectors.toList());
result.setQuestionnaireRecords(questionnaireRecords);
}
}
@ -318,11 +319,10 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
riskInfo.put("suggestions", latestRisk.getSuggestions());
riskInfo.put("totalScore", latestRisk.getTotalScore());
riskInfo.put("assessmentDate", latestRisk.getAssessmentDate());
dataMap.put("riskAssessment", riskInfo);
result.setRiskAssessment(riskInfo);
}
}
result.setData(dataMap);
return result;
}
@ -396,6 +396,35 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
if (report == null) {
return null;
}
fillReportRelatedInfo(report);
return report;
}
@Override
public PageResult<EvaluationReportDO> getReportPage(EvaluationReportPageReqVO pageReqVO) {
PageResult<EvaluationReportDO> pageResult = evaluationReportMapper.selectPage(pageReqVO);
// 填充罪犯模板监区信息
if (CollUtil.isNotEmpty(pageResult.getList())) {
for (EvaluationReportDO report : pageResult.getList()) {
fillReportRelatedInfo(report);
}
}
return pageResult;
}
@Override
public EvaluationReportDO getReportByReportNo(String reportNo) {
EvaluationReportDO report = evaluationReportMapper.selectByReportNo(reportNo);
if (report != null) {
fillReportRelatedInfo(report);
}
return report;
}
/**
* 填充报告关联信息罪犯模板监区
*/
private void fillReportRelatedInfo(EvaluationReportDO report) {
// 填充罪犯信息
if (report.getPrisonerId() != null) {
PrisonerDO prisoner = prisonerMapper.selectById(report.getPrisonerId());
@ -418,22 +447,19 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
report.setAreaName(area.getName());
}
}
return report;
}
@Override
public PageResult<EvaluationReportDO> getReportPage(EvaluationReportPageReqVO pageReqVO) {
return evaluationReportMapper.selectPage(pageReqVO);
}
@Override
public EvaluationReportDO getReportByReportNo(String reportNo) {
return evaluationReportMapper.selectByReportNo(reportNo);
}
@Override
public List<EvaluationReportDO> getReportsByPrisonerId(Long prisonerId) {
return evaluationReportMapper.selectListByPrisonerId(prisonerId);
List<EvaluationReportDO> reports = evaluationReportMapper.selectListByPrisonerId(prisonerId);
if (CollUtil.isEmpty(reports)) {
return reports;
}
// 填充罪犯模板监区信息
for (EvaluationReportDO report : reports) {
fillReportRelatedInfo(report);
}
return reports;
}
@Override

View File

@ -5,6 +5,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
@ -29,15 +30,68 @@ public class DimensionDataSourcesRespDTO {
private Long prisonerId;
/**
* 数据源配置
* 罪犯档案信息
*/
private String dataSources;
private Map<String, Object> prisoner;
/**
* 聚合的数据
* key: 数据源类型 (prisoner, consumption, score, questionnaire, risk, etc.)
* value: 数据对象
* 消费记录列表
*/
private Map<String, Object> data;
private List<Map<String, Object>> consumptionRecords;
/**
* 消费汇总信息
*/
private Map<String, Object> consumptionSummary;
/**
* 计分考核记录列表
*/
private List<Map<String, Object>> scoreRecords;
/**
* 计分考核汇总信息
*/
private Map<String, Object> scoreSummary;
/**
* 问卷测评记录列表
*/
private List<Map<String, Object>> questionnaireRecords;
/**
* 风险评估信息
*/
private Map<String, Object> riskAssessment;
/**
* 违规记录列表
*/
private List<Map<String, Object>> violationRecords;
/**
* 奖励记录列表
*/
private List<Map<String, Object>> rewardRecords;
/**
* 会见记录列表
*/
private List<Map<String, Object>> visitRecords;
/**
* 劳动数据
*/
private Map<String, Object> laborData;
/**
* 家庭帮教数据
*/
private Map<String, Object> familyData;
/**
* 心理测评数据
*/
private Map<String, Object> psychologyData;
}

View File

@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.prison.convert.questionnairerecord.QuestionnaireR
import cn.iocoder.yudao.module.prison.enums.QuestionnaireRecordPassStatusEnum;
import cn.iocoder.yudao.module.prison.enums.QuestionnaireRecordStatusEnum;
import cn.iocoder.yudao.module.prison.enums.RiskLevelEnum;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*;
@ -238,6 +239,17 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic
: QuestionnaireRecordPassStatusEnum.FAILED.getStatus());
record.setRiskLevel(reqVO.getRiskLevel());
// 从登录上下文获取评阅人信息
Long currentUserId = SecurityFrameworkUtils.getLoginUserId();
String currentUserName = SecurityFrameworkUtils.getLoginUser().getInfo() != null
? SecurityFrameworkUtils.getLoginUser().getInfo().get("nickname")
: null;
record.setEvaluatorId(currentUserId);
record.setEvaluatorName(currentUserName);
record.setEvaluateTime(LocalDateTime.now());
// 保存评语
record.setRemark(reqVO.getComment());
questionnaireRecordMapper.updateById(record);
}

View File

@ -16,6 +16,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.prison.dal.mysql.riskassessment.RiskAssessmentMapper;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@ -47,6 +48,12 @@ public class RiskAssessmentServiceImpl implements RiskAssessmentService {
log.info("=== RiskAssessment Debug === violenceScore={}, escapeScore={}, suicideScore={}, totalScore={}",
createReqVO.getViolenceScore(), createReqVO.getEscapeScore(), createReqVO.getSuicideScore(), totalScore);
// 从登录上下文获取评估人信息
Long currentUserId = SecurityFrameworkUtils.getLoginUserId();
String currentUserName = SecurityFrameworkUtils.getLoginUser().getInfo() != null
? SecurityFrameworkUtils.getLoginUser().getInfo().get("nickname")
: null;
// 插入
RiskAssessmentDO riskAssessment = new RiskAssessmentDO();
riskAssessment.setPrisonerId(createReqVO.getPrisonerId());
@ -60,8 +67,9 @@ public class RiskAssessmentServiceImpl implements RiskAssessmentService {
riskAssessment.setRiskLevel(createReqVO.getRiskLevel());
riskAssessment.setRiskFactors(createReqVO.getRiskFactors());
riskAssessment.setSuggestions(createReqVO.getSuggestions());
riskAssessment.setAssessorId(createReqVO.getAssessorId());
riskAssessment.setAssessorName(createReqVO.getAssessorName());
// 从登录上下文获取评估人信息
riskAssessment.setAssessorId(currentUserId);
riskAssessment.setAssessorName(currentUserName);
riskAssessment.setNextAssessmentDate(createReqVO.getNextAssessmentDate());
riskAssessment.setStatus(createReqVO.getStatus());
riskAssessment.setRemark(createReqVO.getRemark());
@ -84,6 +92,12 @@ public class RiskAssessmentServiceImpl implements RiskAssessmentService {
updateReqVO.getSuicideScore()
);
// 从登录上下文获取评估人信息
Long currentUserId = SecurityFrameworkUtils.getLoginUserId();
String currentUserName = SecurityFrameworkUtils.getLoginUser().getInfo() != null
? SecurityFrameworkUtils.getLoginUser().getInfo().get("nickname")
: null;
// 更新
RiskAssessmentDO updateObj = new RiskAssessmentDO();
updateObj.setId(updateReqVO.getId());
@ -98,8 +112,9 @@ public class RiskAssessmentServiceImpl implements RiskAssessmentService {
updateObj.setRiskLevel(updateReqVO.getRiskLevel());
updateObj.setRiskFactors(updateReqVO.getRiskFactors());
updateObj.setSuggestions(updateReqVO.getSuggestions());
updateObj.setAssessorId(updateReqVO.getAssessorId());
updateObj.setAssessorName(updateReqVO.getAssessorName());
// 从登录上下文获取评估人信息
updateObj.setAssessorId(currentUserId);
updateObj.setAssessorName(currentUserName);
updateObj.setNextAssessmentDate(updateReqVO.getNextAssessmentDate());
updateObj.setStatus(updateReqVO.getStatus());
updateObj.setRemark(updateReqVO.getRemark());

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.prison.service.score;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.prison.controller.admin.score.vo.*;
@ -11,6 +12,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.prison.dal.mysql.score.ScoreMapper;
import cn.iocoder.yudao.module.prison.dal.mysql.PrisonerMapper;
import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*;
@ -27,10 +30,23 @@ public class ScoreServiceImpl implements ScoreService {
@Resource
private ScoreMapper scoreMapper;
@Resource
private PrisonerMapper prisonerMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createScore(ScoreSaveReqVO createReqVO) {
// 校验罪犯是否存在
PrisonerDO prisoner = prisonerMapper.selectById(createReqVO.getPrisonerId());
if (prisoner == null) {
throw exception(PRISONER_NOT_EXISTS);
}
// 插入
ScoreDO score = BeanUtils.toBean(createReqVO, ScoreDO.class);
// 填充关联字段监区ID监室ID
score.setPrisonAreaId(prisoner.getPrisonAreaId());
score.setPrisonCellId(prisoner.getPrisonCellId());
scoreMapper.insert(score);
// 返回
@ -38,11 +54,32 @@ public class ScoreServiceImpl implements ScoreService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateScore(ScoreSaveReqVO updateReqVO) {
// 校验存在
validateScoreExists(updateReqVO.getId());
// 查询旧数据获取 prisonerId
ScoreDO existingScore = scoreMapper.selectById(updateReqVO.getId());
if (existingScore == null) {
throw exception(SCORE_NOT_EXISTS);
}
// 如果 prisonerId 变化需要重新查询并更新关联字段
Long prisonerId = updateReqVO.getPrisonerId() != null
? updateReqVO.getPrisonerId()
: existingScore.getPrisonerId();
PrisonerDO prisoner = prisonerMapper.selectById(prisonerId);
if (prisoner == null) {
throw exception(PRISONER_NOT_EXISTS);
}
// 更新
ScoreDO updateObj = BeanUtils.toBean(updateReqVO, ScoreDO.class);
// 填充关联字段监区ID监室ID
updateObj.setPrisonAreaId(prisoner.getPrisonAreaId());
updateObj.setPrisonCellId(prisoner.getPrisonCellId());
scoreMapper.updateById(updateObj);
}

View File

@ -212,6 +212,8 @@ CREATE TABLE IF NOT EXISTS `prison_score` (
`assessor_name` varchar(50) DEFAULT NULL COMMENT '考核人姓名',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态1-待审核 2-已通过 3-已驳回',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`prison_area_id` bigint DEFAULT NULL COMMENT '监区ID',
`prison_cell_id` bigint DEFAULT NULL COMMENT '监室ID',
`creator` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) DEFAULT '' COMMENT '更新者',