Commit 29a0fbfc4339b942d4236ec25eb1ac52c1e216ed
1 parent
ed83b912
BPM:支持多表单,每个流程任务都可以绑定流程表单
Showing
11 changed files
with
117 additions
and
41 deletions
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) { |