From 274dd8b724dd4485658e1d2a3f825a007f625bd7 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期五, 17 七月 2026 09:10:07 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_business' into dev_business

---
 yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java |  247 +++++++++++++++++++++++++++++--------------------
 1 files changed, 145 insertions(+), 102 deletions(-)

diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
index 89b7bb2..73f8a01 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -2,7 +2,9 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.*;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -19,10 +21,7 @@
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
 import cn.iocoder.yudao.module.bpm.enums.definition.*;
-import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
-import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
-import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
-import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.*;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
@@ -49,6 +48,7 @@
 import org.flowable.engine.runtime.ActivityInstance;
 import org.flowable.engine.runtime.Execution;
 import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.task.Attachment;
 import org.flowable.task.api.DelegationState;
 import org.flowable.task.api.Task;
 import org.flowable.task.api.TaskInfo;
@@ -510,6 +510,18 @@
                 .orderByHistoricTaskInstanceStartTime().asc().list();
     }
 
+    @Override
+    public List<Attachment> getAttachments(String processInstanceId, Set<String> taskIds, BpmAttachmentTypeEnum attachmentType) {
+        List<Attachment> result = taskService.getProcessInstanceAttachments(processInstanceId);
+        if (CollUtil.isNotEmpty(taskIds)) {
+            result = filterList(result, attachment -> taskIds.contains(attachment.getTaskId()));
+        }
+        if (attachmentType != null) {
+            result = filterList(result, attachment -> attachmentType.getType().equals(attachment.getType()));
+        }
+        return result;
+    }
+
     /**
      * 鍒ゆ柇鎸囧畾鐢ㄦ埛锛屾槸鍚︽槸褰撳墠浠诲姟鐨勫鎵逛汉
      *
@@ -592,6 +604,11 @@
         // 2.2 娣诲姞璇勮
         taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
                 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
+        // 2.3 娣诲姞闄勪欢
+        if (CollUtil.isNotEmpty(reqVO.getAttachments())) {
+            reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(),
+                    task.getId(), task.getProcessInstanceId(), FileUtil.getName(URLUtil.getPath(attachment)), null, attachment));
+        }
 
         // 3. 璁剧疆娴佺▼鍙橀噺銆傚鏋滄祦绋嬪彉閲忓墠绔紶绌猴紝闇�瑕佷粠鍘嗗彶瀹炰緥涓幏鍙栵紝鍘熷洜锛氬墠绔〃鍗曞鏋滃湪褰撳墠鑺傜偣鏃犲彲缂栬緫鐨勫瓧娈垫椂 variables 涓�瀹氫細涓虹┖
         // 鍦烘櫙涓�锛欰 鑺傜偣鍙戣捣锛孊 鑺傜偣琛ㄥ崟鏃犲彲缂栬緫瀛楁锛屽鎵归�氳繃鏃讹紝C 鑺傜偣闇�瑕佹祦绋嬪彉閲忚幏鍙栦笅涓�涓墽琛岃妭鐐癸紝浣嗗洜涓� B 鑺傜偣鏃犲彲缂栬緫鐨勫瓧娈碉紝variables 涓虹┖锛屾祦绋嬪彲鑳藉嚭鐜伴棶棰樸��
@@ -606,9 +623,9 @@
         }
 
         // 4. 鏍¢獙骞跺鐞� APPROVE_USER_SELECT 褰撳墠瀹℃壒浜猴紝閫夋嫨涓嬩竴鑺傜偣瀹℃壒浜虹殑閫昏緫
-        Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables,
+        validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables,
                 bpmnModel, reqVO.getNextAssignees(), instance);
-        runtimeService.setVariables(task.getProcessInstanceId(), variables);
+        runtimeService.setVariables(task.getProcessInstanceId(), processVariables);
 
         // 5. 濡傛灉褰撳墠鑺傜偣 Id 瀛樺湪浜庨渶瑕侀娴嬬殑娴佺▼鑺傜偣涓紝浠庝腑绉婚櫎銆� 娴佺▼鍙橀噺鍦ㄥ洖閫�鎿嶄綔涓缃�
         Object needSimulateTaskIds = runtimeService.getVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS);
@@ -618,8 +635,16 @@
             runtimeService.setVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskIdsByReturn);
         }
 
-        // 6. 璋冪敤 BPM complete 鍘诲畬鎴愪换鍔�
-        taskService.complete(task.getId(), variables, true);
+        // 6. 娓呯悊閫�鍥炶缃殑涓嶈嚜鍔ㄩ�氳繃鐨勫彉閲忋�備粎鍦ㄨ鏍囪瀛樺湪鏃舵墠鍒犻櫎锛岄伩鍏嶆瘡娆″畬鎴愪换鍔¢兘浜х敓鏃犺皳鐨� DB delete
+        String returnFlagKey = String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey());
+        if (runtimeService.hasVariable(task.getProcessInstanceId(), returnFlagKey)) {
+            log.info("[approveTask][taskId({}) 娓呯悊閫�鍥炴爣璁板彉閲�({})]", task.getId(), returnFlagKey);
+            runtimeService.removeVariable(task.getProcessInstanceId(), returnFlagKey);
+        }
+
+        // 7. 璋冪敤 BPM complete 鍘诲畬鎴愪换鍔�
+        Map<String,Object> taskVariables = MapUtil.emptyIfNull(reqVO.getVariables()); // task local variables. 涓�鑸敤浜庝换鍔″唴宓屾祦绋嬭〃鍗�
+        taskService.complete(task.getId(), taskVariables, true);
 
         // 銆愬姞绛句笓灞炪�戝鐞嗗姞绛句换鍔�
         handleParentTaskIfSign(task.getParentTaskId());
@@ -646,10 +671,10 @@
         }
         // 1. 鑾峰彇涓嬩竴涓皢瑕佹墽琛岀殑鑺傜偣闆嗗悎
         FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
-        List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
+        List<UserTask> nextFlowNodes = getNextUserTasks(flowElement, bpmnModel, variables);
 
         // 2. 鏍¢獙閫夋嫨鐨勪笅涓�涓妭鐐圭殑瀹℃壒浜猴紝鏄惁鍚堟硶
-        for (FlowNode nextFlowNode : nextFlowNodes) {
+        for (UserTask nextFlowNode : nextFlowNodes) {
             Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
             // 2.1 鎯呭喌涓�锛氬鏋滆妭鐐逛腑鐨勫鎵逛汉绛栫暐涓� 鍙戣捣浜鸿嚜閫�
             if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
@@ -675,8 +700,12 @@
 
             // 2.2 鎯呭喌浜岋細濡傛灉鑺傜偣涓殑瀹℃壒浜虹瓥鐣ヤ负 瀹℃壒浜猴紝鍦ㄥ鎵规椂閫夋嫨涓嬩竴涓妭鐐圭殑瀹℃壒浜猴紝骞朵笖璇ヨ妭鐐圭殑瀹℃壒浜轰负绌�
             if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
-                // 濡傛灉鑺傜偣瀛樺湪锛屼絾鏈厤缃鎵逛汉
+                // 鐗规畩锛氬鏋滃綋鍓嶈妭鐐瑰凡缁忓瓨鍦ㄥ鎵逛汉锛屽垯涓嶅厑璁歌鐩栥�� 渚嬪骞惰鑺傜偣鍚庯紝璁剧疆鐨勫鎵逛汉鑷�夎妭鐐广�� https://t.zsxq.com/daxv1
                 Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
+                if (approveUserSelectAssignees != null && CollUtil.isNotEmpty(approveUserSelectAssignees.get(nextFlowNode.getId()))) {
+                    continue;
+                }
+                // 濡傛灉鑺傜偣瀛樺湪锛屼絾鏈厤缃鎵逛汉
                 List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;
                 if (CollUtil.isEmpty(assignees)) {
                     throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
@@ -810,7 +839,13 @@
         // 2.2 娣诲姞娴佺▼璇勮
         taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
                 BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));
-        // 2.3 濡傛灉褰撳墠浠诲姟鏃惰鍔犵鐨勶紝鍒欏姞瀹冪殑鏍逛换鍔′篃鏍囪鎴愭湭閫氳繃
+        // 2.3 娣诲姞闄勪欢
+        if (CollUtil.isNotEmpty(reqVO.getAttachments())) {
+            reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(),
+                    task.getId(), task.getProcessInstanceId(), FileUtil.getName(URLUtil.getPath(attachment)), null, attachment));
+        }
+
+        // 2.4 濡傛灉褰撳墠浠诲姟鏃惰鍔犵鐨勶紝鍒欏姞瀹冪殑鏍逛换鍔′篃鏍囪鎴愭湭閫氳繃
         // 鐤戦棶锛氫负浠�涔堣鏍囪鏈�氳繃鍛紵
         // 鍥炵瓟锛氫緥濡傝 A 浠诲姟琚悜鍓嶅姞绛鹃櫎 B 浠诲姟鏃讹紝B 浠诲姟琚鎵逛笉閫氳繃锛屾鏃� A 浼氳鍙栨秷銆傝�� yudao-ui-admin-vue3 涓嶅睍绀衡�滃凡鍙栨秷鈥濈殑浠诲姟锛屽鑷村睍绀轰笉鍑哄鎵逛笉閫氳繃鐨勭粏鑺傘��
         if (task.getParentTaskId() != null) {
@@ -922,14 +957,12 @@
         // 涓轰粈涔堜笉鐩存帴浣跨敤 runTaskKeyList 鍛紵鍥犱负鍙兘瀛樺湪澶氫釜瀹℃壒鍒嗘敮锛屼緥濡傝锛欰 -> B -> C 鍜� D -> F锛岃�屽彧瑕� C 鎾ゅ洖鍒� A锛岄渶瑕佹帓闄ゆ帀 F
         List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
         List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
-
         // 2. 缁欏綋鍓嶈琚��鍥炵殑 task 鏁扮粍锛岃缃��鍥炴剰瑙�
         taskList.forEach(task -> {
             // 闇�瑕佹帓闄ゆ帀锛屼笉闇�瑕佽缃��鍥炴剰瑙佺殑浠诲姟
             if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
                 return;
             }
-
             // 鍒ゆ柇鏄惁鍒嗛厤缁欒嚜宸变换鍔★紝鍥犱负浼氱浠诲姟锛屼竴涓妭鐐逛細鏈夊涓换鍔�
             if (isAssignUserTask(userId, task)) { // 鎯呭喌涓�锛氳嚜宸辩殑浠诲姟锛岃繘琛� RETURN 鏍囪
                 // 2.1.1 娣诲姞璇勮
@@ -950,14 +983,20 @@
         //    鐩稿叧 issue: https://github.com/flowable/flowable-engine/issues/3944
         // 鈶� flowable 7.2.0 鐗堟湰鍚庯紝缁х画浣跨敤 moveActivityIdsToSingleActivityId 鏂规硶銆傚師鍥狅細flowable 7.2.0 鐗堟湰淇浜嗚闂銆�
         //    鐩稿叧 issue锛歨ttps://github.com/YunaiV/ruoyi-vue-pro/issues/1018
+        // 鈶� moveActivityIdsToSingleActivityId 浣跨敤閬囧埌闂锛� 鐩稿叧 issue https://gitee.com/zhijiantianya/yudao-cloud/issues/IJM8MS
+        //  鏀规垚 moveExecutionsToSingleActivityId 濂藉儚骞舵病鏈夐亣鍒� 鈶� 鎻愬埌鐨勮秴鏃舵彁閱掑け鏁堢殑闂銆傛殏鏃跺厛鏀瑰洖 moveExecutionsToSingleActivityId
+        // 鈶� moveExecutionsToSingleActivityId 鍥為��澶氬疄渚嬬殑鏃跺�欎笉浼氬幓鍒犻櫎澶氬疄渚嬫牴, 搴旀敼鎴� moveActivityIdsToSingleActivityId
+        // flowable 8.0.0  淇涓婇潰鐩稿叧闂锛� 杩樹慨澶嶄簡骞惰鍒嗘敮鍥為��鐨勯棶棰� https://t.zsxq.com/z4d9i銆�
+        // 鈶� 浣跨敤 moveExecutionsToSingleActivityId 鏂规硶杩涜鍥為��鎿嶄綔鏃讹紝濡傛灉鏄瀹炰緥鐨勭敤鎴蜂换鍔°�愯妺閬撶敤鎴蜂换鍔¢粯璁や负澶氬疄渚嬨�戯紝涓嶄細鍒犻櫎澶氬疄渚嬩换鍔$殑鏍规暟鎹� ACT_RU_EXECUTION
+        // 浼氬鑷存湁涓�浜涢棶棰橈紝鎵�浠ヤ娇鐢� moveActivityIdsToSingleActivityId銆� 浣嗘槸璇ユ柟娉曞湪 flowable 6.8.1 ~ 7.1.0 鐨勭増鏈細鏈� bug 闃诲鍥為��鍔熻兘
+        // 鐩稿叧 issue: https://github.com/flowable/flowable-engine/issues/3944
         runtimeService.createChangeActivityStateBuilder()
                 .processInstanceId(currentTask.getProcessInstanceId())
                 .moveActivityIdsToSingleActivityId(returnTaskKeyList, reqVO.getTargetTaskDefinitionKey())
                 // 璁剧疆闇�瑕侀娴嬬殑浠诲姟 ids 鐨勬祦绋嬪彉閲忥紝鐢ㄤ簬杈呭姪棰勬祴
                 .processVariable(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskDefinitionKeys)
-                // 璁剧疆娴佺▼鍙橀噺锛坙ocal锛夎妭鐐归��鍥炴爣璁�, 鐢ㄤ簬閫�鍥炲埌鑺傜偣锛屼笉鎵ц BpmUserTaskAssignStartUserHandlerTypeEnum 绛栫暐锛屽鑷磋嚜鍔ㄩ�氳繃
-                .localVariable(reqVO.getTargetTaskDefinitionKey(),
-                        String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE)
+                // 璁剧疆娴佺▼鍙橀噺鑺傜偣閫�鍥炴爣璁�, 鐢ㄤ簬閫�鍥炲埌鑺傜偣锛屼笉鎵ц BpmUserTaskAssignStartUserHandlerTypeEnum 绛栫暐锛屽鑷磋嚜鍔ㄩ�氳繃
+                .processVariable(String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE)
                 .changeState();
     }
 
@@ -1464,101 +1503,105 @@
                     return;
                 }
 
-                // 鑷姩鍘婚噸锛岄�氳繃鑷姩瀹℃壒鐨勬柟寮�
-                BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId());
-                if (processDefinitionInfo == null) {
-                    log.error("[processTaskAssigned][taskId({}) 娌℃湁鎵惧埌娴佺▼瀹氫箟({})]", task.getId(), task.getProcessDefinitionId());
-                    return;
-                }
-                if (processDefinitionInfo.getAutoApprovalType() != null) {
-                    HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery()
-                            .processInstanceId(task.getProcessInstanceId())
-                            .taskAssignee(task.getAssignee()) // 鐩稿悓瀹℃壒浜�
-                            .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus())
-                            .finished();
-                    if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType())
-                            && sameAssigneeQuery.count() > 0) {
-                        getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
-                                .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName()));
+                // 闇�瑕佸熀浜� instance 璁剧疆绉熸埛缂栧彿锛岄伩鍏� Flowable 鍐呴儴寮傛鎵ц鏃躲�愪緥濡傦細瓒呮椂鑷姩閫氳繃銆� 涓㈠け绉熸埛缂栧彿
+                FlowableUtils.execute(processInstance.getTenantId(), () -> {
+                    // 鑷姩鍘婚噸锛岄�氳繃鑷姩瀹℃壒鐨勬柟寮�
+                    BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId());
+                    if (processDefinitionInfo == null) {
+                        log.error("[processTaskAssigned][taskId({}) 娌℃湁鎵惧埌娴佺▼瀹氫箟({})]", task.getId(), task.getProcessDefinitionId());
                         return;
                     }
-                    if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) {
-                        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
-                        if (bpmnModel == null) {
-                            log.error("[processTaskAssigned][taskId({}) 娌℃湁鎵惧埌娴佺▼妯″瀷({})]", task.getId(), task.getProcessDefinitionId());
-                            return;
-                        }
-                        List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 鑾峰彇鎵�鏈変笂涓�涓妭鐐�
-                                        BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),
-                                SequenceFlow::getSourceRef);
-                        if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) {
+                    if (processDefinitionInfo.getAutoApprovalType() != null) {
+                        HistoricTaskInstanceQuery approvedTaskQuery = historyService.createHistoricTaskInstanceQuery()
+                                .processInstanceId(task.getProcessInstanceId())
+                                .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus())
+                                .finished();
+                        if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType())
+                                && approvedTaskQuery.taskAssignee(task.getAssignee()).count() > 0) {
                             getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
-                                    .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName()));
+                                    .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName()));
                             return;
                         }
-                    }
-                }
-
-                // 鑾峰彇鍙戣捣浜鸿妭鐐�
-                BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
-                if (bpmnModel == null) {
-                    log.error("[processTaskAssigned][taskId({}) 娌℃湁鎵惧埌娴佺▼妯″瀷]", task.getId());
-                    return;
-                }
-                FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
-                // 鍒ゆ柇鏄惁涓洪��鍥炴垨鑰呴┏鍥烇細濡傛灉鏄��鍥炴垨鑰呴┏鍥炰笉璧拌繖涓瓥鐣ワ紙浣跨敤 local variable锛�
-                Boolean returnTaskFlag = runtimeService.getVariableLocal(task.getExecutionId(),
-                        String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
-                Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(),
-                        BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
-                if (userTaskElement.getId().equals(START_USER_NODE_ID)
-                        && (skipStartUserNodeFlag == null // 鐩殑锛氫竴鑸槸鈥滀富娴佺▼鈥濓紝鍙戣捣浜鸿妭鐐癸紝鑷姩閫氳繃瀹℃牳
-                        || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 鐩殑锛氫竴鑸槸鈥滃瓙娴佺▼鈥濓紝鍙戣捣浜鸿妭鐐癸紝鎸夐厤缃嚜鍔ㄩ�氳繃瀹℃牳
-                        && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
-                    getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
-                            .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
-                    return;
-                }
-                // 褰撲笉涓哄彂璧蜂汉鑺傜偣鏃讹紝瀹℃壒浜轰笌鎻愪氦浜轰负鍚屼竴浜烘椂锛屾牴鎹� BpmUserTaskAssignStartUserHandlerTypeEnum 绛栫暐杩涜澶勭悊
-                if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID)
-                        && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
-                    if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
-                        Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
-
-                        // 鎯呭喌涓�锛氳嚜鍔ㄨ烦杩�
-                        if (ObjectUtils.equalsAny(assignStartUserHandlerType,
-                                BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
-                            getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
-                                    .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason()));
-                            return;
-                        }
-                        // 鎯呭喌浜岋細杞氦缁欓儴闂ㄨ礋璐d汉瀹℃壒
-                        if (ObjectUtils.equalsAny(assignStartUserHandlerType,
-                                BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) {
-                            AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
-                            Assert.notNull(startUser, "鎻愪氦浜�({})淇℃伅涓虹┖", processInstance.getStartUserId());
-                            DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
-                            Assert.notNull(dept, "鎻愪氦浜�({})閮ㄩ棬({})淇℃伅涓虹┖", processInstance.getStartUserId(), startUser.getDeptId());
-                            // 鎵句笉鍒伴儴闂ㄨ礋璐d汉鐨勬儏鍐典笅锛岃嚜鍔ㄥ鎵归�氳繃
-                            // noinspection DataFlowIssue
-                            if (dept.getLeaderUserId() == null) {
+                        // 杩炵画瀹℃壒鐨勮妭鐐硅嚜鍔ㄩ�氳繃
+                        if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) {
+                            BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
+                            if (bpmnModel == null) {
+                                log.error("[processTaskAssigned][taskId({}) 娌℃湁鎵惧埌娴佺▼妯″瀷({})]", task.getId(), task.getProcessDefinitionId());
+                                return;
+                            }
+                            List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingUserTaskFlows( // 鑾峰彇鎵�鏈夌殑涓婁竴涓� UserTask 鑺傜偣杩炵嚎
+                                            BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),
+                                    SequenceFlow::getSourceRef);
+                            approvedTaskQuery.taskDefinitionKeys(sourceTaskIds).orderByTaskCreateTime().desc(); // 璁剧疆 taskIds, 骞舵寜鍒涘缓鏃堕棿鍊掑簭鎺掑簭
+                            HistoricTaskInstance firstHisTask = CollUtil.getFirst(approvedTaskQuery.list());
+                            if (firstHisTask != null && StrUtil.equals(firstHisTask.getAssignee(), task.getAssignee())) {
                                 getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
-                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason()));
+                                        .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName()));
                                 return;
                             }
-                            // 鎵惧緱鍒伴儴闂ㄨ礋璐d汉鐨勬儏鍐典笅锛屼慨鏀硅礋璐d汉
-                            if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
-                                getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
-                                        .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
-                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason()));
-                                return;
-                            }
-                            // 濡傛灉閮ㄩ棬璐熻矗浜烘槸鑷繁锛岃繕鏄嚜宸卞鎵瑰惂~
                         }
                     }
-                }
-                // 娉ㄦ剰锛氶渶瑕佸熀浜� instance 璁剧疆绉熸埛缂栧彿锛岄伩鍏� Flowable 鍐呴儴寮傛鏃讹紝涓㈠け绉熸埛缂栧彿
-                FlowableUtils.execute(processInstance.getTenantId(), () -> {
+
+                    // 鑾峰彇鍙戣捣浜鸿妭鐐�
+                    BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
+                    if (bpmnModel == null) {
+                        log.error("[processTaskAssigned][taskId({}) 娌℃湁鎵惧埌娴佺▼妯″瀷]", task.getId());
+                        return;
+                    }
+                    FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
+                    // 鍒ゆ柇鏄惁涓洪��鍥炴垨鑰呴┏鍥烇細濡傛灉鏄��鍥炴垨鑰呴┏鍥炰笉璧拌繖涓瓥鐣�
+                    Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
+                            String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
+                    Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(),
+                            BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
+                    if (userTaskElement.getId().equals(START_USER_NODE_ID)
+                            && (skipStartUserNodeFlag == null // 鐩殑锛氫竴鑸槸鈥滀富娴佺▼鈥濓紝鍙戣捣浜鸿妭鐐癸紝鑷姩閫氳繃瀹℃牳
+                            || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 鐩殑锛氫竴鑸槸鈥滃瓙娴佺▼鈥濓紝鍙戣捣浜鸿妭鐐癸紝鎸夐厤缃嚜鍔ㄩ�氳繃瀹℃牳
+                            && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
+                        getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
+                                .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
+                        return;
+                    }
+                    // 褰撲笉涓哄彂璧蜂汉鑺傜偣鏃讹紝瀹℃壒浜轰笌鎻愪氦浜轰负鍚屼竴浜烘椂锛屾牴鎹� BpmUserTaskAssignStartUserHandlerTypeEnum 绛栫暐杩涜澶勭悊
+                    if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID)
+                            && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
+                        if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
+                            Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
+
+                            // 鎯呭喌涓�锛氳嚜鍔ㄨ烦杩�
+                            if (ObjectUtils.equalsAny(assignStartUserHandlerType,
+                                    BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
+                                getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
+                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason()));
+                                return;
+                            }
+                            // 鎯呭喌浜岋細杞氦缁欓儴闂ㄨ礋璐d汉瀹℃壒
+                            if (ObjectUtils.equalsAny(assignStartUserHandlerType,
+                                    BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) {
+                                AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
+                                Assert.notNull(startUser, "鎻愪氦浜�({})淇℃伅涓虹┖", processInstance.getStartUserId());
+                                DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
+                                Assert.notNull(dept, "鎻愪氦浜�({})閮ㄩ棬({})淇℃伅涓虹┖", processInstance.getStartUserId(), startUser.getDeptId());
+                                // 鎵句笉鍒伴儴闂ㄨ礋璐d汉鐨勬儏鍐典笅锛岃嚜鍔ㄥ鎵归�氳繃
+                                // noinspection DataFlowIssue
+                                if (dept.getLeaderUserId() == null) {
+                                    getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
+                                            .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason()));
+                                    return;
+                                }
+                                // 鎵惧緱鍒伴儴闂ㄨ礋璐d汉鐨勬儏鍐典笅锛屼慨鏀硅礋璐d汉
+                                if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
+                                    getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
+                                            .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
+                                            .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason()));
+                                    return;
+                                }
+                                // 濡傛灉閮ㄩ棬璐熻矗浜烘槸鑷繁锛岃繕鏄嚜宸卞鎵瑰惂~
+                            }
+                        }
+                    }
+
+                    // 鍙戦�佹秷鎭�
                     AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
                     messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
                 });

--
Gitblit v1.9.3