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: