Commit 528a321f0aa178b4b36b764075371ca1d3ada7e0
1 parent
acea73c9
BPM:增加「发起人自选」的任务审批人的分配策略
Showing
33 changed files
with
305 additions
and
189 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java
| ... | ... | @@ -32,14 +32,13 @@ public class BpmProcessInstanceCreateReqDTO { |
| 32 | 32 | @NotEmpty(message = "业务的唯一标识") |
| 33 | 33 | private String businessKey; |
| 34 | 34 | |
| 35 | - // TODO @hai:assignees 复数 | |
| 36 | 35 | /** |
| 37 | - * 提前指派的审批人 | |
| 36 | + * 发起人自选审批人 Map | |
| 38 | 37 | * |
| 39 | 38 | * key:taskKey 任务编码 |
| 40 | 39 | * value:审批人的数组 |
| 41 | - * 例如: { taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批 | |
| 40 | + * 例如:{ taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批 | |
| 42 | 41 | */ |
| 43 | - private Map<String, List<Long>> assignee; | |
| 42 | + private Map<String, List<Long>> startUserSelectAssignees; | |
| 44 | 43 | |
| 45 | 44 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| ... | ... | @@ -29,12 +29,13 @@ public interface ErrorCodeConstants { |
| 29 | 29 | ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); |
| 30 | 30 | ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在"); |
| 31 | 31 | ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态"); |
| 32 | - ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1_009_003_004, "流程定义的模型不存在"); | |
| 33 | 32 | |
| 34 | 33 | // ========== 流程实例 1-009-004-000 ========== |
| 35 | 34 | ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在"); |
| 36 | 35 | ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中"); |
| 37 | 36 | ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的"); |
| 37 | + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "审批任务({})的审批人未配置"); | |
| 38 | + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "审批任务({})的审批人({})不存在"); | |
| 38 | 39 | |
| 39 | 40 | // ========== 流程任务 1-009-005-000 ========== |
| 40 | 41 | ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); |
| ... | ... | @@ -50,8 +51,6 @@ public interface ErrorCodeConstants { |
| 50 | 51 | ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); |
| 51 | 52 | ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); |
| 52 | 53 | ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); |
| 53 | - | |
| 54 | - // ========== 流程任务分配规则 1-009-006-000 TODO 芋艿:这里要改下 ========== | |
| 55 | 54 | ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); |
| 56 | 55 | |
| 57 | 56 | // ========== 动态表单模块 1-009-010-000 ========== | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java
| ... | ... | @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConver |
| 9 | 9 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 10 | 10 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 11 | 11 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 12 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; | |
| 12 | 13 | import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; |
| 13 | 14 | import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; |
| 14 | 15 | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| ... | ... | @@ -16,6 +17,8 @@ import io.swagger.v3.oas.annotations.Operation; |
| 16 | 17 | import io.swagger.v3.oas.annotations.Parameter; |
| 17 | 18 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 18 | 19 | import jakarta.annotation.Resource; |
| 20 | +import org.flowable.bpmn.model.BpmnModel; | |
| 21 | +import org.flowable.bpmn.model.UserTask; | |
| 19 | 22 | import org.flowable.engine.repository.Deployment; |
| 20 | 23 | import org.flowable.engine.repository.ProcessDefinition; |
| 21 | 24 | import org.springframework.security.access.prepost.PreAuthorize; |
| ... | ... | @@ -89,13 +92,23 @@ public class BpmProcessDefinitionController { |
| 89 | 92 | list, null, processDefinitionMap, null, null)); |
| 90 | 93 | } |
| 91 | 94 | |
| 92 | - @GetMapping ("/get-bpmn-xml") | |
| 93 | - @Operation(summary = "获得流程定义的 BPMN XML") | |
| 94 | - @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 95 | + @GetMapping ("/get") | |
| 96 | + @Operation(summary = "获得流程定义") | |
| 97 | + @Parameter(name = "id", description = "流程编号", required = true, example = "1024") | |
| 98 | + @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024") | |
| 95 | 99 | @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')") |
| 96 | - public CommonResult<String> getProcessDefinitionBpmnXML(@RequestParam("id") String id) { | |
| 97 | - String bpmnXML = processDefinitionService.getProcessDefinitionBpmnXML(id); | |
| 98 | - return success(bpmnXML); | |
| 100 | + public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition( | |
| 101 | + @RequestParam(value = "id", required = false) String id, | |
| 102 | + @RequestParam(value = "key", required = false) String key) { | |
| 103 | + ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id) | |
| 104 | + : processDefinitionService.getActiveProcessDefinition(key); | |
| 105 | + if (processDefinition == null) { | |
| 106 | + return success(null); | |
| 107 | + } | |
| 108 | + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); | |
| 109 | + List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); | |
| 110 | + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( | |
| 111 | + processDefinition, null, null, null, null, bpmnModel, userTaskList)); | |
| 99 | 112 | } |
| 100 | 113 | |
| 101 | 114 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java
| ... | ... | @@ -18,6 +18,9 @@ public class BpmCategorySaveReqVO { |
| 18 | 18 | @NotEmpty(message = "分类名不能为空") |
| 19 | 19 | private String name; |
| 20 | 20 | |
| 21 | + @Schema(description = "分类描述", example = "你猜") | |
| 22 | + private String description; | |
| 23 | + | |
| 21 | 24 | @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") |
| 22 | 25 | @NotEmpty(message = "分类标志不能为空") |
| 23 | 26 | private String code; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java
| ... | ... | @@ -48,7 +48,7 @@ public class BpmProcessDefinitionRespVO { |
| 48 | 48 | private String formCustomViewPath; |
| 49 | 49 | |
| 50 | 50 | @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 51 | - private Integer suspensionState; | |
| 51 | + private Integer suspensionState; // 参见 SuspensionState 枚举 | |
| 52 | 52 | |
| 53 | 53 | @Schema(description = "部署时间") |
| 54 | 54 | private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回 |
| ... | ... | @@ -56,4 +56,19 @@ public class BpmProcessDefinitionRespVO { |
| 56 | 56 | @Schema(description = "BPMN XML") |
| 57 | 57 | private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回 |
| 58 | 58 | |
| 59 | + @Schema(description = "发起用户需要选择审批人的任务数组") | |
| 60 | + private List<UserTask> startUserSelectTasks; // 需要从对应的 BpmnModel 读取,非必须返回 | |
| 61 | + | |
| 62 | + @Schema(description = "BPMN UserTask 用户任务") | |
| 63 | + @Data | |
| 64 | + public static class UserTask { | |
| 65 | + | |
| 66 | + @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "sudo") | |
| 67 | + private String id; | |
| 68 | + | |
| 69 | + @Schema(description = "任务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") | |
| 70 | + private String name; | |
| 71 | + | |
| 72 | + } | |
| 73 | + | |
| 59 | 74 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java
| ... | ... | @@ -7,6 +7,8 @@ import lombok.Data; |
| 7 | 7 | import org.springframework.format.annotation.DateTimeFormat; |
| 8 | 8 | |
| 9 | 9 | import java.time.LocalDateTime; |
| 10 | +import java.util.List; | |
| 11 | +import java.util.Map; | |
| 10 | 12 | |
| 11 | 13 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
| 12 | 14 | |
| ... | ... | @@ -30,6 +32,9 @@ public class BpmOALeaveCreateReqVO { |
| 30 | 32 | @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") |
| 31 | 33 | private String reason; |
| 32 | 34 | |
| 35 | + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") | |
| 36 | + private Map<String, List<Long>> startUserSelectAssignees; | |
| 37 | + | |
| 33 | 38 | @AssertTrue(message = "结束时间,需要在开始时间之后") |
| 34 | 39 | public boolean isEndTimeValid() { |
| 35 | 40 | return !getEndTime().isBefore(getStartTime()); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
| ... | ... | @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI |
| 11 | 11 | import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; |
| 12 | 12 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 13 | 13 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 14 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 14 | 15 | import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; |
| 15 | 16 | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| 16 | 17 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; |
| ... | ... | @@ -130,7 +131,8 @@ public class BpmProcessInstanceController { |
| 130 | 131 | processInstance.getProcessDefinitionId()); |
| 131 | 132 | BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( |
| 132 | 133 | processInstance.getProcessDefinitionId()); |
| 133 | - String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); | |
| 134 | + String bpmnXml = BpmnModelUtils.getBpmnXml( | |
| 135 | + processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId())); | |
| 134 | 136 | AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); |
| 135 | 137 | DeptRespDTO dept = null; |
| 136 | 138 | if (startUser != null) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java
| ... | ... | @@ -18,8 +18,7 @@ public class BpmProcessInstanceCreateReqVO { |
| 18 | 18 | @Schema(description = "变量实例(动态表单)") |
| 19 | 19 | private Map<String, Object> variables; |
| 20 | 20 | |
| 21 | - // TODO @hai:assignees 复数 | |
| 22 | - @Schema(description = "提前指派的审批人", requiredMode = Schema.RequiredMode.REQUIRED, example = "{taskKey1: [1, 2]}") | |
| 23 | - private Map<String, List<Long>> assignee; | |
| 21 | + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") | |
| 22 | + private Map<String, List<Long>> startUserSelectAssignees; | |
| 24 | 23 | |
| 25 | 24 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java
| ... | ... | @@ -4,12 +4,14 @@ import cn.hutool.core.date.LocalDateTimeUtil; |
| 4 | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 8 | 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 9 | 8 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; |
| 10 | 9 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 11 | 10 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 12 | 11 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 12 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 13 | +import org.flowable.bpmn.model.BpmnModel; | |
| 14 | +import org.flowable.bpmn.model.UserTask; | |
| 13 | 15 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 14 | 16 | import org.flowable.engine.repository.Deployment; |
| 15 | 17 | import org.flowable.engine.repository.ProcessDefinition; |
| ... | ... | @@ -47,27 +49,50 @@ public interface BpmProcessDefinitionConvert { |
| 47 | 49 | Map<Long, BpmFormDO> formMap, |
| 48 | 50 | Map<String, BpmCategoryDO> categoryMap) { |
| 49 | 51 | return CollectionUtils.convertList(list, definition -> { |
| 50 | - BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class); | |
| 51 | - respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); | |
| 52 | - // Deployment | |
| 53 | - MapUtils.findAndThen(deploymentMap, definition.getDeploymentId(), | |
| 54 | - deployment -> respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()))); | |
| 55 | - // BpmProcessDefinitionInfoDO | |
| 52 | + Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class); | |
| 56 | 53 | BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class); |
| 54 | + BpmFormDO form = null; | |
| 57 | 55 | if (processDefinitionInfo != null) { |
| 58 | - copyTo(processDefinitionInfo, respVO); | |
| 59 | - // Form | |
| 60 | - BpmFormDO form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class); | |
| 61 | - if (form != null) { | |
| 62 | - respVO.setFormName(form.getName()); | |
| 63 | - } | |
| 56 | + form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class); | |
| 64 | 57 | } |
| 65 | - // Category | |
| 66 | - MapUtils.findAndThen(categoryMap, definition.getCategory(), category -> respVO.setCategoryName(category.getName())); | |
| 67 | - return respVO; | |
| 58 | + BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class); | |
| 59 | + return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null); | |
| 68 | 60 | }); |
| 69 | 61 | } |
| 70 | 62 | |
| 63 | + default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition, | |
| 64 | + Deployment deployment, | |
| 65 | + BpmProcessDefinitionInfoDO processDefinitionInfo, | |
| 66 | + BpmFormDO form, | |
| 67 | + BpmCategoryDO category, | |
| 68 | + BpmnModel bpmnModel, | |
| 69 | + List<UserTask> startUserSelectUserTaskList) { | |
| 70 | + BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class); | |
| 71 | + respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); | |
| 72 | + // Deployment | |
| 73 | + if (deployment != null) { | |
| 74 | + respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); | |
| 75 | + } | |
| 76 | + // BpmProcessDefinitionInfoDO | |
| 77 | + if (processDefinitionInfo != null) { | |
| 78 | + copyTo(processDefinitionInfo, respVO); | |
| 79 | + // Form | |
| 80 | + if (form != null) { | |
| 81 | + respVO.setFormName(form.getName()); | |
| 82 | + } | |
| 83 | + } | |
| 84 | + // Category | |
| 85 | + if (category != null) { | |
| 86 | + respVO.setCategoryName(category.getName()); | |
| 87 | + } | |
| 88 | + // BpmnModel | |
| 89 | + if (bpmnModel != null) { | |
| 90 | + respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); | |
| 91 | + respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionRespVO.UserTask.class)); | |
| 92 | + } | |
| 93 | + return respVO; | |
| 94 | + } | |
| 95 | + | |
| 71 | 96 | @Mapping(source = "from.id", target = "to.id", ignore = true) |
| 72 | 97 | void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); |
| 73 | 98 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
| ... | ... | @@ -68,7 +68,7 @@ public interface BpmProcessInstanceConvert { |
| 68 | 68 | DeptRespDTO dept) { |
| 69 | 69 | BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class); |
| 70 | 70 | respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)); |
| 71 | - respVO.setFormVariables(FlowableUtils.filterProcessInstanceFormVariable(processInstance.getProcessVariables())); | |
| 71 | + respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance)); | |
| 72 | 72 | // definition |
| 73 | 73 | respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); |
| 74 | 74 | copyTo(processDefinitionExt, respVO.getProcessDefinition()); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmCategoryDO.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.dal.dataobject.definition; |
| 2 | 2 | |
| 3 | -import lombok.*; | |
| 4 | -import java.util.*; | |
| 5 | -import java.time.LocalDateTime; | |
| 6 | -import java.time.LocalDateTime; | |
| 7 | -import com.baomidou.mybatisplus.annotation.*; | |
| 8 | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import com.baomidou.mybatisplus.annotation.KeySequence; | |
| 5 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 6 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 7 | +import lombok.*; | |
| 9 | 8 | |
| 10 | 9 | /** |
| 11 | 10 | * BPM 流程分类 DO |
| ... | ... | @@ -42,7 +41,7 @@ public class BpmCategoryDO extends BaseDO { |
| 42 | 41 | /** |
| 43 | 42 | * 分类状态 |
| 44 | 43 | * |
| 45 | - * 枚举 {@link TODO common_status 对应的类} | |
| 44 | + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} | |
| 46 | 45 | */ |
| 47 | 46 | private Integer status; |
| 48 | 47 | /** | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/category/BpmCategoryMapper.java
| ... | ... | @@ -21,7 +21,7 @@ public interface BpmCategoryMapper extends BaseMapperX<BpmCategoryDO> { |
| 21 | 21 | default PageResult<BpmCategoryDO> selectPage(BpmCategoryPageReqVO reqVO) { |
| 22 | 22 | return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>() |
| 23 | 23 | .likeIfPresent(BpmCategoryDO::getName, reqVO.getName()) |
| 24 | - .eqIfPresent(BpmCategoryDO::getCode, reqVO.getCode()) | |
| 24 | + .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode()) | |
| 25 | 25 | .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus()) |
| 26 | 26 | .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime()) |
| 27 | 27 | .orderByAsc(BpmCategoryDO::getSort)); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/FlowableContextHolder.java deleted
| 1 | -package cn.iocoder.yudao.module.bpm.framework; | |
| 2 | - | |
| 3 | -import cn.hutool.core.collection.CollUtil; | |
| 4 | -import com.alibaba.ttl.TransmittableThreadLocal; | |
| 5 | - | |
| 6 | -import java.util.Collections; | |
| 7 | -import java.util.List; | |
| 8 | -import java.util.Map; | |
| 9 | - | |
| 10 | -/** | |
| 11 | - * 工作流--用户用到的上下文相关信息 | |
| 12 | - */ | |
| 13 | -@Deprecated // TODO 芋艿:找个方式,去掉这个上下文 | |
| 14 | -public class FlowableContextHolder { | |
| 15 | - | |
| 16 | - private static final ThreadLocal<Map<String, List<Long>>> ASSIGNEE = new TransmittableThreadLocal<>(); | |
| 17 | - | |
| 18 | - /** | |
| 19 | - * 通过流程任务的定义 key ,拿到提前选好的审批人 | |
| 20 | - * 此方法目的:首次创建流程实例时,数据库中还查询不到 assignee 字段,所以存入上下文中获取 | |
| 21 | - * | |
| 22 | - * @param taskDefinitionKey 流程任务 key | |
| 23 | - * @return 审批人 ID 集合 | |
| 24 | - */ | |
| 25 | - public static List<Long> getAssigneeByTaskDefinitionKey(String taskDefinitionKey) { | |
| 26 | - if (CollUtil.isNotEmpty(ASSIGNEE.get())) { | |
| 27 | - return ASSIGNEE.get().get(taskDefinitionKey); | |
| 28 | - } | |
| 29 | - return Collections.emptyList(); | |
| 30 | - } | |
| 31 | - | |
| 32 | - /** | |
| 33 | - * 存入提前选好的审批人到上下文线程变量中 | |
| 34 | - * | |
| 35 | - * @param assignee 流程任务 key -> 审批人 ID 炅和 | |
| 36 | - */ | |
| 37 | - public static void setAssignee(Map<String, List<Long>> assignee) { | |
| 38 | - ASSIGNEE.set(assignee); | |
| 39 | - } | |
| 40 | - | |
| 41 | -} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
| ... | ... | @@ -4,16 +4,13 @@ import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.lang.Assert; |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 7 | -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 8 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 9 | 7 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; |
| 10 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | |
| 8 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 11 | 9 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 12 | 10 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 13 | 11 | import com.google.common.annotations.VisibleForTesting; |
| 14 | 12 | import lombok.extern.slf4j.Slf4j; |
| 15 | 13 | import org.flowable.bpmn.model.BpmnModel; |
| 16 | -import org.flowable.bpmn.model.FlowElement; | |
| 17 | 14 | import org.flowable.bpmn.model.UserTask; |
| 18 | 15 | import org.flowable.engine.delegate.DelegateExecution; |
| 19 | 16 | |
| ... | ... | @@ -60,9 +57,13 @@ public class BpmTaskCandidateInvoker { |
| 60 | 57 | // 遍历所有的 UserTask,校验审批人配置 |
| 61 | 58 | userTaskList.forEach(userTask -> { |
| 62 | 59 | // 1. 非空校验 |
| 63 | - Integer strategy = parseCandidateStrategy(userTask); | |
| 64 | - String param = parseCandidateParam(userTask); | |
| 65 | - if (strategy == null || StrUtil.isBlank(param)) { | |
| 60 | + Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask); | |
| 61 | + String param = BpmnModelUtils.parseCandidateParam(userTask); | |
| 62 | + if (strategy == null) { | |
| 63 | + throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); | |
| 64 | + } | |
| 65 | + BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy); | |
| 66 | + if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) { | |
| 66 | 67 | throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); |
| 67 | 68 | } |
| 68 | 69 | // 2. 具体策略校验 |
| ... | ... | @@ -77,16 +78,8 @@ public class BpmTaskCandidateInvoker { |
| 77 | 78 | * @return 用户编号集合 |
| 78 | 79 | */ |
| 79 | 80 | public Set<Long> calculateUsers(DelegateExecution execution) { |
| 80 | - // TODO 芋艿:这里需要重构 | |
| 81 | -// // 1. 先从提前选好的审批人中获取 | |
| 82 | -// List<Long> assignee = processInstanceService.getAssigneeByProcessInstanceIdAndTaskDefinitionKey( | |
| 83 | -// execution.getProcessInstanceId(), execution.getCurrentActivityId()); | |
| 84 | -// if (CollUtil.isNotEmpty(assignee)) { | |
| 85 | -// // TODO @hai:new HashSet 即可 | |
| 86 | -// return convertSet(assignee, Function.identity()); | |
| 87 | -// } | |
| 88 | - Integer strategy = parseCandidateStrategy(execution.getCurrentFlowElement()); | |
| 89 | - String param = parseCandidateParam(execution.getCurrentFlowElement()); | |
| 81 | + Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement()); | |
| 82 | + String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement()); | |
| 90 | 83 | // 1.1 计算任务的候选人 |
| 91 | 84 | Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param); |
| 92 | 85 | // 1.2 移除被禁用的用户 |
| ... | ... | @@ -113,16 +106,6 @@ public class BpmTaskCandidateInvoker { |
| 113 | 106 | }); |
| 114 | 107 | } |
| 115 | 108 | |
| 116 | - private static Integer parseCandidateStrategy(FlowElement userTask) { | |
| 117 | - return NumberUtils.parseInt(userTask.getAttributeValue( | |
| 118 | - BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); | |
| 119 | - } | |
| 120 | - | |
| 121 | - private static String parseCandidateParam(FlowElement userTask) { | |
| 122 | - return userTask.getAttributeValue( | |
| 123 | - BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); | |
| 124 | - } | |
| 125 | - | |
| 126 | 109 | private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { |
| 127 | 110 | BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); |
| 128 | 111 | Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java
| ... | ... | @@ -36,4 +36,13 @@ public interface BpmTaskCandidateStrategy { |
| 36 | 36 | */ |
| 37 | 37 | Set<Long> calculateUsers(DelegateExecution execution, String param); |
| 38 | 38 | |
| 39 | + /** | |
| 40 | + * 是否一定要输入参数 | |
| 41 | + * | |
| 42 | + * @return 是否 | |
| 43 | + */ | |
| 44 | + default boolean isParamRequired() { | |
| 45 | + return true; | |
| 46 | + } | |
| 47 | + | |
| 39 | 48 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java
| ... | ... | @@ -32,12 +32,12 @@ public class BpmTaskAssignLeaderExpression { |
| 32 | 32 | private DeptApi deptApi; |
| 33 | 33 | |
| 34 | 34 | @Resource |
| 35 | - private BpmProcessInstanceService bpmProcessInstanceService; | |
| 35 | + private BpmProcessInstanceService processInstanceService; | |
| 36 | 36 | |
| 37 | 37 | protected Set<Long> calculateUsers(DelegateExecution execution, int level) { |
| 38 | 38 | Assert.isTrue(level > 0, "level 必须大于 0"); |
| 39 | 39 | // 获得发起人 |
| 40 | - ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId()); | |
| 40 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); | |
| 41 | 41 | Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); |
| 42 | 42 | // 获得对应 leve 的部门 |
| 43 | 43 | DeptRespDTO dept = null; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java
| ... | ... | @@ -19,10 +19,10 @@ import java.util.Set; |
| 19 | 19 | public class BpmTaskAssignStartUserExpression { |
| 20 | 20 | |
| 21 | 21 | @Resource |
| 22 | - private BpmProcessInstanceService bpmProcessInstanceService; | |
| 22 | + private BpmProcessInstanceService processInstanceService; | |
| 23 | 23 | |
| 24 | 24 | public Set<Long> calculateUsers(DelegateExecution execution) { |
| 25 | - ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId()); | |
| 25 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); | |
| 26 | 26 | Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); |
| 27 | 27 | return SetUtils.asSet(startUserId); |
| 28 | 28 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 5 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | |
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; | |
| 7 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 8 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | |
| 9 | +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | |
| 10 | +import jakarta.annotation.Resource; | |
| 11 | +import org.flowable.bpmn.model.BpmnModel; | |
| 12 | +import org.flowable.bpmn.model.UserTask; | |
| 13 | +import org.flowable.engine.delegate.DelegateExecution; | |
| 14 | +import org.flowable.engine.runtime.ProcessInstance; | |
| 15 | +import org.springframework.context.annotation.Lazy; | |
| 16 | +import org.springframework.stereotype.Component; | |
| 17 | + | |
| 18 | +import java.util.*; | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类 | |
| 22 | + * | |
| 23 | + * @author 芋道源码 | |
| 24 | + */ | |
| 25 | +@Component | |
| 26 | +public class BpmTaskCandidateStartUserSelectStrategy implements BpmTaskCandidateStrategy { | |
| 27 | + | |
| 28 | + @Resource | |
| 29 | + @Lazy // 延迟加载,避免循环依赖 | |
| 30 | + private BpmProcessInstanceService processInstanceService; | |
| 31 | + | |
| 32 | + @Override | |
| 33 | + public BpmTaskCandidateStrategyEnum getStrategy() { | |
| 34 | + return BpmTaskCandidateStrategyEnum.START_USER_SELECT; | |
| 35 | + } | |
| 36 | + | |
| 37 | + @Override | |
| 38 | + public void validateParam(String param) {} | |
| 39 | + | |
| 40 | + @Override | |
| 41 | + public Set<Long> calculateUsers(DelegateExecution execution, String param) { | |
| 42 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); | |
| 43 | + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); | |
| 44 | + Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); | |
| 45 | + Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", | |
| 46 | + execution.getProcessInstanceId()); | |
| 47 | + // 获得审批人 | |
| 48 | + List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); | |
| 49 | + return new LinkedHashSet<>(assignees); | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Override | |
| 53 | + public boolean isParamRequired() { | |
| 54 | + return false; | |
| 55 | + } | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * 获得发起人自选审批人的 UserTask 列表 | |
| 59 | + * | |
| 60 | + * @param bpmnModel BPMN 模型 | |
| 61 | + * @return UserTask 列表 | |
| 62 | + */ | |
| 63 | + public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) { | |
| 64 | + if (bpmnModel == null) { | |
| 65 | + return null; | |
| 66 | + } | |
| 67 | + List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); | |
| 68 | + if (CollUtil.isEmpty(userTaskList)) { | |
| 69 | + return null; | |
| 70 | + } | |
| 71 | + userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask), | |
| 72 | + BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); | |
| 73 | + return userTaskList; | |
| 74 | + } | |
| 75 | + | |
| 76 | +} | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmConstants.java
| ... | ... | @@ -15,6 +15,12 @@ public class BpmConstants { |
| 15 | 15 | * @see ProcessInstance#getProcessVariables() |
| 16 | 16 | */ |
| 17 | 17 | public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; |
| 18 | + /** | |
| 19 | + * 流程实例的变量 - 发起用户选择的审批人 Map | |
| 20 | + * | |
| 21 | + * @see ProcessInstance#getProcessVariables() | |
| 22 | + */ | |
| 23 | + public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; | |
| 18 | 24 | |
| 19 | 25 | /** |
| 20 | 26 | * 任务的变量 - 状态 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
| ... | ... | @@ -20,6 +20,7 @@ public enum BpmTaskCandidateStrategyEnum { |
| 20 | 20 | DEPT_LEADER(21, "部门的负责人"), |
| 21 | 21 | POST(22, "岗位"), |
| 22 | 22 | USER(30, "用户"), |
| 23 | + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 | |
| 23 | 24 | USER_GROUP(40, "用户组"), |
| 24 | 25 | EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager |
| 25 | 26 | ; | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| ... | ... | @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.ArrayUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 6 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | |
| 5 | 7 | import org.flowable.bpmn.converter.BpmnXMLConverter; |
| 6 | 8 | import org.flowable.bpmn.model.Process; |
| 7 | 9 | import org.flowable.bpmn.model.*; |
| ... | ... | @@ -14,6 +16,16 @@ import java.util.*; |
| 14 | 16 | */ |
| 15 | 17 | public class BpmnModelUtils { |
| 16 | 18 | |
| 19 | + public static Integer parseCandidateStrategy(FlowElement userTask) { | |
| 20 | + return NumberUtils.parseInt(userTask.getAttributeValue( | |
| 21 | + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); | |
| 22 | + } | |
| 23 | + | |
| 24 | + public static String parseCandidateParam(FlowElement userTask) { | |
| 25 | + return userTask.getAttributeValue( | |
| 26 | + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); | |
| 27 | + } | |
| 28 | + | |
| 17 | 29 | /** |
| 18 | 30 | * 根据节点,获取入口连线 |
| 19 | 31 | * |
| ... | ... | @@ -91,6 +103,14 @@ public class BpmnModelUtils { |
| 91 | 103 | return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); |
| 92 | 104 | } |
| 93 | 105 | |
| 106 | + public static String getBpmnXml(BpmnModel model) { | |
| 107 | + if (model == null) { | |
| 108 | + return null; | |
| 109 | + } | |
| 110 | + BpmnXMLConverter converter = new BpmnXMLConverter(); | |
| 111 | + return new String(converter.convertToXML(model)); | |
| 112 | + } | |
| 113 | + | |
| 94 | 114 | // ========== 遍历相关的方法 ========== |
| 95 | 115 | |
| 96 | 116 | /** | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
| ... | ... | @@ -12,6 +12,7 @@ import org.flowable.engine.runtime.ProcessInstance; |
| 12 | 12 | import org.flowable.task.api.TaskInfo; |
| 13 | 13 | |
| 14 | 14 | import java.util.HashMap; |
| 15 | +import java.util.List; | |
| 15 | 16 | import java.util.Map; |
| 16 | 17 | |
| 17 | 18 | /** |
| ... | ... | @@ -79,7 +80,7 @@ public class FlowableUtils { |
| 79 | 80 | * @param processInstance 流程实例 |
| 80 | 81 | * @return 表单 |
| 81 | 82 | */ |
| 82 | - public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) { | |
| 83 | + public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) { | |
| 83 | 84 | Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables()); |
| 84 | 85 | filterProcessInstanceFormVariable(formVariables); |
| 85 | 86 | return formVariables; |
| ... | ... | @@ -98,6 +99,18 @@ public class FlowableUtils { |
| 98 | 99 | return processVariables; |
| 99 | 100 | } |
| 100 | 101 | |
| 102 | + /** | |
| 103 | + * 获得流程实例的发起用户选择的审批人 Map | |
| 104 | + * | |
| 105 | + * @param processInstance 流程实例 | |
| 106 | + * @return 发起用户选择的审批人 Map | |
| 107 | + */ | |
| 108 | + @SuppressWarnings("unchecked") | |
| 109 | + public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) { | |
| 110 | + return (Map<String, List<Long>>) processInstance.getProcessVariables().get( | |
| 111 | + BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); | |
| 112 | + } | |
| 113 | + | |
| 101 | 114 | // ========== Task 相关的工具方法 ========== |
| 102 | 115 | |
| 103 | 116 | /** | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceImpl.java
| ... | ... | @@ -33,7 +33,7 @@ public class BpmFormServiceImpl implements BpmFormService { |
| 33 | 33 | |
| 34 | 34 | @Override |
| 35 | 35 | public Long createForm(BpmFormSaveReqVO createReqVO) { |
| 36 | - this.vadateFields(createReqVO.getFields()); | |
| 36 | + this.validateFields(createReqVO.getFields()); | |
| 37 | 37 | // 插入 |
| 38 | 38 | BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class); |
| 39 | 39 | formMapper.insert(form); |
| ... | ... | @@ -43,7 +43,7 @@ public class BpmFormServiceImpl implements BpmFormService { |
| 43 | 43 | |
| 44 | 44 | @Override |
| 45 | 45 | public void updateForm(BpmFormSaveReqVO updateReqVO) { |
| 46 | - vadateFields(updateReqVO.getFields()); | |
| 46 | + validateFields(updateReqVO.getFields()); | |
| 47 | 47 | // 校验存在 |
| 48 | 48 | validateFormExists(updateReqVO.getId()); |
| 49 | 49 | // 更新 |
| ... | ... | @@ -93,7 +93,7 @@ public class BpmFormServiceImpl implements BpmFormService { |
| 93 | 93 | * |
| 94 | 94 | * @param fields field 数组 |
| 95 | 95 | */ |
| 96 | - private void vadateFields(List<String> fields) { | |
| 96 | + private void validateFields(List<String> fields) { | |
| 97 | 97 | if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 |
| 98 | 98 | return; |
| 99 | 99 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro |
| 5 | 5 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 6 | 6 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 7 | 7 | import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; |
| 8 | +import org.flowable.bpmn.model.BpmnModel; | |
| 8 | 9 | import org.flowable.engine.repository.Deployment; |
| 9 | 10 | import org.flowable.engine.repository.Model; |
| 10 | 11 | import org.flowable.engine.repository.ProcessDefinition; |
| ... | ... | @@ -61,12 +62,12 @@ public interface BpmProcessDefinitionService { |
| 61 | 62 | void updateProcessDefinitionState(String id, Integer state); |
| 62 | 63 | |
| 63 | 64 | /** |
| 64 | - * 获得流程定义对应的 BPMN XML | |
| 65 | + * 获得流程定义对应的 BPMN | |
| 65 | 66 | * |
| 66 | 67 | * @param id 流程定义编号 |
| 67 | - * @return BPMN XML | |
| 68 | + * @return BPMN | |
| 68 | 69 | */ |
| 69 | - String getProcessDefinitionBpmnXML(String id); | |
| 70 | + BpmnModel getProcessDefinitionBpmnModel(String id); | |
| 70 | 71 | |
| 71 | 72 | /** |
| 72 | 73 | * 获得流程定义的信息 |
| ... | ... | @@ -89,9 +90,9 @@ public interface BpmProcessDefinitionService { |
| 89 | 90 | } |
| 90 | 91 | |
| 91 | 92 | /** |
| 92 | - * 获得编号对应的 ProcessDefinition | |
| 93 | + * 获得流程定义编号对应的 ProcessDefinition | |
| 93 | 94 | * |
| 94 | - * @param id 编号 | |
| 95 | + * @param id 流程定义编号 | |
| 95 | 96 | * @return 流程定义 |
| 96 | 97 | */ |
| 97 | 98 | ProcessDefinition getProcessDefinition(String id); |
| ... | ... | @@ -139,7 +140,7 @@ public interface BpmProcessDefinitionService { |
| 139 | 140 | * @return 流程部署 Map |
| 140 | 141 | */ |
| 141 | 142 | default Map<String, Deployment> getDeploymentMap(Set<String> ids) { |
| 142 | - return convertMap(getDeployments(ids), Deployment::getId); | |
| 143 | + return convertMap(getDeploymentList(ids), Deployment::getId); | |
| 143 | 144 | } |
| 144 | 145 | |
| 145 | 146 | /** |
| ... | ... | @@ -148,7 +149,7 @@ public interface BpmProcessDefinitionService { |
| 148 | 149 | * @param ids 部署编号的数组 |
| 149 | 150 | * @return 流程部署的数组 |
| 150 | 151 | */ |
| 151 | - List<Deployment> getDeployments(Set<String> ids); | |
| 152 | + List<Deployment> getDeploymentList(Set<String> ids); | |
| 152 | 153 | |
| 153 | 154 | /** |
| 154 | 155 | * 获得 id 对应的 Deployment | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
| ... | ... | @@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConsta |
| 14 | 14 | import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; |
| 15 | 15 | import jakarta.annotation.Resource; |
| 16 | 16 | import lombok.extern.slf4j.Slf4j; |
| 17 | -import org.flowable.bpmn.converter.BpmnXMLConverter; | |
| 18 | 17 | import org.flowable.bpmn.model.BpmnModel; |
| 19 | 18 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 20 | 19 | import org.flowable.engine.RepositoryService; |
| ... | ... | @@ -84,7 +83,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ |
| 84 | 83 | } |
| 85 | 84 | |
| 86 | 85 | @Override |
| 87 | - public List<Deployment> getDeployments(Set<String> ids) { | |
| 86 | + public List<Deployment> getDeploymentList(Set<String> ids) { | |
| 88 | 87 | if (CollUtil.isEmpty(ids)) { |
| 89 | 88 | return emptyList(); |
| 90 | 89 | } |
| ... | ... | @@ -156,13 +155,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ |
| 156 | 155 | } |
| 157 | 156 | |
| 158 | 157 | @Override |
| 159 | - public String getProcessDefinitionBpmnXML(String id) { | |
| 160 | - BpmnModel bpmnModel = repositoryService.getBpmnModel(id); | |
| 161 | - if (bpmnModel == null) { | |
| 162 | - return null; | |
| 163 | - } | |
| 164 | - BpmnXMLConverter converter = new BpmnXMLConverter(); | |
| 165 | - return StrUtil.utf8Str(converter.convertToXML(bpmnModel)); | |
| 158 | + public BpmnModel getProcessDefinitionBpmnModel(String id) { | |
| 159 | + return repositoryService.getBpmnModel(id); | |
| 166 | 160 | } |
| 167 | 161 | |
| 168 | 162 | @Override | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java
| ... | ... | @@ -56,7 +56,8 @@ public class BpmOALeaveServiceImpl implements BpmOALeaveService { |
| 56 | 56 | processInstanceVariables.put("day", day); |
| 57 | 57 | String processInstanceId = processInstanceApi.createProcessInstance(userId, |
| 58 | 58 | new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY) |
| 59 | - .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId()))); | |
| 59 | + .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId())) | |
| 60 | + .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees())); | |
| 60 | 61 | |
| 61 | 62 | // 将工作流的编号,更新到 OA 请假单中 |
| 62 | 63 | leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId)); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
| ... | ... | @@ -50,17 +50,6 @@ public interface BpmProcessInstanceService { |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | 52 | /** |
| 53 | - * 获得流程实例名字 Map | |
| 54 | - * | |
| 55 | - * @param ids 流程实例的编号集合 | |
| 56 | - * @return 对应的映射关系 | |
| 57 | - */ | |
| 58 | - default Map<String, String> getProcessInstanceNameMap(Set<String> ids) { | |
| 59 | - return convertMap(getProcessInstances(ids), | |
| 60 | - ProcessInstance::getProcessInstanceId, ProcessInstance::getName); | |
| 61 | - } | |
| 62 | - | |
| 63 | - /** | |
| 64 | 53 | * 获得历史的流程实例 |
| 65 | 54 | * |
| 66 | 55 | * @param id 流程实例的编号 |
| ... | ... | @@ -152,14 +141,4 @@ public interface BpmProcessInstanceService { |
| 152 | 141 | */ |
| 153 | 142 | void updateProcessInstanceReject(String id, String reason); |
| 154 | 143 | |
| 155 | - // TODO @hai:改成 getProcessInstanceAssigneesByTaskDefinitionKey(String id, String taskDefinitionKey) | |
| 156 | - /** | |
| 157 | - * 获取流程实例中,取出指定流程任务提前指定的审批人 | |
| 158 | - * | |
| 159 | - * @param processInstanceId 流程实例的编号 | |
| 160 | - * @param taskDefinitionKey 流程任务定义的 key | |
| 161 | - * @return 审批人集合 | |
| 162 | - */ | |
| 163 | - List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey); | |
| 164 | - | |
| 165 | 144 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| 1 | -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.FlowableContextHolder; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getAssignee()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getAssignee()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> assignee) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 设置上下文信息 // TODO @hai:要不往 variables 存到一个全局固定 key 里,减少对上下文的依赖 FlowableContextHolder.setAssignee(assignee); // 创建流程实例 FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 管理员取消,不用校验是否为自己的 AdminUserRespDTO user = adminUserApi.getUser(userId); // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 2. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 ProcessInstance processInstance = getProcessInstance(id); deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } @Override public List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey) { // 1. 先从上下文获取,首次提交数据库中查询不到 List<Long> result = FlowableContextHolder.getAssigneeByTaskDefinitionKey(taskDefinitionKey); if (CollUtil.isNotEmpty(result)) { return result; } // 2. 从数据库中获取 // TODO @芋艿:指定审批人,这里的存储方案有问题,后续优化下 // BpmProcessInstanceExtDO instance = processInstanceExtMapper.selectByProcessInstanceId(processInstanceId); // if (instance == null) { // throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); // } // if (CollUtil.isNotEmpty(instance.getAssignee())) { // return instance.getAssignee().get(taskDefinitionKey); // } return Collections.emptyList(); } } | |
| 2 | 1 | \ No newline at end of file |
| 2 | +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, BpmProcessInstancePageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() .includeProcessVariables() .orderByProcessInstanceStartTime().desc(); if (userId != null) { // 【我的流程】菜单时,需要传递该字段 processInstanceQuery.startedBy(String.valueOf(userId)); } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); } if (StrUtil.isNotEmpty(pageReqVO.getName())) { processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); } if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); } if (pageReqVO.getStatus() != null) { processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); } if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); } // 查询数量 long processInstanceCount = processInstanceQuery.count(); if (processInstanceCount == 0) { return PageResult.empty(processInstanceCount); } // 查询列表 List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 管理员取消,不用校验是否为自己的 AdminUserRespDTO user = adminUserApi.getUser(userId); // 2. 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 deleteProcessInstance(cancelReqVO.getId(), BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 } @Override public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 2. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); } @Override public void updateProcessInstanceWhenApprove(ProcessInstance instance) { // 1. 更新流程实例 status runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.APPROVE.getStatus()); // 2. 发送流程被【通过】的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceReject(String id, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 ProcessInstance processInstance = getProcessInstance(id); deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } } | |
| 3 | 3 | \ No newline at end of file | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java
| ... | ... | @@ -30,8 +30,9 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest { |
| 30 | 30 | private AdminUserApi adminUserApi; |
| 31 | 31 | @Mock |
| 32 | 32 | private DeptApi deptApi; |
| 33 | + | |
| 33 | 34 | @Mock |
| 34 | - private BpmProcessInstanceService bpmProcessInstanceService; | |
| 35 | + private BpmProcessInstanceService processInstanceService; | |
| 35 | 36 | |
| 36 | 37 | @Test |
| 37 | 38 | public void testCalculateUsers_noDept() { |
| ... | ... | @@ -96,7 +97,7 @@ public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest { |
| 96 | 97 | // mock 返回 startUserId |
| 97 | 98 | ExecutionEntityImpl processInstance = new ExecutionEntityImpl(); |
| 98 | 99 | processInstance.setStartUserId(String.valueOf(startUserId)); |
| 99 | - when(bpmProcessInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))) | |
| 100 | + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))) | |
| 100 | 101 | .thenReturn(processInstance); |
| 101 | 102 | return execution; |
| 102 | 103 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/category/BpmCategoryServiceImplTest.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.service.category; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 3 | 6 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; |
| 4 | 7 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; |
| 5 | -import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl; | |
| 6 | -import org.junit.jupiter.api.Disabled; | |
| 7 | -import org.junit.jupiter.api.Test; | |
| 8 | - | |
| 9 | -import jakarta.annotation.Resource; | |
| 10 | - | |
| 11 | -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 12 | - | |
| 13 | 8 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 14 | 9 | import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper; |
| 15 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 16 | - | |
| 10 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl; | |
| 11 | +import jakarta.annotation.Resource; | |
| 12 | +import org.junit.jupiter.api.Test; | |
| 17 | 13 | import org.springframework.context.annotation.Import; |
| 18 | 14 | |
| 19 | -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | |
| 20 | -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; | |
| 15 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | |
| 16 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | |
| 17 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | |
| 18 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | |
| 19 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | |
| 21 | 20 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; |
| 22 | -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; | |
| 23 | -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; | |
| 21 | +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS; | |
| 24 | 22 | import static org.junit.jupiter.api.Assertions.*; |
| 25 | 23 | |
| 26 | 24 | /** |
| ... | ... | @@ -40,7 +38,8 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { |
| 40 | 38 | @Test |
| 41 | 39 | public void testCreateCategory_success() { |
| 42 | 40 | // 准备参数 |
| 43 | - BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null); | |
| 41 | + BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null) | |
| 42 | + .setStatus(randomCommonStatus()); | |
| 44 | 43 | |
| 45 | 44 | // 调用 |
| 46 | 45 | Long categoryId = categoryService.createCategory(createReqVO); |
| ... | ... | @@ -59,6 +58,7 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { |
| 59 | 58 | // 准备参数 |
| 60 | 59 | BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> { |
| 61 | 60 | o.setId(dbCategory.getId()); // 设置更新的 ID |
| 61 | + o.setStatus(randomCommonStatus()); | |
| 62 | 62 | }); |
| 63 | 63 | |
| 64 | 64 | // 调用 |
| ... | ... | @@ -101,29 +101,28 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { |
| 101 | 101 | } |
| 102 | 102 | |
| 103 | 103 | @Test |
| 104 | - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 105 | 104 | public void testGetCategoryPage() { |
| 106 | 105 | // mock 数据 |
| 107 | 106 | BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到 |
| 108 | - o.setName(null); | |
| 109 | - o.setCode(null); | |
| 110 | - o.setStatus(null); | |
| 111 | - o.setCreateTime(null); | |
| 107 | + o.setName("芋头"); | |
| 108 | + o.setCode("xiaodun"); | |
| 109 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 110 | + o.setCreateTime(buildTime(2023, 2, 2)); | |
| 112 | 111 | }); |
| 113 | 112 | categoryMapper.insert(dbCategory); |
| 114 | 113 | // 测试 name 不匹配 |
| 115 | - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); | |
| 114 | + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾"))); | |
| 116 | 115 | // 测试 code 不匹配 |
| 117 | - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode(null))); | |
| 116 | + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou"))); | |
| 118 | 117 | // 测试 status 不匹配 |
| 119 | - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null))); | |
| 118 | + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | |
| 120 | 119 | // 测试 createTime 不匹配 |
| 121 | - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null))); | |
| 120 | + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2)))); | |
| 122 | 121 | // 准备参数 |
| 123 | 122 | BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO(); |
| 124 | - reqVO.setName(null); | |
| 125 | - reqVO.setCode(null); | |
| 126 | - reqVO.setStatus(null); | |
| 123 | + reqVO.setName("芋"); | |
| 124 | + reqVO.setCode("xiao"); | |
| 125 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 127 | 126 | reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
| 128 | 127 | |
| 129 | 128 | // 调用 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmFormServiceTest.java
| ... | ... | @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 6 | 6 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 7 | 7 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; |
| 9 | -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO; | |
| 10 | 9 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 11 | 10 | import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper; |
| 12 | 11 | import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO; |
| ... | ... | @@ -66,7 +65,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest { |
| 66 | 65 | }); |
| 67 | 66 | formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 |
| 68 | 67 | // 准备参数 |
| 69 | - BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> { | |
| 68 | + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { | |
| 70 | 69 | o.setId(dbForm.getId()); // 设置更新的 ID |
| 71 | 70 | o.setConf("{'yudao': 'yuanma'}"); |
| 72 | 71 | o.setFields(randomFields()); |
| ... | ... | @@ -82,7 +81,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest { |
| 82 | 81 | @Test |
| 83 | 82 | public void testUpdateForm_notExists() { |
| 84 | 83 | // 准备参数 |
| 85 | - BpmFormUpdateReqVO reqVO = randomPojo(BpmFormUpdateReqVO.class, o -> { | |
| 84 | + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { | |
| 86 | 85 | o.setConf("{'yudao': 'yuanma'}"); |
| 87 | 86 | o.setFields(randomFields()); |
| 88 | 87 | }); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/clean.sql
yudao-module-bpm/yudao-module-bpm-biz/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -12,6 +12,21 @@ CREATE TABLE IF NOT EXISTS "bpm_user_group" ( |
| 12 | 12 | PRIMARY KEY ("id") |
| 13 | 13 | ) COMMENT '用户组'; |
| 14 | 14 | |
| 15 | +CREATE TABLE IF NOT EXISTS "bpm_category" ( | |
| 16 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | |
| 17 | + "name" varchar(63) NOT NULL, | |
| 18 | + "code" varchar(63) NOT NULL, | |
| 19 | + "description" varchar(255) NOT NULL, | |
| 20 | + "status" tinyint NOT NULL, | |
| 21 | + "sort" int NOT NULL, | |
| 22 | + "creator" varchar(64) DEFAULT '', | |
| 23 | + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 24 | + "updater" varchar(64) DEFAULT '', | |
| 25 | + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 26 | + "deleted" bit NOT NULL DEFAULT FALSE, | |
| 27 | + PRIMARY KEY ("id") | |
| 28 | +) COMMENT '分类'; | |
| 29 | + | |
| 15 | 30 | CREATE TABLE IF NOT EXISTS "bpm_form" ( |
| 16 | 31 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 17 | 32 | "name" varchar(63) NOT NULL, | ... | ... |