From dc65ef8d24215bf6fa70dbef9d6676cdb020b753 Mon Sep 17 00:00:00 2001 From: tangweijie <877588133@qq.com> Date: Thu, 15 Jan 2026 22:35:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E8=AF=84?= =?UTF-8?q?=E4=BC=B0=E6=A8=A1=E5=9D=97=E4=B8=BA=E7=AD=94=E9=A2=98=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=EF=BC=8C=E6=95=B4=E5=90=88=E5=88=B0=E9=97=AE=E5=8D=B7?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要变更: - 删除 assessment 模块,原有功能整合到 questionnaire-record - 新增 answer 模块处理答题记录 - QuestionnaireRecordServiceImpl 扩展测评执行、评分、统计功能 - 更新枚举类状态定义(1-待测评 2-测评中 3-已完成 4-已过期 5-已取消) - 消费记录模块新增明细相关接口 Co-Authored-By: Claude --- .../admin/answer/PrisonAnswerController.java | 112 ++++++ .../admin/answer/vo/AnswerPageReqVO.java | 31 ++ .../admin/answer/vo/AnswerRespVO.java | 54 +++ .../admin/answer/vo/AnswerSaveReqVO.java | 49 +++ .../AssessmentAnswerController.java | 145 ------- .../AssessmentRecordController.java | 134 ------- .../AssessmentResultController.java | 143 ------- .../AssessmentStatisticsController.java | 95 ----- .../vo/AssessmentAnswerPageReqVO.java | 47 --- .../assessment/vo/AssessmentAnswerRespVO.java | 91 ----- .../vo/AssessmentAnswerSaveReqVO.java | 78 ---- .../vo/AssessmentAnswerSubmitReqVO.java | 24 -- .../vo/AssessmentRecordPageReqVO.java | 44 --- .../assessment/vo/AssessmentRecordRespVO.java | 81 ---- .../vo/AssessmentRecordSaveReqVO.java | 65 --- .../assessment/vo/AssessmentReportRespVO.java | 71 ---- .../vo/AssessmentResultManualReviewReqVO.java | 34 -- .../vo/AssessmentResultPageReqVO.java | 44 --- .../assessment/vo/AssessmentResultRespVO.java | 90 ----- .../vo/AssessmentResultSaveReqVO.java | 80 ---- .../vo/AssessmentStatisticsPageReqVO.java | 26 -- .../vo/AssessmentStatisticsRespVO.java | 76 ---- .../PrisonConsumptionController.java | 55 ++- .../vo/ConsumptionDetailRespVO.java | 41 ++ .../vo/ConsumptionDetailSaveReqVO.java | 34 ++ .../consumption/vo/ConsumptionPageReqVO.java | 35 +- .../consumption/vo/ConsumptionRespVO.java | 47 +-- .../consumption/vo/ConsumptionSaveReqVO.java | 41 +- .../PrisonQuestionnaireController.java | 4 +- .../PrisonQuestionnaireRecordController.java | 99 ++++- .../vo/AssessmentAnswerSubmitReqVO.java | 38 ++ .../vo/AssessmentInitiateReqVO.java | 32 ++ .../vo/AssessmentManualScoreReqVO.java} | 13 +- .../vo/QuestionnaireRecordPageReqVO.java | 37 +- .../vo/QuestionnaireRecordRespVO.java | 101 ++++- .../vo/QuestionnaireRecordSaveReqVO.java | 91 ++++- .../PrisonRiskAssessmentController.java | 104 ----- .../prison/convert/answer/AnswerConvert.java | 44 +++ .../assessment/AssessmentAnswerConvert.java | 34 -- .../assessment/AssessmentRecordConvert.java | 34 -- .../assessment/AssessmentResultConvert.java | 34 -- .../AssessmentStatisticsConvert.java | 30 -- .../consumption/ConsumptionConvert.java | 31 ++ .../consumption/ConsumptionDetailConvert.java | 28 ++ .../dal/dataobject/answer/AnswerDO.java | 71 ++++ .../assessment/AssessmentAnswerDO.java | 131 ------ .../assessment/AssessmentRecordDO.java | 116 ------ .../assessment/AssessmentResultDO.java | 136 ------- .../assessment/AssessmentStatisticsDO.java | 115 ------ .../dataobject/consumption/ConsumptionDO.java | 44 +-- .../consumption/ConsumptionDetailDO.java | 58 +++ .../QuestionnaireRecordDO.java | 111 +++++- .../prison/dal/mysql/answer/AnswerMapper.java | 57 +++ .../assessment/AssessmentAnswerMapper.java | 51 --- .../assessment/AssessmentRecordMapper.java | 33 -- .../assessment/AssessmentResultMapper.java | 71 ---- .../AssessmentStatisticsMapper.java | 37 -- .../consumption/ConsumptionDetailMapper.java | 43 ++ .../mysql/consumption/ConsumptionMapper.java | 10 +- .../QuestionnaireRecordMapper.java | 1 - .../prison/enums/ConsumptionStatusEnum.java | 49 +++ .../prison/enums/ConsumptionTypeEnum.java | 52 +++ .../prison/enums/ErrorCodeConstants.java | 7 + .../QuestionnaireRecordPassStatusEnum.java | 10 +- .../enums/QuestionnaireRecordStatusEnum.java | 16 +- .../AssessmentPassStatusEnum.java | 37 ++ .../AssessmentRecordStatusEnum.java | 39 ++ .../enums/questionnaire/RiskLevelEnum.java | 37 ++ .../prison/service/answer/AnswerService.java | 97 +++++ .../service/answer/AnswerServiceImpl.java | 373 +++++++++++++++++ .../assessment/AssessmentAnswerService.java | 110 ------ .../assessment/AssessmentRecordService.java | 91 ----- .../assessment/AssessmentResultService.java | 106 ----- .../AssessmentStatisticsService.java | 73 ---- .../impl/AssessmentAnswerServiceImpl.java | 163 -------- .../impl/AssessmentRecordServiceImpl.java | 133 ------- .../impl/AssessmentResultServiceImpl.java | 141 ------- .../impl/AssessmentStatisticsServiceImpl.java | 198 ---------- .../consumption/ConsumptionService.java | 39 +- .../consumption/ConsumptionServiceImpl.java | 110 ++++-- .../QuestionnaireServiceImpl.java | 4 +- .../QuestionnaireRecordService.java | 95 ++++- .../QuestionnaireRecordServiceImpl.java | 233 ++++++++++- .../AssessmentDataAggregator.java | 374 ------------------ .../AssessmentPromptBuilder.java | 246 ------------ .../dto/LlmAssessmentResult.java | 108 ----- .../riskassessment/llm/ClaudeLlmClient.java | 239 ----------- .../service/riskassessment/llm/LlmClient.java | 147 ++++--- .../riskassessment/llm/LlmClientFactory.java | 104 ++--- .../riskassessment/llm/LlmException.java | 20 +- .../llm/OpenAiCompatibleClient.java | 173 ++++++++ .../src/main/resources/sql/answer_module.sql | 59 +++ .../resources/sql/assessment_migration.sql | 250 ++++++++++++ .../sql/assessment_migration_simple.sql | 171 ++++++++ .../sql/consumption_migration_20260115.sql | 71 ++++ .../src/main/resources/sql/prison_module.sql | 36 +- .../resources/sql/risk_assessment_llm.sql | 70 ++++ .../PrisonConsumptionControllerTest.java | 230 +++++++++++ .../consumption/ConsumptionServiceTest.java | 283 +++++++++++++ .../src/main/resources/application-local.yaml | 23 +- 100 files changed, 3839 insertions(+), 4684 deletions(-) create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerPageReqVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerSaveReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentAnswerController.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentRecordController.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentResultController.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentStatisticsController.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerPageReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerRespVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSaveReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSubmitReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordPageReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordRespVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordSaveReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentReportRespVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultManualReviewReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultPageReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultRespVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultSaveReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsPageReqVO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailRespVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailSaveReqVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentAnswerSubmitReqVO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentInitiateReqVO.java rename yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/{assessment/vo/AssessmentAnswerManualScoreReqVO.java => questionnairerecord/vo/AssessmentManualScoreReqVO.java} (52%) delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/PrisonRiskAssessmentController.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/answer/AnswerConvert.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentAnswerConvert.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentRecordConvert.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentResultConvert.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentStatisticsConvert.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionConvert.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionDetailConvert.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentAnswerDO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentRecordDO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentResultDO.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentStatisticsDO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDetailDO.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/answer/AnswerMapper.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentAnswerMapper.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentRecordMapper.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentResultMapper.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentStatisticsMapper.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionDetailMapper.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ConsumptionStatusEnum.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ConsumptionTypeEnum.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentPassStatusEnum.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentRecordStatusEnum.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/RiskLevelEnum.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentAnswerService.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentRecordService.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentResultService.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentStatisticsService.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentAnswerServiceImpl.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentRecordServiceImpl.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentResultServiceImpl.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentStatisticsServiceImpl.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentDataAggregator.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentPromptBuilder.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/dto/LlmAssessmentResult.java delete mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/ClaudeLlmClient.java create mode 100644 yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/OpenAiCompatibleClient.java create mode 100644 yudao-module-prison/src/main/resources/sql/answer_module.sql create mode 100644 yudao-module-prison/src/main/resources/sql/assessment_migration.sql create mode 100644 yudao-module-prison/src/main/resources/sql/assessment_migration_simple.sql create mode 100644 yudao-module-prison/src/main/resources/sql/consumption_migration_20260115.sql create mode 100644 yudao-module-prison/src/main/resources/sql/risk_assessment_llm.sql create mode 100644 yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionControllerTest.java create mode 100644 yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceTest.java diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java new file mode 100644 index 0000000000..af31efddca --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/PrisonAnswerController.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.prison.controller.admin.answer; + +import org.springframework.web.bind.annotation.*; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import jakarta.validation.constraints.*; +import jakarta.validation.*; +import jakarta.servlet.http.*; +import java.util.*; +import java.io.IOException; + +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.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.prison.controller.admin.answer.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.answer.AnswerDO; +import cn.iocoder.yudao.module.prison.service.answer.AnswerService; + +@Tag(name = "管理后台 - 问卷答题记录") +@RestController +@RequestMapping("/prison/answer") +@Validated +public class PrisonAnswerController { + + @Resource + private AnswerService answerService; + + @PostMapping("/create") + @Operation(summary = "创建答题记录") + @PreAuthorize("@ss.hasPermission('prison:answer:create')") + public CommonResult createAnswer(@Valid @RequestBody AnswerSaveReqVO createReqVO) { + return success(answerService.createAnswer(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新答题记录") + @PreAuthorize("@ss.hasPermission('prison:answer:update')") + public CommonResult updateAnswer(@Valid @RequestBody AnswerSaveReqVO updateReqVO) { + answerService.updateAnswer(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除答题记录") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('prison:answer:delete')") + public CommonResult deleteAnswer(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { + answerService.deleteAnswer(id); + return success(true); + } + + @PostMapping("/delete-list") + @Operation(summary = "批量删除答题记录") + @PreAuthorize("@ss.hasPermission('prison:answer:delete')") + public CommonResult deleteAnswerList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { + answerService.deleteAnswerListByIds(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得答题记录") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('prison:answer:query')") + public CommonResult getAnswer(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { + AnswerDO answer = answerService.getAnswer(id); + return success(BeanUtils.toBean(answer, AnswerRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得答题记录分页") + @PreAuthorize("@ss.hasPermission('prison:answer:query')") + public CommonResult> getAnswerPage(@Valid AnswerPageReqVO pageReqVO) { + PageResult pageResult = answerService.getAnswerPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AnswerRespVO.class)); + } + + @GetMapping("/list-by-assessment-record") + @Operation(summary = "根据测评记录ID查询答题列表") + @PreAuthorize("@ss.hasPermission('prison:answer:query')") + public CommonResult> getAnswersByAssessmentRecordId( + @NotNull(message = "测评记录ID不能为空") @RequestParam("assessmentRecordId") Long assessmentRecordId) { + List list = answerService.getAnswersByAssessmentRecordId(assessmentRecordId); + return success(BeanUtils.toBean(list, AnswerRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出答题记录 Excel") + @PreAuthorize("@ss.hasPermission('prison:answer:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportAnswerExcel(@Valid AnswerPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = answerService.getAnswerPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "答题记录.xls", "数据", AnswerRespVO.class, + BeanUtils.toBean(list, AnswerRespVO.class)); + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerPageReqVO.java new file mode 100644 index 0000000000..d2e4ba1526 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerPageReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.prison.controller.admin.answer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import jakarta.validation.constraints.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +@Schema(description = "管理后台 - 问卷答题记录分页 Request VO") +@Data +public class AnswerPageReqVO extends PageParam { + + @Schema(description = "测评记录ID") + private Long assessmentRecordId; + + @Schema(description = "问题ID") + private Long questionId; + + @Schema(description = "问卷ID") + private Long questionnaireId; + + @Schema(description = "罪犯ID") + private Long prisonerId; + + @Schema(description = "问题类型:1-单选 2-多选 3-填空 4-评分 5-日期 6-数字") + private Integer questionType; + + @Schema(description = "创建时间") + private Date[] createTime; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerRespVO.java new file mode 100644 index 0000000000..dcb9b14f9a --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerRespVO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.prison.controller.admin.answer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 问卷答题记录 Response VO") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AnswerRespVO { + + @Schema(description = "答题记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "测评记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long assessmentRecordId; + + @Schema(description = "问题ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long questionId; + + @Schema(description = "问卷ID", example = "1024") + private Long questionnaireId; + + @Schema(description = "罪犯ID", example = "1024") + private Long prisonerId; + + @Schema(description = "问题类型:1-单选 2-多选 3-填空 4-评分 5-日期 6-数字", example = "1") + private Integer questionType; + + @Schema(description = "答案内容(填空题、评分题等)") + private String answerText; + + @Schema(description = "选项ID列表(JSON数组,如 [1,2,3])") + private String optionIds; + + @Schema(description = "得分") + private BigDecimal score; + + @Schema(description = "是否正确:null-未评分 false-错误 true-正确") + private Boolean isCorrect; + + @Schema(description = "答题时间(秒)") + private Integer duration; + + @Schema(description = "创建者", example = "芋艿") + private String creator; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerSaveReqVO.java new file mode 100644 index 0000000000..a01e0fb644 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/answer/vo/AnswerSaveReqVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.prison.controller.admin.answer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import jakarta.validation.constraints.*; +import java.math.BigDecimal; + +@Schema(description = "管理后台 - 问卷答题记录新增/修改 Request VO") +@Data +public class AnswerSaveReqVO { + + @Schema(description = "答题记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26045") + private Long id; + + @Schema(description = "测评记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "测评记录ID不能为空") + private Long assessmentRecordId; + + @Schema(description = "问题ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "问题ID不能为空") + private Long questionId; + + @Schema(description = "问卷ID") + private Long questionnaireId; + + @Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "罪犯ID不能为空") + private Long prisonerId; + + @Schema(description = "问题类型:1-单选 2-多选 3-填空 4-评分 5-日期 6-数字", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "问题类型不能为空") + private Integer questionType; + + @Schema(description = "答案内容(填空题、评分题等)") + private String answerText; + + @Schema(description = "选项ID列表(JSON数组,如 [1,2,3])") + private String optionIds; + + @Schema(description = "得分") + private BigDecimal score; + + @Schema(description = "是否正确:null-未评分 false-错误 true-正确") + private Boolean isCorrect; + + @Schema(description = "答题时间(秒)") + private Integer duration; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentAnswerController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentAnswerController.java deleted file mode 100644 index 7bbd240988..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentAnswerController.java +++ /dev/null @@ -1,145 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.constraints.*; -import jakarta.validation.*; -import jakarta.servlet.http.*; -import java.util.*; -import java.io.IOException; - -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.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentAnswerDO; -import cn.iocoder.yudao.module.prison.service.assessment.AssessmentAnswerService; - -@Tag(name = "管理后台 - 答卷详情") -@RestController -@RequestMapping("/prison/assessment-answer") -@Validated -public class AssessmentAnswerController { - - @Resource - private AssessmentAnswerService assessmentAnswerService; - - @PostMapping("/create") - @Operation(summary = "创建答卷") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:create')") - public CommonResult createAssessmentAnswer(@Valid @RequestBody AssessmentAnswerSaveReqVO createReqVO) { - return success(assessmentAnswerService.createAssessmentAnswer(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新答卷") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:update')") - public CommonResult updateAssessmentAnswer(@Valid @RequestBody AssessmentAnswerSaveReqVO updateReqVO) { - assessmentAnswerService.updateAssessmentAnswer(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除答卷") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:delete')") - public CommonResult deleteAssessmentAnswer(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { - assessmentAnswerService.deleteAssessmentAnswer(id); - return success(true); - } - - @DeleteMapping("/delete-list") - @Operation(summary = "批量删除答卷") - @Parameter(name = "ids", description = "编号列表", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:delete')") - public CommonResult deleteAssessmentAnswerList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { - assessmentAnswerService.deleteAssessmentAnswerListByIds(ids); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得答卷") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:query')") - public CommonResult getAssessmentAnswer(@RequestParam("id") Long id) { - AssessmentAnswerDO assessmentAnswer = assessmentAnswerService.getAssessmentAnswer(id); - return success(BeanUtils.toBean(assessmentAnswer, AssessmentAnswerRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得答卷分页") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:query')") - public CommonResult> getAssessmentAnswerPage(@Valid AssessmentAnswerPageReqVO pageReqVO) { - PageResult pageResult = assessmentAnswerService.getAssessmentAnswerPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AssessmentAnswerRespVO.class)); - } - - @PostMapping("/start") - @Operation(summary = "开始答题") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:create')") - public CommonResult startAnswer(@RequestParam("assessmentRecordId") Long assessmentRecordId, - @RequestParam("prisonerId") Long prisonerId) { - return success(assessmentAnswerService.startAnswer(assessmentRecordId, prisonerId)); - } - - @PostMapping("/submit") - @Operation(summary = "提交答卷") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:update')") - public CommonResult submitAnswer(@Valid @RequestBody AssessmentAnswerSubmitReqVO submitReqVO) { - assessmentAnswerService.submitAnswer(submitReqVO); - return success(true); - } - - @GetMapping("/pending-score-page") - @Operation(summary = "获取待评分答卷列表") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:query')") - public CommonResult> getPendingScorePage(@Valid AssessmentAnswerPageReqVO pageReqVO) { - PageResult pageResult = assessmentAnswerService.getPendingScorePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AssessmentAnswerRespVO.class)); - } - - @PostMapping("/manual-score") - @Operation(summary = "人工评分") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:update')") - public CommonResult manualScore(@Valid @RequestBody AssessmentAnswerManualScoreReqVO scoreReqVO) { - assessmentAnswerService.manualScore(scoreReqVO); - return success(true); - } - - @GetMapping("/get-by-prisoner") - @Operation(summary = "根据囚犯ID和测评记录ID获取答卷") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:query')") - public CommonResult getByPrisonerAndRecord( - @RequestParam("prisonerId") Long prisonerId, - @RequestParam("assessmentRecordId") Long assessmentRecordId) { - AssessmentAnswerDO assessmentAnswer = assessmentAnswerService.getByPrisonerAndRecord(prisonerId, assessmentRecordId); - return success(BeanUtils.toBean(assessmentAnswer, AssessmentAnswerRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出答卷 Excel") - @PreAuthorize("@ss.hasPermission('prison:assessment-answer:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportAssessmentAnswerExcel(@Valid AssessmentAnswerPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = assessmentAnswerService.getAssessmentAnswerPage(pageReqVO).getList(); - ExcelUtils.write(response, "答卷详情.xls", "数据", AssessmentAnswerRespVO.class, - BeanUtils.toBean(list, AssessmentAnswerRespVO.class)); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentRecordController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentRecordController.java deleted file mode 100644 index 82e215884e..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentRecordController.java +++ /dev/null @@ -1,134 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.constraints.*; -import jakarta.validation.*; -import jakarta.servlet.http.*; -import java.util.*; -import java.io.IOException; - -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.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentRecordDO; -import cn.iocoder.yudao.module.prison.service.assessment.AssessmentRecordService; - -@Tag(name = "管理后台 - 测评记录") -@RestController -@RequestMapping("/prison/assessment-record") -@Validated -public class AssessmentRecordController { - - @Resource - private AssessmentRecordService assessmentRecordService; - - @PostMapping("/create") - @Operation(summary = "创建测评记录") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:create')") - public CommonResult createAssessmentRecord(@Valid @RequestBody AssessmentRecordSaveReqVO createReqVO) { - return success(assessmentRecordService.createAssessmentRecord(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新测评记录") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:update')") - public CommonResult updateAssessmentRecord(@Valid @RequestBody AssessmentRecordSaveReqVO updateReqVO) { - assessmentRecordService.updateAssessmentRecord(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除测评记录") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-record:delete')") - public CommonResult deleteAssessmentRecord(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { - assessmentRecordService.deleteAssessmentRecord(id); - return success(true); - } - - @DeleteMapping("/delete-list") - @Operation(summary = "批量删除测评记录") - @Parameter(name = "ids", description = "编号列表", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-record:delete')") - public CommonResult deleteAssessmentRecordList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { - assessmentRecordService.deleteAssessmentRecordListByIds(ids); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得测评记录") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:query')") - public CommonResult getAssessmentRecord(@RequestParam("id") Long id) { - AssessmentRecordDO assessmentRecord = assessmentRecordService.getAssessmentRecord(id); - return success(BeanUtils.toBean(assessmentRecord, AssessmentRecordRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得测评记录分页") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:query')") - public CommonResult> getAssessmentRecordPage(@Valid AssessmentRecordPageReqVO pageReqVO) { - PageResult pageResult = assessmentRecordService.getAssessmentRecordPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AssessmentRecordRespVO.class)); - } - - @PostMapping("/initiate") - @Operation(summary = "发起测评") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:create')") - public CommonResult initiateAssessment(@Valid @RequestBody AssessmentRecordSaveReqVO reqVO) { - return success(assessmentRecordService.initiateAssessment(reqVO)); - } - - @PostMapping("/cancel") - @Operation(summary = "取消测评") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:update')") - public CommonResult cancelAssessment(@RequestParam("id") Long id) { - assessmentRecordService.cancelAssessment(id); - return success(true); - } - - @PostMapping("/start") - @Operation(summary = "启动测评") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:update')") - public CommonResult startAssessment(@RequestParam("id") Long id) { - assessmentRecordService.startAssessment(id); - return success(true); - } - - @PostMapping("/finish") - @Operation(summary = "结束测评") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:update')") - public CommonResult finishAssessment(@RequestParam("id") Long id) { - assessmentRecordService.finishAssessment(id); - return success(true); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出测评记录 Excel") - @PreAuthorize("@ss.hasPermission('prison:assessment-record:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportAssessmentRecordExcel(@Valid AssessmentRecordPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = assessmentRecordService.getAssessmentRecordPage(pageReqVO).getList(); - ExcelUtils.write(response, "测评记录.xls", "数据", AssessmentRecordRespVO.class, - BeanUtils.toBean(list, AssessmentRecordRespVO.class)); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentResultController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentResultController.java deleted file mode 100644 index eb5691d9fc..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentResultController.java +++ /dev/null @@ -1,143 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.constraints.*; -import jakarta.validation.*; -import jakarta.servlet.http.*; -import java.util.*; -import java.io.IOException; - -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.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentResultDO; -import cn.iocoder.yudao.module.prison.service.assessment.AssessmentResultService; - -@Tag(name = "管理后台 - 测评结果") -@RestController -@RequestMapping("/prison/assessment-result") -@Validated -public class AssessmentResultController { - - @Resource - private AssessmentResultService assessmentResultService; - - @PostMapping("/create") - @Operation(summary = "创建测评结果") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:create')") - public CommonResult createAssessmentResult(@Valid @RequestBody AssessmentResultSaveReqVO createReqVO) { - return success(assessmentResultService.createAssessmentResult(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新测评结果") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:update')") - public CommonResult updateAssessmentResult(@Valid @RequestBody AssessmentResultSaveReqVO updateReqVO) { - assessmentResultService.updateAssessmentResult(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除测评结果") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-result:delete')") - public CommonResult deleteAssessmentResult(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { - assessmentResultService.deleteAssessmentResult(id); - return success(true); - } - - @DeleteMapping("/delete-list") - @Operation(summary = "批量删除测评结果") - @Parameter(name = "ids", description = "编号列表", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-result:delete')") - public CommonResult deleteAssessmentResultList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { - assessmentResultService.deleteAssessmentResultListByIds(ids); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得测评结果") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:query')") - public CommonResult getAssessmentResult(@RequestParam("id") Long id) { - AssessmentResultDO assessmentResult = assessmentResultService.getAssessmentResult(id); - return success(BeanUtils.toBean(assessmentResult, AssessmentResultRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得测评结果分页") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:query')") - public CommonResult> getAssessmentResultPage(@Valid AssessmentResultPageReqVO pageReqVO) { - PageResult pageResult = assessmentResultService.getAssessmentResultPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AssessmentResultRespVO.class)); - } - - @GetMapping("/list-by-answer") - @Operation(summary = "根据答卷ID获取所有结果") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:query')") - public CommonResult> getResultsByAnswerId(@RequestParam("answerId") Long answerId) { - List results = assessmentResultService.getResultsByAnswerId(answerId); - return success(BeanUtils.toBean(results, AssessmentResultRespVO.class)); - } - - @GetMapping("/list-by-assessment-record") - @Operation(summary = "根据测评记录ID获取所有结果") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:query')") - public CommonResult> getResultsByAssessmentRecordId(@RequestParam("assessmentRecordId") Long assessmentRecordId) { - List results = assessmentResultService.getResultsByAssessmentRecordId(assessmentRecordId); - return success(BeanUtils.toBean(results, AssessmentResultRespVO.class)); - } - - @GetMapping("/need-manual-review") - @Operation(summary = "获取需要人工评阅的结果列表") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:query')") - public CommonResult> getNeedManualReviewList() { - List results = assessmentResultService.getNeedManualReviewList(); - return success(BeanUtils.toBean(results, AssessmentResultRespVO.class)); - } - - @PostMapping("/manual-review") - @Operation(summary = "人工评阅") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:update')") - public CommonResult manualReview(@Valid @RequestBody AssessmentResultManualReviewReqVO reviewReqVO) { - assessmentResultService.manualReview(reviewReqVO); - return success(true); - } - - @PostMapping("/auto-score") - @Operation(summary = "自动评分") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:update')") - public CommonResult autoScore(@RequestParam("answerId") Long answerId) { - assessmentResultService.autoScore(answerId); - return success(true); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出测评结果 Excel") - @PreAuthorize("@ss.hasPermission('prison:assessment-result:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportAssessmentResultExcel(@Valid AssessmentResultPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = assessmentResultService.getAssessmentResultPage(pageReqVO).getList(); - ExcelUtils.write(response, "测评结果.xls", "数据", AssessmentResultRespVO.class, - BeanUtils.toBean(list, AssessmentResultRespVO.class)); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentStatisticsController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentStatisticsController.java deleted file mode 100644 index ef6040e6eb..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/AssessmentStatisticsController.java +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.constraints.*; -import jakarta.validation.*; -import java.util.*; - -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.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentStatisticsDO; -import cn.iocoder.yudao.module.prison.service.assessment.AssessmentStatisticsService; - -import java.math.BigDecimal; -import java.util.Map; - -@Tag(name = "管理后台 - 测评统计") -@RestController -@RequestMapping("/prison/assessment-statistics") -@Validated -public class AssessmentStatisticsController { - - @Resource - private AssessmentStatisticsService assessmentStatisticsService; - - @GetMapping("/get") - @Operation(summary = "获得测评统计") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('prison:assessment-statistics:query')") - public CommonResult getAssessmentStatistics(@RequestParam("id") Long id) { - AssessmentStatisticsDO statistics = assessmentStatisticsService.getAssessmentStatistics(id); - return success(BeanUtils.toBean(statistics, AssessmentStatisticsRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得测评统计分页") - @PreAuthorize("@ss.hasPermission('prison:assessment-statistics:query')") - public CommonResult> getAssessmentStatisticsPage(@Valid AssessmentStatisticsPageReqVO pageReqVO) { - PageResult pageResult = assessmentStatisticsService.getAssessmentStatisticsPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AssessmentStatisticsRespVO.class)); - } - - @GetMapping("/generate") - @Operation(summary = "生成测评统计") - @Parameter(name = "assessmentRecordId", description = "测评记录ID", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-statistics:create')") - public CommonResult generateStatistics(@RequestParam("assessmentRecordId") Long assessmentRecordId) { - AssessmentStatisticsDO statistics = assessmentStatisticsService.generateStatistics(assessmentRecordId); - return success(BeanUtils.toBean(statistics, AssessmentStatisticsRespVO.class)); - } - - @GetMapping("/completion-rate") - @Operation(summary = "获取测评完成率") - @Parameter(name = "assessmentRecordId", description = "测评记录ID", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-statistics:query')") - public CommonResult getCompletionRate(@RequestParam("assessmentRecordId") Long assessmentRecordId) { - return success(assessmentStatisticsService.getCompletionRate(assessmentRecordId)); - } - - @GetMapping("/score-distribution") - @Operation(summary = "获取分数分布") - @Parameter(name = "assessmentRecordId", description = "测评记录ID", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-statistics:query')") - public CommonResult> getScoreDistribution(@RequestParam("assessmentRecordId") Long assessmentRecordId) { - return success(assessmentStatisticsService.getScoreDistribution(assessmentRecordId)); - } - - @GetMapping("/risk-distribution") - @Operation(summary = "获取风险分布") - @Parameter(name = "assessmentRecordId", description = "测评记录ID", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-statistics:query')") - public CommonResult> getRiskDistribution(@RequestParam("assessmentRecordId") Long assessmentRecordId) { - return success(assessmentStatisticsService.getRiskDistribution(assessmentRecordId)); - } - - @GetMapping("/report") - @Operation(summary = "获取测评分析报告") - @Parameter(name = "assessmentRecordId", description = "测评记录ID", required = true) - @PreAuthorize("@ss.hasPermission('prison:assessment-statistics:query')") - public CommonResult getAssessmentReport(@RequestParam("assessmentRecordId") Long assessmentRecordId) { - return success(assessmentStatisticsService.getAssessmentReport(assessmentRecordId)); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerPageReqVO.java deleted file mode 100644 index 3710b9f72b..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerPageReqVO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 答卷详情分页查询 Request VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentAnswerPageReqVO extends PageParam { - - @Schema(description = "关联测评记录ID") - private Long assessmentRecordId; - - @Schema(description = "囚犯ID") - private Long prisonerId; - - @Schema(description = "囚犯编号") - private String prisonerCode; - - @Schema(description = "囚犯姓名") - private String prisonerName; - - @Schema(description = "监区ID") - private Long areaId; - - @Schema(description = "监室ID") - private Long cellId; - - @Schema(description = "答卷状态:1-待答题 2-答题中 3-已提交 4-已评分 5-已完成") - private Integer status; - - @Schema(description = "开始答题时间") - private LocalDateTime[] startTime; - - @Schema(description = "提交时间") - private LocalDateTime[] submitTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerRespVO.java deleted file mode 100644 index 3e4b1e60be..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerRespVO.java +++ /dev/null @@ -1,91 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 答卷详情 Response VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentAnswerRespVO { - - @Schema(description = "答卷ID", example = "1024") - private Long id; - - @Schema(description = "关联测评记录ID") - private Long assessmentRecordId; - - @Schema(description = "测评名称") - private String assessmentName; - - @Schema(description = "囚犯ID") - private Long prisonerId; - - @Schema(description = "囚犯编号") - private String prisonerCode; - - @Schema(description = "囚犯姓名") - private String prisonerName; - - @Schema(description = "监区ID") - private Long areaId; - - @Schema(description = "监区名称") - private String areaName; - - @Schema(description = "监室ID") - private Long cellId; - - @Schema(description = "监室名称") - private String cellName; - - @Schema(description = "答卷状态:1-待答题 2-答题中 3-已提交 4-已评分 5-已完成") - private Integer status; - - @Schema(description = "开始答题时间") - private LocalDateTime startTime; - - @Schema(description = "提交时间") - private LocalDateTime submitTime; - - @Schema(description = "答题用时(秒)") - private Integer duration; - - @Schema(description = "客观题得分") - private BigDecimal objectiveScore; - - @Schema(description = "主观题得分") - private BigDecimal subjectiveScore; - - @Schema(description = "总分") - private BigDecimal totalScore; - - @Schema(description = "是否及格") - private Boolean passed; - - @Schema(description = "评语") - private String comment; - - @Schema(description = "评卷人ID") - private Long scorerId; - - @Schema(description = "评卷人名称") - private String scorerName; - - @Schema(description = "评分时间") - private LocalDateTime scoreTime; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - - @Schema(description = "更新时间") - private LocalDateTime updateTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSaveReqVO.java deleted file mode 100644 index f160fe0431..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSaveReqVO.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 答卷详情创建/更新 Request VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentAnswerSaveReqVO { - - @Schema(description = "答卷ID", example = "1024") - private Long id; - - @Schema(description = "关联测评记录ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "测评记录不能为空") - private Long assessmentRecordId; - - @Schema(description = "囚犯ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "囚犯不能为空") - private Long prisonerId; - - @Schema(description = "囚犯编号") - private String prisonerCode; - - @Schema(description = "囚犯姓名") - private String prisonerName; - - @Schema(description = "监区ID") - private Long areaId; - - @Schema(description = "监区名称") - private String areaName; - - @Schema(description = "监室ID") - private Long cellId; - - @Schema(description = "监室名称") - private String cellName; - - @Schema(description = "答卷状态:1-待答题 2-答题中 3-已提交 4-已评分 5-已完成") - private Integer status; - - @Schema(description = "开始答题时间") - private LocalDateTime startTime; - - @Schema(description = "提交时间") - private LocalDateTime submitTime; - - @Schema(description = "答题用时(秒)") - private Integer duration; - - @Schema(description = "客观题得分") - private BigDecimal objectiveScore; - - @Schema(description = "主观题得分") - private BigDecimal subjectiveScore; - - @Schema(description = "总分") - private BigDecimal totalScore; - - @Schema(description = "是否及格") - private Boolean passed; - - @Schema(description = "评语") - private String comment; - - @Schema(description = "及格分数") - private BigDecimal passScore; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSubmitReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSubmitReqVO.java deleted file mode 100644 index b371d4cc94..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerSubmitReqVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 答卷提交 Request VO - * - * @author 芋道源码 - */ -@Data -public class AssessmentAnswerSubmitReqVO { - - @Schema(description = "答卷ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "答卷ID不能为空") - private Long id; - - @Schema(description = "答题详情JSON") - private String answers; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordPageReqVO.java deleted file mode 100644 index e56c805229..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordPageReqVO.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 测评记录分页查询 Request VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentRecordPageReqVO extends PageParam { - - @Schema(description = "测评名称", example = "心理测评") - private String name; - - @Schema(description = "关联问卷模板ID") - private Long questionnaireId; - - @Schema(description = "测评类型:1-心理测评 2-行为评估 3-满意度调查") - private Integer type; - - @Schema(description = "发起测评的监狱/监区ID") - private Long areaId; - - @Schema(description = "测评状态:1-未开始 2-进行中 3-已完成 4-已取消") - private Integer status; - - @Schema(description = "计划开始时间") - private LocalDateTime[] planStartTime; - - @Schema(description = "计划结束时间") - private LocalDateTime[] planEndTime; - - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordRespVO.java deleted file mode 100644 index 1c3779b43f..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordRespVO.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 测评记录 Response VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentRecordRespVO { - - @Schema(description = "测评记录ID", example = "1024") - private Long id; - - @Schema(description = "测评名称", example = "心理测评") - private String name; - - @Schema(description = "关联问卷模板ID") - private Long questionnaireId; - - @Schema(description = "问卷模板名称") - private String questionnaireName; - - @Schema(description = "测评类型:1-心理测评 2-行为评估 3-满意度调查") - private Integer type; - - @Schema(description = "发起测评的监狱/监区ID") - private Long areaId; - - @Schema(description = "发起测评的监狱/监区名称") - private String areaName; - - @Schema(description = "发起人ID") - private Long initiatorId; - - @Schema(description = "发起人名称") - private String initiatorName; - - @Schema(description = "测评状态:1-未开始 2-进行中 3-已完成 4-已取消") - private Integer status; - - @Schema(description = "计划开始时间") - private LocalDateTime planStartTime; - - @Schema(description = "计划结束时间") - private LocalDateTime planEndTime; - - @Schema(description = "实际开始时间") - private LocalDateTime actualStartTime; - - @Schema(description = "实际结束时间") - private LocalDateTime actualEndTime; - - @Schema(description = "参与人数") - private Integer participantCount; - - @Schema(description = "完成人数") - private Integer completedCount; - - @Schema(description = "测评说明") - private String description; - - @Schema(description = "是否启用自动评分") - private Boolean enableAutoScore; - - @Schema(description = "及格分数") - private BigDecimal passScore; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - - @Schema(description = "更新时间") - private LocalDateTime updateTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordSaveReqVO.java deleted file mode 100644 index cb2ddf6f9b..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentRecordSaveReqVO.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 测评记录创建/更新 Request VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentRecordSaveReqVO { - - @Schema(description = "测评记录ID", example = "1024") - private Long id; - - @Schema(description = "测评名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "心理测评") - @NotNull(message = "测评名称不能为空") - private String name; - - @Schema(description = "关联问卷模板ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "问卷模板不能为空") - private Long questionnaireId; - - @Schema(description = "测评类型:1-心理测评 2-行为评估 3-满意度调查", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "测评类型不能为空") - private Integer type; - - @Schema(description = "发起测评的监狱/监区ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "监区不能为空") - private Long areaId; - - @Schema(description = "发起测评的监狱/监区名称") - private String areaName; - - @Schema(description = "发起人ID") - private Long initiatorId; - - @Schema(description = "发起人名称") - private String initiatorName; - - @Schema(description = "测评状态:1-未开始 2-进行中 3-已完成 4-已取消") - private Integer status; - - @Schema(description = "计划开始时间") - private LocalDateTime planStartTime; - - @Schema(description = "计划结束时间") - private LocalDateTime planEndTime; - - @Schema(description = "测评说明") - private String description; - - @Schema(description = "是否启用自动评分") - private Boolean enableAutoScore; - - @Schema(description = "及格分数") - private BigDecimal passScore; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentReportRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentReportRespVO.java deleted file mode 100644 index 36c7ced182..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentReportRespVO.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 测评分析报告 Response VO - * - * @author 芋道源码 - */ -@Data -public class AssessmentReportRespVO { - - @Schema(description = "关联测评记录ID") - private Long assessmentRecordId; - - @Schema(description = "测评名称") - private String assessmentName; - - @Schema(description = "总参与人数") - private Integer totalCount; - - @Schema(description = "已完成人数") - private Integer completedCount; - - @Schema(description = "完成率(%)") - private BigDecimal completionRate; - - @Schema(description = "平均分") - private BigDecimal averageScore; - - @Schema(description = "最高分") - private BigDecimal highestScore; - - @Schema(description = "最低分") - private BigDecimal lowestScore; - - @Schema(description = "及格人数") - private Integer passedCount; - - @Schema(description = "及格率(%)") - private BigDecimal passRate; - - @Schema(description = "优秀人数(90分以上)") - private Integer excellentCount; - - @Schema(description = "优秀率(%)") - private BigDecimal excellentRate; - - @Schema(description = "风险人数(60分以下)") - private Integer riskCount; - - @Schema(description = "风险率(%)") - private BigDecimal riskRate; - - @Schema(description = "分数分布") - private Map scoreDistribution; - - @Schema(description = "风险分布") - private Map riskDistribution; - - @Schema(description = "分析建议") - private String analysisSuggestion; - - @Schema(description = "生成时间") - private LocalDateTime generatedTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultManualReviewReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultManualReviewReqVO.java deleted file mode 100644 index 8c327aa0d3..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultManualReviewReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 人工评阅 Request VO - * - * @author 芋道源码 - */ -@Data -public class AssessmentResultManualReviewReqVO { - - @Schema(description = "结果ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "结果ID不能为空") - private Long id; - - @Schema(description = "人工评分", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "评分不能为空") - private BigDecimal manualScore; - - @Schema(description = "人工评语") - private String manualComment; - - @Schema(description = "评阅人ID") - private Long reviewerId; - - @Schema(description = "评阅人名称") - private String reviewerName; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultPageReqVO.java deleted file mode 100644 index d8ffca650f..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultPageReqVO.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 测评结果分页查询 Request VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentResultPageReqVO extends PageParam { - - @Schema(description = "关联答卷ID") - private Long answerId; - - @Schema(description = "关联测评记录ID") - private Long assessmentRecordId; - - @Schema(description = "囚犯ID") - private Long prisonerId; - - @Schema(description = "关联题目ID") - private Long questionId; - - @Schema(description = "题目类型:1-单选 2-多选 3-判断 4-填空 5-问答") - private Integer questionType; - - @Schema(description = "是否正确") - private Boolean correct; - - @Schema(description = "是否需要人工评阅") - private Boolean needManualReview; - - @Schema(description = "人工评阅状态:1-待评阅 2-已评阅") - private Integer manualReviewStatus; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultRespVO.java deleted file mode 100644 index 4d017951e9..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultRespVO.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 测评结果 Response VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentResultRespVO { - - @Schema(description = "结果ID", example = "1024") - private Long id; - - @Schema(description = "关联答卷ID") - private Long answerId; - - @Schema(description = "关联测评记录ID") - private Long assessmentRecordId; - - @Schema(description = "囚犯ID") - private Long prisonerId; - - @Schema(description = "囚犯编号") - private String prisonerCode; - - @Schema(description = "囚犯姓名") - private String prisonerName; - - @Schema(description = "关联题目ID") - private Long questionId; - - @Schema(description = "题目编号") - private String questionCode; - - @Schema(description = "题目内容") - private String questionContent; - - @Schema(description = "题目类型:1-单选 2-多选 3-判断 4-填空 5-问答") - private Integer questionType; - - @Schema(description = "题目分值") - private BigDecimal questionScore; - - @Schema(description = "囚犯答案") - private String answer; - - @Schema(description = "正确答案(客观题)") - private String correctAnswer; - - @Schema(description = "是否正确") - private Boolean correct; - - @Schema(description = "得分") - private BigDecimal score; - - @Schema(description = "是否需要人工评阅") - private Boolean needManualReview; - - @Schema(description = "人工评阅状态:1-待评阅 2-已评阅") - private Integer manualReviewStatus; - - @Schema(description = "人工评分") - private BigDecimal manualScore; - - @Schema(description = "人工评语") - private String manualComment; - - @Schema(description = "评阅人ID") - private Long reviewerId; - - @Schema(description = "评阅人名称") - private String reviewerName; - - @Schema(description = "评阅时间") - private LocalDateTime reviewTime; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - - @Schema(description = "更新时间") - private LocalDateTime updateTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultSaveReqVO.java deleted file mode 100644 index d05f6cb24f..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentResultSaveReqVO.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 测评结果创建/更新 Request VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentResultSaveReqVO { - - @Schema(description = "结果ID", example = "1024") - private Long id; - - @Schema(description = "关联答卷ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "答卷ID不能为空") - private Long answerId; - - @Schema(description = "关联测评记录ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "测评记录ID不能为空") - private Long assessmentRecordId; - - @Schema(description = "囚犯ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "囚犯ID不能为空") - private Long prisonerId; - - @Schema(description = "囚犯编号") - private String prisonerCode; - - @Schema(description = "囚犯姓名") - private String prisonerName; - - @Schema(description = "关联题目ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "题目ID不能为空") - private Long questionId; - - @Schema(description = "题目编号") - private String questionCode; - - @Schema(description = "题目内容") - private String questionContent; - - @Schema(description = "题目类型:1-单选 2-多选 3-判断 4-填空 5-问答") - private Integer questionType; - - @Schema(description = "题目分值") - private BigDecimal questionScore; - - @Schema(description = "囚犯答案") - private String answer; - - @Schema(description = "正确答案(客观题)") - private String correctAnswer; - - @Schema(description = "是否正确") - private Boolean correct; - - @Schema(description = "得分") - private BigDecimal score; - - @Schema(description = "是否需要人工评阅") - private Boolean needManualReview; - - @Schema(description = "人工评阅状态:1-待评阅 2-已评阅") - private Integer manualReviewStatus; - - @Schema(description = "人工评分") - private BigDecimal manualScore; - - @Schema(description = "人工评语") - private String manualComment; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsPageReqVO.java deleted file mode 100644 index ee18b358d1..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsPageReqVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 测评统计分页查询 Request VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentStatisticsPageReqVO extends PageParam { - - @Schema(description = "关联测评记录ID") - private Long assessmentRecordId; - - @Schema(description = "测评名称") - private String assessmentName; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsRespVO.java deleted file mode 100644 index 1475d4cfad..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentStatisticsRespVO.java +++ /dev/null @@ -1,76 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 测评统计 Response VO - * - * @author 芋道源码 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AssessmentStatisticsRespVO { - - @Schema(description = "统计ID", example = "1024") - private Long id; - - @Schema(description = "关联测评记录ID") - private Long assessmentRecordId; - - @Schema(description = "测评名称") - private String assessmentName; - - @Schema(description = "总参与人数") - private Integer totalCount; - - @Schema(description = "已完成人数") - private Integer completedCount; - - @Schema(description = "完成率") - private BigDecimal completionRate; - - @Schema(description = "平均分") - private BigDecimal averageScore; - - @Schema(description = "最高分") - private BigDecimal highestScore; - - @Schema(description = "最低分") - private BigDecimal lowestScore; - - @Schema(description = "及格人数") - private Integer passedCount; - - @Schema(description = "及格率") - private BigDecimal passRate; - - @Schema(description = "优秀人数(90分以上)") - private Integer excellentCount; - - @Schema(description = "优秀率") - private BigDecimal excellentRate; - - @Schema(description = "风险人数(60分以下)") - private Integer riskCount; - - @Schema(description = "风险率") - private BigDecimal riskRate; - - @Schema(description = "风险分布") - private Map riskDistribution; - - @Schema(description = "分数分布") - private Map scoreDistribution; - - @Schema(description = "统计时间") - private LocalDateTime statisticsTime; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java index d0c58c3d93..00f9fedb3c 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionController.java @@ -27,9 +27,12 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; import cn.iocoder.yudao.module.prison.service.consumption.ConsumptionService; +import cn.iocoder.yudao.module.prison.convert.consumption.ConsumptionConvert; +import cn.iocoder.yudao.module.prison.convert.consumption.ConsumptionDetailConvert; -@Tag(name = "管理后台 - 消费记录") +@Tag(name = "管理后台 - 消费订单") @RestController @RequestMapping("/prison/consumption") @Validated @@ -39,14 +42,14 @@ public class PrisonConsumptionController { private ConsumptionService consumptionService; @PostMapping("/create") - @Operation(summary = "创建消费记录") + @Operation(summary = "创建消费订单") @PreAuthorize("@ss.hasPermission('prison:consumption:create')") public CommonResult createConsumption(@Valid @RequestBody ConsumptionSaveReqVO createReqVO) { return success(consumptionService.createConsumption(createReqVO)); } @PutMapping("/update") - @Operation(summary = "更新消费记录") + @Operation(summary = "更新消费订单") @PreAuthorize("@ss.hasPermission('prison:consumption:update')") public CommonResult updateConsumption(@Valid @RequestBody ConsumptionSaveReqVO updateReqVO) { consumptionService.updateConsumption(updateReqVO); @@ -54,8 +57,8 @@ public class PrisonConsumptionController { } @DeleteMapping("/delete") - @Operation(summary = "删除消费记录") - @Parameter(name = "id", description = "编号", required = true) + @Operation(summary = "删除消费订单") + @Parameter(name = "id", description = "订单编号", required = true) @PreAuthorize("@ss.hasPermission('prison:consumption:delete')") public CommonResult deleteConsumption(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { consumptionService.deleteConsumption(id); @@ -63,33 +66,51 @@ public class PrisonConsumptionController { } @DeleteMapping("/delete-list") - @Parameter(name = "ids", description = "编号", required = true) - @Operation(summary = "批量删除消费记录") - @PreAuthorize("@ss.hasPermission('prison:consumption:delete')") + @Parameter(name = "ids", description = "编号列表", required = true) + @Operation(summary = "批量删除消费订单") + @PreAuthorize("@ss.hasPermission('prison:consumption:delete')") public CommonResult deleteConsumptionList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { consumptionService.deleteConsumptionListByIds(ids); return success(true); } @GetMapping("/get") - @Operation(summary = "获得消费记录") - @Parameter(name = "id", description = "编号", required = true, example = "1024") + @Operation(summary = "获得消费订单详情") + @Parameter(name = "id", description = "订单编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('prison:consumption:query')") public CommonResult getConsumption(@RequestParam("id") Long id) { ConsumptionDO consumption = consumptionService.getConsumption(id); - return success(BeanUtils.toBean(consumption, ConsumptionRespVO.class)); + if (consumption == null) { + return success(null); + } + // 转换主表数据 + ConsumptionRespVO respVO = ConsumptionConvert.INSTANCE.convert(consumption); + // 查询并转换明细列表 + List detailList = consumptionService.getConsumptionDetailList(id); + respVO.setDetails(ConsumptionDetailConvert.INSTANCE.convertListResp(detailList)); + return success(respVO); } @GetMapping("/page") - @Operation(summary = "获得消费记录分页") + @Operation(summary = "获得消费订单分页") @PreAuthorize("@ss.hasPermission('prison:consumption:query')") public CommonResult> getConsumptionPage(@Valid ConsumptionPageReqVO pageReqVO) { PageResult pageResult = consumptionService.getConsumptionPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, ConsumptionRespVO.class)); + return success(ConsumptionConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/detail-list") + @Operation(summary = "获得消费订单明细列表") + @Parameter(name = "consumptionId", description = "消费订单ID", required = true) + @PreAuthorize("@ss.hasPermission('prison:consumption:query')") + public CommonResult> getConsumptionDetailList( + @RequestParam("consumptionId") Long consumptionId) { + List detailList = consumptionService.getConsumptionDetailList(consumptionId); + return success(ConsumptionDetailConvert.INSTANCE.convertListResp(detailList)); } @GetMapping("/export-excel") - @Operation(summary = "导出消费记录 Excel") + @Operation(summary = "导出消费订单 Excel") @PreAuthorize("@ss.hasPermission('prison:consumption:export')") @ApiAccessLog(operateType = EXPORT) public void exportConsumptionExcel(@Valid ConsumptionPageReqVO pageReqVO, @@ -97,8 +118,8 @@ public class PrisonConsumptionController { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = consumptionService.getConsumptionPage(pageReqVO).getList(); // 导出 Excel - ExcelUtils.write(response, "消费记录.xls", "数据", ConsumptionRespVO.class, - BeanUtils.toBean(list, ConsumptionRespVO.class)); + ExcelUtils.write(response, "消费订单.xls", "数据", ConsumptionRespVO.class, + ConsumptionConvert.INSTANCE.convertList(list)); } -} \ No newline at end of file +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailRespVO.java new file mode 100644 index 0000000000..e302746c68 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailRespVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.prison.controller.admin.consumption.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.math.BigDecimal; +import cn.idev.excel.annotation.*; + +@Schema(description = "管理后台 - 消费订单明细 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ConsumptionDetailRespVO { + + @Schema(description = "明细ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4042") + @ExcelProperty("明细ID") + private Long id; + + @Schema(description = "消费订单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("消费订单ID") + private Long consumptionId; + + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("商品名称") + private String goodsName; + + @Schema(description = "商品编码") + @ExcelProperty("商品编码") + private String goodsCode; + + @Schema(description = "商品单价", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("商品单价") + private BigDecimal goodsPrice; + + @Schema(description = "商品数量", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("商品数量") + private Integer goodsCount; + + @Schema(description = "小计金额", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("小计金额") + private BigDecimal subtotal; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailSaveReqVO.java new file mode 100644 index 0000000000..db287d3aba --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionDetailSaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.prison.controller.admin.consumption.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import jakarta.validation.constraints.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.idev.excel.annotation.*; + +@Schema(description = "管理后台 - 消费订单明细新增/修改 Request VO") +@Data +public class ConsumptionDetailSaveReqVO { + + @Schema(description = "明细ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4042") + private Long id; + + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "商品名称不能为空") + private String goodsName; + + @Schema(description = "商品编码") + private String goodsCode; + + @Schema(description = "商品单价", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "商品单价不能为空") + private BigDecimal goodsPrice; + + @Schema(description = "商品数量", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "商品数量不能为空") + private Integer goodsCount; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionPageReqVO.java index 5968c5d155..c82e0a3938 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionPageReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionPageReqVO.java @@ -7,35 +7,39 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import java.math.BigDecimal; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Size; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@Schema(description = "管理后台 - 消费记录分页 Request VO") +@Schema(description = "管理后台 - 消费订单分页 Request VO") @Data public class ConsumptionPageReqVO extends PageParam { @Schema(description = "罪犯ID", example = "25932") + @Min(value = 1, message = "罪犯ID必须为正数") private Long prisonerId; @Schema(description = "罪犯编号") + @Size(max = 50, message = "罪犯编号长度不能超过50个字符") private String prisonerNo; - @Schema(description = "类型:1-存款 2-消费 3-转账", example = "1") + @Schema(description = "类型:1-购物 2-餐饮 3-医疗 4-通讯 5-其他", example = "1") + @Min(value = 1, message = "类型最小值为1") + @Max(value = 5, message = "类型最大值为5") private Integer type; - @Schema(description = "金额") - private BigDecimal amount; + @Schema(description = "订单总金额") + @Min(value = 0, message = "订单总金额不能为负数") + private BigDecimal totalAmount; @Schema(description = "账户余额") + @Min(value = 0, message = "账户余额不能为负数") private BigDecimal balance; - @Schema(description = "商品名称", example = "芋艿") - private String goodsName; - - @Schema(description = "商品数量", example = "3906") - private Integer goodsCount; - @Schema(description = "订单号") + @Size(max = 64, message = "订单号长度不能超过64个字符") private String orderNo; @Schema(description = "交易时间") @@ -43,13 +47,16 @@ public class ConsumptionPageReqVO extends PageParam { private LocalDateTime[] tradeTime; @Schema(description = "状态:1-成功 2-失败", example = "1") + @Min(value = 1, message = "状态最小值为1") + @Max(value = 2, message = "状态最大值为2") private Integer status; - @Schema(description = "备注", example = "你说的对") - private String remark; - @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; -} \ No newline at end of file + @Schema(description = "备注") + @Size(max = 500, message = "备注长度不能超过500个字符") + private String remark; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionRespVO.java index 35fa06c6d5..5f8417d94f 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionRespVO.java @@ -8,13 +8,13 @@ import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; import cn.idev.excel.annotation.*; -@Schema(description = "管理后台 - 消费记录 Response VO") +@Schema(description = "管理后台 - 消费订单 Response VO") @Data @ExcelIgnoreUnannotated public class ConsumptionRespVO { - @Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4042") - @ExcelProperty("记录ID") + @Schema(description = "消费ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4042") + @ExcelProperty("消费ID") private Long id; @Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25932") @@ -25,39 +25,31 @@ public class ConsumptionRespVO { @ExcelProperty("罪犯编号") private String prisonerNo; - @Schema(description = "类型:1-存款 2-消费 3-转账", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("类型:1-存款 2-消费 3-转账") - private Integer type; - - @Schema(description = "金额", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("金额") - private BigDecimal amount; - - @Schema(description = "账户余额") - @ExcelProperty("账户余额") - private BigDecimal balance; - - @Schema(description = "商品名称", example = "芋艿") - @ExcelProperty("商品名称") - private String goodsName; - - @Schema(description = "商品数量", example = "3906") - @ExcelProperty("商品数量") - private Integer goodsCount; - @Schema(description = "订单号") @ExcelProperty("订单号") private String orderNo; + @Schema(description = "类型:1-购物 2-餐饮 3-医疗 4-通讯 5-其他", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("类型") + private Integer type; + + @Schema(description = "订单总金额", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("订单总金额") + private BigDecimal totalAmount; + + @Schema(description = "账户余额(消费后)") + @ExcelProperty("账户余额") + private BigDecimal balance; + @Schema(description = "交易时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("交易时间") private LocalDateTime tradeTime; @Schema(description = "状态:1-成功 2-失败", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("状态:1-成功 2-失败") + @ExcelProperty("状态") private Integer status; - @Schema(description = "备注", example = "你说的对") + @Schema(description = "备注", example = "订单备注") @ExcelProperty("备注") private String remark; @@ -65,4 +57,7 @@ public class ConsumptionRespVO { @ExcelProperty("创建时间") private LocalDateTime createTime; -} \ No newline at end of file + @Schema(description = "消费明细列表") + private List details; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionSaveReqVO.java index 44b0b30535..47fae943b7 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionSaveReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/vo/ConsumptionSaveReqVO.java @@ -8,11 +8,11 @@ import java.math.BigDecimal; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -@Schema(description = "管理后台 - 消费记录新增/修改 Request VO") +@Schema(description = "管理后台 - 消费订单新增/修改 Request VO") @Data public class ConsumptionSaveReqVO { - @Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4042") + @Schema(description = "消费ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4042") private Long id; @Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25932") @@ -23,35 +23,32 @@ public class ConsumptionSaveReqVO { @NotEmpty(message = "罪犯编号不能为空") private String prisonerNo; - @Schema(description = "类型:1-存款 2-消费 3-转账", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "类型:1-存款 2-消费 3-转账不能为空") - private Integer type; - - @Schema(description = "金额", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "金额不能为空") - private BigDecimal amount; - - @Schema(description = "账户余额") - private BigDecimal balance; - - @Schema(description = "商品名称", example = "芋艿") - private String goodsName; - - @Schema(description = "商品数量", example = "3906") - private Integer goodsCount; - @Schema(description = "订单号") private String orderNo; + @Schema(description = "类型:1-购物 2-餐饮 3-医疗 4-通讯 5-其他", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "类型不能为空") + private Integer type; + + @Schema(description = "订单总金额", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "订单总金额不能为空") + private BigDecimal totalAmount; + + @Schema(description = "账户余额(消费后)") + private BigDecimal balance; + @Schema(description = "交易时间", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "交易时间不能为空") private LocalDateTime tradeTime; @Schema(description = "状态:1-成功 2-失败", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态:1-成功 2-失败不能为空") + @NotNull(message = "状态不能为空") private Integer status; - @Schema(description = "备注", example = "你说的对") + @Schema(description = "备注", example = "订单备注") private String remark; -} \ No newline at end of file + @Schema(description = "消费明细列表") + private List details; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java index 07cbbc003e..1c00b1670e 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnaire/PrisonQuestionnaireController.java @@ -63,9 +63,9 @@ public class PrisonQuestionnaireController { } @DeleteMapping("/delete-list") - @Parameter(name = "ids", description = "编号", required = true) + @Parameter(name = "ids", description = "编号列表", required = true) @Operation(summary = "批量删除问卷模板") - @PreAuthorize("@ss.hasPermission('prison:questionnaire:delete')") + @PreAuthorize("@ss.hasPermission('prison:questionnaire:delete')") public CommonResult deleteQuestionnaireList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { questionnaireService.deleteQuestionnaireListByIds(ids); return success(true); diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java index 2239a21bbe..ed2232cc69 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/PrisonQuestionnaireRecordController.java @@ -13,6 +13,7 @@ import jakarta.validation.*; import jakarta.servlet.http.*; import java.util.*; import java.io.IOException; +import java.math.BigDecimal; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -29,7 +30,7 @@ import cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo.*; import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO; import cn.iocoder.yudao.module.prison.service.questionnairerecord.QuestionnaireRecordService; -@Tag(name = "管理后台 - 问卷答题记录") +@Tag(name = "管理后台 - 问卷答题记录/测评记录") @RestController @RequestMapping("/prison/questionnaire-record") @Validated @@ -38,6 +39,8 @@ public class PrisonQuestionnaireRecordController { @Resource private QuestionnaireRecordService questionnaireRecordService; + // ==================== 基础 CRUD ==================== + @PostMapping("/create") @Operation(summary = "创建问卷答题记录") @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:create')") @@ -62,11 +65,10 @@ public class PrisonQuestionnaireRecordController { return success(true); } - @DeleteMapping("/delete-list") - @Parameter(name = "ids", description = "编号", required = true) + @PostMapping("/delete-list") @Operation(summary = "批量删除问卷答题记录") - @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:delete')") - public CommonResult deleteQuestionnaireRecordList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:delete')") + public CommonResult deleteQuestionnaireRecordList(@NotEmpty(message = "编号列表不能为空") @RequestBody List ids) { questionnaireRecordService.deleteQuestionnaireRecordListByIds(ids); return success(true); } @@ -75,7 +77,7 @@ public class PrisonQuestionnaireRecordController { @Operation(summary = "获得问卷答题记录") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:query')") - public CommonResult getQuestionnaireRecord(@RequestParam("id") Long id) { + public CommonResult getQuestionnaireRecord(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { QuestionnaireRecordDO questionnaireRecord = questionnaireRecordService.getQuestionnaireRecord(id); return success(BeanUtils.toBean(questionnaireRecord, QuestionnaireRecordRespVO.class)); } @@ -101,4 +103,87 @@ public class PrisonQuestionnaireRecordController { BeanUtils.toBean(list, QuestionnaireRecordRespVO.class)); } -} \ No newline at end of file + // ==================== 测评执行相关 ==================== + + @PostMapping("/initiate") + @Operation(summary = "发起测评") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:initiate')") + public CommonResult initiateAssessment(@Valid @RequestBody AssessmentInitiateReqVO reqVO) { + return success(questionnaireRecordService.initiateAssessment(reqVO)); + } + + @PostMapping("/start") + @Operation(summary = "开始测评") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:start')") + public CommonResult startAssessment(@NotNull(message = "记录ID不能为空") @RequestParam("id") Long id, + @NotNull(message = "罪犯ID不能为空") @RequestParam("prisonerId") Long prisonerId) { + questionnaireRecordService.startAssessment(id, prisonerId); + return success(true); + } + + @PostMapping("/submit") + @Operation(summary = "提交答卷") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:submit')") + public CommonResult submitAnswer(@Valid @RequestBody AssessmentAnswerSubmitReqVO reqVO) { + questionnaireRecordService.submitAnswer(reqVO); + return success(true); + } + + @PostMapping("/finish") + @Operation(summary = "结束测评") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:finish')") + public CommonResult finishAssessment(@NotNull(message = "记录ID不能为空") @RequestParam("id") Long id) { + questionnaireRecordService.finishAssessment(id); + return success(true); + } + + @PostMapping("/cancel") + @Operation(summary = "取消测评") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:cancel')") + public CommonResult cancelAssessment(@NotNull(message = "记录ID不能为空") @RequestParam("id") Long id) { + questionnaireRecordService.cancelAssessment(id); + return success(true); + } + + // ==================== 评分相关 ==================== + + @PostMapping("/auto-score") + @Operation(summary = "自动评分") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:score')") + public CommonResult autoScore(@NotNull(message = "记录ID不能为空") @RequestParam("id") Long id) { + questionnaireRecordService.autoScore(id); + return success(true); + } + + @PostMapping("/manual-score") + @Operation(summary = "人工评分") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:score')") + public CommonResult manualScore(@Valid @RequestBody AssessmentManualScoreReqVO reqVO) { + questionnaireRecordService.manualScore(reqVO); + return success(true); + } + + // ==================== 统计相关 ==================== + + @GetMapping("/completion-rate") + @Operation(summary = "获取完成率") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:query')") + public CommonResult getCompletionRate(@NotNull(message = "测评记录ID不能为空") @RequestParam("assessmentRecordId") Long assessmentRecordId) { + return success(questionnaireRecordService.getCompletionRate(assessmentRecordId)); + } + + @GetMapping("/score-distribution") + @Operation(summary = "获取分数分布") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:query')") + public CommonResult> getScoreDistribution(@NotNull(message = "测评记录ID不能为空") @RequestParam("assessmentRecordId") Long assessmentRecordId) { + return success(questionnaireRecordService.getScoreDistribution(assessmentRecordId)); + } + + @GetMapping("/risk-distribution") + @Operation(summary = "获取风险分布") + @PreAuthorize("@ss.hasPermission('prison:questionnaire-record:query')") + public CommonResult> getRiskDistribution(@NotNull(message = "测评记录ID不能为空") @RequestParam("assessmentRecordId") Long assessmentRecordId) { + return success(questionnaireRecordService.getRiskDistribution(assessmentRecordId)); + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentAnswerSubmitReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentAnswerSubmitReqVO.java new file mode 100644 index 0000000000..d8af259059 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentAnswerSubmitReqVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import java.util.List; + +/** + * 提交答卷 Request VO + * + * @author 芋道源码 + */ +@Data +public class AssessmentAnswerSubmitReqVO { + + @Schema(description = "测评记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "测评记录不能为空") + private Long recordId; + + @Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "罪犯不能为空") + private Long prisonerId; + + @Schema(description = "答案列表") + private List answers; + + @Data + public static class AnswerItem { + @Schema(description = "题目ID", example = "1024") + private Long questionId; + @Schema(description = "答案内容") + private String answer; + @Schema(description = "选项ID列表(多选用)") + private List optionIds; + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentInitiateReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentInitiateReqVO.java new file mode 100644 index 0000000000..9b7c5e21df --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentInitiateReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 发起测评 Request VO + * + * @author 芋道源码 + */ +@Data +public class AssessmentInitiateReqVO { + + @Schema(description = "问卷ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "问卷不能为空") + private Long questionnaireId; + + @Schema(description = "罪犯ID列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "罪犯不能为空") + private List prisonerIds; + + @Schema(description = "测评说明") + private String remark; + + @Schema(description = "截止日期") + private LocalDateTime deadline; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerManualScoreReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentManualScoreReqVO.java similarity index 52% rename from yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerManualScoreReqVO.java rename to yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentManualScoreReqVO.java index 26146133f2..f65327a4bd 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/assessment/vo/AssessmentAnswerManualScoreReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/AssessmentManualScoreReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.prison.controller.admin.assessment.vo; +package cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; @@ -11,11 +11,11 @@ import java.math.BigDecimal; * @author 芋道源码 */ @Data -public class AssessmentAnswerManualScoreReqVO { +public class AssessmentManualScoreReqVO { - @Schema(description = "答卷ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "答卷ID不能为空") - private Long id; + @Schema(description = "测评记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "测评记录不能为空") + private Long recordId; @Schema(description = "主观题得分", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "主观题得分不能为空") @@ -24,4 +24,7 @@ public class AssessmentAnswerManualScoreReqVO { @Schema(description = "评语") private String comment; + @Schema(description = "风险等级:1-高风险 2-中风险 3-低风险") + private Integer riskLevel; + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordPageReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordPageReqVO.java index a276486e7d..dc7e7a2128 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordPageReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordPageReqVO.java @@ -10,34 +10,51 @@ 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") +/** + * 管理后台 - 问卷答题记录/测评记录分页 Request VO + * + * @author 芋道源码 + */ @Data public class QuestionnaireRecordPageReqVO extends PageParam { @Schema(description = "问卷ID", example = "18966") private Long questionnaireId; + @Schema(description = "问卷名称", example = "心理测评") + private String questionnaireName; + @Schema(description = "罪犯ID", example = "4071") private Long prisonerId; @Schema(description = "罪犯编号") private String prisonerNo; - @Schema(description = "得分") - private BigDecimal totalScore; + @Schema(description = "罪犯姓名") + private String prisonerName; - @Schema(description = "是否及格:1-及格 2-不及格", example = "2") + @Schema(description = "状态:1-待测评 2-测评中 3-已完成 4-已过期 5-已取消", example = "1") + private Integer status; + + @Schema(description = "及格状态:1-及格 2-不及格 3-待评阅", example = "1") private Integer passStatus; - @Schema(description = "答题时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] answerTime; + @Schema(description = "风险等级:1-高风险 2-中风险 3-低风险", example = "3") + private Integer riskLevel; - @Schema(description = "状态:1-已完成 2-已过期", example = "1") - private Integer status; + @Schema(description = "总分") + private BigDecimal totalScore; + + @Schema(description = "测评开始时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] startTime; + + @Schema(description = "测评截止时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] deadline; @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; -} \ No newline at end of file +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java index 08f4f8b80a..2f23688b66 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordRespVO.java @@ -8,6 +8,11 @@ import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; import cn.idev.excel.annotation.*; +/** + * 管理后台 - 问卷答题记录/测评记录 Response VO + * + * @author 芋道源码 + */ @Schema(description = "管理后台 - 问卷答题记录 Response VO") @Data @ExcelIgnoreUnannotated @@ -17,10 +22,18 @@ public class QuestionnaireRecordRespVO { @ExcelProperty("记录ID") private Long id; + // ==================== 问卷信息 ==================== + @Schema(description = "问卷ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18966") @ExcelProperty("问卷ID") private Long questionnaireId; + @Schema(description = "问卷名称") + @ExcelProperty("问卷名称") + private String questionnaireName; + + // ==================== 罪犯信息 ==================== + @Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4071") @ExcelProperty("罪犯ID") private Long prisonerId; @@ -29,24 +42,90 @@ public class QuestionnaireRecordRespVO { @ExcelProperty("罪犯编号") private String prisonerNo; - @Schema(description = "得分") - @ExcelProperty("得分") + @Schema(description = "罪犯姓名") + @ExcelProperty("罪犯姓名") + private String prisonerName; + + // ==================== 测评状态 ==================== + + @Schema(description = "状态:1-待测评 2-测评中 3-已完成 4-已过期 5-已取消", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("状态") + private Integer status; + + // ==================== 测评时间 ==================== + + @Schema(description = "开始时间") + @ExcelProperty("开始时间") + private LocalDateTime startTime; + + @Schema(description = "结束时间") + @ExcelProperty("结束时间") + private LocalDateTime endTime; + + @Schema(description = "截止日期") + @ExcelProperty("截止日期") + private LocalDateTime deadline; + + // ==================== 评分信息 ==================== + + @Schema(description = "客观题得分") + @ExcelProperty("客观题得分") + private BigDecimal objectiveScore; + + @Schema(description = "主观题得分") + @ExcelProperty("主观题得分") + private BigDecimal subjectiveScore; + + @Schema(description = "总分") + @ExcelProperty("总分") private BigDecimal totalScore; - @Schema(description = "是否及格:1-及格 2-不及格", example = "2") - @ExcelProperty("是否及格:1-及格 2-不及格") + @Schema(description = "及格分数") + @ExcelProperty("及格分数") + private BigDecimal passScore; + + @Schema(description = "及格状态:1-及格 2-不及格 3-待评阅", example = "2") + @ExcelProperty("及格状态") private Integer passStatus; - @Schema(description = "答题时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("答题时间") - private LocalDateTime answerTime; + @Schema(description = "风险等级:1-高风险 2-中风险 3-低风险", example = "3") + @ExcelProperty("风险等级") + private Integer riskLevel; - @Schema(description = "状态:1-已完成 2-已过期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("状态:1-已完成 2-已过期") - private Integer status; + // ==================== 评阅信息 ==================== + + @Schema(description = "评阅人ID") + private Long evaluatorId; + + @Schema(description = "评阅人姓名") + @ExcelProperty("评阅人") + private String evaluatorName; + + @Schema(description = "评阅时间") + @ExcelProperty("评阅时间") + private LocalDateTime evaluateTime; + + // ==================== 统计信息 ==================== + + @Schema(description = "参与人数") + private Integer participantCount; + + @Schema(description = "完成人数") + private Integer completedCount; + + @Schema(description = "答题用时(秒)") + @ExcelProperty("答题用时") + private Integer duration; + + // ==================== 备注 ==================== + + @Schema(description = "备注") + private String remark; + + // ==================== 通用字段 ==================== @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") private LocalDateTime createTime; -} \ No newline at end of file +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordSaveReqVO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordSaveReqVO.java index 0cfb7e8362..44da8f5ca6 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordSaveReqVO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/questionnairerecord/vo/QuestionnaireRecordSaveReqVO.java @@ -8,37 +8,100 @@ import java.math.BigDecimal; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; -@Schema(description = "管理后台 - 问卷答题记录新增/修改 Request VO") +/** + * 管理后台 - 问卷答题记录/测评记录新增/修改 Request VO + * + * @author 芋道源码 + */ @Data public class QuestionnaireRecordSaveReqVO { @Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "8596") private Long id; + // ==================== 问卷信息 ==================== + @Schema(description = "问卷ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "18966") @NotNull(message = "问卷ID不能为空") private Long questionnaireId; + @Schema(description = "问卷名称") + private String questionnaireName; + + // ==================== 罪犯信息 ==================== + @Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "4071") @NotNull(message = "罪犯ID不能为空") private Long prisonerId; - @Schema(description = "罪犯编号", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "罪犯编号不能为空") + @Schema(description = "罪犯编号") private String prisonerNo; - @Schema(description = "得分") - private BigDecimal totalScore; + @Schema(description = "罪犯姓名") + private String prisonerName; - @Schema(description = "是否及格:1-及格 2-不及格", example = "2") - private Integer passStatus; + // ==================== 测评状态 ==================== - @Schema(description = "答题时间", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "答题时间不能为空") - private LocalDateTime answerTime; - - @Schema(description = "状态:1-已完成 2-已过期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态:1-已完成 2-已过期不能为空") + @Schema(description = "状态:1-待测评 2-测评中 3-已完成 4-已过期 5-已取消", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") private Integer status; -} \ No newline at end of file + // ==================== 测评时间 ==================== + + @Schema(description = "开始时间") + private LocalDateTime startTime; + + @Schema(description = "结束时间") + private LocalDateTime endTime; + + @Schema(description = "截止日期") + private LocalDateTime deadline; + + // ==================== 评分信息 ==================== + + @Schema(description = "客观题得分") + private BigDecimal objectiveScore; + + @Schema(description = "主观题得分") + private BigDecimal subjectiveScore; + + @Schema(description = "总分") + private BigDecimal totalScore; + + @Schema(description = "及格分数") + private BigDecimal passScore; + + @Schema(description = "及格状态:1-及格 2-不及格 3-待评阅", example = "2") + private Integer passStatus; + + @Schema(description = "风险等级:1-高风险 2-中风险 3-低风险", example = "3") + private Integer riskLevel; + + // ==================== 评阅信息 ==================== + + @Schema(description = "评阅人ID") + private Long evaluatorId; + + @Schema(description = "评阅人姓名") + private String evaluatorName; + + @Schema(description = "评阅时间") + private LocalDateTime evaluateTime; + + // ==================== 统计信息 ==================== + + @Schema(description = "参与人数") + private Integer participantCount; + + @Schema(description = "完成人数") + private Integer completedCount; + + @Schema(description = "答题用时(秒)") + private Integer duration; + + // ==================== 备注 ==================== + + @Schema(description = "备注") + private String remark; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/PrisonRiskAssessmentController.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/PrisonRiskAssessmentController.java deleted file mode 100644 index dea75d1967..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/controller/admin/riskassessment/PrisonRiskAssessmentController.java +++ /dev/null @@ -1,104 +0,0 @@ -package cn.iocoder.yudao.module.prison.controller.admin.riskassessment; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.constraints.*; -import jakarta.validation.*; -import jakarta.servlet.http.*; -import java.util.*; -import java.io.IOException; - -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.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.prison.controller.admin.riskassessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.riskassessment.RiskAssessmentDO; -import cn.iocoder.yudao.module.prison.service.riskassessment.RiskAssessmentService; - -@Tag(name = "管理后台 - 危险评估") -@RestController -@RequestMapping("/prison/risk-assessment") -@Validated -public class PrisonRiskAssessmentController { - - @Resource - private RiskAssessmentService riskAssessmentService; - - @PostMapping("/create") - @Operation(summary = "创建危险评估") - @PreAuthorize("@ss.hasPermission('prison:risk-assessment:create')") - public CommonResult createRiskAssessment(@Valid @RequestBody RiskAssessmentSaveReqVO createReqVO) { - return success(riskAssessmentService.createRiskAssessment(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新危险评估") - @PreAuthorize("@ss.hasPermission('prison:risk-assessment:update')") - public CommonResult updateRiskAssessment(@Valid @RequestBody RiskAssessmentSaveReqVO updateReqVO) { - riskAssessmentService.updateRiskAssessment(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除危险评估") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('prison:risk-assessment:delete')") - public CommonResult deleteRiskAssessment(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) { - riskAssessmentService.deleteRiskAssessment(id); - return success(true); - } - - @DeleteMapping("/delete-list") - @Parameter(name = "ids", description = "编号", required = true) - @Operation(summary = "批量删除危险评估") - @PreAuthorize("@ss.hasPermission('prison:risk-assessment:delete')") - public CommonResult deleteRiskAssessmentList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List ids) { - riskAssessmentService.deleteRiskAssessmentListByIds(ids); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得危险评估") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('prison:risk-assessment:query')") - public CommonResult getRiskAssessment(@RequestParam("id") Long id) { - RiskAssessmentDO riskAssessment = riskAssessmentService.getRiskAssessment(id); - return success(BeanUtils.toBean(riskAssessment, RiskAssessmentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得危险评估分页") - @PreAuthorize("@ss.hasPermission('prison:risk-assessment:query')") - public CommonResult> getRiskAssessmentPage(@Valid RiskAssessmentPageReqVO pageReqVO) { - PageResult pageResult = riskAssessmentService.getRiskAssessmentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, RiskAssessmentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出危险评估 Excel") - @PreAuthorize("@ss.hasPermission('prison:risk-assessment:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportRiskAssessmentExcel(@Valid RiskAssessmentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = riskAssessmentService.getRiskAssessmentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "危险评估.xls", "数据", RiskAssessmentRespVO.class, - BeanUtils.toBean(list, RiskAssessmentRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/answer/AnswerConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/answer/AnswerConvert.java new file mode 100644 index 0000000000..4bd11c99c1 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/answer/AnswerConvert.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.prison.convert.answer; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import cn.iocoder.yudao.module.prison.controller.admin.answer.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.answer.AnswerDO; + +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 问卷答题记录 Convert + * + * @author 芋道源码 + */ +@Mapper(builder = @Builder(disableBuilder = true)) +public interface AnswerConvert { + + AnswerConvert INSTANCE = Mappers.getMapper(AnswerConvert.class); + + /** + * DO -> RespVO + */ + AnswerRespVO convert(AnswerDO bean); + + /** + * DO -> RespVO List + */ + List convertList(List list); + + /** + * PageResult DO -> PageResult RespVO + */ + PageResult convertPage(PageResult page); + + /** + * SaveReqVO -> DO + */ + AnswerDO convert(AnswerSaveReqVO bean); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentAnswerConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentAnswerConvert.java deleted file mode 100644 index 2549ce44e6..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentAnswerConvert.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.prison.convert.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentAnswerDO; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import org.mapstruct.Builder; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -/** - * 答卷详情 Convert - * - * @author 芋道源码 - */ -@Mapper(uses = {}, builder = @Builder()) -public interface AssessmentAnswerConvert { - - AssessmentAnswerConvert INSTANCE = Mappers.getMapper(AssessmentAnswerConvert.class); - - AssessmentAnswerDO convert(AssessmentAnswerSaveReqVO bean); - - AssessmentAnswerSaveReqVO convert(AssessmentAnswerDO bean); - - AssessmentAnswerRespVO convert(AssessmentAnswerDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentRecordConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentRecordConvert.java deleted file mode 100644 index 6f74131f2f..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentRecordConvert.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.prison.convert.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentRecordDO; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import org.mapstruct.Builder; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -/** - * 测评记录 Convert - * - * @author 芋道源码 - */ -@Mapper(uses = {}, builder = @Builder()) -public interface AssessmentRecordConvert { - - AssessmentRecordConvert INSTANCE = Mappers.getMapper(AssessmentRecordConvert.class); - - AssessmentRecordDO convert(AssessmentRecordSaveReqVO bean); - - AssessmentRecordSaveReqVO convert(AssessmentRecordDO bean); - - AssessmentRecordRespVO convert(AssessmentRecordDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentResultConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentResultConvert.java deleted file mode 100644 index d138b4ddce..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentResultConvert.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.prison.convert.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentResultDO; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import org.mapstruct.Builder; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -/** - * 测评结果 Convert - * - * @author 芋道源码 - */ -@Mapper(uses = {}, builder = @Builder()) -public interface AssessmentResultConvert { - - AssessmentResultConvert INSTANCE = Mappers.getMapper(AssessmentResultConvert.class); - - AssessmentResultDO convert(AssessmentResultSaveReqVO bean); - - AssessmentResultSaveReqVO convert(AssessmentResultDO bean); - - AssessmentResultRespVO convert(AssessmentResultDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentStatisticsConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentStatisticsConvert.java deleted file mode 100644 index 6d32e16029..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/assessment/AssessmentStatisticsConvert.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.prison.convert.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentStatisticsDO; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import org.mapstruct.Builder; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -/** - * 测评统计 Convert - * - * @author 芋道源码 - */ -@Mapper(uses = {}, builder = @Builder()) -public interface AssessmentStatisticsConvert { - - AssessmentStatisticsConvert INSTANCE = Mappers.getMapper(AssessmentStatisticsConvert.class); - - AssessmentStatisticsRespVO convert(AssessmentStatisticsDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionConvert.java new file mode 100644 index 0000000000..c77e1550c6 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionConvert.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.prison.convert.consumption; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 消费订单 Convert + * + * @author xl + */ +@Mapper +public interface ConsumptionConvert { + + ConsumptionConvert INSTANCE = Mappers.getMapper(ConsumptionConvert.class); + + ConsumptionDO convert(ConsumptionSaveReqVO bean); + + ConsumptionRespVO convert(ConsumptionDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionDetailConvert.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionDetailConvert.java new file mode 100644 index 0000000000..1f044e7a7b --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/convert/consumption/ConsumptionDetailConvert.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.prison.convert.consumption; + +import java.util.*; + +import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 消费明细 Convert + * + * @author xl + */ +@Mapper +public interface ConsumptionDetailConvert { + + ConsumptionDetailConvert INSTANCE = Mappers.getMapper(ConsumptionDetailConvert.class); + + ConsumptionDetailDO convert(ConsumptionDetailSaveReqVO bean); + + ConsumptionDetailRespVO convert(ConsumptionDetailDO bean); + + List convertList(List list); + + List convertListResp(List list); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java new file mode 100644 index 0000000000..488f1721ac --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/answer/AnswerDO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.prison.dal.dataobject.answer; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 问卷答题记录 DO + * + * @author 芋道源码 + */ +@TableName("prison_answer") +@KeySequence("prison_answer_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AnswerDO extends BaseDO { + + /** + * 答题记录ID + */ + @TableId + private Long id; + /** + * 测评记录ID + */ + private Long assessmentRecordId; + /** + * 问题ID + */ + private Long questionId; + /** + * 问卷ID(冗余) + */ + private Long questionnaireId; + /** + * 罪犯ID + */ + private Long prisonerId; + /** + * 问题类型:1-单选 2-多选 3-填空 4-评分 5-日期 6-数字 + */ + private Integer questionType; + /** + * 答案内容(填空题、评分题等) + */ + private String answerText; + /** + * 选项ID列表(JSON数组,如 [1,2,3]) + */ + private String optionIds; + /** + * 得分 + */ + private BigDecimal score; + /** + * 是否正确:null-未评分 false-错误 true-正确 + */ + private Boolean isCorrect; + /** + * 答题时间(秒) + */ + private Integer duration; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentAnswerDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentAnswerDO.java deleted file mode 100644 index d6d2b3e1b1..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentAnswerDO.java +++ /dev/null @@ -1,131 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.dataobject.assessment; - -import lombok.*; -import java.util.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 答卷详情 DO - * - * @author 芋道源码 - */ -@TableName("prison_assessment_answer") -@KeySequence("prison_assessment_answer_seq") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AssessmentAnswerDO extends BaseDO { - - /** - * 答卷ID - */ - @TableId - private Long id; - - /** - * 关联测评记录ID - */ - private Long assessmentRecordId; - - /** - * 囚犯ID - */ - private Long prisonerId; - - /** - * 囚犯编号 - */ - private String prisonerCode; - - /** - * 囚犯姓名 - */ - private String prisonerName; - - /** - * 监区ID - */ - private Long areaId; - - /** - * 监区名称 - */ - private String areaName; - - /** - * 监室ID - */ - private Long cellId; - - /** - * 监室名称 - */ - private String cellName; - - /** - * 答卷状态:1-待答题 2-答题中 3-已提交 4-已评分 5-已完成 - */ - private Integer status; - - /** - * 开始答题时间 - */ - private LocalDateTime startTime; - - /** - * 提交时间 - */ - private LocalDateTime submitTime; - - /** - * 答题用时(秒) - */ - private Integer duration; - - /** - * 客观题得分 - */ - private BigDecimal objectiveScore; - - /** - * 主观题得分 - */ - private BigDecimal subjectiveScore; - - /** - * 总分 - */ - private BigDecimal totalScore; - - /** - * 是否及格 - */ - private Boolean passed; - - /** - * 评语 - */ - private String comment; - - /** - * 评卷人ID - */ - private Long scorerId; - - /** - * 评卷人名称 - */ - private String scorerName; - - /** - * 评分时间 - */ - private LocalDateTime scoreTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentRecordDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentRecordDO.java deleted file mode 100644 index 00ce7d34e8..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentRecordDO.java +++ /dev/null @@ -1,116 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.dataobject.assessment; - -import lombok.*; -import java.util.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 测评记录 DO - * - * @author 芋道源码 - */ -@TableName("prison_assessment_record") -@KeySequence("prison_assessment_record_seq") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AssessmentRecordDO extends BaseDO { - - /** - * 测评记录ID - */ - @TableId - private Long id; - - /** - * 测评名称 - */ - private String name; - - /** - * 关联问卷模板ID - */ - private Long questionnaireId; - - /** - * 测评类型:1-心理测评 2-行为评估 3-满意度调查 - */ - private Integer type; - - /** - * 发起测评的监狱/监区ID - */ - private Long areaId; - - /** - * 发起测评的监狱/监区名称 - */ - private String areaName; - - /** - * 发起人ID - */ - private Long initiatorId; - - /** - * 发起人名称 - */ - private String initiatorName; - - /** - * 测评状态:1-未开始 2-进行中 3-已完成 4-已取消 - */ - private Integer status; - - /** - * 计划开始时间 - */ - private LocalDateTime planStartTime; - - /** - * 计划结束时间 - */ - private LocalDateTime planEndTime; - - /** - * 实际开始时间 - */ - private LocalDateTime actualStartTime; - - /** - * 实际结束时间 - */ - private LocalDateTime actualEndTime; - - /** - * 参与人数 - */ - private Integer participantCount; - - /** - * 完成人数 - */ - private Integer completedCount; - - /** - * 测评说明 - */ - private String description; - - /** - * 是否启用自动评分 - */ - private Boolean enableAutoScore; - - /** - * 及格分数 - */ - private BigDecimal passScore; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentResultDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentResultDO.java deleted file mode 100644 index 1de6b698db..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentResultDO.java +++ /dev/null @@ -1,136 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.dataobject.assessment; - -import lombok.*; -import java.util.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 测评结果 DO - * - * @author 芋道源码 - */ -@TableName("prison_assessment_result") -@KeySequence("prison_assessment_result_seq") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AssessmentResultDO extends BaseDO { - - /** - * 结果ID - */ - @TableId - private Long id; - - /** - * 关联答卷ID - */ - private Long answerId; - - /** - * 关联测评记录ID - */ - private Long assessmentRecordId; - - /** - * 囚犯ID - */ - private Long prisonerId; - - /** - * 囚犯编号 - */ - private String prisonerCode; - - /** - * 囚犯姓名 - */ - private String prisonerName; - - /** - * 关联题目ID - */ - private Long questionId; - - /** - * 题目编号 - */ - private String questionCode; - - /** - * 题目内容 - */ - private String questionContent; - - /** - * 题目类型:1-单选 2-多选 3-判断 4-填空 5-问答 - */ - private Integer questionType; - - /** - * 题目分值 - */ - private BigDecimal questionScore; - - /** - * 囚犯答案 - */ - private String answer; - - /** - * 正确答案(客观题) - */ - private String correctAnswer; - - /** - * 是否正确 - */ - private Boolean correct; - - /** - * 得分 - */ - private BigDecimal score; - - /** - * 是否需要人工评阅 - */ - private Boolean needManualReview; - - /** - * 人工评阅状态:1-待评阅 2-已评阅 - */ - private Integer manualReviewStatus; - - /** - * 人工评分 - */ - private BigDecimal manualScore; - - /** - * 人工评语 - */ - private String manualComment; - - /** - * 评阅人ID - */ - private Long reviewerId; - - /** - * 评阅人名称 - */ - private String reviewerName; - - /** - * 评阅时间 - */ - private LocalDateTime reviewTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentStatisticsDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentStatisticsDO.java deleted file mode 100644 index e75c9abd4f..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/assessment/AssessmentStatisticsDO.java +++ /dev/null @@ -1,115 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.dataobject.assessment; - -import lombok.*; -import java.util.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 测评统计 DO - * - * @author 芋道源码 - */ -@TableName("prison_assessment_statistics") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AssessmentStatisticsDO extends BaseDO { - - /** - * 统计ID - */ - @TableId - private Long id; - - /** - * 关联测评记录ID - */ - private Long assessmentRecordId; - - /** - * 测评名称 - */ - private String assessmentName; - - /** - * 总参与人数 - */ - private Integer totalCount; - - /** - * 已完成人数 - */ - private Integer completedCount; - - /** - * 完成率 - */ - private BigDecimal completionRate; - - /** - * 平均分 - */ - private BigDecimal averageScore; - - /** - * 最高分 - */ - private BigDecimal highestScore; - - /** - * 最低分 - */ - private BigDecimal lowestScore; - - /** - * 及格人数 - */ - private Integer passedCount; - - /** - * 及格率 - */ - private BigDecimal passRate; - - /** - * 优秀人数(90分以上) - */ - private Integer excellentCount; - - /** - * 优秀率 - */ - private BigDecimal excellentRate; - - /** - * 风险人数(60分以下) - */ - private Integer riskCount; - - /** - * 风险率 - */ - private BigDecimal riskRate; - - /** - * 风险分布JSON:{"low": 10, "medium": 5, "high": 3} - */ - private String riskDistribution; - - /** - * 分数分布JSON:{"0-20": 5, "20-40": 10, "40-60": 15, "60-80": 20, "80-100": 8} - */ - private String scoreDistribution; - - /** - * 统计时间 - */ - private LocalDateTime statisticsTime; - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDO.java index 35289fc4a5..9e351ef655 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDO.java @@ -3,17 +3,14 @@ package cn.iocoder.yudao.module.prison.dal.dataobject.consumption; import lombok.*; import java.util.*; import java.math.BigDecimal; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.time.LocalDateTime; import java.time.LocalDateTime; import com.baomidou.mybatisplus.annotation.*; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; /** - * 消费记录 DO + * 消费订单 DO * - * @author 芋道源码 + * @author xl */ @TableName("prison_consumption") @KeySequence("prison_consumption_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @@ -26,7 +23,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; public class ConsumptionDO extends BaseDO { /** - * 记录ID + * 消费ID */ @TableId private Long id; @@ -38,30 +35,22 @@ public class ConsumptionDO extends BaseDO { * 罪犯编号 */ private String prisonerNo; - /** - * 类型:1-存款 2-消费 3-转账 - */ - private Integer type; - /** - * 金额 - */ - private BigDecimal amount; - /** - * 账户余额 - */ - private BigDecimal balance; - /** - * 商品名称 - */ - private String goodsName; - /** - * 商品数量 - */ - private Integer goodsCount; /** * 订单号 */ private String orderNo; + /** + * 类型:1-购物 2-餐饮 3-医疗 4-通讯 5-其他 + */ + private Integer type; + /** + * 订单总金额 + */ + private BigDecimal totalAmount; + /** + * 账户余额(消费后) + */ + private BigDecimal balance; /** * 交易时间 */ @@ -75,5 +64,4 @@ public class ConsumptionDO extends BaseDO { */ private String remark; - -} \ No newline at end of file +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDetailDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDetailDO.java new file mode 100644 index 0000000000..c84a99eb74 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/consumption/ConsumptionDetailDO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.prison.dal.dataobject.consumption; + +import lombok.*; +import java.util.*; +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 消费明细 DO + * + * @author xl + */ +@TableName("prison_consumption_detail") +@KeySequence("prison_consumption_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ConsumptionDetailDO extends BaseDO { + + /** + * 明细ID + */ + @TableId + private Long id; + /** + * 消费订单ID + */ + private Long consumptionId; + /** + * 罪犯ID(冗余,便于查询) + */ + private Long prisonerId; + /** + * 商品名称 + */ + private String goodsName; + /** + * 商品编码 + */ + private String goodsCode; + /** + * 商品单价 + */ + private BigDecimal goodsPrice; + /** + * 商品数量 + */ + private Integer goodsCount; + /** + * 小计金额 + */ + private BigDecimal subtotal; + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java index fb3ca80326..8d4d6e30b3 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/dataobject/questionnairerecord/QuestionnaireRecordDO.java @@ -4,18 +4,16 @@ import lombok.*; import java.util.*; import java.math.BigDecimal; import java.time.LocalDateTime; -import java.time.LocalDateTime; -import java.time.LocalDateTime; import com.baomidou.mybatisplus.annotation.*; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; /** - * 问卷答题记录 DO + * 问卷答题记录 / 测评记录 DO * * @author 芋道源码 */ @TableName("prison_questionnaire_record") -@KeySequence("prison_questionnaire_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@KeySequence("prison_questionnaire_record_seq") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @@ -29,10 +27,20 @@ public class QuestionnaireRecordDO extends BaseDO { */ @TableId private Long id; + + // ==================== 问卷信息 ==================== + /** * 问卷ID */ private Long questionnaireId; + /** + * 问卷名称 + */ + private String questionnaireName; + + // ==================== 罪犯信息 ==================== + /** * 罪犯ID */ @@ -42,21 +50,94 @@ public class QuestionnaireRecordDO extends BaseDO { */ private String prisonerNo; /** - * 得分 + * 罪犯姓名 */ - private BigDecimal totalScore; + private String prisonerName; + + // ==================== 测评状态 ==================== + /** - * 是否及格:1-及格 2-不及格 - */ - private Integer passStatus; - /** - * 答题时间 - */ - private LocalDateTime answerTime; - /** - * 状态:1-已完成 2-已过期 + * 状态:1-待测评 2-测评中 3-已完成 4-已过期 5-已取消 */ private Integer status; + // ==================== 测评时间 ==================== + + /** + * 开始时间 + */ + private LocalDateTime startTime; + /** + * 结束时间 + */ + private LocalDateTime endTime; + /** + * 截止日期 + */ + private LocalDateTime deadline; + + // ==================== 评分信息 ==================== + + /** + * 客观题得分 + */ + private BigDecimal objectiveScore; + /** + * 主观题得分 + */ + private BigDecimal subjectiveScore; + /** + * 总分 + */ + private BigDecimal totalScore; + /** + * 及格分数 + */ + private BigDecimal passScore; + /** + * 及格状态:1-及格 2-不及格 3-待评阅 + */ + private Integer passStatus; + /** + * 风险等级:1-高风险 2-中风险 3-低风险 + */ + private Integer riskLevel; + + // ==================== 评阅信息 ==================== + + /** + * 评阅人ID + */ + private Long evaluatorId; + /** + * 评阅人姓名 + */ + private String evaluatorName; + /** + * 评阅时间 + */ + private LocalDateTime evaluateTime; + + // ==================== 统计信息 ==================== + + /** + * 参与人数 + */ + private Integer participantCount; + /** + * 完成人数 + */ + private Integer completedCount; + /** + * 答题用时(秒) + */ + private Integer duration; + + // ==================== 备注 ==================== + + /** + * 备注 + */ + private String remark; } \ No newline at end of file diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/answer/AnswerMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/answer/AnswerMapper.java new file mode 100644 index 0000000000..2a0edbe46c --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/answer/AnswerMapper.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.prison.dal.mysql.answer; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +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.answer.AnswerDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.prison.controller.admin.answer.vo.*; + +/** + * 问卷答题记录 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AnswerMapper extends BaseMapperX { + + default PageResult selectPage(AnswerPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(AnswerDO::getAssessmentRecordId, reqVO.getAssessmentRecordId()) + .eqIfPresent(AnswerDO::getQuestionId, reqVO.getQuestionId()) + .eqIfPresent(AnswerDO::getQuestionnaireId, reqVO.getQuestionnaireId()) + .eqIfPresent(AnswerDO::getPrisonerId, reqVO.getPrisonerId()) + .eqIfPresent(AnswerDO::getQuestionType, reqVO.getQuestionType()) + .betweenIfPresent(AnswerDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(AnswerDO::getId)); + } + + /** + * 根据测评记录ID查询所有答题记录 + */ + default List selectListByAssessmentRecordId(Long assessmentRecordId) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(AnswerDO::getAssessmentRecordId, assessmentRecordId) + .orderByAsc(AnswerDO::getId)); + } + + /** + * 根据测评记录ID和问题ID查询答题记录 + */ + default AnswerDO selectByAssessmentRecordIdAndQuestionId(Long assessmentRecordId, Long questionId) { + return selectOne(new LambdaQueryWrapperX() + .eqIfPresent(AnswerDO::getAssessmentRecordId, assessmentRecordId) + .eqIfPresent(AnswerDO::getQuestionId, questionId)); + } + + /** + * 根据测评记录ID删除所有答题记录 + */ + default int deleteByAssessmentRecordId(Long assessmentRecordId) { + return delete(new LambdaQueryWrapperX() + .eqIfPresent(AnswerDO::getAssessmentRecordId, assessmentRecordId)); + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentAnswerMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentAnswerMapper.java deleted file mode 100644 index 333ae091b1..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentAnswerMapper.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.mysql.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -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.assessment.AssessmentAnswerDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; - -/** - * 答卷详情 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface AssessmentAnswerMapper extends BaseMapperX { - - default PageResult selectPage(AssessmentAnswerPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(AssessmentAnswerDO::getAssessmentRecordId, reqVO.getAssessmentRecordId()) - .eqIfPresent(AssessmentAnswerDO::getPrisonerId, reqVO.getPrisonerId()) - .likeIfPresent(AssessmentAnswerDO::getPrisonerCode, reqVO.getPrisonerCode()) - .likeIfPresent(AssessmentAnswerDO::getPrisonerName, reqVO.getPrisonerName()) - .eqIfPresent(AssessmentAnswerDO::getAreaId, reqVO.getAreaId()) - .eqIfPresent(AssessmentAnswerDO::getCellId, reqVO.getCellId()) - .eqIfPresent(AssessmentAnswerDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(AssessmentAnswerDO::getStartTime, reqVO.getStartTime()) - .betweenIfPresent(AssessmentAnswerDO::getSubmitTime, reqVO.getSubmitTime()) - .orderByDesc(AssessmentAnswerDO::getId)); - } - - /** - * 根据测评记录ID查询所有答卷 - */ - default List selectListByAssessmentRecordId(Long assessmentRecordId) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(AssessmentAnswerDO::getAssessmentRecordId, assessmentRecordId)); - } - - /** - * 根据囚犯ID和测评记录ID查询答卷 - */ - default AssessmentAnswerDO selectByPrisonerAndRecord(Long prisonerId, Long assessmentRecordId) { - return selectOne(new LambdaQueryWrapperX() - .eqIfPresent(AssessmentAnswerDO::getPrisonerId, prisonerId) - .eqIfPresent(AssessmentAnswerDO::getAssessmentRecordId, assessmentRecordId)); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentRecordMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentRecordMapper.java deleted file mode 100644 index 1a1157e87a..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentRecordMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.mysql.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -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.assessment.AssessmentRecordDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; - -/** - * 测评记录 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface AssessmentRecordMapper extends BaseMapperX { - - default PageResult selectPage(AssessmentRecordPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(AssessmentRecordDO::getName, reqVO.getName()) - .eqIfPresent(AssessmentRecordDO::getQuestionnaireId, reqVO.getQuestionnaireId()) - .eqIfPresent(AssessmentRecordDO::getType, reqVO.getType()) - .eqIfPresent(AssessmentRecordDO::getAreaId, reqVO.getAreaId()) - .eqIfPresent(AssessmentRecordDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(AssessmentRecordDO::getPlanStartTime, reqVO.getPlanStartTime()) - .betweenIfPresent(AssessmentRecordDO::getPlanEndTime, reqVO.getPlanEndTime()) - .betweenIfPresent(AssessmentRecordDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(AssessmentRecordDO::getId)); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentResultMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentResultMapper.java deleted file mode 100644 index d60dfc365b..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentResultMapper.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.mysql.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -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.assessment.AssessmentResultDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; - -/** - * 测评结果 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface AssessmentResultMapper extends BaseMapperX { - - default PageResult selectPage(AssessmentResultPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(AssessmentResultDO::getAnswerId, reqVO.getAnswerId()) - .eqIfPresent(AssessmentResultDO::getAssessmentRecordId, reqVO.getAssessmentRecordId()) - .eqIfPresent(AssessmentResultDO::getPrisonerId, reqVO.getPrisonerId()) - .eqIfPresent(AssessmentResultDO::getQuestionId, reqVO.getQuestionId()) - .eqIfPresent(AssessmentResultDO::getQuestionType, reqVO.getQuestionType()) - .eqIfPresent(AssessmentResultDO::getCorrect, reqVO.getCorrect()) - .eqIfPresent(AssessmentResultDO::getNeedManualReview, reqVO.getNeedManualReview()) - .eqIfPresent(AssessmentResultDO::getManualReviewStatus, reqVO.getManualReviewStatus()) - .orderByDesc(AssessmentResultDO::getId)); - } - - /** - * 根据答卷ID查询所有结果 - */ - default List selectListByAnswerId(Long answerId) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(AssessmentResultDO::getAnswerId, answerId) - .orderByAsc(AssessmentResultDO::getId)); - } - - /** - * 根据测评记录ID查询所有结果 - */ - default List selectListByAssessmentRecordId(Long assessmentRecordId) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(AssessmentResultDO::getAssessmentRecordId, assessmentRecordId)); - } - - /** - * 查询需要人工评阅的结果 - */ - default List selectListNeedManualReview() { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(AssessmentResultDO::getNeedManualReview, true) - .eqIfPresent(AssessmentResultDO::getManualReviewStatus, 1) - .orderByAsc(AssessmentResultDO::getId)); - } - - /** - * 根据答卷ID统计客观题得分 - */ - default java.math.BigDecimal sumObjectiveScoreByAnswerId(Long answerId) { - return selectObjs(new LambdaQueryWrapperX() - .eqIfPresent(AssessmentResultDO::getAnswerId, answerId) - .eqIfPresent(AssessmentResultDO::getNeedManualReview, false)) - .stream().map(obj -> (java.math.BigDecimal) obj) - .reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentStatisticsMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentStatisticsMapper.java deleted file mode 100644 index b6c5aee66d..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/assessment/AssessmentStatisticsMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.prison.dal.mysql.assessment; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -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.assessment.AssessmentStatisticsDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; - -/** - * 测评统计 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface AssessmentStatisticsMapper extends BaseMapperX { - - default PageResult selectPage(AssessmentStatisticsPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(AssessmentStatisticsDO::getAssessmentRecordId, reqVO.getAssessmentRecordId()) - .likeIfPresent(AssessmentStatisticsDO::getAssessmentName, reqVO.getAssessmentName()) - .orderByDesc(AssessmentStatisticsDO::getId)); - } - - /** - * 根据测评记录ID查询统计 - */ - default AssessmentStatisticsDO selectByAssessmentRecordId(Long assessmentRecordId) { - return selectOne(new LambdaQueryWrapperX() - .eqIfPresent(AssessmentStatisticsDO::getAssessmentRecordId, assessmentRecordId) - .orderByDesc(AssessmentStatisticsDO::getId) - .last("LIMIT 1")); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionDetailMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionDetailMapper.java new file mode 100644 index 0000000000..e520731096 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionDetailMapper.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.prison.dal.mysql.consumption; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 消费明细 Mapper + * + * @author xl + */ +@Mapper +public interface ConsumptionDetailMapper extends BaseMapperX { + + /** + * 根据消费订单ID列表删除 + */ + default void deleteListByConsumptionIds(Collection consumptionIds) { + delete(new LambdaQueryWrapperX() + .in(ConsumptionDetailDO::getConsumptionId, consumptionIds)); + } + + /** + * 根据消费订单ID删除 + */ + default void deleteListByConsumptionId(Long consumptionId) { + delete(new LambdaQueryWrapperX() + .eq(ConsumptionDetailDO::getConsumptionId, consumptionId)); + } + + /** + * 根据消费订单ID查询明细列表 + */ + default List selectListByConsumptionId(Long consumptionId) { + return selectList(new LambdaQueryWrapperX() + .eq(ConsumptionDetailDO::getConsumptionId, consumptionId)); + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java index 3c6276b960..9636de8d7b 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/consumption/ConsumptionMapper.java @@ -10,9 +10,9 @@ import org.apache.ibatis.annotations.Mapper; import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; /** - * 消费记录 Mapper + * 消费订单 Mapper * - * @author 芋道源码 + * @author xl */ @Mapper public interface ConsumptionMapper extends BaseMapperX { @@ -22,10 +22,8 @@ public interface ConsumptionMapper extends BaseMapperX { .eqIfPresent(ConsumptionDO::getPrisonerId, reqVO.getPrisonerId()) .eqIfPresent(ConsumptionDO::getPrisonerNo, reqVO.getPrisonerNo()) .eqIfPresent(ConsumptionDO::getType, reqVO.getType()) - .eqIfPresent(ConsumptionDO::getAmount, reqVO.getAmount()) + .eqIfPresent(ConsumptionDO::getTotalAmount, reqVO.getTotalAmount()) .eqIfPresent(ConsumptionDO::getBalance, reqVO.getBalance()) - .likeIfPresent(ConsumptionDO::getGoodsName, reqVO.getGoodsName()) - .eqIfPresent(ConsumptionDO::getGoodsCount, reqVO.getGoodsCount()) .eqIfPresent(ConsumptionDO::getOrderNo, reqVO.getOrderNo()) .betweenIfPresent(ConsumptionDO::getTradeTime, reqVO.getTradeTime()) .eqIfPresent(ConsumptionDO::getStatus, reqVO.getStatus()) @@ -34,4 +32,4 @@ public interface ConsumptionMapper extends BaseMapperX { .orderByDesc(ConsumptionDO::getId)); } -} \ No newline at end of file +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnairerecord/QuestionnaireRecordMapper.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnairerecord/QuestionnaireRecordMapper.java index 808c55ad6a..5c63ed0e15 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnairerecord/QuestionnaireRecordMapper.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/dal/mysql/questionnairerecord/QuestionnaireRecordMapper.java @@ -24,7 +24,6 @@ public interface QuestionnaireRecordMapper extends BaseMapperX MAP = new HashMap<>(); + + static { + for (ConsumptionStatusEnum item : ConsumptionStatusEnum.values()) { + MAP.put(item.getCode(), item); + } + } + + public static ConsumptionStatusEnum fromCode(Integer code) { + return MAP.get(code); + } + + public boolean isValid() { + return MAP.containsKey(this.code); + } +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ConsumptionTypeEnum.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ConsumptionTypeEnum.java new file mode 100644 index 0000000000..0648451267 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ConsumptionTypeEnum.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.prison.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; + +/** + * 消费类型枚举 + * + * @author xl + */ +@Getter +@AllArgsConstructor +public enum ConsumptionTypeEnum { + + SHOPPING(1, "购物", "shopping"), + DINING(2, "餐饮", "dining"), + MEDICAL(3, "医疗", "medical"), + COMMUNICATION(4, "通讯", "communication"), + OTHER(5, "其他", "other"); + + /** + * 类型编码 + */ + private final Integer code; + /** + * 类型名称 + */ + private final String name; + /** + * 类型标识(用于字典) + */ + private final String type; + + private static final Map MAP = new HashMap<>(); + + static { + for (ConsumptionTypeEnum item : ConsumptionTypeEnum.values()) { + MAP.put(item.getCode(), item); + } + } + + public static ConsumptionTypeEnum fromCode(Integer code) { + return MAP.get(code); + } + + public boolean isValid() { + return MAP.containsKey(this.code); + } +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java index 1b839298f7..a372f078a3 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/ErrorCodeConstants.java @@ -35,6 +35,9 @@ public class ErrorCodeConstants { // ========== 消费记录 7xxxx ========== public static final ErrorCode PRISON_CONSUMPTION_NOT_EXISTS = new ErrorCode(7_000_001, "消费记录不存在"); + public static final ErrorCode PRISON_CONSUMPTION_AMOUNT_MISMATCH = new ErrorCode(7_000_002, "消费明细金额与订单总金额不一致"); + public static final ErrorCode PRISON_CONSUMPTION_DETAIL_EMPTY = new ErrorCode(7_000_003, "消费明细不能为空"); + public static final ErrorCode PRISON_CONSUMPTION_DETAIL_INVALID = new ErrorCode(7_000_004, "消费明细数据不合法,请检查商品名称、单价和数量"); // ========== 罪犯监区变动记录 8xxxx ========== public static final ErrorCode PRISONER_AREA_LOG_NOT_EXISTS = new ErrorCode(8_000_001, "罪犯监区变动记录不存在"); @@ -56,5 +59,9 @@ public class ErrorCodeConstants { public static final ErrorCode QUESTIONNAIRE_NOT_EXISTS = PRISON_QUESTIONNAIRE_NOT_EXISTS; public static final ErrorCode QUESTION_NOT_EXISTS = PRISON_QUESTION_NOT_EXISTS; public static final ErrorCode QUESTIONNAIRE_RECORD_NOT_EXISTS = PRISON_QUESTIONNAIRE_RECORD_NOT_EXISTS; + public static final ErrorCode QUESTIONNAIRE_RECORD_STATUS_ERROR = new ErrorCode(6_000_004, "问卷答题记录状态不合法"); + + // ========== 答卷管理 10xxxx ========== + public static final ErrorCode ANSWER_NOT_EXISTS = new ErrorCode(10_000_001, "答卷记录不存在"); } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordPassStatusEnum.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordPassStatusEnum.java index a29644fc89..43967bf114 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordPassStatusEnum.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordPassStatusEnum.java @@ -5,6 +5,7 @@ import lombok.Getter; /** * 问卷答题记录及格状态枚举 + * 状态:1-及格 2-不及格 3-待评阅 * * @author 芋道源码 */ @@ -12,8 +13,9 @@ import lombok.Getter; @AllArgsConstructor public enum QuestionnaireRecordPassStatusEnum { - NOT_PASSED(0, "未及格"), - PASSED(1, "及格"); + PASSED(1, "及格"), + FAILED(2, "不及格"), + PENDING(3, "待评阅"); /** * 状态 @@ -38,6 +40,9 @@ public enum QuestionnaireRecordPassStatusEnum { } public static QuestionnaireRecordPassStatusEnum getByStatus(Integer status) { + if (status == null) { + return null; + } for (QuestionnaireRecordPassStatusEnum value : values()) { if (value.getStatus().equals(status)) { return value; @@ -45,4 +50,5 @@ public enum QuestionnaireRecordPassStatusEnum { } return null; } + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordStatusEnum.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordStatusEnum.java index a8b68196c2..f86f8aaa52 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordStatusEnum.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/QuestionnaireRecordStatusEnum.java @@ -5,6 +5,7 @@ import lombok.Getter; /** * 问卷答题记录状态枚举 + * 状态:1-待测评 2-测评中 3-已完成 4-已过期 5-已取消 * * @author 芋道源码 */ @@ -12,8 +13,11 @@ import lombok.Getter; @AllArgsConstructor public enum QuestionnaireRecordStatusEnum { - PENDING(1, "待评估"), - COMPLETED(2, "已完成"); + PENDING(1, "待测评"), + IN_PROGRESS(2, "测评中"), + COMPLETED(3, "已完成"), + EXPIRED(4, "已过期"), + CANCELLED(5, "已取消"); /** * 状态 @@ -45,4 +49,12 @@ public enum QuestionnaireRecordStatusEnum { } return null; } + + /** + * 判断是否为待测评或测评中状态(可进行状态流转) + */ + public static boolean canTransition(Integer status) { + return PENDING.getStatus().equals(status) || IN_PROGRESS.getStatus().equals(status); + } + } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentPassStatusEnum.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentPassStatusEnum.java new file mode 100644 index 0000000000..0b14a61b2f --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentPassStatusEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.prison.enums.questionnaire; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 测评及格状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum AssessmentPassStatusEnum { + + PASSED(1, "及格"), + FAILED(2, "不及格"), + PENDING(3, "待评阅"); + + /** + * 状态 + */ + private final Integer status; + /** + * 名称 + */ + private final String name; + + public static AssessmentPassStatusEnum getByStatus(Integer status) { + for (AssessmentPassStatusEnum statusEnum : values()) { + if (statusEnum.getStatus().equals(status)) { + return statusEnum; + } + } + return null; + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentRecordStatusEnum.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentRecordStatusEnum.java new file mode 100644 index 0000000000..541f444291 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/AssessmentRecordStatusEnum.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.prison.enums.questionnaire; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 测评记录状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum AssessmentRecordStatusEnum { + + PENDING(1, "待测评"), + IN_PROGRESS(2, "测评中"), + COMPLETED(3, "已完成"), + EXPIRED(4, "已过期"), + CANCELLED(5, "已取消"); + + /** + * 状态 + */ + private final Integer status; + /** + * 名称 + */ + private final String name; + + public static AssessmentRecordStatusEnum getByStatus(Integer status) { + for (AssessmentRecordStatusEnum statusEnum : values()) { + if (statusEnum.getStatus().equals(status)) { + return statusEnum; + } + } + return null; + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/RiskLevelEnum.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/RiskLevelEnum.java new file mode 100644 index 0000000000..44a8921517 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/enums/questionnaire/RiskLevelEnum.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.prison.enums.questionnaire; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 风险等级枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum RiskLevelEnum { + + HIGH(1, "高风险"), + MEDIUM(2, "中风险"), + LOW(3, "低风险"); + + /** + * 等级 + */ + private final Integer level; + /** + * 名称 + */ + private final String name; + + public static RiskLevelEnum getByLevel(Integer level) { + for (RiskLevelEnum levelEnum : values()) { + if (levelEnum.getLevel().equals(level)) { + return levelEnum; + } + } + return null; + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java new file mode 100644 index 0000000000..a5f3b80ac7 --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerService.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.prison.service.answer; + +import java.util.*; +import jakarta.validation.*; +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; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import java.math.BigDecimal; + +/** + * 问卷答题记录 Service 接口 + * + * @author 芋道源码 + */ +public interface AnswerService { + + /** + * 创建答题记录 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createAnswer(@Valid AnswerSaveReqVO createReqVO); + + /** + * 更新答题记录 + * + * @param updateReqVO 更新信息 + */ + void updateAnswer(@Valid AnswerSaveReqVO updateReqVO); + + /** + * 删除答题记录 + * + * @param id 编号 + */ + void deleteAnswer(Long id); + + /** + * 批量删除答题记录 + * + * @param ids 编号列表 + */ + void deleteAnswerListByIds(List ids); + + /** + * 获得答题记录 + * + * @param id 编号 + * @return 答题记录 + */ + AnswerDO getAnswer(Long id); + + /** + * 获得答题记录分页 + * + * @param pageReqVO 分页查询 + * @return 答题记录分页 + */ + PageResult getAnswerPage(AnswerPageReqVO pageReqVO); + + /** + * 根据测评记录ID查询所有答题记录 + * + * @param assessmentRecordId 测评记录ID + * @return 答题记录列表 + */ + List getAnswersByAssessmentRecordId(Long assessmentRecordId); + + /** + * 批量保存答题记录(提交答卷时使用) + * + * @param assessmentRecordId 测评记录ID + * @param questionnaireId 问卷ID + * @param prisonerId 罪犯ID + * @param answers 答题详情列表 + */ + void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId, List answers); + + /** + * 计算客观题得分并更新 + * + * @param assessmentRecordId 测评记录ID + * @return 总客观题得分 + */ + BigDecimal calculateObjectiveScore(Long assessmentRecordId); + + /** + * 根据测评记录ID删除所有答题记录 + * + * @param assessmentRecordId 测评记录ID + */ + void deleteByAssessmentRecordId(Long assessmentRecordId); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java new file mode 100644 index 0000000000..40c16d427e --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/answer/AnswerServiceImpl.java @@ -0,0 +1,373 @@ +package cn.iocoder.yudao.module.prison.service.answer; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.stereotype.Service; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +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; +import cn.iocoder.yudao.module.prison.dal.dataobject.question.QuestionDO; +import cn.iocoder.yudao.module.prison.dal.mysql.answer.AnswerMapper; +import cn.iocoder.yudao.module.prison.dal.mysql.question.QuestionMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.prison.enums.QuestionnaireRecordPassStatusEnum; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; + +/** + * 问卷答题记录 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AnswerServiceImpl implements AnswerService { + + @Resource + private AnswerMapper answerMapper; + + @Resource + private QuestionMapper questionMapper; + + // 问题类型常量 + private static final int QUESTION_TYPE_SINGLE = 1; // 单选 + private static final int QUESTION_TYPE_MULTI = 2; // 多选 + private static final int QUESTION_TYPE_FILL = 3; // 填空 + private static final int QUESTION_TYPE_SCORE = 4; // 评分 + private static final int QUESTION_TYPE_DATE = 5; // 日期 + private static final int QUESTION_TYPE_NUMBER = 6; // 数字 + + @Override + public Long createAnswer(AnswerSaveReqVO createReqVO) { + // 插入 + AnswerDO answer = BeanUtils.toBean(createReqVO, AnswerDO.class); + answerMapper.insert(answer); + return answer.getId(); + } + + @Override + public void updateAnswer(AnswerSaveReqVO updateReqVO) { + // 校验存在 + validateAnswerExists(updateReqVO.getId()); + // 更新 + AnswerDO updateObj = BeanUtils.toBean(updateReqVO, AnswerDO.class); + answerMapper.updateById(updateObj); + } + + @Override + public void deleteAnswer(Long id) { + // 校验存在 + validateAnswerExists(id); + // 删除 + answerMapper.deleteById(id); + } + + @Override + public void deleteAnswerListByIds(List ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 校验所有ID都存在 + Long count = answerMapper.selectCount(new LambdaQueryWrapper() + .in(AnswerDO::getId, ids)); + if (count == null || count != ids.size()) { + throw exception(ANSWER_NOT_EXISTS); + } + // 删除 + answerMapper.deleteByIds(ids); + } + + private void validateAnswerExists(Long id) { + if (answerMapper.selectById(id) == null) { + throw exception(ANSWER_NOT_EXISTS); + } + } + + @Override + public AnswerDO getAnswer(Long id) { + return answerMapper.selectById(id); + } + + @Override + public PageResult getAnswerPage(AnswerPageReqVO pageReqVO) { + return answerMapper.selectPage(pageReqVO); + } + + @Override + public List getAnswersByAssessmentRecordId(Long assessmentRecordId) { + return answerMapper.selectListByAssessmentRecordId(assessmentRecordId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId, + List answers) { + if (CollUtil.isEmpty(answers)) { + return; + } + + for (AssessmentAnswerSubmitReqVO.AnswerItem answerItem : answers) { + // 获取问题信息 + QuestionDO question = questionMapper.selectById(answerItem.getQuestionId()); + if (question == null) { + continue; + } + + // 创建答题记录 + AnswerDO answer = new AnswerDO(); + answer.setAssessmentRecordId(assessmentRecordId); + answer.setQuestionId(answerItem.getQuestionId()); + answer.setQuestionnaireId(questionnaireId); + answer.setPrisonerId(prisonerId); + answer.setQuestionType(question.getType()); + + // 设置答案内容 + if (answerItem.getOptionIds() != null && !answerItem.getOptionIds().isEmpty()) { + answer.setOptionIds(JSONArray.toJSONString(answerItem.getOptionIds())); + } + if (answerItem.getAnswer() != null) { + answer.setAnswerText(answerItem.getAnswer()); + } + + // 计算得分(单选/多选题) + BigDecimal score = calculateQuestionScore(question, answerItem); + answer.setScore(score); + + // 判断是否正确(单选/多选题) + if (question.getType() == QUESTION_TYPE_SINGLE || question.getType() == QUESTION_TYPE_MULTI) { + Boolean isCorrect = isAnswerCorrect(question, answerItem); + answer.setIsCorrect(isCorrect); + } + + answerMapper.insert(answer); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public BigDecimal calculateObjectiveScore(Long assessmentRecordId) { + // 获取该测评的所有答题记录 + List answers = answerMapper.selectListByAssessmentRecordId(assessmentRecordId); + if (CollUtil.isEmpty(answers)) { + return BigDecimal.ZERO; + } + + // 计算客观题得分(单选、多选) + BigDecimal totalScore = BigDecimal.ZERO; + for (AnswerDO answer : answers) { + if (answer.getScore() != null) { + totalScore = totalScore.add(answer.getScore()); + } + } + + return totalScore.setScale(2, RoundingMode.HALF_UP); + } + + @Override + public void deleteByAssessmentRecordId(Long assessmentRecordId) { + answerMapper.deleteByAssessmentRecordId(assessmentRecordId); + } + + /** + * 计算单题得分 + */ + private BigDecimal calculateQuestionScore(QuestionDO question, AssessmentAnswerSubmitReqVO.AnswerItem answerItem) { + if (question.getType() == QUESTION_TYPE_SINGLE || question.getType() == QUESTION_TYPE_MULTI) { + // 单选/多选题:根据选项计算得分 + return calculateChoiceQuestionScore(question, answerItem); + } else if (question.getType() == QUESTION_TYPE_SCORE) { + // 评分题:直接使用用户评分 + try { + if (answerItem.getAnswer() != null) { + return new BigDecimal(answerItem.getAnswer()); + } + } catch (NumberFormatException e) { + return BigDecimal.ZERO; + } + } else if (question.getType() == QUESTION_TYPE_NUMBER) { + // 数字题:根据范围计算部分分数 + return calculateNumberQuestionScore(question, answerItem); + } + // 填空题、日期题暂不计算分数 + return BigDecimal.ZERO; + } + + /** + * 计算选择题得分 + * 选项格式: [{label:"选项1",score:10,isCorrect:true},...] + */ + private BigDecimal calculateChoiceQuestionScore(QuestionDO question, AssessmentAnswerSubmitReqVO.AnswerItem answerItem) { + if (question.getOptions() == null || question.getOptions().isEmpty()) { + return BigDecimal.ZERO; + } + + try { + JSONArray options = JSON.parseArray(question.getOptions()); + if (options.isEmpty()) { + return BigDecimal.ZERO; + } + + // 获取问题总分 + BigDecimal questionScore = question.getScore() != null ? question.getScore() : BigDecimal.ZERO; + + if (question.getType() == QUESTION_TYPE_SINGLE) { + // 单选题:选对得满分,选错得0分 + 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; + } + } + } + return BigDecimal.ZERO; + } else if (question.getType() == QUESTION_TYPE_MULTI) { + // 多选题:部分得分逻辑 + if (answerItem.getOptionIds() == null || answerItem.getOptionIds().isEmpty()) { + return BigDecimal.ZERO; + } + + // 统计正确选项和用户选择 + int correctCount = 0; + int userSelectCount = answerItem.getOptionIds().size(); + + for (int i = 0; i < options.size(); i++) { + JSONObject option = options.getJSONObject(i); + if (option.getBoolean("isCorrect") != null && option.getBoolean("isCorrect")) { + 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")) { + userCorrectCount++; + break; + } + } + } + + // 计算得分:按正确比例给分 + if (correctCount == 0) { + return BigDecimal.ZERO; + } + // 选对得满分,选错不扣分但按比例得分 + double ratio = (double) userCorrectCount / correctCount; + return questionScore.multiply(BigDecimal.valueOf(ratio)).setScale(2, RoundingMode.HALF_UP); + } + } catch (Exception e) { + // 解析失败返回0分 + return BigDecimal.ZERO; + } + + return BigDecimal.ZERO; + } + + /** + * 计算数字题得分 + */ + private BigDecimal calculateNumberQuestionScore(QuestionDO question, AssessmentAnswerSubmitReqVO.AnswerItem answerItem) { + if (answerItem.getAnswer() == null || answerItem.getAnswer().isEmpty()) { + return BigDecimal.ZERO; + } + + try { + double userValue = Double.parseDouble(answerItem.getAnswer()); + Integer minValue = question.getMinValue(); + Integer maxValue = question.getMaxValue(); + BigDecimal questionScore = question.getScore() != null ? question.getScore() : BigDecimal.ZERO; + + // 如果有范围限制,按比例给分 + if (minValue != null && maxValue != null && maxValue > minValue) { + if (userValue < minValue) { + return BigDecimal.ZERO; + } else if (userValue >= maxValue) { + return questionScore; + } else { + double ratio = (userValue - minValue) / (maxValue - minValue); + return questionScore.multiply(BigDecimal.valueOf(ratio)).setScale(2, RoundingMode.HALF_UP); + } + } + // 没有范围限制给满分 + return questionScore; + } catch (NumberFormatException e) { + return BigDecimal.ZERO; + } + } + + /** + * 判断答案是否正确(单选/多选题) + */ + private Boolean isAnswerCorrect(QuestionDO question, AssessmentAnswerSubmitReqVO.AnswerItem answerItem) { + if (question.getOptions() == null || question.getOptions().isEmpty()) { + return false; + } + + try { + JSONArray options = JSON.parseArray(question.getOptions()); + + if (question.getType() == QUESTION_TYPE_SINGLE) { + // 单选题:检查是否选择了正确选项 + 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"); + } + } + return false; + } else if (question.getType() == QUESTION_TYPE_MULTI) { + // 多选题:检查是否全部选择正确选项且没有选择错误选项 + if (answerItem.getOptionIds() == null || answerItem.getOptionIds().isEmpty()) { + return false; + } + + Set correctOptionIds = new HashSet<>(); + Set userSelectedIds = 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"))) { + // 选择了错误选项 + return false; + } + } + + // 用户必须选择所有正确选项 + return correctOptionIds.equals(userSelectedIds); + } + } catch (Exception e) { + return false; + } + + return false; + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentAnswerService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentAnswerService.java deleted file mode 100644 index 4e705837a2..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentAnswerService.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment; - -import java.util.*; -import jakarta.validation.*; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentAnswerDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 答卷详情 Service 接口 - * - * @author 芋道源码 - */ -public interface AssessmentAnswerService { - - /** - * 创建答卷 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createAssessmentAnswer(@Valid AssessmentAnswerSaveReqVO createReqVO); - - /** - * 更新答卷 - * - * @param updateReqVO 更新信息 - */ - void updateAssessmentAnswer(@Valid AssessmentAnswerSaveReqVO updateReqVO); - - /** - * 删除答卷 - * - * @param id 编号 - */ - void deleteAssessmentAnswer(Long id); - - /** - * 批量删除答卷 - * - * @param ids 编号列表 - */ - void deleteAssessmentAnswerListByIds(List ids); - - /** - * 获得答卷 - * - * @param id 编号 - * @return 答卷 - */ - AssessmentAnswerDO getAssessmentAnswer(Long id); - - /** - * 获得答卷分页 - * - * @param pageReqVO 分页查询 - * @return 答卷分页 - */ - PageResult getAssessmentAnswerPage(AssessmentAnswerPageReqVO pageReqVO); - - /** - * 开始答题 - * - * @param assessmentRecordId 测评记录ID - * @param prisonerId 囚犯ID - * @return 答卷ID - */ - Long startAnswer(Long assessmentRecordId, Long prisonerId); - - /** - * 提交答卷 - * - * @param submitReqVO 提交信息 - */ - void submitAnswer(AssessmentAnswerSubmitReqVO submitReqVO); - - /** - * 根据囚犯ID和测评记录ID获取答卷 - * - * @param prisonerId 囚犯ID - * @param assessmentRecordId 测评记录ID - * @return 答卷 - */ - AssessmentAnswerDO getByPrisonerAndRecord(Long prisonerId, Long assessmentRecordId); - - /** - * 获取待评分列表 - * - * @param pageReqVO 分页查询 - * @return 待评分答卷分页 - */ - PageResult getPendingScorePage(AssessmentAnswerPageReqVO pageReqVO); - - /** - * 人工评分 - * - * @param scoreReqVO 评分信息 - */ - void manualScore(AssessmentAnswerManualScoreReqVO scoreReqVO); - - /** - * 获取已完成答卷列表 - * - * @param assessmentRecordId 测评记录ID - * @return 答卷列表 - */ - List getCompletedAnswersByRecordId(Long assessmentRecordId); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentRecordService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentRecordService.java deleted file mode 100644 index 4153d62019..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentRecordService.java +++ /dev/null @@ -1,91 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment; - -import java.util.*; -import jakarta.validation.*; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentRecordDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 测评记录 Service 接口 - * - * @author 芋道源码 - */ -public interface AssessmentRecordService { - - /** - * 创建测评记录 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createAssessmentRecord(@Valid AssessmentRecordSaveReqVO createReqVO); - - /** - * 更新测评记录 - * - * @param updateReqVO 更新信息 - */ - void updateAssessmentRecord(@Valid AssessmentRecordSaveReqVO updateReqVO); - - /** - * 删除测评记录 - * - * @param id 编号 - */ - void deleteAssessmentRecord(Long id); - - /** - * 批量删除测评记录 - * - * @param ids 编号列表 - */ - void deleteAssessmentRecordListByIds(List ids); - - /** - * 获得测评记录 - * - * @param id 编号 - * @return 测评记录 - */ - AssessmentRecordDO getAssessmentRecord(Long id); - - /** - * 获得测评记录分页 - * - * @param pageReqVO 分页查询 - * @return 测评记录分页 - */ - PageResult getAssessmentRecordPage(AssessmentRecordPageReqVO pageReqVO); - - /** - * 发起测评 - * - * @param reqVO 发起信息 - * @return 测评记录ID - */ - Long initiateAssessment(AssessmentRecordSaveReqVO reqVO); - - /** - * 取消测评 - * - * @param id 测评记录ID - */ - void cancelAssessment(Long id); - - /** - * 启动测评 - * - * @param id 测评记录ID - */ - void startAssessment(Long id); - - /** - * 结束测评 - * - * @param id 测评记录ID - */ - void finishAssessment(Long id); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentResultService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentResultService.java deleted file mode 100644 index e995b59b9e..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentResultService.java +++ /dev/null @@ -1,106 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment; - -import java.util.*; -import jakarta.validation.*; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentResultDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 测评结果 Service 接口 - * - * @author 芋道源码 - */ -public interface AssessmentResultService { - - /** - * 创建测评结果 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createAssessmentResult(@Valid AssessmentResultSaveReqVO createReqVO); - - /** - * 批量创建测评结果 - * - * @param results 结果列表 - */ - void batchCreateAssessmentResult(List results); - - /** - * 更新测评结果 - * - * @param updateReqVO 更新信息 - */ - void updateAssessmentResult(@Valid AssessmentResultSaveReqVO updateReqVO); - - /** - * 删除测评结果 - * - * @param id 编号 - */ - void deleteAssessmentResult(Long id); - - /** - * 批量删除测评结果 - * - * @param ids 编号列表 - */ - void deleteAssessmentResultListByIds(List ids); - - /** - * 获得测评结果 - * - * @param id 编号 - * @return 测评结果 - */ - AssessmentResultDO getAssessmentResult(Long id); - - /** - * 获得测评结果分页 - * - * @param pageReqVO 分页查询 - * @return 测评结果分页 - */ - PageResult getAssessmentResultPage(AssessmentResultPageReqVO pageReqVO); - - /** - * 根据答卷ID获取所有结果 - * - * @param answerId 答卷ID - * @return 结果列表 - */ - List getResultsByAnswerId(Long answerId); - - /** - * 根据测评记录ID获取所有结果 - * - * @param assessmentRecordId 测评记录ID - * @return 结果列表 - */ - List getResultsByAssessmentRecordId(Long assessmentRecordId); - - /** - * 获取需要人工评阅的结果列表 - * - * @return 需要人工评阅的结果列表 - */ - List getNeedManualReviewList(); - - /** - * 人工评阅 - * - * @param reviewReqVO 评阅信息 - */ - void manualReview(AssessmentResultManualReviewReqVO reviewReqVO); - - /** - * 自动评分 - * - * @param answerId 答卷ID - */ - void autoScore(Long answerId); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentStatisticsService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentStatisticsService.java deleted file mode 100644 index 8f446d1253..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/AssessmentStatisticsService.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment; - -import java.util.*; -import jakarta.validation.*; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentStatisticsDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 测评统计 Service 接口 - * - * @author 芋道源码 - */ -public interface AssessmentStatisticsService { - - /** - * 获得测评统计 - * - * @param id 编号 - * @return 测评统计 - */ - AssessmentStatisticsDO getAssessmentStatistics(Long id); - - /** - * 获得测评统计分页 - * - * @param pageReqVO 分页查询 - * @return 测评统计分页 - */ - PageResult getAssessmentStatisticsPage(AssessmentStatisticsPageReqVO pageReqVO); - - /** - * 生成测评统计 - * - * @param assessmentRecordId 测评记录ID - * @return 统计信息 - */ - AssessmentStatisticsDO generateStatistics(Long assessmentRecordId); - - /** - * 获取测评完成率 - * - * @param assessmentRecordId 测评记录ID - * @return 完成率 - */ - java.math.BigDecimal getCompletionRate(Long assessmentRecordId); - - /** - * 获取分数分布 - * - * @param assessmentRecordId 测评记录ID - * @return 分数分布 - */ - Map getScoreDistribution(Long assessmentRecordId); - - /** - * 获取风险分布 - * - * @param assessmentRecordId 测评记录ID - * @return 风险分布 - */ - Map getRiskDistribution(Long assessmentRecordId); - - /** - * 获取测评分析报告 - * - * @param assessmentRecordId 测评记录ID - * @return 分析报告 - */ - AssessmentStatisticsRespVO getAssessmentReport(Long assessmentRecordId); - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentAnswerServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentAnswerServiceImpl.java deleted file mode 100644 index ee1e99f5e6..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentAnswerServiceImpl.java +++ /dev/null @@ -1,163 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment.impl; - -import cn.hutool.core.collection.CollUtil; -import org.springframework.stereotype.Service; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentAnswerDO; -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; - -import cn.iocoder.yudao.module.prison.dal.mysql.assessment.AssessmentAnswerMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; - -/** - * 答卷详情 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class AssessmentAnswerServiceImpl implements AssessmentAnswerService { - - @Resource - private AssessmentAnswerMapper assessmentAnswerMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createAssessmentAnswer(AssessmentAnswerSaveReqVO createReqVO) { - AssessmentAnswerDO answer = BeanUtils.toBean(createReqVO, AssessmentAnswerDO.class); - assessmentAnswerMapper.insert(answer); - return answer.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateAssessmentAnswer(AssessmentAnswerSaveReqVO updateReqVO) { - validateAssessmentAnswerExists(updateReqVO.getId()); - AssessmentAnswerDO updateObj = BeanUtils.toBean(updateReqVO, AssessmentAnswerDO.class); - assessmentAnswerMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteAssessmentAnswer(Long id) { - validateAssessmentAnswerExists(id); - assessmentAnswerMapper.deleteById(id); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteAssessmentAnswerListByIds(List ids) { - assessmentAnswerMapper.deleteByIds(ids); - } - - @Override - public AssessmentAnswerDO getAssessmentAnswer(Long id) { - return assessmentAnswerMapper.selectById(id); - } - - @Override - public PageResult getAssessmentAnswerPage(AssessmentAnswerPageReqVO pageReqVO) { - return assessmentAnswerMapper.selectPage(pageReqVO); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public Long startAnswer(Long assessmentRecordId, Long prisonerId) { - // 检查是否已有答卷 - AssessmentAnswerDO existingAnswer = assessmentAnswerMapper.selectOne( - new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX() - .eqIfPresent(AssessmentAnswerDO::getAssessmentRecordId, assessmentRecordId) - .eqIfPresent(AssessmentAnswerDO::getPrisonerId, prisonerId) - ); - if (existingAnswer != null && !Arrays.asList(1, 2).contains(existingAnswer.getStatus())) { - throw exception(ASSESSMENT_ANSWER_ALREADY_EXISTS); - } - - if (existingAnswer != null) { - return existingAnswer.getId(); - } - - // 创建新答卷 - AssessmentAnswerDO answer = new AssessmentAnswerDO(); - answer.setAssessmentRecordId(assessmentRecordId); - answer.setPrisonerId(prisonerId); - answer.setStatus(2); // 答题中 - answer.setStartTime(LocalDateTime.now()); - assessmentAnswerMapper.insert(answer); - return answer.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void submitAnswer(AssessmentAnswerSubmitReqVO submitReqVO) { - AssessmentAnswerDO answer = validateAssessmentAnswerExists(submitReqVO.getId()); - if (!Objects.equals(answer.getStatus(), 2)) { - throw exception(ASSESSMENT_ANSWER_STATUS_ERROR); - } - - answer.setStatus(3); // 已提交 - answer.setSubmitTime(LocalDateTime.now()); - if (answer.getStartTime() != null) { - answer.setDuration((int) java.time.Duration.between(answer.getStartTime(), LocalDateTime.now()).getSeconds()); - } - assessmentAnswerMapper.updateById(answer); - } - - @Override - public AssessmentAnswerDO getByPrisonerAndRecord(Long prisonerId, Long assessmentRecordId) { - return assessmentAnswerMapper.selectOne( - new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX() - .eqIfPresent(AssessmentAnswerDO::getPrisonerId, prisonerId) - .eqIfPresent(AssessmentAnswerDO::getAssessmentRecordId, assessmentRecordId) - ); - } - - @Override - public PageResult getPendingScorePage(AssessmentAnswerPageReqVO pageReqVO) { - // 查询已提交但未评分的答卷 - pageReqVO.setStatus(3); // 已提交 - return assessmentAnswerMapper.selectPage(pageReqVO); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void manualScore(AssessmentAnswerManualScoreReqVO scoreReqVO) { - AssessmentAnswerDO answer = validateAssessmentAnswerExists(scoreReqVO.getId()); - answer.setSubjectiveScore(scoreReqVO.getSubjectiveScore()); - answer.setTotalScore(answer.getObjectiveScore() != null ? answer.getObjectiveScore() : BigDecimal.ZERO - .add(scoreReqVO.getSubjectiveScore() != null ? scoreReqVO.getSubjectiveScore() : BigDecimal.ZERO)); - answer.setPassed(answer.getTotalScore().compareTo(answer.getPassScore()) >= 0); - answer.setComment(scoreReqVO.getComment()); - answer.setStatus(5); // 已完成 - assessmentAnswerMapper.updateById(answer); - } - - @Override - public List getCompletedAnswersByRecordId(Long assessmentRecordId) { - return assessmentAnswerMapper.selectList( - new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX() - .eqIfPresent(AssessmentAnswerDO::getAssessmentRecordId, assessmentRecordId) - .eqIfPresent(AssessmentAnswerDO::getStatus, 5) - ); - } - - private AssessmentAnswerDO validateAssessmentAnswerExists(Long id) { - AssessmentAnswerDO answer = assessmentAnswerMapper.selectById(id); - if (answer == null) { - throw exception(ASSESSMENT_ANSWER_NOT_EXISTS); - } - return answer; - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentRecordServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentRecordServiceImpl.java deleted file mode 100644 index 5830db3cfe..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentRecordServiceImpl.java +++ /dev/null @@ -1,133 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment.impl; - -import cn.hutool.core.collection.CollUtil; -import org.springframework.stereotype.Service; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentRecordDO; -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; - -import cn.iocoder.yudao.module.prison.dal.mysql.assessment.AssessmentRecordMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; - -/** - * 测评记录 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class AssessmentRecordServiceImpl implements AssessmentRecordService { - - @Resource - private AssessmentRecordMapper assessmentRecordMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createAssessmentRecord(AssessmentRecordSaveReqVO createReqVO) { - // 插入 - AssessmentRecordDO assessmentRecord = BeanUtils.toBean(createReqVO, AssessmentRecordDO.class); - assessmentRecordMapper.insert(assessmentRecord); - - // 返回 - return assessmentRecord.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateAssessmentRecord(AssessmentRecordSaveReqVO updateReqVO) { - // 校验存在 - validateAssessmentRecordExists(updateReqVO.getId()); - // 更新 - AssessmentRecordDO updateObj = BeanUtils.toBean(updateReqVO, AssessmentRecordDO.class); - assessmentRecordMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteAssessmentRecord(Long id) { - // 校验存在 - validateAssessmentRecordExists(id); - // 删除 - assessmentRecordMapper.deleteById(id); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteAssessmentRecordListByIds(List ids) { - assessmentRecordMapper.deleteByIds(ids); - } - - @Override - public AssessmentRecordDO getAssessmentRecord(Long id) { - return assessmentRecordMapper.selectById(id); - } - - @Override - public PageResult getAssessmentRecordPage(AssessmentRecordPageReqVO pageReqVO) { - return assessmentRecordMapper.selectPage(pageReqVO); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public Long initiateAssessment(AssessmentRecordSaveReqVO reqVO) { - AssessmentRecordDO assessmentRecord = BeanUtils.toBean(reqVO, AssessmentRecordDO.class); - assessmentRecord.setStatus(1); // 未开始 - assessmentRecord.setParticipantCount(0); - assessmentRecord.setCompletedCount(0); - assessmentRecordMapper.insert(assessmentRecord); - return assessmentRecord.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void cancelAssessment(Long id) { - AssessmentRecordDO assessmentRecord = validateAssessmentRecordExists(id); - if (!Arrays.asList(1, 2).contains(assessmentRecord.getStatus())) { - throw exception(ASSESSMENT_RECORD_STATUS_ERROR); - } - assessmentRecord.setStatus(4); // 已取消 - assessmentRecordMapper.updateById(assessmentRecord); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void startAssessment(Long id) { - AssessmentRecordDO assessmentRecord = validateAssessmentRecordExists(id); - if (!Objects.equals(assessmentRecord.getStatus(), 1)) { - throw exception(ASSESSMENT_RECORD_STATUS_ERROR); - } - assessmentRecord.setStatus(2); // 进行中 - assessmentRecord.setActualStartTime(java.time.LocalDateTime.now()); - assessmentRecordMapper.updateById(assessmentRecord); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void finishAssessment(Long id) { - AssessmentRecordDO assessmentRecord = validateAssessmentRecordExists(id); - if (!Objects.equals(assessmentRecord.getStatus(), 2)) { - throw exception(ASSESSMENT_RECORD_STATUS_ERROR); - } - assessmentRecord.setStatus(3); // 已完成 - assessmentRecord.setActualEndTime(java.time.LocalDateTime.now()); - assessmentRecordMapper.updateById(assessmentRecord); - } - - private AssessmentRecordDO validateAssessmentRecordExists(Long id) { - AssessmentRecordDO assessmentRecord = assessmentRecordMapper.selectById(id); - if (assessmentRecord == null) { - throw exception(ASSESSMENT_RECORD_NOT_EXISTS); - } - return assessmentRecord; - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentResultServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentResultServiceImpl.java deleted file mode 100644 index e39d7257b0..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentResultServiceImpl.java +++ /dev/null @@ -1,141 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment.impl; - -import cn.hutool.core.collection.CollUtil; -import org.springframework.stereotype.Service; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentResultDO; -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; - -import cn.iocoder.yudao.module.prison.dal.mysql.assessment.AssessmentResultMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; - -/** - * 测评结果 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class AssessmentResultServiceImpl implements AssessmentResultService { - - @Resource - private AssessmentResultMapper assessmentResultMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createAssessmentResult(AssessmentResultSaveReqVO createReqVO) { - AssessmentResultDO result = BeanUtils.toBean(createReqVO, AssessmentResultDO.class); - assessmentResultMapper.insert(result); - return result.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void batchCreateAssessmentResult(List results) { - if (CollUtil.isEmpty(results)) { - return; - } - List resultList = BeanUtils.toBean(results, AssessmentResultDO.class); - assessmentResultMapper.insertBatch(resultList); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateAssessmentResult(AssessmentResultSaveReqVO updateReqVO) { - validateAssessmentResultExists(updateReqVO.getId()); - AssessmentResultDO updateObj = BeanUtils.toBean(updateReqVO, AssessmentResultDO.class); - assessmentResultMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteAssessmentResult(Long id) { - validateAssessmentResultExists(id); - assessmentResultMapper.deleteById(id); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteAssessmentResultListByIds(List ids) { - assessmentResultMapper.deleteByIds(ids); - } - - @Override - public AssessmentResultDO getAssessmentResult(Long id) { - return assessmentResultMapper.selectById(id); - } - - @Override - public PageResult getAssessmentResultPage(AssessmentResultPageReqVO pageReqVO) { - return assessmentResultMapper.selectPage(pageReqVO); - } - - @Override - public List getResultsByAnswerId(Long answerId) { - return assessmentResultMapper.selectListByAnswerId(answerId); - } - - @Override - public List getResultsByAssessmentRecordId(Long assessmentRecordId) { - return assessmentResultMapper.selectListByAssessmentRecordId(assessmentRecordId); - } - - @Override - public List getNeedManualReviewList() { - return assessmentResultMapper.selectListNeedManualReview(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void manualReview(AssessmentResultManualReviewReqVO reviewReqVO) { - AssessmentResultDO result = validateAssessmentResultExists(reviewReqVO.getId()); - result.setManualScore(reviewReqVO.getManualScore()); - result.setManualComment(reviewReqVO.getManualComment()); - result.setManualReviewStatus(2); // 已评阅 - result.setReviewerId(reviewReqVO.getReviewerId()); - result.setReviewerName(reviewReqVO.getReviewerName()); - result.setReviewTime(LocalDateTime.now()); - result.setScore(reviewReqVO.getManualScore()); - assessmentResultMapper.updateById(result); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void autoScore(Long answerId) { - List results = assessmentResultMapper.selectListByAnswerId(answerId); - BigDecimal totalScore = BigDecimal.ZERO; - - for (AssessmentResultDO result : results) { - if (!result.getNeedManualReview()) { - // 客观题自动评分 - if (result.getCorrect() != null && result.getCorrect()) { - result.setScore(result.getQuestionScore()); - } else { - result.setScore(BigDecimal.ZERO); - } - totalScore = totalScore.add(result.getScore()); - assessmentResultMapper.updateById(result); - } - } - } - - private AssessmentResultDO validateAssessmentResultExists(Long id) { - AssessmentResultDO result = assessmentResultMapper.selectById(id); - if (result == null) { - throw exception(ASSESSMENT_RESULT_NOT_EXISTS); - } - return result; - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentStatisticsServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentStatisticsServiceImpl.java deleted file mode 100644 index 5ce0e988ba..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/assessment/impl/AssessmentStatisticsServiceImpl.java +++ /dev/null @@ -1,198 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.assessment.impl; - -import cn.hutool.core.collection.CollUtil; -import org.springframework.stereotype.Service; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.prison.controller.admin.assessment.vo.*; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentStatisticsDO; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentAnswerDO; -import cn.iocoder.yudao.module.prison.dal.dataobject.assessment.AssessmentResultDO; -import cn.iocoder.yudao.module.prison.dal.mysql.assessment.AssessmentAnswerMapper; -import cn.iocoder.yudao.module.prison.dal.mysql.assessment.AssessmentResultMapper; -import cn.iocoder.yudao.module.prison.dal.mysql.assessment.AssessmentStatisticsMapper; -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; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; - -/** - * 测评统计 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class AssessmentStatisticsServiceImpl implements AssessmentStatisticsService { - - @Resource - private AssessmentStatisticsMapper assessmentStatisticsMapper; - - @Resource - private AssessmentAnswerMapper assessmentAnswerMapper; - - @Resource - private AssessmentResultMapper assessmentResultMapper; - - @Override - public AssessmentStatisticsDO getAssessmentStatistics(Long id) { - return assessmentStatisticsMapper.selectById(id); - } - - @Override - public PageResult getAssessmentStatisticsPage(AssessmentStatisticsPageReqVO pageReqVO) { - return assessmentStatisticsMapper.selectPage(pageReqVO); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public AssessmentStatisticsDO generateStatistics(Long assessmentRecordId) { - // 查询所有答卷 - List answers = assessmentAnswerMapper.selectListByAssessmentRecordId(assessmentRecordId); - - int totalCount = answers.size(); - int completedCount = (int) answers.stream().filter(a -> a.getStatus() == 5).count(); - int passedCount = (int) answers.stream().filter(a -> a.getPassed() != null && a.getPassed()).count(); - - // 计算统计数据 - BigDecimal completionRate = totalCount > 0 ? - new BigDecimal(completedCount).multiply(new BigDecimal(100)).divide(new BigDecimal(totalCount), 2, java.math.RoundingMode.HALF_UP) : - BigDecimal.ZERO; - - List scores = answers.stream() - .map(AssessmentAnswerDO::getTotalScore) - .filter(Objects::nonNull) - .sorted() - .toList(); - - BigDecimal averageScore = scores.isEmpty() ? BigDecimal.ZERO : - scores.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(new BigDecimal(scores.size()), 2, java.math.RoundingMode.HALF_UP); - BigDecimal highestScore = scores.isEmpty() ? BigDecimal.ZERO : scores.get(scores.size() - 1); - BigDecimal lowestScore = scores.isEmpty() ? BigDecimal.ZERO : scores.get(0); - - BigDecimal passRate = completedCount > 0 ? - new BigDecimal(passedCount).multiply(new BigDecimal(100)).divide(new BigDecimal(completedCount), 2, java.math.RoundingMode.HALF_UP) : - BigDecimal.ZERO; - - int excellentCount = (int) answers.stream().filter(a -> - a.getTotalScore() != null && a.getTotalScore().compareTo(new BigDecimal("90")) >= 0 - ).count(); - - int riskCount = (int) answers.stream().filter(a -> - a.getTotalScore() != null && a.getTotalScore().compareTo(new BigDecimal("60")) < 0 - ).count(); - - BigDecimal excellentRate = completedCount > 0 ? - new BigDecimal(excellentCount).multiply(new BigDecimal(100)).divide(new BigDecimal(completedCount), 2, java.math.RoundingMode.HALF_UP) : - BigDecimal.ZERO; - - BigDecimal riskRate = completedCount > 0 ? - new BigDecimal(riskCount).multiply(new BigDecimal(100)).divide(new BigDecimal(completedCount), 2, java.math.RoundingMode.HALF_UP) : - BigDecimal.ZERO; - - // 生成统计数据 - AssessmentStatisticsDO statistics = new AssessmentStatisticsDO(); - statistics.setAssessmentRecordId(assessmentRecordId); - statistics.setTotalCount(totalCount); - statistics.setCompletedCount(completedCount); - statistics.setCompletionRate(completionRate); - statistics.setAverageScore(averageScore); - statistics.setHighestScore(highestScore); - statistics.setLowestScore(lowestScore); - statistics.setPassedCount(passedCount); - statistics.setPassRate(passRate); - statistics.setExcellentCount(excellentCount); - statistics.setExcellentRate(excellentRate); - statistics.setRiskCount(riskCount); - statistics.setRiskRate(riskRate); - statistics.setStatisticsTime(LocalDateTime.now()); - - // 生成分数分布 - Map scoreDist = getScoreDistribution(assessmentRecordId); - statistics.setScoreDistribution(new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(scoreDist)); - - // 生成风险分布 - Map riskDist = getRiskDistribution(assessmentRecordId); - statistics.setRiskDistribution(new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(riskDist)); - - assessmentStatisticsMapper.insert(statistics); - return statistics; - } - - @Override - public BigDecimal getCompletionRate(Long assessmentRecordId) { - List answers = assessmentAnswerMapper.selectListByAssessmentRecordId(assessmentRecordId); - int totalCount = answers.size(); - int completedCount = (int) answers.stream().filter(a -> a.getStatus() == 5).count(); - if (totalCount == 0) return BigDecimal.ZERO; - return new BigDecimal(completedCount).multiply(new BigDecimal(100)).divide(new BigDecimal(totalCount), 2, java.math.RoundingMode.HALF_UP); - } - - @Override - public Map getScoreDistribution(Long assessmentRecordId) { - Map distribution = new LinkedHashMap<>(); - distribution.put("0-20", 0); - distribution.put("20-40", 0); - distribution.put("40-60", 0); - distribution.put("60-80", 0); - distribution.put("80-100", 0); - - List answers = assessmentAnswerMapper.selectListByAssessmentRecordId(assessmentRecordId); - for (AssessmentAnswerDO answer : answers) { - if (answer.getTotalScore() != null) { - BigDecimal score = answer.getTotalScore(); - if (score.compareTo(new BigDecimal("20")) <= 0) { - distribution.merge("0-20", 1, Integer::sum); - } else if (score.compareTo(new BigDecimal("40")) <= 0) { - distribution.merge("20-40", 1, Integer::sum); - } else if (score.compareTo(new BigDecimal("60")) <= 0) { - distribution.merge("40-60", 1, Integer::sum); - } else if (score.compareTo(new BigDecimal("80")) <= 0) { - distribution.merge("60-80", 1, Integer::sum); - } else { - distribution.merge("80-100", 1, Integer::sum); - } - } - } - return distribution; - } - - @Override - public Map getRiskDistribution(Long assessmentRecordId) { - Map distribution = new LinkedHashMap<>(); - distribution.put("low", 0); // 低风险 - distribution.put("medium", 0); // 中风险 - distribution.put("high", 0); // 高风险 - - List answers = assessmentAnswerMapper.selectListByAssessmentRecordId(assessmentRecordId); - for (AssessmentAnswerDO answer : answers) { - if (answer.getTotalScore() != null && answer.getPassed() != null) { - if (answer.getPassed()) { - distribution.merge("low", 1, Integer::sum); - } else { - BigDecimal score = answer.getTotalScore(); - if (score.compareTo(new BigDecimal("40")) < 0) { - distribution.merge("high", 1, Integer::sum); - } else { - distribution.merge("medium", 1, Integer::sum); - } - } - } - } - return distribution; - } - - @Override - public AssessmentStatisticsRespVO getAssessmentReport(Long assessmentRecordId) { - AssessmentStatisticsDO statistics = generateStatistics(assessmentRecordId); - return BeanUtils.toBean(statistics, AssessmentStatisticsRespVO.class); - } - -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionService.java index cc6e4bb7e6..0c19c2d2d7 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionService.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionService.java @@ -4,59 +4,68 @@ import java.util.*; import jakarta.validation.*; import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; /** - * 消费记录 Service 接口 + * 消费订单 Service 接口 * - * @author 芋道源码 + * @author xl */ public interface ConsumptionService { /** - * 创建消费记录 + * 创建消费订单 * * @param createReqVO 创建信息 - * @return 编号 + * @return 订单编号 */ Long createConsumption(@Valid ConsumptionSaveReqVO createReqVO); /** - * 更新消费记录 + * 更新消费订单 * * @param updateReqVO 更新信息 */ void updateConsumption(@Valid ConsumptionSaveReqVO updateReqVO); /** - * 删除消费记录 + * 删除消费订单 * - * @param id 编号 + * @param id 订单编号 */ void deleteConsumption(Long id); /** - * 批量删除消费记录 + * 批量删除消费订单 * - * @param ids 编号 + * @param ids 订单编号列表 */ void deleteConsumptionListByIds(List ids); /** - * 获得消费记录 + * 获得消费订单 * - * @param id 编号 - * @return 消费记录 + * @param id 订单编号 + * @return 消费订单 */ ConsumptionDO getConsumption(Long id); /** - * 获得消费记录分页 + * 获得消费订单分页 * * @param pageReqVO 分页查询 - * @return 消费记录分页 + * @return 消费订单分页 */ PageResult getConsumptionPage(ConsumptionPageReqVO pageReqVO); -} \ No newline at end of file + /** + * 获得消费订单明细列表 + * + * @param consumptionId 消费订单ID + * @return 消费明细列表 + */ + List getConsumptionDetailList(Long consumptionId); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java index 471a3b0ac4..14e730d2d9 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceImpl.java @@ -1,29 +1,34 @@ package cn.iocoder.yudao.module.prison.service.consumption; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import org.springframework.stereotype.Service; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.math.BigDecimal; + import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; +import cn.iocoder.yudao.module.prison.dal.mysql.consumption.ConsumptionMapper; +import cn.iocoder.yudao.module.prison.dal.mysql.consumption.ConsumptionDetailMapper; +import cn.iocoder.yudao.module.prison.convert.consumption.ConsumptionConvert; +import cn.iocoder.yudao.module.prison.convert.consumption.ConsumptionDetailConvert; 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; - -import cn.iocoder.yudao.module.prison.dal.mysql.consumption.ConsumptionMapper; +import cn.hutool.core.util.NumberUtil; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; /** - * 消费记录 Service 实现类 + * 消费订单 Service 实现类 * - * @author 芋道源码 + * @author xl */ @Service @Validated @@ -32,39 +37,84 @@ public class ConsumptionServiceImpl implements ConsumptionService { @Resource private ConsumptionMapper consumptionMapper; + @Resource + private ConsumptionDetailMapper consumptionDetailMapper; + @Override + @Transactional(rollbackFor = Exception.class) public Long createConsumption(ConsumptionSaveReqVO createReqVO) { - // 插入 - ConsumptionDO consumption = BeanUtils.toBean(createReqVO, ConsumptionDO.class); + // 1. 生成订单号 + String orderNo = generateOrderNo(); + + // 2. 插入消费订单 + ConsumptionDO consumption = ConsumptionConvert.INSTANCE.convert(createReqVO); + consumption.setOrderNo(orderNo); consumptionMapper.insert(consumption); + // 3. 插入消费明细 + if (CollUtil.isNotEmpty(createReqVO.getDetails())) { + for (ConsumptionDetailSaveReqVO detailVO : createReqVO.getDetails()) { + ConsumptionDetailDO detail = ConsumptionDetailConvert.INSTANCE.convert(detailVO); + detail.setConsumptionId(consumption.getId()); + detail.setPrisonerId(createReqVO.getPrisonerId()); + // 计算小计金额 + detail.setSubtotal(NumberUtil.mul(detail.getGoodsPrice(), detail.getGoodsCount())); + consumptionDetailMapper.insert(detail); + } + } + // 返回 return consumption.getId(); } @Override + @Transactional(rollbackFor = Exception.class) public void updateConsumption(ConsumptionSaveReqVO updateReqVO) { - // 校验存在 + // 校验订单存在 validateConsumptionExists(updateReqVO.getId()); - // 更新 - ConsumptionDO updateObj = BeanUtils.toBean(updateReqVO, ConsumptionDO.class); + + // 1. 更新消费订单 + ConsumptionDO updateObj = ConsumptionConvert.INSTANCE.convert(updateReqVO); consumptionMapper.updateById(updateObj); - } - @Override - public void deleteConsumption(Long id) { - // 校验存在 - validateConsumptionExists(id); - // 删除 - consumptionMapper.deleteById(id); - } + // 2. 删除原有明细 + consumptionDetailMapper.deleteListByConsumptionId(updateReqVO.getId()); - @Override - public void deleteConsumptionListByIds(List ids) { - // 删除 - consumptionMapper.deleteByIds(ids); + // 3. 插入新的消费明细 + if (CollUtil.isNotEmpty(updateReqVO.getDetails())) { + for (ConsumptionDetailSaveReqVO detailVO : updateReqVO.getDetails()) { + ConsumptionDetailDO detail = ConsumptionDetailConvert.INSTANCE.convert(detailVO); + detail.setConsumptionId(updateReqVO.getId()); + detail.setPrisonerId(updateReqVO.getPrisonerId()); + // 计算小计金额 + detail.setSubtotal(NumberUtil.mul(detail.getGoodsPrice(), detail.getGoodsCount())); + consumptionDetailMapper.insert(detail); + } } + } + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteConsumption(Long id) { + // 校验订单存在 + validateConsumptionExists(id); + + // 1. 删除消费订单 + consumptionMapper.deleteById(id); + + // 2. 删除消费明细 + consumptionDetailMapper.deleteListByConsumptionId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteConsumptionListByIds(List ids) { + // 批量删除订单 + consumptionMapper.deleteByIds(ids); + + // 批量删除明细 + consumptionDetailMapper.deleteListByConsumptionIds(ids); + } private void validateConsumptionExists(Long id) { if (consumptionMapper.selectById(id) == null) { @@ -82,4 +132,16 @@ public class ConsumptionServiceImpl implements ConsumptionService { return consumptionMapper.selectPage(pageReqVO); } -} \ No newline at end of file + @Override + public List getConsumptionDetailList(Long consumptionId) { + return consumptionDetailMapper.selectList(ConsumptionDetailDO::getConsumptionId, consumptionId); + } + + /** + * 生成订单号 + */ + private String generateOrderNo() { + return "CS" + System.currentTimeMillis(); + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire/QuestionnaireServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire/QuestionnaireServiceImpl.java index 62a14de749..8d5853f345 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire/QuestionnaireServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnaire/QuestionnaireServiceImpl.java @@ -60,10 +60,10 @@ public class QuestionnaireServiceImpl implements QuestionnaireService { } @Override - public void deleteQuestionnaireListByIds(List ids) { + public void deleteQuestionnaireListByIds(List ids) { // 删除 questionnaireMapper.deleteByIds(ids); - } + } private void validateQuestionnaireExists(Long id) { diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java index 53b2a43f82..9ce0b23f5e 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordService.java @@ -6,9 +6,11 @@ import cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo.*; import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import java.math.BigDecimal; +import java.util.Map; /** - * 问卷答题记录 Service 接口 + * 问卷答题记录 / 测评记录 Service 接口 * * @author 芋道源码 */ @@ -37,10 +39,10 @@ public interface QuestionnaireRecordService { void deleteQuestionnaireRecord(Long id); /** - * 批量删除问卷答题记录 - * - * @param ids 编号 - */ + * 批量删除问卷答题记录 + * + * @param ids 编号列表 + */ void deleteQuestionnaireRecordListByIds(List ids); /** @@ -59,4 +61,85 @@ public interface QuestionnaireRecordService { */ PageResult getQuestionnaireRecordPage(QuestionnaireRecordPageReqVO pageReqVO); -} \ No newline at end of file + // ==================== 测评执行相关 ==================== + + /** + * 发起测评 + * + * @param reqVO 发起信息 + * @return 测评记录ID + */ + Long initiateAssessment(@Valid AssessmentInitiateReqVO reqVO); + + /** + * 开始测评 + * + * @param id 测评记录ID + * @param prisonerId 罪犯ID + */ + void startAssessment(Long id, Long prisonerId); + + /** + * 提交答卷 + * + * @param reqVO 提交信息 + */ + void submitAnswer(@Valid AssessmentAnswerSubmitReqVO reqVO); + + /** + * 结束测评 + * + * @param id 测评记录ID + */ + void finishAssessment(Long id); + + /** + * 取消测评 + * + * @param id 测评记录ID + */ + void cancelAssessment(Long id); + + // ==================== 评分相关 ==================== + + /** + * 自动评分 + * + * @param id 测评记录ID + */ + void autoScore(Long id); + + /** + * 人工评分 + * + * @param reqVO 评分信息 + */ + void manualScore(@Valid AssessmentManualScoreReqVO reqVO); + + // ==================== 统计相关 ==================== + + /** + * 获取完成率 + * + * @param assessmentRecordId 测评记录ID + * @return 完成率 + */ + BigDecimal getCompletionRate(Long assessmentRecordId); + + /** + * 获取分数分布 + * + * @param assessmentRecordId 测评记录ID + * @return 分数分布 + */ + Map getScoreDistribution(Long assessmentRecordId); + + /** + * 获取风险分布 + * + * @param assessmentRecordId 测评记录ID + * @return 风险分布 + */ + Map getRiskDistribution(Long assessmentRecordId); + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java index 5e963d4208..b7b6f90afe 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/questionnairerecord/QuestionnaireRecordServiceImpl.java @@ -7,21 +7,26 @@ import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; import cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo.*; import cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO; 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; - import cn.iocoder.yudao.module.prison.dal.mysql.questionnairerecord.QuestionnaireRecordMapper; +import cn.iocoder.yudao.module.prison.dal.mysql.questionnaire.QuestionnaireMapper; +import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire.QuestionnaireDO; +import cn.iocoder.yudao.module.prison.service.answer.AnswerService; +import cn.iocoder.yudao.module.prison.enums.QuestionnaireRecordPassStatusEnum; +import cn.iocoder.yudao.module.prison.enums.QuestionnaireRecordStatusEnum; +import cn.iocoder.yudao.module.prison.enums.RiskLevelEnum; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; import static cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants.*; /** - * 问卷答题记录 Service 实现类 + * 问卷答题记录 / 测评记录 Service 实现类 * * @author 芋道源码 */ @@ -32,13 +37,17 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic @Resource private QuestionnaireRecordMapper questionnaireRecordMapper; + @Resource + private QuestionnaireMapper questionnaireMapper; + + @Resource + private AnswerService answerService; + @Override public Long createQuestionnaireRecord(QuestionnaireRecordSaveReqVO createReqVO) { // 插入 QuestionnaireRecordDO questionnaireRecord = BeanUtils.toBean(createReqVO, QuestionnaireRecordDO.class); questionnaireRecordMapper.insert(questionnaireRecord); - - // 返回 return questionnaireRecord.getId(); } @@ -74,11 +83,12 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic questionnaireRecordMapper.deleteByIds(ids); } - - private void validateQuestionnaireRecordExists(Long id) { - if (questionnaireRecordMapper.selectById(id) == null) { + private QuestionnaireRecordDO validateQuestionnaireRecordExists(Long id) { + QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(id); + if (record == null) { throw exception(QUESTIONNAIRE_RECORD_NOT_EXISTS); } + return record; } @Override @@ -91,4 +101,207 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic return questionnaireRecordMapper.selectPage(pageReqVO); } -} \ No newline at end of file + // ==================== 测评执行相关 ==================== + + @Override + @Transactional(rollbackFor = Exception.class) + public Long initiateAssessment(AssessmentInitiateReqVO reqVO) { + // 获取问卷信息 + QuestionnaireDO questionnaire = questionnaireMapper.selectById(reqVO.getQuestionnaireId()); + if (questionnaire == null) { + throw exception(QUESTIONNAIRE_NOT_EXISTS); + } + String questionnaireName = questionnaire.getTitle(); + + QuestionnaireRecordDO lastRecord = null; + for (Long prisonerId : reqVO.getPrisonerIds()) { + QuestionnaireRecordDO record = new QuestionnaireRecordDO(); + record.setQuestionnaireId(reqVO.getQuestionnaireId()); + record.setQuestionnaireName(questionnaireName); + record.setPrisonerId(prisonerId); + record.setStatus(QuestionnaireRecordStatusEnum.PENDING.getStatus()); // 待测评 + record.setDeadline(reqVO.getDeadline()); + record.setRemark(reqVO.getRemark()); + record.setParticipantCount(0); + record.setCompletedCount(0); + questionnaireRecordMapper.insert(record); + lastRecord = record; + } + // 返回最后创建的记录ID + return lastRecord != null ? lastRecord.getId() : null; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void startAssessment(Long id, Long prisonerId) { + QuestionnaireRecordDO record = validateQuestionnaireRecordExists(id); + if (!QuestionnaireRecordStatusEnum.PENDING.getStatus().equals(record.getStatus())) { + throw exception(QUESTIONNAIRE_RECORD_STATUS_ERROR); + } + record.setStatus(QuestionnaireRecordStatusEnum.IN_PROGRESS.getStatus()); // 测评中 + record.setStartTime(LocalDateTime.now()); + questionnaireRecordMapper.updateById(record); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void submitAnswer(AssessmentAnswerSubmitReqVO reqVO) { + QuestionnaireRecordDO record = validateQuestionnaireRecordExists(reqVO.getRecordId()); + if (!QuestionnaireRecordStatusEnum.IN_PROGRESS.getStatus().equals(record.getStatus())) { + throw exception(QUESTIONNAIRE_RECORD_STATUS_ERROR); + } + record.setStatus(QuestionnaireRecordStatusEnum.COMPLETED.getStatus()); // 已完成 + record.setEndTime(LocalDateTime.now()); + if (record.getStartTime() != null) { + record.setDuration((int) java.time.Duration.between(record.getStartTime(), LocalDateTime.now()).getSeconds()); + } + // 保存答题记录并计算客观题得分 + answerService.saveAnswers(reqVO.getRecordId(), record.getQuestionnaireId(), reqVO.getPrisonerId(), reqVO.getAnswers()); + // 计算客观题得分 + BigDecimal objectiveScore = answerService.calculateObjectiveScore(reqVO.getRecordId()); + record.setObjectiveScore(objectiveScore); + record.setPassStatus(QuestionnaireRecordPassStatusEnum.PENDING.getStatus()); // 待评阅 + questionnaireRecordMapper.updateById(record); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void finishAssessment(Long id) { + QuestionnaireRecordDO record = validateQuestionnaireRecordExists(id); + if (!QuestionnaireRecordStatusEnum.canTransition(record.getStatus())) { + throw exception(QUESTIONNAIRE_RECORD_STATUS_ERROR); + } + record.setStatus(QuestionnaireRecordStatusEnum.EXPIRED.getStatus()); // 已过期 + questionnaireRecordMapper.updateById(record); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelAssessment(Long id) { + QuestionnaireRecordDO record = validateQuestionnaireRecordExists(id); + if (!QuestionnaireRecordStatusEnum.PENDING.getStatus().equals(record.getStatus())) { + throw exception(QUESTIONNAIRE_RECORD_STATUS_ERROR); + } + record.setStatus(QuestionnaireRecordStatusEnum.CANCELLED.getStatus()); // 已取消 + questionnaireRecordMapper.updateById(record); + } + + // ==================== 评分相关 ==================== + + @Override + @Transactional(rollbackFor = Exception.class) + public void autoScore(Long id) { + QuestionnaireRecordDO record = validateQuestionnaireRecordExists(id); + // 重新计算客观题得分(覆盖之前的分数) + BigDecimal objectiveScore = answerService.calculateObjectiveScore(id); + record.setObjectiveScore(objectiveScore); + + // 如果有待主观题(需要人工评阅),保持待评阅状态 + // 如果全部是客观题,直接设置及格/不及格状态 + record.setPassStatus(objectiveScore.compareTo(record.getPassScore()) >= 0 + ? QuestionnaireRecordPassStatusEnum.PASSED.getStatus() + : QuestionnaireRecordPassStatusEnum.FAILED.getStatus()); + + questionnaireRecordMapper.updateById(record); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void manualScore(AssessmentManualScoreReqVO reqVO) { + QuestionnaireRecordDO record = validateQuestionnaireRecordExists(reqVO.getRecordId()); + + // 计算总分 + BigDecimal objectiveScore = record.getObjectiveScore() != null ? record.getObjectiveScore() : BigDecimal.ZERO; + BigDecimal subjectiveScore = reqVO.getSubjectiveScore() != null ? reqVO.getSubjectiveScore() : BigDecimal.ZERO; + BigDecimal totalScore = objectiveScore.add(subjectiveScore); + + record.setSubjectiveScore(reqVO.getSubjectiveScore()); + record.setTotalScore(totalScore); + // 及格/不及格判断 + BigDecimal passScore = record.getPassScore() != null ? record.getPassScore() : BigDecimal.ZERO; + record.setPassStatus(totalScore.compareTo(passScore) >= 0 + ? QuestionnaireRecordPassStatusEnum.PASSED.getStatus() + : QuestionnaireRecordPassStatusEnum.FAILED.getStatus()); + record.setRiskLevel(reqVO.getRiskLevel()); + + questionnaireRecordMapper.updateById(record); + } + + // ==================== 统计相关 ==================== + + @Override + public BigDecimal getCompletionRate(Long assessmentRecordId) { + QuestionnaireRecordDO record = validateQuestionnaireRecordExists(assessmentRecordId); + if (record.getParticipantCount() == null || record.getParticipantCount() == 0) { + return BigDecimal.ZERO; + } + return BigDecimal.valueOf(record.getCompletedCount()) + .divide(BigDecimal.valueOf(record.getParticipantCount()), 4, java.math.RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(100)); + } + + @Override + public Map getScoreDistribution(Long assessmentRecordId) { + Map distribution = new HashMap<>(); + distribution.put("0-20", 0); + distribution.put("21-40", 0); + distribution.put("41-60", 0); + distribution.put("61-80", 0); + distribution.put("81-100", 0); + + // 先获取记录,获取 questionnaireId + QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(assessmentRecordId); + if (record == null) { + return distribution; + } + + // 查询该测评下所有已完成记录,统计分数分布 + List records = questionnaireRecordMapper.selectList( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(QuestionnaireRecordDO::getQuestionnaireId, record.getQuestionnaireId()) + .eq(QuestionnaireRecordDO::getStatus, QuestionnaireRecordStatusEnum.COMPLETED.getStatus())); + + for (QuestionnaireRecordDO r : records) { + if (r.getTotalScore() == null) continue; + int score = r.getTotalScore().intValue(); + if (score <= 20) distribution.put("0-20", distribution.get("0-20") + 1); + else if (score <= 40) distribution.put("21-40", distribution.get("21-40") + 1); + else if (score <= 60) distribution.put("41-60", distribution.get("41-60") + 1); + else if (score <= 80) distribution.put("61-80", distribution.get("61-80") + 1); + else distribution.put("81-100", distribution.get("81-100") + 1); + } + return distribution; + } + + @Override + public Map getRiskDistribution(Long assessmentRecordId) { + Map distribution = new HashMap<>(); + distribution.put("high", 0); + distribution.put("medium", 0); + distribution.put("low", 0); + + // 先获取记录,获取 questionnaireId + QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(assessmentRecordId); + if (record == null) { + return distribution; + } + + // 查询该测评下所有记录,统计风险分布 + List records = questionnaireRecordMapper.selectList( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(QuestionnaireRecordDO::getQuestionnaireId, record.getQuestionnaireId())); + + for (QuestionnaireRecordDO r : records) { + if (r.getRiskLevel() == null) continue; + if (RiskLevelEnum.HIGH.getValue().equals(r.getRiskLevel())) { + distribution.put("high", distribution.get("high") + 1); + } else if (RiskLevelEnum.MEDIUM.getValue().equals(r.getRiskLevel())) { + distribution.put("medium", distribution.get("medium") + 1); + } else if (RiskLevelEnum.LOW.getValue().equals(r.getRiskLevel())) { + distribution.put("low", distribution.get("low") + 1); + } + } + return distribution; + } + +} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentDataAggregator.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentDataAggregator.java deleted file mode 100644 index 1420aa2dc1..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentDataAggregator.java +++ /dev/null @@ -1,374 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.riskassessment; - -import cn.iocoder.yudao.module.prison.dal.dataobject.prisoner.PrisonerDO; -import cn.iocoder.yudao.module.prison.dal.dataobject.riskassessment.RiskAssessmentDO; -import cn.iocoder.yudao.module.prison.dal.dataobject.score.ScoreDO; -import cn.iocoder.yudao.module.prison.dal.mysql.prisoner.PrisonerMapper; -import cn.iocoder.yudao.module.prison.dal.mysql.riskassessment.RiskAssessmentMapper; -import cn.iocoder.yudao.module.prison.dal.mysql.score.ScoreDetailMapper; -import cn.iocoder.yudao.module.prison.service.riskassessment.dto.AssessmentContext; -import cn.iocoder.yudao.module.prison.service.riskassessment.enums.AssessmentTypeEnum; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; - -import jakarta.annotation.Resource; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.time.LocalDate; -import java.time.Period; -import java.util.List; -import java.util.Optional; - -/** - * 评估数据聚合服务 - * 从多个数据源聚合评估所需数据 - * - * @author XLLC Team - */ -@Slf4j -@Service -public class AssessmentDataAggregator { - - @Resource - private PrisonerMapper prisonerMapper; - - @Resource - private RiskAssessmentMapper riskAssessmentMapper; - - @Resource - private ScoreDetailMapper scoreDetailMapper; - - @Resource - private DataMasker dataMasker; - - /** - * 聚合指定罪犯的评估数据 - * - * @param prisonerId 罪犯ID - * @param assessmentType 评估类型 - * @param assessorId 评估人ID - * @param assessorName 评估人姓名 - * @return 聚合后的评估上下文 - */ - public AssessmentContext aggregate(Long prisonerId, Integer assessmentType, Long assessorId, String assessorName) { - log.debug("开始聚合罪犯ID={}的评估数据", prisonerId); - - // 1. 获取罪犯基础信息 - PrisonerDO prisoner = prisonerMapper.selectById(prisonerId); - if (prisoner == null) { - throw new IllegalArgumentException("罪犯不存在: " + prisonerId); - } - - // 2. 构建基础上下文 - AssessmentContext.AssessmentContextBuilder builder = AssessmentContext.builder() - .prisonerId(String.valueOf(prisonerId)) - .prisonerNo(prisoner.getPrisonerNo()) - .assessmentType(AssessmentTypeEnum.getName(assessmentType)) - .assessmentDate(LocalDate.now()) - .assessorId(assessorId) - .assessorName(assessorName); - - // 3. 聚合犯罪基本信息(泛化处理) - aggregateCrimeInfo(builder, prisoner); - - // 4. 聚合心理评估得分 - aggregatePsychologyScores(builder, prisonerId); - - // 5. 聚合行为表现数据 - aggregateBehaviorData(builder, prisonerId); - - // 6. 聚合改造态度数据 - aggregateReformData(builder, prisonerId); - - // 7. 聚合消费数据 - aggregateConsumptionData(builder, prisonerId); - - // 8. 聚合历史评估数据 - aggregateHistoryData(builder, prisonerId); - - // 9. 聚合问卷答题数据 - aggregateQuestionnaireData(builder, prisonerId); - - AssessmentContext context = builder.build(); - log.debug("数据聚合完成,罪犯编号: {}", context.getPrisonerNo()); - - return context; - } - - /** - * 聚合犯罪基本信息 - */ - private void aggregateCrimeInfo(AssessmentContext.AssessmentContextBuilder builder, PrisonerDO prisoner) { - // 犯罪类型泛化 - String crimeCategory = dataMasker.generalizeCrimeType( - Optional.ofNullable(prisoner.getCrimeType()).orElse("未知") - ); - - // 刑期处理 - Integer sentenceYears = 0; - if (prisoner.getSentenceDate() != null && prisoner.getReleaseDate() != null) { - Period period = Period.between(prisoner.getSentenceDate(), prisoner.getReleaseDate()); - sentenceYears = period.getYears(); - } - - // 入监时长 - int imprisonmentMonths = 0; - if (prisoner.getEntryDate() != null) { - Period period = Period.between(prisoner.getEntryDate(), LocalDate.now()); - imprisonmentMonths = period.getYears() * 12 + period.getMonths(); - } - - builder - .crimeCategory(crimeCategory) - .sentenceYears(sentenceYears) - .isRecidivist(Boolean.TRUE.equals(prisoner.getIsRecidivist())) - .imprisonmentMonths(imprisonmentMonths); - } - - /** - * 聚合心理评估得分 - */ - private void aggregatePsychologyScores(AssessmentContext.AssessmentContextBuilder builder, Long prisonerId) { - // 获取最近一次危险评估记录 - RiskAssessmentDO latestAssessment = riskAssessmentMapper.selectOne( - new RiskAssessmentMapper.QueryWrapper() - .eq("prisoner_id", prisonerId) - .orderByDesc("assessment_date") - .last("LIMIT 1") - ); - - if (latestAssessment != null) { - builder - .violenceScore(normalizeScore(latestAssessment.getViolenceScore())) - .escapeScore(normalizeScore(latestAssessment.getEscapeScore())) - .suicideScore(normalizeScore(latestAssessment.getSuicideScore())); - } else { - // 默认值 - builder - .violenceScore(BigDecimal.ZERO) - .escapeScore(BigDecimal.ZERO) - .suicideScore(BigDecimal.ZERO); - } - } - - /** - * 聚合行为表现数据 - */ - private void aggregateBehaviorData(AssessmentContext.AssessmentContextBuilder builder, Long prisonerId) { - // 计算近6月的统计数据 - LocalDate sixMonthsAgo = LocalDate.now().minusMonths(6); - - // 查询近6月的计分记录 - List recentScores = scoreDetailMapper.selectList( - new ScoreDO().builder().prisonerId(prisonerId).build(), - new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .ge(ScoreDO::getRecordDate, sixMonthsAgo) - ); - - if (recentScores.isEmpty()) { - builder - .avgScore(new BigDecimal("70")) // 默认中等水平 - .violationCount6Month(0) - .praiseCount6Month(0); - return; - } - - // 计算平均分 - BigDecimal totalScore = recentScores.stream() - .map(ScoreDO::getTotalScore) - .filter(s -> s != null) - .reduce(BigDecimal.ZERO, BigDecimal::add); - - BigDecimal avgScore = totalScore.divide( - new BigDecimal(recentScores.size()), - 2, - RoundingMode.HALF_UP - ); - - // 统计违规和表扬次数(简化处理) - int violationCount = 0; - int praiseCount = 0; - for (ScoreDO score : recentScores) { - if (score.getDeductionPoints() != null && score.getDeductionPoints().intValue() > 0) { - violationCount++; - } - if (score.getRewardPoints() != null && score.getRewardPoints().intValue() > 0) { - praiseCount++; - } - } - - builder - .avgScore(avgScore) - .violationCount6Month(violationCount) - .praiseCount6Month(praiseCount); - } - - /** - * 聚合改造态度数据 - * 这里简化处理,实际应根据问卷答题情况计算 - */ - private void aggregateReformData(AssessmentContext.AssessmentContextBuilder builder, Long prisonerId) { - // 简化实现:基于行为得分推算 - BigDecimal avgScore = builder.build().getAvgScore(); - - builder - .laborPerformance(avgScore) // 用平均分作为劳动表现评分 - .educationParticipation(avgScore) // 用平均分作为教育参与度 - .thoughtReportQuality(avgScore); // 用平均分作为思想汇报质量 - } - - /** - * 聚合消费数据(区间化处理) - */ - private void aggregateConsumptionData(AssessmentContext.AssessmentContextBuilder builder, Long prisonerId) { - // TODO: 待消费模块完成后接入真实数据 - // 暂时使用默认值 - builder - .consumptionLevel(2) // 中等消费 - .hasAbnormalConsumption(false); - } - - /** - * 聚合历史评估数据 - */ - private void aggregateHistoryData(AssessmentContext.AssessmentContextBuilder builder, Long prisonerId) { - // 获取历史评估记录 - List historyAssessments = riskAssessmentMapper.selectList( - new RiskAssessmentMapper.QueryWrapper() - .eq("prisoner_id", prisonerId) - .orderByDesc("assessment_date") - ); - - if (historyAssessments.isEmpty()) { - // 首次评估 - builder - .lastRiskLevel(null) - .lastTotalScore(null) - .lastAssessmentMonthsAgo(null) - .riskTrend("stable") - .assessmentHistoryCount(0); - return; - } - - // 最近一次评估 - RiskAssessmentDO latest = historyAssessments.get(0); - - builder - .lastRiskLevel(latest.getRiskLevel()) - .lastTotalScore(latest.getTotalScore()) - .assessmentHistoryCount(historyAssessments.size()); - - // 计算距今时长 - if (latest.getAssessmentDate() != null) { - Period period = Period.between(latest.getAssessmentDate(), LocalDate.now()); - int monthsAgo = period.getYears() * 12 + period.getMonths(); - builder.lastAssessmentMonthsAgo(monthsAgo); - } - - // 计算风险趋势(基于最近3次评估) - String trend = calculateTrend(historyAssessments); - builder.riskTrend(trend); - } - - /** - * 聚合问卷答题数据 - * 根据各维度问卷的答题情况计算得分 - */ - private void aggregateQuestionnaireData(AssessmentContext.AssessmentContextBuilder builder, Long prisonerId) { - // TODO: 待问卷模块完成后接入真实数据 - // 暂时使用默认值,实际应从问卷答题记录中计算 - builder - .criminalHistoryScore(new BigDecimal("50")) - .familyBackgroundScore(new BigDecimal("50")) - .socialSupportScore(new BigDecimal("50")) - .psychologicalStateScore(builder.build().getViolenceScore()) - .behaviorPerformanceScore(builder.build().getAvgScore()) - .reformAttitudeScore(builder.build().getLaborPerformance()); - } - - /** - * 计算风险趋势 - */ - private String calculateTrend(List assessments) { - if (assessments.size() < 2) { - return "stable"; - } - - // 取最近3次评估 - int count = Math.min(3, assessments.size()); - List recent = assessments.subList(0, count); - - // 计算得分变化 - double sumDiff = 0; - for (int i = 1; i < recent.size(); i++) { - BigDecimal current = recent.get(i).getTotalScore(); - BigDecimal previous = recent.get(i - 1).getTotalScore(); - if (current != null && previous != null) { - sumDiff += current.subtract(previous).doubleValue(); - } - } - - if (sumDiff > 10) { - return "rising"; // 上升 - } else if (sumDiff < -10) { - return "declining"; // 下降 - } else { - return "stable"; // 稳定 - } - } - - /** - * 标准化分数到0-100范围 - */ - private BigDecimal normalizeScore(BigDecimal score) { - if (score == null) { - return BigDecimal.ZERO; - } - // 假设原始分数范围是0-100,直接返回 - return score; - } - - /** - * 数据脱敏工具 - */ - @Service - public static class DataMasker { - - /** - * 犯罪类型泛化映射 - */ - private static final Map CRIME_TYPE_MAPPING = Map.of( - "盗窃", "财产类犯罪", - "抢劫", "暴力类犯罪", - "抢夺", "暴力类犯罪", - "故意伤害", "暴力类犯罪", - "故意杀人", "严重暴力犯罪", - "强奸", "严重暴力犯罪", - "贩毒", "涉毒类犯罪", - "制造毒品", "涉毒类犯罪", - "诈骗", "财产类犯罪", - "敲诈勒索", "财产类犯罪", - "组织卖淫", "其他类型犯罪", - "赌博", "其他类型犯罪" - ); - - /** - * 泛化犯罪类型 - */ - public String generalizeCrimeType(String originalType) { - if (!StringUtils.hasText(originalType)) { - return "其他类型犯罪"; - } - return CRIME_TYPE_MAPPING.getOrDefault(originalType, "其他类型犯罪"); - } - - /** - * 泛化地名(预留) - */ - public String generalizeLocation(String originalLocation) { - // 暂时不处理,实际应根据需要进行脱敏 - return originalLocation; - } - } -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentPromptBuilder.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentPromptBuilder.java deleted file mode 100644 index df3173cbe6..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/AssessmentPromptBuilder.java +++ /dev/null @@ -1,246 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.riskassessment; - -import cn.iocoder.yudao.module.prison.service.riskassessment.dto.AssessmentContext; -import org.springframework.stereotype.Component; - -/** - * 评估提示词构建器 - * 根据评估上下文构建发送给LLM的提示词 - * - * @author XLLC Team - */ -@Component -public class AssessmentPromptBuilder { - - /** - * 构建危险评估提示词 - * - * @param context 评估上下文 - * @return 提示词文本 - */ - public String build(AssessmentContext context) { - StringBuilder prompt = new StringBuilder(); - - // 1. 角色定义 - prompt.append(buildRoleDefinition()); - - // 2. 任务说明 - prompt.append(buildTaskDescription(context)); - - // 3. 评估数据 - prompt.append(buildAssessmentData(context)); - - // 4. 评估规则 - prompt.append(buildAssessmentRules()); - - // 5. 输出要求 - prompt.append(buildOutputRequirements()); - - // 6. 评估原则 - prompt.append(buildPrinciples()); - - // 7. 开始评估指令 - prompt.append(buildStartInstruction()); - - return prompt.toString(); - } - - /** - * 构建角色定义 - */ - private String buildRoleDefinition() { - return """ - # 角色定义 - 你是一位拥有20年监狱工作经验的资深危险评估专家,精通心理学、犯罪学和行为分析。你需要基于提供的数据,对罪犯进行专业的危险等级评估。 - - """; - } - - /** - * 构建任务说明 - */ - private String buildTaskDescription(AssessmentContext context) { - StringBuilder sb = new StringBuilder(); - sb.append("# 任务说明\n\n"); - sb.append("根据以下【评估数据】对罪犯进行综合危险评估。\n\n"); - sb.append("**评估类型**: ").append(context.getAssessmentType()).append("\n\n"); - sb.append("请基于客观数据和专业判断,给出风险等级评估。\n\n"); - return sb.toString(); - } - - /** - * 构建评估数据 - */ - private String buildAssessmentData(AssessmentContext context) { - StringBuilder sb = new StringBuilder(); - sb.append("# 评估数据\n\n"); - sb.append("```json\n"); - sb.append(context.toJson()); - sb.append("\n```\n\n"); - - sb.append("## 数据说明\n\n"); - - // 心理评估得分说明 - sb.append("### 心理评估得分(0-100分,越高越危险)\n"); - sb.append("- 暴力倾向: ").append(context.getViolenceScore()).append("分\n"); - sb.append("- 脱逃倾向: ").append(context.getEscapeScore()).append("分\n"); - sb.append("- 自杀倾向: ").append(context.getSuicideScore()).append("分\n\n"); - - // 行为表现说明 - sb.append("### 行为表现\n"); - sb.append("- 计分考核平均分: ").append(context.getAvgScore()).append("分\n"); - sb.append("- 近6月违规次数: ").append(context.getViolationCount6Month()).append("次\n"); - sb.append("- 近6月表扬次数: ").append(context.getPraiseCount6Month()).append("次\n\n"); - - // 历史评估说明 - if (context.getLastRiskLevel() != null) { - sb.append("### 历史评估\n"); - sb.append("- 上次风险等级: ").append(formatRiskLevel(context.getLastRiskLevel())).append("\n"); - sb.append("- 历史风险趋势: ").append(formatTrend(context.getRiskTrend())).append("\n\n"); - } - - // 特殊因素 - if (hasRiskFactors(context)) { - sb.append("### 特殊风险因素\n"); - if (Boolean.TRUE.equals(context.getHasFamilyCrisis())) { - sb.append("- 存在家庭变故\n"); - } - if (Boolean.TRUE.equals(context.getHasConflictWithPrisoners())) { - sb.append("- 近期与他犯有矛盾\n"); - } - if (Boolean.TRUE.equals(context.getHasMoodAbnormality())) { - sb.append("- 近期情绪异常\n"); - } - if (StringUtils.hasText(context.getOtherRiskFactors())) { - sb.append("- 其他: ").append(context.getOtherRiskFactors()).append("\n"); - } - sb.append("\n"); - } - - return sb.toString(); - } - - /** - * 构建评估规则 - */ - private String buildAssessmentRules() { - return """ - # 评估规则 - - 根据综合得分和风险因素确定风险等级: - - | 风险等级 | 分值范围 | 说明 | - |---------|---------|------| - | 1-低风险 | < 40分 | 无明显风险因素,管理正常 | - | 2-中风险 | 40-69分 | 存在一定风险因素,需要关注 | - | 3-高风险 | 70-89分 | 存在明显风险因素,需要重点管控 | - | 4-极高风险 | ≥ 90分 | 存在严重风险因素,需要立即干预 | - - **注意**: 风险因素的存在可能导致等级上调,即使得分较低。 - - """; - } - - /** - * 构建输出要求 - */ - private String buildOutputRequirements() { - return """ - # 输出要求 - - 请以JSON格式输出评估结果,包含以下字段: - - ```json - { - "riskLevel": <1-4整数>, - "confidence": <0.0-1.0小数>, - "keyRiskFactors": ["因素1", "因素2", "因素3"], - "analysis": "<综合分析说明,100-200字>", - "suggestions": ["建议1", "建议2", "建议3"], - "attentionPoints": ["需要关注的点1", "需要关注的点2"] - } - ``` - - **字段说明**: - - `riskLevel`: 风险等级(1-4) - - `confidence`: 评估置信度(0-1),反映评估的确定性 - - `keyRiskFactors`: 最关键的风险因素(最多3个) - - `analysis`: 综合分析说明 - - `suggestions`: 针对性管控建议(具体可操作) - - `attentionPoints`: 需要特别关注的点 - - """; - } - - /** - * 构建评估原则 - */ - private String buildPrinciples() { - return """ - # 评估原则 - - 1. **客观公正**: 基于数据进行分析,不带主观偏见 - 2. **综合判断**: 考虑各因素之间的关联性,而非简单求和 - 3. **重点突出**: 识别最关键的风险因素 - 4. **建议可行**: 建议要具体、可操作 - 5. **审慎判断**: 对于边界情况给出审慎判断 - - """; - } - - /** - * 构建开始评估指令 - */ - private String buildStartInstruction() { - return """ - # 开始评估 - - 请基于以上数据和规则,开始进行危险等级评估。 - - """; - } - - /** - * 格式化风险等级 - */ - private String formatRiskLevel(Integer level) { - if (level == null) return "无"; - return switch (level) { - case 1 -> "1-低风险"; - case 2 -> "2-中风险"; - case 3 -> "3-高风险"; - case 4 -> "4-极高风险"; - default -> "未知(" + level + ")"; - }; - } - - /** - * 格式化趋势描述 - */ - private String formatTrend(String trend) { - if (trend == null) return "稳定"; - return switch (trend) { - case "rising" -> "上升趋势"; - case "declining" -> "下降趋势"; - case "fluctuating" -> "波动"; - default -> "稳定"; - }; - } - - /** - * 检查是否存在特殊风险因素 - */ - private boolean hasRiskFactors(AssessmentContext context) { - return Boolean.TRUE.equals(context.getHasFamilyCrisis()) - || Boolean.TRUE.equals(context.getHasConflictWithPrisoners()) - || Boolean.TRUE.equals(context.getHasMoodAbnormality()) - || StringUtils.hasText(context.getOtherRiskFactors()); - } - - // 需要引入 StringUtils - private static class StringUtils { - public static boolean hasText(String str) { - return str != null && !str.trim().isEmpty(); - } - } -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/dto/LlmAssessmentResult.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/dto/LlmAssessmentResult.java deleted file mode 100644 index 6443339d71..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/dto/LlmAssessmentResult.java +++ /dev/null @@ -1,108 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.riskassessment.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.List; - -/** - * LLM评估结果 - * - * @author XLLC Team - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class LlmAssessmentResult { - - /** - * 风险等级(1-4) - */ - private Integer riskLevel; - - /** - * 置信度(0-1) - */ - private BigDecimal confidence; - - /** - * 关键风险因素列表 - */ - private List keyRiskFactors; - - /** - * 综合分析说明 - */ - private String analysis; - - /** - * 管控建议列表 - */ - private List suggestions; - - /** - * 需要关注的点 - */ - private List attentionPoints; - - /** - * 原始LLM响应 - */ - private String rawResponse; - - /** - * 使用的模型 - */ - private String modelUsed; - - /** - * 评估耗时(毫秒) - */ - private Long evaluationTimeMs; - - /** - * 评估时间 - */ - private LocalDateTime evaluatedAt; - - /** - * 是否需要人工复核 - */ - public boolean requiresHumanReview() { - // 置信度低于0.7或风险等级为4(极高风险)需要人工复核 - return confidence.compareTo(new BigDecimal("0.7")) < 0 || riskLevel == 4; - } - - /** - * 获取风险等级描述 - */ - public String getRiskLevelDescription() { - if (riskLevel == null) return "未知"; - return switch (riskLevel) { - case 1 -> "低风险"; - case 2 -> "中风险"; - case 3 -> "高风险"; - case 4 -> "极高风险"; - default -> "未知"; - }; - } - - /** - * 获取风险等级颜色标识(前端使用) - */ - public String getRiskLevelColor() { - if (riskLevel == null) return "grey"; - return switch (riskLevel) { - case 1 -> "success"; // 绿色 - case 2 -> "warning"; // 橙色 - case 3 -> "danger"; // 红色 - case 4 -> "danger"; // 深红 - default -> "info"; - }; - } -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/ClaudeLlmClient.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/ClaudeLlmClient.java deleted file mode 100644 index b12f09fefb..0000000000 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/ClaudeLlmClient.java +++ /dev/null @@ -1,239 +0,0 @@ -package cn.iocoder.yudao.module.prison.service.riskassessment.llm; - -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.prison.service.riskassessment.llm.LlmClient.LlmOptions; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; - -/** - * Claude大模型客户端实现 - * - * 基于Anthropic Claude API的客户端实现 - * API文档: https://docs.anthropic.com/claude/reference/getting-started - * - * @author XLLC Team - */ -@Slf4j -@Component -public class ClaudeLlmClient implements LlmClient { - - private static final String BASE_URL = "https://api.anthropic.com/v1/messages"; - private static final String DEFAULT_MODEL = "claude-3-5-sonnet-20241022"; - - @Value("${llm.claude.api-key:}") - private String apiKey; - - @Value("${llm.claude.timeout-seconds:30}") - private int timeoutSeconds; - - private final RestTemplate restTemplate; - - public ClaudeLlmClient() { - this.restTemplate = new RestTemplate(); - // 配置超时 - this.restTemplate.setRequestFactory(() -> { - var requestFactory = new org.springframework.http.client.SimpleClientHttpRequestFactory(); - requestFactory.setConnectTimeout(Duration.ofSeconds(30)); - requestFactory.setReadTimeout(Duration.ofSeconds(30)); - return requestFactory; - }); - } - - @Override - public String complete(String prompt, LlmOptions options) { - try { - ClaudeRequest request = buildRequest(prompt, options, false); - ClaudeResponse response = execute(request); - - if (response.content != null && !response.content.isEmpty()) { - return response.content.get(0).text; - } - - log.warn("Claude响应内容为空"); - return ""; - - } catch (Exception e) { - log.error("Claude API调用失败: {}", e.getMessage(), e); - throw new LlmException("Claude API调用失败: " + e.getMessage(), e); - } - } - - @Override - public String completeJson(String prompt, LlmOptions options) { - try { - // 在提示词中明确要求JSON输出 - String jsonPrompt = prompt + "\n\n请确保输出格式为有效的JSON,不要添加额外的markdown格式。"; - - ClaudeRequest request = buildRequest(jsonPrompt, options, true); - ClaudeResponse response = execute(request); - - if (response.content != null && !response.content.isEmpty()) { - String text = response.content.get(0).text; - // 清理可能的markdown包装 - text = cleanJsonResponse(text); - return text; - } - - log.warn("Claude JSON响应内容为空"); - return ""; - - } catch (Exception e) { - log.error("Claude JSON API调用失败: {}", e.getMessage(), e); - throw new LlmException("Claude JSON API调用失败: " + e.getMessage(), e); - } - } - - @Override - public boolean isAvailable() { - if (apiKey == null || apiKey.isEmpty()) { - log.warn("Claude API密钥未配置"); - return false; - } - - try { - // 简单的健康检查 - String testPrompt = "Hello"; - ClaudeRequest request = buildRequest(testPrompt, - LlmOptions.defaultOptions(), false); - execute(request); - return true; - } catch (Exception e) { - log.warn("Claude服务不可用: {}", e.getMessage()); - return false; - } - } - - @Override - public String getName() { - return "Claude"; - } - - /** - * 构建请求对象 - */ - private ClaudeRequest buildRequest(String prompt, LlmOptions options, boolean isJsonMode) { - // 构建系统提示词(用于JSON模式) - String systemPrompt = isJsonMode - ? "你是一个专业的危险评估助手。请始终以JSON格式输出响应,不要添加任何markdown代码块标记。" - : "你是一位专业的危险评估专家。请基于提供的数据进行专业评估。"; - - return new ClaudeRequest( - options.model() != null ? options.model() : DEFAULT_MODEL, - prompt, - systemPrompt, - options.temperature(), - options.maxTokens() - ); - } - - /** - * 执行API调用 - */ - private ClaudeResponse execute(ClaudeRequest request) { - // 请求头 - Map headers = new HashMap<>(); - headers.put("x-api-key", apiKey); - headers.put("anthropic-version", "2023-06-01"); - headers.put("anthropic-dangerous-direct-browser-access", "true"); - headers.put("content-type", "application/json"); - - // 构建请求体 - Map body = new HashMap<>(); - body.put("model", request.model()); - body.put("messages", new Object[]{ - Map.of("role", "system", "content", request.systemPrompt()) - }); - body.put("max_tokens", request.maxTokens()); - body.put("temperature", request.temperature()); - body.put("stream", false); - - // 添加用户消息 - Object[] messages = new Object[2]; - messages[0] = Map.of("role", "system", "content", request.systemPrompt()); - messages[1] = Map.of("role", "user", "content", request.prompt()); - body.put("messages", messages); - - try { - // 注意: 实际使用时需要配置HTTP客户端处理Anthropic的特殊请求头 - // 这里使用简化实现,实际项目中建议使用官方SDK - log.debug("调用Claude API, model: {}, temperature: {}", - request.model(), request.temperature()); - - // 模拟响应(实际需要接入真实API) - // TODO: 接入真实Claude API - return buildMockResponse(); - - } catch (RestClientException e) { - log.error("Claude API请求失败: {}", e.getMessage()); - throw new LlmException("API请求失败: " + e.getMessage(), e); - } - } - - /** - * 构建模拟响应(开发阶段使用) - */ - private ClaudeResponse buildMockResponse() { - // 开发阶段返回模拟数据 - log.warn("使用Claude API模拟响应,请配置真实API密钥"); - - ClaudeResponse.ContentBlock content = new ClaudeResponse.ContentBlock( - "text", - "{\"riskLevel\": 2, \"confidence\": 0.85, \"keyRiskFactors\": [\"开发阶段模拟数据\"], \"analysis\": \"这是模拟响应,请配置Claude API密钥后使用真实调用\", \"suggestions\": [\"配置API密钥后重启服务\"]}" - ); - - return new ClaudeResponse( - "mock-id", - "assistant", - new java.util.List.of(content), - "stop", - "end_turn" - ); - } - - /** - * 清理JSON响应,去除markdown包装 - */ - private String cleanJsonResponse(String text) { - if (text == null) { - return ""; - } - - // 去除 ```json 和 ``` 包装 - text = text.replaceAll("```json\\s*", "") - .replaceAll("```\\s*", "") - .trim(); - - return text; - } - - /** - * Claude API请求结构 - */ - private record ClaudeRequest( - String model, - String prompt, - String systemPrompt, - double temperature, - int maxTokens - ) {} - - /** - * Claude API响应结构 - */ - private record ClaudeResponse( - String id, - String type, - java.util.List content, - String stop_reason, - String role - ) { - record ContentBlock(String type, String text) {} - } -} diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClient.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClient.java index f49d4c384d..1ca7450b52 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClient.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClient.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.prison.service.riskassessment.llm; -import java.util.Map; +import java.util.List; /** * LLM客户端接口 @@ -9,81 +9,108 @@ import java.util.Map; */ public interface LlmClient { - /** - * 调用大模型生成内容 - * - * @param prompt 提示词 - * @param options 调用选项 - * @return 模型生成的文本响应 - */ - String complete(String prompt, LlmOptions options); - - /** - * 调用大模型生成JSON格式响应 - * - * @param prompt 提示词 - * @param options 调用选项 - * @return JSON格式的响应文本 - */ - String completeJson(String prompt, LlmOptions options); - - /** - * 检查客户端是否可用 - * - * @return 是否可用 - */ - boolean isAvailable(); - /** * 获取客户端名称 - * - * @return 客户端名称 */ String getName(); /** - * 调用选项 + * 检查客户端是否可用 */ - record LlmOptions( - String model, // 模型名称 - double temperature, // 温度参数 (0.0-1.0) - int maxTokens, // 最大Token数 - Map headers // 额外请求头 - ) { + boolean isAvailable(); + + /** + * 简单文本补全 + * + * @param prompt 提示词 + * @param options 选项 + * @return 生成的文本 + */ + String complete(String prompt, LlmOptions options); + + /** + * JSON格式文本补全 + * + * @param prompt 提示词 + * @param options 选项 + * @return 生成的JSON文本 + */ + String completeJson(String prompt, LlmOptions options); + + /** + * LLM选项配置 + */ + class LlmOptions { + + /** + * 最大token数 + */ + private int maxTokens = 4096; + + /** + * 温度参数 (0-2) + */ + private float temperature = 0.7f; + + /** + * 系统提示词 + */ + private String systemPrompt; + + /** + * 是否需要JSON格式响应 + */ + private boolean jsonMode = false; + + // Getters and Setters + public int getMaxTokens() { + return maxTokens; + } + + public void setMaxTokens(int maxTokens) { + this.maxTokens = maxTokens; + } + + public float getTemperature() { + return temperature; + } + + public void setTemperature(float temperature) { + this.temperature = temperature; + } + + public String getSystemPrompt() { + return systemPrompt; + } + + public void setSystemPrompt(String systemPrompt) { + this.systemPrompt = systemPrompt; + } + + public boolean isJsonMode() { + return jsonMode; + } + + public void setJsonMode(boolean jsonMode) { + this.jsonMode = jsonMode; + } + /** * 创建默认选项 */ public static LlmOptions defaultOptions() { - return new LlmOptions( - "claude-3-5-sonnet-20241022", - 0.1, // 低温度,输出更稳定 - 2000, - Map.of() - ); + return new LlmOptions(); } /** - * 创建低温度选项(用于评估场景) + * 创建评估专用选项 */ public static LlmOptions assessmentOptions() { - return new LlmOptions( - "claude-3-5-sonnet-20241022", - 0.05, // 极低温度,减少随机性 - 2048, - Map.of() - ); - } - - /** - * 创建高温度选项(用于创意场景) - */ - public static LlmOptions creativeOptions() { - return new LlmOptions( - "claude-3-5-sonnet-20241022", - 0.7, - 4096, - Map.of() - ); + LlmOptions options = new LlmOptions(); + options.setMaxTokens(2048); + options.setTemperature(0.3f); // 评估场景使用较低温度,减少随机性 + options.setJsonMode(true); + return options; } } } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClientFactory.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClientFactory.java index 8a9fd247fa..d2e2cee0dc 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClientFactory.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmClientFactory.java @@ -1,106 +1,48 @@ package cn.iocoder.yudao.module.prison.service.riskassessment.llm; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import java.util.List; -import java.util.Map; - /** * LLM客户端工厂 - * 根据配置选择不同的LLM客户端 + * 根据配置返回对应的LLM客户端 * * @author XLLC Team */ -@Slf4j @Component public class LlmClientFactory { - private final Map clients; + private final OpenAiCompatibleClient openAiCompatibleClient; - public LlmClientFactory(List clientList) { - // 初始化客户端映射 - this.clients = new java.util.HashMap<>(); - for (LlmClient client : clientList) { - clients.put(client.getName().toLowerCase(), client); - log.info("注册LLM客户端: {}", client.getName()); - } + public LlmClientFactory(OpenAiCompatibleClient openAiCompatibleClient) { + this.openAiCompatibleClient = openAiCompatibleClient; + } + + /** + * 获取评估专用的LLM客户端 + */ + public LlmClient getAssessmentClient() { + // 当前只支持OpenAI兼容客户端 + return openAiCompatibleClient; } /** * 获取指定名称的客户端 - * - * @param name 客户端名称 - * @return LLM客户端 */ - public LlmClient getClient(String name) { - LlmClient client = clients.get(name.toLowerCase()); - if (client == null) { - throw new LlmException("UNKNOWN_CLIENT", "未找到LLM客户端: " + name); + public LlmClient getClient(String clientName) { + if (clientName == null) { + return getAssessmentClient(); } - return client; + + return switch (clientName.toLowerCase()) { + case "openai", "oneapi", "openai-compatible" -> openAiCompatibleClient; + default -> getAssessmentClient(); + }; } /** - * 获取默认客户端 - * - * @return 默认LLM客户端 + * 检查是否有可用的客户端 */ - public LlmClient getDefaultClient() { - // 优先使用Claude - if (clients.containsKey("claude")) { - return clients.get("claude"); - } - - // 如果没有Claude,返回第一个可用的客户端 - return clients.values().stream() - .filter(LlmClient::isAvailable) - .findFirst() - .orElseThrow(() -> new LlmException("NO_AVAILABLE_CLIENT", "没有可用的LLM客户端")); - } - - /** - * 获取用于评估的客户端 - * 优先选择Claude(评估能力强) - * - * @return 评估用的LLM客户端 - */ - public LlmClient getAssessmentClient() { - // 优先使用Claude - LlmClient claude = clients.get("claude"); - if (claude != null && claude.isAvailable()) { - log.debug("选择Claude作为评估客户端"); - return claude; - } - - // 检查其他可用客户端 - for (LlmClient client : clients.values()) { - if (client.isAvailable()) { - log.warn("Claude不可用,使用备用客户端: {}", client.getName()); - return client; - } - } - - throw new LlmException("NO_AVAILABLE_CLIENT", "没有可用的LLM客户端进行评估"); - } - - /** - * 检查指定客户端是否可用 - * - * @param name 客户端名称 - * @return 是否可用 - */ - public boolean isAvailable(String name) { - LlmClient client = clients.get(name.toLowerCase()); - return client != null && client.isAvailable(); - } - - /** - * 获取所有已注册的客户端名称 - * - * @return 客户端名称列表 - */ - public List getRegisteredClients() { - return clients.keySet().stream().toList(); + public boolean hasAvailableClient() { + return openAiCompatibleClient.isAvailable(); } } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmException.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmException.java index 440f0f6405..158ea9f135 100644 --- a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmException.java +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/LlmException.java @@ -1,35 +1,17 @@ package cn.iocoder.yudao.module.prison.service.riskassessment.llm; /** - * LLM调用异常 + * LLM操作异常 * * @author XLLC Team */ public class LlmException extends RuntimeException { - private final String errorCode; - public LlmException(String message) { super(message); - this.errorCode = "LLM_ERROR"; } public LlmException(String message, Throwable cause) { super(message, cause); - this.errorCode = "LLM_ERROR"; - } - - public LlmException(String errorCode, String message) { - super(message); - this.errorCode = errorCode; - } - - public LlmException(String errorCode, String message, Throwable cause) { - super(message, cause); - this.errorCode = errorCode; - } - - public String getErrorCode() { - return errorCode; } } diff --git a/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/OpenAiCompatibleClient.java b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/OpenAiCompatibleClient.java new file mode 100644 index 0000000000..c34d176e6c --- /dev/null +++ b/yudao-module-prison/src/main/java/cn/iocoder/yudao/module/prison/service/riskassessment/llm/OpenAiCompatibleClient.java @@ -0,0 +1,173 @@ +package cn.iocoder.yudao.module.prison.service.riskassessment.llm; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * OpenAI兼容客户端 + * 支持OneAPI等统一接口服务 + * + * @author XLLC Team + */ +@Slf4j +@Component +public class OpenAiCompatibleClient implements LlmClient { + + private static final String DEFAULT_BASE_URL = "https://oneapi.gongjulian.cn/v1"; + private static final String DEFAULT_MODEL = "deepseek-ai/deepseek-v3.2"; + + @Value("${llm.local.base-url:}") + private String baseUrl; + + @Value("${llm.local.api-key:}") + private String apiKey; + + @Value("${llm.local.model:}") + private String model; + + @Value("${llm.local.timeout-seconds:120}") + private int timeoutSeconds; + + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; + + public OpenAiCompatibleClient() { + this.restTemplate = new RestTemplate(); + this.objectMapper = new ObjectMapper(); + } + + @Override + public String getName() { + return "OpenAI Compatible (OneAPI)"; + } + + @Override + public boolean isAvailable() { + try { + // 尝试获取模型列表来检查连接 + String url = getBaseUrl() + "/models"; + HttpHeaders headers = createHeaders(); + HttpEntity entity = new HttpEntity<>(headers); + + restTemplate.exchange(url, HttpMethod.GET, entity, String.class, + Duration.ofSeconds(5)); + return true; + } catch (Exception e) { + log.warn("LLM客户端不可用: {}", e.getMessage()); + return false; + } + } + + @Override + public String complete(String prompt, LlmOptions options) { + String url = getBaseUrl() + "/chat/completions"; + + HttpHeaders headers = createHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + // 构建请求体 + Map requestBody = new HashMap<>(); + requestBody.put("model", getModel()); + + // 消息 + List> messages = new ArrayList<>(); + + if (options.getSystemPrompt() != null && !options.getSystemPrompt().isEmpty()) { + Map systemMessage = new HashMap<>(); + systemMessage.put("role", "system"); + systemMessage.put("content", options.getSystemPrompt()); + messages.add(systemMessage); + } + + Map userMessage = new HashMap<>(); + userMessage.put("role", "user"); + userMessage.put("content", prompt); + messages.add(userMessage); + + requestBody.put("messages", messages); + + // 参数 + if (options.getMaxTokens() > 0) { + requestBody.put("max_tokens", options.getMaxTokens()); + } + if (options.getTemperature() >= 0 && options.getTemperature() <= 2) { + requestBody.put("temperature", options.getTemperature()); + } + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + try { + ResponseEntity response = restTemplate.exchange( + url, HttpMethod.POST, entity, String.class, + Duration.ofSeconds(timeoutSeconds)); + + if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) { + return parseContent(response.getBody()); + } + throw new LlmException("LLM调用失败: " + response.getStatusCode()); + } catch (RestClientException e) { + log.error("LLM调用异常", e); + throw new LlmException("LLM调用异常: " + e.getMessage()); + } + } + + @Override + public String completeJson(String prompt, LlmOptions options) { + // 设置JSON模式 + LlmOptions jsonOptions = new LlmOptions(); + jsonOptions.setMaxTokens(options.getMaxTokens()); + jsonOptions.setTemperature(options.getTemperature()); + jsonOptions.setSystemPrompt(options.getSystemPrompt()); + jsonOptions.setJsonMode(true); + + // 在提示词中强调JSON格式 + String jsonPrompt = prompt + "\n\n请严格按照JSON格式回复,不要包含其他内容。"; + + return complete(jsonPrompt, jsonOptions); + } + + private String getBaseUrl() { + return baseUrl != null && !baseUrl.isEmpty() ? baseUrl : DEFAULT_BASE_URL; + } + + private String getModel() { + return model != null && !model.isEmpty() ? model : DEFAULT_MODEL; + } + + private HttpHeaders createHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + apiKey); + headers.set("Content-Type", MediaType.APPLICATION_JSON_VALUE); + headers.set("Accept", MediaType.APPLICATION_JSON_VALUE); + return headers; + } + + private String parseContent(String responseBody) { + try { + JsonNode root = objectMapper.readTree(responseBody); + JsonNode choices = root.path("choices"); + if (choices.isArray() && choices.size() > 0) { + JsonNode message = choices.get(0).path("message"); + if (!message.isMissingNode()) { + return message.path("content").asText(); + } + } + throw new LlmException("解析LLM响应失败"); + } catch (Exception e) { + log.error("解析LLM响应异常", e); + throw new LlmException("解析LLM响应异常: " + e.getMessage()); + } + } +} diff --git a/yudao-module-prison/src/main/resources/sql/answer_module.sql b/yudao-module-prison/src/main/resources/sql/answer_module.sql new file mode 100644 index 0000000000..ac22571813 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/answer_module.sql @@ -0,0 +1,59 @@ +-- ===================================================== +-- XL监狱综合管理平台 - 问卷答题记录模块数据库脚本 +-- 生成时间: 2026-01-15 +-- ===================================================== + +-- ===================================================== +-- 1. 问卷答题记录表 (prison_answer) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_answer` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '答题记录ID', + `assessment_record_id` bigint NOT NULL COMMENT '测评记录ID', + `question_id` bigint NOT NULL COMMENT '问题ID', + `questionnaire_id` bigint DEFAULT NULL COMMENT '问卷ID(冗余)', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', + `question_type` tinyint NOT NULL DEFAULT 1 COMMENT '问题类型:1-单选 2-多选 3-填空 4-评分 5-日期 6-数字', + `answer_text` text COMMENT '答案内容(填空题、评分题等)', + `option_ids` text COMMENT '选项ID列表(JSON数组,如 [1,2,3])', + `score` decimal(10,2) DEFAULT NULL COMMENT '得分', + `is_correct` tinyint(1) DEFAULT NULL COMMENT '是否正确:null-未评分 false-错误 true-正确', + `duration` int 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_answer_assessment_record_id` (`assessment_record_id`), + KEY `idx_prison_answer_question_id` (`question_id`), + KEY `idx_prison_answer_prisoner_id` (`prisoner_id`), + KEY `idx_prison_answer_questionnaire_id` (`questionnaire_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷答题记录表'; + +-- ===================================================== +-- 2. 序列 (适用于 PostgreSQL、Oracle、DB2 等数据库) +-- ===================================================== +-- MySQL 不需要此序列,使用 AUTO_INCREMENT +-- PostgreSQL: CREATE SEQUENCE prison_answer_seq; +-- Oracle: CREATE SEQUENCE prison_answer_seq MINVALUE 1 START WITH 1 INCREMENT BY 1 CACHE 20; + +-- ===================================================== +-- 3. 菜单权限 SQL +-- ===================================================== +-- 注意: 执行前请确保问卷管理菜单 (id=5047) 已存在 + +-- 8. 答卷管理菜单 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, component_name) +VALUES ('答卷管理', '', 2, 8, 5047, 'answer', '', 'prison/answer/index', 0, 'Answer'); +SELECT @answerParentId := LAST_INSERT_ID(); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('答卷查询', 'prison:answer:query', 3, 1, @answerParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('答卷创建', 'prison:answer:create', 3, 2, @answerParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('答卷更新', 'prison:answer:update', 3, 3, @answerParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('答卷删除', 'prison:answer:delete', 3, 4, @answerParentId, '', '', '', 0); +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +VALUES ('答卷导出', 'prison:answer:export', 3, 5, @answerParentId, '', '', '', 0); diff --git a/yudao-module-prison/src/main/resources/sql/assessment_migration.sql b/yudao-module-prison/src/main/resources/sql/assessment_migration.sql new file mode 100644 index 0000000000..f08b1ec847 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/assessment_migration.sql @@ -0,0 +1,250 @@ +-- ===================================================== +-- XL监狱综合管理平台 - 测评执行模块数据库迁移脚本 +-- 生成时间: 2026-01-15 +-- 说明: 将 assessment 模块功能整合到 questionnairerecord 模块 +-- 兼容 MySQL 8.0 +-- ===================================================== + +-- ===================================================== +-- 1. 更新 prison_questionnaire_record 表结构 +-- 添加测评执行所需字段 +-- ===================================================== + +-- 添加问卷名称字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'questionnaire_name') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `questionnaire_name` varchar(200) DEFAULT NULL COMMENT '问卷名称' AFTER `questionnaire_id`; +END IF; + +-- 添加罪犯姓名字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'prisoner_name') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `prisoner_name` varchar(100) DEFAULT NULL COMMENT '罪犯姓名' AFTER `prisoner_no`; +END IF; + +-- 添加开始时间字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'start_time') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `start_time` datetime DEFAULT NULL COMMENT '开始时间'; +END IF; + +-- 添加结束时间字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'end_time') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `end_time` datetime DEFAULT NULL COMMENT '结束时间'; +END IF; + +-- 添加截止日期字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'deadline') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `deadline` datetime DEFAULT NULL COMMENT '截止日期'; +END IF; + +-- 添加客观题得分字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'objective_score') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `objective_score` decimal(10,2) DEFAULT 0.00 COMMENT '客观题得分'; +END IF; + +-- 添加主观题得分字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'subjective_score') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `subjective_score` decimal(10,2) DEFAULT 0.00 COMMENT '主观题得分'; +END IF; + +-- 添加总分字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'total_score') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `total_score` decimal(10,2) DEFAULT NULL COMMENT '总分'; +END IF; + +-- 添加及格分数字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'pass_score') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `pass_score` decimal(10,2) DEFAULT NULL COMMENT '及格分数'; +END IF; + +-- 添加及格状态字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'pass_status') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `pass_status` tinyint DEFAULT NULL COMMENT '及格状态:1-及格 2-不及格 3-待评阅'; +END IF; + +-- 添加风险等级字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'risk_level') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `risk_level` tinyint DEFAULT NULL COMMENT '风险等级:1-高风险 2-中风险 3-低风险'; +END IF; + +-- 添加评阅人ID字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'evaluator_id') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `evaluator_id` bigint DEFAULT NULL COMMENT '评阅人ID'; +END IF; + +-- 添加评阅人姓名字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'evaluator_name') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `evaluator_name` varchar(100) DEFAULT NULL COMMENT '评阅人姓名'; +END IF; + +-- 添加评阅时间字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'evaluate_time') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `evaluate_time` datetime DEFAULT NULL COMMENT '评阅时间'; +END IF; + +-- 添加参与人数字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'participant_count') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `participant_count` int DEFAULT 0 COMMENT '参与人数'; +END IF; + +-- 添加完成人数字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'completed_count') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `completed_count` int DEFAULT 0 COMMENT '完成人数'; +END IF; + +-- 添加答题用时字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'duration') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `duration` int DEFAULT NULL COMMENT '答题用时(秒)'; +END IF; + +-- 添加备注字段 +IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prison_questionnaire_record' + AND COLUMN_NAME = 'remark') THEN + ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `remark` varchar(500) DEFAULT NULL COMMENT '备注'; +END IF; + +-- ===================================================== +-- 2. 创建答卷详情表 (prison_questionnaire_answer) +-- 用于记录每道题的答题详情 +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_questionnaire_answer` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '答案ID', + `record_id` bigint NOT NULL COMMENT '测评记录ID', + `question_id` bigint NOT NULL COMMENT '题目ID', + `question_type` tinyint NOT NULL COMMENT '题目类型:1-单选 2-多选 3-判断 4-填空 5-简述', + `question_content` varchar(500) NOT NULL COMMENT '题目内容', + `answer_content` text COMMENT '答案内容', + `score` decimal(10,2) DEFAULT 0.00 COMMENT '得分', + `is_correct` bit(1) DEFAULT NULL COMMENT '是否正确', + `is_manual_score` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否需要人工评分', + `manual_score` decimal(10,2) DEFAULT NULL COMMENT '人工评分分数', + `manual_comment` 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_questionnaire_answer_record_id` (`record_id`), + KEY `idx_questionnaire_answer_question_id` (`question_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷答题详情表'; + +-- ===================================================== +-- 3. 创建测评统计表 (prison_questionnaire_statistics) +-- 用于统计测评的整体情况 +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_questionnaire_statistics` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '统计ID', + `questionnaire_id` bigint NOT NULL COMMENT '问卷ID', + `questionnaire_name` varchar(200) NOT NULL COMMENT '问卷名称', + `stat_date` date NOT NULL COMMENT '统计日期', + `total_count` int NOT NULL DEFAULT 0 COMMENT '发起总数', + `completed_count` int NOT NULL DEFAULT 0 COMMENT '完成数量', + `completion_rate` decimal(5,2) DEFAULT 0.00 COMMENT '完成率', + `average_score` decimal(10,2) DEFAULT 0.00 COMMENT '平均分', + `pass_rate` decimal(5,2) DEFAULT 0.00 COMMENT '及格率', + `high_risk_count` int NOT NULL DEFAULT 0 COMMENT '高风险人数', + `medium_risk_count` int NOT NULL DEFAULT 0 COMMENT '中风险人数', + `low_risk_count` 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_questionnaire_statistics_questionnaire_id` (`questionnaire_id`), + KEY `idx_questionnaire_statistics_stat_date` (`stat_date`), + UNIQUE KEY `uk_questionnaire_date` (`questionnaire_id`, `stat_date`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷测评统计表'; + +-- ===================================================== +-- 4. 更新菜单权限 +-- ===================================================== +-- 获取问卷记录父菜单ID +-- SELECT @parentId := id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1; + +-- 添加测评相关权限 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '发起测评', 'prison:questionnaire-record:initiate', 3, 6, parent_id, '', '', '', 0 +FROM (SELECT id as parent_id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1) t +WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE name = '发起测评' AND parent_id = (SELECT id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1)); + +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '开始测评', 'prison:questionnaire-record:start', 3, 7, parent_id, '', '', '', 0 +FROM (SELECT id as parent_id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1) t +WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE name = '开始测评' AND parent_id = (SELECT id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1)); + +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '提交答卷', 'prison:questionnaire-record:submit', 3, 8, parent_id, '', '', '', 0 +FROM (SELECT id as parent_id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1) t +WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE name = '提交答卷' AND parent_id = (SELECT id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1)); + +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '结束测评', 'prison:questionnaire-record:finish', 3, 9, parent_id, '', '', '', 0 +FROM (SELECT id as parent_id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1) t +WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE name = '结束测评' AND parent_id = (SELECT id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1)); + +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '取消测评', 'prison:questionnaire-record:cancel', 3, 10, parent_id, '', '', '', 0 +FROM (SELECT id as parent_id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1) t +WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE name = '取消测评' AND parent_id = (SELECT id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1)); + +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '人工评分', 'prison:questionnaire-record:score', 3, 11, parent_id, '', '', '', 0 +FROM (SELECT id as parent_id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1) t +WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE name = '人工评分' AND parent_id = (SELECT id FROM system_menu WHERE name = '问卷记录管理' LIMIT 1)); + +SELECT '数据库迁移脚本执行完成!'; diff --git a/yudao-module-prison/src/main/resources/sql/assessment_migration_simple.sql b/yudao-module-prison/src/main/resources/sql/assessment_migration_simple.sql new file mode 100644 index 0000000000..f32c17aa90 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/assessment_migration_simple.sql @@ -0,0 +1,171 @@ +-- ===================================================== +-- XL监狱综合管理平台 - 测评执行模块数据库迁移脚本 +-- 简化版(直接执行,无需 IF 判断) +-- ===================================================== + +-- ===================================================== +-- 1. 更新 prison_questionnaire_record 表结构 +-- 添加测评执行所需字段 +-- ===================================================== + +-- 添加问卷名称字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `questionnaire_name` varchar(200) DEFAULT NULL COMMENT '问卷名称' AFTER `questionnaire_id`; + +-- 添加罪犯姓名字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `prisoner_name` varchar(100) DEFAULT NULL COMMENT '罪犯姓名' AFTER `prisoner_no`; + +-- 添加开始时间字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `start_time` datetime DEFAULT NULL COMMENT '开始时间'; + +-- 添加结束时间字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `end_time` datetime DEFAULT NULL COMMENT '结束时间'; + +-- 添加截止日期字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `deadline` datetime DEFAULT NULL COMMENT '截止日期'; + +-- 添加客观题得分字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `objective_score` decimal(10,2) DEFAULT 0.00 COMMENT '客观题得分'; + +-- 添加主观题得分字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `subjective_score` decimal(10,2) DEFAULT 0.00 COMMENT '主观题得分'; + +-- 添加总分字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `total_score` decimal(10,2) DEFAULT NULL COMMENT '总分'; + +-- 添加及格分数字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `pass_score` decimal(10,2) DEFAULT NULL COMMENT '及格分数'; + +-- 添加及格状态字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `pass_status` tinyint DEFAULT NULL COMMENT '及格状态:1-及格 2-不及格 3-待评阅'; + +-- 添加风险等级字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `risk_level` tinyint DEFAULT NULL COMMENT '风险等级:1-高风险 2-中风险 3-低风险'; + +-- 添加评阅人ID字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `evaluator_id` bigint DEFAULT NULL COMMENT '评阅人ID'; + +-- 添加评阅人姓名字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `evaluator_name` varchar(100) DEFAULT NULL COMMENT '评阅人姓名'; + +-- 添加评阅时间字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `evaluate_time` datetime DEFAULT NULL COMMENT '评阅时间'; + +-- 添加参与人数字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `participant_count` int DEFAULT 0 COMMENT '参与人数'; + +-- 添加完成人数字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `completed_count` int DEFAULT 0 COMMENT '完成人数'; + +-- 添加答题用时字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `duration` int DEFAULT NULL COMMENT '答题用时(秒)'; + +-- 添加备注字段 +ALTER TABLE `prison_questionnaire_record` + ADD COLUMN `remark` varchar(500) DEFAULT NULL COMMENT '备注'; + +-- ===================================================== +-- 2. 创建答卷详情表 (prison_questionnaire_answer) +-- ===================================================== +CREATE TABLE `prison_questionnaire_answer` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '答案ID', + `record_id` bigint NOT NULL COMMENT '测评记录ID', + `question_id` bigint NOT NULL COMMENT '题目ID', + `question_type` tinyint NOT NULL COMMENT '题目类型:1-单选 2-多选 3-判断 4-填空 5-简述', + `question_content` varchar(500) NOT NULL COMMENT '题目内容', + `answer_content` text COMMENT '答案内容', + `score` decimal(10,2) DEFAULT 0.00 COMMENT '得分', + `is_correct` bit(1) DEFAULT NULL COMMENT '是否正确', + `is_manual_score` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否需要人工评分', + `manual_score` decimal(10,2) DEFAULT NULL COMMENT '人工评分分数', + `manual_comment` 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_questionnaire_answer_record_id` (`record_id`), + KEY `idx_questionnaire_answer_question_id` (`question_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷答题详情表'; + +-- ===================================================== +-- 3. 创建测评统计表 (prison_questionnaire_statistics) +-- ===================================================== +CREATE TABLE `prison_questionnaire_statistics` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '统计ID', + `questionnaire_id` bigint NOT NULL COMMENT '问卷ID', + `questionnaire_name` varchar(200) NOT NULL COMMENT '问卷名称', + `stat_date` date NOT NULL COMMENT '统计日期', + `total_count` int NOT NULL DEFAULT 0 COMMENT '发起总数', + `completed_count` int NOT NULL DEFAULT 0 COMMENT '完成数量', + `completion_rate` decimal(5,2) DEFAULT 0.00 COMMENT '完成率', + `average_score` decimal(10,2) DEFAULT 0.00 COMMENT '平均分', + `pass_rate` decimal(5,2) DEFAULT 0.00 COMMENT '及格率', + `high_risk_count` int NOT NULL DEFAULT 0 COMMENT '高风险人数', + `medium_risk_count` int NOT NULL DEFAULT 0 COMMENT '中风险人数', + `low_risk_count` 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_questionnaire_statistics_questionnaire_id` (`questionnaire_id`), + KEY `idx_questionnaire_statistics_stat_date` (`stat_date`), + UNIQUE KEY `uk_questionnaire_date` (`questionnaire_id`, `stat_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问卷测评统计表'; + +-- ===================================================== +-- 4. 添加菜单权限 +-- ===================================================== + +-- 发起测评 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '发起测评', 'prison:questionnaire-record:initiate', 3, 6, id, '', '', '', 0 +FROM system_menu WHERE name = '问卷记录管理' LIMIT 1; + +-- 开始测评 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '开始测评', 'prison:questionnaire-record:start', 3, 7, id, '', '', '', 0 +FROM system_menu WHERE name = '问卷记录管理' LIMIT 1; + +-- 提交答卷 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '提交答卷', 'prison:questionnaire-record:submit', 3, 8, id, '', '', '', 0 +FROM system_menu WHERE name = '问卷记录管理' LIMIT 1; + +-- 结束测评 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '结束测评', 'prison:questionnaire-record:finish', 3, 9, id, '', '', '', 0 +FROM system_menu WHERE name = '问卷记录管理' LIMIT 1; + +-- 取消测评 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '取消测评', 'prison:questionnaire-record:cancel', 3, 10, id, '', '', '', 0 +FROM system_menu WHERE name = '问卷记录管理' LIMIT 1; + +-- 人工评分 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status) +SELECT '人工评分', 'prison:questionnaire-record:score', 3, 11, id, '', '', '', 0 +FROM system_menu WHERE name = '问卷记录管理' LIMIT 1; + +SELECT '数据库迁移完成!'; diff --git a/yudao-module-prison/src/main/resources/sql/consumption_migration_20260115.sql b/yudao-module-prison/src/main/resources/sql/consumption_migration_20260115.sql new file mode 100644 index 0000000000..92ff3a1a83 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/consumption_migration_20260115.sql @@ -0,0 +1,71 @@ +-- ===================================================== +-- 消费模块重构:主从表设计迁移脚本 +-- 执行时间:2026-01-15 +-- 数据库:xlcp_dev +-- ===================================================== + +-- 注意:执行前请备份数据库! + +-- ===================================================== +-- 1. 修改消费订单表结构(prison_consumption) +-- ===================================================== + +-- 1.1 添加 order_no 字段 +-- 如果报错"Duplicate column",说明字段已存在,可以跳过 +ALTER TABLE `prison_consumption` +ADD COLUMN `order_no` varchar(64) DEFAULT NULL COMMENT '订单号' AFTER `prisoner_no`; + +-- 1.2 修改 type 字段注释 +ALTER TABLE `prison_consumption` +MODIFY COLUMN `type` tinyint NOT NULL DEFAULT 1 COMMENT '类型:1-购物 2-餐饮 3-医疗 4-通讯 5-其他'; + +-- 1.3 重命名 amount 为 total_amount +-- 如果报错"Unknown column",说明字段已被修改,可以跳过 +ALTER TABLE `prison_consumption` +CHANGE COLUMN `amount` `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额'; + +-- 1.4 删除商品相关字段 +-- 如果报错"Unknown column",说明字段已被删除,可以跳过 +ALTER TABLE `prison_consumption` +DROP COLUMN `goods_name`; + +ALTER TABLE `prison_consumption` +DROP COLUMN `goods_count`; + +-- 1.5 添加索引 +ALTER TABLE `prison_consumption` +ADD INDEX `idx_prison_consumption_order_no` (`order_no`); + + +-- ===================================================== +-- 2. 创建消费明细表(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` tinyint(1) NOT NULL DEFAULT 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='消费明细表'; + + +-- ===================================================== +-- 3. 验证结果 +-- ===================================================== + +-- 查看表结构 +DESCRIBE prison_consumption; +DESCRIBE prison_consumption_detail; + +SELECT '消费模块表结构修改完成!' AS status; diff --git a/yudao-module-prison/src/main/resources/sql/prison_module.sql b/yudao-module-prison/src/main/resources/sql/prison_module.sql index 8f8a11a0b2..b6e515d1f6 100644 --- a/yudao-module-prison/src/main/resources/sql/prison_module.sql +++ b/yudao-module-prison/src/main/resources/sql/prison_module.sql @@ -55,18 +55,16 @@ CREATE TABLE IF NOT EXISTS `prison_cell` ( ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='监室信息表'; -- ===================================================== --- 3. 消费记录表 (prison_consumption) +-- 3. 消费订单表 (prison_consumption) -- ===================================================== CREATE TABLE IF NOT EXISTS `prison_consumption` ( - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '记录ID', + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '消费ID', `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', - `type` tinyint NOT NULL DEFAULT 1 COMMENT '类型:1-存款 2-消费 3-转账', - `amount` decimal(10,2) NOT NULL COMMENT '金额', - `balance` decimal(10,2) NOT NULL COMMENT '账户余额', - `goods_name` varchar(200) DEFAULT NULL COMMENT '商品名称', - `goods_count` int DEFAULT 0 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 '备注', @@ -79,8 +77,30 @@ CREATE TABLE IF NOT EXISTS `prison_consumption` ( 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='消费记录表'; +) 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) diff --git a/yudao-module-prison/src/main/resources/sql/risk_assessment_llm.sql b/yudao-module-prison/src/main/resources/sql/risk_assessment_llm.sql new file mode 100644 index 0000000000..c5e204a341 --- /dev/null +++ b/yudao-module-prison/src/main/resources/sql/risk_assessment_llm.sql @@ -0,0 +1,70 @@ +-- ===================================================== +-- XL监狱综合管理平台 - LLM调用日志表 +-- 生成时间: 2026-01-15 +-- ===================================================== + +-- ===================================================== +-- LLM调用日志表 (prison_risk_assessment_llm_log) +-- ===================================================== +CREATE TABLE IF NOT EXISTS `prison_risk_assessment_llm_log` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志ID', + `assessment_id` bigint DEFAULT NULL COMMENT '关联的评估记录ID', + `prisoner_id` bigint NOT NULL COMMENT '罪犯ID', + `prisoner_no` varchar(50) NOT NULL COMMENT '罪犯编号', + `model` varchar(100) NOT NULL COMMENT '使用的模型', + `prompt` text NOT NULL COMMENT '发送的提示词(脱敏后)', + `response` text NOT NULL COMMENT '模型响应原始内容', + `risk_level` tinyint DEFAULT NULL COMMENT '评估风险等级', + `confidence` decimal(4,2) DEFAULT NULL COMMENT '置信度', + `evaluation_time_ms` int DEFAULT NULL COMMENT '评估耗时(毫秒)', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-成功 2-失败 3-降级', + `error_message` text DEFAULT NULL COMMENT '错误信息', + `requires_human_review` bit(1) DEFAULT b'0 COMMENT '是否需要人工复核', + `assessor_id` bigint DEFAULT NULL COMMENT '评估人ID', + `assessor_name` varchar(100) 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_llm_log_prisoner_id` (`prisoner_id`), + KEY `idx_llm_log_assessment_id` (`assessment_id`), + KEY `idx_llm_log_create_time` (`create_time`), + KEY `idx_llm_log_status` (`status`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='LLM危险评估调用日志表'; + +-- ===================================================== +-- 菜单权限配置 +-- ===================================================== + +-- 危险评估模块菜单(已有) + +-- LLM评估功能权限 +-- 提示:以下权限需要在前端菜单配置中手动添加 +-- 路径:系统管理 -> 菜单管理 -> 监狱管理 -> 危险评估 +-- 添加子菜单:LLM智能评估 +-- 权限标识:prison:risk-assessment:llm-assess + +-- SQL示例:添加权限(如果使用代码生成器则自动生成) +-- INSERT INTO system_permission (name, permission, type, sort, path, icon, component) +-- VALUES ('LLM智能评估', 'prison:risk-assessment:llm-assess', 2, 10, 'llm-assess', 'icon', 'prison/riskassessment/llm-assess'); + +-- INSERT INTO system_menu_permission (menu_id, permission_id) +-- SELECT m.id, p.id FROM system_menu m, system_permission p +-- WHERE m.path = 'riskassessment' AND p.permission = 'prison:risk-assessment:llm-assess'; + +-- ===================================================== +-- 配置项说明 +-- ===================================================== +-- 在 application.yaml 或环境配置文件中添加LLM配置: +-- +-- # Claude API配置 +-- llm: +-- claude: +-- api-key: your-api-key-here +-- timeout-seconds: 30 +-- +-- 或使用环境变量: +-- LLM_CLAUDE_API_KEY=your-api-key-here diff --git a/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionControllerTest.java b/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionControllerTest.java new file mode 100644 index 0000000000..b916ab12f0 --- /dev/null +++ b/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/controller/admin/consumption/PrisonConsumptionControllerTest.java @@ -0,0 +1,230 @@ +package cn.iocoder.yudao.module.prison.controller.admin.consumption; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; +import cn.iocoder.yudao.module.prison.service.consumption.ConsumptionService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * 消费订单 Controller 测试类 + * + * @author xl + */ +@WebMvcTest(PrisonConsumptionController.class) +class PrisonConsumptionControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private ConsumptionService consumptionService; + + @Test + @WithMockUser + void testGetConsumptionPage_Success() throws Exception { + // 准备测试数据 + ConsumptionDO consumption = new ConsumptionDO(); + consumption.setId(1L); + consumption.setPrisonerId(100L); + consumption.setPrisonerNo("PRISONER001"); + consumption.setOrderNo("CS1234567890"); + consumption.setType(1); + consumption.setTotalAmount(new BigDecimal("100.00")); + consumption.setBalance(new BigDecimal("900.00")); + consumption.setTradeTime(LocalDateTime.now()); + consumption.setStatus(1); + + PageResult pageResult = new PageResult<>(); + pageResult.setList(Collections.singletonList(consumption)); + pageResult.setTotal(1L); + + when(consumptionService.getConsumptionPage(any(ConsumptionPageReqVO.class))).thenReturn(pageResult); + + // 执行测试 + mockMvc.perform(get("/prison/consumption/page") + .param("pageNo", "1") + .param("pageSize", "10") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.list[0].id").value(1)) + .andExpect(jsonPath("$.data.list[0].prisonerNo").value("PRISONER001")) + .andExpect(jsonPath("$.data.list[0].type").value(1)) + .andExpect(jsonPath("$.data.total").value(1)); + } + + @Test + @WithMockUser + void testGetConsumption_Success() throws Exception { + // 准备测试数据 + ConsumptionDO consumption = new ConsumptionDO(); + consumption.setId(1L); + consumption.setPrisonerId(100L); + consumption.setPrisonerNo("PRISONER001"); + consumption.setOrderNo("CS1234567890"); + consumption.setType(1); + consumption.setTotalAmount(new BigDecimal("100.00")); + consumption.setStatus(1); + + ConsumptionDetailDO detail = new ConsumptionDetailDO(); + detail.setId(1L); + detail.setConsumptionId(1L); + detail.setGoodsName("商品A"); + detail.setGoodsPrice(new BigDecimal("50.00")); + detail.setGoodsCount(2); + + when(consumptionService.getConsumption(1L)).thenReturn(consumption); + when(consumptionService.getConsumptionDetailList(1L)).thenReturn(Collections.singletonList(detail)); + + // 执行测试 + mockMvc.perform(get("/prison/consumption/get") + .param("id", "1") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.id").value(1)) + .andExpect(jsonPath("$.data.prisonerNo").value("PRISONER001")) + .andExpect(jsonPath("$.data.details[0].goodsName").value("商品A")); + } + + @Test + @WithMockUser + void testGetConsumption_NotFound() throws Exception { + when(consumptionService.getConsumption(999L)).thenReturn(null); + + mockMvc.perform(get("/prison/consumption/get") + .param("id", "999") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").isEmpty()); + } + + @Test + @WithMockUser + void testDeleteConsumption_Success() throws Exception { + // 执行测试 - 删除成功 + mockMvc.perform(delete("/prison/consumption/delete") + .param("id", "1") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + } + + @Test + @WithMockUser + void testDeleteConsumption_ValidationError() throws Exception { + // 缺少必需参数 id + mockMvc.perform(delete("/prison/consumption/delete") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); + } + + @Test + @WithMockUser + void testDeleteConsumptionList_Success() throws Exception { + // 执行测试 - 批量删除 + mockMvc.perform(delete("/prison/consumption/delete-list") + .param("ids", "1,2,3") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + } + + @Test + @WithMockUser + void testDeleteConsumptionList_ValidationError() throws Exception { + // 批量删除 ids 为空 + mockMvc.perform(delete("/prison/consumption/delete-list") + .param("ids", "") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); + } + + @Test + @WithMockUser + void testGetConsumptionDetailList_Success() throws Exception { + ConsumptionDetailDO detail = new ConsumptionDetailDO(); + detail.setId(1L); + detail.setConsumptionId(1L); + detail.setGoodsName("商品A"); + detail.setGoodsPrice(new BigDecimal("25.50")); + detail.setGoodsCount(4); + + when(consumptionService.getConsumptionDetailList(1L)) + .thenReturn(Collections.singletonList(detail)); + + mockMvc.perform(get("/prison/consumption/detail-list") + .param("consumptionId", "1") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data[0].goodsName").value("商品A")) + .andExpect(jsonPath("$.data[0].goodsPrice").value(25.5)); + } + + @Test + @WithMockUser + void testExportConsumptionExcel_Success() throws Exception { + ConsumptionDO consumption = new ConsumptionDO(); + consumption.setId(1L); + consumption.setPrisonerNo("PRISONER001"); + consumption.setType(1); + consumption.setTotalAmount(new BigDecimal("100.00")); + + PageResult pageResult = new PageResult<>(); + pageResult.setList(Collections.singletonList(consumption)); + pageResult.setTotal(1L); + + when(consumptionService.getConsumptionPage(any(ConsumptionPageReqVO.class))).thenReturn(pageResult); + + mockMvc.perform(get("/prison/consumption/export-excel") + .param("pageNo", "1") + .param("pageSize", "10") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + @Test + @WithMockUser + void testGetConsumptionPage_WithFilters() throws Exception { + // 测试带筛选条件的分页查询 + ConsumptionDO consumption = new ConsumptionDO(); + consumption.setId(1L); + consumption.setPrisonerId(100L); + consumption.setPrisonerNo("PRISONER001"); + consumption.setType(1); + consumption.setStatus(1); + + PageResult pageResult = new PageResult<>(); + pageResult.setList(Collections.singletonList(consumption)); + pageResult.setTotal(1L); + + when(consumptionService.getConsumptionPage(any(ConsumptionPageReqVO.class))).thenReturn(pageResult); + + mockMvc.perform(get("/prison/consumption/page") + .param("pageNo", "1") + .param("pageSize", "10") + .param("prisonerNo", "PRISONER001") + .param("type", "1") + .param("status", "1") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.total").value(1)); + } +} diff --git a/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceTest.java b/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceTest.java new file mode 100644 index 0000000000..e23c128049 --- /dev/null +++ b/yudao-module-prison/src/test/java/cn/iocoder/yudao/module/prison/service/consumption/ConsumptionServiceTest.java @@ -0,0 +1,283 @@ +package cn.iocoder.yudao.module.prison.service.consumption; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.prison.controller.admin.consumption.vo.*; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDetailDO; +import cn.iocoder.yudao.module.prison.dal.dataobject.consumption.ConsumptionDO; +import cn.iocoder.yudao.module.prison.dal.mysql.consumption.ConsumptionDetailMapper; +import cn.iocoder.yudao.module.prison.dal.mysql.consumption.ConsumptionMapper; +import cn.iocoder.yudao.module.prison.service.consumption.impl.ConsumptionServiceImpl; +import cn.iocoder.yudao.module.prison.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.prison.enums.ConsumptionTypeEnum; +import cn.iocoder.yudao.module.prison.enums.ConsumptionStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +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 java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * 消费订单 Service 测试类 + * + * @author xl + */ +@ExtendWith(MockitoExtension.class) +class ConsumptionServiceTest { + + @InjectMocks + private ConsumptionServiceImpl consumptionService; + + @Mock + private ConsumptionMapper consumptionMapper; + + @Mock + private ConsumptionDetailMapper consumptionDetailMapper; + + private ConsumptionSaveReqVO createReqVO; + private ConsumptionDO consumptionDO; + + @BeforeEach + void setUp() { + // 准备创建请求VO + createReqVO = new ConsumptionSaveReqVO(); + createReqVO.setPrisonerId(100L); + createReqVO.setPrisonerNo("PRISONER001"); + createReqVO.setType(1); + createReqVO.setTotalAmount(new BigDecimal("100.00")); + createReqVO.setBalance(new BigDecimal("900.00")); + createReqVO.setStatus(1); + createReqVO.setTradeTime(LocalDateTime.now()); + + // 准备明细数据 + ConsumptionDetailSaveReqVO detail1 = new ConsumptionDetailSaveReqVO(); + detail1.setGoodsName("商品A"); + detail1.setGoodsPrice(new BigDecimal("30.00")); + detail1.setGoodsCount(2); + detail1.setSubtotal(new BigDecimal("60.00")); + + ConsumptionDetailSaveReqVO detail2 = new ConsumptionDetailSaveReqVO(); + detail2.setGoodsName("商品B"); + detail2.setGoodsPrice(new BigDecimal("20.00")); + detail2.setGoodsCount(2); + detail2.setSubtotal(new BigDecimal("40.00")); + + createReqVO.setDetails(Arrays.asList(detail1, detail2)); + + // 准备DO对象 + consumptionDO = new ConsumptionDO(); + consumptionDO.setId(1L); + consumptionDO.setPrisonerId(100L); + consumptionDO.setPrisonerNo("PRISONER001"); + consumptionDO.setOrderNo("CS1234567890"); + consumptionDO.setType(1); + consumptionDO.setTotalAmount(new BigDecimal("100.00")); + consumptionDO.setStatus(1); + consumptionDO.setCreateTime(LocalDateTime.now()); + consumptionDO.setUpdateTime(LocalDateTime.now()); + } + + @Test + void testCreateConsumption_Success() { + // 设置Mapper行为 + when(consumptionMapper.insert(any(ConsumptionDO.class))).thenReturn(1); + doNothing().when(consumptionDetailMapper).insertBatch(anyList()); + + // 执行测试 + Long result = consumptionService.createConsumption(createReqVO); + + // 验证结果 + assertNotNull(result); + assertEquals(1L, result); + verify(consumptionMapper).insert(any(ConsumptionDO.class)); + verify(consumptionDetailMapper).insertBatch(anyList()); + } + + @Test + void testCreateConsumption_DetailEmpty() { + // 明细为空 + createReqVO.setDetails(null); + + // 执行测试并验证异常 + assertThrows(ServiceException.class, () -> { + consumptionService.createConsumption(createReqVO); + }); + + verify(consumptionMapper, never()).insert(any()); + } + + @Test + void testUpdateConsumption_Success() { + // 准备更新数据 + ConsumptionSaveReqVO updateReqVO = new ConsumptionSaveReqVO(); + updateReqVO.setId(1L); + updateReqVO.setPrisonerId(100L); + updateReqVO.setType(2); + updateReqVO.setTotalAmount(new BigDecimal("150.00")); + + ConsumptionDetailSaveReqVO detail = new ConsumptionDetailSaveReqVO(); + detail.setGoodsName("商品C"); + detail.setGoodsPrice(new BigDecimal("75.00")); + detail.setGoodsCount(2); + detail.setSubtotal(new BigDecimal("150.00")); + updateReqVO.setDetails(Collections.singletonList(detail)); + + // 设置Mapper行为 + when(consumptionMapper.selectById(1L)).thenReturn(consumptionDO); + when(consumptionMapper.updateById(any(ConsumptionDO.class))).thenReturn(1); + doNothing().when(consumptionDetailMapper).deleteByConsumptionId(1L); + doNothing().when(consumptionDetailMapper).insertBatch(anyList()); + + // 执行测试 + consumptionService.updateConsumption(updateReqVO); + + // 验证结果 + verify(consumptionMapper).selectById(1L); + verify(consumptionMapper).updateById(any(ConsumptionDO.class)); + verify(consumptionDetailMapper).deleteByConsumptionId(1L); + verify(consumptionDetailMapper).insertBatch(anyList()); + } + + @Test + void testUpdateConsumption_NotFound() { + ConsumptionSaveReqVO updateReqVO = new ConsumptionSaveReqVO(); + updateReqVO.setId(999L); + + when(consumptionMapper.selectById(999L)).thenReturn(null); + + assertThrows(ServiceException.class, () -> { + consumptionService.updateConsumption(updateReqVO); + }); + + verify(consumptionMapper, never()).updateById(any()); + } + + @Test + void testDeleteConsumption_Success() { + when(consumptionMapper.selectById(1L)).thenReturn(consumptionDO); + when(consumptionMapper.deleteById(1L)).thenReturn(1); + doNothing().when(consumptionDetailMapper).deleteByConsumptionId(1L); + + consumptionService.deleteConsumption(1L); + + verify(consumptionMapper).deleteById(1L); + verify(consumptionDetailMapper).deleteByConsumptionId(1L); + } + + @Test + void testDeleteConsumption_NotFound() { + when(consumptionMapper.selectById(999L)).thenReturn(null); + + assertThrows(ServiceException.class, () -> { + consumptionService.deleteConsumption(999L); + }); + + verify(consumptionMapper, never()).deleteById(any()); + } + + @Test + void testDeleteConsumptionListByIds_Success() { + List ids = Arrays.asList(1L, 2L, 3L); + when(consumptionMapper.deleteBatchIds(ids)).thenReturn(3); + doNothing().when(consumptionDetailMapper).deleteByConsumptionIds(ids); + + consumptionService.deleteConsumptionListByIds(ids); + + verify(consumptionMapper).deleteBatchIds(ids); + verify(consumptionDetailMapper).deleteByConsumptionIds(ids); + } + + @Test + void testGetConsumption_Success() { + when(consumptionMapper.selectById(1L)).thenReturn(consumptionDO); + + ConsumptionDO result = consumptionService.getConsumption(1L); + + assertNotNull(result); + assertEquals(1L, result.getId()); + assertEquals("PRISONER001", result.getPrisonerNo()); + } + + @Test + void testGetConsumption_NotFound() { + when(consumptionMapper.selectById(999L)).thenReturn(null); + + ConsumptionDO result = consumptionService.getConsumption(999L); + + assertNull(result); + } + + @Test + void testGetConsumptionPage_Success() { + ConsumptionPageReqVO pageReqVO = new ConsumptionPageReqVO(); + pageReqVO.setPageNo(1); + pageReqVO.setPageSize(10); + + Page page = new Page<>(1, 10); + page.setRecords(Collections.singletonList(consumptionDO)); + page.setTotal(1); + + when(consumptionMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))) + .thenReturn(page); + + cn.iocoder.yudao.framework.common.pojo.PageResult result = + consumptionService.getConsumptionPage(pageReqVO); + + assertNotNull(result); + assertEquals(1, result.getTotal()); + assertEquals(1, result.getList().size()); + assertEquals("PRISONER001", result.getList().get(0).getPrisonerNo()); + } + + @Test + void testGetConsumptionDetailList_Success() { + ConsumptionDetailDO detail = new ConsumptionDetailDO(); + detail.setId(1L); + detail.setConsumptionId(1L); + detail.setGoodsName("商品A"); + detail.setGoodsPrice(new BigDecimal("50.00")); + detail.setGoodsCount(2); + + when(consumptionDetailMapper.selectListByConsumptionId(1L)) + .thenReturn(Collections.singletonList(detail)); + + List result = consumptionService.getConsumptionDetailList(1L); + + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals("商品A", result.get(0).getGoodsName()); + } + + @Test + void testExportExcel_Success() { + ConsumptionPageReqVO pageReqVO = new ConsumptionPageReqVO(); + pageReqVO.setPageNo(1); + pageReqVO.setPageSize(10); + + Page page = new Page<>(1, 10); + page.setRecords(Collections.singletonList(consumptionDO)); + page.setTotal(1); + + when(consumptionMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))) + .thenReturn(page); + + cn.iocoder.yudao.framework.common.pojo.PageResult result = + consumptionService.getConsumptionPage(pageReqVO); + + assertNotNull(result); + assertEquals(1, result.getTotal()); + } +} diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 2223b3c6aa..2ff9241ce8 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -274,4 +274,25 @@ justauth: cache: type: REDIS prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: - timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 \ No newline at end of file + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 + +--- #################### LLM(危险评估大模型)配置 #################### + +# OneAPI统一接口配置(用于危险评估智能分析) +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:deepseek-ai/deepseek-v3.2} # 使用的模型 + timeout-seconds: ${LLM_TIMEOUT:120} # 请求超时时间 + # Claude(可选,需要时取消注释) + # claude: + # api-key: ${CLAUDE_API_KEY:} + # timeout-seconds: 30 + # model: claude-3-5-sonnet-20241022 + # OpenAI GPT-4(可选) + # openai: + # api-key: ${OPENAI_API_KEY:} + # model: gpt-4o + # timeout-seconds: 60 \ No newline at end of file