From f0caa491330fbbf1a09afd75cf40381d708aeb48 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Sat, 24 Jan 2026 10:55:36 +0800 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E9=97=AE?= =?UTF-8?q?=E5=8D=B7=E4=BB=BB=E5=8A=A1=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增问卷任务CRUD功能(controller、service、dal) - 新增问卷任务统计相关VO(进度、区域对比、汇总统计) - 新增问卷记录状态枚举 - 更新问卷记录DO结构 - 更新dashboard服务实现 - 新增lombok依赖 --- .../PrisonQuestionnaireTaskController.java | 187 +++++++ .../vo/AreaComparisonRespVO.java | 33 ++ .../vo/QuestionnaireTaskCreateReqVO.java | 49 ++ .../vo/QuestionnaireTaskPageReqVO.java | 29 + .../vo/QuestionnaireTaskRespVO.java | 67 +++ .../vo/QuestionnaireTaskUpdateReqVO.java | 33 ++ .../vo/TaskAreaStatisticsRespVO.java | 60 ++ .../vo/TaskProgressRespVO.java | 75 +++ .../vo/TaskStatisticsSummaryRespVO.java | 33 ++ .../QuestionnaireTaskConvert.java | 24 + .../QuestionnaireTaskDO.java | 117 ++++ .../QuestionnaireRecordDO.java | 15 + .../QuestionnaireTaskMapper.java | 59 ++ .../prison/enums/ErrorCodeConstants.java | 9 + .../QuestionnaireRecordStatusEnum.java | 33 ++ .../impl/PrisonDashboardServiceImpl.java | 4 +- .../QuestionnaireTaskService.java | 149 +++++ .../QuestionnaireTaskServiceImpl.java | 517 ++++++++++++++++++ yudao-module-system/pom.xml | 7 +- 19 files changed, 1495 insertions(+), 5 deletions(-) create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/AreaComparisonRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskCreateReqVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskPageReqVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskUpdateReqVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskAreaStatisticsRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskProgressRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskStatisticsSummaryRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/questionnaire_task/QuestionnaireTaskConvert.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnaire_task/QuestionnaireTaskDO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/QuestionnaireRecordStatusEnum.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java new file mode 100644 index 0000000000..1bb7e02186 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java @@ -0,0 +1,187 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task; + +import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*; +import cn.iocoder.yudao.module.prison.convert.questionnaire_task.QuestionnaireTaskConvert; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO; +import cn.iocoder.yudao.module.prison.service.questionnaire_task.QuestionnaireTaskService; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - 问卷任务") +@RestController +@RequestMapping("/prison/questionnaire-task") +@Validated +public class PrisonQuestionnaireTaskController { + + @Resource + private QuestionnaireTaskService questionnaireTaskService; + + // ==================== 基础 CRUD ==================== + + @PostMapping("/create") + @Operation(summary = "创建问卷任务") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:create')") + public CommonResult createQuestionnaireTask(@Valid @RequestBody QuestionnaireTaskCreateReqVO createReqVO) { + return success(questionnaireTaskService.createQuestionnaireTask(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新问卷任务") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:update')") + public CommonResult updateQuestionnaireTask(@Valid @RequestBody QuestionnaireTaskUpdateReqVO updateReqVO) { + questionnaireTaskService.updateQuestionnaireTask(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除问卷任务") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:delete')") + public CommonResult deleteQuestionnaireTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + questionnaireTaskService.deleteQuestionnaireTask(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Operation(summary = "批量删除问卷任务") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:delete')") + public CommonResult deleteQuestionnaireTaskList(@NotEmpty(message = "任务ID列表不能为空") @RequestParam("ids") List ids) { + questionnaireTaskService.deleteQuestionnaireTaskListByIds(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得问卷任务") + @Parameter(name = "id", description = "任务ID", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult getQuestionnaireTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + QuestionnaireTaskDO task = questionnaireTaskService.getQuestionnaireTask(id); + return success(QuestionnaireTaskConvert.INSTANCE.convert(task)); + } + + @GetMapping("/page") + @Operation(summary = "获得问卷任务分页") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult> getQuestionnaireTaskPage(@Valid QuestionnaireTaskPageReqVO pageReqVO) { + PageResult pageResult = questionnaireTaskService.getQuestionnaireTaskPage(pageReqVO); + return success(QuestionnaireTaskConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出问卷任务 Excel") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportQuestionnaireTaskExcel(@Valid QuestionnaireTaskPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = questionnaireTaskService.getQuestionnaireTaskPage(pageReqVO).getList(); + ExcelUtils.write(response, "问卷任务.xls", "数据", QuestionnaireTaskRespVO.class, + QuestionnaireTaskConvert.INSTANCE.convertList(list)); + } + + // ==================== 任务执行相关 ==================== + + @PostMapping("/cancel") + @Operation(summary = "取消任务") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:cancel')") + public CommonResult cancelTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + questionnaireTaskService.cancelTask(id); + return success(true); + } + + @PostMapping("/finish") + @Operation(summary = "结束任务") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:finish')") + public CommonResult finishTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + questionnaireTaskService.finishTask(id); + return success(true); + } + + @PostMapping("/restart") + @Operation(summary = "重新开始任务") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:restart')") + public CommonResult restartTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + questionnaireTaskService.restartTask(id); + return success(true); + } + + // ==================== 进度跟踪相关 ==================== + + @GetMapping("/progress") + @Operation(summary = "获取任务进度") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult getTaskProgress(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + return success(questionnaireTaskService.getTaskProgress(id)); + } + + @GetMapping("/pending-prisoners") + @Operation(summary = "获取任务未完成人员") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult> getPendingPrisoners( + @NotNull(message = "任务ID不能为空") @RequestParam("id") Long id, + @Valid PageParam pageReqVO) { + return success(questionnaireTaskService.getPendingPrisoners(id, pageReqVO)); + } + + @PostMapping("/remind") + @Operation(summary = "提醒未完成人员") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:remind')") + public CommonResult remindPendingPrisoners(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + return success(questionnaireTaskService.remindPendingPrisoners(id)); + } + + // ==================== 统计相关 ==================== + + @GetMapping("/area-statistics") + @Operation(summary = "按监区统计任务完成情况") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult> getTaskAreaStatistics( + @NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + return success(questionnaireTaskService.getTaskAreaStatistics(id)); + } + + @GetMapping("/statistics-summary") + @Operation(summary = "获取全局任务统计汇总") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult getStatisticsSummary() { + return success(questionnaireTaskService.getStatisticsSummary()); + } + + @GetMapping("/area-comparison") + @Operation(summary = "按监区对比分析") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult> compareAreasByQuestionnaire( + @RequestParam(value = "questionnaireId", required = false) Long questionnaireId, + @RequestParam(value = "areaIds", required = false) List areaIds) { + return success(questionnaireTaskService.compareAreasByQuestionnaire(questionnaireId, areaIds)); + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/AreaComparisonRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/AreaComparisonRespVO.java new file mode 100644 index 0000000000..1c68f592ab --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/AreaComparisonRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.math.BigDecimal; + +/** + * 管理后台 - 按监区对比分析 Response VO + * + * @author xlcp + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AreaComparisonRespVO { + + @Schema(description = "任务ID", example = "18966") + private Long taskId; + + @Schema(description = "任务名称", example = "一月心理测评") + private String taskName; + + @Schema(description = "目标总人数") + private Integer totalCount; + + @Schema(description = "已完成人数") + private Integer completedCount; + + @Schema(description = "完成率(%)") + private BigDecimal completionRate; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskCreateReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskCreateReqVO.java new file mode 100644 index 0000000000..f3ec941fd1 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskCreateReqVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import jakarta.validation.constraints.*; +import java.util.*; +import java.time.LocalDateTime; + +/** + * 管理后台 - 问卷任务创建 Request VO + * + * @author xlcp + */ +@Data +@EqualsAndHashCode +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuestionnaireTaskCreateReqVO { + + @Schema(description = "任务名称", required = true, example = "一月心理测评") + @NotBlank(message = "任务名称不能为空") + private String taskName; + + @Schema(description = "问卷ID", required = true, example = "18966") + @NotNull(message = "问卷ID不能为空") + private Long questionnaireId; + + @Schema(description = "目标类型:1-指定犯人 2-指定监区 3-全部犯人", required = true, example = "2") + @NotNull(message = "目标类型不能为空") + private Integer targetType; + + @Schema(description = "犯人ID列表(当targetType=1时)") + private List prisonerIds; + + @Schema(description = "监区ID(当targetType=2时)") + private Long areaId; + + @Schema(description = "任务开始时间") + private LocalDateTime startTime; + + @Schema(description = "截止时间", required = true) + @NotNull(message = "截止时间不能为空") + private LocalDateTime deadline; + + @Schema(description = "备注", example = "一月度常规心理测评") + private String remark; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskPageReqVO.java new file mode 100644 index 0000000000..553487219e --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskPageReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 问卷任务分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class QuestionnaireTaskPageReqVO extends PageParam { + + @Schema(description = "任务名称", example = "测试任务") + private String taskName; + + @Schema(description = "问卷ID", example = "1") + private Long questionnaireId; + + @Schema(description = "状态:1-未开始 2-进行中 3-已完成 4-已取消", example = "2") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskRespVO.java new file mode 100644 index 0000000000..c3a0afbed8 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskRespVO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 管理后台 - 问卷任务详情 Response VO + * + * @author xlcp + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuestionnaireTaskRespVO { + + @Schema(description = "任务ID", example = "18966") + private Long id; + + @Schema(description = "任务名称", example = "一月心理测评") + private String taskName; + + @Schema(description = "问卷ID", example = "18966") + private Long questionnaireId; + + @Schema(description = "问卷名称", example = "心理评估问卷") + private String questionnaireName; + + @Schema(description = "目标类型:1-指定犯人 2-指定监区 3-全部犯人", example = "2") + private Integer targetType; + + @Schema(description = "监区ID", example = "456") + private Long areaId; + + @Schema(description = "监区名称", example = "一监区") + private String areaName; + + @Schema(description = "目标总人数") + private Integer totalCount; + + @Schema(description = "已完成人数") + private Integer completedCount; + + @Schema(description = "待完成人数") + private Integer pendingCount; + + @Schema(description = "完成率(%)") + private BigDecimal completionRate; + + @Schema(description = "状态:1-草稿 2-进行中 3-已结束 4-已取消", example = "2") + private Integer status; + + @Schema(description = "任务开始时间") + private LocalDateTime startTime; + + @Schema(description = "截止时间") + private LocalDateTime deadline; + + @Schema(description = "备注", example = "一月度常规心理测评") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskUpdateReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskUpdateReqVO.java new file mode 100644 index 0000000000..235fc9b556 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/QuestionnaireTaskUpdateReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import jakarta.validation.constraints.*; +import java.time.LocalDateTime; + +/** + * 管理后台 - 问卷任务更新 Request VO + * + * @author xlcp + */ +@Data +@EqualsAndHashCode +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuestionnaireTaskUpdateReqVO { + + @Schema(description = "任务ID", required = true, example = "18966") + @NotNull(message = "任务ID不能为空") + private Long id; + + @Schema(description = "任务名称", example = "一月心理测评") + private String taskName; + + @Schema(description = "截止时间") + private LocalDateTime deadline; + + @Schema(description = "备注", example = "一月度常规心理测评") + private String remark; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskAreaStatisticsRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskAreaStatisticsRespVO.java new file mode 100644 index 0000000000..1476e2758b --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskAreaStatisticsRespVO.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.math.BigDecimal; +import java.util.List; + +/** + * 管理后台 - 任务按监区统计 Response VO + * + * @author xlcp + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TaskAreaStatisticsRespVO { + + @Schema(description = "监区ID", example = "456") + private Long areaId; + + @Schema(description = "监区名称", example = "一监区") + private String areaName; + + @Schema(description = "目标总人数") + private Integer totalCount; + + @Schema(description = "已完成人数") + private Integer completedCount; + + @Schema(description = "完成率(%)") + private BigDecimal completionRate; + + @Schema(description = "平均分") + private BigDecimal avgScore; + + @Schema(description = "及格率(%)") + private BigDecimal passRate; + + @Schema(description = "风险分布") + private RiskDistribution riskDistribution; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RiskDistribution { + + @Schema(description = "高风险人数") + private Integer highRisk; + + @Schema(description = "中风险人数") + private Integer mediumRisk; + + @Schema(description = "低风险人数") + private Integer lowRisk; + + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskProgressRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskProgressRespVO.java new file mode 100644 index 0000000000..55de07e3fa --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskProgressRespVO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 管理后台 - 任务进度详情 Response VO + * + * @author xlcp + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TaskProgressRespVO { + + @Schema(description = "任务ID", example = "18966") + private Long taskId; + + @Schema(description = "任务名称", example = "一月心理测评") + private String taskName; + + @Schema(description = "问卷名称", example = "心理评估问卷") + private String questionnaireName; + + @Schema(description = "状态:1-草稿 2-进行中 3-已结束 4-已取消", example = "2") + private Integer status; + + @Schema(description = "任务开始时间") + private LocalDateTime startTime; + + @Schema(description = "截止时间") + private LocalDateTime deadline; + + @Schema(description = "目标总人数") + private Integer totalCount; + + @Schema(description = "已完成人数") + private Integer completedCount; + + @Schema(description = "待完成人数") + private Integer pendingCount; + + @Schema(description = "完成率(%)") + private BigDecimal completionRate; + + @Schema(description = "状态分布") + private StatusBreakdown statusBreakdown; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class StatusBreakdown { + + @Schema(description = "待测评人数") + private Integer pending; + + @Schema(description = "测评中人数") + private Integer inProgress; + + @Schema(description = "已完成人数") + private Integer completed; + + @Schema(description = "已过期人数") + private Integer expired; + + @Schema(description = "已取消人数") + private Integer cancelled; + + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskStatisticsSummaryRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskStatisticsSummaryRespVO.java new file mode 100644 index 0000000000..a51f9ddd2b --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/TaskStatisticsSummaryRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.math.BigDecimal; + +/** + * 管理后台 - 任务统计汇总 Response VO + * + * @author xlcp + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TaskStatisticsSummaryRespVO { + + @Schema(description = "任务总数") + private Integer taskCount; + + @Schema(description = "目标总人数") + private Integer totalPrisoners; + + @Schema(description = "已完成人数") + private Integer totalCompleted; + + @Schema(description = "待完成人数") + private Integer totalPending; + + @Schema(description = "整体完成率(%)") + private BigDecimal overallCompletionRate; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/questionnaire_task/QuestionnaireTaskConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/questionnaire_task/QuestionnaireTaskConvert.java new file mode 100644 index 0000000000..48de7d57a3 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/questionnaire_task/QuestionnaireTaskConvert.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.prison.convert.questionnaire_task; + +import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface QuestionnaireTaskConvert { + + QuestionnaireTaskConvert INSTANCE = Mappers.getMapper(QuestionnaireTaskConvert.class); + + QuestionnaireTaskDO convert(QuestionnaireTaskUpdateReqVO bean); + + QuestionnaireTaskRespVO convert(QuestionnaireTaskDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnaire_task/QuestionnaireTaskDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnaire_task/QuestionnaireTaskDO.java new file mode 100644 index 0000000000..6acba90c3a --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnaire_task/QuestionnaireTaskDO.java @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; + +/** + * 问卷任务 DO + * + * @author xlcp + */ +@TableName("prison_questionnaire_task") +@KeySequence("prison_questionnaire_task_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuestionnaireTaskDO extends TenantBaseDO { + + /** + * 任务ID + */ + @TableId + private Long id; + + /** + * 任务名称 + */ + private String taskName; + + // ==================== 问卷信息 ==================== + + /** + * 问卷ID + */ + private Long questionnaireId; + + /** + * 问卷名称 + */ + private String questionnaireName; + + // ==================== 目标范围 ==================== + + /** + * 目标类型:1-指定犯人 2-指定监区 3-全部犯人 + */ + private Integer targetType; + + /** + * 监区ID(当targetType=2时) + */ + private Long areaId; + + /** + * 监区名称 + */ + private String areaName; + + /** + * 犯人ID列表(当targetType=1时,JSON格式) + */ + private String prisonerIds; + + // ==================== 时间设置 ==================== + + /** + * 任务开始时间 + */ + private LocalDateTime startTime; + + /** + * 截止时间 + */ + private LocalDateTime deadline; + + // ==================== 任务状态 ==================== + + /** + * 状态:1-草稿 2-进行中 3-已结束 4-已取消 + */ + private Integer status; + + // ==================== 统计信息 ==================== + + /** + * 目标总人数 + */ + private Integer totalCount; + + /** + * 已完成人数 + */ + private Integer completedCount; + + /** + * 待完成人数 + */ + private Integer pendingCount; + + /** + * 完成率(%) + */ + private java.math.BigDecimal completionRate; + + // ==================== 备注 ==================== + + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java index 6023c428c4..410da48e1f 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java @@ -54,6 +54,21 @@ public class QuestionnaireRecordDO extends TenantBaseDO { */ private String prisonerName; + // ==================== 任务关联 ==================== + + /** + * 所属任务ID + */ + private Long taskId; + /** + * 犯人所属监区ID + */ + private Long prisonerAreaId; + /** + * 犯人所属监区名称 + */ + private String prisonerAreaName; + // ==================== 测评状态 ==================== /** diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java new file mode 100644 index 0000000000..c1f043fb1b --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; + +/** + * 问卷任务 Mapper + * + * @author xlcp + */ +@Mapper +public interface QuestionnaireTaskMapper extends BaseMapperX { + + default PageResult selectPage(QuestionnaireTaskPageReqVO pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .likeIfPresent(QuestionnaireTaskDO::getTaskName, pageReqVO.getTaskName()) + .eqIfPresent(QuestionnaireTaskDO::getStatus, pageReqVO.getStatus()) + .eqIfPresent(QuestionnaireTaskDO::getQuestionnaireId, pageReqVO.getQuestionnaireId()) + .betweenIfPresent(QuestionnaireTaskDO::getCreateTime, pageReqVO.getCreateTime()) + .orderByDesc(QuestionnaireTaskDO::getCreateTime)); + } + + @Select("SELECT qt.*, q.title as questionnaire_title FROM prison_questionnaire_task qt " + + "LEFT JOIN prison_questionnaire q ON qt.questionnaire_id = q.id " + + "WHERE qt.id = #{id}") + Map selectTaskDetailById(@Param("id") Long id); + + @Select("UPDATE prison_questionnaire_task qt " + + "SET total_count = (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId}), " + + "completed_count = (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId} AND r.status = 3), " + + "pending_count = (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId} AND r.status IN (1, 2)), " + + "completion_rate = CASE " + + "WHEN (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId}) > 0 " + + "THEN (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId} AND r.status = 3) * 100.0 / " + + "(SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId}) " + + "ELSE 0 END, " + + "update_time = NOW() " + + "WHERE qt.id = #{taskId}") + void updateTaskStatistics(@Param("taskId") Long taskId); + + @Select("SELECT COUNT(*) as task_count, " + + "SUM(total_count) as total_prisoners, " + + "SUM(completed_count) as total_completed, " + + "AVG(completion_rate) as avg_completion_rate " + + "FROM prison_questionnaire_task " + + "WHERE deleted = 0 AND status IN (2, 3)") + Map selectTaskStatisticsSummary(); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java index 204591de15..d2fad0ce19 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java @@ -89,4 +89,13 @@ public class ErrorCodeConstants { 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, "评估报告模板不存在"); + // ========== 问卷任务 14xxxx ========== + public static final ErrorCode QUESTIONNAIRE_TASK_NOT_EXISTS = new ErrorCode(14_000_001, "问卷任务不存在"); + public static final ErrorCode QUESTIONNAIRE_TASK_CANNOT_UPDATE = new ErrorCode(14_000_002, "只有草稿状态的任务可以修改"); + public static final ErrorCode QUESTIONNAIRE_TASK_CANNOT_CANCEL = new ErrorCode(14_000_003, "已结束或已取消的任务不能取消"); + public static final ErrorCode QUESTIONNAIRE_TASK_ALREADY_CANCELLED = new ErrorCode(14_000_004, "任务已被取消"); + public static final ErrorCode QUESTIONNAIRE_TASK_CANNOT_RESTART = new ErrorCode(14_000_005, "只有已结束的任务可以重新开始"); + public static final ErrorCode ERROR_TASK_PRISONER_EMPTY = new ErrorCode(14_000_006, "请选择要参与的犯人"); + public static final ErrorCode ERROR_TASK_AREA_EMPTY = new ErrorCode(14_000_007, "请选择监区"); + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/QuestionnaireRecordStatusEnum.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/QuestionnaireRecordStatusEnum.java new file mode 100644 index 0000000000..53190d3336 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/QuestionnaireRecordStatusEnum.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.prison.enums.questionnaire; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 问卷记录状态枚举 + * + * @author xlcp + */ +@Getter +@AllArgsConstructor +public enum QuestionnaireRecordStatusEnum { + + PENDING(1, "待完成"), + IN_PROGRESS(2, "进行中"), + COMPLETED(3, "已完成"), + EXPIRED(4, "已过期"), + CANCELLED(5, "已取消"); + + private final Integer code; + private final String name; + + public static QuestionnaireRecordStatusEnum getByCode(Integer code) { + for (QuestionnaireRecordStatusEnum statusEnum : values()) { + if (statusEnum.getCode().equals(code)) { + return statusEnum; + } + } + return null; + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java index e75c7bad9e..4a5bc349a1 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java @@ -369,9 +369,11 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { Collectors.summingDouble(c -> c.getTotalAmount() != null ? c.getTotalAmount().doubleValue() : 0) )); for (Map.Entry entry : monthlyConsumptionMap.entrySet()) { + Double consumption = entry.getValue(); monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder() .category(entry.getKey()) - .perCapita(entry.getValue().intValue()) + .monthlyStandard(consumption.intValue()) // 支出 = 该月实际消费总额 + .perCapita(consumption.intValue()) // 人均消费 = 该月实际消费总额 .build()); } vo.setConsumptionMonthlyData(monthlyDataList); diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java new file mode 100644 index 0000000000..2f24cbe345 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.prison.service.questionnaire_task; + +import java.util.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import jakarta.validation.Valid; + +import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire.QuestionnaireDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.area.AreaDO; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 问卷任务 Service 接口 + * + * @author xlcp + */ +public interface QuestionnaireTaskService { + + // ==================== 基础 CRUD ==================== + + /** + * 创建问卷任务 + * + * @param createReqVO 创建信息 + * @return 任务ID + */ + Long createQuestionnaireTask(@Valid QuestionnaireTaskCreateReqVO createReqVO); + + /** + * 更新问卷任务 + * + * @param updateReqVO 更新信息 + */ + void updateQuestionnaireTask(@Valid QuestionnaireTaskUpdateReqVO updateReqVO); + + /** + * 删除问卷任务 + * + * @param id 任务ID + */ + void deleteQuestionnaireTask(Long id); + + /** + * 批量删除问卷任务 + * + * @param ids 任务ID列表 + */ + void deleteQuestionnaireTaskListByIds(List ids); + + /** + * 获得问卷任务 + * + * @param id 任务ID + * @return 问卷任务 + */ + QuestionnaireTaskDO getQuestionnaireTask(Long id); + + /** + * 获得问卷任务分页 + * + * @param pageReqVO 分页查询 + * @return 问卷任务分页 + */ + PageResult getQuestionnaireTaskPage(QuestionnaireTaskPageReqVO pageReqVO); + + // ==================== 任务执行相关 ==================== + + /** + * 取消任务 + * + * @param id 任务ID + */ + void cancelTask(Long id); + + /** + * 结束任务 + * + * @param id 任务ID + */ + void finishTask(Long id); + + /** + * 重新开始已结束的任务 + * + * @param id 任务ID + */ + void restartTask(Long id); + + // ==================== 进度跟踪相关 ==================== + + /** + * 获取任务进度详情 + * + * @param id 任务ID + * @return 进度详情 + */ + TaskProgressRespVO getTaskProgress(Long id); + + /** + * 获取任务未完成人员列表 + * + * @param id 任务ID + * @param pageReqVO 分页参数 + * @return 未完成人员分页 + */ + PageResult getPendingPrisoners(Long id, PageParam pageReqVO); + + /** + * 提醒未完成人员 + * + * @param id 任务ID + * @return 提醒人数 + */ + Integer remindPendingPrisoners(Long id); + + // ==================== 统计相关 ==================== + + /** + * 按监区统计任务完成情况 + * + * @param id 任务ID + * @return 监区统计列表 + */ + List getTaskAreaStatistics(Long id); + + /** + * 获取全局任务统计汇总 + * + * @return 统计汇总 + */ + TaskStatisticsSummaryRespVO getStatisticsSummary(); + + /** + * 按监区对比分析 + * + * @param questionnaireId 问卷ID(可选,为空则统计所有问卷) + * @param areaIds 监区ID列表 + * @return 对比数据 + */ + List compareAreasByQuestionnaire(Long questionnaireId, List areaIds); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java new file mode 100644 index 0000000000..cd5510eb57 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java @@ -0,0 +1,517 @@ +package cn.iocoder.yudao.module.prison.service.questionnaire_task; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire.QuestionnaireDO; +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.mysql.questionnaire_task.QuestionnaireTaskMapper; +import cn.iocoder.yudao.module.prison.dal.mysql.questionnaire.QuestionnaireMapper; +import cn.iocoder.yudao.module.prison.dal.mysql.questionnairerecord.QuestionnaireRecordMapper; +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.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.prison.enums.questionnaire.QuestionnaireRecordStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 问卷任务 Service 实现类 + * + * @author xlcp + */ +@Service +@Slf4j +public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { + + @Resource + private QuestionnaireTaskMapper questionnaireTaskMapper; + + @Resource + private QuestionnaireMapper questionnaireMapper; + + @Resource + private QuestionnaireRecordMapper questionnaireRecordMapper; + + @Resource + private PrisonerMapper prisonerMapper; + + @Resource + private AreaMapper areaMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createQuestionnaireTask(QuestionnaireTaskCreateReqVO createReqVO) { + QuestionnaireDO questionnaire = questionnaireMapper.selectById(createReqVO.getQuestionnaireId()); + if (questionnaire == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_NOT_EXISTS); + } + + validateTarget(createReqVO); + + QuestionnaireTaskDO task = QuestionnaireTaskDO.builder() + .taskName(createReqVO.getTaskName()) + .questionnaireId(createReqVO.getQuestionnaireId()) + .questionnaireName(questionnaire.getTitle()) + .targetType(createReqVO.getTargetType()) + .startTime(createReqVO.getStartTime()) + .deadline(createReqVO.getDeadline()) + .status(2) + .totalCount(0) + .completedCount(0) + .pendingCount(0) + .completionRate(BigDecimal.ZERO) + .remark(createReqVO.getRemark()) + .build(); + + if (createReqVO.getTargetType() == 1) { + task.setPrisonerIds(CollUtil.join(createReqVO.getPrisonerIds(), ",")); + } else if (createReqVO.getTargetType() == 2) { + task.setAreaId(createReqVO.getAreaId()); + AreaDO area = areaMapper.selectById(createReqVO.getAreaId()); + if (area != null) { + task.setAreaName(area.getName()); + } + } + + questionnaireTaskMapper.insert(task); + + List prisoners = getTargetPrisoners(createReqVO); + if (CollUtil.isNotEmpty(prisoners)) { + createRecordsForPrisoners(task, prisoners); + } + + updateTaskStatistics(task.getId()); + + return task.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateQuestionnaireTask(QuestionnaireTaskUpdateReqVO updateReqVO) { + QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(updateReqVO.getId()); + if (task == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS); + } + + if (!task.getStatus().equals(1)) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_CANNOT_UPDATE); + } + + if (updateReqVO.getTaskName() != null) { + task.setTaskName(updateReqVO.getTaskName()); + } + if (updateReqVO.getDeadline() != null) { + task.setDeadline(updateReqVO.getDeadline()); + } + if (updateReqVO.getRemark() != null) { + task.setRemark(updateReqVO.getRemark()); + } + + questionnaireTaskMapper.updateById(task); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteQuestionnaireTask(Long id) { + QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id); + if (task == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS); + } + + questionnaireRecordMapper.delete(new LambdaQueryWrapper() + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)); + + questionnaireTaskMapper.deleteById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteQuestionnaireTaskListByIds(List ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + + List records = questionnaireRecordMapper.selectList( + new LambdaQueryWrapper() + .in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, ids)); + + if (CollUtil.isNotEmpty(records)) { + questionnaireRecordMapper.deleteBatchIds(records.stream() + .map(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getId) + .collect(Collectors.toList())); + } + + questionnaireTaskMapper.deleteBatchIds(ids); + } + + @Override + public QuestionnaireTaskDO getQuestionnaireTask(Long id) { + return questionnaireTaskMapper.selectById(id); + } + + @Override + public PageResult getQuestionnaireTaskPage(QuestionnaireTaskPageReqVO pageReqVO) { + Page page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .likeIfPresent(QuestionnaireTaskDO::getTaskName, pageReqVO.getTaskName()) + .eqIfPresent(QuestionnaireTaskDO::getStatus, pageReqVO.getStatus()) + .eqIfPresent(QuestionnaireTaskDO::getQuestionnaireId, pageReqVO.getQuestionnaireId()) + .betweenIfPresent(QuestionnaireTaskDO::getCreateTime, pageReqVO.getCreateTime()) + .orderByDesc(QuestionnaireTaskDO::getCreateTime); + + Page result = questionnaireTaskMapper.selectPage(page, wrapper); + return new PageResult<>(result.getRecords(), result.getTotal()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelTask(Long id) { + QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id); + if (task == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS); + } + + if (task.getStatus().equals(3) || task.getStatus().equals(4)) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_CANNOT_CANCEL); + } + + task.setStatus(4); + questionnaireTaskMapper.updateById(task); + + questionnaireRecordMapper.update(null, + new LambdaUpdateWrapper() + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id) + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.PENDING.getCode()) + .set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.CANCELLED.getCode())); + + updateTaskStatistics(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void finishTask(Long id) { + QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id); + if (task == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS); + } + + if (task.getStatus().equals(4)) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_ALREADY_CANCELLED); + } + + task.setStatus(3); + questionnaireTaskMapper.updateById(task); + + questionnaireRecordMapper.update(null, + new LambdaUpdateWrapper() + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id) + .in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, + QuestionnaireRecordStatusEnum.PENDING.getCode(), + QuestionnaireRecordStatusEnum.IN_PROGRESS.getCode()) + .set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.EXPIRED.getCode())); + + updateTaskStatistics(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void restartTask(Long id) { + QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id); + if (task == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS); + } + + if (!task.getStatus().equals(3)) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_CANNOT_RESTART); + } + + task.setStatus(2); + task.setDeadline(LocalDateTime.now().plusDays(7)); + questionnaireTaskMapper.updateById(task); + + questionnaireRecordMapper.update(null, + new LambdaUpdateWrapper() + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id) + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.EXPIRED.getCode()) + .set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.PENDING.getCode()) + .set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getDeadline, task.getDeadline())); + } + + @Override + public TaskProgressRespVO getTaskProgress(Long id) { + QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id); + if (task == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS); + } + + Map stats = questionnaireRecordMapper.selectMaps( + new QueryWrapper() + .select("COUNT(*) as total", + "SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as pending", + "SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as in_progress", + "SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as completed", + "SUM(CASE WHEN status = 4 THEN 1 ELSE 0 END) as expired", + "SUM(CASE WHEN status = 5 THEN 1 ELSE 0 END) as cancelled") + .eq("task_id", id) + ).stream().findFirst().orElse(Collections.emptyMap()); + + int total = ((Number) stats.getOrDefault("total", 0)).intValue(); + int pending = ((Number) stats.getOrDefault("pending", 0)).intValue(); + int inProgress = ((Number) stats.getOrDefault("in_progress", 0)).intValue(); + int completed = ((Number) stats.getOrDefault("completed", 0)).intValue(); + + return TaskProgressRespVO.builder() + .taskId(id) + .taskName(task.getTaskName()) + .questionnaireName(task.getQuestionnaireName()) + .status(task.getStatus()) + .startTime(task.getStartTime()) + .deadline(task.getDeadline()) + .totalCount(total) + .completedCount(completed) + .pendingCount(pending + inProgress) + .completionRate(total > 0 ? BigDecimal.valueOf(completed * 100.0 / total).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO) + .statusBreakdown(TaskProgressRespVO.StatusBreakdown.builder() + .pending(pending) + .inProgress(inProgress) + .completed(completed) + .expired(((Number) stats.getOrDefault("expired", 0)).intValue()) + .cancelled(((Number) stats.getOrDefault("cancelled", 0)).intValue()) + .build()) + .build(); + } + + @Override + public PageResult getPendingPrisoners(Long id, PageParam pageReqVO) { + Page page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id) + .in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, + QuestionnaireRecordStatusEnum.PENDING.getCode(), + QuestionnaireRecordStatusEnum.IN_PROGRESS.getCode()) + .orderByAsc(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getCreateTime); + + Page result = questionnaireRecordMapper.selectPage(page, wrapper); + return new PageResult<>(result.getRecords(), result.getTotal()); + } + + @Override + public Integer remindPendingPrisoners(Long id) { + List pendingRecords = questionnaireRecordMapper.selectList( + new LambdaQueryWrapper() + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id) + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.PENDING.getCode()) + ); + + if (CollUtil.isEmpty(pendingRecords)) { + return 0; + } + + log.info("提醒任务 {} 中 {} 位未完成人员", id, pendingRecords.size()); + return pendingRecords.size(); + } + + @Override + public List getTaskAreaStatistics(Long id) { + List> stats = questionnaireRecordMapper.selectMaps( + new QueryWrapper() + .select("area_id", "area_name", + "COUNT(*) as total_count", + "SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as completed_count", + "AVG(CASE WHEN status = 3 THEN total_score ELSE NULL END) as avg_score", + "SUM(CASE WHEN status = 3 AND pass_status = 1 THEN 1 ELSE 0 END) * 100.0 / NULLIF(SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END), 0) as pass_rate", + "SUM(CASE WHEN risk_level = 1 THEN 1 ELSE 0 END) as high_risk_count", + "SUM(CASE WHEN risk_level = 2 THEN 1 ELSE 0 END) as medium_risk_count", + "SUM(CASE WHEN risk_level = 3 THEN 1 ELSE 0 END) as low_risk_count") + .eq("task_id", id) + .groupBy("area_id", "area_name") + .orderByDesc("completed_count") + ); + + return stats.stream().map(stat -> { + int total = ((Number) stat.getOrDefault("total_count", 0)).intValue(); + int completed = ((Number) stat.getOrDefault("completed_count", 0)).intValue(); + BigDecimal avgScore = stat.get("avg_score") != null ? + new BigDecimal(stat.get("avg_score").toString()) : BigDecimal.ZERO; + BigDecimal passRate = stat.get("pass_rate") != null ? + new BigDecimal(stat.get("pass_rate").toString()) : BigDecimal.ZERO; + + return TaskAreaStatisticsRespVO.builder() + .areaId(stat.get("area_id") != null ? ((Number) stat.get("area_id")).longValue() : null) + .areaName((String) stat.get("area_name")) + .totalCount(total) + .completedCount(completed) + .completionRate(total > 0 ? BigDecimal.valueOf(completed * 100.0 / total).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO) + .avgScore(avgScore.setScale(2, RoundingMode.HALF_UP)) + .passRate(passRate.setScale(2, RoundingMode.HALF_UP)) + .riskDistribution(TaskAreaStatisticsRespVO.RiskDistribution.builder() + .highRisk(((Number) stat.getOrDefault("high_risk_count", 0)).intValue()) + .mediumRisk(((Number) stat.getOrDefault("medium_risk_count", 0)).intValue()) + .lowRisk(((Number) stat.getOrDefault("low_risk_count", 0)).intValue()) + .build()) + .build(); + }).collect(Collectors.toList()); + } + + @Override + public TaskStatisticsSummaryRespVO getStatisticsSummary() { + Map stats = questionnaireTaskMapper.selectMaps( + new QueryWrapper() + .select("COUNT(*) as task_count", + "SUM(total_count) as total_prisoners", + "SUM(completed_count) as total_completed", + "AVG(completion_rate) as avg_completion_rate") + .eq("deleted", 0) + .in("status", Arrays.asList(2, 3)) + ).stream().findFirst().orElse(Collections.emptyMap()); + + int taskCount = ((Number) stats.getOrDefault("task_count", 0)).intValue(); + int totalPrisoners = ((Number) stats.getOrDefault("total_prisoners", 0)).intValue(); + int totalCompleted = ((Number) stats.getOrDefault("total_completed", 0)).intValue(); + + return TaskStatisticsSummaryRespVO.builder() + .taskCount(taskCount) + .totalPrisoners(totalPrisoners) + .totalCompleted(totalCompleted) + .totalPending(totalPrisoners - totalCompleted) + .overallCompletionRate(totalPrisoners > 0 ? + BigDecimal.valueOf(totalCompleted * 100.0 / totalPrisoners).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO) + .build(); + } + + @Override + public List compareAreasByQuestionnaire(Long questionnaireId, List areaIds) { + if (CollUtil.isEmpty(areaIds)) { + return Collections.emptyList(); + } + + List> stats; + if (questionnaireId != null) { + stats = questionnaireTaskMapper.selectMaps( + new QueryWrapper() + .select("qt.id as task_id", "qt.task_name", + "SUM(qr.total_count) as total_count", + "SUM(qr.completed_count) as completed_count") + .from("prison_questionnaire_task qt") + .innerJoin("prison_questionnaire_record qr ON qt.id = qr.task_id") + .eq("qt.deleted", 0) + .eq("qt.questionnaire_id", questionnaireId) + .in("qr.area_id", areaIds) + .groupBy("qt.id", "qt.task_name") + ); + } else { + stats = questionnaireTaskMapper.selectMaps( + new QueryWrapper() + .select("qt.id as task_id", "qt.task_name", + "SUM(qr.total_count) as total_count", + "SUM(qr.completed_count) as completed_count") + .from("prison_questionnaire_task qt") + .innerJoin("prison_questionnaire_record qr ON qt.id = qr.task_id") + .eq("qt.deleted", 0) + .in("qr.area_id", areaIds) + .groupBy("qt.id", "qt.task_name") + ); + } + + return stats.stream().map(stat -> AreaComparisonRespVO.builder() + .taskId(((Number) stat.get("task_id")).longValue()) + .taskName((String) stat.get("task_name")) + .totalCount(((Number) stat.getOrDefault("total_count", 0)).intValue()) + .completedCount(((Number) stat.getOrDefault("completed_count", 0)).intValue()) + .build() + ).collect(Collectors.toList()); + } + + private void validateTarget(QuestionnaireTaskCreateReqVO createReqVO) { + if (createReqVO.getTargetType() == 1) { + if (CollUtil.isEmpty(createReqVO.getPrisonerIds())) { + throw new ServiceException(ErrorCodeConstants.ERROR_TASK_PRISONER_EMPTY); + } + } else if (createReqVO.getTargetType() == 2) { + if (createReqVO.getAreaId() == null) { + throw new ServiceException(ErrorCodeConstants.ERROR_TASK_AREA_EMPTY); + } + AreaDO area = areaMapper.selectById(createReqVO.getAreaId()); + if (area == null) { + throw new ServiceException(ErrorCodeConstants.AREA_NOT_EXISTS); + } + } + } + + private List getTargetPrisoners(QuestionnaireTaskCreateReqVO createReqVO) { + switch (createReqVO.getTargetType()) { + case 1: + return prisonerMapper.selectBatchIds(createReqVO.getPrisonerIds()); + case 2: + return prisonerMapper.selectList( + new LambdaQueryWrapper() + .eq(PrisonerDO::getPrisonAreaId, createReqVO.getAreaId()) + .eq(PrisonerDO::getStatus, 1) + ); + case 3: + return prisonerMapper.selectList( + new LambdaQueryWrapper() + .eq(PrisonerDO::getStatus, 1) + ); + default: + return Collections.emptyList(); + } + } + + private void createRecordsForPrisoners(QuestionnaireTaskDO task, List prisoners) { + QuestionnaireDO questionnaire = questionnaireMapper.selectById(task.getQuestionnaireId()); + + List records = prisoners.stream() + .filter(p -> p.getStatus() != null && p.getStatus().getValue() == 1) + .map(prisoner -> cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO.builder() + .questionnaireId(task.getQuestionnaireId()) + .questionnaireName(task.getQuestionnaireName()) + .prisonerId(prisoner.getId()) + .prisonerNo(prisoner.getPrisonerNo()) + .prisonerName(prisoner.getName()) + .prisonerAreaId(prisoner.getPrisonAreaId()) + .prisonerAreaName(getAreaName(prisoner.getPrisonAreaId())) + .taskId(task.getId()) + .status(QuestionnaireRecordStatusEnum.PENDING.getCode()) + .startTime(task.getStartTime()) + .deadline(task.getDeadline()) + .passScore(questionnaire != null ? questionnaire.getPassScore() : null) + .build()) + .collect(Collectors.toList()); + + if (CollUtil.isNotEmpty(records)) { + questionnaireRecordMapper.insertBatch(records); + } + } + + private String getAreaName(Long areaId) { + if (areaId == null) { + return null; + } + AreaDO area = areaMapper.selectById(areaId); + return area != null ? area.getName() : null; + } + + private void updateTaskStatistics(Long taskId) { + questionnaireTaskMapper.updateTaskStatistics(taskId); + } + +} diff --git a/yudao-module-system/pom.xml b/yudao-module-system/pom.xml index 57e24b1979..8ac462462c 100644 --- a/yudao-module-system/pom.xml +++ b/yudao-module-system/pom.xml @@ -95,16 +95,15 @@ justauth-spring-boot-starter - com.github.binarywang - wx-java-mp-spring-boot-starter + wx-java-mp-spring-boot-starter com.github.binarywang - wx-java-miniapp-spring-boot-starter + wx-java-miniapp-spring-boot-starter - --> com.anji-plus -- 2.47.2 From ff09efa216b6a8fefe26c2400c742ca5259a9a23 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Mon, 26 Jan 2026 16:02:45 +0800 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20=E9=97=AE=E5=8D=B7=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E5=A2=9E=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增犯人完成进度查询接口 - 新增批量分配 Agent 填写功能 - 优化仪表盘统计逻辑 - 新增 Agent 填写权限 SQL --- .../vo/PrisonerDashboardStatsRespVO.java | 15 +++ .../PrisonQuestionnaireTaskController.java | 27 +++++ .../vo/PrisonerProgressRespVO.java | 55 +++++++++ .../PrisonQuestionnaireRecordController.java | 8 ++ .../dal/dataobject/answer/AnswerDO.java | 16 +++ .../QuestionnaireTaskMapper.java | 31 +++++ .../prison/service/answer/AnswerService.java | 7 +- .../service/answer/AnswerServiceImpl.java | 16 ++- .../impl/PrisonDashboardServiceImpl.java | 29 +++++ .../QuestionnaireTaskService.java | 22 +++- .../QuestionnaireTaskServiceImpl.java | 105 +++++++++++------ .../QuestionnaireRecordService.java | 7 ++ .../QuestionnaireRecordServiceImpl.java | 22 +++- .../sql/insert_agent_fill_permission.sql | 110 ++++++++++++++++++ .../sql/migrate/add_agent_fill_fields.sql | 35 ++++++ 15 files changed, 468 insertions(+), 37 deletions(-) create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java create mode 100644 yudao-module-prison/src/main/resources/sql/insert_agent_fill_permission.sql create mode 100644 yudao-module-prison/src/main/resources/sql/migrate/add_agent_fill_fields.sql diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java index 8b79d99439..be195286a2 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java @@ -69,6 +69,21 @@ public class PrisonerDashboardStatsRespVO { @Schema(description = "风险等级:1-低风险 2-中风险 3-高风险 4-极高风险", example = "1") private Integer riskLevel; + @Schema(description = "剩余刑期天数", example = "1000") + private Integer remainingDays; + + @Schema(description = "累计违规次数", example = "5") + private Integer violationCount; + + @Schema(description = "累计表扬天数", example = "-") + private String praiseDays; + + @Schema(description = "累计扣分次数", example = "3") + private Integer penaltyCount; + + @Schema(description = "累计加分次数", example = "8") + private Integer rewardCount; + @Schema(description = "中心左侧数据") private CenterLeftData centerLeftData; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java index 1bb7e02186..c90f425663 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java @@ -149,6 +149,15 @@ public class PrisonQuestionnaireTaskController { return success(questionnaireTaskService.getPendingPrisoners(id, pageReqVO)); } + @GetMapping("/prisoner-progress") + @Operation(summary = "获取任务的人员填写进度列表") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')") + public CommonResult> getPrisonerProgress( + @NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) { + return success(questionnaireTaskService.getPrisonerProgress(id)); + } + @PostMapping("/remind") @Operation(summary = "提醒未完成人员") @Parameter(name = "id", description = "任务ID", required = true) @@ -157,6 +166,24 @@ public class PrisonQuestionnaireTaskController { return success(questionnaireTaskService.remindPendingPrisoners(id)); } + @PostMapping("/notify-prisoner") + @Operation(summary = "通知单个人员") + @Parameter(name = "recordId", description = "记录ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:notify')") + public CommonResult notifyPrisoner(@NotNull(message = "记录ID不能为空") @RequestParam("recordId") Long recordId) { + questionnaireTaskService.notifyPrisoner(recordId); + return success(true); + } + + @PostMapping("/reset-record") + @Operation(summary = "重置人员答题记录") + @Parameter(name = "recordId", description = "记录ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:reset')") + public CommonResult resetPrisonerRecord(@NotNull(message = "记录ID不能为空") @RequestParam("recordId") Long recordId) { + questionnaireTaskService.resetPrisonerRecord(recordId); + return success(true); + } + // ==================== 统计相关 ==================== @GetMapping("/area-statistics") diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java new file mode 100644 index 0000000000..f2aea0018e --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 问卷任务人员填写进度 Response VO") +@Data +public class PrisonerProgressRespVO { + + @Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long prisonerId; + + @Schema(description = "罪犯编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "P001") + private String prisonerNo; + + @Schema(description = "罪犯姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + private String prisonerName; + + @Schema(description = "监区ID", example = "100") + private Long areaId; + + @Schema(description = "监区名称", example = "一监区") + private String areaName; + + @Schema(description = "填写状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "客观分", example = "80") + private Integer objectiveScore; + + @Schema(description = "主观分", example = "85") + private Integer subjectiveScore; + + @Schema(description = "总分", example = "165") + private Integer totalScore; + + @Schema(description = "风险等级", example = "2") + private Integer riskLevel; + + @Schema(description = "答题用时(秒)", example = "300") + private Integer duration; + + @Schema(description = "开始时间") + private LocalDateTime startTime; + + @Schema(description = "完成时间") + private LocalDateTime finishTime; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java index 7f8d777c8a..918bbe1c1f 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java @@ -129,6 +129,14 @@ public class PrisonQuestionnaireRecordController { return success(true); } + @PostMapping("/submit-by-agent") + @Operation(summary = "代为提交答卷(民警代填)") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:agent-fill')") + public CommonResult submitAnswerByAgent(@Valid @RequestBody AssessmentAnswerSubmitReqVO reqVO) { + questionnaireRecordService.submitAnswerByAgent(reqVO); + return success(true); + } + @PostMapping("/finish") @Operation(summary = "结束测评") @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:finish')") diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java index e720224824..8f9fff60c2 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java @@ -67,5 +67,21 @@ public class AnswerDO extends TenantBaseDO { * 答题时间(秒) */ private Integer duration; + /** + * 是否代填:true-民警代填 false-本人填写 null-未知 + */ + private Boolean isAgentFill; + /** + * 代填操作人ID(民警ID) + */ + private Long agentOperatorId; + /** + * 代填操作人姓名 + */ + private String agentOperatorName; + /** + * 代填时间 + */ + private LocalDateTime agentFillTime; } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java index c1f043fb1b..147a62c94c 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.QuestionnaireTaskPageReqVO; import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @@ -56,4 +57,34 @@ public interface QuestionnaireTaskMapper extends BaseMapperX selectTaskStatisticsSummary(); + @Select("SELECT qt.id as task_id, qt.task_name, " + + "SUM(qr.total_count) as total_count, " + + "SUM(qr.completed_count) as completed_count " + + "FROM prison_questionnaire_task qt " + + "INNER JOIN prison_questionnaire_record qr ON qt.id = qr.task_id " + + "WHERE qt.deleted = 0 " + + "AND qt.questionnaire_id = #{questionnaireId} " + + "AND qr.area_id IN " + + "" + + "#{id}" + + " " + + "GROUP BY qt.id, qt.task_name") + List> selectAreaComparisonByQuestionnaire( + @Param("questionnaireId") Long questionnaireId, + @Param("areaIds") List areaIds); + + @Select("SELECT qt.id as task_id, qt.task_name, " + + "SUM(qr.total_count) as total_count, " + + "SUM(qr.completed_count) as completed_count " + + "FROM prison_questionnaire_task qt " + + "INNER JOIN prison_questionnaire_record qr ON qt.id = qr.task_id " + + "WHERE qt.deleted = 0 " + + "AND qr.area_id IN " + + "" + + "#{id}" + + " " + + "GROUP BY qt.id, qt.task_name") + List> selectAreaComparisonByAreas( + @Param("areaIds") List areaIds); + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java index a5f3b80ac7..f92d08ca0d 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java @@ -76,8 +76,13 @@ public interface AnswerService { * @param questionnaireId 问卷ID * @param prisonerId 罪犯ID * @param answers 答题详情列表 + * @param isAgentFill 是否代填 + * @param agentOperatorId 代填操作人ID + * @param agentOperatorName 代填操作人姓名 */ - void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId, List answers); + void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId, + List answers, + Boolean isAgentFill, Long agentOperatorId, String agentOperatorName); /** * 计算客观题得分并更新 diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java index 40c16d427e..6114a22cf6 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java @@ -13,6 +13,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.LocalDateTime; import cn.iocoder.yudao.module.prison.controller.admin.answer.vo.*; import cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo.AssessmentAnswerSubmitReqVO; import cn.iocoder.yudao.module.prison.dal.dataobject.answer.AnswerDO; @@ -113,10 +114,13 @@ public class AnswerServiceImpl implements AnswerService { @Override @Transactional(rollbackFor = Exception.class) public void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId, - List answers) { + List answers, + Boolean isAgentFill, Long agentOperatorId, String agentOperatorName) { if (CollUtil.isEmpty(answers)) { return; } + // 删除该测评已有的旧答题记录 + deleteByAssessmentRecordId(assessmentRecordId); for (AssessmentAnswerSubmitReqVO.AnswerItem answerItem : answers) { // 获取问题信息 @@ -151,6 +155,16 @@ public class AnswerServiceImpl implements AnswerService { answer.setIsCorrect(isCorrect); } + // 设置代填信息 + if (Boolean.TRUE.equals(isAgentFill)) { + answer.setIsAgentFill(true); + answer.setAgentOperatorId(agentOperatorId); + answer.setAgentOperatorName(agentOperatorName); + answer.setAgentFillTime(LocalDateTime.now()); + } else { + answer.setIsAgentFill(false); + } + answerMapper.insert(answer); } } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java index 4a5bc349a1..bfcea53067 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java @@ -208,6 +208,11 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { } if (prisoner.getReleaseDate() != null) { vo.setReleaseDate(prisoner.getReleaseDate().toString()); + // 计算剩余刑期天数 + long remainingDays = ChronoUnit.DAYS.between(LocalDate.now(), prisoner.getReleaseDate()); + vo.setRemainingDays(remainingDays > 0 ? (int) remainingDays : 0); + } else { + vo.setRemainingDays(null); } // 监区信息(简单拼接) @@ -298,6 +303,30 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .orderByDesc(SituationDO::getOccurTime) .last("LIMIT 10")); + // 8. 查询累计违规次数(狱情收集-监管安全类型) + Long violationCount = situationMapper.selectCount(new LambdaQueryWrapper() + .eq(SituationDO::getPrisonerId, prisonerId) + .eq(SituationDO::getStatus, 3) // 已处理 + .eq(SituationDO::getCategory, 1)); // 监管安全类型 + vo.setViolationCount(violationCount != null ? violationCount.intValue() : 0); + + // 9. 查询累计计分考核记录(统计加分和扣分次数) + List allScores = scoreMapper.selectList(new LambdaQueryWrapper() + .eq(ScoreDO::getPrisonerId, prisonerId) + .eq(ScoreDO::getStatus, 1)); // 已通过 + + long rewardCount = allScores.stream() + .filter(s -> s.getRewardScore() != null && s.getRewardScore().doubleValue() > 0) + .count(); + long penaltyCount = allScores.stream() + .filter(s -> s.getPenaltyScore() != null && s.getPenaltyScore().doubleValue() > 0) + .count(); + vo.setRewardCount((int) rewardCount); + vo.setPenaltyCount((int) penaltyCount); + + // 10. 设置累计表扬天数(固定返回"-") + vo.setPraiseDays("-"); + // ==================== 构建中心数据 ==================== // 左侧数据:本月消费、奖励、惩罚、余额 diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java index 2f24cbe345..88012e8dfe 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskService.java @@ -112,6 +112,14 @@ public interface QuestionnaireTaskService { */ PageResult getPendingPrisoners(Long id, PageParam pageReqVO); + /** + * 获取任务的人员填写进度列表 + * + * @param id 任务ID + * @return 人员填写进度列表 + */ + List getPrisonerProgress(Long id); + /** * 提醒未完成人员 * @@ -120,7 +128,19 @@ public interface QuestionnaireTaskService { */ Integer remindPendingPrisoners(Long id); - // ==================== 统计相关 ==================== + /** + * 通知单个人员 + * + * @param recordId 记录ID + */ + void notifyPrisoner(Long recordId); + + /** + * 重置人员答题记录 + * + * @param recordId 记录ID + */ + void resetPrisonerRecord(Long recordId); /** * 按监区统计任务完成情况 diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java index cd5510eb57..9b06f59f32 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*; import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire.QuestionnaireDO; @@ -168,16 +169,12 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { @Override public PageResult getQuestionnaireTaskPage(QuestionnaireTaskPageReqVO pageReqVO) { - Page page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()); - LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + return questionnaireTaskMapper.selectPage(pageReqVO, new LambdaQueryWrapperX() .likeIfPresent(QuestionnaireTaskDO::getTaskName, pageReqVO.getTaskName()) .eqIfPresent(QuestionnaireTaskDO::getStatus, pageReqVO.getStatus()) .eqIfPresent(QuestionnaireTaskDO::getQuestionnaireId, pageReqVO.getQuestionnaireId()) .betweenIfPresent(QuestionnaireTaskDO::getCreateTime, pageReqVO.getCreateTime()) - .orderByDesc(QuestionnaireTaskDO::getCreateTime); - - Page result = questionnaireTaskMapper.selectPage(page, wrapper); - return new PageResult<>(result.getRecords(), result.getTotal()); + .orderByDesc(QuestionnaireTaskDO::getCreateTime)); } @Override @@ -329,11 +326,74 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { return pendingRecords.size(); } + @Override + public List getPrisonerProgress(Long id) { + List records = questionnaireRecordMapper.selectList( + new LambdaQueryWrapper() + .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id) + .orderByAsc(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getCreateTime) + ); + + if (CollUtil.isEmpty(records)) { + return Collections.emptyList(); + } + + return records.stream().map(record -> { + PrisonerProgressRespVO vo = new PrisonerProgressRespVO(); + vo.setId(record.getId()); + vo.setPrisonerId(record.getPrisonerId()); + vo.setPrisonerNo(record.getPrisonerNo()); + vo.setPrisonerName(record.getPrisonerName()); + vo.setAreaId(record.getPrisonerAreaId()); + vo.setAreaName(record.getPrisonerAreaName()); + vo.setStatus(record.getStatus()); + vo.setObjectiveScore(record.getObjectiveScore() != null ? record.getObjectiveScore().intValue() : null); + vo.setSubjectiveScore(record.getSubjectiveScore() != null ? record.getSubjectiveScore().intValue() : null); + vo.setTotalScore(record.getTotalScore() != null ? record.getTotalScore().intValue() : null); + vo.setRiskLevel(record.getRiskLevel()); + vo.setStartTime(record.getStartTime()); + vo.setFinishTime(record.getEndTime()); + vo.setDuration(record.getDuration()); + return vo; + }).collect(Collectors.toList()); + } + + @Override + public void notifyPrisoner(Long recordId) { + cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(recordId); + if (record == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_RECORD_NOT_EXISTS); + } + // TODO: 发送站内信通知,此处为占位实现 + log.info("通知任务 {} 中的罪犯 {} 完成问卷", record.getTaskId(), record.getPrisonerName()); + } + + @Override + public void resetPrisonerRecord(Long recordId) { + cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(recordId); + if (record == null) { + throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_RECORD_NOT_EXISTS); + } + // 重置记录状态为待测评 + cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO updateRecord = new cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO(); + updateRecord.setId(recordId); + updateRecord.setStatus(QuestionnaireRecordStatusEnum.PENDING.getCode()); + updateRecord.setObjectiveScore(null); + updateRecord.setSubjectiveScore(null); + updateRecord.setTotalScore(null); + updateRecord.setRiskLevel(null); + updateRecord.setStartTime(null); + updateRecord.setEndTime(null); + updateRecord.setDuration(null); + questionnaireRecordMapper.updateById(updateRecord); + log.info("重置任务 {} 中的罪犯 {} 答题记录", record.getTaskId(), record.getPrisonerName()); + } + @Override public List getTaskAreaStatistics(Long id) { List> stats = questionnaireRecordMapper.selectMaps( new QueryWrapper() - .select("area_id", "area_name", + .select("prisoner_area_id", "prisoner_area_name", "COUNT(*) as total_count", "SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as completed_count", "AVG(CASE WHEN status = 3 THEN total_score ELSE NULL END) as avg_score", @@ -342,7 +402,7 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { "SUM(CASE WHEN risk_level = 2 THEN 1 ELSE 0 END) as medium_risk_count", "SUM(CASE WHEN risk_level = 3 THEN 1 ELSE 0 END) as low_risk_count") .eq("task_id", id) - .groupBy("area_id", "area_name") + .groupBy("prisoner_area_id", "prisoner_area_name") .orderByDesc("completed_count") ); @@ -355,8 +415,8 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { new BigDecimal(stat.get("pass_rate").toString()) : BigDecimal.ZERO; return TaskAreaStatisticsRespVO.builder() - .areaId(stat.get("area_id") != null ? ((Number) stat.get("area_id")).longValue() : null) - .areaName((String) stat.get("area_name")) + .areaId(stat.get("prisoner_area_id") != null ? ((Number) stat.get("prisoner_area_id")).longValue() : null) + .areaName((String) stat.get("prisoner_area_name")) .totalCount(total) .completedCount(completed) .completionRate(total > 0 ? BigDecimal.valueOf(completed * 100.0 / total).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO) @@ -405,30 +465,9 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { List> stats; if (questionnaireId != null) { - stats = questionnaireTaskMapper.selectMaps( - new QueryWrapper() - .select("qt.id as task_id", "qt.task_name", - "SUM(qr.total_count) as total_count", - "SUM(qr.completed_count) as completed_count") - .from("prison_questionnaire_task qt") - .innerJoin("prison_questionnaire_record qr ON qt.id = qr.task_id") - .eq("qt.deleted", 0) - .eq("qt.questionnaire_id", questionnaireId) - .in("qr.area_id", areaIds) - .groupBy("qt.id", "qt.task_name") - ); + stats = questionnaireTaskMapper.selectAreaComparisonByQuestionnaire(questionnaireId, areaIds); } else { - stats = questionnaireTaskMapper.selectMaps( - new QueryWrapper() - .select("qt.id as task_id", "qt.task_name", - "SUM(qr.total_count) as total_count", - "SUM(qr.completed_count) as completed_count") - .from("prison_questionnaire_task qt") - .innerJoin("prison_questionnaire_record qr ON qt.id = qr.task_id") - .eq("qt.deleted", 0) - .in("qr.area_id", areaIds) - .groupBy("qt.id", "qt.task_name") - ); + stats = questionnaireTaskMapper.selectAreaComparisonByAreas(areaIds); } return stats.stream().map(stat -> AreaComparisonRespVO.builder() diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java index 9ce0b23f5e..f614cf748c 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java @@ -86,6 +86,13 @@ public interface QuestionnaireRecordService { */ void submitAnswer(@Valid AssessmentAnswerSubmitReqVO reqVO); + /** + * 代为提交答卷(民警代填) + * + * @param reqVO 提交信息,包含代填操作人信息 + */ + void submitAnswerByAgent(@Valid AssessmentAnswerSubmitReqVO reqVO); + /** * 结束测评 * diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java index 8013db34d3..22068f45d5 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java @@ -161,6 +161,25 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic @Override @Transactional(rollbackFor = Exception.class) public void submitAnswer(AssessmentAnswerSubmitReqVO reqVO) { + submitAnswerInternal(reqVO, false, null, null); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void submitAnswerByAgent(AssessmentAnswerSubmitReqVO reqVO) { + // 获取当前登录的民警信息 + Long currentUserId = SecurityFrameworkUtils.getLoginUserId(); + String currentUserName = SecurityFrameworkUtils.getLoginUser().getInfo() != null + ? SecurityFrameworkUtils.getLoginUser().getInfo().get("nickname") + : "未知"; + submitAnswerInternal(reqVO, true, currentUserId, currentUserName); + } + + /** + * 内部提交答卷方法 + */ + private void submitAnswerInternal(AssessmentAnswerSubmitReqVO reqVO, Boolean isAgentFill, + Long agentOperatorId, String agentOperatorName) { QuestionnaireRecordDO record = validateQuestionnaireRecordExists(reqVO.getRecordId()); if (!QuestionnaireRecordStatusEnum.IN_PROGRESS.getStatus().equals(record.getStatus())) { throw exception(QUESTIONNAIRE_RECORD_STATUS_ERROR); @@ -171,7 +190,8 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic record.setDuration((int) java.time.Duration.between(record.getStartTime(), LocalDateTime.now()).getSeconds()); } // 保存答题记录并计算客观题得分 - answerService.saveAnswers(reqVO.getRecordId(), record.getQuestionnaireId(), reqVO.getPrisonerId(), reqVO.getAnswers()); + answerService.saveAnswers(reqVO.getRecordId(), record.getQuestionnaireId(), reqVO.getPrisonerId(), + reqVO.getAnswers(), isAgentFill, agentOperatorId, agentOperatorName); // 计算客观题得分 BigDecimal objectiveScore = answerService.calculateObjectiveScore(reqVO.getRecordId()); record.setObjectiveScore(objectiveScore); diff --git a/yudao-module-prison/src/main/resources/sql/insert_agent_fill_permission.sql b/yudao-module-prison/src/main/resources/sql/insert_agent_fill_permission.sql new file mode 100644 index 0000000000..5d9d07a377 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/insert_agent_fill_permission.sql @@ -0,0 +1,110 @@ +-- ===================================================== +-- XL监狱综合管理平台 - 问卷代填权限SQL +-- 生成时间: 2026-01-26 +-- 功能: 添加问卷代填按钮权限 +-- ===================================================== + +-- ===================================================== +-- 方法1: 自动查找并插入(推荐) +-- ===================================================== + +-- 1. 查找问卷记录管理(assessment-record)的父菜单ID +SELECT @parentId := parent_id +FROM system_menu +WHERE name = '测评记录管理' AND type = 2 AND deleted = 0 +LIMIT 1; + +-- 如果找不到,尝试查找 questionnaire-record +SELECT @parentId := id +FROM system_menu +WHERE path = 'assessment-record' AND type = 2 AND deleted = 0 +LIMIT 1; + +-- 2. 插入代填权限 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '代为填写', 'prison:questionnaire-record:agent-fill', 3, 8, @parentId, '', '', '', 0 +WHERE @parentId IS NOT NULL; + +-- 3. 验证插入结果 +SELECT * FROM system_menu +WHERE permission = 'prison:questionnaire-record:agent-fill'; + +-- ===================================================== +-- 方法2: 手动指定父菜单ID插入 +-- ===================================================== +-- 请将 5047 替换为实际的测评记录管理父菜单ID + +-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +-- VALUES ('代为填写', 'prison:questionnaire-record:agent-fill', 3, 8, 5047, '', '', '', 0); + +-- ===================================================== +-- 方法3: 完整的查找和插入脚本(包含错误处理) +-- ===================================================== + +-- 设置临时变量 +SET @parentId := NULL; + +-- 查找问卷记录管理菜单的父ID +SELECT parent_id INTO @parentId +FROM system_menu +WHERE name = '测评记录管理' + AND type = 2 + AND deleted = 0 +ORDER BY id DESC +LIMIT 1; + +-- 如果没找到,尝试通过path查找 +IF @parentId IS NULL THEN + SELECT parent_id INTO @parentId + FROM system_menu + WHERE path = 'assessment-record' + AND type = 2 + AND deleted = 0 + ORDER BY id DESC + LIMIT 1; +END IF; + +-- 如果还是没找到,输出提示 +SELECT + CASE + WHEN @parentId IS NULL THEN '未找到测评记录管理菜单,请手动检查!' + ELSE CONCAT('找到父菜单ID: ', @parentId, ',即将插入代填权限...') + END AS result; + +-- 插入代填权限(使用 INSERT IGNORE 避免重复插入) +INSERT IGNORE INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) +VALUES ( + '代为填写', + 'prison:questionnaire-record:agent-fill', + 3, -- type=3 表示按钮 + 8, -- sort=8 排在最后(根据现有排序调整) + @parentId, -- 父菜单ID + '', -- path + '', -- icon + '', -- component + 0, -- status + b'1', -- visible + b'0', -- keep_alive + b'1', -- always_show + 'system', + NOW(), + '', + NOW(), + b'0' +); + +-- ===================================================== +-- 验证结果 +-- ===================================================== + +-- 查看所有 questionnaire-record 相关的权限 +SELECT id, name, permission, type, sort, parent_id +FROM system_menu +WHERE (permission LIKE 'prison:questionnaire-record:%' + OR permission LIKE 'prison:assessment-record:%') + AND deleted = 0 +ORDER BY parent_id, sort; + +-- 查看插入的代填权限 +SELECT * FROM system_menu +WHERE permission = 'prison:questionnaire-record:agent-fill'; diff --git a/yudao-module-prison/src/main/resources/sql/migrate/add_agent_fill_fields.sql b/yudao-module-prison/src/main/resources/sql/migrate/add_agent_fill_fields.sql new file mode 100644 index 0000000000..d6a527cd29 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/migrate/add_agent_fill_fields.sql @@ -0,0 +1,35 @@ +-- ================================================ +-- 代填功能数据库迁移脚本 +-- 添加 prison_answer 表的代填相关字段 +-- ================================================ + +-- 检查字段是否已存在,如果不存在则添加 +-- 注意:MySQL 语法 + +-- 1. 添加是否代填字段 +ALTER TABLE `prison_answer` +ADD COLUMN `is_agent_fill` TINYINT(1) NULL COMMENT '是否代填:1-是,0-否,null-未知' AFTER `duration`; + +-- 2. 添加代填操作人ID字段 +ALTER TABLE `prison_answer` +ADD COLUMN `agent_operator_id` BIGINT NULL COMMENT '代填操作人ID(民警ID)' AFTER `is_agent_fill`; + +-- 3. 添加代填操作人姓名字段 +ALTER TABLE `prison_answer` +ADD COLUMN `agent_operator_name` VARCHAR(100) NULL COMMENT '代填操作人姓名' AFTER `agent_operator_id`; + +-- 4. 添加代填时间字段 +ALTER TABLE `prison_answer` +ADD COLUMN `agent_fill_time` DATETIME NULL COMMENT '代填时间' AFTER `agent_operator_name`; + +-- ================================================ +-- 回滚脚本(如需回滚) +-- ================================================ +-- ALTER TABLE `prison_answer` +-- DROP COLUMN IF EXISTS `is_agent_fill`; +-- ALTER TABLE `prison_answer` +-- DROP COLUMN IF EXISTS `agent_operator_id`; +-- ALTER TABLE `prison_answer` +-- DROP COLUMN IF EXISTS `agent_operator_name`; +-- ALTER TABLE `prison_answer` +-- DROP COLUMN IF EXISTS `agent_fill_time`; -- 2.47.2 From cea9ed73352ef2df2322322bd5a41d79115e3878 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Mon, 26 Jan 2026 18:16:43 +0800 Subject: [PATCH 03/12] =?UTF-8?q?feat(dashboard):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=A4=A7=E5=B8=90=E7=BB=9F=E8=AE=A1=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 大帐统计改为显示账户余额,移除出入库卡片 - 柱状图展示收入和支出数据,按月份正序排列 - 奖惩记录从数据库真实查询,区分奖励和惩罚 - 修复惩罚记录显示问题(类型匹配) - 新增查询方法:selectRecentRewardsPunishments、selectLatestBalance - PrisonerDashboardStatsRespVO新增balance字段 - ConsumptionMapper新增selectLatestBalance方法 --- .../vo/PrisonerDashboardStatsRespVO.java | 3 + .../mysql/consumption/ConsumptionMapper.java | 14 ++ .../dashboard/PrisonDashboardMapper.java | 58 ++++++++ .../impl/PrisonDashboardServiceImpl.java | 133 +++++++++++++----- 4 files changed, 172 insertions(+), 36 deletions(-) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java index be195286a2..eb414d207d 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java @@ -93,6 +93,9 @@ public class PrisonerDashboardStatsRespVO { @Schema(description = "月度消费数据") private List consumptionMonthlyData; + @Schema(description = "账户余额") + private Integer balance; + @Schema(description = "消费汇总") private ConsumptionSummary consumptionSummary; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java index 56888c99c1..1553d1c2bf 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java @@ -123,4 +123,18 @@ public interface ConsumptionMapper extends BaseMapperX { @ResultMap("ConsumptionDetailResultMap") List selectConsumptionDetailPage(@Param("reqVO") ConsumptionPageReqVO reqVO); + /** + * 查询罪犯最新账户余额 + */ + @Select(""" + SELECT balance + FROM prison_consumption + WHERE deleted = 0 + AND status = 1 + AND prisoner_id = #{prisonerId} + ORDER BY id DESC + LIMIT 1 + """) + java.math.BigDecimal selectLatestBalance(@Param("prisonerId") Long prisonerId); + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java index 4f7411d564..c7dd277b7d 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java @@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import java.util.List; +import java.util.Map; /** * 监管看板 Mapper @@ -18,6 +19,63 @@ import java.util.List; @Mapper public interface PrisonDashboardMapper { + /** + * 查询最近6个月罪犯的汇款记录(收入) + */ + @Select(""" + SELECT + DATE_FORMAT(arrive_date, '%Y-%m') AS month, + SUM(amount) AS total_amount + FROM prison_remittance + WHERE deleted = 0 + AND status = 1 + AND prisoner_id = #{prisonerId} + AND arrive_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) + GROUP BY DATE_FORMAT(arrive_date, '%Y-%m') + ORDER BY month DESC + """) + List> selectRecentRemittances(@Param("prisonerId") Long prisonerId); + + /** + * 查询最近6个月罪犯的汇款详情(用于展示列表) + */ + @Select(""" + SELECT + arrive_date, + remitter_name, + amount, + relationship, + remark + FROM prison_remittance + WHERE deleted = 0 + AND status = 1 + AND prisoner_id = #{prisonerId} + AND arrive_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) + ORDER BY arrive_date DESC + LIMIT 10 + """) + List> selectRecentRemittanceDetails(@Param("prisonerId") Long prisonerId); + + /** + * 查询最近6个月罪犯的奖惩记录 + */ + @Select(""" + SELECT + id, + occur_date, + type, + category, + content + FROM prison_rewards_punishments + WHERE deleted = 0 + AND status = 1 + AND prisoner_id = #{prisonerId} + AND occur_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) + ORDER BY occur_date DESC + LIMIT 10 + """) + List> selectRecentRewardsPunishments(@Param("prisonerId") Long prisonerId); + /** * 查询核心指标卡片数据 * diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java index bfcea53067..09065d373a 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java @@ -20,7 +20,6 @@ import cn.iocoder.yudao.module.prison.dal.mysql.situation.SituationMapper; import cn.iocoder.yudao.module.prison.enums.GenderEnum; import cn.iocoder.yudao.module.prison.service.dashboard.PrisonDashboardService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.Cacheable; @@ -30,7 +29,6 @@ import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.Period; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; @@ -215,8 +213,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { vo.setRemainingDays(null); } - // 监区信息(简单拼接) - vo.setPrisonArea(prisoner.getAreaId() != null ? "监区" + prisoner.getAreaId() : "未分配"); + // 监区信息(查询实际监区名称) + if (prisoner.getAreaId() != null) { + AreaDO area = areaMapper.selectById(prisoner.getAreaId()); + vo.setPrisonArea(area != null ? area.getName() : "监区" + prisoner.getAreaId()); + } else { + vo.setPrisonArea("未分配"); + } // ==================== 查询关联数据 ==================== @@ -238,7 +241,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { List monthlyConsumptions = consumptionMapper.selectList(new LambdaQueryWrapper() .eq(ConsumptionDO::getPrisonerId, prisonerId) .eq(ConsumptionDO::getStatus, 1) - .eq(ConsumptionDO::getType, 1) // 消费类型 + // type: 1-购物 2-餐饮 3-医疗 4-通讯 5-其他,都属于消费 .apply("YEAR(trade_time) = {0} AND MONTH(trade_time) = {1}", currentYear, currentMonth) .orderByDesc(ConsumptionDO::getTradeTime)); @@ -288,6 +291,9 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .orderByDesc(ConsumptionDO::getTradeTime) .last("LIMIT 20")); + // 5.2 查询最近6个月汇款记录(收入) + List> recentRemittances = dashboardMapper.selectRecentRemittances(prisonerId); + // 6. 查询最近6个月的危险评估 List recentAssessments = riskAssessmentMapper.selectList(new LambdaQueryWrapper() .eq(RiskAssessmentDO::getPrisonerId, prisonerId) @@ -295,17 +301,17 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .orderByDesc(RiskAssessmentDO::getAssessmentDate) .last("LIMIT 6")); - // 7. 查询狱情收集(心理访谈记录),按监区查询 + // 8. 查询狱情收集(心理访谈记录),按监区查询 List situations = situationMapper.selectList(new LambdaQueryWrapper() .eq(SituationDO::getAreaId, prisoner.getAreaId()) .eq(SituationDO::getStatus, 3) // 已处理 - .eq(SituationDO::getCategory, 2) // 教育改造类型 + .eq(SituationDO::getCategory, 1) // 心理访谈类型 .orderByDesc(SituationDO::getOccurTime) .last("LIMIT 10")); // 8. 查询累计违规次数(狱情收集-监管安全类型) Long violationCount = situationMapper.selectCount(new LambdaQueryWrapper() - .eq(SituationDO::getPrisonerId, prisonerId) + .eq(SituationDO::getAreaId, prisoner.getAreaId()) .eq(SituationDO::getStatus, 3) // 已处理 .eq(SituationDO::getCategory, 1)); // 监管安全类型 vo.setViolationCount(violationCount != null ? violationCount.intValue() : 0); @@ -334,7 +340,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { double monthlyPenalty = 0; if (currentMonthScore != null) { monthlyReward = currentMonthScore.getRewardScore() != null ? currentMonthScore.getRewardScore().doubleValue() : 0; - monthlyPenalty = currentMonthScore.getPenaltyScore() != null ? currentMonthScore.getPenaltyScore().doubleValue() : 0; + monthlyPenalty = currentMonthScore.getPenaltyScore() != null ? Math.abs(currentMonthScore.getPenaltyScore().doubleValue()) : 0; } vo.setCenterLeftData(PrisonerDashboardStatsRespVO.CenterLeftData.builder() .topValue(String.valueOf((int) monthlyTotal)) @@ -368,7 +374,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .middleLeftLabel("基础分") .middleRightValue(String.valueOf((int) monthlyReward)) .middleRightLabel("加分项") - .bottomLeftValue(String.valueOf((int) monthlyPenalty)) + .bottomLeftValue(String.valueOf((int) Math.abs(monthlyPenalty))) .bottomLeftLabel("扣分项") .bottomRightValue(levelText) .bottomRightLabel("考核等级") @@ -390,23 +396,59 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { // ==================== 构建消费月度数据 ==================== List monthlyDataList = new ArrayList<>(); - // 按月份汇总消费 + // 按月份汇总消费(支出)和汇款(收入) Map monthlyConsumptionMap = recentConsumptions.stream() .filter(c -> c.getTradeTime() != null) .collect(Collectors.groupingBy( c -> c.getTradeTime().getYear() + "-" + String.format("%02d", c.getTradeTime().getMonthValue()), Collectors.summingDouble(c -> c.getTotalAmount() != null ? c.getTotalAmount().doubleValue() : 0) )); + + // 解析汇款数据(收入) + Map monthlyIncomeMap = new java.util.HashMap<>(); + for (Map remittance : recentRemittances) { + Object monthObj = remittance.get("month"); + Object amountObj = remittance.get("total_amount"); + if (monthObj != null && amountObj != null) { + String month = monthObj.toString(); + double amount = Double.parseDouble(amountObj.toString()); + monthlyIncomeMap.merge(month, amount, Double::sum); + } + } + + // 构建月度数据:monthlyStandard=支出,perCapita=收入 for (Map.Entry entry : monthlyConsumptionMap.entrySet()) { - Double consumption = entry.getValue(); + String month = entry.getKey(); + double consumption = entry.getValue(); + double income = monthlyIncomeMap.getOrDefault(month, 0.0); monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder() - .category(entry.getKey()) - .monthlyStandard(consumption.intValue()) // 支出 = 该月实际消费总额 - .perCapita(consumption.intValue()) // 人均消费 = 该月实际消费总额 + .category(month) + .monthlyStandard((int) consumption) // 支出 + .perCapita((int) income) // 收入 .build()); } + + // 如果某月只有收入没有消费,也添加进去 + for (Map.Entry entry : monthlyIncomeMap.entrySet()) { + String month = entry.getKey(); + if (!monthlyConsumptionMap.containsKey(month)) { + monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder() + .category(month) + .monthlyStandard(0) // 支出 + .perCapita(entry.getValue().intValue()) // 收入 + .build()); + } + } + + // 按月份排序(从左到右:时间正序 1月→2月→3月→...) + monthlyDataList.sort((a, b) -> a.getCategory().compareTo(b.getCategory())); + vo.setConsumptionMonthlyData(monthlyDataList); + // ==================== 查询账户余额 ==================== + java.math.BigDecimal latestBalance = consumptionMapper.selectLatestBalance(prisonerId); + vo.setBalance(latestBalance != null ? latestBalance.intValue() : 0); + // ==================== 构建计分考核记录 ==================== List scoreRecords = recentScores.stream() .map(s -> { @@ -433,11 +475,24 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { List consumptionRecords = recentConsumptions.stream() .limit(10) .map(c -> { - String typeName = c.getType() == 1 ? "消费" : "存款"; + // type: 1-购物 2-餐饮 3-医疗 4-通讯 5-其他 + String typeName = "消费"; + String nameColor = "#f56c6c"; // 红色-消费 + if (c.getType() == null) { + typeName = "未知"; + } else if (c.getType() == 2) { + typeName = "餐饮"; + } else if (c.getType() == 3) { + typeName = "医疗"; + } else if (c.getType() == 4) { + typeName = "通讯"; + } else if (c.getType() == 5) { + typeName = "其他"; + } return PrisonerDashboardStatsRespVO.ConsumptionRecord.builder() .date(c.getTradeTime() != null ? c.getTradeTime().toLocalDate().toString() : "") .name(typeName) - .nameColor(c.getType() == 1 ? "#f56c6c" : "#67c23a") + .nameColor(nameColor) .category("普通消费") .amount(c.getTotalAmount() != null ? c.getTotalAmount().intValue() : 0) .build(); @@ -445,16 +500,17 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .collect(Collectors.toList()); vo.setConsumptionRecords(consumptionRecords); - // ==================== 构建危险评估记录 ==================== - List rewardsPunishments = recentAssessments.stream() - .map(a -> { + // ==================== 构建奖惩记录 ==================== + List> rewardsPunishmentsList = dashboardMapper.selectRecentRewardsPunishments(prisonerId); + List rewardsPunishments = rewardsPunishmentsList.stream() + .map(rp -> { + Object typeObj = rp.get("type"); + int type = typeObj != null ? Integer.parseInt(typeObj.toString()) : 1; return PrisonerDashboardStatsRespVO.RewardsPunishment.builder() - .date(a.getAssessmentDate() != null ? a.getAssessmentDate().toString() : "") - .type("danger") - .typeText("危险评估") - .content("暴力倾向:" + (a.getViolenceScore() != null ? a.getViolenceScore() : 0) + - " | 脱逃倾向:" + (a.getEscapeScore() != null ? a.getEscapeScore() : 0) + - " | 自杀倾向:" + (a.getSuicideScore() != null ? a.getSuicideScore() : 0)) + .date(rp.get("occur_date") != null ? rp.get("occur_date").toString().substring(0, 10) : "") + .type(type == 1 ? "reward" : "punishment") + .typeText(rp.get("category") != null ? rp.get("category").toString() : (type == 1 ? "表扬奖励" : "惩罚")) + .content(rp.get("content") != null ? rp.get("content").toString() : "") .build(); }) .collect(Collectors.toList()); @@ -471,16 +527,21 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { vo.setInterviewRecords(interviewRecords); // ==================== 构建汇款记录 ==================== - List remittanceRecords = recentConsumptions.stream() - .filter(c -> c.getType() == 2) // 存款类型 - .limit(10) - .map(c -> PrisonerDashboardStatsRespVO.RemittanceRecord.builder() - .date(c.getTradeTime() != null ? c.getTradeTime().toLocalDate().toString() : "") - .name("存款") - .nameColor("#409eff") - .category("亲属汇款") - .amount(c.getTotalAmount() != null ? c.getTotalAmount().intValue() : 0) - .build()) + // 查询最近6个月的汇款记录 + List> remittanceList = dashboardMapper.selectRecentRemittances(prisonerId); + List remittanceRecords = remittanceList.stream() + .map(r -> { + Object dateObj = r.get("arrive_date"); + Object nameObj = r.get("remitter_name"); + Object amountObj = r.get("amount"); + return PrisonerDashboardStatsRespVO.RemittanceRecord.builder() + .date(dateObj != null ? dateObj.toString() : "") + .name(nameObj != null ? nameObj.toString() : "") + .nameColor("#67c23a") + .category("汇款") + .amount(amountObj != null ? Integer.parseInt(amountObj.toString()) : 0) + .build(); + }) .collect(Collectors.toList()); vo.setRemittanceRecords(remittanceRecords); -- 2.47.2 From 653e55a0754faf95014dd15975088d6c751d5778 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Tue, 27 Jan 2026 10:27:52 +0800 Subject: [PATCH 04/12] =?UTF-8?q?feat(prison):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=B4=AF=E8=AE=A1=E8=A1=A8=E6=89=AC=E6=AC=A1=E6=95=B0=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端新增 praiseCount 字段(Integer类型) - 从 prison_situation 表统计 category=2(表扬类型)的记录数 - 遵循 violationCount 的实现模式 参考计划: .sisyphus/plans/dashboard-center-update.md Task 1 --- .../vo/PrisonerDashboardStatsRespVO.java | 3 + .../impl/PrisonDashboardServiceImpl.java | 59 ++++++++++++++----- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java index eb414d207d..576d8b0832 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java @@ -78,6 +78,9 @@ public class PrisonerDashboardStatsRespVO { @Schema(description = "累计表扬天数", example = "-") private String praiseDays; + @Schema(description = "累计表扬次数", example = "8") + private Integer praiseCount; + @Schema(description = "累计扣分次数", example = "3") private Integer penaltyCount; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java index 09065d373a..a0f43eb6b1 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java @@ -251,9 +251,9 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .sum(); // 获取最新余额 - Double latestBalance = null; + java.math.BigDecimal latestBalance = null; if (!monthlyConsumptions.isEmpty() && monthlyConsumptions.get(0).getBalance() != null) { - latestBalance = monthlyConsumptions.get(0).getBalance().doubleValue(); + latestBalance = java.math.BigDecimal.valueOf(monthlyConsumptions.get(0).getBalance().doubleValue()); } if (latestBalance == null) { ConsumptionDO latestConsumption = consumptionMapper.selectOne(new LambdaQueryWrapper() @@ -262,7 +262,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .orderByDesc(ConsumptionDO::getTradeTime) .last("LIMIT 1")); if (latestConsumption != null && latestConsumption.getBalance() != null) { - latestBalance = latestConsumption.getBalance().doubleValue(); + latestBalance = java.math.BigDecimal.valueOf(latestConsumption.getBalance().doubleValue()); } } @@ -316,6 +316,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .eq(SituationDO::getCategory, 1)); // 监管安全类型 vo.setViolationCount(violationCount != null ? violationCount.intValue() : 0); + // 8.1 查询累计表扬次数(狱情收集-表扬类型) + Long praiseCount = situationMapper.selectCount(new LambdaQueryWrapper() + .eq(SituationDO::getAreaId, prisoner.getAreaId()) + .eq(SituationDO::getStatus, 3) // 已处理 + .eq(SituationDO::getCategory, 2)); // 表扬类型 + vo.setPraiseCount(praiseCount != null ? praiseCount.intValue() : 0); + // 9. 查询累计计分考核记录(统计加分和扣分次数) List allScores = scoreMapper.selectList(new LambdaQueryWrapper() .eq(ScoreDO::getPrisonerId, prisonerId) @@ -446,7 +453,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { vo.setConsumptionMonthlyData(monthlyDataList); // ==================== 查询账户余额 ==================== - java.math.BigDecimal latestBalance = consumptionMapper.selectLatestBalance(prisonerId); + latestBalance = consumptionMapper.selectLatestBalance(prisonerId); vo.setBalance(latestBalance != null ? latestBalance.intValue() : 0); // ==================== 构建计分考核记录 ==================== @@ -500,17 +507,41 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .collect(Collectors.toList()); vo.setConsumptionRecords(consumptionRecords); - // ==================== 构建奖惩记录 ==================== - List> rewardsPunishmentsList = dashboardMapper.selectRecentRewardsPunishments(prisonerId); - List rewardsPunishments = rewardsPunishmentsList.stream() - .map(rp -> { - Object typeObj = rp.get("type"); - int type = typeObj != null ? Integer.parseInt(typeObj.toString()) : 1; + // ==================== 构建风险评估记录 ==================== + List> riskList = dashboardMapper.selectRecentRiskAssessments(prisonerId); + List rewardsPunishments = riskList.stream() + .map(r -> { + Object dateObj = r.get("assessment_date"); + Object scoreObj = r.get("overall_score"); + Object levelObj = r.get("risk_level"); + Object violenceObj = r.get("violence_risk"); + Object escapeObj = r.get("escape_risk"); + Object selfHarmObj = r.get("self_harm_risk"); + Object mentalObj = r.get("mental_state"); + Object assessorObj = r.get("assessor_name"); + + String date = dateObj != null ? dateObj.toString().substring(0, 10) : ""; + String localLevelText = "低风险"; + if (levelObj != null) { + int level = Integer.parseInt(levelObj.toString()); + localLevelText = switch (level) { + case 1 -> "低风险"; + case 2 -> "中风险"; + case 3 -> "高风险"; + case 4 -> "极高风险"; + default -> "未知"; + }; + } + + String content = String.format("综合得分: %s | 风险等级: %s", + scoreObj != null ? scoreObj.toString() : "0", + localLevelText); + return PrisonerDashboardStatsRespVO.RewardsPunishment.builder() - .date(rp.get("occur_date") != null ? rp.get("occur_date").toString().substring(0, 10) : "") - .type(type == 1 ? "reward" : "punishment") - .typeText(rp.get("category") != null ? rp.get("category").toString() : (type == 1 ? "表扬奖励" : "惩罚")) - .content(rp.get("content") != null ? rp.get("content").toString() : "") + .date(date) + .type("danger") + .typeText("风险评估") + .content(content) .build(); }) .collect(Collectors.toList()); -- 2.47.2 From 182b7a7b43722b961b4bcb2e97a0c5861a009e37 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Tue, 27 Jan 2026 12:11:42 +0800 Subject: [PATCH 05/12] =?UTF-8?q?fix(prison):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=88=91=E6=9C=9F=E6=80=BB=E5=A4=A9=E6=95=B0=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 ChronoUnit.DAYS.between 计算入监日期到释放日期的总天数 - 修复后:2024-01-15 到 2027-07-14 = 1276天(原错误计算为743天) - 确保与其他日期计算逻辑一致 --- .../vo/PrisonerDashboardStatsRespVO.java | 3 +++ .../impl/PrisonDashboardServiceImpl.java | 21 ++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java index 576d8b0832..19abc50c26 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/dashboard/vo/PrisonerDashboardStatsRespVO.java @@ -72,6 +72,9 @@ public class PrisonerDashboardStatsRespVO { @Schema(description = "剩余刑期天数", example = "1000") private Integer remainingDays; + @Schema(description = "刑期总天数", example = "1276") + private Integer sentenceDays; + @Schema(description = "累计违规次数", example = "5") private Integer violationCount; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java index a0f43eb6b1..135f7befb3 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java @@ -200,17 +200,28 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { } // 入狱和出狱时间 - if (prisoner.getImprisonmentDate() != null) { + if (prisoner.getImprisonmentDate() != null && prisoner.getReleaseDate() != null) { vo.setImprisonmentDate(prisoner.getImprisonmentDate().toString()); - vo.setServedDays((int) ChronoUnit.DAYS.between(prisoner.getImprisonmentDate(), LocalDate.now())); - } - if (prisoner.getReleaseDate() != null) { vo.setReleaseDate(prisoner.getReleaseDate().toString()); - // 计算剩余刑期天数 + // 计算刑期总天数(从入监日期到释放日期) + long sentenceDays = ChronoUnit.DAYS.between(prisoner.getImprisonmentDate(), prisoner.getReleaseDate()); + vo.setSentenceDays((int) sentenceDays); + // 计算已服刑天数(从入监日期到当前日期) + vo.setServedDays((int) ChronoUnit.DAYS.between(prisoner.getImprisonmentDate(), LocalDate.now())); + // 计算剩余刑期天数(从当前日期到释放日期) long remainingDays = ChronoUnit.DAYS.between(LocalDate.now(), prisoner.getReleaseDate()); vo.setRemainingDays(remainingDays > 0 ? (int) remainingDays : 0); + } else if (prisoner.getImprisonmentDate() != null) { + vo.setImprisonmentDate(prisoner.getImprisonmentDate().toString()); + vo.setServedDays((int) ChronoUnit.DAYS.between(prisoner.getImprisonmentDate(), LocalDate.now())); + if (prisoner.getReleaseDate() != null) { + vo.setReleaseDate(prisoner.getReleaseDate().toString()); + long remainingDays = ChronoUnit.DAYS.between(LocalDate.now(), prisoner.getReleaseDate()); + vo.setRemainingDays(remainingDays > 0 ? (int) remainingDays : 0); + } } else { vo.setRemainingDays(null); + vo.setSentenceDays(null); } // 监区信息(查询实际监区名称) -- 2.47.2 From 984b95b6ed19c05eace112a601abff245043b557 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Tue, 27 Jan 2026 14:55:56 +0800 Subject: [PATCH 06/12] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=BD=AA?= =?UTF-8?q?=E7=8A=AF=E5=B7=A5=E4=BD=9C=E5=8F=B0=E6=B1=87=E6=AC=BE=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复调用错误方法导致的汇款记录为空问题 - 修复金额解析异常(BigDecimal处理小数) - 修改selectRecentRemittanceDetails替代selectRecentRemittances - 优化selectRecentRiskAssessments方法注释 --- .../dashboard/PrisonDashboardMapper.java | 26 ++++++++++++------- .../impl/PrisonDashboardServiceImpl.java | 6 ++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java index c7dd277b7d..10a0c36772 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/dashboard/PrisonDashboardMapper.java @@ -57,24 +57,30 @@ public interface PrisonDashboardMapper { List> selectRecentRemittanceDetails(@Param("prisonerId") Long prisonerId); /** - * 查询最近6个月罪犯的奖惩记录 + * 查询最近6个月罪犯的风险评估记录 */ @Select(""" SELECT id, - occur_date, - type, - category, - content - FROM prison_rewards_punishments + assessment_date, + assessment_type, + overall_score, + risk_level, + violence_risk, + escape_risk, + self_harm_risk, + mental_state, + recommendation, + assessor_name + FROM prison_risk WHERE deleted = 0 - AND status = 1 + AND status = 3 AND prisoner_id = #{prisonerId} - AND occur_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) - ORDER BY occur_date DESC + AND assessment_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) + ORDER BY assessment_date DESC LIMIT 10 """) - List> selectRecentRewardsPunishments(@Param("prisonerId") Long prisonerId); + List> selectRecentRiskAssessments(@Param("prisonerId") Long prisonerId); /** * 查询核心指标卡片数据 diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java index 135f7befb3..369e2753ce 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java @@ -569,8 +569,8 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { vo.setInterviewRecords(interviewRecords); // ==================== 构建汇款记录 ==================== - // 查询最近6个月的汇款记录 - List> remittanceList = dashboardMapper.selectRecentRemittances(prisonerId); + // 查询最近6个月的汇款记录(详情) + List> remittanceList = dashboardMapper.selectRecentRemittanceDetails(prisonerId); List remittanceRecords = remittanceList.stream() .map(r -> { Object dateObj = r.get("arrive_date"); @@ -581,7 +581,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .name(nameObj != null ? nameObj.toString() : "") .nameColor("#67c23a") .category("汇款") - .amount(amountObj != null ? Integer.parseInt(amountObj.toString()) : 0) + .amount(amountObj != null ? new java.math.BigDecimal(amountObj.toString()).intValue() : 0) .build(); }) .collect(Collectors.toList()); -- 2.47.2 From 25aa6144c4d78652aa4cc5a19fb6593aca9e6084 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Wed, 28 Jan 2026 11:50:34 +0800 Subject: [PATCH 07/12] =?UTF-8?q?feat:=20=E6=89=A9=E5=B1=95=E9=97=AE?= =?UTF-8?q?=E5=8D=B7=E9=97=AE=E9=A2=98=E7=AE=A1=E7=90=86=E6=9D=83=E9=99=90?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E9=97=AE=E5=8D=B7=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=91=98=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端:PrisonQuestionController 接口增加 prison:questionnaire:update 和 prison:questionnaire:query 权限判断 - 前端:QuestionList.vue 按钮增加 prison:questionnaire:update 权限判断 - 允许拥有问卷模板管理权限的用户也能管理问卷问题 --- .../admin/question/PrisonQuestionController.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java index 6b110ba196..3c74fe4ecc 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java @@ -40,7 +40,7 @@ public class PrisonQuestionController { @PostMapping("/create") @Operation(summary = "创建问卷问题") - @PreAuthorize("@ss.hasPermission('prison:question:create')") + @PreAuthorize("@ss.hasPermission('prison:question:create') or @ss.hasPermission('prison:questionnaire:update')") @ApiAccessLog(operateType = CREATE) public CommonResult createQuestion(@Valid @RequestBody QuestionSaveReqVO createReqVO) { return success(questionService.createQuestion(createReqVO)); @@ -48,7 +48,7 @@ public class PrisonQuestionController { @PutMapping("/update") @Operation(summary = "更新问卷问题") - @PreAuthorize("@ss.hasPermission('prison:question:update')") + @PreAuthorize("@ss.hasPermission('prison:question:update') or @ss.hasPermission('prison:questionnaire:update')") @ApiAccessLog(operateType = UPDATE) public CommonResult updateQuestion(@Valid @RequestBody QuestionSaveReqVO updateReqVO) { questionService.updateQuestion(updateReqVO); @@ -58,7 +58,7 @@ public class PrisonQuestionController { @DeleteMapping("/delete") @Operation(summary = "删除问卷问题") @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('prison:question:delete')") + @PreAuthorize("@ss.hasPermission('prison:question:delete') or @ss.hasPermission('prison:questionnaire:update')") @ApiAccessLog(operateType = DELETE) public CommonResult deleteQuestion(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { questionService.deleteQuestion(id); @@ -68,7 +68,7 @@ public class PrisonQuestionController { @DeleteMapping("/delete-list") @Parameter(name = "ids", description = "编号", required = true) @Operation(summary = "批量删除问卷问题") - @PreAuthorize("@ss.hasPermission('prison:question:delete')") + @PreAuthorize("@ss.hasPermission('prison:question:delete') or @ss.hasPermission('prison:questionnaire:update')") @ApiAccessLog(operateType = DELETE) public CommonResult deleteQuestionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { questionService.deleteQuestionListByIds(ids); @@ -78,7 +78,7 @@ public class PrisonQuestionController { @GetMapping("/get") @Operation(summary = "获得问卷问题") @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('prison:question:query')") + @PreAuthorize("@ss.hasPermission('prison:question:query') or @ss.hasPermission('prison:questionnaire:query') or @ss.hasPermission('prison:questionnaire:update')") public CommonResult getQuestion(@RequestParam("id") Long id) { QuestionDO question = questionService.getQuestion(id); return success(QuestionConvert.INSTANCE.convert(question)); @@ -86,7 +86,7 @@ public class PrisonQuestionController { @GetMapping("/page") @Operation(summary = "获得问卷问题分页") - @PreAuthorize("@ss.hasPermission('prison:question:query')") + @PreAuthorize("@ss.hasPermission('prison:question:query') or @ss.hasPermission('prison:questionnaire:query') or @ss.hasPermission('prison:questionnaire:update')") public CommonResult> getQuestionPage(@Valid QuestionPageReqVO pageReqVO) { PageResult pageResult = questionService.getQuestionPage(pageReqVO); return success(QuestionConvert.INSTANCE.convertPage(pageResult)); @@ -94,7 +94,7 @@ public class PrisonQuestionController { @PostMapping("/batch-update") @Operation(summary = "批量更新问卷问题") - @PreAuthorize("@ss.hasPermission('prison:question:update')") + @PreAuthorize("@ss.hasPermission('prison:question:update') or @ss.hasPermission('prison:questionnaire:update')") @ApiAccessLog(operateType = UPDATE) public CommonResult batchUpdateQuestion(@Valid @RequestBody QuestionBatchUpdateReqVO reqVO) { // 转换为 Service 需要的格式 @@ -113,7 +113,7 @@ public class PrisonQuestionController { @GetMapping("/export-excel") @Operation(summary = "导出问卷问题 Excel") - @PreAuthorize("@ss.hasPermission('prison:question:export')") + @PreAuthorize("@ss.hasPermission('prison:question:export') or @ss.hasPermission('prison:questionnaire:export') or @ss.hasPermission('prison:questionnaire:update')") @ApiAccessLog(operateType = EXPORT) public void exportQuestionExcel(@Valid QuestionPageReqVO pageReqVO, HttpServletResponse response) throws IOException { -- 2.47.2 From 483b95633bb73e89fb7054982479067a948be895 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Wed, 4 Feb 2026 18:30:18 +0800 Subject: [PATCH 08/12] =?UTF-8?q?fix(questionnaire):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=97=AE=E5=8D=B7=E6=A8=A1=E5=9D=97=E5=90=8E=E7=AB=AF=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 AnswerServiceImpl 评分逻辑:使用数组索引替代 option.id - 修复 AnswerServiceImpl 正确性判断:使用 score > 0 替代 isCorrect - 优化选项匹配逻辑,适配前端 optionIds 数据格式 Refs #questionnaire-fixes --- .../TimestampLocalDateTimeDeserializer.java | 50 +- .../core/handler/GlobalExceptionHandler.java | 21 +- .../admin/answer/PrisonAnswerController.java | 2 +- .../EvaluationReportController.java | 16 + .../vo/PrisonerProgressRespVO.java | 6 + .../vo/RiskAssessmentSaveReqVO.java | 5 + .../admin/situation/vo/SituationRespVO.java | 4 + .../situation/vo/SituationSaveReqVO.java | 3 + .../admin/warning/WarningController.java | 9 +- .../admin/warning/vo/WarningPageReqVO.java | 3 + .../admin/warning/vo/WarningRespVO.java | 9 + .../admin/warning/vo/WarningSaveReqVO.java | 6 + .../dal/mysql/question/QuestionMapper.java | 3 +- .../QuestionnaireTaskMapper.java | 12 +- .../dal/mysql/warning/WarningMapper.java | 90 +++ .../service/answer/AnswerServiceImpl.java | 62 +- .../consumption/ConsumptionServiceImpl.java | 14 +- .../EvaluationReportService.java | 8 + .../EvaluationReportServiceImpl.java | 236 +++++++ .../service/impl/PrisonerServiceImpl.java | 30 +- .../QuestionnaireTaskServiceImpl.java | 39 +- .../situation/impl/SituationServiceImpl.java | 10 +- .../service/warning/WarningService.java | 2 +- .../warning/impl/WarningServiceImpl.java | 18 +- .../main/resources/mapper/WarningMapper.xml | 20 + .../sql/migrate_questionnaire_record.sql | 70 ++ .../src/main/resources/sql/prison_module.sql | 25 +- .../main/resources/sql/prison_module.sql.bak | 609 ++++++++++++++++++ ...pgrade_risk_assessment_fields_20260130.sql | 56 ++ ...estionnaireTaskServiceImplRestartTest.java | 95 +++ yudao-module-temp | 1 - yudao-server/pom.xml | 10 +- .../src/main/resources/application-local.yaml | 6 +- 33 files changed, 1471 insertions(+), 79 deletions(-) create mode 100644 yudao-module-prison/src/main/resources/mapper/WarningMapper.xml create mode 100644 yudao-module-prison/src/main/resources/sql/migrate_questionnaire_record.sql create mode 100644 yudao-module-prison/src/main/resources/sql/prison_module.sql.bak create mode 100644 yudao-module-prison/src/main/resources/sql/upgrade_risk_assessment_fields_20260130.sql create mode 100644 yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImplRestartTest.java delete mode 160000 yudao-module-temp diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java index 5bf5d6c633..e45744da41 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java @@ -8,20 +8,66 @@ import java.io.IOException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; /** * 基于时间戳的 LocalDateTime 反序列化器 + * 支持 Long 时间戳、ISO 8601 格式(带/不带时区)、普通日期时间格式 + * 默认时区:Asia/Shanghai(上海) * * @author 老五 */ public class TimestampLocalDateTimeDeserializer extends JsonDeserializer { + /** 默认时区:上海 */ + private static final ZoneId DEFAULT_ZONE = ZoneId.of("Asia/Shanghai"); + + private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_DATE_TIME; + private static final DateTimeFormatter NORMAL_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter NORMAL_FORMATTER_WITH_ZONE = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DEFAULT_ZONE); + public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer(); @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - // 将 Long 时间戳,转换为 LocalDateTime 对象 - return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); + String value = p.getValueAsString(); + if (value == null || value.isEmpty()) { + return null; + } + value = value.trim(); + + // 1. 尝试解析 Long 时间戳(毫秒) + try { + long millis = Long.parseLong(value); + return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), DEFAULT_ZONE); + } catch (NumberFormatException e) { + // 不是时间戳,继续尝试其他格式 + } + + // 2. 尝试解析 ISO 8601 格式(可能带时区,如 2026-01-31T16:00:00+08:00 或 2026-01-31T16:00:00.000Z) + try { + return LocalDateTime.parse(value, ISO_FORMATTER); + } catch (Exception e) { + // 不是 ISO 格式,继续尝试其他格式 + } + + // 3. 尝试解析普通日期时间格式 (yyyy-MM-dd HH:mm:ss) + // 明确使用 Asia/Shanghai 时区,将字符串当作上海时区的时间来解析 + try { + return ZonedDateTime.parse(value + "+08:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")) + .withZoneSameInstant(DEFAULT_ZONE) + .toLocalDateTime(); + } catch (Exception e) { + // 解析失败,尝试使用 NORMAL_FORMATTER + } + + // 4. 兜底:尝试普通格式化器 + try { + return LocalDateTime.parse(value, NORMAL_FORMATTER); + } catch (Exception e) { + throw new IOException("无法解析日期时间格式: " + value); + } } } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index b99925e651..279fadfa12 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -73,9 +73,14 @@ public class GlobalExceptionHandler { * * @param request 请求 * @param ex 异常 - * @return 通用返回 + * @return 通用返回,如果是 SSE 请求则返回 null */ public CommonResult allExceptionHandler(HttpServletRequest request, Throwable ex) { + // 检测是否是 SSE 请求 + if (isSseRequest(request)) { + log.warn("[allExceptionHandler][SSE 请求异常,不返回 CommonResult: {}]", ex.getMessage()); + return null; + } if (ex instanceof MissingServletRequestParameterException) { return missingServletRequestParameterExceptionHandler((MissingServletRequestParameterException) ex); } @@ -319,6 +324,12 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(value = Exception.class) public CommonResult defaultExceptionHandler(HttpServletRequest req, Throwable ex) { + // SSE 请求不返回 CommonResult,避免二次异常 + if (isSseRequest(req)) { + log.warn("[defaultExceptionHandler][SSE 请求异常,不返回 CommonResult: {}]", ex.getMessage()); + return null; + } + // 特殊:如果是 ServiceException 的异常,则直接返回 // 例如说:https://gitee.com/zhijiantianya/yudao-cloud/issues/ICSSRM、https://gitee.com/zhijiantianya/yudao-cloud/issues/ICT6FM if (ex.getCause() != null && ex.getCause() instanceof ServiceException) { @@ -450,4 +461,12 @@ public class GlobalExceptionHandler { return null; } + /** + * 判断是否是 SSE 请求 + */ + private boolean isSseRequest(HttpServletRequest request) { + String accept = request.getHeader("Accept"); + return accept != null && accept.contains("text/event-stream"); + } + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java index af31efddca..f724c9a692 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java @@ -89,7 +89,7 @@ public class PrisonAnswerController { @GetMapping("/list-by-assessment-record") @Operation(summary = "根据测评记录ID查询答题列表") - @PreAuthorize("@ss.hasPermission('prison:answer:query')") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:query')") public CommonResult> getAnswersByAssessmentRecordId( @NotNull(message = "测评记录ID不能为空") @RequestParam("assessmentRecordId") Long assessmentRecordId) { List list = answerService.getAnswersByAssessmentRecordId(assessmentRecordId); diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java index e0f8875fce..c2d7f3bada 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java @@ -13,6 +13,7 @@ import jakarta.validation.*; import jakarta.servlet.http.*; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.io.IOException; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @@ -377,6 +378,21 @@ public class EvaluationReportController { BeanUtils.toBean(list, EvaluationReportRespVO.class)); } + @PostMapping("/report/generate-by-ai") + @Operation(summary = "AI生成报告维度内容") + @PreAuthorize("@ss.hasPermission('prison:evaluation-report:report:ai-generate')") + public CommonResult generateReportByAi(@RequestBody Map requestData) { + Long reportId = Long.valueOf(requestData.get("reportId").toString()); + @SuppressWarnings("unchecked") + List dimensionIds = requestData.get("dimensionIds") != null + ? ((List) requestData.get("dimensionIds")).stream() + .map(Number::longValue) + .collect(Collectors.toList()) + : null; + evaluationReportService.generateReportByAi(reportId, dimensionIds); + return success(true); + } + // ========== 维度数据管理 ========== @PostMapping("/dimension-data/create") diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java index f2aea0018e..ed6d387092 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/vo/PrisonerProgressRespVO.java @@ -10,6 +10,12 @@ import java.time.LocalDateTime; @Data public class PrisonerProgressRespVO { + @Schema(description = "问卷ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long questionnaireId; + + @Schema(description = "问卷名称", example = "风险评估问卷") + private String questionnaireName; + @Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/vo/RiskAssessmentSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/vo/RiskAssessmentSaveReqVO.java index 93351d4586..3d50a31b19 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/vo/RiskAssessmentSaveReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/vo/RiskAssessmentSaveReqVO.java @@ -6,6 +6,9 @@ import java.util.*; import jakarta.validation.constraints.*; import java.math.BigDecimal; import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; @Schema(description = "管理后台 - 危险评估新增/修改 Request VO") @Data @@ -28,6 +31,7 @@ public class RiskAssessmentSaveReqVO { @Schema(description = "评估日期", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "评估日期不能为空") + @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate assessmentDate; @Schema(description = "暴力倾向得分") @@ -55,6 +59,7 @@ public class RiskAssessmentSaveReqVO { // 评估人ID和评估人姓名由后端自动从登录上下文获取,不从前端传递 @Schema(description = "下次评估日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate nextAssessmentDate; @Schema(description = "状态:1-待审核 2-已通过", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationRespVO.java index 86294f967a..f696ddfb49 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationRespVO.java @@ -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.*; /** @@ -67,6 +68,7 @@ public class SituationRespVO { @Schema(description = "处理时间") @ExcelProperty("处理时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private LocalDateTime handleTime; @Schema(description = "处理结果") @@ -79,10 +81,12 @@ public class SituationRespVO { @Schema(description = "发生时间") @ExcelProperty("发生时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private LocalDateTime occurTime; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private LocalDateTime createTime; } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationSaveReqVO.java index fb10f8e550..b2636b70eb 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationSaveReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/vo/SituationSaveReqVO.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.util.*; import jakarta.validation.constraints.*; +import com.fasterxml.jackson.annotation.JsonFormat; /** * 管理后台 - 狱情收集新增/修改 Request VO @@ -54,6 +55,7 @@ public class SituationSaveReqVO { private String handler; @Schema(description = "处理时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private java.time.LocalDateTime handleTime; @Schema(description = "处理结果") @@ -63,6 +65,7 @@ public class SituationSaveReqVO { private String remark; @Schema(description = "发生时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private java.time.LocalDateTime occurTime; } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java index e21f2eb30f..bf6aa706d8 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java @@ -87,8 +87,8 @@ public class WarningController { @Operation(summary = "分页查询预警信息") @PreAuthorize("@ss.hasPermission('prison:warning:query')") public CommonResult> page(@Valid WarningPageReqVO pageReqVO) { - PageResult pageResult = warningService.getWarningPage(pageReqVO); - return success(WarningConvert.INSTANCE.convertPage(pageResult)); + PageResult pageResult = warningService.getWarningPage(pageReqVO); + return success(pageResult); } @GetMapping("/export-excel") @@ -97,10 +97,9 @@ public class WarningController { public void exportExcel(@Valid WarningPageReqVO pageReqVO, HttpServletResponse response) throws Exception { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = warningService.getWarningPage(pageReqVO).getList(); + List list = warningService.getWarningPage(pageReqVO).getList(); // 导出 Excel - ExcelUtils.write(response, "预警信息.xls", "预警信息数据", WarningRespVO.class, - WarningConvert.INSTANCE.convertList(list)); + ExcelUtils.write(response, "预警信息.xls", "预警信息数据", WarningRespVO.class, list); } @GetMapping("/get-import-template") diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningPageReqVO.java index f023b99d5e..544f1c9084 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningPageReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningPageReqVO.java @@ -39,6 +39,9 @@ public class WarningPageReqVO extends PageParam { @Schema(description = "关联监区ID") private Long areaId; + @Schema(description = "监区名称(查询条件)") + private String areaName; + @Schema(description = "关联监室ID") private Long cellId; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningRespVO.java index 66051f225c..481c1736ac 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningRespVO.java @@ -54,6 +54,10 @@ public class WarningRespVO { @ExcelProperty("关联监区ID") private Long areaId; + @Schema(description = "监区名称", example = "一监区") + @ExcelProperty("监区名称") + private String areaName; + @Schema(description = "关联监室ID") @ExcelProperty("关联监室ID") private Long cellId; @@ -120,4 +124,9 @@ public class WarningRespVO { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private LocalDateTime createTime; + @Schema(description = "更新时间") + @ExcelProperty("更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") + private LocalDateTime updateTime; + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningSaveReqVO.java index 7c682266ef..72afad0698 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningSaveReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/vo/WarningSaveReqVO.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import java.util.*; import jakarta.validation.constraints.*; +import com.fasterxml.jackson.annotation.JsonFormat; /** * 管理后台 - 预警信息新增/修改 Request VO @@ -51,9 +52,11 @@ public class WarningSaveReqVO { private Long cellId; @Schema(description = "预警时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.time.LocalDateTime alertTime; @Schema(description = "核实时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.time.LocalDateTime verifyTime; @Schema(description = "核实人") @@ -63,6 +66,7 @@ public class WarningSaveReqVO { private String verifyResult; @Schema(description = "处置时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.time.LocalDateTime handleTime; @Schema(description = "处置人") @@ -75,6 +79,7 @@ public class WarningSaveReqVO { private String handleResult; @Schema(description = "解除时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.time.LocalDateTime releaseTime; @Schema(description = "解除人") @@ -84,6 +89,7 @@ public class WarningSaveReqVO { private String releaseReason; @Schema(description = "发生时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.time.LocalDateTime occurTime; @Schema(description = "备注") diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/question/QuestionMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/question/QuestionMapper.java index fa9ade537c..56336a5fc8 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/question/QuestionMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/question/QuestionMapper.java @@ -27,7 +27,8 @@ public interface QuestionMapper extends BaseMapperX { .eqIfPresent(QuestionDO::getSort, reqVO.getSort()) .eqIfPresent(QuestionDO::getIsRequired, reqVO.getIsRequired()) .betweenIfPresent(QuestionDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(QuestionDO::getId)); + .orderByAsc(QuestionDO::getPartSort) + .orderByAsc(QuestionDO::getSort)); } } \ No newline at end of file diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java index 147a62c94c..693e2d9e6a 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnaire_task/QuestionnaireTaskMapper.java @@ -36,7 +36,14 @@ public interface QuestionnaireTaskMapper extends BaseMapperX selectTaskDetailById(@Param("id") Long id); - @Select("UPDATE prison_questionnaire_task qt " + + /** + * 更新任务统计信息 + * 注意:使用 @Select 是因为 MyBatis-Plus 需要返回结果,但这里我们只关心执行结果 + * 实际执行的是 UPDATE 操作,MyBatis 会忽略返回的查询结果 + * @param taskId 任务ID + */ + @Select("") void updateTaskStatistics(@Param("taskId") Long taskId); @Select("SELECT COUNT(*) as task_count, " + diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/warning/WarningMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/warning/WarningMapper.java index 18832c90be..45fbb34821 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/warning/WarningMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/warning/WarningMapper.java @@ -7,6 +7,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.module.prison.dal.dataobject.warning.WarningDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.ResultMap; +import org.apache.ibatis.annotations.Select; import cn.iocoder.yudao.module.prison.controller.admin.warning.vo.*; /** @@ -36,4 +39,91 @@ public interface WarningMapper extends BaseMapperX { .orderByDesc(WarningDO::getId)); } + /** + * 分页查询预警信息(包含监区名称) + */ + @Select(""" + + """) + @ResultMap("WarningResultMap") + List selectWarningPage(@Param("reqVO") WarningPageReqVO reqVO); + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java index 6114a22cf6..26d6ac3f12 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java @@ -14,6 +14,7 @@ import java.util.*; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; +import java.util.stream.Collectors; import cn.iocoder.yudao.module.prison.controller.admin.answer.vo.*; import cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo.AssessmentAnswerSubmitReqVO; import cn.iocoder.yudao.module.prison.dal.dataobject.answer.AnswerDO; @@ -220,7 +221,8 @@ public class AnswerServiceImpl implements AnswerService { /** * 计算选择题得分 - * 选项格式: [{label:"选项1",score:10,isCorrect:true},...] + * 选项格式: [{label:"选项1",score:10,isOther:false},...] + * 使用数组索引匹配选项(从0开始) */ private BigDecimal calculateChoiceQuestionScore(QuestionDO question, AssessmentAnswerSubmitReqVO.AnswerItem answerItem) { if (question.getOptions() == null || question.getOptions().isEmpty()) { @@ -241,14 +243,13 @@ public class AnswerServiceImpl implements AnswerService { if (answerItem.getOptionIds() == null || answerItem.getOptionIds().isEmpty()) { return BigDecimal.ZERO; } - Long selectedOptionId = answerItem.getOptionIds().get(0); - for (int i = 0; i < options.size(); i++) { - JSONObject option = options.getJSONObject(i); - if (option.getLong("id") != null && option.getLong("id").equals(selectedOptionId)) { - // 检查是否正确选项 - if (option.getBoolean("isCorrect") != null && option.getBoolean("isCorrect")) { - return questionScore; - } + // 使用数组索引(从0开始)匹配选项 + Long selectedOptionIndex = answerItem.getOptionIds().get(0); + if (selectedOptionIndex >= 0 && selectedOptionIndex < options.size()) { + JSONObject option = options.getJSONObject(selectedOptionIndex.intValue()); + // 检查是否正确选项(score > 0 表示正确) + if (option.getBigDecimal("score") != null && option.getBigDecimal("score").compareTo(BigDecimal.ZERO) > 0) { + return questionScore; } } return BigDecimal.ZERO; @@ -262,22 +263,21 @@ public class AnswerServiceImpl implements AnswerService { int correctCount = 0; int userSelectCount = answerItem.getOptionIds().size(); + // 统计正确选项数量(score > 0 表示正确) for (int i = 0; i < options.size(); i++) { JSONObject option = options.getJSONObject(i); - if (option.getBoolean("isCorrect") != null && option.getBoolean("isCorrect")) { + if (option.getBigDecimal("score") != null && option.getBigDecimal("score").compareTo(BigDecimal.ZERO) > 0) { correctCount++; } } // 用户选择的正确选项数量 int userCorrectCount = 0; - for (Long selectedOptionId : answerItem.getOptionIds()) { - for (int i = 0; i < options.size(); i++) { - JSONObject option = options.getJSONObject(i); - if (option.getLong("id") != null && option.getLong("id").equals(selectedOptionId) - && option.getBoolean("isCorrect") != null && option.getBoolean("isCorrect")) { + for (Long selectedOptionIndex : answerItem.getOptionIds()) { + if (selectedOptionIndex >= 0 && selectedOptionIndex < options.size()) { + JSONObject option = options.getJSONObject(selectedOptionIndex.intValue()); + if (option.getBigDecimal("score") != null && option.getBigDecimal("score").compareTo(BigDecimal.ZERO) > 0) { userCorrectCount++; - break; } } } @@ -332,6 +332,7 @@ public class AnswerServiceImpl implements AnswerService { /** * 判断答案是否正确(单选/多选题) + * 使用数组索引匹配选项(从0开始) */ private Boolean isAnswerCorrect(QuestionDO question, AssessmentAnswerSubmitReqVO.AnswerItem answerItem) { if (question.getOptions() == null || question.getOptions().isEmpty()) { @@ -346,12 +347,12 @@ public class AnswerServiceImpl implements AnswerService { if (answerItem.getOptionIds() == null || answerItem.getOptionIds().isEmpty()) { return false; } - Long selectedOptionId = answerItem.getOptionIds().get(0); - for (int i = 0; i < options.size(); i++) { - JSONObject option = options.getJSONObject(i); - if (option.getLong("id") != null && option.getLong("id").equals(selectedOptionId)) { - return option.getBoolean("isCorrect") != null && option.getBoolean("isCorrect"); - } + // 使用数组索引匹配选项 + Long selectedOptionIndex = answerItem.getOptionIds().get(0); + if (selectedOptionIndex >= 0 && selectedOptionIndex < options.size()) { + JSONObject option = options.getJSONObject(selectedOptionIndex.intValue()); + // score > 0 表示正确选项 + return option.getBigDecimal("score") != null && option.getBigDecimal("score").compareTo(BigDecimal.ZERO) > 0; } return false; } else if (question.getType() == QUESTION_TYPE_MULTI) { @@ -360,22 +361,23 @@ public class AnswerServiceImpl implements AnswerService { return false; } - Set correctOptionIds = new HashSet<>(); - Set userSelectedIds = new HashSet<>(answerItem.getOptionIds()); + // 收集正确选项的索引 + Set correctOptionIndices = new HashSet<>(); + Set userSelectedIndices = new HashSet<>(answerItem.getOptionIds()); for (int i = 0; i < options.size(); i++) { JSONObject option = options.getJSONObject(i); - if (option.getBoolean("isCorrect") != null && option.getBoolean("isCorrect")) { - correctOptionIds.add(option.getLong("id")); - } else if (option.getBoolean("isCorrect") != null && !option.getBoolean("isCorrect") - && userSelectedIds.contains(option.getLong("id"))) { - // 选择了错误选项 + if (option.getBigDecimal("score") != null && option.getBigDecimal("score").compareTo(BigDecimal.ZERO) > 0) { + correctOptionIndices.add(i); + } else if (userSelectedIndices.contains((long) i)) { + // 选择了错误选项(分数为0或负数的选项) return false; } } // 用户必须选择所有正确选项 - return correctOptionIds.equals(userSelectedIds); + Set correctIndexSet = correctOptionIndices.stream().map(Long::valueOf).collect(Collectors.toSet()); + return correctIndexSet.equals(userSelectedIndices); } } catch (Exception e) { return false; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java index 48d9962dfe..aeb2c3e07b 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java @@ -7,8 +7,9 @@ import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; -import java.util.*; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import java.math.BigDecimal; +import java.util.*; import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; import cn.iocoder.yudao.module.prison.controller.admin.vo.ImportRespVO; @@ -79,9 +80,16 @@ public class ConsumptionServiceImpl implements ConsumptionService { // 校验订单存在 validateConsumptionExists(updateReqVO.getId()); - // 1. 更新消费订单 + // 1. 更新消费订单(使用updateWrapper确保balance字段被正确更新) ConsumptionDO updateObj = ConsumptionConvert.INSTANCE.convert(updateReqVO); - consumptionMapper.updateById(updateObj); + if (updateReqVO.getBalance() != null) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(ConsumptionDO::getId, updateReqVO.getId()) + .set(ConsumptionDO::getBalance, updateReqVO.getBalance()); + consumptionMapper.update(updateObj, wrapper); + } else { + consumptionMapper.updateById(updateObj); + } // 2. 删除原有明细 consumptionDetailMapper.deleteListByConsumptionId(updateReqVO.getId()); diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportService.java index 62a5f8f2ef..9ca74aa64f 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportService.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportService.java @@ -237,4 +237,12 @@ public interface EvaluationReportService { */ SseEmitter streamGenerateDimension(Long dimensionId, Long prisonerId, String customPrompt, String systemPrompt); + /** + * AI生成报告维度内容(同步方式) + * @param reportId 报告ID + * @param dimensionIds 维度ID列表(可选,为空则生成所有维度) + * @return 生成结果 + */ + void generateReportByAi(Long reportId, List dimensionIds); + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java index e9867898d2..3121327b11 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java @@ -48,6 +48,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import cn.hutool.core.thread.ThreadUtil; +import lombok.extern.slf4j.Slf4j; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; @@ -63,6 +64,7 @@ import static cn.iocoder.yudao.module.prison.enums.EvaluationAiStatusEnum.PENDIN */ @Service @Validated +@Slf4j public class EvaluationReportServiceImpl implements EvaluationReportService { /** @@ -924,4 +926,238 @@ public class EvaluationReportServiceImpl implements EvaluationReportService { } } + @Override + @Transactional(rollbackFor = Exception.class) + public void generateReportByAi(Long reportId, List dimensionIds) { + // 获取报告信息 + EvaluationReportDO report = evaluationReportMapper.selectById(reportId); + if (report == null) { + throw exception(EVALUATION_REPORT_NOT_EXISTS); + } + + // 获取报告关联的模板和维度 + List dimensions; + if (dimensionIds != null && !dimensionIds.isEmpty()) { + // 只生成指定维度 + dimensions = dimensionIds.stream() + .map(dimensionMapper::selectById) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } else { + // 获取模板下所有启用的维度 + dimensions = dimensionMapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(EvaluationDimensionDO::getTemplateId, report.getTemplateId()) + .eq(EvaluationDimensionDO::getStatus, 1) // 启用状态 + .orderByAsc(EvaluationDimensionDO::getSort)); + } + + // 获取罪犯信息 + PrisonerDO prisoner = prisonerMapper.selectById(report.getPrisonerId()); + if (prisoner == null) { + throw exception(PRISONER_NOT_EXISTS); + } + + // 遍历每个维度生成内容 + for (EvaluationDimensionDO dimension : dimensions) { + try { + // 获取维度数据源 + DimensionDataSourcesRespDTO dataSources = getDimensionDataSources(dimension.getId(), report.getPrisonerId()); + + // 调用LLM生成内容 + String analysis = generateDimensionAnalysisByLlm(dimension, dataSources); + + // 保存或更新维度数据 + EvaluationDimensionDataDO existingData = dimensionDataMapper.selectOne( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(EvaluationDimensionDataDO::getReportId, reportId) + .eq(EvaluationDimensionDataDO::getDimensionId, dimension.getId())); + + if (existingData != null) { + // 更新现有数据 + existingData.setAiAnalysis(analysis); + dimensionDataMapper.updateById(existingData); + } else { + // 创建新数据 + EvaluationDimensionDataDO newData = new EvaluationDimensionDataDO(); + newData.setReportId(reportId); + newData.setDimensionId(dimension.getId()); + newData.setDimensionName(dimension.getName()); + newData.setAiAnalysis(analysis); + dimensionDataMapper.insert(newData); + } + } catch (Exception e) { + log.error("AI生成维度[{}]内容失败: {}", dimension.getName(), e.getMessage(), e); + // 继续处理其他维度,不中断 + } + } + + // 更新报告的AI状态 + evaluationReportMapper.updateById(new EvaluationReportDO() {{ + setId(reportId); + setAiStatus(2); // 生成完成 + }}); + } + + /** + * 调用LLM生成维度分析内容 + */ + private String generateDimensionAnalysisByLlm(EvaluationDimensionDO dimension, DimensionDataSourcesRespDTO dataSources) { + try { + LlmClient client = llmClientFactory.getAssessmentClient(); + LlmClient.LlmOptions options = LlmClient.LlmOptions.assessmentOptions(); + options.setJsonMode(false); + options.setTemperature(0.3f); + options.setMaxTokens(2048); + + // 构建提示词 + String prompt = buildDimensionPrompt(dimension, dataSources); + options.setSystemPrompt(buildSystemPrompt(dimension)); + + // 调用LLM + String response = client.complete(prompt, options); + return cleanLlmResponse(response); + } catch (Exception e) { + log.error("调用LLM生成维度分析失败: {}", e.getMessage()); + // 降级为规则生成 + return generateDimensionAnalysisByRules(dimension, dataSources); + } + } + + /** + * 构建维度提示词 + */ + private String buildDimensionPrompt(EvaluationDimensionDO dimension, DimensionDataSourcesRespDTO dataSources) { + StringBuilder prompt = new StringBuilder(); + prompt.append("请根据以下数据,对罪犯进行维度分析评估。\n\n"); + + // 添加罪犯基本信息 + Map prisoner = dataSources.getPrisoner(); + if (prisoner != null && !prisoner.isEmpty()) { + prompt.append("【罪犯基本信息】\n"); + prompt.append(cn.hutool.json.JSONUtil.toJsonStr(prisoner)).append("\n\n"); + } + + // 添加消费数据 + Map consumption = dataSources.getConsumptionSummary(); + if (consumption != null && !consumption.isEmpty()) { + prompt.append("【消费情况】\n"); + prompt.append(cn.hutool.json.JSONUtil.toJsonStr(consumption)).append("\n\n"); + } + + // 添加计分考核数据 + Map score = dataSources.getScoreSummary(); + if (score != null && !score.isEmpty()) { + prompt.append("【计分考核情况】\n"); + prompt.append(cn.hutool.json.JSONUtil.toJsonStr(score)).append("\n\n"); + } + + // 添加风险评估数据 + Map risk = dataSources.getRiskAssessment(); + if (risk != null && !risk.isEmpty()) { + prompt.append("【风险评估情况】\n"); + prompt.append(cn.hutool.json.JSONUtil.toJsonStr(risk)).append("\n\n"); + } + + // 添加自定义提示词 + if (StrUtil.isNotBlank(dimension.getAiPrompt())) { + prompt.append("【特殊分析要求】\n"); + prompt.append(dimension.getAiPrompt()).append("\n\n"); + } + + prompt.append("请基于以上数据,提供详细的分析评估,包括数据分析、特点总结和针对性建议。"); + return prompt.toString(); + } + + /** + * 构建系统提示词 + */ + private String buildSystemPrompt(EvaluationDimensionDO dimension) { + StringBuilder prompt = new StringBuilder(); + prompt.append("你是一位专业的监狱心理评估专家,擅长根据罪犯的各项数据进行综合分析评估。\n"); + prompt.append("你的分析应当客观、专业、具有建设性。\n"); + prompt.append("请用流畅的中文段落形式输出分析结果,不要使用Markdown格式或特殊标记。\n"); + prompt.append("分析应包含:数据解读、特点总结、改造建议三部分。"); + return prompt.toString(); + } + + /** + * 使用规则生成维度分析(降级方案) + */ + private String generateDimensionAnalysisByRules(EvaluationDimensionDO dimension, DimensionDataSourcesRespDTO dataSources) { + StringBuilder analysis = new StringBuilder(); + + // 分析计分考核 + Map scoreSummary = dataSources.getScoreSummary(); + if (scoreSummary != null) { + Object totalScore = scoreSummary.get("totalScore"); + if (totalScore != null) { + double score = Double.parseDouble(totalScore.toString()); + if (score >= 1500) { + analysis.append("该罪犯计分考核表现优秀,月均分数保持在较高水平。"); + } else if (score >= 1000) { + analysis.append("该罪犯计分考核表现良好,能够完成各项改造任务。"); + } else { + analysis.append("该罪犯计分考核表现一般,需要加强教育引导。"); + } + } + } + + // 分析消费情况 + Map consumptionSummary = dataSources.getConsumptionSummary(); + if (consumptionSummary != null) { + analysis.append("在消费方面,"); + Object totalConsumption = consumptionSummary.get("totalAmount"); + if (totalConsumption != null) { + double amount = Double.parseDouble(totalConsumption.toString()); + analysis.append("月均消费约").append(String.format("%.2f", amount / 100)).append("元。"); + } + } + + // 风险评估 + Map riskAssessment = dataSources.getRiskAssessment(); + if (riskAssessment != null) { + Object riskLevel = riskAssessment.get("riskLevel"); + if (riskLevel != null) { + int level = Integer.parseInt(riskLevel.toString()); + switch (level) { + case 1: + analysis.append("该罪犯风险等级评估为低风险。"); + break; + case 2: + analysis.append("该罪犯风险等级评估为中风险。"); + break; + case 3: + case 4: + analysis.append("该罪犯风险等级评估为高风险,需重点关注。"); + break; + } + } + } + + analysis.append("\n\n建议继续保持良好的改造状态,积极参与各项劳动和学习活动,争取更好的改造成绩。"); + return analysis.toString(); + } + + /** + * 清理LLM返回结果,去除JSON包装和特殊标记 + */ + private String cleanLlmResponse(String response) { + if (StrUtil.isBlank(response)) { + return ""; + } + // 去除 Markdown 代码块标记 + String cleaned = response.replaceAll("(?s)```json\\s*", "").replaceAll("(?s)```\\s*", ""); + cleaned = cleaned.replaceAll("(?s)`{3,}\\s*json\\s*", "").replaceAll("(?s)`{3,}\\s*", ""); + // 去除 JSON 对象的外层括号 + cleaned = cleaned.replaceAll("^\\s*\\{[\\s\\S]*?\\}\\s*$", "").trim(); + // 去除分析内容中的特殊前缀 + cleaned = cleaned.replaceAll("(?m)^\\s*[【\\[].*?[】\\]]\\s*", ""); + // 去除“综合分析建议”标题或前缀 + cleaned = cleaned.replaceAll("(?m)^\\s*#+\\s*综合分析建议\\s*$", ""); + cleaned = cleaned.replaceAll("(?m)^\\s*综合分析建议\\s*[::]\\s*", ""); + // 去除多余空行 + cleaned = cleaned.replaceAll("(?m)^\\s*$\\n", ""); + return cleaned.trim(); + } + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/impl/PrisonerServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/impl/PrisonerServiceImpl.java index 728ab8e10f..535b673e81 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/impl/PrisonerServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/impl/PrisonerServiceImpl.java @@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.prison.service.impl; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.prison.controller.admin.prisoner.vo.PrisonerPageReqVO; -import cn.iocoder.yudao.module.prison.controller.admin.prisoner.vo.PrisonerRespVO; -import cn.iocoder.yudao.module.prison.controller.admin.prisoner.vo.PrisonerSaveReqVO; -import cn.iocoder.yudao.module.prison.controller.admin.prisoner.vo.TransferReqVO; +import cn.iocoder.yudao.module.prison.controller.admin.prisoner.vo.*; import cn.iocoder.yudao.module.prison.convert.prisoner.PrisonerConvert; import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerAreaLogDO; import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO; @@ -17,10 +14,13 @@ import cn.iocoder.yudao.module.prison.dal.mysql.area.AreaMapper; import cn.iocoder.yudao.module.prison.dal.mysql.cell.CellMapper; import cn.iocoder.yudao.module.prison.enums.AreaTypeEnum; import cn.iocoder.yudao.module.prison.enums.CellStatusEnum; +import cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.prison.enums.PrisonerStatusEnum; import cn.iocoder.yudao.module.prison.enums.SupervisionLevelEnum; import cn.iocoder.yudao.module.prison.service.PrisonerService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -38,6 +38,7 @@ import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; */ @Service @Validated +@Slf4j public class PrisonerServiceImpl implements PrisonerService { @Resource @@ -66,6 +67,10 @@ public class PrisonerServiceImpl implements PrisonerService { // 插入 PrisonerDO prisoner = PrisonerConvert.INSTANCE.convert(reqVO); + // 确保状态不为空,默认设置为在押状态 + if (prisoner.getStatus() == null) { + prisoner.setStatus(PrisonerStatusEnum.IMPRISONED); + } prisonerMapper.insert(prisoner); return prisoner.getId(); } @@ -138,9 +143,20 @@ public class PrisonerServiceImpl implements PrisonerService { @Override public PageResult getPrisonerPage(PrisonerPageReqVO reqVO) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.like(reqVO.getPrisonerNo() != null, PrisonerDO::getPrisonerNo, reqVO.getPrisonerNo()) - .like(reqVO.getName() != null, PrisonerDO::getName, reqVO.getName()) - .eq(reqVO.getGender() != null, PrisonerDO::getGender, reqVO.getGender()) + + // 使用 OR 关系:编号或姓名任一匹配即可 + if (reqVO.getPrisonerNo() != null && reqVO.getName() != null) { + wrapper.and(w -> w.like(PrisonerDO::getPrisonerNo, reqVO.getPrisonerNo()) + .or() + .like(PrisonerDO::getName, reqVO.getName())); + } else if (reqVO.getPrisonerNo() != null) { + wrapper.like(PrisonerDO::getPrisonerNo, reqVO.getPrisonerNo()); + } else if (reqVO.getName() != null) { + wrapper.like(PrisonerDO::getName, reqVO.getName()); + } + + // 其他筛选条件保持 AND 关系 + wrapper.eq(reqVO.getGender() != null, PrisonerDO::getGender, reqVO.getGender()) .eq(reqVO.getIdCard() != null, PrisonerDO::getIdCard, reqVO.getIdCard()) .eq(reqVO.getCrime() != null, PrisonerDO::getCrime, reqVO.getCrime()) .eq(reqVO.getSupervisionLevel() != null, PrisonerDO::getSupervisionLevel, reqVO.getSupervisionLevel()) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java index 9b06f59f32..17da8449b8 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImpl.java @@ -59,13 +59,20 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { @Override @Transactional(rollbackFor = Exception.class) public Long createQuestionnaireTask(QuestionnaireTaskCreateReqVO createReqVO) { + log.info("开始创建问卷任务: taskName={}, questionnaireId={}, targetType={}", + createReqVO.getTaskName(), createReqVO.getQuestionnaireId(), createReqVO.getTargetType()); + + // 验证问卷是否存在 QuestionnaireDO questionnaire = questionnaireMapper.selectById(createReqVO.getQuestionnaireId()); if (questionnaire == null) { + log.warn("创建问卷任务失败,问卷不存在: questionnaireId={}", createReqVO.getQuestionnaireId()); throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_NOT_EXISTS); } + // 验证目标参数 validateTarget(createReqVO); + // 构建任务对象 QuestionnaireTaskDO task = QuestionnaireTaskDO.builder() .taskName(createReqVO.getTaskName()) .questionnaireId(createReqVO.getQuestionnaireId()) @@ -73,7 +80,7 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { .targetType(createReqVO.getTargetType()) .startTime(createReqVO.getStartTime()) .deadline(createReqVO.getDeadline()) - .status(2) + .status(2) // 进行中 .totalCount(0) .completedCount(0) .pendingCount(0) @@ -81,6 +88,7 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { .remark(createReqVO.getRemark()) .build(); + // 设置目标范围 if (createReqVO.getTargetType() == 1) { task.setPrisonerIds(CollUtil.join(createReqVO.getPrisonerIds(), ",")); } else if (createReqVO.getTargetType() == 2) { @@ -91,14 +99,27 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { } } + // 插入任务记录 questionnaireTaskMapper.insert(task); + log.info("问卷任务创建成功: taskId={}", task.getId()); + // 获取目标罪犯列表并创建答题记录 List prisoners = getTargetPrisoners(createReqVO); if (CollUtil.isNotEmpty(prisoners)) { + log.info("为任务 {} 创建 {} 条答题记录", task.getId(), prisoners.size()); createRecordsForPrisoners(task, prisoners); + } else { + log.warn("任务 {} 没有符合条件的目标罪犯", task.getId()); } - updateTaskStatistics(task.getId()); + // 更新任务统计信息 + try { + updateTaskStatistics(task.getId()); + log.info("任务 {} 统计信息更新成功", task.getId()); + } catch (Exception e) { + log.error("任务 {} 统计信息更新失败: {}", task.getId(), e.getMessage(), e); + // 统计更新失败不应影响任务创建 + } return task.getId(); } @@ -249,6 +270,8 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { .eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.EXPIRED.getCode()) .set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.PENDING.getCode()) .set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getDeadline, task.getDeadline())); + + updateTaskStatistics(id); } @Override @@ -340,6 +363,8 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { return records.stream().map(record -> { PrisonerProgressRespVO vo = new PrisonerProgressRespVO(); + vo.setQuestionnaireId(record.getQuestionnaireId()); + vo.setQuestionnaireName(record.getQuestionnaireName()); vo.setId(record.getId()); vo.setPrisonerId(record.getPrisonerId()); vo.setPrisonerNo(record.getPrisonerNo()); @@ -503,12 +528,18 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService { return prisonerMapper.selectList( new LambdaQueryWrapper() .eq(PrisonerDO::getPrisonAreaId, createReqVO.getAreaId()) - .eq(PrisonerDO::getStatus, 1) + .in(PrisonerDO::getStatus, java.util.Arrays.asList( + cn.iocoder.yudao.module.prison.enums.PrisonerStatusEnum.IMPRISONED, + cn.iocoder.yudao.module.prison.enums.PrisonerStatusEnum.PAROLED + )) ); case 3: return prisonerMapper.selectList( new LambdaQueryWrapper() - .eq(PrisonerDO::getStatus, 1) + .in(PrisonerDO::getStatus, java.util.Arrays.asList( + cn.iocoder.yudao.module.prison.enums.PrisonerStatusEnum.IMPRISONED, + cn.iocoder.yudao.module.prison.enums.PrisonerStatusEnum.PAROLED + )) ); default: return Collections.emptyList(); diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/situation/impl/SituationServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/situation/impl/SituationServiceImpl.java index 4dc9dda659..c4d3d8f4b1 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/situation/impl/SituationServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/situation/impl/SituationServiceImpl.java @@ -13,6 +13,7 @@ import java.util.*; import cn.iocoder.yudao.module.prison.controller.admin.situation.vo.*; import cn.iocoder.yudao.module.prison.controller.admin.vo.ImportRespVO; import cn.iocoder.yudao.module.prison.dal.dataobject.situation.SituationDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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; @@ -55,7 +56,14 @@ public class SituationServiceImpl implements SituationService { validateSituationExists(updateReqVO.getId()); // 更新 SituationDO updateObj = BeanUtils.toBean(updateReqVO, SituationDO.class); - situationMapper.updateById(updateObj); + // 使用updateWrapper确保occurTime字段被正确更新 + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SituationDO::getId, updateReqVO.getId()); + // 修复:直接使用从请求VO中解析好的LocalDateTime值(Spring已自动从ISO 8601格式解析) + if (updateReqVO.getOccurTime() != null) { + wrapper.set(SituationDO::getOccurTime, updateReqVO.getOccurTime()); + } + situationMapper.update(updateObj, wrapper); } @Override diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/WarningService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/WarningService.java index c5132a9a00..5d59d0ca87 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/WarningService.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/WarningService.java @@ -58,7 +58,7 @@ public interface WarningService { * @param pageReqVO 分页查询 * @return 预警信息分页 */ - PageResult getWarningPage(WarningPageReqVO pageReqVO); + PageResult getWarningPage(WarningPageReqVO pageReqVO); /** * 导入预警信息 diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/impl/WarningServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/impl/WarningServiceImpl.java index 8987131545..748b829ad3 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/impl/WarningServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/warning/impl/WarningServiceImpl.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.Collections; import cn.iocoder.yudao.module.prison.controller.admin.warning.vo.*; import cn.iocoder.yudao.module.prison.controller.admin.vo.ImportRespVO; import cn.iocoder.yudao.module.prison.dal.dataobject.warning.WarningDO; @@ -79,8 +80,21 @@ public class WarningServiceImpl implements WarningService { } @Override - public PageResult getWarningPage(WarningPageReqVO pageReqVO) { - return warningMapper.selectPage(pageReqVO); + public PageResult getWarningPage(WarningPageReqVO pageReqVO) { + // 使用联表查询获取带 areaName 的数据 + List list = warningMapper.selectWarningPage(pageReqVO); + // 计算总记录数 + long total = list.size(); + // 分页(这里简化处理,实际项目可优化为 SQL 分页) + int fromIndex = (int) ((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize()); + int toIndex = fromIndex + pageReqVO.getPageSize(); + if (fromIndex >= total) { + return new PageResult<>(Collections.emptyList(), total); + } + List pageList = fromIndex < total + ? list.subList(fromIndex, Math.min(toIndex, (int) total)) + : Collections.emptyList(); + return new PageResult<>(pageList, total); } @Override diff --git a/yudao-module-prison/src/main/resources/mapper/WarningMapper.xml b/yudao-module-prison/src/main/resources/mapper/WarningMapper.xml new file mode 100644 index 0000000000..e775078eb8 --- /dev/null +++ b/yudao-module-prison/src/main/resources/mapper/WarningMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/yudao-module-prison/src/main/resources/sql/migrate_questionnaire_record.sql b/yudao-module-prison/src/main/resources/sql/migrate_questionnaire_record.sql new file mode 100644 index 0000000000..bcfb003092 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/migrate_questionnaire_record.sql @@ -0,0 +1,70 @@ +-- ===================================================== +-- 问卷答题记录表字段迁移脚本 +-- ===================================================== +-- 日期:2026-02-04 +-- 说明:添加缺失的字段以匹配 QuestionnaireRecordDO + +USE xlcp_dev; + +-- 1. 添加问卷名称字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS questionnaire_name varchar(200) DEFAULT NULL COMMENT '问卷名称' AFTER questionnaire_id; + +-- 2. 添加罪犯姓名字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS prisoner_name varchar(100) DEFAULT NULL COMMENT '罪犯姓名' AFTER prisoner_no; + +-- 3. 删除旧字段(如果存在) +ALTER TABLE prison_questionnaire_record DROP COLUMN IF EXISTS answers; +ALTER TABLE prison_questionnaire_record DROP COLUMN IF EXISTS score; +ALTER TABLE prison_questionnaire_record DROP COLUMN IF EXISTS result; + +-- 4. 添加任务关联字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS task_id bigint DEFAULT NULL COMMENT '所属任务ID' AFTER prisoner_name; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS prisoner_area_id bigint DEFAULT NULL COMMENT '犯人所属监区ID' AFTER task_id; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS prisoner_area_name varchar(50) DEFAULT NULL COMMENT '犯人所属监区名称' AFTER prisoner_area_id; + +-- 5. 修改状态字段注释 +ALTER TABLE prison_questionnaire_record MODIFY COLUMN status tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-待测评 2-测评中 3-已完成 4-已过期 5-已取消'; + +-- 6. 添加时间相关字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS start_time datetime DEFAULT NULL COMMENT '开始时间' AFTER status; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS end_time datetime DEFAULT NULL COMMENT '结束时间' AFTER start_time; +-- answer_time 字段已修改为允许 NULL + +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS deadline datetime DEFAULT NULL COMMENT '截止日期' AFTER answer_time; + +-- 7. 添加评分相关字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS objective_score decimal(10,2) DEFAULT 0.00 COMMENT '客观题得分' AFTER deadline; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS subjective_score decimal(10,2) DEFAULT 0.00 COMMENT '主观题得分' AFTER objective_score; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS total_score decimal(10,2) DEFAULT 0.00 COMMENT '总分' AFTER subjective_score; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS pass_score decimal(10,2) DEFAULT NULL COMMENT '及格分数' AFTER total_score; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS pass_status tinyint DEFAULT NULL COMMENT '及格状态:1-及格 2-不及格 3-待评阅' AFTER pass_score; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS risk_level tinyint DEFAULT NULL COMMENT '风险等级:1-高风险 2-中风险 3-低风险' AFTER pass_status; + +-- 8. 添加评阅相关字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS evaluator_id bigint DEFAULT NULL COMMENT '评阅人ID' AFTER risk_level; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS evaluator_name varchar(100) DEFAULT NULL COMMENT '评阅人姓名' AFTER evaluator_id; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS evaluate_time datetime DEFAULT NULL COMMENT '评阅时间' AFTER evaluator_name; + +-- 9. 添加统计相关字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS participant_count int DEFAULT 0 COMMENT '参与人数' AFTER evaluate_time; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS completed_count int DEFAULT 0 COMMENT '完成人数' AFTER participant_count; +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS duration int DEFAULT NULL COMMENT '答题用时(秒)' AFTER completed_count; + +-- 10. 添加备注字段 +ALTER TABLE prison_questionnaire_record ADD COLUMN IF NOT EXISTS remark varchar(500) DEFAULT NULL COMMENT '备注' AFTER duration; + +-- 11. 添加索引 +ALTER TABLE prison_questionnaire_record ADD INDEX IF NOT EXISTS idx_task_id (task_id); +ALTER TABLE prison_questionnaire_record ADD INDEX IF NOT EXISTS idx_prisoner_area_id (prisoner_area_id); + +-- 验证修改 +SELECT + COLUMN_NAME as '字段名', + DATA_TYPE as '数据类型', + IS_NULLABLE as '允许NULL', + COLUMN_DEFAULT as '默认值', + COLUMN_COMMENT as '注释' +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_SCHEMA = 'xlcp_dev' + AND TABLE_NAME = 'prison_questionnaire_record' +ORDER BY ORDINAL_POSITION; diff --git a/yudao-module-prison/src/main/resources/sql/prison_module.sql b/yudao-module-prison/src/main/resources/sql/prison_module.sql index 5892bf5a7a..0ca5298dec 100644 --- a/yudao-module-prison/src/main/resources/sql/prison_module.sql +++ b/yudao-module-prison/src/main/resources/sql/prison_module.sql @@ -154,7 +154,7 @@ CREATE TABLE IF NOT EXISTS `prison_questionnaire_record` ( `score` int DEFAULT 0 COMMENT '得分', `result` text COMMENT '评估结果', `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-待评估 2-已完成', - `answer_time` datetime NOT NULL COMMENT '答题时间', + `answer_time` datetime DEFAULT NULL COMMENT '答题时间', `creator` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` varchar(64) DEFAULT '' COMMENT '更新者', @@ -169,25 +169,30 @@ CREATE TABLE IF NOT EXISTS `prison_questionnaire_record` ( -- ===================================================== -- 7. 危险评估表 (prison_risk_assessment) -- ===================================================== +-- 更新说明:2026-01-30 添加精细化评估字段 CREATE TABLE IF NOT EXISTS `prison_risk_assessment` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '评估ID', `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', - `assessment_type` tinyint NOT NULL DEFAULT 1 COMMENT '评估类型:1-入监评估 2-定期评估 3-出监评估', - `risk_level` tinyint NOT NULL COMMENT '风险等级:1-高风险 2-中风险 3-低风险', - `risk_score` int DEFAULT 0 COMMENT '风险评分', - `factors` text COMMENT '风险因素,JSON格式', - `measures` text COMMENT '管控措施', + `assessment_type` tinyint NOT NULL DEFAULT 1 COMMENT '评估类型:1-入监评估 2-定期评估 3-专项评估', + `assessment_date` date NOT NULL COMMENT '评估日期', + `violence_score` decimal(10,2) DEFAULT NULL COMMENT '暴力倾向得分', + `escape_score` decimal(10,2) DEFAULT NULL COMMENT '脱逃倾向得分', + `suicide_score` decimal(10,2) DEFAULT NULL COMMENT '自杀倾向得分', + `total_score` decimal(10,2) DEFAULT NULL COMMENT '综合得分', + `risk_level` tinyint NOT NULL COMMENT '风险等级:1-低风险 2-中风险 3-高风险 4-极高风险', + `risk_factors` text COMMENT '风险因素', + `suggestions` text COMMENT '管控建议', `assessor_id` bigint DEFAULT NULL COMMENT '评估人ID', `assessor_name` varchar(50) DEFAULT NULL COMMENT '评估人姓名', - `assessment_date` date NOT NULL COMMENT '评估日期', - `next_date` date DEFAULT NULL COMMENT '下次评估日期', - `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-有效 2-已过期', + `next_assessment_date` date DEFAULT NULL COMMENT '下次评估日期', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-待审核 2-已通过', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', `creator` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`), KEY `idx_prison_risk_assessment_prisoner_id` (`prisoner_id`), diff --git a/yudao-module-prison/src/main/resources/sql/prison_module.sql.bak b/yudao-module-prison/src/main/resources/sql/prison_module.sql.bak new file mode 100644 index 0000000000..d1ba55ed6c --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/prison_module.sql.bak @@ -0,0 +1,609 @@ +-- ===================================================== +-- XL监狱综合管理平台 - 监狱模块数据库脚本 +-- 生成时间: 2026-01-12 +-- ===================================================== + +-- 注意: 执行前请确保已经创建了 prison 模块的基础表 prison_prisoner +-- 此脚本包含 8 个子模块的表结构和菜单权限 + +-- ===================================================== +-- 1. 监区信息表 (prison_area) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_area` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '监区ID', + `name` varchar(100) NOT NULL COMMENT '监区名称', + `code` varchar(50) NOT NULL COMMENT '监区编码', + `type` tinyint NOT NULL DEFAULT 1 COMMENT '监区类型:1-普通监区 2-严管监区 3-医院 4-禁闭室', + `capacity` int NOT NULL DEFAULT 0 COMMENT '容纳人数', + `current_count` int NOT NULL DEFAULT 0 COMMENT '当前人数', + `sort` int NOT NULL DEFAULT 0 COMMENT '排序', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-启用 2-禁用', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_code` (`code`), + KEY `idx_prison_area_code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='监区信息表'; + +-- ===================================================== +-- 2. 监室信息表 (prison_cell) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_cell` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '监室ID', + `area_id` bigint NOT NULL COMMENT '所属监区ID', + `name` varchar(100) NOT NULL COMMENT '监室名称', + `code` varchar(50) NOT NULL COMMENT '监室编码', + `capacity` int NOT NULL DEFAULT 0 COMMENT '床位数量', + `current_count` int NOT NULL DEFAULT 0 COMMENT '当前人数', + `sort` int NOT NULL DEFAULT 0 COMMENT '排序', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-启用 2-禁用', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_code` (`code`), + KEY `idx_prison_cell_area_id` (`area_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='监室信息表'; + +-- ===================================================== +-- 3. 消费订单表 (prison_consumption) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_consumption` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '消费ID', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', + `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', + `order_no` varchar(64) DEFAULT NULL COMMENT '订单号', + `type` tinyint NOT NULL DEFAULT 1 COMMENT '类型:1-购物 2-餐饮 3-医疗 4-通讯 5-其他', + `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额', + `balance` decimal(10,2) NOT NULL COMMENT '账户余额(消费后)', + `trade_time` datetime NOT NULL COMMENT '交易时间', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-成功 2-失败', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_consumption_prisoner_id` (`prisoner_id`), + KEY `idx_prison_consumption_prisoner_no` (`prisoner_no`), + KEY `idx_prison_consumption_order_no` (`order_no`), + KEY `idx_prison_consumption_trade_time` (`trade_time`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='消费订单表'; + +-- ===================================================== +-- 3.1 消费明细表 (prison_consumption_detail) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_consumption_detail` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `consumption_id` bigint NOT NULL COMMENT '消费订单ID', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID(冗余,便于查询)', + `goods_name` varchar(100) NOT NULL COMMENT '商品名称', + `goods_code` varchar(50) DEFAULT NULL COMMENT '商品编码', + `goods_price` decimal(10,2) NOT NULL COMMENT '商品单价', + `goods_count` int NOT NULL COMMENT '商品数量', + `subtotal` decimal(10,2) NOT NULL COMMENT '小计金额', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_consumption_detail_consumption_id` (`consumption_id`), + KEY `idx_consumption_detail_prisoner_id` (`prisoner_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='消费明细表'; + +-- ===================================================== +-- 4. 问卷模板表 (prison_questionnaire) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_questionnaire` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '问卷ID', + `title` varchar(200) NOT NULL COMMENT '问卷标题', + `description` varchar(500) DEFAULT NULL COMMENT '问卷描述', + `type` tinyint NOT NULL DEFAULT 1 COMMENT '问卷类型:1-心理测评 2-风险评估 3-日常调查', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-草稿 2-已发布 3-已停用', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_questionnaire_status` (`status`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷模板表'; + +-- ===================================================== +-- 5. 问卷问题表 (prison_question) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_question` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '问题ID', + `questionnaire_id` bigint NOT NULL COMMENT '问卷ID', + `title` varchar(500) NOT NULL COMMENT '问题标题', + `type` tinyint NOT NULL DEFAULT 1 COMMENT '问题类型:1-单选 2-多选 3-问答 4-评分', + `options` text COMMENT '选项,JSON格式:[{ "label": "选项1", "value": "1" }]', + `score` int DEFAULT 0 COMMENT '分值', + `sort` int NOT NULL DEFAULT 0 COMMENT '排序', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_question_questionnaire_id` (`questionnaire_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷问题表'; + +-- ===================================================== +-- 6. 问卷答题记录表 (prison_questionnaire_record) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_questionnaire_record` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '记录ID', + `questionnaire_id` bigint NOT NULL COMMENT '问卷ID', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', + `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', + `answers` text NOT NULL COMMENT '答案,JSON格式', + `score` int DEFAULT 0 COMMENT '得分', + `result` text COMMENT '评估结果', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-待评估 2-已完成', + `answer_time` datetime NOT NULL COMMENT '答题时间', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_questionnaire_record_questionnaire_id` (`questionnaire_id`), + KEY `idx_prison_questionnaire_record_prisoner_id` (`prisoner_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷答题记录表'; + +-- ===================================================== +-- 7. 危险评估表 (prison_risk_assessment) +-- ===================================================== +-- 更新说明:2026-01-30 添加精细化评估字段 +CREATE TABLE IF NOT EXISTS `prison_risk_assessment` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '评估ID', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', + `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', + `assessment_type` tinyint NOT NULL DEFAULT 1 COMMENT '评估类型:1-入监评估 2-定期评估 3-专项评估', + `assessment_date` date NOT NULL COMMENT '评估日期', + `violence_score` decimal(10,2) DEFAULT NULL COMMENT '暴力倾向得分', + `escape_score` decimal(10,2) DEFAULT NULL COMMENT '脱逃倾向得分', + `suicide_score` decimal(10,2) DEFAULT NULL COMMENT '自杀倾向得分', + `total_score` decimal(10,2) DEFAULT NULL COMMENT '综合得分', + `risk_level` tinyint NOT NULL COMMENT '风险等级:1-低风险 2-中风险 3-高风险 4-极高风险', + `risk_factors` text COMMENT '风险因素', + `suggestions` text COMMENT '管控建议', + `assessor_id` bigint DEFAULT NULL COMMENT '评估人ID', + `assessor_name` varchar(50) DEFAULT NULL COMMENT '评估人姓名', + `next_assessment_date` date DEFAULT NULL COMMENT '下次评估日期', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-待审核 2-已通过', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_risk_assessment_prisoner_id` (`prisoner_id`), + KEY `idx_prison_risk_assessment_assessment_date` (`assessment_date`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='危险评估表'; +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='危险评估表'; + +-- ===================================================== +-- 8. 计分考核表 (prison_score) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_score` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '记录ID', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', + `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', + `year` int NOT NULL COMMENT '考核年份', + `month` int NOT NULL COMMENT '考核月份', + `base_score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '基础分', + `reward_score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '加分', + `penalty_score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '扣分', + `total_score` decimal(10,2) NOT NULL COMMENT '总分', + `level` tinyint DEFAULT NULL COMMENT '考核等级:1-优秀 2-良好 3-合格 4-不合格', + `assessor_id` bigint DEFAULT NULL COMMENT '考核人ID', + `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 '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0 COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_prisoner_year_month` (`prisoner_no`, `year`, `month`), + KEY `idx_prison_score_prisoner_id` (`prisoner_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='计分考核表'; + +-- ===================================================== +-- 菜单权限 SQL +-- 注意: 请将 ${table.parentMenuId} 替换为实际的父菜单ID +-- 监狱管理模块的父菜单ID通常为 5047 (系统管理) 或对应的监狱模块菜单ID +-- ===================================================== + +-- 获取监狱模块父菜单ID (假设为 5047,请根据实际情况调整) +-- SELECT @parentId := 5047; + +-- 1. 监区信息管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('监区信息管理', '', 2, 1, 5047, 'area', '', 'prison/area/index', 0, 'Area'); +SELECT @areaParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监区信息查询', 'prison:area:query', 3, 1, @areaParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监区信息创建', 'prison:area:create', 3, 2, @areaParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监区信息更新', 'prison:area:update', 3, 3, @areaParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监区信息删除', 'prison:area:delete', 3, 4, @areaParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监区信息导出', 'prison:area:export', 3, 5, @areaParentId, '', '', '', 0); + +-- 2. 监室信息管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('监室信息管理', '', 2, 2, 5047, 'cell', '', 'prison/cell/index', 0, 'Cell'); +SELECT @cellParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监室信息查询', 'prison:cell:query', 3, 1, @cellParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监室信息创建', 'prison:cell:create', 3, 2, @cellParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监室信息更新', 'prison:cell:update', 3, 3, @cellParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监室信息删除', 'prison:cell:delete', 3, 4, @cellParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('监室信息导出', 'prison:cell:export', 3, 5, @cellParentId, '', '', '', 0); + +-- 3. 消费记录管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('消费记录管理', '', 2, 3, 5047, 'consumption', '', 'prison/consumption/index', 0, 'Consumption'); +SELECT @consumptionParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('消费记录查询', 'prison:consumption:query', 3, 1, @consumptionParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('消费记录创建', 'prison:consumption:create', 3, 2, @consumptionParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('消费记录更新', 'prison:consumption:update', 3, 3, @consumptionParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('消费记录删除', 'prison:consumption:delete', 3, 4, @consumptionParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('消费记录导出', 'prison:consumption:export', 3, 5, @consumptionParentId, '', '', '', 0); + +-- 4. 问卷模板管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('问卷模板管理', '', 2, 4, 5047, 'questionnaire', '', 'prison/questionnaire/index', 0, 'Questionnaire'); +SELECT @questionnaireParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷模板查询', 'prison:questionnaire:query', 3, 1, @questionnaireParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷模板创建', 'prison:questionnaire:create', 3, 2, @questionnaireParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷模板更新', 'prison:questionnaire:update', 3, 3, @questionnaireParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷模板删除', 'prison:questionnaire:delete', 3, 4, @questionnaireParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷模板导出', 'prison:questionnaire:export', 3, 5, @questionnaireParentId, '', '', '', 0); + +-- 5. 问卷问题管理菜单 (已移除独立页面,问题管理集成在问卷模板页面内) +-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +-- VALUES ('问卷问题管理', '', 2, 5, 5047, 'question', '', 'prison/question/index', 0, 'Question'); +-- SELECT @questionParentId := LAST_INSERT_ID(); +-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +-- VALUES ('问卷问题查询', 'prison:question:query', 3, 1, @questionParentId, '', '', '', 0); +-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +-- VALUES ('问卷问题创建', 'prison:question:create', 3, 2, @questionParentId, '', '', '', 0); +-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +-- VALUES ('问卷问题更新', 'prison:question:update', 3, 3, @questionParentId, '', '', '', 0); +-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +-- VALUES ('问卷问题删除', 'prison:question:delete', 3, 4, @questionParentId, '', '', '', 0); +-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +-- VALUES ('问卷问题导出', 'prison:question:export', 3, 5, @questionParentId, '', '', '', 0); + +-- 5. 问卷答题记录管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('问卷答题记录管理', '', 2, 5, 5047, 'questionnaire-record', '', 'prison/questionnairerecord/index', 0, 'QuestionnaireRecord'); +SELECT @recordParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷答题记录查询', 'prison:questionnaire-record:query', 3, 1, @recordParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷答题记录创建', 'prison:questionnaire-record:create', 3, 2, @recordParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷答题记录更新', 'prison:questionnaire-record:update', 3, 3, @recordParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷答题记录删除', 'prison:questionnaire-record:delete', 3, 4, @recordParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('问卷答题记录导出', 'prison:questionnaire-record:export', 3, 5, @recordParentId, '', '', '', 0); + +-- 6. 危险评估管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('危险评估管理', '', 2, 6, 5047, 'risk-assessment', '', 'prison/riskassessment/index', 0, 'RiskAssessment'); +SELECT @riskParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('危险评估查询', 'prison:risk-assessment:query', 3, 1, @riskParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('危险评估创建', 'prison:risk-assessment:create', 3, 2, @riskParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('危险评估更新', 'prison:risk-assessment:update', 3, 3, @riskParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('危险评估删除', 'prison:risk-assessment:delete', 3, 4, @riskParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('危险评估导出', 'prison:risk-assessment:export', 3, 5, @riskParentId, '', '', '', 0); + +-- 8. 计分考核管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('计分考核管理', '', 2, 8, 5047, 'score', '', 'prison/score/index', 0, 'Score'); +SELECT @scoreParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('计分考核查询', 'prison:score:query', 3, 1, @scoreParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('计分考核创建', 'prison:score:create', 3, 2, @scoreParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('计分考核更新', 'prison:score:update', 3, 3, @scoreParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('计分考核删除', 'prison:score:delete', 3, 4, @scoreParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('计分考核导出', 'prison:score:export', 3, 5, @scoreParentId, '', '', '', 0); + +-- 9. 狱情收集管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('狱情收集管理', '', 2, 9, 5047, 'situation', '', 'prison/situation/index', 0, 'Situation'); +SELECT @situationParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('狱情收集查询', 'prison:situation:query', 3, 1, @situationParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('狱情收集创建', 'prison:situation:create', 3, 2, @situationParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('狱情收集更新', 'prison:situation:update', 3, 3, @situationParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('狱情收集删除', 'prison:situation:delete', 3, 4, @situationParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('狱情收集导出', 'prison:situation:export', 3, 5, @situationParentId, '', '', '', 0); + +-- ===================================================== +-- 10. 数据结构迁移 SQL (2026-01-20) +-- ===================================================== +-- 补充 prison_prisoner 表缺失字段 +-- 执行此迁移前请备份数据库 +ALTER TABLE `prison_prisoner` + ADD COLUMN IF NOT EXISTS `release_type` tinyint DEFAULT 0 COMMENT '释放类型:0-未知 1-刑满释放 2-假释 3-保外就医 4-减刑 5-暂予监外执行 6-特赦 7-死亡 8-其他', + ADD COLUMN IF NOT EXISTS `release_reason` varchar(500) DEFAULT NULL COMMENT '释放原因', + ADD COLUMN IF NOT EXISTS `photo` varchar(512) DEFAULT NULL COMMENT '照片URL', + ADD COLUMN IF NOT EXISTS `sub_area_id` bigint DEFAULT NULL COMMENT '分区ID', + ADD COLUMN IF NOT EXISTS `marital_status` tinyint DEFAULT NULL COMMENT '婚姻状态:1-未婚 2-已婚 3-离异 4-丧偶', + ADD COLUMN IF NOT EXISTS `crime_type` varchar(100) DEFAULT NULL COMMENT '罪名类型', + ADD COLUMN IF NOT EXISTS `sentence` varchar(100) DEFAULT NULL COMMENT '刑期'; + +-- ===================================================== +-- 10. 字典数据 SQL +-- ===================================================== +-- 监室状态字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('监室状态', 'prison_cell_status', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_cell_status', 1, '启用', '1', 'success', '', 0, 'admin', NOW()), +('prison_cell_status', 2, '禁用', '2', 'danger', '', 0, 'admin', NOW()); + +-- ===================================================== +-- 11. 狱情收集表 (prison_situation) - 新增 2026-01-16 +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_situation` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '狱情ID', + `title` varchar(200) NOT NULL COMMENT '标题', + `content` text COMMENT '详情内容', + `category` tinyint NOT NULL DEFAULT 1 COMMENT '分类:1-监管安全 2-教育改造 3-生活卫生 4-生产安全 5-狱内案件 6-其他', + `level` tinyint NOT NULL DEFAULT 1 COMMENT '等级:1-一般 2-重要 3-紧急', + `source` tinyint NOT NULL DEFAULT 1 COMMENT '来源:1-民警报告 2-监控系统 3-举报 4-罪犯自首 5-其他', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-待处理 2-处理中 3-已处理', + `area_id` bigint DEFAULT NULL COMMENT '关联监区ID', + `cell_id` bigint DEFAULT NULL COMMENT '关联监室ID', + `reporter` varchar(50) DEFAULT NULL COMMENT '报告人', + `handler` varchar(50) DEFAULT NULL COMMENT '处理人', + `handle_time` datetime DEFAULT NULL COMMENT '处理时间', + `handle_result` text COMMENT '处理结果', + `occur_time` datetime DEFAULT NULL COMMENT '发生时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_situation_status` (`status`), + KEY `idx_prison_situation_category` (`category`), + KEY `idx_prison_situation_level` (`level`), + KEY `idx_prison_situation_area_id` (`area_id`), + KEY `idx_prison_situation_cell_id` (`cell_id`), + KEY `idx_prison_situation_occur_time` (`occur_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='狱情收集表'; + +-- ===================================================== +-- 12. 预警管理表 (prison_warning) - 新增 2026-01-16 +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_warning` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '预警ID', + `title` varchar(200) NOT NULL COMMENT '预警标题', + `content` text COMMENT '预警内容', + `type` tinyint NOT NULL DEFAULT 1 COMMENT '预警类型:1-安全预警 2-监管预警 3-改造预警 4-生产预警 5-生活卫生预警 6-其他', + `level` tinyint NOT NULL DEFAULT 1 COMMENT '预警等级:1-一般 2-重要 3-紧急 4-严重', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '预警状态:1-待核实 2-已核实 3-已处置 4-已解除', + `source` tinyint NOT NULL DEFAULT 1 COMMENT '预警来源:1-民警报告 2-监控系统 3-举报 4-罪犯自首 5-智能分析 6-其他', + `situation_id` bigint DEFAULT NULL COMMENT '关联狱情ID', + `area_id` bigint DEFAULT NULL COMMENT '关联监区ID', + `cell_id` bigint DEFAULT NULL COMMENT '关联监室ID', + `alert_time` datetime DEFAULT NULL COMMENT '预警时间', + `verify_time` datetime DEFAULT NULL COMMENT '核实时间', + `verifier` varchar(50) DEFAULT NULL COMMENT '核实人', + `verify_result` text COMMENT '核实结果', + `handle_time` datetime DEFAULT NULL COMMENT '处置时间', + `handler` varchar(50) DEFAULT NULL COMMENT '处置人', + `handle_method` varchar(200) DEFAULT NULL COMMENT '处置方式', + `handle_result` text COMMENT '处置结果', + `release_time` datetime DEFAULT NULL COMMENT '解除时间', + `releaser` varchar(50) DEFAULT NULL COMMENT '解除人', + `release_reason` text COMMENT '解除原因', + `occur_time` datetime DEFAULT NULL COMMENT '发生时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_warning_status` (`status`), + KEY `idx_prison_warning_level` (`level`), + KEY `idx_prison_warning_type` (`type`), + KEY `idx_prison_warning_situation_id` (`situation_id`), + KEY `idx_prison_warning_area_id` (`area_id`), + KEY `idx_prison_warning_cell_id` (`cell_id`), + KEY `idx_prison_warning_alert_time` (`alert_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预警管理表'; + +-- ===================================================== +-- 13. 风险评估表 (prison_risk) - 新增 2026-01-16 +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_risk` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '评估ID', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', + `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', + `prisoner_name` varchar(50) DEFAULT NULL COMMENT '罪犯姓名', + `assessment_type` tinyint NOT NULL DEFAULT 1 COMMENT '评估类型:1-入监评估 2-定期评估 3-专项评估 4-出监评估', + `assessment_date` date NOT NULL COMMENT '评估日期', + `overall_score` decimal(5,2) DEFAULT NULL COMMENT '综合风险得分', + `risk_level` tinyint DEFAULT NULL COMMENT '风险等级:1-低风险 2-中风险 3-高风险 4-极高风险', + `mental_state` varchar(500) DEFAULT NULL COMMENT '精神状态评估', + `escape_risk` varchar(500) DEFAULT NULL COMMENT '脱逃风险评估', + `violence_risk` varchar(500) DEFAULT NULL COMMENT '暴力倾向评估', + `revolt_risk` varchar(500) DEFAULT NULL COMMENT '抗改风险评估', + `self_harm_risk` varchar(500) DEFAULT NULL COMMENT '自杀自伤风险评估', + `recommendation` text COMMENT '评估建议', + `assessor` varchar(50) DEFAULT NULL COMMENT '评估人', + `assess_method` tinyint DEFAULT NULL COMMENT '评估方式:1-问卷评估 2-量表评估 3-综合评估', + `item_scores` text COMMENT '评估项目得分JSON', + `conclusion` text COMMENT '评估结论', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `creator` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` varchar(64) DEFAULT '' COMMENT '更新者', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`), + KEY `idx_prison_risk_prisoner_id` (`prisoner_id`), + KEY `idx_prison_risk_prisoner_no` (`prisoner_no`), + KEY `idx_prison_risk_assessment_type` (`assessment_type`), + KEY `idx_prison_risk_assessment_date` (`assessment_date`), + KEY `idx_prison_risk_risk_level` (`risk_level`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='风险评估表'; + +-- ===================================================== +-- 14. 字典数据 SQL (2026-01-16) +-- ===================================================== + +-- 狱情分类字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('狱情分类', 'prison_situation_category', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_situation_category', 1, '监管安全', '1', 'danger', '', 0, 'admin', NOW()), +('prison_situation_category', 2, '教育改造', '2', 'warning', '', 0, 'admin', NOW()), +('prison_situation_category', 3, '生活卫生', '3', 'success', '', 0, 'admin', NOW()), +('prison_situation_category', 4, '生产安全', '4', 'info', '', 0, 'admin', NOW()), +('prison_situation_category', 5, '狱内案件', '5', 'danger', '', 0, 'admin', NOW()), +('prison_situation_category', 6, '其他', '6', '', '', 0, 'admin', NOW()); + +-- 狱情等级字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('狱情等级', 'prison_situation_level', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_situation_level', 1, '一般', '1', 'success', '', 0, 'admin', NOW()), +('prison_situation_level', 2, '重要', '2', 'warning', '', 0, 'admin', NOW()), +('prison_situation_level', 3, '紧急', '3', 'danger', '', 0, 'admin', NOW()); + +-- 狱情来源字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('狱情来源', 'prison_situation_source', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_situation_source', 1, '民警报告', '1', '', '', 0, 'admin', NOW()), +('prison_situation_source', 2, '监控系统', '2', '', '', 0, 'admin', NOW()), +('prison_situation_source', 3, '举报', '3', '', '', 0, 'admin', NOW()), +('prison_situation_source', 4, '罪犯自首', '4', '', '', 0, 'admin', NOW()), +('prison_situation_source', 5, '其他', '5', '', '', 0, 'admin', NOW()); + +-- 狱情状态字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('狱情状态', 'prison_situation_status', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_situation_status', 1, '待处理', '1', 'warning', '', 0, 'admin', NOW()), +('prison_situation_status', 2, '处理中', '2', 'info', '', 0, 'admin', NOW()), +('prison_situation_status', 3, '已处理', '3', 'success', '', 0, 'admin', NOW()); + +-- 预警类型字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('预警类型', 'prison_warning_type', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_warning_type', 1, '安全预警', '1', 'danger', '', 0, 'admin', NOW()), +('prison_warning_type', 2, '监管预警', '2', 'warning', '', 0, 'admin', NOW()), +('prison_warning_type', 3, '改造预警', '3', 'info', '', 0, 'admin', NOW()), +('prison_warning_type', 4, '生产预警', '4', '', '', 0, 'admin', NOW()), +('prison_warning_type', 5, '生活卫生预警', '5', 'success', '', 0, 'admin', NOW()), +('prison_warning_type', 6, '其他', '6', '', '', 0, 'admin', NOW()); + +-- 预警等级字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('预警等级', 'prison_warning_level', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_warning_level', 1, '一般', '1', 'success', '', 0, 'admin', NOW()), +('prison_warning_level', 2, '重要', '2', 'warning', '', 0, 'admin', NOW()), +('prison_warning_level', 3, '紧急', '3', 'danger', '', 0, 'admin', NOW()), +('prison_warning_level', 4, '严重', '4', 'danger', '', 0, 'admin', NOW()); + +-- 预警状态字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('预警状态', 'prison_warning_status', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_warning_status', 1, '待核实', '1', 'warning', '', 0, 'admin', NOW()), +('prison_warning_status', 2, '已核实', '2', 'info', '', 0, 'admin', NOW()), +('prison_warning_status', 3, '已处置', '3', 'success', '', 0, 'admin', NOW()), +('prison_warning_status', 4, '已解除', '4', '', '', 0, 'admin', NOW()); + +-- 预警来源字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('预警来源', 'prison_warning_source', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_warning_source', 1, '民警报告', '1', '', '', 0, 'admin', NOW()), +('prison_warning_source', 2, '监控系统', '2', '', '', 0, 'admin', NOW()), +('prison_warning_source', 3, '举报', '3', '', '', 0, 'admin', NOW()), +('prison_warning_source', 4, '罪犯自首', '4', '', '', 0, 'admin', NOW()), +('prison_warning_source', 5, '智能分析', '5', '', '', 0, 'admin', NOW()), +('prison_warning_source', 6, '其他', '6', '', '', 0, 'admin', NOW()); + +-- 风险评估类型字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('风险评估类型', 'prison_risk_assessment_type', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_risk_assessment_type', 1, '入监评估', '1', 'info', '', 0, 'admin', NOW()), +('prison_risk_assessment_type', 2, '定期评估', '2', 'success', '', 0, 'admin', NOW()), +('prison_risk_assessment_type', 3, '专项评估', '3', 'warning', '', 0, 'admin', NOW()), +('prison_risk_assessment_type', 4, '出监评估', '4', '', '', 0, 'admin', NOW()); + +-- 风险等级字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('风险等级', 'prison_risk_level', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_risk_level', 1, '低风险', '1', 'success', '', 0, 'admin', NOW()), +('prison_risk_level', 2, '中风险', '2', 'warning', '', 0, 'admin', NOW()), +('prison_risk_level', 3, '高风险', '3', 'danger', '', 0, 'admin', NOW()), +('prison_risk_level', 4, '极高风险', '4', 'danger', '', 0, 'admin', NOW()); + +-- 评估方式字典 +INSERT INTO system_dict_type (name, type, status, creator, create_time) VALUES ('评估方式', 'prison_risk_assess_method', 0, 'admin', NOW()); +INSERT INTO system_dict_data (dict_type, sort, label, value, color_type, css_class, status, creator, create_time) VALUES +('prison_risk_assess_method', 1, '问卷评估', '1', '', '', 0, 'admin', NOW()), +('prison_risk_assess_method', 2, '量表评估', '2', '', '', 0, 'admin', NOW()), +('prison_risk_assess_method', 3, '综合评估', '3', '', '', 0, 'admin', NOW()); diff --git a/yudao-module-prison/src/main/resources/sql/upgrade_risk_assessment_fields_20260130.sql b/yudao-module-prison/src/main/resources/sql/upgrade_risk_assessment_fields_20260130.sql new file mode 100644 index 0000000000..d056c8d656 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/upgrade_risk_assessment_fields_20260130.sql @@ -0,0 +1,56 @@ +-- ===================================================== +-- XL监狱综合管理平台 - 危险评估表结构升级 +-- 生成时间: 2026-01-30 +-- 升级说明: 添加危险评估详细字段以支持精细化评估 +-- ===================================================== + +-- 危险评估表 (prison_risk_assessment) 添加新字段 +-- 如果字段已存在会忽略错误 +-- ===================================================== + +-- 1. 添加暴力倾向得分字段 +ALTER TABLE `prison_risk_assessment` +ADD COLUMN IF NOT EXISTS `violence_score` decimal(10,2) DEFAULT NULL COMMENT '暴力倾向得分' AFTER `suicide_score`; + +-- 2. 添加脱逃倾向得分字段 +ALTER TABLE `prison_risk_assessment` +ADD COLUMN IF NOT EXISTS `escape_score` decimal(10,2) DEFAULT NULL COMMENT '脱逃倾向得分' AFTER `violence_score`; + +-- 3. 添加自杀倾向得分字段 +ALTER TABLE `prison_risk_assessment` +ADD COLUMN IF NOT EXISTS `suicide_score` decimal(10,2) DEFAULT NULL COMMENT '自杀倾向得分' AFTER `escape_score`; + +-- 4. 添加综合得分字段 +ALTER TABLE `prison_risk_assessment` +ADD COLUMN IF NOT EXISTS `total_score` decimal(10,2) DEFAULT NULL COMMENT '综合得分' AFTER `suicide_score`; + +-- 5. 添加风险因素字段(改为支持更详细的风险因素描述) +ALTER TABLE `prison_risk_assessment` +ADD COLUMN IF NOT EXISTS `risk_factors` text COMMENT '风险因素' AFTER `total_score`; + +-- 6. 添加管控建议字段 +ALTER TABLE `prison_risk_assessment` +ADD COLUMN IF NOT EXISTS `suggestions` text COMMENT '管控建议' AFTER `risk_factors`; + +-- 7. 添加备注字段 +ALTER TABLE `prison_risk_assessment` +ADD COLUMN IF NOT EXISTS `remark` varchar(500) DEFAULT NULL COMMENT '备注' AFTER `suggestions`; + +-- ===================================================== +-- 注意:对于MySQL 5.7,需要移除 IF NOT EXISTS 条件手动执行 +-- 或者使用以下兼容性写法(忽略错误): +-- ===================================================== + +-- ALTER TABLE `prison_risk_assessment` ADD COLUMN `violence_score` decimal(10,2) DEFAULT NULL COMMENT '暴力倾向得分'; +-- ALTER TABLE `prison_risk_assessment` ADD COLUMN `escape_score` decimal(10,2) DEFAULT NULL COMMENT '脱逃倾向得分'; +-- ALTER TABLE `prison_risk_assessment` ADD COLUMN `suicide_score` decimal(10,2) DEFAULT NULL COMMENT '自杀倾向得分'; +-- ALTER TABLE `prison_risk_assessment` ADD COLUMN `total_score` decimal(10,2) DEFAULT NULL COMMENT '综合得分'; +-- ALTER TABLE `prison_risk_assessment` ADD COLUMN `risk_factors` text COMMENT '风险因素'; +-- ALTER TABLE `prison_risk_assessment` ADD COLUMN `suggestions` text COMMENT '管控建议'; +-- ALTER TABLE `prison_risk_assessment` ADD COLUMN `remark` varchar(500) DEFAULT NULL COMMENT '备注'; + +-- ===================================================== +-- 执行方式: +-- 1. MySQL 8.0+: 直接执行 +-- 2. MySQL 5.7: 使用注释中的兼容性写法 +-- ===================================================== diff --git a/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImplRestartTest.java b/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImplRestartTest.java new file mode 100644 index 0000000000..3abc642240 --- /dev/null +++ b/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/questionnaire_task/QuestionnaireTaskServiceImplRestartTest.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.prison.service.questionnaire_task; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO; +import cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task.QuestionnaireTaskMapper; +import cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_record.QuestionnaireRecordMapper; +import cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +/** + * Bug #7448 修复验证 - 问卷任务启用后目标人数被清空 + */ +@ExtendWith(MockitoExtension.class) +class QuestionnaireTaskServiceImplRestartTest { + + @InjectMocks + private QuestionnaireTaskServiceImpl questionnaireTaskService; + + @Mock + private QuestionnaireTaskMapper questionnaireTaskMapper; + + @Mock + private QuestionnaireRecordMapper questionnaireRecordMapper; + + @Mock + private QuestionnaireMapper questionnaireMapper; + + @Mock + private PrisonerMapper prisonerMapper; + + @Mock + private AreaMapper areaMapper; + + private QuestionnaireTaskDO createTestTask(Integer status, Integer totalCount, Integer completedCount) { + QuestionnaireTaskDO task = new QuestionnaireTaskDO(); + task.setId(1L); + task.setTaskName("测试任务"); + task.setQuestionnaireId(100L); + task.setStatus(status); + task.setTotalCount(totalCount); + task.setCompletedCount(completedCount); + task.setPendingCount(totalCount - completedCount); + task.setCompletionRate(totalCount > 0 ? BigDecimal.valueOf(completedCount * 100.0 / totalCount) : BigDecimal.ZERO); + return task; + } + + @Test + void testRestartTask_Success_CallsUpdateStatistics() { + Long taskId = 1L; + QuestionnaireTaskDO task = createTestTask(3, 50, 30); + when(questionnaireTaskMapper.selectById(taskId)).thenReturn(task); + + questionnaireTaskService.restartTask(taskId); + + verify(questionnaireTaskMapper).updateById(any()); + verify(questionnaireTaskMapper).updateTaskStatistics(taskId); + } + + @Test + void testRestartTask_ThrowsException_WhenTaskNotExists() { + Long taskId = 999L; + when(questionnaireTaskMapper.selectById(taskId)).thenReturn(null); + + ServiceException exception = assertThrows(ServiceException.class, + () -> questionnaireTaskService.restartTask(taskId)); + + assertEquals(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS, exception.getCode()); + verify(questionnaireTaskMapper, never()).updateTaskStatistics(any()); + } + + @Test + void testRestartTask_ThrowsException_WhenStatusNotEnded() { + Long taskId = 3L; + QuestionnaireTaskDO task = createTestTask(2, 50, 30); + when(questionnaireTaskMapper.selectById(taskId)).thenReturn(task); + + ServiceException exception = assertThrows(ServiceException.class, + () -> questionnaireTaskService.restartTask(taskId)); + + assertEquals(ErrorCodeConstants.QUESTIONNAIRE_TASK_CANNOT_RESTART, exception.getCode()); + verify(questionnaireTaskMapper, never()).updateTaskStatistics(any()); + } +} diff --git a/yudao-module-temp b/yudao-module-temp deleted file mode 160000 index bf6875adf6..0000000000 --- a/yudao-module-temp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf6875adf6a99d90ca95f84a6045aea1cc8779c8 diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index b2e1247ba1..87739d4b96 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -130,11 +130,11 @@ - - org.springframework.boot - spring-boot-devtools - true - + + + + + diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 5a1fe2e78f..d28ab93340 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -290,9 +290,9 @@ justauth: llm: # 本地OneAPI服务 local: - base-url: ${LLM_BASE_URL:https://oneapi.gongjulian.cn/v1} # OneAPI服务地址 - api-key: ${LLM_API_KEY:sk-lB2Fc9ssY5UuwmiV5dD441F997364d29Be547e008dF5Cf41} # API密钥,建议通过环境变量配置 - model: ${LLM_MODEL:minimaxai/minimax-m2.1} # 使用的模型(deepseek-ai/deepseek-v3.2 暂时不可用) + base-url: ${LLM_BASE_URL:http://192.168.10.150:2100/v1} # OneAPI服务地址 + api-key: ${LLM_API_KEY:} # API密钥,建议通过环境变量配置 + model: ${LLM_MODEL:qwen3} # 使用的模型(deepseek-ai/deepseek-v3.2 暂时不可用) timeout-seconds: ${LLM_TIMEOUT:120} # 请求超时时间 # Claude(可选,需要时取消注释) # claude: -- 2.47.2 From 9ceed8408acb1b524398b4073ecc2d2058766f4d Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Wed, 4 Feb 2026 19:21:50 +0800 Subject: [PATCH 09/12] =?UTF-8?q?feat(backend):=20=E9=97=AE=E5=8D=B7?= =?UTF-8?q?=E8=B0=83=E6=9F=A5=E8=AE=B0=E5=BD=95=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化问卷记录控制器,添加分页查询和状态筛选 - 扩展问卷记录响应VO,增加完成时间字段 - 完善问卷记录服务实现,添加状态更新逻辑 --- .../PrisonQuestionnaireRecordController.java | 40 ++++++++++++++++++- .../vo/QuestionnaireRecordRespVO.java | 4 ++ .../QuestionnaireRecordServiceImpl.java | 3 ++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java index 918bbe1c1f..c3c1092536 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java @@ -39,6 +39,9 @@ public class PrisonQuestionnaireRecordController { @Resource private QuestionnaireRecordService questionnaireRecordService; + @Resource + private cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task.QuestionnaireTaskMapper questionnaireTaskMapper; + // ==================== 基础 CRUD ==================== @PostMapping("/create") @@ -87,7 +90,42 @@ public class PrisonQuestionnaireRecordController { @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:query')") public CommonResult> getQuestionnaireRecordPage(@Valid QuestionnaireRecordPageReqVO pageReqVO) { PageResult pageResult = questionnaireRecordService.getQuestionnaireRecordPage(pageReqVO); - return success(QuestionnaireRecordConvert.INSTANCE.convertPage(pageResult)); + PageResult voPageResult = QuestionnaireRecordConvert.INSTANCE.convertPage(pageResult); + // 填充任务备注 + fillTaskRemark(voPageResult.getList()); + return success(voPageResult); + } + + /** + * 填充任务备注 + */ + private void fillTaskRemark(List records) { + if (cn.hutool.core.collection.CollUtil.isEmpty(records)) { + return; + } + // 收集所有 taskId + Set taskIds = records.stream() + .map(QuestionnaireRecordRespVO::getTaskId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + if (cn.hutool.core.collection.CollUtil.isEmpty(taskIds)) { + return; + } + // 批量查询任务备注 + List tasks = questionnaireTaskMapper.selectList( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getId, taskIds)); + // 构建 taskId -> remark 映射 + Map taskRemarkMap = tasks.stream() + .collect(Collectors.toMap( + cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getId, + cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getRemark, + (v1, v2) -> v1)); + // 填充备注到 VO + for (QuestionnaireRecordRespVO record : records) { + String remark = taskRemarkMap.get(record.getTaskId()); + record.setTaskRemark(remark); + } } @GetMapping("/export-excel") diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java index c8cd61e747..87ec5014a3 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java @@ -132,6 +132,10 @@ public class QuestionnaireRecordRespVO { @Schema(description = "备注") private String remark; + @Schema(description = "任务备注", example = "一月度常规心理测评") + @ExcelProperty("任务备注") + private String taskRemark; + // ==================== 通用字段 ==================== @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java index 22068f45d5..6db5266d82 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java @@ -49,6 +49,9 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic @Resource private AnswerService answerService; + @Resource + private cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task.QuestionnaireTaskMapper questionnaireTaskMapper; + @Override public Long createQuestionnaireRecord(QuestionnaireRecordSaveReqVO createReqVO) { // 插入 -- 2.47.2 From 0825da5f7f96706d5cb967c3b698b1f023c34f37 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Fri, 6 Mar 2026 10:29:51 +0800 Subject: [PATCH 10/12] =?UTF-8?q?fix(prison):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=AF=84=E4=BC=B0=E6=8A=A5=E5=91=8A=E6=97=A5=E6=9C=9F=E5=92=8C?= =?UTF-8?q?=E6=98=A0=E5=B0=84=E6=9E=84=E5=BB=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../vo/EvaluationReportPageReqVO.java | 6 +- .../vo/EvaluationReportRespVO.java | 5 +- .../vo/EvaluationReportSaveReqVO.java | 4 +- .../PrisonQuestionnaireRecordController.java | 24 +++-- .../vo/QuestionnaireRecordRespVO.java | 4 + .../quickcomment/QuickCommentController.java | 8 +- .../prison/service/cell/CellServiceImpl.java | 10 +- .../impl/PrisonDashboardServiceImpl.java | 98 +++++++++++++------ .../EvaluationReportServiceImpl.java | 7 ++ 10 files changed, 116 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 23fc194d35..b433ca7c08 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,5 @@ application-my.yaml # Generated codegen files /codegen/ + +.omc/ diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportPageReqVO.java index 2fbe0b23d5..950bc7391b 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportPageReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportPageReqVO.java @@ -6,8 +6,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import cn.iocoder.yudao.framework.common.pojo.PageParam; import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; +import java.time.LocalDate; import java.time.LocalDateTime; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @Schema(description = "管理后台 - 评估报告分页 Request VO") @@ -45,8 +47,8 @@ public class EvaluationReportPageReqVO extends PageParam { private Long areaId; @Schema(description = "评估日期") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] evaluationDate; + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) + private LocalDate[] evaluationDate; @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportRespVO.java index 156a4599df..c8f51e0754 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportRespVO.java @@ -6,6 +6,7 @@ import lombok.*; import java.util.*; import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; +import java.time.LocalDate; import java.time.LocalDateTime; import cn.idev.excel.annotation.*; @@ -52,8 +53,8 @@ public class EvaluationReportRespVO { @Schema(description = "评估日期") @ExcelProperty("评估日期") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime evaluationDate; + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate evaluationDate; @Schema(description = "评估人员ID") private Long evaluatorId; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportSaveReqVO.java index 72dcb8ee2d..e469e8b3a1 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportSaveReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/vo/EvaluationReportSaveReqVO.java @@ -5,7 +5,7 @@ import lombok.*; import java.util.*; import jakarta.validation.constraints.*; import java.math.BigDecimal; -import java.time.LocalDateTime; +import java.time.LocalDate; @Schema(description = "管理后台 - 评估报告新增/修改 Request VO") @Data @@ -41,7 +41,7 @@ public class EvaluationReportSaveReqVO { @Schema(description = "评估日期", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "评估日期不能为空") - private LocalDateTime evaluationDate; + private LocalDate evaluationDate; @Schema(description = "评估人员ID") private Long evaluatorId; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java index c3c1092536..5dda436c21 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java @@ -104,10 +104,12 @@ public class PrisonQuestionnaireRecordController { return; } // 收集所有 taskId - Set taskIds = records.stream() - .map(QuestionnaireRecordRespVO::getTaskId) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); + Set taskIds = new HashSet<>(); + for (QuestionnaireRecordRespVO record : records) { + if (record != null && record.getTaskId() != null) { + taskIds.add(record.getTaskId()); + } + } if (cn.hutool.core.collection.CollUtil.isEmpty(taskIds)) { return; } @@ -115,12 +117,14 @@ public class PrisonQuestionnaireRecordController { List tasks = questionnaireTaskMapper.selectList( new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() .in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getId, taskIds)); - // 构建 taskId -> remark 映射 - Map taskRemarkMap = tasks.stream() - .collect(Collectors.toMap( - cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getId, - cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getRemark, - (v1, v2) -> v1)); + // 构建 taskId -> remark 映射,避免 Collectors.toMap 在脏数据场景下抛出 NPE + Map taskRemarkMap = new HashMap<>(); + for (cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO task : tasks) { + if (task == null || task.getId() == null) { + continue; + } + taskRemarkMap.putIfAbsent(task.getId(), task.getRemark() == null ? "" : task.getRemark()); + } // 填充备注到 VO for (QuestionnaireRecordRespVO record : records) { String remark = taskRemarkMap.get(record.getTaskId()); diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java index 87ec5014a3..e708063d6b 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java @@ -29,6 +29,10 @@ public class QuestionnaireRecordRespVO { @ExcelProperty("问卷ID") private Long questionnaireId; + @Schema(description = "任务ID", example = "1001") + @ExcelProperty("任务ID") + private Long taskId; + @Schema(description = "问卷名称") @ExcelProperty("问卷名称") private String questionnaireName; diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java index 473e91fde2..c0110d0ba5 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java @@ -110,8 +110,12 @@ public class QuickCommentController { new LambdaQueryWrapper() .in(CommentCategoryDO::getId, categoryIds) ); - categoryNameMap = categories.stream() - .collect(Collectors.toMap(CommentCategoryDO::getId, CommentCategoryDO::getName)); + for (CommentCategoryDO category : categories) { + if (category == null || category.getId() == null) { + continue; + } + categoryNameMap.putIfAbsent(category.getId(), category.getName() == null ? "" : category.getName()); + } } // 填充分类名称 diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/cell/CellServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/cell/CellServiceImpl.java index db80b9a08e..2adb2fb0a1 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/cell/CellServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/cell/CellServiceImpl.java @@ -108,8 +108,14 @@ public class CellServiceImpl implements CellService { if (CollUtil.isNotEmpty(areaIds)) { List areas = areaMapper.selectList(new LambdaQueryWrapperX() .inIfPresent(AreaDO::getId, new ArrayList<>(areaIds))); - areaMap = areas.stream() - .collect(Collectors.toMap(AreaDO::getId, Function.identity())); + Map tempAreaMap = new HashMap<>(); + for (AreaDO area : areas) { + if (area == null || area.getId() == null) { + continue; + } + tempAreaMap.putIfAbsent(area.getId(), area); + } + areaMap = tempAreaMap; } else { areaMap = new HashMap<>(); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java index 369e2753ce..31ad650aca 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/dashboard/impl/PrisonDashboardServiceImpl.java @@ -33,8 +33,10 @@ import java.time.Period; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -627,12 +629,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .orderByDesc(EvaluationReportDO::getEvaluationDate)); // 按罪犯ID分组,取最新的评估记录 - Map latestReportMap = allReports.stream() - .collect(Collectors.toMap( - EvaluationReportDO::getPrisonerId, - r -> r, - (existing, replacement) -> existing // 保留第一个(最新的) - )); + Map latestReportMap = new HashMap<>(); + for (EvaluationReportDO report : allReports) { + if (report == null || report.getPrisonerId() == null) { + continue; + } + latestReportMap.putIfAbsent(report.getPrisonerId(), report); // 保留第一条(最新的) + } List latestReports = new ArrayList<>(latestReportMap.values()); // 统计各风险等级人数 @@ -662,14 +665,21 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .toList(); // 按罪犯ID分组,取本月第一次评估 - Map thisMonthNewMap = thisMonthReports.stream() - .filter(r -> !latestReportMap.containsKey(r.getPrisonerId()) || - latestReportMap.get(r.getPrisonerId()).getEvaluationDate().toLocalDate().isAfter(firstDayOfMonth.minusDays(1))) - .collect(Collectors.toMap( - EvaluationReportDO::getPrisonerId, - r -> r, - (existing, replacement) -> existing - )); + Map thisMonthNewMap = new HashMap<>(); + for (EvaluationReportDO report : thisMonthReports) { + if (report == null || report.getPrisonerId() == null) { + continue; + } + EvaluationReportDO latestReport = latestReportMap.get(report.getPrisonerId()); + if (latestReport == null) { + thisMonthNewMap.putIfAbsent(report.getPrisonerId(), report); + continue; + } + if (latestReport.getEvaluationDate() != null + && latestReport.getEvaluationDate().toLocalDate().isAfter(firstDayOfMonth.minusDays(1))) { + thisMonthNewMap.putIfAbsent(report.getPrisonerId(), report); + } + } int monthlyNewHighRisk = 0, monthlyNewWarning = 0, monthlyNewNormal = 0; for (EvaluationReportDO report : thisMonthNewMap.values()) { @@ -717,12 +727,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { !r.getEvaluationDate().toLocalDate().isAfter(monthEnd)) .toList(); - Map monthEndLatestMap = monthEndReports.stream() - .collect(Collectors.toMap( - EvaluationReportDO::getPrisonerId, - r -> r, - (existing, replacement) -> existing - )); + Map monthEndLatestMap = new HashMap<>(); + for (EvaluationReportDO report : monthEndReports) { + if (report == null || report.getPrisonerId() == null) { + continue; + } + monthEndLatestMap.putIfAbsent(report.getPrisonerId(), report); + } int monthHigh = 0, monthWarning = 0, monthNormal = 0; for (EvaluationReportDO report : monthEndLatestMap.values()) { @@ -778,12 +789,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { List allReports = evaluationReportMapper.selectList(wrapper); // 按罪犯ID去重,保留最新的评估记录 - Map latestReportMap = allReports.stream() - .collect(Collectors.toMap( - EvaluationReportDO::getPrisonerId, - r -> r, - (existing, replacement) -> existing - )); + Map latestReportMap = new HashMap<>(); + for (EvaluationReportDO report : allReports) { + if (report == null || report.getPrisonerId() == null) { + continue; + } + latestReportMap.putIfAbsent(report.getPrisonerId(), report); + } List uniqueReports = new ArrayList<>(latestReportMap.values()); // 手动分页 @@ -795,10 +807,22 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { // 获取罪犯详细信息 List prisonerIds = pagedReports.stream() .map(EvaluationReportDO::getPrisonerId) + .filter(Objects::nonNull) + .distinct() .toList(); - Map prisonerMap = prisonerIds.isEmpty() ? Map.of() : - prisonerMapper.selectBatchIds(prisonerIds).stream() - .collect(Collectors.toMap(PrisonerDO::getId, p -> p)); + Map prisonerMap; + if (prisonerIds.isEmpty()) { + prisonerMap = Map.of(); + } else { + Map tempPrisonerMap = new HashMap<>(); + for (PrisonerDO prisoner : prisonerMapper.selectBatchIds(prisonerIds)) { + if (prisoner == null || prisoner.getId() == null) { + continue; + } + tempPrisonerMap.putIfAbsent(prisoner.getId(), prisoner); + } + prisonerMap = tempPrisonerMap; + } // 获取监区信息 List areaIds = pagedReports.stream() @@ -806,9 +830,19 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService { .filter(id -> id != null) .distinct() .toList(); - Map areaMap = areaIds.isEmpty() ? Map.of() : - areaMapper.selectBatchIds(areaIds).stream() - .collect(Collectors.toMap(AreaDO::getId, a -> a)); + Map areaMap; + if (areaIds.isEmpty()) { + areaMap = Map.of(); + } else { + Map tempAreaMap = new HashMap<>(); + for (AreaDO area : areaMapper.selectBatchIds(areaIds)) { + if (area == null || area.getId() == null) { + continue; + } + tempAreaMap.putIfAbsent(area.getId(), area); + } + areaMap = tempAreaMap; + } // 判断本月新增 LocalDate firstDayOfMonth = LocalDate.now().withDayOfMonth(1); diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java index 3121327b11..3ceeedcce1 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/evaluationreport/EvaluationReportServiceImpl.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.stream.Collectors; @@ -417,6 +418,9 @@ public class EvaluationReportServiceImpl implements EvaluationReportService { @Transactional(rollbackFor = Exception.class) public Long createReport(EvaluationReportSaveReqVO createReqVO) { EvaluationReportDO report = BeanUtils.toBean(createReqVO, EvaluationReportDO.class); + if (createReqVO.getEvaluationDate() != null) { + report.setEvaluationDate(createReqVO.getEvaluationDate().atStartOfDay()); + } // 生成报告编号 report.setReportNo(generateReportNo()); // 初始状态为草稿 @@ -436,6 +440,9 @@ public class EvaluationReportServiceImpl implements EvaluationReportService { public void updateReport(EvaluationReportSaveReqVO updateReqVO) { validateReportExists(updateReqVO.getId()); EvaluationReportDO updateObj = BeanUtils.toBean(updateReqVO, EvaluationReportDO.class); + if (updateReqVO.getEvaluationDate() != null) { + updateObj.setEvaluationDate(updateReqVO.getEvaluationDate().atStartOfDay()); + } evaluationReportMapper.updateById(updateObj); } -- 2.47.2 From 91f32de8dd4fecf55279ac8e8b0154bcb6edad5c Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Fri, 6 Mar 2026 16:54:11 +0800 Subject: [PATCH 11/12] =?UTF-8?q?refactor(prison):=20=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=88=A0=E9=99=A4=E6=8E=A5=E5=8F=A3=E4=B8=BA?= =?UTF-8?q?=20POST=20=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一 prison 模块 delete-list 接口的前后端调用方式,避免批量删除协议不一致导致调用失败。 --- .../admin/area/PrisonAreaController.java | 4 +-- .../admin/cell/PrisonCellController.java | 4 +-- .../PrisonConsumptionController.java | 4 +-- .../EvaluationReportController.java | 16 ++++++------ .../admin/prisoner/PrisonerController.java | 4 +-- .../arealog/PrisonerAreaLogController.java | 4 +-- .../question/PrisonQuestionController.java | 4 +-- .../PrisonQuestionnaireController.java | 4 +-- .../PrisonQuestionnaireTaskController.java | 4 +-- .../PrisonQuestionnaireRecordController.java | 26 +++++++++++-------- .../quickcomment/QuickCommentController.java | 4 +-- .../admin/release/ReleaseController.java | 4 +-- .../admin/report/ReportController.java | 4 +-- .../ReportTemplateController.java | 4 +-- .../controller/admin/risk/RiskController.java | 4 +-- .../RiskAssessmentController.java | 6 +++-- .../admin/score/PrisonScoreController.java | 6 ++--- .../scorerecord/ScoreDetailController.java | 4 +-- .../admin/scorerule/ScoreRuleController.java | 4 +-- .../admin/situation/SituationController.java | 4 +-- .../admin/warning/WarningController.java | 4 +-- 21 files changed, 64 insertions(+), 58 deletions(-) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/area/PrisonAreaController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/area/PrisonAreaController.java index dee240e0bf..38c5ce177b 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/area/PrisonAreaController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/area/PrisonAreaController.java @@ -63,11 +63,11 @@ public class PrisonAreaController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Parameter(name = "ids", description = "编号", required = true) @Operation(summary = "批量删除监区信息") @PreAuthorize("@ss.hasPermission('prison:area:delete')") - public CommonResult deleteAreaList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteAreaList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { areaService.deleteAreaListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/cell/PrisonCellController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/cell/PrisonCellController.java index 2397f6f124..581299e27e 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/cell/PrisonCellController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/cell/PrisonCellController.java @@ -68,11 +68,11 @@ public class PrisonCellController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Parameter(name = "ids", description = "编号", required = true) @Operation(summary = "批量删除监室信息") @PreAuthorize("@ss.hasPermission('prison:cell:delete')") - public CommonResult deleteCellList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteCellList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { cellService.deleteCellListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java index b51f27a709..e8bcbf4292 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java @@ -64,11 +64,11 @@ public class PrisonConsumptionController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Parameter(name = "ids", description = "编号列表", required = true) @Operation(summary = "批量删除消费订单") @PreAuthorize("@ss.hasPermission('prison:consumption:delete')") - public CommonResult deleteConsumptionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteConsumptionList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { consumptionService.deleteConsumptionListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java index c2d7f3bada..4c629adf61 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/evaluationreport/EvaluationReportController.java @@ -72,11 +72,11 @@ public class EvaluationReportController { return success(true); } - @DeleteMapping("/template/delete-list") + @PostMapping("/template/delete-list") @Operation(summary = "批量删除评估模板") @Parameter(name = "ids", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('prison:evaluation-report:template:delete')") - public CommonResult deleteTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { evaluationReportService.deleteTemplateListByIds(ids); return success(true); } @@ -210,11 +210,11 @@ public class EvaluationReportController { return success(true); } - @DeleteMapping("/dimension/delete-list") + @PostMapping("/dimension/delete-list") @Operation(summary = "批量删除评估维度") @Parameter(name = "ids", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('prison:evaluation-report:template:update')") - public CommonResult deleteDimensionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteDimensionList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { evaluationReportService.deleteDimensionListByIds(ids); return success(true); } @@ -298,11 +298,11 @@ public class EvaluationReportController { return success(true); } - @DeleteMapping("/report/delete-list") + @PostMapping("/report/delete-list") @Operation(summary = "批量删除评估报告") @Parameter(name = "ids", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('prison:evaluation-report:report:delete')") - public CommonResult deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { evaluationReportService.deleteReportListByIds(ids); return success(true); } @@ -454,11 +454,11 @@ public class EvaluationReportController { return success(true); } - @DeleteMapping("/comment/delete-list") + @PostMapping("/comment/delete-list") @Operation(summary = "批量删除快捷评语") @Parameter(name = "ids", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('prison:evaluation-report:comment:delete')") - public CommonResult deleteCommentList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteCommentList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { evaluationReportService.deleteCommentListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/PrisonerController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/PrisonerController.java index 5d1f60ae00..4b46d6956e 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/PrisonerController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/PrisonerController.java @@ -80,11 +80,11 @@ public class PrisonerController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除服刑人员") @Parameter(name = "ids", description = "编号列表", required = true) @PreAuthorize("@ss.hasPermission('prison:prisoner:delete')") - public CommonResult deletePrisonerList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deletePrisonerList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { prisonerService.deletePrisonerList(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/arealog/PrisonerAreaLogController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/arealog/PrisonerAreaLogController.java index 6756511da3..8e70a7433d 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/arealog/PrisonerAreaLogController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/prisoner/arealog/PrisonerAreaLogController.java @@ -57,10 +57,10 @@ public class PrisonerAreaLogController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除罪犯监区变动记录") @PreAuthorize("@ss.hasPermission('prison:prisoner-area-log:delete')") - public CommonResult deletePrisonerAreaLogList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deletePrisonerAreaLogList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { prisonerAreaLogService.deletePrisonerAreaLogList(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java index 3c74fe4ecc..2fe874b6fb 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/question/PrisonQuestionController.java @@ -65,12 +65,12 @@ public class PrisonQuestionController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Parameter(name = "ids", description = "编号", required = true) @Operation(summary = "批量删除问卷问题") @PreAuthorize("@ss.hasPermission('prison:question:delete') or @ss.hasPermission('prison:questionnaire:update')") @ApiAccessLog(operateType = DELETE) - public CommonResult deleteQuestionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteQuestionList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { questionService.deleteQuestionListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java index 760ba1137e..705a55f30e 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java @@ -65,12 +65,12 @@ public class PrisonQuestionnaireController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Parameter(name = "ids", description = "编号列表", required = true) @Operation(summary = "批量删除问卷模板") @PreAuthorize("@ss.hasPermission('prison:questionnaire:delete')") @ApiAccessLog(operateType = DELETE) - public CommonResult deleteQuestionnaireList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteQuestionnaireList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { questionnaireService.deleteQuestionnaireListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java index c90f425663..851a4596b8 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire_task/PrisonQuestionnaireTaskController.java @@ -63,10 +63,10 @@ public class PrisonQuestionnaireTaskController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除问卷任务") @PreAuthorize("@ss.hasPermission('prison:questionnaire-task:delete')") - public CommonResult deleteQuestionnaireTaskList(@NotEmpty(message = "任务ID列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteQuestionnaireTaskList(@NotEmpty(message = "任务ID列表不能为空") @RequestBody List ids) { questionnaireTaskService.deleteQuestionnaireTaskListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java index 5dda436c21..f808c4e7b2 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java @@ -68,10 +68,11 @@ public class PrisonQuestionnaireRecordController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除问卷答题记录") + @Parameter(name = "ids", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:delete')") - public CommonResult deleteQuestionnaireRecordList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteQuestionnaireRecordList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { questionnaireRecordService.deleteQuestionnaireRecordListByIds(ids); return success(true); } @@ -91,15 +92,15 @@ public class PrisonQuestionnaireRecordController { public CommonResult> getQuestionnaireRecordPage(@Valid QuestionnaireRecordPageReqVO pageReqVO) { PageResult pageResult = questionnaireRecordService.getQuestionnaireRecordPage(pageReqVO); PageResult voPageResult = QuestionnaireRecordConvert.INSTANCE.convertPage(pageResult); - // 填充任务备注 - fillTaskRemark(voPageResult.getList()); + // 填充任务信息 + fillTaskInfo(voPageResult.getList()); return success(voPageResult); } /** - * 填充任务备注 + * 填充任务信息 */ - private void fillTaskRemark(List records) { + private void fillTaskInfo(List records) { if (cn.hutool.core.collection.CollUtil.isEmpty(records)) { return; } @@ -113,22 +114,25 @@ public class PrisonQuestionnaireRecordController { if (cn.hutool.core.collection.CollUtil.isEmpty(taskIds)) { return; } - // 批量查询任务备注 + // 批量查询任务信息 List tasks = questionnaireTaskMapper.selectList( new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() .in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getId, taskIds)); - // 构建 taskId -> remark 映射,避免 Collectors.toMap 在脏数据场景下抛出 NPE + // 构建 taskId -> 任务信息映射,避免 Collectors.toMap 在脏数据场景下抛出 NPE Map taskRemarkMap = new HashMap<>(); + Map taskNameMap = new HashMap<>(); for (cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO task : tasks) { if (task == null || task.getId() == null) { continue; } taskRemarkMap.putIfAbsent(task.getId(), task.getRemark() == null ? "" : task.getRemark()); + taskNameMap.putIfAbsent(task.getId(), task.getTaskName() == null ? "" : task.getTaskName()); } - // 填充备注到 VO + // 填充任务信息到 VO for (QuestionnaireRecordRespVO record : records) { - String remark = taskRemarkMap.get(record.getTaskId()); - record.setTaskRemark(remark); + Long taskId = record.getTaskId(); + record.setTaskRemark(taskRemarkMap.get(taskId)); + record.setTaskName(taskNameMap.get(taskId)); } } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java index c0110d0ba5..ae7d9db86a 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/quickcomment/QuickCommentController.java @@ -70,11 +70,11 @@ public class QuickCommentController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除快捷评语") @Parameter(name = "ids", description = "编号列表", required = true) @PreAuthorize("@ss.hasPermission('prison:quick-comment:delete')") - public CommonResult deleteQuickCommentList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteQuickCommentList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { quickCommentService.deleteQuickCommentListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/release/ReleaseController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/release/ReleaseController.java index 68fbd44081..097721a09d 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/release/ReleaseController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/release/ReleaseController.java @@ -57,10 +57,10 @@ public class ReleaseController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除释放登记") @PreAuthorize("@ss.hasPermission('prison:release:delete')") - public CommonResult deleteReleaseList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteReleaseList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { releaseService.deleteReleaseListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/report/ReportController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/report/ReportController.java index 8038b72335..7228924c7e 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/report/ReportController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/report/ReportController.java @@ -62,11 +62,11 @@ public class ReportController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除评估报告") @Parameter(name = "ids", description = "编号列表", required = true) @PreAuthorize("@ss.hasPermission('prison:report:delete')") - public CommonResult deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { reportService.deleteReportListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/reporttemplate/ReportTemplateController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/reporttemplate/ReportTemplateController.java index 4a547189e1..8283ea78e5 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/reporttemplate/ReportTemplateController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/reporttemplate/ReportTemplateController.java @@ -62,11 +62,11 @@ public class ReportTemplateController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除评估报告模板") @Parameter(name = "ids", description = "编号列表", required = true) @PreAuthorize("@ss.hasPermission('prison:report-template:delete')") - public CommonResult deleteReportTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteReportTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { reportTemplateService.deleteReportTemplateListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/risk/RiskController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/risk/RiskController.java index c54d595783..590a643a02 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/risk/RiskController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/risk/RiskController.java @@ -67,10 +67,10 @@ public class RiskController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除风险评估") @PreAuthorize("@ss.hasPermission('prison:risk:delete')") - public CommonResult deleteList(@RequestParam("ids") List ids) { + public CommonResult deleteList(@RequestBody List ids) { riskService.deleteRiskListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/RiskAssessmentController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/RiskAssessmentController.java index d4a1e1cb9a..c75980ebdc 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/RiskAssessmentController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/RiskAssessmentController.java @@ -19,6 +19,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; @@ -63,10 +64,11 @@ public class RiskAssessmentController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除危险评估") + @Parameter(name = "ids", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('prison:risk-assessment:delete')") - public CommonResult deleteList(@RequestParam("ids") List ids) { + public CommonResult deleteList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { riskAssessmentService.deleteRiskAssessmentListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/score/PrisonScoreController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/score/PrisonScoreController.java index 763f2b0b48..e41bd28dbf 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/score/PrisonScoreController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/score/PrisonScoreController.java @@ -62,11 +62,11 @@ public class PrisonScoreController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Parameter(name = "ids", description = "编号", required = true) @Operation(summary = "批量删除计分考核") - @PreAuthorize("@ss.hasPermission('prison:score:delete')") - public CommonResult deleteScoreList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + @PreAuthorize("@ss.hasPermission('prison:score:delete')") + public CommonResult deleteScoreList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { scoreService.deleteScoreListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerecord/ScoreDetailController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerecord/ScoreDetailController.java index e0d33d5aff..9f877c6e5e 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerecord/ScoreDetailController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerecord/ScoreDetailController.java @@ -57,10 +57,10 @@ public class ScoreDetailController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除考核记录") @PreAuthorize("@ss.hasPermission('prison:score-detail:delete')") - public CommonResult deleteScoreDetailList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteScoreDetailList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { scoreDetailService.deleteScoreDetailListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerule/ScoreRuleController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerule/ScoreRuleController.java index 0d175b3e34..4f911b2819 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerule/ScoreRuleController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/scorerule/ScoreRuleController.java @@ -59,10 +59,10 @@ public class ScoreRuleController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除考核规则") @PreAuthorize("@ss.hasPermission('prison:score-rule:delete')") - public CommonResult deleteScoreRuleList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + public CommonResult deleteScoreRuleList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { scoreRuleService.deleteScoreRuleListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/SituationController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/SituationController.java index 113d1fcf38..0958b77a54 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/SituationController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/situation/SituationController.java @@ -67,10 +67,10 @@ public class SituationController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除狱情收集") @PreAuthorize("@ss.hasPermission('prison:situation:delete')") - public CommonResult deleteList(@RequestParam("ids") List ids) { + public CommonResult deleteList(@RequestBody List ids) { situationService.deleteSituationListByIds(ids); return success(true); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java index bf6aa706d8..ebe4b5dfdd 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/warning/WarningController.java @@ -67,10 +67,10 @@ public class WarningController { return success(true); } - @DeleteMapping("/delete-list") + @PostMapping("/delete-list") @Operation(summary = "批量删除预警信息") @PreAuthorize("@ss.hasPermission('prison:warning:delete')") - public CommonResult deleteList(@RequestParam("ids") List ids) { + public CommonResult deleteList(@RequestBody List ids) { warningService.deleteWarningListByIds(ids); return success(true); } -- 2.47.2 From e51432cc32e209870967b0d323f3fcf569fe112c Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Fri, 6 Mar 2026 16:58:16 +0800 Subject: [PATCH 12/12] =?UTF-8?q?feat(questionnaire-record):=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E7=AD=94=E9=A2=98=E8=AE=B0=E5=BD=95=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../questionnairerecord/vo/QuestionnaireRecordRespVO.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java index e708063d6b..7736069c5f 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java @@ -33,6 +33,10 @@ public class QuestionnaireRecordRespVO { @ExcelProperty("任务ID") private Long taskId; + @Schema(description = "任务名称", example = "一月心理测评任务") + @ExcelProperty("任务名称") + private String taskName; + @Schema(description = "问卷名称") @ExcelProperty("问卷名称") private String questionnaireName; -- 2.47.2