- 1.4.1-SNAPSHOT
+ 1.4.2-SNAPSHOT
2.0.0-SNAPSHOT
2.0.0-SNAPSHOT
11.8
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/annotation/WorkflowEngineFeignClient.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/annotation/WorkflowEngineFeignClient.java
new file mode 100644
index 000000000..f6dc5a700
--- /dev/null
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/annotation/WorkflowEngineFeignClient.java
@@ -0,0 +1,22 @@
+package cn.axzo.workflow.client.annotation;
+
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 用于开启原生 FeignClient
+ *
+ * @author wangli
+ * @since 2024-09-10 11:30
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface WorkflowEngineFeignClient {
+}
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java
index 0e460def2..7cdfa0b6c 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java
@@ -1,9 +1,11 @@
package cn.axzo.workflow.client.feign.bpmn;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.common.annotation.Manageable;
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 cn.azxo.framework.common.model.CommonResponse;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated;
@@ -21,16 +23,27 @@ import javax.validation.constraints.NotBlank;
* @since 2023/11/17 16:28
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
public interface ProcessActivityApi {
/**
- * 业务节点唤醒
+ * 业务节点唤醒, 该节点废弃,请换成 {@link ProcessActivityApi#trigger(cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO)} 接口
*
* 当模型中使用了“业务节点”,且设置了“不设置审批人”模式,则当业务监听到 PROCESS_ACTIVITY_START 事件时,可通过该接口推动流程继续运行
*/
+ @Deprecated
@GetMapping("/api/process/activity/trigger")
CommonResponse trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId);
+ /**
+ * 业务节点唤醒
+ *
+ * @param dto
+ * @return
+ */
+ @PostMapping("/api/process/activity/trigger")
+ CommonResponse trigger(@Validated @RequestBody BpmnActivityTriggerDTO dto);
+
/**
* 业务节点设置审批人, 不支持重复设置
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java
index 7a04b923a..ccdb2696a 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java
@@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
@@ -28,6 +29,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
* @since 2023/9/21 16:25
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
@Manageable
public interface ProcessDefinitionApi {
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java
index e2368600f..4b43d1b74 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java
@@ -1,6 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn;
-import cn.axzo.workflow.client.config.CommonFeignConfiguration;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
@@ -10,18 +10,19 @@ import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCar
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.BpmnProcessInstanceCreateWithFormDTO;
+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.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
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.ProcessNodeDetailVO;
import cn.azxo.framework.common.model.CommonResponse;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Operation;
-import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -45,6 +46,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
* @since 2023/9/21 16:26
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
public interface ProcessInstanceApi {
/**
* 创建审批流程
@@ -241,4 +243,15 @@ public interface ProcessInstanceApi {
@Manageable
@InvokeMode(SYNC)
CommonResponse checkInstanceApprover(@Validated @RequestBody BpmnProcessInstanceCheckApproverDTO dto);
+
+ /**
+ * 获取指定流程的日志
+ *
+ * @param dto
+ * @return
+ */
+ @Operation(summary = "获取指定流程的日志")
+ @PostMapping("/api/process/instance/logs")
+ @InvokeMode(SYNC)
+ CommonResponse getProcessInstanceLogs(@Validated @RequestBody BpmnProcessInstanceLogQueryDTO dto);
}
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessJobApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessJobApi.java
index 906bbc1f3..5e6da7692 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessJobApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessJobApi.java
@@ -1,16 +1,13 @@
package cn.axzo.workflow.client.feign.bpmn;
-import cn.axzo.workflow.client.config.CommonFeignConfiguration;
-import cn.axzo.workflow.common.annotation.InvokeMode;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.azxo.framework.common.model.CommonResponse;
-import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
-import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
-
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
public interface ProcessJobApi {
/**
@@ -27,4 +24,22 @@ public interface ProcessJobApi {
@Manageable
CommonResponse executeDeadLetterJobAction(@RequestParam(required = false) String jobId,
@RequestParam(required = false) String procInstId);
+
+ /**
+ * 查询死信消息数据
+ * @param procInstId 流程实例id
+ * @return
+ */
+ @GetMapping("/dead-letter/exception/stacktrace")
+ @Manageable
+ String getDeadLetterJobExceptionStacktrace(@RequestParam String procInstId);
+
+ /**
+ * 查询死信消息数据
+ * @param jobId 死信job的id
+ * @return
+ */
+ @GetMapping("/dead-letter/exception/stacktrace/byId")
+ @Manageable
+ String getDeadLetterJobExceptionStacktraceByJobId(@RequestParam String jobId);
}
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java
index 8fc7f7f00..d06e7c0ea 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java
@@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
@@ -33,6 +34,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
* @since 2023/9/21 15:47
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
@Manageable
public interface ProcessModelApi {
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java
index 80d57d980..e311d8c1a 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java
@@ -1,12 +1,14 @@
package cn.axzo.workflow.client.feign.bpmn;
-import cn.axzo.workflow.client.config.CommonFeignConfiguration;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
+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.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.BpmnTaskPageSearchDTO;
@@ -21,7 +23,6 @@ import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import cn.azxo.framework.common.model.CommonResponse;
import io.swagger.v3.oas.annotations.Operation;
-import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -44,6 +45,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
* @since 2023/9/21 16:26
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
public interface ProcessTaskApi {
/**
@@ -71,6 +73,24 @@ public interface ProcessTaskApi {
@PostMapping("/api/process/task/batch/approve")
CommonResponse batchApproveTask(@Validated @RequestBody List dtos);
+ /**
+ * 获取当前节点可回退节点选项列表
+ * @param taskId 当前任务id
+ * @return 可以回退节点列表
+ */
+ @Operation(summary = "获取当前节点可回退节点选项列表")
+ @GetMapping("/api/process/task/back/optional/nodes")
+ CommonResponse> getBackOptionalNodes(@RequestParam @NotBlank(message = "任务id不能为空") String taskId);
+
+ /**
+ * 回退到指定节点
+ * @param dto
+ * @return
+ */
+ @Operation(summary = "回退")
+ @PostMapping("/api/process/task/back")
+ CommonResponse backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto);
+
/**
* 驳回
*
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java
index 2c219f88b..614216a7a 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java
@@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
@@ -21,6 +22,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
* 流程变量api
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
@Manageable
public interface ProcessVariableApi {
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java
index aa1d51447..7dea9ce08 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java
@@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.manage;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
@@ -36,6 +37,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
@Manageable
public interface ProcessCategoryApi {
diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java
index 5f56d5d64..13bf5e8e0 100644
--- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java
+++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java
@@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.manage;
+import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
@@ -22,6 +23,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
*/
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
+@WorkflowEngineFeignClient
@Manageable
public interface ProcessConfigApi {
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java
index 6c132c786..49661639a 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java
@@ -40,6 +40,7 @@ public interface BpmnConstants {
@Deprecated
String OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT = "[_ASSIGNEE_INFO_SNAPSHOT_]";
String INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT = "[_ACTIVITY_INFO_SNAPSHOT_]";
+ String BIZ_NODE_ALTER = "[_BIZ_NODE_ALTER_]";
String PROCESS_PREFIX = "Flowable";
@Deprecated
String OLD_TASK_ASSIGNEE_SKIP_FLAT = "taskSkip";
@@ -108,6 +109,7 @@ public interface BpmnConstants {
String BPM_MODEL_CATEGORY = "bpm_model_category";
String BPM_ALLOW_SKIP_USER_TASK = "_INTERNAL_SKIP_USER_TASK_";
String AUTO_APPROVAL_TYPE = "autoApprovalType";
+ String PROCESS_CLOSING_TYPE = "[_PROCESS_CLOSING_TYPE]";
/**
* 用于国内审批节点填写审批建议
*
@@ -120,6 +122,8 @@ public interface BpmnConstants {
String NUMBER_OF_INSTANCES = "nrOfInstances";
String MULTI_INSTANCE_LOOP_COUNTER = "loopCounter";
String TASK_COMPLETE_OPERATION_TYPE = "_TASK_COMPLETE_TYPE";
+ String TASK_ATTACHMENTS_VAR_NAME = "TASK_ATTACHMENTS";
+
/**
* 会签表达式
*/
@@ -178,4 +182,9 @@ public interface BpmnConstants {
* 加签显示人员数量
*/
Integer COUNTERSIGN_ASSIGNER_SHOW_NUMBER = 2;
+
+ /**
+ * 回退操作次数上限
+ */
+ Integer MAX_BACKED_OPERATE_COUNT = 20;
}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java
index 81f213001..2d6451b8e 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java
@@ -1,5 +1,8 @@
package cn.axzo.workflow.common.enums;
+import java.util.Arrays;
+import java.util.Objects;
+
/**
* 审批方式枚举
*
@@ -48,4 +51,11 @@ public enum ApprovalMethodEnum {
public void setRemark(String remark) {
this.remark = remark;
}
+
+ public static ApprovalMethodEnum valueOfType(String type) {
+ return Arrays.stream(ApprovalMethodEnum.values())
+ .filter(i -> Objects.equals(i.getType(), type))
+ .findAny()
+ .orElse(null);
+ }
}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeMode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeMode.java
index e812d2986..00bad8570 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeMode.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeMode.java
@@ -1,6 +1,9 @@
package cn.axzo.workflow.common.enums;
+import java.util.Arrays;
+import java.util.Objects;
+
public enum BpmnFlowNodeMode {
GENERAL("GENERAL", "普通节点"),
OR("OR", "或签节点"),
@@ -35,4 +38,11 @@ public enum BpmnFlowNodeMode {
public void setDesc(String desc) {
this.desc = desc;
}
+
+ public static BpmnFlowNodeMode valueOfType(String type) {
+ return Arrays.stream(BpmnFlowNodeMode.values())
+ .filter(i -> Objects.equals(i.getType(), type))
+ .findAny()
+ .orElse(null);
+ }
}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java
index 1dc7eb85d..1856447f6 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java
@@ -1,6 +1,8 @@
package cn.axzo.workflow.common.enums;
+import org.springframework.util.StringUtils;
+
import java.util.Arrays;
import java.util.Objects;
@@ -51,6 +53,19 @@ public enum BpmnFlowNodeType {
this.desc = desc;
}
+ public static BpmnFlowNodeType getByType(String type) {
+ if (!StringUtils.hasText(type)) {
+ return null;
+ }
+ BpmnFlowNodeType[] values = BpmnFlowNodeType.values();
+ for (BpmnFlowNodeType value : values) {
+ if (value.getType().equals(type)) {
+ return value;
+ }
+ }
+ return null;
+ }
+
public static BpmnFlowNodeType valueOfType(String type) {
return Arrays.stream(BpmnFlowNodeType.values())
.filter(i -> Objects.equals(i.getType(), type))
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java
index 81ba92e01..150ecbc35 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java
@@ -6,6 +6,7 @@ public enum BpmnProcessInstanceResultEnum {
PROCESSING("PROCESSING", "审批中"),
APPROVED("APPROVED", "已通过"),
REJECTED("REJECTED", "已驳回"),
+ BACKED("BACKED", "已回退"),
CANCELLED("CANCELLED", "已撤回"),
ABORTED("ABORTED", "已中止"),
TRANSFER("TRANSFER", "已转交"),
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java
index 124af9a6e..cfe9b8531 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java
@@ -10,8 +10,8 @@ import cn.axzo.framework.rocketmq.Event;
*/
public enum ProcessTaskEventEnum {
- PROCESS_TASK_CREATED("process-task", "process-task-created", "流程任务已创建"),
PROCESS_TASK_ASSIGNED("process-task", "process-task-assigned", "流程任务已分配"),
+ PROCESS_TASK_CREATED("process-task", "process-task-created", "流程任务已创建"),
PROCESS_TASK_COMPLETED("process-task", "process-task-completed", "流程任务已结束"),
PROCESS_TASK_DELETED("process-task", "process-task-deleted", "流程任务已删除"),
;
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/AlterDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/AlterDTO.java
new file mode 100644
index 000000000..e0408a44e
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/AlterDTO.java
@@ -0,0 +1,25 @@
+package cn.axzo.workflow.common.model.dto;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 告警对象
+ *
+ * @author wangli
+ * @since 2024-09-13 11:37
+ */
+@Data
+public class AlterDTO {
+ private String processInstanceId;
+
+ private String activityId;
+
+ private String taskId;
+
+ private Date startTime;
+
+ private String prettyStartTime;
+
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/TermNodePausingDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/TermNodePausingDTO.java
new file mode 100644
index 000000000..6444bf20a
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/TermNodePausingDTO.java
@@ -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 2024-09-11 13:57
+ */
+@Data
+@Accessors(chain = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class TermNodePausingDTO implements Serializable {
+
+ private static final long serialVersionUID = -1L;
+ private String processInstanceId;
+
+ private String activityId;
+
+ /**
+ * 重试次数
+ */
+ private Integer retries;
+
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java
index abc5fe45e..ed9af9805 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java
@@ -3,18 +3,23 @@ package cn.axzo.workflow.common.model.request;
import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
import lombok.Data;
-import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import javax.validation.Valid;
@ApiModel("JSON 版本的 BPMN 协议模型中流程配置管理")
@Data
-@NoArgsConstructor
+@AllArgsConstructor
@Accessors(chain = true)
public class BpmnApproveConf {
+ public BpmnApproveConf() {
+ this.supportBatchOperation = false;
+ this.userAgreeSignature = false;
+ }
+
/**
* 是否支持批量审批
*/
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonConf.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonConf.java
index cf78a06ed..61f052ac0 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonConf.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonConf.java
@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -25,25 +26,25 @@ public class BpmnButtonConf implements Serializable {
* 发起人的按钮配置信息, 需要给全量按钮的配置
*/
@ApiModelProperty(value = "发起人的按钮配置信息")
- private List initiator;
+ private List initiator = new ArrayList<>();
/**
* 当前审批人的按钮配置信息, JSON 格式
*/
@ApiModelProperty(value = "当前审批人的按钮配置信息")
- private List current;
+ private List current = new ArrayList<>();
/**
* 历史审批人的按钮配置信息, JSON 格式
*/
@ApiModelProperty(value = "历史审批人的按钮配置信息")
- private List history;
+ private List history = new ArrayList<>();
/**
* 抄送人的按钮配置信息, JSON 格式
*/
@ApiModelProperty(value = "抄送人的按钮配置信息")
- private List carbonCopy;
+ private List carbonCopy = new ArrayList<>();
public List getInitiator() {
return initiator;
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java
index 5b44b327c..4bf8718b5 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java
@@ -36,6 +36,7 @@ public class BpmnProcessInstanceCancelDTO {
* 工作台 ID
*/
@ApiModelProperty(value = "工作台 ID")
+ @NotBlank(message = "工作台不能为空")
private String tenantId;
/**
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java
index b948cb521..1dd607cb9 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java
@@ -43,9 +43,9 @@ public class BpmnProcessInstanceCreateDTO {
* 发起流程实例归属租户 ID
*
* 为空时,默认是编辑公共流程模型, 如果是代运营创建,则必填
+ *
建议都传值,在安心筑中对应工作台 ID
*/
@ApiModelProperty(value = "发起的审批是属于哪个租户")
-// @NotBlank(message = "审批实例归属租户 ID 不能为空")
private String tenantId;
/**
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceLogQueryDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceLogQueryDTO.java
new file mode 100644
index 000000000..17e7ad583
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceLogQueryDTO.java
@@ -0,0 +1,54 @@
+package cn.axzo.workflow.common.model.request.bpmn.process;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 查询流程实例日志
+ *
+ * @author wangli
+ * @since 2024-09-07 17:32
+ */
+@ApiModel("查询流程实例日志")
+@Data
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class BpmnProcessInstanceLogQueryDTO {
+
+ /**
+ * 流程实例 ID
+ */
+ @ApiModelProperty(value = "流程实例 ID")
+ @NotBlank(message = "流程实例 ID 不能为空")
+ private String processInstanceId;
+
+ /**
+ * 谁来访问该实例日志,如果为空,则始终不就返回按钮信息
+ *
+ * 注意,为了确保历史审批数据的查询,需要将除 avatar 外的其他所有属性补全
+ */
+ @ApiModelProperty(value = "访问者信息", notes = "如果为空,则始终不就返回按钮信息")
+ private BpmnTaskDelegateAssigner visitor;
+
+ /**
+ * 返回结果中是否包含按钮
+ */
+ @ApiModelProperty(value = "返回结果中是否包含按钮", notes = "如果访问者为空,该属性为 true 时,同样也不会返回按钮")
+ private Boolean hasButton = false;
+
+ /**
+ * 是否需要加密(同一个实例的日志,在不同端[cms/oms]下,审批人的信息需要按一定规则进行隐藏控制)
+ */
+ @ApiModelProperty(value = "是否需要加密", notes = "同一个实例的日志,在不同端[cms/oms]下,审批人的信息需要按一定规则进行隐藏控制")
+ private Boolean encrypt;
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java
index 0ce3b76f0..6fd66819b 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java
@@ -3,10 +3,14 @@ package cn.axzo.workflow.common.model.request.bpmn.task;
import cn.axzo.workflow.common.enums.AttachmentTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.Data;
+import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
+import java.io.Serializable;
/**
* 附件模型
@@ -16,8 +20,12 @@ import javax.validation.constraints.NotNull;
*/
@ApiModel("附件模型")
@Data
-public class AttachmentDTO {
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class AttachmentDTO implements Serializable {
+ private static final long serialVersionUID = 6954179791395744269L;
/**
* 附件 ID
*/
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java
index 6f5a22aff..14ab875fe 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java
@@ -53,7 +53,6 @@ public class BpmnActivitySetAssigneeDTO {
* 业务如果传入的 assigners 集合为空, 引擎则会对该流程进行自动驳回处理,且驳回意见为“业务未指定审批人”
*/
@ApiModelProperty(value = "审批人集合信息", notes = "业务传参时,需要注意去重")
- @Valid
@Size(max = 60, message = "指定审批人数量限制为60")
private List assigners;
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivityTriggerDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivityTriggerDTO.java
new file mode 100644
index 000000000..8165317fe
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivityTriggerDTO.java
@@ -0,0 +1,37 @@
+package cn.axzo.workflow.common.model.request.bpmn.task;
+
+import io.swagger.annotations.ApiModel;
+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 2024-09-09 13:46
+ */
+@ApiModel("业务节点设置审批人")
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class BpmnActivityTriggerDTO {
+
+ /**
+ * 业务节点的触发 ID
+ */
+ @NotBlank(message = "触发 ID 不能为空")
+ @ApiModelProperty(value = "触发 ID", notes = "数据来源于事件")
+ private String triggerId;
+
+ /**
+ * 是否异步执行
+ */
+ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据")
+ private Boolean async = true;
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnOptionalNodeDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnOptionalNodeDTO.java
new file mode 100644
index 000000000..935f5d201
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnOptionalNodeDTO.java
@@ -0,0 +1,52 @@
+package cn.axzo.workflow.common.model.request.bpmn.task;
+
+import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 回退到指定节点,可选节点模型
+ */
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Builder
+public class BpmnOptionalNodeDTO {
+
+ /**
+ * 对应流程实例id
+ */
+ private String processInstanceId;
+
+ /**
+ * 对应流程定义id
+ */
+ private String processDefinitionId;
+
+ /**
+ * 节点id
+ */
+ private String processActivityId;
+
+ /**
+ * 节点名称
+ */
+ private String processActivityName;
+
+ /**
+ * 节点描述,用于页面展示
+ */
+ private String processNodeDesc;
+
+ /**
+ * 节点类型
+ */
+ private BpmnFlowNodeType nodeType;
+
+ /**
+ * 序号,越小越靠近发起节点
+ */
+ private Integer ordinal;
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskBackAuditDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskBackAuditDTO.java
new file mode 100644
index 000000000..b4a2aed67
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskBackAuditDTO.java
@@ -0,0 +1,27 @@
+package cn.axzo.workflow.common.model.request.bpmn.task;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("回退到指定节点入参模型")
+@Data
+@Validated
+@AllArgsConstructor
+@NoArgsConstructor
+public class BpmnTaskBackAuditDTO extends BpmnTaskAuditDTO implements Serializable {
+
+ private static final long serialVersionUID = -4160538355403179298L;
+
+ @ApiModelProperty(value = "目标流程节点id", required = true)
+ @NotBlank(message = "目标流程节点id不能为空")
+ private String toActivityId;
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnProcessInstanceLogVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnProcessInstanceLogVO.java
new file mode 100644
index 000000000..8ddcb835b
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnProcessInstanceLogVO.java
@@ -0,0 +1,159 @@
+package cn.axzo.workflow.common.model.response.bpmn.process;
+
+import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
+import cn.axzo.workflow.common.enums.WorkspaceType;
+import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
+import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo;
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceLogVO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 流程实例日志模型
+ *
+ * @author wangli
+ * @since 2024-09-07 17:07
+ */
+@ApiModel("流程实例日志模型")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class BpmnProcessInstanceLogVO {
+ /**
+ * 流程实例的编号
+ */
+ @ApiModelProperty(value = "流程实例的编号", example = "1024")
+ private String id;
+
+ /**
+ * 流程名称
+ */
+ @ApiModelProperty(value = "流程名称", example = "权限点申请")
+ private String name;
+
+ /**
+ * 流程分类
+ */
+ @ApiModelProperty(value = "流程分类", notes = "关联的业务分类", example = "1")
+ private String category;
+
+ /**
+ * 审核状态
+ */
+ @ApiModelProperty(value = "审核状态(PROCESSING:审核中,APPROVED:已通过,REJECTED:已拒绝,CANCELLED:已取消)", example = "APPROVED")
+ private BpmnProcessInstanceResultEnum result;
+
+ /**
+ * 发起时间
+ */
+ @ApiModelProperty("发起时间")
+ private Date startTime;
+
+ /**
+ * 结束时间
+ */
+ @ApiModelProperty("结束时间")
+ private Date endTime;
+
+ /**
+ * 流程定义 KEY
+ */
+ @ApiModelProperty("流程定义 KEY")
+ private String processDefinitionKey;
+
+ /**
+ * 流程定义 ID
+ */
+ @ApiModelProperty("流程定义 ID")
+ private String processDefinitionId;
+
+ /**
+ * 业务的唯一标识
+ */
+ @ApiModelProperty(value = "业务的唯一标识", example = "1", notes = "例如说,请假申请的编号")
+ private String businessKey;
+
+ /**
+ * 流程最终状态
+ */
+ @ApiModelProperty("流程最终状态")
+ private String businessStatus;
+
+ /**
+ * 发起人
+ */
+ @ApiModelProperty("发起人")
+ private BpmnTaskDelegateAssigner initiator;
+
+ /**
+ * 当前流程发起租户
+ */
+ @ApiModelProperty("当前流程发起租户")
+ private String tenantId;
+
+ /**
+ * 是代运营的流程
+ */
+ @ApiModelProperty("是代运营的流程")
+ private Boolean agented;
+
+ /**
+ * 任务信息集合
+ */
+ @ApiModelProperty("任务信息集合")
+ private List taskDetails;
+
+ /**
+ * 当前实例对应模型的全局兜底按钮配置
+ */
+ @ApiModelProperty(value = "当前实例对应模型的全局兜底按钮配置")
+ private BpmnButtonConf defaultButtonConf;
+
+ /**
+ * 指定人访问实例日志时,计算其流程应该有权限操作的按钮
+ */
+ @ApiModelProperty(value = "指定人访问实例日志时,计算其流程应该有权限操作的按钮", notes = "流程有权限,不代表待办消息中一定能看到按钮")
+ private List currentUserButtons;
+
+ /**
+ * 需要隐藏的自定义按钮集合
+ */
+ @ApiModelProperty(value = "需要隐藏的自定义按钮集合")
+ private List customHiddenButtons;
+
+ /**
+ * 是否支持批量审批
+ */
+ @ApiModelProperty(value = "是否支持批量审批")
+ private Boolean supportBatchOperation;
+
+ /**
+ * 审批同意录入手写签名
+ */
+ @ApiModelProperty(value = "审批同意录入手写签名")
+ private Boolean userAgreeSignature;
+
+ /**
+ * 数据产生版本
+ */
+ @ApiModelProperty(value = "数据产生版本")
+ private String workflowEngineVersion;
+
+ /**
+ * 当前流程对应工作台类型
+ */
+ @ApiModelProperty(value = "工作台类型")
+ private WorkspaceType workspaceType;
+
+ @ApiModelProperty(value = "程序计算按钮使用,非对外使用", hidden = true)
+ private transient BpmnButtonConf calculatingButtonConf;
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskInstanceLogVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskInstanceLogVO.java
new file mode 100644
index 000000000..2c191b8d4
--- /dev/null
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskInstanceLogVO.java
@@ -0,0 +1,127 @@
+package cn.axzo.workflow.common.model.response.bpmn.task;
+
+import cn.axzo.workflow.common.enums.ApprovalMethodEnum;
+import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
+import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
+import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
+import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 流程任务日志模型
+ *
+ * @author wangli
+ * @since 2024-09-07 17:08
+ */
+@ApiModel("流程任务日志模型")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class BpmnTaskInstanceLogVO {
+
+ /**
+ * 审批任务 ID
+ */
+ @ApiModelProperty(value = "审批任务 ID")
+ private String taskId;
+ /**
+ * 审批任务节点定义KEY
+ */
+ @ApiModelProperty(value = "审批任务节点定义KEY")
+ private String taskDefinitionKey;
+ /**
+ * 审批任务节点名称
+ */
+ @ApiModelProperty(value = "审批任务节点名称")
+ private String name;
+ /**
+ * 任务创建时间
+ */
+ @ApiModelProperty(value = "任务创建时间")
+ private Date createTime;
+ /**
+ * 任务结束时间
+ */
+ @ApiModelProperty(value = "任务结束时间")
+ private Date endTime;
+ /**
+ * 审批方式
+ */
+ @ApiModelProperty(value = "审批方式")
+ private ApprovalMethodEnum approvalMethod;
+ /**
+ * 节点类型
+ */
+ @ApiModelProperty(value = "节点类型")
+ private BpmnFlowNodeType nodeType;
+ /**
+ * 审批任务节点的类型
+ */
+ @ApiModelProperty(value = "审批任务节点的类型")
+ private BpmnFlowNodeMode nodeMode;
+ /**
+ * 任务状态
+ */
+ @ApiModelProperty(value = "任务状态")
+ private BpmnProcessInstanceResultEnum result;
+
+ /**
+ * 操作描述
+ */
+ @ApiModelProperty(value = "操作描述")
+ private String operationDesc;
+ /**
+ * 审批建议
+ */
+ @ApiModelProperty(value = "审批建议")
+ private String advice;
+ /**
+ * 一些扩展信息
+ */
+ @ApiModelProperty(value = "一些扩展信息")
+ private String commentExt;
+ /**
+ * 图片列表
+ */
+ @ApiModelProperty(value = "图片列表")
+ private List imageList;
+ /**
+ * 附件列表
+ */
+ @ApiModelProperty(value = "附件列表")
+ private List fileList;
+ /**
+ * 手写签名地址
+ */
+ @ApiModelProperty(value = "手写签名地址")
+ private String signatureUrl;
+ /**
+ * 审批人快照信息
+ */
+ @ApiModelProperty(value = "审批人快照信息")
+ private BpmnTaskDelegateAssigner assigneeSnapshot;
+ /**
+ * 未完成节点多实例模式的审批人信息
+ */
+ @ApiModelProperty(value = "未完成节点多实例模式的审批人信息")
+ private List forecastAssignees;
+
+ @ApiModelProperty(value = "程序计算按钮使用,非对外使用", hidden = true)
+ private transient BpmnButtonConf buttonConf;
+
+ public boolean isVirtual() {
+ return StringUtils.isBlank(this.taskId);
+ }
+}
diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java
index 0dfef6a2f..2822419c5 100644
--- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java
+++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java
@@ -3,6 +3,7 @@ package cn.axzo.workflow.common.model.response.mq;
import cn.axzo.workflow.common.enums.BpmnNoticeEnum;
import cn.axzo.workflow.common.enums.ProcessTaskEventEnum;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf;
+import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -44,6 +45,10 @@ public class ProcessTaskDTO implements Serializable {
* 流程实例 ID
*/
private String processInstanceId;
+ /**
+ * 流程实例的businessKey
+ */
+ private String businessKey;
/**
* 流程实例所属业务分类,同时也等于流程模型对应的业务分类 ID
*/
@@ -103,4 +108,13 @@ public class ProcessTaskDTO implements Serializable {
* 当前数据的流程引擎版本
*/
private String workflowEngineVersion;
+ /**
+ * 任务关联的附件
+ */
+ private List attachments;
+
+ /**
+ * 审批意见
+ */
+ private String advice;
}
diff --git a/workflow-engine-core/pom.xml b/workflow-engine-core/pom.xml
index 00e1896d7..5b41b626b 100644
--- a/workflow-engine-core/pom.xml
+++ b/workflow-engine-core/pom.xml
@@ -89,17 +89,25 @@
cn.axzo.workflow
workflow-engine-api
+
cn.axzo.workflow
workflow-engine-common
+
org.apache.maven
maven-artifact
+
jakarta.servlet
jakarta.servlet-api
+
+
+ com.aliyun
+ alibaba-dingtalk-service-sdk
+
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java
index 281569769..e1e8d2bbe 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java
@@ -27,6 +27,7 @@ public enum BpmnInstanceRespCode implements IModuleRespCode {
TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS("012", "流程实例【{}】不存在, 不能评论"),
RUNNING_INSTANCE_ONLY_FORECAST("013", "仅运行中的实例可以推测"),
ENGINE_EXEC_EXCEPTION("014", "引擎内部异常"),
+ PROCESS_TASK_NOT_EXISTS("015", "流程任务不存在或已处理"),
;
private final String code;
private final String message;
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java
index 3fdbeea92..732e6be42 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java
@@ -37,6 +37,9 @@ public enum BpmnTaskRespCode implements IModuleRespCode {
TASK_TYPE_MISMATCH("020", "节点类型不匹配,当前节点类型:【{}】,指定节点类型:【{}】!"),
PROCESS_CANT_SET_ASSIGNEE("021", "当前审批状态不允许设置审批人"),
ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT("022", String.format("人员数量超过限制,节点审批人限制数量为: %d!", APPROVAL_ASSIGNER_LIMIT_NUMBER)),
+ BACK_TARGET_ACTIVITY_NOT_EXISTS("023", "回退到指定节点【{}】失败!"),
+ BACK_NODE_CANNOT_REACHABLE("024", "退回节点【{}】不可达,不允许退回"),
+ REACHED_BACKED_MAXIMUM_NUM("025", "达到回退操作次数上限【{}】次"),
;
private final String code;
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java
index 7fbdc0886..319e88a95 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java
@@ -19,7 +19,7 @@ public enum FlowableEngineRespCode implements IModuleRespCode {
ENGINE_USER_TASK_PARAM_ERROR("004", "构建后的查询审批人入参为空. 任务节点【nodeId:{}】, 该节点选择的\"审批人所在范围\"是:【{}】,请检查 cooperationOrg 参数"),
ENGINE_NOTICE_CUSTOM_FLOW_ELEMENT_ERROR("005", "查询通知目标用户前参数发生异常,未获取到 WorkspaceType"),
ENGINE_ASYNC_COMMAND_EXECUTION_ERROR("006", "引擎出现 SQL 相关异常, 异常信息:【{}】"),
- ENGINE_ASYNC_COMMAND_EXECUTION_RETRY_GIVE_UP("007", "命令重试尝试【{}】次仍然失败,并出现异常, 将放弃"),
+ ENGINE_ASYNC_COMMAND_EXECUTION_RETRY_GIVE_UP("007", "命令重试尝试【{}】次仍然失败,并出现异常, 将放弃, 错误信息:{}"),
;
private final String code;
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java
index 3b6532d74..23eadcd0f 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java
@@ -9,7 +9,8 @@ import java.util.Arrays;
* @since 2023/9/18 17:11
*/
public enum BpmnProcessTaskResultEnum {
-
+ PENDING("PENDING", "待处理"),
+ PROCESSED("PROCESSED", "已处理"),
AUTO_SKIP("AUTO_SKIP", "任务自动跳过"),
// 引擎默认的标识,不允许修改
MI_END("MI_END", "多实例任务运行结束"),
@@ -17,8 +18,7 @@ public enum BpmnProcessTaskResultEnum {
DELETE_MI_EXECUTION("Delete MI execution", "多实例任务被删除"),
INITIATOR_REVOCATION("INITIATOR_REVOCATION", "发起者主动撤回"),
REJECTION_AUTO_COMPLETED("REJECTION_AUTO_COMPLETED", "审批驳回自动结束"),
- BACKED("BACKED", "退回");
-
+ ;
private final String status;
/**
* 描述
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnModelUtils.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnModelUtils.java
new file mode 100644
index 000000000..80ace39f9
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnModelUtils.java
@@ -0,0 +1,91 @@
+package cn.axzo.workflow.core.common.utils;
+
+import org.flowable.bpmn.model.EventSubProcess;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.FlowElementsContainer;
+import org.flowable.bpmn.model.FlowNode;
+import org.flowable.bpmn.model.SequenceFlow;
+import org.flowable.bpmn.model.StartEvent;
+import org.flowable.bpmn.model.SubProcess;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class BpmnModelUtils {
+
+ /**
+ * 节点是否可达
+ * @param process
+ * @param sourceElement
+ * @param targetElement
+ * @return
+ */
+ public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement) {
+ return isReachable(process, sourceElement, targetElement, new HashSet<>());
+ }
+
+ public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement, Set visitedElements) {
+ // Special case: start events in an event subprocess might exist as an execution and are most likely be able to
+ // reach the target
+ // when the target is in the event subprocess, but should be ignored as they are not 'real' runtime executions
+ // (but rather waiting for trigger)
+ if (sourceElement instanceof StartEvent && isInEventSubprocess(sourceElement)) {
+ return false;
+ }
+ // No outgoing seq flow: could be the end of eg . the process or an embedded subprocess
+ if (sourceElement.getOutgoingFlows().isEmpty()) {
+ visitedElements.add(sourceElement.getId());
+ FlowElementsContainer parentElement = process.findParent(sourceElement);
+ if (parentElement instanceof SubProcess) {
+ sourceElement = (SubProcess) parentElement;
+ // 子流程的结束节点,若目标节点在该子流程中,说明无法到达,返回false
+ if (((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ if (sourceElement.getId().equals(targetElement.getId())) {
+ return true;
+ }
+ // To avoid infinite looping, we must capture every node we visit
+ // and check before going further in the graph if we have already
+ // visited the node.
+ visitedElements.add(sourceElement.getId());
+ // 当前节点能够到达子流程,且目标节点在子流程中,说明可以到达,返回true
+ if (sourceElement instanceof SubProcess && ((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
+ return true;
+ }
+ List sequenceFlows = sourceElement.getOutgoingFlows();
+ if (sequenceFlows != null && !sequenceFlows.isEmpty()) {
+ for (SequenceFlow sequenceFlow : sequenceFlows) {
+ String targetRef = sequenceFlow.getTargetRef();
+ FlowNode sequenceFlowTarget = (FlowNode) process.getFlowElement(targetRef, true);
+ if (sequenceFlowTarget != null && !visitedElements.contains(sequenceFlowTarget.getId())) {
+ boolean reachable = isReachable(process, sequenceFlowTarget, targetElement, visitedElements);
+ if (reachable) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ protected static boolean isInEventSubprocess(FlowNode flowNode) {
+ FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
+ while (flowElementsContainer != null) {
+ if (flowElementsContainer instanceof EventSubProcess) {
+ return true;
+ }
+ if (flowElementsContainer instanceof FlowElement) {
+ flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
+ } else {
+ flowElementsContainer = null;
+ }
+ }
+ return false;
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java
index 8fc32f0b3..195d53b6e 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java
@@ -5,15 +5,18 @@ import cn.axzo.workflow.core.engine.behavior.CustomActivityBehaviorFactory;
import cn.axzo.workflow.core.engine.cmd.CustomCommandContextFactory;
import cn.axzo.workflow.core.engine.id.BasedNacosSnowflakeIdGenerator;
import cn.axzo.workflow.core.engine.interceptor.CustomRetryInterceptor;
-import cn.axzo.workflow.core.engine.job.AsyncAbortProcessInstanceHandler;
import cn.axzo.workflow.core.engine.job.AsyncActivityCallbackJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncActivityLeaveJobHandler;
+import cn.axzo.workflow.core.engine.job.AsyncAbortProcessInstanceJobHandler;
+import cn.axzo.workflow.core.engine.job.AsyncActivitySetAssigneeJobHandler;
+import cn.axzo.workflow.core.engine.job.AsyncActivityTriggerJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler;
-import cn.axzo.workflow.core.engine.job.AsyncBpmnProcessActivityJobHandler;
-import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceHandler;
+import cn.axzo.workflow.core.engine.job.AsyncBackTaskJobHandler;
+import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncCountersignUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncExtTaskInstJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncRejectTaskJobHandler;
+import cn.axzo.workflow.core.engine.job.AsyncTermNodeAlterJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncTransferUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncJobLogClearTraceExceptionHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncRunnableExceptionExceptionHandler;
@@ -67,7 +70,7 @@ public class FlowableConfiguration {
List jobProcessors,
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties,
- StringRedisTemplate redisTemplate) {
+ SupportRefreshProperties refreshProperties) {
return configuration -> {
configuration.setEnableHistoricTaskLogging(true);
configuration.setHistoryLevel(HistoryLevel.AUDIT);
@@ -83,14 +86,18 @@ public class FlowableConfiguration {
configuration.setIdGenerator(new BasedNacosSnowflakeIdGenerator(nacosServiceManager, nacosDiscoveryProperties));
configuration.setHistoricProcessInstanceDataManager(new CustomMybatisHistoricProcessInstanceDataManager(configuration));
// 自定义的异步任务处理器
+ configuration.addCustomJobHandler(new AsyncAbortProcessInstanceJobHandler(extAxHiTaskInstService));
+ configuration.addCustomJobHandler(new AsyncActivitySetAssigneeJobHandler());
+ configuration.addCustomJobHandler(new AsyncActivityTriggerJobHandler());
configuration.addCustomJobHandler(new AsyncApproveTaskJobHandler());
- configuration.addCustomJobHandler(new AsyncRejectTaskJobHandler(extAxHiTaskInstService));
- configuration.addCustomJobHandler(new AsyncExtTaskInstJobHandler(extAxHiTaskInstService));
- configuration.addCustomJobHandler(new AsyncTransferUserTaskJobHandler());
+ configuration.addCustomJobHandler(new AsyncBackTaskJobHandler());
+ configuration.addCustomJobHandler(new AsyncCancelProcessInstanceJobHandler(extAxHiTaskInstService));
+ configuration.addCustomJobHandler(new AsyncCountersignUserTaskJobHandler(extAxHiTaskInstService));
+ configuration.addCustomJobHandler(new AsyncExtTaskInstJobHandler(extAxHiTaskInstService));
+ configuration.addCustomJobHandler(new AsyncRejectTaskJobHandler(extAxHiTaskInstService));
+ configuration.addCustomJobHandler(new AsyncTransferUserTaskJobHandler());
+ configuration.addCustomJobHandler(new AsyncTermNodeAlterJobHandler(refreshProperties));
configuration.addCustomJobHandler(new AsyncCountersignUserTaskJobHandler(extAxHiTaskInstService));
- configuration.addCustomJobHandler(new AsyncCancelProcessInstanceHandler(extAxHiTaskInstService));
- configuration.addCustomJobHandler(new AsyncAbortProcessInstanceHandler(extAxHiTaskInstService));
- configuration.addCustomJobHandler(new AsyncBpmnProcessActivityJobHandler(bpmnProcessActivityService));
configuration.addCustomJobHandler(new AsyncActivityLeaveJobHandler(bpmnProcessActivityService));
configuration.addCustomJobHandler(new AsyncActivityCallbackJobHandler());
// 异步任务异常重试时间间隔
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java
index 36bceb854..cc4db9e20 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java
@@ -1,13 +1,14 @@
package cn.axzo.workflow.core.conf;
-import com.google.common.collect.Lists;
+import com.alibaba.nacos.api.config.annotation.NacosValue;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
-import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* 支持动态刷新配置属性
@@ -28,4 +29,49 @@ public class SupportRefreshProperties {
@Value("${workflow.apiLog.filterApiType:}")
private String filterApiType;
+
+ @Value("${workflow.api.timeout:10}")
+ private Long apiTimeout;
+ @Value("${workflow.mock:false}")
+ private Boolean mock;
+ @Value("${workflow.assignee.global:true}")
+ private Boolean global;
+ @Value("${workflow.assignee.category:''}")
+ private String category;
+ @Value("#{${workflow.assignee.map:{}}}")
+ private Map assigneeMap;
+
+ @Value(value = "${workflow.alter.enable:false}")
+ private Boolean alterEnable;
+ /**
+ * 节点卡住多久才告警
+ */
+ @Value("${workflow.alter.delay:10}")
+ private Integer pauseDelay;
+ /**
+ * 业务节点暂停告警的次数,该值不建议改小
+ */
+ @Value(value = "${workflow.alter.retries:1000}")
+ private Integer alterRetries;
+
+ /**
+ * 业务节点暂停告警次数间的间隔
+ */
+ @Value(value = "${workflow.alter.interval:10}")
+ private Integer alterInterval;
+
+ /**
+ * 业务节点暂停告警次数间隔的时间单位
+ */
+ @Value(value = "${workflow.alter.intervalUnit:minutes}")
+ private TimeUnit alterIntervalUnit;
+
+ @Value(value = "${workflow.alter.mobiles:}")
+ private List alterMobiles;
+
+ /**
+ * 用于控制转交管理员的 API
+ */
+ @Value("${workflow.useNewToAdminApi:true}")
+ private Boolean useNewToAdminApi;
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ButtonConfTypeHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ButtonConfTypeHandler.java
new file mode 100644
index 000000000..c235e42c9
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ButtonConfTypeHandler.java
@@ -0,0 +1,57 @@
+package cn.axzo.workflow.core.conf.handler;
+
+import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
+import com.baomidou.mybatisplus.core.toolkit.Assert;
+import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * BpmnButtonConf 数据映射转换
+ *
+ * @author wangli
+ * @since 2024-09-07 22:40
+ */
+@Slf4j
+@MappedTypes({List.class})
+@MappedJdbcTypes({JdbcType.VARCHAR})
+public class ButtonConfTypeHandler extends AbstractJsonTypeHandler {
+ private static ObjectMapper objectMapper = new ObjectMapper();
+
+ public ButtonConfTypeHandler(Class> type) {
+ if (log.isTraceEnabled()) {
+ log.trace("JacksonTypeHandler(" + type + ")");
+ }
+ Assert.notNull(type, "Type argument cannot be null", new Object[0]);
+ }
+
+ protected BpmnButtonConf parse(String json) {
+ try {
+ // 这里进行了json解析,同样在这里也可以进行字段查询后的处理,如对象内部的手机号字段的加密展示等
+ return objectMapper.readValue(json, new TypeReference() {
+ });
+ } catch (IOException var3) {
+ throw new RuntimeException(var3);
+ }
+ }
+
+ protected String toJson(BpmnButtonConf obj) {
+ try {
+ return objectMapper.writeValueAsString(obj);
+ } catch (JsonProcessingException var3) {
+ throw new RuntimeException(var3);
+ }
+ }
+
+ public static void setObjectMapper(ObjectMapper om) {
+ objectMapper = om;
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListAssigneeTypeHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListAssigneeTypeHandler.java
new file mode 100644
index 000000000..cd54a7f40
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListAssigneeTypeHandler.java
@@ -0,0 +1,57 @@
+package cn.axzo.workflow.core.conf.handler;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import com.baomidou.mybatisplus.core.toolkit.Assert;
+import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * BpmnTaskDelegateAssigner 数据映射转换
+ *
+ * @author wangli
+ * @since 2024-09-07 22:40
+ */
+@Slf4j
+@MappedTypes({List.class})
+@MappedJdbcTypes({JdbcType.VARCHAR})
+public class ListAssigneeTypeHandler extends AbstractJsonTypeHandler> {
+ private static ObjectMapper objectMapper = new ObjectMapper();
+
+ public ListAssigneeTypeHandler(Class> type) {
+ if (log.isTraceEnabled()) {
+ log.trace("JacksonTypeHandler(" + type + ")");
+ }
+ Assert.notNull(type, "Type argument cannot be null", new Object[0]);
+ }
+
+ protected List parse(String json) {
+ try {
+ // 这里进行了json解析,同样在这里也可以进行字段查询后的处理,如对象内部的手机号字段的加密展示等
+ return objectMapper.readValue(json, new TypeReference>() {
+ });
+ } catch (IOException var3) {
+ throw new RuntimeException(var3);
+ }
+ }
+
+ protected String toJson(List obj) {
+ try {
+ return objectMapper.writeValueAsString(obj);
+ } catch (JsonProcessingException var3) {
+ throw new RuntimeException(var3);
+ }
+ }
+
+ public static void setObjectMapper(ObjectMapper om) {
+ objectMapper = om;
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java
index 3004da535..1837174b1 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java
@@ -12,12 +12,12 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.TaskHelper;
-import org.flowable.task.api.Task;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.Objects;
+import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
@@ -48,7 +48,8 @@ public class CustomReceiveTaskActivityBehavior extends ReceiveTaskActivityBehavi
task.setTaskDefinitionKey(receiveTask.getId());
task.setPropagatedStageInstanceId(execution.getPropagatedStageInstanceId());
task.setName(receiveTask.getName());
- TaskHelper.insertTask(task, (ExecutionEntity) execution, false, false);
+ TaskHelper.insertTask(task, (ExecutionEntity) execution, true, false);
+
// 添加 taskInst 扩展表数据
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
@@ -65,14 +66,17 @@ public class CustomReceiveTaskActivityBehavior extends ReceiveTaskActivityBehavi
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
org.flowable.engine.TaskService taskService = processEngineConfiguration.getTaskService();
- Task task = taskService.createTaskQuery().executionId(execution.getId())
+ TaskEntity task = (TaskEntity) taskService.createTaskQuery().executionId(execution.getId())
.taskDefinitionKey(execution.getCurrentActivityId()).singleResult();
if (Objects.nonNull(task)) {
+ // 用于新版日志
+ task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), APPROVED.getStatus());
+
eventDispatcher.dispatchEvent(new ExtTaskInstUpdateEvent(execution.getProcessInstanceId(),
receiveTask.getId(), task.getId(), APPROVED),
processEngineConfiguration.getEngineCfgKey());
} else {
- log.warn("task is null, executionId: {}, activityId: {}", execution.getId(),
+ log.warn("ReceiveTask is null, executionId: {}, activityId: {}", execution.getId(),
execution.getCurrentActivityId());
}
super.leave(execution);
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomServiceTaskDelegateExpressionActivityBehavior.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomServiceTaskDelegateExpressionActivityBehavior.java
index 378fa8998..b7aa7ce82 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomServiceTaskDelegateExpressionActivityBehavior.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomServiceTaskDelegateExpressionActivityBehavior.java
@@ -2,6 +2,7 @@ package cn.axzo.workflow.core.engine.behavior;
import cn.axzo.workflow.core.engine.event.ExtTaskInstCreateEvent;
import cn.axzo.workflow.core.engine.event.ExtTaskInstUpdateEvent;
+import cn.axzo.workflow.core.engine.listener.EngineCarbonCopyEventListener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.MapExceptionEntry;
import org.flowable.bpmn.model.ServiceTask;
@@ -19,14 +20,16 @@ import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.List;
+import java.util.Objects;
+import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
/**
* 自定义的服务任务活动行为处理器
*
- * 主要用来创建审批日志
+ * 主要用来创建抄送节点审批日志,真实计算抄送人的集合是由 {@link EngineCarbonCopyEventListener} 来完成的
*
* @author wangli
* @since 13/03/2024 14:17
@@ -35,6 +38,7 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCES
public class CustomServiceTaskDelegateExpressionActivityBehavior extends ServiceTaskDelegateExpressionActivityBehavior {
protected final ServiceTask serviceTask;
+ // thread safe
private TaskEntity task;
public CustomServiceTaskDelegateExpressionActivityBehavior(String serviceTaskId, Expression expression,
@@ -57,7 +61,7 @@ public class CustomServiceTaskDelegateExpressionActivityBehavior extends Service
task.setTaskDefinitionKey(serviceTask.getId());
task.setPropagatedStageInstanceId(execution.getPropagatedStageInstanceId());
task.setName(serviceTask.getName());
- TaskHelper.insertTask(task, (ExecutionEntity) execution, false, false);
+ TaskHelper.insertTask(task, (ExecutionEntity) execution, true, false);
// 添加 taskInst 扩展表数据
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
@@ -72,11 +76,24 @@ public class CustomServiceTaskDelegateExpressionActivityBehavior extends Service
CommandContext commandContext = CommandContextUtil.getCommandContext();
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
-
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
- eventDispatcher.dispatchEvent(new ExtTaskInstUpdateEvent(execution.getProcessInstanceId(),
- execution.getCurrentActivityId(), task.getId(), APPROVED),
+
+ org.flowable.engine.TaskService taskService = processEngineConfiguration.getTaskService();
+ TaskEntity serviceTask = (TaskEntity) taskService.createTaskQuery().taskId(task.getId())
+ .taskDefinitionKey(execution.getCurrentActivityId()).singleResult();
+ if (Objects.nonNull(serviceTask)) {
+ // 用于新版日志
+ serviceTask.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + serviceTask.getId(), APPROVED.getStatus());
+
+ eventDispatcher.dispatchEvent(new ExtTaskInstUpdateEvent(execution.getProcessInstanceId(),
+ execution.getCurrentActivityId(), serviceTask.getId(), APPROVED),
processEngineConfiguration.getEngineCfgKey());
+ TaskHelper.deleteTask(serviceTask, "complete carbon", false, true, true);
+ } else {
+ log.warn("ServiceTask is null, executionId: {}, activityId: {}", execution.getId(),
+ execution.getCurrentActivityId());
+ }
+
super.leave(execution);
}
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java
index bd6eb0de8..4f9495fcd 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java
@@ -8,7 +8,7 @@ import org.flowable.common.engine.impl.interceptor.CommandContext;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.ENGINE_EXEC_EXCEPTION;
/**
- * TODO
+ * 抽象的命令,用于将在 Command 中执行的逻辑所抛出的非 WorkflowEngineException 的异常都包装为 WorkflowEngineException
*
* @author wangli
* @since 2024/7/1 13:59
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceAsyncCmd.java
index ec87d66fa..e11e5ea2f 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceAsyncCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceAsyncCmd.java
@@ -2,7 +2,7 @@ package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
-import cn.axzo.workflow.core.engine.job.AsyncAbortProcessInstanceHandler;
+import cn.axzo.workflow.core.engine.job.AsyncAbortProcessInstanceJobHandler;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.CommandContext;
@@ -73,9 +73,9 @@ public class CustomAbortProcessInstanceAsyncCmd extends AbstractCommand im
job.setExecutionId(instance.getId());
job.setProcessInstanceId(instance.getId());
job.setProcessDefinitionId(instance.getProcessDefinitionId());
- job.setElementId(AsyncAbortProcessInstanceHandler.TYPE);
+ job.setElementId(AsyncAbortProcessInstanceJobHandler.TYPE);
job.setElementName(instance.getName());
- job.setJobHandlerType(AsyncAbortProcessInstanceHandler.TYPE);
+ job.setJobHandlerType(AsyncAbortProcessInstanceJobHandler.TYPE);
job.setTenantId(instance.getTenantId());
// 携带自定义的数据
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java
index cb5836728..2ccd617a4 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java
@@ -2,6 +2,7 @@ package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import cn.axzo.workflow.core.engine.model.AddComment;
import cn.axzo.workflow.core.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON;
@@ -20,7 +21,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-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;
@@ -35,7 +35,7 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.CANCEL
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_ABORT;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS;
-import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.completeVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;
/**
@@ -101,14 +101,15 @@ public class CustomAbortProcessInstanceCmd extends AbstractCommand impleme
runtimeService.setVariables(instance.getId(), variables);
CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext,
- processInstanceId, extAxHiTaskInstService));
+ processInstanceId, extAxHiTaskInstService, ABORTED));
// 添加自定义的节点,用于展示最后的操作
Task task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId,
"系统中止", NODE_ABORT.getType(), null, BpmnTaskDelegateAssigner.buildDummyAssigner("system",
- TASK_ASSIGNEE_SKIP_FLAT, "系统"), ABORTED.getStatus());
- addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, reason);
+ TASK_ASSIGNEE_SKIP_FLAT, "系统"), ABORTED.getStatus(), new AddComment(reason));
runtimeService.setVariable(task.getProcessInstanceId(), TASK_COMPLETE_OPERATION_TYPE + task.getId(), ABORTED);
+
+ completeVirtualTask(commandContext, task);
return null;
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerAsyncCmd.java
new file mode 100644
index 000000000..1e194e008
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerAsyncCmd.java
@@ -0,0 +1,85 @@
+package cn.axzo.workflow.core.engine.cmd;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
+import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import cn.axzo.workflow.core.engine.job.AsyncActivityTriggerJobHandler;
+import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+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.JobService;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.task.api.Task;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_TRIGGER_NOT_EXISTS;
+
+/**
+ * 自定义(异步)流转业务姐弟那的命令器实现
+ *
+ * @author wangli
+ * @since 2024-09-09 13:58
+ */
+public class CustomActivityTriggerAsyncCmd extends AbstractCommand implements Serializable {
+
+ private static final Logger log = LoggerFactory.getLogger(CustomActivityTriggerAsyncCmd.class);
+ private final BpmnActivityTriggerDTO dto;
+
+ public CustomActivityTriggerAsyncCmd(BpmnActivityTriggerDTO dto) {
+ this.dto = dto;
+ }
+
+ @Override
+ public String paramToJsonString() {
+ return JSON.toJSONString(dto);
+ }
+
+ @Override
+ public String execute(CommandContext commandContext) {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ Execution execution = runtimeService.createExecutionQuery().executionId(dto.getTriggerId()).singleResult();
+ if (Objects.isNull(execution)) {
+ throw new WorkflowEngineException(ACTIVITY_TRIGGER_NOT_EXISTS, dto.getTriggerId());
+ }
+
+ return startAsync(commandContext);
+ }
+
+ private String startAsync(CommandContext commandContext) {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ TaskService taskService = processEngineConfiguration.getTaskService();
+ TaskEntity task = (TaskEntity) taskService.createTaskQuery().executionId(dto.getTriggerId()).singleResult();
+ JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();
+
+ JobEntity job = jobService.createJob();
+ // 这里的 executionId 可为 null
+ job.setExecutionId(task.getExecutionId());
+ job.setProcessInstanceId(task.getProcessInstanceId());
+ job.setProcessDefinitionId(task.getProcessDefinitionId());
+ job.setElementId(task.getTaskDefinitionKey());
+ job.setElementName(task.getName());
+ job.setJobHandlerType(AsyncActivityTriggerJobHandler.TYPE);
+ job.setTenantId(task.getTenantId());
+
+ // 携带自定义的数据
+ job.setCustomValues(JSONUtil.toJsonStr(dto));
+
+ // 创建异步任务并调度
+ jobService.createAsyncJob(job, false);
+ jobService.scheduleAsyncJob(job);
+ return job.getId();
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerCmd.java
new file mode 100644
index 000000000..918e0cfb7
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerCmd.java
@@ -0,0 +1,56 @@
+package cn.axzo.workflow.core.engine.cmd;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
+import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import com.alibaba.fastjson.JSON;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_TRIGGER_NOT_EXISTS;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
+
+/**
+ * 自定义(同步)流转业务姐弟那的命令器实现
+ *
+ * @author wangli
+ * @since 2024-09-09 13:58
+ */
+public class CustomActivityTriggerCmd extends AbstractCommand implements Serializable {
+
+ private static final Logger log = LoggerFactory.getLogger(CustomActivityTriggerCmd.class);
+ private final BpmnActivityTriggerDTO dto;
+
+ public CustomActivityTriggerCmd(BpmnActivityTriggerDTO dto) {
+ this.dto = dto;
+ }
+
+ @Override
+ public String paramToJsonString() {
+ return JSON.toJSONString(dto);
+ }
+
+ @Override
+ public Void execute(CommandContext commandContext) {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+
+ TaskEntity task = (TaskEntity) processEngineConfiguration.getTaskService()
+ .createTaskQuery().executionId(dto.getTriggerId()).singleResult();
+ if (Objects.isNull(task)) {
+ throw new WorkflowEngineException(ACTIVITY_TRIGGER_NOT_EXISTS, dto.getTriggerId());
+ }
+ addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "已处理");
+ RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ runtimeService.trigger(dto.getTriggerId());
+ return null;
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskAsyncCmd.java
index f7bccb3ab..c89d20c74 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskAsyncCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskAsyncCmd.java
@@ -29,8 +29,10 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask
* @since 2024/1/4 15:50
*/
public class CustomApproveTaskAsyncCmd extends AbstractCommand implements Serializable {
+ private static final long serialVersionUID = -4706627700694867170L;
private static final Logger log = LoggerFactory.getLogger(CustomApproveTaskAsyncCmd.class);
+
private final BpmnTaskAuditDTO dto;
public CustomApproveTaskAsyncCmd(BpmnTaskAuditDTO dto) {
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java
index e6e961069..f04a60914 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java
@@ -101,7 +101,7 @@ public class CustomApproveTaskCmd extends AbstractCommand implements Seria
if (Objects.nonNull(operationDesc)) {
this.operationDesc = operationDesc;
} else {
- this.operationDesc = "已通过";
+ this.operationDesc = "(已通过)";
}
}
@@ -115,8 +115,8 @@ public class CustomApproveTaskCmd extends AbstractCommand implements Seria
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(taskId).singleResult();
- Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
- validTask(historicTaskInstance, (TaskEntity) task, approver, nodeTypes);
+ TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
+ validTask(historicTaskInstance, task, approver, nodeTypes);
// TODO 所有的跟 Task 相关的动作都可以在这里进行扩展,用于扩展八大按钮标准动作以外的一些逻辑,但这里需要结合 Spring 能力,需设计好扩展点,否则无法进行扩展
// 其他动态也应该在类似的地方预留扩展点
@@ -127,7 +127,7 @@ public class CustomApproveTaskCmd extends AbstractCommand implements Seria
Authentication.setAuthenticatedUserId(null);
}
- batchAddAttachment(commandContext, task.getProcessInstanceId(), taskId, attachmentList, approver);
+ batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList, approver);
Authentication.setAuthenticatedUserId(Objects.nonNull(approver) ? approver.buildAssigneeId() : null);
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, operationDesc);
@@ -139,8 +139,7 @@ public class CustomApproveTaskCmd extends AbstractCommand implements Seria
runtimeService.setVariable(task.getProcessInstanceId(), INTERNAL_SPECIFY_NEXT_APPROVER,
nextApprover);
}
- ((TaskEntity) task).setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus());
-
+ task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus());
executeSynchronous(task, taskService, runtimeService);
return null;
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskAsyncCmd.java
new file mode 100644
index 000000000..dcf368651
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskAsyncCmd.java
@@ -0,0 +1,95 @@
+package cn.axzo.workflow.core.engine.cmd;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
+import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import cn.axzo.workflow.core.common.utils.BpmnModelUtils;
+import cn.axzo.workflow.core.engine.job.AsyncBackTaskJobHandler;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.FlowNode;
+import org.flowable.bpmn.model.Process;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+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.JobService;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.task.api.history.HistoricTaskInstanceQuery;
+import org.flowable.task.service.TaskService;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
+
+@Slf4j
+public class CustomBackTaskAsyncCmd extends AbstractCommand implements Serializable {
+
+ private static final long serialVersionUID = 1773108485033787095L;
+
+ private final BpmnTaskBackAuditDTO dto;
+
+ public CustomBackTaskAsyncCmd(BpmnTaskBackAuditDTO dto) {
+ this.dto = dto;
+ }
+
+ @Override
+ public String executeInternal(CommandContext commandContext) {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ HistoricTaskInstanceQuery taskQuery =
+ processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
+ TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService();
+ HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();
+
+ TaskEntity taskEntity = taskService.getTask(dto.getTaskId());
+ validTask(historicTaskInstance, taskEntity, dto.getApprover(), dto.getNodeTypes());
+
+ Process process = ProcessDefinitionUtil.getProcess(taskEntity.getProcessDefinitionId());
+ FlowElement targetFlowElement = process.getFlowElement(dto.getToActivityId(), true);
+ if (Objects.isNull(targetFlowElement)) {
+ throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, dto.getToActivityId());
+ }
+ FlowElement sourceFlowElement = process.getFlowElement(taskEntity.getTaskDefinitionKey(), true);
+ // 退回节点到当前节点不可达到,不允许退回
+ if (!BpmnModelUtils.isReachable(process, (FlowNode) targetFlowElement, (FlowNode) sourceFlowElement)) {
+ throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
+ }
+
+ return startAsync(processEngineConfiguration, taskEntity);
+ }
+
+ private String startAsync(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) {
+ JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();
+
+ JobEntity job = jobService.createJob();
+ // 这里的 executionId 可为 null
+ job.setExecutionId(task.getExecutionId());
+ job.setProcessInstanceId(task.getProcessInstanceId());
+ job.setProcessDefinitionId(task.getProcessDefinitionId());
+ job.setElementId(task.getTaskDefinitionKey());
+ job.setElementName(task.getName());
+ job.setJobHandlerType(AsyncBackTaskJobHandler.TYPE);
+ job.setTenantId(task.getTenantId());
+
+ // 携带自定义的数据
+ job.setCustomValues(JSONUtil.toJsonStr(dto));
+
+ // 创建异步任务并调度
+ jobService.createAsyncJob(job, false);
+ jobService.scheduleAsyncJob(job);
+ return job.getId();
+ }
+
+ @Override
+ public String paramToJsonString() {
+ return JSON.toJSONString(dto);
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskCmd.java
new file mode 100644
index 000000000..31df9ca27
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskCmd.java
@@ -0,0 +1,108 @@
+package cn.axzo.workflow.core.engine.cmd;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
+import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import cn.axzo.workflow.core.common.utils.BpmnModelUtils;
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.FlowNode;
+import org.flowable.bpmn.model.Process;
+import org.flowable.common.engine.impl.identity.Authentication;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.engine.impl.util.ProcessDefinitionUtil;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.task.api.history.HistoricTaskInstanceQuery;
+import org.flowable.task.service.TaskService;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+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_TASK_RELATION_ASSIGNEE_LIST_INFO;
+import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.BACKED;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
+
+/**
+ * 回退命令
+ */
+@Slf4j
+public class CustomBackTaskCmd extends AbstractCommand implements Serializable {
+ private static final long serialVersionUID = -1241290344311892346L;
+
+ private final BpmnTaskBackAuditDTO dto;
+
+ private static final String OPERATION_DESC = "回退至";
+
+ @Override
+ public String paramToJsonString() {
+ Map params = new HashMap<>();
+ params.put("taskId", dto.getTaskId());
+ params.put("advice", dto.getAdvice());
+ params.put("operationDesc", OPERATION_DESC);
+ params.put("attachmentList", JSON.toJSONString(dto.getAttachmentList()));
+ params.put("approver", JSON.toJSONString(dto.getApprover()));
+ params.put("nodeTypes", JSON.toJSONString(dto.getNodeTypes()));
+ return JSON.toJSONString(params);
+ }
+
+ public CustomBackTaskCmd(BpmnTaskBackAuditDTO dto) {
+ this.dto = dto;
+ }
+
+ @Override
+ public Void execute(CommandContext commandContext) {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ HistoricTaskInstanceQuery taskQuery =
+ processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
+ TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService();
+
+ HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();
+
+ TaskEntity task = taskService.getTask(dto.getTaskId());
+ validTask(historicTaskInstance, task, dto.getApprover(), dto.getNodeTypes());
+
+ Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId());
+ FlowElement targetFlowElement = process.getFlowElement(dto.getToActivityId(), true);
+ if (Objects.isNull(targetFlowElement)) {
+ throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, dto.getToActivityId());
+ }
+ FlowElement sourceFlowElement = process.getFlowElement(task.getTaskDefinitionKey(), true);
+ // 退回节点到当前节点不可达到,不允许退回
+ if (!BpmnModelUtils.isReachable(process, (FlowNode) targetFlowElement, (FlowNode) sourceFlowElement)) {
+ throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
+ }
+ batchAddAttachment(commandContext, task.getProcessInstanceId(), task, dto.getAttachmentList(), dto.getApprover());
+
+ Authentication.setAuthenticatedUserId(dto.getApprover().buildAssigneeId());
+ addComment(commandContext, task, COMMENT_TYPE_ADVICE, dto.getAdvice());
+ addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, OPERATION_DESC + targetFlowElement.getName());
+ Authentication.setAuthenticatedUserId(null);
+
+ RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + dto.getTaskId(), BACKED.getStatus());
+
+ // 移除回退到的指定节点的变量,让 EngineExecutionStartListener 重新计算该节点的人
+ runtimeService.removeVariable(task.getProcessInstanceId(), INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + dto.getToActivityId());
+ runtimeService.createChangeActivityStateBuilder()
+ .processInstanceId(task.getProcessInstanceId())
+ .moveActivityIdsToSingleActivityId(Collections.singletonList(task.getTaskDefinitionKey()), dto.getToActivityId())
+ .changeState();
+ return null;
+ }
+
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskAsyncCmd.java
new file mode 100644
index 000000000..af1e5d076
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskAsyncCmd.java
@@ -0,0 +1,102 @@
+package cn.axzo.workflow.core.engine.cmd;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO;
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
+import cn.axzo.workflow.core.engine.job.AsyncActivitySetAssigneeJobHandler;
+import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.ProcessEngineConfiguration;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.job.service.JobService;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.task.api.Task;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.springframework.util.CollectionUtils;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID;
+import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
+import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
+import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED;
+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.BpmnTaskRespCode.ACTIVITY_BIZ_SET_ASSIGNEE_ERROR;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_CANT_SET_ASSIGNEE;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.PROCESS_CANT_SET_ASSIGNEE;
+import static cn.axzo.workflow.core.engine.cmd.CustomBizSpecifyAssigneeToTaskCmd.getOperateTask;
+import static cn.axzo.workflow.core.engine.cmd.CustomBizSpecifyAssigneeToTaskCmd.validProcessInstance;
+import static cn.axzo.workflow.core.engine.cmd.CustomBizSpecifyAssigneeToTaskCmd.validate;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount;
+
+/**
+ * 自定的业务指定审批人命令实现
+ *
+ * @author wangli
+ * @since 2023/12/22 13:51
+ */
+public class CustomBizSpecifyAssigneeToTaskAsyncCmd extends AbstractCommand implements Serializable {
+
+ private final BpmnActivitySetAssigneeDTO dto;
+
+ public CustomBizSpecifyAssigneeToTaskAsyncCmd(BpmnActivitySetAssigneeDTO dto) {
+ this.dto = dto;
+ }
+
+ @Override
+ public String paramToJsonString() {
+ return JSON.toJSONString(dto);
+ }
+
+ @Override
+ public String execute(CommandContext commandContext) {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ TaskService taskService = processEngineConfiguration.getTaskService();
+ TaskEntity task = (TaskEntity) getOperateTask(taskService, dto.getTriggerId());
+ //校验
+ validate(processEngineConfiguration.getRuntimeService(), dto.getTriggerId(), task, dto.getAssigners());
+
+ validProcessInstance(commandContext, task);
+
+ return startAsync(processEngineConfiguration, task);
+ }
+
+ private String startAsync(ProcessEngineConfigurationImpl processEngineConfiguration, TaskEntity task) {
+ JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();
+
+ JobEntity job = jobService.createJob();
+ // 这里的 executionId 可为 null
+ job.setExecutionId(task.getExecutionId());
+ job.setProcessInstanceId(task.getProcessInstanceId());
+ job.setProcessDefinitionId(task.getProcessDefinitionId());
+ job.setElementId(task.getTaskDefinitionKey());
+ job.setElementName(task.getName());
+ job.setJobHandlerType(AsyncActivitySetAssigneeJobHandler.TYPE);
+ job.setTenantId(task.getTenantId());
+
+ // 携带自定义的数据
+ job.setCustomValues(JSONUtil.toJsonStr(dto));
+
+ // 创建异步任务并调度
+ jobService.createAsyncJob(job, false);
+ jobService.scheduleAsyncJob(job);
+ return job.getId();
+ }
+
+
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java
index f108311bf..e6b891e63 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java
@@ -6,11 +6,14 @@ import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
+import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.job.api.Job;
+import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils;
@@ -59,8 +62,8 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand
public static Task getOperateTask(TaskService taskService, String executionId) {
return taskService.createTaskQuery().executionId(executionId)
- .taskAssignee(NO_ASSIGNEE)
- .singleResult();
+ .taskAssignee(NO_ASSIGNEE)
+ .singleResult();
}
/**
@@ -79,9 +82,9 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand
@Override
public Boolean execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
- CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
- Task task = getOperateTask(taskService, executionId);
+ TaskEntity task = (TaskEntity) getOperateTask(taskService, executionId);
//校验
validate(processEngineConfiguration.getRuntimeService(), executionId, task, addedAssigners);
@@ -91,12 +94,29 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand
addAssignee(commandContext, taskService, task);
+ clearAlterTimeJob(commandContext, task);
+
return true;
}
- private void validProcessInstance(CommandContext commandContext, Task task) {
+ /**
+ * 清空告警的任务
+ *
+ * @param commandContext
+ * @param task
+ */
+ private void clearAlterTimeJob(CommandContext commandContext, TaskEntity task) {
+ ManagementService managementService = CommandContextUtil.getProcessEngineConfiguration(commandContext).getManagementService();
+ Job timerJob = managementService.createTimerJobQuery().elementId(task.getTaskDefinitionKey()).processInstanceId(task.getProcessInstanceId()).singleResult();
+ if (Objects.nonNull(timerJob)) {
+ CommandContextUtil.getTimerJobService().deleteTimerJob((TimerJobEntity) timerJob);
+ }
+
+ }
+
+ public static void validProcessInstance(CommandContext commandContext, Task task) {
ProcessEngineConfigurationImpl processEngineConfiguration =
- CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoryService historyService = processEngineConfiguration.getHistoryService();
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
if (Objects.isNull(processInstance)) {
@@ -110,11 +130,11 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand
private void changeAssigneeSnapshot(CommandContext commandContext, Task task) {
ProcessEngineConfigurationImpl processEngineConfiguration =
- CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(),
- INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class);
+ INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class);
for (BpmnTaskDelegateAssigner assigner : originAssingeeList) {
if (Objects.equals(assigner.buildAssigneeId(), NO_ASSIGNEE)) {
@@ -124,8 +144,8 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand
}
originAssingeeList.addAll(addedAssigners);
runtimeService.setVariable(task.getProcessInstanceId(),
- INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
- originAssingeeList);
+ INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
+ originAssingeeList);
}
private void addAssignee(CommandContext commandContext, TaskService taskService, Task task) {
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java
index 38e8174f8..effb4cebf 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java
@@ -3,7 +3,7 @@ package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
-import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceHandler;
+import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceJobHandler;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.CommandContext;
@@ -74,9 +74,9 @@ public class CustomCancelProcessInstanceAsyncCmd extends AbstractCommand i
job.setExecutionId(instance.getId());
job.setProcessInstanceId(instance.getId());
job.setProcessDefinitionId(instance.getProcessDefinitionId());
- job.setElementId(AsyncCancelProcessInstanceHandler.TYPE);
+ job.setElementId(AsyncCancelProcessInstanceJobHandler.TYPE);
job.setElementName(instance.getName());
- job.setJobHandlerType(AsyncCancelProcessInstanceHandler.TYPE);
+ job.setJobHandlerType(AsyncCancelProcessInstanceJobHandler.TYPE);
job.setTenantId(instance.getTenantId());
// 携带自定义的数据
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java
index e235d8fdd..f32129f5c 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java
@@ -2,6 +2,7 @@ package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import cn.axzo.workflow.core.engine.model.AddComment;
import cn.axzo.workflow.core.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON;
@@ -18,7 +19,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-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;
@@ -32,7 +32,7 @@ import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INS
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_CANCEL;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS;
-import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.completeVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;
/**
@@ -104,12 +104,12 @@ public class CustomCancelProcessInstanceCmd extends AbstractCommand implem
runtimeService.setVariables(instance.getId(), variables);
CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext,
- processInstanceId, extAxHiTaskInstService));
+ processInstanceId, extAxHiTaskInstService, CANCELLED));
// 添加自定义的节点,用于展示最后的操作
Task task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId,
- "发起人撤回", NODE_CANCEL.getType(), reason, initiator, CANCELLED.getStatus());
- addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "已撤回");
+ "发起人撤回", NODE_CANCEL.getType(), reason, initiator, CANCELLED.getStatus(), new AddComment(CANCELLED.getDesc()));
+ completeVirtualTask(commandContext, task);
return null;
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContext.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContext.java
index 18e70a31c..5af132216 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContext.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContext.java
@@ -1,12 +1,16 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
+import org.apache.ibatis.exceptions.PersistenceException;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableOptimisticLockingException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import java.util.Arrays;
/**
* 对 CommandContext 中的 WorkflowEngineException 进行日志降级
@@ -15,7 +19,9 @@ import org.slf4j.LoggerFactory;
* @since 2024/5/21 09:46
*/
public class CustomCommandContext extends CommandContext {
- private static final Logger LOGGER = LoggerFactory.getLogger(CommandContext.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(CustomCommandContext.class);
+
+ private static final String[] PERSISTENCE_EXCEPTION_WARN_MESSAGE = new String[]{"act_ru_job"};
public CustomCommandContext(Command> command) {
super(command);
@@ -36,6 +42,10 @@ public class CustomCommandContext extends CommandContext {
LOGGER.info("Error while closing command context", exception);
} else if (exception instanceof WorkflowEngineException) {
LOGGER.warn("Workflow error while closing command context", exception);
+ } else if (exception instanceof PersistenceException &&
+ StringUtils.hasText(exception.getMessage()) &&
+ Arrays.stream(PERSISTENCE_EXCEPTION_WARN_MESSAGE).anyMatch(m -> exception.getMessage().contains(m))) {
+ LOGGER.warn("persistence error while closing command context", exception);
} else {
LOGGER.error("Error while closing command context", exception);
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContextFactory.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContextFactory.java
index df53139bf..6e3cd116e 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContextFactory.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommandContextFactory.java
@@ -5,7 +5,7 @@ import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.common.engine.impl.interceptor.CommandContextFactory;
/**
- * TODO
+ * CommandContextFactory
*
* @author wangli
* @since 2024/5/21 09:45
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommentTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommentTaskCmd.java
index 0faf9d4ee..cd34b8cf7 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommentTaskCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCommentTaskCmd.java
@@ -4,13 +4,11 @@ import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentExtDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
-import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.cfg.IdGenerator;
import org.flowable.common.engine.impl.identity.Authentication;
-import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
@@ -22,7 +20,6 @@ import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.variable.service.HistoricVariableService;
import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntity;
import org.flowable.variable.service.impl.types.StringType;
-import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
@@ -36,9 +33,13 @@ import java.util.Objects;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_COMMENT_EXT;
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_COMMENT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COMMENTED;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createExtTaskInst;
import static org.flowable.task.api.Task.DEFAULT_PRIORITY;
/**
@@ -80,11 +81,11 @@ public class CustomCommentTaskCmd extends AbstractCommand implements Seria
@Override
public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
- CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoryService historyService = processEngineConfiguration.getHistoryService();
HistoricProcessInstance processInstance =
- historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
+ historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (Objects.isNull(processInstance)) {
throw new WorkflowEngineException(TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS, processInstanceId);
}
@@ -103,32 +104,34 @@ public class CustomCommentTaskCmd extends AbstractCommand implements Seria
task.setTaskDefinitionKey(NODE_COMMENT.getType());
task.setPriority(DEFAULT_PRIORITY);
task.setCreateTime(new Date());
- // 创建临时节点
- taskService.saveTask(task);
+
// 处理该评论节点的评论人
buildAndInsertHistoryVariable(task, processInstance, processEngineConfiguration);
CommandContextUtil.getEntityCache().findInCache(HistoricTaskInstanceEntity.class).stream()
- .filter(i -> Objects.equals(i.getId(), task.getId())).findAny()
- .ifPresent(i -> i.setAssignee(operator.buildAssigneeId()));
- // 完成临时节点
- taskService.complete(task.getId());
+ .filter(i -> Objects.equals(i.getId(), task.getId())).findAny()
+ .ifPresent(i -> i.setAssignee(operator.buildAssigneeId()));
+
+ createExtTaskInst(extAxHiTaskInstService, processInstanceId,
+ task.getId(), task.getTaskDefinitionKey(), operator, COMMENTED.getStatus());
+ task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), COMMENTED.getStatus());
+
+ // 保存临时节点
+ taskService.saveTask(task);
// 新增评论
Authentication.setAuthenticatedUserId(operator.buildAssigneeId());
- if (StringUtils.hasText(comment)) {
- CustomTaskHelper.addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment);
- }
- if (Objects.nonNull(commentExt)) {
- CustomTaskHelper.addComment(commandContext, task, COMMENT_TYPE_COMMENT_EXT, JSONUtil.toJsonStr(commentExt));
- }
+ addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment);
+ addComment(commandContext, task, COMMENT_TYPE_COMMENT_EXT, JSONUtil.toJsonStr(commentExt));
Authentication.setAuthenticatedUserId(null);
-
// 处理附件
- CustomTaskHelper.batchAddAttachment(commandContext, processInstanceId, task.getId(), attachmentList, operator);
+ batchAddAttachment(commandContext, processInstanceId, task, attachmentList, operator);
- CustomTaskHelper.createExtTaskInst(extAxHiTaskInstService, processInstanceId,
- task.getId(), task.getTaskDefinitionKey(), operator, COMMENTED.getStatus());
+ // 设置快照信息
+ task.setTransientVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + task.getId(), operator.toJson());
+
+ // 完成临时节点
+ taskService.complete(task.getId());
return null;
}
@@ -136,9 +139,9 @@ public class CustomCommentTaskCmd extends AbstractCommand implements Seria
HistoricProcessInstance processInstance,
ProcessEngineConfigurationImpl processEngineConfiguration) {
HistoricVariableService historicVariableService =
- processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService();
+ processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService();
HistoricVariableInstanceEntity historicVariableInstance =
- historicVariableService.createHistoricVariableInstance();
+ historicVariableService.createHistoricVariableInstance();
historicVariableInstance.setTaskId(task.getId());
historicVariableInstance.setExecutionId(task.getExecutionId());
historicVariableInstance.setProcessInstanceId(processInstance.getId());
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCompleteDummyTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCompleteDummyTaskCmd.java
index a00361cbf..d193f80cb 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCompleteDummyTaskCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCompleteDummyTaskCmd.java
@@ -7,12 +7,12 @@ import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.identity.Authentication;
-import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.api.Task;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -65,7 +65,7 @@ public class CustomCompleteDummyTaskCmd extends AbstractCommand implements
CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
- Task task = taskService.createTaskQuery().processInstanceId(processInstanceId)
+ TaskEntity task = (TaskEntity) taskService.createTaskQuery().processInstanceId(processInstanceId)
.taskId(taskId).singleResult();
if (Objects.isNull(task)) {
throw new WorkflowEngineException(DUMMY_TASK_NOT_EXISTS, processInstanceId, taskId);
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java
index 7d5f7c2bb..4d5396dd9 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java
@@ -4,6 +4,7 @@ import cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
+import cn.axzo.workflow.core.engine.model.AddComment;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
@@ -23,12 +24,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.COUNTERSIGN_ASSIGNER_SHOW_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COUNTERSIGN;
-import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.completeVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount;
@@ -81,24 +81,24 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand implemen
@Override
public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
- CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoricTaskInstanceQuery taskQuery =
- processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
+ processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(originTaskId).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService();
- Task task = taskService.createTaskQuery().taskId(originTaskId).singleResult();
+ TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(originTaskId).singleResult();
- validTask(historicTaskInstance, (TaskEntity) task, originTaskAssignee, null);
+ validTask(historicTaskInstance, task, originTaskAssignee, null);
- validTaskAssignerCount(processEngineConfiguration.getRuntimeService(), (TaskEntity) task, targetTaskAssigneeList);
+ validTaskAssignerCount(processEngineConfiguration.getRuntimeService(), task, targetTaskAssigneeList);
List taskDelegateAssigners =
- validTaskAssignerDuplicated(commandContext, (TaskEntity) task, targetTaskAssigneeList);
+ validTaskAssignerDuplicated(commandContext, task, targetTaskAssigneeList);
resolveOriginTask(commandContext, extAxHiTaskInstService, taskService, task);
- batchAddAttachment(commandContext, task.getProcessInstanceId(), task.getId(), attachmentList,
+ batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList,
originTaskAssignee);
switch (countersignType) {
@@ -110,7 +110,7 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand implemen
break;
default:
// share_counterSign
- shareCountSign(commandContext, (TaskEntity) task, taskDelegateAssigners);
+ shareCountSign(commandContext, task, taskDelegateAssigners);
break;
}
@@ -127,11 +127,11 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand implemen
private void shareCountSign(CommandContext commandContext, TaskEntity taskEntity,
List taskDelegateAssigners) {
ProcessEngineConfigurationImpl processEngineConfiguration =
- CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
// 这个节点下所有审批人快照
String activityListSnapshot =
- INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey();
+ INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey();
taskDelegateAssigners.addAll(targetTaskAssigneeList);
runtimeService.setVariable(taskEntity.getProcessInstanceId(), activityListSnapshot, taskDelegateAssigners);
log.info("正在进行加签任务:{},待加签人合并列表:{}", taskEntity.getId(), JSONUtil.toJsonStr(taskDelegateAssigners));
@@ -142,22 +142,22 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand implemen
}
private void resolveOriginTask(CommandContext commandContext, ExtAxHiTaskInstService extAxHiTaskInstService,
- TaskService taskService, Task task) {
+ TaskService taskService, TaskEntity task) {
// 构建评论内容
StringBuilder message = new StringBuilder("添加");
int end = Math.min(targetTaskAssigneeList.size(), COUNTERSIGN_ASSIGNER_SHOW_NUMBER);
//加签人员数量显示指定个数
for (int i = 0; i < end; i++) {
message.append(targetTaskAssigneeList.get(i).getAssignerName());
- if (i < targetTaskAssigneeList.size() - 1) {
+ if (i < end - 1) {
message.append("、");
}
}
message.append("等").append(targetTaskAssigneeList.size()).append("人进行审批");
Task virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(),
- task.getTaskDefinitionKey(), advice, originTaskAssignee, COUNTERSIGN.getStatus());
- addComment(commandContext, virtualTask, COMMENT_TYPE_OPERATION_DESC, message.toString());
- batchAddAttachment(commandContext, task.getProcessInstanceId(), task.getId(), attachmentList, originTaskAssignee);
+ task.getTaskDefinitionKey(), advice, originTaskAssignee, COUNTERSIGN.getStatus(), new AddComment(message.toString()));
+ batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList, originTaskAssignee);
+ completeVirtualTask(commandContext, virtualTask);
}
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java
index 259d69036..f73f1ccff 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java
@@ -4,6 +4,7 @@ import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import cn.axzo.workflow.core.engine.model.AddComment;
import cn.axzo.workflow.core.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON;
@@ -23,7 +24,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
-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;
@@ -31,9 +31,10 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_N
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_DELETE_REASON;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_REJECT;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
-import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
+import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.completeVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
@@ -69,7 +70,7 @@ public class CustomRejectionTaskCmd extends AbstractCommand implements Ser
if (Objects.nonNull(operationDesc)) {
this.operationDesc = operationDesc;
} else {
- this.operationDesc = "已驳回";
+ this.operationDesc = "(已驳回)";
}
this.attachmentList = dto.getAttachmentList();
this.approver = dto.getApprover();
@@ -97,17 +98,18 @@ public class CustomRejectionTaskCmd extends AbstractCommand implements Ser
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(taskId).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService();
- Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
+ TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
- validTask(historicTaskInstance, (TaskEntity) task, approver, nodeTypes);
+ validTask(historicTaskInstance, task, approver, nodeTypes);
- ((TaskEntity) task).setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), REJECTED.getStatus());
- Task virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(),
- task.getTaskDefinitionKey(), advice, Objects.equals(operationDesc, "自动驳回") ? null : approver, REJECTED.getStatus());
+ task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), DELETED.getStatus());
+ TaskEntity virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(),
+ task.getTaskDefinitionKey(), advice,
+ Objects.equals(operationDesc, "自动驳回") ? null : approver, REJECTED.getStatus(),
+ new AddComment(operationDesc));
- addComment(commandContext, virtualTask, COMMENT_TYPE_OPERATION_DESC, operationDesc);
-
- batchAddAttachment(commandContext, task.getProcessInstanceId(), virtualTask.getId(), attachmentList, approver);
+ batchAddAttachment(commandContext, task.getProcessInstanceId(), virtualTask, attachmentList, approver);
+ completeVirtualTask(commandContext, virtualTask);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
finishProcessInstance(commandContext, runtimeService, task, advice);
@@ -125,7 +127,7 @@ public class CustomRejectionTaskCmd extends AbstractCommand implements Ser
runtimeService.setVariables(task.getProcessInstanceId(), variables);
CommandContextUtil.getAgenda(commandContext)
.planOperation(new DeleteProcessInstanceOperation(commandContext, task.getProcessInstanceId(),
- extAxHiTaskInstService));
+ extAxHiTaskInstService, REJECTED));
}
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTermNodePausingAlertCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTermNodePausingAlertCmd.java
new file mode 100644
index 000000000..3b6a2262a
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTermNodePausingAlertCmd.java
@@ -0,0 +1,28 @@
+package cn.axzo.workflow.core.engine.cmd;
+
+import org.flowable.bpmn.model.TimerEventDefinition;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+
+import java.io.Serializable;
+
+/**
+ * 自定义的节点长时间卡住的告警
+ *
+ * @author wangli
+ * @since 2024-09-11 13:44
+ */
+public class CustomTermNodePausingAlertCmd extends AbstractCommand implements Serializable {
+ @Override
+ public String paramToJsonString() {
+ return "";
+ }
+
+ @Override
+ public Void executeInternal(CommandContext commandContext) {
+
+
+ return null;
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java
index 5cf121307..770238200 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java
@@ -6,7 +6,6 @@ import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import org.flowable.common.engine.impl.identity.Authentication;
-import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
@@ -16,7 +15,6 @@ import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
-import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.util.HashMap;
@@ -77,52 +75,54 @@ public class CustomTransferUserTaskCmd extends AbstractCommand implements
@Override
public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
- CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoricTaskInstanceQuery taskQuery =
- processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
+ processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(originTaskId).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService();
- Task task = taskService.createTaskQuery().taskId(originTaskId).singleResult();
+ TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(originTaskId).singleResult();
- validTask(historicTaskInstance, (TaskEntity) task, originTaskAssignee, null);
+ validTask(historicTaskInstance, task, originTaskAssignee, null);
- validTaskAssignerDuplicated(commandContext, (TaskEntity) task, Lists.newArrayList(targetTaskAssignee));
+ validTaskAssignerDuplicated(commandContext, task, Lists.newArrayList(targetTaskAssignee));
+ // 修改节点对应的审批人集合快照信息
processAssignee(processEngineConfiguration, task);
+ // 对被转交的任务进行建议和附件的处理
resolveOriginTask(commandContext, taskService, task);
+ batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList,
+ originTaskAssignee);
- batchAddAttachment(commandContext, task.getProcessInstanceId(), task.getId(), attachmentList,
- originTaskAssignee);
-
- addMultiTask(commandContext, (TaskEntity) task, targetTaskAssignee);
- ((TaskEntity) task).setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), TRANSFER.getStatus());
- deleteMultiTask(commandContext, (TaskEntity) task);
+ // 生成转交任务
+ addMultiTask(commandContext, task, targetTaskAssignee);
+ task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), TRANSFER.getStatus());
+ // 结束被转交任务
+ deleteMultiTask(commandContext, task);
return null;
}
- private void resolveOriginTask(CommandContext commandContext, TaskService taskService, Task task) {
+ private void resolveOriginTask(CommandContext commandContext, TaskService taskService, TaskEntity task) {
BpmnTaskDelegateAssigner assigner = buildDummyAssigner("transfer", TASK_ASSIGNEE_SKIP_FLAT, "dummyApprover");
task.setAssignee(assigner.buildAssigneeId());
- ((TaskEntity) task).setScopeType("TRANSFER");
- taskService.saveTask(task);
+ task.setScopeType("TRANSFER");
Authentication.setAuthenticatedUserId(originTaskAssignee.buildAssigneeId());
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "转交给" + targetTaskAssignee.getAssignerName());
- if (StringUtils.hasLength(advice)) {
- addComment(commandContext, task, COMMENT_TYPE_ADVICE, advice);
- }
+ addComment(commandContext, task, COMMENT_TYPE_ADVICE, advice);
Authentication.setAuthenticatedUserId(null);
+
+ taskService.saveTask(task);
}
public void processAssignee(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) {
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(),
- INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class);
+ INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class);
Optional exists = originAssingeeList.stream()
- .filter(i -> Objects.equals(i.buildAssigneeId(), targetTaskAssignee.buildAssigneeId())).findAny();
+ .filter(i -> Objects.equals(i.buildAssigneeId(), targetTaskAssignee.buildAssigneeId())).findAny();
if (exists.isPresent()) {
throw new WorkflowEngineException(ASSIGNEE_HAS_BEEN_EXISTS);
}
@@ -135,8 +135,8 @@ public class CustomTransferUserTaskCmd extends AbstractCommand implements
}
originAssingeeList.add(targetTaskAssignee);
runtimeService.setVariable(task.getProcessInstanceId(),
- INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
- originAssingeeList);
+ INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
+ originAssingeeList);
}
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/helper/CustomTaskHelper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/helper/CustomTaskHelper.java
index b9a134431..5cb212b31 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/helper/CustomTaskHelper.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/helper/CustomTaskHelper.java
@@ -6,6 +6,7 @@ import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
+import cn.axzo.workflow.core.engine.model.AddComment;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter;
@@ -27,7 +28,6 @@ import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.task.Attachment;
-import org.flowable.engine.task.Comment;
import org.flowable.engine.task.Event;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
@@ -55,6 +55,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_R
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
+import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ATTACHMENTS_VAR_NAME;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
@@ -63,6 +64,7 @@ import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ASSIGNER_NUMBER
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_HAS_BEEN_COMPLETE;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_TYPE_MISMATCH;
import static org.flowable.task.api.Task.DEFAULT_PRIORITY;
/**
@@ -154,7 +156,7 @@ public class CustomTaskHelper {
//不包含对应的任务
if (!nodeTypes.contains(nodeType)) {
// log.warn(TASK_TYPE_MISMATCH.getMessage(), nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
- throw new WorkflowEngineException(ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT, nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
+ throw new WorkflowEngineException(TASK_TYPE_MISMATCH, nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
}
}
}
@@ -219,10 +221,10 @@ public class CustomTaskHelper {
*
* @param commandContext
* @param processInstanceId
- * @param taskId
+ * @param task
* @param attachmentList
*/
- public static void batchAddAttachment(CommandContext commandContext, String processInstanceId, String taskId,
+ public static void batchAddAttachment(CommandContext commandContext, String processInstanceId, TaskEntity task,
List attachmentList, BpmnTaskDelegateAssigner assigner) {
if (CollectionUtils.isEmpty(attachmentList)) {
return;
@@ -232,22 +234,30 @@ public class CustomTaskHelper {
TaskService taskService = processEngineConfiguration.getTaskService();
Authentication.setAuthenticatedUserId(assigner.buildAssigneeId());
attachmentList.forEach(dto -> {
- Attachment attachment = taskService.createAttachment(dto.getType().getType(), taskId, processInstanceId,
+ Attachment attachment = taskService.createAttachment(dto.getType().getType(), task.getId(), processInstanceId,
dto.getName(), dto.getDescription(), dto.getUrl());
taskService.saveAttachment(attachment);
});
+ task.setTransientVariableLocal(TASK_ATTACHMENTS_VAR_NAME, attachmentList);
Authentication.setAuthenticatedUserId(null);
}
- public static Comment addComment(CommandContext commandContext, String taskId, String processInstanceId,
- String type, String message) {
+ public static void addComment(CommandContext commandContext, String taskId, String processInstanceId,
+ String type, String message) {
TaskEntity task = new TaskEntityImpl();
task.setId(taskId);
task.setProcessInstanceId(processInstanceId);
- return addComment(commandContext, task, type, message);
+ addComment(commandContext, task, type, message);
}
- public static Comment addComment(CommandContext commandContext, Task task, String type, String message) {
+ public static void addComment(CommandContext commandContext, TaskEntity task, AddComment addComment) {
+ addComment(commandContext, task, addComment.getCommentType(), addComment.getContent());
+ }
+
+ public static void addComment(CommandContext commandContext, TaskEntity task, String type, String message) {
+ if (!StringUtils.hasText(type) || !StringUtils.hasText(message)) {
+ return;
+ }
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
@@ -269,8 +279,7 @@ public class CustomTaskHelper {
comment.setFullMessage(message);
processEngineConfiguration.getCommentEntityManager().insert(comment);
-
- return comment;
+ task.setTransientVariableLocal(type, message);
}
public static Attachment addAttachment(CommandContext commandContext, Task task, AttachmentDTO attachmentDto) {
@@ -313,10 +322,10 @@ public class CustomTaskHelper {
* @param extTaskInstStatus 节点状态
* @return
*/
- public static Task createVirtualTask(CommandContext commandContext, ExtAxHiTaskInstService extAxHiTaskInstService
+ public static TaskEntity createVirtualTask(CommandContext commandContext, ExtAxHiTaskInstService extAxHiTaskInstService
, String processInstanceId, String nodeName, String taskDefinitionKey, String advice,
BpmnTaskDelegateAssigner assigner,
- String extTaskInstStatus) {
+ String extTaskInstStatus, AddComment addComment) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoryService historyService = processEngineConfiguration.getHistoryService();
@@ -336,8 +345,6 @@ public class CustomTaskHelper {
task.setTaskDefinitionKey(taskDefinitionKey);
task.setPriority(DEFAULT_PRIORITY);
task.setCreateTime(new Date());
- // 创建临时节点
- taskService.saveTask(task);
if (Objects.nonNull(assigner)) {
CommandContextUtil.getEntityCache().findInCache(TaskEntity.class).stream()
@@ -351,23 +358,36 @@ public class CustomTaskHelper {
// 添加审批意见
addAdvice(commandContext, task, advice, Objects.nonNull(assigner) ? assigner.buildAssigneeId() : null);
+ // 添加操作描述
+ addComment(commandContext, task, addComment);
CustomTaskHelper.createExtTaskInst(extAxHiTaskInstService, task.getProcessInstanceId(), task.getId(),
task.getTaskDefinitionKey(), assigner, extTaskInstStatus);
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), extTaskInstStatus);
+ // 保存任务
+ taskService.saveTask(task);
+
if (Objects.nonNull(assigner)) {
// 设置快照信息
task.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + task.getId(), assigner.toJson());
}
- // 完成临时节点
- taskService.complete(task.getId());
+ // 完成临时节点, 1.4.2虚拟节点创建方法不再默认完成,需主动调用 completeVirtualTask 完成
+ // taskService.complete(task.getId());
return task;
}
- private static void addAdvice(CommandContext commandContext, Task task, String comment, String userId) {
+ public static Task completeVirtualTask(CommandContext commandContext, Task task) {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ TaskService taskService = processEngineConfiguration.getTaskService();
+ taskService.complete(task.getId());
+ return task;
+ }
+
+ private static void addAdvice(CommandContext commandContext, TaskEntity task, String comment, String userId) {
if (StringUtils.hasLength(comment)) {
Authentication.setAuthenticatedUserId(userId);
addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment);
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java
index 04671e6f4..1c5d4e989 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java
@@ -10,6 +10,8 @@ import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandConfig;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
+import java.util.Objects;
+
import static cn.axzo.workflow.core.common.code.FlowableEngineRespCode.ENGINE_ASYNC_COMMAND_EXECUTION_RETRY_GIVE_UP;
/**
@@ -29,6 +31,7 @@ public class CustomRetryInterceptor extends AbstractCommandInterceptor {
public T execute(CommandConfig config, Command command, CommandExecutor commandExecutor) {
long waitTime = waitTimeInMs;
int failedAttempts = 0;
+ Throwable lastException = null;
do {
if (failedAttempts > 0) {
log.warn("Waiting for {}ms before retrying the command.", waitTime);
@@ -38,9 +41,6 @@ public class CustomRetryInterceptor extends AbstractCommandInterceptor {
try {
// try to execute the command
- if (log.isDebugEnabled()) {
- log.debug("assignableFrom result: {}", AbstractCommand.class.isAssignableFrom(command.getClass()));
- }
if (AbstractCommand.class.isAssignableFrom(command.getClass())) {
// 如果在以后,重试三次也不能解决的话, 可以利用这里的拿到的参数,重新自动构造CMD,并执行.
log.info("Executing command params: {} traceId:{} ", TraceUtil.traceId(),
@@ -50,12 +50,13 @@ public class CustomRetryInterceptor extends AbstractCommandInterceptor {
} catch (PersistenceException e) {
log.warn("Caught persistence exception: {}", e.getMessage(), e);
+ lastException = e;
}
failedAttempts++;
} while (failedAttempts <= numOfRetries);
- throw new WorkflowEngineException(ENGINE_ASYNC_COMMAND_EXECUTION_RETRY_GIVE_UP, String.valueOf(numOfRetries));
+ throw new WorkflowEngineException(ENGINE_ASYNC_COMMAND_EXECUTION_RETRY_GIVE_UP, String.valueOf(numOfRetries), lastException.getMessage());
}
protected void waitBeforeRetry(long waitTime) {
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceJobHandler.java
similarity index 88%
rename from workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceHandler.java
rename to workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceJobHandler.java
index 27b269614..979f305fd 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceHandler.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceJobHandler.java
@@ -13,11 +13,11 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
@Slf4j
-public class AsyncAbortProcessInstanceHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
+public class AsyncAbortProcessInstanceJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
public static final String TYPE = "async-abort-instance";
private final ExtAxHiTaskInstService extAxHiTaskInstService;
- public AsyncAbortProcessInstanceHandler(ExtAxHiTaskInstService extAxHiTaskInstService) {
+ public AsyncAbortProcessInstanceJobHandler(ExtAxHiTaskInstService extAxHiTaskInstService) {
this.extAxHiTaskInstService = extAxHiTaskInstService;
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivityLeaveJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivityLeaveJobHandler.java
index 399513265..ea5f658eb 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivityLeaveJobHandler.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivityLeaveJobHandler.java
@@ -1,6 +1,7 @@
package cn.axzo.workflow.core.engine.job;
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO;
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
import cn.axzo.workflow.core.service.BpmnProcessActivityService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
@@ -33,6 +34,6 @@ public class AsyncActivityLeaveJobHandler extends AbstractJobHandler implements
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.warn("AsyncActivityLeaveJobHandler exec start...");
BpmnActivityTimeoutTriggerDTO dto = JSON.parseObject(job.getCustomValues(), BpmnActivityTimeoutTriggerDTO.class);
- bpmnProcessActivityService.trigger(dto.getTriggerId());
+ bpmnProcessActivityService.trigger(BpmnActivityTriggerDTO.builder().async(false).triggerId(dto.getTriggerId()).build());
}
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivitySetAssigneeJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivitySetAssigneeJobHandler.java
new file mode 100644
index 000000000..13b91524e
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivitySetAssigneeJobHandler.java
@@ -0,0 +1,37 @@
+package cn.axzo.workflow.core.engine.job;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO;
+import cn.axzo.workflow.core.engine.cmd.CustomBizSpecifyAssigneeToTaskCmd;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.job.service.JobHandler;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.variable.api.delegate.VariableScope;
+
+/**
+ * 异步处理业务节点设置审批人的处理器
+ *
+ * @author wangli
+ * @since 2024-09-09 14:54
+ */
+@Slf4j
+public class AsyncActivitySetAssigneeJobHandler extends AbstractJobHandler implements JobHandler {
+ public static String TYPE = "async-activity-set-assignee";
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ @Override
+ public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
+ log.info("AsyncActivitySetAssigneeJobHandler executing...");
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ BpmnActivitySetAssigneeDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnActivitySetAssigneeDTO.class);
+ processEngineConfiguration.getCommandExecutor().execute(new CustomBizSpecifyAssigneeToTaskCmd(dto.getTriggerId(), dto.getAssigners()));
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivityTriggerJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivityTriggerJobHandler.java
new file mode 100644
index 000000000..c59fe7897
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncActivityTriggerJobHandler.java
@@ -0,0 +1,37 @@
+package cn.axzo.workflow.core.engine.job;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
+import cn.axzo.workflow.core.engine.cmd.CustomActivityTriggerCmd;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.job.service.JobHandler;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.variable.api.delegate.VariableScope;
+
+/**
+ * 异步处理业务节点触发的处理器
+ *
+ * @author wangli
+ * @since 2024-09-09 14:36
+ */
+@Slf4j
+public class AsyncActivityTriggerJobHandler extends AbstractJobHandler implements JobHandler {
+ public static final String TYPE = "async-activity-trigger";
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ @Override
+ public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
+ log.info("AsyncActivityTriggerJobHandler executing...");
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ BpmnActivityTriggerDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnActivityTriggerDTO.class);
+ processEngineConfiguration.getCommandExecutor().execute(new CustomActivityTriggerCmd(dto));
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBackTaskJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBackTaskJobHandler.java
new file mode 100644
index 000000000..ec466959c
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBackTaskJobHandler.java
@@ -0,0 +1,41 @@
+package cn.axzo.workflow.core.engine.job;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
+import cn.axzo.workflow.core.engine.cmd.CustomBackTaskCmd;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.job.service.JobHandler;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.task.api.Task;
+import org.flowable.variable.api.delegate.VariableScope;
+
+import java.util.Objects;
+
+@Slf4j
+public class AsyncBackTaskJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
+
+ public static final String TYPE = "async-back-task";
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ @Override
+ public void executeInternal(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
+ log.info("AsyncBackTaskJobHandler executing...");
+ log(job);
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ BpmnTaskBackAuditDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnTaskBackAuditDTO.class);
+ Task task = processEngineConfiguration.getTaskService().createTaskQuery().taskId(dto.getTaskId()).singleResult();
+ if (Objects.isNull(task)) {
+ return;
+ }
+ processEngineConfiguration.getCommandExecutor().execute(new CustomBackTaskCmd(dto));
+ }
+
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBpmnProcessActivityJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBpmnProcessActivityJobHandler.java
deleted file mode 100644
index fffdbb6ba..000000000
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBpmnProcessActivityJobHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package cn.axzo.workflow.core.engine.job;
-
-import cn.axzo.workflow.core.service.BpmnProcessActivityService;
-import lombok.extern.slf4j.Slf4j;
-import org.flowable.common.engine.impl.interceptor.CommandContext;
-import org.flowable.job.service.JobHandler;
-import org.flowable.job.service.impl.persistence.entity.JobEntity;
-import org.flowable.variable.api.delegate.VariableScope;
-
-@Slf4j
-public class AsyncBpmnProcessActivityJobHandler extends AbstractJobHandler implements JobHandler {
-
- public static final String TYPE = "async-bpmn-process-activity";
-
- private final BpmnProcessActivityService activityService;
-
- public AsyncBpmnProcessActivityJobHandler(BpmnProcessActivityService activityService) {
- this.activityService = activityService;
- }
-
- @Override
- public String getType() {
- return TYPE;
- }
-
- @Override
- public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
- log(job);
- activityService.executeAsyncJob(job);
- }
-}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceJobHandler.java
similarity index 89%
rename from workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceHandler.java
rename to workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceJobHandler.java
index 1557e3b60..6407d79ad 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceHandler.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceJobHandler.java
@@ -13,13 +13,13 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
@Slf4j
-public class AsyncCancelProcessInstanceHandler extends AbstractJobHandler implements JobHandler {
+public class AsyncCancelProcessInstanceJobHandler extends AbstractJobHandler implements JobHandler {
public static final String TYPE = "async-cancel-process";
private final ExtAxHiTaskInstService extAxHiTaskInstService;
- public AsyncCancelProcessInstanceHandler(ExtAxHiTaskInstService extAxHiTaskInstService) {
+ public AsyncCancelProcessInstanceJobHandler(ExtAxHiTaskInstService extAxHiTaskInstService) {
this.extAxHiTaskInstService = extAxHiTaskInstService;
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncTermNodeAlterJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncTermNodeAlterJobHandler.java
new file mode 100644
index 000000000..897d855e4
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncTermNodeAlterJobHandler.java
@@ -0,0 +1,77 @@
+package cn.axzo.workflow.core.engine.job;
+
+import cn.axzo.workflow.common.model.dto.AlterDTO;
+import cn.axzo.workflow.common.model.dto.TermNodePausingDTO;
+import cn.axzo.workflow.core.common.utils.SpringContextUtils;
+import cn.axzo.workflow.core.conf.SupportRefreshProperties;
+import cn.axzo.workflow.core.listener.Alter;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.job.service.JobHandler;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.task.api.Task;
+import org.flowable.variable.api.delegate.VariableScope;
+
+import java.util.Objects;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_NODE_ALTER;
+
+/**
+ * 检查指定节点是否长时间卡住,如果卡住则进行钉钉告警
+ *
+ * @author wangli
+ * @since 2024-09-11 13:50
+ */
+@Slf4j
+public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements JobHandler {
+ public static final String TYPE = "term-node-alter-cycle";
+ private final SupportRefreshProperties refreshProperties;
+
+ public AsyncTermNodeAlterJobHandler(SupportRefreshProperties refreshProperties) {
+ this.refreshProperties = refreshProperties;
+ }
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ @Override
+ public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
+ log.warn("AsyncActivityLeaveJobHandler exec start...");
+ ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
+ JSONObject jsonObject = JSON.parseObject(job.getJobHandlerConfiguration());
+ if (!jsonObject.containsKey("activityId")) {
+ return;
+ }
+ String activityId = jsonObject.getString("activityId");
+ RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ TermNodePausingDTO dto = runtimeService.getVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, TermNodePausingDTO.class);
+ TaskService taskService = processEngineConfiguration.getTaskService();
+ Task task = taskService.createTaskQuery()
+ .processInstanceId(dto.getProcessInstanceId())
+ .taskDefinitionKey(dto.getActivityId())
+ .singleResult();
+ if (Objects.isNull(task)) {
+ return;
+ }
+ if (DateUtil.compare(DateUtil.date(), DateUtil.offsetMinute(task.getCreateTime(), refreshProperties.getPauseDelay())) > 0) {
+ // 发送告警对象
+ Alter alter = SpringContextUtils.getBean(Alter.class);
+ AlterDTO alterDTO = new AlterDTO();
+ alterDTO.setProcessInstanceId(dto.getProcessInstanceId());
+ alterDTO.setActivityId(dto.getActivityId());
+ alterDTO.setTaskId(task.getId());
+ alterDTO.setStartTime(task.getCreateTime());
+ alterDTO.setPrettyStartTime(DateUtil.formatDateTime(task.getCreateTime()));
+ alter.invoke(alterDTO);
+ }
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java
index b1a66b45b..4a8e8ff8b 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java
@@ -4,12 +4,18 @@ import cn.axzo.workflow.common.constant.BpmnConstants;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+import cn.axzo.workflow.common.model.dto.CooperationOrgDTO;
+import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
+import cn.axzo.workflow.core.conf.SupportRefreshProperties;
import cn.axzo.workflow.core.deletage.BpmnTaskAssigneeSelector;
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.service.ExtAxHiTaskInstService;
+import cn.axzo.workflow.core.util.DingTalkUtils;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
@@ -20,6 +26,7 @@ import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
+import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
@@ -38,6 +45,7 @@ import java.util.Objects;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ASSIGNER_LIMIT_NUMBER;
+import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_ORG_RELATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_ALLOW_SKIP_USER_TASK;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_TYPE;
@@ -68,22 +76,17 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.removeDup
@Slf4j
public class EngineExecutionStartListener implements ExecutionListener {
private static final long serialVersionUID = 1L;
+ @Value("${spring.profiles.active}")
+ private String profile;
@Resource
@Deprecated
private ObjectProvider bpmTaskDelegate;
@Resource
@Lazy
private List selectors;
- @Value("${workflow.api.timeout:10}")
- private Long apiTimeout;
- @Value("${workflow.mock:false}")
- private Boolean mock;
- @Value("${workflow.assignee.global:true}")
- private Boolean global;
- @Value("${workflow.assignee.category:''}")
- private String category;
- @Value("#{${workflow.assignee.map:{}}}")
- private Map assigneeMap;
+ @Resource
+ private SupportRefreshProperties refreshProperties;
+
@Override
public void notify(DelegateExecution execution) {
@@ -180,6 +183,7 @@ public class EngineExecutionStartListener implements ExecutionListener {
}
log.info("当前节点id: [{}], name: [{}] 审批人为空, 将执行审批人为空的兜底配置!", userTask.getId(), userTask.getName());
getApproverEmptyHandleType(userTask).ifPresent(type -> {
+ log.info("节点兜底的配置模式:[{}]", type.getType());
switch (type) {
case autoPassed:
case autoRejection:
@@ -189,19 +193,32 @@ public class EngineExecutionStartListener implements ExecutionListener {
case transferToAdmin:
assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.transferToAdmin.getType(), userTask,
execution, true));
+ finalEmptyAssigneeHandle(assigners, userTask, execution, "转交管理员失败,系统中止");
break;
case specifyAssignee:
- List emptyAssignees =
- BpmnMetaParserHelper.getEmptyApproverSpecify(userTask)
- .map(listStr -> JSON.parseArray(listStr, BpmnTaskDelegateAssigner.class))
- .orElse(Collections.emptyList());
- assigners.addAll(emptyAssignees);
+ assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.specifyAssignee.getType(), userTask,
+ execution, true));
+ finalEmptyAssigneeHandle(assigners, userTask, execution, "转交指定人员失败,系统自动中止");
default:
break;
}
+
});
}
+ private void finalEmptyAssigneeHandle(List assigners, UserTask userTask, DelegateExecution execution, String operationDesc) {
+ if (CollectionUtils.isEmpty(assigners)) {
+ CooperationOrgDTO orgScopes = execution.getVariable(BIZ_ORG_RELATION, CooperationOrgDTO.class);
+ DingTalkUtils.sendDingTalkForTransferToAdminError(profile, execution.getProcessInstanceId(), userTask.getId(), orgScopes);
+ BpmnProcessInstanceAbortDTO abortDTO = new BpmnProcessInstanceAbortDTO();
+ abortDTO.setProcessInstanceId(execution.getProcessInstanceId());
+ abortDTO.setReason("转交管理员失败,系统中止");
+ CommandContextUtil.getProcessEngineConfiguration().getCommandExecutor()
+ .execute(new CustomAbortProcessInstanceAsyncCmd(abortDTO));
+ }
+ }
+
+
/**
* 根据审批人指定类型查询审批人
*
@@ -235,6 +252,10 @@ public class EngineExecutionStartListener implements ExecutionListener {
* \"personId\":\"89508\",\"assignee\":\"2000560\",\"assigneeType\":\"3\"}]'
* }"
*/
+ Boolean mock = refreshProperties.getMock();
+ Boolean global = refreshProperties.getGlobal();
+ String category = refreshProperties.getCategory();
+ Map assigneeMap = refreshProperties.getAssigneeMap();
if ((mock && global) ||
(mock && !global && Objects.equals(category, execution.getProcessDefinitionId().split(":")[0]))) {
log.info("当前系统 Nacos 配置中开启了 mock: {}, 将使用 mock 方式查找审批人", mock);
@@ -308,7 +329,4 @@ public class EngineExecutionStartListener implements ExecutionListener {
});
}
- public Long getApiTimeout() {
- return apiTimeout;
- }
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/EngineEntityEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/EngineEntityEventListener.java
new file mode 100644
index 000000000..7dd04b9e3
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/EngineEntityEventListener.java
@@ -0,0 +1,115 @@
+package cn.axzo.workflow.core.engine.listener.entity;
+
+import com.google.common.collect.ImmutableSet;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.common.engine.api.delegate.event.AbstractFlowableEventListener;
+import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
+import org.flowable.common.engine.api.delegate.event.FlowableEntityEvent;
+import org.flowable.common.engine.api.delegate.event.FlowableEvent;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static org.flowable.common.engine.api.delegate.event.FlowableEngineEventType.ENTITY_ACTIVATED;
+import static org.flowable.common.engine.api.delegate.event.FlowableEngineEventType.ENTITY_CREATED;
+import static org.flowable.common.engine.api.delegate.event.FlowableEngineEventType.ENTITY_DELETED;
+import static org.flowable.common.engine.api.delegate.event.FlowableEngineEventType.ENTITY_INITIALIZED;
+import static org.flowable.common.engine.api.delegate.event.FlowableEngineEventType.ENTITY_SUSPENDED;
+import static org.flowable.common.engine.api.delegate.event.FlowableEngineEventType.ENTITY_UPDATED;
+
+/**
+ * TODO
+ *
+ * @author wangli
+ * @since 2024-09-02 15:34
+ */
+@Component
+@Slf4j
+@AllArgsConstructor
+public class EngineEntityEventListener extends AbstractFlowableEventListener {
+
+ private final List handles;
+ public static final Set SUPPORTED =
+ ImmutableSet.builder()
+ .add(ENTITY_CREATED)
+ .add(ENTITY_INITIALIZED)
+ .add(ENTITY_UPDATED)
+ .add(ENTITY_DELETED)
+ .add(ENTITY_SUSPENDED)
+ .add(ENTITY_ACTIVATED)
+ .build();
+
+ @Override
+ public void onEvent(FlowableEvent event) {
+ if (event instanceof FlowableEntityEvent && SUPPORTED.contains(event.getType())) {
+ FlowableEntityEvent entityEvent = (FlowableEntityEvent) event;
+// log.warn("entity event type: {}, class: {}",entityEvent.getType(), entityEvent.getEntity().getClass());
+ handles.forEach(handle -> {
+ Object entity = entityEvent.getEntity();
+ if (handle.support(entity)) {
+ Object convert = handle.convert(entity);
+ if (Objects.equals(event.getType(), ENTITY_CREATED)) {
+ handle.onCreate(convert);
+ } else if (Objects.equals(event.getType(), ENTITY_INITIALIZED)) {
+ handle.onInitialized(convert);
+ } else if (Objects.equals(event.getType(), ENTITY_UPDATED)) {
+ handle.onUpdated(convert);
+ } else if (Objects.equals(event.getType(), ENTITY_DELETED)) {
+ handle.onDeleted(convert);
+ } else if (Objects.equals(event.getType(), ENTITY_SUSPENDED)) {
+ handle.onSuspended(convert);
+ } else if (Objects.equals(event.getType(), ENTITY_ACTIVATED)) {
+ handle.onActivated(convert);
+ }
+ }
+ });
+// if (entityEvent.getEntity() instanceof TaskEntity) {
+// TaskEntity taskEntity = (TaskEntity) entityEvent.getEntity();
+// log.error("event taskId :{}, taskDefKey: {}", taskEntity.getId(), taskEntity.getTaskDefinitionKey());
+//
+// if (Objects.equals(event.getType(), ENTITY_CREATED)) {
+// onCreate(taskEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_INITIALIZED)) {
+// onInitialized(taskEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_UPDATED)) {
+// onUpdated(taskEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_DELETED)) {
+// onDeleted(taskEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_SUSPENDED)) {
+// onSuspended(taskEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_ACTIVATED)) {
+// onActivated(taskEntity);
+// }
+// } else if(entityEvent.getEntity() instanceof CommentEntity) {
+// CommentEntity commentEntity = (CommentEntity) entityEvent.getEntity();
+// log.error("event taskId :{}", commentEntity.getId());
+//
+// if (Objects.equals(event.getType(), ENTITY_CREATED)) {
+// onCreate(commentEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_INITIALIZED)) {
+// onInitialized(commentEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_UPDATED)) {
+// onUpdated(commentEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_DELETED)) {
+// onDeleted(commentEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_SUSPENDED)) {
+// onSuspended(commentEntity);
+// } else if (Objects.equals(event.getType(), ENTITY_ACTIVATED)) {
+// onActivated(commentEntity);
+// }
+// }
+
+ }
+ }
+
+
+
+
+ @Override
+ public boolean isFailOnException() {
+ return true;
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/EntityEventHandle.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/EntityEventHandle.java
new file mode 100644
index 000000000..e3ae053ff
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/EntityEventHandle.java
@@ -0,0 +1,27 @@
+package cn.axzo.workflow.core.engine.listener.entity;
+
+/**
+ * TODO
+ *
+ * @author wangli
+ * @since 2024-09-06 00:03
+ */
+public interface EntityEventHandle {
+
+ boolean support(Object entity);
+
+ T convert(Object entity);
+
+ void onCreate(T entity);
+
+ void onInitialized(T entity);
+
+ void onUpdated(T entity);
+
+ void onDeleted(T entity);
+
+ void onSuspended(T entity);
+
+ void onActivated(T entity);
+
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/CommentEntityEventHandle.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/CommentEntityEventHandle.java
new file mode 100644
index 000000000..c7e03e86d
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/CommentEntityEventHandle.java
@@ -0,0 +1,81 @@
+package cn.axzo.workflow.core.engine.listener.entity.type;
+
+import cn.axzo.workflow.core.engine.listener.entity.EntityEventHandle;
+import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
+import cn.axzo.workflow.core.service.ExtAxProcessLogService;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.impl.persistence.entity.CommentEntity;
+
+import java.util.Objects;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
+import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
+
+/**
+ * TODO
+ *
+ * @author wangli
+ * @since 2024-09-06 00:14
+ */
+@Slf4j
+//@Component
+@AllArgsConstructor
+public class CommentEntityEventHandle implements EntityEventHandle {
+ private final ExtAxProcessLogService processLogService;
+
+ @Override
+ public boolean support(Object entity) {
+ return entity instanceof CommentEntity;
+ }
+
+ @Override
+ public CommentEntity convert(Object entity) {
+ return (CommentEntity) entity;
+ }
+
+ @Override
+ public void onCreate(CommentEntity entity) {
+ log.info("comment event onCreate: {}", entity.getId());
+ ExtAxProcessLog queryLog = new ExtAxProcessLog();
+ queryLog.setProcessInstanceId(entity.getProcessInstanceId());
+ queryLog.setTaskId(entity.getId());
+ ExtAxProcessLog update = new ExtAxProcessLog();
+ if (Objects.equals(COMMENT_TYPE_ADVICE, entity.getType())) {
+ update.setAdvice(entity.getFullMessage());
+ } else if (Objects.equals(COMMENT_TYPE_OPERATION_DESC, entity.getType())) {
+ update.setOperationDesc(entity.getFullMessage());
+ }
+ processLogService.update(queryLog, update);
+ }
+
+ @Override
+ public void onInitialized(CommentEntity entity) {
+ log.info("comment event onInitialized: {}", entity.getId());
+
+ }
+
+ @Override
+ public void onUpdated(CommentEntity entity) {
+ log.info("comment event onUpdated: {}", entity.getId());
+
+ }
+
+ @Override
+ public void onDeleted(CommentEntity entity) {
+ log.info("comment event onDeleted: {}", entity.getId());
+
+ }
+
+ @Override
+ public void onSuspended(CommentEntity entity) {
+ log.info("comment event onSuspended: {}", entity.getId());
+
+ }
+
+ @Override
+ public void onActivated(CommentEntity entity) {
+ log.info("comment event onSuspended: {}", entity.getId());
+
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/TaskEntityEventHandle.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/TaskEntityEventHandle.java
new file mode 100644
index 000000000..c3984bfa9
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/TaskEntityEventHandle.java
@@ -0,0 +1,241 @@
+package cn.axzo.workflow.core.engine.listener.entity.type;
+
+import cn.axzo.framework.jackson.utility.JSON;
+import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
+import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
+import cn.axzo.workflow.core.engine.listener.entity.EntityEventHandle;
+import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
+import cn.axzo.workflow.core.service.ExtAxProcessLogService;
+import com.google.common.collect.Lists;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.ListUtils;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.engine.impl.util.ProcessDefinitionUtil;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION;
+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.HIDDEN_ASSIGNEE_ID;
+import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
+import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
+import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
+import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
+import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.nobody;
+import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.AND;
+import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.GENERAL;
+import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.OR;
+import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
+import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.PENDING;
+import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod;
+import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNodeType;
+
+/**
+ * 同意、评论、加签、转交、驳回、撤回、中止、抄送
+ *
+ * 回退
+ *
+ * @author wangli
+ * @since 2024-09-06 00:02
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class TaskEntityEventHandle implements EntityEventHandle {
+ private final ExtAxProcessLogService processLogService;
+
+ @Override
+ public boolean support(Object entity) {
+ return entity instanceof TaskEntity;
+ }
+
+ @Override
+ public TaskEntity convert(Object entity) {
+ return (TaskEntity) entity;
+ }
+
+ @Override
+ public void onActivated(TaskEntity taskEntity) {
+ log.debug("onActivated");
+ }
+
+ public void onSuspended(TaskEntity taskEntity) {
+ log.debug("onSuspended");
+ }
+
+ public void onDeleted(TaskEntity taskEntity) {
+ log.debug("onDeleted");
+ ExtAxProcessLog queryLog = new ExtAxProcessLog();
+ queryLog.setProcessInstanceId(taskEntity.getProcessInstanceId());
+ queryLog.setTaskId(taskEntity.getId());
+ queryLog.setOperationDesc(PENDING.getDesc());
+ ExtAxProcessLog update = new ExtAxProcessLog();
+
+ ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
+ RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ BpmnTaskDelegateAssigner assignee = BpmnTaskDelegateAssigner.toObjectCompatible(taskEntity.getVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + taskEntity.getId()));
+ if (Objects.nonNull(assignee) && !Objects.equals(NO_ASSIGNEE, assignee.buildAssigneeId())) {
+ update.setAssigneeFull(Lists.newArrayList(assignee));
+ update.setAssigneeId(Long.valueOf(assignee.getPersonId()));
+ update.setAssigneeTenantId(assignee.getTenantId());
+ update.setAssigneeName(assignee.getAssignerName());
+ update.setAssigneeOuId(assignee.getOuId());
+ }
+
+ boolean needDelete = false;
+ if (Objects.equals(taskEntity.getTaskDefinitionKey(), NODE_STARTER.getType())) {
+ update.setStatus(APPROVED.getStatus());
+ } else {
+
+ Object advice = taskEntity.getTransientVariableLocal(COMMENT_TYPE_ADVICE);
+ if (Objects.nonNull(advice) && StringUtils.hasText(advice.toString())) {
+ log.info("COMMENT_TYPE_ADVICE: {}", advice);
+ update.setAdvice(advice.toString());
+ }
+ Object operationDesc = taskEntity.getTransientVariableLocal(COMMENT_TYPE_OPERATION_DESC);
+ if (Objects.nonNull(operationDesc) && StringUtils.hasText(operationDesc.toString())) {
+ log.info("COMMENT_TYPE_OPERATION_DESC: {}", operationDesc);
+ update.setOperationDesc(Objects.nonNull(assignee) ? assignee.getAssignerName() + operationDesc : operationDesc.toString());
+ } else {
+ update.setOperationDesc(Objects.nonNull(assignee) ? assignee.getAssignerName() : "");
+ // 评论节点会给 operationDesc 赋 COMMENTED 的值,所以注释
+// update.setOperationDesc(BpmnProcessInstanceResultEnum.valueOfStatus(completionType).getDesc());
+ }
+
+
+ String completionType = taskEntity.getVariable(TASK_COMPLETE_OPERATION_TYPE + taskEntity.getId(), String.class);
+ if (StringUtils.hasText(completionType) && !Objects.equals(DELETED.getStatus(), completionType)) {
+ log.info("TASK_COMPLETE_OPERATION_TYPE: {}", completionType);
+ update.setStatus(completionType);
+ } else {
+ // 多实例除操作人以外的任务,直接删除日志, 例如一个节点有两个人或签,A 人驳回了,那么 B 人不再需要操作,任务自动删除。而会签也同理
+ update.setStatus(DELETED.getStatus());// delete标志着是多实例删除
+ needDelete = true;
+ }
+ }
+ update.setEndTime(new Date());
+
+ // 判断是否抄送节点,如果是的话,需要将抄送人集合放入对应字段
+ if (isCarbonCopyNode(queryLog)) {
+ // 抄送人集合
+ List carbonCopies = runtimeService.getVariable(taskEntity.getProcessInstanceId(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey(), List.class);
+ update.setAssigneeFull(carbonCopies);
+ update.setOperationDesc("抄送" + carbonCopies.size() + "人");
+ }
+
+ processLogService.update(queryLog, update);
+
+ if (needDelete) {
+ // 再逻辑删除该记录
+ ExtAxProcessLog deleteLog = new ExtAxProcessLog();
+ deleteLog.setProcessInstanceId(taskEntity.getProcessInstanceId());
+ deleteLog.setTaskId(taskEntity.getId());
+ processLogService.delete(deleteLog);
+ }
+ }
+
+ private boolean isCarbonCopyNode(ExtAxProcessLog queryLog) {
+ List logs = processLogService.genericQuery(queryLog);
+ if (CollectionUtils.isEmpty(logs) || logs.size() != 1) {
+ return false;
+ }
+ return Objects.equals(logs.get(0).getNodeType(), BpmnFlowNodeType.NODE_CARBON_COPY.getType());
+ }
+
+ public void onUpdated(TaskEntity taskEntity) {
+ log.debug("onUpdated");
+ if (Objects.equals(HIDDEN_ASSIGNEE_ID, taskEntity.getAssignee())) {
+ ExtAxProcessLog queryLog = new ExtAxProcessLog();
+ queryLog.setProcessInstanceId(taskEntity.getProcessInstanceId());
+ queryLog.setTaskId(taskEntity.getId());
+ processLogService.delete(queryLog);
+ } else {
+ ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
+ RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ List assigneeList = runtimeService.getVariable(taskEntity.getProcessInstanceId(),
+ INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey(), List.class);
+ ListUtils.emptyIfNull(assigneeList).stream().filter(e -> Objects.equals(e.buildAssigneeId(), taskEntity.getAssignee())).findAny()
+ .ifPresent(assignee -> {
+ log.debug("审批人: {}", JSON.toJSONString(assignee));
+ ExtAxProcessLog queryLog = new ExtAxProcessLog();
+ queryLog.setProcessInstanceId(taskEntity.getProcessInstanceId());
+ queryLog.setTaskId(taskEntity.getId());
+ processLogService.updateAssignee(queryLog, assignee);
+ });
+ }
+ }
+
+ public void onInitialized(TaskEntity taskEntity) {
+ log.debug("onInitialized");
+ BpmnMetaParserHelper.getButtonConfig(ProcessDefinitionUtil.getProcess(taskEntity.getProcessDefinitionId()), taskEntity.getTaskDefinitionKey())
+ .ifPresent(buttons -> {
+ ExtAxProcessLog queryLog = new ExtAxProcessLog();
+ queryLog.setProcessInstanceId(taskEntity.getProcessInstanceId());
+ queryLog.setTaskId(taskEntity.getId());
+
+ ExtAxProcessLog updateLog = new ExtAxProcessLog();
+ updateLog.setButtonConf(buttons);
+ processLogService.update(queryLog, updateLog);
+ });
+ }
+
+ public void onCreate(TaskEntity taskEntity) {
+ log.debug("onCreate");
+ ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
+ // 记录发起人
+ boolean isNodeStarter = Objects.equals(taskEntity.getTaskDefinitionKey(), NODE_STARTER.getType());
+
+ RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
+ BpmnModel bpmnModel = repositoryService.getBpmnModel(taskEntity.getProcessDefinitionId());
+ FlowElement flowElement = bpmnModel.getFlowElement(taskEntity.getTaskDefinitionKey());
+
+ ExtAxProcessLog log = new ExtAxProcessLog();
+ log.setProcessInstanceId(taskEntity.getProcessInstanceId());
+ log.setTenantId(taskEntity.getTenantId());
+ log.setActivityId(taskEntity.getTaskDefinitionKey());
+ log.setActivityName(taskEntity.getName());
+ log.setApprovalMethod((isNodeStarter ? nobody : getApprovalMethod(flowElement).orElse(nobody)).getType());
+ log.setNodeType((getNodeType(flowElement).orElse(BpmnFlowNodeType.NODE_EMPTY)).getType());
+ log.setNodeMode((isNodeStarter ? BpmnFlowNodeMode.GENERAL : getNodeMode(flowElement)).getType());
+ log.setTaskId(taskEntity.getId());
+ log.setOperationDesc(PENDING.getDesc());
+ log.setStartTime(taskEntity.getCreateTime());
+ log.setStatus(PROCESSING.getStatus());
+
+ processLogService.insert(log);
+ }
+
+ private BpmnFlowNodeMode getNodeMode(FlowElement flowElement) {
+ BpmnFlowNodeMode node = GENERAL;
+ if (flowElement instanceof UserTask) {
+ UserTask userTask = (UserTask) flowElement;
+ if (userTask.getBehavior() instanceof MultiInstanceActivityBehavior) {
+ MultiInstanceActivityBehavior behavior =
+ (MultiInstanceActivityBehavior) userTask.getBehavior();
+ node = Objects.equals(AND_SIGN_EXPRESSION, behavior.getCompletionCondition()) ? AND : OR;
+ }
+ }
+ return node;
+ }
+
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/model/AddComment.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/model/AddComment.java
new file mode 100644
index 000000000..acbce8564
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/model/AddComment.java
@@ -0,0 +1,40 @@
+package cn.axzo.workflow.core.engine.model;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
+
+/**
+ * 评论模型
+ *
+ * @author wangli
+ * @since 2024-09-05 23:18
+ */
+public class AddComment {
+ private String commentType;
+ private String content;
+
+ public AddComment(String commentType, String content) {
+ this.commentType = commentType;
+ this.content = content;
+ }
+
+ public AddComment(String content) {
+ this.commentType = COMMENT_TYPE_OPERATION_DESC;
+ this.content = content;
+ }
+
+ public String getCommentType() {
+ return commentType;
+ }
+
+ public void setCommentType(String commentType) {
+ this.commentType = commentType;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java
index df7346997..d1e14ba0b 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java
@@ -1,5 +1,6 @@
package cn.axzo.workflow.core.engine.operation;
+import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
@@ -16,6 +17,7 @@ import java.util.List;
import java.util.Objects;
import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID;
+import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_CLOSING_TYPE;
/**
* 通用的在 Command 内执行删除流程实例时的额外操作
@@ -27,21 +29,25 @@ public class DeleteProcessInstanceOperation extends AbstractOperation {
private final String processInstanceId;
private final ExtAxHiTaskInstService extAxHiTaskInstService;
private String customDeleteReason;
+ private final BpmnProcessInstanceResultEnum closingType;
public DeleteProcessInstanceOperation(CommandContext commandContext, String processInstanceId,
- ExtAxHiTaskInstService extAxHiTaskInstService) {
+ ExtAxHiTaskInstService extAxHiTaskInstService,
+ BpmnProcessInstanceResultEnum closingType) {
super(commandContext, null);
this.processInstanceId = processInstanceId;
this.extAxHiTaskInstService = extAxHiTaskInstService;
+ this.closingType = closingType;
}
public DeleteProcessInstanceOperation(CommandContext commandContext, String processInstanceId,
ExtAxHiTaskInstService extAxHiTaskInstService,
- String customDeleteReason) {
+ String customDeleteReason, BpmnProcessInstanceResultEnum closingType) {
super(commandContext, null);
this.processInstanceId = processInstanceId;
this.extAxHiTaskInstService = extAxHiTaskInstService;
this.customDeleteReason = customDeleteReason;
+ this.closingType = closingType;
}
@Override
@@ -65,6 +71,7 @@ public class DeleteProcessInstanceOperation extends AbstractOperation {
}
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ runtimeService.setVariableLocal(processInstanceId, PROCESS_CLOSING_TYPE, closingType);
runtimeService.deleteProcessInstance(processInstanceId, StringUtils.hasText(customDeleteReason) ? customDeleteReason : HIDDEN_ASSIGNEE_ID);
// 将结束流程实例的原因记录下来
// runtimeService.deleteProcessInstance(processInstanceId, reason);
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java
index 886585c98..f6f7b4259 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java
@@ -59,7 +59,7 @@ public class AutoPassTransactionListener implements TransactionListener {
pass.setTaskId(delegateTask.getId());
pass.setAdvice(advice);
pass.setApprover(assigner);
- pass.setOperationDesc("自动通过");
+ pass.setOperationDesc("(自动通过)");
String jobId = commandExecutor.execute(commandConfig, new CustomApproveTaskAsyncCmd(pass));
// 重置任务,因为上面的 cmd 和这个 cmd 的 lock 对象不一致
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/Alter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/Alter.java
new file mode 100644
index 000000000..5ad691f57
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/Alter.java
@@ -0,0 +1,14 @@
+package cn.axzo.workflow.core.listener;
+
+import cn.axzo.workflow.common.model.dto.AlterDTO;
+
+/**
+ * Core 往外转发的钩子
+ *
+ * @author wangli
+ * @since 2024-09-13 11:33
+ */
+public interface Alter {
+
+ void invoke(AlterDTO alterDTO);
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/impl/InternalBpmnActivityEventListener_lo_Listener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/impl/InternalBpmnActivityEventListener_lo_Listener.java
new file mode 100644
index 000000000..fb136ee72
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/impl/InternalBpmnActivityEventListener_lo_Listener.java
@@ -0,0 +1,131 @@
+package cn.axzo.workflow.core.listener.impl;
+
+import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+import cn.axzo.workflow.common.model.dto.TermNodePausingDTO;
+import cn.axzo.workflow.core.common.context.ActivityOperationContext;
+import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
+import cn.axzo.workflow.core.conf.SupportRefreshProperties;
+import cn.axzo.workflow.core.engine.job.AsyncTermNodeAlterJobHandler;
+import cn.axzo.workflow.core.listener.AbstractBpmnEventListener;
+import cn.axzo.workflow.core.listener.BpmnActivityEventListener;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.TimerEventDefinition;
+import org.flowable.engine.ManagementService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.engine.impl.jobexecutor.TimerEventHandler;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.engine.impl.util.ProcessDefinitionUtil;
+import org.flowable.engine.impl.util.TimerUtil;
+import org.flowable.job.api.Job;
+import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
+import org.springframework.context.annotation.Scope;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_NODE_ALTER;
+
+/**
+ * Core 包内置的活动事件处理,可共用与 Jar 包集成和微服务集成
+ *
+ * 该监听主要是监听启动“无人”的业务节点超时告警功能
+ *
+ * @author wangli
+ * @since 2024-09-11 11:44
+ */
+@Slf4j
+@Component
+@Scope("prototype")
+@AllArgsConstructor
+public class InternalBpmnActivityEventListener_lo_Listener extends AbstractBpmnEventListener implements BpmnActivityEventListener, Ordered {
+ private final SupportRefreshProperties refreshProperties;
+
+ @Override
+ public int getOrder() {
+ return Integer.MIN_VALUE;
+ }
+
+ /**
+ * 节点已启动
+ *
+ * 创建一个周期性监控业务节点执行状态的任务
+ *
+ * @param execution
+ */
+ @Override
+ public void onStart(DelegateExecution execution) {
+ if (!Boolean.TRUE.equals(refreshProperties.getAlterEnable())) {
+ return;
+ }
+ ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
+ RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
+ TermNodePausingDTO dto = new TermNodePausingDTO(execution.getProcessInstanceId(), execution.getCurrentActivityId(), refreshProperties.getAlterRetries());
+ runtimeService.setVariable(execution.getProcessInstanceId(), BIZ_NODE_ALTER + execution.getCurrentActivityId(), dto);
+ BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(execution.getProcessDefinitionId());
+ FlowElement flowElement = bpmnModel.getFlowElement(execution.getCurrentActivityId());
+ BpmnMetaParserHelper.getNodeType(flowElement).ifPresent(e -> {
+ if (Objects.equals(BpmnFlowNodeType.NODE_BUSINESS, e)) {
+ BpmnMetaParserHelper.getApprovalMethod(flowElement).ifPresent(method -> {
+ switch (method) {
+ case nobody:
+ case bizSpecify:
+ // FIXME 业务指定审批人,需要在业务设置了人后,清除定时
+ TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
+ String timeUnit;
+ switch (refreshProperties.getAlterIntervalUnit()) {
+ case SECONDS:
+ timeUnit = "S";
+ break;
+ case HOURS:
+ timeUnit = "H";
+ break;
+ case DAYS:
+ timeUnit = "D";
+ break;
+ default:
+ // MINUTES
+ timeUnit = "M";
+ break;
+ }
+ timerEventDefinition.setTimeCycle("R" + refreshProperties.getAlterRetries() + "/PT" + refreshProperties.getAlterInterval() + timeUnit);
+
+ TimerJobEntity timerJob = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition, execution.getCurrentFlowElement(),
+ false, (ExecutionEntity) execution, AsyncTermNodeAlterJobHandler.TYPE,
+ TimerEventHandler.createConfiguration(execution.getCurrentActivityId(), null, timerEventDefinition.getCalendarName()));
+ if (timerJob != null) {
+ CommandContextUtil.getTimerJobService().scheduleTimerJob(timerJob);
+ }
+ break;
+ default:
+ break;
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * 节点已取消
+ *
+ * @param execution
+ */
+ @Override
+ public void onEnd(DelegateExecution execution) {
+ ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
+ ManagementService managementService = processEngineConfiguration.getManagementService();
+ Job timerJob = managementService.createTimerJobQuery()
+ .elementId(execution.getCurrentActivityId())
+ .processInstanceId(execution.getProcessInstanceId())
+ .singleResult();
+ if (Objects.nonNull(timerJob)) {
+ CommandContextUtil.getTimerJobService().deleteTimerJob((TimerJobEntity) timerJob);
+ }
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessLog.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessLog.java
new file mode 100644
index 000000000..4c5476f90
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessLog.java
@@ -0,0 +1,114 @@
+package cn.axzo.workflow.core.repository.entity;
+
+import cn.axzo.framework.data.mybatisplus.model.BaseEntity;
+import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import cn.axzo.workflow.core.conf.handler.ButtonConfTypeHandler;
+import cn.axzo.workflow.core.conf.handler.ListAssigneeTypeHandler;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 审批日志
+ *
+ * @author wangli
+ * @since 2024-08-30 15:29
+ */
+@EqualsAndHashCode(callSuper = true)
+@TableName(value = "ext_ax_process_log", autoResultMap = true)
+@Data
+@ToString(callSuper = true)
+public class ExtAxProcessLog extends BaseEntity {
+
+ /**
+ * 流程实例 ID
+ */
+ private String processInstanceId;
+ /**
+ * 实例归属租户
+ */
+ private String tenantId;
+ /**
+ * 活动节点 ID
+ */
+ private String activityId;
+ /**
+ * 活动节点名称
+ */
+ private String activityName;
+ /**
+ * 审批方式:配置审批人/业务指定/业务触发(不含人)
+ */
+ private String approvalMethod;
+ /**
+ * 节点类型:审批节点/业务节点/评论节点/抄送节点
+ */
+ private String nodeType;
+ /**
+ * 节点模式:会签/或签
+ */
+ private String nodeMode;
+ /**
+ * 任务 ID
+ */
+ private String taskId;
+ /**
+ * 操作建议
+ */
+ private String advice;
+ /**
+ * 操作描述
+ */
+ private String operationDesc;
+ /**
+ * 审批人对象信息
+ */
+ @TableField(typeHandler = ListAssigneeTypeHandler.class)
+ private List assigneeFull;
+ /**
+ * 审批人标识
+ */
+ private Long assigneeId;
+ /**
+ * 审批人归属租户
+ */
+ private String assigneeTenantId;
+ /**
+ * 审批人姓名
+ */
+ private String assigneeName;
+ /**
+ * 审批人归属单位
+ */
+ private String assigneeOuId;
+ /**
+ * 任务开始时间
+ */
+ private Date startTime;
+ /**
+ * 任务结束时间
+ */
+ private Date endTime;
+ /**
+ * 节点按钮的全量配置
+ */
+ @TableField(typeHandler = ButtonConfTypeHandler.class)
+ private BpmnButtonConf buttonConf;
+ /**
+ * 任务状态:审批中/通过/驳回/转交/加签/回退
+ */
+ private String status;
+ /**
+ * 扩展字段
+ */
+ @TableField(typeHandler = JacksonTypeHandler.class)
+ private JSONObject extra;
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessLogMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessLogMapper.java
new file mode 100644
index 000000000..cf9ebd84f
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessLogMapper.java
@@ -0,0 +1,8 @@
+package cn.axzo.workflow.core.repository.mapper;
+
+import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface ExtAxProcessLogMapper extends BaseMapperX {
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java
index fa21f62cb..f47c4bd2e 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java
@@ -3,6 +3,7 @@ package cn.axzo.workflow.core.service;
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 org.flowable.job.service.impl.persistence.entity.JobEntity;
/**
@@ -13,26 +14,14 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
*/
public interface BpmnProcessActivityService {
- /**
- * 执行异步任务
- *
- * @param job 需要执行的任务
- */
- void executeAsyncJob(JobEntity job);
-
/**
* 唤醒业务节点
*
- * @param executionId 活动 ID
+ * @param dto
*/
- void trigger(String executionId);
+ void trigger(BpmnActivityTriggerDTO dto);
+
- /**
- * 唤醒业务节点-异步
- *
- * @param executionId 活动 ID
- */
- void triggerAsync(String executionId);
/**
* 给指定实例的指定节点重设审批人
@@ -41,13 +30,6 @@ public interface BpmnProcessActivityService {
*/
void setAssignee(BpmnActivitySetAssigneeDTO dto);
- /**
- * 给指定实例的指定节点重设审批人
- *
- * @param dto
- */
- void setAssigneeAsync(BpmnActivitySetAssigneeDTO dto);
-
/**
* 设置指定业务接口继续往下流转的触发时间
*
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessDefinitionService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessDefinitionService.java
index 88e3a7648..e67398dcc 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessDefinitionService.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessDefinitionService.java
@@ -4,6 +4,9 @@ import cn.axzo.workflow.common.model.request.bpmn.definition.BpmnProcessDefiniti
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessDefinitionPageDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO;
+import org.flowable.bpmn.model.EndEvent;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.FlowNode;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
@@ -82,4 +85,12 @@ public interface BpmnProcessDefinitionService {
List getProcessDefinitionListByDeploymentIds(Set deploymentIds);
void delete(String deploymentId, Boolean cascade);
+
+ List findEndFlowElement(String processDefinitionId);
+
+ List findFlowNodes(String processDefinitionId);
+
+ List findFlowElements(String processDefinitionId);
+
+ List findFlowElementsByIds(String processDefinitionId, List flowElementIds);
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java
index 82e629a77..4de469d2a 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java
@@ -7,18 +7,21 @@ import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCar
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.BpmnProcessInstanceCreateWithFormDTO;
+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.HistoricProcessInstanceSearchDTO;
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.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.HistoricProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.form.api.FormInfo;
import javax.annotation.Nullable;
@@ -107,7 +110,7 @@ public interface BpmnProcessInstanceService {
* 获得流程实例
*
* @param processDefinitionId 流程实例的编号
- * @param status 状态
+ * @param status 状态
* @link SuspensionState.ACTIVE.getStateCode()
*/
Boolean updateProcessStatus(String processDefinitionId, Integer status);
@@ -143,9 +146,28 @@ public interface BpmnProcessInstanceService {
*/
List getProcessInstanceNodeForecast(String processInstanceId, String tenantId);
+ /**
+ * 对指定流程的指定节点开始推送未来的结点,并结合变量计算正确的分支
+ *
+ * @param processInstanceId 实例编号
+ * @param startNodeDefinitionKey 从该节点开始推断后续的节点
+ * @param containSelf 是否包含起始节点
+ * @param checkAliveThrowException 如果给的实例编号已到终态,不会执行推测,用该参数为 true 时,抛出实例完结的异常信息,如果为 false时,直接返回空集合
+ * @return
+ */
+ List getProcessInstanceNodeForecastWithSpecifyTaskDefinitionKey(String processInstanceId, ProcessInstance instance, String startNodeDefinitionKey, Boolean containSelf, Boolean checkAliveThrowException);
+
List getProcessInstanceNodeFilterForecast(String processInstanceId, String tenantId, List nodeDefinitionKeys);
List getTenantIds();
Boolean checkInstanceApprover(BpmnProcessInstanceCheckApproverDTO dto);
+
+ /**
+ * 获取指定流程实例的日志
+ *
+ * @param dto
+ * @return
+ */
+ BpmnProcessInstanceLogVO getProcessInstanceLog(BpmnProcessInstanceLogQueryDTO dto);
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessJobService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessJobService.java
index d3b8db1f9..1bad2f2cd 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessJobService.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessJobService.java
@@ -5,4 +5,8 @@ public interface BpmnProcessJobService {
void executeDeadLetterJobActionByJobId(String jobId);
void executeDeadLetterJobActionByProcInstId(String processInstanceId);
+
+ String getDeadLetterJobExceptionStacktrace(String processInstId);
+
+ String getDeadLetterJobExceptionStacktraceByJobId(String jobId);
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java
index cdcd624df..7e1d18f6f 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java
@@ -1,10 +1,11 @@
package cn.axzo.workflow.core.service;
-import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+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.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.BpmnTaskPageSearchDTO;
@@ -41,6 +42,11 @@ public interface BpmnProcessTaskService {
*/
void approveTask(BpmnTaskAuditDTO taskAuditDTO);
+ /**
+ * 回退
+ */
+ void backTask(BpmnTaskBackAuditDTO taskAuditDTO);
+
/**
* 批量同意
*
@@ -48,6 +54,12 @@ public interface BpmnProcessTaskService {
*/
BatchOperationResultVO batchApproveTask(List taskAuditDTOS);
+ /**
+ * 回退到指定节点,可以回退节点选项
+ * @param taskId 任务id
+ */
+ List getBackOptionalNodes(String taskId);
+
/**
* 驳回
*/
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessLogService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessLogService.java
new file mode 100644
index 000000000..ef2791f63
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessLogService.java
@@ -0,0 +1,56 @@
+package cn.axzo.workflow.core.service;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
+
+import java.util.List;
+
+/**
+ * Api Log 表操作服务
+ *
+ * @author wangli
+ * @since 2024/4/3 10:40
+ */
+public interface ExtAxProcessLogService {
+ /**
+ * 新增审批流程日志
+ *
+ * @param log
+ * @return
+ */
+ Long insert(ExtAxProcessLog log);
+
+ /**
+ * 根据参数删除指定任务
+ *
+ * @param deleteLog 查询条件
+ */
+ void delete(ExtAxProcessLog deleteLog);
+
+ /**
+ * 根据有 ID 的实体直接更新
+ *
+ * @param updateLog
+ */
+ void updateById(ExtAxProcessLog updateLog);
+
+ /**
+ * 根据条件更新指定对象的列
+ *
+ * @param query
+ * @param update
+ */
+ void update(ExtAxProcessLog query, ExtAxProcessLog update);
+
+ /**
+ * 更新指定任务的审批人
+ *
+ * @param updateLog
+ * @param assignee
+ */
+ void updateAssignee(ExtAxProcessLog updateLog, BpmnTaskDelegateAssigner assignee);
+
+ void updateAssignee(ExtAxProcessLog updateLog, BpmnTaskDelegateAssigner assignee, String operationDesc);
+
+ List genericQuery(ExtAxProcessLog query);
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmnProcessInstanceAdminPageItemConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmnProcessInstanceAdminPageItemConverter.java
index 42ff1140f..a4a51262e 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmnProcessInstanceAdminPageItemConverter.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/BpmnProcessInstanceAdminPageItemConverter.java
@@ -30,12 +30,12 @@ import static org.mapstruct.NullValueCheckStrategy.ALWAYS;
* @since 2024/1/25 16:31
*/
@Mapper(
- componentModel = "spring",
- nullValueCheckStrategy = ALWAYS,
- imports = Arrays.class
+ componentModel = "spring",
+ nullValueCheckStrategy = ALWAYS,
+ imports = Arrays.class
)
public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConverter {
+ HistoricProcessInstance> {
@Mapping(target = "processInstanceId", source = "id")
@Mapping(target = "processInstanceName", source = "name")
@@ -58,8 +58,8 @@ public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConvert
if (Objects.equals(PROCESSING.getStatus(), i.getBusinessStatus())) {
List flowElements = instanceFlowElementMap.get(i.getId()).stream()
- .filter(j -> j instanceof UserTask || j instanceof ReceiveTask || j instanceof ServiceTask)
- .collect(Collectors.toList());
+ .filter(j -> j instanceof UserTask || j instanceof ReceiveTask || j instanceof ServiceTask)
+ .collect(Collectors.toList());
vo.setTotalNodeCount(flowElements.size());
// 进行中的节点
@@ -85,7 +85,7 @@ public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConvert
vo.setCategoryDesc(category.getLabel());
vo.setWorkspaceTypeCode(category.getWorkspaceTypeCode());
vo.setBusinessStatusDesc(BpmnProcessInstanceResultEnum.valueOfStatus(vo.getBusinessStatus()).getDesc());
- vo.setWorkspaceType(WorkspaceType.getType(Integer.valueOf(vo.getWorkspaceTypeCode())));
+ vo.setWorkspaceType(WorkspaceType.getType(Integer.valueOf(Objects.nonNull(vo.getWorkspaceTypeCode()) ? vo.getWorkspaceTypeCode() : "0")));
result.add(vo);
});
return result;
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java
index b259166b1..838bbf0ed 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java
@@ -3,34 +3,31 @@ package cn.axzo.workflow.core.service.impl;
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 cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceCmd;
+import cn.axzo.workflow.core.engine.cmd.CustomActivityTriggerAsyncCmd;
+import cn.axzo.workflow.core.engine.cmd.CustomActivityTriggerCmd;
+import cn.axzo.workflow.core.engine.cmd.CustomBizSpecifyAssigneeToTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomBizSpecifyAssigneeToTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomBusinessNodeTimeoutCallbackCmd;
import cn.axzo.workflow.core.engine.cmd.CustomBusinessNodeTimeoutTriggerCmd;
-import cn.axzo.workflow.core.engine.job.AsyncBpmnProcessActivityJobHandler;
import cn.axzo.workflow.core.service.BpmnProcessActivityService;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
-import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
-import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.Execution;
-import org.flowable.engine.runtime.ProcessInstance;
-import org.flowable.job.service.JobService;
-import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.spring.SpringProcessEngineConfiguration;
-import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Objects;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_TRIGGER_NOT_EXISTS;
-import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.PROCESS_INSTANCE_IS_NOT_EXIST;
@Service
@@ -46,42 +43,15 @@ public class BpmnProcessActivityServiceImpl implements BpmnProcessActivityServic
@Lazy
private SpringProcessEngineConfiguration processEngineConfiguration;
- private static final String JOB_TRIGGER_ASYNC_NAME = "asyncTrigger";
- private static final String JOB_ASSIGNEE_ASYNC_NAME = "asyncSetAssignee";
-
@Override
- public void executeAsyncJob(JobEntity job) {
- if (job == null) {
- log.warn("job is null");
- throw new NullPointerException("job is null");
+ @Transactional(rollbackFor = Exception.class)
+ public void trigger(BpmnActivityTriggerDTO dto) {
+ CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
+ if (Boolean.TRUE.equals(dto.getAsync())) {
+ commandExecutor.execute(new CustomActivityTriggerAsyncCmd(dto));
+ } else {
+ commandExecutor.execute(new CustomActivityTriggerCmd(dto));
}
- String type = job.getElementName();
- String customValues = job.getCustomValues();
- switch (type) {
- case JOB_TRIGGER_ASYNC_NAME:
- trigger(customValues);
- break;
- case JOB_ASSIGNEE_ASYNC_NAME:
- BpmnActivitySetAssigneeDTO dto = JSONUtil.toBean(customValues, BpmnActivitySetAssigneeDTO.class);
- setAssignee(dto);
- break;
- default:
- throw new UnsupportedOperationException(String.format("不支持'%s'类型操作", type));
- }
- }
-
- @Override
- public void trigger(String executionId) {
- Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
- if (Objects.isNull(execution)) {
- throw new WorkflowEngineException(ACTIVITY_TRIGGER_NOT_EXISTS, executionId);
- }
- runtimeService.trigger(executionId);
- }
-
- @Override
- public void triggerAsync(String executionId) {
- validateAndStartAsyncJob(executionId, executionId, JOB_TRIGGER_ASYNC_NAME);
}
@Override
@@ -95,43 +65,12 @@ public class BpmnProcessActivityServiceImpl implements BpmnProcessActivityServic
commandExecutor.execute(new CustomAbortProcessInstanceCmd(execution.getProcessInstanceId(), null, "业务未指定审批人", extAxHiTaskInstService));
return;
}
- commandExecutor.execute(new CustomBizSpecifyAssigneeToTaskCmd(dto.getTriggerId(), dto.getAssigners()));
- }
- @Override
- public void setAssigneeAsync(BpmnActivitySetAssigneeDTO dto) {
- //查询任务
- Task task = CustomBizSpecifyAssigneeToTaskCmd.getOperateTask(processEngineConfiguration.getTaskService(), dto.getTriggerId());
- //先校验
- CustomBizSpecifyAssigneeToTaskCmd.validate(processEngineConfiguration.getRuntimeService(), dto.getTriggerId(), task, dto.getAssigners());
- validateAndStartAsyncJob(dto.getTriggerId(), dto, JOB_ASSIGNEE_ASYNC_NAME);
- }
-
- private void validateAndStartAsyncJob(String executionId, Object customValue, String actionName) {
- processEngineConfiguration.getCommandExecutor().execute((Command) commandContext -> {
- Execution execution = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
- if (Objects.isNull(execution)) {
- throw new WorkflowEngineException(ACTIVITY_TRIGGER_NOT_EXISTS, executionId);
- }
- ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(execution.getProcessInstanceId()).singleResult();
- if (Objects.isNull(processInstance)) {
- throw new WorkflowEngineException(PROCESS_INSTANCE_IS_NOT_EXIST, execution.getId());
- }
- JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();
- JobEntity job = jobService.createJob();
- // 这里的 executionId 可为 null
- job.setExecutionId(executionId);
- job.setProcessInstanceId(execution.getProcessInstanceId());
- job.setProcessDefinitionId(processInstance.getProcessDefinitionId());
- job.setElementId(AsyncBpmnProcessActivityJobHandler.TYPE);
- job.setElementName(actionName);
- job.setJobHandlerType(AsyncBpmnProcessActivityJobHandler.TYPE);
- job.setTenantId(execution.getTenantId());
- job.setCustomValues(JSONUtil.toJsonStr(customValue));
- jobService.createAsyncJob(job, false);
- jobService.scheduleAsyncJob(job);
- return null;
- });
+ if(Boolean.TRUE.equals(dto.getAsync())) {
+ commandExecutor.execute(new CustomBizSpecifyAssigneeToTaskAsyncCmd(dto));
+ } else {
+ commandExecutor.execute(new CustomBizSpecifyAssigneeToTaskCmd(dto.getTriggerId(), dto.getAssigners()));
+ }
}
@Override
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessDefinitionServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessDefinitionServiceImpl.java
index 55956d45c..e2df82aa3 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessDefinitionServiceImpl.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessDefinitionServiceImpl.java
@@ -14,6 +14,9 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.EndEvent;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.FlowNode;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
@@ -27,6 +30,7 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -282,4 +286,50 @@ public class BpmnProcessDefinitionServiceImpl implements BpmnProcessDefinitionSe
}
return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();
}
+
+ @Override
+ public List findEndFlowElement(String processDefinitionId) {
+ return getNodesByType(processDefinitionId, EndEvent.class);
+ }
+
+ @Override
+ public List findFlowNodes(String processDefinitionId) {
+ return getNodesByType(processDefinitionId, FlowNode.class);
+ }
+
+ @Override
+ public List findFlowElements(String processDefinitionId) {
+ return getNodesByType(processDefinitionId, FlowElement.class);
+ }
+
+ @Override
+ public List findFlowElementsByIds(String processDefinitionId, List flowElementIds) {
+ if (CollectionUtils.isEmpty(flowElementIds)) {
+ return emptyList();
+ }
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
+ .processDefinitionId(processDefinitionId)
+ .singleResult();
+ if (processDefinition == null) {
+ return emptyList();
+ }
+ BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
+ return flowElementIds.stream().map(bpmnModel::getFlowElement).collect(Collectors.toList());
+ }
+
+ private List getNodesByType(String processDefinitionId, Class clazz) {
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
+ .processDefinitionId(processDefinitionId)
+ .singleResult();
+ if (processDefinition == null) {
+ return emptyList();
+ }
+ BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
+ Collection flowNodes = bpmnModel.getMainProcess().findFlowElementsOfType(clazz);
+ if (CollectionUtils.isEmpty(flowNodes)) {
+ return emptyList();
+ }
+ return new ArrayList<>(flowNodes);
+ }
+
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java
index 50ef58cf9..426b1f7d6 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java
@@ -1,8 +1,15 @@
package cn.axzo.workflow.core.service.impl;
import cn.axzo.workflow.common.constant.BpmnConstants;
+import cn.axzo.workflow.common.enums.ApprovalMethodEnum;
+import cn.axzo.workflow.common.enums.AttachmentTypeEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
+import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.enums.WorkspaceType;
+import cn.axzo.workflow.common.model.request.BpmnApproveConf;
+import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
+import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo;
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;
@@ -10,9 +17,11 @@ import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCar
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.BpmnProcessInstanceCreateWithFormDTO;
+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.HistoricProcessInstanceSearchDTO;
+import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.category.CategorySearchDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
@@ -20,10 +29,12 @@ import cn.axzo.workflow.common.model.response.bpmn.BatchOperationItemResultVO;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
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.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.HistoricProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
+import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceLogVO;
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;
@@ -34,10 +45,12 @@ import cn.axzo.workflow.core.engine.cmd.CustomCancelProcessInstanceCmd;
import cn.axzo.workflow.core.engine.cmd.CustomCarbonCopyUserSelectorCmd;
import cn.axzo.workflow.core.engine.cmd.CustomForecastUserTaskAssigneeCmd;
import cn.axzo.workflow.core.engine.listener.EngineExecutionStartListener;
+import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
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.ExtAxProcessLogService;
import cn.axzo.workflow.core.service.converter.BpmnHistoricProcessInstanceConverter;
import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter;
import cn.axzo.workflow.core.service.converter.BpmnProcessInstanceAdminPageItemConverter;
@@ -77,6 +90,7 @@ import org.flowable.engine.runtime.NativeActivityInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.engine.runtime.ProcessInstanceQuery;
+import org.flowable.engine.task.Attachment;
import org.flowable.form.api.FormInfo;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.task.api.Task;
@@ -95,6 +109,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -109,6 +124,10 @@ import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_S
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.CONFIG_BUTTON_TYPE_CARBON_COPY;
+import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_CURRENT;
+import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_HISTORY;
+import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.CREATE_INSTANCE_PARAMS;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
@@ -125,8 +144,14 @@ import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.AND;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.EXCEPTIONAL;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.GENERAL;
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.BpmnFlowNodeType.NODE_TASK;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
import static cn.axzo.workflow.common.enums.WorkspaceType.GOVERNMENT;
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;
@@ -187,6 +212,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
@Resource
@Lazy
private BpmnProcessInstanceService bpmnProcessInstanceService;
+ @Resource
+ private ExtAxProcessLogService processLogService;
@Override
public HistoricProcessInstance getProcessInstanceByBusinessKey(String businessKey, @Nullable String tenantId,
@@ -844,6 +871,30 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
return getProcessInstanceNodeFilterForecast(processInstanceId, tenantId, Collections.emptyList());
}
+ @Override
+ public List getProcessInstanceNodeForecastWithSpecifyTaskDefinitionKey(String processInstanceId,
+ ProcessInstance instance,
+ String startNodeDefinitionKey,
+ Boolean containSelf,
+ Boolean checkAliveThrowException) {
+ if (Objects.isNull(instance)) {
+ instance = runtimeService.createProcessInstanceQuery()
+ .processInstanceId(processInstanceId).singleResult();
+ if (Objects.isNull(instance)) {
+ if (checkAliveThrowException) {
+ throw new WorkflowEngineException(RUNNING_INSTANCE_ONLY_FORECAST);
+ } else {
+ return Collections.emptyList();
+ }
+ }
+ }
+
+ List flowElements = forecastService.performProcessForecasting(instance.getProcessInstanceId(), instance, startNodeDefinitionKey, containSelf);
+
+ return buildNodeDetailVos(processInstanceId, Collections.emptyList(), instance, flowElements);
+ }
+
+
@Override
public List getProcessInstanceNodeFilterForecast(String processInstanceId, String tenantId, List nodeDefinitionKeys) {
ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery()
@@ -857,6 +908,10 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
}
List flowElements = forecastService.performProcessForecasting(processInstanceId, instance);
+ return buildNodeDetailVos(processInstanceId, nodeDefinitionKeys, instance, flowElements);
+ }
+
+ private List buildNodeDetailVos(String processInstanceId, List nodeDefinitionKeys, ProcessInstance instance, List flowElements) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
List resultList = new ArrayList<>(flowElements.size());
// 发起人节点,也是一个 UserTask 节点, 所以这里默认就包含了发起人节点
@@ -961,4 +1016,361 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
.list();
return !CollectionUtils.isEmpty(list);
}
+
+ @Override
+ public BpmnProcessInstanceLogVO getProcessInstanceLog(BpmnProcessInstanceLogQueryDTO dto) {
+ HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
+ .processInstanceId(dto.getProcessInstanceId())
+ .includeProcessVariables().singleResult();
+ if (Objects.isNull(historicProcessInstance)) {
+ throw new WorkflowEngineException(PROCESS_INSTANCE_ID_NOT_EXISTS, dto.getProcessInstanceId());
+ }
+
+ ExtAxProcessLog query = new ExtAxProcessLog();
+ query.setProcessInstanceId(dto.getProcessInstanceId());
+ List logs = processLogService.genericQuery(query).stream()
+ .sorted(Comparator.comparing(ExtAxProcessLog::getEndTime, Comparator.nullsLast(Comparator.naturalOrder())))
+ .collect(Collectors.toList());
+
+ List forecasting = new ArrayList<>();
+ // 只有还在运行中的实例才需要推测后续节点
+ if (Objects.equals(historicProcessInstance.getBusinessStatus(), PROCESSING.getStatus())) {
+ ProcessInstance instance = runtimeService.createProcessInstanceQuery()
+ .processInstanceId(dto.getProcessInstanceId())
+ .includeProcessVariables()
+ .singleResult();
+ logs.stream().reduce((f, s) -> s).ifPresent(e -> forecasting.addAll(
+ getProcessInstanceNodeForecastWithSpecifyTaskDefinitionKey(dto.getProcessInstanceId(), instance, e.getActivityId(), false, false))
+ );
+ }
+
+ BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
+
+ Map variables = historicProcessInstance.getProcessVariables();
+ BpmnProcessInstanceLogVO logVO = BpmnProcessInstanceLogVO.builder()
+ .id(historicProcessInstance.getId())
+ .name(historicProcessInstance.getName())
+ .result(BpmnProcessInstanceResultEnum.valueOfStatus(historicProcessInstance.getBusinessStatus()))
+ .startTime(historicProcessInstance.getStartTime())
+ .endTime(historicProcessInstance.getEndTime())
+ .processDefinitionKey(historicProcessInstance.getProcessDefinitionKey())
+ .processDefinitionId(historicProcessInstance.getProcessDefinitionId())
+ .businessKey(historicProcessInstance.getBusinessKey())
+ .businessStatus(historicProcessInstance.getBusinessStatus())
+ .initiator(BpmnTaskDelegateAssigner.toObjectCompatible(Optional.ofNullable(variables.getOrDefault(INTERNAL_INITIATOR, null))
+ .orElse(variables.getOrDefault(OLD_INTERNAL_INITIATOR, null))))
+ .tenantId(historicProcessInstance.getTenantId())
+ .agented((Boolean) Optional.ofNullable(variables.get(INTERNAL_PROCESS_AGENT)).orElse(false))
+ .taskDetails(genericTaskLogVos(historicProcessInstance.getId(), logs, forecasting, dto))
+ .defaultButtonConf(getButtonConfig(bpmnModel.getMainProcess()).orElse(new BpmnButtonConf()))
+ .supportBatchOperation(getProcessApproveConf(bpmnModel.getMainProcess()).orElse(new BpmnApproveConf()).getSupportBatchOperation())
+ .userAgreeSignature(getProcessApproveConf(bpmnModel.getMainProcess()).orElse(new BpmnApproveConf()).getUserAgreeSignature())
+ .workflowEngineVersion((String) variables.getOrDefault(WORKFLOW_ENGINE_VERSION, FLOW_SERVER_VERSION_121))
+ .build();
+
+
+ categoryService.get(BPM_MODEL_CATEGORY, historicProcessInstance.getProcessDefinitionKey()).ifPresent(category -> {
+ logVO.setWorkspaceType(WorkspaceType.getType(Integer.valueOf(category.getWorkspaceTypeCode())));
+ logVO.setCategory(category.getValue());
+ });
+
+ // 根据传入的访问人计算有权限的按钮
+ calcAuthorizedButtons(logVO, dto.getVisitor());
+ return logVO;
+ }
+
+ private void calcAuthorizedButtons(BpmnProcessInstanceLogVO logVO, BpmnTaskDelegateAssigner visitor) {
+ List authorizedButtons = new ArrayList<>();
+ if (Objects.nonNull(logVO.getDefaultButtonConf())
+ && !CollectionUtils.isEmpty(logVO.getDefaultButtonConf().getCarbonCopy())) {
+ authorizedButtons.addAll(logVO.getDefaultButtonConf().getCarbonCopy());
+ }
+
+ if (Objects.equals(PROCESSING, logVO.getResult()) && Objects.nonNull(visitor)) {
+ String ge130Assignee = getGe130Assignee(visitor);
+ String le130Assignee = getLe130Assignee(visitor);
+
+ // 运行到的当前节点的按钮配置
+ logVO.getTaskDetails().stream()
+ .filter(i -> Objects.equals(PROCESSING, i.getResult()))
+ .findFirst()
+ .ifPresent(i -> logVO.setCalculatingButtonConf(i.getButtonConf()));
+
+
+ // 比对发起人
+ if (Objects.nonNull(logVO.getInitiator()) &&
+ (Objects.equals(logVO.getInitiator().buildAssigneeId_1_2_1(), le130Assignee)
+ || logVO.getInitiator().buildAssigneeId().contains(ge130Assignee))) {
+ authorizedButtons.addAll(chooseButtons(logVO, CONFIG_BUTTON_TYPE_INITIATOR));
+ }
+
+
+ // 比对当前审批人
+ logVO.getTaskDetails().stream().filter(i -> Objects.equals(PROCESSING, i.getResult())
+ || (Objects.equals(DELETED, i.getResult()) && Objects.isNull(i.getEndTime())))
+ .findFirst()
+ .map(i -> {
+ List list = new ArrayList<>();
+ if (Objects.nonNull(i.getAssigneeSnapshot())) {
+ list.add(i.getAssigneeSnapshot());
+ }
+ if (!CollectionUtils.isEmpty(i.getForecastAssignees())) {
+ list.addAll(i.getForecastAssignees());
+ }
+ return list;
+ })
+ .orElse(Collections.emptyList())
+ .stream()
+ .filter(Objects::nonNull)
+ .filter(i -> i.buildAssigneeId().contains(ge130Assignee) || Objects.equals(i.buildAssigneeId_1_2_1(), le130Assignee))
+ .findAny()
+ .ifPresent(i -> authorizedButtons.addAll(chooseButtons(logVO, CONFIG_BUTTON_TYPE_CURRENT)));
+
+
+ // 比对历史审批人
+ logVO.getTaskDetails().stream()
+ .filter(i -> Objects.equals(i.getNodeType(), NODE_TASK) || Objects.equals(i.getNodeType(), NODE_BUSINESS))
+ .filter(i -> !Objects.equals(PROCESSING, i.getResult()))
+ .map(BpmnTaskInstanceLogVO::getAssigneeSnapshot)
+ .filter(Objects::nonNull)
+ .filter(i -> i.buildAssigneeId().contains(ge130Assignee) || Objects.equals(i.buildAssigneeId_1_2_1(), le130Assignee))
+ .findAny()
+ .ifPresent(i -> authorizedButtons.addAll(chooseButtons(logVO, CONFIG_BUTTON_TYPE_HISTORY)));
+
+ // 比对抄送人
+ logVO.getTaskDetails().stream()
+ .filter(i -> Objects.equals(i.getNodeType(), NODE_CARBON_COPY))
+ .flatMap(i -> ListUtils.emptyIfNull(i.getForecastAssignees()).stream())
+ .filter(i -> i.buildAssigneeId().contains(ge130Assignee) || Objects.equals(i.buildAssigneeId_1_2_1(), le130Assignee))
+ .findAny()
+ .ifPresent(i -> authorizedButtons.addAll(chooseButtons(logVO, CONFIG_BUTTON_TYPE_CARBON_COPY)));
+ }
+
+ logVO.setCurrentUserButtons(authorizedButtons);
+ // 有权限访问的自定义按钮
+ List customButtonKeys = authorizedButtons.stream()
+ .filter(i -> Objects.equals(i.getType(), "CUSTOM"))
+ .map(BpmnButtonMetaInfo::getBtnKey)
+ .distinct().collect(Collectors.toList());
+ List customButtons = logVO.getDefaultButtonConf().getInitiator().stream()
+ .filter(i -> Objects.equals(i.getType(), "CUSTOM"))
+ .filter(i -> !customButtonKeys.contains(i.getBtnKey()))
+ .collect(Collectors.toList());
+ logVO.setCustomHiddenButtons(customButtons);
+ }
+
+ /**
+ * 按钮的通用处理, 有限使用节点的按钮配置,如果没有则按兜底按钮配置
+ *
+ * @param logVO 该对象中的 calcButtonConf 字段为当前节点的按钮配置
+ * @param buttonConfigName String CONFIG_BUTTON_TYPE_INITIATOR = "initiator";
+ * String CONFIG_BUTTON_TYPE_CURRENT = "current";
+ * String CONFIG_BUTTON_TYPE_HISTORY = "history";
+ * String CONFIG_BUTTON_TYPE_CARBON_COPY = "carbonCopy";
+ * @return
+ */
+ private List chooseButtons(BpmnProcessInstanceLogVO logVO, String buttonConfigName) {
+ List mergeButtons = new ArrayList<>();
+ if (Objects.isNull(logVO.getCalculatingButtonConf())) {
+ BpmnButtonConf defaultButtonConf = logVO.getDefaultButtonConf();
+ if (Objects.isNull(defaultButtonConf)) {
+ return mergeButtons;
+ }
+ logVO.setCalculatingButtonConf(defaultButtonConf);
+ }
+ switch (buttonConfigName) {
+ case CONFIG_BUTTON_TYPE_INITIATOR:
+ mergeButtons.addAll(logVO.getCalculatingButtonConf().getInitiator());
+ break;
+ case CONFIG_BUTTON_TYPE_CURRENT:
+ mergeButtons.addAll(logVO.getCalculatingButtonConf().getCurrent());
+ break;
+ case CONFIG_BUTTON_TYPE_HISTORY:
+ mergeButtons.addAll(logVO.getCalculatingButtonConf().getHistory());
+ break;
+ case CONFIG_BUTTON_TYPE_CARBON_COPY:
+ mergeButtons.addAll(logVO.getCalculatingButtonConf().getCarbonCopy());
+ break;
+ default:
+ break;
+ }
+ return mergeButtons;
+ }
+
+ public static String getLe130Assignee(BpmnTaskDelegateAssigner visitor) {
+ return visitor.getTenantId() + "|" + visitor.getAssignee() + "|" + visitor.getAssigneeType();
+ }
+
+ public static String getGe130Assignee(BpmnTaskDelegateAssigner visitor) {
+ // String ge130Assignee = contextInfo.getOuId() + "|" + contextInfo.getUserInfo().getPersonId();
+ // 130版本以上,产品要求仅校验 personId
+ return "|" + visitor.getPersonId();
+ }
+
+ private List genericTaskLogVos(String processInstanceId,
+ List logs,
+ List forecasting,
+ BpmnProcessInstanceLogQueryDTO dto) {
+ List tasks = new ArrayList<>();
+ Map> attachmentByTaskMap =
+ taskService.getProcessInstanceAttachments(processInstanceId).stream()
+ .collect(Collectors.groupingBy(Attachment::getTaskId));
+ // 已完成的和进行中的
+ getHistoricTasks(logs, tasks, attachmentByTaskMap, dto.getVisitor());
+ // 未来节点
+ getFutureTasks(forecasting, tasks);
+ // 处理是否加密
+ handleEncrypt(dto.getEncrypt(), tasks);
+ // reset field forecastAssignees Empty to null;
+ resetCollectionToNull(tasks);
+ return tasks;
+ }
+
+ private void resetCollectionToNull(List tasks) {
+ tasks.forEach(i -> {
+ if (ListUtils.emptyIfNull(i.getForecastAssignees()).isEmpty()) {
+ i.setForecastAssignees(null);
+ }
+ });
+ }
+
+ private static void handleEncrypt(Boolean encrypt, List tasks) {
+ if (Boolean.FALSE.equals(encrypt)) {
+ return;
+ }
+ tasks.forEach(i -> {
+ if (Objects.equals(NODE_STARTER.getType(), i.getTaskDefinitionKey())) {
+ i.setOperationDesc(i.getAssigneeSnapshot().getAssignerName());
+ } else if (Objects.equals(i.getResult(), APPROVED)) {
+ i.setOperationDesc(APPROVED.getDesc());
+ } else if (Objects.equals(i.getResult(), REJECTED)) {
+ i.setOperationDesc(REJECTED.getDesc());
+ } else if (Objects.equals(i.getResult(), PROCESSING) || Objects.isNull(i.getTaskId())) {
+ i.setOperationDesc("待处理");
+ } else {
+ i.setOperationDesc("已处理");
+ }
+ // 统一将多人节点数据全部置空
+ i.setForecastAssignees(null);
+ // 统一将签名数据置空
+ i.setSignatureUrl(null);
+ });
+ }
+
+ private static void getFutureTasks(List forecasting, List tasks) {
+ ListUtils.emptyIfNull(forecasting).forEach(e -> {
+ BpmnTaskInstanceLogVO build = BpmnTaskInstanceLogVO.builder()
+ .taskDefinitionKey(e.getId())
+ .name(e.getName())
+ .approvalMethod(e.getApprovalMethod())
+ .nodeType(e.getNodeType())
+ .nodeMode(e.getNodeMode())
+ .forecastAssignees(e.getForecastAssigners())
+ .build();
+ if (Objects.nonNull(e.getApprovalMethod())) {
+ switch (e.getApprovalMethod()) {
+ case bizSpecify:
+ case nobody:
+ build.setOperationDesc("待处理");
+ break;
+ case autoPassed:
+ case autoRejection:
+ break;
+ case human:
+ if (Objects.equals(e.getNodeMode(), EXCEPTIONAL)) {
+ build.setOperationDesc("节点异常");
+ } else {
+ int countPerson = e.getForecastAssigners().size();
+ if (Objects.equals(BpmnFlowNodeMode.AND, e.getNodeMode())) {
+ build.setOperationDesc(countPerson + "人会签,需要全部同意");
+ } else if (Objects.equals(BpmnFlowNodeMode.OR, e.getNodeMode())) {
+ build.setOperationDesc(countPerson + "人或签,仅一人同意即可");
+ }
+ }
+ break;
+ }
+ }
+ if (Objects.equals(e.getNodeType(), NODE_CARBON_COPY)) {
+ build.setOperationDesc("抄送" + e.getForecastAssigners().size() + "人");
+ }
+ tasks.add(build);
+ });
+ }
+
+ private void getHistoricTasks(List logs,
+ List tasks,
+ Map> attachmentByTaskMap,
+ BpmnTaskDelegateAssigner visitor) {
+ ListUtils.emptyIfNull(logs).forEach(e -> {
+ Optional processingTask = tasks.stream().filter(i -> Objects.equals(PROCESSING, i.getResult()))
+ .filter(i -> Objects.equals(i.getTaskDefinitionKey(), e.getActivityId())).findAny();
+
+ if (processingTask.isPresent()) {
+ // 多实例的情况,需要合并节点
+ processingTask.ifPresent(i -> {
+ List assigners = new ArrayList<>(ListUtils.emptyIfNull(i.getForecastAssignees()));
+ if (CollectionUtils.isEmpty(assigners)) {
+ if (Objects.nonNull(i.getAssigneeSnapshot())) {
+ assigners.add(i.getAssigneeSnapshot());
+ }
+ }
+ assigners.add(BpmnTaskDelegateAssigner.toObjectCompatible(CollectionUtils.isEmpty(ListUtils.emptyIfNull(e.getAssigneeFull())) ? null : e.getAssigneeFull().get(0)));
+ switch (i.getNodeMode()) {
+ case AND:
+ i.setOperationDesc(assigners.size() + "人会签,需要全部同意");
+ break;
+ case OR:
+ i.setOperationDesc(assigners.size() + "人或签,仅一人同意即可");
+ break;
+ default:
+ // 不修改操作描述
+ break;
+ }
+ i.setAssigneeSnapshot(null);
+ i.setForecastAssignees(assigners);
+ i.setButtonConf(e.getButtonConf());
+ // 根据当前登录人重设聚合后的节点 taskId
+ assigners.stream().filter(user -> Objects.equals(user.getPersonId(), visitor.getPersonId())).findFirst()
+ .ifPresent(user -> i.setTaskId(e.getTaskId()));
+ });
+ } else {
+ tasks.add(BpmnTaskInstanceLogVO.builder()
+ .taskId(e.getTaskId())
+ .taskDefinitionKey(e.getActivityId())
+ .name(e.getActivityName())
+ .createTime(e.getStartTime())
+ .endTime(e.getEndTime())
+ .approvalMethod(ApprovalMethodEnum.valueOfType(e.getApprovalMethod()))
+ .nodeType(BpmnFlowNodeType.valueOfType(e.getNodeType()))
+ .nodeMode(BpmnFlowNodeMode.valueOfType(e.getNodeMode()))
+ .result(BpmnProcessInstanceResultEnum.valueOfStatus(e.getStatus()))
+ .operationDesc(e.getOperationDesc())
+ .advice(e.getAdvice())
+ .commentExt("")
+ .buttonConf(e.getButtonConf())
+ .imageList(getAttachmentByType(attachmentByTaskMap, e.getTaskId(), AttachmentTypeEnum.image))
+ .fileList(getAttachmentByType(attachmentByTaskMap, e.getTaskId(), AttachmentTypeEnum.file))
+ .signatureUrl(getAttachmentByType(attachmentByTaskMap, e.getTaskId(), AttachmentTypeEnum.signature).stream().findFirst().orElse(new AttachmentDTO()).getUrl())
+ .assigneeSnapshot(Objects.equals(e.getNodeType(), BpmnFlowNodeType.NODE_CARBON_COPY.getType()) ? null :
+ BpmnTaskDelegateAssigner.toObjectCompatible(CollectionUtils.isEmpty(ListUtils.emptyIfNull(e.getAssigneeFull())) ? null : e.getAssigneeFull().get(0)))
+ .forecastAssignees(Objects.equals(e.getNodeType(), BpmnFlowNodeType.NODE_CARBON_COPY.getType()) ? ListUtils.emptyIfNull(e.getAssigneeFull()) : Collections.emptyList())
+ .build());
+ }
+ });
+
+ }
+
+ public List getAttachmentByType(Map> attachmentByTaskMap, String taskId, AttachmentTypeEnum type) {
+ return ListUtils.emptyIfNull(attachmentByTaskMap.get(taskId)).stream()
+ .filter(attachment -> Objects.equals(type.getType(), attachment.getType()))
+ .map(e -> AttachmentDTO.builder()
+ .id(e.getId())
+ .type(type)
+ .name(e.getName())
+ .description(e.getDescription())
+ .url(e.getUrl())
+ .build())
+ .collect(Collectors.toList());
+ }
+
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessJobServiceImp.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessJobServiceImp.java
index c320b7a73..c336f5f9a 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessJobServiceImp.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessJobServiceImp.java
@@ -8,6 +8,7 @@ import org.flowable.engine.ManagementService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.job.api.Job;
import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.List;
@@ -39,6 +40,31 @@ public class BpmnProcessJobServiceImp implements BpmnProcessJobService {
}
}
+ @Override
+ public String getDeadLetterJobExceptionStacktrace(String processInstId) {
+ List jobs = managementService.createDeadLetterJobQuery().processInstanceId(processInstId).list();
+ if (CollectionUtils.isEmpty(jobs)) {
+ return "";
+ }
+ StringBuilder builder = new StringBuilder();
+ jobs.forEach(job -> {
+ String deadLetterJobExceptionStacktrace = managementService.getDeadLetterJobExceptionStacktrace(job.getId());
+ if (StringUtils.hasText(deadLetterJobExceptionStacktrace)) {
+ builder.append(deadLetterJobExceptionStacktrace);
+ builder.append("\n");
+ }
+ });
+ return builder.toString();
+ }
+
+ @Override
+ public String getDeadLetterJobExceptionStacktraceByJobId(String jobId) {
+ if (!StringUtils.hasText(jobId)) {
+ return "";
+ }
+ return managementService.getDeadLetterJobExceptionStacktrace(jobId);
+ }
+
protected Job getDeadLetterJobById(String jobId) {
Job job = managementService.createDeadLetterJobQuery().jobId(jobId).singleResult();
if (job == null) {
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java
index 86f7d06f8..8930b24a0 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java
@@ -1,11 +1,16 @@
package cn.axzo.workflow.core.service.impl;
+import cn.axzo.framework.domain.ServiceException;
import cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum;
+import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
+import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf;
+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.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;
@@ -25,6 +30,8 @@ import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskCmd;
+import cn.axzo.workflow.core.engine.cmd.CustomBackTaskAsyncCmd;
+import cn.axzo.workflow.core.engine.cmd.CustomBackTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomCommentTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomCompleteDummyTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomCountersignUserTaskAsyncCmd;
@@ -38,6 +45,7 @@ import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder;
import cn.axzo.workflow.core.engine.event.MessagePushEventImpl;
import cn.axzo.workflow.core.engine.event.MessagePushEventType;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
+import cn.axzo.workflow.core.service.BpmnProcessDefinitionService;
import cn.axzo.workflow.core.service.BpmnProcessTaskService;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.service.converter.BpmnHistoricAttachmentConverter;
@@ -48,7 +56,11 @@ import cn.axzo.workflow.core.service.converter.BpmnTaskTodoPageItemConverter;
import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.flowable.bpmn.model.BaseElement;
import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
@@ -80,7 +92,6 @@ import org.flowable.variable.service.impl.persistence.entity.HistoricVariableIns
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
@@ -89,11 +100,13 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -103,16 +116,27 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_COMMEN
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
+import static cn.axzo.workflow.common.constant.BpmnConstants.MAX_BACKED_OPERATE_COUNT;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION;
+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_EMPTY;
+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.BACKED;
+import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.valueOfStatus;
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.BpmnInstanceRespCode.PROCESS_TASK_NOT_EXISTS;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.FIND_TASK_BY_PERSON_ID_ERROR;
+import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.REACHED_BACKED_MAXIMUM_NUM;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_REMIND_ERROR_NOT_EXISTS;
import static cn.axzo.workflow.core.common.utils.BpmnCollectionUtils.convertSet;
import static cn.axzo.workflow.core.common.utils.BpmnNativeQueryUtil.countSql;
@@ -151,6 +175,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Resource
@Lazy
private BpmnProcessTaskService bpmnProcessTaskService;
+ @Resource
+ private BpmnProcessDefinitionService bpmnProcessModelService;
@Override
public BpmPageResult getTodoTaskPage(BpmnTaskPageSearchDTO dto) {
@@ -158,8 +184,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
Long resultTotalCount;
if (CollectionUtils.isEmpty(dto.getResults())) {
HistoricTaskInstanceQuery query =
- historyService.createHistoricTaskInstanceQuery().unfinished().taskAssignee(dto.getUserId()) // 分配给自己
- .orderByTaskCreateTime().desc();
+ historyService.createHistoricTaskInstanceQuery().unfinished().taskAssignee(dto.getUserId()) // 分配给自己
+ .orderByTaskCreateTime().desc();
populateQuery(dto, query);
tasks = query.listPage((dto.getPageNo() - 1) * dto.getPageSize(), dto.getPageSize());
resultTotalCount = query.count();
@@ -178,7 +204,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
// 获得 ProcessInstance Map
Map processInstanceMap =
- processInstanceService.getProcessInstanceMap(processInstanceIds);
+ processInstanceService.getProcessInstanceMap(processInstanceIds);
List vos = todoPageItemConverter.toVos(tasks, processInstanceMap);
return new BpmPageResult<>(vos, resultTotalCount);
@@ -192,8 +218,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
if (CollectionUtils.isEmpty(dto.getResults())) {
// 查询已办任务
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().finished() // 已完成
- .taskAssignee(dto.getUserId()) // 分配给自己
- .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
+ .taskAssignee(dto.getUserId()) // 分配给自己
+ .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
populateQuery(dto, query);
// 执行查询
tasks = query.listPage((dto.getPageNo() - 1) * dto.getPageSize(), dto.getPageSize());
@@ -210,7 +236,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
}
Set processInstanceIds = convertSet(tasks, HistoricTaskInstance::getProcessInstanceId);
Map historicProcessInstanceMap =
- processInstanceService.getHistoricProcessInstanceMap(processInstanceIds);
+ processInstanceService.getHistoricProcessInstanceMap(processInstanceIds);
List vos = donePageItemConverter.toVos(tasks, historicProcessInstanceMap);
return new BpmPageResult<>(vos, resultTotalCount);
}
@@ -234,7 +260,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
NativeHistoricTaskInstanceQuery nativeQuery = historyService.createNativeHistoricTaskInstanceQuery();
String tableName = managementService.getTableName(HistoricTaskInstance.class);
baseQuerySql.append("SELECT a.* FROM ").append(tableName).append(" a JOIN " + "ACT_HI_PROCINST b").append(" " +
- "ON b.PROC_INST_ID_ = a.PROC_INST_ID_");
+ "ON b.PROC_INST_ID_ = a.PROC_INST_ID_");
if (Objects.nonNull(dto.getTenantId())) {
baseQuerySql.append(sqlConnectors(baseQuerySql)).append(" b.TENANT_ID_ = #{tenantId}").append(sqlConnectors(baseQuerySql)).append(" a.TENANT_ID_ = #{tenantId}");
@@ -253,7 +279,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
}
if (StringUtils.hasLength(dto.getCategories())) {
List categories =
- Arrays.stream(dto.getCategories().split(",")).map(String::trim).collect(Collectors.toList());
+ Arrays.stream(dto.getCategories().split(",")).map(String::trim).collect(Collectors.toList());
baseQuerySql.append(sqlConnectors(baseQuerySql)).append(" a.CATEGORY_ in (");
for (int i = 0; i < categories.size(); i++) {
baseQuerySql.append("#{category").append(i).append("}");
@@ -294,7 +320,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
}
if (StringUtils.hasLength(dto.getCategories())) {
List categories =
- Arrays.stream(dto.getCategories().split(",")).map(String::trim).collect(Collectors.toList());
+ Arrays.stream(dto.getCategories().split(",")).map(String::trim).collect(Collectors.toList());
query.processCategoryIn(categories);
}
}
@@ -303,13 +329,140 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Transactional(rollbackFor = Exception.class)
public void approveTask(BpmnTaskAuditDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
- if (dto.getAsync()) {
+ if (Boolean.TRUE.equals(dto.getAsync())) {
commandExecutor.execute(new CustomApproveTaskAsyncCmd(dto));
} else {
commandExecutor.execute(new CustomApproveTaskCmd(dto));
}
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void backTask(BpmnTaskBackAuditDTO dto) {
+ List backOptionalNodes = getBackOptionalNodes(dto.getTaskId());
+ if (CollectionUtils.isEmpty(backOptionalNodes)) {
+ throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
+ }
+ List activityList = backOptionalNodes.stream().map(BpmnOptionalNodeDTO::getProcessActivityId).collect(Collectors.toList());
+ if (!activityList.contains(dto.getToActivityId())) {
+ throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
+ }
+ Optional instOpt = backOptionalNodes.stream()
+ .map(BpmnOptionalNodeDTO::getProcessInstanceId)
+ .filter(StringUtils::hasText)
+ .findAny();
+ if (instOpt.isPresent()) {
+ ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
+ searchDTO.setProcessInstanceId(instOpt.get());
+ List extAxHiTaskInsts = extAxHiTaskInstService.queryList(searchDTO);
+ if (CollectionUtils.isNotEmpty(extAxHiTaskInsts)) {
+ long backOpeCount = extAxHiTaskInsts.stream()
+ .filter(ext -> BACKED.getStatus().equals(ext.getStatus()))
+ .count();
+ if (backOpeCount > MAX_BACKED_OPERATE_COUNT) {
+ throw new WorkflowEngineException(REACHED_BACKED_MAXIMUM_NUM, String.valueOf(MAX_BACKED_OPERATE_COUNT));
+ }
+ }
+ }
+ CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
+ if (Boolean.TRUE.equals(dto.getAsync())) {
+ commandExecutor.execute(new CustomBackTaskAsyncCmd(dto));
+ } else {
+ commandExecutor.execute(new CustomBackTaskCmd(dto));
+ }
+ }
+
+ @Override
+ public List getBackOptionalNodes(String taskId) {
+ Task task = processEngineConfiguration.getTaskService().createTaskQuery().taskId(taskId).singleResult();
+ if (task == null) {
+ throw new WorkflowEngineException(PROCESS_TASK_NOT_EXISTS);
+ }
+ String processInstanceId = task.getProcessInstanceId();
+ //1.获取当前的流程实例
+ ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
+ if (processInstance == null) {
+ //流程为空,已经结束,返回空
+ throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS);
+ }
+ List tasks = this.getHistoricTaskListByProcessInstanceId(processInstanceId, null);
+ tasks = tasks.stream()
+ .filter(t -> t.getNodeType() == NODE_STARTER || t.getNodeType() == NODE_TASK || t.getNodeType() == NODE_BUSINESS)
+ .collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(tasks)) {
+ return Collections.emptyList();
+ }
+ tasks.sort(Comparator.comparing(BpmnHistoricTaskInstanceVO::getCreateTime));
+ LinkedList>> executedList = new LinkedList<>();
+ for (BpmnHistoricTaskInstanceVO vo : tasks) {
+ Pair> last = org.springframework.util.CollectionUtils.isEmpty(executedList) ? null : executedList.getLast();
+ if (last != null && last.getLeft().equals(vo.getTaskDefinitionKey())) {
+ last.getRight().add(vo);
+ continue;
+ }
+ ArrayList objects = new ArrayList<>();
+ objects.add(vo);
+ executedList.addLast(Pair.of(vo.getTaskDefinitionKey(), objects));
+ }
+ List>> valuableList = new LinkedList<>();
+ for (Pair> pair : executedList) {
+ List taskInstanceVOList = pair.getRight();
+ Optional backTaskOpt = taskInstanceVOList
+ .stream()
+ .filter(t -> t.getResult() != null)
+ .filter(t -> t.getResult() == BpmnProcessInstanceResultEnum.BACKED)
+ .findFirst();
+ if (backTaskOpt.isPresent()) {
+ String deleteReason = backTaskOpt.get().getDeleteReason();
+ String changeParentActivityTo = deleteReason
+ .replace("Change parent activity to ", "")
+ .replace("Change activity to ", "");
+ if (org.springframework.util.CollectionUtils.isEmpty(valuableList)) {
+ throw new ServiceException("状态异常,首个节点进行了退回操作");
+ }
+ int j = valuableList.size() - 1;
+ for (; j >= 0; j--) {
+ Pair> vPair = valuableList.get(j);
+ if (vPair.getLeft().equals(changeParentActivityTo)) {
+ break;
+ }
+ }
+ valuableList = valuableList.subList(0, j);
+ } else {
+ valuableList.add(pair);
+ }
+ }
+ List flowElements = bpmnProcessModelService.findFlowElements(processInstance.getProcessDefinitionId());
+ Map flowElementMap = flowElements.stream().collect(Collectors.toMap(BaseElement::getId, f -> f));
+ AtomicInteger index = new AtomicInteger(0);
+ List resultList = valuableList
+ .stream()
+ .filter(pair -> !task.getTaskDefinitionKey().equals(pair.getLeft())) //排除当前节点
+ .map(pair -> flowElementMap.get(pair.getLeft()))
+ .filter(flowElement -> {
+ BpmnFlowNodeType currentNodeType = BpmnMetaParserHelper.getNodeType(flowElement).orElse(NODE_EMPTY);
+ return currentNodeType == NODE_TASK || currentNodeType == NODE_BUSINESS;
+ })
+ .filter(flowElement -> !NODE_STARTER.getType().equals(flowElement.getId()))
+ .map(flowElement -> BpmnOptionalNodeDTO
+ .builder()
+ .processInstanceId(task.getProcessInstanceId())
+ .processDefinitionId(task.getProcessDefinitionId())
+ .processActivityId(flowElement.getId())
+ .processActivityName(flowElement.getName())
+ .processNodeDesc(flowElement.getName())
+ .nodeType(BpmnMetaParserHelper.getNodeType(flowElement).orElse(NODE_EMPTY))
+ .ordinal(index.incrementAndGet())
+ .build())
+ .collect(Collectors.toList());
+ if (CollectionUtils.isNotEmpty(resultList)) {
+ BpmnOptionalNodeDTO bpmnOptionalNodeDTO = resultList.get(resultList.size() - 1);
+ bpmnOptionalNodeDTO.setProcessNodeDesc(bpmnOptionalNodeDTO.getProcessActivityName() + "(上一步)");
+ }
+ resultList.sort((o1, o2) -> o2.getOrdinal() - o1.getOrdinal());
+ return resultList;
+ }
+
@Override
public BatchOperationResultVO batchApproveTask(List dtos) {
return batchOperation(dtos, bpmnProcessTaskService::approveTask);
@@ -335,13 +488,13 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public List getHistoricTaskListByProcessInstanceId(String processInstanceId,
String tenantId) {
HistoricTaskInstanceQuery query =
- historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId);
+ historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId);
if (StringUtils.hasLength(tenantId)) {
query.taskTenantId(tenantId);
}
HistoricProcessInstanceQuery instanceQuery =
- historyService.createHistoricProcessInstanceQuery()
- .processInstanceId(processInstanceId);
+ historyService.createHistoricProcessInstanceQuery()
+ .processInstanceId(processInstanceId);
// .includeProcessVariables();
if (StringUtils.hasLength(tenantId)) {
instanceQuery.processInstanceTenantId(tenantId);
@@ -352,67 +505,72 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
}
List taskInstances = query.orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
- .list();
+ .list();
taskInstances.forEach(task -> ((HistoricTaskInstanceEntity) task).setCreateTime(((HistoricTaskInstanceEntity) task).getLastUpdateTime()));
taskInstances.sort(Comparator.comparing(p -> ((HistoricTaskInstanceEntity) p).getLastUpdateTime()));
Map variableInstanceMap =
- // 不能使用框架提供的历史变量 API 查询,有 BUG
- historyService.createNativeHistoricVariableInstanceQuery()
- .sql("select * from ACT_HI_VARINST t where t.proc_inst_id_= #{processInstanceId}")
- .parameter("processInstanceId", processInstanceId)
- .list().stream()
- .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, Function.identity(),
- (s, t) -> s));
+ // 不能使用框架提供的历史变量 API 查询,有 BUG
+ historyService.createNativeHistoricVariableInstanceQuery()
+ .sql("select * from ACT_HI_VARINST t where t.proc_inst_id_= #{processInstanceId}")
+ .parameter("processInstanceId", processInstanceId)
+ .list().stream()
+ .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, Function.identity(),
+ (s, t) -> s));
HistoricVariableInstance instanceVersion = variableInstanceMap.getOrDefault(WORKFLOW_ENGINE_VERSION, null);
// 过滤了多实例或签自动完成的任务
List vos = historicTaskInstanceConverter.toVosSkipSystemOperation(taskInstances,
- Objects.isNull(instanceVersion) ? null :
- ((HistoricVariableInstanceEntityImpl) instanceVersion).getTextValue());
+ Objects.isNull(instanceVersion) ? null :
+ ((HistoricVariableInstanceEntityImpl) instanceVersion).getTextValue());
Map> commentByTaskIdMap =
- taskService.getProcessInstanceComments(processInstanceId).stream()
- .collect(Collectors.groupingBy(Comment::getTaskId));
+ taskService.getProcessInstanceComments(processInstanceId).stream()
+ .collect(Collectors.groupingBy(Comment::getTaskId));
Map> attachmentByTaskIdMap =
- taskService.getProcessInstanceAttachments(processInstanceId).stream()
- .collect(Collectors.groupingBy(Attachment::getTaskId));
+ taskService.getProcessInstanceAttachments(processInstanceId).stream()
+ .collect(Collectors.groupingBy(Attachment::getTaskId));
ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
searchDTO.setProcessInstanceId(processInstanceId);
Map extTaskInstMap = extAxHiTaskInstService.queryList(searchDTO).stream()
- .collect(Collectors.toMap(ExtAxHiTaskInst::getTaskId, Function.identity(), (s, t) -> s));
+ .collect(Collectors.toMap(ExtAxHiTaskInst::getTaskId, Function.identity(), (s, t) -> s));
BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
+ List resultList = new ArrayList<>();
for (BpmnHistoricTaskInstanceVO vo : vos) {
ExtAxHiTaskInst taskInst = extTaskInstMap.getOrDefault(vo.getTaskId(), new ExtAxHiTaskInst());
+ if (Objects.equals(taskInst.getStatus(), DELETED.getStatus())) {
+ continue;
+ }
+ resultList.add(vo);
vo.setResult(valueOfStatus(taskInst.getStatus()));
List taskComments = commentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList());
// 处理 advice
taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId()))
- .filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_ADVICE)).findFirst()
- .ifPresent(i -> vo.setAdvice(i.getFullMessage()));
+ .filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_ADVICE)).findFirst()
+ .ifPresent(i -> vo.setAdvice(i.getFullMessage()));
// 处理 operationDesc
taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId()))
- .filter(i -> Objects.equals(COMMENT_TYPE_OPERATION_DESC, i.getType()))
- .max(Comparator.comparing(Comment::getTime))
- .ifPresent(i -> vo.setOperationDesc(i.getFullMessage()));
+ .filter(i -> Objects.equals(COMMENT_TYPE_OPERATION_DESC, i.getType()))
+ .max(Comparator.comparing(Comment::getTime))
+ .ifPresent(i -> vo.setOperationDesc(i.getFullMessage()));
// 处理 CommentExt
taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId()))
- .filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_COMMENT_EXT)).findFirst()
- .ifPresent(i -> vo.setCommentExt(i.getFullMessage()));
+ .filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_COMMENT_EXT)).findFirst()
+ .ifPresent(i -> vo.setCommentExt(i.getFullMessage()));
List attachments = attachmentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList());
vo.setAttachments(attachmentConverter.toVos(attachments));
HistoricVariableInstance assginerSnapshot =
- variableInstanceMap.getOrDefault(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + vo.getTaskId(),
- null);
+ variableInstanceMap.getOrDefault(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + vo.getTaskId(),
+ null);
if (Objects.isNull(assginerSnapshot)) {
assginerSnapshot =
- variableInstanceMap.getOrDefault(OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + vo.getTaskId(), null);
+ variableInstanceMap.getOrDefault(OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + vo.getTaskId(), null);
}
BpmnTaskDelegateAssigner assigner = null;
@@ -426,17 +584,17 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
vo.setNodeType(nodeType);
if (Objects.equals(NODE_CARBON_COPY, nodeType)) {
HistoricVariableInstance carbonUsers =
- variableInstanceMap.getOrDefault(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + vo.getTaskDefinitionKey(), null);
+ variableInstanceMap.getOrDefault(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + vo.getTaskDefinitionKey(), null);
vo.setForecastAssignees(Objects.isNull(carbonUsers) ? Collections.emptyList() :
- (List) carbonUsers.getValue());
+ (List) carbonUsers.getValue());
} else {
vo.setForecastAssignees(Collections.emptyList());
}
});
BpmnMetaParserHelper.getApprovalMethod(bpmnModel.getFlowElement(vo.getTaskDefinitionKey()))
- .ifPresent(vo::setApprovalMethod);
+ .ifPresent(vo::setApprovalMethod);
}
- return vos;
+ return resultList;
}
/**
@@ -448,11 +606,11 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
*/
@Override
public List getHistoricTaskListGroupByProcessInstanceId(String processInstanceId
- , String tenantId) {
+ , String tenantId) {
List vos = getHistoricTaskListByProcessInstanceId(processInstanceId, tenantId);
Map> voMapByTaskDefKey =
- vos.stream().collect(Collectors.groupingBy(BpmnHistoricTaskInstanceVO::getTaskDefinitionKey));
+ vos.stream().collect(Collectors.groupingBy(BpmnHistoricTaskInstanceVO::getTaskDefinitionKey));
List groupVos = new ArrayList<>();
for (Map.Entry> entry : voMapByTaskDefKey.entrySet()) {
@@ -490,9 +648,9 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
}
List vos = bpmnTaskConverter.toVos(query.list());
List snapshotTaskIds =
- vos.stream().map(i -> INTERNAL_TASK_RELATION_ASSIGNEE_INFO + i.getTaskId()).collect(Collectors.toList());
+ vos.stream().map(i -> INTERNAL_TASK_RELATION_ASSIGNEE_INFO + i.getTaskId()).collect(Collectors.toList());
Map instanceMap = runtimeService.getVariableInstances(processInstanceId,
- snapshotTaskIds);
+ snapshotTaskIds);
vos.forEach(i -> i.setAssigner(BpmnTaskDelegateAssigner.toObjectCompatible(instanceMap.get(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + i.getTaskId()).getValue())));
return vos;
}
@@ -526,7 +684,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
commandExecutor.execute(new CustomTransferUserTaskAsyncCmd(dto));
} else {
commandExecutor.execute(new CustomTransferUserTaskCmd(dto.getTaskId(),
- dto.getOriginAssigner(), dto.getAdvice(), dto.getAttachmentList(), dto.getTargetAssigner()));
+ dto.getOriginAssigner(), dto.getAdvice(), dto.getAttachmentList(), dto.getTargetAssigner()));
}
}
@@ -568,8 +726,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public void commentTask(BpmnTaskCommentDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new CustomCommentTaskCmd(dto.getProcessInstanceId(),
- dto.getOperator(), dto.getComment(), dto.getCommentExt(), dto.getAttachmentList(),
- extAxHiTaskInstService));
+ dto.getOperator(), dto.getComment(), dto.getCommentExt(), dto.getAttachmentList(),
+ extAxHiTaskInstService));
}
@Override
@@ -577,8 +735,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public void attachmentTask(BpmnTaskAttachmentDTO dto, String assignee) {
Authentication.setAuthenticatedUserId(assignee);
Attachment attachment = taskService.createAttachment(dto.getType(), dto.getTaskId(),
- dto.getProcessInstanceId(), dto.getName(), dto.getDescription(),
- dto.getUrl());
+ dto.getProcessInstanceId(), dto.getName(), dto.getDescription(),
+ dto.getUrl());
taskService.saveAttachment(attachment);
Authentication.setAuthenticatedUserId(null);
}
@@ -592,8 +750,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
commandExecutor.execute(new CustomCountersignUserTaskAsyncCmd(dto));
} else {
commandExecutor.execute(new CustomCountersignUserTaskCmd(BpmnCountersignTypeEnum.valueOfType(dto.getCountersignType()), dto.getTaskId(),
- dto.getOriginAssigner(), dto.getAdvice(), dto.getAttachmentList(), dto.getTargetAssignerList(),
- extAxHiTaskInstService));
+ dto.getOriginAssigner(), dto.getAdvice(), dto.getAttachmentList(), dto.getTargetAssignerList(),
+ extAxHiTaskInstService));
}
}
@@ -614,22 +772,22 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
ProcessInstance processInstance =
- runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessInstanceId()).singleResult();
+ runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessInstanceId()).singleResult();
Optional noticeConfig =
- BpmnMetaParserHelper.getNoticeConfig(ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId()));
+ BpmnMetaParserHelper.getNoticeConfig(ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId()));
List assigners =
- (List) runtimeService.getVariable(dto.getProcessInstanceId(),
- INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + dto.getTaskDefinitionKey());
+ (List) runtimeService.getVariable(dto.getProcessInstanceId(),
+ INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + dto.getTaskDefinitionKey());
// 过滤出未审批的任何,用选择的方式去发送消息
dto.getRemindTypes().forEach(type -> {
list.forEach(task -> {
assigners.stream().filter(i -> Objects.equals(task.getAssignee(), i.buildAssigneeId())).findAny().ifPresent(assigner -> {
MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.valueOf(type),
- Lists.newArrayList(assigner), noticeConfig.orElse(null),
- processInstance.getProcessInstanceId(),
- processInstance.getProcessDefinitionKey(),
- processInstance.getTenantId(), task.getId());
+ Lists.newArrayList(assigner), noticeConfig.orElse(null),
+ processInstance.getProcessInstanceId(),
+ processInstance.getProcessDefinitionKey(),
+ processInstance.getTenantId(), task.getId());
event.setProcessInstanceId(processInstance.getProcessInstanceId());
event.setTenantId(processInstance.getTenantId());
event.setTaskId(task.getId());
@@ -644,8 +802,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public String createRobotTask(BpmnRobotTaskCreateDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
return commandExecutor.execute(new CustomCreateDummyTaskCmd(dto.getProcessInstanceId(),
- dto.getRobotNode().getFlowNodeName(), dto.getRobotNode().getOperationDesc(), dto.getApprover(),
- extAxHiTaskInstService));
+ dto.getRobotNode().getFlowNodeName(), dto.getRobotNode().getOperationDesc(), dto.getApprover(),
+ extAxHiTaskInstService));
}
@Override
@@ -653,17 +811,17 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public void completeRobotTask(BpmnRobotTaskCompleteDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new CustomCompleteDummyTaskCmd(dto.getProcessInstanceId(), dto.getTaskId(),
- Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getFlowNodeName(),
- Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getOperationDesc(),
- extAxHiTaskInstService));
+ Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getFlowNodeName(),
+ Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getOperationDesc(),
+ extAxHiTaskInstService));
}
@Override
public String findTaskIdByInstanceIdAndPersonId(String processInstanceId, String personId) {
List list = taskService.createTaskQuery().processInstanceId(processInstanceId)
- .taskAssigneeLike("%" + personId)
- .active()
- .list();
+ .taskAssigneeLike("%" + personId)
+ .active()
+ .list();
if (CollectionUtils.isEmpty(list) || list.size() > 1) {
throw new WorkflowEngineException(FIND_TASK_BY_PERSON_ID_ERROR, processInstanceId, personId);
}
@@ -673,9 +831,9 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Override
public Map findTaskIdByInstanceIdsAndPersonId(List processInstanceIds, String personId) {
List tasks = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds)
- .taskAssigneeLike("%" + personId)
- .active()
- .list();
+ .taskAssigneeLike("%" + personId)
+ .active()
+ .list();
if (CollectionUtils.isEmpty(tasks)) {
return new HashMap<>();
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessLogServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessLogServiceImpl.java
new file mode 100644
index 000000000..c9c1e8005
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessLogServiceImpl.java
@@ -0,0 +1,91 @@
+package cn.axzo.workflow.core.service.impl;
+
+import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
+import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog;
+import cn.axzo.workflow.core.repository.mapper.ExtAxProcessLogMapper;
+import cn.axzo.workflow.core.service.ExtAxProcessLogService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Objects;
+
+import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_ID;
+import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID;
+import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
+import static cn.axzo.workflow.common.constant.BpmnConstants.ROBOT_ASSIGNEE_ID;
+
+/**
+ * Api Log 表操服务实现
+ *
+ * @author wangli
+ * @since 2024/4/3 10:41
+ */
+@Service
+@Slf4j
+public class ExtAxProcessLogServiceImpl implements ExtAxProcessLogService {
+ @Resource
+ private ExtAxProcessLogMapper extAxProcessLogMapper;
+
+ @Override
+ public Long insert(ExtAxProcessLog log) {
+ extAxProcessLogMapper.insert(log);
+ return log.getId();
+ }
+
+ @Override
+ public void delete(ExtAxProcessLog deleteLog) {
+ extAxProcessLogMapper.delete(buildQueryWrapper(deleteLog));
+ }
+
+ public void updateById(ExtAxProcessLog updateLog) {
+ extAxProcessLogMapper.updateById(updateLog);
+ }
+
+ public void update(ExtAxProcessLog query, ExtAxProcessLog update) {
+ extAxProcessLogMapper.update(update, buildQueryWrapper(query));
+ }
+
+ @Override
+ public void updateAssignee(ExtAxProcessLog queryLog, BpmnTaskDelegateAssigner assignee) {
+ updateAssignee(queryLog, assignee, assignee.getAssignerName());
+
+ }
+
+ @Override
+ public void updateAssignee(ExtAxProcessLog queryLog, BpmnTaskDelegateAssigner assignee, String operationDesc) {
+ List filterAssignee = Lists.newArrayList(NO_ASSIGNEE, HIDDEN_ASSIGNEE_ID, ROBOT_ASSIGNEE_ID,
+ DUMMY_ASSIGNEE_ID);
+ if (Objects.isNull(assignee) || filterAssignee.contains(assignee.buildAssigneeId())) {
+ return;
+ }
+ ExtAxProcessLog update = new ExtAxProcessLog();
+ update.setOperationDesc(StringUtils.hasText(operationDesc) ? operationDesc : assignee.getAssignerName());
+ update.setAssigneeFull(Lists.newArrayList(assignee));
+ update.setAssigneeId(Long.valueOf(assignee.getPersonId()));
+ update.setAssigneeTenantId(assignee.getTenantId());
+ update.setAssigneeOuId(assignee.getOuId());
+ update.setAssigneeName(assignee.getAssignerName());
+ extAxProcessLogMapper.update(update, buildQueryWrapper(queryLog));
+ }
+
+ @Override
+ public List genericQuery(ExtAxProcessLog query) {
+ return extAxProcessLogMapper.selectList(buildQueryWrapper(query));
+ }
+
+ LambdaQueryWrapper buildQueryWrapper(ExtAxProcessLog log) {
+ return new LambdaQueryWrapper()
+ .eq(Objects.nonNull(log.getId()), ExtAxProcessLog::getId, log.getId())
+ .eq(StringUtils.hasText(log.getProcessInstanceId()), ExtAxProcessLog::getProcessInstanceId, log.getProcessInstanceId())
+ .eq(StringUtils.hasText(log.getActivityId()), ExtAxProcessLog::getActivityId, log.getActivityId())
+ .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(ExtAxProcessLog::getIsDelete, 0);
+ }
+}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java
index 96ebf4e17..a28d48fbd 100644
--- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java
@@ -6,7 +6,6 @@ import cn.axzo.workflow.core.service.support.forecast.Forecast;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.StartEvent;
-import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
@@ -98,8 +97,6 @@ public class FlowNodeForecastService implements InitializingBean {
@Resource
private RepositoryService repositoryService;
@Resource
- private HistoryService historyService;
- @Resource
private RuntimeService runtimeService;
@Resource
private List> forecasts;
@@ -114,15 +111,31 @@ public class FlowNodeForecastService implements InitializingBean {
*/
public List performProcessForecasting(@Nullable String processInstanceId,
@Nullable ProcessInstance instance) {
+ return performProcessForecasting(processInstanceId, instance, null, null);
+ }
+
+ /**
+ * 执行运行中的流程预测
+ *
+ * 已完成的流程可以直接查询流程审批记录就行
+ *
+ * @param processInstanceId 指定运行时的流程实例 ID
+ * @param instance 外部传入流程实例 (与另外一个参数必须二选一)
+ * @param taskDefinitionKey 从哪个节点开始推测
+ * @param containSelf 配合第三个参数使用,为 true 时,推送的起始节点为第三个参数,否则,以第三个参数的下个节点开始
+ */
+ public List performProcessForecasting(@Nullable String processInstanceId,
+ @Nullable ProcessInstance instance,
+ @Nullable String taskDefinitionKey,
+ Boolean containSelf) {
if (Objects.nonNull(instance)) {
processInstanceId = instance.getProcessInstanceId();
} else if (!StringUtils.hasLength(processInstanceId)) {
throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS);
} else {
instance = runtimeService.createProcessInstanceQuery()
- .processInstanceId(processInstanceId)
- // .includeProcessVariables()
- .singleResult();
+ .processInstanceId(processInstanceId)
+ .singleResult();
}
if (Objects.isNull(instance)) {
throw new WorkflowEngineException(RUNNING_INSTANCE_ONLY_FORECAST);
@@ -134,10 +147,27 @@ public class FlowNodeForecastService implements InitializingBean {
// 流程定义中所有的FlowElement
Collection flowElements = bpmnModel.getMainProcess().getFlowElements();
- // 开始节点
- findStartNode(flowElements).ifPresent(startNode -> {
- addOrderFlowNodes(orderedNodes, startNode);
- });
+ if (StringUtils.hasText(taskDefinitionKey)) {
+ if (Boolean.TRUE.equals(containSelf)) {
+ flowElements.stream().filter(e -> Objects.equals(e.getId(), taskDefinitionKey)).findFirst().ifPresent(orderedNodes::add);
+ } else {
+ FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
+ String finalProcessInstanceId = processInstanceId;
+ forecasts.forEach(i -> {
+ if (i.support(flowElement)) {
+ List list = i.nextFlowElement(flowElement, finalProcessInstanceId);
+ if (!CollectionUtils.isEmpty(list)) {
+ addOrderFlowNodes(orderedNodes, list.get(0));
+ }
+ }
+ });
+ }
+ } else {
+ // 开始节点
+ findStartNode(flowElements).ifPresent(startNode -> {
+ addOrderFlowNodes(orderedNodes, startNode);
+ });
+ }
startForecasting(orderedNodes, processInstanceId);
return orderedNodes;
@@ -165,8 +195,8 @@ public class FlowNodeForecastService implements InitializingBean {
*/
private Optional findStartNode(Collection flowElements) {
return Optional.ofNullable(flowElements.stream().filter(StartEvent.class::isInstance).findFirst()
- .map(StartEvent.class::cast)
- .orElseThrow(() -> new IllegalArgumentException("非法的参数, 正确的流程定义一定有一个 StartEvent 节点")));
+ .map(StartEvent.class::cast)
+ .orElseThrow(() -> new IllegalArgumentException("非法的参数, 正确的流程定义一定有一个 StartEvent 节点")));
}
@@ -187,7 +217,7 @@ public class FlowNodeForecastService implements InitializingBean {
public void afterPropertiesSet() {
forecasts.forEach(i -> {
Class> rawClass = ResolvableType.forClass(i.getClass(), Forecast.class)
- .getSuperType().getGenerics()[0].getRawClass();
+ .getSuperType().getGenerics()[0].getRawClass();
FORECAST_MAP.put(rawClass, (AbstractForecast extends FlowElement>) i);
});
}
diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/util/DingTalkUtils.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/util/DingTalkUtils.java
new file mode 100644
index 000000000..55f0c5e92
--- /dev/null
+++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/util/DingTalkUtils.java
@@ -0,0 +1,170 @@
+package cn.axzo.workflow.core.util;
+
+import cn.axzo.workflow.common.model.dto.AlterDTO;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiRobotSendRequest;
+import com.dingtalk.api.response.OapiRobotSendResponse;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.MDC;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static cn.azxo.framework.common.constatns.Constants.CTX_LOG_ID_MDC;
+
+/**
+ * 钉钉告警处理
+ *
+ * @author wangli
+ * @since 2024/1/12 14:34
+ */
+@Slf4j
+public class DingTalkUtils {
+
+ private static final String dingtalk_robot_webhook = "https://oapi.dingtalk" +
+ ".com/robot/send?access_token=341ee2907f3ebc15dc495fb7771a646230058710999fec7838066c109849878e";
+
+ private static final Map ENV_URL_MAPPING = new HashMap<>();
+ private static final Map WEB_URL_MAPPING = new HashMap<>();
+
+ static {
+ ENV_URL_MAPPING.put("local", "https://dev-app.axzo.cn");
+ ENV_URL_MAPPING.put("dev", "https://dev-app.axzo.cn");
+ ENV_URL_MAPPING.put("test", "https://test-api.axzo.cn");
+ ENV_URL_MAPPING.put("pre", "https://pre-api.axzo.cn");
+ ENV_URL_MAPPING.put("live", "https://live-api.axzo.cn");
+ ENV_URL_MAPPING.put("default", "https://api.axzo.cn");
+
+ WEB_URL_MAPPING.put("local", "https://dev-new-workflow-web.axzo.cn");
+ WEB_URL_MAPPING.put("dev", "https://dev-new-workflow-web.axzo.cn");
+ WEB_URL_MAPPING.put("test", "https://test-new-workflow-web.axzo.cn");
+ WEB_URL_MAPPING.put("pre", "https://pre-new-workflow-web.axzo.cn");
+ WEB_URL_MAPPING.put("live", "https://live-new-workflow-web.axzo.cn");
+ WEB_URL_MAPPING.put("default", "https://new-workflow-web.axzo.cn");
+ }
+
+ public static String getEnvUrl(String profile) {
+ String urlPrefix = ENV_URL_MAPPING.get(profile);
+ if (!StringUtils.hasText(urlPrefix)) {
+ urlPrefix = ENV_URL_MAPPING.get("default");
+ }
+ return urlPrefix;
+ }
+
+ public static String getWebUrl(String profile) {
+ String urlPrefix = WEB_URL_MAPPING.get(profile);
+ if (!StringUtils.hasText(urlPrefix)) {
+ urlPrefix = WEB_URL_MAPPING.get("default");
+ }
+ return urlPrefix;
+ }
+
+ @SneakyThrows
+ public static void sendDingTalk(String profile, String title, Object req, String invokeServerName, Throwable throwable) {
+ OapiRobotSendRequest request = new OapiRobotSendRequest();
+ request.setMsgtype("markdown");
+ OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
+ markdown.setTitle("Notice " + title + ", Env: " + profile);
+ String text = "#### [" + profile + "]" + title + "\n" +
+ "> 入参: " + JSONUtil.toJsonStr(req) + "\n\n" +
+ "> ###### 异常信息: " + JSONUtil.toJsonStr(Objects.isNull(throwable.getCause()) ? throwable.getMessage() :
+ throwable.getCause().getMessage()) + " \n\n" +
+ "> ##### traceId: " + MDC.get(CTX_LOG_ID_MDC) + " \n" +
+ "> ##### 调用方服务名称:" + invokeServerName + " \n";
+ String deadLetterStacktrace = getDeadLetterStacktrace(profile, req);
+ text = text + (StringUtils.hasText(deadLetterStacktrace) ? "> ##### [点击查看异常明细](" + deadLetterStacktrace + ") \n" : "");
+ markdown.setText(text);
+ request.setMarkdown(markdown);
+ sendDingTalk(request);
+ }
+
+ private static String getDeadLetterStacktrace(String profile, Object req) {
+ try {
+ JSONObject entries = JSONUtil.parseObj(req);
+ String jobId = entries.getStr("id");
+ if (!StringUtils.hasText(jobId)) {
+ return "";
+ }
+ return getEnvUrl(profile) + "/workflow-engine/web/v1/api/process/job/dead-letter/exception/stacktrace/byId?jobId=" + jobId;
+ } catch (Exception e) {
+ log.warn("构造查询错误堆栈地址异常", e);
+ return "";
+ }
+ }
+
+ @SneakyThrows
+ public static void sendDingTalkForSlowUrl(String profile, Double time, String apiUrl, Object requestParam, Object responseBody) {
+ OapiRobotSendRequest request = new OapiRobotSendRequest();
+ request.setMsgtype("markdown");
+ OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
+ markdown.setTitle("Notice 请求二方接口慢 URL, Env: " + profile);
+ markdown.setText("#### [" + profile + "]请求二方接口过慢\n" +
+ "> 接口地址: " + apiUrl + ",经过了" + time + "秒\n\n" +
+ "> 请求参数: " + JSONUtil.toJsonStr(requestParam) + "\n\n" +
+ "> ###### 结果: " + JSONUtil.toJsonStr(responseBody) + " \n");
+ request.setMarkdown(markdown);
+ sendDingTalk(request);
+ }
+
+ @SneakyThrows
+ public static void sendDingTalkForSlowApi(String profile, Double time, String apiUrl, String uniqueId) {
+ OapiRobotSendRequest request = new OapiRobotSendRequest();
+ request.setMsgtype("markdown");
+ OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
+ markdown.setTitle("Notice API 处理耗时报告, Env: " + profile);
+ markdown.setText("#### [" + profile + "]API 处理耗时报告\n" +
+ "> 接口地址: " + apiUrl + ",经过了" + time + "秒\n\n" +
+ "> ###### 标识: " + uniqueId + " \n");
+ request.setMarkdown(markdown);
+ sendDingTalk(request);
+ }
+
+ @SneakyThrows
+ private static void sendDingTalk(OapiRobotSendRequest request) {
+ DingTalkClient client = new DefaultDingTalkClient(dingtalk_robot_webhook);
+ OapiRobotSendResponse response = client.execute(request);
+ }
+
+ public static void sendDingTalkForBizNodeAlter(String profile, AlterDTO alterDTO, List atMobiles) {
+ if (CollectionUtils.isEmpty(atMobiles)) {
+ return;
+ }
+ String processInstanceId = alterDTO.getProcessInstanceId();
+ 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" +
+ "> ##### [点击查看审批日志](" + getWebUrl(profile) + "/#/workflow/examples?processInstanceId=" + processInstanceId + ") \n\n" +
+ "> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询");
+ request.setMarkdown(markdown);
+ OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
+ at.setAtMobiles(atMobiles);
+ at.setIsAtAll(false);
+ request.setAt(at);
+// sendDingTalk(request);
+ }
+
+ public static void sendDingTalkForTransferToAdminError(String profile, String processInstanceId, String taskDefinitionKey, Object orgScopes) {
+ OapiRobotSendRequest request = new OapiRobotSendRequest();
+ request.setMsgtype("markdown");
+ OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
+ markdown.setTitle("Notice 转交管理员后的审批人为空, Env: " + profile);
+ markdown.setText("#### [" + profile + "]转交管理员后的审批人为空\n" +
+ "> 流程实例 ID: " + processInstanceId + "\n\n" +
+ "> 节点参数(节点 ID): " + taskDefinitionKey + "\n\n" +
+ "> OrgScopes 参数信息: " + JSONUtil.toJsonStr(orgScopes) + "\n\n" +
+ "> ##### 提示:仅作为提示信息,不代表是异常");
+ request.setMarkdown(markdown);
+ sendDingTalk(request);
+ }
+}
diff --git a/workflow-engine-core/src/main/resources/sql/upgrade_to_1.4.2.sql b/workflow-engine-core/src/main/resources/sql/upgrade_to_1.4.2.sql
new file mode 100644
index 000000000..78de441ee
--- /dev/null
+++ b/workflow-engine-core/src/main/resources/sql/upgrade_to_1.4.2.sql
@@ -0,0 +1,33 @@
+create table `workflow-engine`.ext_ax_process_log
+(
+ id bigint auto_increment comment '主键'
+ primary key,
+ process_instance_id varchar(64) default '' not null comment '流程实例 ID',
+ tenant_id varchar(64) default '' not null comment '实例归属租户',
+ activity_id varchar(64) default '' not null comment '节点 ID',
+ activity_name varchar(255) default '' not null comment '节点名称',
+ approval_method varchar(32) default '' not null comment '审批方式:配置审批人/业务指定/业务触发(不含人)',
+ node_type varchar(32) default '' not null comment '节点类型:审批节点/业务节点/评论节点/抄送节点',
+ node_mode varchar(32) default '' not null comment '节点模式:会签/或签',
+ task_id varchar(64) default '' not null comment '任务 ID',
+ advice varchar(4000) default '' not null comment '操作建议',
+ operation_desc varchar(4000) default '' not null comment '操作描述',
+ assignee_full json null comment '审批人(JSON)',
+ assignee_id bigint default 0 not null comment '审批人标识(axzo=personId)',
+ assignee_tenant_id varchar(255) default '' not null comment '审批人归属租户',
+ assignee_name varchar(255) default '' not null comment '审批人姓名',
+ assignee_ou_id varchar(64) default '' not null comment '审批人归属单位',
+ start_time datetime(3) default CURRENT_TIMESTAMP(3) not null comment '任务开始时间',
+ end_time datetime(3) null comment '任务结束时间',
+ button_conf json null comment '按钮配置',
+ status varchar(16) default '' not null comment '任务状态:审批中/通过/驳回/转交/加签',
+ extra json null comment '扩展字段',
+ create_at datetime default CURRENT_TIMESTAMP not null comment '创建时间',
+ update_at datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
+ is_delete bigint default 0 not null comment '是否删除'
+) comment '审批日志持久化';
+
+create unique index idx_process_task_status
+ on ext_ax_process_log (process_instance_id asc, task_id asc, status asc, create_at desc);
+
+
diff --git a/workflow-engine-server/pom.xml b/workflow-engine-server/pom.xml
index afb7ae83a..46a9a9419 100644
--- a/workflow-engine-server/pom.xml
+++ b/workflow-engine-server/pom.xml
@@ -21,10 +21,6 @@
3.25.0