fix(prison): 修复评估报告日期和映射构建问题 #6
2
.gitignore
vendored
2
.gitignore
vendored
@ -55,3 +55,5 @@ application-my.yaml
|
||||
|
||||
# Generated codegen files
|
||||
/codegen/
|
||||
|
||||
.omc/
|
||||
|
||||
@ -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<LocalDateTime> {
|
||||
|
||||
/** 默认时区:上海 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<List<AnswerRespVO>> getAnswersByAssessmentRecordId(
|
||||
@NotNull(message = "测评记录ID不能为空") @RequestParam("assessmentRecordId") Long assessmentRecordId) {
|
||||
List<AnswerDO> list = answerService.getAnswersByAssessmentRecordId(assessmentRecordId);
|
||||
|
||||
@ -63,11 +63,11 @@ public class PrisonAreaController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除监区信息")
|
||||
@PreAuthorize("@ss.hasPermission('prison:area:delete')")
|
||||
public CommonResult<Boolean> deleteAreaList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteAreaList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
areaService.deleteAreaListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -68,11 +68,11 @@ public class PrisonCellController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除监室信息")
|
||||
@PreAuthorize("@ss.hasPermission('prison:cell:delete')")
|
||||
public CommonResult<Boolean> deleteCellList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteCellList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
cellService.deleteCellListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -64,11 +64,11 @@ public class PrisonConsumptionController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@Operation(summary = "批量删除消费订单")
|
||||
@PreAuthorize("@ss.hasPermission('prison:consumption:delete')")
|
||||
public CommonResult<Boolean> deleteConsumptionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteConsumptionList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
consumptionService.deleteConsumptionListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -69,6 +69,27 @@ public class PrisonerDashboardStatsRespVO {
|
||||
@Schema(description = "风险等级:1-低风险 2-中风险 3-高风险 4-极高风险", example = "1")
|
||||
private Integer riskLevel;
|
||||
|
||||
@Schema(description = "剩余刑期天数", example = "1000")
|
||||
private Integer remainingDays;
|
||||
|
||||
@Schema(description = "刑期总天数", example = "1276")
|
||||
private Integer sentenceDays;
|
||||
|
||||
@Schema(description = "累计违规次数", example = "5")
|
||||
private Integer violationCount;
|
||||
|
||||
@Schema(description = "累计表扬天数", example = "-")
|
||||
private String praiseDays;
|
||||
|
||||
@Schema(description = "累计表扬次数", example = "8")
|
||||
private Integer praiseCount;
|
||||
|
||||
@Schema(description = "累计扣分次数", example = "3")
|
||||
private Integer penaltyCount;
|
||||
|
||||
@Schema(description = "累计加分次数", example = "8")
|
||||
private Integer rewardCount;
|
||||
|
||||
@Schema(description = "中心左侧数据")
|
||||
private CenterLeftData centerLeftData;
|
||||
|
||||
@ -78,6 +99,9 @@ public class PrisonerDashboardStatsRespVO {
|
||||
@Schema(description = "月度消费数据")
|
||||
private List<MonthlyConsumptionData> consumptionMonthlyData;
|
||||
|
||||
@Schema(description = "账户余额")
|
||||
private Integer balance;
|
||||
|
||||
@Schema(description = "消费汇总")
|
||||
private ConsumptionSummary consumptionSummary;
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -71,11 +72,11 @@ public class EvaluationReportController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/template/delete-list")
|
||||
@PostMapping("/template/delete-list")
|
||||
@Operation(summary = "批量删除评估模板")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:evaluation-report:template:delete')")
|
||||
public CommonResult<Boolean> deleteTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
evaluationReportService.deleteTemplateListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
@ -209,11 +210,11 @@ public class EvaluationReportController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/dimension/delete-list")
|
||||
@PostMapping("/dimension/delete-list")
|
||||
@Operation(summary = "批量删除评估维度")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:evaluation-report:template:update')")
|
||||
public CommonResult<Boolean> deleteDimensionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteDimensionList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
evaluationReportService.deleteDimensionListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
@ -297,11 +298,11 @@ public class EvaluationReportController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/report/delete-list")
|
||||
@PostMapping("/report/delete-list")
|
||||
@Operation(summary = "批量删除评估报告")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:evaluation-report:report:delete')")
|
||||
public CommonResult<Boolean> deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
evaluationReportService.deleteReportListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
@ -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<Boolean> generateReportByAi(@RequestBody Map<String, Object> requestData) {
|
||||
Long reportId = Long.valueOf(requestData.get("reportId").toString());
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> dimensionIds = requestData.get("dimensionIds") != null
|
||||
? ((List<Number>) requestData.get("dimensionIds")).stream()
|
||||
.map(Number::longValue)
|
||||
.collect(Collectors.toList())
|
||||
: null;
|
||||
evaluationReportService.generateReportByAi(reportId, dimensionIds);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ========== 维度数据管理 ==========
|
||||
|
||||
@PostMapping("/dimension-data/create")
|
||||
@ -438,11 +454,11 @@ public class EvaluationReportController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/comment/delete-list")
|
||||
@PostMapping("/comment/delete-list")
|
||||
@Operation(summary = "批量删除快捷评语")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:evaluation-report:comment:delete')")
|
||||
public CommonResult<Boolean> deleteCommentList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteCommentList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
evaluationReportService.deleteCommentListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -6,8 +6,10 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 评估报告分页 Request VO")
|
||||
@ -45,8 +47,8 @@ public class EvaluationReportPageReqVO extends PageParam {
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "评估日期")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] evaluationDate;
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate[] evaluationDate;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
|
||||
@ -6,6 +6,7 @@ import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.idev.excel.annotation.*;
|
||||
|
||||
@ -52,8 +53,8 @@ public class EvaluationReportRespVO {
|
||||
|
||||
@Schema(description = "评估日期")
|
||||
@ExcelProperty("评估日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime evaluationDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate evaluationDate;
|
||||
|
||||
@Schema(description = "评估人员ID")
|
||||
private Long evaluatorId;
|
||||
|
||||
@ -5,7 +5,7 @@ import lombok.*;
|
||||
import java.util.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Schema(description = "管理后台 - 评估报告新增/修改 Request VO")
|
||||
@Data
|
||||
@ -41,7 +41,7 @@ public class EvaluationReportSaveReqVO {
|
||||
|
||||
@Schema(description = "评估日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "评估日期不能为空")
|
||||
private LocalDateTime evaluationDate;
|
||||
private LocalDate evaluationDate;
|
||||
|
||||
@Schema(description = "评估人员ID")
|
||||
private Long evaluatorId;
|
||||
|
||||
@ -80,11 +80,11 @@ public class PrisonerController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除服刑人员")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:prisoner:delete')")
|
||||
public CommonResult<Boolean> deletePrisonerList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deletePrisonerList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
prisonerService.deletePrisonerList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -57,10 +57,10 @@ public class PrisonerAreaLogController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除罪犯监区变动记录")
|
||||
@PreAuthorize("@ss.hasPermission('prison:prisoner-area-log:delete')")
|
||||
public CommonResult<Boolean> deletePrisonerAreaLogList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deletePrisonerAreaLogList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
prisonerAreaLogService.deletePrisonerAreaLogList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ public class PrisonQuestionController {
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建问卷问题")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:create')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:create') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
@ApiAccessLog(operateType = CREATE)
|
||||
public CommonResult<Long> createQuestion(@Valid @RequestBody QuestionSaveReqVO createReqVO) {
|
||||
return success(questionService.createQuestion(createReqVO));
|
||||
@ -48,7 +48,7 @@ public class PrisonQuestionController {
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新问卷问题")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:update')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:update') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
@ApiAccessLog(operateType = UPDATE)
|
||||
public CommonResult<Boolean> updateQuestion(@Valid @RequestBody QuestionSaveReqVO updateReqVO) {
|
||||
questionService.updateQuestion(updateReqVO);
|
||||
@ -58,19 +58,19 @@ public class PrisonQuestionController {
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除问卷问题")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:delete')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:delete') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
@ApiAccessLog(operateType = DELETE)
|
||||
public CommonResult<Boolean> deleteQuestion(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) {
|
||||
questionService.deleteQuestion(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除问卷问题")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:delete')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:delete') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
@ApiAccessLog(operateType = DELETE)
|
||||
public CommonResult<Boolean> deleteQuestionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteQuestionList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
questionService.deleteQuestionListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
@ -78,7 +78,7 @@ public class PrisonQuestionController {
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得问卷问题")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:query')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:query') or @ss.hasPermission('prison:questionnaire:query') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
public CommonResult<QuestionRespVO> getQuestion(@RequestParam("id") Long id) {
|
||||
QuestionDO question = questionService.getQuestion(id);
|
||||
return success(QuestionConvert.INSTANCE.convert(question));
|
||||
@ -86,7 +86,7 @@ public class PrisonQuestionController {
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得问卷问题分页")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:query')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:query') or @ss.hasPermission('prison:questionnaire:query') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
public CommonResult<PageResult<QuestionRespVO>> getQuestionPage(@Valid QuestionPageReqVO pageReqVO) {
|
||||
PageResult<QuestionDO> pageResult = questionService.getQuestionPage(pageReqVO);
|
||||
return success(QuestionConvert.INSTANCE.convertPage(pageResult));
|
||||
@ -94,7 +94,7 @@ public class PrisonQuestionController {
|
||||
|
||||
@PostMapping("/batch-update")
|
||||
@Operation(summary = "批量更新问卷问题")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:update')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:update') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
@ApiAccessLog(operateType = UPDATE)
|
||||
public CommonResult<Boolean> batchUpdateQuestion(@Valid @RequestBody QuestionBatchUpdateReqVO reqVO) {
|
||||
// 转换为 Service 需要的格式
|
||||
@ -113,7 +113,7 @@ public class PrisonQuestionController {
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出问卷问题 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:export')")
|
||||
@PreAuthorize("@ss.hasPermission('prison:question:export') or @ss.hasPermission('prison:questionnaire:export') or @ss.hasPermission('prison:questionnaire:update')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportQuestionExcel(@Valid QuestionPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
@ -65,12 +65,12 @@ public class PrisonQuestionnaireController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@Operation(summary = "批量删除问卷模板")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire:delete')")
|
||||
@ApiAccessLog(operateType = DELETE)
|
||||
public CommonResult<Boolean> deleteQuestionnaireList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteQuestionnaireList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
questionnaireService.deleteQuestionnaireListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -0,0 +1,214 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task;
|
||||
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*;
|
||||
import cn.iocoder.yudao.module.prison.convert.questionnaire_task.QuestionnaireTaskConvert;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO;
|
||||
import cn.iocoder.yudao.module.prison.service.questionnaire_task.QuestionnaireTaskService;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
|
||||
@Tag(name = "管理后台 - 问卷任务")
|
||||
@RestController
|
||||
@RequestMapping("/prison/questionnaire-task")
|
||||
@Validated
|
||||
public class PrisonQuestionnaireTaskController {
|
||||
|
||||
@Resource
|
||||
private QuestionnaireTaskService questionnaireTaskService;
|
||||
|
||||
// ==================== 基础 CRUD ====================
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建问卷任务")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:create')")
|
||||
public CommonResult<Long> createQuestionnaireTask(@Valid @RequestBody QuestionnaireTaskCreateReqVO createReqVO) {
|
||||
return success(questionnaireTaskService.createQuestionnaireTask(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新问卷任务")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:update')")
|
||||
public CommonResult<Boolean> updateQuestionnaireTask(@Valid @RequestBody QuestionnaireTaskUpdateReqVO updateReqVO) {
|
||||
questionnaireTaskService.updateQuestionnaireTask(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除问卷任务")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:delete')")
|
||||
public CommonResult<Boolean> deleteQuestionnaireTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
questionnaireTaskService.deleteQuestionnaireTask(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除问卷任务")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:delete')")
|
||||
public CommonResult<Boolean> deleteQuestionnaireTaskList(@NotEmpty(message = "任务ID列表不能为空") @RequestBody List<Long> ids) {
|
||||
questionnaireTaskService.deleteQuestionnaireTaskListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得问卷任务")
|
||||
@Parameter(name = "id", description = "任务ID", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<QuestionnaireTaskRespVO> getQuestionnaireTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
QuestionnaireTaskDO task = questionnaireTaskService.getQuestionnaireTask(id);
|
||||
return success(QuestionnaireTaskConvert.INSTANCE.convert(task));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得问卷任务分页")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<PageResult<QuestionnaireTaskRespVO>> getQuestionnaireTaskPage(@Valid QuestionnaireTaskPageReqVO pageReqVO) {
|
||||
PageResult<QuestionnaireTaskDO> pageResult = questionnaireTaskService.getQuestionnaireTaskPage(pageReqVO);
|
||||
return success(QuestionnaireTaskConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出问卷任务 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportQuestionnaireTaskExcel(@Valid QuestionnaireTaskPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<QuestionnaireTaskDO> list = questionnaireTaskService.getQuestionnaireTaskPage(pageReqVO).getList();
|
||||
ExcelUtils.write(response, "问卷任务.xls", "数据", QuestionnaireTaskRespVO.class,
|
||||
QuestionnaireTaskConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
// ==================== 任务执行相关 ====================
|
||||
|
||||
@PostMapping("/cancel")
|
||||
@Operation(summary = "取消任务")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:cancel')")
|
||||
public CommonResult<Boolean> cancelTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
questionnaireTaskService.cancelTask(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/finish")
|
||||
@Operation(summary = "结束任务")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:finish')")
|
||||
public CommonResult<Boolean> finishTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
questionnaireTaskService.finishTask(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/restart")
|
||||
@Operation(summary = "重新开始任务")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:restart')")
|
||||
public CommonResult<Boolean> restartTask(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
questionnaireTaskService.restartTask(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 进度跟踪相关 ====================
|
||||
|
||||
@GetMapping("/progress")
|
||||
@Operation(summary = "获取任务进度")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<TaskProgressRespVO> getTaskProgress(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
return success(questionnaireTaskService.getTaskProgress(id));
|
||||
}
|
||||
|
||||
@GetMapping("/pending-prisoners")
|
||||
@Operation(summary = "获取任务未完成人员")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<PageResult<QuestionnaireRecordDO>> getPendingPrisoners(
|
||||
@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id,
|
||||
@Valid PageParam pageReqVO) {
|
||||
return success(questionnaireTaskService.getPendingPrisoners(id, pageReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/prisoner-progress")
|
||||
@Operation(summary = "获取任务的人员填写进度列表")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<List<PrisonerProgressRespVO>> getPrisonerProgress(
|
||||
@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
return success(questionnaireTaskService.getPrisonerProgress(id));
|
||||
}
|
||||
|
||||
@PostMapping("/remind")
|
||||
@Operation(summary = "提醒未完成人员")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:remind')")
|
||||
public CommonResult<Integer> remindPendingPrisoners(@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
return success(questionnaireTaskService.remindPendingPrisoners(id));
|
||||
}
|
||||
|
||||
@PostMapping("/notify-prisoner")
|
||||
@Operation(summary = "通知单个人员")
|
||||
@Parameter(name = "recordId", description = "记录ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:notify')")
|
||||
public CommonResult<Boolean> notifyPrisoner(@NotNull(message = "记录ID不能为空") @RequestParam("recordId") Long recordId) {
|
||||
questionnaireTaskService.notifyPrisoner(recordId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/reset-record")
|
||||
@Operation(summary = "重置人员答题记录")
|
||||
@Parameter(name = "recordId", description = "记录ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:reset')")
|
||||
public CommonResult<Boolean> resetPrisonerRecord(@NotNull(message = "记录ID不能为空") @RequestParam("recordId") Long recordId) {
|
||||
questionnaireTaskService.resetPrisonerRecord(recordId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 统计相关 ====================
|
||||
|
||||
@GetMapping("/area-statistics")
|
||||
@Operation(summary = "按监区统计任务完成情况")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<List<TaskAreaStatisticsRespVO>> getTaskAreaStatistics(
|
||||
@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
return success(questionnaireTaskService.getTaskAreaStatistics(id));
|
||||
}
|
||||
|
||||
@GetMapping("/statistics-summary")
|
||||
@Operation(summary = "获取全局任务统计汇总")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<TaskStatisticsSummaryRespVO> getStatisticsSummary() {
|
||||
return success(questionnaireTaskService.getStatisticsSummary());
|
||||
}
|
||||
|
||||
@GetMapping("/area-comparison")
|
||||
@Operation(summary = "按监区对比分析")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<List<AreaComparisonRespVO>> compareAreasByQuestionnaire(
|
||||
@RequestParam(value = "questionnaireId", required = false) Long questionnaireId,
|
||||
@RequestParam(value = "areaIds", required = false) List<Long> areaIds) {
|
||||
return success(questionnaireTaskService.compareAreasByQuestionnaire(questionnaireId, areaIds));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 管理后台 - 按监区对比分析 Response VO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AreaComparisonRespVO {
|
||||
|
||||
@Schema(description = "任务ID", example = "18966")
|
||||
private Long taskId;
|
||||
|
||||
@Schema(description = "任务名称", example = "一月心理测评")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "目标总人数")
|
||||
private Integer totalCount;
|
||||
|
||||
@Schema(description = "已完成人数")
|
||||
private Integer completedCount;
|
||||
|
||||
@Schema(description = "完成率(%)")
|
||||
private BigDecimal completionRate;
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 问卷任务人员填写进度 Response VO")
|
||||
@Data
|
||||
public class PrisonerProgressRespVO {
|
||||
|
||||
@Schema(description = "问卷ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Long questionnaireId;
|
||||
|
||||
@Schema(description = "问卷名称", example = "风险评估问卷")
|
||||
private String questionnaireName;
|
||||
|
||||
@Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long prisonerId;
|
||||
|
||||
@Schema(description = "罪犯编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "P001")
|
||||
private String prisonerNo;
|
||||
|
||||
@Schema(description = "罪犯姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String prisonerName;
|
||||
|
||||
@Schema(description = "监区ID", example = "100")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "监区名称", example = "一监区")
|
||||
private String areaName;
|
||||
|
||||
@Schema(description = "填写状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "客观分", example = "80")
|
||||
private Integer objectiveScore;
|
||||
|
||||
@Schema(description = "主观分", example = "85")
|
||||
private Integer subjectiveScore;
|
||||
|
||||
@Schema(description = "总分", example = "165")
|
||||
private Integer totalScore;
|
||||
|
||||
@Schema(description = "风险等级", example = "2")
|
||||
private Integer riskLevel;
|
||||
|
||||
@Schema(description = "答题用时(秒)", example = "300")
|
||||
private Integer duration;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "完成时间")
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理后台 - 问卷任务创建 Request VO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionnaireTaskCreateReqVO {
|
||||
|
||||
@Schema(description = "任务名称", required = true, example = "一月心理测评")
|
||||
@NotBlank(message = "任务名称不能为空")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "问卷ID", required = true, example = "18966")
|
||||
@NotNull(message = "问卷ID不能为空")
|
||||
private Long questionnaireId;
|
||||
|
||||
@Schema(description = "目标类型:1-指定犯人 2-指定监区 3-全部犯人", required = true, example = "2")
|
||||
@NotNull(message = "目标类型不能为空")
|
||||
private Integer targetType;
|
||||
|
||||
@Schema(description = "犯人ID列表(当targetType=1时)")
|
||||
private List<Long> prisonerIds;
|
||||
|
||||
@Schema(description = "监区ID(当targetType=2时)")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "任务开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "截止时间", required = true)
|
||||
@NotNull(message = "截止时间不能为空")
|
||||
private LocalDateTime deadline;
|
||||
|
||||
@Schema(description = "备注", example = "一月度常规心理测评")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import lombok.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 问卷任务分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class QuestionnaireTaskPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "任务名称", example = "测试任务")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "问卷ID", example = "1")
|
||||
private Long questionnaireId;
|
||||
|
||||
@Schema(description = "状态:1-未开始 2-进行中 3-已完成 4-已取消", example = "2")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理后台 - 问卷任务详情 Response VO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionnaireTaskRespVO {
|
||||
|
||||
@Schema(description = "任务ID", example = "18966")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "任务名称", example = "一月心理测评")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "问卷ID", example = "18966")
|
||||
private Long questionnaireId;
|
||||
|
||||
@Schema(description = "问卷名称", example = "心理评估问卷")
|
||||
private String questionnaireName;
|
||||
|
||||
@Schema(description = "目标类型:1-指定犯人 2-指定监区 3-全部犯人", example = "2")
|
||||
private Integer targetType;
|
||||
|
||||
@Schema(description = "监区ID", example = "456")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "监区名称", example = "一监区")
|
||||
private String areaName;
|
||||
|
||||
@Schema(description = "目标总人数")
|
||||
private Integer totalCount;
|
||||
|
||||
@Schema(description = "已完成人数")
|
||||
private Integer completedCount;
|
||||
|
||||
@Schema(description = "待完成人数")
|
||||
private Integer pendingCount;
|
||||
|
||||
@Schema(description = "完成率(%)")
|
||||
private BigDecimal completionRate;
|
||||
|
||||
@Schema(description = "状态:1-草稿 2-进行中 3-已结束 4-已取消", example = "2")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "任务开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "截止时间")
|
||||
private LocalDateTime deadline;
|
||||
|
||||
@Schema(description = "备注", example = "一月度常规心理测评")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理后台 - 问卷任务更新 Request VO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionnaireTaskUpdateReqVO {
|
||||
|
||||
@Schema(description = "任务ID", required = true, example = "18966")
|
||||
@NotNull(message = "任务ID不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "任务名称", example = "一月心理测评")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "截止时间")
|
||||
private LocalDateTime deadline;
|
||||
|
||||
@Schema(description = "备注", example = "一月度常规心理测评")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理后台 - 任务按监区统计 Response VO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskAreaStatisticsRespVO {
|
||||
|
||||
@Schema(description = "监区ID", example = "456")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "监区名称", example = "一监区")
|
||||
private String areaName;
|
||||
|
||||
@Schema(description = "目标总人数")
|
||||
private Integer totalCount;
|
||||
|
||||
@Schema(description = "已完成人数")
|
||||
private Integer completedCount;
|
||||
|
||||
@Schema(description = "完成率(%)")
|
||||
private BigDecimal completionRate;
|
||||
|
||||
@Schema(description = "平均分")
|
||||
private BigDecimal avgScore;
|
||||
|
||||
@Schema(description = "及格率(%)")
|
||||
private BigDecimal passRate;
|
||||
|
||||
@Schema(description = "风险分布")
|
||||
private RiskDistribution riskDistribution;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class RiskDistribution {
|
||||
|
||||
@Schema(description = "高风险人数")
|
||||
private Integer highRisk;
|
||||
|
||||
@Schema(description = "中风险人数")
|
||||
private Integer mediumRisk;
|
||||
|
||||
@Schema(description = "低风险人数")
|
||||
private Integer lowRisk;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 管理后台 - 任务进度详情 Response VO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskProgressRespVO {
|
||||
|
||||
@Schema(description = "任务ID", example = "18966")
|
||||
private Long taskId;
|
||||
|
||||
@Schema(description = "任务名称", example = "一月心理测评")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "问卷名称", example = "心理评估问卷")
|
||||
private String questionnaireName;
|
||||
|
||||
@Schema(description = "状态:1-草稿 2-进行中 3-已结束 4-已取消", example = "2")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "任务开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "截止时间")
|
||||
private LocalDateTime deadline;
|
||||
|
||||
@Schema(description = "目标总人数")
|
||||
private Integer totalCount;
|
||||
|
||||
@Schema(description = "已完成人数")
|
||||
private Integer completedCount;
|
||||
|
||||
@Schema(description = "待完成人数")
|
||||
private Integer pendingCount;
|
||||
|
||||
@Schema(description = "完成率(%)")
|
||||
private BigDecimal completionRate;
|
||||
|
||||
@Schema(description = "状态分布")
|
||||
private StatusBreakdown statusBreakdown;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class StatusBreakdown {
|
||||
|
||||
@Schema(description = "待测评人数")
|
||||
private Integer pending;
|
||||
|
||||
@Schema(description = "测评中人数")
|
||||
private Integer inProgress;
|
||||
|
||||
@Schema(description = "已完成人数")
|
||||
private Integer completed;
|
||||
|
||||
@Schema(description = "已过期人数")
|
||||
private Integer expired;
|
||||
|
||||
@Schema(description = "已取消人数")
|
||||
private Integer cancelled;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 管理后台 - 任务统计汇总 Response VO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskStatisticsSummaryRespVO {
|
||||
|
||||
@Schema(description = "任务总数")
|
||||
private Integer taskCount;
|
||||
|
||||
@Schema(description = "目标总人数")
|
||||
private Integer totalPrisoners;
|
||||
|
||||
@Schema(description = "已完成人数")
|
||||
private Integer totalCompleted;
|
||||
|
||||
@Schema(description = "待完成人数")
|
||||
private Integer totalPending;
|
||||
|
||||
@Schema(description = "整体完成率(%)")
|
||||
private BigDecimal overallCompletionRate;
|
||||
|
||||
}
|
||||
@ -39,6 +39,9 @@ public class PrisonQuestionnaireRecordController {
|
||||
@Resource
|
||||
private QuestionnaireRecordService questionnaireRecordService;
|
||||
|
||||
@Resource
|
||||
private cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task.QuestionnaireTaskMapper questionnaireTaskMapper;
|
||||
|
||||
// ==================== 基础 CRUD ====================
|
||||
|
||||
@PostMapping("/create")
|
||||
@ -65,10 +68,11 @@ public class PrisonQuestionnaireRecordController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除问卷答题记录")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:delete')")
|
||||
public CommonResult<Boolean> deleteQuestionnaireRecordList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteQuestionnaireRecordList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
questionnaireRecordService.deleteQuestionnaireRecordListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
@ -87,7 +91,49 @@ public class PrisonQuestionnaireRecordController {
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:query')")
|
||||
public CommonResult<PageResult<QuestionnaireRecordRespVO>> getQuestionnaireRecordPage(@Valid QuestionnaireRecordPageReqVO pageReqVO) {
|
||||
PageResult<QuestionnaireRecordDO> pageResult = questionnaireRecordService.getQuestionnaireRecordPage(pageReqVO);
|
||||
return success(QuestionnaireRecordConvert.INSTANCE.convertPage(pageResult));
|
||||
PageResult<QuestionnaireRecordRespVO> voPageResult = QuestionnaireRecordConvert.INSTANCE.convertPage(pageResult);
|
||||
// 填充任务信息
|
||||
fillTaskInfo(voPageResult.getList());
|
||||
return success(voPageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充任务信息
|
||||
*/
|
||||
private void fillTaskInfo(List<QuestionnaireRecordRespVO> records) {
|
||||
if (cn.hutool.core.collection.CollUtil.isEmpty(records)) {
|
||||
return;
|
||||
}
|
||||
// 收集所有 taskId
|
||||
Set<Long> taskIds = new HashSet<>();
|
||||
for (QuestionnaireRecordRespVO record : records) {
|
||||
if (record != null && record.getTaskId() != null) {
|
||||
taskIds.add(record.getTaskId());
|
||||
}
|
||||
}
|
||||
if (cn.hutool.core.collection.CollUtil.isEmpty(taskIds)) {
|
||||
return;
|
||||
}
|
||||
// 批量查询任务信息
|
||||
List<cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO> tasks = questionnaireTaskMapper.selectList(
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO>()
|
||||
.in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO::getId, taskIds));
|
||||
// 构建 taskId -> 任务信息映射,避免 Collectors.toMap 在脏数据场景下抛出 NPE
|
||||
Map<Long, String> taskRemarkMap = new HashMap<>();
|
||||
Map<Long, String> taskNameMap = new HashMap<>();
|
||||
for (cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO task : tasks) {
|
||||
if (task == null || task.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
taskRemarkMap.putIfAbsent(task.getId(), task.getRemark() == null ? "" : task.getRemark());
|
||||
taskNameMap.putIfAbsent(task.getId(), task.getTaskName() == null ? "" : task.getTaskName());
|
||||
}
|
||||
// 填充任务信息到 VO
|
||||
for (QuestionnaireRecordRespVO record : records) {
|
||||
Long taskId = record.getTaskId();
|
||||
record.setTaskRemark(taskRemarkMap.get(taskId));
|
||||
record.setTaskName(taskNameMap.get(taskId));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@ -129,6 +175,14 @@ public class PrisonQuestionnaireRecordController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/submit-by-agent")
|
||||
@Operation(summary = "代为提交答卷(民警代填)")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:agent-fill')")
|
||||
public CommonResult<Boolean> submitAnswerByAgent(@Valid @RequestBody AssessmentAnswerSubmitReqVO reqVO) {
|
||||
questionnaireRecordService.submitAnswerByAgent(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/finish")
|
||||
@Operation(summary = "结束测评")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:finish')")
|
||||
|
||||
@ -29,6 +29,14 @@ public class QuestionnaireRecordRespVO {
|
||||
@ExcelProperty("问卷ID")
|
||||
private Long questionnaireId;
|
||||
|
||||
@Schema(description = "任务ID", example = "1001")
|
||||
@ExcelProperty("任务ID")
|
||||
private Long taskId;
|
||||
|
||||
@Schema(description = "任务名称", example = "一月心理测评任务")
|
||||
@ExcelProperty("任务名称")
|
||||
private String taskName;
|
||||
|
||||
@Schema(description = "问卷名称")
|
||||
@ExcelProperty("问卷名称")
|
||||
private String questionnaireName;
|
||||
@ -132,6 +140,10 @@ public class QuestionnaireRecordRespVO {
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "任务备注", example = "一月度常规心理测评")
|
||||
@ExcelProperty("任务备注")
|
||||
private String taskRemark;
|
||||
|
||||
// ==================== 通用字段 ====================
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
|
||||
@ -70,11 +70,11 @@ public class QuickCommentController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除快捷评语")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:quick-comment:delete')")
|
||||
public CommonResult<Boolean> deleteQuickCommentList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteQuickCommentList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
quickCommentService.deleteQuickCommentListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
@ -110,8 +110,12 @@ public class QuickCommentController {
|
||||
new LambdaQueryWrapper<CommentCategoryDO>()
|
||||
.in(CommentCategoryDO::getId, categoryIds)
|
||||
);
|
||||
categoryNameMap = categories.stream()
|
||||
.collect(Collectors.toMap(CommentCategoryDO::getId, CommentCategoryDO::getName));
|
||||
for (CommentCategoryDO category : categories) {
|
||||
if (category == null || category.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
categoryNameMap.putIfAbsent(category.getId(), category.getName() == null ? "" : category.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// 填充分类名称
|
||||
|
||||
@ -57,10 +57,10 @@ public class ReleaseController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除释放登记")
|
||||
@PreAuthorize("@ss.hasPermission('prison:release:delete')")
|
||||
public CommonResult<Boolean> deleteReleaseList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteReleaseList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
releaseService.deleteReleaseListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -62,11 +62,11 @@ public class ReportController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除评估报告")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:report:delete')")
|
||||
public CommonResult<Boolean> deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteReportList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
reportService.deleteReportListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -62,11 +62,11 @@ public class ReportTemplateController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除评估报告模板")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:report-template:delete')")
|
||||
public CommonResult<Boolean> deleteReportTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteReportTemplateList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
reportTemplateService.deleteReportTemplateListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -67,10 +67,10 @@ public class RiskController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除风险评估")
|
||||
@PreAuthorize("@ss.hasPermission('prison:risk:delete')")
|
||||
public CommonResult<Boolean> deleteList(@RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteList(@RequestBody List<Long> ids) {
|
||||
riskService.deleteRiskListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -63,10 +64,11 @@ public class RiskAssessmentController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除危险评估")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:risk-assessment:delete')")
|
||||
public CommonResult<Boolean> deleteList(@RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
riskAssessmentService.deleteRiskAssessmentListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -62,11 +62,11 @@ public class PrisonScoreController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除计分考核")
|
||||
@PreAuthorize("@ss.hasPermission('prison:score:delete')")
|
||||
public CommonResult<Boolean> deleteScoreList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
@PreAuthorize("@ss.hasPermission('prison:score:delete')")
|
||||
public CommonResult<Boolean> deleteScoreList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
scoreService.deleteScoreListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -57,10 +57,10 @@ public class ScoreDetailController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除考核记录")
|
||||
@PreAuthorize("@ss.hasPermission('prison:score-detail:delete')")
|
||||
public CommonResult<Boolean> deleteScoreDetailList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteScoreDetailList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
scoreDetailService.deleteScoreDetailListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -59,10 +59,10 @@ public class ScoreRuleController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除考核规则")
|
||||
@PreAuthorize("@ss.hasPermission('prison:score-rule:delete')")
|
||||
public CommonResult<Boolean> deleteScoreRuleList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteScoreRuleList(@NotEmpty(message = "编号列表不能为空") @RequestBody List<Long> ids) {
|
||||
scoreRuleService.deleteScoreRuleListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -67,10 +67,10 @@ public class SituationController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除狱情收集")
|
||||
@PreAuthorize("@ss.hasPermission('prison:situation:delete')")
|
||||
public CommonResult<Boolean> deleteList(@RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteList(@RequestBody List<Long> ids) {
|
||||
situationService.deleteSituationListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -67,10 +67,10 @@ public class WarningController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@PostMapping("/delete-list")
|
||||
@Operation(summary = "批量删除预警信息")
|
||||
@PreAuthorize("@ss.hasPermission('prison:warning:delete')")
|
||||
public CommonResult<Boolean> deleteList(@RequestParam("ids") List<Long> ids) {
|
||||
public CommonResult<Boolean> deleteList(@RequestBody List<Long> ids) {
|
||||
warningService.deleteWarningListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
@ -87,8 +87,8 @@ public class WarningController {
|
||||
@Operation(summary = "分页查询预警信息")
|
||||
@PreAuthorize("@ss.hasPermission('prison:warning:query')")
|
||||
public CommonResult<PageResult<WarningRespVO>> page(@Valid WarningPageReqVO pageReqVO) {
|
||||
PageResult<WarningDO> pageResult = warningService.getWarningPage(pageReqVO);
|
||||
return success(WarningConvert.INSTANCE.convertPage(pageResult));
|
||||
PageResult<WarningRespVO> 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<WarningDO> list = warningService.getWarningPage(pageReqVO).getList();
|
||||
List<WarningRespVO> 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")
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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 = "备注")
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.prison.convert.questionnaire_task;
|
||||
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface QuestionnaireTaskConvert {
|
||||
|
||||
QuestionnaireTaskConvert INSTANCE = Mappers.getMapper(QuestionnaireTaskConvert.class);
|
||||
|
||||
QuestionnaireTaskDO convert(QuestionnaireTaskUpdateReqVO bean);
|
||||
|
||||
QuestionnaireTaskRespVO convert(QuestionnaireTaskDO bean);
|
||||
|
||||
List<QuestionnaireTaskRespVO> convertList(List<QuestionnaireTaskDO> list);
|
||||
|
||||
PageResult<QuestionnaireTaskRespVO> convertPage(PageResult<QuestionnaireTaskDO> page);
|
||||
|
||||
}
|
||||
@ -67,5 +67,21 @@ public class AnswerDO extends TenantBaseDO {
|
||||
* 答题时间(秒)
|
||||
*/
|
||||
private Integer duration;
|
||||
/**
|
||||
* 是否代填:true-民警代填 false-本人填写 null-未知
|
||||
*/
|
||||
private Boolean isAgentFill;
|
||||
/**
|
||||
* 代填操作人ID(民警ID)
|
||||
*/
|
||||
private Long agentOperatorId;
|
||||
/**
|
||||
* 代填操作人姓名
|
||||
*/
|
||||
private String agentOperatorName;
|
||||
/**
|
||||
* 代填时间
|
||||
*/
|
||||
private LocalDateTime agentFillTime;
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,117 @@
|
||||
package cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
|
||||
/**
|
||||
* 问卷任务 DO
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@TableName("prison_questionnaire_task")
|
||||
@KeySequence("prison_questionnaire_task_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class QuestionnaireTaskDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
private String taskName;
|
||||
|
||||
// ==================== 问卷信息 ====================
|
||||
|
||||
/**
|
||||
* 问卷ID
|
||||
*/
|
||||
private Long questionnaireId;
|
||||
|
||||
/**
|
||||
* 问卷名称
|
||||
*/
|
||||
private String questionnaireName;
|
||||
|
||||
// ==================== 目标范围 ====================
|
||||
|
||||
/**
|
||||
* 目标类型:1-指定犯人 2-指定监区 3-全部犯人
|
||||
*/
|
||||
private Integer targetType;
|
||||
|
||||
/**
|
||||
* 监区ID(当targetType=2时)
|
||||
*/
|
||||
private Long areaId;
|
||||
|
||||
/**
|
||||
* 监区名称
|
||||
*/
|
||||
private String areaName;
|
||||
|
||||
/**
|
||||
* 犯人ID列表(当targetType=1时,JSON格式)
|
||||
*/
|
||||
private String prisonerIds;
|
||||
|
||||
// ==================== 时间设置 ====================
|
||||
|
||||
/**
|
||||
* 任务开始时间
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 截止时间
|
||||
*/
|
||||
private LocalDateTime deadline;
|
||||
|
||||
// ==================== 任务状态 ====================
|
||||
|
||||
/**
|
||||
* 状态:1-草稿 2-进行中 3-已结束 4-已取消
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
// ==================== 统计信息 ====================
|
||||
|
||||
/**
|
||||
* 目标总人数
|
||||
*/
|
||||
private Integer totalCount;
|
||||
|
||||
/**
|
||||
* 已完成人数
|
||||
*/
|
||||
private Integer completedCount;
|
||||
|
||||
/**
|
||||
* 待完成人数
|
||||
*/
|
||||
private Integer pendingCount;
|
||||
|
||||
/**
|
||||
* 完成率(%)
|
||||
*/
|
||||
private java.math.BigDecimal completionRate;
|
||||
|
||||
// ==================== 备注 ====================
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@ -54,6 +54,21 @@ public class QuestionnaireRecordDO extends TenantBaseDO {
|
||||
*/
|
||||
private String prisonerName;
|
||||
|
||||
// ==================== 任务关联 ====================
|
||||
|
||||
/**
|
||||
* 所属任务ID
|
||||
*/
|
||||
private Long taskId;
|
||||
/**
|
||||
* 犯人所属监区ID
|
||||
*/
|
||||
private Long prisonerAreaId;
|
||||
/**
|
||||
* 犯人所属监区名称
|
||||
*/
|
||||
private String prisonerAreaName;
|
||||
|
||||
// ==================== 测评状态 ====================
|
||||
|
||||
/**
|
||||
|
||||
@ -123,4 +123,18 @@ public interface ConsumptionMapper extends BaseMapperX<ConsumptionDO> {
|
||||
@ResultMap("ConsumptionDetailResultMap")
|
||||
List<ConsumptionRespVO> selectConsumptionDetailPage(@Param("reqVO") ConsumptionPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 查询罪犯最新账户余额
|
||||
*/
|
||||
@Select("""
|
||||
SELECT balance
|
||||
FROM prison_consumption
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND prisoner_id = #{prisonerId}
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
""")
|
||||
java.math.BigDecimal selectLatestBalance(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Results;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 监管看板 Mapper
|
||||
@ -18,6 +19,69 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface PrisonDashboardMapper {
|
||||
|
||||
/**
|
||||
* 查询最近6个月罪犯的汇款记录(收入)
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
DATE_FORMAT(arrive_date, '%Y-%m') AS month,
|
||||
SUM(amount) AS total_amount
|
||||
FROM prison_remittance
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND prisoner_id = #{prisonerId}
|
||||
AND arrive_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
GROUP BY DATE_FORMAT(arrive_date, '%Y-%m')
|
||||
ORDER BY month DESC
|
||||
""")
|
||||
List<Map<String, Object>> selectRecentRemittances(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
/**
|
||||
* 查询最近6个月罪犯的汇款详情(用于展示列表)
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
arrive_date,
|
||||
remitter_name,
|
||||
amount,
|
||||
relationship,
|
||||
remark
|
||||
FROM prison_remittance
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND prisoner_id = #{prisonerId}
|
||||
AND arrive_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
ORDER BY arrive_date DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
List<Map<String, Object>> selectRecentRemittanceDetails(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
/**
|
||||
* 查询最近6个月罪犯的风险评估记录
|
||||
*/
|
||||
@Select("""
|
||||
SELECT
|
||||
id,
|
||||
assessment_date,
|
||||
assessment_type,
|
||||
overall_score,
|
||||
risk_level,
|
||||
violence_risk,
|
||||
escape_risk,
|
||||
self_harm_risk,
|
||||
mental_state,
|
||||
recommendation,
|
||||
assessor_name
|
||||
FROM prison_risk
|
||||
WHERE deleted = 0
|
||||
AND status = 3
|
||||
AND prisoner_id = #{prisonerId}
|
||||
AND assessment_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
|
||||
ORDER BY assessment_date DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
List<Map<String, Object>> selectRecentRiskAssessments(@Param("prisonerId") Long prisonerId);
|
||||
|
||||
/**
|
||||
* 查询核心指标卡片数据
|
||||
*
|
||||
|
||||
@ -27,7 +27,8 @@ public interface QuestionMapper extends BaseMapperX<QuestionDO> {
|
||||
.eqIfPresent(QuestionDO::getSort, reqVO.getSort())
|
||||
.eqIfPresent(QuestionDO::getIsRequired, reqVO.getIsRequired())
|
||||
.betweenIfPresent(QuestionDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(QuestionDO::getId));
|
||||
.orderByAsc(QuestionDO::getPartSort)
|
||||
.orderByAsc(QuestionDO::getSort));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.QuestionnaireTaskPageReqVO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 问卷任务 Mapper
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Mapper
|
||||
public interface QuestionnaireTaskMapper extends BaseMapperX<QuestionnaireTaskDO> {
|
||||
|
||||
default PageResult<QuestionnaireTaskDO> selectPage(QuestionnaireTaskPageReqVO pageReqVO) {
|
||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<QuestionnaireTaskDO>()
|
||||
.likeIfPresent(QuestionnaireTaskDO::getTaskName, pageReqVO.getTaskName())
|
||||
.eqIfPresent(QuestionnaireTaskDO::getStatus, pageReqVO.getStatus())
|
||||
.eqIfPresent(QuestionnaireTaskDO::getQuestionnaireId, pageReqVO.getQuestionnaireId())
|
||||
.betweenIfPresent(QuestionnaireTaskDO::getCreateTime, pageReqVO.getCreateTime())
|
||||
.orderByDesc(QuestionnaireTaskDO::getCreateTime));
|
||||
}
|
||||
|
||||
@Select("SELECT qt.*, q.title as questionnaire_title FROM prison_questionnaire_task qt " +
|
||||
"LEFT JOIN prison_questionnaire q ON qt.questionnaire_id = q.id " +
|
||||
"WHERE qt.id = #{id}")
|
||||
Map<String, Object> selectTaskDetailById(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 更新任务统计信息
|
||||
* 注意:使用 @Select 是因为 MyBatis-Plus 需要返回结果,但这里我们只关心执行结果
|
||||
* 实际执行的是 UPDATE 操作,MyBatis 会忽略返回的查询结果
|
||||
* @param taskId 任务ID
|
||||
*/
|
||||
@Select("<script>" +
|
||||
"UPDATE prison_questionnaire_task qt " +
|
||||
"SET total_count = (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId}), " +
|
||||
"completed_count = (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId} AND r.status = 3), " +
|
||||
"pending_count = (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId} AND r.status IN (1, 2)), " +
|
||||
"completion_rate = CASE " +
|
||||
"WHEN (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId}) > 0 " +
|
||||
"THEN (SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId} AND r.status = 3) * 100.0 / " +
|
||||
"(SELECT COUNT(*) FROM prison_questionnaire_record r WHERE r.task_id = #{taskId}) " +
|
||||
"ELSE 0 END, " +
|
||||
"update_time = NOW() " +
|
||||
"WHERE qt.id = #{taskId}" +
|
||||
"</script>")
|
||||
void updateTaskStatistics(@Param("taskId") Long taskId);
|
||||
|
||||
@Select("SELECT COUNT(*) as task_count, " +
|
||||
"SUM(total_count) as total_prisoners, " +
|
||||
"SUM(completed_count) as total_completed, " +
|
||||
"AVG(completion_rate) as avg_completion_rate " +
|
||||
"FROM prison_questionnaire_task " +
|
||||
"WHERE deleted = 0 AND status IN (2, 3)")
|
||||
Map<String, Object> selectTaskStatisticsSummary();
|
||||
|
||||
@Select("SELECT qt.id as task_id, qt.task_name, " +
|
||||
"SUM(qr.total_count) as total_count, " +
|
||||
"SUM(qr.completed_count) as completed_count " +
|
||||
"FROM prison_questionnaire_task qt " +
|
||||
"INNER JOIN prison_questionnaire_record qr ON qt.id = qr.task_id " +
|
||||
"WHERE qt.deleted = 0 " +
|
||||
"AND qt.questionnaire_id = #{questionnaireId} " +
|
||||
"AND qr.area_id IN " +
|
||||
"<foreach item='id' collection='areaIds' open='(' separator=',' close=')'>" +
|
||||
"#{id}" +
|
||||
"</foreach> " +
|
||||
"GROUP BY qt.id, qt.task_name")
|
||||
List<Map<String, Object>> selectAreaComparisonByQuestionnaire(
|
||||
@Param("questionnaireId") Long questionnaireId,
|
||||
@Param("areaIds") List<Long> areaIds);
|
||||
|
||||
@Select("SELECT qt.id as task_id, qt.task_name, " +
|
||||
"SUM(qr.total_count) as total_count, " +
|
||||
"SUM(qr.completed_count) as completed_count " +
|
||||
"FROM prison_questionnaire_task qt " +
|
||||
"INNER JOIN prison_questionnaire_record qr ON qt.id = qr.task_id " +
|
||||
"WHERE qt.deleted = 0 " +
|
||||
"AND qr.area_id IN " +
|
||||
"<foreach item='id' collection='areaIds' open='(' separator=',' close=')'>" +
|
||||
"#{id}" +
|
||||
"</foreach> " +
|
||||
"GROUP BY qt.id, qt.task_name")
|
||||
List<Map<String, Object>> selectAreaComparisonByAreas(
|
||||
@Param("areaIds") List<Long> areaIds);
|
||||
|
||||
}
|
||||
@ -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<WarningDO> {
|
||||
.orderByDesc(WarningDO::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询预警信息(包含监区名称)
|
||||
*/
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT
|
||||
pw.id,
|
||||
pw.title,
|
||||
pw.content,
|
||||
pw.type,
|
||||
pw.level,
|
||||
pw.status,
|
||||
pw.source,
|
||||
pw.situation_id,
|
||||
pw.area_id,
|
||||
pa.name AS area_name,
|
||||
pw.cell_id,
|
||||
pw.alert_time,
|
||||
pw.verify_time,
|
||||
pw.verifier,
|
||||
pw.verify_result,
|
||||
pw.handle_time,
|
||||
pw.handler,
|
||||
pw.handle_method,
|
||||
pw.handle_result,
|
||||
pw.release_time,
|
||||
pw.releaser,
|
||||
pw.release_reason,
|
||||
pw.occur_time,
|
||||
pw.remark,
|
||||
pw.creator,
|
||||
pw.create_time,
|
||||
pw.updater,
|
||||
pw.update_time,
|
||||
pw.deleted,
|
||||
pw.tenant_id
|
||||
FROM prison_warning pw
|
||||
LEFT JOIN prison_area pa ON pw.area_id = pa.id
|
||||
WHERE pw.deleted = 0
|
||||
<if test="reqVO.title != null and reqVO.title != ''">
|
||||
AND pw.title LIKE CONCAT('%', #{reqVO.title}, '%')
|
||||
</if>
|
||||
<if test="reqVO.type != null">
|
||||
AND pw.type = #{reqVO.type}
|
||||
</if>
|
||||
<if test="reqVO.level != null">
|
||||
AND pw.level = #{reqVO.level}
|
||||
</if>
|
||||
<if test="reqVO.status != null">
|
||||
AND pw.status = #{reqVO.status}
|
||||
</if>
|
||||
<if test="reqVO.source != null">
|
||||
AND pw.source = #{reqVO.source}
|
||||
</if>
|
||||
<if test="reqVO.situationId != null">
|
||||
AND pw.situation_id = #{reqVO.situationId}
|
||||
</if>
|
||||
<if test="reqVO.areaId != null">
|
||||
AND pw.area_id = #{reqVO.areaId}
|
||||
</if>
|
||||
<if test="reqVO.cellId != null">
|
||||
AND pw.cell_id = #{reqVO.cellId}
|
||||
</if>
|
||||
<if test="reqVO.verifier != null and reqVO.verifier != ''">
|
||||
AND pw.verifier = #{reqVO.verifier}
|
||||
</if>
|
||||
<if test="reqVO.handler != null and reqVO.handler != ''">
|
||||
AND pw.handler = #{reqVO.handler}
|
||||
</if>
|
||||
<if test="reqVO.releaser != null and reqVO.releaser != ''">
|
||||
AND pw.releaser = #{reqVO.releaser}
|
||||
</if>
|
||||
<if test="reqVO.alertTime != null">
|
||||
AND pw.alert_time BETWEEN #{reqVO.alertTime[0]} AND #{reqVO.alertTime[1]}
|
||||
</if>
|
||||
<if test="reqVO.occurTime != null">
|
||||
AND pw.occur_time BETWEEN #{reqVO.occurTime[0]} AND #{reqVO.occurTime[1]}
|
||||
</if>
|
||||
<if test="reqVO.createTime != null">
|
||||
AND pw.create_time BETWEEN #{reqVO.createTime[0]} AND #{reqVO.createTime[1]}
|
||||
</if>
|
||||
ORDER BY pw.id DESC
|
||||
</script>
|
||||
""")
|
||||
@ResultMap("WarningResultMap")
|
||||
List<WarningRespVO> selectWarningPage(@Param("reqVO") WarningPageReqVO reqVO);
|
||||
|
||||
}
|
||||
|
||||
@ -89,4 +89,13 @@ public class ErrorCodeConstants {
|
||||
public static final ErrorCode REPORT_COMMENT_NOT_EXISTS = new ErrorCode(13_000_006, "快捷评语不存在");
|
||||
public static final ErrorCode PRISON_REPORT_TEMPLATE_NOT_EXISTS = new ErrorCode(13_000_007, "评估报告模板不存在");
|
||||
|
||||
// ========== 问卷任务 14xxxx ==========
|
||||
public static final ErrorCode QUESTIONNAIRE_TASK_NOT_EXISTS = new ErrorCode(14_000_001, "问卷任务不存在");
|
||||
public static final ErrorCode QUESTIONNAIRE_TASK_CANNOT_UPDATE = new ErrorCode(14_000_002, "只有草稿状态的任务可以修改");
|
||||
public static final ErrorCode QUESTIONNAIRE_TASK_CANNOT_CANCEL = new ErrorCode(14_000_003, "已结束或已取消的任务不能取消");
|
||||
public static final ErrorCode QUESTIONNAIRE_TASK_ALREADY_CANCELLED = new ErrorCode(14_000_004, "任务已被取消");
|
||||
public static final ErrorCode QUESTIONNAIRE_TASK_CANNOT_RESTART = new ErrorCode(14_000_005, "只有已结束的任务可以重新开始");
|
||||
public static final ErrorCode ERROR_TASK_PRISONER_EMPTY = new ErrorCode(14_000_006, "请选择要参与的犯人");
|
||||
public static final ErrorCode ERROR_TASK_AREA_EMPTY = new ErrorCode(14_000_007, "请选择监区");
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.prison.enums.questionnaire;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 问卷记录状态枚举
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum QuestionnaireRecordStatusEnum {
|
||||
|
||||
PENDING(1, "待完成"),
|
||||
IN_PROGRESS(2, "进行中"),
|
||||
COMPLETED(3, "已完成"),
|
||||
EXPIRED(4, "已过期"),
|
||||
CANCELLED(5, "已取消");
|
||||
|
||||
private final Integer code;
|
||||
private final String name;
|
||||
|
||||
public static QuestionnaireRecordStatusEnum getByCode(Integer code) {
|
||||
for (QuestionnaireRecordStatusEnum statusEnum : values()) {
|
||||
if (statusEnum.getCode().equals(code)) {
|
||||
return statusEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -76,8 +76,13 @@ public interface AnswerService {
|
||||
* @param questionnaireId 问卷ID
|
||||
* @param prisonerId 罪犯ID
|
||||
* @param answers 答题详情列表
|
||||
* @param isAgentFill 是否代填
|
||||
* @param agentOperatorId 代填操作人ID
|
||||
* @param agentOperatorName 代填操作人姓名
|
||||
*/
|
||||
void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId, List<AssessmentAnswerSubmitReqVO.AnswerItem> answers);
|
||||
void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId,
|
||||
List<AssessmentAnswerSubmitReqVO.AnswerItem> answers,
|
||||
Boolean isAgentFill, Long agentOperatorId, String agentOperatorName);
|
||||
|
||||
/**
|
||||
* 计算客观题得分并更新
|
||||
|
||||
@ -13,6 +13,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
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;
|
||||
@ -113,10 +115,13 @@ public class AnswerServiceImpl implements AnswerService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId,
|
||||
List<AssessmentAnswerSubmitReqVO.AnswerItem> answers) {
|
||||
List<AssessmentAnswerSubmitReqVO.AnswerItem> answers,
|
||||
Boolean isAgentFill, Long agentOperatorId, String agentOperatorName) {
|
||||
if (CollUtil.isEmpty(answers)) {
|
||||
return;
|
||||
}
|
||||
// 删除该测评已有的旧答题记录
|
||||
deleteByAssessmentRecordId(assessmentRecordId);
|
||||
|
||||
for (AssessmentAnswerSubmitReqVO.AnswerItem answerItem : answers) {
|
||||
// 获取问题信息
|
||||
@ -151,6 +156,16 @@ public class AnswerServiceImpl implements AnswerService {
|
||||
answer.setIsCorrect(isCorrect);
|
||||
}
|
||||
|
||||
// 设置代填信息
|
||||
if (Boolean.TRUE.equals(isAgentFill)) {
|
||||
answer.setIsAgentFill(true);
|
||||
answer.setAgentOperatorId(agentOperatorId);
|
||||
answer.setAgentOperatorName(agentOperatorName);
|
||||
answer.setAgentFillTime(LocalDateTime.now());
|
||||
} else {
|
||||
answer.setIsAgentFill(false);
|
||||
}
|
||||
|
||||
answerMapper.insert(answer);
|
||||
}
|
||||
}
|
||||
@ -206,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()) {
|
||||
@ -227,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;
|
||||
@ -248,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -318,6 +332,7 @@ public class AnswerServiceImpl implements AnswerService {
|
||||
|
||||
/**
|
||||
* 判断答案是否正确(单选/多选题)
|
||||
* 使用数组索引匹配选项(从0开始)
|
||||
*/
|
||||
private Boolean isAnswerCorrect(QuestionDO question, AssessmentAnswerSubmitReqVO.AnswerItem answerItem) {
|
||||
if (question.getOptions() == null || question.getOptions().isEmpty()) {
|
||||
@ -332,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) {
|
||||
@ -346,22 +361,23 @@ public class AnswerServiceImpl implements AnswerService {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<Long> correctOptionIds = new HashSet<>();
|
||||
Set<Long> userSelectedIds = new HashSet<>(answerItem.getOptionIds());
|
||||
// 收集正确选项的索引
|
||||
Set<Integer> correctOptionIndices = new HashSet<>();
|
||||
Set<Long> 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<Long> correctIndexSet = correctOptionIndices.stream().map(Long::valueOf).collect(Collectors.toSet());
|
||||
return correctIndexSet.equals(userSelectedIndices);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
|
||||
@ -108,8 +108,14 @@ public class CellServiceImpl implements CellService {
|
||||
if (CollUtil.isNotEmpty(areaIds)) {
|
||||
List<AreaDO> areas = areaMapper.selectList(new LambdaQueryWrapperX<AreaDO>()
|
||||
.inIfPresent(AreaDO::getId, new ArrayList<>(areaIds)));
|
||||
areaMap = areas.stream()
|
||||
.collect(Collectors.toMap(AreaDO::getId, Function.identity()));
|
||||
Map<Long, AreaDO> tempAreaMap = new HashMap<>();
|
||||
for (AreaDO area : areas) {
|
||||
if (area == null || area.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
tempAreaMap.putIfAbsent(area.getId(), area);
|
||||
}
|
||||
areaMap = tempAreaMap;
|
||||
} else {
|
||||
areaMap = new HashMap<>();
|
||||
}
|
||||
|
||||
@ -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<ConsumptionDO> 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());
|
||||
|
||||
@ -20,7 +20,6 @@ import cn.iocoder.yudao.module.prison.dal.mysql.situation.SituationMapper;
|
||||
import cn.iocoder.yudao.module.prison.enums.GenderEnum;
|
||||
import cn.iocoder.yudao.module.prison.service.dashboard.PrisonDashboardService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
@ -30,13 +29,14 @@ import org.springframework.util.StringUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Period;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -202,16 +202,37 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
}
|
||||
|
||||
// 入狱和出狱时间
|
||||
if (prisoner.getImprisonmentDate() != null) {
|
||||
if (prisoner.getImprisonmentDate() != null && prisoner.getReleaseDate() != null) {
|
||||
vo.setImprisonmentDate(prisoner.getImprisonmentDate().toString());
|
||||
vo.setReleaseDate(prisoner.getReleaseDate().toString());
|
||||
// 计算刑期总天数(从入监日期到释放日期)
|
||||
long sentenceDays = ChronoUnit.DAYS.between(prisoner.getImprisonmentDate(), prisoner.getReleaseDate());
|
||||
vo.setSentenceDays((int) sentenceDays);
|
||||
// 计算已服刑天数(从入监日期到当前日期)
|
||||
vo.setServedDays((int) ChronoUnit.DAYS.between(prisoner.getImprisonmentDate(), LocalDate.now()));
|
||||
// 计算剩余刑期天数(从当前日期到释放日期)
|
||||
long remainingDays = ChronoUnit.DAYS.between(LocalDate.now(), prisoner.getReleaseDate());
|
||||
vo.setRemainingDays(remainingDays > 0 ? (int) remainingDays : 0);
|
||||
} else if (prisoner.getImprisonmentDate() != null) {
|
||||
vo.setImprisonmentDate(prisoner.getImprisonmentDate().toString());
|
||||
vo.setServedDays((int) ChronoUnit.DAYS.between(prisoner.getImprisonmentDate(), LocalDate.now()));
|
||||
}
|
||||
if (prisoner.getReleaseDate() != null) {
|
||||
vo.setReleaseDate(prisoner.getReleaseDate().toString());
|
||||
if (prisoner.getReleaseDate() != null) {
|
||||
vo.setReleaseDate(prisoner.getReleaseDate().toString());
|
||||
long remainingDays = ChronoUnit.DAYS.between(LocalDate.now(), prisoner.getReleaseDate());
|
||||
vo.setRemainingDays(remainingDays > 0 ? (int) remainingDays : 0);
|
||||
}
|
||||
} else {
|
||||
vo.setRemainingDays(null);
|
||||
vo.setSentenceDays(null);
|
||||
}
|
||||
|
||||
// 监区信息(简单拼接)
|
||||
vo.setPrisonArea(prisoner.getAreaId() != null ? "监区" + prisoner.getAreaId() : "未分配");
|
||||
// 监区信息(查询实际监区名称)
|
||||
if (prisoner.getAreaId() != null) {
|
||||
AreaDO area = areaMapper.selectById(prisoner.getAreaId());
|
||||
vo.setPrisonArea(area != null ? area.getName() : "监区" + prisoner.getAreaId());
|
||||
} else {
|
||||
vo.setPrisonArea("未分配");
|
||||
}
|
||||
|
||||
// ==================== 查询关联数据 ====================
|
||||
|
||||
@ -233,7 +254,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
List<ConsumptionDO> monthlyConsumptions = consumptionMapper.selectList(new LambdaQueryWrapper<ConsumptionDO>()
|
||||
.eq(ConsumptionDO::getPrisonerId, prisonerId)
|
||||
.eq(ConsumptionDO::getStatus, 1)
|
||||
.eq(ConsumptionDO::getType, 1) // 消费类型
|
||||
// type: 1-购物 2-餐饮 3-医疗 4-通讯 5-其他,都属于消费
|
||||
.apply("YEAR(trade_time) = {0} AND MONTH(trade_time) = {1}", currentYear, currentMonth)
|
||||
.orderByDesc(ConsumptionDO::getTradeTime));
|
||||
|
||||
@ -243,9 +264,9 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.sum();
|
||||
|
||||
// 获取最新余额
|
||||
Double latestBalance = null;
|
||||
java.math.BigDecimal latestBalance = null;
|
||||
if (!monthlyConsumptions.isEmpty() && monthlyConsumptions.get(0).getBalance() != null) {
|
||||
latestBalance = monthlyConsumptions.get(0).getBalance().doubleValue();
|
||||
latestBalance = java.math.BigDecimal.valueOf(monthlyConsumptions.get(0).getBalance().doubleValue());
|
||||
}
|
||||
if (latestBalance == null) {
|
||||
ConsumptionDO latestConsumption = consumptionMapper.selectOne(new LambdaQueryWrapper<ConsumptionDO>()
|
||||
@ -254,7 +275,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.orderByDesc(ConsumptionDO::getTradeTime)
|
||||
.last("LIMIT 1"));
|
||||
if (latestConsumption != null && latestConsumption.getBalance() != null) {
|
||||
latestBalance = latestConsumption.getBalance().doubleValue();
|
||||
latestBalance = java.math.BigDecimal.valueOf(latestConsumption.getBalance().doubleValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,6 +304,9 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.orderByDesc(ConsumptionDO::getTradeTime)
|
||||
.last("LIMIT 20"));
|
||||
|
||||
// 5.2 查询最近6个月汇款记录(收入)
|
||||
List<Map<String, Object>> recentRemittances = dashboardMapper.selectRecentRemittances(prisonerId);
|
||||
|
||||
// 6. 查询最近6个月的危险评估
|
||||
List<RiskAssessmentDO> recentAssessments = riskAssessmentMapper.selectList(new LambdaQueryWrapper<RiskAssessmentDO>()
|
||||
.eq(RiskAssessmentDO::getPrisonerId, prisonerId)
|
||||
@ -290,14 +314,45 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.orderByDesc(RiskAssessmentDO::getAssessmentDate)
|
||||
.last("LIMIT 6"));
|
||||
|
||||
// 7. 查询狱情收集(心理访谈记录),按监区查询
|
||||
// 8. 查询狱情收集(心理访谈记录),按监区查询
|
||||
List<SituationDO> situations = situationMapper.selectList(new LambdaQueryWrapper<SituationDO>()
|
||||
.eq(SituationDO::getAreaId, prisoner.getAreaId())
|
||||
.eq(SituationDO::getStatus, 3) // 已处理
|
||||
.eq(SituationDO::getCategory, 2) // 教育改造类型
|
||||
.eq(SituationDO::getCategory, 1) // 心理访谈类型
|
||||
.orderByDesc(SituationDO::getOccurTime)
|
||||
.last("LIMIT 10"));
|
||||
|
||||
// 8. 查询累计违规次数(狱情收集-监管安全类型)
|
||||
Long violationCount = situationMapper.selectCount(new LambdaQueryWrapper<SituationDO>()
|
||||
.eq(SituationDO::getAreaId, prisoner.getAreaId())
|
||||
.eq(SituationDO::getStatus, 3) // 已处理
|
||||
.eq(SituationDO::getCategory, 1)); // 监管安全类型
|
||||
vo.setViolationCount(violationCount != null ? violationCount.intValue() : 0);
|
||||
|
||||
// 8.1 查询累计表扬次数(狱情收集-表扬类型)
|
||||
Long praiseCount = situationMapper.selectCount(new LambdaQueryWrapper<SituationDO>()
|
||||
.eq(SituationDO::getAreaId, prisoner.getAreaId())
|
||||
.eq(SituationDO::getStatus, 3) // 已处理
|
||||
.eq(SituationDO::getCategory, 2)); // 表扬类型
|
||||
vo.setPraiseCount(praiseCount != null ? praiseCount.intValue() : 0);
|
||||
|
||||
// 9. 查询累计计分考核记录(统计加分和扣分次数)
|
||||
List<ScoreDO> allScores = scoreMapper.selectList(new LambdaQueryWrapper<ScoreDO>()
|
||||
.eq(ScoreDO::getPrisonerId, prisonerId)
|
||||
.eq(ScoreDO::getStatus, 1)); // 已通过
|
||||
|
||||
long rewardCount = allScores.stream()
|
||||
.filter(s -> s.getRewardScore() != null && s.getRewardScore().doubleValue() > 0)
|
||||
.count();
|
||||
long penaltyCount = allScores.stream()
|
||||
.filter(s -> s.getPenaltyScore() != null && s.getPenaltyScore().doubleValue() > 0)
|
||||
.count();
|
||||
vo.setRewardCount((int) rewardCount);
|
||||
vo.setPenaltyCount((int) penaltyCount);
|
||||
|
||||
// 10. 设置累计表扬天数(固定返回"-")
|
||||
vo.setPraiseDays("-");
|
||||
|
||||
// ==================== 构建中心数据 ====================
|
||||
|
||||
// 左侧数据:本月消费、奖励、惩罚、余额
|
||||
@ -305,7 +360,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
double monthlyPenalty = 0;
|
||||
if (currentMonthScore != null) {
|
||||
monthlyReward = currentMonthScore.getRewardScore() != null ? currentMonthScore.getRewardScore().doubleValue() : 0;
|
||||
monthlyPenalty = currentMonthScore.getPenaltyScore() != null ? currentMonthScore.getPenaltyScore().doubleValue() : 0;
|
||||
monthlyPenalty = currentMonthScore.getPenaltyScore() != null ? Math.abs(currentMonthScore.getPenaltyScore().doubleValue()) : 0;
|
||||
}
|
||||
vo.setCenterLeftData(PrisonerDashboardStatsRespVO.CenterLeftData.builder()
|
||||
.topValue(String.valueOf((int) monthlyTotal))
|
||||
@ -339,7 +394,7 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.middleLeftLabel("基础分")
|
||||
.middleRightValue(String.valueOf((int) monthlyReward))
|
||||
.middleRightLabel("加分项")
|
||||
.bottomLeftValue(String.valueOf((int) monthlyPenalty))
|
||||
.bottomLeftValue(String.valueOf((int) Math.abs(monthlyPenalty)))
|
||||
.bottomLeftLabel("扣分项")
|
||||
.bottomRightValue(levelText)
|
||||
.bottomRightLabel("考核等级")
|
||||
@ -361,21 +416,59 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
|
||||
// ==================== 构建消费月度数据 ====================
|
||||
List<PrisonerDashboardStatsRespVO.MonthlyConsumptionData> monthlyDataList = new ArrayList<>();
|
||||
// 按月份汇总消费
|
||||
// 按月份汇总消费(支出)和汇款(收入)
|
||||
Map<String, Double> monthlyConsumptionMap = recentConsumptions.stream()
|
||||
.filter(c -> c.getTradeTime() != null)
|
||||
.collect(Collectors.groupingBy(
|
||||
c -> c.getTradeTime().getYear() + "-" + String.format("%02d", c.getTradeTime().getMonthValue()),
|
||||
Collectors.summingDouble(c -> c.getTotalAmount() != null ? c.getTotalAmount().doubleValue() : 0)
|
||||
));
|
||||
|
||||
// 解析汇款数据(收入)
|
||||
Map<String, Double> monthlyIncomeMap = new java.util.HashMap<>();
|
||||
for (Map<String, Object> remittance : recentRemittances) {
|
||||
Object monthObj = remittance.get("month");
|
||||
Object amountObj = remittance.get("total_amount");
|
||||
if (monthObj != null && amountObj != null) {
|
||||
String month = monthObj.toString();
|
||||
double amount = Double.parseDouble(amountObj.toString());
|
||||
monthlyIncomeMap.merge(month, amount, Double::sum);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建月度数据:monthlyStandard=支出,perCapita=收入
|
||||
for (Map.Entry<String, Double> entry : monthlyConsumptionMap.entrySet()) {
|
||||
String month = entry.getKey();
|
||||
double consumption = entry.getValue();
|
||||
double income = monthlyIncomeMap.getOrDefault(month, 0.0);
|
||||
monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder()
|
||||
.category(entry.getKey())
|
||||
.perCapita(entry.getValue().intValue())
|
||||
.category(month)
|
||||
.monthlyStandard((int) consumption) // 支出
|
||||
.perCapita((int) income) // 收入
|
||||
.build());
|
||||
}
|
||||
|
||||
// 如果某月只有收入没有消费,也添加进去
|
||||
for (Map.Entry<String, Double> entry : monthlyIncomeMap.entrySet()) {
|
||||
String month = entry.getKey();
|
||||
if (!monthlyConsumptionMap.containsKey(month)) {
|
||||
monthlyDataList.add(PrisonerDashboardStatsRespVO.MonthlyConsumptionData.builder()
|
||||
.category(month)
|
||||
.monthlyStandard(0) // 支出
|
||||
.perCapita(entry.getValue().intValue()) // 收入
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
// 按月份排序(从左到右:时间正序 1月→2月→3月→...)
|
||||
monthlyDataList.sort((a, b) -> a.getCategory().compareTo(b.getCategory()));
|
||||
|
||||
vo.setConsumptionMonthlyData(monthlyDataList);
|
||||
|
||||
// ==================== 查询账户余额 ====================
|
||||
latestBalance = consumptionMapper.selectLatestBalance(prisonerId);
|
||||
vo.setBalance(latestBalance != null ? latestBalance.intValue() : 0);
|
||||
|
||||
// ==================== 构建计分考核记录 ====================
|
||||
List<PrisonerDashboardStatsRespVO.ScoreRecord> scoreRecords = recentScores.stream()
|
||||
.map(s -> {
|
||||
@ -402,11 +495,24 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
List<PrisonerDashboardStatsRespVO.ConsumptionRecord> consumptionRecords = recentConsumptions.stream()
|
||||
.limit(10)
|
||||
.map(c -> {
|
||||
String typeName = c.getType() == 1 ? "消费" : "存款";
|
||||
// type: 1-购物 2-餐饮 3-医疗 4-通讯 5-其他
|
||||
String typeName = "消费";
|
||||
String nameColor = "#f56c6c"; // 红色-消费
|
||||
if (c.getType() == null) {
|
||||
typeName = "未知";
|
||||
} else if (c.getType() == 2) {
|
||||
typeName = "餐饮";
|
||||
} else if (c.getType() == 3) {
|
||||
typeName = "医疗";
|
||||
} else if (c.getType() == 4) {
|
||||
typeName = "通讯";
|
||||
} else if (c.getType() == 5) {
|
||||
typeName = "其他";
|
||||
}
|
||||
return PrisonerDashboardStatsRespVO.ConsumptionRecord.builder()
|
||||
.date(c.getTradeTime() != null ? c.getTradeTime().toLocalDate().toString() : "")
|
||||
.name(typeName)
|
||||
.nameColor(c.getType() == 1 ? "#f56c6c" : "#67c23a")
|
||||
.nameColor(nameColor)
|
||||
.category("普通消费")
|
||||
.amount(c.getTotalAmount() != null ? c.getTotalAmount().intValue() : 0)
|
||||
.build();
|
||||
@ -414,16 +520,41 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.collect(Collectors.toList());
|
||||
vo.setConsumptionRecords(consumptionRecords);
|
||||
|
||||
// ==================== 构建危险评估记录 ====================
|
||||
List<PrisonerDashboardStatsRespVO.RewardsPunishment> rewardsPunishments = recentAssessments.stream()
|
||||
.map(a -> {
|
||||
// ==================== 构建风险评估记录 ====================
|
||||
List<Map<String, Object>> riskList = dashboardMapper.selectRecentRiskAssessments(prisonerId);
|
||||
List<PrisonerDashboardStatsRespVO.RewardsPunishment> rewardsPunishments = riskList.stream()
|
||||
.map(r -> {
|
||||
Object dateObj = r.get("assessment_date");
|
||||
Object scoreObj = r.get("overall_score");
|
||||
Object levelObj = r.get("risk_level");
|
||||
Object violenceObj = r.get("violence_risk");
|
||||
Object escapeObj = r.get("escape_risk");
|
||||
Object selfHarmObj = r.get("self_harm_risk");
|
||||
Object mentalObj = r.get("mental_state");
|
||||
Object assessorObj = r.get("assessor_name");
|
||||
|
||||
String date = dateObj != null ? dateObj.toString().substring(0, 10) : "";
|
||||
String localLevelText = "低风险";
|
||||
if (levelObj != null) {
|
||||
int level = Integer.parseInt(levelObj.toString());
|
||||
localLevelText = switch (level) {
|
||||
case 1 -> "低风险";
|
||||
case 2 -> "中风险";
|
||||
case 3 -> "高风险";
|
||||
case 4 -> "极高风险";
|
||||
default -> "未知";
|
||||
};
|
||||
}
|
||||
|
||||
String content = String.format("综合得分: %s | 风险等级: %s",
|
||||
scoreObj != null ? scoreObj.toString() : "0",
|
||||
localLevelText);
|
||||
|
||||
return PrisonerDashboardStatsRespVO.RewardsPunishment.builder()
|
||||
.date(a.getAssessmentDate() != null ? a.getAssessmentDate().toString() : "")
|
||||
.date(date)
|
||||
.type("danger")
|
||||
.typeText("危险评估")
|
||||
.content("暴力倾向:" + (a.getViolenceScore() != null ? a.getViolenceScore() : 0) +
|
||||
" | 脱逃倾向:" + (a.getEscapeScore() != null ? a.getEscapeScore() : 0) +
|
||||
" | 自杀倾向:" + (a.getSuicideScore() != null ? a.getSuicideScore() : 0))
|
||||
.typeText("风险评估")
|
||||
.content(content)
|
||||
.build();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
@ -440,16 +571,21 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
vo.setInterviewRecords(interviewRecords);
|
||||
|
||||
// ==================== 构建汇款记录 ====================
|
||||
List<PrisonerDashboardStatsRespVO.RemittanceRecord> remittanceRecords = recentConsumptions.stream()
|
||||
.filter(c -> c.getType() == 2) // 存款类型
|
||||
.limit(10)
|
||||
.map(c -> PrisonerDashboardStatsRespVO.RemittanceRecord.builder()
|
||||
.date(c.getTradeTime() != null ? c.getTradeTime().toLocalDate().toString() : "")
|
||||
.name("存款")
|
||||
.nameColor("#409eff")
|
||||
.category("亲属汇款")
|
||||
.amount(c.getTotalAmount() != null ? c.getTotalAmount().intValue() : 0)
|
||||
.build())
|
||||
// 查询最近6个月的汇款记录(详情)
|
||||
List<Map<String, Object>> remittanceList = dashboardMapper.selectRecentRemittanceDetails(prisonerId);
|
||||
List<PrisonerDashboardStatsRespVO.RemittanceRecord> remittanceRecords = remittanceList.stream()
|
||||
.map(r -> {
|
||||
Object dateObj = r.get("arrive_date");
|
||||
Object nameObj = r.get("remitter_name");
|
||||
Object amountObj = r.get("amount");
|
||||
return PrisonerDashboardStatsRespVO.RemittanceRecord.builder()
|
||||
.date(dateObj != null ? dateObj.toString() : "")
|
||||
.name(nameObj != null ? nameObj.toString() : "")
|
||||
.nameColor("#67c23a")
|
||||
.category("汇款")
|
||||
.amount(amountObj != null ? new java.math.BigDecimal(amountObj.toString()).intValue() : 0)
|
||||
.build();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
vo.setRemittanceRecords(remittanceRecords);
|
||||
|
||||
@ -493,12 +629,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.orderByDesc(EvaluationReportDO::getEvaluationDate));
|
||||
|
||||
// 按罪犯ID分组,取最新的评估记录
|
||||
Map<Long, EvaluationReportDO> latestReportMap = allReports.stream()
|
||||
.collect(Collectors.toMap(
|
||||
EvaluationReportDO::getPrisonerId,
|
||||
r -> r,
|
||||
(existing, replacement) -> existing // 保留第一个(最新的)
|
||||
));
|
||||
Map<Long, EvaluationReportDO> latestReportMap = new HashMap<>();
|
||||
for (EvaluationReportDO report : allReports) {
|
||||
if (report == null || report.getPrisonerId() == null) {
|
||||
continue;
|
||||
}
|
||||
latestReportMap.putIfAbsent(report.getPrisonerId(), report); // 保留第一条(最新的)
|
||||
}
|
||||
List<EvaluationReportDO> latestReports = new ArrayList<>(latestReportMap.values());
|
||||
|
||||
// 统计各风险等级人数
|
||||
@ -528,14 +665,21 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.toList();
|
||||
|
||||
// 按罪犯ID分组,取本月第一次评估
|
||||
Map<Long, EvaluationReportDO> thisMonthNewMap = thisMonthReports.stream()
|
||||
.filter(r -> !latestReportMap.containsKey(r.getPrisonerId()) ||
|
||||
latestReportMap.get(r.getPrisonerId()).getEvaluationDate().toLocalDate().isAfter(firstDayOfMonth.minusDays(1)))
|
||||
.collect(Collectors.toMap(
|
||||
EvaluationReportDO::getPrisonerId,
|
||||
r -> r,
|
||||
(existing, replacement) -> existing
|
||||
));
|
||||
Map<Long, EvaluationReportDO> thisMonthNewMap = new HashMap<>();
|
||||
for (EvaluationReportDO report : thisMonthReports) {
|
||||
if (report == null || report.getPrisonerId() == null) {
|
||||
continue;
|
||||
}
|
||||
EvaluationReportDO latestReport = latestReportMap.get(report.getPrisonerId());
|
||||
if (latestReport == null) {
|
||||
thisMonthNewMap.putIfAbsent(report.getPrisonerId(), report);
|
||||
continue;
|
||||
}
|
||||
if (latestReport.getEvaluationDate() != null
|
||||
&& latestReport.getEvaluationDate().toLocalDate().isAfter(firstDayOfMonth.minusDays(1))) {
|
||||
thisMonthNewMap.putIfAbsent(report.getPrisonerId(), report);
|
||||
}
|
||||
}
|
||||
|
||||
int monthlyNewHighRisk = 0, monthlyNewWarning = 0, monthlyNewNormal = 0;
|
||||
for (EvaluationReportDO report : thisMonthNewMap.values()) {
|
||||
@ -583,12 +727,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
!r.getEvaluationDate().toLocalDate().isAfter(monthEnd))
|
||||
.toList();
|
||||
|
||||
Map<Long, EvaluationReportDO> monthEndLatestMap = monthEndReports.stream()
|
||||
.collect(Collectors.toMap(
|
||||
EvaluationReportDO::getPrisonerId,
|
||||
r -> r,
|
||||
(existing, replacement) -> existing
|
||||
));
|
||||
Map<Long, EvaluationReportDO> monthEndLatestMap = new HashMap<>();
|
||||
for (EvaluationReportDO report : monthEndReports) {
|
||||
if (report == null || report.getPrisonerId() == null) {
|
||||
continue;
|
||||
}
|
||||
monthEndLatestMap.putIfAbsent(report.getPrisonerId(), report);
|
||||
}
|
||||
|
||||
int monthHigh = 0, monthWarning = 0, monthNormal = 0;
|
||||
for (EvaluationReportDO report : monthEndLatestMap.values()) {
|
||||
@ -644,12 +789,13 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
List<EvaluationReportDO> allReports = evaluationReportMapper.selectList(wrapper);
|
||||
|
||||
// 按罪犯ID去重,保留最新的评估记录
|
||||
Map<Long, EvaluationReportDO> latestReportMap = allReports.stream()
|
||||
.collect(Collectors.toMap(
|
||||
EvaluationReportDO::getPrisonerId,
|
||||
r -> r,
|
||||
(existing, replacement) -> existing
|
||||
));
|
||||
Map<Long, EvaluationReportDO> latestReportMap = new HashMap<>();
|
||||
for (EvaluationReportDO report : allReports) {
|
||||
if (report == null || report.getPrisonerId() == null) {
|
||||
continue;
|
||||
}
|
||||
latestReportMap.putIfAbsent(report.getPrisonerId(), report);
|
||||
}
|
||||
List<EvaluationReportDO> uniqueReports = new ArrayList<>(latestReportMap.values());
|
||||
|
||||
// 手动分页
|
||||
@ -661,10 +807,22 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
// 获取罪犯详细信息
|
||||
List<Long> prisonerIds = pagedReports.stream()
|
||||
.map(EvaluationReportDO::getPrisonerId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<Long, PrisonerDO> prisonerMap = prisonerIds.isEmpty() ? Map.of() :
|
||||
prisonerMapper.selectBatchIds(prisonerIds).stream()
|
||||
.collect(Collectors.toMap(PrisonerDO::getId, p -> p));
|
||||
Map<Long, PrisonerDO> prisonerMap;
|
||||
if (prisonerIds.isEmpty()) {
|
||||
prisonerMap = Map.of();
|
||||
} else {
|
||||
Map<Long, PrisonerDO> tempPrisonerMap = new HashMap<>();
|
||||
for (PrisonerDO prisoner : prisonerMapper.selectBatchIds(prisonerIds)) {
|
||||
if (prisoner == null || prisoner.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
tempPrisonerMap.putIfAbsent(prisoner.getId(), prisoner);
|
||||
}
|
||||
prisonerMap = tempPrisonerMap;
|
||||
}
|
||||
|
||||
// 获取监区信息
|
||||
List<Long> areaIds = pagedReports.stream()
|
||||
@ -672,9 +830,19 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.filter(id -> id != null)
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<Long, AreaDO> areaMap = areaIds.isEmpty() ? Map.of() :
|
||||
areaMapper.selectBatchIds(areaIds).stream()
|
||||
.collect(Collectors.toMap(AreaDO::getId, a -> a));
|
||||
Map<Long, AreaDO> areaMap;
|
||||
if (areaIds.isEmpty()) {
|
||||
areaMap = Map.of();
|
||||
} else {
|
||||
Map<Long, AreaDO> tempAreaMap = new HashMap<>();
|
||||
for (AreaDO area : areaMapper.selectBatchIds(areaIds)) {
|
||||
if (area == null || area.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
tempAreaMap.putIfAbsent(area.getId(), area);
|
||||
}
|
||||
areaMap = tempAreaMap;
|
||||
}
|
||||
|
||||
// 判断本月新增
|
||||
LocalDate firstDayOfMonth = LocalDate.now().withDayOfMonth(1);
|
||||
|
||||
@ -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<Long> dimensionIds);
|
||||
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -48,6 +49,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 +65,7 @@ import static cn.iocoder.yudao.module.prison.enums.EvaluationAiStatusEnum.PENDIN
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class EvaluationReportServiceImpl implements EvaluationReportService {
|
||||
|
||||
/**
|
||||
@ -415,6 +418,9 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createReport(EvaluationReportSaveReqVO createReqVO) {
|
||||
EvaluationReportDO report = BeanUtils.toBean(createReqVO, EvaluationReportDO.class);
|
||||
if (createReqVO.getEvaluationDate() != null) {
|
||||
report.setEvaluationDate(createReqVO.getEvaluationDate().atStartOfDay());
|
||||
}
|
||||
// 生成报告编号
|
||||
report.setReportNo(generateReportNo());
|
||||
// 初始状态为草稿
|
||||
@ -434,6 +440,9 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
|
||||
public void updateReport(EvaluationReportSaveReqVO updateReqVO) {
|
||||
validateReportExists(updateReqVO.getId());
|
||||
EvaluationReportDO updateObj = BeanUtils.toBean(updateReqVO, EvaluationReportDO.class);
|
||||
if (updateReqVO.getEvaluationDate() != null) {
|
||||
updateObj.setEvaluationDate(updateReqVO.getEvaluationDate().atStartOfDay());
|
||||
}
|
||||
evaluationReportMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@ -924,4 +933,238 @@ public class EvaluationReportServiceImpl implements EvaluationReportService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void generateReportByAi(Long reportId, List<Long> dimensionIds) {
|
||||
// 获取报告信息
|
||||
EvaluationReportDO report = evaluationReportMapper.selectById(reportId);
|
||||
if (report == null) {
|
||||
throw exception(EVALUATION_REPORT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 获取报告关联的模板和维度
|
||||
List<EvaluationDimensionDO> 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<EvaluationDimensionDO>()
|
||||
.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<EvaluationDimensionDataDO>()
|
||||
.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<String, Object> prisoner = dataSources.getPrisoner();
|
||||
if (prisoner != null && !prisoner.isEmpty()) {
|
||||
prompt.append("【罪犯基本信息】\n");
|
||||
prompt.append(cn.hutool.json.JSONUtil.toJsonStr(prisoner)).append("\n\n");
|
||||
}
|
||||
|
||||
// 添加消费数据
|
||||
Map<String, Object> consumption = dataSources.getConsumptionSummary();
|
||||
if (consumption != null && !consumption.isEmpty()) {
|
||||
prompt.append("【消费情况】\n");
|
||||
prompt.append(cn.hutool.json.JSONUtil.toJsonStr(consumption)).append("\n\n");
|
||||
}
|
||||
|
||||
// 添加计分考核数据
|
||||
Map<String, Object> score = dataSources.getScoreSummary();
|
||||
if (score != null && !score.isEmpty()) {
|
||||
prompt.append("【计分考核情况】\n");
|
||||
prompt.append(cn.hutool.json.JSONUtil.toJsonStr(score)).append("\n\n");
|
||||
}
|
||||
|
||||
// 添加风险评估数据
|
||||
Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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<PrisonerRespVO> getPrisonerPage(PrisonerPageReqVO reqVO) {
|
||||
LambdaQueryWrapper<PrisonerDO> 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())
|
||||
|
||||
@ -0,0 +1,169 @@
|
||||
package cn.iocoder.yudao.module.prison.service.questionnaire_task;
|
||||
|
||||
import java.util.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire.QuestionnaireDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.area.AreaDO;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* 问卷任务 Service 接口
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
public interface QuestionnaireTaskService {
|
||||
|
||||
// ==================== 基础 CRUD ====================
|
||||
|
||||
/**
|
||||
* 创建问卷任务
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 任务ID
|
||||
*/
|
||||
Long createQuestionnaireTask(@Valid QuestionnaireTaskCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新问卷任务
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateQuestionnaireTask(@Valid QuestionnaireTaskUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除问卷任务
|
||||
*
|
||||
* @param id 任务ID
|
||||
*/
|
||||
void deleteQuestionnaireTask(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除问卷任务
|
||||
*
|
||||
* @param ids 任务ID列表
|
||||
*/
|
||||
void deleteQuestionnaireTaskListByIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得问卷任务
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @return 问卷任务
|
||||
*/
|
||||
QuestionnaireTaskDO getQuestionnaireTask(Long id);
|
||||
|
||||
/**
|
||||
* 获得问卷任务分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 问卷任务分页
|
||||
*/
|
||||
PageResult<QuestionnaireTaskDO> getQuestionnaireTaskPage(QuestionnaireTaskPageReqVO pageReqVO);
|
||||
|
||||
// ==================== 任务执行相关 ====================
|
||||
|
||||
/**
|
||||
* 取消任务
|
||||
*
|
||||
* @param id 任务ID
|
||||
*/
|
||||
void cancelTask(Long id);
|
||||
|
||||
/**
|
||||
* 结束任务
|
||||
*
|
||||
* @param id 任务ID
|
||||
*/
|
||||
void finishTask(Long id);
|
||||
|
||||
/**
|
||||
* 重新开始已结束的任务
|
||||
*
|
||||
* @param id 任务ID
|
||||
*/
|
||||
void restartTask(Long id);
|
||||
|
||||
// ==================== 进度跟踪相关 ====================
|
||||
|
||||
/**
|
||||
* 获取任务进度详情
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @return 进度详情
|
||||
*/
|
||||
TaskProgressRespVO getTaskProgress(Long id);
|
||||
|
||||
/**
|
||||
* 获取任务未完成人员列表
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @param pageReqVO 分页参数
|
||||
* @return 未完成人员分页
|
||||
*/
|
||||
PageResult<QuestionnaireRecordDO> getPendingPrisoners(Long id, PageParam pageReqVO);
|
||||
|
||||
/**
|
||||
* 获取任务的人员填写进度列表
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @return 人员填写进度列表
|
||||
*/
|
||||
List<PrisonerProgressRespVO> getPrisonerProgress(Long id);
|
||||
|
||||
/**
|
||||
* 提醒未完成人员
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @return 提醒人数
|
||||
*/
|
||||
Integer remindPendingPrisoners(Long id);
|
||||
|
||||
/**
|
||||
* 通知单个人员
|
||||
*
|
||||
* @param recordId 记录ID
|
||||
*/
|
||||
void notifyPrisoner(Long recordId);
|
||||
|
||||
/**
|
||||
* 重置人员答题记录
|
||||
*
|
||||
* @param recordId 记录ID
|
||||
*/
|
||||
void resetPrisonerRecord(Long recordId);
|
||||
|
||||
/**
|
||||
* 按监区统计任务完成情况
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @return 监区统计列表
|
||||
*/
|
||||
List<TaskAreaStatisticsRespVO> getTaskAreaStatistics(Long id);
|
||||
|
||||
/**
|
||||
* 获取全局任务统计汇总
|
||||
*
|
||||
* @return 统计汇总
|
||||
*/
|
||||
TaskStatisticsSummaryRespVO getStatisticsSummary();
|
||||
|
||||
/**
|
||||
* 按监区对比分析
|
||||
*
|
||||
* @param questionnaireId 问卷ID(可选,为空则统计所有问卷)
|
||||
* @param areaIds 监区ID列表
|
||||
* @return 对比数据
|
||||
*/
|
||||
List<AreaComparisonRespVO> compareAreasByQuestionnaire(Long questionnaireId, List<Long> areaIds);
|
||||
|
||||
}
|
||||
@ -0,0 +1,587 @@
|
||||
package cn.iocoder.yudao.module.prison.service.questionnaire_task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire.QuestionnaireDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.PrisonerDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.area.AreaDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task.QuestionnaireTaskMapper;
|
||||
import cn.iocoder.yudao.module.prison.dal.mysql.questionnaire.QuestionnaireMapper;
|
||||
import cn.iocoder.yudao.module.prison.dal.mysql.questionnairerecord.QuestionnaireRecordMapper;
|
||||
import cn.iocoder.yudao.module.prison.dal.mysql.PrisonerMapper;
|
||||
import cn.iocoder.yudao.module.prison.dal.mysql.area.AreaMapper;
|
||||
import cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants;
|
||||
import cn.iocoder.yudao.module.prison.enums.questionnaire.QuestionnaireRecordStatusEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 问卷任务 Service 实现类
|
||||
*
|
||||
* @author xlcp
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService {
|
||||
|
||||
@Resource
|
||||
private QuestionnaireTaskMapper questionnaireTaskMapper;
|
||||
|
||||
@Resource
|
||||
private QuestionnaireMapper questionnaireMapper;
|
||||
|
||||
@Resource
|
||||
private QuestionnaireRecordMapper questionnaireRecordMapper;
|
||||
|
||||
@Resource
|
||||
private PrisonerMapper prisonerMapper;
|
||||
|
||||
@Resource
|
||||
private AreaMapper areaMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createQuestionnaireTask(QuestionnaireTaskCreateReqVO createReqVO) {
|
||||
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())
|
||||
.questionnaireName(questionnaire.getTitle())
|
||||
.targetType(createReqVO.getTargetType())
|
||||
.startTime(createReqVO.getStartTime())
|
||||
.deadline(createReqVO.getDeadline())
|
||||
.status(2) // 进行中
|
||||
.totalCount(0)
|
||||
.completedCount(0)
|
||||
.pendingCount(0)
|
||||
.completionRate(BigDecimal.ZERO)
|
||||
.remark(createReqVO.getRemark())
|
||||
.build();
|
||||
|
||||
// 设置目标范围
|
||||
if (createReqVO.getTargetType() == 1) {
|
||||
task.setPrisonerIds(CollUtil.join(createReqVO.getPrisonerIds(), ","));
|
||||
} else if (createReqVO.getTargetType() == 2) {
|
||||
task.setAreaId(createReqVO.getAreaId());
|
||||
AreaDO area = areaMapper.selectById(createReqVO.getAreaId());
|
||||
if (area != null) {
|
||||
task.setAreaName(area.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// 插入任务记录
|
||||
questionnaireTaskMapper.insert(task);
|
||||
log.info("问卷任务创建成功: taskId={}", task.getId());
|
||||
|
||||
// 获取目标罪犯列表并创建答题记录
|
||||
List<PrisonerDO> prisoners = getTargetPrisoners(createReqVO);
|
||||
if (CollUtil.isNotEmpty(prisoners)) {
|
||||
log.info("为任务 {} 创建 {} 条答题记录", task.getId(), prisoners.size());
|
||||
createRecordsForPrisoners(task, prisoners);
|
||||
} else {
|
||||
log.warn("任务 {} 没有符合条件的目标罪犯", task.getId());
|
||||
}
|
||||
|
||||
// 更新任务统计信息
|
||||
try {
|
||||
updateTaskStatistics(task.getId());
|
||||
log.info("任务 {} 统计信息更新成功", task.getId());
|
||||
} catch (Exception e) {
|
||||
log.error("任务 {} 统计信息更新失败: {}", task.getId(), e.getMessage(), e);
|
||||
// 统计更新失败不应影响任务创建
|
||||
}
|
||||
|
||||
return task.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateQuestionnaireTask(QuestionnaireTaskUpdateReqVO updateReqVO) {
|
||||
QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(updateReqVO.getId());
|
||||
if (task == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS);
|
||||
}
|
||||
|
||||
if (!task.getStatus().equals(1)) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_CANNOT_UPDATE);
|
||||
}
|
||||
|
||||
if (updateReqVO.getTaskName() != null) {
|
||||
task.setTaskName(updateReqVO.getTaskName());
|
||||
}
|
||||
if (updateReqVO.getDeadline() != null) {
|
||||
task.setDeadline(updateReqVO.getDeadline());
|
||||
}
|
||||
if (updateReqVO.getRemark() != null) {
|
||||
task.setRemark(updateReqVO.getRemark());
|
||||
}
|
||||
|
||||
questionnaireTaskMapper.updateById(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteQuestionnaireTask(Long id) {
|
||||
QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id);
|
||||
if (task == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS);
|
||||
}
|
||||
|
||||
questionnaireRecordMapper.delete(new LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id));
|
||||
|
||||
questionnaireTaskMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteQuestionnaireTaskListByIds(List<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> records = questionnaireRecordMapper.selectList(
|
||||
new LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, ids));
|
||||
|
||||
if (CollUtil.isNotEmpty(records)) {
|
||||
questionnaireRecordMapper.deleteBatchIds(records.stream()
|
||||
.map(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getId)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
questionnaireTaskMapper.deleteBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuestionnaireTaskDO getQuestionnaireTask(Long id) {
|
||||
return questionnaireTaskMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<QuestionnaireTaskDO> getQuestionnaireTaskPage(QuestionnaireTaskPageReqVO pageReqVO) {
|
||||
return questionnaireTaskMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<QuestionnaireTaskDO>()
|
||||
.likeIfPresent(QuestionnaireTaskDO::getTaskName, pageReqVO.getTaskName())
|
||||
.eqIfPresent(QuestionnaireTaskDO::getStatus, pageReqVO.getStatus())
|
||||
.eqIfPresent(QuestionnaireTaskDO::getQuestionnaireId, pageReqVO.getQuestionnaireId())
|
||||
.betweenIfPresent(QuestionnaireTaskDO::getCreateTime, pageReqVO.getCreateTime())
|
||||
.orderByDesc(QuestionnaireTaskDO::getCreateTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelTask(Long id) {
|
||||
QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id);
|
||||
if (task == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS);
|
||||
}
|
||||
|
||||
if (task.getStatus().equals(3) || task.getStatus().equals(4)) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_CANNOT_CANCEL);
|
||||
}
|
||||
|
||||
task.setStatus(4);
|
||||
questionnaireTaskMapper.updateById(task);
|
||||
|
||||
questionnaireRecordMapper.update(null,
|
||||
new LambdaUpdateWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.PENDING.getCode())
|
||||
.set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.CANCELLED.getCode()));
|
||||
|
||||
updateTaskStatistics(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void finishTask(Long id) {
|
||||
QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id);
|
||||
if (task == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS);
|
||||
}
|
||||
|
||||
if (task.getStatus().equals(4)) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_ALREADY_CANCELLED);
|
||||
}
|
||||
|
||||
task.setStatus(3);
|
||||
questionnaireTaskMapper.updateById(task);
|
||||
|
||||
questionnaireRecordMapper.update(null,
|
||||
new LambdaUpdateWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)
|
||||
.in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus,
|
||||
QuestionnaireRecordStatusEnum.PENDING.getCode(),
|
||||
QuestionnaireRecordStatusEnum.IN_PROGRESS.getCode())
|
||||
.set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.EXPIRED.getCode()));
|
||||
|
||||
updateTaskStatistics(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void restartTask(Long id) {
|
||||
QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id);
|
||||
if (task == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS);
|
||||
}
|
||||
|
||||
if (!task.getStatus().equals(3)) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_CANNOT_RESTART);
|
||||
}
|
||||
|
||||
task.setStatus(2);
|
||||
task.setDeadline(LocalDateTime.now().plusDays(7));
|
||||
questionnaireTaskMapper.updateById(task);
|
||||
|
||||
questionnaireRecordMapper.update(null,
|
||||
new LambdaUpdateWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.EXPIRED.getCode())
|
||||
.set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.PENDING.getCode())
|
||||
.set(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getDeadline, task.getDeadline()));
|
||||
|
||||
updateTaskStatistics(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskProgressRespVO getTaskProgress(Long id) {
|
||||
QuestionnaireTaskDO task = questionnaireTaskMapper.selectById(id);
|
||||
if (task == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_TASK_NOT_EXISTS);
|
||||
}
|
||||
|
||||
Map<String, Object> stats = questionnaireRecordMapper.selectMaps(
|
||||
new QueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.select("COUNT(*) as total",
|
||||
"SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as pending",
|
||||
"SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as in_progress",
|
||||
"SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as completed",
|
||||
"SUM(CASE WHEN status = 4 THEN 1 ELSE 0 END) as expired",
|
||||
"SUM(CASE WHEN status = 5 THEN 1 ELSE 0 END) as cancelled")
|
||||
.eq("task_id", id)
|
||||
).stream().findFirst().orElse(Collections.emptyMap());
|
||||
|
||||
int total = ((Number) stats.getOrDefault("total", 0)).intValue();
|
||||
int pending = ((Number) stats.getOrDefault("pending", 0)).intValue();
|
||||
int inProgress = ((Number) stats.getOrDefault("in_progress", 0)).intValue();
|
||||
int completed = ((Number) stats.getOrDefault("completed", 0)).intValue();
|
||||
|
||||
return TaskProgressRespVO.builder()
|
||||
.taskId(id)
|
||||
.taskName(task.getTaskName())
|
||||
.questionnaireName(task.getQuestionnaireName())
|
||||
.status(task.getStatus())
|
||||
.startTime(task.getStartTime())
|
||||
.deadline(task.getDeadline())
|
||||
.totalCount(total)
|
||||
.completedCount(completed)
|
||||
.pendingCount(pending + inProgress)
|
||||
.completionRate(total > 0 ? BigDecimal.valueOf(completed * 100.0 / total).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO)
|
||||
.statusBreakdown(TaskProgressRespVO.StatusBreakdown.builder()
|
||||
.pending(pending)
|
||||
.inProgress(inProgress)
|
||||
.completed(completed)
|
||||
.expired(((Number) stats.getOrDefault("expired", 0)).intValue())
|
||||
.cancelled(((Number) stats.getOrDefault("cancelled", 0)).intValue())
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> getPendingPrisoners(Long id, PageParam pageReqVO) {
|
||||
Page<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
||||
|
||||
LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> wrapper = new LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)
|
||||
.in(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus,
|
||||
QuestionnaireRecordStatusEnum.PENDING.getCode(),
|
||||
QuestionnaireRecordStatusEnum.IN_PROGRESS.getCode())
|
||||
.orderByAsc(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getCreateTime);
|
||||
|
||||
Page<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> result = questionnaireRecordMapper.selectPage(page, wrapper);
|
||||
return new PageResult<>(result.getRecords(), result.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer remindPendingPrisoners(Long id) {
|
||||
List<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> pendingRecords = questionnaireRecordMapper.selectList(
|
||||
new LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.PENDING.getCode())
|
||||
);
|
||||
|
||||
if (CollUtil.isEmpty(pendingRecords)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
log.info("提醒任务 {} 中 {} 位未完成人员", id, pendingRecords.size());
|
||||
return pendingRecords.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrisonerProgressRespVO> getPrisonerProgress(Long id) {
|
||||
List<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> records = questionnaireRecordMapper.selectList(
|
||||
new LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)
|
||||
.orderByAsc(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getCreateTime)
|
||||
);
|
||||
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return records.stream().map(record -> {
|
||||
PrisonerProgressRespVO vo = new PrisonerProgressRespVO();
|
||||
vo.setQuestionnaireId(record.getQuestionnaireId());
|
||||
vo.setQuestionnaireName(record.getQuestionnaireName());
|
||||
vo.setId(record.getId());
|
||||
vo.setPrisonerId(record.getPrisonerId());
|
||||
vo.setPrisonerNo(record.getPrisonerNo());
|
||||
vo.setPrisonerName(record.getPrisonerName());
|
||||
vo.setAreaId(record.getPrisonerAreaId());
|
||||
vo.setAreaName(record.getPrisonerAreaName());
|
||||
vo.setStatus(record.getStatus());
|
||||
vo.setObjectiveScore(record.getObjectiveScore() != null ? record.getObjectiveScore().intValue() : null);
|
||||
vo.setSubjectiveScore(record.getSubjectiveScore() != null ? record.getSubjectiveScore().intValue() : null);
|
||||
vo.setTotalScore(record.getTotalScore() != null ? record.getTotalScore().intValue() : null);
|
||||
vo.setRiskLevel(record.getRiskLevel());
|
||||
vo.setStartTime(record.getStartTime());
|
||||
vo.setFinishTime(record.getEndTime());
|
||||
vo.setDuration(record.getDuration());
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPrisoner(Long recordId) {
|
||||
cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(recordId);
|
||||
if (record == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_RECORD_NOT_EXISTS);
|
||||
}
|
||||
// TODO: 发送站内信通知,此处为占位实现
|
||||
log.info("通知任务 {} 中的罪犯 {} 完成问卷", record.getTaskId(), record.getPrisonerName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPrisonerRecord(Long recordId) {
|
||||
cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(recordId);
|
||||
if (record == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_RECORD_NOT_EXISTS);
|
||||
}
|
||||
// 重置记录状态为待测评
|
||||
cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO updateRecord = new cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO();
|
||||
updateRecord.setId(recordId);
|
||||
updateRecord.setStatus(QuestionnaireRecordStatusEnum.PENDING.getCode());
|
||||
updateRecord.setObjectiveScore(null);
|
||||
updateRecord.setSubjectiveScore(null);
|
||||
updateRecord.setTotalScore(null);
|
||||
updateRecord.setRiskLevel(null);
|
||||
updateRecord.setStartTime(null);
|
||||
updateRecord.setEndTime(null);
|
||||
updateRecord.setDuration(null);
|
||||
questionnaireRecordMapper.updateById(updateRecord);
|
||||
log.info("重置任务 {} 中的罪犯 {} 答题记录", record.getTaskId(), record.getPrisonerName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TaskAreaStatisticsRespVO> getTaskAreaStatistics(Long id) {
|
||||
List<Map<String, Object>> stats = questionnaireRecordMapper.selectMaps(
|
||||
new QueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.select("prisoner_area_id", "prisoner_area_name",
|
||||
"COUNT(*) as total_count",
|
||||
"SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as completed_count",
|
||||
"AVG(CASE WHEN status = 3 THEN total_score ELSE NULL END) as avg_score",
|
||||
"SUM(CASE WHEN status = 3 AND pass_status = 1 THEN 1 ELSE 0 END) * 100.0 / NULLIF(SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END), 0) as pass_rate",
|
||||
"SUM(CASE WHEN risk_level = 1 THEN 1 ELSE 0 END) as high_risk_count",
|
||||
"SUM(CASE WHEN risk_level = 2 THEN 1 ELSE 0 END) as medium_risk_count",
|
||||
"SUM(CASE WHEN risk_level = 3 THEN 1 ELSE 0 END) as low_risk_count")
|
||||
.eq("task_id", id)
|
||||
.groupBy("prisoner_area_id", "prisoner_area_name")
|
||||
.orderByDesc("completed_count")
|
||||
);
|
||||
|
||||
return stats.stream().map(stat -> {
|
||||
int total = ((Number) stat.getOrDefault("total_count", 0)).intValue();
|
||||
int completed = ((Number) stat.getOrDefault("completed_count", 0)).intValue();
|
||||
BigDecimal avgScore = stat.get("avg_score") != null ?
|
||||
new BigDecimal(stat.get("avg_score").toString()) : BigDecimal.ZERO;
|
||||
BigDecimal passRate = stat.get("pass_rate") != null ?
|
||||
new BigDecimal(stat.get("pass_rate").toString()) : BigDecimal.ZERO;
|
||||
|
||||
return TaskAreaStatisticsRespVO.builder()
|
||||
.areaId(stat.get("prisoner_area_id") != null ? ((Number) stat.get("prisoner_area_id")).longValue() : null)
|
||||
.areaName((String) stat.get("prisoner_area_name"))
|
||||
.totalCount(total)
|
||||
.completedCount(completed)
|
||||
.completionRate(total > 0 ? BigDecimal.valueOf(completed * 100.0 / total).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO)
|
||||
.avgScore(avgScore.setScale(2, RoundingMode.HALF_UP))
|
||||
.passRate(passRate.setScale(2, RoundingMode.HALF_UP))
|
||||
.riskDistribution(TaskAreaStatisticsRespVO.RiskDistribution.builder()
|
||||
.highRisk(((Number) stat.getOrDefault("high_risk_count", 0)).intValue())
|
||||
.mediumRisk(((Number) stat.getOrDefault("medium_risk_count", 0)).intValue())
|
||||
.lowRisk(((Number) stat.getOrDefault("low_risk_count", 0)).intValue())
|
||||
.build())
|
||||
.build();
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskStatisticsSummaryRespVO getStatisticsSummary() {
|
||||
Map<String, Object> stats = questionnaireTaskMapper.selectMaps(
|
||||
new QueryWrapper<QuestionnaireTaskDO>()
|
||||
.select("COUNT(*) as task_count",
|
||||
"SUM(total_count) as total_prisoners",
|
||||
"SUM(completed_count) as total_completed",
|
||||
"AVG(completion_rate) as avg_completion_rate")
|
||||
.eq("deleted", 0)
|
||||
.in("status", Arrays.asList(2, 3))
|
||||
).stream().findFirst().orElse(Collections.emptyMap());
|
||||
|
||||
int taskCount = ((Number) stats.getOrDefault("task_count", 0)).intValue();
|
||||
int totalPrisoners = ((Number) stats.getOrDefault("total_prisoners", 0)).intValue();
|
||||
int totalCompleted = ((Number) stats.getOrDefault("total_completed", 0)).intValue();
|
||||
|
||||
return TaskStatisticsSummaryRespVO.builder()
|
||||
.taskCount(taskCount)
|
||||
.totalPrisoners(totalPrisoners)
|
||||
.totalCompleted(totalCompleted)
|
||||
.totalPending(totalPrisoners - totalCompleted)
|
||||
.overallCompletionRate(totalPrisoners > 0 ?
|
||||
BigDecimal.valueOf(totalCompleted * 100.0 / totalPrisoners).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AreaComparisonRespVO> compareAreasByQuestionnaire(Long questionnaireId, List<Long> areaIds) {
|
||||
if (CollUtil.isEmpty(areaIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Map<String, Object>> stats;
|
||||
if (questionnaireId != null) {
|
||||
stats = questionnaireTaskMapper.selectAreaComparisonByQuestionnaire(questionnaireId, areaIds);
|
||||
} else {
|
||||
stats = questionnaireTaskMapper.selectAreaComparisonByAreas(areaIds);
|
||||
}
|
||||
|
||||
return stats.stream().map(stat -> AreaComparisonRespVO.builder()
|
||||
.taskId(((Number) stat.get("task_id")).longValue())
|
||||
.taskName((String) stat.get("task_name"))
|
||||
.totalCount(((Number) stat.getOrDefault("total_count", 0)).intValue())
|
||||
.completedCount(((Number) stat.getOrDefault("completed_count", 0)).intValue())
|
||||
.build()
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void validateTarget(QuestionnaireTaskCreateReqVO createReqVO) {
|
||||
if (createReqVO.getTargetType() == 1) {
|
||||
if (CollUtil.isEmpty(createReqVO.getPrisonerIds())) {
|
||||
throw new ServiceException(ErrorCodeConstants.ERROR_TASK_PRISONER_EMPTY);
|
||||
}
|
||||
} else if (createReqVO.getTargetType() == 2) {
|
||||
if (createReqVO.getAreaId() == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.ERROR_TASK_AREA_EMPTY);
|
||||
}
|
||||
AreaDO area = areaMapper.selectById(createReqVO.getAreaId());
|
||||
if (area == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.AREA_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<PrisonerDO> getTargetPrisoners(QuestionnaireTaskCreateReqVO createReqVO) {
|
||||
switch (createReqVO.getTargetType()) {
|
||||
case 1:
|
||||
return prisonerMapper.selectBatchIds(createReqVO.getPrisonerIds());
|
||||
case 2:
|
||||
return prisonerMapper.selectList(
|
||||
new LambdaQueryWrapper<PrisonerDO>()
|
||||
.eq(PrisonerDO::getPrisonAreaId, createReqVO.getAreaId())
|
||||
.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<PrisonerDO>()
|
||||
.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();
|
||||
}
|
||||
}
|
||||
|
||||
private void createRecordsForPrisoners(QuestionnaireTaskDO task, List<PrisonerDO> prisoners) {
|
||||
QuestionnaireDO questionnaire = questionnaireMapper.selectById(task.getQuestionnaireId());
|
||||
|
||||
List<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> records = prisoners.stream()
|
||||
.filter(p -> p.getStatus() != null && p.getStatus().getValue() == 1)
|
||||
.map(prisoner -> cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO.builder()
|
||||
.questionnaireId(task.getQuestionnaireId())
|
||||
.questionnaireName(task.getQuestionnaireName())
|
||||
.prisonerId(prisoner.getId())
|
||||
.prisonerNo(prisoner.getPrisonerNo())
|
||||
.prisonerName(prisoner.getName())
|
||||
.prisonerAreaId(prisoner.getPrisonAreaId())
|
||||
.prisonerAreaName(getAreaName(prisoner.getPrisonAreaId()))
|
||||
.taskId(task.getId())
|
||||
.status(QuestionnaireRecordStatusEnum.PENDING.getCode())
|
||||
.startTime(task.getStartTime())
|
||||
.deadline(task.getDeadline())
|
||||
.passScore(questionnaire != null ? questionnaire.getPassScore() : null)
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (CollUtil.isNotEmpty(records)) {
|
||||
questionnaireRecordMapper.insertBatch(records);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAreaName(Long areaId) {
|
||||
if (areaId == null) {
|
||||
return null;
|
||||
}
|
||||
AreaDO area = areaMapper.selectById(areaId);
|
||||
return area != null ? area.getName() : null;
|
||||
}
|
||||
|
||||
private void updateTaskStatistics(Long taskId) {
|
||||
questionnaireTaskMapper.updateTaskStatistics(taskId);
|
||||
}
|
||||
|
||||
}
|
||||
@ -86,6 +86,13 @@ public interface QuestionnaireRecordService {
|
||||
*/
|
||||
void submitAnswer(@Valid AssessmentAnswerSubmitReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 代为提交答卷(民警代填)
|
||||
*
|
||||
* @param reqVO 提交信息,包含代填操作人信息
|
||||
*/
|
||||
void submitAnswerByAgent(@Valid AssessmentAnswerSubmitReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 结束测评
|
||||
*
|
||||
|
||||
@ -49,6 +49,9 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic
|
||||
@Resource
|
||||
private AnswerService answerService;
|
||||
|
||||
@Resource
|
||||
private cn.iocoder.yudao.module.prison.dal.mysql.questionnaire_task.QuestionnaireTaskMapper questionnaireTaskMapper;
|
||||
|
||||
@Override
|
||||
public Long createQuestionnaireRecord(QuestionnaireRecordSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
@ -161,6 +164,25 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitAnswer(AssessmentAnswerSubmitReqVO reqVO) {
|
||||
submitAnswerInternal(reqVO, false, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitAnswerByAgent(AssessmentAnswerSubmitReqVO reqVO) {
|
||||
// 获取当前登录的民警信息
|
||||
Long currentUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
String currentUserName = SecurityFrameworkUtils.getLoginUser().getInfo() != null
|
||||
? SecurityFrameworkUtils.getLoginUser().getInfo().get("nickname")
|
||||
: "未知";
|
||||
submitAnswerInternal(reqVO, true, currentUserId, currentUserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部提交答卷方法
|
||||
*/
|
||||
private void submitAnswerInternal(AssessmentAnswerSubmitReqVO reqVO, Boolean isAgentFill,
|
||||
Long agentOperatorId, String agentOperatorName) {
|
||||
QuestionnaireRecordDO record = validateQuestionnaireRecordExists(reqVO.getRecordId());
|
||||
if (!QuestionnaireRecordStatusEnum.IN_PROGRESS.getStatus().equals(record.getStatus())) {
|
||||
throw exception(QUESTIONNAIRE_RECORD_STATUS_ERROR);
|
||||
@ -171,7 +193,8 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic
|
||||
record.setDuration((int) java.time.Duration.between(record.getStartTime(), LocalDateTime.now()).getSeconds());
|
||||
}
|
||||
// 保存答题记录并计算客观题得分
|
||||
answerService.saveAnswers(reqVO.getRecordId(), record.getQuestionnaireId(), reqVO.getPrisonerId(), reqVO.getAnswers());
|
||||
answerService.saveAnswers(reqVO.getRecordId(), record.getQuestionnaireId(), reqVO.getPrisonerId(),
|
||||
reqVO.getAnswers(), isAgentFill, agentOperatorId, agentOperatorName);
|
||||
// 计算客观题得分
|
||||
BigDecimal objectiveScore = answerService.calculateObjectiveScore(reqVO.getRecordId());
|
||||
record.setObjectiveScore(objectiveScore);
|
||||
|
||||
@ -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<SituationDO> 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
|
||||
|
||||
@ -58,7 +58,7 @@ public interface WarningService {
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 预警信息分页
|
||||
*/
|
||||
PageResult<WarningDO> getWarningPage(WarningPageReqVO pageReqVO);
|
||||
PageResult<WarningRespVO> getWarningPage(WarningPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 导入预警信息
|
||||
|
||||
@ -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<WarningDO> getWarningPage(WarningPageReqVO pageReqVO) {
|
||||
return warningMapper.selectPage(pageReqVO);
|
||||
public PageResult<WarningRespVO> getWarningPage(WarningPageReqVO pageReqVO) {
|
||||
// 使用联表查询获取带 areaName 的数据
|
||||
List<WarningRespVO> 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<WarningRespVO> pageList = fromIndex < total
|
||||
? list.subList(fromIndex, Math.min(toIndex, (int) total))
|
||||
: Collections.emptyList();
|
||||
return new PageResult<>(pageList, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.prison.dal.mysql.warning.WarningMapper">
|
||||
|
||||
<resultMap id="WarningResultMap" type="cn.iocoder.yudao.module.prison.controller.admin.warning.vo.WarningRespVO" autoMapping="true">
|
||||
<id property="id" column="id"/>
|
||||
<result property="areaId" column="area_id"/>
|
||||
<result property="areaName" column="area_name"/>
|
||||
<result property="cellId" column="cell_id"/>
|
||||
<result property="situationId" column="situation_id"/>
|
||||
<result property="alertTime" column="alert_time"/>
|
||||
<result property="verifyTime" column="verify_time"/>
|
||||
<result property="handleTime" column="handle_time"/>
|
||||
<result property="releaseTime" column="release_time"/>
|
||||
<result property="occurTime" column="occur_time"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,110 @@
|
||||
-- =====================================================
|
||||
-- XL监狱综合管理平台 - 问卷代填权限SQL
|
||||
-- 生成时间: 2026-01-26
|
||||
-- 功能: 添加问卷代填按钮权限
|
||||
-- =====================================================
|
||||
|
||||
-- =====================================================
|
||||
-- 方法1: 自动查找并插入(推荐)
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 查找问卷记录管理(assessment-record)的父菜单ID
|
||||
SELECT @parentId := parent_id
|
||||
FROM system_menu
|
||||
WHERE name = '测评记录管理' AND type = 2 AND deleted = 0
|
||||
LIMIT 1;
|
||||
|
||||
-- 如果找不到,尝试查找 questionnaire-record
|
||||
SELECT @parentId := id
|
||||
FROM system_menu
|
||||
WHERE path = 'assessment-record' AND type = 2 AND deleted = 0
|
||||
LIMIT 1;
|
||||
|
||||
-- 2. 插入代填权限
|
||||
INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
SELECT '代为填写', 'prison:questionnaire-record:agent-fill', 3, 8, @parentId, '', '', '', 0
|
||||
WHERE @parentId IS NOT NULL;
|
||||
|
||||
-- 3. 验证插入结果
|
||||
SELECT * FROM system_menu
|
||||
WHERE permission = 'prison:questionnaire-record:agent-fill';
|
||||
|
||||
-- =====================================================
|
||||
-- 方法2: 手动指定父菜单ID插入
|
||||
-- =====================================================
|
||||
-- 请将 5047 替换为实际的测评记录管理父菜单ID
|
||||
|
||||
-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
-- VALUES ('代为填写', 'prison:questionnaire-record:agent-fill', 3, 8, 5047, '', '', '', 0);
|
||||
|
||||
-- =====================================================
|
||||
-- 方法3: 完整的查找和插入脚本(包含错误处理)
|
||||
-- =====================================================
|
||||
|
||||
-- 设置临时变量
|
||||
SET @parentId := NULL;
|
||||
|
||||
-- 查找问卷记录管理菜单的父ID
|
||||
SELECT parent_id INTO @parentId
|
||||
FROM system_menu
|
||||
WHERE name = '测评记录管理'
|
||||
AND type = 2
|
||||
AND deleted = 0
|
||||
ORDER BY id DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- 如果没找到,尝试通过path查找
|
||||
IF @parentId IS NULL THEN
|
||||
SELECT parent_id INTO @parentId
|
||||
FROM system_menu
|
||||
WHERE path = 'assessment-record'
|
||||
AND type = 2
|
||||
AND deleted = 0
|
||||
ORDER BY id DESC
|
||||
LIMIT 1;
|
||||
END IF;
|
||||
|
||||
-- 如果还是没找到,输出提示
|
||||
SELECT
|
||||
CASE
|
||||
WHEN @parentId IS NULL THEN '未找到测评记录管理菜单,请手动检查!'
|
||||
ELSE CONCAT('找到父菜单ID: ', @parentId, ',即将插入代填权限...')
|
||||
END AS result;
|
||||
|
||||
-- 插入代填权限(使用 INSERT IGNORE 避免重复插入)
|
||||
INSERT IGNORE INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
|
||||
VALUES (
|
||||
'代为填写',
|
||||
'prison:questionnaire-record:agent-fill',
|
||||
3, -- type=3 表示按钮
|
||||
8, -- sort=8 排在最后(根据现有排序调整)
|
||||
@parentId, -- 父菜单ID
|
||||
'', -- path
|
||||
'', -- icon
|
||||
'', -- component
|
||||
0, -- status
|
||||
b'1', -- visible
|
||||
b'0', -- keep_alive
|
||||
b'1', -- always_show
|
||||
'system',
|
||||
NOW(),
|
||||
'',
|
||||
NOW(),
|
||||
b'0'
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- 验证结果
|
||||
-- =====================================================
|
||||
|
||||
-- 查看所有 questionnaire-record 相关的权限
|
||||
SELECT id, name, permission, type, sort, parent_id
|
||||
FROM system_menu
|
||||
WHERE (permission LIKE 'prison:questionnaire-record:%'
|
||||
OR permission LIKE 'prison:assessment-record:%')
|
||||
AND deleted = 0
|
||||
ORDER BY parent_id, sort;
|
||||
|
||||
-- 查看插入的代填权限
|
||||
SELECT * FROM system_menu
|
||||
WHERE permission = 'prison:questionnaire-record:agent-fill';
|
||||
@ -0,0 +1,35 @@
|
||||
-- ================================================
|
||||
-- 代填功能数据库迁移脚本
|
||||
-- 添加 prison_answer 表的代填相关字段
|
||||
-- ================================================
|
||||
|
||||
-- 检查字段是否已存在,如果不存在则添加
|
||||
-- 注意:MySQL 语法
|
||||
|
||||
-- 1. 添加是否代填字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `is_agent_fill` TINYINT(1) NULL COMMENT '是否代填:1-是,0-否,null-未知' AFTER `duration`;
|
||||
|
||||
-- 2. 添加代填操作人ID字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `agent_operator_id` BIGINT NULL COMMENT '代填操作人ID(民警ID)' AFTER `is_agent_fill`;
|
||||
|
||||
-- 3. 添加代填操作人姓名字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `agent_operator_name` VARCHAR(100) NULL COMMENT '代填操作人姓名' AFTER `agent_operator_id`;
|
||||
|
||||
-- 4. 添加代填时间字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `agent_fill_time` DATETIME NULL COMMENT '代填时间' AFTER `agent_operator_name`;
|
||||
|
||||
-- ================================================
|
||||
-- 回滚脚本(如需回滚)
|
||||
-- ================================================
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `is_agent_fill`;
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `agent_operator_id`;
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `agent_operator_name`;
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `agent_fill_time`;
|
||||
@ -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;
|
||||
@ -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`),
|
||||
|
||||
609
yudao-module-prison/src/main/resources/sql/prison_module.sql.bak
Normal file
609
yudao-module-prison/src/main/resources/sql/prison_module.sql.bak
Normal file
@ -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());
|
||||
@ -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: 使用注释中的兼容性写法
|
||||
-- =====================================================
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -95,16 +95,15 @@
|
||||
<artifactId>justauth-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 注释掉微信登录依赖(监狱系统不需要)
|
||||
<!-- 微信登录依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
|
||||
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
|
||||
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.anji-plus</groupId>
|
||||
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit bf6875adf6a99d90ca95f84a6045aea1cc8779c8
|
||||
@ -130,11 +130,11 @@
|
||||
</dependency>
|
||||
|
||||
<!-- 开发工具 - 热启动 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-devtools</artifactId>-->
|
||||
<!-- <optional>true</optional>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- 服务保障相关 -->
|
||||
<dependency>
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user