fix(project): 修复项目执行管理中的多项问题并优化字典数据功能

- 修复字典数据分页接口命名错误,从 getDictTypePage 改为 getDictDataPage
- 修复字典数据查询排序逻辑,从 orderByDesc 改为 orderByAsc 并增加 id 排序
- 更新字典数据分页请求参数验证,将 dictType 设为必填项并添加非空验证
- 在字典数据简单响应对象中添加备注字段
- 修复项目执行删除权限验证,允许非初始态执行删除但阻止已完成执行删除
- 添加项目执行和任务优先级验证错误码常量
- 优化项目执行删除逻辑,支持级联软删相关任务、工作日志和协办数据
- 添加项目需求关联验证,防止无效需求关联到执行
- 修复执行协办数据批量删除方法的参数验证逻辑
- 添加工时完成难度验证错误码,完善项目需求删除前检查机制
- 更新 CLAUDE.md 文档,补充种子 SQL 编写规范和雪花 ID 处理说明
This commit is contained in:
2026-05-21 21:17:54 +08:00
parent 19637d74a4
commit 1bee5eb05b
45 changed files with 1439 additions and 76 deletions

View File

@@ -103,9 +103,9 @@ public class DictDataController {
}
@GetMapping("/page")
@Operation(summary = "获得字典类型的分页")
@Operation(summary = "获得字典数据的分页")
@PreAuthorize("@ss.hasPermission('system:dict:query')")
public CommonResult<PageResult<DictDataRespVO>> getDictTypePage(@Valid DictDataPageReqVO pageReqVO) {
public CommonResult<PageResult<DictDataRespVO>> getDictDataPage(@Valid DictDataPageReqVO pageReqVO) {
PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, DictDataRespVO.class));
}

View File

@@ -4,11 +4,12 @@ import com.njcn.rdms.framework.common.enums.CommonStatusEnum;
import com.njcn.rdms.framework.common.pojo.PageParam;
import com.njcn.rdms.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 字典类型分页列表 Request VO")
@Schema(description = "管理后台 - 字典数据分页列表 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class DictDataPageReqVO extends PageParam {
@@ -17,8 +18,9 @@ public class DictDataPageReqVO extends PageParam {
@Size(max = 100, message = "字典标签长度不能超过100个字符")
private String label;
@Schema(description = "字典类型,模糊匹配", example = "sys_common_sex")
@Size(max = 100, message = "字典类型类型长度不能超过100个字符")
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
@NotBlank(message = "字典类型不能为空")
@Size(max = 100, message = "字典类型长度不能超过100个字符")
private String dictType;
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")

View File

@@ -28,4 +28,7 @@ public class DictDataSimpleRespVO {
@Schema(description = "css 样式", example = "btn-visible")
private String cssClass;
@Schema(description = "备注", example = "仅内部使用")
private String remark;
}

View File

@@ -8,7 +8,6 @@ import com.njcn.rdms.module.system.dal.dataobject.dict.DictDataDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -37,7 +36,8 @@ public interface DictDataMapper extends BaseMapperX<DictDataDO> {
.likeIfPresent(DictDataDO::getLabel, reqVO.getLabel())
.eqIfPresent(DictDataDO::getDictType, reqVO.getDictType())
.eqIfPresent(DictDataDO::getStatus, reqVO.getStatus())
.orderByDesc(Arrays.asList(DictDataDO::getDictType, DictDataDO::getSort)));
.orderByAsc(DictDataDO::getSort)
.orderByAsc(DictDataDO::getId));
}
default List<DictDataDO> selectListByStatusAndDictType(Integer status, String dictType) {

View File

@@ -0,0 +1,61 @@
package com.njcn.rdms.module.system.dal.mysql.dict;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
import com.njcn.rdms.module.system.dal.dataobject.dict.DictDataDO;
import jakarta.validation.constraints.NotBlank;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.lang.reflect.Field;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
class DictDataMapperTest {
@BeforeAll
static void initMyBatisPlusTableInfo() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), DictDataDO.class);
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
void selectPage_shouldOrderBySortAscForSingleDictType() {
DictDataMapper mapper = mock(DictDataMapper.class, invocation -> invocation.callRealMethod());
DictDataPageReqVO reqVO = new DictDataPageReqVO();
reqVO.setDictType("sys_common_status");
doReturn(PageResult.empty()).when(mapper).selectPage(eq(reqVO), any(Wrapper.class));
mapper.selectPage(reqVO);
ArgumentCaptor<Wrapper<DictDataDO>> wrapperCaptor = ArgumentCaptor.forClass(Wrapper.class);
verify(mapper).selectPage(eq(reqVO), wrapperCaptor.capture());
String sqlSegment = wrapperCaptor.getValue().getSqlSegment().toLowerCase(Locale.ROOT);
assertTrue(sqlSegment.contains("dict_type"));
assertTrue(sqlSegment.contains("order by sort asc,id asc"));
assertFalse(sqlSegment.contains("dict_type desc"));
assertFalse(sqlSegment.contains("sort desc"));
}
@Test
void pageReqVO_shouldRequireDictType() throws NoSuchFieldException {
Field dictTypeField = DictDataPageReqVO.class.getDeclaredField("dictType");
NotBlank notBlank = dictTypeField.getAnnotation(NotBlank.class);
assertNotNull(notBlank);
assertEquals("字典类型不能为空", notBlank.message());
}
}