JAVA Simple Approval Flow-07

public enum NodeTypeEnum {
    /**
     * 审批节点
     */
    APPROVAL("审批节点"),

    /**
     * 条件节点
     */
    CONDITION("条件节点"),

    /**
     * 系统节点
     */
    SYSTEM("系统节点");

    private final String name;

    NodeTypeEnum(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
public enum HandlerTypeEnum {
    /**
     * 审批人
     */
    APPROVER,
    /**
     * 抄送人
     */
    CCPERSON
}
public enum ApprovalFlowNodeEnum {
    /**
     * 填写Site基本信息
     */
    FILL_SITE_INFO("填写Site基本信息", "Input Site Basic Information"),

    /**
     * 维护银行函信息
     */
    MAIN_BANK_LETTER_INFO("维护银行函信息", "Maintain Bank Letter Information"),

    /**
     * 填写OU信息
     */
    FILL_OU_INFO("填写OU信息", "Input OU Information"),

    /**
     * 修改OU信息
     */
    MODIFY_OU_INFO("修改OU信息", "Change OU Information"),

    /**
     * 供应商Owner审批
     */
    SUPPLIER_OWNER_APPROVAL("供应商Owner审核", "Supplier Owner Review"),

    /**
     * 供应商填写
     */
    SUPPLIER_FILL("供应商填写", "Supplier Input"),

    /**
     * AP审核
     */
    AP_APPROVAL("AP审核", "AP Review"),

    /**
     * 金融合规审核
     */
    COMPLIANCE_APPROVAL("金融合规审核", "Financial Compliance Review"),

    /**
     * 采购复核
     */
    PROCUREMENT_REAPPROVAL("采购复核", "Procurement Information Review"),

    /**
     * CEG Leader 审核
     */
    CEG_LEADER_APPROVAL("CEG Leader 审批", "CEG Leader Approval"),

    /**
     * EBS集成
     */
    EBS_INTE("EBS集成", "EBS Integration"),

    /**
     * 已邀请
     */
    INVITED("已邀请", "Invited"),

    /**
     * 变更site信息
     */
    MODIFY_SITE_INFO("变更Site基本信息", "Change Site Basic Information"),

    /**
     * 变更银行函信息
     */
    MODIFY_BANK_LETTER_INFO("变更银行函信息", "Change Bank Letter Information");

    private final String nameCn;

    private final String nameEn;

    ApprovalFlowNodeEnum(String nameCn, String nameEn) {
        this.nameCn = nameCn;
        this.nameEn = nameEn;
    }

    private String nameEn() {
        return this.nameEn;
    }

    /**
     * 根据中文获取英文
     *
     * @param nameCn nameCn
     * @return String String
     */
    public static String getNameEn(String nameCn) {
        ApprovalFlowNodeEnum[] approvalNodeEnEnums = values();
        for (ApprovalFlowNodeEnum approvalNodeEnEnum : approvalNodeEnEnums) {
            if (approvalNodeEnEnum.nameCn.equals(nameCn)) {
                return approvalNodeEnEnum.nameEn();
            }
        }
        return nameCn;
    }
}
public enum ApprovalErrorEnum {
    /**
     * 没有权限撤回
     */
    NO_AUTH_REVOKE(5001, "No Auth Revoke"),

    /**
     * 非法的实例状态
     */
    ILLEGAL_INSTANCE_STATUS(5002, "Illegal Instance Status"),

    /**
     * 该审批人已经在使用中
     */
    APPROVER_REALLY_USE(5003, "approver really use"),

    /**
     * 没有权限作废(只允许责任人作废)
     */
    NO_AUTH_ABORT(5004, "No Auth Abort"),

    /**
     * 没有找到实例
     */
    NOT_FIND_INSTANCE(5005, "Not Find Instance"),

    /**
     * 没有权限
     */
    NO_AUTH(5006, "No Auth"),

    /**
     * 流程不在当前节点,无法审批
     */
    CURRENT_NODE_ERROR(5007, "The process is not in the current node and cannot be approved"),

    /**
     * 没有权限审批
     */
    NO_AUTH_APPROVE(5008, "No Auth Approve"),

    /**
     * 不能转审给自己
     */
    NOT_REFERRAL_TO_SELF(5009, "Can't refer to myself");

    private final Integer code;

    private final String msg;

    ApprovalErrorEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

public enum ApprovalEnum {
    /**
     * 提交
     */
    SUBMIT("提交"),

    /**
     * 同意
     */
    AGREE("同意"),

    /**
     * 驳回
     */
    REJECT("驳回"),

    /**
     * 重新启动
     */
    RESTART("重新启动"),

    /**
     * 重新提交
     */
    RESUBMIT("重新提交"),

    /**
     * 撤回
     */
    REVOKE("撤回"),

    /**
     * 转审
     */
    REFERRAL("转审"),

    /**
     * 修改owner
     */
    MODIFY_OWNER("修改owner"),

    /**
     * 作废
     */
    ABORT("作废"),

    /**
     * 删除
     */
    DELETE("删除"),

    /**
     * 未处理
     */
    UNHANDLED("未处理"),

    /**
     * 进行中
     */
    RUNNING("进行中"),

    /**
     * 已完成
     */
    FINISHED("已完成"),

    /**
     * 取消
     */
    CANCEL("取消");

    private final String name;

    ApprovalEnum(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
public class ApprovalConstants {
    /**
     * tip for flowInstanceId is required
     */
    public static final String FLOW_INSTANCE_ID_NULL_ERROR_MSG = "flowInstanceId is required";

    /**
     * tip for flowInstanceId is required
     */
    public static final String FLOW_ID_NULL_ERROR_MSG = "flowId is required";

    /**
     * tip for flowNodeId is required
     */
    public static final String FLOW_NODE_ID_NULL_ERROR_MSG = "flowNodeId is required";

    /**
     * tip for flowNodeVersion is required
     */
    public static final String VERSION_NULL_ERROR_MSG = "flowNodeVersion is required";

    /**
     * RULE_STATIC
     */
    public static final String RULE_STATIC = "static:";

    /**
     * RULE_VAR
     */
    public static final String RULE_VAR = "var:";

    /**
     * RULE_SPEL
     */
    public static final String SPEL_VAR = "spel:";

    /**
     * RULE_BEAN
     */
    public static final String RULE_BEAN = "bean:";

    /**
     * COMMA
     */
    public static final String COMMA = ",";

    /**
     * NEGATIVE_ONE
     */
    public static final int NEGATIVE_ONE = -1;

    /**
     * YES
     */
    public static final String YES = "Y";

    /**
     * NO
     */
    public static final String NO = "N";

    /**
     * AGREE
     */
    public static final String AGREE = "AGREE";

    /**
     * DISAGREE
     */
    public static final String DISAGREE = "DISAGREE";

    /**
     * SORT_NUM_1
     */
    public static final Integer SORT_NUM_1 = 1;

    /**
     * SORT_NUM_2
     */
    public static final Integer SORT_NUM_2 = 2;

    private ApprovalConstants() {
    }
}
@Component
public class ApprovalAgreeUtil {
    @Autowired
    private ProApprovalFlowInstanceDomainService instanceService;

    @Autowired
    private ProApprovalFlowTodoDomainService toDoService;

    @Autowired
    private ProApprovalFlowLogDomainService logService;

    /**
     * 权限校验
     *
     * @param bizType   bizType
     * @param bizDataId bizDataId
     * @return ResultDto
     */
    public ResultDto checkAuth(String bizType, String bizDataId) {
        UserVO user = CommonUtil.getCurrentUser();
        ProApprovalFlowInstanceDto instance = instanceService.loadByBizTypeBizDataId(bizType, bizDataId);
        if (instance.getFlowInstanceId() == null) {
            return ResultDto.fail(ApprovalErrorEnum.NOT_FIND_INSTANCE);
        }
        if (!instance.getStatus().equals(ApprovalEnum.RUNNING.name())) {
            return ResultDto.fail(ApprovalErrorEnum.ILLEGAL_INSTANCE_STATUS);
        }
        Optional<ProApprovalFlowTodoDto> todoOpt =
                toDoService.loadByHandler(instance.getFlowInstanceId(), instance.getCurrentNode(), user.getUserAccount());
        if (!todoOpt.isPresent()) {
            return ResultDto.fail(ApprovalErrorEnum.NO_AUTH_APPROVE);
        }
        ResultDto resultDto = new ResultDto(todoOpt.get());
        resultDto.setResult(instance);
        return resultDto;
    }

    /**
     * 转审权限校验
     *
     * @param approvalDto approvalDto
     * @return ResultDto
     */
    public ResultDto checkAuthReferral(ApprovalDto approvalDto) {
        String bizType = approvalDto.getBizType();
        String bizDataId = approvalDto.getBizDataId();
        UserVO user = CommonUtil.getCurrentUser();

        // 不能自己转自己
        if (user.getUserAccount().equals(approvalDto.getForwardUser())) {
            return ResultDto.fail(ApprovalErrorEnum.NOT_REFERRAL_TO_SELF);
        }

        ProApprovalFlowInstanceDto instance = instanceService.loadByBizTypeBizDataId(bizType, bizDataId);
        if (instance.getFlowInstanceId() == null) {
            return ResultDto.fail(ApprovalErrorEnum.NOT_FIND_INSTANCE);
        }

        Optional<ProApprovalFlowTodoDto> todoOpt =
                toDoService.loadByHandler(instance.getFlowInstanceId(), instance.getCurrentNode(), user.getUserAccount());
        if (!todoOpt.isPresent() && !user.getUserAccount().equals(instance.getOwner())) {
            return ResultDto.fail(ApprovalErrorEnum.NO_AUTH_APPROVE);
        }

        ResultDto resultDto = new ResultDto();
        if (todoOpt.isPresent()) {
            resultDto.setData(todoOpt.get());
        } else {
            List<ProApprovalFlowTodoDto> todoDtoList =
                    toDoService.loadByCurNode(instance.getFlowInstanceId(), instance.getCurrentNode());
            resultDto.setData(todoDtoList.stream()
                    .filter(item -> ApprovalEnum.UNHANDLED.name().equals(item.getResult()))
                    .findFirst()
                    .get());
        }
        resultDto.setResult(instance);
        return resultDto;
    }

    /**
     * 节点是否完成
     *
     * @param instance instance
     * @param curNode  curNode
     * @return 节点是否完成
     */
    public boolean isFinished(ProApprovalFlowInstanceDto instance, ProApprovalFlowNodeDto curNode) {
        boolean isFinishedFlag = false;

        // 如果通过比例为空则默认全部通过才通过,否则计算比例
        List<ProApprovalFlowTodoDto> todoList =
                toDoService.loadByCurNode(instance.getFlowInstanceId(), curNode.getFlowNodeId());
        todoList = todoList.stream()
                .filter(item -> !item.getResult().equals(ApprovalEnum.REFERRAL.name()))
                .collect(Collectors.toList());
        if (ObjectUtils.isEmpty(curNode.getPassRatio())) {
            isFinishedFlag = true;
        } else {
            BigDecimal shouldPassNum =
                    curNode.getPassRatio().multiply(new BigDecimal(todoList.size())).setScale(0, RoundingMode.CEILING);
            long realPassNum =
                    todoList.stream().filter(item -> item.getResult().equals(ApprovalEnum.AGREE.name())).count();
            if (new BigDecimal(realPassNum).compareTo(shouldPassNum) > NEGATIVE_ONE) {
                isFinishedFlag = true;
            }
        }
        return isFinishedFlag;
    }
}
@Component
public class ApprovalRejectUtil {
    @Autowired
    private ProApprovalFlowInstanceDomainService instanceService;

    @Autowired
    private ProApprovalFlowTodoDomainService toDoService;

    @Autowired
    private TodoWeLinkService todoWelinkService;

    /**
     * 节点是否驳回
     *
     * @param instance instance
     * @param curNode curNode
     * @return 节点是否驳回
     */
    public boolean isReject(ProApprovalFlowInstanceDto instance, ProApprovalFlowNodeDto curNode) {
        boolean isRejectFlag = false;

        if (ObjectUtils.isEmpty(curNode.getRejectRatio())) {
            return true;
        }
        List<ProApprovalFlowTodoDto> todoList =
            toDoService.loadByCurNode(instance.getFlowInstanceId(), curNode.getFlowNodeId());
        todoList = todoList.stream()
            .filter(item -> !item.getResult().equals(ApprovalEnum.REFERRAL.name()))
            .collect(Collectors.toList());
        BigDecimal shouldRejectNum =
            curNode.getRejectRatio().multiply(new BigDecimal(todoList.size())).setScale(0, RoundingMode.CEILING);
        long realRejectNum =
            todoList.stream().filter(item -> item.getResult().equals(ApprovalEnum.REJECT.name())).count();
        if (new BigDecimal(realRejectNum).compareTo(shouldRejectNum) > NEGATIVE_ONE) {
            isRejectFlag = true;
        }
        return isRejectFlag;
    }

    /**
     * 完成当前实例的所有待办,待办结果置为取消
     *
     * @param flowInstanceId 流程实例ID
     */
    public void cancelOtherTodo(Long flowInstanceId) {
        List<ProApprovalFlowTodoDto> todoList = toDoService.loadByInstance(flowInstanceId);
        for (ProApprovalFlowTodoDto todo : todoList) {
            if (todo.getResult().equals(ApprovalEnum.UNHANDLED.name())) {
                todoWelinkService.completeToDoAsync(new TodoCompleteDto(todo.getAppTaskId(), todo.getHandler()));
                todo.setResult(ApprovalEnum.CANCEL.name());
            }
        }
        toDoService.invalid(todoList);
    }

    /**
     * 获取驳回节点
     *
     * @param bizData 业务数据
     * @param rule 驳回节点规则
     * @return RejectNode
     */
    public Long getRejectNode(String bizData, String rule) {
        if (rule.startsWith(RULE_STATIC)) {
            return Long.valueOf(rule.substring(RULE_STATIC.length()));
        }
        if (rule.startsWith(SPEL_VAR)) {
            String spelExpression = rule.substring(SPEL_VAR.length());
            EvaluationContext evaluationContext = new StandardEvaluationContext();
            evaluationContext.setVariable("map", JSON.parseObject(bizData));
            ExpressionParser parser = new SpelExpressionParser();
            Expression exp = parser.parseExpression(spelExpression);
            return exp.getValue(evaluationContext, Long.class);
        }
        throw new IllegalArgumentException("RejectNodeRule is Illegal");
    }

    /**
     * 获取是否重新提交到自己
     *
     * @param bizData 业务数据
     * @param rule 驳回节点规则
     * @return RejectNode
     */
    public boolean getResubmitToSelf(String bizData, String rule) {
        if (StringUtils.isBlank(rule)) {
            return false;
        }
        if (YES.equals(rule)) {
            return true;
        }
        if (rule.startsWith(SPEL_VAR)) {
            String spelExpression = rule.substring(SPEL_VAR.length());
            EvaluationContext evaluationContext = new StandardEvaluationContext();
            evaluationContext.setVariable("map", JSON.parseObject(bizData));
            ExpressionParser parser = new SpelExpressionParser();
            Expression exp = parser.parseExpression(spelExpression);
            return exp.getValue(evaluationContext, Boolean.class);
        }
        throw new IllegalArgumentException("ResubmitToSelf Rule is Illegal");
    }
}
@Component
@Slf4j
public class ApprovalSubmitUtil {
    private static final String VAR_MAP = "map";

    @Autowired
    private ProApprovalFlowInstanceDomainService instanceService;

    @Autowired
    private ProApprovalFlowNodeDomainService nodeService;

    @Autowired
    private ProApprovalFlowNodeHandlerDomainService nodeHandlerService;

    @Autowired
    private ProApprovalFlowDomainService flowService;

    @Autowired
    private ProApprovalFlowTodoDomainService toDoService;

    @Autowired
    private ApplicationContext context;

    @Autowired
    private TodoWeLinkService todoWelinkService;

    @Autowired
    private ApprovalEventPublisher eventPublisher;

    @Autowired
    private ProApprovalFlowLogDomainService logService;

    /**
     * 保存流程实例
     *
     * @param submitDto   提交参数
     * @param oldInstance 旧流程实例
     * @return ProApprovalFlowInstanceDto
     */
    public ProApprovalFlowInstanceDto saveInstance(SubmitDto submitDto, ProApprovalFlowInstanceDto oldInstance) {
        Long flowId = submitDto.getFlowId();
        if (flowId == null) {
            throw new IllegalArgumentException("flowId is required");
        }
        Long flowInstanceId = oldInstance.getFlowInstanceId();
        boolean isAdd = flowInstanceId == null;
        ProApprovalFlowInstanceEntity instance = new ProApprovalFlowInstanceEntity();
        instance.setFlowInstanceId(flowInstanceId);
        BeanUtils.copyProperties(submitDto, instance);
        CommonUtil.setUser(instance, isAdd);
        if (ObjectUtils.isEmpty(submitDto.getOwner())) {
            UserVO user = CommonUtil.getCurrentUser();
            submitDto.setOwner(user.getUserAccount());
            submitDto.setOwnerName(user.getUserCN());
        }
        instance.setOwner(submitDto.getOwner());
        instance.setOwnerName(submitDto.getOwnerName());
        instance.setStatus(ApprovalEnum.RUNNING.name());
        Object bizData = submitDto.getBizData();
        if (!ObjectUtils.isEmpty(bizData)) {
            instance.setBizData(JSON.toJSONString(bizData));
        }
        if (isAdd) {
            instance.setFlowNodeVersion(nodeService.queryLastVersion(flowId));
            instanceService.save(instance);
        } else {
            instance.setFlowNodeVersion(oldInstance.getFlowNodeVersion());
            instanceService.modify(instance);
        }
        return ProApprovalAssembler.instanceToDto(instance);
    }

    /**
     * 保存节点处理人
     *
     * @param instance  流程实例
     * @param submitDto 提交参数
     */
    public void saveNodeHandler(ProApprovalFlowInstanceDto instance, SubmitDto submitDto) {
        Long flowId = submitDto.getFlowId();
        Long flowInstanceId = instance.getFlowInstanceId();
        String flowNodeVersion = instance.getFlowNodeVersion();
        List<ProApprovalFlowNodeDto> nodeList = nodeService.loadByFlowIdAndVersion(flowId, flowNodeVersion);
        List<ProApprovalFlowNodeDto> hasHandlerList =
                nodeList.stream().filter(item -> StringUtils.hasText(item.getApproverRule())).collect(Collectors.toList());
        for (ProApprovalFlowNodeDto node : hasHandlerList) {
            // 审批人
            processNodeHandlerRule(flowInstanceId, node, submitDto, false);

            // 抄送人
            processNodeHandlerRule(flowInstanceId, node, submitDto, true);
        }
    }

    /**
     * 处理节点处理人规则
     *
     * @param flowInstanceId 实例ID
     * @param node           节点
     * @param submitDto      提交参数
     * @param isCc           是否抄送
     */
    private void processNodeHandlerRule(Long flowInstanceId, ProApprovalFlowNodeDto node, SubmitDto submitDto,
                                        boolean isCc) {
        Long flowNodeId = node.getFlowNodeId();
        String rule = isCc ? node.getCcUserRule() : node.getApproverRule();
        if (!StringUtils.hasText(rule)) {
            return;
        }
        Map data = JSON.parseObject(JSON.toJSONString(submitDto.getApprover()));
        NodeHandlerDto owner = new NodeHandlerDto(submitDto.getOwner(), submitDto.getOwnerName());
        data.put("owner", Collections.singletonList(owner));
        String handlerStr = "";
        if (rule.startsWith(RULE_STATIC)) {
            // static:[{"account":"lw0043690","name":"luoyunyong W0043690"}]
            handlerStr = rule.substring(RULE_STATIC.length());
        }
        if (rule.startsWith(RULE_VAR)) {
            // var:approver
            String handlerKey = rule.substring(RULE_VAR.length());
            Object handler = data.get(handlerKey);
            if (handler != null) {
                handlerStr = JSON.toJSONString(handler);
            }
        }
        if (rule.startsWith(RULE_BEAN)) {
            // bean:node_handler_bean:blacklist
            String beanName = rule.substring(RULE_BEAN.length());
            NodeHandlerFactory handlerFactory = context.getBean(beanName, NodeHandlerFactory.class);
            handlerStr = handlerFactory.get(submitDto);
        }
        if (!StringUtils.hasText(handlerStr)) {
            return;
        }
        List<NodeHandlerDto> handlerList = JSON.parseArray(handlerStr, NodeHandlerDto.class);
        String handlerType = isCc ? HandlerTypeEnum.CCPERSON.name() : HandlerTypeEnum.APPROVER.name();
        for (NodeHandlerDto handler : handlerList) {
            ProApprovalFlowNodeHandlerEntity handlerEntity = new ProApprovalFlowNodeHandlerEntity();
            handlerEntity.setFlowInstanceId(flowInstanceId);
            handlerEntity.setFlowNodeId(flowNodeId);
            handlerEntity.setHandler(handler.getAccount());
            handlerEntity.setHandlerName(handler.getName());
            handlerEntity.setHandlerType(handlerType);
            CommonUtil.setUser(handlerEntity);
            nodeHandlerService.save(handlerEntity);
        }
    }

    /**
     * 激活节点
     *
     * @param instance 实例DTO
     * @param curNode  当前节点
     * @param opType   操作类型
     * @param comment  审批备注
     */
    public void activateNode(ProApprovalFlowInstanceDto instance, ProApprovalFlowNodeDto curNode, String opType,
                             String comment) {
        log.info("activateNode instance: {}, curNode: {}, opType: {}, comment: {}",
                JSON.toJSONString(instance), curNode.getFlowNodeCode(), opType, comment);

        // 条件节点
        boolean isPass = !ApprovalEnum.REJECT.name().equals(opType);
        if (NodeTypeEnum.CONDITION.name().equals(curNode.getNodeType())) {
            ProApprovalFlowNodeDto nextNode = evalCondition(curNode.getConditionExpression(), instance.getBizData());
            activateNode(instance, nextNode, opType, comment);
        }

        // 系统节点
        if (NodeTypeEnum.SYSTEM.name().equals(curNode.getNodeType())) {
            // 激活当前节点
            activate(instance, curNode, isPass);

            // 发布上个节点完成事件
            nodeFinish(instance, opType, comment);

            // 运行系统任务
            SystemActivity systemActivityService = context.getBean(curNode.getSystemActivity(), SystemActivity.class);
            boolean isGo = systemActivityService.run(instance, curNode);

            // 如果系统活动返回true则流程继续,返回false则挂起流程
            if (isGo) {
                // 如果已经是最后一个节点则发布完成事件
                if (YES.equals(curNode.getLastFlag())) {
                    processLastNode(instance, curNode, null);
                    return;
                }

                // 激活下一个节点
                ProApprovalFlowNodeDto nextNode = nodeService.loadByFlowIdAndVersion(instance.getFlowId(),
                        instance.getFlowNodeVersion(), curNode.getSortNum() + 1);
                activateNode(instance, nextNode, ApprovalEnum.AGREE.name(), instance.getRemark());
            } else {
                eventPublisher.publishEvent(new PendingEvent(instance));
            }
        }

        // 审批节点
        if (NodeTypeEnum.APPROVAL.name().equals(curNode.getNodeType())) {
            activate(instance, curNode, isPass);
            nodeFinish(instance, opType, comment);
        }
    }

    /**
     * 处理节点已完成
     *
     * @param instance instance
     * @param curNode  curNode
     * @param todoDto  todoDto
     */
    public void processLastNode(ProApprovalFlowInstanceDto instance, ProApprovalFlowNodeDto curNode, ProApprovalFlowTodoDto todoDto) {
        // 发布当前节点完成事件
        Long flowInstanceId = instance.getFlowInstanceId();
        instance.setPreNodeId(curNode.getFlowNodeId());
        instance.setPreNodeCode(curNode.getFlowNodeCode());
        instance.setPreNodeName(curNode.getFlowNodeName());
        eventPublisher.publishEvent(new NodeFinishedEvent(instance));
        logService.save(flowInstanceId, curNode.getFlowNodeId(), ApprovalEnum.AGREE.name(), instance.getRemark());

        // 最后节点则更新实例状态为已完成
        instanceService.modifyStatus(flowInstanceId, ApprovalEnum.FINISHED.name());
        ProApprovalFlowDto flowDto = flowService.load(instance.getFlowId());
        instance.setBizStatus(flowDto.getBizFinishedStatus());

        // 给责任人发已完成WeLink通知
        TodoDto todo = ApprovalSubmitUtil.getTodo(instance.getBizData(), flowDto.getTodoDoneRule());
        sendWeLinkNotice(todo.getTitle(), todo.getDesc(), instance.getOwner(),
                todo.getUrl());

        // 最后节点,触发流程已完成事件
        eventPublisher.publishEvent(new FinishedEvent(instance));
        logService.save(flowInstanceId, curNode.getFlowNodeId(), ApprovalEnum.FINISHED.name(),
                ApprovalEnum.FINISHED.getName());

        // 最后一个节点将待办失效
        if (todoDto != null) {
            todoDto.setActiveFlag(NO);
            toDoService.modify(todoDto);
        }
    }

    private void nodeFinish(ProApprovalFlowInstanceDto instance, String opType, String comment) {
        BaseEvent event = ApprovalEnum.REJECT.name().equals(opType) ? new RejectEvent(instance) : new NodeFinishedEvent(instance);
        eventPublisher.publishEvent(event);
        logService.save(instance.getFlowInstanceId(), instance.getPreNodeId(), opType,
                comment);
    }

    private ProApprovalFlowNodeDto evalCondition(String conditionExpression, String bizData) {
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(conditionExpression);
        EvaluationContext evaluationContext = new StandardEvaluationContext();
        evaluationContext.setVariable(VAR_MAP, JSON.parseObject(bizData));
        Long nextNodeId = expression.getValue(evaluationContext, Long.class);
        return nodeService.load(nextNodeId);
    }

    /**
     * 激活
     *
     * @param instance 实例DTO
     * @param curNode  当前节点
     * @param isPass   是否通过
     */
    public void activate(ProApprovalFlowInstanceDto instance, ProApprovalFlowNodeDto curNode, boolean isPass) {
        log.info("activate start instance : {}, curNode: {}", instance.getFlowInstanceId(), curNode.getFlowNodeCode());
        TimerUtil timerAll = new TimerUtil();
        Long flowInstanceId = instance.getFlowInstanceId();
        Long originNodeId = instance.getCurrentNode();
        ProApprovalFlowNodeDto originNode = nodeService.load(originNodeId);
        instance.setPreNodeId(originNodeId);
        instance.setPreNodeCode(originNode.getFlowNodeCode());
        instance.setPreNodeName(originNode.getFlowNodeName());

        // 激活
        TimerUtil timer = new TimerUtil();
        instanceService.updateCurNode(flowInstanceId, curNode.getFlowNodeId());
        instance.setCurrentNode(curNode.getFlowNodeId());
        instance.setCurrentNodeCode(curNode.getFlowNodeCode());
        instance.setCurrentNodeName(curNode.getFlowNodeName());
        instance.setBizStatus(curNode.getBizStatus());
        timer.print("=====updateCurNode");

        // 给审批人发送待办和WeLink通知
        timer = new TimerUtil();
        String todoRule = isPass ? curNode.getActiveTodoRule() : originNode.getRejectTodoRule();
        TodoDto todo = getTodo(instance.getBizData(), todoRule);
        List<ProApprovalFlowNodeHandlerDto> nodeHandlerList =
                nodeHandlerService.loadByInstanceAndNodeId(flowInstanceId, curNode.getFlowNodeId());
        if (ObjectUtils.isEmpty(nodeHandlerList)) {
            return;
        }
        List<ProApprovalFlowNodeHandlerDto> approverList = nodeHandlerList.stream()
                .filter(item -> item.getHandlerType().equals(HandlerTypeEnum.APPROVER.name()))
                .collect(Collectors.toList());
        if (!ObjectUtils.isEmpty(approverList)) {
            for (ProApprovalFlowNodeHandlerDto handlerDto : approverList) {
                approvalSendTodo(instance, handlerDto, todo, new ApprovalDto());
            }
            timer.print("=====SendTodo approverList");
        }

        // 给抄送人发送WeLink通知
        timer = new TimerUtil();
        List<ProApprovalFlowNodeHandlerDto> ccUserList = nodeHandlerList.stream()
                .filter(item -> item.getHandlerType().equals(HandlerTypeEnum.CCPERSON.name()))
                .collect(Collectors.toList());
        if (!ObjectUtils.isEmpty(ccUserList)) {
            for (ProApprovalFlowNodeHandlerDto ccUser : ccUserList) {
                sendWeLinkNotice(todo.getTitle(), todo.getDesc(), ccUser.getHandler(), todo.getUrl());
            }
            timer.print("=====sendWeLinkNotice ccUserList");
        }

        timerAll.print("activate total");
    }

    /**
     * 获取待办
     *
     * @param bizData  业务数据
     * @param todoRule 待办
     * @return TodoDto
     */
    public static TodoDto getTodo(String bizData, String todoRule) {
        if (!StringUtils.hasText(todoRule)) {
            return new TodoDto();
        }
        String todoJson = "";
        if (todoRule.startsWith(RULE_STATIC)) {
            // static:{"title":"新增Site- AP审核-待审核(单据号:${documentNo})","desc":"新增Site-
            // AP审核-待审核","url":"${todoHost}/#/group/procurement/site-create?id=${siteMainId}"}
            todoJson = todoRule.substring(RULE_STATIC.length());
        }
        if (todoRule.startsWith(SPEL_VAR)) {
            // spel:#map['isSupMain']?'{\"title\":\"新增Site- AP审核-待审核(单据号:${documentNo})\",\"desc\":\"新增Site-
            // AP审核-待审核\",\"url\":\"${todoHost}/#/group/procurement/site-create?id=${siteMainId}\"}':7
            String spelExpression = todoRule.substring(SPEL_VAR.length());
            EvaluationContext evaluationContext = new StandardEvaluationContext();
            evaluationContext.setVariable(VAR_MAP, JSON.parseObject(bizData));
            ExpressionParser parser = new SpelExpressionParser();
            Expression exp = parser.parseExpression(spelExpression);
            todoJson = exp.getValue(evaluationContext, String.class);
        }
        if (StringUtils.hasText(bizData)) {
            Map<String, Object> map = JSON.parseObject(bizData).getInnerMap();
            for (String key : map.keySet()) {
                String replaceKey = "${" + key + "}";
                String value = map.get(key) == null ? "" : map.get(key).toString();
                value = value.replace("\"", "\\\"");
                todoJson = todoJson.replace(replaceKey, value);
            }
        }
        return JSON.parseObject(todoJson, TodoDto.class);
    }

    /**
     * 审批流发送待办
     *
     * @param instance    流程信息
     * @param handlerDto  处理人
     * @param todoDto     待办
     * @param approvalDto 审批参数
     */
    public void approvalSendTodo(ProApprovalFlowInstanceDto instance, ProApprovalFlowNodeHandlerDto handlerDto,
                                 TodoDto todoDto, ApprovalDto approvalDto) {
        String title = todoDto.getTitle();
        String desc = todoDto.getDesc();
        String url = todoDto.getUrl();
        Assert.hasText(title, "Todo title is null");
        Assert.hasText(title, "Todo desc is null");
        Assert.hasText(title, "Todo url is null");
        Assert.hasText(handlerDto.getHandler(), "Todo Handler is null");

        // 发送待办
        UserVO user = CommonUtil.getCurrentUser();
        TodoSaveDto toDoDto = new TodoSaveDto();
        toDoDto.setAppTaskId("APPROVAL_" + IdUtil.getSnowflake().nextIdStr());
        toDoDto.setType("COUNTERSIGN");
        toDoDto.setTitle(title);
        toDoDto.setDescription(desc);
        toDoDto.setApplicantAccount(user.getUserAccount());
        toDoDto.setHandlersAccount(Collections.singletonList(handlerDto.getHandler()));
        toDoDto.setUrl(url);
        toDoDto.setWpMsg(true);
        toDoDto.setWelinkMsg(true);
        toDoDto.setShowApplicantName(false);
        todoWelinkService.newToDoAsync(toDoDto);

        // 保存待办
        ProApprovalFlowTodoEntity todoEntity = new ProApprovalFlowTodoEntity();
        todoEntity.setAppTaskId(toDoDto.getAppTaskId());
        todoEntity.setFlowInstanceId(instance.getFlowInstanceId());
        todoEntity.setFlowNodeId(instance.getCurrentNode());
        todoEntity.setHandler(handlerDto.getHandler());
        todoEntity.setHandlerName(handlerDto.getHandlerName());
        todoEntity.setForwardUser(approvalDto.getForwardUser());
        todoEntity.setForwardUserName(approvalDto.getForwardUserName());
        todoEntity.setResult(ApprovalEnum.UNHANDLED.name());
        todoEntity.setActiveFlag(YES);
        CommonUtil.setUser(todoEntity);
        toDoService.save(todoEntity);

        // 发送WeLink通知
        sendWeLinkNotice(title, desc, handlerDto.getHandler(), url);
    }

    /**
     * 发送WeLink通知
     *
     * @param title   标题
     * @param desc    描述
     * @param handler 处理人
     * @param url     链接
     */
    public void sendWeLinkNotice(String title, String desc, String handler, String url) {
        WelinkSendDto sendDto = new WelinkSendDto();
        sendDto.setTitle(title);
        sendDto.setContent(desc);
        sendDto.setAccounts(handler);
        sendDto.setUrl(url);
        todoWelinkService.sendWeLinkNoticeAsync(sendDto);
    }

    /**
     * 获取节点
     *
     * @param curNode  当前节点
     * @param instance instanceInfo
     * @param nodeList nodeList
     * @param nodeMap  nodeMap
     */
    public void getNode(ProApprovalFlowNodeDto curNode, ProApprovalFlowInstanceDto instance,
                        List<ProApprovalFlowNodeDto> nodeList, Map<Integer, ProApprovalFlowNodeDto> nodeMap) {
        // 条件节点
        if (NodeTypeEnum.CONDITION.name().equals(curNode.getNodeType())) {
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(curNode.getConditionExpression());
            EvaluationContext evaluationContext = new StandardEvaluationContext();
            evaluationContext.setVariable(VAR_MAP, JSON.parseObject(instance.getBizData()));
            Long nextNodeId = expression.getValue(evaluationContext, Long.class);
            Optional<ProApprovalFlowNodeDto> nextOpt =
                    nodeMap.values().stream().filter(item -> item.getFlowNodeId().equals(nextNodeId)).findFirst();
            getNode(nextOpt.get(), instance, nodeList, nodeMap);
        }

        // 系统节点和审批节点
        if (NodeTypeEnum.SYSTEM.name().equals(curNode.getNodeType())
                || NodeTypeEnum.APPROVAL.name().equals(curNode.getNodeType())) {
            nodeList.add(curNode);
            if (YES.equals(curNode.getLastFlag())) {
                return;
            }
            getNode(nodeMap.get(curNode.getSortNum() + 1), instance, nodeList, nodeMap);
        }
    }
}
public class ProApprovalAssembler {
    private ProApprovalAssembler() {
    }

    /**
     * 实例entity转DTO
     *
     * @param entity ProApprovalFlowInstanceEntity
     * @return ProApprovalFlowInstanceDto
     */
    public static ProApprovalFlowInstanceDto instanceToDto(ProApprovalFlowInstanceEntity entity) {
        ProApprovalFlowInstanceDto flowInfoDto = new ProApprovalFlowInstanceDto();
        if (entity == null) {
            return flowInfoDto;
        }
        BeanUtils.copyProperties(entity, flowInfoDto);
        return flowInfoDto;
    }

    /**
     * 节点entity转DTO
     *
     * @param entity ProApprovalFlowNodeEntity
     * @return ProApprovalFlowInfoDto
     */
    public static ProApprovalFlowNodeDto nodeToDto(ProApprovalFlowNodeEntity entity) {
        ProApprovalFlowNodeDto nodeDto = new ProApprovalFlowNodeDto();
        if (entity == null) {
            return nodeDto;
        }
        BeanUtils.copyProperties(entity, nodeDto);
        return nodeDto;
    }

    /**
     * 流程entity转DTO
     *
     * @param entity ProApprovalFlowEntity
     * @return ProApprovalFlowDto
     */
    public static ProApprovalFlowDto flowToDto(ProApprovalFlowEntity entity) {
        ProApprovalFlowDto flowDto = new ProApprovalFlowDto();
        if (entity == null) {
            return flowDto;
        }
        BeanUtils.copyProperties(entity, flowDto);
        return flowDto;
    }

    /**
     * 处理人entity转DTO
     *
     * @param entity ProApprovalFlowNodeHandlerEntity
     * @return ProApprovalFlowNodeHandlerDto
     */
    public static ProApprovalFlowNodeHandlerDto nodeHandlerToDto(ProApprovalFlowNodeHandlerEntity entity) {
        ProApprovalFlowNodeHandlerDto nodeHandlerDto = new ProApprovalFlowNodeHandlerDto();
        if (entity == null) {
            return nodeHandlerDto;
        }
        BeanUtils.copyProperties(entity, nodeHandlerDto);
        return nodeHandlerDto;
    }
}

Guess you like

Origin blog.csdn.net/luo381821/article/details/131821956