Merge branch 'release/20241028' into 'master'

Release/20241028

See merge request universal/infrastructure/backend/workflow-engine!9
This commit is contained in:
王粒 2024-10-28 09:31:22 +00:00
commit 2a339ead96
145 changed files with 5331 additions and 891 deletions

2
.gitignore vendored
View File

@ -18,6 +18,8 @@ target/
*.iws *.iws
*.iml *.iml
*.ipr *.ipr
WorkflowCoreService.java
WorkflowManageService.java
### NetBeans ### ### NetBeans ###
/nbproject/private/ /nbproject/private/

View File

@ -16,7 +16,7 @@
<name>workflow-engine</name> <name>workflow-engine</name>
<properties> <properties>
<revision>1.4.1-SNAPSHOT</revision> <revision>1.4.2-SNAPSHOT</revision>
<axzo-bom.version>2.0.0-SNAPSHOT</axzo-bom.version> <axzo-bom.version>2.0.0-SNAPSHOT</axzo-bom.version>
<axzo-dependencies.version>2.0.0-SNAPSHOT</axzo-dependencies.version> <axzo-dependencies.version>2.0.0-SNAPSHOT</axzo-dependencies.version>
<feign-httpclient.version>11.8</feign-httpclient.version> <feign-httpclient.version>11.8</feign-httpclient.version>

View File

@ -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 {
}

View File

@ -1,9 +1,11 @@
package cn.axzo.workflow.client.feign.bpmn; 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.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO; 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.activity.BpmnActivityTimeoutTriggerDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO; 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 cn.azxo.framework.common.model.CommonResponse;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -21,16 +23,27 @@ import javax.validation.constraints.NotBlank;
* @since 2023/11/17 16:28 * @since 2023/11/17 16:28
*/ */
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
public interface ProcessActivityApi { public interface ProcessActivityApi {
/** /**
* 业务节点唤醒 * 业务节点唤醒, 该节点废弃请换成 {@link ProcessActivityApi#trigger(cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO)} 接口
* <p> * <p>
* 当模型中使用了业务节点且设置了不设置审批人模式则当业务监听到 PROCESS_ACTIVITY_START 事件时可通过该接口推动流程继续运行 * 当模型中使用了业务节点且设置了不设置审批人模式则当业务监听到 PROCESS_ACTIVITY_START 事件时可通过该接口推动流程继续运行
*/ */
@Deprecated
@GetMapping("/api/process/activity/trigger") @GetMapping("/api/process/activity/trigger")
CommonResponse<Boolean> trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId); CommonResponse<Boolean> trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId);
/**
* 业务节点唤醒
*
* @param dto
* @return
*/
@PostMapping("/api/process/activity/trigger")
CommonResponse<Boolean> trigger(@Validated @RequestBody BpmnActivityTriggerDTO dto);
/** /**
* 业务节点设置审批人, 不支持重复设置 * 业务节点设置审批人, 不支持重复设置

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn; 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.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable; 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 * @since 2023/9/21 16:25
*/ */
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
@Manageable @Manageable
public interface ProcessDefinitionApi { public interface ProcessDefinitionApi {

View File

@ -1,6 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn; 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.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable; import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO; 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.BpmnProcessInstanceCheckApproverDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO; 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.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.BpmnProcessInstanceMyPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO; 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.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO; 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.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.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import cn.azxo.framework.common.model.CommonResponse; import cn.azxo.framework.common.model.CommonResponse;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping; 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 * @since 2023/9/21 16:26
*/ */
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
public interface ProcessInstanceApi { public interface ProcessInstanceApi {
/** /**
* 创建审批流程 * 创建审批流程
@ -241,4 +243,15 @@ public interface ProcessInstanceApi {
@Manageable @Manageable
@InvokeMode(SYNC) @InvokeMode(SYNC)
CommonResponse<Boolean> checkInstanceApprover(@Validated @RequestBody BpmnProcessInstanceCheckApproverDTO dto); CommonResponse<Boolean> checkInstanceApprover(@Validated @RequestBody BpmnProcessInstanceCheckApproverDTO dto);
/**
* 获取指定流程的日志
*
* @param dto
* @return
*/
@Operation(summary = "获取指定流程的日志")
@PostMapping("/api/process/instance/logs")
@InvokeMode(SYNC)
CommonResponse<BpmnProcessInstanceLogVO> getProcessInstanceLogs(@Validated @RequestBody BpmnProcessInstanceLogQueryDTO dto);
} }

View File

@ -1,16 +1,13 @@
package cn.axzo.workflow.client.feign.bpmn; 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.annotation.Manageable;
import cn.azxo.framework.common.model.CommonResponse; 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.GetMapping;
import org.springframework.web.bind.annotation.RequestParam; 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) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
public interface ProcessJobApi { public interface ProcessJobApi {
/** /**
@ -27,4 +24,22 @@ public interface ProcessJobApi {
@Manageable @Manageable
CommonResponse<Void> executeDeadLetterJobAction(@RequestParam(required = false) String jobId, CommonResponse<Void> executeDeadLetterJobAction(@RequestParam(required = false) String jobId,
@RequestParam(required = false) String procInstId); @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);
} }

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn; 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.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable; 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 * @since 2023/9/21 15:47
*/ */
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
@Manageable @Manageable
public interface ProcessModelApi { public interface ProcessModelApi {

View File

@ -1,12 +1,14 @@
package cn.axzo.workflow.client.feign.bpmn; 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.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable; 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.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO; 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.BpmnTaskAttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; 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.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO; import cn.axzo.workflow.common.model.request.bpmn.task.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.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import cn.azxo.framework.common.model.CommonResponse; import cn.azxo.framework.common.model.CommonResponse;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; 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 * @since 2023/9/21 16:26
*/ */
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
public interface ProcessTaskApi { public interface ProcessTaskApi {
/** /**
@ -71,6 +73,24 @@ public interface ProcessTaskApi {
@PostMapping("/api/process/task/batch/approve") @PostMapping("/api/process/task/batch/approve")
CommonResponse<BatchOperationResultVO> batchApproveTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos); CommonResponse<BatchOperationResultVO> batchApproveTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
/**
* 获取当前节点可回退节点选项列表
* @param taskId 当前任务id
* @return 可以回退节点列表
*/
@Operation(summary = "获取当前节点可回退节点选项列表")
@GetMapping("/api/process/task/back/optional/nodes")
CommonResponse<List<BpmnOptionalNodeDTO>> getBackOptionalNodes(@RequestParam @NotBlank(message = "任务id不能为空") String taskId);
/**
* 回退到指定节点
* @param dto
* @return
*/
@Operation(summary = "回退")
@PostMapping("/api/process/task/back")
CommonResponse<Boolean> backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto);
/** /**
* 驳回 * 驳回
* *

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.bpmn; 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.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable; import cn.axzo.workflow.common.annotation.Manageable;
@ -21,6 +22,7 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
* 流程变量api * 流程变量api
*/ */
//@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
@Manageable @Manageable
public interface ProcessVariableApi { public interface ProcessVariableApi {

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.manage; 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.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable; 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) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
@Manageable @Manageable
public interface ProcessCategoryApi { public interface ProcessCategoryApi {

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.client.feign.manage; 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.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable; 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) //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@WorkflowEngineFeignClient
@Manageable @Manageable
public interface ProcessConfigApi { public interface ProcessConfigApi {

View File

@ -40,6 +40,7 @@ public interface BpmnConstants {
@Deprecated @Deprecated
String OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT = "[_ASSIGNEE_INFO_SNAPSHOT_]"; String OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT = "[_ASSIGNEE_INFO_SNAPSHOT_]";
String INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT = "[_ACTIVITY_INFO_SNAPSHOT_]"; String INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT = "[_ACTIVITY_INFO_SNAPSHOT_]";
String BIZ_NODE_ALTER = "[_BIZ_NODE_ALTER_]";
String PROCESS_PREFIX = "Flowable"; String PROCESS_PREFIX = "Flowable";
@Deprecated @Deprecated
String OLD_TASK_ASSIGNEE_SKIP_FLAT = "taskSkip"; String OLD_TASK_ASSIGNEE_SKIP_FLAT = "taskSkip";
@ -108,6 +109,7 @@ public interface BpmnConstants {
String BPM_MODEL_CATEGORY = "bpm_model_category"; String BPM_MODEL_CATEGORY = "bpm_model_category";
String BPM_ALLOW_SKIP_USER_TASK = "_INTERNAL_SKIP_USER_TASK_"; String BPM_ALLOW_SKIP_USER_TASK = "_INTERNAL_SKIP_USER_TASK_";
String AUTO_APPROVAL_TYPE = "autoApprovalType"; String AUTO_APPROVAL_TYPE = "autoApprovalType";
String PROCESS_CLOSING_TYPE = "[_PROCESS_CLOSING_TYPE]";
/** /**
* 用于国内审批节点填写审批建议 * 用于国内审批节点填写审批建议
* <p> * <p>
@ -120,6 +122,8 @@ public interface BpmnConstants {
String NUMBER_OF_INSTANCES = "nrOfInstances"; String NUMBER_OF_INSTANCES = "nrOfInstances";
String MULTI_INSTANCE_LOOP_COUNTER = "loopCounter"; String MULTI_INSTANCE_LOOP_COUNTER = "loopCounter";
String TASK_COMPLETE_OPERATION_TYPE = "_TASK_COMPLETE_TYPE"; 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 COUNTERSIGN_ASSIGNER_SHOW_NUMBER = 2;
/**
* 回退操作次数上限
*/
Integer MAX_BACKED_OPERATE_COUNT = 20;
} }

View File

@ -1,5 +1,8 @@
package cn.axzo.workflow.common.enums; 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) { public void setRemark(String remark) {
this.remark = 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);
}
} }

View File

@ -1,6 +1,9 @@
package cn.axzo.workflow.common.enums; package cn.axzo.workflow.common.enums;
import java.util.Arrays;
import java.util.Objects;
public enum BpmnFlowNodeMode { public enum BpmnFlowNodeMode {
GENERAL("GENERAL", "普通节点"), GENERAL("GENERAL", "普通节点"),
OR("OR", "或签节点"), OR("OR", "或签节点"),
@ -35,4 +38,11 @@ public enum BpmnFlowNodeMode {
public void setDesc(String desc) { public void setDesc(String desc) {
this.desc = 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);
}
} }

View File

@ -1,6 +1,8 @@
package cn.axzo.workflow.common.enums; package cn.axzo.workflow.common.enums;
import org.springframework.util.StringUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
@ -51,6 +53,19 @@ public enum BpmnFlowNodeType {
this.desc = desc; 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) { public static BpmnFlowNodeType valueOfType(String type) {
return Arrays.stream(BpmnFlowNodeType.values()) return Arrays.stream(BpmnFlowNodeType.values())
.filter(i -> Objects.equals(i.getType(), type)) .filter(i -> Objects.equals(i.getType(), type))

View File

@ -6,6 +6,7 @@ public enum BpmnProcessInstanceResultEnum {
PROCESSING("PROCESSING", "审批中"), PROCESSING("PROCESSING", "审批中"),
APPROVED("APPROVED", "已通过"), APPROVED("APPROVED", "已通过"),
REJECTED("REJECTED", "已驳回"), REJECTED("REJECTED", "已驳回"),
BACKED("BACKED", "已回退"),
CANCELLED("CANCELLED", "已撤回"), CANCELLED("CANCELLED", "已撤回"),
ABORTED("ABORTED", "已中止"), ABORTED("ABORTED", "已中止"),
TRANSFER("TRANSFER", "已转交"), TRANSFER("TRANSFER", "已转交"),

View File

@ -10,8 +10,8 @@ import cn.axzo.framework.rocketmq.Event;
*/ */
public enum ProcessTaskEventEnum { public enum ProcessTaskEventEnum {
PROCESS_TASK_CREATED("process-task", "process-task-created", "流程任务已创建"),
PROCESS_TASK_ASSIGNED("process-task", "process-task-assigned", "流程任务已分配"), 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_COMPLETED("process-task", "process-task-completed", "流程任务已结束"),
PROCESS_TASK_DELETED("process-task", "process-task-deleted", "流程任务已删除"), PROCESS_TASK_DELETED("process-task", "process-task-deleted", "流程任务已删除"),
; ;

View File

@ -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;
}

View File

@ -0,0 +1,34 @@
package cn.axzo.workflow.common.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 卡住节点的查询条件模型
*
* @author wangli
* @since 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;
}

View File

@ -3,18 +3,23 @@ package cn.axzo.workflow.common.model.request;
import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum; import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import javax.validation.Valid; import javax.validation.Valid;
@ApiModel("JSON 版本的 BPMN 协议模型中流程配置管理") @ApiModel("JSON 版本的 BPMN 协议模型中流程配置管理")
@Data @Data
@NoArgsConstructor @AllArgsConstructor
@Accessors(chain = true) @Accessors(chain = true)
public class BpmnApproveConf { public class BpmnApproveConf {
public BpmnApproveConf() {
this.supportBatchOperation = false;
this.userAgreeSignature = false;
}
/** /**
* 是否支持批量审批 * 是否支持批量审批
*/ */

View File

@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -25,25 +26,25 @@ public class BpmnButtonConf implements Serializable {
* 发起人的按钮配置信息, 需要给全量按钮的配置 * 发起人的按钮配置信息, 需要给全量按钮的配置
*/ */
@ApiModelProperty(value = "发起人的按钮配置信息") @ApiModelProperty(value = "发起人的按钮配置信息")
private List<BpmnButtonMetaInfo> initiator; private List<BpmnButtonMetaInfo> initiator = new ArrayList<>();
/** /**
* 当前审批人的按钮配置信息, JSON 格式 * 当前审批人的按钮配置信息, JSON 格式
*/ */
@ApiModelProperty(value = "当前审批人的按钮配置信息") @ApiModelProperty(value = "当前审批人的按钮配置信息")
private List<BpmnButtonMetaInfo> current; private List<BpmnButtonMetaInfo> current = new ArrayList<>();
/** /**
* 历史审批人的按钮配置信息, JSON 格式 * 历史审批人的按钮配置信息, JSON 格式
*/ */
@ApiModelProperty(value = "历史审批人的按钮配置信息") @ApiModelProperty(value = "历史审批人的按钮配置信息")
private List<BpmnButtonMetaInfo> history; private List<BpmnButtonMetaInfo> history = new ArrayList<>();
/** /**
* 抄送人的按钮配置信息, JSON 格式 * 抄送人的按钮配置信息, JSON 格式
*/ */
@ApiModelProperty(value = "抄送人的按钮配置信息") @ApiModelProperty(value = "抄送人的按钮配置信息")
private List<BpmnButtonMetaInfo> carbonCopy; private List<BpmnButtonMetaInfo> carbonCopy = new ArrayList<>();
public List<BpmnButtonMetaInfo> getInitiator() { public List<BpmnButtonMetaInfo> getInitiator() {
return initiator; return initiator;

View File

@ -36,6 +36,7 @@ public class BpmnProcessInstanceCancelDTO {
* 工作台 ID * 工作台 ID
*/ */
@ApiModelProperty(value = "工作台 ID") @ApiModelProperty(value = "工作台 ID")
@NotBlank(message = "工作台不能为空")
private String tenantId; private String tenantId;
/** /**

View File

@ -43,9 +43,9 @@ public class BpmnProcessInstanceCreateDTO {
* 发起流程实例归属租户 ID * 发起流程实例归属租户 ID
* <p> * <p>
* 为空时,默认是编辑公共流程模型, 如果是代运营创建,则必填 * 为空时,默认是编辑公共流程模型, 如果是代运营创建,则必填
* <h1 color=red>建议都传值在安心筑中对应工作台 ID</h1>
*/ */
@ApiModelProperty(value = "发起的审批是属于哪个租户") @ApiModelProperty(value = "发起的审批是属于哪个租户")
// @NotBlank(message = "审批实例归属租户 ID 不能为空")
private String tenantId; private String tenantId;
/** /**

View File

@ -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;
/**
* 谁来访问该实例日志如果为空则始终不就返回按钮信息
* <p>
* 注意为了确保历史审批数据的查询需要将除 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;
}

View File

@ -3,10 +3,14 @@ package cn.axzo.workflow.common.model.request.bpmn.task;
import cn.axzo.workflow.common.enums.AttachmentTypeEnum; import cn.axzo.workflow.common.enums.AttachmentTypeEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.io.Serializable;
/** /**
* 附件模型 * 附件模型
@ -16,8 +20,12 @@ import javax.validation.constraints.NotNull;
*/ */
@ApiModel("附件模型") @ApiModel("附件模型")
@Data @Data
public class AttachmentDTO { @Builder
@AllArgsConstructor
@NoArgsConstructor
public class AttachmentDTO implements Serializable {
private static final long serialVersionUID = 6954179791395744269L;
/** /**
* 附件 ID * 附件 ID
*/ */

View File

@ -53,7 +53,6 @@ public class BpmnActivitySetAssigneeDTO {
* 业务如果传入的 assigners 集合为空, 引擎则会对该流程进行自动驳回处理且驳回意见为业务未指定审批人 * 业务如果传入的 assigners 集合为空, 引擎则会对该流程进行自动驳回处理且驳回意见为业务未指定审批人
*/ */
@ApiModelProperty(value = "审批人集合信息", notes = "业务传参时,需要注意去重") @ApiModelProperty(value = "审批人集合信息", notes = "业务传参时,需要注意去重")
@Valid
@Size(max = 60, message = "指定审批人数量限制为60") @Size(max = 60, message = "指定审批人数量限制为60")
private List<BpmnTaskDelegateAssigner> assigners; private List<BpmnTaskDelegateAssigner> assigners;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<BpmnTaskInstanceLogVO> taskDetails;
/**
* 当前实例对应模型的全局兜底按钮配置
*/
@ApiModelProperty(value = "当前实例对应模型的全局兜底按钮配置")
private BpmnButtonConf defaultButtonConf;
/**
* 指定人访问实例日志时计算其流程应该有权限操作的按钮
*/
@ApiModelProperty(value = "指定人访问实例日志时,计算其流程应该有权限操作的按钮", notes = "流程有权限,不代表待办消息中一定能看到按钮")
private List<BpmnButtonMetaInfo> currentUserButtons;
/**
* 需要隐藏的自定义按钮集合
*/
@ApiModelProperty(value = "需要隐藏的自定义按钮集合")
private List<BpmnButtonMetaInfo> 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;
}

View File

@ -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<AttachmentDTO> imageList;
/**
* 附件列表
*/
@ApiModelProperty(value = "附件列表")
private List<AttachmentDTO> fileList;
/**
* 手写签名地址
*/
@ApiModelProperty(value = "手写签名地址")
private String signatureUrl;
/**
* 审批人快照信息
*/
@ApiModelProperty(value = "审批人快照信息")
private BpmnTaskDelegateAssigner assigneeSnapshot;
/**
* 未完成节点多实例模式的审批人信息
*/
@ApiModelProperty(value = "未完成节点多实例模式的审批人信息")
private List<BpmnTaskDelegateAssigner> forecastAssignees;
@ApiModelProperty(value = "程序计算按钮使用,非对外使用", hidden = true)
private transient BpmnButtonConf buttonConf;
public boolean isVirtual() {
return StringUtils.isBlank(this.taskId);
}
}

View File

@ -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.BpmnNoticeEnum;
import cn.axzo.workflow.common.enums.ProcessTaskEventEnum; import cn.axzo.workflow.common.enums.ProcessTaskEventEnum;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; 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 cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@ -44,6 +45,10 @@ public class ProcessTaskDTO implements Serializable {
* 流程实例 ID * 流程实例 ID
*/ */
private String processInstanceId; private String processInstanceId;
/**
* 流程实例的businessKey
*/
private String businessKey;
/** /**
* 流程实例所属业务分类同时也等于流程模型对应的业务分类 ID * 流程实例所属业务分类同时也等于流程模型对应的业务分类 ID
*/ */
@ -103,4 +108,13 @@ public class ProcessTaskDTO implements Serializable {
* 当前数据的流程引擎版本 * 当前数据的流程引擎版本
*/ */
private String workflowEngineVersion; private String workflowEngineVersion;
/**
* 任务关联的附件
*/
private List<AttachmentDTO> attachments;
/**
* 审批意见
*/
private String advice;
} }

View File

@ -89,17 +89,25 @@
<groupId>cn.axzo.workflow</groupId> <groupId>cn.axzo.workflow</groupId>
<artifactId>workflow-engine-api</artifactId> <artifactId>workflow-engine-api</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.axzo.workflow</groupId> <groupId>cn.axzo.workflow</groupId>
<artifactId>workflow-engine-common</artifactId> <artifactId>workflow-engine-common</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven</groupId> <groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId> <artifactId>maven-artifact</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.servlet</groupId> <groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId> <artifactId>jakarta.servlet-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -27,6 +27,7 @@ public enum BpmnInstanceRespCode implements IModuleRespCode {
TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS("012", "流程实例【{}】不存在, 不能评论"), TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS("012", "流程实例【{}】不存在, 不能评论"),
RUNNING_INSTANCE_ONLY_FORECAST("013", "仅运行中的实例可以推测"), RUNNING_INSTANCE_ONLY_FORECAST("013", "仅运行中的实例可以推测"),
ENGINE_EXEC_EXCEPTION("014", "引擎内部异常"), ENGINE_EXEC_EXCEPTION("014", "引擎内部异常"),
PROCESS_TASK_NOT_EXISTS("015", "流程任务不存在或已处理"),
; ;
private final String code; private final String code;
private final String message; private final String message;

View File

@ -37,6 +37,9 @@ public enum BpmnTaskRespCode implements IModuleRespCode {
TASK_TYPE_MISMATCH("020", "节点类型不匹配,当前节点类型:【{}】,指定节点类型:【{}】!"), TASK_TYPE_MISMATCH("020", "节点类型不匹配,当前节点类型:【{}】,指定节点类型:【{}】!"),
PROCESS_CANT_SET_ASSIGNEE("021", "当前审批状态不允许设置审批人"), PROCESS_CANT_SET_ASSIGNEE("021", "当前审批状态不允许设置审批人"),
ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT("022", String.format("人员数量超过限制,节点审批人限制数量为: %d!", APPROVAL_ASSIGNER_LIMIT_NUMBER)), 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; private final String code;

View File

@ -19,7 +19,7 @@ public enum FlowableEngineRespCode implements IModuleRespCode {
ENGINE_USER_TASK_PARAM_ERROR("004", "构建后的查询审批人入参为空. 任务节点【nodeId:{}】, 该节点选择的\"审批人所在范围\"是:【{}】,请检查 cooperationOrg 参数"), ENGINE_USER_TASK_PARAM_ERROR("004", "构建后的查询审批人入参为空. 任务节点【nodeId:{}】, 该节点选择的\"审批人所在范围\"是:【{}】,请检查 cooperationOrg 参数"),
ENGINE_NOTICE_CUSTOM_FLOW_ELEMENT_ERROR("005", "查询通知目标用户前参数发生异常,未获取到 WorkspaceType"), ENGINE_NOTICE_CUSTOM_FLOW_ELEMENT_ERROR("005", "查询通知目标用户前参数发生异常,未获取到 WorkspaceType"),
ENGINE_ASYNC_COMMAND_EXECUTION_ERROR("006", "引擎出现 SQL 相关异常, 异常信息:【{}】"), 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; private final String code;

View File

@ -9,7 +9,8 @@ import java.util.Arrays;
* @since 2023/9/18 17:11 * @since 2023/9/18 17:11
*/ */
public enum BpmnProcessTaskResultEnum { public enum BpmnProcessTaskResultEnum {
PENDING("PENDING", "待处理"),
PROCESSED("PROCESSED", "已处理"),
AUTO_SKIP("AUTO_SKIP", "任务自动跳过"), AUTO_SKIP("AUTO_SKIP", "任务自动跳过"),
// 引擎默认的标识,不允许修改 // 引擎默认的标识,不允许修改
MI_END("MI_END", "多实例任务运行结束"), MI_END("MI_END", "多实例任务运行结束"),
@ -17,8 +18,7 @@ public enum BpmnProcessTaskResultEnum {
DELETE_MI_EXECUTION("Delete MI execution", "多实例任务被删除"), DELETE_MI_EXECUTION("Delete MI execution", "多实例任务被删除"),
INITIATOR_REVOCATION("INITIATOR_REVOCATION", "发起者主动撤回"), INITIATOR_REVOCATION("INITIATOR_REVOCATION", "发起者主动撤回"),
REJECTION_AUTO_COMPLETED("REJECTION_AUTO_COMPLETED", "审批驳回自动结束"), REJECTION_AUTO_COMPLETED("REJECTION_AUTO_COMPLETED", "审批驳回自动结束"),
BACKED("BACKED", "退回"); ;
private final String status; private final String status;
/** /**
* 描述 * 描述

View File

@ -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<String> 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<SequenceFlow> 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;
}
}

View File

@ -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.cmd.CustomCommandContextFactory;
import cn.axzo.workflow.core.engine.id.BasedNacosSnowflakeIdGenerator; import cn.axzo.workflow.core.engine.id.BasedNacosSnowflakeIdGenerator;
import cn.axzo.workflow.core.engine.interceptor.CustomRetryInterceptor; 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.AsyncActivityCallbackJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncActivityLeaveJobHandler; 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.AsyncApproveTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncBpmnProcessActivityJobHandler; import cn.axzo.workflow.core.engine.job.AsyncBackTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceHandler; import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncCountersignUserTaskJobHandler; import cn.axzo.workflow.core.engine.job.AsyncCountersignUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncExtTaskInstJobHandler; import cn.axzo.workflow.core.engine.job.AsyncExtTaskInstJobHandler;
import cn.axzo.workflow.core.engine.job.AsyncRejectTaskJobHandler; 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.AsyncTransferUserTaskJobHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncJobLogClearTraceExceptionHandler; import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncJobLogClearTraceExceptionHandler;
import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncRunnableExceptionExceptionHandler; import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncRunnableExceptionExceptionHandler;
@ -67,7 +70,7 @@ public class FlowableConfiguration {
List<JobProcessor> jobProcessors, List<JobProcessor> jobProcessors,
NacosServiceManager nacosServiceManager, NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties, NacosDiscoveryProperties nacosDiscoveryProperties,
StringRedisTemplate redisTemplate) { SupportRefreshProperties refreshProperties) {
return configuration -> { return configuration -> {
configuration.setEnableHistoricTaskLogging(true); configuration.setEnableHistoricTaskLogging(true);
configuration.setHistoryLevel(HistoryLevel.AUDIT); configuration.setHistoryLevel(HistoryLevel.AUDIT);
@ -83,14 +86,18 @@ public class FlowableConfiguration {
configuration.setIdGenerator(new BasedNacosSnowflakeIdGenerator(nacosServiceManager, nacosDiscoveryProperties)); configuration.setIdGenerator(new BasedNacosSnowflakeIdGenerator(nacosServiceManager, nacosDiscoveryProperties));
configuration.setHistoricProcessInstanceDataManager(new CustomMybatisHistoricProcessInstanceDataManager(configuration)); 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 AsyncApproveTaskJobHandler());
configuration.addCustomJobHandler(new AsyncRejectTaskJobHandler(extAxHiTaskInstService)); configuration.addCustomJobHandler(new AsyncBackTaskJobHandler());
configuration.addCustomJobHandler(new AsyncExtTaskInstJobHandler(extAxHiTaskInstService)); configuration.addCustomJobHandler(new AsyncCancelProcessInstanceJobHandler(extAxHiTaskInstService));
configuration.addCustomJobHandler(new AsyncTransferUserTaskJobHandler()); 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 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 AsyncActivityLeaveJobHandler(bpmnProcessActivityService));
configuration.addCustomJobHandler(new AsyncActivityCallbackJobHandler()); configuration.addCustomJobHandler(new AsyncActivityCallbackJobHandler());
// 异步任务异常重试时间间隔 // 异步任务异常重试时间间隔

View File

@ -1,13 +1,14 @@
package cn.axzo.workflow.core.conf; package cn.axzo.workflow.core.conf;
import com.google.common.collect.Lists; import com.alibaba.nacos.api.config.annotation.NacosValue;
import lombok.Data; import lombok.Data;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/** /**
* 支持动态刷新配置属性 * 支持动态刷新配置属性
@ -28,4 +29,49 @@ public class SupportRefreshProperties {
@Value("${workflow.apiLog.filterApiType:}") @Value("${workflow.apiLog.filterApiType:}")
private String 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<String, String> 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<String> alterMobiles;
/**
* 用于控制转交管理员的 API
*/
@Value("${workflow.useNewToAdminApi:true}")
private Boolean useNewToAdminApi;
} }

View File

@ -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<BpmnButtonConf> {
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<BpmnButtonConf>() {
});
} 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;
}
}

View File

@ -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<List<BpmnTaskDelegateAssigner>> {
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<BpmnTaskDelegateAssigner> parse(String json) {
try {
// 这里进行了json解析同样在这里也可以进行字段查询后的处理如对象内部的手机号字段的加密展示等
return objectMapper.readValue(json, new TypeReference<List<BpmnTaskDelegateAssigner>>() {
});
} catch (IOException var3) {
throw new RuntimeException(var3);
}
}
protected String toJson(List<BpmnTaskDelegateAssigner> obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException var3) {
throw new RuntimeException(var3);
}
}
public static void setObjectMapper(ObjectMapper om) {
objectMapper = om;
}
}

View File

@ -12,12 +12,12 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.TaskHelper; import org.flowable.engine.impl.util.TaskHelper;
import org.flowable.task.api.Task;
import org.flowable.task.service.TaskService; import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity; import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.Objects; 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.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
@ -48,7 +48,8 @@ public class CustomReceiveTaskActivityBehavior extends ReceiveTaskActivityBehavi
task.setTaskDefinitionKey(receiveTask.getId()); task.setTaskDefinitionKey(receiveTask.getId());
task.setPropagatedStageInstanceId(execution.getPropagatedStageInstanceId()); task.setPropagatedStageInstanceId(execution.getPropagatedStageInstanceId());
task.setName(receiveTask.getName()); task.setName(receiveTask.getName());
TaskHelper.insertTask(task, (ExecutionEntity) execution, false, false); TaskHelper.insertTask(task, (ExecutionEntity) execution, true, false);
// 添加 taskInst 扩展表数据 // 添加 taskInst 扩展表数据
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher(); FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
@ -65,14 +66,17 @@ public class CustomReceiveTaskActivityBehavior extends ReceiveTaskActivityBehavi
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher(); FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
org.flowable.engine.TaskService taskService = processEngineConfiguration.getTaskService(); 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(); .taskDefinitionKey(execution.getCurrentActivityId()).singleResult();
if (Objects.nonNull(task)) { if (Objects.nonNull(task)) {
// 用于新版日志
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), APPROVED.getStatus());
eventDispatcher.dispatchEvent(new ExtTaskInstUpdateEvent(execution.getProcessInstanceId(), eventDispatcher.dispatchEvent(new ExtTaskInstUpdateEvent(execution.getProcessInstanceId(),
receiveTask.getId(), task.getId(), APPROVED), receiveTask.getId(), task.getId(), APPROVED),
processEngineConfiguration.getEngineCfgKey()); processEngineConfiguration.getEngineCfgKey());
} else { } else {
log.warn("task is null, executionId: {}, activityId: {}", execution.getId(), log.warn("ReceiveTask is null, executionId: {}, activityId: {}", execution.getId(),
execution.getCurrentActivityId()); execution.getCurrentActivityId());
} }
super.leave(execution); super.leave(execution);

View File

@ -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.ExtTaskInstCreateEvent;
import cn.axzo.workflow.core.engine.event.ExtTaskInstUpdateEvent; import cn.axzo.workflow.core.engine.event.ExtTaskInstUpdateEvent;
import cn.axzo.workflow.core.engine.listener.EngineCarbonCopyEventListener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.MapExceptionEntry; import org.flowable.bpmn.model.MapExceptionEntry;
import org.flowable.bpmn.model.ServiceTask; 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 org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.List; 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.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
/** /**
* 自定义的服务任务活动行为处理器 * 自定义的服务任务活动行为处理器
* <p> * <p>
* 主要用来创建审批日志 * 主要用来创建抄送节点审批日志真实计算抄送人的集合是由 {@link EngineCarbonCopyEventListener} 来完成的
* *
* @author wangli * @author wangli
* @since 13/03/2024 14:17 * @since 13/03/2024 14:17
@ -35,6 +38,7 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCES
public class CustomServiceTaskDelegateExpressionActivityBehavior extends ServiceTaskDelegateExpressionActivityBehavior { public class CustomServiceTaskDelegateExpressionActivityBehavior extends ServiceTaskDelegateExpressionActivityBehavior {
protected final ServiceTask serviceTask; protected final ServiceTask serviceTask;
// thread safe
private TaskEntity task; private TaskEntity task;
public CustomServiceTaskDelegateExpressionActivityBehavior(String serviceTaskId, Expression expression, public CustomServiceTaskDelegateExpressionActivityBehavior(String serviceTaskId, Expression expression,
@ -57,7 +61,7 @@ public class CustomServiceTaskDelegateExpressionActivityBehavior extends Service
task.setTaskDefinitionKey(serviceTask.getId()); task.setTaskDefinitionKey(serviceTask.getId());
task.setPropagatedStageInstanceId(execution.getPropagatedStageInstanceId()); task.setPropagatedStageInstanceId(execution.getPropagatedStageInstanceId());
task.setName(serviceTask.getName()); task.setName(serviceTask.getName());
TaskHelper.insertTask(task, (ExecutionEntity) execution, false, false); TaskHelper.insertTask(task, (ExecutionEntity) execution, true, false);
// 添加 taskInst 扩展表数据 // 添加 taskInst 扩展表数据
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher(); FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
@ -72,11 +76,24 @@ public class CustomServiceTaskDelegateExpressionActivityBehavior extends Service
CommandContext commandContext = CommandContextUtil.getCommandContext(); CommandContext commandContext = CommandContextUtil.getCommandContext();
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher(); 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()); 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); super.leave(execution);
} }
} }

View File

@ -8,7 +8,7 @@ import org.flowable.common.engine.impl.interceptor.CommandContext;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.ENGINE_EXEC_EXCEPTION; import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.ENGINE_EXEC_EXCEPTION;
/** /**
* TODO * 抽象的命令用于将在 Command 中执行的逻辑所抛出的非 WorkflowEngineException 的异常都包装为 WorkflowEngineException
* *
* @author wangli * @author wangli
* @since 2024/7/1 13:59 * @since 2024/7/1 13:59

View File

@ -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.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; 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 cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.interceptor.CommandContext;
@ -73,9 +73,9 @@ public class CustomAbortProcessInstanceAsyncCmd extends AbstractCommand<Void> im
job.setExecutionId(instance.getId()); job.setExecutionId(instance.getId());
job.setProcessInstanceId(instance.getId()); job.setProcessInstanceId(instance.getId());
job.setProcessDefinitionId(instance.getProcessDefinitionId()); job.setProcessDefinitionId(instance.getProcessDefinitionId());
job.setElementId(AsyncAbortProcessInstanceHandler.TYPE); job.setElementId(AsyncAbortProcessInstanceJobHandler.TYPE);
job.setElementName(instance.getName()); job.setElementName(instance.getName());
job.setJobHandlerType(AsyncAbortProcessInstanceHandler.TYPE); job.setJobHandlerType(AsyncAbortProcessInstanceJobHandler.TYPE);
job.setTenantId(instance.getTenantId()); job.setTenantId(instance.getTenantId());
// 携带自定义的数据 // 携带自定义的数据

View File

@ -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.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; 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.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
@ -20,7 +21,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; 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_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_TENANT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_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_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_CANT_ABORT;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS; 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; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;
/** /**
@ -101,14 +101,15 @@ public class CustomAbortProcessInstanceCmd extends AbstractCommand<Void> impleme
runtimeService.setVariables(instance.getId(), variables); runtimeService.setVariables(instance.getId(), variables);
CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext, CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext,
processInstanceId, extAxHiTaskInstService)); processInstanceId, extAxHiTaskInstService, ABORTED));
// 添加自定义的节点,用于展示最后的操作 // 添加自定义的节点,用于展示最后的操作
Task task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId, Task task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId,
"系统中止", NODE_ABORT.getType(), null, BpmnTaskDelegateAssigner.buildDummyAssigner("system", "系统中止", NODE_ABORT.getType(), null, BpmnTaskDelegateAssigner.buildDummyAssigner("system",
TASK_ASSIGNEE_SKIP_FLAT, "系统"), ABORTED.getStatus()); TASK_ASSIGNEE_SKIP_FLAT, "系统"), ABORTED.getStatus(), new AddComment(reason));
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, reason);
runtimeService.setVariable(task.getProcessInstanceId(), TASK_COMPLETE_OPERATION_TYPE + task.getId(), ABORTED); runtimeService.setVariable(task.getProcessInstanceId(), TASK_COMPLETE_OPERATION_TYPE + task.getId(), ABORTED);
completeVirtualTask(commandContext, task);
return null; return null;
} }

View File

@ -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<String> 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();
}
}

View File

@ -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<Void> 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;
}
}

View File

@ -29,8 +29,10 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask
* @since 2024/1/4 15:50 * @since 2024/1/4 15:50
*/ */
public class CustomApproveTaskAsyncCmd extends AbstractCommand<String> implements Serializable { public class CustomApproveTaskAsyncCmd extends AbstractCommand<String> implements Serializable {
private static final long serialVersionUID = -4706627700694867170L;
private static final Logger log = LoggerFactory.getLogger(CustomApproveTaskAsyncCmd.class); private static final Logger log = LoggerFactory.getLogger(CustomApproveTaskAsyncCmd.class);
private final BpmnTaskAuditDTO dto; private final BpmnTaskAuditDTO dto;
public CustomApproveTaskAsyncCmd(BpmnTaskAuditDTO dto) { public CustomApproveTaskAsyncCmd(BpmnTaskAuditDTO dto) {

View File

@ -101,7 +101,7 @@ public class CustomApproveTaskCmd extends AbstractCommand<Void> implements Seria
if (Objects.nonNull(operationDesc)) { if (Objects.nonNull(operationDesc)) {
this.operationDesc = operationDesc; this.operationDesc = operationDesc;
} else { } else {
this.operationDesc = "已通过"; this.operationDesc = "已通过";
} }
} }
@ -115,8 +115,8 @@ public class CustomApproveTaskCmd extends AbstractCommand<Void> implements Seria
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(taskId).singleResult(); HistoricTaskInstance historicTaskInstance = taskQuery.taskId(taskId).singleResult();
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);
// TODO 所有的跟 Task 相关的动作都可以在这里进行扩展用于扩展八大按钮标准动作以外的一些逻辑但这里需要结合 Spring 能力需设计好扩展点否则无法进行扩展 // TODO 所有的跟 Task 相关的动作都可以在这里进行扩展用于扩展八大按钮标准动作以外的一些逻辑但这里需要结合 Spring 能力需设计好扩展点否则无法进行扩展
// 其他动态也应该在类似的地方预留扩展点 // 其他动态也应该在类似的地方预留扩展点
@ -127,7 +127,7 @@ public class CustomApproveTaskCmd extends AbstractCommand<Void> implements Seria
Authentication.setAuthenticatedUserId(null); 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); Authentication.setAuthenticatedUserId(Objects.nonNull(approver) ? approver.buildAssigneeId() : null);
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, operationDesc); addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, operationDesc);
@ -139,8 +139,7 @@ public class CustomApproveTaskCmd extends AbstractCommand<Void> implements Seria
runtimeService.setVariable(task.getProcessInstanceId(), INTERNAL_SPECIFY_NEXT_APPROVER, runtimeService.setVariable(task.getProcessInstanceId(), INTERNAL_SPECIFY_NEXT_APPROVER,
nextApprover); nextApprover);
} }
((TaskEntity) task).setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus()); task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus());
executeSynchronous(task, taskService, runtimeService); executeSynchronous(task, taskService, runtimeService);
return null; return null;
} }

View File

@ -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<String> 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);
}
}

View File

@ -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<Void> implements Serializable {
private static final long serialVersionUID = -1241290344311892346L;
private final BpmnTaskBackAuditDTO dto;
private static final String OPERATION_DESC = "回退至";
@Override
public String paramToJsonString() {
Map<String, Object> 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;
}
}

View File

@ -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<String> 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();
}
}

View File

@ -6,11 +6,14 @@ import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService; import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService; import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService; import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil; 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.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity; import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -59,8 +62,8 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand<Boolean>
public static Task getOperateTask(TaskService taskService, String executionId) { public static Task getOperateTask(TaskService taskService, String executionId) {
return taskService.createTaskQuery().executionId(executionId) return taskService.createTaskQuery().executionId(executionId)
.taskAssignee(NO_ASSIGNEE) .taskAssignee(NO_ASSIGNEE)
.singleResult(); .singleResult();
} }
/** /**
@ -79,9 +82,9 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand<Boolean>
@Override @Override
public Boolean execute(CommandContext commandContext) { public Boolean execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService(); TaskService taskService = processEngineConfiguration.getTaskService();
Task task = getOperateTask(taskService, executionId); TaskEntity task = (TaskEntity) getOperateTask(taskService, executionId);
//校验 //校验
validate(processEngineConfiguration.getRuntimeService(), executionId, task, addedAssigners); validate(processEngineConfiguration.getRuntimeService(), executionId, task, addedAssigners);
@ -91,12 +94,29 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand<Boolean>
addAssignee(commandContext, taskService, task); addAssignee(commandContext, taskService, task);
clearAlterTimeJob(commandContext, task);
return true; 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 = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoryService historyService = processEngineConfiguration.getHistoryService(); HistoryService historyService = processEngineConfiguration.getHistoryService();
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
if (Objects.isNull(processInstance)) { if (Objects.isNull(processInstance)) {
@ -110,11 +130,11 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand<Boolean>
private void changeAssigneeSnapshot(CommandContext commandContext, Task task) { private void changeAssigneeSnapshot(CommandContext commandContext, Task task) {
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List<BpmnTaskDelegateAssigner> originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(), List<BpmnTaskDelegateAssigner> 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) { for (BpmnTaskDelegateAssigner assigner : originAssingeeList) {
if (Objects.equals(assigner.buildAssigneeId(), NO_ASSIGNEE)) { if (Objects.equals(assigner.buildAssigneeId(), NO_ASSIGNEE)) {
@ -124,8 +144,8 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand<Boolean>
} }
originAssingeeList.addAll(addedAssigners); originAssingeeList.addAll(addedAssigners);
runtimeService.setVariable(task.getProcessInstanceId(), runtimeService.setVariable(task.getProcessInstanceId(),
INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
originAssingeeList); originAssingeeList);
} }
private void addAssignee(CommandContext commandContext, TaskService taskService, Task task) { private void addAssignee(CommandContext commandContext, TaskService taskService, Task task) {

View File

@ -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.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; 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 cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.interceptor.CommandContext;
@ -74,9 +74,9 @@ public class CustomCancelProcessInstanceAsyncCmd extends AbstractCommand<Void> i
job.setExecutionId(instance.getId()); job.setExecutionId(instance.getId());
job.setProcessInstanceId(instance.getId()); job.setProcessInstanceId(instance.getId());
job.setProcessDefinitionId(instance.getProcessDefinitionId()); job.setProcessDefinitionId(instance.getProcessDefinitionId());
job.setElementId(AsyncCancelProcessInstanceHandler.TYPE); job.setElementId(AsyncCancelProcessInstanceJobHandler.TYPE);
job.setElementName(instance.getName()); job.setElementName(instance.getName());
job.setJobHandlerType(AsyncCancelProcessInstanceHandler.TYPE); job.setJobHandlerType(AsyncCancelProcessInstanceJobHandler.TYPE);
job.setTenantId(instance.getTenantId()); job.setTenantId(instance.getTenantId());
// 携带自定义的数据 // 携带自定义的数据

View File

@ -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.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; 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.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
@ -18,7 +19,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; 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_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_TENANT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_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_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_CANT_CANCEL;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS; 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; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;
/** /**
@ -104,12 +104,12 @@ public class CustomCancelProcessInstanceCmd extends AbstractCommand<Void> implem
runtimeService.setVariables(instance.getId(), variables); runtimeService.setVariables(instance.getId(), variables);
CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext, CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext,
processInstanceId, extAxHiTaskInstService)); processInstanceId, extAxHiTaskInstService, CANCELLED));
// 添加自定义的节点,用于展示最后的操作 // 添加自定义的节点,用于展示最后的操作
Task task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId, Task task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId,
"发起人撤回", NODE_CANCEL.getType(), reason, initiator, CANCELLED.getStatus()); "发起人撤回", NODE_CANCEL.getType(), reason, initiator, CANCELLED.getStatus(), new AddComment(CANCELLED.getDesc()));
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "已撤回"); completeVirtualTask(commandContext, task);
return null; return null;
} }

View File

@ -1,12 +1,16 @@
package cn.axzo.workflow.core.engine.cmd; package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; 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.FlowableException;
import org.flowable.common.engine.api.FlowableOptimisticLockingException; import org.flowable.common.engine.api.FlowableOptimisticLockingException;
import org.flowable.common.engine.impl.interceptor.Command; import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.util.Arrays;
/** /**
* CommandContext 中的 WorkflowEngineException 进行日志降级 * CommandContext 中的 WorkflowEngineException 进行日志降级
@ -15,7 +19,9 @@ import org.slf4j.LoggerFactory;
* @since 2024/5/21 09:46 * @since 2024/5/21 09:46
*/ */
public class CustomCommandContext extends CommandContext { 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) { public CustomCommandContext(Command<?> command) {
super(command); super(command);
@ -36,6 +42,10 @@ public class CustomCommandContext extends CommandContext {
LOGGER.info("Error while closing command context", exception); LOGGER.info("Error while closing command context", exception);
} else if (exception instanceof WorkflowEngineException) { } else if (exception instanceof WorkflowEngineException) {
LOGGER.warn("Workflow error while closing command context", exception); 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 { } else {
LOGGER.error("Error while closing command context", exception); LOGGER.error("Error while closing command context", exception);

View File

@ -5,7 +5,7 @@ import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.common.engine.impl.interceptor.CommandContextFactory; import org.flowable.common.engine.impl.interceptor.CommandContextFactory;
/** /**
* TODO * CommandContextFactory
* *
* @author wangli * @author wangli
* @since 2024/5/21 09:45 * @since 2024/5/21 09:45

View File

@ -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.BpmnTaskCommentExtDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; 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.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.cfg.IdGenerator; import org.flowable.common.engine.impl.cfg.IdGenerator;
import org.flowable.common.engine.impl.identity.Authentication; 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.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService; import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService; 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.HistoricVariableService;
import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntity; import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntity;
import org.flowable.variable.service.impl.types.StringType; import org.flowable.variable.service.impl.types.StringType;
import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream; 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_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_COMMENT_EXT; 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.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.BpmnFlowNodeType.NODE_COMMENT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COMMENTED; 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.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; import static org.flowable.task.api.Task.DEFAULT_PRIORITY;
/** /**
@ -80,11 +81,11 @@ public class CustomCommentTaskCmd extends AbstractCommand<Void> implements Seria
@Override @Override
public Void execute(CommandContext commandContext) { public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoryService historyService = processEngineConfiguration.getHistoryService(); HistoryService historyService = processEngineConfiguration.getHistoryService();
HistoricProcessInstance processInstance = HistoricProcessInstance processInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (Objects.isNull(processInstance)) { if (Objects.isNull(processInstance)) {
throw new WorkflowEngineException(TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS, processInstanceId); throw new WorkflowEngineException(TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS, processInstanceId);
} }
@ -103,32 +104,34 @@ public class CustomCommentTaskCmd extends AbstractCommand<Void> implements Seria
task.setTaskDefinitionKey(NODE_COMMENT.getType()); task.setTaskDefinitionKey(NODE_COMMENT.getType());
task.setPriority(DEFAULT_PRIORITY); task.setPriority(DEFAULT_PRIORITY);
task.setCreateTime(new Date()); task.setCreateTime(new Date());
// 创建临时节点
taskService.saveTask(task);
// 处理该评论节点的评论人 // 处理该评论节点的评论人
buildAndInsertHistoryVariable(task, processInstance, processEngineConfiguration); buildAndInsertHistoryVariable(task, processInstance, processEngineConfiguration);
CommandContextUtil.getEntityCache().findInCache(HistoricTaskInstanceEntity.class).stream() CommandContextUtil.getEntityCache().findInCache(HistoricTaskInstanceEntity.class).stream()
.filter(i -> Objects.equals(i.getId(), task.getId())).findAny() .filter(i -> Objects.equals(i.getId(), task.getId())).findAny()
.ifPresent(i -> i.setAssignee(operator.buildAssigneeId())); .ifPresent(i -> i.setAssignee(operator.buildAssigneeId()));
// 完成临时节点
taskService.complete(task.getId()); 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()); Authentication.setAuthenticatedUserId(operator.buildAssigneeId());
if (StringUtils.hasText(comment)) { addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment);
CustomTaskHelper.addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment); addComment(commandContext, task, COMMENT_TYPE_COMMENT_EXT, JSONUtil.toJsonStr(commentExt));
}
if (Objects.nonNull(commentExt)) {
CustomTaskHelper.addComment(commandContext, task, COMMENT_TYPE_COMMENT_EXT, JSONUtil.toJsonStr(commentExt));
}
Authentication.setAuthenticatedUserId(null); 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; return null;
} }
@ -136,9 +139,9 @@ public class CustomCommentTaskCmd extends AbstractCommand<Void> implements Seria
HistoricProcessInstance processInstance, HistoricProcessInstance processInstance,
ProcessEngineConfigurationImpl processEngineConfiguration) { ProcessEngineConfigurationImpl processEngineConfiguration) {
HistoricVariableService historicVariableService = HistoricVariableService historicVariableService =
processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService(); processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService();
HistoricVariableInstanceEntity historicVariableInstance = HistoricVariableInstanceEntity historicVariableInstance =
historicVariableService.createHistoricVariableInstance(); historicVariableService.createHistoricVariableInstance();
historicVariableInstance.setTaskId(task.getId()); historicVariableInstance.setTaskId(task.getId());
historicVariableInstance.setExecutionId(task.getExecutionId()); historicVariableInstance.setExecutionId(task.getExecutionId());
historicVariableInstance.setProcessInstanceId(processInstance.getId()); historicVariableInstance.setProcessInstanceId(processInstance.getId());

View File

@ -7,12 +7,12 @@ import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.identity.Authentication; 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.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService; import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.api.Task; import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -65,7 +65,7 @@ public class CustomCompleteDummyTaskCmd extends AbstractCommand<Void> implements
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService(); TaskService taskService = processEngineConfiguration.getTaskService();
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId) TaskEntity task = (TaskEntity) taskService.createTaskQuery().processInstanceId(processInstanceId)
.taskId(taskId).singleResult(); .taskId(taskId).singleResult();
if (Objects.isNull(task)) { if (Objects.isNull(task)) {
throw new WorkflowEngineException(DUMMY_TASK_NOT_EXISTS, processInstanceId, taskId); throw new WorkflowEngineException(DUMMY_TASK_NOT_EXISTS, processInstanceId, taskId);

View File

@ -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.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; 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.cmd.helper.CustomTaskHelper;
import cn.axzo.workflow.core.engine.model.AddComment;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
@ -23,12 +24,11 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.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.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COUNTERSIGN; 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.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.createVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount;
@ -81,24 +81,24 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
@Override @Override
public Void execute(CommandContext commandContext) { public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoricTaskInstanceQuery taskQuery = HistoricTaskInstanceQuery taskQuery =
processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery(); processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(originTaskId).singleResult(); HistoricTaskInstance historicTaskInstance = taskQuery.taskId(originTaskId).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService(); 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<BpmnTaskDelegateAssigner> taskDelegateAssigners = List<BpmnTaskDelegateAssigner> taskDelegateAssigners =
validTaskAssignerDuplicated(commandContext, (TaskEntity) task, targetTaskAssigneeList); validTaskAssignerDuplicated(commandContext, task, targetTaskAssigneeList);
resolveOriginTask(commandContext, extAxHiTaskInstService, taskService, task); resolveOriginTask(commandContext, extAxHiTaskInstService, taskService, task);
batchAddAttachment(commandContext, task.getProcessInstanceId(), task.getId(), attachmentList, batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList,
originTaskAssignee); originTaskAssignee);
switch (countersignType) { switch (countersignType) {
@ -110,7 +110,7 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
break; break;
default: default:
// share_counterSign // share_counterSign
shareCountSign(commandContext, (TaskEntity) task, taskDelegateAssigners); shareCountSign(commandContext, task, taskDelegateAssigners);
break; break;
} }
@ -127,11 +127,11 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
private void shareCountSign(CommandContext commandContext, TaskEntity taskEntity, private void shareCountSign(CommandContext commandContext, TaskEntity taskEntity,
List<BpmnTaskDelegateAssigner> taskDelegateAssigners) { List<BpmnTaskDelegateAssigner> taskDelegateAssigners) {
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
// 这个节点下所有审批人快照 // 这个节点下所有审批人快照
String activityListSnapshot = String activityListSnapshot =
INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey(); INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey();
taskDelegateAssigners.addAll(targetTaskAssigneeList); taskDelegateAssigners.addAll(targetTaskAssigneeList);
runtimeService.setVariable(taskEntity.getProcessInstanceId(), activityListSnapshot, taskDelegateAssigners); runtimeService.setVariable(taskEntity.getProcessInstanceId(), activityListSnapshot, taskDelegateAssigners);
log.info("正在进行加签任务:{},待加签人合并列表:{}", taskEntity.getId(), JSONUtil.toJsonStr(taskDelegateAssigners)); log.info("正在进行加签任务:{},待加签人合并列表:{}", taskEntity.getId(), JSONUtil.toJsonStr(taskDelegateAssigners));
@ -142,22 +142,22 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
} }
private void resolveOriginTask(CommandContext commandContext, ExtAxHiTaskInstService extAxHiTaskInstService, private void resolveOriginTask(CommandContext commandContext, ExtAxHiTaskInstService extAxHiTaskInstService,
TaskService taskService, Task task) { TaskService taskService, TaskEntity task) {
// 构建评论内容 // 构建评论内容
StringBuilder message = new StringBuilder("添加"); StringBuilder message = new StringBuilder("添加");
int end = Math.min(targetTaskAssigneeList.size(), COUNTERSIGN_ASSIGNER_SHOW_NUMBER); int end = Math.min(targetTaskAssigneeList.size(), COUNTERSIGN_ASSIGNER_SHOW_NUMBER);
//加签人员数量显示指定个数 //加签人员数量显示指定个数
for (int i = 0; i < end; i++) { for (int i = 0; i < end; i++) {
message.append(targetTaskAssigneeList.get(i).getAssignerName()); message.append(targetTaskAssigneeList.get(i).getAssignerName());
if (i < targetTaskAssigneeList.size() - 1) { if (i < end - 1) {
message.append(""); message.append("");
} }
} }
message.append("").append(targetTaskAssigneeList.size()).append("人进行审批"); message.append("").append(targetTaskAssigneeList.size()).append("人进行审批");
Task virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(), Task virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(),
task.getTaskDefinitionKey(), advice, originTaskAssignee, COUNTERSIGN.getStatus()); task.getTaskDefinitionKey(), advice, originTaskAssignee, COUNTERSIGN.getStatus(), new AddComment(message.toString()));
addComment(commandContext, virtualTask, COMMENT_TYPE_OPERATION_DESC, message.toString()); batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList, originTaskAssignee);
batchAddAttachment(commandContext, task.getProcessInstanceId(), task.getId(), attachmentList, originTaskAssignee); completeVirtualTask(commandContext, virtualTask);
} }
} }

View File

@ -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.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; 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.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
@ -23,7 +24,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; 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_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_TENANT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_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_DELETE_REASON;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_REJECT; 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.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.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.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.createVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
@ -69,7 +70,7 @@ public class CustomRejectionTaskCmd extends AbstractCommand<Void> implements Ser
if (Objects.nonNull(operationDesc)) { if (Objects.nonNull(operationDesc)) {
this.operationDesc = operationDesc; this.operationDesc = operationDesc;
} else { } else {
this.operationDesc = "已驳回"; this.operationDesc = "已驳回";
} }
this.attachmentList = dto.getAttachmentList(); this.attachmentList = dto.getAttachmentList();
this.approver = dto.getApprover(); this.approver = dto.getApprover();
@ -97,17 +98,18 @@ public class CustomRejectionTaskCmd extends AbstractCommand<Void> implements Ser
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(taskId).singleResult(); HistoricTaskInstance historicTaskInstance = taskQuery.taskId(taskId).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService(); 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.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), DELETED.getStatus());
Task virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(), TaskEntity virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(),
task.getTaskDefinitionKey(), advice, Objects.equals(operationDesc, "自动驳回") ? null : approver, REJECTED.getStatus()); 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, attachmentList, approver);
completeVirtualTask(commandContext, virtualTask);
batchAddAttachment(commandContext, task.getProcessInstanceId(), virtualTask.getId(), attachmentList, approver);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
finishProcessInstance(commandContext, runtimeService, task, advice); finishProcessInstance(commandContext, runtimeService, task, advice);
@ -125,7 +127,7 @@ public class CustomRejectionTaskCmd extends AbstractCommand<Void> implements Ser
runtimeService.setVariables(task.getProcessInstanceId(), variables); runtimeService.setVariables(task.getProcessInstanceId(), variables);
CommandContextUtil.getAgenda(commandContext) CommandContextUtil.getAgenda(commandContext)
.planOperation(new DeleteProcessInstanceOperation(commandContext, task.getProcessInstanceId(), .planOperation(new DeleteProcessInstanceOperation(commandContext, task.getProcessInstanceId(),
extAxHiTaskInstService)); extAxHiTaskInstService, REJECTED));
} }
} }

View File

@ -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<Void> implements Serializable {
@Override
public String paramToJsonString() {
return "";
}
@Override
public Void executeInternal(CommandContext commandContext) {
return null;
}
}

View File

@ -6,7 +6,6 @@ import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.flowable.common.engine.impl.identity.Authentication; 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.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService; import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService; 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.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery; import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity; import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.StringUtils;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
@ -77,52 +75,54 @@ public class CustomTransferUserTaskCmd extends AbstractCommand<Void> implements
@Override @Override
public Void execute(CommandContext commandContext) { public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoricTaskInstanceQuery taskQuery = HistoricTaskInstanceQuery taskQuery =
processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery(); processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(originTaskId).singleResult(); HistoricTaskInstance historicTaskInstance = taskQuery.taskId(originTaskId).singleResult();
TaskService taskService = processEngineConfiguration.getTaskService(); 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); processAssignee(processEngineConfiguration, task);
// 对被转交的任务进行建议和附件的处理
resolveOriginTask(commandContext, taskService, task); resolveOriginTask(commandContext, taskService, task);
batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList,
originTaskAssignee);
batchAddAttachment(commandContext, task.getProcessInstanceId(), task.getId(), attachmentList, // 生成转交任务
originTaskAssignee); addMultiTask(commandContext, task, targetTaskAssignee);
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), TRANSFER.getStatus());
addMultiTask(commandContext, (TaskEntity) task, targetTaskAssignee); // 结束被转交任务
((TaskEntity) task).setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), TRANSFER.getStatus()); deleteMultiTask(commandContext, task);
deleteMultiTask(commandContext, (TaskEntity) task);
return null; 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"); BpmnTaskDelegateAssigner assigner = buildDummyAssigner("transfer", TASK_ASSIGNEE_SKIP_FLAT, "dummyApprover");
task.setAssignee(assigner.buildAssigneeId()); task.setAssignee(assigner.buildAssigneeId());
((TaskEntity) task).setScopeType("TRANSFER"); task.setScopeType("TRANSFER");
taskService.saveTask(task);
Authentication.setAuthenticatedUserId(originTaskAssignee.buildAssigneeId()); Authentication.setAuthenticatedUserId(originTaskAssignee.buildAssigneeId());
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "转交给" + targetTaskAssignee.getAssignerName()); 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); Authentication.setAuthenticatedUserId(null);
taskService.saveTask(task);
} }
public void processAssignee(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) { public void processAssignee(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) {
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List<BpmnTaskDelegateAssigner> originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(), List<BpmnTaskDelegateAssigner> 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<BpmnTaskDelegateAssigner> exists = originAssingeeList.stream() Optional<BpmnTaskDelegateAssigner> exists = originAssingeeList.stream()
.filter(i -> Objects.equals(i.buildAssigneeId(), targetTaskAssignee.buildAssigneeId())).findAny(); .filter(i -> Objects.equals(i.buildAssigneeId(), targetTaskAssignee.buildAssigneeId())).findAny();
if (exists.isPresent()) { if (exists.isPresent()) {
throw new WorkflowEngineException(ASSIGNEE_HAS_BEEN_EXISTS); throw new WorkflowEngineException(ASSIGNEE_HAS_BEEN_EXISTS);
} }
@ -135,8 +135,8 @@ public class CustomTransferUserTaskCmd extends AbstractCommand<Void> implements
} }
originAssingeeList.add(targetTaskAssignee); originAssingeeList.add(targetTaskAssignee);
runtimeService.setVariable(task.getProcessInstanceId(), runtimeService.setVariable(task.getProcessInstanceId(),
INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
originAssingeeList); originAssingeeList);
} }
} }

View File

@ -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.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; 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.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter; 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.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.Execution;
import org.flowable.engine.task.Attachment; import org.flowable.engine.task.Attachment;
import org.flowable.engine.task.Comment;
import org.flowable.engine.task.Event; import org.flowable.engine.task.Event;
import org.flowable.task.api.Task; import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance; 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.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE; 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.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.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; 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_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_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_HAS_BEEN_COMPLETE;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_TYPE_MISMATCH;
import static org.flowable.task.api.Task.DEFAULT_PRIORITY; import static org.flowable.task.api.Task.DEFAULT_PRIORITY;
/** /**
@ -154,7 +156,7 @@ public class CustomTaskHelper {
//不包含对应的任务 //不包含对应的任务
if (!nodeTypes.contains(nodeType)) { if (!nodeTypes.contains(nodeType)) {
// log.warn(TASK_TYPE_MISMATCH.getMessage(), nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(","))); // 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 commandContext
* @param processInstanceId * @param processInstanceId
* @param taskId * @param task
* @param attachmentList * @param attachmentList
*/ */
public static void batchAddAttachment(CommandContext commandContext, String processInstanceId, String taskId, public static void batchAddAttachment(CommandContext commandContext, String processInstanceId, TaskEntity task,
List<AttachmentDTO> attachmentList, BpmnTaskDelegateAssigner assigner) { List<AttachmentDTO> attachmentList, BpmnTaskDelegateAssigner assigner) {
if (CollectionUtils.isEmpty(attachmentList)) { if (CollectionUtils.isEmpty(attachmentList)) {
return; return;
@ -232,22 +234,30 @@ public class CustomTaskHelper {
TaskService taskService = processEngineConfiguration.getTaskService(); TaskService taskService = processEngineConfiguration.getTaskService();
Authentication.setAuthenticatedUserId(assigner.buildAssigneeId()); Authentication.setAuthenticatedUserId(assigner.buildAssigneeId());
attachmentList.forEach(dto -> { 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()); dto.getName(), dto.getDescription(), dto.getUrl());
taskService.saveAttachment(attachment); taskService.saveAttachment(attachment);
}); });
task.setTransientVariableLocal(TASK_ATTACHMENTS_VAR_NAME, attachmentList);
Authentication.setAuthenticatedUserId(null); Authentication.setAuthenticatedUserId(null);
} }
public static Comment addComment(CommandContext commandContext, String taskId, String processInstanceId, public static void addComment(CommandContext commandContext, String taskId, String processInstanceId,
String type, String message) { String type, String message) {
TaskEntity task = new TaskEntityImpl(); TaskEntity task = new TaskEntityImpl();
task.setId(taskId); task.setId(taskId);
task.setProcessInstanceId(processInstanceId); 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 = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
@ -269,8 +279,7 @@ public class CustomTaskHelper {
comment.setFullMessage(message); comment.setFullMessage(message);
processEngineConfiguration.getCommentEntityManager().insert(comment); processEngineConfiguration.getCommentEntityManager().insert(comment);
task.setTransientVariableLocal(type, message);
return comment;
} }
public static Attachment addAttachment(CommandContext commandContext, Task task, AttachmentDTO attachmentDto) { public static Attachment addAttachment(CommandContext commandContext, Task task, AttachmentDTO attachmentDto) {
@ -313,10 +322,10 @@ public class CustomTaskHelper {
* @param extTaskInstStatus 节点状态 * @param extTaskInstStatus 节点状态
* @return * @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, , String processInstanceId, String nodeName, String taskDefinitionKey, String advice,
BpmnTaskDelegateAssigner assigner, BpmnTaskDelegateAssigner assigner,
String extTaskInstStatus) { String extTaskInstStatus, AddComment addComment) {
ProcessEngineConfigurationImpl processEngineConfiguration = ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext); CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoryService historyService = processEngineConfiguration.getHistoryService(); HistoryService historyService = processEngineConfiguration.getHistoryService();
@ -336,8 +345,6 @@ public class CustomTaskHelper {
task.setTaskDefinitionKey(taskDefinitionKey); task.setTaskDefinitionKey(taskDefinitionKey);
task.setPriority(DEFAULT_PRIORITY); task.setPriority(DEFAULT_PRIORITY);
task.setCreateTime(new Date()); task.setCreateTime(new Date());
// 创建临时节点
taskService.saveTask(task);
if (Objects.nonNull(assigner)) { if (Objects.nonNull(assigner)) {
CommandContextUtil.getEntityCache().findInCache(TaskEntity.class).stream() CommandContextUtil.getEntityCache().findInCache(TaskEntity.class).stream()
@ -351,23 +358,36 @@ public class CustomTaskHelper {
// 添加审批意见 // 添加审批意见
addAdvice(commandContext, task, advice, Objects.nonNull(assigner) ? assigner.buildAssigneeId() : null); addAdvice(commandContext, task, advice, Objects.nonNull(assigner) ? assigner.buildAssigneeId() : null);
// 添加操作描述
addComment(commandContext, task, addComment);
CustomTaskHelper.createExtTaskInst(extAxHiTaskInstService, task.getProcessInstanceId(), task.getId(), CustomTaskHelper.createExtTaskInst(extAxHiTaskInstService, task.getProcessInstanceId(), task.getId(),
task.getTaskDefinitionKey(), assigner, extTaskInstStatus); task.getTaskDefinitionKey(), assigner, extTaskInstStatus);
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), extTaskInstStatus); task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), extTaskInstStatus);
// 保存任务
taskService.saveTask(task);
if (Objects.nonNull(assigner)) { if (Objects.nonNull(assigner)) {
// 设置快照信息 // 设置快照信息
task.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + task.getId(), assigner.toJson()); task.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + task.getId(), assigner.toJson());
} }
// 完成临时节点 // 完成临时节点, 1.4.2虚拟节点创建方法不再默认完成需主动调用 completeVirtualTask 完成
taskService.complete(task.getId()); // taskService.complete(task.getId());
return task; 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)) { if (StringUtils.hasLength(comment)) {
Authentication.setAuthenticatedUserId(userId); Authentication.setAuthenticatedUserId(userId);
addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment); addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment);

View File

@ -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.CommandConfig;
import org.flowable.common.engine.impl.interceptor.CommandExecutor; 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; 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> T execute(CommandConfig config, Command<T> command, CommandExecutor commandExecutor) { public <T> T execute(CommandConfig config, Command<T> command, CommandExecutor commandExecutor) {
long waitTime = waitTimeInMs; long waitTime = waitTimeInMs;
int failedAttempts = 0; int failedAttempts = 0;
Throwable lastException = null;
do { do {
if (failedAttempts > 0) { if (failedAttempts > 0) {
log.warn("Waiting for {}ms before retrying the command.", waitTime); log.warn("Waiting for {}ms before retrying the command.", waitTime);
@ -38,9 +41,6 @@ public class CustomRetryInterceptor extends AbstractCommandInterceptor {
try { try {
// try to execute the command // try to execute the command
if (log.isDebugEnabled()) {
log.debug("assignableFrom result: {}", AbstractCommand.class.isAssignableFrom(command.getClass()));
}
if (AbstractCommand.class.isAssignableFrom(command.getClass())) { if (AbstractCommand.class.isAssignableFrom(command.getClass())) {
// 如果在以后,重试三次也不能解决的话, 可以利用这里的拿到的参数,重新自动构造CMD,并执行. // 如果在以后,重试三次也不能解决的话, 可以利用这里的拿到的参数,重新自动构造CMD,并执行.
log.info("Executing command params: {} traceId:{} ", TraceUtil.traceId(), log.info("Executing command params: {} traceId:{} ", TraceUtil.traceId(),
@ -50,12 +50,13 @@ public class CustomRetryInterceptor extends AbstractCommandInterceptor {
} catch (PersistenceException e) { } catch (PersistenceException e) {
log.warn("Caught persistence exception: {}", e.getMessage(), e); log.warn("Caught persistence exception: {}", e.getMessage(), e);
lastException = e;
} }
failedAttempts++; failedAttempts++;
} while (failedAttempts <= numOfRetries); } 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) { protected void waitBeforeRetry(long waitTime) {

View File

@ -13,11 +13,11 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope; import org.flowable.variable.api.delegate.VariableScope;
@Slf4j @Slf4j
public class AsyncAbortProcessInstanceHandler extends AbstractExecuteWithLockJobHandler implements JobHandler { public class AsyncAbortProcessInstanceJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
public static final String TYPE = "async-abort-instance"; public static final String TYPE = "async-abort-instance";
private final ExtAxHiTaskInstService extAxHiTaskInstService; private final ExtAxHiTaskInstService extAxHiTaskInstService;
public AsyncAbortProcessInstanceHandler(ExtAxHiTaskInstService extAxHiTaskInstService) { public AsyncAbortProcessInstanceJobHandler(ExtAxHiTaskInstService extAxHiTaskInstService) {
this.extAxHiTaskInstService = extAxHiTaskInstService; this.extAxHiTaskInstService = extAxHiTaskInstService;
} }

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.core.engine.job; 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.activity.BpmnActivityTimeoutTriggerDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
import cn.axzo.workflow.core.service.BpmnProcessActivityService; import cn.axzo.workflow.core.service.BpmnProcessActivityService;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j; 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) { public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.warn("AsyncActivityLeaveJobHandler exec start..."); log.warn("AsyncActivityLeaveJobHandler exec start...");
BpmnActivityTimeoutTriggerDTO dto = JSON.parseObject(job.getCustomValues(), BpmnActivityTimeoutTriggerDTO.class); BpmnActivityTimeoutTriggerDTO dto = JSON.parseObject(job.getCustomValues(), BpmnActivityTimeoutTriggerDTO.class);
bpmnProcessActivityService.trigger(dto.getTriggerId()); bpmnProcessActivityService.trigger(BpmnActivityTriggerDTO.builder().async(false).triggerId(dto.getTriggerId()).build());
} }
} }

View File

@ -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()));
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -13,13 +13,13 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope; import org.flowable.variable.api.delegate.VariableScope;
@Slf4j @Slf4j
public class AsyncCancelProcessInstanceHandler extends AbstractJobHandler implements JobHandler { public class AsyncCancelProcessInstanceJobHandler extends AbstractJobHandler implements JobHandler {
public static final String TYPE = "async-cancel-process"; public static final String TYPE = "async-cancel-process";
private final ExtAxHiTaskInstService extAxHiTaskInstService; private final ExtAxHiTaskInstService extAxHiTaskInstService;
public AsyncCancelProcessInstanceHandler(ExtAxHiTaskInstService extAxHiTaskInstService) { public AsyncCancelProcessInstanceJobHandler(ExtAxHiTaskInstService extAxHiTaskInstService) {
this.extAxHiTaskInstService = extAxHiTaskInstService; this.extAxHiTaskInstService = extAxHiTaskInstService;
} }

View File

@ -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);
}
}
}

View File

@ -4,12 +4,18 @@ import cn.axzo.workflow.common.constant.BpmnConstants;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum; import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum; import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType; 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.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; 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.BpmnTaskAssigneeSelector;
import cn.axzo.workflow.core.deletage.BpmnTaskCalculateDTO; import cn.axzo.workflow.core.deletage.BpmnTaskCalculateDTO;
import cn.axzo.workflow.core.deletage.BpmnTaskDelegate; import cn.axzo.workflow.core.deletage.BpmnTaskDelegate;
import cn.axzo.workflow.core.deletage.MockTaskAssigneeSelector; 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 cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists; 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.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener; import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil; import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -38,6 +45,7 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ASSIGNER_LIMIT_NUMBER; 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.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;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_TYPE; 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 @Slf4j
public class EngineExecutionStartListener implements ExecutionListener { public class EngineExecutionStartListener implements ExecutionListener {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Value("${spring.profiles.active}")
private String profile;
@Resource @Resource
@Deprecated @Deprecated
private ObjectProvider<BpmnTaskDelegate> bpmTaskDelegate; private ObjectProvider<BpmnTaskDelegate> bpmTaskDelegate;
@Resource @Resource
@Lazy @Lazy
private List<BpmnTaskAssigneeSelector> selectors; private List<BpmnTaskAssigneeSelector> selectors;
@Value("${workflow.api.timeout:10}") @Resource
private Long apiTimeout; private SupportRefreshProperties refreshProperties;
@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<String, String> assigneeMap;
@Override @Override
public void notify(DelegateExecution execution) { public void notify(DelegateExecution execution) {
@ -180,6 +183,7 @@ public class EngineExecutionStartListener implements ExecutionListener {
} }
log.info("当前节点id: [{}], name: [{}] 审批人为空, 将执行审批人为空的兜底配置!", userTask.getId(), userTask.getName()); log.info("当前节点id: [{}], name: [{}] 审批人为空, 将执行审批人为空的兜底配置!", userTask.getId(), userTask.getName());
getApproverEmptyHandleType(userTask).ifPresent(type -> { getApproverEmptyHandleType(userTask).ifPresent(type -> {
log.info("节点兜底的配置模式:[{}]", type.getType());
switch (type) { switch (type) {
case autoPassed: case autoPassed:
case autoRejection: case autoRejection:
@ -189,19 +193,32 @@ public class EngineExecutionStartListener implements ExecutionListener {
case transferToAdmin: case transferToAdmin:
assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.transferToAdmin.getType(), userTask, assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.transferToAdmin.getType(), userTask,
execution, true)); execution, true));
finalEmptyAssigneeHandle(assigners, userTask, execution, "转交管理员失败,系统中止");
break; break;
case specifyAssignee: case specifyAssignee:
List<BpmnTaskDelegateAssigner> emptyAssignees = assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.specifyAssignee.getType(), userTask,
BpmnMetaParserHelper.getEmptyApproverSpecify(userTask) execution, true));
.map(listStr -> JSON.parseArray(listStr, BpmnTaskDelegateAssigner.class)) finalEmptyAssigneeHandle(assigners, userTask, execution, "转交指定人员失败,系统自动中止");
.orElse(Collections.emptyList());
assigners.addAll(emptyAssignees);
default: default:
break; break;
} }
}); });
} }
private void finalEmptyAssigneeHandle(List<BpmnTaskDelegateAssigner> 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\"}]' * \"personId\":\"89508\",\"assignee\":\"2000560\",\"assigneeType\":\"3\"}]'
* }" * }"
*/ */
Boolean mock = refreshProperties.getMock();
Boolean global = refreshProperties.getGlobal();
String category = refreshProperties.getCategory();
Map<String, String> assigneeMap = refreshProperties.getAssigneeMap();
if ((mock && global) || if ((mock && global) ||
(mock && !global && Objects.equals(category, execution.getProcessDefinitionId().split(":")[0]))) { (mock && !global && Objects.equals(category, execution.getProcessDefinitionId().split(":")[0]))) {
log.info("当前系统 Nacos 配置中开启了 mock: {}, 将使用 mock 方式查找审批人", mock); log.info("当前系统 Nacos 配置中开启了 mock: {}, 将使用 mock 方式查找审批人", mock);
@ -308,7 +329,4 @@ public class EngineExecutionStartListener implements ExecutionListener {
}); });
} }
public Long getApiTimeout() {
return apiTimeout;
}
} }

View File

@ -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<EntityEventHandle> handles;
public static final Set<FlowableEngineEventType> SUPPORTED =
ImmutableSet.<FlowableEngineEventType>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;
}
}

View File

@ -0,0 +1,27 @@
package cn.axzo.workflow.core.engine.listener.entity;
/**
* TODO
*
* @author wangli
* @since 2024-09-06 00:03
*/
public interface EntityEventHandle<T> {
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);
}

View File

@ -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<CommentEntity> {
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());
}
}

View File

@ -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;
/**
* 同意评论加签转交驳回撤回中止抄送
* <p>
* 回退
*
* @author wangli
* @since 2024-09-06 00:02
*/
@Slf4j
@Component
@AllArgsConstructor
public class TaskEntityEventHandle implements EntityEventHandle<TaskEntity> {
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<BpmnTaskDelegateAssigner> 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<ExtAxProcessLog> 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<BpmnTaskDelegateAssigner> 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;
}
}

View File

@ -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;
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.core.engine.operation; package cn.axzo.workflow.core.engine.operation;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService; import org.flowable.engine.HistoryService;
@ -16,6 +17,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_CLOSING_TYPE;
/** /**
* 通用的在 Command 内执行删除流程实例时的额外操作 * 通用的在 Command 内执行删除流程实例时的额外操作
@ -27,21 +29,25 @@ public class DeleteProcessInstanceOperation extends AbstractOperation {
private final String processInstanceId; private final String processInstanceId;
private final ExtAxHiTaskInstService extAxHiTaskInstService; private final ExtAxHiTaskInstService extAxHiTaskInstService;
private String customDeleteReason; private String customDeleteReason;
private final BpmnProcessInstanceResultEnum closingType;
public DeleteProcessInstanceOperation(CommandContext commandContext, String processInstanceId, public DeleteProcessInstanceOperation(CommandContext commandContext, String processInstanceId,
ExtAxHiTaskInstService extAxHiTaskInstService) { ExtAxHiTaskInstService extAxHiTaskInstService,
BpmnProcessInstanceResultEnum closingType) {
super(commandContext, null); super(commandContext, null);
this.processInstanceId = processInstanceId; this.processInstanceId = processInstanceId;
this.extAxHiTaskInstService = extAxHiTaskInstService; this.extAxHiTaskInstService = extAxHiTaskInstService;
this.closingType = closingType;
} }
public DeleteProcessInstanceOperation(CommandContext commandContext, String processInstanceId, public DeleteProcessInstanceOperation(CommandContext commandContext, String processInstanceId,
ExtAxHiTaskInstService extAxHiTaskInstService, ExtAxHiTaskInstService extAxHiTaskInstService,
String customDeleteReason) { String customDeleteReason, BpmnProcessInstanceResultEnum closingType) {
super(commandContext, null); super(commandContext, null);
this.processInstanceId = processInstanceId; this.processInstanceId = processInstanceId;
this.extAxHiTaskInstService = extAxHiTaskInstService; this.extAxHiTaskInstService = extAxHiTaskInstService;
this.customDeleteReason = customDeleteReason; this.customDeleteReason = customDeleteReason;
this.closingType = closingType;
} }
@Override @Override
@ -65,6 +71,7 @@ public class DeleteProcessInstanceOperation extends AbstractOperation {
} }
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
runtimeService.setVariableLocal(processInstanceId, PROCESS_CLOSING_TYPE, closingType);
runtimeService.deleteProcessInstance(processInstanceId, StringUtils.hasText(customDeleteReason) ? customDeleteReason : HIDDEN_ASSIGNEE_ID); runtimeService.deleteProcessInstance(processInstanceId, StringUtils.hasText(customDeleteReason) ? customDeleteReason : HIDDEN_ASSIGNEE_ID);
// 将结束流程实例的原因记录下来 // 将结束流程实例的原因记录下来
// runtimeService.deleteProcessInstance(processInstanceId, reason); // runtimeService.deleteProcessInstance(processInstanceId, reason);

View File

@ -59,7 +59,7 @@ public class AutoPassTransactionListener implements TransactionListener {
pass.setTaskId(delegateTask.getId()); pass.setTaskId(delegateTask.getId());
pass.setAdvice(advice); pass.setAdvice(advice);
pass.setApprover(assigner); pass.setApprover(assigner);
pass.setOperationDesc("自动通过"); pass.setOperationDesc("自动通过");
String jobId = commandExecutor.execute(commandConfig, new CustomApproveTaskAsyncCmd(pass)); String jobId = commandExecutor.execute(commandConfig, new CustomApproveTaskAsyncCmd(pass));
// 重置任务因为上面的 cmd 和这个 cmd lock 对象不一致 // 重置任务因为上面的 cmd 和这个 cmd lock 对象不一致

View File

@ -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);
}

View File

@ -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 包集成和微服务集成
* <p>
* 该监听主要是监听启动无人的业务节点超时告警功能
*
* @author wangli
* @since 2024-09-11 11:44
*/
@Slf4j
@Component
@Scope("prototype")
@AllArgsConstructor
public class InternalBpmnActivityEventListener_lo_Listener extends AbstractBpmnEventListener<ActivityOperationContext> implements BpmnActivityEventListener, Ordered {
private final SupportRefreshProperties refreshProperties;
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
/**
* 节点已启动
* <p>
* 创建一个周期性监控业务节点执行状态的任务
*
* @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);
}
}
}

View File

@ -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<ExtAxProcessLog> {
/**
* 流程实例 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<BpmnTaskDelegateAssigner> 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;
}

View File

@ -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<ExtAxProcessLog> {
}

View File

@ -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.BpmnActivityTimeoutCallbackDTO;
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO; 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.BpmnActivitySetAssigneeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
import org.flowable.job.service.impl.persistence.entity.JobEntity; 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 { 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); void setAssignee(BpmnActivitySetAssigneeDTO dto);
/**
* 给指定实例的指定节点重设审批人
*
* @param dto
*/
void setAssigneeAsync(BpmnActivitySetAssigneeDTO dto);
/** /**
* 设置指定业务接口继续往下流转的触发时间 * 设置指定业务接口继续往下流转的触发时间
* *

View File

@ -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.request.bpmn.process.BpmnProcessDefinitionPageDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; import cn.axzo.workflow.common.model.response.bpmn.process.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.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Model; import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinition;
@ -82,4 +85,12 @@ public interface BpmnProcessDefinitionService {
List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds); List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds);
void delete(String deploymentId, Boolean cascade); void delete(String deploymentId, Boolean cascade);
List<EndEvent> findEndFlowElement(String processDefinitionId);
List<FlowNode> findFlowNodes(String processDefinitionId);
List<FlowElement> findFlowElements(String processDefinitionId);
List<FlowElement> findFlowElementsByIds(String processDefinitionId, List<String> flowElementIds);
} }

View File

@ -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.BpmnProcessInstanceCheckApproverDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO; 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.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.BpmnProcessInstanceMyPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO; 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.process.HistoricProcessInstanceSearchDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult; 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.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO; 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.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO; 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.HistoricProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.form.api.FormInfo; import org.flowable.form.api.FormInfo;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -107,7 +110,7 @@ public interface BpmnProcessInstanceService {
* 获得流程实例 * 获得流程实例
* *
* @param processDefinitionId 流程实例的编号 * @param processDefinitionId 流程实例的编号
* @param status 状态 * @param status 状态
* @link SuspensionState.ACTIVE.getStateCode() * @link SuspensionState.ACTIVE.getStateCode()
*/ */
Boolean updateProcessStatus(String processDefinitionId, Integer status); Boolean updateProcessStatus(String processDefinitionId, Integer status);
@ -143,9 +146,28 @@ public interface BpmnProcessInstanceService {
*/ */
List<ProcessNodeDetailVO> getProcessInstanceNodeForecast(String processInstanceId, String tenantId); List<ProcessNodeDetailVO> getProcessInstanceNodeForecast(String processInstanceId, String tenantId);
/**
* 对指定流程的指定节点开始推送未来的结点并结合变量计算正确的分支
*
* @param processInstanceId 实例编号
* @param startNodeDefinitionKey 从该节点开始推断后续的节点
* @param containSelf 是否包含起始节点
* @param checkAliveThrowException 如果给的实例编号已到终态不会执行推测用该参数为 true 抛出实例完结的异常信息如果为 false时直接返回空集合
* @return
*/
List<ProcessNodeDetailVO> getProcessInstanceNodeForecastWithSpecifyTaskDefinitionKey(String processInstanceId, ProcessInstance instance, String startNodeDefinitionKey, Boolean containSelf, Boolean checkAliveThrowException);
List<ProcessNodeDetailVO> getProcessInstanceNodeFilterForecast(String processInstanceId, String tenantId, List<String> nodeDefinitionKeys); List<ProcessNodeDetailVO> getProcessInstanceNodeFilterForecast(String processInstanceId, String tenantId, List<String> nodeDefinitionKeys);
List<String> getTenantIds(); List<String> getTenantIds();
Boolean checkInstanceApprover(BpmnProcessInstanceCheckApproverDTO dto); Boolean checkInstanceApprover(BpmnProcessInstanceCheckApproverDTO dto);
/**
* 获取指定流程实例的日志
*
* @param dto
* @return
*/
BpmnProcessInstanceLogVO getProcessInstanceLog(BpmnProcessInstanceLogQueryDTO dto);
} }

View File

@ -5,4 +5,8 @@ public interface BpmnProcessJobService {
void executeDeadLetterJobActionByJobId(String jobId); void executeDeadLetterJobActionByJobId(String jobId);
void executeDeadLetterJobActionByProcInstId(String processInstanceId); void executeDeadLetterJobActionByProcInstId(String processInstanceId);
String getDeadLetterJobExceptionStacktrace(String processInstId);
String getDeadLetterJobExceptionStacktraceByJobId(String jobId);
} }

View File

@ -1,10 +1,11 @@
package cn.axzo.workflow.core.service; 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.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO; 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.BpmnTaskAttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; 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.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO;
@ -41,6 +42,11 @@ public interface BpmnProcessTaskService {
*/ */
void approveTask(BpmnTaskAuditDTO taskAuditDTO); void approveTask(BpmnTaskAuditDTO taskAuditDTO);
/**
* 回退
*/
void backTask(BpmnTaskBackAuditDTO taskAuditDTO);
/** /**
* 批量同意 * 批量同意
* *
@ -48,6 +54,12 @@ public interface BpmnProcessTaskService {
*/ */
BatchOperationResultVO batchApproveTask(List<BpmnTaskAuditDTO> taskAuditDTOS); BatchOperationResultVO batchApproveTask(List<BpmnTaskAuditDTO> taskAuditDTOS);
/**
* 回退到指定节点可以回退节点选项
* @param taskId 任务id
*/
List<BpmnOptionalNodeDTO> getBackOptionalNodes(String taskId);
/** /**
* 驳回 * 驳回
*/ */

View File

@ -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<ExtAxProcessLog> genericQuery(ExtAxProcessLog query);
}

View File

@ -30,12 +30,12 @@ import static org.mapstruct.NullValueCheckStrategy.ALWAYS;
* @since 2024/1/25 16:31 * @since 2024/1/25 16:31
*/ */
@Mapper( @Mapper(
componentModel = "spring", componentModel = "spring",
nullValueCheckStrategy = ALWAYS, nullValueCheckStrategy = ALWAYS,
imports = Arrays.class imports = Arrays.class
) )
public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConverter<BpmnProcessInstanceAdminPageItemVO, public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConverter<BpmnProcessInstanceAdminPageItemVO,
HistoricProcessInstance> { HistoricProcessInstance> {
@Mapping(target = "processInstanceId", source = "id") @Mapping(target = "processInstanceId", source = "id")
@Mapping(target = "processInstanceName", source = "name") @Mapping(target = "processInstanceName", source = "name")
@ -58,8 +58,8 @@ public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConvert
if (Objects.equals(PROCESSING.getStatus(), i.getBusinessStatus())) { if (Objects.equals(PROCESSING.getStatus(), i.getBusinessStatus())) {
List<FlowElement> flowElements = instanceFlowElementMap.get(i.getId()).stream() List<FlowElement> flowElements = instanceFlowElementMap.get(i.getId()).stream()
.filter(j -> j instanceof UserTask || j instanceof ReceiveTask || j instanceof ServiceTask) .filter(j -> j instanceof UserTask || j instanceof ReceiveTask || j instanceof ServiceTask)
.collect(Collectors.toList()); .collect(Collectors.toList());
vo.setTotalNodeCount(flowElements.size()); vo.setTotalNodeCount(flowElements.size());
// 进行中的节点 // 进行中的节点
@ -85,7 +85,7 @@ public interface BpmnProcessInstanceAdminPageItemConverter extends EntityConvert
vo.setCategoryDesc(category.getLabel()); vo.setCategoryDesc(category.getLabel());
vo.setWorkspaceTypeCode(category.getWorkspaceTypeCode()); vo.setWorkspaceTypeCode(category.getWorkspaceTypeCode());
vo.setBusinessStatusDesc(BpmnProcessInstanceResultEnum.valueOfStatus(vo.getBusinessStatus()).getDesc()); 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); result.add(vo);
}); });
return result; return result;

View File

@ -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.BpmnActivityTimeoutCallbackDTO;
import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO; 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.BpmnActivitySetAssigneeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceCmd; 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.CustomBizSpecifyAssigneeToTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomBusinessNodeTimeoutCallbackCmd; import cn.axzo.workflow.core.engine.cmd.CustomBusinessNodeTimeoutCallbackCmd;
import cn.axzo.workflow.core.engine.cmd.CustomBusinessNodeTimeoutTriggerCmd; 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.BpmnProcessActivityService;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandExecutor; import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.RuntimeService; import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.Execution; 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.spring.SpringProcessEngineConfiguration;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Objects; 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.ACTIVITY_TRIGGER_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.PROCESS_INSTANCE_IS_NOT_EXIST;
@Service @Service
@ -46,42 +43,15 @@ public class BpmnProcessActivityServiceImpl implements BpmnProcessActivityServic
@Lazy @Lazy
private SpringProcessEngineConfiguration processEngineConfiguration; private SpringProcessEngineConfiguration processEngineConfiguration;
private static final String JOB_TRIGGER_ASYNC_NAME = "asyncTrigger";
private static final String JOB_ASSIGNEE_ASYNC_NAME = "asyncSetAssignee";
@Override @Override
public void executeAsyncJob(JobEntity job) { @Transactional(rollbackFor = Exception.class)
if (job == null) { public void trigger(BpmnActivityTriggerDTO dto) {
log.warn("job is null"); CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
throw new NullPointerException("job is null"); 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 @Override
@ -95,43 +65,12 @@ public class BpmnProcessActivityServiceImpl implements BpmnProcessActivityServic
commandExecutor.execute(new CustomAbortProcessInstanceCmd(execution.getProcessInstanceId(), null, "业务未指定审批人", extAxHiTaskInstService)); commandExecutor.execute(new CustomAbortProcessInstanceCmd(execution.getProcessInstanceId(), null, "业务未指定审批人", extAxHiTaskInstService));
return; return;
} }
commandExecutor.execute(new CustomBizSpecifyAssigneeToTaskCmd(dto.getTriggerId(), dto.getAssigners()));
}
@Override if(Boolean.TRUE.equals(dto.getAsync())) {
public void setAssigneeAsync(BpmnActivitySetAssigneeDTO dto) { commandExecutor.execute(new CustomBizSpecifyAssigneeToTaskAsyncCmd(dto));
//查询任务 } else {
Task task = CustomBizSpecifyAssigneeToTaskCmd.getOperateTask(processEngineConfiguration.getTaskService(), dto.getTriggerId()); commandExecutor.execute(new CustomBizSpecifyAssigneeToTaskCmd(dto.getTriggerId(), dto.getAssigners()));
//先校验 }
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<Void>) 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;
});
} }
@Override @Override

View File

@ -14,6 +14,9 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel; 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.common.engine.impl.db.SuspensionState;
import org.flowable.engine.RepositoryService; import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.Deployment;
@ -27,6 +30,7 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -282,4 +286,50 @@ public class BpmnProcessDefinitionServiceImpl implements BpmnProcessDefinitionSe
} }
return repositoryService.createDeploymentQuery().deploymentId(id).singleResult(); return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();
} }
@Override
public List<EndEvent> findEndFlowElement(String processDefinitionId) {
return getNodesByType(processDefinitionId, EndEvent.class);
}
@Override
public List<FlowNode> findFlowNodes(String processDefinitionId) {
return getNodesByType(processDefinitionId, FlowNode.class);
}
@Override
public List<FlowElement> findFlowElements(String processDefinitionId) {
return getNodesByType(processDefinitionId, FlowElement.class);
}
@Override
public List<FlowElement> findFlowElementsByIds(String processDefinitionId, List<String> 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 <T extends FlowElement> List<T> getNodesByType(String processDefinitionId, Class<T> clazz) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(processDefinitionId)
.singleResult();
if (processDefinition == null) {
return emptyList();
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
Collection<T> flowNodes = bpmnModel.getMainProcess().findFlowElementsOfType(clazz);
if (CollectionUtils.isEmpty(flowNodes)) {
return emptyList();
}
return new ArrayList<>(flowNodes);
}
} }

View File

@ -1,8 +1,15 @@
package cn.axzo.workflow.core.service.impl; package cn.axzo.workflow.core.service.impl;
import cn.axzo.workflow.common.constant.BpmnConstants; 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.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.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.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO; import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO; import cn.axzo.workflow.common.model.request.bpmn.process.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.BpmnProcessInstanceCheckApproverDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO; 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.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.BpmnProcessInstanceMyPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO; 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.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.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.category.CategorySearchDTO; import cn.axzo.workflow.common.model.request.category.CategorySearchDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult; 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.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; 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.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.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO; 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.HistoricProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; 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.common.model.response.category.CategoryItemVO;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException; import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.common.utils.BpmnCollectionUtils; 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.CustomCarbonCopyUserSelectorCmd;
import cn.axzo.workflow.core.engine.cmd.CustomForecastUserTaskAssigneeCmd; import cn.axzo.workflow.core.engine.cmd.CustomForecastUserTaskAssigneeCmd;
import cn.axzo.workflow.core.engine.listener.EngineExecutionStartListener; 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.BpmnProcessDefinitionService;
import cn.axzo.workflow.core.service.BpmnProcessInstanceService; import cn.axzo.workflow.core.service.BpmnProcessInstanceService;
import cn.axzo.workflow.core.service.CategoryService; import cn.axzo.workflow.core.service.CategoryService;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; 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.BpmnHistoricProcessInstanceConverter;
import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter; import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter;
import cn.axzo.workflow.core.service.converter.BpmnProcessInstanceAdminPageItemConverter; 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.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder; import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.engine.runtime.ProcessInstanceQuery; import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.engine.task.Attachment;
import org.flowable.form.api.FormInfo; import org.flowable.form.api.FormInfo;
import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.task.api.Task; import org.flowable.task.api.Task;
@ -95,6 +109,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; 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.AND_SIGN_EXPRESSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_ORG_RELATION; 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.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.CREATE_INSTANCE_PARAMS;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121; import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR; 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.EXCEPTIONAL;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.GENERAL; 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.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_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.PROCESSING;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
import static cn.axzo.workflow.common.enums.WorkspaceType.GOVERNMENT; 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_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_INSTANCE_NOT_EXISTS;
@ -187,6 +212,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
@Resource @Resource
@Lazy @Lazy
private BpmnProcessInstanceService bpmnProcessInstanceService; private BpmnProcessInstanceService bpmnProcessInstanceService;
@Resource
private ExtAxProcessLogService processLogService;
@Override @Override
public HistoricProcessInstance getProcessInstanceByBusinessKey(String businessKey, @Nullable String tenantId, public HistoricProcessInstance getProcessInstanceByBusinessKey(String businessKey, @Nullable String tenantId,
@ -844,6 +871,30 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
return getProcessInstanceNodeFilterForecast(processInstanceId, tenantId, Collections.emptyList()); return getProcessInstanceNodeFilterForecast(processInstanceId, tenantId, Collections.emptyList());
} }
@Override
public List<ProcessNodeDetailVO> 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<FlowElement> flowElements = forecastService.performProcessForecasting(instance.getProcessInstanceId(), instance, startNodeDefinitionKey, containSelf);
return buildNodeDetailVos(processInstanceId, Collections.emptyList(), instance, flowElements);
}
@Override @Override
public List<ProcessNodeDetailVO> getProcessInstanceNodeFilterForecast(String processInstanceId, String tenantId, List<String> nodeDefinitionKeys) { public List<ProcessNodeDetailVO> getProcessInstanceNodeFilterForecast(String processInstanceId, String tenantId, List<String> nodeDefinitionKeys) {
ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery() ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery()
@ -857,6 +908,10 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
} }
List<FlowElement> flowElements = forecastService.performProcessForecasting(processInstanceId, instance); List<FlowElement> flowElements = forecastService.performProcessForecasting(processInstanceId, instance);
return buildNodeDetailVos(processInstanceId, nodeDefinitionKeys, instance, flowElements);
}
private List<ProcessNodeDetailVO> buildNodeDetailVos(String processInstanceId, List<String> nodeDefinitionKeys, ProcessInstance instance, List<FlowElement> flowElements) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId()); BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
List<ProcessNodeDetailVO> resultList = new ArrayList<>(flowElements.size()); List<ProcessNodeDetailVO> resultList = new ArrayList<>(flowElements.size());
// 发起人节点,也是一个 UserTask 节点, 所以这里默认就包含了发起人节点 // 发起人节点,也是一个 UserTask 节点, 所以这里默认就包含了发起人节点
@ -961,4 +1016,361 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
.list(); .list();
return !CollectionUtils.isEmpty(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<ExtAxProcessLog> logs = processLogService.genericQuery(query).stream()
.sorted(Comparator.comparing(ExtAxProcessLog::getEndTime, Comparator.nullsLast(Comparator.naturalOrder())))
.collect(Collectors.toList());
List<ProcessNodeDetailVO> 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<String, Object> 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<BpmnButtonMetaInfo> 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<BpmnTaskDelegateAssigner> 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<String> customButtonKeys = authorizedButtons.stream()
.filter(i -> Objects.equals(i.getType(), "CUSTOM"))
.map(BpmnButtonMetaInfo::getBtnKey)
.distinct().collect(Collectors.toList());
List<BpmnButtonMetaInfo> 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<BpmnButtonMetaInfo> chooseButtons(BpmnProcessInstanceLogVO logVO, String buttonConfigName) {
List<BpmnButtonMetaInfo> 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<BpmnTaskInstanceLogVO> genericTaskLogVos(String processInstanceId,
List<ExtAxProcessLog> logs,
List<ProcessNodeDetailVO> forecasting,
BpmnProcessInstanceLogQueryDTO dto) {
List<BpmnTaskInstanceLogVO> tasks = new ArrayList<>();
Map<String, List<Attachment>> 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<BpmnTaskInstanceLogVO> tasks) {
tasks.forEach(i -> {
if (ListUtils.emptyIfNull(i.getForecastAssignees()).isEmpty()) {
i.setForecastAssignees(null);
}
});
}
private static void handleEncrypt(Boolean encrypt, List<BpmnTaskInstanceLogVO> 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<ProcessNodeDetailVO> forecasting, List<BpmnTaskInstanceLogVO> 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<ExtAxProcessLog> logs,
List<BpmnTaskInstanceLogVO> tasks,
Map<String, List<Attachment>> attachmentByTaskMap,
BpmnTaskDelegateAssigner visitor) {
ListUtils.emptyIfNull(logs).forEach(e -> {
Optional<BpmnTaskInstanceLogVO> 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<BpmnTaskDelegateAssigner> 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<AttachmentDTO> getAttachmentByType(Map<String, List<Attachment>> 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());
}
} }

View File

@ -8,6 +8,7 @@ import org.flowable.engine.ManagementService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.job.api.Job; import org.flowable.job.api.Job;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
@ -39,6 +40,31 @@ public class BpmnProcessJobServiceImp implements BpmnProcessJobService {
} }
} }
@Override
public String getDeadLetterJobExceptionStacktrace(String processInstId) {
List<Job> 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) { protected Job getDeadLetterJobById(String jobId) {
Job job = managementService.createDeadLetterJobQuery().jobId(jobId).singleResult(); Job job = managementService.createDeadLetterJobQuery().jobId(jobId).singleResult();
if (job == null) { if (job == null) {

View File

@ -1,11 +1,16 @@
package cn.axzo.workflow.core.service.impl; 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.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.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.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO; 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.BpmnTaskAttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; 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.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.request.bpmn.task.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.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskAsyncCmd; import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskCmd; 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.CustomCommentTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomCompleteDummyTaskCmd; import cn.axzo.workflow.core.engine.cmd.CustomCompleteDummyTaskCmd;
import cn.axzo.workflow.core.engine.cmd.CustomCountersignUserTaskAsyncCmd; 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.MessagePushEventImpl;
import cn.axzo.workflow.core.engine.event.MessagePushEventType; import cn.axzo.workflow.core.engine.event.MessagePushEventType;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst; 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.BpmnProcessTaskService;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.service.converter.BpmnHistoricAttachmentConverter; 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 cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; 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.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.CommandExecutor; 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.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -89,11 +100,13 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; 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.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_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.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.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.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION; 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_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.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.PROCESSING;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.valueOfStatus; 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_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.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.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.BpmnCollectionUtils.convertSet;
import static cn.axzo.workflow.core.common.utils.BpmnNativeQueryUtil.countSql; import static cn.axzo.workflow.core.common.utils.BpmnNativeQueryUtil.countSql;
@ -151,6 +175,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Resource @Resource
@Lazy @Lazy
private BpmnProcessTaskService bpmnProcessTaskService; private BpmnProcessTaskService bpmnProcessTaskService;
@Resource
private BpmnProcessDefinitionService bpmnProcessModelService;
@Override @Override
public BpmPageResult<BpmnTaskTodoPageItemVO> getTodoTaskPage(BpmnTaskPageSearchDTO dto) { public BpmPageResult<BpmnTaskTodoPageItemVO> getTodoTaskPage(BpmnTaskPageSearchDTO dto) {
@ -158,8 +184,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
Long resultTotalCount; Long resultTotalCount;
if (CollectionUtils.isEmpty(dto.getResults())) { if (CollectionUtils.isEmpty(dto.getResults())) {
HistoricTaskInstanceQuery query = HistoricTaskInstanceQuery query =
historyService.createHistoricTaskInstanceQuery().unfinished().taskAssignee(dto.getUserId()) // 分配给自己 historyService.createHistoricTaskInstanceQuery().unfinished().taskAssignee(dto.getUserId()) // 分配给自己
.orderByTaskCreateTime().desc(); .orderByTaskCreateTime().desc();
populateQuery(dto, query); populateQuery(dto, query);
tasks = query.listPage((dto.getPageNo() - 1) * dto.getPageSize(), dto.getPageSize()); tasks = query.listPage((dto.getPageNo() - 1) * dto.getPageSize(), dto.getPageSize());
resultTotalCount = query.count(); resultTotalCount = query.count();
@ -178,7 +204,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
// 获得 ProcessInstance Map // 获得 ProcessInstance Map
Map<String, ProcessInstance> processInstanceMap = Map<String, ProcessInstance> processInstanceMap =
processInstanceService.getProcessInstanceMap(processInstanceIds); processInstanceService.getProcessInstanceMap(processInstanceIds);
List<BpmnTaskTodoPageItemVO> vos = todoPageItemConverter.toVos(tasks, processInstanceMap); List<BpmnTaskTodoPageItemVO> vos = todoPageItemConverter.toVos(tasks, processInstanceMap);
return new BpmPageResult<>(vos, resultTotalCount); return new BpmPageResult<>(vos, resultTotalCount);
@ -192,8 +218,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
if (CollectionUtils.isEmpty(dto.getResults())) { if (CollectionUtils.isEmpty(dto.getResults())) {
// 查询已办任务 // 查询已办任务
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().finished() // 已完成 HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().finished() // 已完成
.taskAssignee(dto.getUserId()) // 分配给自己 .taskAssignee(dto.getUserId()) // 分配给自己
.orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
populateQuery(dto, query); populateQuery(dto, query);
// 执行查询 // 执行查询
tasks = query.listPage((dto.getPageNo() - 1) * dto.getPageSize(), dto.getPageSize()); tasks = query.listPage((dto.getPageNo() - 1) * dto.getPageSize(), dto.getPageSize());
@ -210,7 +236,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
} }
Set<String> processInstanceIds = convertSet(tasks, HistoricTaskInstance::getProcessInstanceId); Set<String> processInstanceIds = convertSet(tasks, HistoricTaskInstance::getProcessInstanceId);
Map<String, HistoricProcessInstance> historicProcessInstanceMap = Map<String, HistoricProcessInstance> historicProcessInstanceMap =
processInstanceService.getHistoricProcessInstanceMap(processInstanceIds); processInstanceService.getHistoricProcessInstanceMap(processInstanceIds);
List<BpmnTaskDonePageItemVO> vos = donePageItemConverter.toVos(tasks, historicProcessInstanceMap); List<BpmnTaskDonePageItemVO> vos = donePageItemConverter.toVos(tasks, historicProcessInstanceMap);
return new BpmPageResult<>(vos, resultTotalCount); return new BpmPageResult<>(vos, resultTotalCount);
} }
@ -234,7 +260,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
NativeHistoricTaskInstanceQuery nativeQuery = historyService.createNativeHistoricTaskInstanceQuery(); NativeHistoricTaskInstanceQuery nativeQuery = historyService.createNativeHistoricTaskInstanceQuery();
String tableName = managementService.getTableName(HistoricTaskInstance.class); String tableName = managementService.getTableName(HistoricTaskInstance.class);
baseQuerySql.append("SELECT a.* FROM ").append(tableName).append(" a JOIN " + "ACT_HI_PROCINST b").append(" " + 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())) { if (Objects.nonNull(dto.getTenantId())) {
baseQuerySql.append(sqlConnectors(baseQuerySql)).append(" b.TENANT_ID_ = #{tenantId}").append(sqlConnectors(baseQuerySql)).append(" a.TENANT_ID_ = #{tenantId}"); 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())) { if (StringUtils.hasLength(dto.getCategories())) {
List<String> categories = List<String> 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 ("); baseQuerySql.append(sqlConnectors(baseQuerySql)).append(" a.CATEGORY_ in (");
for (int i = 0; i < categories.size(); i++) { for (int i = 0; i < categories.size(); i++) {
baseQuerySql.append("#{category").append(i).append("}"); baseQuerySql.append("#{category").append(i).append("}");
@ -294,7 +320,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
} }
if (StringUtils.hasLength(dto.getCategories())) { if (StringUtils.hasLength(dto.getCategories())) {
List<String> categories = List<String> 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); query.processCategoryIn(categories);
} }
} }
@ -303,13 +329,140 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void approveTask(BpmnTaskAuditDTO dto) { public void approveTask(BpmnTaskAuditDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
if (dto.getAsync()) { if (Boolean.TRUE.equals(dto.getAsync())) {
commandExecutor.execute(new CustomApproveTaskAsyncCmd(dto)); commandExecutor.execute(new CustomApproveTaskAsyncCmd(dto));
} else { } else {
commandExecutor.execute(new CustomApproveTaskCmd(dto)); commandExecutor.execute(new CustomApproveTaskCmd(dto));
} }
} }
@Override
@Transactional(rollbackFor = Exception.class)
public void backTask(BpmnTaskBackAuditDTO dto) {
List<BpmnOptionalNodeDTO> backOptionalNodes = getBackOptionalNodes(dto.getTaskId());
if (CollectionUtils.isEmpty(backOptionalNodes)) {
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
}
List<String> activityList = backOptionalNodes.stream().map(BpmnOptionalNodeDTO::getProcessActivityId).collect(Collectors.toList());
if (!activityList.contains(dto.getToActivityId())) {
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
}
Optional<String> instOpt = backOptionalNodes.stream()
.map(BpmnOptionalNodeDTO::getProcessInstanceId)
.filter(StringUtils::hasText)
.findAny();
if (instOpt.isPresent()) {
ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
searchDTO.setProcessInstanceId(instOpt.get());
List<ExtAxHiTaskInst> 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<BpmnOptionalNodeDTO> 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<BpmnHistoricTaskInstanceVO> 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<Pair<String, List<BpmnHistoricTaskInstanceVO>>> executedList = new LinkedList<>();
for (BpmnHistoricTaskInstanceVO vo : tasks) {
Pair<String, List<BpmnHistoricTaskInstanceVO>> last = org.springframework.util.CollectionUtils.isEmpty(executedList) ? null : executedList.getLast();
if (last != null && last.getLeft().equals(vo.getTaskDefinitionKey())) {
last.getRight().add(vo);
continue;
}
ArrayList<BpmnHistoricTaskInstanceVO> objects = new ArrayList<>();
objects.add(vo);
executedList.addLast(Pair.of(vo.getTaskDefinitionKey(), objects));
}
List<Pair<String, List<BpmnHistoricTaskInstanceVO>>> valuableList = new LinkedList<>();
for (Pair<String, List<BpmnHistoricTaskInstanceVO>> pair : executedList) {
List<BpmnHistoricTaskInstanceVO> taskInstanceVOList = pair.getRight();
Optional<BpmnHistoricTaskInstanceVO> 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<String, List<BpmnHistoricTaskInstanceVO>> vPair = valuableList.get(j);
if (vPair.getLeft().equals(changeParentActivityTo)) {
break;
}
}
valuableList = valuableList.subList(0, j);
} else {
valuableList.add(pair);
}
}
List<FlowElement> flowElements = bpmnProcessModelService.findFlowElements(processInstance.getProcessDefinitionId());
Map<String, FlowElement> flowElementMap = flowElements.stream().collect(Collectors.toMap(BaseElement::getId, f -> f));
AtomicInteger index = new AtomicInteger(0);
List<BpmnOptionalNodeDTO> 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 @Override
public BatchOperationResultVO batchApproveTask(List<BpmnTaskAuditDTO> dtos) { public BatchOperationResultVO batchApproveTask(List<BpmnTaskAuditDTO> dtos) {
return batchOperation(dtos, bpmnProcessTaskService::approveTask); return batchOperation(dtos, bpmnProcessTaskService::approveTask);
@ -335,13 +488,13 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public List<BpmnHistoricTaskInstanceVO> getHistoricTaskListByProcessInstanceId(String processInstanceId, public List<BpmnHistoricTaskInstanceVO> getHistoricTaskListByProcessInstanceId(String processInstanceId,
String tenantId) { String tenantId) {
HistoricTaskInstanceQuery query = HistoricTaskInstanceQuery query =
historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId); historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId);
if (StringUtils.hasLength(tenantId)) { if (StringUtils.hasLength(tenantId)) {
query.taskTenantId(tenantId); query.taskTenantId(tenantId);
} }
HistoricProcessInstanceQuery instanceQuery = HistoricProcessInstanceQuery instanceQuery =
historyService.createHistoricProcessInstanceQuery() historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId); .processInstanceId(processInstanceId);
// .includeProcessVariables(); // .includeProcessVariables();
if (StringUtils.hasLength(tenantId)) { if (StringUtils.hasLength(tenantId)) {
instanceQuery.processInstanceTenantId(tenantId); instanceQuery.processInstanceTenantId(tenantId);
@ -352,67 +505,72 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
} }
List<HistoricTaskInstance> taskInstances = query.orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序 List<HistoricTaskInstance> taskInstances = query.orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
.list(); .list();
taskInstances.forEach(task -> ((HistoricTaskInstanceEntity) task).setCreateTime(((HistoricTaskInstanceEntity) task).getLastUpdateTime())); taskInstances.forEach(task -> ((HistoricTaskInstanceEntity) task).setCreateTime(((HistoricTaskInstanceEntity) task).getLastUpdateTime()));
taskInstances.sort(Comparator.comparing(p -> ((HistoricTaskInstanceEntity) p).getLastUpdateTime())); taskInstances.sort(Comparator.comparing(p -> ((HistoricTaskInstanceEntity) p).getLastUpdateTime()));
Map<String, HistoricVariableInstance> variableInstanceMap = Map<String, HistoricVariableInstance> variableInstanceMap =
// 不能使用框架提供的历史变量 API 查询, BUG // 不能使用框架提供的历史变量 API 查询, BUG
historyService.createNativeHistoricVariableInstanceQuery() historyService.createNativeHistoricVariableInstanceQuery()
.sql("select * from ACT_HI_VARINST t where t.proc_inst_id_= #{processInstanceId}") .sql("select * from ACT_HI_VARINST t where t.proc_inst_id_= #{processInstanceId}")
.parameter("processInstanceId", processInstanceId) .parameter("processInstanceId", processInstanceId)
.list().stream() .list().stream()
.collect(Collectors.toMap(HistoricVariableInstance::getVariableName, Function.identity(), .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, Function.identity(),
(s, t) -> s)); (s, t) -> s));
HistoricVariableInstance instanceVersion = variableInstanceMap.getOrDefault(WORKFLOW_ENGINE_VERSION, null); HistoricVariableInstance instanceVersion = variableInstanceMap.getOrDefault(WORKFLOW_ENGINE_VERSION, null);
// 过滤了多实例或签自动完成的任务 // 过滤了多实例或签自动完成的任务
List<BpmnHistoricTaskInstanceVO> vos = historicTaskInstanceConverter.toVosSkipSystemOperation(taskInstances, List<BpmnHistoricTaskInstanceVO> vos = historicTaskInstanceConverter.toVosSkipSystemOperation(taskInstances,
Objects.isNull(instanceVersion) ? null : Objects.isNull(instanceVersion) ? null :
((HistoricVariableInstanceEntityImpl) instanceVersion).getTextValue()); ((HistoricVariableInstanceEntityImpl) instanceVersion).getTextValue());
Map<String, List<Comment>> commentByTaskIdMap = Map<String, List<Comment>> commentByTaskIdMap =
taskService.getProcessInstanceComments(processInstanceId).stream() taskService.getProcessInstanceComments(processInstanceId).stream()
.collect(Collectors.groupingBy(Comment::getTaskId)); .collect(Collectors.groupingBy(Comment::getTaskId));
Map<String, List<Attachment>> attachmentByTaskIdMap = Map<String, List<Attachment>> attachmentByTaskIdMap =
taskService.getProcessInstanceAttachments(processInstanceId).stream() taskService.getProcessInstanceAttachments(processInstanceId).stream()
.collect(Collectors.groupingBy(Attachment::getTaskId)); .collect(Collectors.groupingBy(Attachment::getTaskId));
ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO(); ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
searchDTO.setProcessInstanceId(processInstanceId); searchDTO.setProcessInstanceId(processInstanceId);
Map<String, ExtAxHiTaskInst> extTaskInstMap = extAxHiTaskInstService.queryList(searchDTO).stream() Map<String, ExtAxHiTaskInst> 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()); BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
List<BpmnHistoricTaskInstanceVO> resultList = new ArrayList<>();
for (BpmnHistoricTaskInstanceVO vo : vos) { for (BpmnHistoricTaskInstanceVO vo : vos) {
ExtAxHiTaskInst taskInst = extTaskInstMap.getOrDefault(vo.getTaskId(), new ExtAxHiTaskInst()); ExtAxHiTaskInst taskInst = extTaskInstMap.getOrDefault(vo.getTaskId(), new ExtAxHiTaskInst());
if (Objects.equals(taskInst.getStatus(), DELETED.getStatus())) {
continue;
}
resultList.add(vo);
vo.setResult(valueOfStatus(taskInst.getStatus())); vo.setResult(valueOfStatus(taskInst.getStatus()));
List<Comment> taskComments = commentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList()); List<Comment> taskComments = commentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList());
// 处理 advice // 处理 advice
taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId())) taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId()))
.filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_ADVICE)).findFirst() .filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_ADVICE)).findFirst()
.ifPresent(i -> vo.setAdvice(i.getFullMessage())); .ifPresent(i -> vo.setAdvice(i.getFullMessage()));
// 处理 operationDesc // 处理 operationDesc
taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId())) taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId()))
.filter(i -> Objects.equals(COMMENT_TYPE_OPERATION_DESC, i.getType())) .filter(i -> Objects.equals(COMMENT_TYPE_OPERATION_DESC, i.getType()))
.max(Comparator.comparing(Comment::getTime)) .max(Comparator.comparing(Comment::getTime))
.ifPresent(i -> vo.setOperationDesc(i.getFullMessage())); .ifPresent(i -> vo.setOperationDesc(i.getFullMessage()));
// 处理 CommentExt // 处理 CommentExt
taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId())) taskComments.stream().filter(i -> Objects.equals(i.getTaskId(), vo.getTaskId()))
.filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_COMMENT_EXT)).findFirst() .filter(i -> Objects.equals(i.getType(), COMMENT_TYPE_COMMENT_EXT)).findFirst()
.ifPresent(i -> vo.setCommentExt(i.getFullMessage())); .ifPresent(i -> vo.setCommentExt(i.getFullMessage()));
List<Attachment> attachments = attachmentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList()); List<Attachment> attachments = attachmentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList());
vo.setAttachments(attachmentConverter.toVos(attachments)); vo.setAttachments(attachmentConverter.toVos(attachments));
HistoricVariableInstance assginerSnapshot = HistoricVariableInstance assginerSnapshot =
variableInstanceMap.getOrDefault(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + vo.getTaskId(), variableInstanceMap.getOrDefault(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + vo.getTaskId(),
null); null);
if (Objects.isNull(assginerSnapshot)) { if (Objects.isNull(assginerSnapshot)) {
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; BpmnTaskDelegateAssigner assigner = null;
@ -426,17 +584,17 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
vo.setNodeType(nodeType); vo.setNodeType(nodeType);
if (Objects.equals(NODE_CARBON_COPY, nodeType)) { if (Objects.equals(NODE_CARBON_COPY, nodeType)) {
HistoricVariableInstance carbonUsers = 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() : vo.setForecastAssignees(Objects.isNull(carbonUsers) ? Collections.emptyList() :
(List<BpmnTaskDelegateAssigner>) carbonUsers.getValue()); (List<BpmnTaskDelegateAssigner>) carbonUsers.getValue());
} else { } else {
vo.setForecastAssignees(Collections.emptyList()); vo.setForecastAssignees(Collections.emptyList());
} }
}); });
BpmnMetaParserHelper.getApprovalMethod(bpmnModel.getFlowElement(vo.getTaskDefinitionKey())) 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 @Override
public List<BpmnHistoricTaskInstanceGroupVO> getHistoricTaskListGroupByProcessInstanceId(String processInstanceId public List<BpmnHistoricTaskInstanceGroupVO> getHistoricTaskListGroupByProcessInstanceId(String processInstanceId
, String tenantId) { , String tenantId) {
List<BpmnHistoricTaskInstanceVO> vos = getHistoricTaskListByProcessInstanceId(processInstanceId, tenantId); List<BpmnHistoricTaskInstanceVO> vos = getHistoricTaskListByProcessInstanceId(processInstanceId, tenantId);
Map<String, List<BpmnHistoricTaskInstanceVO>> voMapByTaskDefKey = Map<String, List<BpmnHistoricTaskInstanceVO>> voMapByTaskDefKey =
vos.stream().collect(Collectors.groupingBy(BpmnHistoricTaskInstanceVO::getTaskDefinitionKey)); vos.stream().collect(Collectors.groupingBy(BpmnHistoricTaskInstanceVO::getTaskDefinitionKey));
List<BpmnHistoricTaskInstanceGroupVO> groupVos = new ArrayList<>(); List<BpmnHistoricTaskInstanceGroupVO> groupVos = new ArrayList<>();
for (Map.Entry<String, List<BpmnHistoricTaskInstanceVO>> entry : voMapByTaskDefKey.entrySet()) { for (Map.Entry<String, List<BpmnHistoricTaskInstanceVO>> entry : voMapByTaskDefKey.entrySet()) {
@ -490,9 +648,9 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
} }
List<BpmnTaskInstanceVO> vos = bpmnTaskConverter.toVos(query.list()); List<BpmnTaskInstanceVO> vos = bpmnTaskConverter.toVos(query.list());
List<String> snapshotTaskIds = List<String> 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<String, VariableInstance> instanceMap = runtimeService.getVariableInstances(processInstanceId, Map<String, VariableInstance> instanceMap = runtimeService.getVariableInstances(processInstanceId,
snapshotTaskIds); snapshotTaskIds);
vos.forEach(i -> i.setAssigner(BpmnTaskDelegateAssigner.toObjectCompatible(instanceMap.get(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + i.getTaskId()).getValue()))); vos.forEach(i -> i.setAssigner(BpmnTaskDelegateAssigner.toObjectCompatible(instanceMap.get(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + i.getTaskId()).getValue())));
return vos; return vos;
} }
@ -526,7 +684,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
commandExecutor.execute(new CustomTransferUserTaskAsyncCmd(dto)); commandExecutor.execute(new CustomTransferUserTaskAsyncCmd(dto));
} else { } else {
commandExecutor.execute(new CustomTransferUserTaskCmd(dto.getTaskId(), 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) { public void commentTask(BpmnTaskCommentDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new CustomCommentTaskCmd(dto.getProcessInstanceId(), commandExecutor.execute(new CustomCommentTaskCmd(dto.getProcessInstanceId(),
dto.getOperator(), dto.getComment(), dto.getCommentExt(), dto.getAttachmentList(), dto.getOperator(), dto.getComment(), dto.getCommentExt(), dto.getAttachmentList(),
extAxHiTaskInstService)); extAxHiTaskInstService));
} }
@Override @Override
@ -577,8 +735,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public void attachmentTask(BpmnTaskAttachmentDTO dto, String assignee) { public void attachmentTask(BpmnTaskAttachmentDTO dto, String assignee) {
Authentication.setAuthenticatedUserId(assignee); Authentication.setAuthenticatedUserId(assignee);
Attachment attachment = taskService.createAttachment(dto.getType(), dto.getTaskId(), Attachment attachment = taskService.createAttachment(dto.getType(), dto.getTaskId(),
dto.getProcessInstanceId(), dto.getName(), dto.getDescription(), dto.getProcessInstanceId(), dto.getName(), dto.getDescription(),
dto.getUrl()); dto.getUrl());
taskService.saveAttachment(attachment); taskService.saveAttachment(attachment);
Authentication.setAuthenticatedUserId(null); Authentication.setAuthenticatedUserId(null);
} }
@ -592,8 +750,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
commandExecutor.execute(new CustomCountersignUserTaskAsyncCmd(dto)); commandExecutor.execute(new CustomCountersignUserTaskAsyncCmd(dto));
} else { } else {
commandExecutor.execute(new CustomCountersignUserTaskCmd(BpmnCountersignTypeEnum.valueOfType(dto.getCountersignType()), dto.getTaskId(), commandExecutor.execute(new CustomCountersignUserTaskCmd(BpmnCountersignTypeEnum.valueOfType(dto.getCountersignType()), dto.getTaskId(),
dto.getOriginAssigner(), dto.getAdvice(), dto.getAttachmentList(), dto.getTargetAssignerList(), dto.getOriginAssigner(), dto.getAdvice(), dto.getAttachmentList(), dto.getTargetAssignerList(),
extAxHiTaskInstService)); extAxHiTaskInstService));
} }
} }
@ -614,22 +772,22 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher(); FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
ProcessInstance processInstance = ProcessInstance processInstance =
runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessInstanceId()).singleResult(); runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessInstanceId()).singleResult();
Optional<BpmnNoticeConf> noticeConfig = Optional<BpmnNoticeConf> noticeConfig =
BpmnMetaParserHelper.getNoticeConfig(ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId())); BpmnMetaParserHelper.getNoticeConfig(ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId()));
List<BpmnTaskDelegateAssigner> assigners = List<BpmnTaskDelegateAssigner> assigners =
(List<BpmnTaskDelegateAssigner>) runtimeService.getVariable(dto.getProcessInstanceId(), (List<BpmnTaskDelegateAssigner>) runtimeService.getVariable(dto.getProcessInstanceId(),
INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + dto.getTaskDefinitionKey()); INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + dto.getTaskDefinitionKey());
// 过滤出未审批的任何,用选择的方式去发送消息 // 过滤出未审批的任何,用选择的方式去发送消息
dto.getRemindTypes().forEach(type -> { dto.getRemindTypes().forEach(type -> {
list.forEach(task -> { list.forEach(task -> {
assigners.stream().filter(i -> Objects.equals(task.getAssignee(), i.buildAssigneeId())).findAny().ifPresent(assigner -> { assigners.stream().filter(i -> Objects.equals(task.getAssignee(), i.buildAssigneeId())).findAny().ifPresent(assigner -> {
MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.valueOf(type), MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.valueOf(type),
Lists.newArrayList(assigner), noticeConfig.orElse(null), Lists.newArrayList(assigner), noticeConfig.orElse(null),
processInstance.getProcessInstanceId(), processInstance.getProcessInstanceId(),
processInstance.getProcessDefinitionKey(), processInstance.getProcessDefinitionKey(),
processInstance.getTenantId(), task.getId()); processInstance.getTenantId(), task.getId());
event.setProcessInstanceId(processInstance.getProcessInstanceId()); event.setProcessInstanceId(processInstance.getProcessInstanceId());
event.setTenantId(processInstance.getTenantId()); event.setTenantId(processInstance.getTenantId());
event.setTaskId(task.getId()); event.setTaskId(task.getId());
@ -644,8 +802,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public String createRobotTask(BpmnRobotTaskCreateDTO dto) { public String createRobotTask(BpmnRobotTaskCreateDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
return commandExecutor.execute(new CustomCreateDummyTaskCmd(dto.getProcessInstanceId(), return commandExecutor.execute(new CustomCreateDummyTaskCmd(dto.getProcessInstanceId(),
dto.getRobotNode().getFlowNodeName(), dto.getRobotNode().getOperationDesc(), dto.getApprover(), dto.getRobotNode().getFlowNodeName(), dto.getRobotNode().getOperationDesc(), dto.getApprover(),
extAxHiTaskInstService)); extAxHiTaskInstService));
} }
@Override @Override
@ -653,17 +811,17 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
public void completeRobotTask(BpmnRobotTaskCompleteDTO dto) { public void completeRobotTask(BpmnRobotTaskCompleteDTO dto) {
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new CustomCompleteDummyTaskCmd(dto.getProcessInstanceId(), dto.getTaskId(), commandExecutor.execute(new CustomCompleteDummyTaskCmd(dto.getProcessInstanceId(), dto.getTaskId(),
Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getFlowNodeName(), Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getFlowNodeName(),
Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getOperationDesc(), Objects.isNull(dto.getRobotNode()) ? null : dto.getRobotNode().getOperationDesc(),
extAxHiTaskInstService)); extAxHiTaskInstService));
} }
@Override @Override
public String findTaskIdByInstanceIdAndPersonId(String processInstanceId, String personId) { public String findTaskIdByInstanceIdAndPersonId(String processInstanceId, String personId) {
List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId) List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId)
.taskAssigneeLike("%" + personId) .taskAssigneeLike("%" + personId)
.active() .active()
.list(); .list();
if (CollectionUtils.isEmpty(list) || list.size() > 1) { if (CollectionUtils.isEmpty(list) || list.size() > 1) {
throw new WorkflowEngineException(FIND_TASK_BY_PERSON_ID_ERROR, processInstanceId, personId); throw new WorkflowEngineException(FIND_TASK_BY_PERSON_ID_ERROR, processInstanceId, personId);
} }
@ -673,9 +831,9 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Override @Override
public Map<String, String> findTaskIdByInstanceIdsAndPersonId(List<String> processInstanceIds, String personId) { public Map<String, String> findTaskIdByInstanceIdsAndPersonId(List<String> processInstanceIds, String personId) {
List<Task> tasks = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds) List<Task> tasks = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds)
.taskAssigneeLike("%" + personId) .taskAssigneeLike("%" + personId)
.active() .active()
.list(); .list();
if (CollectionUtils.isEmpty(tasks)) { if (CollectionUtils.isEmpty(tasks)) {
return new HashMap<>(); return new HashMap<>();
} }

Some files were not shown because too many files have changed in this diff Show More