Merge remote-tracking branch 'origin/master' into feature/REQ-4624

# Conflicts:
#	workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/FormAdminApi.java
#	workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java
#	workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java
This commit is contained in:
wangli 2025-08-06 17:14:17 +08:00
commit bb0d93cfce
24 changed files with 1264 additions and 701 deletions

View File

@ -5,6 +5,7 @@ import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.dto.SignFileDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.log.LogApproveSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
@ -27,6 +28,7 @@ import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAd
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO;
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.ExtProcessLogVO;
import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO;
@ -329,4 +331,16 @@ public interface ProcessInstanceApi {
@GetMapping("/api/process/instance/final/docs")
@InvokeMode(SYNC)
CommonResponse<List<SignFileDTO>> getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId);
/**
* 查询 ExtAxProcessLog 表中审批人的冗余信息
*
* @param dto
* @return
*/
@Operation(summary = "查询 ExtAxProcessLog 表中审批人的冗余信息")
@GetMapping("/api/process/instance/log/approve/ext")
@Manageable
@InvokeMode(SYNC)
CommonResponse<List<ExtProcessLogVO>> getProcessLogByInstanceIdAndPersonId(@Validated @RequestBody LogApproveSearchDTO dto);
}

View File

@ -108,6 +108,7 @@ public interface ProcessTaskApi {
/**
* 用于系统内部操作跳转到指定节点
*
* @param dto 请求参数
* @return 是否成功
*/
@ -177,7 +178,7 @@ public interface ProcessTaskApi {
@PostMapping("/api/process/task/countersign")
CommonResponse<Boolean> countersignTask(@Validated @RequestBody BpmnTaskCountersignDTO dto);
/**
/**
* 重置节点审批人提级审批
*
* @param dto

View File

@ -6,10 +6,8 @@ import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FromDataSearchDTO;
import cn.axzo.workflow.common.model.response.form.FormVO;
import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO;
import cn.axzo.workflow.common.model.response.form.instance.FormDataVO;
import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.validation.annotation.Validated;
@ -37,8 +35,7 @@ public interface FormAdminApi {
CommonResponse<List<FormVO>> formPage(@Validated @RequestBody FormSearchDTO dto);
/**
* 获取流程配置的表单模型和权限主要用于发起页
*
* 获取指定审批业务的流程表单设置
* @param dto
* @return
*/
@ -58,14 +55,4 @@ public interface FormAdminApi {
@PostMapping("/api/form/admin/instance/render")
@InvokeMode(SYNC)
CommonResponse<FormInstanceVO> getFormInstance(@Validated @RequestBody FormDetailDTO dto);
/**
* 获取流程中表单直接数据不包含模型
*
* @param dto
* @return
*/
@PostMapping("/instance/form/data")
@InvokeMode(SYNC)
CommonResponse<List<FormDataVO>> getFormData(@Validated @RequestBody FromDataSearchDTO dto);
}

View File

@ -32,6 +32,7 @@ public enum BpmnInstanceRespCode implements IModuleRespCode {
PROCESS_DOC_ID_NOT_IN_MODEL("017", "当前流程中,不存在指定文档"),
PROCESS_SIGN_DATA_NOT_EXISTS("018", "签署业务审批未获取到初始模板复制数据"),
PROCESS_INSTANCE_CANT_REMIND("019", "流程实例【{}】不存在, 不能评论"),
PROCESS_EXT_LOG_PARAM_ERROR("020", "查询流程日志的审批人PersonId参数不合法"),
;
private final String code;
private final String message;

View File

@ -0,0 +1,19 @@
package cn.axzo.workflow.common.model;
import lombok.Data;
/**
* 节点检测告警对象
*
* @author wangli
* @since 2024-09-13 11:37
*/
@Data
public class NextNodePreCheckAlterDTO {
private String processInstanceId;
private String activityId;
private String errorMsg;
}

View File

@ -0,0 +1,34 @@
package cn.axzo.workflow.common.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 岗位信息
*
* @author wangli
* @since 2025-06-23 19:35
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class NodeInfo implements Serializable {
private static final long serialVersionUID = -6092011348559752255L;
/**
* 岗位名称
*/
private Long nodeId;
/**
* 岗位编码
*/
private String nodeName;
}

View File

@ -58,6 +58,11 @@ public class OrgSnapshotInfo implements Serializable {
*/
private String topNodeId;
/**
* 部门快照信息集合
*/
private List<NodeInfo> nodeInfos;
/**
* 岗位快照信息集合
*/

View File

@ -7,7 +7,6 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 审批人员的组织架构信息
@ -29,12 +28,12 @@ public class OrgStructureSnapshotInfo implements Serializable {
private String personName;
/**
* 头像
* 头像(仅为审批任务被接受那一刻的电话不可作为对外展示数据)
*/
private String avatarUrl;
/**
* 手机号
* 手机号(仅为审批任务被接受那一刻的电话不可作为对外展示数据)
*/
private String phone;
@ -49,7 +48,7 @@ public class OrgStructureSnapshotInfo implements Serializable {
private int workspaceType;
/**
* 项目快照信息
* 组织快照信息
*/
private OrgSnapshotInfo snapshotInfo;

View File

@ -0,0 +1,57 @@
package cn.axzo.workflow.common.model.request.bpmn.log;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
/**
* 用于查询审批日志的人的模型
*
* @author wangli
* @since 2025-07-07 19:40
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LogApproveSearchDTO {
@ApiModelProperty("流程实例 ID")
@NotBlank(message = "流程实例 ID 不能为空")
private String processInstanceId;
/**
* 审批任务 ID如果有值优先使用 taskId 查询
* <p>
* 如果没有值 personIdtenantIdouId 一定不能为 null
*/
@ApiModelProperty("审批任务 ID")
private String taskId;
/**
* 如果没传 taskId那么该属性必有值
*/
@ApiModelProperty("审批人 PersonId")
private String personId;
/**
* 如果没传 taskId那么该属性必有值
*/
@ApiModelProperty("审批人 tenantId")
private String tenantId;
/**
* 如果没传 taskId那么该属性应该有值如果是工人可以没有值
*/
@ApiModelProperty("审批人 ouId")
private String ouId;
@ApiModelProperty("指定状态,如果为空,默认指定审批中状态的数据")
private BpmnProcessInstanceResultEnum status;
}

View File

@ -26,9 +26,12 @@ public class StartFormSearchDTO {
/**
* 租户 ID
* <p>
* 对应安心筑的工作台 ID
*/
@ApiModelProperty(value = "租户 ID")
private String tenantId = NO_TENANT_ID;
@NotBlank(message = "租户 ID (工作台)不能为空")
private String tenantId = NO_TENANT_ID;
/**
* 显示原始默认值的变量

View File

@ -0,0 +1,53 @@
package cn.axzo.workflow.common.model.response.bpmn.process;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.dto.OrgStructureSnapshotInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 审批扩展日志表的响应模型
*
* @author wangli
* @since 2025-07-08 10:09
*/
@ApiModel("流程实例日志表响应模型")
@Data
public class ExtProcessLogVO {
@ApiModelProperty("日志表主键 ID")
private Long id;
@ApiModelProperty("流程实例 ID")
private String processInstanceId;
@ApiModelProperty("流程归属的租户 ID工作台 ID")
private String processTenantId;
@ApiModelProperty("审批节点 ID")
private String activityId;
@ApiModelProperty("审批节点名称")
private String activityName;
@ApiModelProperty("审批任务 ID")
private String taskId;
@ApiModelProperty("审批人自然人 ID")
private String assigneePersonId;
@ApiModelProperty("审批人租户 ID工作台 ID")
private String assigneeTenantId;
@ApiModelProperty("审批人姓名")
private String assigneeName;
@ApiModelProperty("审批人单位 ID")
private String assigneeOuId;
@ApiModelProperty("日志的处理状态")
private BpmnProcessInstanceResultEnum status;
@ApiModelProperty("审批人在接受到审批任务时的组织快照信息")
private OrgStructureSnapshotInfo orgStructureSnapshotInfo;
}

View File

@ -22,6 +22,7 @@ import cn.axzo.workflow.core.engine.job.AsyncRemindTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncResetApproversUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncTermNodeAlterJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncTransferUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.NextActivityConfigCheckJobHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncJobLogClearTraceExceptionHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncRunnableExceptionExceptionHandler;
import cn.axzo.workflow.core.engine.persistence.CustomMybatisHistoricProcessInstanceDataManager;
@ -120,6 +121,7 @@ public class FlowableConfiguration {
configuration.addCustomJobHandler(new AsyncApproveTaskWithFormJobHandler());
configuration.addCustomJobHandler(new AsyncRemindTaskJobHandler(refreshProperties));
configuration.addCustomJobHandler(new AsyncResetApproversUserTaskJobHandler(extAxHiTaskInstService));
configuration.addCustomJobHandler(new NextActivityConfigCheckJobHandler());
configurers.forEach(i -> configuration.addCustomJobHandler(i.getJobHandler()));
// 异步任务异常重试时间间隔
configuration.setDefaultFailedJobWaitTime(30);

View File

@ -0,0 +1,186 @@
package cn.axzo.workflow.core.engine.job;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.model.NextNodePreCheckAlterDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
import cn.axzo.workflow.core.deletage.BpmnTaskAssigneeSelector;
import cn.axzo.workflow.core.listener.Alter;
import cn.axzo.workflow.core.service.support.FlowNodeForecastService;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.ReceiveTask;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.query.QueryProperty;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverEmptyHandleType;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify;
/**
* 检查节点配置是否合法
*
* @author wangli
* @since 2025-07-08 19:44
*/
@Slf4j
public class NextActivityConfigCheckJobHandler extends AbstractJobHandler implements JobHandler {
public static final String TYPE = "next-activity-config-check";
@Override
public String getType() {
return TYPE;
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("NextActivityConfigCheckJobHandler executing... 当前节点为: {}", job.getJobHandlerConfiguration());
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
FlowNodeForecastService forecastService = SpringContextUtils.getBean(FlowNodeForecastService.class);
String currentActivityId = job.getJobHandlerConfiguration();
List<FlowElement> flowElements = forecastService.performProcessForecasting(job.getProcessInstanceId(), null, currentActivityId, false);
if (CollectionUtils.isEmpty(flowElements)) {
return;
}
try {
doCheck(job, flowElements, processEngineConfiguration);
} catch (Exception e) {
// 有任何异常则通过钉钉告警
log.warn("NextActivityConfigCheckJobHandler msg: {}", e.getMessage(), e);
Alter alter = SpringContextUtils.getBean(Alter.class);
FlowElement flowElement = ListUtils.emptyIfNull(flowElements).stream().filter(i -> i instanceof UserTask || i instanceof ReceiveTask || i instanceof ServiceTask).findFirst().orElse(null);
NextNodePreCheckAlterDTO alterDTO = new NextNodePreCheckAlterDTO();
alterDTO.setProcessInstanceId(job.getProcessInstanceId());
alterDTO.setActivityId(Objects.nonNull(flowElement) ? flowElement.getId() : null);
alterDTO.setErrorMsg(e.getMessage());
alter.invoke(alterDTO);
}
}
private void doCheck(JobEntity job, List<FlowElement> flowElements, ProcessEngineConfigurationImpl processEngineConfiguration) {
AtomicReference<String> checkActivityId = new AtomicReference<>("");
ListUtils.emptyIfNull(flowElements).stream()
.filter(i -> i instanceof UserTask || i instanceof ReceiveTask || i instanceof ServiceTask)
.findFirst().ifPresent(flowElement -> {
log.info("NextActivityConfigCheckJobHandler, 实例 ID{}, 计算节点为:{}", job.getJobHandlerConfiguration(), flowElement.getId());
getApprovalMethod(flowElement).ifPresent(method -> {
switch (method) {
case autoPassed:
case autoRejection:
case bizSpecify:
case nobody:
break;
default:
List<Execution> executions = processEngineConfiguration.getRuntimeService().createExecutionQuery()
.processInstanceId(job.getProcessInstanceId())
.orderBy((QueryProperty) () -> "START_TIME_").desc().list();
if (CollectionUtils.isEmpty(executions)) {
return;
}
// 这里只会是 human 这一种情况 因为 nobody 在转 BPMN 协议时Activity 直接变成了 ReceiveTask 节点了
List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
getApproverSpecify(flowElement).ifPresent(specify -> {
assigners.addAll(approverSelect(specify.getType(), flowElement, (DelegateExecution) executions.get(0), true));
});
// 审批候选人为空时的兜底
emptyAssigneeHandle(assigners, flowElement, (DelegateExecution) executions.get(0));
break;
}
});
checkActivityId.set(flowElement.getId());
});
checkActivityId.get();
}
public List<BpmnTaskDelegateAssigner> approverSelect(String type, FlowElement flowElement,
DelegateExecution execution,
Boolean throwException) {
List<BpmnTaskAssigneeSelector> selectors = new ArrayList<>(SpringContextUtils.getBeansOfType(BpmnTaskAssigneeSelector.class).values());
List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
ListUtils.emptyIfNull(selectors).forEach(select -> {
if (select.support(type)) {
log.info("NextActivityConfigCheckJobHandler-审批任务节点 Id:{}, Name: {}, 审批人指定枚举: {}", flowElement.getId(), flowElement.getName(), type);
List<BpmnTaskDelegateAssigner> selected = select.select(flowElement, execution, throwException);
log.info("NextActivityConfigCheckJobHandler-审批任务节点 Id:{} 的审批人集合为: {}", flowElement.getId(), JSONUtil.toJsonStr(selected));
assigners.addAll(selected);
}
});
return assigners;
}
/**
* 计算节点的待审批人为空时, 执行模型配置中的审批人为空时的处理方式
*
* @param assigners 节点计算的待审批人集合, 可能为空
* @param flowElement 当前节点
* @param execution 当前执行实例
*/
private void emptyAssigneeHandle(List<BpmnTaskDelegateAssigner> assigners, FlowElement flowElement,
DelegateExecution execution) {
// 审批人为空并且当前节点设置了自动跳过条件
if (!CollectionUtils.isEmpty(assigners)) {
return;
}
log.info("NextActivityConfigCheckJobHandler-当前节点id: [{}], name: [{}] 审批人为空, 将执行审批人为空的兜底配置!", flowElement.getId(), flowElement.getName());
getApproverEmptyHandleType(flowElement).ifPresent(type -> {
log.info("NextActivityConfigCheckJobHandler-节点兜底的配置模式:[{}]", type.getType());
switch (type) {
case autoPassed:
case autoRejection:
break;
case transferToAdmin:
try {
assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.transferToAdmin.getType(), flowElement,
execution, true));
if (CollectionUtils.isEmpty(assigners)) {
throw new IllegalArgumentException("审批人为空后转交管理员仍然为空");
}
} catch (Exception e) {
if (e instanceof IllegalArgumentException) {
throw new RuntimeException(e.getMessage());
} else {
throw new RuntimeException("审批人为空后转交管理员失败, 内部计算信息:" + e.getMessage());
}
}
break;
case specifyAssignee:
try {
assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.specifyAssignee.getType(), flowElement,
execution, true));
if (CollectionUtils.isEmpty(assigners)) {
throw new IllegalArgumentException("审批人为空后转交指定人员仍然为空,可能被指定人员已离职");
}
} catch (Exception e) {
if (e instanceof IllegalArgumentException) {
throw new RuntimeException(e.getMessage());
} else {
throw new RuntimeException("审批人为空后转交指定人员失败,内部计算信息:" + e.getMessage());
}
}
break;
default:
break;
}
});
}
}

View File

@ -13,6 +13,7 @@ import cn.axzo.workflow.core.deletage.BpmnTaskCalculateDTO;
import cn.axzo.workflow.core.deletage.BpmnTaskDelegate;
import cn.axzo.workflow.core.deletage.MockTaskAssigneeSelector;
import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceAsyncCmd;
import cn.axzo.workflow.core.engine.job.NextActivityConfigCheckJobHandler;
import cn.axzo.workflow.core.util.DingTalkUtils;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
@ -21,10 +22,14 @@ import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.ManagementService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.job.service.TimerJobService;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
@ -35,6 +40,7 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -84,7 +90,6 @@ public class EngineExecutionStartListener implements ExecutionListener {
@Resource
private SupportRefreshProperties refreshProperties;
@Override
public void notify(DelegateExecution execution) {
String currentActivityId = execution.getCurrentActivityId();
@ -98,11 +103,39 @@ public class EngineExecutionStartListener implements ExecutionListener {
// version=1.2.1-SNAPSHOT 开始才给 process 节点增加了 serverVersion 属性
Optional<String> processServerVersion = getProcessServerVersion(mainProcess);
if (processServerVersion.isPresent()) {
// 创建检查下个节点的配置
createCheckNextActivityJob(execution.getProcessInstanceId(), currentActivityId);
calcTaskAssigner121(execution, userTask, processServerVersion.get(), assigneeListVariableName,
currentActivityId);
} else {
calcTaskAssignerDefault(execution, userTask, currentActivityId, assigneeListVariableName);
}
}
/**
* 提前检查下个节点的配置是否异常
*/
private void createCheckNextActivityJob(String processInstanceId, String activityId) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
ManagementService managementService = processEngineConfiguration.getManagementService();
// 创建一个只执行一次的 Job
managementService.executeCommand(commandContext -> {
TimerJobService timerJobService = CommandContextUtil.getTimerJobService();
TimerJobEntity timerJobEntity = timerJobService.createTimerJob();
timerJobEntity.setJobType("timer");
timerJobEntity.setJobHandlerType(NextActivityConfigCheckJobHandler.TYPE); // 这里填写你自定义的 JobHandler 类型
timerJobEntity.setProcessInstanceId(processInstanceId);
timerJobEntity.setExecutionId(null);
timerJobEntity.setDuedate(new Date()); // 立即执行
timerJobEntity.setRepeat(null); // 不重复
timerJobEntity.setRetries(1);
timerJobEntity.setJobHandlerConfiguration(activityId); // 可选传递参数
timerJobService.scheduleTimerJob(timerJobEntity);
return null;
});
}
private void calcTaskAssigner121(DelegateExecution execution, UserTask userTask, String processServerVersion,

View File

@ -10,5 +10,5 @@ import cn.axzo.workflow.common.model.dto.AlterDTO;
*/
public interface Alter {
void invoke(AlterDTO alterDTO);
void invoke(Object obj);
}

View File

@ -1627,6 +1627,12 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
} else if (Objects.equals(BpmnFlowNodeMode.OR, e.getNodeMode())) {
build.setOperationDesc(countPerson + "人或签,仅一人同意即可");
}
if (Objects.equals(countPerson, 1)) {
// 如果未来节点是单人则按单人节点展示
build.setAssigneeSnapshot(build.getForecastAssignees().get(0));
build.setOperationDesc(build.getAssigneeSnapshot().getAssignerName());
build.setForecastAssignees(null);
}
}
break;
}

View File

@ -114,6 +114,10 @@ public class ExtAxProcessLogServiceImpl implements ExtAxProcessLogService {
.eq(StringUtils.hasText(log.getActivityName()), ExtAxProcessLog::getActivityName, log.getActivityName())
.eq(StringUtils.hasText(log.getTaskId()), ExtAxProcessLog::getTaskId, log.getTaskId())
.eq(StringUtils.hasText(log.getTenantId()), ExtAxProcessLog::getTenantId, log.getTenantId())
.eq(StringUtils.hasText(log.getStatus()), ExtAxProcessLog::getStatus, log.getStatus())
.eq(Objects.nonNull(log.getAssigneeId()), ExtAxProcessLog::getAssigneeId, log.getAssigneeId())
.eq(StringUtils.hasText(log.getAssigneeTenantId()), ExtAxProcessLog::getAssigneeTenantId, log.getAssigneeTenantId())
.eq(StringUtils.hasText(log.getAssigneeOuId()), ExtAxProcessLog::getAssigneeOuId, log.getAssigneeOuId())
.eq(ExtAxProcessLog::getIsDelete, log.getIsDelete());
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.core.util;
import cn.axzo.workflow.common.model.NextNodePreCheckAlterDTO;
import cn.axzo.workflow.common.model.dto.AlterDTO;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
@ -167,6 +168,26 @@ public class DingTalkUtils {
return sb.toString();
}
public static void sendDingTalkForNodePreCheck(String profile, NextNodePreCheckAlterDTO alterDTO, List<String> alterMobiles) {
OapiRobotSendRequest request = new OapiRobotSendRequest();
request.setMsgtype("markdown");
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
markdown.setTitle("Notice 审批模板节点预检查告警, Env: " + profile);
markdown.setText("#### [" + profile + "]审批模板节点预检查告警\n" +
// "> 相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" +
"> 实例 ID" + alterDTO.getProcessInstanceId() + "\n\n" +
"> 检测节点 ID" + alterDTO.getActivityId() + "\n\n" +
"> ##### 错误信息:" + alterDTO.getErrorMsg() + "\n\n" +
mobiles(alterMobiles));
request.setMarkdown(markdown);
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
at.setAtMobiles(alterMobiles);
at.setIsAtAll(false);
request.setAt(at);
sendDingTalk(request);
}
public static void sendDingTalkForTransferToAdminError(String profile, String processInstanceId, String taskDefinitionKey, Object orgScopes, String targetUrl) {
OapiRobotSendRequest request = new OapiRobotSendRequest();
request.setMsgtype("markdown");

View File

@ -5,6 +5,7 @@ import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum;
import cn.axzo.riven.client.feign.DingDingMsgApi;
import cn.axzo.riven.client.model.SampleMarkdown;
import cn.axzo.riven.client.req.DingDingSendRebootGroupMsgReq;
import cn.axzo.workflow.common.model.NextNodePreCheckAlterDTO;
import cn.axzo.workflow.common.model.dto.AlterDTO;
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
import cn.axzo.workflow.core.listener.Alter;
@ -39,13 +40,47 @@ public class DingTalkAlter implements Alter {
private DingDingMsgApi dingDingMsgApi;
@Override
public void invoke(AlterDTO alterDTO) {
log.info("send biz node alter : {}", JSON.toJSONString(alterDTO));
if (Objects.equals(profile, "master")) {
DingTalkUtils.sendDingTalkForBizNodeAlter(profile, alterDTO, refreshProperties.getAlterMobiles());
} else {
rivenDingtalk(alterDTO);
public void invoke(Object obj) {
log.info("send biz node alter : {}", JSON.toJSONString(obj));
if (obj instanceof AlterDTO) {
AlterDTO alterDTO = (AlterDTO) obj;
if (Objects.equals(profile, "master")) {
DingTalkUtils.sendDingTalkForBizNodeAlter(profile, alterDTO, refreshProperties.getAlterMobiles());
} else {
rivenDingtalk(alterDTO);
}
}
if (obj instanceof NextNodePreCheckAlterDTO) {
NextNodePreCheckAlterDTO alterDTO = (NextNodePreCheckAlterDTO) obj;
if(Objects.equals(profile, "master")){
DingTalkUtils.sendDingTalkForNodePreCheck(profile, alterDTO, refreshProperties.getAlterMobiles());
} else {
rivenDingtalkForNodePreCheck(alterDTO);
}
}
}
private void rivenDingtalkForNodePreCheck(NextNodePreCheckAlterDTO alterDTO) {
DingDingSendRebootGroupMsgReq req = new DingDingSendRebootGroupMsgReq();
req.setDingDingScene("WORKFLOW_ENGINE_BIZNODE_ALTER");
String processInstanceId = alterDTO.getProcessInstanceId();
String title = "Notice 审批模板节点预检查告警, Env: " + profile;
String text = "#### [" + profile + "]审批模板节点预检查告警\n" +
// "> 相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" +
"> 实例 ID" + alterDTO.getProcessInstanceId() + "\n\n" +
"> 检测节点 ID" + alterDTO.getActivityId() + "\n\n" +
"> ##### 错误信息:" + alterDTO.getErrorMsg() + "\n\n" +
mobiles(refreshProperties.getAlterMobiles());
SampleMarkdown markdown = new SampleMarkdown(title, text);
JSONObject markdownJson = JSONObject.parseObject(markdown.toJson());
JSONObject atMobiles = new JSONObject();
atMobiles.put("atMobiles", refreshProperties.getAlterMobiles());
markdownJson.put("at", atMobiles);
markdownJson.put("isAtAll", false);
req.setDingDingJson(markdownJson.toJSONString());
req.setMsgType(DingTalkMsgTypeEnum.sampleMarkdown);
dingDingMsgApi.sendRebootGroupMsg(req);
}
private void rivenDingtalk(AlterDTO alterDTO) {

View File

@ -5,9 +5,11 @@ import cn.axzo.oss.http.api.ServerFileServiceApi;
import cn.axzo.oss.http.model.ApiSignUrlDownloadRequest;
import cn.axzo.oss.http.model.ApiSignUrlDownloadResponse;
import cn.axzo.workflow.client.feign.bpmn.ProcessInstanceApi;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.dto.SignFileDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.log.LogApproveSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
@ -33,6 +35,7 @@ import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAd
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO;
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.ExtProcessLogVO;
import cn.axzo.workflow.common.model.response.bpmn.process.HistoricProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
@ -41,9 +44,11 @@ import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceLogVO;
import cn.axzo.workflow.common.valid.group.ValidGroup;
import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd;
import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper;
import cn.axzo.workflow.core.service.BpmnProcessInstanceService;
import cn.axzo.workflow.core.service.BpmnProcessTaskService;
import cn.axzo.workflow.core.service.ExtAxProcessLogService;
import cn.axzo.workflow.core.service.ExtAxProcessSignService;
import cn.axzo.workflow.core.service.ExtAxReModelService;
import cn.axzo.workflow.core.service.ExtAxReadRecordService;
@ -52,6 +57,7 @@ import cn.axzo.workflow.server.common.annotation.RepeatSubmit;
import cn.axzo.workflow.server.common.util.RpcExternalUtil;
import cn.axzo.workflow.server.controller.web.BasicPopulateAvatarController;
import cn.azxo.framework.common.model.CommonResponse;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Operation;
@ -86,6 +92,7 @@ import java.util.stream.Stream;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_ID_NOT_IN_MODEL;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_READ_PARAM_ERROR;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_EXT_LOG_PARAM_ERROR;
import static cn.azxo.framework.common.model.CommonResponse.success;
/**
@ -116,6 +123,8 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
private ExtAxProcessSignService extAxProcessSignService;
@Resource
private ExtAxReModelService extAxReModelService;
@Resource
private ExtAxProcessLogService extAxProcessLogService;
/**
* 超管查询所有流程实例
@ -527,4 +536,52 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
public CommonResponse<List<SignFileDTO>> getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId) {
return success(extAxProcessSignService.findByProcessInstanceId(processInstanceId).getFileArchive());
}
/**
* 查询 ExtAxProcessLog 表中审批人的冗余信息
*
* @param dto
* @return
*/
@Operation(summary = "查询 ExtAxProcessLog 表中审批人的冗余信息")
@GetMapping("/log/approve/ext")
@Override
public CommonResponse<List<ExtProcessLogVO>> getProcessLogByInstanceIdAndPersonId(@Validated @RequestBody LogApproveSearchDTO dto) {
ExtAxProcessLog query = new ExtAxProcessLog();
query.setProcessInstanceId(dto.getProcessInstanceId());
if (StringUtils.hasText(dto.getTaskId())) {
query.setTaskId(dto.getTaskId());
} else {
if (!NumberUtil.isLong(dto.getPersonId())) {
throw new WorkflowEngineException(PROCESS_EXT_LOG_PARAM_ERROR);
}
query.setAssigneeId(Long.valueOf(dto.getPersonId()));
query.setAssigneeTenantId(dto.getTenantId());
query.setAssigneeOuId(Objects.isNull(dto.getOuId()) ? "" : dto.getOuId());
}
if (Objects.isNull(dto.getStatus())) {
query.setStatus(BpmnProcessInstanceResultEnum.PROCESSING.getStatus());
} else {
query.setStatus(dto.getStatus().getStatus());
}
List<ExtAxProcessLog> extAxProcessLogs = extAxProcessLogService.genericQuery(query);
return success(ListUtils.emptyIfNull(extAxProcessLogs).stream().map(i -> {
ExtProcessLogVO logVO = new ExtProcessLogVO();
logVO.setId(i.getId());
logVO.setProcessInstanceId(i.getProcessInstanceId());
logVO.setProcessTenantId(i.getTenantId());
logVO.setActivityId(i.getActivityId());
logVO.setActivityName(i.getActivityName());
logVO.setTaskId(i.getTaskId());
logVO.setAssigneePersonId(String.valueOf(i.getAssigneeId()));
logVO.setAssigneeTenantId(i.getAssigneeTenantId());
logVO.setAssigneeName(i.getAssigneeName());
logVO.setAssigneeOuId(i.getAssigneeOuId());
logVO.setStatus(BpmnProcessInstanceResultEnum.valueOfStatus(i.getStatus()));
logVO.setOrgStructureSnapshotInfo(i.getExtra());
return logVO;
}).collect(Collectors.toList()));
}
}

View File

@ -2,10 +2,29 @@ package cn.axzo.workflow.server.controller.web.bpmn;
import cn.axzo.workflow.client.feign.bpmn.ProcessTaskApi;
import cn.axzo.workflow.common.enums.AttachmentTypeEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.*;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditWithFormDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.task.*;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceGroupVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskDonePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import cn.axzo.workflow.core.service.BpmnProcessTaskService;
import cn.axzo.workflow.server.common.annotation.ErrorReporter;
import cn.axzo.workflow.server.common.annotation.RepeatSubmit;
@ -19,7 +38,12 @@ import org.flowable.form.api.FormInfo;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Nullable;
import javax.annotation.Resource;

View File

@ -13,6 +13,7 @@ import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.enums.SignApproverOrgLimitEnum;
import cn.axzo.workflow.common.model.dto.JobInfo;
import cn.axzo.workflow.common.model.dto.NodeInfo;
import cn.axzo.workflow.common.model.dto.OrgSnapshotInfo;
import cn.axzo.workflow.common.model.dto.OrgStructureSnapshotInfo;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
@ -164,9 +165,11 @@ public class TaskEntityEventHandle implements EntityEventHandle<TaskEntity> {
private OrgStructureSnapshotInfo buildApproverOrgStructureInfo(BpmnTaskDelegateAssigner assignee, TaskEntity taskEntity) {
// 先将审批人的提级审批重置为 false
assignee.setSupportUpgradeApproval(false);
log.info("build approve org info -> approve : {}, activity: {}", JSON.toJSONString(assignee), taskEntity.getTaskDefinitionKey());
List<OrgNodeUserDTO> nodeUsers = fetchUserExtInfo(assignee);
OrgStructureSnapshotInfo.OrgStructureSnapshotInfoBuilder builder = OrgStructureSnapshotInfo.builder();
if (CollectionUtils.isEmpty(nodeUsers)) {
log.info("fetch user ext info is empty");
return builder.build();
}
OrgNodeUserDTO firstNodeUser = nodeUsers.get(0);
@ -185,13 +188,22 @@ public class TaskEntityEventHandle implements EntityEventHandle<TaskEntity> {
.ouName(firstNodeUser.getCooperateShip().getOrganizationalUnitName())
.ouId(String.valueOf(firstNodeUser.getCooperateShip().getOrganizationalUnitId()))
.topNodeId(String.valueOf(firstNodeUser.getTopNodeId()))
.nodeInfos(ListUtils.emptyIfNull(nodeUsers).stream()
.filter(i -> Objects.nonNull(i.getNode()))
.map(i -> NodeInfo.builder()
.nodeId(i.getNode().getId())
.nodeName(i.getNode().getNodeName())
.build())
.collect(Collectors.toList())
)
.jobInfos(ListUtils.emptyIfNull(nodeUsers).stream()
.filter(i -> Objects.nonNull(i.getJob()))
.map(i -> JobInfo.builder()
.jobCode(i.getJob().getCode())
.jobName(i.getJob().getName())
.build()).collect(Collectors.toList()))
.build() : null)
.build())
.collect(Collectors.toList()))
.build() : OrgSnapshotInfo.builder().jobInfos(Collections.emptyList()).build())
.build();
FlowElement flowElement = ProcessDefinitionUtil.getBpmnModel(taskEntity.getProcessDefinitionId()).getFlowElement(taskEntity.getTaskDefinitionKey());
@ -324,6 +336,7 @@ public class TaskEntityEventHandle implements EntityEventHandle<TaskEntity> {
.needs(ListNodeUserReq.Needs.builder()
.job(true)
.unit(true)
.node(true)
.cooperateShip(true)
.personProfile(true)
.workspace(true)
@ -331,7 +344,7 @@ public class TaskEntityEventHandle implements EntityEventHandle<TaskEntity> {
.pageSize(Integer.MAX_VALUE)
.build();
if(NumberUtils.isDigits(nodeId)) {
if (NumberUtils.isDigits(nodeId)) {
build.setAncestorNodeIds(Lists.newArrayList(Long.parseLong(nodeId)));
}
return orgNodeUserList(build).getData();

View File

@ -1,62 +1,79 @@
package cn.axzo.workflow.starter.api;
import cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration;
import cn.axzo.workflow.common.util.ThreadUtil;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.model.dto.SignFileDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCarbonCopyDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceLogQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO;
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import javax.validation.constraints.NotBlank;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditWithFormDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskResetApproversDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO;
import cn.axzo.workflow.common.model.request.form.instance.FromDataSearchDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO;
import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo;
import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO;
import cn.axzo.workflow.common.model.response.form.instance.FormDataVO;
import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO;
import cn.axzo.workflow.common.util.ThreadUtil;
import cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceGroupVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskDonePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import javax.annotation.Nullable;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
import cn.axzo.workflow.common.model.dto.SignFileDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO;
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.BpmnProcessInstanceCarbonCopyDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCheckApproverDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceLogQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceMyPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO;
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.NodesByModelVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import javax.validation.constraints.NotNull;
/**
* Workflow Engine Starter Core Service
@ -102,37 +119,160 @@ public interface WorkflowCoreService {
Boolean setAssignee(@Validated @RequestBody BpmnActivitySetAssigneeDTO dto);
/**
* 获取流程配置的表单模型和权限主要用于发起页
* 同意
*
* @param dto
* @return
* <pre>
* MQ 触发规则:
* 1. 当前审批任务会依次触发 process-task-completed process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,
* 如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)
* 2.1. 下一级审批任务会依次触发 process-task-assigned process-task-created 事件
* 2.2. 流程实例正常结束会触发 process-instance-completed 事件
* </pre>
*/
@PostMapping("/api/form/admin/start/form")
@InvokeMode(SYNC)
FormDefinitionVO getFormDefinition(@Validated @RequestBody StartFormSearchDTO dto);
@Operation(summary = "同意MQ 触发规则:1. 当前审批任务会依次触发 process-task-completed 和 process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)2.1. 下一级审批任务会依次触发 process-task-assigned 和 process-task-created 事件2.2. 流程实例正常结束会触发 process-instance-completed 事件")
@PostMapping("/api/process/task/approve")
Boolean approveTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
/**
* 查询指定审批实例的表单模型和数据
* <p>
* dto 中的 processInstanceId taskId至少有一个属性有值一般建议直接使用实例 ID
* 当传入 taskId 将只查询该任务绑定的表单模型和数据
* 同意时并提交表单数据
*
* @param dto
* @return
*/
@PostMapping("/api/form/admin/instance/render")
@InvokeMode(SYNC)
FormInstanceVO getFormInstance(@Validated @RequestBody FormDetailDTO dto);
@Operation(summary = "同意时并提交表单")
@PostMapping("/api/process/task/form/approve")
Boolean approveTaskWithForm(@Validated @RequestBody BpmnTaskAuditWithFormDTO dto);
/**
* 获取流程中表单直接数据不包含模型
* 批量同意
*
* @param dtos
* @return
*/
@Operation(summary = "批量同意")
@PostMapping("/api/process/task/batch/approve")
BatchOperationResultVO batchApproveTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
/**
* 获取当前节点可回退节点选项列表
*
* @param taskId 当前任务id
* @return 可以回退节点列表
*/
@Operation(summary = "获取当前节点可回退节点选项列表")
@GetMapping("/api/process/task/back/optional/nodes")
List<BpmnOptionalNodeDTO> getBackOptionalNodes(@RequestParam @NotBlank(message = "任务id不能为空") String taskId);
/**
* 回退到指定节点
*
* @param dto
* @return
*/
@PostMapping("/instance/form/data")
@InvokeMode(SYNC)
List<FormDataVO> getFormData(@Validated @RequestBody FromDataSearchDTO dto);
@Operation(summary = "回退")
@PostMapping("/api/process/task/back")
Boolean backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto);
/**
* 用于系统内部操作跳转到指定节点
* @param dto 请求参数
* @return 是否成功
*/
@Operation(summary = "系统操作回退任务到指定节点")
@PostMapping("/api/process/task/system/back")
Boolean systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto);
/**
* 驳回
*
* <pre>
* MQ 触发规则:
* 1. 当前审批任务会触发 process-task-deleted 事件
* 2. 当前流程实例会触发 process-instance-rejected 事件
* </pre>
*/
@Operation(summary = "驳回MQ 触发规则1. 当前审批任务会触发 process-task-deleted 事件, 2. 当前流程实例会触发 process-instance-rejected 事件")
@PostMapping("/api/process/task/reject")
Boolean rejectTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
/**
* 批量驳回
*
* @param dtos 批量请求参数
* @return
*/
@PostMapping("/api/process/task/batch/reject")
BatchOperationResultVO batchRejectTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
/**
* 转交
*
* @param dto
* @return
*/
@Operation(summary = "直接修改审批任务的审批人")
@PostMapping("/api/process/task/transfer")
Boolean transferTask(@Validated @RequestBody BpmnTaskTransferDTO dto);
/**
* 批量转交
*
* @param dtos
* @return
*/
@Operation(summary = "批量修改审批任务的审批人")
@PostMapping("/api/process/task/batch/transfer")
BatchOperationResultVO batchTransferTask(@Validated @RequestBody List<BpmnTaskTransferDTO> dtos);
/**
* 评论
*
* @param dto 评论请求参数
* @return
*/
@Operation(summary = "审批流程评论")
@PostMapping("/api/process/task/comment")
Boolean commentTask(@Validated @RequestBody BpmnTaskCommentDTO dto);
/**
* 加签
*
* @param dto 加签请求参数
* @return
*/
@Operation(summary = "审批流程加签")
@PostMapping("/api/process/task/countersign")
Boolean countersignTask(@Validated @RequestBody BpmnTaskCountersignDTO dto);
/**
* 重置节点审批人提级审批
*
* @param dto
* @return
*/
@Operation(summary = "重置节点审批人(提级审批)")
@PostMapping("/api/process/task/approvers/reset")
Boolean resetTaskApprovers(@Validated @RequestBody BpmnTaskResetApproversDTO dto);
/**
* 暂停流程任务,并创建机器人节点,等待业务推动
*
* @param dto
* @return 返回机器人节点任务 ID
*/
@Operation(summary = "创建机器人节点, 暂停流程任务")
@PostMapping("/api/process/task/robot/create")
String createRobotTask(@Validated @RequestBody BpmnRobotTaskCreateDTO dto);
/**
* 完成机器人节点
*
* @param dto
* @return
*/
@Operation(summary = "完成机器人节点, 继续流程任务")
@PostMapping("/api/process/task/robot/complete")
Boolean completeRobotTask(@Validated @RequestBody BpmnRobotTaskCompleteDTO dto);
/**
* 创建流程前的节点列表
@ -302,163 +442,6 @@ public interface WorkflowCoreService {
@InvokeMode(SYNC)
List<SignFileDTO> getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId);
/**
* 同意
*
* <pre>
* MQ 触发规则:
* 1. 当前审批任务会依次触发 process-task-completed process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,
* 如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)
* 2.1. 下一级审批任务会依次触发 process-task-assigned process-task-created 事件
* 2.2. 流程实例正常结束会触发 process-instance-completed 事件
* </pre>
*/
@Operation(summary = "同意MQ 触发规则:1. 当前审批任务会依次触发 process-task-completed 和 process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)2.1. 下一级审批任务会依次触发 process-task-assigned 和 process-task-created 事件2.2. 流程实例正常结束会触发 process-instance-completed 事件")
@PostMapping("/api/process/task/approve")
Boolean approveTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
/**
* 同意时并提交表单数据
*
* @param dto
* @return
*/
@Operation(summary = "同意时并提交表单")
@PostMapping("/api/process/task/form/approve")
Boolean approveTaskWithForm(@Validated @RequestBody BpmnTaskAuditWithFormDTO dto);
/**
* 批量同意
*
* @param dtos
* @return
*/
@Operation(summary = "批量同意")
@PostMapping("/api/process/task/batch/approve")
BatchOperationResultVO batchApproveTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
/**
* 获取当前节点可回退节点选项列表
*
* @param taskId 当前任务id
* @return 可以回退节点列表
*/
@Operation(summary = "获取当前节点可回退节点选项列表")
@GetMapping("/api/process/task/back/optional/nodes")
List<BpmnOptionalNodeDTO> getBackOptionalNodes(@RequestParam @NotBlank(message = "任务id不能为空") String taskId);
/**
* 回退到指定节点
*
* @param dto
* @return
*/
@Operation(summary = "回退")
@PostMapping("/api/process/task/back")
Boolean backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto);
/**
* 用于系统内部操作跳转到指定节点
*
* @param dto 请求参数
* @return 是否成功
*/
@Operation(summary = "系统操作回退任务到指定节点")
@PostMapping("/api/process/task/system/back")
Boolean systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto);
/**
* 驳回
*
* <pre>
* MQ 触发规则:
* 1. 当前审批任务会触发 process-task-deleted 事件
* 2. 当前流程实例会触发 process-instance-rejected 事件
* </pre>
*/
@Operation(summary = "驳回MQ 触发规则1. 当前审批任务会触发 process-task-deleted 事件, 2. 当前流程实例会触发 process-instance-rejected 事件")
@PostMapping("/api/process/task/reject")
Boolean rejectTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
/**
* 批量驳回
*
* @param dtos 批量请求参数
* @return
*/
@PostMapping("/api/process/task/batch/reject")
BatchOperationResultVO batchRejectTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
/**
* 转交
*
* @param dto
* @return
*/
@Operation(summary = "直接修改审批任务的审批人")
@PostMapping("/api/process/task/transfer")
Boolean transferTask(@Validated @RequestBody BpmnTaskTransferDTO dto);
/**
* 批量转交
*
* @param dtos
* @return
*/
@Operation(summary = "批量修改审批任务的审批人")
@PostMapping("/api/process/task/batch/transfer")
BatchOperationResultVO batchTransferTask(@Validated @RequestBody List<BpmnTaskTransferDTO> dtos);
/**
* 评论
*
* @param dto 评论请求参数
* @return
*/
@Operation(summary = "审批流程评论")
@PostMapping("/api/process/task/comment")
Boolean commentTask(@Validated @RequestBody BpmnTaskCommentDTO dto);
/**
* 加签
*
* @param dto 加签请求参数
* @return
*/
@Operation(summary = "审批流程加签")
@PostMapping("/api/process/task/countersign")
Boolean countersignTask(@Validated @RequestBody BpmnTaskCountersignDTO dto);
/**
* 重置节点审批人提级审批
*
* @param dto
* @return
*/
@Operation(summary = "重置节点审批人(提级审批)")
@PostMapping("/api/process/task/approvers/reset")
Boolean resetTaskApprovers(@Validated @RequestBody BpmnTaskResetApproversDTO dto);
/**
* 暂停流程任务,并创建机器人节点,等待业务推动
*
* @param dto
* @return 返回机器人节点任务 ID
*/
@Operation(summary = "创建机器人节点, 暂停流程任务")
@PostMapping("/api/process/task/robot/create")
String createRobotTask(@Validated @RequestBody BpmnRobotTaskCreateDTO dto);
/**
* 完成机器人节点
*
* @param dto
* @return
*/
@Operation(summary = "完成机器人节点, 继续流程任务")
@PostMapping("/api/process/task/robot/complete")
Boolean completeRobotTask(@Validated @RequestBody BpmnRobotTaskCompleteDTO dto);
/**
* 强制使用异步模式调用该方法请在调用真实方法前调用该方法
* <pre>