diff --git a/yudao-framework/yudao-spring-boot-starter-web/pom.xml b/yudao-framework/yudao-spring-boot-starter-web/pom.xml
index 519d0e21bd..dadd9f6ab7 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-web/pom.xml
@@ -53,7 +53,13 @@
             provided 
         
 
-        
+        
+        
+            com.google.guava
+            guava
+            provided 
+        
+
         
             org.jsoup
             jsoup
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
index 764dd59e79..f5651b6ae8 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
@@ -18,6 +18,7 @@ import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 import com.fasterxml.jackson.databind.exc.InvalidFormatException;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.concurrent.UncheckedExecutionException;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.util.Assert;
@@ -106,6 +107,9 @@ public class GlobalExceptionHandler {
         if (ex instanceof AccessDeniedException) {
             return accessDeniedExceptionHandler(request, (AccessDeniedException) ex);
         }
+        if (ex instanceof UncheckedExecutionException && ex.getCause() != ex) {
+            return allExceptionHandler(request, ex.getCause());
+        }
         return defaultExceptionHandler(request, ex);
     }
 
@@ -252,6 +256,16 @@ public class GlobalExceptionHandler {
         return CommonResult.error(FORBIDDEN);
     }
 
+    /**
+     * 处理 Guava UncheckedExecutionException
+     *
+     * 例如说,缓存加载报错,可见 https://t.zsxq.com/UszdH
+     */
+    @ExceptionHandler(value = UncheckedExecutionException.class)
+    public CommonResult> uncheckedExecutionExceptionHandler(HttpServletRequest req, UncheckedExecutionException ex) {
+        return allExceptionHandler(req, ex.getCause());
+    }
+
     /**
      * 处理业务异常 ServiceException
      *
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
index 57f4d393f3..87e6a605ff 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
@@ -7,44 +7,35 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
 import lombok.Setter;
-import org.flowable.bpmn.model.Activity;
-import org.flowable.bpmn.model.CallActivity;
-import org.flowable.bpmn.model.FlowElement;
-import org.flowable.bpmn.model.UserTask;
+import org.flowable.bpmn.model.*;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
-import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
+import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
 
 import java.util.List;
 import java.util.Set;
 
 /**
- * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配
- * 第一步,基于分配规则,计算出分配任务的【多个】候选人们。
- * 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它
+ * 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配
  *
- * @author kemengkai
- * @since 2022-04-21 16:57
+ * 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样
+ *
+ * @author 芋道源码
  */
 @Setter
-public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
+public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior {
 
     private BpmTaskCandidateInvoker taskCandidateInvoker;
 
-    public BpmParallelMultiInstanceBehavior(Activity activity,
-                                            AbstractBpmnActivityBehavior innerActivityBehavior) {
+    public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
         super(activity, innerActivityBehavior);
     }
 
     /**
-     * 重写该方法,主要实现两个功能:
-     * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的
-     * 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人
+     * 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似
      *
-     * 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量
-     *
-     * @param execution 执行任务
-     * @return 数量
+     * 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序!
      */
     @Override
     protected int resolveNrOfInstances(DelegateExecution execution) {
@@ -58,8 +49,9 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
             super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
 
             // 第二步,获取任务的所有处理人
+            // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人
             @SuppressWarnings("unchecked")
-            Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class);
+            Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class);
             if (assigneeUserIds == null) {
                 assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
                 if (CollUtil.isEmpty(assigneeUserIds)) {
@@ -88,4 +80,19 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
         return super.resolveNrOfInstances(execution);
     }
 
+    @Override
+    protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) {
+        // 参见 https://t.zsxq.com/53Meo 情况
+        if (execution.getCurrentFlowElement() instanceof CallActivity
+                || execution.getCurrentFlowElement() instanceof SubProcess) {
+            super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);
+            return;
+        }
+        // 参见 https://gitee.com/zhijiantianya/yudao-cloud/issues/IC239F
+        super.collectionExpression = null;
+        super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
+        super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
+        super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);
+    }
+
 }
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
index 2bd43fdc4d..eca8a9f1ec 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -67,7 +67,6 @@ import java.util.*;
 
 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.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;
 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID;
@@ -221,11 +220,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,
                 processDefinitionInfo,
                 processVariables, activities);
-        // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点
-        if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) {
-            simulateActivityNodes.removeIf(node ->
-                    BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()));
-        }
 
         // 4. 拼接最终数据
         return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance,
@@ -415,7 +409,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         endActivities.forEach(activity -> {
             // StartEvent:只处理 BPMN 的场景。因为,SIMPLE 情况下,已经有 START_USER_NODE 节点
             if (ELEMENT_EVENT_START.equals(activity.getActivityType())
-                    && BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())) {
+                    && BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())
+                    && !CollUtil.contains(activities, // 特殊:如果已经存在用户手动创建的 START_USER_NODE_ID 节点,则忽略 StartEvent
+                    historicActivity -> historicActivity.getActivityId().equals(START_USER_NODE_ID))) {
                 ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID)
                         .setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus());
                 ActivityNode startNode = new ActivityNode().setId(startTask.getId())
@@ -555,7 +551,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         // 情况一:BPMN 设计器
         if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) {
             List flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables);
-            return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(startUserId, bpmnModel,
+            return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(
+                    startUserId, bpmnModel, flowElements,
                     processDefinitionInfo, processVariables, flowElement, runActivityIds));
         }
         // 情况二:SIMPLE 设计器
@@ -563,7 +560,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
             BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(),
                     BpmSimpleModelNodeVO.class);
             List simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables);
-            return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel,
+            return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(
+                    startUserId, bpmnModel,
                     processDefinitionInfo, processVariables, simpleNode, runActivityIds));
         }
         throw new IllegalArgumentException("未知设计器类型:" + processDefinitionInfo.getModelType());
@@ -618,8 +616,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         return null;
     }
 
-    private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel,
-                                                       BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables,
+    private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, List flowElements,
+                                                       BpmProcessDefinitionInfoDO processDefinitionInfo,
+                                                       Map processVariables,
                                                        FlowElement node, Set runActivityIds) {
         if (runActivityIds.contains(node.getId())) {
             return null;
@@ -634,6 +633,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
 
         // 1. 开始节点
         if (node instanceof StartEvent) {
+            if (CollUtil.contains(flowElements, // 特殊:如果已经存在用户手动创建的 START_USER_NODE_ID 节点,则忽略 StartEvent
+                    flowElement -> flowElement.getId().equals(START_USER_NODE_ID))) {
+                return null;
+            }
             return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName())
                     .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType());
         }
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
index 9920359d51..dc9f32a0f1 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
@@ -159,10 +159,10 @@ public class CrmContactServiceImpl implements CrmContactService {
         // 2. 删除联系人
         contactMapper.deleteById(id);
 
-        // 4.1 删除数据权限
-        permissionService.deletePermission(CrmBizTypeEnum.CRM_CONTACT.getType(), id);
-        // 4.2 删除商机关联
+        // 4.1 删除商机关联
         contactBusinessService.deleteContactBusinessByContactId(id);
+        // 4.2 删除数据权限
+        permissionService.deletePermission(CrmBizTypeEnum.CRM_CONTACT.getType(), id);
 
         // 记录操作日志上下文
         LogRecordContext.putVariable("contactName", contact.getName());