疯狂的狮子Li
2025-01-20 3c8d864b5f68af5167199e0d5c9ff6c0c5852638
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,679 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.UserService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.entity.*;
import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.core.enums.SkipType;
import org.dromara.warm.flow.core.service.*;
import org.dromara.warm.flow.orm.entity.*;
import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper;
import org.dromara.warm.flow.orm.mapper.FlowTaskMapper;
import org.dromara.workflow.common.ConditionalOnEnable;
import org.dromara.workflow.common.enums.TaskAssigneeType;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.bo.*;
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
import org.dromara.workflow.domain.vo.FlowTaskVo;
import org.dromara.workflow.handler.FlowProcessEventHandler;
import org.dromara.workflow.handler.WorkflowPermissionHandler;
import org.dromara.workflow.mapper.FlwCategoryMapper;
import org.dromara.workflow.mapper.FlwTaskMapper;
import org.dromara.workflow.service.IFlwTaskService;
import org.dromara.workflow.utils.WorkflowUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import static org.dromara.workflow.common.constant.FlowConstant.*;
/**
 * ä»»åŠ¡ æœåŠ¡å±‚å®žçŽ°
 *
 * @author may
 */
@ConditionalOnEnable
@Slf4j
@RequiredArgsConstructor
@Service
public class FlwTaskServiceImpl implements IFlwTaskService {
    private final TaskService taskService;
    private final InsService insService;
    private final DefService defService;
    private final HisTaskService hisTaskService;
    private final NodeService nodeService;
    private final FlowInstanceMapper flowInstanceMapper;
    private final FlowTaskMapper flowTaskMapper;
    private final FlowHisTaskMapper flowHisTaskMapper;
    private final IdentifierGenerator identifierGenerator;
    private final FlowProcessEventHandler flowProcessEventHandler;
    private final UserService userService;
    private final FlwTaskMapper flwTaskMapper;
    private final FlwCategoryMapper flwCategoryMapper;
    /**
     * å¯åŠ¨ä»»åŠ¡
     *
     * @param startProcessBo å¯åŠ¨æµç¨‹å‚æ•°
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> startWorkFlow(StartProcessBo startProcessBo) {
        String businessId = startProcessBo.getBusinessId();
        if (StringUtils.isBlank(businessId)) {
            throw new ServiceException("启动工作流时必须包含业务ID");
        }
        // å¯åŠ¨æµç¨‹å®žä¾‹ï¼ˆæäº¤ç”³è¯·ï¼‰
        Map<String, Object> variables = startProcessBo.getVariables();
        // æµç¨‹å‘起人
        variables.put(INITIATOR, LoginHelper.getUserIdStr());
        // ä¸šåŠ¡id
        variables.put(BUSINESS_ID, businessId);
        FlowInstance flowInstance = flowInstanceMapper.selectOne(new LambdaQueryWrapper<>(FlowInstance.class)
            .eq(FlowInstance::getBusinessId, businessId));
        if (ObjectUtil.isNotNull(flowInstance)) {
            BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus());
            List<Task> taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId()));
            return Map.of(PROCESS_INSTANCE_ID, taskList.get(0).getInstanceId(), TASK_ID, taskList.get(0).getId());
        }
        FlowParams flowParams = new FlowParams();
        flowParams.flowCode(startProcessBo.getFlowCode());
        flowParams.variable(startProcessBo.getVariables());
        flowParams.flowStatus(BusinessStatusEnum.DRAFT.getStatus());
        Instance instance;
        try {
            instance = insService.start(businessId, flowParams);
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        // ç”³è¯·äººæ‰§è¡Œæµç¨‹
        List<Task> taskList = taskService.list(new FlowTask().setInstanceId(instance.getId()));
        if (taskList.size() > 1) {
            throw new ServiceException("请检查流程第一个环节是否为申请人!");
        }
        return Map.of(PROCESS_INSTANCE_ID, instance.getId(), TASK_ID, taskList.get(0).getId());
    }
    /**
     * åŠžç†ä»»åŠ¡
     *
     * @param completeTaskBo åŠžç†ä»»åŠ¡å‚æ•°
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean completeTask(CompleteTaskBo completeTaskBo) {
        try {
            // èŽ·å–ä»»åŠ¡ID并查询对应的流程任务和实例信息
            Long taskId = completeTaskBo.getTaskId();
            List<String> messageType = completeTaskBo.getMessageType();
            String notice = completeTaskBo.getNotice();
            // èŽ·å–æŠ„é€äºº
            List<FlowCopyBo> flowCopyList = completeTaskBo.getFlowCopyList();
            FlowTask flowTask = flowTaskMapper.selectById(taskId);
            if (ObjectUtil.isNull(flowTask)) {
                throw new ServiceException("流程任务不存在或任务已审批!");
            }
            Instance ins = insService.getById(flowTask.getInstanceId());
            // èŽ·å–æµç¨‹å®šä¹‰ä¿¡æ¯
            Definition definition = defService.getById(flowTask.getDefinitionId());
            // æ£€æŸ¥æµç¨‹çŠ¶æ€æ˜¯å¦ä¸ºè‰ç¨¿ã€å·²æ’¤é”€æˆ–å·²é€€å›žçŠ¶æ€ï¼Œè‹¥æ˜¯åˆ™æ‰§è¡Œæµç¨‹æäº¤ç›‘å¬
            if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) {
                flowProcessEventHandler.processHandler(definition.getFlowCode(), ins.getBusinessId(), ins.getFlowStatus(), true);
            }
            // æž„建流程参数,包括变量、跳转类型、消息、处理人、权限等信息
            FlowParams flowParams = new FlowParams();
            flowParams.variable(completeTaskBo.getVariables());
            flowParams.skipType(SkipType.PASS.getKey());
            flowParams.message(completeTaskBo.getMessage());
            flowParams.flowStatus(BusinessStatusEnum.WAITING.getStatus()).hisStatus(TaskStatusEnum.PASS.getStatus());
            flowParams.hisTaskExt(completeTaskBo.getFileId());
            // æ‰§è¡Œä»»åŠ¡è·³è½¬ï¼Œå¹¶æ ¹æ®è¿”å›žçš„å¤„ç†äººè®¾ç½®ä¸‹ä¸€æ­¥å¤„ç†äºº
            Instance instance = taskService.skip(taskId, flowParams);
            this.setHandler(instance, flowTask, flowCopyList);
            // æ¶ˆæ¯é€šçŸ¥
            WorkflowUtils.sendMessage(definition.getFlowName(), ins.getId(), messageType, notice);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * è®¾ç½®åŠžç†äºº
     *
     * @param instance     å®žä¾‹
     * @param task         (当前任务)未办理的任务
     * @param flowCopyList æŠ„送人
     */
    private void setHandler(Instance instance, FlowTask task, List<FlowCopyBo> flowCopyList) {
        if (ObjectUtil.isNull(instance)) {
            return;
        }
        // æ·»åŠ æŠ„é€äºº
        this.setCopy(task, flowCopyList);
        // æ ¹æ®æµç¨‹å®žä¾‹ID查询所有关联的任务
        List<FlowTask> flowTasks = this.selectByInstId(instance.getId());
        if (CollUtil.isEmpty(flowTasks)) {
            return;
        }
        List<Long> taskIdList = StreamUtils.toList(flowTasks, FlowTask::getId);
        // èŽ·å–ä¸Žå½“å‰ä»»åŠ¡å…³è”çš„ç”¨æˆ·åˆ—è¡¨
        List<User> associatedUsers = WorkflowUtils.getFlowUserService().getByAssociateds(taskIdList);
        if (CollUtil.isEmpty(associatedUsers)) {
            return;
        }
        List<User> userList = new ArrayList<>();
        // éåŽ†ä»»åŠ¡åˆ—è¡¨ï¼Œå¤„ç†æ¯ä¸ªä»»åŠ¡çš„åŠžç†äºº
        for (FlowTask flowTask : flowTasks) {
            List<User> users = StreamUtils.filter(associatedUsers, user -> Objects.equals(user.getAssociated(), flowTask.getId()));
            if (CollUtil.isNotEmpty(users)) {
                userList.addAll(WorkflowUtils.buildUser(users, flowTask.getId()));
            }
        }
        // æ‰¹é‡åˆ é™¤çŽ°æœ‰ä»»åŠ¡çš„åŠžç†äººè®°å½•
        WorkflowUtils.getFlowUserService().deleteByTaskIds(taskIdList);
        // ç¡®ä¿è¦ä¿å­˜çš„ userList ä¸ä¸ºç©º
        if (CollUtil.isEmpty(userList)) {
            return;
        }
        WorkflowUtils.getFlowUserService().saveBatch(userList);
    }
    /**
     * æ·»åŠ æŠ„é€äºº
     *
     * @param task         ä»»åŠ¡ä¿¡æ¯
     * @param flowCopyList æŠ„送人
     */
    public void setCopy(FlowTask task, List<FlowCopyBo> flowCopyList) {
        if (CollUtil.isEmpty(flowCopyList)) {
            return;
        }
        // æ·»åŠ æŠ„é€äººè®°å½•
        FlowHisTask flowHisTask = flowHisTaskMapper.selectList(new LambdaQueryWrapper<>(FlowHisTask.class).eq(FlowHisTask::getTaskId, task.getId())).get(0);
        FlowNode flowNode = new FlowNode();
        flowNode.setNodeCode(flowHisTask.getTargetNodeCode());
        flowNode.setNodeName(flowHisTask.getTargetNodeName());
        //生成新的任务id
        long taskId = identifierGenerator.nextId(null).longValue();
        task.setId(taskId);
        task.setNodeName("【抄送】" + task.getNodeName());
        Date updateTime = new Date(flowHisTask.getUpdateTime().getTime() - 1000);
        FlowParams flowParams = FlowParams.build();
        flowParams.skipType(SkipType.NONE.getKey());
        flowParams.hisStatus(TaskStatusEnum.COPY.getStatus());
        flowParams.message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName));
        HisTask hisTask = hisTaskService.setSkipHisTask(task, flowNode, flowParams);
        hisTask.setCreateTime(updateTime);
        hisTask.setUpdateTime(updateTime);
        hisTaskService.save(hisTask);
        List<User> userList = flowCopyList.stream()
            .map(flowCopy -> {
                FlowUser flowUser = new FlowUser();
                flowUser.setType(TaskAssigneeType.COPY.getCode());
                flowUser.setProcessedBy(String.valueOf(flowCopy.getUserId()));
                flowUser.setAssociated(taskId);
                return flowUser;
            }).collect(Collectors.toList());
        // æ‰¹é‡ä¿å­˜æŠ„送人员
        WorkflowUtils.getFlowUserService().saveBatch(userList);
    }
    /**
     * æŸ¥è¯¢å½“前用户的待办任务
     *
     * @param flowTaskBo å‚æ•°
     * @param pageQuery  åˆ†é¡µ
     */
    @Override
    public TableDataInfo<FlowTaskVo> pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
        QueryWrapper<FlowTaskBo> queryWrapper = buildQueryWrapper(flowTaskBo);
        queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey());
        queryWrapper.in("t.processed_by", SpringUtils.getBean(WorkflowPermissionHandler.class).permissions());
        queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus());
        Page<FlowTaskVo> page = this.getFlowTaskVoPage(pageQuery, queryWrapper);
        return TableDataInfo.build(page);
    }
    /**
     * æŸ¥è¯¢å½“前用户的已办任务
     *
     * @param flowTaskBo å‚æ•°
     * @param pageQuery  åˆ†é¡µ
     */
    @Override
    public TableDataInfo<FlowHisTaskVo> pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
        QueryWrapper<FlowTaskBo> queryWrapper = buildQueryWrapper(flowTaskBo);
        queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey());
        queryWrapper.in("t.approver", LoginHelper.getUserIdStr());
        queryWrapper.orderByDesc("t.create_time").orderByDesc("t.update_time");
        Page<FlowHisTaskVo> page = flwTaskMapper.getListFinishTask(pageQuery.build(), queryWrapper);
        return TableDataInfo.build(page);
    }
    /**
     * æŸ¥è¯¢å¾…办任务
     *
     * @param flowTaskBo å‚æ•°
     * @param pageQuery  åˆ†é¡µ
     */
    @Override
    public TableDataInfo<FlowTaskVo> pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
        QueryWrapper<FlowTaskBo> queryWrapper = buildQueryWrapper(flowTaskBo);
        queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey());
        Page<FlowTaskVo> page = getFlowTaskVoPage(pageQuery, queryWrapper);
        return TableDataInfo.build(page);
    }
    private Page<FlowTaskVo> getFlowTaskVoPage(PageQuery pageQuery, QueryWrapper<FlowTaskBo> queryWrapper) {
        Page<FlowTaskVo> page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper);
        List<FlowTaskVo> records = page.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<Long> taskIds = StreamUtils.toList(records, FlowTaskVo::getId);
            Map<Long, List<UserDTO>> listMap = currentTaskAllUser(taskIds);
            records.forEach(t -> {
                List<UserDTO> userList = listMap.getOrDefault(t.getId(), Collections.emptyList());
                if (CollUtil.isNotEmpty(userList)) {
                    t.setAssigneeIds(StreamUtils.join(userList, e -> String.valueOf(e.getUserId())));
                    t.setAssigneeNames(StreamUtils.join(userList, UserDTO::getNickName));
                }
            });
        }
        return page;
    }
    /**
     * æŸ¥è¯¢å·²åŠžä»»åŠ¡
     *
     * @param flowTaskBo å‚æ•°
     * @param pageQuery  åˆ†é¡µ
     */
    @Override
    public TableDataInfo<FlowHisTaskVo> pageByAllTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
        QueryWrapper<FlowTaskBo> queryWrapper = buildQueryWrapper(flowTaskBo);
        Page<FlowHisTaskVo> page = flwTaskMapper.getListFinishTask(pageQuery.build(), queryWrapper);
        return TableDataInfo.build(page);
    }
    /**
     * æŸ¥è¯¢å½“前用户的抄送
     *
     * @param flowTaskBo å‚æ•°
     * @param pageQuery  åˆ†é¡µ
     */
    @Override
    public TableDataInfo<FlowTaskVo> pageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery) {
        QueryWrapper<FlowTaskBo> queryWrapper = buildQueryWrapper(flowTaskBo);
        queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr());
        Page<FlowTaskVo> page = flwTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper);
        return TableDataInfo.build(page);
    }
    private QueryWrapper<FlowTaskBo> buildQueryWrapper(FlowTaskBo flowTaskBo) {
        QueryWrapper<FlowTaskBo> wrapper = Wrappers.query();
        wrapper.like(StringUtils.isNotBlank(flowTaskBo.getNodeName()), "t.node_name", flowTaskBo.getNodeName());
        wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowName()), "t.flow_name", flowTaskBo.getFlowName());
        wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowCode()), "t.flow_code", flowTaskBo.getFlowCode());
        wrapper.in(CollUtil.isNotEmpty(flowTaskBo.getCreateByIds()), "t.create_by", flowTaskBo.getCreateByIds());
        if (StringUtils.isNotBlank(flowTaskBo.getCategory())) {
            List<Long> categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowTaskBo.getCategory()));
            wrapper.in("t.category", categoryIds);
        }
        wrapper.orderByDesc("t.create_time");
        return wrapper;
    }
    /**
     * é©³å›žä»»åŠ¡
     *
     * @param bo å‚æ•°
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean backProcess(BackProcessBo bo) {
        try {
            Long taskId = bo.getTaskId();
            String notice = bo.getNotice();
            List<String> messageType = bo.getMessageType();
            String message = bo.getMessage();
            FlowTask task = flowTaskMapper.selectById(taskId);
            if (ObjectUtil.isNull(task)) {
                throw new ServiceException("任务不存在!");
            }
            Instance inst = insService.getById(task.getInstanceId());
            BusinessStatusEnum.checkBackStatus(inst.getFlowStatus());
            Long definitionId = task.getDefinitionId();
            Definition definition = defService.getById(definitionId);
            String applyNodeCode = WorkflowUtils.applyNodeCode(definitionId);
            FlowParams flowParams = FlowParams.build();
            flowParams.nodeCode(bo.getNodeCode());
            flowParams.message(message);
            flowParams.skipType(SkipType.REJECT.getKey());
            flowParams.flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus())
                .hisStatus(TaskStatusEnum.BACK.getStatus());
            taskService.skip(task.getId(), flowParams);
            Instance instance = insService.getById(inst.getId());
            this.setHandler(instance, task, null);
            // æ¶ˆæ¯é€šçŸ¥
            WorkflowUtils.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * èŽ·å–å¯é©³å›žçš„å‰ç½®èŠ‚ç‚¹
     *
     * @param definitionId æµç¨‹å®šä¹‰id
     * @param nowNodeCode  å½“前节点
     */
    @Override
    public List<Node> getBackTaskNode(Long definitionId, String nowNodeCode) {
        List<Node> nodeCodes = nodeService.getByNodeCodes(Collections.singletonList(nowNodeCode), definitionId);
        if (!CollUtil.isNotEmpty(nodeCodes)) {
            return nodeCodes;
        }
        //判断是否配置了固定驳回节点
        Node node = nodeCodes.get(0);
        if (StringUtils.isNotBlank(node.getAnyNodeSkip())) {
            return nodeService.getByNodeCodes(Collections.singletonList(node.getAnyNodeSkip()), definitionId);
        }
        //获取可驳回的前置节点
        List<Node> nodes = nodeService.previousNodeList(definitionId, nowNodeCode);
        if (CollUtil.isNotEmpty(nodes)) {
            return StreamUtils.filter(nodes, e -> NodeType.BETWEEN.getKey().equals(e.getNodeType()));
        }
        return nodes;
    }
    /**
     * ç»ˆæ­¢ä»»åŠ¡
     *
     * @param bo å‚æ•°
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean terminationTask(FlowTerminationBo bo) {
        try {
            Long taskId = bo.getTaskId();
            Task task = taskService.getById(taskId);
            if (task == null) {
                throw new ServiceException("任务不存在!");
            }
            Instance instance = insService.getById(task.getInstanceId());
            if (ObjectUtil.isNotNull(instance)) {
                BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus());
            }
            FlowParams flowParams = new FlowParams();
            flowParams.message(bo.getComment());
            flowParams.flowStatus(BusinessStatusEnum.TERMINATION.getStatus())
                .hisStatus(TaskStatusEnum.TERMINATION.getStatus());
            taskService.termination(taskId, flowParams);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * æŒ‰ç…§ä»»åŠ¡id查询任务
     *
     * @param taskIdList ä»»åŠ¡id
     */
    @Override
    public List<FlowTask> selectByIdList(List<Long> taskIdList) {
        return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class)
            .in(FlowTask::getId, taskIdList));
    }
    /**
     * æŒ‰ç…§ä»»åŠ¡id查询任务
     *
     * @param taskId ä»»åŠ¡id
     */
    @Override
    public FlowTaskVo selectById(Long taskId) {
        Task task = taskService.getById(taskId);
        if (ObjectUtil.isNull(task)) {
            return null;
        }
        FlowTaskVo flowTaskVo = BeanUtil.toBean(task, FlowTaskVo.class);
        Instance instance = insService.getById(task.getInstanceId());
        Definition definition = defService.getById(task.getDefinitionId());
        flowTaskVo.setFlowStatus(instance.getFlowStatus());
        flowTaskVo.setVersion(definition.getVersion());
        flowTaskVo.setFlowCode(definition.getFlowCode());
        flowTaskVo.setFlowName(definition.getFlowName());
        flowTaskVo.setBusinessId(instance.getBusinessId());
        List<Node> nodeList = nodeService.getByNodeCodes(Collections.singletonList(flowTaskVo.getNodeCode()), instance.getDefinitionId());
        if (CollUtil.isNotEmpty(nodeList)) {
            Node node = nodeList.get(0);
            flowTaskVo.setNodeRatio(node.getNodeRatio());
        }
        return flowTaskVo;
    }
    /**
     * æŒ‰ç…§ä»»åŠ¡id查询任务
     *
     * @param taskIdList ä»»åŠ¡id
     * @return ç»“æžœ
     */
    @Override
    public List<FlowHisTask> selectHisTaskByIdList(List<Long> taskIdList) {
        return flowHisTaskMapper.selectList(new LambdaQueryWrapper<>(FlowHisTask.class)
            .in(FlowHisTask::getId, taskIdList));
    }
    /**
     * æŒ‰ç…§ä»»åŠ¡id查询任务
     *
     * @param taskId ä»»åŠ¡id
     * @return ç»“æžœ
     */
    @Override
    public FlowHisTask selectHisTaskById(Long taskId) {
        return flowHisTaskMapper.selectOne(new LambdaQueryWrapper<>(FlowHisTask.class)
            .eq(FlowHisTask::getId, taskId));
    }
    /**
     * æŒ‰ç…§å®žä¾‹id查询任务
     *
     * @param instanceIdList æµç¨‹å®žä¾‹id
     */
    @Override
    public List<FlowTask> selectByInstIdList(List<Long> instanceIdList) {
        return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class)
            .in(FlowTask::getInstanceId, instanceIdList));
    }
    /**
     * æŒ‰ç…§å®žä¾‹id查询任务
     *
     * @param instanceId æµç¨‹å®žä¾‹id
     */
    @Override
    public List<FlowTask> selectByInstId(Long instanceId) {
        return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class)
            .eq(FlowTask::getInstanceId, instanceId));
    }
    /**
     * ä»»åŠ¡æ“ä½œ
     *
     * @param bo            å‚æ•°
     * @param taskOperation æ“ä½œç±»åž‹ï¼Œå§”æ´¾ delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean taskOperation(TaskOperationBo bo, String taskOperation) {
        FlowParams flowParams = new FlowParams();
        flowParams.message(bo.getMessage());
        if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
            flowParams.ignore(true);
        }
        // æ ¹æ®æ“ä½œç±»åž‹æž„建 FlowParams
        switch (taskOperation) {
            case DELEGATE_TASK, TRANSFER_TASK -> {
                ValidatorUtils.validate(bo, AddGroup.class);
                flowParams.addHandlers(Collections.singletonList(bo.getUserId()));
            }
            case ADD_SIGNATURE -> {
                ValidatorUtils.validate(bo, EditGroup.class);
                flowParams.addHandlers(bo.getUserIds());
            }
            case REDUCTION_SIGNATURE -> {
                ValidatorUtils.validate(bo, EditGroup.class);
                flowParams.reductionHandlers(bo.getUserIds());
            }
            default -> {
                log.error("Invalid operation type:{} ", taskOperation);
                throw new ServiceException("Invalid operation type " + taskOperation);
            }
        }
        Long taskId = bo.getTaskId();
        FlowTaskVo flowTaskVo = selectById(taskId);
        if ("addSignature".equals(taskOperation) || "reductionSignature".equals(taskOperation)) {
            if (flowTaskVo.getNodeRatio().compareTo(BigDecimal.ZERO) == 0) {
                throw new ServiceException(flowTaskVo.getNodeName() + "不是会签节点!");
            }
        }
        // è®¾ç½®ä»»åŠ¡çŠ¶æ€å¹¶æ‰§è¡Œå¯¹åº”çš„ä»»åŠ¡æ“ä½œ
        switch (taskOperation) {
            //委派任务
            case DELEGATE_TASK -> {
                flowParams.hisStatus(TaskStatusEnum.DEPUTE.getStatus());
                return taskService.depute(taskId, flowParams);
            }
            //转办任务
            case TRANSFER_TASK -> {
                flowParams.hisStatus(TaskStatusEnum.TRANSFER.getStatus());
                return taskService.transfer(taskId, flowParams);
            }
            //加签,增加办理人
            case ADD_SIGNATURE -> {
                flowParams.hisStatus(TaskStatusEnum.SIGN.getStatus());
                return taskService.addSignature(taskId, flowParams);
            }
            //减签,减少办理人
            case REDUCTION_SIGNATURE -> {
                flowParams.hisStatus(TaskStatusEnum.SIGN_OFF.getStatus());
                return taskService.reductionSignature(taskId, flowParams);
            }
            default -> {
                log.error("Invalid operation type:{} ", taskOperation);
                throw new ServiceException("Invalid operation type " + taskOperation);
            }
        }
    }
    /**
     * ä¿®æ”¹ä»»åŠ¡åŠžç†äººï¼ˆæ­¤æ–¹æ³•å°†ä¼šæ‰¹é‡ä¿®æ”¹æ‰€æœ‰ä»»åŠ¡çš„åŠžç†äººï¼‰
     *
     * @param taskIdList ä»»åŠ¡id
     * @param userId     ç”¨æˆ·id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAssignee(List<Long> taskIdList, String userId) {
        if (CollUtil.isEmpty(taskIdList)) {
            return false;
        }
        try {
            List<FlowTask> flowTasks = this.selectByIdList(taskIdList);
            // æ‰¹é‡åˆ é™¤çŽ°æœ‰ä»»åŠ¡çš„åŠžç†äººè®°å½•
            if (CollUtil.isNotEmpty(flowTasks)) {
                WorkflowUtils.getFlowUserService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId));
                List<User> userList = flowTasks.stream()
                    .map(flowTask -> {
                        FlowUser flowUser = new FlowUser();
                        flowUser.setType(TaskAssigneeType.APPROVER.getCode());
                        flowUser.setProcessedBy(userId);
                        flowUser.setAssociated(flowTask.getId());
                        return flowUser;
                    })
                    .collect(Collectors.toList());
                if (CollUtil.isNotEmpty(userList)) {
                    WorkflowUtils.getFlowUserService().saveBatch(userList);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage());
        }
        return true;
    }
    /**
     * èŽ·å–ä»»åŠ¡æ‰€æœ‰åŠžç†äºº
     *
     * @param taskIdList ä»»åŠ¡id
     */
    @Override
    public Map<Long, List<UserDTO>> currentTaskAllUser(List<Long> taskIdList) {
        Map<Long, List<UserDTO>> map = new HashMap<>();
        // èŽ·å–ä¸Žå½“å‰ä»»åŠ¡å…³è”çš„ç”¨æˆ·åˆ—è¡¨
        List<User> associatedUsers = WorkflowUtils.getFlowUserService().getByAssociateds(taskIdList);
        Map<Long, List<User>> listMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated);
        for (Map.Entry<Long, List<User>> entry : listMap.entrySet()) {
            List<User> value = entry.getValue();
            if (CollUtil.isNotEmpty(value)) {
                List<UserDTO> userDTOS = userService.selectListByIds(StreamUtils.toList(value, e -> Long.valueOf(e.getProcessedBy())));
                map.put(entry.getKey(), userDTOS);
            }
        }
        return map;
    }
    /**
     * èŽ·å–å½“å‰ä»»åŠ¡çš„æ‰€æœ‰åŠžç†äºº
     *
     * @param taskId ä»»åŠ¡id
     */
    @Override
    public List<UserDTO> currentTaskAllUser(Long taskId) {
        // èŽ·å–ä¸Žå½“å‰ä»»åŠ¡å…³è”çš„ç”¨æˆ·åˆ—è¡¨
        List<User> userList = WorkflowUtils.getFlowUserService().getByAssociateds(Collections.singletonList(taskId));
        if (CollUtil.isEmpty(userList)) {
            return Collections.emptyList();
        }
        return userService.selectListByIds(StreamUtils.toList(userList, e -> Long.valueOf(e.getProcessedBy())));
    }
}