From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-flowable/jnpf-flowable-biz/src/main/java/jnpf/flowable/service/impl/OperatorServiceImpl.java | 1346 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1,346 insertions(+), 0 deletions(-)

diff --git a/jnpf-flowable/jnpf-flowable-biz/src/main/java/jnpf/flowable/service/impl/OperatorServiceImpl.java b/jnpf-flowable/jnpf-flowable-biz/src/main/java/jnpf/flowable/service/impl/OperatorServiceImpl.java
new file mode 100644
index 0000000..8622e74
--- /dev/null
+++ b/jnpf-flowable/jnpf-flowable-biz/src/main/java/jnpf/flowable/service/impl/OperatorServiceImpl.java
@@ -0,0 +1,1346 @@
+package jnpf.flowable.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.dynamic.datasource.annotation.DSTransactional;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.google.common.collect.ImmutableList;
+import jnpf.base.Pagination;
+import jnpf.base.model.flow.FlowFormDataModel;
+import jnpf.base.service.SuperServiceImpl;
+import jnpf.constant.MsgCode;
+import jnpf.exception.WorkFlowException;
+import jnpf.flowable.entity.*;
+import jnpf.flowable.enums.*;
+import jnpf.flowable.job.FlowJobUtil;
+import jnpf.flowable.mapper.*;
+import jnpf.flowable.model.candidates.CandidateCheckFo;
+import jnpf.flowable.model.candidates.CandidateCheckVo;
+import jnpf.flowable.model.candidates.CandidateUserVo;
+import jnpf.flowable.model.flowable.*;
+import jnpf.flowable.model.message.FlowMsgModel;
+import jnpf.flowable.model.operator.AddSignModel;
+import jnpf.flowable.model.operator.FlowBatchModel;
+import jnpf.flowable.model.operator.OperatorVo;
+import jnpf.flowable.model.record.NodeRecordModel;
+import jnpf.flowable.model.task.FlowMethod;
+import jnpf.flowable.model.task.FlowModel;
+import jnpf.flowable.model.task.TaskPagination;
+import jnpf.flowable.model.templatejson.TemplateJsonInfoVO;
+import jnpf.flowable.model.templatenode.BackNodeModel;
+import jnpf.flowable.model.templatenode.nodejson.NodeModel;
+import jnpf.flowable.model.util.FlowContextHolder;
+import jnpf.flowable.model.util.FlowNature;
+import jnpf.flowable.service.OperatorService;
+import jnpf.flowable.util.*;
+import jnpf.model.FlowWorkListVO;
+import jnpf.model.FlowWorkModel;
+import jnpf.permission.entity.UserEntity;
+import jnpf.permission.model.user.WorkHandoverModel;
+import jnpf.util.*;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 绫荤殑鎻忚堪
+ *
+ * @author JNPF@YinMai Info. Co., Ltd
+ * @version 5.0.x
+ * @since 2024/4/18 15:30
+ */
+@Service
+@RequiredArgsConstructor
+public class OperatorServiceImpl extends SuperServiceImpl<OperatorMapper, OperatorEntity> implements OperatorService {
+
+    private final FlowAbleUrl flowAbleUrl;
+
+    private final OperatorUtil operatorUtil;
+
+    private final FlowUtil flowUtil;
+
+    private final ButtonUtil buttonUtil;
+
+    private final RedisUtil redisUtil;
+
+    private final MsgUtil msgUtil;
+
+    private final ServiceUtil serviceUtil;
+
+
+    private final TaskMapper taskMapper;
+
+    private final TemplateNodeMapper templateNodeMapper;
+
+    private final TemplateJsonMapper templateJsonMapper;
+
+    private final CandidatesMapper candidatesMapper;
+
+    private final RejectDataMapper rejectDataMapper;
+
+    private final RecordMapper recordMapper;
+
+    private final LaunchUserMapper launchUserMapper;
+
+    private final TemplateMapper templateMapper;
+
+    private final NodeRecordMapper nodeRecordMapper;
+
+    private final RevokeMapper revokeMapper;
+
+    private final EventLogMapper eventLogMapper;
+
+    @Override
+    public OperatorEntity getInfo(String id) throws WorkFlowException {
+        return this.baseMapper.getInfo(id);
+    }
+
+    @Override
+    public List<OperatorEntity> getList(String taskId) {
+        return this.baseMapper.getList(taskId);
+    }
+
+    @Override
+    public List<OperatorVo> getList(TaskPagination pagination) {
+        return flowUtil.getOperatorList(pagination);
+    }
+
+    @Override
+    public List<OperatorEntity> handleOperator(FlowModel flowModel)  throws WorkFlowException{
+        return operatorUtil.handleOperator(flowModel);
+    }
+
+    @DSTransactional
+    @Override
+    public void auditWithCheck(String id, FlowModel flowModel) throws WorkFlowException{
+        this.audit(id, flowModel);
+    }
+
+    @Override
+    public void audit(String id, FlowModel flowModel) throws WorkFlowException {
+
+        OperatorEntity operator = operatorUtil.checkOperator(id);
+        flowUtil.setFlowModel(operator.getTaskId(), flowModel);
+        TaskEntity taskEntity = flowModel.getTaskEntity();
+        String handleOpinion = flowModel.getHandleOpinion();
+        String signId = flowModel.getSignId();
+        String signImg = flowModel.getSignImg();
+        Boolean useSignNext = flowModel.getUseSignNext();
+        Map<String, Object> formData = flowModel.getFormData();
+        Map<String, NodeModel> nodes = flowModel.getNodes();
+        NodeModel global = nodes.get(NodeEnum.GLOBAL.getType());
+        NodeModel nodeModel = nodes.get(operator.getNodeCode());
+        TemplateJsonEntity jsonEntity = flowModel.getJsonEntity();
+        List<TemplateNodeEntity> nodeEntityList = flowModel.getNodeEntityList();
+        TemplateNodeEntity nodeEntity = nodeEntityList.stream()
+                .filter(e -> StringUtils.equals(e.getNodeCode(), operator.getNodeCode())).findFirst().orElse(null);
+        String formId = null != nodeEntity ? nodeEntity.getFormId() : "";
+        // 鑾峰彇娴佺▼鍙傛暟
+        operatorUtil.getGlobalParam(taskEntity, nodeModel, global, formData);
+        flowModel.setTaskEntity(taskEntity);
+        RevokeEntity revokeEntity = revokeMapper.getRevokeTask(taskEntity.getId());
+        if (CollUtil.isNotEmpty(formData) && null == revokeEntity) {
+            FlowFormDataModel model = new FlowFormDataModel();
+            model.setFormId(formId);
+            model.setId(taskEntity.getId());
+            model.setFormOperates(nodeModel.getFormOperates());
+            model.setMap(formData);
+            model.setFlowId(jsonEntity.getId());
+            serviceUtil.saveOrUpdateFormData(model);
+        }
+        formData = serviceUtil.infoData(formId, taskEntity.getId());
+        // 琛ㄥ崟
+        if (CollUtil.isNotEmpty(formData)) {
+            flowModel.setFormData(formData);
+            // 淇濆瓨琛ㄥ崟鍒扮嚎绋�
+            FlowContextHolder.addChildData(taskEntity.getId(), formId, formData, nodeModel.getFormOperates(), false);
+        }
+        this.audit(operator, flowModel);
+        if (Boolean.TRUE.equals(useSignNext)) {
+            serviceUtil.updateSign(signId, signImg);
+        }
+        serviceUtil.addCommonWordsNum(handleOpinion);
+        // 淇濆瓨琛ㄥ崟鏁版嵁
+        FlowContextHolder.deleteFormOperator();
+        serviceUtil.handleFormData(taskEntity.getFlowId(), true);
+    }
+
+    @Override
+    public void audit(OperatorEntity operator, FlowModel flowModel) throws WorkFlowException {
+        operatorUtil.audit(operator, flowModel);
+    }
+
+    @Override
+    public void sign(FlowModel flowModel) throws WorkFlowException {
+        UpdateWrapper<OperatorEntity> updateWrapper = new UpdateWrapper<>();
+        if (CollUtil.isNotEmpty(flowModel.getIds())) {
+            List<String> ids = new ArrayList<>();
+            if (flowModel.getType().equals(0)) {
+                operatorUtil.checkBatch(flowModel.getIds(), ids, false);
+                List<String> list = new ArrayList<>();
+                operatorUtil.checkCancel(ids, list);
+                // 绛炬敹
+                updateWrapper.lambda().in(OperatorEntity::getId, list)
+                        .set(OperatorEntity::getSignTime, new Date());
+            } else {
+                operatorUtil.checkBatch(flowModel.getIds(), ids, true);
+                List<String> list = new ArrayList<>();
+                operatorUtil.checkCancel(ids, list);
+                // 閫�绛�
+                updateWrapper.lambda().in(OperatorEntity::getId, list)
+                        .set(OperatorEntity::getSignTime, null);
+            }
+            this.update(updateWrapper);
+        }
+    }
+
+    @Override
+    public void startHandle(FlowModel flowModel) throws WorkFlowException {
+        List<String> ids = new ArrayList<>();
+        operatorUtil.checkBatch(flowModel.getIds(), ids, false);
+        List<String> list = new ArrayList<>();
+        operatorUtil.checkCancel(ids, list);
+
+        UpdateWrapper<OperatorEntity> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().in(OperatorEntity::getId, list)
+                .set(OperatorEntity::getStartHandleTime, new Date());
+        this.update(updateWrapper);
+    }
+
+    @Override
+    public void saveAudit(String id, FlowModel flowModel) throws WorkFlowException {
+        OperatorEntity entity = this.getById(id);
+        if (null == entity) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+        TaskEntity taskEntity = taskMapper.getInfo(entity.getTaskId());
+        operatorUtil.checkTemplateHide(taskEntity.getTemplateId());
+        flowUtil.isSuspend(taskEntity);
+        flowUtil.isCancel(taskEntity);
+        entity.setDraftData(JsonUtil.getObjectToString(flowModel.getFormData()));
+        this.updateById(entity);
+    }
+
+    @DSTransactional
+    @Override
+    public void addSign(String id, FlowModel flowModel) throws WorkFlowException {
+        OperatorEntity operator = operatorUtil.checkOperator(id);
+        List<OperatorEntity> childList = operatorUtil.getChildList(operator.getId());
+        childList = childList.stream().filter(e -> ObjectUtil.equals(e.getCompletion(), FlowNature.NORMAL) && !ObjectUtil.equals(e.getStatus(), OperatorStateEnum.FUTILITY.getCode())).collect(Collectors.toList());
+        if (CollUtil.isNotEmpty(childList)) {
+            throw new WorkFlowException(MsgCode.WF081.get());
+        }
+        AddSignModel parameter = flowModel.getAddSignParameter() != null ? flowModel.getAddSignParameter() : new AddSignModel();
+        List<String> idList = parameter.getAddSignUserIdList();
+        buttonUtil.checkAddSign(operator, parameter);
+        // 淇濆瓨閫夋嫨鍒嗘敮
+        List<String> branchList = flowModel.getBranchList();
+        if (CollUtil.isNotEmpty(branchList)) {
+            candidatesMapper.createBranch(branchList, operator);
+        }
+        if (idList.isEmpty() || operator.getStatus().equals(OperatorStateEnum.TRANSFER.getCode())
+                || operator.getStatus().equals(OperatorStateEnum.ASSIST.getCode())) {
+            throw new WorkFlowException(MsgCode.WF081.get());
+        }
+        String userId = UserProvider.getLoginUserId();
+        if (idList.contains(userId)) {
+            throw new WorkFlowException(MsgCode.WF106.get());
+        }
+        if (!ObjectUtil.equals(operator.getHandleId(), userId)) {
+            List<DelegateEntity> delegateList = flowUtil.getByToUserId(userId);
+            List<String> userIds = delegateList.stream().map(DelegateEntity::getUserId).distinct().collect(Collectors.toList());
+            List<String> filterList = userIds.stream().filter(idList::contains).collect(Collectors.toList());
+            if (CollUtil.isNotEmpty(filterList)) {
+                throw new WorkFlowException(MsgCode.WF116.get());
+            }
+        }
+        // 灏嗕箣鍓嶇殑鍔犵缁忓姙浣滃簾
+        QueryWrapper<OperatorEntity> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(OperatorEntity::getTaskId, operator.getTaskId()).eq(OperatorEntity::getParentId, id)
+                .and(i -> i.eq(OperatorEntity::getStatus, OperatorStateEnum.ADD_SIGN.getCode())
+                        .or().eq(OperatorEntity::getStatus, OperatorStateEnum.RECALL.getCode()));
+        List<OperatorEntity> list = this.list(queryWrapper);
+        if (CollUtil.isNotEmpty(list)) {
+            list.forEach(e -> e.setStatus(OperatorStateEnum.FUTILITY.getCode()));
+            super.updateBatchById(list);
+        }
+        flowUtil.setFlowModel(operator.getTaskId(), flowModel);
+        TaskEntity taskEntity = flowModel.getTaskEntity();
+        operatorUtil.checkTemplateHide(taskEntity.getTemplateId());
+        flowUtil.isSuspend(taskEntity);
+        flowUtil.isCancel(taskEntity);
+        List<TemplateNodeEntity> nodeEntityList = flowModel.getNodeEntityList();
+        Map<String, Object> formData = flowModel.getFormData();
+        // 琛ㄥ崟
+        if (CollUtil.isNotEmpty(formData)) {
+            TemplateNodeEntity nodeEntity = nodeEntityList.stream()
+                    .filter(e -> StringUtils.equals(e.getNodeCode(), operator.getNodeCode())).findFirst().orElse(null);
+            if (null != nodeEntity) {
+                NodeModel nodeModel = JsonUtil.getJsonToBean(nodeEntity.getNodeJson(), NodeModel.class);
+                // 淇濆瓨琛ㄥ崟鍒扮嚎绋�
+                FlowContextHolder.addChildData(taskEntity.getId(), nodeEntity.getFormId(), formData, nodeModel.getFormOperates(), true);
+            }
+        }
+        // 淇濆瓨鍊欓�変汉銆佸紓甯镐汉
+        flowUtil.create(flowModel, operator.getTaskId(), nodeEntityList, operator);
+        // 鏇存柊缁忓姙淇℃伅
+        operator.setHandleParameter(JsonUtil.getObjectToString(parameter));
+        if (parameter.getAddSignType().equals(FlowNature.LATER)) {
+            operator.setHandleStatus(FlowNature.AUDIT_COMPLETION);
+        }
+        operator.setCompletion(FlowNature.ACTION);
+        if (this.updateById(operator)) {
+            TemplateNodeEntity nodeEntity = nodeEntityList.stream()
+                    .filter(e -> e.getNodeCode().equals(operator.getNodeCode())).findFirst().orElse(null);
+            if (null == nodeEntity) {
+                throw new WorkFlowException(MsgCode.WF076.get());
+            }
+
+            NodeModel nodeModel = JsonUtil.getJsonToBean(nodeEntity.getNodeJson(), NodeModel.class);
+            // 鍏ㄥ眬鑺傜偣
+            TemplateNodeEntity globalEntity = nodeEntityList.stream()
+                    .filter(e -> StringUtils.equals(NodeEnum.GLOBAL.getType(), e.getNodeType())).findFirst().orElse(new TemplateNodeEntity());
+            NodeModel global = JsonUtil.getJsonToBean(globalEntity.getNodeJson(), NodeModel.class);
+            Date date = new Date();
+            // 鍚庡姞绛鹃粯璁ゅ悓鎰忕殑璁板綍
+            if (parameter.getAddSignType().equals(FlowNature.LATER)) {
+                FlowMethod flowMethod = new FlowMethod();
+                flowMethod.setType(RecordEnum.AUDIT.getCode());
+                flowMethod.setFlowModel(flowModel);
+                operator.setHandleTime(date);
+                flowMethod.setOperatorEntity(operator);
+                recordMapper.createRecord(flowMethod);
+            }
+            // 鐢熸垚鍔犵鐨勭粡鍔�
+            List<OperatorEntity> entityList = new ArrayList<>();
+            for (String handleId : idList) {
+                FlowMethod flowMethod = new FlowMethod();
+                flowMethod.setTaskEntity(taskEntity);
+                flowMethod.setNodeEntity(nodeEntity);
+                flowMethod.setNodeModel(nodeModel);
+                flowMethod.setSignFor(global.getHasSignFor());
+                flowMethod.setFlowableTaskId(operator.getNodeId());
+                OperatorEntity entity = operatorUtil.createOperatorEntity(flowMethod);
+                entity.setHandleId(handleId);
+                entity.setParentId(operator.getId());
+                entity.setStatus(OperatorStateEnum.ADD_SIGN.getCode());
+                entityList.add(entity);
+                if (ObjectUtil.equals(parameter.getCounterSign(), FlowNature.IMPROPER_APPROVER)) {
+                    break;
+                }
+            }
+            // 鍔犵璁板綍
+            FlowMethod flowMethod = new FlowMethod();
+            flowMethod.setFlowModel(flowModel);
+            operator.setHandleTime(date);
+            flowMethod.setOperatorEntity(operator);
+            Integer type = RecordEnum.ADD_SIGN.getCode();
+            flowMethod.setType(type);
+            flowMethod.setUserId(String.join(",", idList));
+            recordMapper.createRecord(flowMethod);
+            if (CollUtil.isNotEmpty(entityList)) {
+                super.saveBatch(entityList);
+                // 娑堟伅
+                FlowMsgModel flowMsgModel = new FlowMsgModel();
+                flowMsgModel.setOperatorList(entityList);
+                flowMsgModel.setNodeList(nodeEntityList);
+                flowMsgModel.setUserInfo(flowModel.getUserInfo());
+                flowMsgModel.setTaskEntity(taskEntity);
+                flowMsgModel.setNodeCode(operator.getNodeCode());
+                flowMsgModel.setFormData(FlowContextHolder.getAllData());
+                msgUtil.message(flowMsgModel);
+                operatorUtil.addOperatorList(entityList, flowModel);
+            }
+        }
+        serviceUtil.handleFormData(taskEntity.getFlowId(), false);
+    }
+
+    @Override
+    public List<CandidateUserVo> getReduceList(String id, Pagination pagination) throws WorkFlowException {
+        RecordEntity recordEntity = recordMapper.getInfo(id);
+        if (null == recordEntity) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+        OperatorEntity operator = this.getById(recordEntity.getOperatorId());
+        if (null == operator) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+        if (null == operator.getHandleParameter()) {
+            throw new WorkFlowException(MsgCode.WF082.get());
+        }
+
+        AddSignModel model = JsonUtil.getJsonToBean(operator.getHandleParameter(), AddSignModel.class);
+
+        List<CandidateUserVo> list = new ArrayList<>();
+
+        QueryWrapper<OperatorEntity> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(OperatorEntity::getTaskId, operator.getTaskId()).eq(OperatorEntity::getParentId, operator.getId())
+                .ne(OperatorEntity::getStatus, OperatorStateEnum.FUTILITY.getCode())
+                .ne(OperatorEntity::getStatus, OperatorStateEnum.ASSIST.getCode());
+        List<OperatorEntity> entityList = this.list(queryWrapper);
+        if (CollUtil.isEmpty(entityList)) {
+            return list;
+        }
+
+        // 鑾峰彇鏈鎵圭殑鍔犵缁忓姙
+        List<OperatorEntity> todoList = entityList.stream().filter(e -> null == e.getHandleStatus()
+                && ObjectUtil.equals(e.getCompletion(), FlowNature.NORMAL)).collect(Collectors.toList());
+
+        // 渚濇瀹℃壒锛岃幏鍙栨湭鍒涘缓缁忓姙鐨勪汉鍛�
+        if (ObjectUtil.equals(model.getCounterSign(), FlowNature.IMPROPER_APPROVER) && CollUtil.isNotEmpty(todoList)) {
+            List<String> idList = model.getAddSignUserIdList();
+            OperatorEntity operatorEntity = todoList.get(0);
+            int index = idList.indexOf(operatorEntity.getHandleId());
+            if (index < idList.size() - 1) {
+                for (int i = index + 1; i < idList.size(); i++) {
+                    OperatorEntity op = new OperatorEntity();
+                    op.setHandleId(idList.get(i));
+                    todoList.add(op);
+                }
+            }
+        }
+
+        return operatorUtil.getReduceUsers(todoList, pagination);
+    }
+
+    // 鍑忕
+    @Override
+    @DSTransactional
+    public void reduce(String id, FlowModel flowModel) throws WorkFlowException {
+        List<String> ids = flowModel.getIds();
+        RecordEntity recordEntity = recordMapper.getInfo(id);
+        if (null == recordEntity) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+        OperatorEntity operator = this.getById(recordEntity.getOperatorId());
+        if (null == operator) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+
+        QueryWrapper<OperatorEntity> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(OperatorEntity::getTaskId, operator.getTaskId()).eq(OperatorEntity::getParentId, operator.getId())
+                .ne(OperatorEntity::getStatus, OperatorStateEnum.FUTILITY.getCode())
+                .ne(OperatorEntity::getStatus, OperatorStateEnum.ASSIST.getCode());
+        List<OperatorEntity> list = this.list(queryWrapper);
+        if (CollUtil.isEmpty(list)) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+        if (CollUtil.isNotEmpty(ids)) {
+            String userId = ids.get(0);
+            OperatorEntity operatorEntity = list.stream()
+                    .filter(e -> ObjectUtil.equals(e.getHandleId(), userId) && e.getHandleTime() != null).findFirst().orElse(null);
+            if (null != operatorEntity && null != operatorEntity.getHandleStatus()) {
+                throw new WorkFlowException(MsgCode.WF128.get());
+            }
+        }
+        AddSignModel model = JsonUtil.getJsonToBean(operator.getHandleParameter(), AddSignModel.class);
+        boolean inTurn = ObjectUtil.equals(model.getCounterSign(), FlowNature.IMPROPER_APPROVER);
+
+        List<OperatorEntity> unApproved = list.stream()
+                .filter(e -> ObjectUtil.equals(e.getCompletion(), FlowNature.NORMAL) && e.getHandleTime() == null).collect(Collectors.toList());
+        if (inTurn) {
+            // 渚濇瀹℃壒鐨勫鐞�
+            List<String> idList = model.getAddSignUserIdList();
+            OperatorEntity entity = unApproved.stream().filter(e -> ids.contains(e.getHandleId())).findFirst().orElse(null);
+            if (null != entity) {
+                flowUtil.setFlowModel(operator.getTaskId(), flowModel);
+                Map<String, NodeModel> nodes = flowModel.getNodes();
+                NodeModel global = nodes.get(NodeEnum.GLOBAL.getType());
+
+                int index = idList.indexOf(entity.getHandleId());
+                if (index == idList.size() - 1) {
+                    throw new WorkFlowException(MsgCode.WF109.get());
+                }
+                String handleId = idList.get(index + 1);
+                // 鍒涘缓涓嬩竴涓粡鍔�
+                OperatorEntity newOperator = operatorUtil.createOperator(entity, OperatorStateEnum.ADD_SIGN.getCode(), handleId, global);
+                this.save(newOperator);
+                FlowMethod flowMethod = new FlowMethod();
+                flowMethod.setFlowModel(flowModel);
+                flowMethod.setAddSignModel(model);
+                flowMethod.setOperatorEntity(operator);
+                flowMethod.setTaskEntity(flowModel.getTaskEntity());
+                List<TemplateNodeEntity> nodeEntityList = flowModel.getNodeEntityList();
+                flowMethod.setNodeEntityList(nodeEntityList);
+                TemplateNodeEntity nodeEntity = nodeEntityList.stream().filter(e -> ObjectUtil.equals(e.getNodeCode(), operator.getNodeCode()))
+                        .findFirst().orElse(null);
+                flowMethod.setNodeEntity(nodeEntity);
+                operatorUtil.improperApproverMessage(flowMethod, newOperator);
+                // 鍒犻櫎闇�瑕佸噺绛剧殑缁忓姙
+                this.removeById(entity);
+            }
+            // 鏇存柊handleParameter
+            idList.removeAll(ids);
+            model.setAddSignUserIdList(idList);
+            operator.setHandleParameter(JsonUtil.getObjectToString(model));
+            this.updateById(operator);
+        } else {
+            if (unApproved.size() == 1) {
+                throw new WorkFlowException(MsgCode.WF109.get());
+            }
+        }
+        // 璁板綍
+        FlowMethod flowMethod = new FlowMethod();
+        flowMethod.setFlowModel(flowModel);
+        flowMethod.setOperatorEntity(operator);
+        Integer type = RecordEnum.SUBTRACT_SIGN.getCode();
+        flowMethod.setType(type);
+        flowMethod.setUserId(String.join(",", ids));
+        recordMapper.createRecord(flowMethod);
+
+        if (inTurn) {
+            return;
+        }
+
+        List<String> handleIds = list.stream().map(OperatorEntity::getHandleId).collect(Collectors.toList());
+
+        handleIds.removeAll(ids);
+
+        if (handleIds.isEmpty()) {
+            // 鍏ㄩ儴鍑忕
+            this.removeByIds(list);
+            // 杩樺師缁忓姙
+            UpdateWrapper<OperatorEntity> updateWrapper = new UpdateWrapper<>();
+            updateWrapper.lambda().eq(OperatorEntity::getId, operator.getId())
+                    .set(OperatorEntity::getCompletion, FlowNature.NORMAL)
+                    .set(OperatorEntity::getHandleParameter, null);
+            this.update(updateWrapper);
+        } else {
+            List<OperatorEntity> collect = list.stream().filter(t -> ids.contains(t.getHandleId())).collect(Collectors.toList());
+            this.removeByIds(collect);
+        }
+    }
+
+    // 鑾峰彇鍙��鍥炵殑鑺傜偣
+    @Override
+    public List<BackNodeModel> getFallbacks(String id) throws WorkFlowException {
+        OperatorEntity operator = this.getInfo(id);
+        if (null == operator) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+        TaskEntity taskEntity = taskMapper.getInfo(operator.getTaskId());
+        if (null == taskEntity) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+        TemplateJsonEntity jsonEntity = templateJsonMapper.getInfo(taskEntity.getFlowId());
+        List<TemplateNodeEntity> nodeEntityList = templateNodeMapper.getList(taskEntity.getFlowId());
+        Map<String, NodeModel> nodes = new HashMap<>();
+        for (TemplateNodeEntity nodeEntity : nodeEntityList) {
+            nodes.put(nodeEntity.getNodeCode(), JsonUtil.getJsonToBean(nodeEntity.getNodeJson(), NodeModel.class));
+        }
+
+        NodeModel nodeModel = nodes.get(operator.getNodeCode());
+        List<BackNodeModel> list = new ArrayList<>();
+
+        // 鑾峰彇缁忓姙杩囨护
+        QueryWrapper<OperatorEntity> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(OperatorEntity::getTaskId, operator.getTaskId());
+        List<OperatorEntity> operatorEntityList = this.list(queryWrapper);
+        List<String> nodeList = operatorEntityList.stream().map(OperatorEntity::getNodeCode).distinct().collect(Collectors.toList());
+
+        TemplateNodeEntity start = nodeEntityList.stream().filter(e -> e.getNodeType().equals(NodeEnum.START.getType())).findFirst().orElse(null);
+        if (null == start) {
+            throw new WorkFlowException(MsgCode.WF076.get());
+        }
+        NodeModel startModel = JsonUtil.getJsonToBean(start.getNodeJson(), NodeModel.class);
+        if (ObjectUtil.equals(nodeModel.getBackNodeCode(), FlowNature.START)) {
+            BackNodeModel model = new BackNodeModel();
+            model.setNodeName(startModel.getNodeName());
+            model.setNodeCode(startModel.getNodeId());
+            list.add(model);
+        } else if (ObjectUtil.equals(nodeModel.getBackNodeCode(), FlowNature.UP)) {
+            List<String> resList = new ArrayList<>();
+            List<String> nodeCodeList = new ArrayList<>();
+            flowUtil.prevNode(jsonEntity.getFlowableId(), operator.getNodeCode(), nodeEntityList, nodeCodeList);
+
+            List<String> nodeCodes = this.baseMapper.getList(taskEntity.getId()).stream()
+                    .filter(e -> !e.getStatus().equals(OperatorStateEnum.FUTILITY.getCode()))
+                    .map(OperatorEntity::getNodeCode).distinct().collect(Collectors.toList());
+            nodeCodes.add(start.getNodeCode());
+
+            for (String nodeCode : nodeCodeList) {
+                NodeModel node = nodes.get(nodeCode);
+                // 閫�鍥炶妭鐐瑰寘鍚瓙娴佺▼
+                if (null != node) {
+                    if (NodeEnum.SUB_FLOW.getType().equals(node.getType())) {
+                        throw new WorkFlowException(MsgCode.WF046.get());
+                    }
+                    if (NodeEnum.OUTSIDE.getType().equals(node.getType())) {
+                        throw new WorkFlowException(MsgCode.WF154.get());
+                    }
+                }
+
+                if (!nodeCodes.contains(nodeCode)) {
+                    continue;
+                }
+                resList.add(nodeCode);
+            }
+
+            if (resList.isEmpty()) {
+                resList.addAll(nodeCodeList);
+            }
+
+            BackNodeModel model = new BackNodeModel();
+            model.setNodeName("涓婄骇瀹℃壒鑺傜偣");
+            model.setNodeCode(String.join(",", resList));
+            list.add(model);
+        } else if (ObjectUtil.equals(nodeModel.getBackNodeCode(), FlowNature.REJECT)) {
+            List<String> fallbacks = flowAbleUrl.getFallbacks(operator.getNodeId());
+            nodeList.add(start.getNodeCode());
+            fallbacks = fallbacks.stream().filter(nodeList::contains).collect(Collectors.toList());
+            // 鑾峰彇缁忓姙杩涜杩囨护
+            List<String> nodeCodeList = this.baseMapper.getList(taskEntity.getId()).stream()
+                    .filter(e -> !e.getStatus().equals(OperatorStateEnum.FUTILITY.getCode()))
+                    .map(OperatorEntity::getNodeCode).distinct().collect(Collectors.toList());
+            nodeCodeList.add(start.getNodeCode());
+
+            for (String nodeCode : fallbacks) {
+                // 杩囨护娌℃湁璧拌繃鐨勭偣
+                if (!nodeCodeList.contains(nodeCode) || ObjectUtil.equals(nodeCode, operator.getNodeCode())) {
+                    continue;
+                }
+                NodeModel node = nodes.get(nodeCode);
+                BackNodeModel model = new BackNodeModel();
+                model.setNodeName(node.getNodeName());
+                model.setNodeCode(node.getNodeId());
+                list.add(model);
+            }
+        } else {
+            NodeModel optional = nodes.get(nodeModel.getBackNodeCode());
+            if (null != optional) {
+                BackNodeModel model = new BackNodeModel();
+                model.setNodeName(optional.getNodeName());
+                model.setNodeCode(nodeModel.getBackNodeCode());
+                list.add(model);
+            }
+        }
+        return list;
+    }
+
+    // 閫�鍥�
+    @DSTransactional
+    @Override
+    public void back(String id, FlowModel flowModel) throws WorkFlowException {
+        operatorUtil.back(id, flowModel);
+    }
+
+    // 鎾ゅ洖
+    @DSTransactional
+    @Override
+    public void recall(String id, FlowModel flowModel) throws WorkFlowException {
+        RecordEntity recordEntity = flowModel.getRecordEntity();
+        OperatorEntity operator = flowModel.getOperatorEntity();
+
+        flowUtil.setFlowModel(operator.getTaskId(), flowModel);
+        TaskEntity taskEntity = flowModel.getTaskEntity();
+        operatorUtil.checkTemplateHide(taskEntity.getTemplateId());
+        flowUtil.isSuspend(taskEntity);
+        flowUtil.isCancel(taskEntity);
+        flowUtil.isTrigger(taskEntity);
+        List<TemplateNodeEntity> nodeEntityList = flowModel.getNodeEntityList();
+        if (CollUtil.isEmpty(nodeEntityList)) {
+            throw new WorkFlowException(MsgCode.WF076.get());
+        }
+        TemplateNodeEntity nodeEntity = nodeEntityList.stream().filter(e -> e.getNodeCode().equals(operator.getNodeCode())).findFirst().orElse(null);
+        if (null == nodeEntity) {
+            throw new WorkFlowException(MsgCode.WF076.get());
+        }
+
+        flowModel.setNodeEntity(nodeEntity);
+        flowModel.setRecordEntity(recordEntity);
+        flowModel.setFlag(FlowNature.APPROVAL_FLAG);
+        flowModel.setIsException(true);
+        if (!buttonUtil.checkRecall(flowModel)) {
+            throw new WorkFlowException(MsgCode.WF077.get());
+        }
+        operatorUtil.addTask(ImmutableList.of(taskEntity.getId()));
+        List<String> nextCodes = flowModel.getNextCodes();
+        if (CollUtil.isNotEmpty(nextCodes)) {
+            // 鍒犻櫎澶栭儴鑺傜偣
+            eventLogMapper.delete(taskEntity.getId(), nextCodes);
+            // 鍒犻櫎瀛愭祦绋�
+            operatorUtil.deleteSubflow(taskEntity.getId(), nextCodes);
+        }
+
+        // 鏇存柊璁板綍鐘舵��
+        recordEntity.setStatus(FlowNature.INVALID);
+        recordMapper.updateById(recordEntity);
+
+        // 璁板綍
+        FlowMethod flowMethod = new FlowMethod();
+        flowMethod.setType(RecordEnum.RECALL.getCode());
+        flowMethod.setFlowModel(flowModel);
+        operator.setHandleTime(null);
+        flowMethod.setOperatorEntity(operator);
+        recordMapper.createRecord(flowMethod);
+
+        List<String> currentNodes = new ArrayList<>();
+        String currentNodeCode = taskEntity.getCurrentNodeCode();
+        if (StringUtils.isNotBlank(currentNodeCode)) {
+            currentNodes = Arrays.stream(currentNodeCode.split(",")).collect(Collectors.toList());
+        }
+        if (currentNodes.contains(operator.getNodeCode())) {
+            // 鍔犵 鎾ゅ洖鐨勫鐞�
+            if (!ObjectUtil.equals(operator.getParentId(), FlowNature.PARENT_ID)) {
+                OperatorEntity parentEntity = this.getById(operator.getParentId());
+                if (null != parentEntity && ObjectUtil.equals(parentEntity.getCompletion(), FlowNature.NORMAL)) {
+                    parentEntity.setCompletion(FlowNature.ACTION);
+                    this.updateById(parentEntity);
+                }
+
+            }
+            // 褰撳墠鑺傜偣宸插鎵圭殑浜� 鎾ゅ洖鏃犻渶璺宠浆
+            UpdateWrapper<OperatorEntity> wrapper = new UpdateWrapper<>();
+            wrapper.lambda().eq(OperatorEntity::getId, operator.getId())
+                    .set(OperatorEntity::getCompletion, FlowNature.NORMAL)
+                    .set(OperatorEntity::getStatus, OperatorStateEnum.RECALL.getCode())
+                    .set(OperatorEntity::getHandleStatus, null)
+                    .set(OperatorEntity::getCreatorTime, new Date())
+                    .set(OperatorEntity::getHandleTime, null);
+            this.update(wrapper);
+
+            //鍒犻櫎鍏朵粬缁忓姙
+            if (ObjectUtil.equals(operator.getParentId(), FlowNature.PARENT_ID)) {
+                // 渚濇瀹℃壒
+                if (StringUtils.isNotBlank(operator.getHandleAll())) {
+                    operatorUtil.deleteInTurnOperator(operator, operator.getHandleId());
+                }
+                //閫愮骇瀹℃壒
+                operatorUtil.deleteStepOperator(operator);
+            }
+            return;
+        }
+        // 鑺傜偣璁板綍
+        NodeRecordModel nodeRecordModel = new NodeRecordModel();
+        nodeRecordModel.setTaskId(operator.getTaskId());
+        nodeRecordModel.setNodeId(operator.getNodeId());
+        nodeRecordModel.setNodeCode(operator.getNodeCode());
+        nodeRecordModel.setNodeName(operator.getNodeName());
+        nodeRecordModel.setNodeStatus(NodeStateEnum.RECALL.getCode());
+        nodeRecordMapper.update(nodeRecordModel);
+
+        // 鍒犻櫎璇ヨ妭鐐逛笅涓�绾х殑缁忓姙
+        if (CollUtil.isNotEmpty(nextCodes)) {
+            QueryWrapper<OperatorEntity> wrapper = new QueryWrapper<>();
+            wrapper.lambda().eq(OperatorEntity::getTaskId, taskEntity.getId())
+                    .in(OperatorEntity::getNodeCode, nextCodes);
+            List<OperatorEntity> list = this.list(wrapper);
+            if (CollUtil.isNotEmpty(list)) {
+                this.remove(wrapper);
+                // 鍒犻櫎瓒呮椂鐩稿叧
+                FlowJobUtil.deleteByOperatorId(operator.getId(), redisUtil);
+                for (OperatorEntity operatorEntity : list) {
+                    FlowJobUtil.deleteByOperatorId(operatorEntity.getId(), redisUtil);
+                }
+            }
+
+            //閫愮骇淇℃伅鍒犻櫎
+            QueryWrapper<LaunchUserEntity> launchWrapper = new QueryWrapper<>();
+            launchWrapper.lambda().eq(LaunchUserEntity::getType, FlowNature.STEP_INITIATION)
+                    .eq(LaunchUserEntity::getTaskId, taskEntity.getId())
+                    .in(LaunchUserEntity::getNodeCode, nextCodes);
+            launchUserMapper.delete(launchWrapper);
+        }
+
+        // 鎾ゅ洖璺宠浆
+        JumpFo fo = new JumpFo();
+        fo.setInstanceId(taskEntity.getInstanceId());
+        List<String> executeNodes = this.getNodeCodeByGroupId(nextCodes, flowModel.getNodes());
+        if (CollUtil.isNotEmpty(executeNodes)) {
+            nextCodes.addAll(executeNodes);
+        }
+        fo.setSource(nextCodes);
+        List<String> targetList = new ArrayList<>();
+        targetList.add(nodeEntity.getNodeCode());
+        fo.setTarget(targetList);
+        flowAbleUrl.jump(fo);
+
+        List<FlowableTaskModel> taskModelList = flowAbleUrl.getCurrentTask(taskEntity.getInstanceId());
+        FlowableTaskModel model = taskModelList.stream().filter(e -> e.getTaskKey().equals(operator.getNodeCode())).findFirst().orElse(null);
+        String nodeId = null;
+        if (null != model) {
+            nodeId = model.getTaskId();
+        }
+
+        // 杩樺師缁忓姙锛堝叾瀹冨凡瀹℃壒銆佷綔搴熺殑缁忓姙鏃犻渶杩樺師锛�
+        UpdateWrapper<OperatorEntity> wrapper = new UpdateWrapper<>();
+        wrapper.lambda().eq(OperatorEntity::getId, operator.getId())
+                .set(OperatorEntity::getCompletion, FlowNature.NORMAL)
+                .set(OperatorEntity::getNodeId, nodeId)
+                .set(OperatorEntity::getStatus, OperatorStateEnum.RECALL.getCode())
+                .set(OperatorEntity::getHandleStatus, null)
+                .set(OperatorEntity::getCreatorTime, new Date())
+                .set(OperatorEntity::getHandleTime, null);
+        this.update(wrapper);
+        // 鏇存柊鑺傜偣缁忓姙鐨刵odeId
+        wrapper.clear();
+        wrapper.lambda().eq(OperatorEntity::getTaskId, operator.getTaskId())
+                .eq(OperatorEntity::getNodeCode, operator.getNodeCode()).eq(OperatorEntity::getParentId, operator.getParentId())
+                .set(OperatorEntity::getNodeId, nodeId);
+        this.update(wrapper);
+
+        List<String> userIds = new ArrayList<>();
+        userIds.add(operator.getHandleId());
+        // 鑾峰彇鏈鎵硅繃鐨勫鍘�
+        operatorUtil.recallRestore(operator, nodeId, userIds);
+
+        // 鍒犻櫎璇ヨ妭鐐逛笅涓�绾х殑鍊欓�変汉锛屾湁淇敼鎿嶄綔鐨�
+        candidatesMapper.delete(taskEntity.getId(), nextCodes, userIds);
+
+        // 瓒呮椂
+        List<OperatorEntity> entityList = new ArrayList<>();
+        OperatorEntity entity = this.getById(operator.getId());
+        entityList.add(entity);
+        operatorUtil.addOperatorList(entityList, flowModel);
+
+        // 鑾峰彇褰撳墠鑺傜偣淇℃伅
+        operatorUtil.updateCurrentNode(taskModelList, flowModel.getNodes(), taskEntity);
+    }
+
+    // 鏍规嵁缁処D鑾峰彇鍚岀粍鐨勬墽琛岃妭鐐�
+    public List<String> getNodeCodeByGroupId(List<String> nextCodes, Map<String, NodeModel> nodes) {
+        List<String> list = new ArrayList<>();
+        if (CollUtil.isEmpty(nextCodes) || CollUtil.isEmpty(nodes)) {
+            return list;
+        }
+        for (String nextCode : nextCodes) {
+            NodeModel nodeModel = nodes.get(nextCode);
+            if (null != nodeModel && ObjectUtil.equals(nodeModel.getType(), NodeEnum.TRIGGER.getType())) {
+                String groupId = nodeModel.getGroupId();
+                for (Map.Entry<String, NodeModel> stringNodeModelEntry : nodes.entrySet()) {
+                    String key = stringNodeModelEntry.getKey();
+                    NodeModel model = nodes.get(key);
+                    if (null != model && ObjectUtil.equals(model.getGroupId(), groupId)) {
+                        list.add(key);
+                    }
+                }
+
+            }
+        }
+        return list;
+    }
+
+    // 杞
+    @Override
+    public void transfer(String id, FlowModel flowModel) throws WorkFlowException {
+        String transferUserId = flowModel.getHandleIds();
+        OperatorEntity operator = operatorUtil.checkOperator(id);
+        boolean isProcessing = ObjectUtil.equals(operator.getIsProcessing(), FlowNature.PROCESSING);
+        if (StringUtils.isBlank(transferUserId) || operator.getStatus().equals(OperatorStateEnum.TRANSFER.getCode())
+                || operator.getStatus().equals(OperatorStateEnum.ADD_SIGN.getCode())) {
+            throw new WorkFlowException(isProcessing ? MsgCode.WF151.get() : MsgCode.WF084.get());
+        }
+        String userId = flowModel.getUserInfo() == null ? UserProvider.getLoginUserId() : flowModel.getUserInfo().getUserId();
+        if (StringUtils.equals(transferUserId, userId)) {
+            throw new WorkFlowException(isProcessing ? MsgCode.WF149.get() : MsgCode.WF107.get());
+        }
+        if (!ObjectUtil.equals(operator.getHandleId(), userId)) {
+            List<DelegateEntity> delegateList = flowUtil.getByToUserId(userId);
+            List<String> userIds = delegateList.stream().map(DelegateEntity::getUserId).distinct().collect(Collectors.toList());
+            if (userIds.contains(transferUserId)) {
+                throw new WorkFlowException(isProcessing ? MsgCode.WF150.get() : MsgCode.WF117.get());
+            }
+        }
+
+        flowUtil.setFlowModel(operator.getTaskId(), flowModel);
+        TaskEntity taskEntity = flowModel.getTaskEntity();
+        operatorUtil.checkTemplateHide(taskEntity.getTemplateId());
+        flowUtil.isSuspend(taskEntity);
+        flowUtil.isCancel(taskEntity);
+
+        List<TemplateNodeEntity> nodeEntityList = flowModel.getNodeEntityList();
+
+        TemplateNodeEntity globalEntity = nodeEntityList.stream()
+                .filter(e -> StringUtils.equals(NodeEnum.GLOBAL.getType(), e.getNodeType())).findFirst().orElse(new TemplateNodeEntity());
+        NodeModel global = JsonUtil.getJsonToBean(globalEntity.getNodeJson(), NodeModel.class);
+
+        // 璁板綍
+        FlowMethod flowMethod = new FlowMethod();
+        flowMethod.setType(ObjectUtil.equals(operator.getIsProcessing(), FlowNature.NOT_PROCESSING) ? RecordEnum.TRANSFER.getCode() : RecordEnum.TRANSFER_PROCESSING.getCode());
+        flowMethod.setFlowModel(flowModel);
+        flowMethod.setOperatorEntity(operator);
+        List<String> userIds = new ArrayList<>();
+        userIds.add(transferUserId);
+        flowMethod.setUserId(String.join(",", userIds));
+        recordMapper.createRecord(flowMethod);
+
+        // 鏇存柊缁忓姙
+        UpdateWrapper<OperatorEntity> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(OperatorEntity::getId, operator.getId())
+                .set(OperatorEntity::getHandleId, transferUserId)
+                .set(OperatorEntity::getCreatorTime, new Date())
+                .set(OperatorEntity::getDraftData, null)
+                .set(OperatorEntity::getStatus, OperatorStateEnum.TRANSFER.getCode());
+        Boolean flowTodo = serviceUtil.getFlowTodo();
+        if (Boolean.FALSE.equals(flowTodo)) {
+            updateWrapper.lambda().set(OperatorEntity::getStartHandleTime, null);
+            Boolean flowSign = serviceUtil.getFlowSign();
+            if (Boolean.TRUE.equals(!flowSign && global != null) && Boolean.TRUE.equals(global.getHasSignFor())) {
+                updateWrapper.lambda().set(OperatorEntity::getSignTime, null);
+            }
+
+        }
+        this.update(updateWrapper);
+
+        // 娑堟伅
+        FlowMsgModel flowMsgModel = new FlowMsgModel();
+        List<OperatorEntity> list = new ArrayList<>();
+        operator = this.getById(operator.getId());
+        list.add(operator);
+        flowMsgModel.setOperatorList(list);
+        flowMsgModel.setNodeList(nodeEntityList);
+        flowMsgModel.setUserInfo(flowModel.getUserInfo());
+        flowMsgModel.setTaskEntity(taskEntity);
+        flowMsgModel.setNodeCode(operator.getNodeCode());
+        flowMsgModel.setFormData(FlowContextHolder.getAllData());
+        flowMsgModel.setTransfer(true);
+        msgUtil.message(flowMsgModel);
+
+        // 鍒犻櫎瓒呮椂鐩稿叧
+        FlowJobUtil.deleteByOperatorId(operator.getId(), redisUtil);
+        operatorUtil.addOperatorList(list, flowModel);
+    }
+
+    // 鍗忓姙锛� 鏂板缁忓姙锛岃妭鐐归�氳繃鍚庯紝鍒犻櫎鍗忓姙
+    @Override
+    public void assist(String id, FlowModel flowModel) throws WorkFlowException {
+        String handleIds = flowModel.getHandleIds();
+        List<String> ids = Arrays.stream(handleIds.split(",")).collect(Collectors.toList());
+        String userId = UserProvider.getLoginUserId();
+        if (ids.contains(userId)) {
+            throw new WorkFlowException(MsgCode.WF108.get());
+        }
+
+        OperatorEntity operator = operatorUtil.checkOperator(id);
+
+        if (!ObjectUtil.equals(operator.getHandleId(), userId)) {
+            List<DelegateEntity> delegateList = flowUtil.getByToUserId(userId);
+            List<String> userIds = delegateList.stream().map(DelegateEntity::getUserId).distinct().collect(Collectors.toList());
+            List<String> filterList = userIds.stream().filter(ids::contains).collect(Collectors.toList());
+            if (CollUtil.isNotEmpty(filterList)) {
+                throw new WorkFlowException(MsgCode.WF118.get());
+            }
+        }
+
+
+        flowUtil.setFlowModel(operator.getTaskId(), flowModel);
+        TaskEntity taskEntity = flowModel.getTaskEntity();
+        operatorUtil.checkTemplateHide(taskEntity.getTemplateId());
+        flowUtil.isSuspend(taskEntity);
+        flowUtil.isCancel(taskEntity);
+
+        Map<String, NodeModel> nodes = flowModel.getNodes();
+        NodeModel global = nodes.get(NodeEnum.GLOBAL.getType());
+        boolean signFor = false;
+        if (null != global) {
+            signFor = global.getHasSignFor();
+        }
+        List<UserEntity> userList = serviceUtil.getUserName(ids);
+        if (CollUtil.isNotEmpty(userList)) {
+            Boolean flowTodo = serviceUtil.getFlowTodo();
+            Boolean flowSign = serviceUtil.getFlowSign();
+            List<OperatorEntity> list = new ArrayList<>();
+            NodeModel nodeModel = nodes.get(operator.getNodeCode());
+            int processing = ObjectUtil.isNotEmpty(nodeModel) && ObjectUtil.equals(nodeModel.getType(), NodeEnum.PROCESSING.getType()) ? FlowNature.PROCESSING : FlowNature.NOT_PROCESSING;
+            for (UserEntity user : userList) {
+                OperatorEntity operatorEntity = new OperatorEntity();
+                operatorEntity.setParentId(operator.getId());
+                operatorEntity.setNodeName(operator.getNodeName());
+                operatorEntity.setNodeCode(operator.getNodeCode());
+                operatorEntity.setTaskId(operator.getTaskId());
+                operatorEntity.setNodeId(operator.getNodeId());
+                operatorEntity.setEngineType(operator.getEngineType());
+                operatorEntity.setHandleId(user.getId());
+                operatorEntity.setStatus(OperatorStateEnum.ASSIST.getCode());
+                operatorEntity.setIsProcessing(processing);
+                operatorEntity.setCompletion(FlowNature.NORMAL);
+                if (Boolean.TRUE.equals(flowTodo)) {
+                    operatorEntity.setSignTime(new Date());
+                    operatorEntity.setStartHandleTime(new Date());
+                } else {
+                    if (Boolean.TRUE.equals(flowSign)) {
+                        operatorEntity.setSignTime(new Date());
+                    } else {
+                        if (!signFor) {
+                            operatorEntity.setSignTime(new Date());
+                        }
+                    }
+                }
+                list.add(operatorEntity);
+            }
+            super.saveBatch(list);
+
+            // 璁板綍
+            FlowMethod flowMethod = new FlowMethod();
+            flowMethod.setType(RecordEnum.ASSIST.getCode());
+            flowMethod.setFlowModel(flowModel);
+            flowMethod.setOperatorEntity(operator);
+            List<String> userIds = userList.stream().map(UserEntity::getId).collect(Collectors.toList());
+            flowMethod.setUserId(String.join(",", userIds));
+            recordMapper.createRecord(flowMethod);
+
+            // 娑堟伅
+            FlowMsgModel flowMsgModel = new FlowMsgModel();
+            flowMsgModel.setOperatorList(list);
+            flowMsgModel.setNodeList(flowModel.getNodeEntityList());
+            flowMsgModel.setUserInfo(flowModel.getUserInfo());
+            flowMsgModel.setTaskEntity(taskEntity);
+            flowMsgModel.setNodeCode(operator.getNodeCode());
+            msgUtil.message(flowMsgModel);
+        }
+    }
+
+    // 鍗忓姙淇濆瓨锛屼粎淇濆瓨琛ㄥ崟鏁版嵁
+    @DSTransactional
+    @Override
+    public void assistSave(String id, FlowModel flowModel) throws WorkFlowException {
+        OperatorEntity operator = operatorUtil.checkOperator(id);
+        flowUtil.setFlowModel(operator.getTaskId(), flowModel);
+        TaskEntity taskEntity = flowModel.getTaskEntity();
+        operatorUtil.checkTemplateHide(taskEntity.getTemplateId());
+        flowUtil.isSuspend(taskEntity);
+        flowUtil.isCancel(taskEntity);
+        Map<String, Object> formData = flowModel.getFormData();
+        List<TemplateNodeEntity> nodeEntityList = flowModel.getNodeEntityList();
+        TemplateNodeEntity nodeEntity = nodeEntityList.stream().filter(e -> e.getNodeCode().equals(operator.getNodeCode())).findFirst().orElse(null);
+        if (null == nodeEntity) {
+            throw new WorkFlowException(MsgCode.WF076.get());
+        }
+        NodeModel nodeModel = JsonUtil.getJsonToBean(nodeEntity.getNodeJson(), NodeModel.class);
+
+        FlowContextHolder.addChildData(operator.getTaskId(), nodeEntity.getFormId(), formData, nodeModel.getFormOperates(), true);
+        serviceUtil.handleFormData(taskEntity.getFlowId(), false);
+    }
+
+    @Override
+    public List<FlowBatchModel> batchFlowSelector() {
+        List<OperatorEntity> batchList = this.getBatchList();
+        List<String> taskIdList = batchList.stream().map(OperatorEntity::getTaskId).collect(Collectors.toList());
+
+        List<TaskEntity> taskList = taskMapper.getOrderStaList(taskIdList);
+
+        Map<String, List<TaskEntity>> flowList = taskList.stream()
+                .collect(Collectors.groupingBy(TaskEntity::getTemplateId));
+
+        List<FlowBatchModel> batchFlowList = new ArrayList<>();
+        for (Map.Entry<String, List<TaskEntity>> entry : flowList.entrySet()) {
+            String key = entry.getKey();
+            List<TaskEntity> flowTaskList = flowList.get(key);
+            if (CollUtil.isNotEmpty(flowTaskList)) {
+                List<String> taskIds = flowTaskList.stream().map(TaskEntity::getId).collect(Collectors.toList());
+                List<String> flowIds = flowTaskList.stream().map(TaskEntity::getFlowId).collect(Collectors.toList());
+                List<Integer> flowType = flowTaskList.stream().map(TaskEntity::getFlowType).collect(Collectors.toList());
+                if (flowType.contains(FlowNature.FREE)) {
+                    continue;
+                }
+                List<TemplateJsonEntity> jsonList = templateJsonMapper.selectByIds(flowIds);
+
+                String fullName = flowTaskList.stream().map(TaskEntity::getFlowName).distinct().collect(Collectors.joining(","));
+                String flowId = jsonList.stream().map(TemplateJsonEntity::getTemplateId).distinct().collect(Collectors.joining(","));
+                long count = batchList.stream().filter(e -> taskIds.contains(e.getTaskId())).count();
+
+                FlowBatchModel model = new FlowBatchModel();
+                model.setNum(count);
+                model.setId(flowId);
+                model.setFullName(fullName + "(" + count + ")");
+                batchFlowList.add(model);
+            }
+        }
+        batchFlowList = batchFlowList.stream().sorted(Comparator.comparing(FlowBatchModel::getNum).reversed()).collect(Collectors.toList());
+        return batchFlowList;
+    }
+
+    public List<OperatorEntity> getBatchList() {
+        String userId = UserProvider.getLoginUserId();
+        List<String> userList = flowUtil.getByToUserId(userId).stream()
+                .map(DelegateEntity::getCreatorUserId).collect(Collectors.toList());
+        userList.add(userId);
+        QueryWrapper<OperatorEntity> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().in(OperatorEntity::getHandleId, userList)
+                .eq(OperatorEntity::getCompletion, FlowNature.NORMAL)
+                .ne(OperatorEntity::getStatus, OperatorStateEnum.FUTILITY.getCode())
+                .ne(OperatorEntity::getStatus, OperatorStateEnum.ASSIST.getCode())
+                .isNotNull(OperatorEntity::getSignTime).isNotNull(OperatorEntity::getStartHandleTime)
+                .isNull(OperatorEntity::getHandleStatus)
+                .select(OperatorEntity::getTaskId);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public List<FlowBatchModel> batchVersionSelector(String templateId) {
+        List<String> taskIdList = this.getBatchList().stream().map(OperatorEntity::getTaskId).collect(Collectors.toList());
+        List<TaskEntity> taskList = taskMapper.getOrderStaList(taskIdList);
+        List<String> flowIdList = taskList.stream().filter(e -> e.getTemplateId().equals(templateId))
+                .map(TaskEntity::getFlowId).collect(Collectors.toList());
+        List<FlowBatchModel> batchFlowList = new ArrayList<>();
+        if (CollUtil.isEmpty(flowIdList)) {
+            return batchFlowList;
+        }
+
+        List<TemplateJsonEntity> jsonList = templateJsonMapper.selectByIds(flowIdList);
+        List<TemplateEntity> templateList = templateMapper.selectByIds(jsonList.stream().map(TemplateJsonEntity::getTemplateId).distinct().collect(Collectors.toList()));
+
+        for (TemplateJsonEntity jsonEntity : jsonList) {
+            FlowBatchModel model = JsonUtil.getJsonToBean(jsonEntity, FlowBatchModel.class);
+            TemplateEntity template = templateList.stream().filter(e -> e.getId().equals(jsonEntity.getTemplateId())).findFirst().orElse(null);
+            if (null != template) {
+                model.setFullName(template.getFullName() + "(v" + jsonEntity.getVersion() + ")");
+                batchFlowList.add(model);
+            }
+        }
+        return batchFlowList;
+    }
+
+    @Override
+    public List<FlowBatchModel> batchNodeSelector(String flowId) {
+        List<TemplateNodeEntity> nodeList = templateNodeMapper.getList(flowId);
+        List<FlowBatchModel> list = new ArrayList<>();
+        for (TemplateNodeEntity nodeEntity : nodeList) {
+            if (NodeEnum.APPROVER.getType().equals(nodeEntity.getNodeType())) {
+                NodeModel nodeModel = JsonUtil.getJsonToBean(nodeEntity.getNodeJson(), NodeModel.class);
+                FlowBatchModel model = new FlowBatchModel();
+                model.setId(nodeModel.getNodeId());
+                model.setFullName(nodeModel.getNodeName());
+                list.add(model);
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public Map<String, Object> batchNode(FlowModel flowModel) throws WorkFlowException {
+        String flowId = flowModel.getFlowId();
+        String nodeCode = flowModel.getNodeCode();
+
+        TemplateJsonInfoVO jsonInfoVO = flowUtil.getInfoVo(flowId);
+        Map<String, Map<String, Object>> flowNodes = jsonInfoVO.getFlowNodes();
+
+        return flowNodes.get(nodeCode);
+    }
+
+    // 鎵归噺鑾峰彇鍊欓�変汉锛屼笉鍏佽鏉′欢 1.绾夸笂瀛樺湪鏉′欢  2.涓嬩竴绾ц妭鐐瑰瓨鍦ㄥ�欓�変汉锛�20240711 绗簩椤瑰幓闄わ級
+    @Override
+    public CandidateCheckVo batchCandidates(String flowId, String operatorId, Integer batchType) throws WorkFlowException {
+        TemplateJsonEntity jsonEntity = templateJsonMapper.getInfo(flowId);
+        OperatorEntity operator = this.getById(operatorId);
+        if (null == operator) {
+            throw new WorkFlowException(MsgCode.FA001.get());
+        }
+
+        List<TemplateNodeEntity> nodeEntityList = templateNodeMapper.getList(flowId);
+        Map<String, NodeModel> nodes = new HashMap<>();
+        for (TemplateNodeEntity nodeEntity : nodeEntityList) {
+            nodes.put(nodeEntity.getNodeCode(), JsonUtil.getJsonToBean(nodeEntity.getNodeJson(), NodeModel.class));
+        }
+
+        NodeModel nodeModel = nodes.get(operator.getNodeCode());
+        if (flowUtil.checkBranch(nodeModel)) {
+            throw new WorkFlowException("涓嬩竴鑺傜偣涓洪�夋嫨鍒嗘敮鏃犳硶鎵归噺瀹℃壒");
+        }
+
+        NodeModel global = nodes.get(NodeEnum.GLOBAL.getType());
+        if (ObjectUtil.equals(batchType, 1) && Boolean.TRUE.equals(!global.getHasContinueAfterReject())) {
+            return new CandidateCheckVo();
+        }
+        // 鑾峰彇鑺傜偣鐨勫嚭绾�
+        OutgoingFlowsFo flowsFo = new OutgoingFlowsFo();
+        flowsFo.setDeploymentId(jsonEntity.getFlowableId());
+        flowsFo.setTaskKey(operator.getNodeCode());
+        List<String> outgoingFlows = flowAbleUrl.getOutgoingFlows(flowsFo);
+        // 绾夸笂瀛樺湪鏉′欢
+        for (String outgoingFlow : outgoingFlows) {
+            NodeModel flowNode = nodes.get(outgoingFlow);
+            if (flowNode != null && CollUtil.isNotEmpty(flowNode.getConditions())) {
+                throw new WorkFlowException(MsgCode.WF119.get());
+            }
+        }
+
+        CandidateCheckFo fo = new CandidateCheckFo();
+        fo.setFlowId(flowId);
+        fo.setHandleStatus(ObjectUtil.equals(batchType, 1) ? FlowNature.REJECT_COMPLETION : FlowNature.AUDIT_COMPLETION);
+        return operatorUtil.checkCandidates(operatorId, fo);
+    }
+
+    @DSTransactional
+    @Override
+    public void batch(FlowModel flowModel) throws WorkFlowException {
+        Integer batchType = flowModel.getBatchType();
+        List<String> ids = new ArrayList<>();
+        List<String> idList = new ArrayList<>();
+        operatorUtil.checkBatch(flowModel.getIds(), idList, false);
+        operatorUtil.checkBatchRevoke(idList, ids, batchType);
+        int count = 0;
+        List<TaskEntity> taskList = new ArrayList<>();
+        for (String id : ids) {
+            operatorUtil.checkOperatorPermission(id);
+            OperatorEntity operator = operatorUtil.checkOperator(id);
+            TaskEntity taskEntity = taskMapper.getInfo(operator.getTaskId());
+            taskList.add(taskEntity);
+            FlowModel model = JsonUtil.getJsonToBean(flowModel, FlowModel.class);
+            model.setId(id);
+            switch (batchType) {
+                case 0:
+                case 1:
+                    model.setHandleStatus(Objects.equals(1, batchType) ? FlowNature.REJECT_COMPLETION : FlowNature.AUDIT_COMPLETION);
+                    try {
+                        this.audit(id, model);
+                    } catch (Exception e) {
+                        if (e instanceof WorkFlowException) {
+                            // 褰撻�夋嫨澶氭潯鏁版嵁涓斾笅涓鎵硅妭鐐瑰嚭鐜板紓甯告椂锛岃烦杩囧紓甯歌妭鐐硅兘閫氳繃鐨勫嵆瀹℃壒閫氳繃锛屽紓甯哥殑涓嶅鐞�
+                            WorkFlowException workFlowException = (WorkFlowException) e;
+                            if (ObjectUtil.equals(workFlowException.getCode(), 200)) {
+                                count++;
+                                break;
+                            }
+                        }
+                        operatorUtil.compensate(taskEntity);
+                        throw e;
+                    }
+                    break;
+                case 2:
+                    this.transfer(id, model);
+                    break;
+                case 3:
+                    try {
+                        this.back(id, model);
+                        break;
+                    } catch (Exception e) {
+                        operatorUtil.compensate(taskEntity);
+                        throw e;
+                    }
+                default:
+                    break;
+            }
+        }
+        flowModel.setTaskList(taskList);
+        // 褰撻�夋嫨鐨勬暟鎹兘鏄紓甯告椂锛屾彁绀猴細鈥滀笅涓�鑺傜偣瀹℃壒寮傚父锛屾棤娉曟壒閲忓鎵光��
+        if (ids.size() == count) {
+            throw new WorkFlowException(MsgCode.WF120.get());
+        }
+    }
+
+    @Override
+    public FlowWorkListVO flowWork(String fromId) {
+        FlowWorkListVO vo = new FlowWorkListVO();
+        List<FlowWorkModel> waitList = new ArrayList<>();
+        // 缁忓姙
+        QueryWrapper<OperatorEntity> operatorWrapper = new QueryWrapper<>();
+        operatorWrapper.lambda().eq(OperatorEntity::getHandleId, fromId).eq(OperatorEntity::getCompletion, FlowNature.NORMAL)
+                .ne(OperatorEntity::getStatus, OperatorStateEnum.FUTILITY.getCode());
+        List<OperatorEntity> operatorList = this.list(operatorWrapper);
+
+        if (CollUtil.isNotEmpty(operatorList)) {
+            List<String> taskIds = operatorList.stream().map(OperatorEntity::getTaskId).collect(Collectors.toList());
+            List<TaskEntity> taskList = taskMapper.getOrderStaList(taskIds);
+
+            List<TemplateEntity> templateList = templateMapper.getListOfHidden(taskList.stream().map(TaskEntity::getTemplateId).collect(Collectors.toList()));
+
+            for (OperatorEntity operator : operatorList) {
+                FlowWorkModel workModel = JsonUtil.getJsonToBean(operator, FlowWorkModel.class);
+                TaskEntity taskEntity = taskList.stream().filter(e -> e.getId().equals(operator.getTaskId())).findFirst().orElse(null);
+                if (null != taskEntity) {
+                    workModel.setFullName(taskEntity.getFullName());
+                    TemplateEntity template = templateList.stream()
+                            .filter(e -> e.getId().equals(taskEntity.getTemplateId())).findFirst().orElse(null);
+                    if (null != template) {
+                        workModel.setIcon(template.getIcon());
+                        waitList.add(workModel);
+                    }
+                }
+            }
+        }
+        vo.setFlowTask(waitList);
+
+        // 娴佺▼
+        List<FlowWorkModel> flowList = new ArrayList<>();
+        List<TemplateNodeEntity> nodeList = templateNodeMapper.getListLikeUserId(fromId);
+        if (CollUtil.isNotEmpty(nodeList)) {
+            List<String> flowIds = nodeList.stream().map(TemplateNodeEntity::getFlowId).collect(Collectors.toList());
+            List<TemplateJsonEntity> versionList = templateJsonMapper.selectByIds(flowIds);
+            List<TemplateEntity> templateList = flowUtil.getListByFlowIds(flowIds);
+            for (TemplateJsonEntity jsonEntity : versionList) {
+                if (!ObjectUtil.equals(jsonEntity.getState(), TemplateJsonStatueEnum.START.getCode())) {
+                    continue;
+                }
+                FlowWorkModel workModel = JsonUtil.getJsonToBean(jsonEntity, FlowWorkModel.class);
+                for (TemplateNodeEntity nodeEntity : nodeList) {
+                    if (nodeEntity.getNodeJson().contains(fromId)) {
+                        TemplateEntity template = templateList.stream()
+                                .filter(e -> e.getId().equals(jsonEntity.getTemplateId())).findFirst().orElse(null);
+                        if (null != template) {
+                            workModel.setIcon(template.getIcon());
+                            workModel.setFullName(template.getFullName() + "(V" + jsonEntity.getVersion() + ")");
+                            flowList.add(workModel);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        vo.setFlow(flowList);
+        return vo;
+    }
+
+    @Override
+    public boolean flowWork(WorkHandoverModel workHandoverModel) {
+        String fromId = workHandoverModel.getFromId();
+        String toId = workHandoverModel.getHandoverUser();
+        List<String> waitList = workHandoverModel.getFlowTaskList();
+        if (!waitList.isEmpty()) {
+            // 鏇存柊缁忓姙
+            QueryWrapper<OperatorEntity> operator = new QueryWrapper<>();
+            operator.lambda().in(OperatorEntity::getId, waitList)
+                    .eq(OperatorEntity::getHandleId, fromId);
+            List<OperatorEntity> operatorList = this.list(operator);
+            if (CollUtil.isNotEmpty(operatorList)) {
+                for (OperatorEntity entity : operatorList) {
+                    entity.setHandleId(toId);
+                    String handleAll = entity.getHandleAll();
+                    if (StringUtils.isNotBlank(handleAll)) {
+                        String str = handleAll.replaceAll(fromId, toId);
+                        entity.setHandleAll(str);
+                    }
+                }
+                super.updateBatchById(operatorList);
+            }
+            // 鏇存柊鍊欓�変汉
+            UpdateWrapper<CandidatesEntity> candidate = new UpdateWrapper<>();
+            candidate.lambda().in(CandidatesEntity::getOperatorId, waitList)
+                    .eq(CandidatesEntity::getHandleId, fromId)
+                    .set(CandidatesEntity::getHandleId, toId);
+            candidatesMapper.update(candidate);
+        }
+        // 娴佺▼
+        List<String> flowList = workHandoverModel.getFlowList();
+        if (!flowList.isEmpty()) {
+            UserEntity toUser = serviceUtil.getUserInfo(toId);
+            String toUserName = toUser != null ? toUser.getRealName() + "/" + toUser.getAccount() : "";
+            UserEntity fromUser = serviceUtil.getUserInfo(fromId);
+            String fromUserName = fromUser != null ? fromUser.getRealName() + "/" + fromUser.getAccount() : "";
+
+            List<TemplateJsonEntity> versionList = templateJsonMapper.selectByIds(flowList);
+            if (CollUtil.isNotEmpty(versionList)) {
+                List<TemplateNodeEntity> updateList = new ArrayList<>();
+                for (TemplateJsonEntity jsonEntity : versionList) {
+                    List<TemplateNodeEntity> nodeList = templateNodeMapper.getList(jsonEntity.getId());
+                    for (TemplateNodeEntity nodeEntity : nodeList) {
+                        if (nodeEntity.getNodeJson().contains(fromId)) {
+                            String str = nodeEntity.getNodeJson().replaceAll(fromId, toId).replaceAll(fromUserName, toUserName);
+                            nodeEntity.setNodeJson(str);
+                            updateList.add(nodeEntity);
+                        }
+                    }
+                }
+                if (CollUtil.isNotEmpty(updateList)) {
+                    templateNodeMapper.updateById(updateList);
+                }
+            }
+        }
+        return true;
+    }
+
+}

--
Gitblit v1.8.0