diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmProcessInstanceService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmProcessInstanceService.java index 6cc1537fd..ca4c93797 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmProcessInstanceService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmProcessInstanceService.java @@ -1,6 +1,5 @@ package cn.axzo.workflow.core.service; -import cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum; import cn.axzo.workflow.core.service.dto.request.process.*; import cn.axzo.workflow.core.service.dto.response.BpmPageResult; import cn.axzo.workflow.core.service.dto.response.process.BpmProcessInstancePageItemVO; @@ -85,7 +84,7 @@ public interface BpmProcessInstanceService { */ BpmProcessInstanceVO getProcessInstanceVO(BpmProcessInstanceQueryDTO dto); - void deleteProcessInstance(String id, String reason, BpmProcessInstanceResultEnum resultEnum); + void deleteProcessInstance(String id, String reason); ObjectNode getProcessInstanceGraphical(String processInstanceId, @Nullable String tenantId); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmTaskService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmTaskService.java index 67e37d382..d1f1639b8 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmTaskService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmTaskService.java @@ -5,6 +5,7 @@ import cn.axzo.workflow.core.service.dto.request.task.BpmTaskAuditDTO; import cn.axzo.workflow.core.service.dto.request.task.BpmTaskCommentDTO; import cn.axzo.workflow.core.service.dto.request.task.BpmTaskTodoPageSearchDTO; import cn.axzo.workflow.core.service.dto.response.BpmPageResult; +import cn.axzo.workflow.core.service.dto.response.task.BpmHistoricTaskInstanceGroupVO; import cn.axzo.workflow.core.service.dto.response.task.BpmHistoricTaskInstanceVO; import cn.axzo.workflow.core.service.dto.response.task.BpmTaskDonePageItemVO; import cn.axzo.workflow.core.service.dto.response.task.BpmTaskTodoPageItemVO; @@ -39,9 +40,23 @@ public interface BpmTaskService { /** * 获取历史已审批的列表详情 + *

+ * 扁平的任务列表 */ List getHistoricTaskListByProcessInstanceId(String processInstanceId, String tenantId); + /** + * 获取历史已审批的列表详情 + *

+ * 任务根据定义 ID 分组 + * + * @param processInstanceId + * @param tenantId + * @return + */ + List getHistoricTaskListGroupByProcessInstanceId(String processInstanceId, + String tenantId); + /** * 获取实例正在审核的人列表 *

@@ -71,4 +86,6 @@ public interface BpmTaskService { void assigneeTask(BpmTaskAssigneeDTO dto); void commentTask(BpmTaskCommentDTO dto); + + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmHistoricTaskInstanceConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmHistoricTaskInstanceConverter.java index e6071d3d0..23f3d8d22 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmHistoricTaskInstanceConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmHistoricTaskInstanceConverter.java @@ -1,11 +1,13 @@ package cn.axzo.workflow.core.service.converter; import cn.axzo.workflow.core.service.dto.response.task.BpmHistoricTaskInstanceVO; +import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior; import org.flowable.task.api.history.HistoricTaskInstance; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.springframework.util.CollectionUtils; -import java.util.Arrays; +import java.util.*; import static org.mapstruct.NullValueCheckStrategy.ALWAYS; @@ -22,6 +24,10 @@ import static org.mapstruct.NullValueCheckStrategy.ALWAYS; ) public interface BpmHistoricTaskInstanceConverter extends EntityConverter { + /** + * @see MultiInstanceActivityBehavior#DELETE_REASON_END + */ + String DELETE_REASON_END = "MI_END"; @Override @Mapping(target = "taskId", source = "id") @@ -29,9 +35,24 @@ public interface BpmHistoricTaskInstanceConverter extends EntityConverter toVosSkipMiEnd(List entities) { + if (CollectionUtils.isEmpty(entities)) { + return Collections.emptyList(); + } + List vos = new ArrayList<>(); + entities.stream() + .filter(i -> !Objects.equals(DELETE_REASON_END, i.getDeleteReason())) + .forEach(i -> vos.add(toVo(i))); + return vos; + } + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/dto/response/task/BpmHistoricTaskInstanceGroupVO.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/dto/response/task/BpmHistoricTaskInstanceGroupVO.java new file mode 100644 index 000000000..afdcfd341 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/dto/response/task/BpmHistoricTaskInstanceGroupVO.java @@ -0,0 +1,22 @@ +package cn.axzo.workflow.core.service.dto.response.task; + +import lombok.Data; + +import java.util.List; + +/** + * TODO + * + * @author wangli + * @sine 2023/7/29 22:59 + */ +@Data +public class BpmHistoricTaskInstanceGroupVO { + + private String taskDefinitionKey; + + private String processInstanceId; + + private List tasks; + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/dto/response/task/BpmHistoricTaskInstanceVO.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/dto/response/task/BpmHistoricTaskInstanceVO.java index d891551c1..e34a0643e 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/dto/response/task/BpmHistoricTaskInstanceVO.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/dto/response/task/BpmHistoricTaskInstanceVO.java @@ -25,6 +25,8 @@ public class BpmHistoricTaskInstanceVO { private String processDefinitionId; + private String assignee; + private Date createTime; private Date endTime; @@ -36,4 +38,7 @@ public class BpmHistoricTaskInstanceVO { private BpmProcessInstanceResultEnum result; private String comment; + + private transient String deleteReason; + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/engine/EngineProcessInstanceEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/engine/EngineProcessInstanceEventListener.java index 4bd6d9850..a2e377817 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/engine/EngineProcessInstanceEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/engine/EngineProcessInstanceEventListener.java @@ -1,11 +1,15 @@ package cn.axzo.workflow.core.service.engine; +import cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum; import cn.axzo.workflow.core.listener.BpmProcessEventListener; -import cn.axzo.workflow.core.service.BpmProcessInstanceService; import com.google.common.collect.ImmutableSet; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.delegate.event.FlowableProcessStartedEvent; +import org.flowable.engine.delegate.event.impl.FlowableProcessStartedEventImpl; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -19,7 +23,7 @@ public class EngineProcessInstanceEventListener extends AbstractFlowableEngineEv ObjectProvider processEventListener; @Resource @Lazy - BpmProcessInstanceService processInstanceService; + private RuntimeService runtimeService; public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() .add(FlowableEngineEventType.PROCESS_CREATED) @@ -32,26 +36,29 @@ public class EngineProcessInstanceEventListener extends AbstractFlowableEngineEv super(PROCESS_INSTANCE_EVENTS); } - // @Override - // protected void processCreated(FlowableEngineEntityEvent event) { - // processInstanceService.createProcessInstanceExt((ProcessInstance) event.getEntity()); - // processEventListener.ifAvailable(listener -> listener.onCreated(event)); - // } + @Override + protected void processCreated(FlowableEngineEntityEvent event) { + processEventListener.ifAvailable(listener -> listener.onCreated(event)); + } @Override protected void processStarted(FlowableProcessStartedEvent event) { + runtimeService.updateBusinessStatus(((FlowableProcessStartedEventImpl) event).getProcessInstanceId(), + BpmProcessInstanceResultEnum.PROCESSING.getStatus()); processEventListener.ifAvailable(listener -> listener.onStarted(event)); } - // @Override - // protected void processCompleted(FlowableEngineEntityEvent event) { - // this.processInstanceService.updateProcessInstanceExtComplete((ProcessInstance) event.getEntity()); - // processEventListener.ifAvailable(listener -> listener.onCompleted(event)); - // } + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + runtimeService.updateBusinessStatus(event.getProcessInstanceId(), + BpmProcessInstanceResultEnum.APPROVED.getStatus()); + processEventListener.ifAvailable(listener -> listener.onCompleted(event)); + } - // @Override - // protected void processCancelled(FlowableCancelledEvent event) { - // processInstanceService.updateProcessInstanceExtCancel(event); - // processEventListener.ifAvailable(listener -> listener.onCancelled(event)); - // } + @Override + protected void processCancelled(FlowableCancelledEvent event) { + runtimeService.updateBusinessStatus(event.getProcessInstanceId(), + BpmProcessInstanceResultEnum.REJECTED.getStatus()); + processEventListener.ifAvailable(listener -> listener.onCancelled(event)); + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmProcessInstanceServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmProcessInstanceServiceImpl.java index ac04e4786..f1462d41c 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmProcessInstanceServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmProcessInstanceServiceImpl.java @@ -1,7 +1,6 @@ package cn.axzo.workflow.core.service.impl; import cn.axzo.workflow.core.common.enums.BpmProcessInstanceDeleteReasonEnum; -import cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum; import cn.axzo.workflow.core.common.exception.WorkflowEngineException; import cn.axzo.workflow.core.common.utils.BpmCollectionUtils; import cn.axzo.workflow.core.repository.mapper.InfoMapper; @@ -45,8 +44,6 @@ import java.util.*; import static cn.axzo.workflow.core.common.BpmConstants.*; import static cn.axzo.workflow.core.common.enums.BpmErrorCode.*; -import static cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum.CANCELLED; -import static cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum.PROCESSING; @Service @Slf4j @@ -214,7 +211,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // extDO.setExt(dto.getExt()); // // 补全流程实例的拓展表 // processInstanceExtMapper.updateByProcessInstanceId(extDO); - runtimeService.updateBusinessStatus(instance.getProcessInstanceId(), PROCESSING.getStatus()); + // runtimeService.updateBusinessStatus(instance.getProcessInstanceId(), PROCESSING.getStatus()); return instance.getProcessInstanceId(); } @@ -251,7 +248,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // extDO.setExt(dto.getExt()); // // 补全流程实例的拓展表 // processInstanceExtMapper.updateByProcessInstanceId(extDO); - runtimeService.updateBusinessStatus(instance.getProcessInstanceId(), PROCESSING.getStatus()); + // runtimeService.updateBusinessStatus(instance.getProcessInstanceId(), PROCESSING.getStatus()); return instance.getProcessInstanceId(); } @@ -284,7 +281,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService runtimeService.setVariables(instance.getId(), variables); deleteProcessInstance(instance.getId(), - BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(dto.getReason()), CANCELLED); + BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(dto.getReason())); return true; } @@ -418,9 +415,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // } @Override - public void deleteProcessInstance(String id, String reason, BpmProcessInstanceResultEnum resultEnum) { + public void deleteProcessInstance(String id, String reason) { log.info("当前线程: {}", Thread.currentThread().getName()); - runtimeService.updateBusinessStatus(id, resultEnum.getStatus()); runtimeService.deleteProcessInstance(id, reason); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmTaskServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmTaskServiceImpl.java index f78e9ae98..76c80ab98 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmTaskServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmTaskServiceImpl.java @@ -11,12 +11,14 @@ import cn.axzo.workflow.core.service.dto.request.task.BpmTaskAuditDTO; import cn.axzo.workflow.core.service.dto.request.task.BpmTaskCommentDTO; import cn.axzo.workflow.core.service.dto.request.task.BpmTaskTodoPageSearchDTO; import cn.axzo.workflow.core.service.dto.response.BpmPageResult; +import cn.axzo.workflow.core.service.dto.response.task.BpmHistoricTaskInstanceGroupVO; import cn.axzo.workflow.core.service.dto.response.task.BpmHistoricTaskInstanceVO; import cn.axzo.workflow.core.service.dto.response.task.BpmTaskDonePageItemVO; import cn.axzo.workflow.core.service.dto.response.task.BpmTaskTodoPageItemVO; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; @@ -41,7 +43,7 @@ import java.util.stream.Collectors; import static cn.axzo.workflow.core.common.BpmConstants.*; import static cn.axzo.workflow.core.common.enums.BpmErrorCode.*; -import static cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum.REJECTED; +import static cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum.APPROVED; import static cn.axzo.workflow.core.common.enums.BpmProcessInstanceResultEnum.valueOfStatus; import static cn.axzo.workflow.core.common.utils.BpmCollectionUtils.convertSet; @@ -191,6 +193,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // Map transientMap = new HashMap<>(); // transientMap.put(BpmConstants.INTERNAL_TASK_COMMENT, dto.getComment()); + Authentication.setAuthenticatedUserId(dto.getUserId()); taskService.addComment(dto.getTaskId(), instance.getId(), dto.getComment()); // 完成任务,审批通过 // FIXME 如果 task 被重复删除会抛出异常 @@ -217,8 +220,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { runtimeService.setVariables(instance.getId(), variables); // 删除流程实例,以实现驳回任务时,取消整个审批流程 - processInstanceService.deleteProcessInstance(instance.getId(), dto.getComment(), - REJECTED); + processInstanceService.deleteProcessInstance(instance.getId(), dto.getComment()); } @@ -243,25 +245,46 @@ public class BpmTaskServiceImpl implements BpmTaskService { // Map> taskMapByDefKey = // taskInstances.stream().collect(Collectors.groupingBy // (HistoricTaskInstance::getTaskDefinitionKey)); - List vos = historicTaskInstanceConverter.toVos(taskInstances); - List comments = taskService.getProcessInstanceComments(processInstanceId, CommentEntity.TYPE_COMMENT); - if (!CollectionUtils.isEmpty(vos) && !CollectionUtils.isEmpty(comments)) { - for (BpmHistoricTaskInstanceVO vo : vos) { - vo.setResult(valueOfStatus(instance.getBusinessStatus())); + List vos = historicTaskInstanceConverter.toVosSkipMiEnd(taskInstances); + Map> commentByTaskIdMap = taskService.getProcessInstanceComments(processInstanceId, + CommentEntity.TYPE_COMMENT).stream().collect(Collectors.groupingBy(Comment::getTaskId)); - for (Comment comment : comments) { - if (Objects.equals(vo.getTaskId(), comment.getTaskId()) - // 使用流程引擎的 Comment 表来记录审批意见,现目前业务一般只针对一个审批任务写一条审批建议 - // 但防止后续新增多条评论,被动覆盖,所以简单粗暴的只取第一条评论 - && !StringUtils.hasLength(vo.getComment())) { - vo.setComment(comment.getFullMessage()); - } - } + Set taskDefinitionKeys = new HashSet<>(); + int count = 0; + for (BpmHistoricTaskInstanceVO vo : vos) { + if (!taskDefinitionKeys.contains(vo.getTaskDefinitionKey()) && count == 0) { + vo.setResult(valueOfStatus(instance.getBusinessStatus())); + } else { + vo.setResult(APPROVED); } + Optional first = commentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList()) + .stream().filter(i -> Objects.equals(i.getUserId(), vo.getAssignee())) + .findFirst(); + first.ifPresent(i -> vo.setComment(i.getFullMessage())); + count++; } return vos; } + @Override + public List getHistoricTaskListGroupByProcessInstanceId(String processInstanceId, + String tenantId) { + List vos = getHistoricTaskListByProcessInstanceId(processInstanceId, tenantId); + + Map> voMapByTaskDefKey = + vos.stream().collect(Collectors.groupingBy(BpmHistoricTaskInstanceVO::getTaskDefinitionKey)); + + List groupVos = new ArrayList<>(); + for (Map.Entry> entry : voMapByTaskDefKey.entrySet()) { + BpmHistoricTaskInstanceGroupVO groupVO = new BpmHistoricTaskInstanceGroupVO(); + groupVO.setProcessInstanceId(processInstanceId); + groupVO.setTaskDefinitionKey(entry.getKey()); + groupVO.setTasks(entry.getValue()); + groupVos.add(groupVO); + } + return groupVos; + } + @Override public List getActiveTasksByProcessInstanceId(String processInstanceId) { if (StrUtil.isEmpty(processInstanceId)) { diff --git a/workflow-engine-core/src/main/resources/test.bpmn20.xml b/workflow-engine-core/src/main/resources/test.bpmn20.xml index f1bb18ad6..0a389e728 100644 --- a/workflow-engine-core/src/main/resources/test.bpmn20.xml +++ b/workflow-engine-core/src/main/resources/test.bpmn20.xml @@ -25,7 +25,7 @@ - + ${nrOfInstances / nrOfCompletedInstances > 0} calculateAssignerAtExecution(BpmTaskCalculateDTO delegateTask) { log.info("计算审核人,当前的审批任务 DefinitionKey 为: {}", delegateTask.getTaskDefinitionKey()); List assigners = new ArrayList<>(); - if (Objects.equals("NODE1639041346796_0.5413743892974803_0", delegateTask.getTaskDefinitionKey())) { + if (Objects.equals("sid-6589489F-EE93-43F2-A98F-78F5A578D39B", delegateTask.getTaskDefinitionKey())) { BpmTaskDelegateAssigner user1 = new BpmTaskDelegateAssigner(); user1.setAssignerId("66646"); user1.setAssignerName("王军"); @@ -30,6 +30,10 @@ public class BpmTaskDelegateImpl implements BpmTaskDelegate { user2.setAssignerId("66647"); user2.setAssignerName("王粒"); assigners.add(user2); + BpmTaskDelegateAssigner user3 = new BpmTaskDelegateAssigner(); + user3.setAssignerId("66648"); + user3.setAssignerName("小吴"); + assigners.add(user3); } if (Objects.equals("NODE1687933854432_0.45762305710855467_1", delegateTask.getTaskDefinitionKey())) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/BpmTaskController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/BpmTaskController.java index 3228a10e2..dbb150655 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/BpmTaskController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/BpmTaskController.java @@ -5,6 +5,7 @@ import cn.axzo.workflow.core.service.dto.request.task.BpmTaskAssigneeDTO; import cn.axzo.workflow.core.service.dto.request.task.BpmTaskAuditDTO; import cn.axzo.workflow.core.service.dto.request.task.BpmTaskTodoPageSearchDTO; import cn.axzo.workflow.core.service.dto.response.BpmPageResult; +import cn.axzo.workflow.core.service.dto.response.task.BpmHistoricTaskInstanceGroupVO; import cn.axzo.workflow.core.service.dto.response.task.BpmHistoricTaskInstanceVO; import cn.axzo.workflow.core.service.dto.response.task.BpmTaskDonePageItemVO; import cn.axzo.workflow.core.service.dto.response.task.BpmTaskTodoPageItemVO; @@ -78,16 +79,27 @@ public class BpmTaskController { /** * 获取指定流程实例的审批过程信息 + *

+ * 同一层级机构 */ - @GetMapping("/list") - public CommonResponse> getTaskListByProcessInstanceId(@RequestParam String processInstanceId, - @RequestParam(required = - false) String tenantId) { + @GetMapping("/list/flat") + public CommonResponse> getTaskListFlatByProcessInstanceId(@RequestParam String processInstanceId, + @RequestParam(required + = false) String tenantId) { log.info("获取历史已审批的列表详情 getTaskListByProcessInstanceId===>>>参数:{}", processInstanceId); return CommonResponse.success(bpmTaskService.getHistoricTaskListByProcessInstanceId(processInstanceId, tenantId)); } + @GetMapping("/list/group") + public CommonResponse> getTaskListGroupByProcessInstanceId(@RequestParam String processInstanceId, + @RequestParam(required + = false) String tenantId) { + log.info("获取历史已审批的列表详情 getTaskListByProcessInstanceId===>>>参数:{}", processInstanceId); + return CommonResponse.success(bpmTaskService.getHistoricTaskListGroupByProcessInstanceId(processInstanceId, + tenantId)); + } + /** * 获取实例正在审核的人列表 */