feat(project): 新增对象状态模型与状态流转管理能力

- 新增对象状态模型和状态流转的后台管理接口
  - 补充分页查询、增删改查及批量删除能力
  - 增加状态编码、初始状态和流转配置的唯一性校验
  - 增加状态引用校验和删除前校验
  - 统一 Swagger 注解依赖为 jakarta 版本以适配 Spring Boot 3
This commit is contained in:
caozehui
2026-05-15 09:21:10 +08:00
parent be7e0d6162
commit 9ee49b1863
18 changed files with 939 additions and 5 deletions

View File

@@ -29,6 +29,7 @@
<spring.boot.version>3.5.9</spring.boot.version>
<mapstruct.version>1.6.3</mapstruct.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<swagger.version>2.2.38</swagger.version>
</properties>
<dependencyManagement>

View File

@@ -72,7 +72,8 @@
<!-- Swagger 注解,用于 API 文档生成(@Schema、@Operation 等) -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- RPC 远程调用相关 -->
@@ -180,4 +181,4 @@
</dependency>
</dependencies>
</project>
</project>

View File

@@ -24,7 +24,8 @@
<!-- Web 相关 -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- 参数校验 -->

View File

@@ -0,0 +1,79 @@
package com.njcn.rdms.module.project.controller.admin.status;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelPageReqVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelRespVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelSaveReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.service.status.ObjectStatusModelService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
/**
* 对象状态模型管理接口。
*/
@Tag(name = "管理后台 - 对象状态模型")
@RestController
@RequestMapping("/project/status/model")
@Validated
public class ObjectStatusModelController {
@Resource
private ObjectStatusModelService objectStatusModelService;
@PostMapping("/create")
@Operation(summary = "创建对象状态模型")
public CommonResult<Long> createModel(@Valid @RequestBody ObjectStatusModelSaveReqVO createReqVO) {
return success(objectStatusModelService.createModel(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改对象状态模型")
public CommonResult<Boolean> updateModel(@Valid @RequestBody ObjectStatusModelSaveReqVO updateReqVO) {
objectStatusModelService.updateModel(updateReqVO);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获取对象状态模型")
@Parameter(name = "id", description = "对象状态模型Id", required = true, example = "1024")
public CommonResult<ObjectStatusModelRespVO> getModel(@RequestParam("id") Long id) {
ObjectStatusModelRespVO model = objectStatusModelService.getModel(id);
return success(BeanUtils.toBean(model, ObjectStatusModelRespVO.class));
}
@DeleteMapping("/delete")
@Operation(summary = "删除对象状态模型")
@Parameter(name = "id", description = "对象状态模型Id", required = true, example = "1024")
public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) {
objectStatusModelService.deleteModel(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "对象状态模型Id列表", required = true)
@Operation(summary = "批量删除对象状态模型")
public CommonResult<Boolean> deleteModelList(@RequestParam("ids") List<Long> ids) {
objectStatusModelService.deleteModelList(ids);
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获取对象状态模型分页")
public CommonResult<PageResult<ObjectStatusModelRespVO>> getModelPage(@Valid ObjectStatusModelPageReqVO pageReqVO) {
PageResult<ObjectStatusModelDO> pageResult = objectStatusModelService.getModelPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ObjectStatusModelRespVO.class));
}
}

View File

@@ -0,0 +1,88 @@
package com.njcn.rdms.module.project.controller.admin.status;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelRespVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionPageReqVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionRespVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionSaveReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import com.njcn.rdms.module.project.service.status.ObjectStatusTransitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
/**
* 对象状态流转管理接口。
*/
@Tag(name = "管理后台 - 对象状态流转")
@RestController
@RequestMapping("/project/status/transition")
@Validated
public class ObjectStatusTransitionController {
@Resource
private ObjectStatusTransitionService objectStatusTransitionService;
@PostMapping("/create")
@Operation(summary = "创建对象状态流转")
public CommonResult<Long> createTransition(@Valid @RequestBody ObjectStatusTransitionSaveReqVO createReqVO) {
return success(objectStatusTransitionService.createTransition(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改对象状态流转")
public CommonResult<Boolean> updateTransition(@Valid @RequestBody ObjectStatusTransitionSaveReqVO updateReqVO) {
objectStatusTransitionService.updateTransition(updateReqVO);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获取对象状态流转")
@Parameter(name = "id", description = "对象状态流转Id", required = true, example = "1024")
public CommonResult<ObjectStatusTransitionRespVO> getTransition(@RequestParam("id") Long id) {
ObjectStatusTransitionRespVO transition = objectStatusTransitionService.getTransition(id);
return success(BeanUtils.toBean(transition, ObjectStatusTransitionRespVO.class));
}
@DeleteMapping("/delete")
@Operation(summary = "删除对象状态流转")
@Parameter(name = "id", description = "状态流转Id", required = true, example = "1024")
public CommonResult<Boolean> deleteTransition(@RequestParam("id") Long id) {
objectStatusTransitionService.deleteTransition(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "状态流转Id列表", required = true)
@Operation(summary = "批量删除对象状态流转")
public CommonResult<Boolean> deleteModelList(@RequestParam("ids") List<Long> ids) {
objectStatusTransitionService.deleteTransitionList(ids);
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获取对象状态流转分页")
public CommonResult<PageResult<ObjectStatusTransitionRespVO>> getTransitionPage(
@Valid ObjectStatusTransitionPageReqVO pageReqVO) {
PageResult<ObjectStatusTransitionDO> pageResult = objectStatusTransitionService.getTransitionPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ObjectStatusTransitionRespVO.class));
}
}

View File

@@ -0,0 +1,35 @@
package com.njcn.rdms.module.project.controller.admin.status.vo.model;
import com.njcn.rdms.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 管理后台 - 对象状态模型分页 Request VO
*/
@Schema(description = "管理后台 - 对象状态模型分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ObjectStatusModelPageReqVO extends PageParam {
@Schema(description = "关键字,匹配状态编码或状态名称", example = "active")
private String keyword;
@Schema(description = "对象类型", example = "project")
private String objectType;
@Schema(description = "配置状态", example = "0")
private Integer status;
@Schema(description = "是否初始状态", example = "true")
private Boolean initialFlag;
@Schema(description = "是否终态", example = "false")
private Boolean terminalFlag;
// @Schema(description = "更新时间", example = "[2026-05-01 00:00:00, 2026-05-31 23:59:59]")
// @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
// private LocalDateTime[] updateTime;
}

View File

@@ -0,0 +1,51 @@
package com.njcn.rdms.module.project.controller.admin.status.vo.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 管理后台 - 对象状态模型 Response VO
*/
@Schema(description = "管理后台 - 对象状态模型 Response VO")
@Data
public class ObjectStatusModelRespVO {
@Schema(description = "主键 ID", example = "1024")
private Long id;
@Schema(description = "对象类型", example = "project")
private String objectType;
@Schema(description = "状态编码", example = "active")
private String statusCode;
@Schema(description = "状态名称", example = "进行中")
private String statusName;
@Schema(description = "排序值", example = "1")
private Integer sort;
@Schema(description = "配置状态", example = "0")
private Integer status;
@Schema(description = "是否初始状态", example = "true")
private Boolean initialFlag;
@Schema(description = "是否终态", example = "false")
private Boolean terminalFlag;
@Schema(description = "是否允许编辑对象主数据", example = "true")
private Boolean allowEdit;
@Schema(description = "备注", example = "默认启用状态")
private String remark;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,58 @@
package com.njcn.rdms.module.project.controller.admin.status.vo.model;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 管理后台 - 对象状态模型保存 Request VO
*/
@Schema(description = "管理后台 - 对象状态模型保存 Request VO")
@Data
public class ObjectStatusModelSaveReqVO {
@Schema(description = "主键 ID", example = "1024")
private Long id;
@Schema(description = "对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "project")
@NotBlank(message = "对象类型不能为空")
@Size(max = 64, message = "对象类型长度不能超过64个字符")
private String objectType;
@Schema(description = "状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "active")
@NotBlank(message = "状态编码不能为空")
@Size(max = 64, message = "状态编码长度不能超过64个字符")
private String statusCode;
@Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")
@NotBlank(message = "状态名称不能为空")
@Size(max = 100, message = "状态名称长度不能超过100个字符")
private String statusName;
@Schema(description = "排序值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "排序值不能为空")
private Integer sort;
@Schema(description = "配置状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "配置状态不能为空")
private Integer status;
@Schema(description = "是否初始状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否初始状态不能为空")
private Boolean initialFlag;
@Schema(description = "是否终态", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否终态不能为空")
private Boolean terminalFlag;
@Schema(description = "是否允许编辑对象主数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否允许编辑不能为空")
private Boolean allowEdit;
@Schema(description = "备注", example = "默认状态")
@Size(max = 500, message = "备注长度不能超过500个字符")
private String remark;
}

View File

@@ -0,0 +1,40 @@
package com.njcn.rdms.module.project.controller.admin.status.vo.transition;
import com.njcn.rdms.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 管理后台 - 对象状态流转分页 Request VO
*/
@Schema(description = "管理后台 - 对象状态流转分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ObjectStatusTransitionPageReqVO extends PageParam {
@Schema(description = "关键字,匹配动作编码或动作名称", example = "start")
private String keyword;
@Schema(description = "对象类型", example = "project")
private String objectType;
@Schema(description = "起始状态编码", example = "pending")
private String fromStatusCode;
@Schema(description = "目标状态编码", example = "active")
private String toStatusCode;
@Schema(description = "配置状态", example = "0")
private Integer status;
@Schema(description = "更新时间", example = "[2026-05-01 00:00:00, 2026-05-31 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] updateTime;
}

View File

@@ -0,0 +1,48 @@
package com.njcn.rdms.module.project.controller.admin.status.vo.transition;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 管理后台 - 对象状态流转 Response VO
*/
@Schema(description = "管理后台 - 对象状态流转 Response VO")
@Data
public class ObjectStatusTransitionRespVO {
@Schema(description = "主键 ID", example = "1024")
private Long id;
@Schema(description = "对象类型", example = "project")
private String objectType;
@Schema(description = "动作编码", example = "start")
private String actionCode;
@Schema(description = "动作名称", example = "开始")
private String actionName;
@Schema(description = "起始状态编码", example = "pending")
private String fromStatusCode;
@Schema(description = "目标状态编码", example = "active")
private String toStatusCode;
@Schema(description = "是否必须填写原因", example = "false")
private Boolean needReason;
@Schema(description = "配置状态", example = "0")
private Integer status;
@Schema(description = "备注", example = "默认流转")
private String remark;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,56 @@
package com.njcn.rdms.module.project.controller.admin.status.vo.transition;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 管理后台 - 对象状态流转保存 Request VO
*/
@Schema(description = "管理后台 - 对象状态流转保存 Request VO")
@Data
public class ObjectStatusTransitionSaveReqVO {
@Schema(description = "主键 ID", example = "1024")
private Long id;
@Schema(description = "对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "project")
@NotBlank(message = "对象类型不能为空")
@Size(max = 64, message = "对象类型长度不能超过64个字符")
private String objectType;
@Schema(description = "动作编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "start")
@NotBlank(message = "动作编码不能为空")
@Size(max = 64, message = "动作编码长度不能超过64个字符")
private String actionCode;
@Schema(description = "动作名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "开始")
@NotBlank(message = "动作名称不能为空")
@Size(max = 100, message = "动作名称长度不能超过100个字符")
private String actionName;
@Schema(description = "起始状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "pending")
@NotBlank(message = "起始状态编码不能为空")
@Size(max = 64, message = "起始状态编码长度不能超过64个字符")
private String fromStatusCode;
@Schema(description = "目标状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "active")
@NotBlank(message = "目标状态编码不能为空")
@Size(max = 64, message = "目标状态编码长度不能超过64个字符")
private String toStatusCode;
@Schema(description = "是否必须填写原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否必须填写原因不能为空")
private Boolean needReason;
@Schema(description = "配置状态0启用 1停用", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "配置状态不能为空")
private Integer status;
@Schema(description = "备注", example = "默认流转")
@Size(max = 500, message = "备注长度不能超过500个字符")
private String remark;
}

View File

@@ -1,9 +1,13 @@
package com.njcn.rdms.module.project.dal.mysql.status;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelPageReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.stream.Collectors;
@@ -11,6 +15,24 @@ import java.util.stream.Collectors;
@Mapper
public interface ObjectStatusModelMapper extends BaseMapperX<ObjectStatusModelDO> {
default PageResult<ObjectStatusModelDO> selectPage(ObjectStatusModelPageReqVO reqVO) {
LambdaQueryWrapperX<ObjectStatusModelDO> queryWrapper = new LambdaQueryWrapperX<>();
if (StringUtils.hasText(reqVO.getKeyword())) {
queryWrapper.and(wrapper -> wrapper.like(ObjectStatusModelDO::getStatusCode, reqVO.getKeyword())
.or()
.like(ObjectStatusModelDO::getStatusName, reqVO.getKeyword()));
}
queryWrapper.eqIfPresent(ObjectStatusModelDO::getObjectType, reqVO.getObjectType())
.eqIfPresent(ObjectStatusModelDO::getStatus, reqVO.getStatus())
.eqIfPresent(ObjectStatusModelDO::getInitialFlag, reqVO.getInitialFlag())
.eqIfPresent(ObjectStatusModelDO::getTerminalFlag, reqVO.getTerminalFlag())
// .betweenIfPresent(BaseDO::getUpdateTime, reqVO.getUpdateTime())
// .orderByAsc(ObjectStatusModelDO::getObjectType)
.orderByAsc(ObjectStatusModelDO::getSort)
.orderByAsc(ObjectStatusModelDO::getUpdateTime);
return selectPage(reqVO, queryWrapper);
}
default ObjectStatusModelDO selectByObjectTypeAndStatusCode(String objectType, String statusCode) {
return selectOne(new LambdaQueryWrapperX<ObjectStatusModelDO>()
.eq(ObjectStatusModelDO::getObjectType, objectType)
@@ -31,6 +53,13 @@ public interface ObjectStatusModelMapper extends BaseMapperX<ObjectStatusModelDO
.eq(ObjectStatusModelDO::getStatus, 0));
}
default ObjectStatusModelDO selectInitialByObjectType(String objectType) {
return selectOne(new LambdaQueryWrapperX<ObjectStatusModelDO>()
.eq(ObjectStatusModelDO::getObjectType, objectType)
.eq(ObjectStatusModelDO::getInitialFlag, true));
}
default List<ObjectStatusModelDO> selectListByObjectType(String objectType) {
return selectList(new LambdaQueryWrapperX<ObjectStatusModelDO>()
.eq(ObjectStatusModelDO::getObjectType, objectType)
@@ -70,4 +99,9 @@ public interface ObjectStatusModelMapper extends BaseMapperX<ObjectStatusModelDO
.collect(Collectors.toList());
}
/**
* 物理删除
*/
@Delete("DELETE FROM rdms_object_status_model WHERE id = #{id}")
int physicalDeleteById(Long id);
}

View File

@@ -1,15 +1,49 @@
package com.njcn.rdms.module.project.dal.mysql.status;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionPageReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.util.StringUtils;
import java.util.List;
@Mapper
public interface ObjectStatusTransitionMapper extends BaseMapperX<ObjectStatusTransitionDO> {
default PageResult<ObjectStatusTransitionDO> selectPage(ObjectStatusTransitionPageReqVO reqVO) {
LambdaQueryWrapperX<ObjectStatusTransitionDO> queryWrapper = new LambdaQueryWrapperX<>();
if (StringUtils.hasText(reqVO.getKeyword())) {
queryWrapper.and(wrapper -> wrapper.like(ObjectStatusTransitionDO::getActionCode, reqVO.getKeyword())
.or()
.like(ObjectStatusTransitionDO::getActionName, reqVO.getKeyword()));
}
queryWrapper.eqIfPresent(ObjectStatusTransitionDO::getObjectType, reqVO.getObjectType())
.eqIfPresent(ObjectStatusTransitionDO::getFromStatusCode, reqVO.getFromStatusCode())
.eqIfPresent(ObjectStatusTransitionDO::getToStatusCode, reqVO.getToStatusCode())
.eqIfPresent(ObjectStatusTransitionDO::getStatus, reqVO.getStatus())
.betweenIfPresent(BaseDO::getUpdateTime, reqVO.getUpdateTime())
.orderByAsc(ObjectStatusTransitionDO::getObjectType)
.orderByAsc(ObjectStatusTransitionDO::getFromStatusCode)
.orderByAsc(ObjectStatusTransitionDO::getActionCode)
.orderByAsc(ObjectStatusTransitionDO::getId);
return selectPage(reqVO, queryWrapper);
}
/**
* 按对象类型、起始状态、动作编码查询唯一流转,不区分启停状态。
*/
default ObjectStatusTransitionDO selectByUniqueKey(String objectType, String fromStatusCode, String actionCode) {
return selectOne(new LambdaQueryWrapperX<ObjectStatusTransitionDO>()
.eq(ObjectStatusTransitionDO::getObjectType, objectType)
.eq(ObjectStatusTransitionDO::getFromStatusCode, fromStatusCode)
.eq(ObjectStatusTransitionDO::getActionCode, actionCode));
}
default ObjectStatusTransitionDO selectByObjectTypeAndFromStatusAndAction(String objectType,
String fromStatusCode,
String actionCode) {
@@ -27,4 +61,21 @@ public interface ObjectStatusTransitionMapper extends BaseMapperX<ObjectStatusTr
.eq(ObjectStatusTransitionDO::getStatus, 0));
}
/**
* 统计某状态编码在流转配置中的引用次数。
*/
default Long selectReferenceCountByStatusCode(String objectType, String statusCode) {
return selectCount(new LambdaQueryWrapperX<ObjectStatusTransitionDO>()
.eq(ObjectStatusTransitionDO::getObjectType, objectType)
.and(wrapper -> wrapper.eq(ObjectStatusTransitionDO::getFromStatusCode, statusCode)
.or()
.eq(ObjectStatusTransitionDO::getToStatusCode, statusCode)));
}
/**
* 物理删除
*/
@Delete("DELETE FROM rdms_object_status_transition WHERE id = #{id}")
int physicalDeleteById(Long id);
}

View File

@@ -0,0 +1,58 @@
package com.njcn.rdms.module.project.service.status;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelPageReqVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelRespVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelSaveReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import java.util.List;
/**
* 对象状态模型 Service 接口。
*/
public interface ObjectStatusModelService {
/**
* 新增状态模型。
*
* @param createReqVO 新增请求
* @return 新记录编号
*/
Long createModel(ObjectStatusModelSaveReqVO createReqVO);
/**
* 修改状态模型。
*
* @param updateReqVO 修改请求
*/
void updateModel(ObjectStatusModelSaveReqVO updateReqVO);
/**
* 获得状态模型。
* @param id
* @return
*/
ObjectStatusModelRespVO getModel(Long id);
/**
* 删除状态模型。
*
* @param id 状态模型编号
*/
void deleteModel(Long id);
/**
* 批量删除状态模型。
* @param ids
*/
void deleteModelList(List<Long> ids);
/**
* 分页查询状态模型。
*
* @param pageReqVO 分页请求
* @return 分页结果
*/
PageResult<ObjectStatusModelDO> getModelPage(ObjectStatusModelPageReqVO pageReqVO);
}

View File

@@ -0,0 +1,134 @@
package com.njcn.rdms.module.project.service.status;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelPageReqVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelRespVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.model.ObjectStatusModelSaveReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusTransitionMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
/**
* 对象状态模型 Service 实现。
*/
@Service
public class ObjectStatusModelServiceImpl implements ObjectStatusModelService {
@Resource
private ObjectStatusModelMapper objectStatusModelMapper;
@Resource
private ObjectStatusTransitionMapper objectStatusTransitionMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createModel(ObjectStatusModelSaveReqVO createReqVO) {
validateDuplicateStatusCode(createReqVO.getObjectType(), createReqVO.getStatusCode(), null);
validateInitialStatusUnique(createReqVO.getObjectType(), createReqVO.getInitialFlag(), null);
ObjectStatusModelDO model = BeanUtil.copyProperties(createReqVO, ObjectStatusModelDO.class);
objectStatusModelMapper.insert(model);
return model.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateModel(ObjectStatusModelSaveReqVO updateReqVO) {
ObjectStatusModelDO existing = validateModelExists(updateReqVO.getId());
ObjectStatusModelDO update = BeanUtil.copyProperties(updateReqVO, ObjectStatusModelDO.class);
// if (!Objects.equals(existing.getObjectType(), update.getObjectType())) {
// throw invalidParamException("对象类型创建后不允许修改");
// }
// if (!Objects.equals(existing.getStatusCode(), update.getStatusCode())) {
// throw invalidParamException("状态编码创建后不允许修改");
// }
validateDuplicateStatusCode(update.getObjectType(), update.getStatusCode(), existing.getId());
validateInitialStatusUnique(update.getObjectType(), update.getInitialFlag(), existing.getId());
existing.setStatusName(update.getStatusName());
existing.setSort(update.getSort());
existing.setStatus(update.getStatus());
existing.setInitialFlag(update.getInitialFlag());
existing.setTerminalFlag(update.getTerminalFlag());
existing.setAllowEdit(update.getAllowEdit());
existing.setRemark(update.getRemark());
objectStatusModelMapper.updateById(existing);
}
@Override
public ObjectStatusModelRespVO getModel(Long id) {
return BeanUtil.copyProperties(objectStatusModelMapper.selectById(id), ObjectStatusModelRespVO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteModel(Long id) {
ObjectStatusModelDO existing = validateModelExists(id);
Long referenceCount = objectStatusTransitionMapper.selectReferenceCountByStatusCode(existing.getObjectType(), existing.getStatusCode());
if (referenceCount != null && referenceCount > 0) {
throw invalidParamException("状态编码 [{}] 已被状态流转引用,不能删除", existing.getStatusCode());
}
objectStatusModelMapper.physicalDeleteById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteModelList(List<Long> ids) {
for (int i = 0; i < ids.size(); i++) {
this.deleteModel(ids.get(i));
}
}
@Override
public PageResult<ObjectStatusModelDO> getModelPage(ObjectStatusModelPageReqVO pageReqVO) {
return objectStatusModelMapper.selectPage(pageReqVO);
}
/**
* 校验同一对象类型下的状态编码不能重复。
*/
private void validateDuplicateStatusCode(String objectType, String statusCode, Long excludeId) {
ObjectStatusModelDO duplicate = objectStatusModelMapper.selectByObjectTypeAndStatusCode(objectType, statusCode);
if (ObjectUtil.isNotNull(duplicate) && !Objects.equals(duplicate.getId(), excludeId)) {
throw invalidParamException("对象状态模型 [{}] 下状态编码 [{}] 已存在", objectType, statusCode);
}
}
/**
* 校验同一对象状态模型只能存在一个初始状态。
*/
private void validateInitialStatusUnique(String objectType, Boolean initialFlag, Long excludeId) {
if (!Boolean.TRUE.equals(initialFlag)) {
return;
}
ObjectStatusModelDO initialStatus = objectStatusModelMapper.selectInitialByObjectType(objectType);
if (ObjectUtil.isNotNull(initialStatus) && !Objects.equals(initialStatus.getId(), excludeId)) {
throw invalidParamException("对象状态模型 [{}] 已存在初始状态,不能重复配置", objectType);
}
}
/**
* 校验状态模型存在。
*/
private ObjectStatusModelDO validateModelExists(Long id) {
ObjectStatusModelDO model = objectStatusModelMapper.selectById(id);
if (Objects.isNull(model)) {
throw invalidParamException("状态模型不存在");
}
return model;
}
}

View File

@@ -0,0 +1,59 @@
package com.njcn.rdms.module.project.service.status;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionPageReqVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionRespVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionSaveReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import java.util.List;
/**
* 对象状态流转 Service 接口。
*/
public interface ObjectStatusTransitionService {
/**
* 新增状态流转。
*
* @param createReqVO 新增请求
* @return 新记录编号
*/
Long createTransition(ObjectStatusTransitionSaveReqVO createReqVO);
/**
* 修改状态流转。
*
* @param updateReqVO 修改请求
*/
void updateTransition(ObjectStatusTransitionSaveReqVO updateReqVO);
/**
* 获取状态流转。
*
* @param id 状态流转编号
* @return 状态流转
*/
ObjectStatusTransitionRespVO getTransition(Long id);
/**
* 删除状态流转。
*
* @param id 状态流转编号
*/
void deleteTransition(Long id);
/**
* 批量删除状态流转
* @param ids
*/
void deleteTransitionList(List<Long> ids);
/**
* 分页查询状态流转。
*
* @param pageReqVO 分页请求
* @return 分页结果
*/
PageResult<ObjectStatusTransitionDO> getTransitionPage(ObjectStatusTransitionPageReqVO pageReqVO);
}

View File

@@ -0,0 +1,139 @@
package com.njcn.rdms.module.project.service.status;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionPageReqVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionRespVO;
import com.njcn.rdms.module.project.controller.admin.status.vo.transition.ObjectStatusTransitionSaveReqVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusTransitionMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
/**
* 对象状态流转 Service 实现。
*/
@Service
public class ObjectStatusTransitionServiceImpl implements ObjectStatusTransitionService {
@Resource
private ObjectStatusTransitionMapper objectStatusTransitionMapper;
@Resource
private ObjectStatusModelMapper objectStatusModelMapper;
private static final String FROM_OBJECT_STATUS_MODEL_NOT_FOUND = "起始对象状态模型不存在";
private static final String TO_OBJECT_STATUS_MODEL_NOT_FOUND = "目标对象状态模型不存在";
@Override
@Transactional(rollbackFor = Exception.class)
public Long createTransition(ObjectStatusTransitionSaveReqVO createReqVO) {
ObjectStatusTransitionDO transition = BeanUtil.copyProperties(createReqVO, ObjectStatusTransitionDO.class);
validateDuplicateTransition(transition.getObjectType(), transition.getFromStatusCode(), transition.getActionCode(), null);
validateStatusModelExists(transition.getObjectType(), transition.getFromStatusCode(), FROM_OBJECT_STATUS_MODEL_NOT_FOUND);
validateStatusModelExists(transition.getObjectType(), transition.getToStatusCode(), TO_OBJECT_STATUS_MODEL_NOT_FOUND);
validateFromAndToStatus(transition.getFromStatusCode(), transition.getToStatusCode());
objectStatusTransitionMapper.insert(transition);
return transition.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateTransition(ObjectStatusTransitionSaveReqVO updateReqVO) {
ObjectStatusTransitionDO existing = validateTransitionExists(updateReqVO.getId());
ObjectStatusTransitionDO update = BeanUtil.copyProperties(updateReqVO, ObjectStatusTransitionDO.class);
// if (!Objects.equals(existing.getObjectType(), update.getObjectType())) {
// throw invalidParamException("对象类型创建后不允许修改");
// }
// if (!Objects.equals(existing.getFromStatusCode(), update.getFromStatusCode())) {
// throw invalidParamException("起始状态编码创建后不允许修改");
// }
// if (!Objects.equals(existing.getActionCode(), update.getActionCode())) {
// throw invalidParamException("动作编码创建后不允许修改");
// }
validateDuplicateTransition(update.getObjectType(), update.getFromStatusCode(), update.getActionCode(), existing.getId());
validateStatusModelExists(update.getObjectType(), update.getFromStatusCode(), FROM_OBJECT_STATUS_MODEL_NOT_FOUND);
validateStatusModelExists(update.getObjectType(), update.getToStatusCode(), TO_OBJECT_STATUS_MODEL_NOT_FOUND);
validateFromAndToStatus(update.getFromStatusCode(), update.getToStatusCode());
BeanUtil.copyProperties(update, existing, "id");
objectStatusTransitionMapper.updateById(existing);
}
@Override
public ObjectStatusTransitionRespVO getTransition(Long id) {
return BeanUtil.copyProperties(objectStatusTransitionMapper.selectById(id), ObjectStatusTransitionRespVO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteTransition(Long id) {
validateTransitionExists(id);
objectStatusTransitionMapper.physicalDeleteById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteTransitionList(List<Long> ids) {
for (int i = 0; i < ids.size(); i++) {
this.deleteTransition(ids.get(i));
}
}
@Override
public PageResult<ObjectStatusTransitionDO> getTransitionPage(ObjectStatusTransitionPageReqVO pageReqVO) {
return objectStatusTransitionMapper.selectPage(pageReqVO);
}
/**
* 校验同一对象类型、起始状态、动作编码的流转不能重复。
*/
private void validateDuplicateTransition(String objectType, String fromStatusCode, String actionCode, Long excludeId) {
ObjectStatusTransitionDO duplicate = objectStatusTransitionMapper.selectByUniqueKey(objectType, fromStatusCode, actionCode);
if (ObjectUtil.isNotNull(duplicate) && !Objects.equals(duplicate.getId(), excludeId)) {
throw invalidParamException("对象类型 [{}] 下起始状态 [{}] 与动作编码 [{}] 的流转已存在",
objectType, fromStatusCode, actionCode);
}
}
/**
* 校验流转引用的状态编码存在。
*/
private void validateStatusModelExists(String objectType, String statusCode, String message) {
ObjectStatusModelDO objectStatusModel = objectStatusModelMapper.selectByObjectTypeAndStatusCode(objectType, statusCode);
if (Objects.isNull(objectStatusModel)) {
throw invalidParamException(message);
}
}
/**
* 校验起始状态和目标状态不能相同。
*/
private void validateFromAndToStatus(String fromStatusCode, String toStatusCode) {
if (Objects.equals(fromStatusCode, toStatusCode)) {
throw invalidParamException("起始状态和目标状态不能相同");
}
}
/**
* 校验状态流转存在。
*/
private ObjectStatusTransitionDO validateTransitionExists(Long id) {
ObjectStatusTransitionDO transition = objectStatusTransitionMapper.selectById(id);
if (Objects.isNull(transition)) {
throw invalidParamException("状态流转不存在");
}
return transition;
}
}

View File

@@ -24,7 +24,8 @@
<!-- Web 相关 -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- 参数校验 -->
@@ -43,4 +44,4 @@
</dependencies>
</project>
</project>