From 75886d7af545a4ff084cc4b2d67a267a8a1b14b4 Mon Sep 17 00:00:00 2001 From: dk <1260500659@qq.com> Date: Mon, 18 May 2026 16:44:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E4=BA=A7=E5=93=81=E9=9C=80=E6=B1=82?= =?UTF-8?q?=E3=80=81=E9=A1=B9=E7=9B=AE=E9=9C=80=E6=B1=82):=20=E6=8C=89?= =?UTF-8?q?=E7=85=A7=E4=BC=9A=E8=AE=AE=E6=89=80=E8=AF=B4=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../biz/system/dict/dto/DictDataRespDTO.java | 3 + .../project/enums/ErrorCodeConstants.java | 6 +- .../product/ProductRequirementController.java | 14 ++ ...quirementAllowedTransitionBatchRespVO.java | 18 ++ .../ProductRequirementBatchReqVO.java | 22 +++ ...equirementDispatchedProjectLinkRespVO.java | 2 +- ...ctRequirementHasDispatchedBatchRespVO.java | 15 ++ .../requirement/ProductRequirementRespVO.java | 5 +- .../ProductRequirementSaveReqVO.java | 6 +- .../ProductRequirementSplitReqVO.java | 6 +- .../ProductRequirementStatusActionReqVO.java | 2 +- .../ProductRequirementUpdateReqVO.java | 6 +- .../project/ProjectRequirementController.java | 9 + ...quirementAllowedTransitionBatchRespVO.java | 18 ++ .../ProjectRequirementBatchReqVO.java | 22 +++ .../requirement/ProjectRequirementRespVO.java | 5 +- .../ProjectRequirementSaveReqVO.java | 6 +- .../ProjectRequirementSplitReqVO.java | 6 +- .../ProjectRequirementUpdateReqVO.java | 6 +- .../product/ProductRequirementDO.java | 7 +- .../project/ProjectRequirementDO.java | 7 +- .../product/ProductRequirementMapper.java | 2 +- .../status/ObjectStatusTransitionMapper.java | 7 + .../product/ProductRequirementService.java | 18 +- .../ProductRequirementServiceImpl.java | 173 +++++++++++++++--- .../project/ProjectRequirementService.java | 7 + .../ProjectRequirementServiceImpl.java | 127 ++++++++++--- .../service/project/ProjectServiceImpl.java | 3 + .../system/api/user/dto/AdminUserRespDTO.java | 3 + .../admin/dict/vo/data/DictDataRespVO.java | 4 + .../admin/dict/vo/data/DictDataSaveReqVO.java | 4 + .../dict/vo/data/DictDataSimpleRespVO.java | 3 + .../controller/admin/user/UserController.java | 4 +- .../admin/user/vo/user/UserImportExcelVO.java | 3 + .../admin/user/vo/user/UserRespVO.java | 4 + .../admin/user/vo/user/UserSaveReqVO.java | 5 + .../admin/user/vo/user/UserSimpleRespVO.java | 4 + .../app/dict/vo/AppDictDataRespVO.java | 3 + .../dal/dataobject/dict/DictDataDO.java | 5 + .../dal/dataobject/user/AdminUserDO.java | 5 + .../dal/mysql/user/AdminUserMapper.java | 21 ++- .../service/user/AdminUserServiceImpl.java | 2 + 42 files changed, 506 insertions(+), 92 deletions(-) create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementAllowedTransitionBatchRespVO.java create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementBatchReqVO.java create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementHasDispatchedBatchRespVO.java create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementAllowedTransitionBatchRespVO.java create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementBatchReqVO.java diff --git a/rdms-framework/rdms-common/src/main/java/com/njcn/rdms/framework/common/biz/system/dict/dto/DictDataRespDTO.java b/rdms-framework/rdms-common/src/main/java/com/njcn/rdms/framework/common/biz/system/dict/dto/DictDataRespDTO.java index 179dcfb..c5068d4 100644 --- a/rdms-framework/rdms-common/src/main/java/com/njcn/rdms/framework/common/biz/system/dict/dto/DictDataRespDTO.java +++ b/rdms-framework/rdms-common/src/main/java/com/njcn/rdms/framework/common/biz/system/dict/dto/DictDataRespDTO.java @@ -19,4 +19,7 @@ public class DictDataRespDTO { @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer status; // 参见 CommonStatusEnum 枚举 + @Schema(description = "标识", example = "system") + private String sign; + } diff --git a/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java b/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java index 47aa465..2b855c8 100644 --- a/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java +++ b/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java @@ -59,11 +59,12 @@ public interface ErrorCodeConstants { ErrorCode REQUIREMENT_MODULE_HAS_NON_TERMINAL_REQUIREMENTS = new ErrorCode(1_008_002_012, "模块下存在非终态需求,不可删除"); ErrorCode REQUIREMENT_MODULE_HAS_CHILDREN = new ErrorCode(1_008_002_015, "存在子模块,请先删除子模块"); ErrorCode REQUIREMENT_MODULE_HAS_REQUIREMENTS = new ErrorCode(1_008_002_016, "模块下存在需求,请先删除需求"); - ErrorCode PROJECT_REQUIREMENT_MODULE_ROOT_NOT_EXISTS = new ErrorCode(1_008_002_018, "实现项目下不存在根模块,请先创建项目模块"); + ErrorCode PROJECT_REQUIREMENT_MODULE_ROOT_NOT_EXISTS = new ErrorCode(1_008_002_018, "关联项目下不存在根模块,请先创建项目模块"); ErrorCode REQUIREMENT_DISPATCHED_NOT_ALLOW_SPLIT = new ErrorCode(1_008_002_019, "产品需求已分流生成项目需求,不允许再在产品端拆分"); - ErrorCode REQUIREMENT_NOT_DISPATCHED = new ErrorCode(1_008_002_020, "该产品需求尚未分流到实现项目"); + ErrorCode REQUIREMENT_NOT_DISPATCHED = new ErrorCode(1_008_002_020, "该产品需求尚未分流到关联项目"); ErrorCode REQUIREMENT_DISPATCHED_PROJECT_REQUIREMENT_NOT_FOUND = new ErrorCode(1_008_002_021, "未找到该产品需求对应的项目需求"); ErrorCode REQUIREMENT_NOT_PROJECT_MEMBER = new ErrorCode(1_008_002_022, "您不是该项目的成员,无权访问"); + ErrorCode REQUIREMENT_HANDLER_NOT_PROJECT_MEMBER = new ErrorCode(1_008_002_023, "当前需求负责人不是所选分流项目的成员,请重新选择"); // ========== 项目管理 1-008-002-000 ========== ErrorCode PROJECT_NOT_EXISTS = new ErrorCode(1_008_002_000, "项目不存在"); @@ -189,4 +190,5 @@ public interface ErrorCodeConstants { ErrorCode PROJECT_REQUIREMENT_MODULE_HAS_CHILDREN = new ErrorCode(1_008_007_015, "存在子模块,请先删除子模块"); ErrorCode PROJECT_REQUIREMENT_MODULE_HAS_REQUIREMENTS = new ErrorCode(1_008_007_016, "模块下存在项目需求,请先删除需求"); ErrorCode PROJECT_REQUIREMENT_CHILD_NOT_ALLOW_CANCEL = new ErrorCode(1_008_007_017, "只有不存在子需求,或子需求都处于已取消和已拒绝状态时,父需求才允许取消"); + ErrorCode PROJECT_REQUIREMENT_SYNCED_FROM_PRODUCT_NOT_ALLOW_CANCEL = new ErrorCode(1_008_007_019, "\u7531\u4ea7\u54c1\u9700\u6c42\u6d41\u8f6c\u751f\u6210\u7684\u9879\u76ee\u9700\u6c42\u4e0d\u5141\u8bb8\u53d6\u6d88"); } diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/ProductRequirementController.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/ProductRequirementController.java index 18d9900..6877501 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/ProductRequirementController.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/ProductRequirementController.java @@ -101,6 +101,13 @@ public class ProductRequirementController { return success(requirementService.getAllowedTransitions(requirementId, productId)); } + @PostMapping("/allowed-transitions/batch") + @Operation(summary = "批量获取需求可执行的状态动作列表") + public CommonResult> getAllowedTransitionsBatch( + @Valid @RequestBody ProductRequirementBatchReqVO reqVO) { + return success(requirementService.getAllowedTransitionsBatch(reqVO)); + } + @GetMapping("/has-dispatched") @Operation(summary = "判断产品需求是否已分流生成项目需求") @Parameter(name = "requirementId", description = "需求编号", required = true, example = "1024") @@ -111,6 +118,13 @@ public class ProductRequirementController { return success(requirementService.hasDispatchedProjectRequirement(requirementId, productId)); } + @PostMapping("/has-dispatched/batch") + @Operation(summary = "批量判断产品需求是否已分流生成项目需求") + public CommonResult> hasDispatchedProjectRequirementBatch( + @Valid @RequestBody ProductRequirementBatchReqVO reqVO) { + return success(requirementService.hasDispatchedProjectRequirementBatch(reqVO)); + } + @GetMapping("/lifecycle") @Operation(summary = "获取需求生命周期信息") @Parameter(name = "requirementId", description = "需求编号", required = true, example = "1024") diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementAllowedTransitionBatchRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementAllowedTransitionBatchRespVO.java new file mode 100644 index 0000000..14315f3 --- /dev/null +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementAllowedTransitionBatchRespVO.java @@ -0,0 +1,18 @@ +package com.njcn.rdms.module.project.controller.admin.product.vo.requirement; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 产品需求批量可执行动作 Response VO") +@Data +public class ProductRequirementAllowedTransitionBatchRespVO { + + @Schema(description = "需求编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long requirementId; + + @Schema(description = "可执行动作列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List transitions; + +} diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementBatchReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementBatchReqVO.java new file mode 100644 index 0000000..4427622 --- /dev/null +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementBatchReqVO.java @@ -0,0 +1,22 @@ +package com.njcn.rdms.module.project.controller.admin.product.vo.requirement; + +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; + +@Schema(description = "管理后台 - 产品需求批量查询 Request VO") +@Data +public class ProductRequirementBatchReqVO { + + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "产品编号不能为空") + private Long productId; + + @Schema(description = "需求编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3]") + @NotEmpty(message = "需求编号列表不能为空") + private List requirementIds; + +} diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementDispatchedProjectLinkRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementDispatchedProjectLinkRespVO.java index 068d61a..2bb17eb 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementDispatchedProjectLinkRespVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementDispatchedProjectLinkRespVO.java @@ -13,7 +13,7 @@ public class ProductRequirementDispatchedProjectLinkRespVO { @Schema(description = "项目需求ID", example = "10086") private Long projectRequirementId; - @Schema(description = "实现项目ID", example = "8888") + @Schema(description = "关联项目ID", example = "8888") private Long projectId; } \ No newline at end of file diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementHasDispatchedBatchRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementHasDispatchedBatchRespVO.java new file mode 100644 index 0000000..3ebc670 --- /dev/null +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementHasDispatchedBatchRespVO.java @@ -0,0 +1,15 @@ +package com.njcn.rdms.module.project.controller.admin.product.vo.requirement; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 产品需求批量分流状态 Response VO") +@Data +public class ProductRequirementHasDispatchedBatchRespVO { + + @Schema(description = "需求编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long requirementId; + + @Schema(description = "是否已分流生成项目需求", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean hasDispatched; +} diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementRespVO.java index e8433ff..4af269e 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementRespVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementRespVO.java @@ -4,6 +4,7 @@ import com.njcn.rdms.module.project.dal.dataobject.attachment.AttachmentItem; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -65,8 +66,8 @@ public class ProductRequirementRespVO { @Schema(description = "提出人用户姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSaveReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSaveReqVO.java index 523e42f..9ec2364 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSaveReqVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSaveReqVO.java @@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; +import java.time.LocalDate; import java.util.List; /** @@ -55,9 +56,8 @@ public class ProductRequirementSaveReqVO { @Schema(description = "提出人姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - @NotNull(message = "所需工时不能为空") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSplitReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSplitReqVO.java index 154c9d1..a5a5216 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSplitReqVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementSplitReqVO.java @@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; +import java.time.LocalDate; import java.util.List; /** @@ -56,9 +57,8 @@ public class ProductRequirementSplitReqVO { @Schema(description = "提出人姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - @NotNull(message = "所需工时不能为空") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementStatusActionReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementStatusActionReqVO.java index 6b27fbb..5c4b0fe 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementStatusActionReqVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementStatusActionReqVO.java @@ -30,6 +30,6 @@ public class ProductRequirementStatusActionReqVO { @Schema(description = "状态变更原因", example = "评审通过,进入分流阶段") private String reason; - @Schema(description = "实现项目编号(dispatch动作时可选)", example = "1024") + @Schema(description = "关联项目编号(dispatch动作时可选)", example = "1024") private Long implementProjectId; } diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementUpdateReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementUpdateReqVO.java index a988110..ef25405 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementUpdateReqVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/product/vo/requirement/ProductRequirementUpdateReqVO.java @@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; +import java.time.LocalDate; import java.util.List; /** @@ -56,9 +57,8 @@ public class ProductRequirementUpdateReqVO { @Schema(description = "提出人姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - @NotNull(message = "所需工时不能为空") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/ProjectRequirementController.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/ProjectRequirementController.java index 3634bca..075fe58 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/ProjectRequirementController.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/ProjectRequirementController.java @@ -2,6 +2,8 @@ package com.njcn.rdms.module.project.controller.admin.project; import com.njcn.rdms.framework.common.pojo.CommonResult; import com.njcn.rdms.framework.common.pojo.PageResult; +import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementAllowedTransitionBatchRespVO; +import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementBatchReqVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementCloseReqVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementDeleteReqVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementLifecycleRespVO; @@ -112,6 +114,13 @@ public class ProjectRequirementController { return success(requirementService.getAllowedTransitions(requirementId, projectId)); } + @PostMapping("/allowed-transitions/batch") + @Operation(summary = "批量获取需求可执行的状态动作列表") + public CommonResult> getAllowedTransitionsBatch( + @Valid @RequestBody ProjectRequirementBatchReqVO reqVO) { + return success(requirementService.getAllowedTransitionsBatch(reqVO)); + } + @GetMapping("/lifecycle") @Operation(summary = "获取需求生命周期信息") @Parameter(name = "requirementId", description = "需求编号", required = true, example = "1024") diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementAllowedTransitionBatchRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementAllowedTransitionBatchRespVO.java new file mode 100644 index 0000000..2c50490 --- /dev/null +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementAllowedTransitionBatchRespVO.java @@ -0,0 +1,18 @@ +package com.njcn.rdms.module.project.controller.admin.project.vo.requirement; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 项目需求批量可执行动作 Response VO") +@Data +public class ProjectRequirementAllowedTransitionBatchRespVO { + + @Schema(description = "需求编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long requirementId; + + @Schema(description = "可执行动作列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List transitions; + +} diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementBatchReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementBatchReqVO.java new file mode 100644 index 0000000..8975553 --- /dev/null +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementBatchReqVO.java @@ -0,0 +1,22 @@ +package com.njcn.rdms.module.project.controller.admin.project.vo.requirement; + +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; + +@Schema(description = "管理后台 - 项目需求批量查询 Request VO") +@Data +public class ProjectRequirementBatchReqVO { + + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "项目编号不能为空") + private Long projectId; + + @Schema(description = "需求编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3]") + @NotEmpty(message = "需求编号列表不能为空") + private List requirementIds; + +} diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementRespVO.java index 449f6de..600fce7 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementRespVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementRespVO.java @@ -4,6 +4,7 @@ import com.njcn.rdms.module.project.dal.dataobject.attachment.AttachmentItem; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -68,8 +69,8 @@ public class ProjectRequirementRespVO { @Schema(description = "提出人姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSaveReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSaveReqVO.java index fe2a271..e789a76 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSaveReqVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSaveReqVO.java @@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; +import java.time.LocalDate; import java.util.List; /** @@ -55,9 +56,8 @@ public class ProjectRequirementSaveReqVO { @Schema(description = "提出人姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - @NotNull(message = "所需工时不能为空") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSplitReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSplitReqVO.java index 352348d..fee65aa 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSplitReqVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementSplitReqVO.java @@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; +import java.time.LocalDate; import java.util.List; /** @@ -56,9 +57,8 @@ public class ProjectRequirementSplitReqVO { @Schema(description = "提出人姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - @NotNull(message = "所需工时不能为空") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementUpdateReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementUpdateReqVO.java index 878c765..efced0f 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementUpdateReqVO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/project/vo/requirement/ProjectRequirementUpdateReqVO.java @@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; +import java.time.LocalDate; import java.util.List; /** @@ -56,9 +57,8 @@ public class ProjectRequirementUpdateReqVO { @Schema(description = "提出人姓名", example = "张三") private String proposerNickname; - @Schema(description = "所需工时", example = "8") - @NotNull(message = "所需工时不能为空") - private Double workHours; + @Schema(description = "预期完成时间", example = "2026-05-31") + private LocalDate expectedTime; @Schema(description = "当前处理人用户ID", example = "1024") private Long currentHandlerUserId; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/product/ProductRequirementDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/product/ProductRequirementDO.java index fcf20ae..9dd97b8 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/product/ProductRequirementDO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/product/ProductRequirementDO.java @@ -9,6 +9,7 @@ import com.njcn.rdms.module.project.dal.dataobject.attachment.AttachmentItem; import lombok.Data; import lombok.EqualsAndHashCode; +import java.time.LocalDate; import java.util.List; /** @@ -81,9 +82,9 @@ public class ProductRequirementDO extends BaseDO { */ private String proposerNickname; /** - * 所需工时 + * 预期完成时间 */ - private Double workHours; + private LocalDate expectedTime; /** * 当前处理人用户ID */ @@ -101,7 +102,7 @@ public class ProductRequirementDO extends BaseDO { */ private Integer sort; /** - * 闄勪欢鍒楄〃锛圝SON锛夈€傚厓绱?{@link AttachmentItem}锛歩d / url / name / size / contentType銆? + * 附件列表 */ @TableField(typeHandler = JacksonTypeHandler.class) private List attachments; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/project/ProjectRequirementDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/project/ProjectRequirementDO.java index f29727b..d109ab7 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/project/ProjectRequirementDO.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/project/ProjectRequirementDO.java @@ -9,6 +9,7 @@ import com.njcn.rdms.module.project.dal.dataobject.attachment.AttachmentItem; import lombok.Data; import lombok.EqualsAndHashCode; +import java.time.LocalDate; import java.util.List; /** @@ -93,15 +94,15 @@ public class ProjectRequirementDO extends BaseDO { */ private String currentHandlerUserNickname; /** - * 预估工时 + * 预期完成时间 */ - private Double workHours; + private LocalDate expectedTime; /** * 排序值 */ private Integer sort; /** - * 闄勪欢鍒楄〃锛圝SON锛夈€傚厓绱?{@link AttachmentItem}锛歩d / url / name / size / contentType銆? + * 附件项列表 */ @TableField(typeHandler = JacksonTypeHandler.class) private List attachments; diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/product/ProductRequirementMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/product/ProductRequirementMapper.java index b5e8573..929a782 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/product/ProductRequirementMapper.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/product/ProductRequirementMapper.java @@ -112,7 +112,7 @@ public interface ProductRequirementMapper extends BaseMapperX selectListByObjectTypeAndFromStatuses(String objectType, List fromStatusCodes) { + return selectList(new LambdaQueryWrapperX() + .eq(ObjectStatusTransitionDO::getObjectType, objectType) + .in(ObjectStatusTransitionDO::getFromStatusCode, fromStatusCodes) + .eq(ObjectStatusTransitionDO::getStatus, 0)); + } + /** * 统计某状态编码在流转配置中的引用次数。 */ diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementService.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementService.java index 73c2614..748bc72 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementService.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementService.java @@ -88,6 +88,14 @@ public interface ProductRequirementService { */ List getAllowedTransitions(Long requirementId, Long productId); + /** + * 批量获取需求当前可执行的状态动作列表 + * + * @param reqVO 批量查询请求 + * @return 按需求编号标识的可执行动作列表 + */ + List getAllowedTransitionsBatch(ProductRequirementBatchReqVO reqVO); + /** * 判断需求是否已分流并生成项目需求 * @@ -97,6 +105,14 @@ public interface ProductRequirementService { */ boolean hasDispatchedProjectRequirement(Long requirementId, Long productId); + /** + * 批量判断需求是否已分流并生成项目需求 + * + * @param reqVO 批量查询请求 + * @return 按需求编号标识的分流状态 + */ + List hasDispatchedProjectRequirementBatch(ProductRequirementBatchReqVO reqVO); + /** * 获取需求生命周期信息(当前状态 + 可执行动作) * @@ -157,7 +173,7 @@ public interface ProductRequirementService { * 获取产品需求分流后对应的项目需求跳转链接 * * @param productRequirementId 产品需求编号 - * @return 项目需求ID和实现项目ID + * @return 项目需求ID和关联项目ID */ ProductRequirementDispatchedProjectLinkRespVO getDispatchedProjectLink(Long productRequirementId); diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementServiceImpl.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementServiceImpl.java index 46cc80e..f0ade1a 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementServiceImpl.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/product/ProductRequirementServiceImpl.java @@ -9,6 +9,7 @@ import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.rdms.module.project.controller.admin.product.vo.requirement.*; import com.njcn.rdms.module.project.constant.ProjectObjectConstants; import com.njcn.rdms.module.project.dal.dataobject.audit.BizAuditLogDO; +import com.njcn.rdms.module.project.dal.dataobject.member.UserObjectRoleDO; import com.njcn.rdms.module.project.dal.dataobject.product.ProductRequirementDO; import com.njcn.rdms.module.project.dal.dataobject.product.ProductRequirementModuleDO; import com.njcn.rdms.module.project.dal.dataobject.product.ProductRequirementStatusLogDO; @@ -143,11 +144,11 @@ public class ProductRequirementServiceImpl implements ProductRequirementService requirement.setStatusCode(initialStatus); requirement.setProposerId(createReqVO.getProposerId()); requirement.setProposerNickname(normalizeNullableText(createReqVO.getProposerNickname())); - requirement.setWorkHours(createReqVO.getWorkHours()); + requirement.setExpectedTime(createReqVO.getExpectedTime()); requirement.setCurrentHandlerUserId(createReqVO.getCurrentHandlerUserId()); requirement.setCurrentHandlerUserNickname(normalizeNullableText(createReqVO.getCurrentHandlerUserNickname())); requirement.setImplementProjectId(createReqVO.getImplementProjectId()); - requirement.setSort(createReqVO.getSort() != null ? createReqVO.getSort() : 0); + requirement.setSort(createReqVO.getSort()); requirement.setAttachments(createReqVO.getAttachments()); requirementMapper.insert(requirement); @@ -182,11 +183,11 @@ public class ProductRequirementServiceImpl implements ProductRequirementService requirement.setPriority(updateReqVO.getPriority()); requirement.setProposerId(updateReqVO.getProposerId()); requirement.setProposerNickname(normalizeNullableText(updateReqVO.getProposerNickname())); - requirement.setWorkHours(updateReqVO.getWorkHours()); + requirement.setExpectedTime(updateReqVO.getExpectedTime()); requirement.setCurrentHandlerUserId(updateReqVO.getCurrentHandlerUserId()); requirement.setCurrentHandlerUserNickname(normalizeNullableText(updateReqVO.getCurrentHandlerUserNickname())); requirement.setImplementProjectId(updateReqVO.getImplementProjectId()); - requirement.setSort(updateReqVO.getSort() != null ? updateReqVO.getSort() : 0); + requirement.setSort(updateReqVO.getSort()); requirement.setAttachments(updateReqVO.getAttachments()); requirementMapper.updateById(requirement); @@ -262,7 +263,7 @@ public class ProductRequirementServiceImpl implements ProductRequirementService // 第三步:查询根需求详情并按排序值升序、创建时间倒排 List rootRequirements = requirementMapper.selectBatchIds(rootIds); rootRequirements.sort((a, b) -> { - int sortCompare = Integer.compare(a.getSort() != null ? a.getSort() : 0, b.getSort() != null ? b.getSort() : 0); + int sortCompare = Comparator.nullsLast(Integer::compareTo).compare(a.getSort(), b.getSort()); if (sortCompare != 0) { return sortCompare; } @@ -496,11 +497,19 @@ public class ProductRequirementServiceImpl implements ProductRequirementService if (ACTION_CLOSE.equals(actionCode)) { closeAllAcceptedChildren(reqVO.getId(), reason); } - // dispatch动作且选择了实现项目时,自动创建对应的项目需求 + // dispatch动作且选择了关联项目时,校验负责人是否在项目中,然后自动创建对应的项目需求 if (ACTION_DISPATCH.equals(actionCode) && implementProjectId != null) { + // 校验负责人是否为目标项目的成员 + if (requirement.getCurrentHandlerUserId() != null) { + List userObjectRoleDOS = userObjectRoleMapper.selectActiveListByObjectAndUserId( + ProjectObjectConstants.OBJECT_TYPE, implementProjectId, requirement.getCurrentHandlerUserId()); + if (userObjectRoleDOS.isEmpty()) { + throw exception(ErrorCodeConstants.REQUIREMENT_HANDLER_NOT_PROJECT_MEMBER); + } + } createProjectRequirementFromProduct(requirement, implementProjectId); } - // 带并发控制的状态更新(支持同时更新实现项目ID) + // 带并发控制的状态更新(支持同时更新关联项目ID) int updateCount = requirementMapper.updateStatusByIdAndStatusWithProject(requirement.getId(), fromStatus, toStatus, reason, implementProjectId); if (updateCount != 1) { throw exception(ErrorCodeConstants.REQUIREMENT_STATUS_CONCURRENT_MODIFIED); @@ -613,11 +622,11 @@ public class ProductRequirementServiceImpl implements ProductRequirementService childRequirement.setStatusCode(initialStatus); childRequirement.setProposerId(reqVO.getProposerId()); childRequirement.setProposerNickname(normalizeNullableText(reqVO.getProposerNickname())); - childRequirement.setWorkHours(reqVO.getWorkHours()); + childRequirement.setExpectedTime(reqVO.getExpectedTime()); childRequirement.setCurrentHandlerUserId(reqVO.getCurrentHandlerUserId()); childRequirement.setCurrentHandlerUserNickname(normalizeNullableText(reqVO.getCurrentHandlerUserNickname())); childRequirement.setImplementProjectId(reqVO.getImplementProjectId()); - childRequirement.setSort(reqVO.getSort() != null ? reqVO.getSort() : 0); + childRequirement.setSort(reqVO.getSort()); childRequirement.setAttachments(reqVO.getAttachments()); requirementMapper.insert(childRequirement); @@ -721,16 +730,38 @@ public class ProductRequirementServiceImpl implements ProductRequirementService // 取消动作不满足前置条件时,不再返回给前端展示按钮 .filter(transition -> shouldExposeTransition(requirement, transition)) .map(transition -> { - ProductRequirementStatusTransitionRespVO vo = new ProductRequirementStatusTransitionRespVO(); - vo.setActionCode(transition.getActionCode()); - vo.setActionName(transition.getActionName()); - vo.setToStatusCode(transition.getToStatusCode()); - // 查询目标状态名称 - ObjectStatusModelDO statusModel = statusModelMapper - .selectByObjectTypeAndStatusCode(REQUIREMENT_OBJECT_TYPE, transition.getToStatusCode()); - vo.setToStatusName(statusModel != null ? statusModel.getStatusName() : transition.getToStatusCode()); - vo.setNeedReason(transition.getNeedReason()); - return vo; + ProductRequirementStatusTransitionRespVO vo = new ProductRequirementStatusTransitionRespVO(); + vo.setActionCode(transition.getActionCode()); + vo.setActionName(transition.getActionName()); + vo.setToStatusCode(transition.getToStatusCode()); + // 查询目标状态名称 + ObjectStatusModelDO statusModel = statusModelMapper + .selectByObjectTypeAndStatusCode(REQUIREMENT_OBJECT_TYPE, transition.getToStatusCode()); + vo.setToStatusName(statusModel != null ? statusModel.getStatusName() : transition.getToStatusCode()); + vo.setNeedReason(transition.getNeedReason()); + return vo; + }).collect(Collectors.toList()); + } + + @Override + @CheckObjectPermission(objectType = PRODUCT_OBJECT_TYPE, objectId = "#reqVO.productId", + permission = PRODUCT_QUERY_PERMISSION) + public List getAllowedTransitionsBatch(ProductRequirementBatchReqVO reqVO) { + List requirements = getBatchRequirements(reqVO.getRequirementIds(), reqVO.getProductId()); + Set dispatchedRequirementIds = getDispatchedProductRequirementIds(requirements); + Map> transitionsByStatus = getTransitionsByStatus(requirements); + Map statusModelMap = getStatusModelMap(); + + return requirements.stream().map(requirement -> { + ProductRequirementAllowedTransitionBatchRespVO respVO = new ProductRequirementAllowedTransitionBatchRespVO(); + respVO.setRequirementId(requirement.getId()); + if (dispatchedRequirementIds.contains(requirement.getId())) { + respVO.setTransitions(Collections.emptyList()); + } else { + respVO.setTransitions(buildAllowedTransitions(requirement, + transitionsByStatus.getOrDefault(requirement.getStatusCode(), Collections.emptyList()), statusModelMap)); + } + return respVO; }).collect(Collectors.toList()); } @@ -742,13 +773,27 @@ public class ProductRequirementServiceImpl implements ProductRequirementService return hasDispatchedProjectRequirement(requirement); } + @Override + @CheckObjectPermission(objectType = PRODUCT_OBJECT_TYPE, objectId = "#reqVO.productId", + permission = PRODUCT_QUERY_PERMISSION) + public List hasDispatchedProjectRequirementBatch(ProductRequirementBatchReqVO reqVO) { + List requirements = getBatchRequirements(reqVO.getRequirementIds(), reqVO.getProductId()); + Set dispatchedRequirementIds = getDispatchedProductRequirementIds(requirements); + return requirements.stream().map(requirement -> { + ProductRequirementHasDispatchedBatchRespVO respVO = new ProductRequirementHasDispatchedBatchRespVO(); + respVO.setRequirementId(requirement.getId()); + respVO.setHasDispatched(dispatchedRequirementIds.contains(requirement.getId())); + return respVO; + }).collect(Collectors.toList()); + } + /** * 该方法作用和getAllowedTransitions()类似,是用来获取当前状态下可以进行的动作 - * @deprecated 产品需求页面最开始用来下拉框改状态时使用的,已经弃用 * * @param requirementId 需求编号 * @param productId 产品编号 * @return ProductRequirementLifecycleRespVO + * @deprecated 产品需求页面最开始用来下拉框改状态时使用的,已经弃用 */ @Override @Deprecated @@ -778,7 +823,7 @@ public class ProductRequirementServiceImpl implements ProductRequirementService @Override public ProductRequirementDispatchedProjectLinkRespVO getDispatchedProjectLink(Long productRequirementId) { - // 校验产品需求是否存在,以及是否已分流到具体的实现项目 + // 校验产品需求是否存在,以及是否已分流到具体的关联项目 ProductRequirementDO requirement = validateRequirementExists(productRequirementId); if (requirement.getImplementProjectId() == null) { throw exception(ErrorCodeConstants.REQUIREMENT_NOT_DISPATCHED); @@ -823,6 +868,78 @@ public class ProductRequirementServiceImpl implements ProductRequirementService return !projectRequirements.isEmpty(); } + private List getBatchRequirements(List requirementIds, Long productId) { + if (requirementIds == null || requirementIds.isEmpty()) { + return Collections.emptyList(); + } + if (requirementIds.stream().anyMatch(Objects::isNull)) { + throw invalidParamException("需求编号不能为空"); + } + List distinctIds = requirementIds.stream().distinct().collect(Collectors.toList()); + Map requirementMap = requirementMapper.selectByIds(distinctIds).stream() + .collect(Collectors.toMap(ProductRequirementDO::getId, Function.identity())); + if (requirementMap.size() != distinctIds.size()) { + throw exception(ErrorCodeConstants.REQUIREMENT_NOT_EXISTS); + } + List requirements = distinctIds.stream() + .map(requirementMap::get) + .collect(Collectors.toList()); + for (ProductRequirementDO requirement : requirements) { + if (!Objects.equals(requirement.getProductId(), productId)) { + throw invalidParamException("需求不属于当前产品"); + } + } + return requirements; + } + + private Set getDispatchedProductRequirementIds(List requirements) { + List candidateIds = requirements.stream() + .filter(requirement -> requirement.getImplementProjectId() != null) + .map(ProductRequirementDO::getId) + .collect(Collectors.toList()); + if (candidateIds.isEmpty()) { + return Collections.emptySet(); + } + return projectRequirementMapper.selectList(new LambdaQueryWrapperX() + .eq(ProjectRequirementDO::getSourceType, "product_requirement") + .in(ProjectRequirementDO::getProductRequirementId, candidateIds)) + .stream() + .map(ProjectRequirementDO::getProductRequirementId) + .collect(Collectors.toSet()); + } + + private Map> getTransitionsByStatus(List requirements) { + List statusCodes = requirements.stream() + .map(ProductRequirementDO::getStatusCode) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + if (statusCodes.isEmpty()) { + return Collections.emptyMap(); + } + return statusTransitionMapper.selectListByObjectTypeAndFromStatuses(REQUIREMENT_OBJECT_TYPE, statusCodes) + .stream() + .collect(Collectors.groupingBy(ObjectStatusTransitionDO::getFromStatusCode)); + } + + private List buildAllowedTransitions( + ProductRequirementDO requirement, + List transitions, + Map statusModelMap) { + return transitions.stream() + .filter(transition -> shouldExposeTransition(requirement, transition)) + .map(transition -> { + ProductRequirementStatusTransitionRespVO vo = new ProductRequirementStatusTransitionRespVO(); + vo.setActionCode(transition.getActionCode()); + vo.setActionName(transition.getActionName()); + vo.setToStatusCode(transition.getToStatusCode()); + ObjectStatusModelDO statusModel = statusModelMap.get(transition.getToStatusCode()); + vo.setToStatusName(statusModel != null ? statusModel.getStatusName() : transition.getToStatusCode()); + vo.setNeedReason(transition.getNeedReason()); + return vo; + }).collect(Collectors.toList()); + } + /** * 已分流并生成项目需求后,产品需求端不再允许继续拆分。 */ @@ -1265,7 +1382,7 @@ public class ProductRequirementServiceImpl implements ProductRequirementService target.setLastStatusReason(source.getLastStatusReason()); target.setProposerId(source.getProposerId()); target.setProposerNickname(source.getProposerNickname()); - target.setWorkHours(source.getWorkHours()); + target.setExpectedTime(source.getExpectedTime()); target.setCurrentHandlerUserId(source.getCurrentHandlerUserId()); target.setCurrentHandlerUserNickname(source.getCurrentHandlerUserNickname()); target.setImplementProjectId(source.getImplementProjectId()); @@ -1307,8 +1424,8 @@ public class ProductRequirementServiceImpl implements ProductRequirementService valueOf(after, ProductRequirementDO::getProposerId)); appendFieldChange(fieldChanges, "proposerNickname", valueOf(before, ProductRequirementDO::getProposerNickname), valueOf(after, ProductRequirementDO::getProposerNickname)); - appendFieldChange(fieldChanges, "workHours", valueOf(before, ProductRequirementDO::getWorkHours), - valueOf(after, ProductRequirementDO::getWorkHours)); + appendFieldChange(fieldChanges, "expectedTime", valueOf(before, ProductRequirementDO::getExpectedTime), + valueOf(after, ProductRequirementDO::getExpectedTime)); appendFieldChange(fieldChanges, "currentHandlerUserId", valueOf(before, ProductRequirementDO::getCurrentHandlerUserId), valueOf(after, ProductRequirementDO::getCurrentHandlerUserId)); appendFieldChange(fieldChanges, "currentHandlerUserNickname", @@ -1358,10 +1475,10 @@ public class ProductRequirementServiceImpl implements ProductRequirementService } /** - * dispatch动作且已选择实现项目时,自动将产品需求转化为项目需求 + * dispatch动作且已选择关联项目时,自动将产品需求转化为项目需求 * * @param productRequirement 产品需求 - * @param implementProjectId 实现项目ID + * @param implementProjectId 关联项目ID */ @VisibleForTesting void createProjectRequirementFromProduct(ProductRequirementDO productRequirement, Long implementProjectId) { @@ -1374,7 +1491,7 @@ public class ProductRequirementServiceImpl implements ProductRequirementService return; } - // 查询实现项目下的根模块(parentId = 0) + // 查询关联项目下的根模块(parentId = 0) ProjectRequirementModuleDO rootModule = projectRequirementModuleMapper.selectByProjectIdAndParentId(implementProjectId, 0L); if (rootModule == null) { throw exception(ErrorCodeConstants.PROJECT_REQUIREMENT_MODULE_ROOT_NOT_EXISTS); @@ -1397,9 +1514,9 @@ public class ProductRequirementServiceImpl implements ProductRequirementService newRequirement.setPriority(productRequirement.getPriority()); newRequirement.setProposerId(productRequirement.getProposerId()); newRequirement.setProposerNickname(productRequirement.getProposerNickname()); + newRequirement.setExpectedTime(productRequirement.getExpectedTime()); newRequirement.setCurrentHandlerUserId(productRequirement.getCurrentHandlerUserId()); newRequirement.setCurrentHandlerUserNickname(productRequirement.getCurrentHandlerUserNickname()); - newRequirement.setWorkHours(productRequirement.getWorkHours()); newRequirement.setAttachments(productRequirement.getAttachments()); newRequirement.setCreator(productRequirement.getCreator()); newRequirement.setCreateTime(productRequirement.getCreateTime()); diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementService.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementService.java index c4ed66f..524eb3b 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementService.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementService.java @@ -1,6 +1,8 @@ package com.njcn.rdms.module.project.service.project; import com.njcn.rdms.framework.common.pojo.PageResult; +import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementAllowedTransitionBatchRespVO; +import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementBatchReqVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementCloseReqVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementLifecycleRespVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementModuleReqVO; @@ -71,6 +73,11 @@ public interface ProjectRequirementService { */ List getAllowedTransitions(Long requirementId, Long projectId); + /** + * 批量获取需求可执行动作列表 + */ + List getAllowedTransitionsBatch(ProjectRequirementBatchReqVO reqVO); + /** * 获取需求生命周期信息 */ diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementServiceImpl.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementServiceImpl.java index 846a43d..3b7702b 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementServiceImpl.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectRequirementServiceImpl.java @@ -7,6 +7,8 @@ import com.njcn.rdms.framework.common.util.object.BeanUtils; import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX; import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.rdms.module.project.constant.ProjectObjectConstants; +import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementAllowedTransitionBatchRespVO; +import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementBatchReqVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementCloseReqVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementLifecycleRespVO; import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementModuleReqVO; @@ -44,15 +46,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -152,10 +146,10 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService requirement.setStatusCode(initialStatus); requirement.setProposerId(createReqVO.getProposerId()); requirement.setProposerNickname(normalizeNullableText(createReqVO.getProposerNickname())); - requirement.setWorkHours(createReqVO.getWorkHours()); + requirement.setExpectedTime(createReqVO.getExpectedTime()); requirement.setCurrentHandlerUserId(createReqVO.getCurrentHandlerUserId()); requirement.setCurrentHandlerUserNickname(normalizeNullableText(createReqVO.getCurrentHandlerUserNickname())); - requirement.setSort(createReqVO.getSort() != null ? createReqVO.getSort() : 0); + requirement.setSort(createReqVO.getSort()); requirement.setAttachments(createReqVO.getAttachments()); requirementMapper.insert(requirement); @@ -188,10 +182,10 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService requirement.setPriority(updateReqVO.getPriority()); requirement.setProposerId(updateReqVO.getProposerId()); requirement.setProposerNickname(normalizeNullableText(updateReqVO.getProposerNickname())); - requirement.setWorkHours(updateReqVO.getWorkHours()); + requirement.setExpectedTime(updateReqVO.getExpectedTime()); requirement.setCurrentHandlerUserId(updateReqVO.getCurrentHandlerUserId()); requirement.setCurrentHandlerUserNickname(normalizeNullableText(updateReqVO.getCurrentHandlerUserNickname())); - requirement.setSort(updateReqVO.getSort() != null ? updateReqVO.getSort() : 0); + requirement.setSort(updateReqVO.getSort()); requirement.setAttachments(updateReqVO.getAttachments()); requirementMapper.updateById(requirement); @@ -259,8 +253,7 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService List rootRequirements = requirementMapper.selectBatchIds(rootIds); rootRequirements.sort((a, b) -> { - int sortCompare = Integer.compare(a.getSort() != null ? a.getSort() : 0, - b.getSort() != null ? b.getSort() : 0); + int sortCompare = Comparator.nullsLast(Integer::compareTo).compare(a.getSort(), b.getSort()); if (sortCompare != 0) { return sortCompare; } @@ -303,6 +296,10 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService if (ACTION_ACCEPT.equals(actionCode) || ACTION_CLOSE.equals(actionCode)) { validateAllChildrenAllowCloseOrAccept(reqVO.getId()); } + // 产品需求流转生成的项目需求不允许在项目侧取消,避免与产品侧主状态冲突。 + if (ACTION_CANCEL.equals(actionCode) && isFromProductRequirement(requirement)) { + throw exception(ErrorCodeConstants.PROJECT_REQUIREMENT_SYNCED_FROM_PRODUCT_NOT_ALLOW_CANCEL); + } if (ACTION_CANCEL.equals(actionCode) && hasChildren(requirement.getId())) { validateParentCancelAllowed(reqVO.getId()); } @@ -379,10 +376,10 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService childRequirement.setStatusCode(initialStatus); childRequirement.setProposerId(reqVO.getProposerId()); childRequirement.setProposerNickname(normalizeNullableText(reqVO.getProposerNickname())); - childRequirement.setWorkHours(reqVO.getWorkHours()); + childRequirement.setExpectedTime(reqVO.getExpectedTime()); childRequirement.setCurrentHandlerUserId(reqVO.getCurrentHandlerUserId()); childRequirement.setCurrentHandlerUserNickname(normalizeNullableText(reqVO.getCurrentHandlerUserNickname())); - childRequirement.setSort(reqVO.getSort() != null ? reqVO.getSort() : 0); + childRequirement.setSort(reqVO.getSort()); childRequirement.setAttachments(reqVO.getAttachments()); requirementMapper.insert(childRequirement); @@ -451,6 +448,23 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService }).collect(Collectors.toList()); } + @Override + @CheckObjectPermission(objectType = PROJECT_OBJECT_TYPE, objectId = "#reqVO.projectId", + permission = PROJECT_QUERY_PERMISSION) + public List getAllowedTransitionsBatch(ProjectRequirementBatchReqVO reqVO) { + List requirements = getBatchRequirements(reqVO.getRequirementIds(), reqVO.getProjectId()); + Map> transitionsByStatus = getTransitionsByStatus(requirements); + Map statusModelMap = getStatusModelMap(); + + return requirements.stream().map(requirement -> { + ProjectRequirementAllowedTransitionBatchRespVO respVO = new ProjectRequirementAllowedTransitionBatchRespVO(); + respVO.setRequirementId(requirement.getId()); + respVO.setTransitions(buildAllowedTransitions(requirement, + transitionsByStatus.getOrDefault(requirement.getStatusCode(), Collections.emptyList()), statusModelMap)); + return respVO; + }).collect(Collectors.toList()); + } + @Override @CheckObjectPermission(objectType = PROJECT_OBJECT_TYPE, objectId = "#projectId", permission = PROJECT_QUERY_PERMISSION) @@ -654,6 +668,60 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService (left, right) -> left, LinkedHashMap::new)); } + private List getBatchRequirements(List requirementIds, Long projectId) { + if (requirementIds == null || requirementIds.isEmpty()) { + return Collections.emptyList(); + } + if (requirementIds.stream().anyMatch(Objects::isNull)) { + throw invalidParamException("需求编号不能为空"); + } + List distinctIds = requirementIds.stream().distinct().collect(Collectors.toList()); + Map requirementMap = requirementMapper.selectByIds(distinctIds).stream() + .collect(Collectors.toMap(ProjectRequirementDO::getId, Function.identity())); + if (requirementMap.size() != distinctIds.size()) { + throw exception(ErrorCodeConstants.PROJECT_REQUIREMENT_NOT_EXISTS); + } + List requirements = distinctIds.stream() + .map(requirementMap::get) + .collect(Collectors.toList()); + for (ProjectRequirementDO requirement : requirements) { + validateRequirementBelongsToProject(requirement, projectId); + } + return requirements; + } + + private Map> getTransitionsByStatus(List requirements) { + List statusCodes = requirements.stream() + .map(ProjectRequirementDO::getStatusCode) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + if (statusCodes.isEmpty()) { + return Collections.emptyMap(); + } + return statusTransitionMapper.selectListByObjectTypeAndFromStatuses(REQUIREMENT_OBJECT_TYPE, statusCodes) + .stream() + .collect(Collectors.groupingBy(ObjectStatusTransitionDO::getFromStatusCode)); + } + + private List buildAllowedTransitions( + ProjectRequirementDO requirement, + List transitions, + Map statusModelMap) { + return transitions.stream() + .filter(transition -> shouldExposeTransition(requirement, transition)) + .map(transition -> { + ProjectRequirementStatusTransitionRespVO vo = new ProjectRequirementStatusTransitionRespVO(); + vo.setActionCode(transition.getActionCode()); + vo.setActionName(transition.getActionName()); + vo.setToStatusCode(transition.getToStatusCode()); + ObjectStatusModelDO statusModel = statusModelMap.get(transition.getToStatusCode()); + vo.setToStatusName(statusModel != null ? statusModel.getStatusName() : transition.getToStatusCode()); + vo.setNeedReason(transition.getNeedReason()); + return vo; + }).collect(Collectors.toList()); + } + /** * 判断指定模块是否为“全部需求”根模块。 */ @@ -742,12 +810,23 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService if (!ACTION_CANCEL.equals(transition.getActionCode())) { return true; } + if (isFromProductRequirement(requirement)) { + return false; + } if (!hasChildren(requirement.getId())) { return true; } return isParentCancelAllowed(requirement.getId()); } + /** + * 判断当前项目需求是否由产品需求流转生成。 + */ + private boolean isFromProductRequirement(ProjectRequirementDO requirement) { + return Objects.equals(requirement.getSourceType(), SOURCE_TYPE_PRODUCT_REQUIREMENT) + && requirement.getProductRequirementId() != null; + } + /** * 父需求存在子需求时,只有全部子需求都已取消或已拒绝,才允许展示取消动作。 */ @@ -1119,7 +1198,7 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService target.setLastStatusReason(source.getLastStatusReason()); target.setProposerId(source.getProposerId()); target.setProposerNickname(source.getProposerNickname()); - target.setWorkHours(source.getWorkHours()); + target.setExpectedTime(source.getExpectedTime()); target.setCurrentHandlerUserId(source.getCurrentHandlerUserId()); target.setCurrentHandlerUserNickname(source.getCurrentHandlerUserNickname()); target.setSort(source.getSort()); @@ -1162,8 +1241,8 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService valueOf(after, ProjectRequirementDO::getProposerId)); appendFieldChange(fieldChanges, "proposerNickname", valueOf(before, ProjectRequirementDO::getProposerNickname), valueOf(after, ProjectRequirementDO::getProposerNickname)); - appendFieldChange(fieldChanges, "workHours", valueOf(before, ProjectRequirementDO::getWorkHours), - valueOf(after, ProjectRequirementDO::getWorkHours)); + appendFieldChange(fieldChanges, "expectedTime", valueOf(before, ProjectRequirementDO::getExpectedTime), + valueOf(after, ProjectRequirementDO::getExpectedTime)); appendFieldChange(fieldChanges, "currentHandlerUserId", valueOf(before, ProjectRequirementDO::getCurrentHandlerUserId), valueOf(after, ProjectRequirementDO::getCurrentHandlerUserId)); @@ -1229,7 +1308,7 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService target.setLastStatusReason(source.getLastStatusReason()); target.setProposerId(source.getProposerId()); target.setProposerNickname(source.getProposerNickname()); - target.setWorkHours(source.getWorkHours()); + target.setExpectedTime(source.getExpectedTime()); target.setCurrentHandlerUserId(source.getCurrentHandlerUserId()); target.setCurrentHandlerUserNickname(source.getCurrentHandlerUserNickname()); target.setImplementProjectId(source.getImplementProjectId()); @@ -1268,8 +1347,8 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService valueOf(after, ProductRequirementDO::getProposerId)); appendFieldChange(fieldChanges, "proposerNickname", valueOf(before, ProductRequirementDO::getProposerNickname), valueOf(after, ProductRequirementDO::getProposerNickname)); - appendFieldChange(fieldChanges, "workHours", valueOf(before, ProductRequirementDO::getWorkHours), - valueOf(after, ProductRequirementDO::getWorkHours)); + appendFieldChange(fieldChanges, "expectedTime", valueOf(before, ProductRequirementDO::getExpectedTime), + valueOf(after, ProductRequirementDO::getExpectedTime)); appendFieldChange(fieldChanges, "currentHandlerUserId", valueOf(before, ProductRequirementDO::getCurrentHandlerUserId), valueOf(after, ProductRequirementDO::getCurrentHandlerUserId)); appendFieldChange(fieldChanges, "currentHandlerUserNickname", @@ -1313,4 +1392,4 @@ public class ProjectRequirementServiceImpl implements ProjectRequirementService return StringUtils.hasText(value) ? value : ""; } -} +} \ No newline at end of file diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectServiceImpl.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectServiceImpl.java index aa75552..6b27868 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectServiceImpl.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/project/ProjectServiceImpl.java @@ -221,6 +221,9 @@ class ProjectServiceImpl implements ProjectService { // 8) 项目创建审计 writeBizAuditLog(project, ObjectActivityConstants.PROJECT_ACTION_CREATE, null, initialStatus, buildProjectFieldChanges(null, project), null); + + // 9) 初始化项目需求的根模块 + initDefaultRequirementModule(project); return project.getId(); } diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/dto/AdminUserRespDTO.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/dto/AdminUserRespDTO.java index f5665d3..aeb315e 100644 --- a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/dto/AdminUserRespDTO.java +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/user/dto/AdminUserRespDTO.java @@ -14,6 +14,9 @@ public class AdminUserRespDTO implements VO { @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王") private String nickname; + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer sort; + @Schema(description = "所属公司", example = "灿能") private String company; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataRespVO.java index 943a1b6..bc04828 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataRespVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataRespVO.java @@ -43,6 +43,10 @@ public class DictDataRespVO { @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") private String colorType; + @Schema(description = "标识", example = "system") + @ExcelProperty("标识") + private String sign; + @Schema(description = "css 样式", example = "btn-visible") private String cssClass; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java index cf8c6f8..d317a12 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java @@ -42,6 +42,10 @@ public class DictDataSaveReqVO { @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") private String colorType; + @Schema(description = "标识", example = "system") + @Size(max = 255, message = "标识长度不能超过255个字符") + private String sign; + @Schema(description = "css 样式", example = "btn-visible") private String cssClass; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java index d4596cc..043f37b 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java @@ -22,6 +22,9 @@ public class DictDataSimpleRespVO { @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") private String colorType; + @Schema(description = "标识", example = "system") + private String sign; + @Schema(description = "css 样式", example = "btn-visible") private String cssClass; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/UserController.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/UserController.java index e770a64..60470fc 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/UserController.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/UserController.java @@ -184,9 +184,9 @@ public class UserController { public void importTemplate(HttpServletResponse response) throws IOException { // 手动创建导出 demo List list = Arrays.asList( - UserImportExcelVO.builder().username("yunai").deptId(1L).positionId(1L).email("yunai@iocoder.cn").mobile("15601691300") + UserImportExcelVO.builder().username("yunai").deptId(1L).positionId(1L).sort(10).email("yunai@iocoder.cn").mobile("15601691300") .nickname("灿能").status(CommonStatusEnum.ENABLE.getStatus()).sex(SexEnum.MALE.getSex()).build(), - UserImportExcelVO.builder().username("yuanma").deptId(2L).positionId(2L).email("yuanma@iocoder.cn").mobile("15601701300") + UserImportExcelVO.builder().username("yuanma").deptId(2L).positionId(2L).sort(20).email("yuanma@iocoder.cn").mobile("15601701300") .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SexEnum.FEMALE.getSex()).build() ); // 输出 diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserImportExcelVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserImportExcelVO.java index f05da40..7cf7a82 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserImportExcelVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserImportExcelVO.java @@ -33,6 +33,9 @@ public class UserImportExcelVO { @ExcelProperty("主岗位编号") private Long positionId; + @ExcelProperty("显示顺序") + private Integer sort; + @ExcelProperty("用户邮箱") private String email; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserRespVO.java index c3b1e64..f4e72ae 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserRespVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserRespVO.java @@ -48,6 +48,10 @@ public class UserRespVO { @ExcelProperty("主岗位") private String positionName; + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty("显示顺序") + private Integer sort; + @Schema(description = "用户邮箱", example = "rdms@iocoder.cn") @ExcelProperty("用户邮箱") private String email; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSaveReqVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSaveReqVO.java index 7dd0395..721eadb 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSaveReqVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSaveReqVO.java @@ -58,6 +58,11 @@ public class UserSaveReqVO { @DiffLogField(name = "主岗位", function = PostParseFunction.NAME) private Long positionId; + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "显示顺序不能为空") + @DiffLogField(name = "显示顺序") + private Integer sort; + @Schema(description = "离职时间", example = "2026-03-19 00:00:00") @DiffLogField(name = "离职时间") private LocalDateTime resignedAt; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java index 4945a52..42dbd3f 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java @@ -17,8 +17,12 @@ public class UserSimpleRespVO { @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能") private String nickname; + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer sort; + @Schema(description = "部门ID", example = "我是一个用户") private Long deptId; + @Schema(description = "部门名称", example = "IT 部") private String deptName; diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/app/dict/vo/AppDictDataRespVO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/app/dict/vo/AppDictDataRespVO.java index 455b357..923f2c2 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/app/dict/vo/AppDictDataRespVO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/app/dict/vo/AppDictDataRespVO.java @@ -23,4 +23,7 @@ public class AppDictDataRespVO { @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") private String dictType; + @Schema(description = "标识", example = "system") + private String sign; + } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dict/DictDataDO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dict/DictDataDO.java index 2062cca..b796c86 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dict/DictDataDO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/dict/DictDataDO.java @@ -52,6 +52,11 @@ public class DictDataDO extends BaseDO { * 对应到 element-ui 为 default、primary、success、info、warning、danger */ private String colorType; + /** + * 标识 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String sign; /** * css 样式 */ diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/user/AdminUserDO.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/user/AdminUserDO.java index 0a56b8c..6a4ee1d 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/user/AdminUserDO.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/dataobject/user/AdminUserDO.java @@ -52,6 +52,11 @@ public class AdminUserDO extends BaseDO { */ private String nickname; + /** + * 显示排序 + */ + private Integer sort; + /** * 备注 */ diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/user/AdminUserMapper.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/user/AdminUserMapper.java index 45d5ab5..764597b 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/user/AdminUserMapper.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/user/AdminUserMapper.java @@ -35,23 +35,36 @@ public interface AdminUserMapper extends BaseMapperX { .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime()) .inIfPresent(AdminUserDO::getDeptId, deptIds) .inIfPresent(AdminUserDO::getId, userIds) + .orderByAsc(AdminUserDO::getSort) .orderByDesc(AdminUserDO::getId)); } default List selectListByNickname(String nickname) { - return selectList(new LambdaQueryWrapperX().like(AdminUserDO::getNickname, nickname)); + return selectList(new LambdaQueryWrapperX() + .like(AdminUserDO::getNickname, nickname) + .orderByAsc(AdminUserDO::getSort) + .orderByDesc(AdminUserDO::getId)); } default List selectListByStatus(Integer status) { - return selectList(AdminUserDO::getStatus, status); + return selectList(new LambdaQueryWrapperX() + .eq(AdminUserDO::getStatus, status) + .orderByAsc(AdminUserDO::getSort) + .orderByDesc(AdminUserDO::getId)); } default List selectListByDeptIds(Collection deptIds) { - return selectList(AdminUserDO::getDeptId, deptIds); + return selectList(new LambdaQueryWrapperX() + .in(AdminUserDO::getDeptId, deptIds) + .orderByAsc(AdminUserDO::getSort) + .orderByDesc(AdminUserDO::getId)); } default List selectListByPositionIds(Collection positionIds) { - return selectList(AdminUserDO::getPositionId, positionIds); + return selectList(new LambdaQueryWrapperX() + .in(AdminUserDO::getPositionId, positionIds) + .orderByAsc(AdminUserDO::getSort) + .orderByDesc(AdminUserDO::getId)); } } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java index b195790..c408df5 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/user/AdminUserServiceImpl.java @@ -58,6 +58,7 @@ public class AdminUserServiceImpl implements AdminUserService { static final String USER_INIT_PASSWORD_KEY = "system.user.init-password"; static final String USER_REGISTER_ENABLED_KEY = "system.user.register-enabled"; + private static final Integer DEFAULT_REGISTER_USER_SORT = 999; @Resource private AdminUserMapper userMapper; @@ -107,6 +108,7 @@ public class AdminUserServiceImpl implements AdminUserService { // 2. 插入用户 AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class); user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 + user.setSort(DEFAULT_REGISTER_USER_SORT); user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码 userMapper.insert(user); return user.getId();