update - 迁移部分 2090 原分支代码

This commit is contained in:
wangli 2024-03-06 14:54:06 +08:00
parent 0df7a1ce0d
commit 5e7debcb7a
13 changed files with 599 additions and 94 deletions

View File

@ -18,10 +18,6 @@
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-consumer-spring-cloud-starter</artifactId>
</dependency>
<!--<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-spring-boot-starter</artifactId>
</dependency>-->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>workflow-engine-common</artifactId>

View File

@ -0,0 +1,40 @@
package cn.axzo.workflow.common.model.request.bpmn.process;
import cn.axzo.workflow.common.model.request.BpmPageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
/**
* 用于超管查询所有的流程实例入参模型
*
* @author wangli
* @since 06/03/2024 2:35pm
*/
@ApiModel("用于超管查询所有的流程实例入参模型")
@Data
@ToString(callSuper = true)
public class BpmnProcessInstanceAdminPageReqVO extends BpmPageParam {
@ApiModelProperty(value = "流程名称")
private String name;
@ApiModelProperty(value = "审批业务分类")
private String category;
@ApiModelProperty(value = "流程状态")
private String businessStatus;
@ApiModelProperty(value = "租户Id")
private String tenantId;
@ApiModelProperty(value = "开始时间")
private Date startTime;
@ApiModelProperty(value = "结束时间")
private Date endTime;
}

View File

@ -5,10 +5,12 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import java.io.Serializable;
import java.util.Objects;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID;
@ -29,6 +31,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID;
@AllArgsConstructor
@Accessors(chain = true)
@Validated
@Slf4j
public class BpmnTaskDelegateAssigner implements Serializable {
private static final long serialVersionUID = -8106887960942113552L;
@ -102,6 +105,31 @@ public class BpmnTaskDelegateAssigner implements Serializable {
}
}
public final boolean compareToOther(BpmnTaskDelegateAssigner other) {
return Objects.equals(personId, other.getPersonId());
}
/**
* 对引擎表中存的真实 assignee 进行比对
*
* @param assignee
* @return
*/
public final boolean compareToOther(String assignee) {
if (!StringUtils.hasText(assignee)) {
return false;
}
try {
String[] split = assignee.split("\\|");
if (Objects.equals(split[1], personId)) {
return true;
}
} catch (Exception e) {
log.warn("审批人模型数据比对发现意外数据, assignee: " + assignee);
}
return false;
}
public static BpmnTaskDelegateAssigner buildDummyAssigner(String assignee,
String assigneeType,
String assignerName) {

View File

@ -0,0 +1,109 @@
package cn.axzo.workflow.common.model.response.bpmn.process;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 超管流程实例的分页响应模型
*
* @author wangli
* @since 06/03/2024 2:37pm
*/
@ApiModel("超管流程实例的分页响应模型")
@Data
public class BpmnProcessInstanceAdminPageItemVO {
/**
* 流程实例 ID
*/
@ApiModelProperty(value = "流程实例 ID")
private String processInstanceId;
/**
* 流程实例名称
*/
@ApiModelProperty(value = "流程实例名称")
private String processInstanceName;
/**
* 业务状态
*/
@ApiModelProperty(value = "业务状态")
private String businessStatus;
/**
* 业务状态描述
*/
@ApiModelProperty(value = "业务状态描述")
private String businessStatusDesc;
/**
* 分类
*/
@ApiModelProperty(value = "审批业务分类")
private String category;
/**
* 分类描述
*/
@ApiModelProperty(value = "审批业务分类描述")
private String categoryDesc;
/**
* 分类关联项目部类型
*/
@ApiModelProperty(value = "项目部类型")
private String workspaceTypeCode;
/**
* 租户 ID
*/
@ApiModelProperty(value = "租户 ID")
private String tenantId;
/**
* 流程实例的总节点数
*/
@ApiModelProperty(value = "流程实例的总节点数")
private Integer totalNodeCount;
/**
* 当前运行到的节点
*/
@ApiModelProperty(value = "当前运行到的节点")
private Integer currentNodeCount;
/**
* 最近已完成的节点的结束时间
*/
@ApiModelProperty(value = "最近已完成的节点的结束时间")
private Date leastCompleteActivityEndTime;
/**
* 当前活动 ID
*/
@ApiModelProperty(value = "当前活动 ID")
private String currentActivityId;
/**
* 当期活动的名称
*/
@ApiModelProperty(value = "当期活动的名称")
private String currentActivityName;
/**
* 流程实例开始时间
*/
@ApiModelProperty(value = "流程实例开始时间")
private Date startTime;
/**
* 流程实例结束事件
*/
@ApiModelProperty(value = "流程实例结束事件")
private Date endTime;
}

View File

@ -14,6 +14,7 @@ import cn.axzo.workflow.core.engine.event.BizSpecifyAssigneeEventImpl;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
@ -42,10 +43,11 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_ALLOW_SKIP_USER
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_130;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner.buildDummyAssigner;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod;
@ -89,6 +91,16 @@ public class EngineExecutionStartListener implements ExecutionListener {
if (execution.hasVariable(assigneeListVariableName)) {
return;
}
if (Objects.equals(NODE_STARTER.getType(), currentActivityId)) {
// UserTask 多实例, 该变量用于引擎
BpmnTaskDelegateAssigner initiator = execution.getVariable(INTERNAL_INITIATOR,
BpmnTaskDelegateAssigner.class);
execution.setVariable(assigneeListVariableName, initiator.buildAssigneeId());
execution.setVariable(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
Lists.newArrayList(initiator));
return;
}
Process mainProcess = ProcessDefinitionUtil.getBpmnModel(execution.getProcessDefinitionId()).getMainProcess();
UserTask userTask = (UserTask) mainProcess.getFlowElement(currentActivityId);
@ -96,67 +108,81 @@ public class EngineExecutionStartListener implements ExecutionListener {
// version=1.2.1-SNAPSHOT 开始才给 process 节点增加了 serverVersion 属性
Optional<String> processServerVersion = getProcessServerVersion(mainProcess);
if (processServerVersion.isPresent()) {
Optional<BpmnFlowNodeType> nodeType = getNodeType(userTask);
if ((Objects.equals(FLOW_SERVER_VERSION_121, processServerVersion.get()) || Objects.equals(FLOW_SERVER_VERSION_130, processServerVersion.get()))
&& nodeType.isPresent()
&& (Objects.equals(NODE_TASK, nodeType.get()) || Objects.equals(NODE_BUSINESS, nodeType.get()))) {
getApprovalMethod(userTask).ifPresent(method -> {
List<String> assigneeIdList = new ArrayList<>();
switch (method) {
case autoPassed:
case autoRejection:
// Do nothing.
// 统一由 cn.axzo.workflow.server.controller.listener.task.AutoOperatorEventListener 来处理
break;
case bizSpecify:
// 构建一个虚拟的审批人避免因为没有审批人而导致流程触发了"审批人为空时"的处理逻辑, 当业务传入审批人时,需要更新这里的变量
BpmnTaskDelegateAssigner dummyApprover = buildDummyAssigner(DUMMY_ASSIGNEE,
DUMMY_ASSIGNEE_TYPE, "dummyApprover");
List<String> dummyAssigneeIdList = new ArrayList<>();
dummyAssigneeIdList.add(dummyApprover.buildAssigneeId());
execution.setVariable(assigneeListVariableName, dummyAssigneeIdList);
execution.setVariable(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId, Lists.newArrayList(dummyApprover));
// 触发事件
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration();
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
eventDispatcher.dispatchEvent(new BizSpecifyAssigneeEventImpl(ADD_ASSIGNEE, execution),
processEngineConfiguration.getEngineCfgKey());
break;
default:
// 这里只会是 human 这一种情况 因为 nobody 在转 BPMN 协议时Activity 直接变成了 ReceiveTask 节点了
List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
getApproverSpecify(userTask).ifPresent(specify -> {
if (log.isDebugEnabled()) {
log.debug("当前审批节点ID: {}, 节点名称: {}, 审批人指定方式: {}", userTask.getId(),
userTask.getName(), specify.getDesc());
}
assigners.addAll(approverSelect(specify.getType(), userTask, execution, true));
});
// 审批候选人为空时的兜底
emptyAssigneeHandle(assigners, userTask, execution);
for (BpmnTaskDelegateAssigner user : assigners) {
assigneeIdList.add(user.buildAssigneeId());
}
execution.setVariable(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
assigners);
break;
}
// UserTask 多实例, 该变量用于引擎
execution.setVariable(assigneeListVariableName, assigneeIdList);
});
}
calcTaskAssigner121(execution, userTask, processServerVersion.get(), assigneeListVariableName,
currentActivityId);
} else {
defaultCalcTaskAssigner(execution, userTask, currentActivityId, assigneeListVariableName);
calcTaskAssignerDefault(execution, userTask, currentActivityId, assigneeListVariableName);
}
}
private void calcTaskAssigner121(DelegateExecution execution, UserTask userTask, String processServerVersion,
String assigneeListVariableName, String currentActivityId) {
Optional<BpmnFlowNodeType> nodeType = getNodeType(userTask);
DefaultArtifactVersion supportVersion = new DefaultArtifactVersion(FLOW_SERVER_VERSION_121);
DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(processServerVersion);
if (currentVersion.compareTo(supportVersion) >= 0 && nodeType.isPresent()
&& (Objects.equals(NODE_TASK, nodeType.get()) || Objects.equals(NODE_BUSINESS, nodeType.get()))) {
getApprovalMethod(userTask).ifPresent(method -> {
List<String> assigneeIdList = new ArrayList<>();
switch (method) {
case autoPassed:
case autoRejection:
// Do nothing.
// 统一由 cn.axzo.workflow.server.controller.listener.task.AutoOperatorEventListener 来处理
break;
case bizSpecify:
// 构建一个虚拟的审批人避免因为没有审批人而导致流程触发了"审批人为空时"的处理逻辑, 当业务传入审批人时,需要更新这里的变量
BpmnTaskDelegateAssigner dummyApprover = buildDummyAssigner(DUMMY_ASSIGNEE,
DUMMY_ASSIGNEE_TYPE, "dummyApprover");
List<String> dummyAssigneeIdList = new ArrayList<>();
dummyAssigneeIdList.add(dummyApprover.buildAssigneeId());
execution.setVariable(assigneeListVariableName, dummyAssigneeIdList);
execution.setVariable(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId, Lists.newArrayList(dummyApprover));
// 触发事件
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration();
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
eventDispatcher.dispatchEvent(new BizSpecifyAssigneeEventImpl(ADD_ASSIGNEE, execution),
processEngineConfiguration.getEngineCfgKey());
break;
default:
// 这里只会是 human 这一种情况 因为 nobody 在转 BPMN 协议时Activity 直接变成了 ReceiveTask 节点了
List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
getApproverSpecify(userTask).ifPresent(specify -> {
if (log.isDebugEnabled()) {
log.debug("当前审批节点ID: {}, 节点名称: {}, 审批人指定方式: {}", userTask.getId(),
userTask.getName(), specify.getDesc());
}
assigners.addAll(approverSelect(specify.getType(), userTask, execution, true));
});
// 审批候选人为空时的兜底
emptyAssigneeHandle(assigners, userTask, execution);
for (BpmnTaskDelegateAssigner user : assigners) {
assigneeIdList.add(user.buildAssigneeId());
}
execution.setVariable(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
assigners);
break;
}
// UserTask 多实例, 该变量用于引擎
execution.setVariable(assigneeListVariableName, assigneeIdList);
});
}
}
/**
* 计算节点的待审批人为空时, 执行模型配置中的审批人为空时的处理方式
*
* @param assigners 节点计算的待审批人集合, 可能为空
* @param userTask 当前节点
* @param execution 当前执行实例
*/
private void emptyAssigneeHandle(List<BpmnTaskDelegateAssigner> assigners, UserTask userTask,
DelegateExecution execution) {
// 审批人为空并且当前节点设置了自动跳过条件
@ -250,7 +276,7 @@ public class EngineExecutionStartListener implements ExecutionListener {
* @param assigneeListVariableName
*/
@Deprecated
private void defaultCalcTaskAssigner(DelegateExecution execution, UserTask userTask, String currentActivityId,
private void calcTaskAssignerDefault(DelegateExecution execution, UserTask userTask, String currentActivityId,
String assigneeListVariableName) {
BpmnTaskCalculateDTO calculateDTO = new BpmnTaskCalculateDTO();

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.core.service;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateWithFormDTO;
@ -8,6 +9,7 @@ import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceMyP
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.HistoricProcessInstanceSearchDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.HistoricProcessInstanceVO;
@ -46,6 +48,14 @@ public interface BpmnProcessInstanceService {
*/
Boolean abortProcessInstance(BpmnProcessInstanceAbortDTO dto);
/**
* 超管查询所有流程实例数据
*
* @param dto
* @return
*/
BpmPageResult<BpmnProcessInstanceAdminPageItemVO> getAdminProcessInstancePage(BpmnProcessInstanceAdminPageReqVO dto);
/**
* 获得流程实例的分页 / 我发起的审批列表
*

View File

@ -62,6 +62,15 @@ public interface CategoryService {
*/
List<CategoryItemVO> list(CategorySearchDTO dto);
/**
* 查询指定类型和指定标签的数据
*
* @param type
* @param values
* @return
*/
List<CategoryItemVO> listByValue(String type, List<String> values);
/**
* 分页搜索
*

View File

@ -0,0 +1,83 @@
package cn.axzo.workflow.core.service.converter;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO;
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ActivityInstance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
import static org.mapstruct.NullValueCheckStrategy.ALWAYS;
/**
* 流程实例分页模型的 MapStruts 转换器
*
* @author wangli
* @since 2024/1/25 16:31
*/
@Mapper(
componentModel = "spring",
nullValueCheckStrategy = ALWAYS,
imports = Arrays.class
)
public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConverter<BpmnProcessInstanceAdminPageItemVO,
HistoricProcessInstance> {
@Mapping(target = "processInstanceId", source = "id")
@Mapping(target = "processInstanceName", source = "name")
@Mapping(target = "businessStatus", source = "businessStatus")
@Mapping(target = "category", source = "processDefinitionKey")
@Mapping(target = "startTime", source = "startTime")
@Mapping(target = "endTime", source = "endTime")
@Mapping(target = "tenantId", source = "tenantId")
@Override
BpmnProcessInstanceAdminPageItemVO toVo(HistoricProcessInstance entity);
default List<BpmnProcessInstanceAdminPageItemVO> toVos(List<HistoricProcessInstance> instances,
Map<String, List<FlowElement>> instanceFlowElementMap,
Map<String, ActivityInstance> liveActivityMap,
Map<String, ActivityInstance> leastEndActivityMap,
Map<String, CategoryItemVO> categoryMap) {
List<BpmnProcessInstanceAdminPageItemVO> result = new ArrayList<>();
instances.forEach(i -> {
BpmnProcessInstanceAdminPageItemVO vo = toVo(i);
if (Objects.equals(PROCESSING.getStatus(), i.getBusinessStatus())) {
List<FlowElement> flowElements =
instanceFlowElementMap.get(i.getId()).stream().filter(UserTask.class::isInstance).collect(Collectors.toList());
vo.setTotalNodeCount(flowElements.size());
// 进行中的节点
ActivityInstance activityInstance = liveActivityMap.get(i.getId());
for (int j = 0; j < flowElements.size(); j++) {
if (Objects.equals(flowElements.get(j).getId(), activityInstance.getActivityId())) {
vo.setCurrentNodeCount(j + 1);
vo.setCurrentActivityId(activityInstance.getActivityId());
vo.setCurrentActivityName(activityInstance.getActivityName());
}
}
// 最后已结束的节点
ActivityInstance leastEndActivity = leastEndActivityMap.get(i.getId());
vo.setLeastCompleteActivityEndTime(leastEndActivity.getEndTime());
}
CategoryItemVO category = categoryMap.getOrDefault(i.getProcessDefinitionKey(), new CategoryItemVO());
vo.setCategoryDesc(category.getLabel());
vo.setWorkspaceTypeCode(category.getWorkspaceTypeCode());
vo.setBusinessStatusDesc(BpmnProcessInstanceResultEnum.valueOfStatus(vo.getBusinessStatus()).getDesc());
result.add(vo);
});
return result;
}
}

View File

@ -2,6 +2,7 @@ package cn.axzo.workflow.core.service.impl;
import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateWithFormDTO;
@ -11,10 +12,12 @@ import cn.axzo.workflow.common.model.request.bpmn.process.HistoricProcessInstanc
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.HistoricProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.common.utils.BpmnCollectionUtils;
import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceCmd;
@ -23,8 +26,10 @@ import cn.axzo.workflow.core.engine.cmd.CustomForecastUserTaskAssigneeCmd;
import cn.axzo.workflow.core.engine.listener.EngineExecutionStartListener;
import cn.axzo.workflow.core.service.BpmnProcessDefinitionService;
import cn.axzo.workflow.core.service.BpmnProcessInstanceService;
import cn.axzo.workflow.core.service.CategoryService;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.service.converter.BpmnHistoricProcessInstanceConverter;
import cn.axzo.workflow.core.service.converter.BpmnProcessInstanceAdminPageItemConverter;
import cn.axzo.workflow.core.service.converter.BpmnProcessInstanceConverter;
import cn.axzo.workflow.core.service.converter.BpmnProcessInstancePageItemConverter;
import cn.axzo.workflow.core.service.support.FlowNodeForecastService;
@ -50,7 +55,10 @@ import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.history.NativeHistoricProcessInstanceQuery;
import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.NativeActivityInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.form.api.FormInfo;
@ -63,15 +71,19 @@ import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_ORG_RELATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_130;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
@ -88,6 +100,7 @@ import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.OR;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CARBON_COPY;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnProcessDefinitionRespCode.PROCESS_DEFINITION_ID_NOT_EXISTS;
@ -113,6 +126,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
@Resource
private HistoryService historyService;
@Resource
private BpmnProcessInstanceAdminPageItemConverter instanceAdminPageItemConverter;
@Resource
private BpmnProcessInstancePageItemConverter instancePageItemConverter;
@Resource
private BpmnProcessInstanceConverter instanceConverter;
@ -130,6 +145,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
private SpringProcessEngineConfiguration springProcessEngineConfiguration;
@Resource
private ExtAxHiTaskInstService extAxHiTaskInstService;
@Resource
private CategoryService categoryService;
@Override
public HistoricProcessInstance getProcessInstanceByBusinessKey(String businessKey, @Nullable String tenantId,
@ -327,6 +344,93 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
return true;
}
@Override
public BpmPageResult<BpmnProcessInstanceAdminPageItemVO> getAdminProcessInstancePage(BpmnProcessInstanceAdminPageReqVO dto) {
HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
if (StringUtils.isNotBlank(dto.getName())) {
query.processInstanceNameLike(dto.getName());
}
if (StringUtils.isNotBlank(dto.getCategory())) {
query.processDefinitionCategory(dto.getCategory());
}
if (StringUtils.isNotBlank(dto.getTenantId())) {
query.processInstanceTenantId(dto.getTenantId());
}
if (StringUtils.isNotBlank(dto.getBusinessStatus())) {
query.processInstanceBusinessStatus(dto.getBusinessStatus());
}
if (Objects.nonNull(dto.getStartTime()) && Objects.nonNull(dto.getEndTime())) {
query.startedAfter(dto.getStartTime());
query.startedBefore(dto.getEndTime());
}
List<HistoricProcessInstance> instances = query.orderByProcessInstanceStartTime().desc()
.listPage((dto.getPageNo() - 1) * dto.getPageSize(), dto.getPageSize());
if (CollectionUtils.isEmpty(instances)) {
return BpmPageResult.empty();
}
List<String> processingInstanceIds =
instances.stream().filter(i -> Objects.equals(PROCESSING.getStatus(), i.getBusinessStatus()))
.map(HistoricProcessInstance::getId).distinct().collect(Collectors.toList());
Map<String, List<FlowElement>> instanceFlowElementMap = new HashMap<>();
processingInstanceIds.forEach(i -> {
// 查询每个流程推测出来的总节点数
instanceFlowElementMap.put(i, forecastService.performProcessForecasting(i, null));
});
// 实例对应的当前运行到的节点
Map<String, ActivityInstance> liveActivityMap = getInstanceCurrentActivity(processingInstanceIds, false);
// 实例对应的最后一个已经完成的节点
Map<String, ActivityInstance> leastEndActivityMap = getInstanceLastActivity(processingInstanceIds);
List<String> categories = instances.stream().map(HistoricProcessInstance::getProcessDefinitionKey)
.distinct().collect(Collectors.toList());
Map<String, CategoryItemVO> categoryLabelMap = categoryService.listByValue(BPM_MODEL_CATEGORY, categories)
.stream().collect(Collectors.toMap(CategoryItemVO::getValue, Function.identity(), (s, t) -> s));
List<BpmnProcessInstanceAdminPageItemVO> vos = instanceAdminPageItemConverter.toVos(instances,
instanceFlowElementMap, liveActivityMap, leastEndActivityMap,
categoryLabelMap);
return new BpmPageResult<>(vos, query.count());
}
private Map<String, ActivityInstance> getInstanceLastActivity(List<String> processInstanceIds) {
return getInstanceCurrentActivity(processInstanceIds, true);
}
private Map<String, ActivityInstance> getInstanceCurrentActivity(List<String> processInstanceIds,
boolean leastEndActivity) {
if (CollectionUtils.isEmpty(processInstanceIds)) {
return Collections.emptyMap();
}
NativeActivityInstanceQuery nativeQuery = runtimeService.createNativeActivityInstanceQuery();
String tableName = managementService.getTableName(ActivityInstanceEntity.class);
StringBuilder querySql = new StringBuilder();
querySql.append("SELECT * FROM (")
.append("SELECT *, ROW_NUMBER() OVER (PARTITION BY PROC_INST_ID_ ORDER BY START_TIME_ DESC) AS rn ")
.append(" FROM ")
.append(tableName)
.append(" WHERE PROC_INST_ID_ in (");
for (int i = 0; i < processInstanceIds.size(); i++) {
querySql.append("#{processInstanceId_").append(i).append("}");
if (i < processInstanceIds.size() - 1) {
querySql.append(",");
}
nativeQuery.parameter("processInstanceId_" + i, processInstanceIds.get(i));
}
querySql.append(") and TASK_ID_ is not null ");
if (leastEndActivity) {
querySql.append(" and END_TIME_ is not null");
}
querySql.append(") as t WHERE rn = 1");
return nativeQuery.sql(querySql.toString()).list().stream()
.collect(Collectors.toMap(ActivityInstance::getProcessInstanceId, Function.identity(),
(s, t) -> s));
}
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id, String tenantId) {
HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery().processInstanceId(id);

View File

@ -230,6 +230,16 @@ public class CategoryServiceImpl extends ServiceImpl<ExtAxDictMapper, ExtAxDict>
return categoryConverter.toVos(extAxDicts);
}
@Override
public List<CategoryItemVO> listByValue(String type, List<String> values) {
LambdaQueryWrapper<ExtAxDict> queryWrapper = Wrappers.lambdaQuery(ExtAxDict.class)
.eq(StringUtils.isNotBlank(type), ExtAxDict::getType, type)
.in(!CollectionUtils.isEmpty(values), ExtAxDict::getValue, values)
.eq(ExtAxDict::getIsDelete, 0);
List<ExtAxDict> extAxDicts = dictMapper.selectList(queryWrapper);
return categoryConverter.toVos(extAxDicts);
}
@Override
public Boolean checkCategoryStatus(Long tenantId, String categoryCode) {
Optional<CategoryItemVO> optCategory = get(BPM_MODEL_CATEGORY, categoryCode);

View File

@ -1,9 +1,12 @@
package cn.axzo.workflow.server.controller.listener.task;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.listener.BpmnTaskEventListener;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.HistoryService;
@ -21,14 +24,20 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_TENANT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_NAME;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_REJECT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.REJECTION_AUTO_COMPLETED;
@ -45,23 +54,129 @@ import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.REJEC
public class AutoOperatorEventListener implements BpmnTaskEventListener, Ordered {
@Override
public int getOrder() {
return Integer.MIN_VALUE + 103;
return Integer.MIN_VALUE + 102;
}
private final TaskService taskService;
private final RuntimeService runtimeService;
private final RepositoryService repositoryService;
private final HistoryService historyService;
private final ExtAxHiTaskInstService extAxHiTaskInstService;
@Override
public void onCreated(DelegateTask delegateTask) {
if (log.isDebugEnabled()) {
log.debug("AutoOperatorEventListener#onCreated...{}", delegateTask.getTaskDefinitionKey());
}
if (Objects.equals(NODE_STARTER.getType(), delegateTask.getId())) {
BpmnTaskDelegateAssigner initiator = delegateTask.getVariable(INTERNAL_INITIATOR,
BpmnTaskDelegateAssigner.class);
delegateTask.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + delegateTask.getId(),
initiator);
delegateTask.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + delegateTask.getId(),
APPROVED.getStatus());
// 直接完成
taskService.complete(delegateTask.getId(), runtimeService.getVariables(delegateTask.getExecutionId()));
return;
}
Process mainProcess = repositoryService.getBpmnModel(delegateTask.getProcessDefinitionId()).getMainProcess();
UserTask userTask = (UserTask) mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());
// 如果 approverMethod = 自动通过或自动驳回时
checkApprovalMethod(delegateTask, userTask);
boolean exists = checkApproverExists(delegateTask, userTask, mainProcess);
if (exists) {
taskService.addComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), COMMENT_TYPE_ADVICE,
"同一审批人,自动过审");
autoPass(delegateTask);
}
if (log.isDebugEnabled()) {
log.debug("AutoOperatorEventListener#onCreated...end: {}", delegateTask.getTaskDefinitionKey());
}
}
/**
* 校验当前的审批人是否存在过前一个节点
*
* @param delegateTask
* @param userTask
* @param mainProcess
*/
private boolean checkApproverExists(DelegateTask delegateTask, UserTask userTask, Process mainProcess) {
AtomicBoolean exists = new AtomicBoolean(false);
historyService.createHistoricActivityInstanceQuery()
.processInstanceId(delegateTask.getProcessInstanceId())
.orderByHistoricActivityInstanceStartTime()
.desc().list().stream()
.filter(i -> !Objects.equals(i.getActivityId(), userTask.getId()))
.filter(i -> Objects.equals(i.getActivityType(), "userTask"))
.findFirst()
.ifPresent(i -> {
// 与发起人比对
if (Objects.equals(NODE_STARTER.getType(), i.getActivityId())) {
BpmnTaskDelegateAssigner initiator = delegateTask.getVariable(INTERNAL_INITIATOR,
BpmnTaskDelegateAssigner.class);
// FIXME 这里可能需要进行多版本处理
if (initiator.compareToOther(delegateTask.getAssignee())) {
exists.compareAndSet(false, true);
}
} else {
FlowElement flowElement = mainProcess.getFlowElement(i.getActivityId());
BpmnMetaParserHelper.getNodeType(flowElement).ifPresent(j -> {
if (Objects.equals(NODE_TASK, j)) {
// TODO 这里可能需要用 extAxHiTaskInstService 来支撑, 而不是用引擎自带表
historyService.createHistoricTaskInstanceQuery()
.processInstanceId(delegateTask.getProcessInstanceId())
.taskDefinitionKey(i.getActivityId()).list()
.stream().map(HistoricTaskInstance::getAssignee)
.filter(k -> Objects.equals(k, delegateTask.getAssignee()))
.findAny().ifPresent(k -> {
exists.compareAndSet(false, true);
});
}
});
}
});
return exists.get();
}
/**
* 如果审批人为空时, 读取 approverEmptyHandleType = 自动通过或自动驳回时
*
* @param delegateTask
* @param userTask
*/
private void checkApproverEmptyHandle(DelegateTask delegateTask, UserTask userTask) {
if (!StringUtils.hasLength(delegateTask.getAssignee())) {
BpmnMetaParserHelper.getApproverEmptyHandleType(userTask)
.ifPresent(approverEmptyHandleTypeEnum -> {
switch (approverEmptyHandleTypeEnum) {
case autoPassed:
autoPass(delegateTask);
break;
case autoRejection:
autoReject(delegateTask);
break;
case autoSkipped:
// autoReject(delegateTask);
// 非产品需求, 暂时不实现, 这里的功能似乎可以用 taskService.deleteTask 来实现
break;
default:
break;
}
});
}
}
/**
* 如果 approverMethod = 自动通过或自动驳回时
*
* @param delegateTask
* @param userTask
*/
private void checkApprovalMethod(DelegateTask delegateTask, UserTask userTask) {
BpmnMetaParserHelper.getApprovalMethod(userTask)
.ifPresent(approvalMethodEnum -> {
switch (approvalMethodEnum) {
@ -72,33 +187,10 @@ public class AutoOperatorEventListener implements BpmnTaskEventListener, Ordered
autoReject(delegateTask);
break;
default:
// 如果审批人为空时, 读取 approverEmptyHandleType = 自动通过或自动驳回时
if (!StringUtils.hasLength(delegateTask.getAssignee())) {
BpmnMetaParserHelper.getApproverEmptyHandleType(userTask)
.ifPresent(approverEmptyHandleTypeEnum -> {
switch (approverEmptyHandleTypeEnum) {
case autoPassed:
autoPass(delegateTask);
break;
case autoRejection:
autoReject(delegateTask);
break;
case autoSkipped:
// autoReject(delegateTask);
// 非产品需求, 暂时不实现, 这里的功能似乎可以用 taskService.deleteTask 来实现
break;
default:
break;
}
});
}
checkApproverEmptyHandle(delegateTask, userTask);
break;
}
});
if (log.isDebugEnabled()) {
log.debug("AutoOperatorEventListener#onCreated...end: {}", delegateTask.getTaskDefinitionKey());
}
}
private void autoReject(DelegateTask delegateTask) {

View File

@ -45,7 +45,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_
public class MessagePushTaskEventListener implements BpmnTaskEventListener, Ordered {
@Override
public int getOrder() {
return Integer.MIN_VALUE + 104;
return Integer.MIN_VALUE + 103;
}
private final RuntimeService runtimeService;

View File

@ -4,7 +4,5 @@
1. SnapshotBpmTaskTaskEventListener `(Integer.MIN_VALUE + 100)`
2. RocketMqBpmTaskEventListener `(Integer.MIN_VALUE+101)`
3. StartNodeAutoCompleteEventListener `(Integer.MIN_VALUE+102)`
4. AutoOperatorEventListener `(Integer.MIN_VALUE+103)`
5. PushPendingTaskEventListener `(Integer.MIN_VALUE+104)`
6. HistoryTaskHandleEventListener`(Integer.MIN_VALUE+105)`
3. ApprovalMethodAutoOperatorEventListener `(Integer.MIN_VALUE+102)`
4. MessagePushTaskEventListener `(Integer.MIN_VALUE+103)`