diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
index ff475f92c..2e335f40d 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
@@ -14,7 +14,6 @@
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum;
-import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.tika.TikaDocumentReader;
@@ -25,6 +24,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
index 5523fe278..bfb22eecd 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -15,7 +15,6 @@
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
-import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
@@ -23,6 +22,7 @@
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
index 28398a702..af9c1fef1 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
@@ -19,8 +19,6 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
@@ -28,6 +26,8 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import javax.validation.Valid;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java
index 55d7533d6..053583d4f 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java
@@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
+import javax.validation.constraints.NotEmpty;
+
@Schema(description = "管理后台 - 流程模型的更新 BPMN XML Request VO")
@Data
public class BpmModeUpdateBpmnReqVO {
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
index fa82ab1e6..a43a4f969 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
@@ -4,11 +4,11 @@
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
import java.util.List;
/**
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java
index dd15e67ae..6f91f158f 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java
@@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
+import javax.validation.constraints.NotEmpty;
+
@Schema(description = "管理后台 - 流程模型的保存 Request VO")
@Data
public class BpmModelSaveReqVO extends BpmModelMetaInfoVO {
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
index 502511753..afee5ff19 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
@@ -6,11 +6,11 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java
index 10d967261..c3a525ae6 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java
@@ -1,11 +1,12 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
// TODO @jason:需要考虑,如果某个节点的配置不正确,需要有提示;具体怎么实现,可以讨论下;
@Schema(description = "管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO")
@Data
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
index 8409d2e2f..c49ea7661 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
@@ -18,8 +18,6 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.history.HistoricProcessInstance;
@@ -30,6 +28,8 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import javax.validation.Valid;
import java.util.Collections;
import java.util.List;
import java.util.Map;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java
index ffe0b0139..445a09321 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java
@@ -3,9 +3,10 @@
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.AssertTrue;
import lombok.Data;
+import javax.validation.constraints.AssertTrue;
+
// TODO @jason:这个可以简化下,使用 @RequestParam。嘿嘿,主要 VO 项不要太多
@Schema(description = "管理后台 - 审批详情 Request VO")
@Data
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java
index c5dc824de..39dda8246 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java
@@ -3,9 +3,10 @@
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.AssertTrue;
import lombok.Data;
+import javax.validation.constraints.AssertTrue;
+
@Schema(description = "管理后台 - 表单字段权限 Request VO")
@Data
public class BpmFormFieldsPermissionReqVO {
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java
index d6bd19caf..611f34ab1 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java
@@ -8,11 +8,11 @@
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java
index 8a0f40f22..e607f62e6 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateRoleStrategy.java
@@ -6,9 +6,9 @@
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.Set;
/**
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java
index 1ba520e4a..9dfe95eed 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java
@@ -9,12 +9,12 @@
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java
index 7bad6b8c2..4d1a83da9 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java
@@ -9,12 +9,12 @@
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java
index af9438c56..eac6663fa 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserSelectStrategy.java
@@ -7,7 +7,6 @@
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import jakarta.annotation.Resource;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
@@ -15,6 +14,7 @@
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.*;
/**
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java
index ddc990c61..9b4c89519 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java
@@ -4,12 +4,12 @@
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.Set;
/**
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java
index 3b9b34e59..e956b1f64 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java
@@ -3,12 +3,12 @@
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;
-import jakarta.annotation.Resource;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.Set;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate.BEAN_NAME;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
index f9e74e981..2ea7bf14b 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
@@ -22,6 +22,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.stream.Collectors;
import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting;
import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler;
@@ -234,7 +235,7 @@ public static String buildConditionExpression(BpmSimpleModelNodeVO conditionNode
} else {
return "";
}
- }).toList();
+ }).collect(Collectors.toList());
conditionExpression = String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || "));
}
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
index a2dcba480..9c3388599 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
@@ -5,10 +5,11 @@
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
-import jakarta.validation.Valid;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.repository.Model;
+import javax.validation.Valid;
+
/**
* Flowable流程模型接口
*
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
index 64ba6ef16..8c47eb94a 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -19,8 +19,6 @@
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
@@ -34,6 +32,8 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
+import javax.annotation.Resource;
+import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
index 01abad2f8..61ae34a37 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
@@ -12,7 +12,6 @@
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
-import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.db.SuspensionState;
@@ -24,6 +23,7 @@
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageService.java
index 268be727c..b6c2d7747 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageService.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageService.java
@@ -4,7 +4,8 @@
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO;
-import jakarta.validation.Valid;
+
+import javax.validation.Valid;
/**
* BPM 消息 Service 接口
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java
index 22d86ed65..abe9ef431 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java
@@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.bpm.service.message.dto;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
/**
* BPM 发送任务审批超时 Request DTO
*/
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java
index 26da5ad0e..6ae77ea03 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmActivityServiceImpl.java
@@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.bpm.service.task;
-import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+import javax.annotation.Resource;
import java.util.List;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
index a14624d93..3459c3a5c 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
@@ -3,10 +3,10 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
-import jakarta.validation.Valid;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
+import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
index 4f4747e3b..1c65f73a8 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -1 +1 @@
-package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ApprovalTaskInfo;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.bpm.service.task.bo.AlreadyRunApproveNodeRespBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.variable.VariableContainer;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ApprovalNodeInfo;
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.User;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum.USER;
import static cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum.START_USER;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*
* @author 芋道源码
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private RuntimeService runtimeService;
@Resource
private HistoryService historyService;
@Resource
private ManagementService managementService;
@Resource
private BpmActivityService activityService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
@Lazy // 避免循环依赖
private BpmTaskService taskService;
@Resource
private BpmMessageService messageService;
@Resource
private BpmTaskCandidateInvoker bpmTaskCandidateInvoker;
@Resource
private AdminUserApi adminUserApi;
@Resource
private BpmProcessInstanceEventPublisher processInstanceEventPublisher;
// ========== Query 查询相关方法 ==========
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery()
.includeProcessVariables()
.processInstanceId(id)
.singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public PageResult getProcessInstancePage(Long userId,
BpmProcessInstancePageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
.includeProcessVariables()
.processInstanceTenantId(FlowableUtils.getTenantId())
.orderByProcessInstanceStartTime().desc();
if (userId != null) { // 【我的流程】菜单时,需要传递该字段
processInstanceQuery.startedBy(String.valueOf(userId));
} else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
}
if (StrUtil.isNotEmpty(pageReqVO.getName())) {
processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
}
if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) {
processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey());
}
if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
}
if (pageReqVO.getStatus() != null) {
processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus());
}
if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
}
// 查询数量
long processInstanceCount = processInstanceQuery.count();
if (processInstanceCount == 0) {
return PageResult.empty(processInstanceCount);
}
// 查询列表
List processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize());
return new PageResult<>(processInstanceList, processInstanceCount);
}
@Override
public Map getFormFieldsPermission(BpmFormFieldsPermissionReqVO reqVO) {
// 1.1 获取流程定义 Id
String processDefinitionId = reqVO.getProcessDefinitionId();
if (StrUtil.isEmpty(processDefinitionId) && StrUtil.isNotEmpty(reqVO.getProcessInstanceId())) {
HistoricProcessInstance processInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId());
if (processInstance == null) {
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
}
processDefinitionId = processInstance.getProcessDefinitionId();
}
// 1.2 获取流程活动编号
String activityId = reqVO.getActivityId();
if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(reqVO.getTaskId())) { // 流程活动 Id 为空。从流程任务中获取流程活动 Id
activityId = Optional.ofNullable(taskService.getHistoricTask(reqVO.getTaskId()))
.map(HistoricTaskInstance::getTaskDefinitionKey).orElse(null);
}
if (StrUtil.isEmpty(activityId)) {
return null;
}
// 2. 从 BpmnModel 中解析表单字段权限
return BpmnModelUtils.parseFormFieldsPermission(
processDefinitionService.getProcessDefinitionBpmnModel(processDefinitionId), activityId);
}
@Override
public BpmApprovalDetailRespVO getApprovalDetail(Long startUserId, BpmApprovalDetailReqVO reqVO) {
// 1. 审批详情
BpmApprovalDetailRespVO respVO = new BpmApprovalDetailRespVO();
String processDefinitionId = reqVO.getProcessDefinitionId();
ProcessInstance runProcessInstance = null; // 正在运行的流程实例
Set runNodeIds = new HashSet<>(); // 已经运行的节点 Ids (BPMN XML 节点 Id)
Map runningApprovalNodes = new HashMap<>(); // 正在运行的节点的审批信息
List approvalNodes = new ArrayList<>();
// 1.1 情况一:流程未发起
if (reqVO.getProcessInstanceId() == null) {
respVO.setStatus(BpmProcessInstanceStatusEnum.NOT_START.getStatus());
// 1.2 情况二:流程已发起
} else {
// 1.2.1 获取流程实例状态
HistoricProcessInstance processInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId());
if (processInstance == null) {
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
}
Integer processInstanceStatus = FlowableUtils.getProcessInstanceStatus(processInstance);
respVO.setStatus(processInstanceStatus);
// 1.2.2 构建已运行节点的审批信息
List historicActivityList = activityService.getActivityListByProcessInstanceId(processInstance.getId());
AlreadyRunApproveNodeRespBO respBO = buildAlreadyRunApproveNodes(processInstance.getId(), processInstanceStatus, historicActivityList);
approvalNodes = respBO.getApproveNodes();
runNodeIds = respBO.getRunNodeIds();
// 1.2.3 特殊:流程已经结束,直接 return,无需预测
if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) {
respVO.setApproveNodes(approvalNodes);
return respVO;
}
runningApprovalNodes = respBO.getRunningApprovalNodes();
processDefinitionId = processInstance.getProcessDefinitionId();
runProcessInstance = getProcessInstance(processInstance.getId());
startUserId = Long.valueOf(runProcessInstance.getStartUserId());
}
// 2. 流程未结束,预测未运行节点的审批信息。需要区分 BPMN 设计器 和 SIMPLE 设计器
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinitionId);
// 2.1 情况一:仿钉钉流程设计器
if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) {
BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class);
List notRunApproveNodes = new ArrayList<>();
traverseSimpleModelNodeToBuildNotRunApproveNodes(
startUserId, runProcessInstance, simpleModel, runNodeIds, runningApprovalNodes, notRunApproveNodes);
approvalNodes.addAll(notRunApproveNodes);
respVO.setApproveNodes(approvalNodes);
// 会不会有极端的情况:对于依次审批来说,它是已经 running,但是当前节点也要计算其它审批人?(已修改)
// 2.2 情况二:BPMN 流程设计器
} else if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {
// TODO 芋艿:需要把 start 节点加出来
// TODO Bpmn 设计器,构建未运行节点的审批信息;未完全实现
respVO.setApproveNodes(approvalNodes);
}
return respVO;
}
/**
* 遍历 SIMPLE 设计器模型 构建未运行节点的审批信息
*
* @param startUserId 流程发起人编号
* @param processInstance 流程实例
* @param simpleModelNode SIMPLE 设计器模型
* @param runNodeIds 已经运行节点的 Ids
* @param runningApprovalNodes 正在运行的节点的审批信息
* @param approveNodeList 未运行节点的审批信息列表
*/
private void traverseSimpleModelNodeToBuildNotRunApproveNodes(Long startUserId,
ProcessInstance processInstance,
BpmSimpleModelNodeVO simpleModelNode,
Set runNodeIds,
Map runningApprovalNodes,
List approveNodeList) {
if (!SimpleModelUtils.isValidNode(simpleModelNode)) {
return;
}
buildNotRunApproveNodes(startUserId, processInstance, simpleModelNode, runNodeIds, runningApprovalNodes, approveNodeList);
// 如果有子节点递归遍历子节点
traverseSimpleModelNodeToBuildNotRunApproveNodes(
startUserId, processInstance, simpleModelNode.getChildNode(), runNodeIds, runningApprovalNodes, approveNodeList);
}
private void buildNotRunApproveNodes(Long startUserId, ProcessInstance processInstance,
BpmSimpleModelNodeVO node, Set runNodeIds,
Map runningApprovalNodes,
List approveNodeList) {
// 情况一:节点未运行:需要进行预测
if (!runNodeIds.contains(node.getId())) {
// 1. 对需要人工审批的审批节点,进行预测
if (APPROVE_NODE.getType().equals(node.getType()) && USER.getType().equals(node.getApproveType())
|| START_USER_NODE.getType().equals(node.getType())) {
ApprovalNodeInfo approvalNodeInfo = new ApprovalNodeInfo().setNodeType(node.getType())
.setName(node.getName()).setStatus(NOT_START.getStatus());
Integer candidateStrategy = START_USER_NODE.getType().equals(node.getType()) ?
START_USER.getStrategy() : node.getCandidateStrategy();
approvalNodeInfo.setCandidateUserList(
getNotRunTaskCandidateUserList(startUserId, processInstance, node.getId(), candidateStrategy, node.getCandidateParam()));
approveNodeList.add(approvalNodeInfo);
// 2. 对分支节点,进行预测
} else if (BpmSimpleModelNodeType.isBranchNode(node.getType())) {
// 并行分支,不用预测条件。所有分支都需要遍历
if (PARALLEL_BRANCH_NODE.getType().equals(node.getType())) {
node.getConditionNodes().forEach(conditionNode ->
traverseSimpleModelNodeToBuildNotRunApproveNodes(startUserId, processInstance, conditionNode.getChildNode()
, runNodeIds, runningApprovalNodes, approveNodeList));
} else if (CONDITION_BRANCH_NODE.getType().equals(node.getType())) {
for (BpmSimpleModelNodeVO conditionNode : node.getConditionNodes()) {
// 满足一个条件, 遍历该分支并
if ((processInstance != null && evalConditionExpress(processInstance, SimpleModelUtils.buildConditionExpression(conditionNode))) // 预测条件表达式的值
|| BooleanUtil.isTrue(conditionNode.getDefaultFlow())) { // 是否默认的序列
traverseSimpleModelNodeToBuildNotRunApproveNodes(startUserId, processInstance, conditionNode.getChildNode(),
runNodeIds, runningApprovalNodes, approveNodeList);
break;
}
}
}
// TODO 包容分支待实现
// 3. 结束节点
} else if (END_NODE.getType().equals(node.getType())) {
ApprovalNodeInfo nodeProgress = new ApprovalNodeInfo();
nodeProgress.setNodeType(node.getType());
nodeProgress.setName(node.getName());
nodeProgress.setStatus(NOT_START.getStatus());
approveNodeList.add(nodeProgress);
}
} else {
// 情况二:节点已经运行
// 如果是分支节点,需要检查分支节点的运行情况
if (BpmSimpleModelNodeType.isBranchNode(node.getType()) && ArrayUtil.isNotEmpty(node.getConditionNodes())) {
node.getConditionNodes().forEach(conditionNode -> {
// 只有运行的条件,才需要遍历
if (runNodeIds.contains(conditionNode.getId())) {
traverseSimpleModelNodeToBuildNotRunApproveNodes(startUserId, processInstance, conditionNode.getChildNode()
, runNodeIds, runningApprovalNodes, approveNodeList);
}
});
// 如果是依次审批, 需要加其它未审批候选人
} else if (SimpleModelUtils.isSequentialApproveNode(node) && runningApprovalNodes.containsKey(node.getId())) {
ApprovalNodeInfo approvalNodeInfo = runningApprovalNodes.get(node.getId());
List candidateUserList = getNotRunTaskCandidateUserList(
startUserId, processInstance, node.getId(), node.getCandidateStrategy(), node.getCandidateParam());
// TODO @jason:这里的逻辑,可能可以简化成,直接拿已经审批过的人的 userId 集合,从 candidateUserList remove 下。一方面简单一点,方面 calculateUsers 返回的是 set,目前不是很有序。
ApprovalTaskInfo approvalTaskInfo = CollUtil.getFirst(approvalNodeInfo.getTasks());
Long currentAssignedUserId = null;
if (approvalTaskInfo != null && approvalTaskInfo.getAssigneeUser() != null) {
currentAssignedUserId = approvalTaskInfo.getAssigneeUser().getId();
}
// 找到当前审批人在候选人列表的位置
int index = 0;
for (User user : candidateUserList) {
if (user.getId().equals(currentAssignedUserId)) {
break;
}
index++;
}
// 截取当前审批人位置后面的候选人, 不包含当前审批人
approvalNodeInfo.setCandidateUserList(CollUtil.sub(candidateUserList, ++index, candidateUserList.size()));
}
}
}
/**
* 从已经运行活动节点构建的审批信息列表
*
* @param processInstanceId 流程实例 Id
* @param processInstanceStatus 流程实例状态
* @param historicActivityList 已经运行活动
*/
private AlreadyRunApproveNodeRespBO buildAlreadyRunApproveNodes(String processInstanceId,
Integer processInstanceStatus,
List historicActivityList) {
// 1.1 获取待处理活动:只有 "userTask" 和 "endEvent" 需要处理
List pendingActivityNodes = filterList(historicActivityList,
item -> BpmSimpleModelNodeType.isRecordNode(item.getActivityType()));
// 1.2 已运行节点的 activityId
Set runNodeIds = convertSet(historicActivityList, HistoricActivityInstance::getActivityId);
// 2.1 获取已运行的任务(包括运行中的任务)
List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId);
Map taskMap = convertMap(taskList, HistoricTaskInstance::getId);
// 2.2 获取加签的任务
Map> addSignTaskMap = convertMultiMap(
filterList(taskList, task -> StrUtil.isNotEmpty(task.getParentTaskId())), HistoricTaskInstance::getParentTaskId);
// 3.1 获取节点的用户信息
Set userIds = CollectionUtils.convertSetByFlatMap(pendingActivityNodes, activity -> {
if (BPMN_USER_TASK_TYPE.equals(activity.getActivityType())) {
HistoricTaskInstance task = taskMap.get(activity.getTaskId());
Set taskUsers = CollUtil.newHashSet();
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(task.getAssignee(), null));
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(task.getOwner(), null));
List addSignTasks = addSignTaskMap.get(activity.getTaskId());
if (CollUtil.isNotEmpty(addSignTasks)) {
addSignTasks.forEach(item -> {
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(item.getAssignee(), null));
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(item.getOwner(), null));
});
}
return taskUsers.stream();
} else {
return Stream.empty();
}
});
Map userMap = convertMap(adminUserApi.getUserList(userIds).getCheckedData(), AdminUserRespDTO::getId);
// 3.2 已经结束的任务转换为审批信息
final Multimap runningTask = ArrayListMultimap.create(); // 运行中的任务
List approvalNodeList = convertList(pendingActivityNodes, activity -> {
ApprovalNodeInfo approvalNodeInfo = new ApprovalNodeInfo().setId(activity.getId()).setName(activity.getActivityName())
.setStartTime(DateUtils.of(activity.getStartTime())).setEndTime(DateUtils.of(activity.getEndTime()));
if (BPMN_USER_TASK_TYPE.equals(activity.getActivityType())) { // 用户任务
// nodeType
approvalNodeInfo.setNodeType(START_USER_NODE_ID.equals(activity.getActivityId()) ?
START_USER_NODE.getType() : APPROVE_NODE.getType());
// status
HistoricTaskInstance task = taskMap.get(activity.getTaskId());
Integer taskStatus = FlowableUtils.getTaskStatus(task);
// 运行中的任务, 会签,或签任务聚合在一起。
if (!BpmTaskStatusEnum.isEndStatus(taskStatus)) {
runningTask.put(activity.getActivityId(), activity);
return null;
}
// tasks
ApprovalTaskInfo approveTask = convertApproveTaskInfo(task, userMap);
List approveTasks = CollUtil.newArrayList(approveTask);
List addSignTasks = addSignTaskMap.get(activity.getTaskId());
if (CollUtil.isNotEmpty(addSignTasks)) { // 处理加签任务
approveTasks.addAll(convertList(addSignTasks, item -> convertApproveTaskInfo(item, userMap)));
}
approvalNodeInfo.setStatus(taskStatus);
approvalNodeInfo.setTasks(approveTasks);
} else if (END_NODE.getBpmnType().equals(activity.getActivityType())) {
approvalNodeInfo.setNodeType(END_NODE.getType());
approvalNodeInfo.setStatus(processInstanceStatus);
}
return approvalNodeInfo;
});
// 3.3 运行中的任务转换为审批信息。
final Map runningApprovalNodes = new HashMap<>(); // 正在运行节点的审批信息
runningTask.asMap().forEach((activityId, activities) -> {
if (CollUtil.isNotEmpty(activities)) {
ApprovalNodeInfo approvalNodeInfo = new ApprovalNodeInfo();
approvalNodeInfo.setNodeType(APPROVE_NODE.getType());
approvalNodeInfo.setStatus(RUNNING.getStatus());
List approveTasks = CollUtil.newArrayList();
int i = 0;
for (HistoricActivityInstance activity : activities) {
HistoricTaskInstance task = taskMap.get(activity.getTaskId());
// 取第一个任务, 会签/或签的任务。开始时间相同的
if (i == 0) {
approvalNodeInfo.setId(activity.getId()).setName(activity.getActivityName()).
setStartTime(DateUtils.of(activity.getStartTime()));
}
// tasks
ApprovalTaskInfo approveTask = convertApproveTaskInfo(task, userMap);
approveTasks.add(approveTask);
List addSignTasks = addSignTaskMap.get(activity.getTaskId());
if (CollUtil.isNotEmpty(addSignTasks)) { // 处理加签任务
approveTasks.addAll(convertList(addSignTasks, item -> convertApproveTaskInfo(item, userMap)));
}
i++;
}
approvalNodeInfo.setTasks(approveTasks);
approvalNodeList.add(approvalNodeInfo);
runningApprovalNodes.put(activityId, approvalNodeInfo);
}
});
return new AlreadyRunApproveNodeRespBO().setApproveNodes(approvalNodeList).setRunNodeIds(runNodeIds)
.setRunningApprovalNodes(runningApprovalNodes);
}
private ApprovalTaskInfo convertApproveTaskInfo(HistoricTaskInstance task, Map userMap) {
if (task == null) {
return null;
}
ApprovalTaskInfo approveTask = BeanUtils.toBean(task, ApprovalTaskInfo.class);
approveTask.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
Long taskAssignee = NumberUtil.parseLong(task.getAssignee(), null);
if (taskAssignee != null) {
approveTask.setAssigneeUser(BeanUtils.toBean(userMap.get(taskAssignee), User.class));
}
Long taskOwner = NumberUtil.parseLong(task.getOwner(), null);
if (taskOwner != null) {
approveTask.setOwnerUser(BeanUtils.toBean(userMap.get(taskOwner), User.class));
}
return approveTask;
}
private List getNotRunTaskCandidateUserList(Long startUserId, ProcessInstance processInstance, String activityId,
Integer candidateStrategy, String candidateParam) {
BpmTaskCandidateStrategy taskCandidateStrategy = bpmTaskCandidateInvoker.getCandidateStrategy(candidateStrategy);
Set userIds = taskCandidateStrategy.calculateUsers(startUserId, processInstance, activityId, candidateParam);
Map adminUserMap = adminUserApi.getUserMap(userIds);
// 需要按照候选人的顺序返回。原因是,依次审批需要按顺序展示用户
return convertList(userIds, userId -> BeanUtils.toBean(adminUserMap.get(userId), User.class));
}
/**
* 计算条件表达式的值
*
* @param processInstance 流程实例
* @param express 条件表达式
*/
private Boolean evalConditionExpress(ProcessInstance processInstance, String express) {
if (express == null) {
return Boolean.FALSE;
}
Object result = managementService.executeCommand(context -> {
try {
return FlowableUtils.getExpressionValue((VariableContainer) processInstance, express);
} catch (FlowableException ex) {
log.error("[evalConditionExpress][条件表达式({}) 解析报错", express, ex);
return Boolean.FALSE;
}
});
return Boolean.TRUE.equals(result);
}
// ========== Update 写入相关方法 ==========
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null,
createReqVO.getStartUserSelectAssignees());
}
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(),
createReqDTO.getStartUserSelectAssignees());
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey,
Map> startUserSelectAssignees) {
// 1.1 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(definition.getId());
if (processDefinitionInfo == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
// 1.2 校验是否能够发起
if (!processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId)) {
throw exception(PROCESS_INSTANCE_START_USER_CAN_START);
}
// 1.3 校验发起人自选审批人
validateStartUserSelectAssignees(definition, startUserSelectAssignees);
// 2. 创建流程实例
if (variables == null) {
variables = new HashMap<>();
}
FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
BpmProcessInstanceStatusEnum.RUNNING.getStatus());
if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
}
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
return instance.getId();
}
private void validateStartUserSelectAssignees(ProcessDefinition definition, Map> startUserSelectAssignees) {
// 1. 获得发起人自选审批人的 UserTask 列表
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId());
List userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
if (CollUtil.isEmpty(userTaskList)) {
return;
}
// 2. 校验发起人自选审批人的 UserTask 是否都配置了
userTaskList.forEach(userTask -> {
List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null;
if (CollUtil.isEmpty(assignees)) {
throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName());
}
Map userMap = adminUserApi.getUserMap(assignees);
assignees.forEach(assignee -> {
if (userMap.get(assignee) == null) {
throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee);
}
});
});
}
@Override
public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 1.1 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 1.2 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 2. 取消流程
updateProcessInstanceCancel(cancelReqVO.getId(),
BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason()));
}
@Override
public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) {
// 1.1 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 2. 取消流程
AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData();
updateProcessInstanceCancel(cancelReqVO.getId(),
BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason()));
}
private void updateProcessInstanceCancel(String id, String reason) {
// 1. 更新流程实例 status
runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
BpmProcessInstanceStatusEnum.CANCEL.getStatus());
runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason);
// 2. 结束流程
taskService.moveTaskToEnd(id);
}
@Override
public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) {
runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
BpmProcessInstanceStatusEnum.REJECT.getStatus());
runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON,
BpmReasonEnum.REJECT_TASK.format(reason));
}
// ========== Event 事件相关方法 ==========
@Override
public void processProcessInstanceCompleted(ProcessInstance instance) {
// 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号
FlowableUtils.execute(instance.getTenantId(), () -> {
// 1.1 获取当前状态
Integer status = (Integer) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
String reason = (String) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
// 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态
// 为什么这么处理?因为流程完成,并且完成了,说明审批通过了
if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {
status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();
runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, status);
}
// 2. 发送对应的消息通知
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
} else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {
messageService.sendMessageWhenProcessInstanceReject(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));
}
// 3. 发送流程实例的状态事件
processInstanceEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status));
});
}
}
\ No newline at end of file
+package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ApprovalTaskInfo;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.bpm.service.task.bo.AlreadyRunApproveNodeRespBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.variable.VariableContainer;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.*;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ApprovalNodeInfo;
import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.User;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum.USER;
import static cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum.*;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum.START_USER;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*
* @author 芋道源码
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private RuntimeService runtimeService;
@Resource
private HistoryService historyService;
@Resource
private ManagementService managementService;
@Resource
private BpmActivityService activityService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
@Lazy // 避免循环依赖
private BpmTaskService taskService;
@Resource
private BpmMessageService messageService;
@Resource
private BpmTaskCandidateInvoker bpmTaskCandidateInvoker;
@Resource
private AdminUserApi adminUserApi;
@Resource
private BpmProcessInstanceEventPublisher processInstanceEventPublisher;
// ========== Query 查询相关方法 ==========
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery()
.includeProcessVariables()
.processInstanceId(id)
.singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public PageResult getProcessInstancePage(Long userId,
BpmProcessInstancePageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
.includeProcessVariables()
.processInstanceTenantId(FlowableUtils.getTenantId())
.orderByProcessInstanceStartTime().desc();
if (userId != null) { // 【我的流程】菜单时,需要传递该字段
processInstanceQuery.startedBy(String.valueOf(userId));
} else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
}
if (StrUtil.isNotEmpty(pageReqVO.getName())) {
processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
}
if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) {
processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey());
}
if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
}
if (pageReqVO.getStatus() != null) {
processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus());
}
if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
}
// 查询数量
long processInstanceCount = processInstanceQuery.count();
if (processInstanceCount == 0) {
return PageResult.empty(processInstanceCount);
}
// 查询列表
List processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize());
return new PageResult<>(processInstanceList, processInstanceCount);
}
@Override
public Map getFormFieldsPermission(BpmFormFieldsPermissionReqVO reqVO) {
// 1.1 获取流程定义 Id
String processDefinitionId = reqVO.getProcessDefinitionId();
if (StrUtil.isEmpty(processDefinitionId) && StrUtil.isNotEmpty(reqVO.getProcessInstanceId())) {
HistoricProcessInstance processInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId());
if (processInstance == null) {
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
}
processDefinitionId = processInstance.getProcessDefinitionId();
}
// 1.2 获取流程活动编号
String activityId = reqVO.getActivityId();
if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(reqVO.getTaskId())) { // 流程活动 Id 为空。从流程任务中获取流程活动 Id
activityId = Optional.ofNullable(taskService.getHistoricTask(reqVO.getTaskId()))
.map(HistoricTaskInstance::getTaskDefinitionKey).orElse(null);
}
if (StrUtil.isEmpty(activityId)) {
return null;
}
// 2. 从 BpmnModel 中解析表单字段权限
return BpmnModelUtils.parseFormFieldsPermission(
processDefinitionService.getProcessDefinitionBpmnModel(processDefinitionId), activityId);
}
@Override
public BpmApprovalDetailRespVO getApprovalDetail(Long startUserId, BpmApprovalDetailReqVO reqVO) {
// 1. 审批详情
BpmApprovalDetailRespVO respVO = new BpmApprovalDetailRespVO();
String processDefinitionId = reqVO.getProcessDefinitionId();
ProcessInstance runProcessInstance = null; // 正在运行的流程实例
Set runNodeIds = new HashSet<>(); // 已经运行的节点 Ids (BPMN XML 节点 Id)
Map runningApprovalNodes = new HashMap<>(); // 正在运行的节点的审批信息
List approvalNodes = new ArrayList<>();
// 1.1 情况一:流程未发起
if (reqVO.getProcessInstanceId() == null) {
respVO.setStatus(BpmProcessInstanceStatusEnum.NOT_START.getStatus());
// 1.2 情况二:流程已发起
} else {
// 1.2.1 获取流程实例状态
HistoricProcessInstance processInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId());
if (processInstance == null) {
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
}
Integer processInstanceStatus = FlowableUtils.getProcessInstanceStatus(processInstance);
respVO.setStatus(processInstanceStatus);
// 1.2.2 构建已运行节点的审批信息
List historicActivityList = activityService.getActivityListByProcessInstanceId(processInstance.getId());
AlreadyRunApproveNodeRespBO respBO = buildAlreadyRunApproveNodes(processInstance.getId(), processInstanceStatus, historicActivityList);
approvalNodes = respBO.getApproveNodes();
runNodeIds = respBO.getRunNodeIds();
// 1.2.3 特殊:流程已经结束,直接 return,无需预测
if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) {
respVO.setApproveNodes(approvalNodes);
return respVO;
}
runningApprovalNodes = respBO.getRunningApprovalNodes();
processDefinitionId = processInstance.getProcessDefinitionId();
runProcessInstance = getProcessInstance(processInstance.getId());
startUserId = Long.valueOf(runProcessInstance.getStartUserId());
}
// 2. 流程未结束,预测未运行节点的审批信息。需要区分 BPMN 设计器 和 SIMPLE 设计器
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinitionId);
// 2.1 情况一:仿钉钉流程设计器
if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) {
BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class);
List notRunApproveNodes = new ArrayList<>();
traverseSimpleModelNodeToBuildNotRunApproveNodes(
startUserId, runProcessInstance, simpleModel, runNodeIds, runningApprovalNodes, notRunApproveNodes);
approvalNodes.addAll(notRunApproveNodes);
respVO.setApproveNodes(approvalNodes);
// 会不会有极端的情况:对于依次审批来说,它是已经 running,但是当前节点也要计算其它审批人?(已修改)
// 2.2 情况二:BPMN 流程设计器
} else if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {
// TODO 芋艿:需要把 start 节点加出来
// TODO Bpmn 设计器,构建未运行节点的审批信息;未完全实现
respVO.setApproveNodes(approvalNodes);
}
return respVO;
}
/**
* 遍历 SIMPLE 设计器模型 构建未运行节点的审批信息
*
* @param startUserId 流程发起人编号
* @param processInstance 流程实例
* @param simpleModelNode SIMPLE 设计器模型
* @param runNodeIds 已经运行节点的 Ids
* @param runningApprovalNodes 正在运行的节点的审批信息
* @param approveNodeList 未运行节点的审批信息列表
*/
private void traverseSimpleModelNodeToBuildNotRunApproveNodes(Long startUserId,
ProcessInstance processInstance,
BpmSimpleModelNodeVO simpleModelNode,
Set runNodeIds,
Map runningApprovalNodes,
List approveNodeList) {
if (!SimpleModelUtils.isValidNode(simpleModelNode)) {
return;
}
buildNotRunApproveNodes(startUserId, processInstance, simpleModelNode, runNodeIds, runningApprovalNodes, approveNodeList);
// 如果有子节点递归遍历子节点
traverseSimpleModelNodeToBuildNotRunApproveNodes(
startUserId, processInstance, simpleModelNode.getChildNode(), runNodeIds, runningApprovalNodes, approveNodeList);
}
private void buildNotRunApproveNodes(Long startUserId, ProcessInstance processInstance,
BpmSimpleModelNodeVO node, Set runNodeIds,
Map runningApprovalNodes,
List approveNodeList) {
// 情况一:节点未运行:需要进行预测
if (!runNodeIds.contains(node.getId())) {
// 1. 对需要人工审批的审批节点,进行预测
if (APPROVE_NODE.getType().equals(node.getType()) && USER.getType().equals(node.getApproveType())
|| START_USER_NODE.getType().equals(node.getType())) {
ApprovalNodeInfo approvalNodeInfo = new ApprovalNodeInfo().setNodeType(node.getType())
.setName(node.getName()).setStatus(NOT_START.getStatus());
Integer candidateStrategy = START_USER_NODE.getType().equals(node.getType()) ?
START_USER.getStrategy() : node.getCandidateStrategy();
approvalNodeInfo.setCandidateUserList(
getNotRunTaskCandidateUserList(startUserId, processInstance, node.getId(), candidateStrategy, node.getCandidateParam()));
approveNodeList.add(approvalNodeInfo);
// 2. 对分支节点,进行预测
} else if (BpmSimpleModelNodeType.isBranchNode(node.getType())) {
// 并行分支,不用预测条件。所有分支都需要遍历
if (PARALLEL_BRANCH_NODE.getType().equals(node.getType())) {
node.getConditionNodes().forEach(conditionNode ->
traverseSimpleModelNodeToBuildNotRunApproveNodes(startUserId, processInstance, conditionNode.getChildNode()
, runNodeIds, runningApprovalNodes, approveNodeList));
} else if (CONDITION_BRANCH_NODE.getType().equals(node.getType())) {
for (BpmSimpleModelNodeVO conditionNode : node.getConditionNodes()) {
// 满足一个条件, 遍历该分支并
if ((processInstance != null && evalConditionExpress(processInstance, SimpleModelUtils.buildConditionExpression(conditionNode))) // 预测条件表达式的值
|| BooleanUtil.isTrue(conditionNode.getDefaultFlow())) { // 是否默认的序列
traverseSimpleModelNodeToBuildNotRunApproveNodes(startUserId, processInstance, conditionNode.getChildNode(),
runNodeIds, runningApprovalNodes, approveNodeList);
break;
}
}
}
// TODO 包容分支待实现
// 3. 结束节点
} else if (END_NODE.getType().equals(node.getType())) {
ApprovalNodeInfo nodeProgress = new ApprovalNodeInfo();
nodeProgress.setNodeType(node.getType());
nodeProgress.setName(node.getName());
nodeProgress.setStatus(NOT_START.getStatus());
approveNodeList.add(nodeProgress);
}
} else {
// 情况二:节点已经运行
// 如果是分支节点,需要检查分支节点的运行情况
if (BpmSimpleModelNodeType.isBranchNode(node.getType()) && ArrayUtil.isNotEmpty(node.getConditionNodes())) {
node.getConditionNodes().forEach(conditionNode -> {
// 只有运行的条件,才需要遍历
if (runNodeIds.contains(conditionNode.getId())) {
traverseSimpleModelNodeToBuildNotRunApproveNodes(startUserId, processInstance, conditionNode.getChildNode()
, runNodeIds, runningApprovalNodes, approveNodeList);
}
});
// 如果是依次审批, 需要加其它未审批候选人
} else if (SimpleModelUtils.isSequentialApproveNode(node) && runningApprovalNodes.containsKey(node.getId())) {
ApprovalNodeInfo approvalNodeInfo = runningApprovalNodes.get(node.getId());
List candidateUserList = getNotRunTaskCandidateUserList(
startUserId, processInstance, node.getId(), node.getCandidateStrategy(), node.getCandidateParam());
// TODO @jason:这里的逻辑,可能可以简化成,直接拿已经审批过的人的 userId 集合,从 candidateUserList remove 下。一方面简单一点,方面 calculateUsers 返回的是 set,目前不是很有序。
ApprovalTaskInfo approvalTaskInfo = CollUtil.getFirst(approvalNodeInfo.getTasks());
Long currentAssignedUserId = null;
if (approvalTaskInfo != null && approvalTaskInfo.getAssigneeUser() != null) {
currentAssignedUserId = approvalTaskInfo.getAssigneeUser().getId();
}
// 找到当前审批人在候选人列表的位置
int index = 0;
for (User user : candidateUserList) {
if (user.getId().equals(currentAssignedUserId)) {
break;
}
index++;
}
// 截取当前审批人位置后面的候选人, 不包含当前审批人
approvalNodeInfo.setCandidateUserList(CollUtil.sub(candidateUserList, ++index, candidateUserList.size()));
}
}
}
/**
* 从已经运行活动节点构建的审批信息列表
*
* @param processInstanceId 流程实例 Id
* @param processInstanceStatus 流程实例状态
* @param historicActivityList 已经运行活动
*/
private AlreadyRunApproveNodeRespBO buildAlreadyRunApproveNodes(String processInstanceId,
Integer processInstanceStatus,
List historicActivityList) {
// 1.1 获取待处理活动:只有 "userTask" 和 "endEvent" 需要处理
List pendingActivityNodes = filterList(historicActivityList,
item -> BpmSimpleModelNodeType.isRecordNode(item.getActivityType()));
// 1.2 已运行节点的 activityId
Set runNodeIds = convertSet(historicActivityList, HistoricActivityInstance::getActivityId);
// 2.1 获取已运行的任务(包括运行中的任务)
List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId);
Map taskMap = convertMap(taskList, HistoricTaskInstance::getId);
// 2.2 获取加签的任务
Map> addSignTaskMap = convertMultiMap(
filterList(taskList, task -> StrUtil.isNotEmpty(task.getParentTaskId())), HistoricTaskInstance::getParentTaskId);
// 3.1 获取节点的用户信息
Set userIds = CollectionUtils.convertSetByFlatMap(pendingActivityNodes, activity -> {
if (BPMN_USER_TASK_TYPE.equals(activity.getActivityType())) {
HistoricTaskInstance task = taskMap.get(activity.getTaskId());
Set taskUsers = CollUtil.newHashSet();
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(task.getAssignee(), null));
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(task.getOwner(), null));
List addSignTasks = addSignTaskMap.get(activity.getTaskId());
if (CollUtil.isNotEmpty(addSignTasks)) {
addSignTasks.forEach(item -> {
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(item.getAssignee(), null));
CollUtil.addIfAbsent(taskUsers, NumberUtil.parseLong(item.getOwner(), null));
});
}
return taskUsers.stream();
} else {
return Stream.empty();
}
});
Map userMap = convertMap(adminUserApi.getUserList(userIds).getCheckedData(), AdminUserRespDTO::getId);
// 3.2 已经结束的任务转换为审批信息
final Multimap runningTask = ArrayListMultimap.create(); // 运行中的任务
List approvalNodeList = convertList(pendingActivityNodes, activity -> {
ApprovalNodeInfo approvalNodeInfo = new ApprovalNodeInfo().setId(activity.getId()).setName(activity.getActivityName())
.setStartTime(DateUtils.of(activity.getStartTime())).setEndTime(DateUtils.of(activity.getEndTime()));
if (BPMN_USER_TASK_TYPE.equals(activity.getActivityType())) { // 用户任务
// nodeType
approvalNodeInfo.setNodeType(START_USER_NODE_ID.equals(activity.getActivityId()) ?
START_USER_NODE.getType() : APPROVE_NODE.getType());
// status
HistoricTaskInstance task = taskMap.get(activity.getTaskId());
Integer taskStatus = FlowableUtils.getTaskStatus(task);
// 运行中的任务, 会签,或签任务聚合在一起。
if (!BpmTaskStatusEnum.isEndStatus(taskStatus)) {
runningTask.put(activity.getActivityId(), activity);
return null;
}
// tasks
ApprovalTaskInfo approveTask = convertApproveTaskInfo(task, userMap);
List approveTasks = CollUtil.newArrayList(approveTask);
List addSignTasks = addSignTaskMap.get(activity.getTaskId());
if (CollUtil.isNotEmpty(addSignTasks)) { // 处理加签任务
approveTasks.addAll(convertList(addSignTasks, item -> convertApproveTaskInfo(item, userMap)));
}
approvalNodeInfo.setStatus(taskStatus);
approvalNodeInfo.setTasks(approveTasks);
} else if (END_NODE.getBpmnType().equals(activity.getActivityType())) {
approvalNodeInfo.setNodeType(END_NODE.getType());
approvalNodeInfo.setStatus(processInstanceStatus);
}
return approvalNodeInfo;
});
// 3.3 运行中的任务转换为审批信息。
final Map runningApprovalNodes = new HashMap<>(); // 正在运行节点的审批信息
runningTask.asMap().forEach((activityId, activities) -> {
if (CollUtil.isNotEmpty(activities)) {
ApprovalNodeInfo approvalNodeInfo = new ApprovalNodeInfo();
approvalNodeInfo.setNodeType(APPROVE_NODE.getType());
approvalNodeInfo.setStatus(RUNNING.getStatus());
List approveTasks = CollUtil.newArrayList();
int i = 0;
for (HistoricActivityInstance activity : activities) {
HistoricTaskInstance task = taskMap.get(activity.getTaskId());
// 取第一个任务, 会签/或签的任务。开始时间相同的
if (i == 0) {
approvalNodeInfo.setId(activity.getId()).setName(activity.getActivityName()).
setStartTime(DateUtils.of(activity.getStartTime()));
}
// tasks
ApprovalTaskInfo approveTask = convertApproveTaskInfo(task, userMap);
approveTasks.add(approveTask);
List addSignTasks = addSignTaskMap.get(activity.getTaskId());
if (CollUtil.isNotEmpty(addSignTasks)) { // 处理加签任务
approveTasks.addAll(convertList(addSignTasks, item -> convertApproveTaskInfo(item, userMap)));
}
i++;
}
approvalNodeInfo.setTasks(approveTasks);
approvalNodeList.add(approvalNodeInfo);
runningApprovalNodes.put(activityId, approvalNodeInfo);
}
});
return new AlreadyRunApproveNodeRespBO().setApproveNodes(approvalNodeList).setRunNodeIds(runNodeIds)
.setRunningApprovalNodes(runningApprovalNodes);
}
private ApprovalTaskInfo convertApproveTaskInfo(HistoricTaskInstance task, Map userMap) {
if (task == null) {
return null;
}
ApprovalTaskInfo approveTask = BeanUtils.toBean(task, ApprovalTaskInfo.class);
approveTask.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
Long taskAssignee = NumberUtil.parseLong(task.getAssignee(), null);
if (taskAssignee != null) {
approveTask.setAssigneeUser(BeanUtils.toBean(userMap.get(taskAssignee), User.class));
}
Long taskOwner = NumberUtil.parseLong(task.getOwner(), null);
if (taskOwner != null) {
approveTask.setOwnerUser(BeanUtils.toBean(userMap.get(taskOwner), User.class));
}
return approveTask;
}
private List getNotRunTaskCandidateUserList(Long startUserId, ProcessInstance processInstance, String activityId,
Integer candidateStrategy, String candidateParam) {
BpmTaskCandidateStrategy taskCandidateStrategy = bpmTaskCandidateInvoker.getCandidateStrategy(candidateStrategy);
Set userIds = taskCandidateStrategy.calculateUsers(startUserId, processInstance, activityId, candidateParam);
Map adminUserMap = adminUserApi.getUserMap(userIds);
// 需要按照候选人的顺序返回。原因是,依次审批需要按顺序展示用户
return convertList(userIds, userId -> BeanUtils.toBean(adminUserMap.get(userId), User.class));
}
/**
* 计算条件表达式的值
*
* @param processInstance 流程实例
* @param express 条件表达式
*/
private Boolean evalConditionExpress(ProcessInstance processInstance, String express) {
if (express == null) {
return Boolean.FALSE;
}
Object result = managementService.executeCommand(context -> {
try {
return FlowableUtils.getExpressionValue((VariableContainer) processInstance, express);
} catch (FlowableException ex) {
log.error("[evalConditionExpress][条件表达式({}) 解析报错", express, ex);
return Boolean.FALSE;
}
});
return Boolean.TRUE.equals(result);
}
// ========== Update 写入相关方法 ==========
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null,
createReqVO.getStartUserSelectAssignees());
}
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(),
createReqDTO.getStartUserSelectAssignees());
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey,
Map> startUserSelectAssignees) {
// 1.1 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(definition.getId());
if (processDefinitionInfo == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
// 1.2 校验是否能够发起
if (!processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId)) {
throw exception(PROCESS_INSTANCE_START_USER_CAN_START);
}
// 1.3 校验发起人自选审批人
validateStartUserSelectAssignees(definition, startUserSelectAssignees);
// 2. 创建流程实例
if (variables == null) {
variables = new HashMap<>();
}
FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
BpmProcessInstanceStatusEnum.RUNNING.getStatus());
if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
}
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
return instance.getId();
}
private void validateStartUserSelectAssignees(ProcessDefinition definition, Map> startUserSelectAssignees) {
// 1. 获得发起人自选审批人的 UserTask 列表
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId());
List userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
if (CollUtil.isEmpty(userTaskList)) {
return;
}
// 2. 校验发起人自选审批人的 UserTask 是否都配置了
userTaskList.forEach(userTask -> {
List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null;
if (CollUtil.isEmpty(assignees)) {
throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName());
}
Map userMap = adminUserApi.getUserMap(assignees);
assignees.forEach(assignee -> {
if (userMap.get(assignee) == null) {
throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee);
}
});
});
}
@Override
public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 1.1 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 1.2 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 2. 取消流程
updateProcessInstanceCancel(cancelReqVO.getId(),
BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason()));
}
@Override
public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) {
// 1.1 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 2. 取消流程
AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData();
updateProcessInstanceCancel(cancelReqVO.getId(),
BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason()));
}
private void updateProcessInstanceCancel(String id, String reason) {
// 1. 更新流程实例 status
runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
BpmProcessInstanceStatusEnum.CANCEL.getStatus());
runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason);
// 2. 结束流程
taskService.moveTaskToEnd(id);
}
@Override
public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) {
runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
BpmProcessInstanceStatusEnum.REJECT.getStatus());
runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON,
BpmReasonEnum.REJECT_TASK.format(reason));
}
// ========== Event 事件相关方法 ==========
@Override
public void processProcessInstanceCompleted(ProcessInstance instance) {
// 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号
FlowableUtils.execute(instance.getTenantId(), () -> {
// 1.1 获取当前状态
Integer status = (Integer) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
String reason = (String) instance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON);
// 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态
// 为什么这么处理?因为流程完成,并且完成了,说明审批通过了
if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) {
status = BpmProcessInstanceStatusEnum.APPROVE.getStatus();
runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, status);
}
// 2. 发送对应的消息通知
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
} else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) {
messageService.sendMessageWhenProcessInstanceReject(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason));
}
// 3. 发送流程实例的状态事件
processInstanceEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status));
});
}
}
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
index 4a71b63fa..fe3fb5c44 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
@@ -4,11 +4,11 @@
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;
-import jakarta.validation.Valid;
import org.flowable.bpmn.model.UserTask;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
+import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import java.util.Map;
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java
index e642636ba..9158ddd9d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java
@@ -13,13 +13,13 @@
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java
index 89d1f64e4..94a443535 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java
@@ -11,13 +11,13 @@
import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import com.google.common.annotations.VisibleForTesting;
-import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+import javax.annotation.Resource;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;