fix: 代码审查修复 - 调查问卷模块优化

后端修复:
- QuestionnaireRecordServiceImpl: 修复缩进问题,添加空值校验和ID存在性校验
- QuestionnaireSaveReqVO/RespVO/PageReqVO: 同步DO新增的5个字段
- QuestionnaireMapper: 添加新字段查询条件
- QuestionServiceImpl: 优化批量更新性能,修复缩进问题
- 新增枚举类: QuestionnaireStatusEnum, QuestionnaireRecordStatusEnum, QuestionnaireRecordPassStatusEnum
- 新增数据库迁移脚本: question_add_columns.sql, questionnaire_add_columns.sql

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tangweijie 2026-01-15 20:14:32 +08:00
parent 5f9bcfc9b2
commit f7f318bed8
18 changed files with 320 additions and 48 deletions

View File

@ -57,7 +57,7 @@ public class PrisonAreaController {
@Operation(summary = "删除监区信息")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('prison:area:delete')")
public CommonResult<Boolean> deleteArea(@RequestParam("id") Long id) {
public CommonResult<Boolean> deleteArea(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) {
areaService.deleteArea(id);
return success(true);
}
@ -65,8 +65,8 @@ public class PrisonAreaController {
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除监区信息")
@PreAuthorize("@ss.hasPermission('prison:area:delete')")
public CommonResult<Boolean> deleteAreaList(@RequestParam("ids") List<Long> ids) {
@PreAuthorize("@ss.hasPermission('prison:area:delete')")
public CommonResult<Boolean> deleteAreaList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
areaService.deleteAreaListByIds(ids);
return success(true);
}
@ -97,7 +97,7 @@ public class PrisonAreaController {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<AreaDO> list = areaService.getAreaPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "监区信息.xls", "数据", AreaRespVO.class,
ExcelUtils.write(response, "监区信息.xlsx", "数据", AreaRespVO.class,
BeanUtils.toBean(list, AreaRespVO.class));
}

View File

@ -13,28 +13,28 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
@Data
public class AreaPageReqVO extends PageParam {
@Schema(description = "监区名称", example = "李四")
@Schema(description = "监区名称", example = "第一监区")
private String name;
@Schema(description = "监区编码")
private String code;
@Schema(description = "监区类型1-普通监区 2-严管监区 3-医院 4-禁闭室", example = "1")
@Schema(description = "监区类型1-普通监区 2-严管监区 3-集训监区 4-出监监区 5-医院 6-禁闭室", example = "1")
private Integer type;
@Schema(description = "容纳人数")
private Integer capacity;
@Schema(description = "当前人数", example = "26596")
@Schema(description = "当前人数", example = "100")
private Integer currentCount;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "状态1-启用 2-禁用", example = "2")
@Schema(description = "状态1-启用 2-禁用", example = "1")
private Integer status;
@Schema(description = "备注", example = "你猜")
@Schema(description = "备注", example = "正常运行的监区")
private String remark;
@Schema(description = "创建时间")

View File

@ -6,6 +6,7 @@ import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.idev.excel.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Schema(description = "管理后台 - 监区信息 Response VO")
@Data
@ -16,7 +17,7 @@ public class AreaRespVO {
@ExcelProperty("监区ID")
private Long id;
@Schema(description = "监区名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@Schema(description = "监区名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "第一监区")
@ExcelProperty("监区名称")
private String name;
@ -40,7 +41,7 @@ public class AreaRespVO {
@ExcelProperty("容纳人数")
private Integer capacity;
@Schema(description = "当前人数", example = "26596")
@Schema(description = "当前人数", example = "100")
@ExcelProperty("当前人数")
private Integer currentCount;
@ -48,11 +49,11 @@ public class AreaRespVO {
@ExcelProperty("排序")
private Integer sort;
@Schema(description = "状态1-启用 2-禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@Schema(description = "状态1-启用 2-禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("状态1-启用 2-禁用")
private Integer status;
@Schema(description = "备注", example = "你猜")
@Schema(description = "备注", example = "正常运行的监区")
@ExcelProperty("备注")
private String remark;
@ -61,6 +62,7 @@ public class AreaRespVO {
private LocalDateTime createTime;
@Schema(description = "子监区列表,仅一级监区返回")
@JsonIgnore
private List<AreaRespVO> children;
}

View File

@ -12,7 +12,7 @@ public class AreaSaveReqVO {
@Schema(description = "监区ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "19443")
private Long id;
@Schema(description = "监区名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@Schema(description = "监区名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "第一监区")
@NotEmpty(message = "监区名称不能为空")
private String name;
@ -24,25 +24,30 @@ public class AreaSaveReqVO {
private Long parentId;
@Schema(description = "级别1-监区(大队) 2-分监区(中队)")
@NotNull(message = "级别不能为空")
private Integer level;
@Schema(description = "监区类型1-普通监区 2-严管监区 3-集训监区 4-出监监区 5-医院 6-禁闭室", example = "1")
@NotNull(message = "监区类型不能为空")
private Integer type;
@Schema(description = "容纳人数")
@Min(value = 0, message = "容纳人数不能为负数")
private Integer capacity;
@Schema(description = "当前人数", example = "26596")
@Schema(description = "当前人数", example = "100")
@Min(value = 0, message = "当前人数不能为负数")
private Integer currentCount;
@Schema(description = "排序")
@Min(value = 0, message = "排序不能为负数")
private Integer sort;
@Schema(description = "状态1-启用 2-禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@Schema(description = "状态1-启用 2-禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态1-启用 2-禁用不能为空")
private Integer status;
@Schema(description = "备注", example = "你猜")
@Schema(description = "备注", example = "正常运行的监区")
private String remark;
}

View File

@ -61,7 +61,7 @@ public class PrisonCellController {
@Operation(summary = "删除监室信息")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('prison:cell:delete')")
public CommonResult<Boolean> deleteCell(@RequestParam("id") Long id) {
public CommonResult<Boolean> deleteCell(@NotNull(message = "编号不能为空") @RequestParam("id") Long id) {
cellService.deleteCell(id);
return success(true);
}
@ -69,8 +69,8 @@ public class PrisonCellController {
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除监室信息")
@PreAuthorize("@ss.hasPermission('prison:cell:delete')")
public CommonResult<Boolean> deleteCellList(@RequestParam("ids") List<Long> ids) {
@PreAuthorize("@ss.hasPermission('prison:cell:delete')")
public CommonResult<Boolean> deleteCellList(@NotEmpty(message = "编号列表不能为空") @RequestParam("ids") List<Long> ids) {
cellService.deleteCellListByIds(ids);
return success(true);
}
@ -111,7 +111,7 @@ public class PrisonCellController {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CellRespVO> list = cellService.getCellPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "监室信息.xls", "数据", CellRespVO.class, list);
ExcelUtils.write(response, "监室信息.xlsx", "数据", CellRespVO.class, list);
}
}
}

View File

@ -16,7 +16,7 @@ public class CellPageReqVO extends PageParam {
@Schema(description = "所属监区ID", example = "9889")
private Long areaId;
@Schema(description = "监室名称", example = "张三")
@Schema(description = "监室名称", example = "101监室")
private String name;
@Schema(description = "监室编码")
@ -25,7 +25,7 @@ public class CellPageReqVO extends PageParam {
@Schema(description = "床位数量")
private Integer capacity;
@Schema(description = "当前人数", example = "31423")
@Schema(description = "当前人数", example = "5")
private Integer currentCount;
@Schema(description = "排序")
@ -34,11 +34,11 @@ public class CellPageReqVO extends PageParam {
@Schema(description = "状态1-启用 2-禁用", example = "1")
private Integer status;
@Schema(description = "备注", example = "随便")
@Schema(description = "备注", example = "正常使用的监室")
private String remark;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
}

View File

@ -16,7 +16,7 @@ public class CellSaveReqVO {
@NotNull(message = "所属监区ID不能为空")
private Long areaId;
@Schema(description = "监室名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
@Schema(description = "监室名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "101监室")
@NotEmpty(message = "监室名称不能为空")
private String name;
@ -24,20 +24,25 @@ public class CellSaveReqVO {
@NotEmpty(message = "监室编码不能为空")
private String code;
@Schema(description = "床位数量")
@Schema(description = "床位数量", example = "10")
@Min(value = 0, message = "床位数量不能小于0")
private Integer capacity;
@Schema(description = "当前人数", example = "31423")
@Schema(description = "当前人数", example = "5")
@Min(value = 0, message = "当前人数不能小于0")
private Integer currentCount;
@Schema(description = "排序")
@Schema(description = "排序", example = "1")
@Min(value = 0, message = "排序不能小于0")
private Integer sort;
@Schema(description = "状态1-启用 2-禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态1-启用 2-禁用不能为空")
@Min(value = 1, message = "状态最小值为1")
@Max(value = 2, message = "状态最大值为2")
private Integer status;
@Schema(description = "备注", example = "随便")
@Schema(description = "备注", example = "正常使用的监室")
private String remark;
}
}

View File

@ -36,4 +36,21 @@ public class QuestionnairePageReqVO extends PageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
// ==================== 新增字段 ====================
@Schema(description = "封面图片URL")
private String coverImage;
@Schema(description = "填写说明")
private String instruction;
@Schema(description = "预计耗时(分钟)")
private Integer estimatedTime;
@Schema(description = "分区数量")
private Integer partCount;
@Schema(description = "是否允许匿名")
private Boolean allowAnonymous;
}

View File

@ -45,4 +45,26 @@ public class QuestionnaireRespVO {
@ExcelProperty("创建时间")
private LocalDateTime createTime;
// ==================== 新增字段 ====================
@Schema(description = "封面图片URL")
@ExcelProperty("封面图片URL")
private String coverImage;
@Schema(description = "填写说明")
@ExcelProperty("填写说明")
private String instruction;
@Schema(description = "预计耗时(分钟)")
@ExcelProperty("预计耗时(分钟)")
private Integer estimatedTime;
@Schema(description = "分区数量")
@ExcelProperty("分区数量")
private Integer partCount;
@Schema(description = "是否允许匿名false-不允许 true-允许")
@ExcelProperty("是否允许匿名")
private Boolean allowAnonymous;
}

View File

@ -31,7 +31,24 @@ public class QuestionnaireSaveReqVO {
private BigDecimal passScore;
@Schema(description = "状态1-草稿 2-已发布 3-已禁用", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "状态1-草稿 2-已发布 3-已禁用不能为空")
@NotNull(message = "状态不能为空")
private Integer status;
// ==================== 新增字段 ====================
@Schema(description = "封面图片URL")
private String coverImage;
@Schema(description = "填写说明")
private String instruction;
@Schema(description = "预计耗时(分钟)")
private Integer estimatedTime;
@Schema(description = "分区数量")
private Integer partCount;
@Schema(description = "是否允许匿名false-不允许 true-允许")
private Boolean allowAnonymous;
}

View File

@ -25,6 +25,11 @@ public interface QuestionnaireMapper extends BaseMapperX<QuestionnaireDO> {
.eqIfPresent(QuestionnaireDO::getTotalScore, reqVO.getTotalScore())
.eqIfPresent(QuestionnaireDO::getPassScore, reqVO.getPassScore())
.eqIfPresent(QuestionnaireDO::getStatus, reqVO.getStatus())
.eqIfPresent(QuestionnaireDO::getCoverImage, reqVO.getCoverImage())
.eqIfPresent(QuestionnaireDO::getInstruction, reqVO.getInstruction())
.eqIfPresent(QuestionnaireDO::getEstimatedTime, reqVO.getEstimatedTime())
.eqIfPresent(QuestionnaireDO::getPartCount, reqVO.getPartCount())
.eqIfPresent(QuestionnaireDO::getAllowAnonymous, reqVO.getAllowAnonymous())
.betweenIfPresent(QuestionnaireDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(QuestionnaireDO::getId));
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.prison.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 问卷答题记录及格状态枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum QuestionnaireRecordPassStatusEnum {
NOT_PASSED(0, "未及格"),
PASSED(1, "及格");
/**
* 状态
*/
private final Integer status;
/**
* 名称
*/
private final String name;
public static boolean isValid(Integer status) {
if (status == null) {
return false;
}
for (QuestionnaireRecordPassStatusEnum value : values()) {
if (value.getStatus().equals(status)) {
return true;
}
}
return false;
}
public static QuestionnaireRecordPassStatusEnum getByStatus(Integer status) {
for (QuestionnaireRecordPassStatusEnum value : values()) {
if (value.getStatus().equals(status)) {
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.prison.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 问卷答题记录状态枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum QuestionnaireRecordStatusEnum {
PENDING(1, "待评估"),
COMPLETED(2, "已完成");
/**
* 状态
*/
private final Integer status;
/**
* 名称
*/
private final String name;
public static boolean isValid(Integer status) {
if (status == null) {
return false;
}
for (QuestionnaireRecordStatusEnum value : values()) {
if (value.getStatus().equals(status)) {
return true;
}
}
return false;
}
public static QuestionnaireRecordStatusEnum getByStatus(Integer status) {
for (QuestionnaireRecordStatusEnum value : values()) {
if (value.getStatus().equals(status)) {
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.prison.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 问卷状态枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum QuestionnaireStatusEnum {
DRAFT(1, "草稿"),
PUBLISHED(2, "已发布"),
DISABLED(3, "已禁用");
/**
* 状态
*/
private final Integer status;
/**
* 名称
*/
private final String name;
public static boolean isValid(Integer status) {
if (status == null) {
return false;
}
for (QuestionnaireStatusEnum value : values()) {
if (value.getStatus().equals(status)) {
return true;
}
}
return false;
}
public static QuestionnaireStatusEnum getByStatus(Integer status) {
for (QuestionnaireStatusEnum value : values()) {
if (value.getStatus().equals(status)) {
return value;
}
}
return null;
}
}

View File

@ -60,10 +60,19 @@ public class QuestionServiceImpl implements QuestionService {
}
@Override
public void deleteQuestionListByIds(List<Long> ids) {
public void deleteQuestionListByIds(List<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 校验所有ID都存在
Long count = questionMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<QuestionDO>()
.in(QuestionDO::getId, ids));
if (count == null || count != ids.size()) {
throw exception(QUESTION_NOT_EXISTS);
}
// 删除
questionMapper.deleteByIds(ids);
}
}
private void validateQuestionExists(Long id) {
@ -88,18 +97,23 @@ public class QuestionServiceImpl implements QuestionService {
if (CollUtil.isEmpty(updateList)) {
return;
}
// 批量更新
for (QuestionSaveReqVO updateReqVO : updateList) {
// 校验存在
validateQuestionExists(updateReqVO.getId());
// 更新仅更新排序和分区相关字段
QuestionDO updateObj = new QuestionDO();
updateObj.setId(updateReqVO.getId());
updateObj.setPartName(updateReqVO.getPartName());
updateObj.setPartSort(updateReqVO.getPartSort());
updateObj.setSort(updateReqVO.getSort());
questionMapper.updateById(updateObj);
// 校验所有ID存在性
List<Long> ids = convertList(updateList, QuestionSaveReqVO::getId);
Long count = questionMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<QuestionDO>()
.in(QuestionDO::getId, ids));
if (count == null || count != ids.size()) {
throw exception(QUESTION_NOT_EXISTS);
}
// 批量更新 - 使用 updateBatchById 提升性能
List<QuestionDO> updateObjs = convertList(updateList, vo -> {
QuestionDO obj = new QuestionDO();
obj.setId(vo.getId());
obj.setPartName(vo.getPartName());
obj.setPartSort(vo.getPartSort());
obj.setSort(vo.getSort());
return obj;
});
this.updateBatchById(updateObjs);
}
}

View File

@ -60,10 +60,19 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic
}
@Override
public void deleteQuestionnaireRecordListByIds(List<Long> ids) {
public void deleteQuestionnaireRecordListByIds(List<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 校验所有ID都存在
Long count = questionnaireRecordMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<QuestionnaireRecordDO>()
.in(QuestionnaireRecordDO::getId, ids));
if (count == null || count != ids.size()) {
throw exception(QUESTIONNAIRE_RECORD_NOT_EXISTS);
}
// 删除
questionnaireRecordMapper.deleteByIds(ids);
}
}
private void validateQuestionnaireRecordExists(Long id) {

View File

@ -0,0 +1,18 @@
-- 问题表新增字段
-- 执行前请确保已备份数据库
-- 连接数据库: mysql -h 192.168.10.130 -u xlcp_dev -p xlcp_dev
ALTER TABLE prison_question
ADD COLUMN part_name VARCHAR(100) DEFAULT NULL COMMENT '分区名称' AFTER `sort`,
ADD COLUMN part_sort INT DEFAULT NULL COMMENT '分区排序' AFTER `part_name`,
ADD COLUMN help_text VARCHAR(500) DEFAULT NULL COMMENT '帮助说明' AFTER `part_sort`,
ADD COLUMN placeholder VARCHAR(200) DEFAULT NULL COMMENT '占位提示' AFTER `help_text`,
ADD COLUMN default_value VARCHAR(500) DEFAULT NULL COMMENT '默认值' AFTER `placeholder`,
ADD COLUMN auto_fill_type VARCHAR(20) DEFAULT NULL COMMENT '自动填充类型NONE-无 AUTO-自动 MANUAL-手动' AFTER `default_value`,
ADD COLUMN auto_fill_source VARCHAR(500) DEFAULT NULL COMMENT '自动填充来源' AFTER `auto_fill_type`,
ADD COLUMN display_condition VARCHAR(1000) DEFAULT NULL COMMENT '显示条件JSON' AFTER `auto_fill_source`,
ADD COLUMN min_value DECIMAL(10,2) DEFAULT NULL COMMENT '最小值' AFTER `display_condition`,
ADD COLUMN max_value DECIMAL(10,2) DEFAULT NULL COMMENT '最大值' AFTER `min_value`;
-- 验证字段是否添加成功
DESCRIBE prison_question;

View File

@ -0,0 +1,13 @@
-- 问卷模板表新增字段
-- 执行前请确保已备份数据库
-- 连接数据库: mysql -h 192.168.10.130 -u xlcp_dev -p xlcp_dev
ALTER TABLE prison_questionnaire
ADD COLUMN cover_image VARCHAR(500) DEFAULT NULL COMMENT '封面图片URL' AFTER `status`,
ADD COLUMN instruction VARCHAR(500) DEFAULT NULL COMMENT '填写说明' AFTER `cover_image`,
ADD COLUMN estimated_time INT DEFAULT NULL COMMENT '预计耗时(分钟)' AFTER `instruction`,
ADD COLUMN part_count INT DEFAULT NULL COMMENT '分区数量' AFTER `estimated_time`,
ADD COLUMN allow_anonymous TINYINT(1) DEFAULT NULL COMMENT '是否允许匿名0-否 1-是' AFTER `part_count`;
-- 验证字段是否添加成功
SELECT * FROM prison_questionnaire LIMIT 1;