Commit ba4b3701bf0a8d0fd2bc51f0aba9ee1b20166607
1 parent
e5444c5d
BPM:新增【流程实例】菜单,用于全部流程实例的查询
Showing
13 changed files
with
97 additions
and
38 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmDeleteReasonEnum.java
| @@ -16,11 +16,12 @@ public enum BpmDeleteReasonEnum { | @@ -16,11 +16,12 @@ public enum BpmDeleteReasonEnum { | ||
| 16 | // ========== 流程实例的独有原因 ========== | 16 | // ========== 流程实例的独有原因 ========== |
| 17 | 17 | ||
| 18 | REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法 | 18 | REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法 |
| 19 | - CANCEL_PROCESS_INSTANCE("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程 | 19 | + CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程 |
| 20 | + CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程 | ||
| 20 | 21 | ||
| 21 | // ========== 流程任务的独有原因 ========== | 22 | // ========== 流程任务的独有原因 ========== |
| 22 | 23 | ||
| 23 | - CANCEL_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 | 24 | + CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 |
| 24 | ; | 25 | ; |
| 25 | 26 | ||
| 26 | private final String reason; | 27 | private final String reason; |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java
| @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process; | @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | -import jakarta.validation.constraints.NotEmpty; | ||
| 7 | - | ||
| 8 | import java.time.LocalDateTime; | 6 | import java.time.LocalDateTime; |
| 9 | import java.util.List; | 7 | import java.util.List; |
| 10 | 8 | ||
| @@ -19,9 +17,11 @@ public class BpmProcessDefinitionRespVO { | @@ -19,9 +17,11 @@ public class BpmProcessDefinitionRespVO { | ||
| 19 | private Integer version; | 17 | private Integer version; |
| 20 | 18 | ||
| 21 | @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") | 19 | @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") |
| 22 | - @NotEmpty(message = "流程名称不能为空") | ||
| 23 | private String name; | 20 | private String name; |
| 24 | 21 | ||
| 22 | + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") | ||
| 23 | + private String key; | ||
| 24 | + | ||
| 25 | @Schema(description = "流程描述", example = "我是描述") | 25 | @Schema(description = "流程描述", example = "我是描述") |
| 26 | private String description; | 26 | private String description; |
| 27 | 27 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
| @@ -4,7 +4,10 @@ import cn.hutool.core.collection.CollUtil; | @@ -4,7 +4,10 @@ import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 6 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 7 | -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; | 7 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; |
| 8 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; | ||
| 10 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; | ||
| 8 | import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; | 11 | import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; |
| 9 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; | 12 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 10 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; | 13 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| @@ -59,9 +62,10 @@ public class BpmProcessInstanceController { | @@ -59,9 +62,10 @@ public class BpmProcessInstanceController { | ||
| 59 | @GetMapping("/my-page") | 62 | @GetMapping("/my-page") |
| 60 | @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") | 63 | @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") |
| 61 | @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") | 64 | @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") |
| 62 | - public CommonResult<PageResult<BpmProcessInstanceRespVO>> getMyProcessInstancePage( | ||
| 63 | - @Valid BpmProcessInstanceMyPageReqVO pageReqVO) { | ||
| 64 | - PageResult<HistoricProcessInstance> pageResult = processInstanceService.getMyProcessInstancePage(getLoginUserId(), pageReqVO); | 65 | + public CommonResult<PageResult<BpmProcessInstanceRespVO>> getProcessInstanceMyPage( |
| 66 | + @Valid BpmProcessInstancePageReqVO pageReqVO) { | ||
| 67 | + PageResult<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage( | ||
| 68 | + getLoginUserId(), pageReqVO); | ||
| 65 | if (CollUtil.isEmpty(pageResult.getList())) { | 69 | if (CollUtil.isEmpty(pageResult.getList())) { |
| 66 | return success(PageResult.empty(pageResult.getTotal())); | 70 | return success(PageResult.empty(pageResult.getTotal())); |
| 67 | } | 71 | } |
| @@ -73,8 +77,35 @@ public class BpmProcessInstanceController { | @@ -73,8 +77,35 @@ public class BpmProcessInstanceController { | ||
| 73 | convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); | 77 | convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); |
| 74 | Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap( | 78 | Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap( |
| 75 | convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); | 79 | convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); |
| 76 | - return success(BpmProcessInstanceConvert.INSTANCE.buildMyProcessInstancePage(pageResult, | ||
| 77 | - processDefinitionMap, categoryMap, taskMap)); | 80 | + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, |
| 81 | + processDefinitionMap, categoryMap, taskMap, null, null)); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @GetMapping("/manager-page") | ||
| 85 | + @Operation(summary = "获得管理流程实例的分页列表", description = "在【流程实例】菜单中,进行调用") | ||
| 86 | + @PreAuthorize("@ss.hasPermission('bpm:process-instance:manager-query')") | ||
| 87 | + public CommonResult<PageResult<BpmProcessInstanceRespVO>> getProcessInstanceManagerPage( | ||
| 88 | + @Valid BpmProcessInstancePageReqVO pageReqVO) { | ||
| 89 | + PageResult<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage( | ||
| 90 | + null, pageReqVO); | ||
| 91 | + if (CollUtil.isEmpty(pageResult.getList())) { | ||
| 92 | + return success(PageResult.empty(pageResult.getTotal())); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + // 拼接返回 | ||
| 96 | + Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds( | ||
| 97 | + convertList(pageResult.getList(), HistoricProcessInstance::getId)); | ||
| 98 | + Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap( | ||
| 99 | + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); | ||
| 100 | + Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap( | ||
| 101 | + convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); | ||
| 102 | + // 发起人信息 | ||
| 103 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | ||
| 104 | + convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId()))); | ||
| 105 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( | ||
| 106 | + convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); | ||
| 107 | + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, | ||
| 108 | + processDefinitionMap, categoryMap, taskMap, userMap, deptMap)); | ||
| 78 | } | 109 | } |
| 79 | 110 | ||
| 80 | @PostMapping("/create") | 111 | @PostMapping("/create") |
| @@ -109,11 +140,21 @@ public class BpmProcessInstanceController { | @@ -109,11 +140,21 @@ public class BpmProcessInstanceController { | ||
| 109 | processDefinition, processDefinitionInfo, bpmnXml, startUser, dept)); | 140 | processDefinition, processDefinitionInfo, bpmnXml, startUser, dept)); |
| 110 | } | 141 | } |
| 111 | 142 | ||
| 112 | - @DeleteMapping("/cancel") | ||
| 113 | - @Operation(summary = "取消流程实例", description = "撤回发起的流程") | 143 | + @DeleteMapping("/cancel-by-start-user") |
| 144 | + @Operation(summary = "用户取消流程实例", description = "取消发起的流程") | ||
| 114 | @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") | 145 | @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") |
| 115 | - public CommonResult<Boolean> cancelProcessInstance(@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { | ||
| 116 | - processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO); | 146 | + public CommonResult<Boolean> cancelProcessInstanceByStartUser( |
| 147 | + @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { | ||
| 148 | + processInstanceService.cancelProcessInstanceByStartUser(getLoginUserId(), cancelReqVO); | ||
| 149 | + return success(true); | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + @DeleteMapping("/cancel-by-admin") | ||
| 153 | + @Operation(summary = "管理员取消流程实例", description = "管理员撤回流程") | ||
| 154 | + @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel-by-admin')") | ||
| 155 | + public CommonResult<Boolean> cancelProcessInstanceByManager( | ||
| 156 | + @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { | ||
| 157 | + processInstanceService.cancelProcessInstanceByAdmin(getLoginUserId(), cancelReqVO); | ||
| 117 | return success(true); | 158 | return success(true); |
| 118 | } | 159 | } |
| 119 | 160 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceMyPageReqVO.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java
| @@ -5,19 +5,15 @@ import cn.iocoder.yudao.framework.common.validation.InEnum; | @@ -5,19 +5,15 @@ import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 5 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; | 5 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; |
| 6 | import io.swagger.v3.oas.annotations.media.Schema; | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 7 | import lombok.Data; | 7 | import lombok.Data; |
| 8 | -import lombok.EqualsAndHashCode; | ||
| 9 | -import lombok.ToString; | ||
| 10 | import org.springframework.format.annotation.DateTimeFormat; | 8 | import org.springframework.format.annotation.DateTimeFormat; |
| 11 | 9 | ||
| 12 | import java.time.LocalDateTime; | 10 | import java.time.LocalDateTime; |
| 13 | 11 | ||
| 14 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | 12 | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; |
| 15 | 13 | ||
| 16 | -@Schema(description = "管理后台 - 流程实例的分页 Item Response VO") | 14 | +@Schema(description = "管理后台 - 流程实例分页 Request VO") |
| 17 | @Data | 15 | @Data |
| 18 | -@EqualsAndHashCode(callSuper = true) | ||
| 19 | -@ToString(callSuper = true) | ||
| 20 | -public class BpmProcessInstanceMyPageReqVO extends PageParam { | 16 | +public class BpmProcessInstancePageReqVO extends PageParam { |
| 21 | 17 | ||
| 22 | @Schema(description = "流程名称", example = "芋道") | 18 | @Schema(description = "流程名称", example = "芋道") |
| 23 | private String name; | 19 | private String name; |
| @@ -36,4 +32,7 @@ public class BpmProcessInstanceMyPageReqVO extends PageParam { | @@ -36,4 +32,7 @@ public class BpmProcessInstanceMyPageReqVO extends PageParam { | ||
| 36 | @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | 32 | @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
| 37 | private LocalDateTime[] createTime; | 33 | private LocalDateTime[] createTime; |
| 38 | 34 | ||
| 35 | + @Schema(description = "发起用户编号", example = "1024") | ||
| 36 | + private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数 | ||
| 37 | + | ||
| 39 | } | 38 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java
| @@ -26,12 +26,15 @@ public class BpmProcessInstanceRespVO { | @@ -26,12 +26,15 @@ public class BpmProcessInstanceRespVO { | ||
| 26 | @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 26 | @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 27 | private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 | 27 | private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 |
| 28 | 28 | ||
| 29 | - @Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED) | 29 | + @Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 30 | private LocalDateTime startTime; | 30 | private LocalDateTime startTime; |
| 31 | 31 | ||
| 32 | @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) | 32 | @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 33 | private LocalDateTime endTime; | 33 | private LocalDateTime endTime; |
| 34 | 34 | ||
| 35 | + @Schema(description = "持续时间", example = "1000") | ||
| 36 | + private Long durationInMillis; | ||
| 37 | + | ||
| 35 | @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) | 38 | @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) |
| 36 | private Map<String, Object> formVariables; | 39 | private Map<String, Object> formVariables; |
| 37 | 40 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
| @@ -24,7 +24,7 @@ public class BpmTaskRespVO { | @@ -24,7 +24,7 @@ public class BpmTaskRespVO { | ||
| 24 | @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) | 24 | @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 25 | private LocalDateTime endTime; | 25 | private LocalDateTime endTime; |
| 26 | 26 | ||
| 27 | - @Schema(description = "持续时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") | 27 | + @Schema(description = "持续时间", example = "1000") |
| 28 | private Long durationInMillis; | 28 | private Long durationInMillis; |
| 29 | 29 | ||
| 30 | @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | 30 | @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
| @@ -36,10 +36,12 @@ public interface BpmProcessInstanceConvert { | @@ -36,10 +36,12 @@ public interface BpmProcessInstanceConvert { | ||
| 36 | 36 | ||
| 37 | BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); | 37 | BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); |
| 38 | 38 | ||
| 39 | - default PageResult<BpmProcessInstanceRespVO> buildMyProcessInstancePage(PageResult<HistoricProcessInstance> pageResult, | ||
| 40 | - Map<String, ProcessDefinition> processDefinitionMap, | ||
| 41 | - Map<String, BpmCategoryDO> categoryMap, | ||
| 42 | - Map<String, List<Task>> taskMap) { | 39 | + default PageResult<BpmProcessInstanceRespVO> buildProcessInstancePage(PageResult<HistoricProcessInstance> pageResult, |
| 40 | + Map<String, ProcessDefinition> processDefinitionMap, | ||
| 41 | + Map<String, BpmCategoryDO> categoryMap, | ||
| 42 | + Map<String, List<Task>> taskMap, | ||
| 43 | + Map<Long, AdminUserRespDTO> userMap, | ||
| 44 | + Map<Long, DeptRespDTO> deptMap) { | ||
| 43 | PageResult<BpmProcessInstanceRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class); | 45 | PageResult<BpmProcessInstanceRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class); |
| 44 | for (int i = 0; i < pageResult.getList().size(); i++) { | 46 | for (int i = 0; i < pageResult.getList().size(); i++) { |
| 45 | BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i); | 47 | BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i); |
| @@ -48,6 +50,10 @@ public interface BpmProcessInstanceConvert { | @@ -48,6 +50,10 @@ public interface BpmProcessInstanceConvert { | ||
| 48 | processDefinition -> respVO.setCategory(processDefinition.getCategory())); | 50 | processDefinition -> respVO.setCategory(processDefinition.getCategory())); |
| 49 | MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName())); | 51 | MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName())); |
| 50 | respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class)); | 52 | respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class)); |
| 53 | + // user | ||
| 54 | + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId())); | ||
| 55 | + respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)); | ||
| 56 | + MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName())); | ||
| 51 | } | 57 | } |
| 52 | return vpPageResult; | 58 | return vpPageResult; |
| 53 | } | 59 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
| @@ -46,6 +46,7 @@ public interface BpmTaskConvert { | @@ -46,6 +46,7 @@ public interface BpmTaskConvert { | ||
| 46 | if (processInstance == null) { | 46 | if (processInstance == null) { |
| 47 | return; | 47 | return; |
| 48 | } | 48 | } |
| 49 | + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); | ||
| 49 | AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); | 50 | AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); |
| 50 | taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)); | 51 | taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)); |
| 51 | }); | 52 | }); |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java
| @@ -4,7 +4,7 @@ import cn.hutool.core.collection.ListUtil; | @@ -4,7 +4,7 @@ import cn.hutool.core.collection.ListUtil; | ||
| 4 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory; | 4 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory; |
| 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; | 5 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; |
| 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; | 6 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; |
| 7 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceResultEventPublisher; | 7 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; |
| 8 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | 8 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 9 | import org.flowable.common.engine.api.delegate.event.FlowableEventListener; | 9 | import org.flowable.common.engine.api.delegate.event.FlowableEventListener; |
| 10 | import org.flowable.spring.SpringProcessEngineConfiguration; | 10 | import org.flowable.spring.SpringProcessEngineConfiguration; |
| @@ -84,8 +84,8 @@ public class BpmFlowableConfiguration { | @@ -84,8 +84,8 @@ public class BpmFlowableConfiguration { | ||
| 84 | // =========== 自己拓展的 Bean ========== | 84 | // =========== 自己拓展的 Bean ========== |
| 85 | 85 | ||
| 86 | @Bean | 86 | @Bean |
| 87 | - public BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher(ApplicationEventPublisher publisher) { | ||
| 88 | - return new BpmProcessInstanceResultEventPublisher(publisher); | 87 | + public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) { |
| 88 | + return new BpmProcessInstanceEventPublisher(publisher); | ||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | } | 91 | } |
| 92 | \ No newline at end of file | 92 | \ No newline at end of file |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/event/BpmProcessInstanceResultEventPublisher.java renamed to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java
| @@ -14,7 +14,7 @@ import jakarta.validation.Valid; | @@ -14,7 +14,7 @@ import jakarta.validation.Valid; | ||
| 14 | */ | 14 | */ |
| 15 | @AllArgsConstructor | 15 | @AllArgsConstructor |
| 16 | @Validated | 16 | @Validated |
| 17 | -public class BpmProcessInstanceResultEventPublisher { | 17 | +public class BpmProcessInstanceEventPublisher { |
| 18 | 18 | ||
| 19 | private final ApplicationEventPublisher publisher; | 19 | private final ApplicationEventPublisher publisher; |
| 20 | 20 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
| @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; | 4 | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| 5 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; | 5 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; |
| 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; | 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; |
| 7 | -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceMyPageReqVO; | 7 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; |
| 8 | import jakarta.validation.Valid; | 8 | import jakarta.validation.Valid; |
| 9 | import org.flowable.engine.delegate.event.FlowableCancelledEvent; | 9 | import org.flowable.engine.delegate.event.FlowableCancelledEvent; |
| 10 | import org.flowable.engine.history.HistoricProcessInstance; | 10 | import org.flowable.engine.history.HistoricProcessInstance; |
| @@ -93,8 +93,8 @@ public interface BpmProcessInstanceService { | @@ -93,8 +93,8 @@ public interface BpmProcessInstanceService { | ||
| 93 | * @param pageReqVO 分页请求 | 93 | * @param pageReqVO 分页请求 |
| 94 | * @return 流程实例的分页 | 94 | * @return 流程实例的分页 |
| 95 | */ | 95 | */ |
| 96 | - PageResult<HistoricProcessInstance> getMyProcessInstancePage(Long userId, | ||
| 97 | - @Valid BpmProcessInstanceMyPageReqVO pageReqVO); | 96 | + PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, |
| 97 | + @Valid BpmProcessInstancePageReqVO pageReqVO); | ||
| 98 | 98 | ||
| 99 | /** | 99 | /** |
| 100 | * 创建流程实例(提供给前端) | 100 | * 创建流程实例(提供给前端) |
| @@ -115,12 +115,20 @@ public interface BpmProcessInstanceService { | @@ -115,12 +115,20 @@ public interface BpmProcessInstanceService { | ||
| 115 | String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO); | 115 | String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO); |
| 116 | 116 | ||
| 117 | /** | 117 | /** |
| 118 | - * 取消流程实例 | 118 | + * 发起人取消流程实例 |
| 119 | * | 119 | * |
| 120 | * @param userId 用户编号 | 120 | * @param userId 用户编号 |
| 121 | * @param cancelReqVO 取消信息 | 121 | * @param cancelReqVO 取消信息 |
| 122 | */ | 122 | */ |
| 123 | - void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); | 123 | + void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); |
| 124 | + | ||
| 125 | + /** | ||
| 126 | + * 管理员取消流程实例 | ||
| 127 | + * | ||
| 128 | + * @param userId 用户编号 | ||
| 129 | + * @param cancelReqVO 取消信息 | ||
| 130 | + */ | ||
| 131 | + void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); | ||
| 124 | 132 | ||
| 125 | /** | 133 | /** |
| 126 | * 更新 ProcessInstance 拓展记录为取消 | 134 | * 更新 ProcessInstance 拓展记录为取消 |
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.BpmProcessInstanceMyPageReqVO; 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.BpmProcessInstanceResultEventPublisher; 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 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 BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @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> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery().includeProcessVariables() .startedBy(String.valueOf(userId)) .orderByProcessInstanceStartTime().desc(); 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 cancelProcessInstance(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.format(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()); // 发送流程实例的状态事件 processInstanceResultEventPublisher.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()); processInstanceResultEventPublisher.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. 发送流程实例的状态事件 processInstanceResultEventPublisher.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 | \ No newline at end of file | 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.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(); } } | ||
| 3 | \ No newline at end of file | 3 | \ No newline at end of file |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -375,7 +375,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -375,7 +375,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 375 | log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); | 375 | log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); |
| 376 | return; | 376 | return; |
| 377 | } | 377 | } |
| 378 | - updateTaskStatusAndReason(taskId, BpmTaskStatustEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_SYSTEM.getReason()); | 378 | + updateTaskStatusAndReason(taskId, BpmTaskStatustEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason()); |
| 379 | // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 | 379 | // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 |
| 380 | } | 380 | } |
| 381 | 381 |