Commit 29a0fbfc4339b942d4236ec25eb1ac52c1e216ed

Authored by YunaiV
1 parent ed83b912

BPM:支持多表单,每个流程任务都可以绑定流程表单

yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java
@@ -20,7 +20,7 @@ public class BpmProcessInstanceCreateReqDTO { @@ -20,7 +20,7 @@ public class BpmProcessInstanceCreateReqDTO {
20 @NotEmpty(message = "流程定义的标识不能为空") 20 @NotEmpty(message = "流程定义的标识不能为空")
21 private String processDefinitionKey; 21 private String processDefinitionKey;
22 /** 22 /**
23 - * 变量实例 23 + * 变量实例(动态表单)
24 */ 24 */
25 private Map<String, Object> variables; 25 private Map<String, Object> variables;
26 26
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
@@ -6,6 +6,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,6 +6,8 @@ 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.task.*; 7 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
8 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; 8 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
  9 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
  10 +import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
9 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; 11 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
10 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; 12 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
11 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 13 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -46,6 +48,8 @@ public class BpmTaskController { @@ -46,6 +48,8 @@ public class BpmTaskController {
46 private BpmTaskService taskService; 48 private BpmTaskService taskService;
47 @Resource 49 @Resource
48 private BpmProcessInstanceService processInstanceService; 50 private BpmProcessInstanceService processInstanceService;
  51 + @Resource
  52 + private BpmFormService formService;
49 53
50 @Resource 54 @Resource
51 private AdminUserApi adminUserApi; 55 private AdminUserApi adminUserApi;
@@ -98,7 +102,11 @@ public class BpmTaskController { @@ -98,7 +102,11 @@ public class BpmTaskController {
98 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); 102 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
99 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap( 103 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
100 convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); 104 convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
101 - return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance, userMap, deptMap)); 105 + // 获得 Form Map
  106 + Map<Long, BpmFormDO> formMap = formService.getFormMap(
  107 + convertSet(taskList, task -> Long.parseLong(task.getFormKey())));
  108 + return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance,
  109 + formMap, userMap, deptMap));
102 } 110 }
103 111
104 @PutMapping("/approve") 112 @PutMapping("/approve")
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java
@@ -15,7 +15,7 @@ public class BpmProcessInstanceCreateReqVO { @@ -15,7 +15,7 @@ public class BpmProcessInstanceCreateReqVO {
15 @NotEmpty(message = "流程定义编号不能为空") 15 @NotEmpty(message = "流程定义编号不能为空")
16 private String processDefinitionId; 16 private String processDefinitionId;
17 17
18 - @Schema(description = "变量实例") 18 + @Schema(description = "变量实例(动态表单)")
19 private Map<String, Object> variables; 19 private Map<String, Object> variables;
20 20
21 // TODO @hai:assignees 复数 21 // TODO @hai:assignees 复数
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
@@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotEmpty; @@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotEmpty;
5 import lombok.Data; 5 import lombok.Data;
6 6
7 import java.util.Collection; 7 import java.util.Collection;
  8 +import java.util.Map;
8 9
9 @Schema(description = "管理后台 - 通过流程任务的 Request VO") 10 @Schema(description = "管理后台 - 通过流程任务的 Request VO")
10 @Data 11 @Data
@@ -21,4 +22,7 @@ public class BpmTaskApproveReqVO { @@ -21,4 +22,7 @@ public class BpmTaskApproveReqVO {
21 @Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2") 22 @Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2")
22 private Collection<Long> copyUserIds; 23 private Collection<Long> copyUserIds;
23 24
  25 + @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
  26 + private Map<String, Object> variables;
  27 +
24 } 28 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
@@ -6,6 +6,7 @@ import lombok.Data; @@ -6,6 +6,7 @@ import lombok.Data;
6 6
7 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
8 import java.util.List; 8 import java.util.List;
  9 +import java.util.Map;
9 10
10 @Schema(description = "管理后台 - 流程任务 Response VO") 11 @Schema(description = "管理后台 - 流程任务 Response VO")
11 @Data 12 @Data
@@ -49,7 +50,6 @@ public class BpmTaskRespVO { @@ -49,7 +50,6 @@ public class BpmTaskRespVO {
49 50
50 @Schema(description = "所属流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8888") 51 @Schema(description = "所属流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8888")
51 private String processInstanceId; 52 private String processInstanceId;
52 -  
53 /** 53 /**
54 * 所属流程实例 54 * 所属流程实例
55 */ 55 */
@@ -57,10 +57,20 @@ public class BpmTaskRespVO { @@ -57,10 +57,20 @@ public class BpmTaskRespVO {
57 57
58 @Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 58 @Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
59 private String parentTaskId; 59 private String parentTaskId;
60 -  
61 @Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask") 60 @Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask")
62 private List<BpmTaskRespVO> children; 61 private List<BpmTaskRespVO> children;
63 62
  63 + @Schema(description = "表单编号", example = "1024")
  64 + private Long formId;
  65 + @Schema(description = "表单名字", example = "请假表单")
  66 + private String formName;
  67 + @Schema(description = "表单的配置-JSON 字符串")
  68 + private String formConf;
  69 + @Schema(description = "表单项的数组")
  70 + private List<String> formFields;
  71 + @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED)
  72 + private Map<String, Object> formVariables;
  73 +
64 @Data 74 @Data
65 @Schema(description = "流程实例") 75 @Schema(description = "流程实例")
66 public static class ProcessInstance { 76 public static class ProcessInstance {
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java
1 package cn.iocoder.yudao.module.bpm.convert.definition; 1 package cn.iocoder.yudao.module.bpm.convert.definition;
2 2
  3 +import cn.hutool.core.util.ArrayUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@@ -56,7 +57,9 @@ public interface BpmModelConvert { @@ -56,7 +57,9 @@ public interface BpmModelConvert {
56 byte[] bpmnBytes) { 57 byte[] bpmnBytes) {
57 BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); 58 BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
58 BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null); 59 BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null);
59 - modelVO.setBpmnXml(new String(bpmnBytes)); 60 + if (ArrayUtil.isNotEmpty(bpmnBytes)) {
  61 + modelVO.setBpmnXml(new String(bpmnBytes));
  62 + }
60 return modelVO; 63 return modelVO;
61 } 64 }
62 65
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -2,15 +2,14 @@ package cn.iocoder.yudao.module.bpm.convert.task; @@ -2,15 +2,14 @@ package cn.iocoder.yudao.module.bpm.convert.task;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 -import cn.iocoder.yudao.framework.common.util.date.DateUtils;  
6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 5 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
8 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO;
9 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; 9 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; 10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
11 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 11 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
12 import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent; 12 import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent;
13 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;  
14 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; 13 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
15 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; 14 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
16 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 15 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -19,7 +18,6 @@ import org.flowable.engine.history.HistoricProcessInstance; @@ -19,7 +18,6 @@ import org.flowable.engine.history.HistoricProcessInstance;
19 import org.flowable.engine.repository.ProcessDefinition; 18 import org.flowable.engine.repository.ProcessDefinition;
20 import org.flowable.engine.runtime.ProcessInstance; 19 import org.flowable.engine.runtime.ProcessInstance;
21 import org.flowable.task.api.Task; 20 import org.flowable.task.api.Task;
22 -import org.mapstruct.Mapper;  
23 import org.mapstruct.Mapping; 21 import org.mapstruct.Mapping;
24 import org.mapstruct.MappingTarget; 22 import org.mapstruct.MappingTarget;
25 import org.mapstruct.factory.Mappers; 23 import org.mapstruct.factory.Mappers;
@@ -32,7 +30,6 @@ import java.util.Map; @@ -32,7 +30,6 @@ import java.util.Map;
32 * 30 *
33 * @author 芋道源码 31 * @author 芋道源码
34 */ 32 */
35 -@Mapper(uses = DateUtils.class)  
36 public interface BpmProcessInstanceConvert { 33 public interface BpmProcessInstanceConvert {
37 34
38 BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); 35 BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);
@@ -44,7 +41,7 @@ public interface BpmProcessInstanceConvert { @@ -44,7 +41,7 @@ public interface BpmProcessInstanceConvert {
44 PageResult<BpmProcessInstancePageItemRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstancePageItemRespVO.class); 41 PageResult<BpmProcessInstancePageItemRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstancePageItemRespVO.class);
45 for (int i = 0; i < pageResult.getList().size(); i++) { 42 for (int i = 0; i < pageResult.getList().size(); i++) {
46 BpmProcessInstancePageItemRespVO respVO = vpPageResult.getList().get(i); 43 BpmProcessInstancePageItemRespVO respVO = vpPageResult.getList().get(i);
47 - respVO.setStatus((Integer) pageResult.getList().get(i).getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS)); 44 + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i)));
48 MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(), 45 MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(),
49 processDefinition -> respVO.setCategory(processDefinition.getCategory())); 46 processDefinition -> respVO.setCategory(processDefinition.getCategory()));
50 MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName())); 47 MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName()));
@@ -57,8 +54,8 @@ public interface BpmProcessInstanceConvert { @@ -57,8 +54,8 @@ public interface BpmProcessInstanceConvert {
57 ProcessDefinition processDefinition, BpmProcessDefinitionInfoDO processDefinitionExt, 54 ProcessDefinition processDefinition, BpmProcessDefinitionInfoDO processDefinitionExt,
58 String bpmnXml, AdminUserRespDTO startUser, DeptRespDTO dept) { 55 String bpmnXml, AdminUserRespDTO startUser, DeptRespDTO dept) {
59 BpmProcessInstanceRespVO respVO = convert2(processInstance); 56 BpmProcessInstanceRespVO respVO = convert2(processInstance);
60 - respVO.setStatus((Integer) processInstance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS));  
61 - respVO.setFormVariables(processInstance.getProcessVariables()); // TODO 芋艿:真的这么搞么???formVariable 要不要换个 key 之类的 57 + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
  58 + respVO.setFormVariables(FlowableUtils.filterProcessInstanceFormVariable(processInstance.getProcessVariables()));
62 // definition 59 // definition
63 respVO.setProcessDefinition(convert2(processDefinition)); 60 respVO.setProcessDefinition(convert2(processDefinition));
64 copyTo(processDefinitionExt, respVO.getProcessDefinition()); 61 copyTo(processDefinitionExt, respVO.getProcessDefinition());
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
1 package cn.iocoder.yudao.module.bpm.convert.task; 1 package cn.iocoder.yudao.module.bpm.convert.task;
2 2
  3 +import cn.hutool.core.map.MapUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 -import cn.iocoder.yudao.framework.common.util.date.DateUtils;  
7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
9 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; 10 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
10 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; 11 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
11 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; 12 +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
12 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; 13 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
13 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 14 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
14 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 15 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -17,7 +18,6 @@ import org.flowable.engine.runtime.ProcessInstance; @@ -17,7 +18,6 @@ import org.flowable.engine.runtime.ProcessInstance;
17 import org.flowable.task.api.Task; 18 import org.flowable.task.api.Task;
18 import org.flowable.task.api.history.HistoricTaskInstance; 19 import org.flowable.task.api.history.HistoricTaskInstance;
19 import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; 20 import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
20 -import org.mapstruct.Mapper;  
21 import org.mapstruct.factory.Mappers; 21 import org.mapstruct.factory.Mappers;
22 22
23 import java.util.Date; 23 import java.util.Date;
@@ -25,13 +25,13 @@ import java.util.List; @@ -25,13 +25,13 @@ import java.util.List;
25 import java.util.Map; 25 import java.util.Map;
26 26
27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
28 29
29 /** 30 /**
30 * Bpm 任务 Convert 31 * Bpm 任务 Convert
31 * 32 *
32 * @author 芋道源码 33 * @author 芋道源码
33 */ 34 */
34 -@Mapper(uses = DateUtils.class)  
35 public interface BpmTaskConvert { 35 public interface BpmTaskConvert {
36 36
37 BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class); 37 BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
@@ -45,8 +45,7 @@ public interface BpmTaskConvert { @@ -45,8 +45,7 @@ public interface BpmTaskConvert {
45 return; 45 return;
46 } 46 }
47 AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); 47 AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
48 - taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class,  
49 - processInstanceVO -> processInstanceVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)))); 48 + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
50 }); 49 });
51 } 50 }
52 51
@@ -55,14 +54,13 @@ public interface BpmTaskConvert { @@ -55,14 +54,13 @@ public interface BpmTaskConvert {
55 Map<Long, AdminUserRespDTO> userMap) { 54 Map<Long, AdminUserRespDTO> userMap) {
56 List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> { 55 List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> {
57 BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); 56 BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
58 - taskVO.setStatus((Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS));  
59 - taskVO.setReason((String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON)); 57 + taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
60 // 流程实例 58 // 流程实例
61 HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); 59 HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId());
62 if (processInstance != null) { 60 if (processInstance != null) {
63 AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); 61 AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
64 - taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class,  
65 - processInstanceVO -> processInstanceVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)))); 62 + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
  63 + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
66 } 64 }
67 return taskVO; 65 return taskVO;
68 }); 66 });
@@ -71,32 +69,32 @@ public interface BpmTaskConvert { @@ -71,32 +69,32 @@ public interface BpmTaskConvert {
71 69
72 default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList, 70 default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,
73 HistoricProcessInstance processInstance, 71 HistoricProcessInstance processInstance,
  72 + Map<Long, BpmFormDO> formMap,
74 Map<Long, AdminUserRespDTO> userMap, 73 Map<Long, AdminUserRespDTO> userMap,
75 Map<Long, DeptRespDTO> deptMap) { 74 Map<Long, DeptRespDTO> deptMap) {
76 List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(taskList, task -> { 75 List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(taskList, task -> {
77 BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); 76 BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
78 - taskVO.setStatus((Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS));  
79 - taskVO.setReason((String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON)); 77 + taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
80 // 流程实例 78 // 流程实例
81 AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); 79 AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
82 - taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class,  
83 - processInstanceVO -> processInstanceVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class)))); 80 + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
  81 + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceRespVO.User.class));
  82 + // 表单信息
  83 + BpmFormDO form = MapUtil.get(formMap, Long.parseLong(task.getFormKey()), BpmFormDO.class);
  84 + if (form != null) {
  85 + taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf())
  86 + .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));
  87 + }
84 // 用户信息 88 // 用户信息
85 AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); 89 AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
86 if (assignUser != null) { 90 if (assignUser != null) {
87 taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceRespVO.User.class)); 91 taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceRespVO.User.class));
88 - DeptRespDTO dept = deptMap.get(assignUser.getDeptId());  
89 - if (dept != null) {  
90 - taskVO.getAssigneeUser().setDeptName(dept.getName());  
91 - } 92 + findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));
92 } 93 }
93 AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner())); 94 AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));
94 if (ownerUser != null) { 95 if (ownerUser != null) {
95 taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceRespVO.User.class)); 96 taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceRespVO.User.class));
96 - DeptRespDTO dept = deptMap.get(ownerUser.getDeptId());  
97 - if (dept != null) {  
98 - taskVO.getOwnerUser().setDeptName(dept.getName());  
99 - } 97 + findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
100 } 98 }
101 return taskVO; 99 return taskVO;
102 }); 100 });
@@ -126,10 +124,7 @@ public interface BpmTaskConvert { @@ -126,10 +124,7 @@ public interface BpmTaskConvert {
126 AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner())); 124 AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));
127 if (ownerUser != null) { 125 if (ownerUser != null) {
128 taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceRespVO.User.class)); 126 taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceRespVO.User.class));
129 - DeptRespDTO dept = deptMap.get(ownerUser.getDeptId());  
130 - if (dept != null) {  
131 - taskVO.getOwnerUser().setDeptName(dept.getName());  
132 - } 127 + findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
133 } 128 }
134 })); 129 }));
135 } 130 }
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.lang.Assert; 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.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.flowable.core.context.FlowableContextHolder; 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.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 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.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; 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.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 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 BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @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); // 创建流程实例 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 BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionInfoDO processDefinitionExt = processDefinitionService.getProcessDefinitionInfo( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @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.convert(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.convert2ApprovedReq(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(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.convert2RejectReq(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(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.lang.Assert; 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.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.flowable.core.context.FlowableContextHolder; import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils; 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.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 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.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; 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.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 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 BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @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 BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionInfoDO processDefinitionExt = processDefinitionService.getProcessDefinitionInfo( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @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.convert(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.convert2ApprovedReq(instance)); // 3. 发送流程实例的状态事件 // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(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.convert2RejectReq(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(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
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils; @@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils;
10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
11 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 11 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
12 import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils; 12 import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
  13 +import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
13 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 14 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
14 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; 15 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
15 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; 16 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
@@ -185,7 +186,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -185,7 +186,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
185 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), 186 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
186 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); 187 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
187 // 3.3 调用 BPM complete 去完成任务 188 // 3.3 调用 BPM complete 去完成任务
188 - taskService.complete(task.getId(), instance.getProcessVariables()); 189 + // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用
  190 + Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());
  191 + taskService.complete(task.getId(), variables, true);
189 192
190 // 【加签专属】处理加签任务 193 // 【加签专属】处理加签任务
191 handleParentTaskIfSign(task.getParentTaskId()); 194 handleParentTaskIfSign(task.getParentTaskId());
yudao-module-bpm/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/FlowableUtils.java
@@ -4,8 +4,14 @@ import org.flowable.common.engine.api.delegate.Expression; @@ -4,8 +4,14 @@ import org.flowable.common.engine.api.delegate.Expression;
4 import org.flowable.common.engine.api.variable.VariableContainer; 4 import org.flowable.common.engine.api.variable.VariableContainer;
5 import org.flowable.common.engine.impl.el.ExpressionManager; 5 import org.flowable.common.engine.impl.el.ExpressionManager;
6 import org.flowable.common.engine.impl.identity.Authentication; 6 import org.flowable.common.engine.impl.identity.Authentication;
  7 +import org.flowable.engine.history.HistoricProcessInstance;
7 import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; 8 import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
8 import org.flowable.engine.impl.util.CommandContextUtil; 9 import org.flowable.engine.impl.util.CommandContextUtil;
  10 +import org.flowable.engine.runtime.ProcessInstance;
  11 +import org.flowable.task.api.TaskInfo;
  12 +
  13 +import java.util.HashMap;
  14 +import java.util.Map;
9 15
10 /** 16 /**
11 * Flowable 相关的工具方法 17 * Flowable 相关的工具方法
@@ -34,6 +40,56 @@ public class FlowableUtils { @@ -34,6 +40,56 @@ public class FlowableUtils {
34 return activityId + "_assignee"; 40 return activityId + "_assignee";
35 } 41 }
36 42
  43 + // ========== ProcessInstance 相关的工具方法 ==========
  44 +
  45 + public static Integer getProcessInstanceStatus(ProcessInstance processInstance) {
  46 + return getProcessInstanceStatus(processInstance.getProcessVariables());
  47 + }
  48 +
  49 + public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) {
  50 + return getProcessInstanceStatus(processInstance.getProcessVariables());
  51 + }
  52 +
  53 + // TODO 芋艿:需要再搞搞
  54 + private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) {
  55 + return (Integer) processVariables.get("PROCESS_STATUS");
  56 + }
  57 +
  58 + public static Map<String, Object> getProcessInstanceFormVariable(ProcessInstance processInstance) {
  59 + Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
  60 + filterProcessInstanceFormVariable(formVariables);
  61 + return formVariables;
  62 + }
  63 +
  64 + public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) {
  65 + processVariables.remove("PROCESS_STATUS");
  66 + return processVariables;
  67 + }
  68 +
  69 + // ========== Task 相关的工具方法 ==========
  70 +
  71 + // TODO 芋艿:需要再搞搞
  72 +
  73 + public static Integer getTaskStatus(TaskInfo task) {
  74 + return (Integer) task.getTaskLocalVariables().get("TASK_STATUS");
  75 + }
  76 +
  77 + public static String getTaskReason(TaskInfo task) {
  78 + return (String) task.getTaskLocalVariables().get("TASK_REASON");
  79 + }
  80 +
  81 + public static Map<String, Object> getTaskFormVariable(TaskInfo task) {
  82 + Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables());
  83 + filterTaskFormVariable(formVariables);
  84 + return formVariables;
  85 + }
  86 +
  87 + public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) {
  88 + taskLocalVariables.remove("TASK_STATUS");
  89 + taskLocalVariables.remove("TASK_REASON");
  90 + return taskLocalVariables;
  91 + }
  92 +
37 // ========== Expression 相关的工具方法 ========== 93 // ========== Expression 相关的工具方法 ==========
38 94
39 public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) { 95 public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {