Java与MySQL的精准结合:打造高效审批流程

news2024/10/7 18:20:52

1流程思路分析

审批流程🐱‍💻

1.串行流程

当前节点审批完成后,下一次节点才能进行操作,例如经理通过之后,总监才能审批;

图片

2.并行流程

一个审批节点需要多人联审。一般有两种方式:会签、或签;

图片

会签:

注意:别跟串行审批混淆。会签的多个角色是平行的,没有上下级关系、前后顺序。

例如:在某项文件、合同、决策或其他重要事项上,需要多个相关人员进行确认、批准或签署的情况下,参与该过程的人员同时批准,这时会签就能很大程度上提高效率,顺便也能解决人情世故的问题~~。

会签支持并签:

业务模式支持:"全部投票"、"按投票比例"、"按投票通过比例"、支持设置 "百分比" 和 "分数" 两种方式。

或签(也有叫"竞签"、"串签"):指同一个审批节点设置多个人,如ABC三人,三人会同时收到审批,只要其中任意一人审批即可到下一审批节点;

3.条件流程

根据不同规则,走不同流程

图片

例如报销5000以下直属领导审批就行,5000~10000需要总监审批。

4.混合流程

就是上述的流程混合运用;

图片

例如请假的正常流程是到直属领导审批,3天以上需要总监审批。5天以上需要总裁审批。

审批动作🙋‍♀️

  1. 通过: 通过申请,流转到下一人。原因选填。

  2. 驳回: 驳回到发起人、驳回到上一环节、驳回到之前的某一环节

  3. 保存: 保存当前填写的信息。

  4. 转交: 转交给某人审批

  5. 加签: 处理者可以在审批时临时增加审核人员

  6. 抄送: 抄送人会知晓审核过程,但不进行操作

执行动作👩‍🎓

申请: 针对当前业务表单信息发起申请,开始审批流程的传递。分为2部分:由申请提交人手动发起申请、由程序自动判断满足触发规则的数据自动发起申请;另外还要注意的2点:是否允许提交人撤销(是、否)、记录编辑(不可编辑、管理员可编辑、管理员和审批人都可编辑 );

通过: 当前步骤处理通过,进入下一步骤,若为末步骤,则流程处理完成;

退回: 将步骤退回至上一步骤,即返回至上一处理人处,若为首步骤,则不进行退回;

否决: 将步骤直接结束,执行结束动作拒绝活动,不再进行操作,或者回退至第一步骤;

撤回: 若当前步骤已处理,下一处理人未处理的情况下可进行撤回操作。

图片

节点状态🕵️‍♀️

提交人: 未提交、已提交、处理中、已结束。

处理人: 待处理、已处理。

创建业务工单: 提交人登录系统之后,选择想要发起的业务工单,填写业务工单。填写完成可以选择提交或者保存;

未提交: 列表中显示所有保存但未提交的业务工单,未提交列表中的业务工单都没有业务工单编号(唯一),状态为“未提交”。提交人可以选择某个业务工单后能方便对其进行修改、删除和提交;

已提交: 列表显示所有已经提交的业务工单,有业务工单编号,并显示(未处理)。提交后的业务工单进入处理流程,提交人无法进行修改和删除;

处理中: 列表显示所有提交并已经有节点处理的业务工单,提交人可以查询某个业务工单的处理进度;

已结束: 列表显示所有已经处理完成的业务工单;

待处理: 列表显示所有待处理业务工单;

已处理: 列表显示所有当前处理人已处理的业务工单,既被当前处理人流转到下一个流程节点的业务工单;

2UI预览🛹

图片

图片

图片

图片

图片

3表设计🚧

流程配置表🚗

CREATE TABLE `approval_config` (
  `approval_config_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '雪花算法',
  `corp_id` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '企业ID',
  `node_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '节点名称',
  `form_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '表单ID 属于那个表单的流程',
  `approval_auto_pass` tinyint(1) DEFAULT '0' COMMENT '审批节点人员相同时自动通过[1:true;0:false]',
  `current_node_approver_type` tinyint DEFAULT NULL COMMENT '审批人类型[1:发起人自己;2:指定角色;3:指定人员;4:表单内人员];注:1/3/4存userid、2存roleId',
  `current_node_approver` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '待审批人如果是角色就是角色ID,如果是其他则是userId格式:[xxx,xxx]',
  `care_of` tinyint(1) DEFAULT NULL COMMENT '允许审批人转交[1:true;2:false]',
  `approval_type` tinyint DEFAULT NULL COMMENT '审批类型[1:会签:2:或签]',
  `approval_node` int DEFAULT NULL COMMENT '审批节点[取值:1~N 从小到大;0:发起节点 、1:是第一个节点、 2:第二个节点 N:是第N个节点;最大的是结束节点]',
  `carbon_copy_recipients_type` tinyint DEFAULT '1' COMMENT '抄送人类型[1:发起人自己;2:指定角色;3:指定人员;4:表单内人员]',
  `carbon_copy_recipients` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '抄送人userId 数组格式',
  `create_time` datetime DEFAULT NULL COMMENT '规则创建时间;默认拿最新的',
  `group_id` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分组[标识那些节点是同一个组的]',
  `visible_fields` json DEFAULT NULL COMMENT '可见字段:[{"bt": false, "kj": true, "kx": true, "field": "create_time", "children": "purchase_requisition_details"}];\r\nkj:可见;kx:可写;kb:必填。如果是最后一个节点就需要单独拉一个配置',
  PRIMARY KEY (`approval_config_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='审批流程节点规则配置表';

审批流程发起表🚓

CREATE TABLE `approval_info` (
  `approval_info_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '雪花算法',
  `corp_id` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '企业ID',
  `approval_config_group_id` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则表的groupID',
  `form_id` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '表单ID 属于那个表单',
  `founder` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '发起人',
  `launch_time` datetime NOT NULL COMMENT '发起时间',
  `end_state` tinyint(1) DEFAULT '0' COMMENT '是否结束流程[1是]',
  `end_time` datetime DEFAULT NULL COMMENT '流程结束时间',
  `object_id` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '关联某条数据',
  `process_progress` int DEFAULT '0' COMMENT '用来记录整个流程进行到那个节点',
  `record_node_history` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '记录历史经历节点,英文逗号分隔0,1,2,1,2,3 最后一个值和当前节点同步',
  PRIMARY KEY (`approval_info_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='审批流程发起表';

审批记录表🚕

CREATE TABLE `approval_record` (
  `approval_record_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '雪花算法',
  `corp_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '企业ID',
  `approval_info_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '审批发起表approval_info',
  `form_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '表单ID 属于那个表单的流程',
  `approver_user_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '审批人or 转交人 userId',
  `approver_time` datetime NOT NULL COMMENT '审批时间',
  `approver_opinions` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '审批意见',
  `approver_result` tinyint NOT NULL COMMENT '审批结果[1:同意;2:拒绝:3:转交]',
  `transferee` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '被转交人userId;approver_result=3时必填',
  `transferee_text` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '转交备注(此字段暂时无用)',
  `accessory_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '附件数组',
  `approver_node` tinyint NOT NULL COMMENT '属于个节点的审批记录',
  `approver_count` tinyint DEFAULT '1' COMMENT '审批次数默认1; 例:当节点B是会签,且有人同意,有人拒绝,当拒绝后会返回上一个节点A。当再次进入节点B时,用来区分,此审批记录是第一次记录还是第二次审批记录或者第N次',
  PRIMARY KEY (`approval_record_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='审批记录表';

抄送记录表🛺

CREATE TABLE `approval_carbon_copy` (
  `carbon_copy_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '雪花',
  `corp_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '企业ID',
  `form_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '属于那个表单',
  `approval_info_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '推送那条审批的ID',
  `user_id` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '接收抄送人',
  `create_time` datetime NOT NULL COMMENT '抄送时间',
  `read` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已读[1:已读;0:未读]',
  `approval_node` tinyint NOT NULL COMMENT '那个节点抄送的',
  PRIMARY KEY (`carbon_copy_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='审批抄送记录表';

4思路👩‍🎓👩‍🎓

流程配置

1.通过流程节点规则配置表,配置每个节点的审批人、抄送人、审批规则、可见、可写、必填字段的规则等(可以给不同的表单配置不同的审批规则)。

2.配置完毕即可,每次新增数据,读取流程规则配置表,生成一条审批流程发起记录,记录发起人,发起时间,关联某条流程规则(旧数据还是使用旧流程),流程当前节点等。

3.每个节点的审批人、抄送人、可见、可写、必填字段都是根据流程规则配置表中动态配置。

5代码🥇

审批按钮

@Transactional(rollbackFor = Exception.class)
public ResultUtils approval(String token, ApprovalDTO param) {
    String corpId = TokenUtils.getCorpId(token);
    String userId = TokenUtils.getUserId(token);
    LocalDateTime time = LocalDateTime.now();

    // 找到当前数据待审批节点,是否需要自己审批,或者已经审批过
    ApprovalInfo info = approvalInfoMapper.selectOne(new QueryWrapper<ApprovalInfo>().eq("corp_id", corpId).eq("object_id", param.getObjectId())
            .eq("form_id", param.getFormId()).select("approval_info_id", "process_progress", "record_node_history", "end_state",
                    "approval_config_group_id","founder"));
    Assert.isFalse(info.getEndState(), ErrorMsg.msg18);
    Integer currentNode = info.getProcessProgress();// 当前节点

    // 每个节点的审批设置
    List<ApprovalConfig> approvalConfigs = approvalConfigMapper.selectList(new QueryWrapper<ApprovalConfig>().eq("corp_id", corpId)
            .eq("group_id", info.getApprovalConfigGroupId())
            .eq("form_id", param.getFormId()).select("approval_auto_pass", "current_node_approver_type", "visible_fields",
                    "current_node_approver", "care_of", "approval_type", "approval_node", "carbon_copy_recipients_type", "carbon_copy_recipients"));

    Map<Integer, ApprovalConfig> configMap = approvalConfigs.stream().collect(Collectors.toMap(ApprovalConfig::getApprovalNode, ApprovalConfig -> ApprovalConfig));
    ApprovalConfig config = configMap.get(info.getProcessProgress());

    List<String> historyNode = Lists.newArrayList(info.getRecordNodeHistory().split(","));
    // 历史节点需要剔除最后一个
    historyNode.remove(historyNode.size() - 1);
    Bag bag = new HashBag(historyNode);
    int count = bag.getCount(currentNode.toString());// 这个节点历史是否出现过
    count = count == 0 ? 1 : (count + 1); // 用于标注这个节点是否多次审批

    if (param.getType() == 3) {
        // 转交
        ApprovalRecord record = new ApprovalRecord();
        record.setApprovalRecordId(SnowFlakeUtil.getDefaultSnowFlakeId() + "");
        record.setCorpId(corpId);
        record.setApprovalInfoId(info.getApprovalInfoId());
        record.setFormId(param.getFormId());
        record.setApproverUserId(userId);
        record.setApproverTime(time);
        record.setApproverOpinions(param.getMessage());
        record.setApproverResult(param.getType());
        record.setTransferee(param.getTransfereeUserId());
        if (ObjectUtil.isNotEmpty(param.getAccessoryUrl())) {
            record.setAccessoryUrl(JSONUtil.toJsonStr(param.getAccessoryUrl()));
        }
        record.setApproverNode(info.getProcessProgress().byteValue());
        record.setApproverCount(new Byte(count + ""));
        approvalRecordMapper.insertSelective(record);
        return ResultUtils.success();
    }

    ApprovalRecord records = approvalRecordMapper.selectOne(new QueryWrapper<ApprovalRecord>()
            .eq("corp_id", corpId).eq("form_id", param.getFormId())
            .eq("approver_user_id", userId).eq("approval_info_id", info.getApprovalInfoId())
            .eq("approver_node", currentNode).eq("approver_count", count));
    Assert.isFalse(ObjectUtil.isNotEmpty(records) && StrUtil.isNotBlank(records.getTransferee()), ErrorMsg.msg29);

    Assert.isFalse(ObjectUtil.isNotEmpty(records), ErrorMsg.msg16);
    // 查询是否是被转交人
    List<String> transferees = approvalRecordMapper.findTransferee(corpId, param.getFormId(), info.getApprovalInfoId(), currentNode, count);
    if (CollUtil.isEmpty(transferees)) {
        // 1.是否需要自己审批
        if (config.getCurrentNodeApproverType() == 2) {
            // 指定角色
            // 查询自己拥有那些角色
            List<UserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<UserRole>().eq("user_id", userId).eq("corp_id", corpId)
                    .select("role_id"));
            List<String> roleIds = userRoles.stream().map(key -> key.getRoleId()).collect(Collectors.toList());
            List<Integer> currentNodeApproverRoleId = JSONUtil.toList(config.getCurrentNodeApprover().toString(), Integer.class);
            ArrayList<String> integers = new ArrayList<>(roleIds);
            boolean b = integers.retainAll(currentNodeApproverRoleId);// 存在交集则返回true
            Assert.isFalse(!b, ErrorMsg.msg17);
        } else if (config.getCurrentNodeApproverType()==3){
            // 指定人员 用户ID
            List<String> currentNodeApproverUserId = JSONUtil.toList(config.getCurrentNodeApprover().toString(), String.class);
            boolean contains = currentNodeApproverUserId.contains(userId);
            Assert.isFalse(!contains, ErrorMsg.msg17);
        } else if (config.getCurrentNodeApproverType() == 1) {
           // 发起人自己
            Assert.isFalse(!info.getFounder().equals(userId), ErrorMsg.msg17);
        }
    }
    if (param.getType() == 2) {
        // 拒绝
        approvalRefuse(info, approvalConfigs, param, userId, corpId, count, time);
        return ResultUtils.success();
    }

    // 判断了需要自己审批之后,先编辑数据,然后在进行审批操作
    JSONObject paramData = param.getMainData();
    approvalEditData(corpId, paramData, time, userId, param.getFormId());

    // 进行审批操作
    if (param.getType() == 1) {
        // 同意
        approvalAgreeing(currentNode, info, configMap, param, userId, corpId, time, param.getObjectId());
    }
    return ResultUtils.success();
}

审批按钮-拒绝

/**
 * 审批按钮-拒绝
 *
 * @param info            当前数据的审批详情
 * @param approvalConfigs 审批规则
 * @param param           参数
 * @param userId          当前审批人id
 * @param corpId          企业id
 * @param count           审批次数
 */
private void approvalRefuse(ApprovalInfo info, List<ApprovalConfig> approvalConfigs, ApprovalDTO param, String userId, String corpId, int count, LocalDateTime time) {
    // 生成审批记录
    ApprovalRecord record = new ApprovalRecord();
    record.setApprovalRecordId(SnowFlakeUtil.getDefaultSnowFlakeId() + "");
    record.setCorpId(corpId);
    record.setApprovalInfoId(info.getApprovalInfoId());
    record.setFormId(param.getFormId());
    record.setApproverUserId(userId);
    record.setApproverTime(time);
    record.setApproverOpinions(param.getMessage());
    record.setApproverResult(param.getType());
    if (ObjectUtil.isNotEmpty(param.getAccessoryUrl())) {
        record.setAccessoryUrl(JSONUtil.toJsonStr(param.getAccessoryUrl()));
    }
    record.setApproverNode(info.getProcessProgress().byteValue());
    record.setApproverCount(new Byte(count + ""));
    approvalRecordMapper.insertSelective(record);

    // 回滚上一个节点
    info.setProcessProgress(info.getProcessProgress() - 1);
    info.setRecordNodeHistory(info.getRecordNodeHistory() + "," + info.getProcessProgress());
    // info.setCurrentState(1);
    approvalInfoMapper.updateByPrimaryKeySelective(info);

    if (info.getProcessProgress() == 0) {
        // 如果回到第一个节点,数据状态则变成初始
        String formCode = fromConfigMapper.findFormCode(corpId, param.getFormId());
        commonMapper.updateDataState(formCode, 0, param.getObjectId(), corpId);


    }
}

审批按钮-同意

/**
 * 审批按钮-同意
 *
 * @param currentNode 处理的当前节点
 * @param info        流程详情
 * @param configMap   配置集合 key=节点;val=配置
 * @param param       参数
 * @param userId      当前审批人userId
 * @param corpId      企业ID
 * @param time        当前时间
 */
private void approvalAgreeing(Integer currentNode, ApprovalInfo info, Map<Integer, ApprovalConfig> configMap,
                              ApprovalDTO param, String userId, String corpId, LocalDateTime time, String objectId) {
    // 查询这个节点以前是否审批过
    List<String> historyNode = Lists.newArrayList(info.getRecordNodeHistory().split(","));// 历史节点需要剔除最后一个
    historyNode.remove(historyNode.size() - 1);
    Bag bag = new HashBag(historyNode);
    int count = bag.getCount(currentNode.toString());// 这个节点历史是否出现过
    count = count == 0 ? 1 : count + 1; // 用于标注这个节点是否多次审批

    // 生成审批记录
    ApprovalRecord record2 = new ApprovalRecord();
    record2.setApprovalRecordId(SnowFlakeUtil.getDefaultSnowFlakeId() + "");
    record2.setCorpId(corpId);
    record2.setApprovalInfoId(info.getApprovalInfoId());
    record2.setFormId(param.getFormId());
    record2.setApproverUserId(userId);
    record2.setApproverTime(time);
    record2.setApproverOpinions(param.getMessage());
    record2.setApproverResult(param.getType());
    if (ObjectUtil.isNotEmpty(param.getAccessoryUrl())) {
        record2.setAccessoryUrl(JSONUtil.toJsonStr(param.getAccessoryUrl()));
    }
    record2.setApproverNode(currentNode.byteValue());
    record2.setApproverCount(new Byte(count + ""));
    approvalRecordMapper.insertSelective(record2);
    // 获取当前节点配置
    ApprovalConfig currentConfig = configMap.get(currentNode);
    if (currentConfig.getApprovalType() == 1) {// 1会签
        // 判断是否是最后一个审批的,如果不是return出去
        List<String> userIds = null;
        if (currentConfig.getCurrentNodeApproverType() == 2) {
            // 角色id
            // 查询自己拥有那些角色
            List<UserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<UserRole>().eq("user_id", userId)
                    .eq("corp_id", corpId).select("user_id"));
            userIds = userRoles.stream().map(key -> key.getUserId()).collect(Collectors.toList());
        } else if (currentConfig.getCurrentNodeApproverType()==3){
            // 用户ID
            userIds = JSONUtil.toList(currentConfig.getCurrentNodeApprover().toString(), String.class);
        } else if (currentConfig.getCurrentNodeApproverType() == 1) {
            // 发起人自己
            userIds = Arrays.asList(info.getFounder());
        }
        // 查询已经审批的人!=待审批人数则不是最后一个人
        // 1.查询已经审批的人数
        Integer count1 = approvalRecordMapper.selectCount(new QueryWrapper<ApprovalRecord>().eq("corp_id", corpId).eq("approval_info_id", info.getApprovalInfoId())
                .eq("form_id", param.getFormId()).eq("approver_node", currentNode).eq("approver_count", count));
        if (userIds.size() != count1) {
            return;// 不是最后一个可以退出
        }
    }
    Integer nextNode = currentNode + 1;
    // 更新流程信息
    info.setProcessProgress(nextNode);
    info.setRecordNodeHistory(info.getRecordNodeHistory() + "," + info.getProcessProgress());
    // info.setCurrentState(2);
    // 到这里如果不是或签,就是会签是最后一个人同意可以判断进入下一个节点
    //  判断下一个节点是不是最后一个节点,如果是可以结束节点了,不是则继续往下递归
    if ((configMap.keySet().size() - 1) == nextNode) {
        info.setEndTime(time);
        info.setEndState(true);//  结束流程
        approvalInfoMapper.updateByPrimaryKeySelective(info);
        // 数据状态变成生效
        String formCode = fromConfigMapper.findFormCode(corpId, param.getFormId());
        commonMapper.updateDataState(formCode, 2, objectId, corpId);
        return;
    }
    approvalInfoMapper.updateByPrimaryKeySelective(info);


    // 校验下一个配置是否需要审批
    ApprovalConfig nextConfig = configMap.get(nextNode);// 下一个节点的配置

    List<RuleVO> rule = JSONUtil.toList(JSONUtil.parseArray(nextConfig.getVisibleFields()), RuleVO.class);
    for (RuleVO ruleVO : rule) {
        if (ruleVO.getKx()) {
            return;// 判断是否开了可写,如果开了可写,不管有没有开相同审批人自动通过,都不能往下走
        }
    }

    if (nextConfig.getApprovalAutoPass()) {
        // 开了 审批人员相同自动通过
        // 1.是否需要自己审批
        if (nextConfig.getCurrentNodeApproverType() == 2) {
            // 角色id
            // 查询自己拥有那些角色
            List<UserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<UserRole>().eq("user_id", userId).eq("corp_id", corpId).select("role_id"));
            List<String> roleIds = userRoles.stream().map(key -> key.getRoleId()).collect(Collectors.toList());
            List<Integer> currentNodeApproverRoleId = JSONUtil.toList(nextConfig.getCurrentNodeApprover().toString(), Integer.class);
            ArrayList<String> integers = new ArrayList<>(roleIds);
            boolean b = integers.retainAll(currentNodeApproverRoleId);// 存在交集返回true
            if (!b) return;
        } else {
            // 用户ID
            List<String> currentNodeApproverUserId = JSONUtil.toList(nextConfig.getCurrentNodeApprover().toString(), String.class);
            boolean contains = currentNodeApproverUserId.contains(userId);
            if (!contains) return;
        }
        // 需要自己审批
        approvalAgreeing(nextNode, info, configMap, param, userId, corpId, time, objectId);
    }
    // approvalRecordMapper.insertSelective(record2);

}

合并权限(如果最后一个节点存在多个自定义权限组,就需要合并)

/**
 * 合并权限组
 *
 * @param roleIds      当前用户拥有的角色
 * @param dbPowerGroup 自定义权限组
 */
private List<RuleVO> mergePowerGroup(List<String> roleIds, List<PowerGroup> dbPowerGroup) {
    List<RuleVO> result = new ArrayList<>();
    // 判断当前自定义权限组是否包含 当前用户的角色
    for (PowerGroup power : dbPowerGroup) {
        List<Integer> dbRoleIds = JSONUtil.toList(power.getRoleId().toString(), Integer.class);
        List<String> retainAll = new ArrayList<>(roleIds);
        retainAll.retainAll(dbRoleIds);
        if (retainAll.size() != 0) {// 存在交集,证明这个自定义权限组有当前操作用户
            List<RuleVO> rule = JSONUtil.toList(JSONUtil.parseArray(power.getVisibleFields()), RuleVO.class);
            Map<String, RuleVO> tempMapDB = rule.stream().collect(Collectors.toMap(ruleVo -> ruleVo.getChildren() + ruleVo.getField(), RuleVO -> RuleVO));
            if (CollUtil.isEmpty(result)) {
                result = rule;
            } else {
                Map<String, RuleVO> tempListMap = result.stream().collect(Collectors.toMap(ruleVo -> ruleVo.getChildren() + ruleVo.getField(), RuleVO -> RuleVO));
                if (tempMapDB.keySet().size() > result.size()) {// 如果B权限组字段比A权限组多,就拿多的一方进行循环
                    for (RuleVO item : rule) {
                        RuleVO ruleVO = tempListMap.get(item.getChildren() + item.getField());
                        if (ruleVO.getBt()) {
                            item.setBt(ruleVO.getBt());
                        }
                        if (ruleVO.getKj()) {
                            item.setKj(ruleVO.getKj());
                        }
                        if (ruleVO.getKx()) {
                            item.setKx(ruleVO.getKx());
                        }
                    }
                    result = rule;
                } else {
                    // 否则就拿A权限组遍历
                    for (RuleVO item : result) {
                        // 合并组成新的规则,如果A规则为true,B规则为false,则取true的一方
                        RuleVO ruleDB = tempMapDB.get(item.getChildren() + item.getField());
                        if (ruleDB.getBt()) {
                            item.setBt(ruleDB.getBt());
                        }
                        if (ruleDB.getKj()) {
                            item.setKj(ruleDB.getKj());
                        }
                        if (ruleDB.getKx()) {
                            item.setKx(ruleDB.getKx());
                        }
                    }
                }
            }

        }
    }
    return result;
}

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1443477.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python中的嵌套字典访问与操作详解

前言 在Python编程中&#xff0c;嵌套字典是一种常见的数据结构&#xff0c;它可以以层次结构的方式组织和存储数据。嵌套字典通常包含字典内嵌套在其他字典中&#xff0c;创建了一种多层级的数据结构。本文将详细介绍如何在Python中访问和操作嵌套字典&#xff0c;包括访问、…

视频无损放大修复工具Topaz Video AI 新手入门教程

想要自学Topaz Video AI &#xff1f;Topaz Video AI 如何使用&#xff1f;这里给大家带来了视频无损放大修复工具Topaz Video AI 新手入门教程&#xff0c;快来看看吧&#xff01; 下载&#xff1a;Topaz Video AI for mac 导入您的文件 有两种方法可以将文件导入 Topaz Vid…

C++重新入门-C++变量作用域

目录 1.C变量定义 2.C作用域 3.局部变量 4.全局变量 5.块作用域变量 6.初始化局部变量和全局变量 1.C变量定义 一般来说有三个地方可以定义变量&#xff1a; 在函数或一个代码块内部声明的变量&#xff0c;称为局部变量。 在函数参数的定义中声明的变量&#xff0c;称为…

【博云2023】乘龙一跃腾云海,侧目抬手摘星河

癸卯渐远&#xff0c;甲辰渐至&#xff0c;预示着被汗水浇灌的种子&#xff0c;必将顶开冻土&#xff0c;迎接阳光。 每逢春节&#xff0c;当亲友彼此问候&#xff0c;博云人总能自豪地说&#xff0c;我们认真地、努力地奋斗&#xff0c;让我们能自信地踏上新的征程。 我们的…

架构(十二)动态Excel

一、引言 作者最近的平台项目需要生成excel&#xff0c;excel的导入导出是常用的功能&#xff0c;但是作者想做成动态的&#xff0c;不要固定模板&#xff0c;那就看看怎么实现。 二、后端 先捋一下原理&#xff0c;前后端的交互看起来是制定好的接口&#xff0c;其实根本上是…

安全的接口访问策略

渗透测试 一、Token与签名 一般客户端和服务端的设计过程中&#xff0c;大部分分为有状态和无状态接口。 一般用户登录状态下&#xff0c;判断用户是否有权限或者能否请求接口&#xff0c;都是根据用户登录成功后&#xff0c;服务端授予的token进行控制的。 但并不是说有了tok…

4核8G服务器配置性能怎么样?12M带宽配置服务器能干什么?

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线&#xff1f;通用型-4核8G-180G-2000G&#xff0c;2000GB月流量&#xff0c;系统盘为180GB SSD盘&#xff0c;12M公网带宽&#xff0c;下载速度峰值为1536KB/s&#xff0c;即1.5M/秒&#xff0c;假设网站内页平均大小为60KB…

16.1 Spring框架_SpringIoC容器与Bean管理(❤❤❤❤)

16.1 Spring框架_SpringIoC容器与Bean管理 1. Spring IOC1.1 IoC控制反转 1. Spring IOC 1.1 IoC控制反转 需要自己查找3种苹果的特色,从而选择符合自己的需求 告诉水果店老板自己的口味,由老板推荐哪种苹果,省去自己查询水果特点 在java中,各种水果就是各种对象,买水果就是创…

问题:2、计算机网络的目标是实现________。 #媒体#知识分享

问题&#xff1a;2、计算机网络的目标是实现________。 A&#xff0e;数据处理 B&#xff0e;信息传输与数据处理 C&#xff0e;资源共享与信息传输 D&#xff0e;文献查询 参考答案如图所示

【用pycharm安装第三方库时出现错误】【‘pip‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。】

目录 一、问题描述 二、解决过程 1、pip的版本不是最新版本导致安装不了第三方库 2、pip最新版本安装出错 3、pip 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件” 4、 ERROR: Could not find a version that satisfies the requirement PIL (from versi…

政安晨:示例演绎机器学习中(深度学习)神经网络的数学基础——快速理解核心概念(二){两篇文章讲清楚}

这一篇与上一篇是兄弟篇&#xff0c;意在通过两篇文章讲清楚深度学习中神经网络的数学基础&#xff0c;第一次看到这篇文章的小伙伴可以从上一篇文章看起&#xff08;包括搭建环境等等都在上一篇&#xff09;&#xff0c;上一篇链接如下&#xff1a; 政安晨&#xff1a;示例演…

Flink基础篇|001_Flink是什么

&#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f3c6; 近期荣誉&#xff1a;华为云云享专家、阿里云专家博主、腾讯云优秀创作者 &#x1f525; 三连支持&#xff1a;欢迎 ❤️关注、&#x…

Linux系统调试课:Linux错误码介绍

文章目录 一、错误码二、错误码返回案例三、使用 goto 语句沉淀、分享、成长,让自己和他人都能有所收获!😄 📢错误代码由内核或用户空间应用程序(通过errno变量)解释。错误处理在软件开发中非常重要,而不仅仅是在内核开发中。幸运的是,内核提供的几种错误,几乎涵盖了可…

InternLM大模型实战-1.书生浦语大模型全链路开源体系

文章目录 前言笔记正文大模型成为热门关键词书生浦语开源历程从模型到应用书生浦语全链条开源开放体系数据预训练微调评测部署部署智能体LagentAgentLego 总结 前言 本系列文章是参与书生浦语全链路开源体系学习的笔记文章。B站视频教程地址&#xff1a; 笔记正文 大模型成为…

【笔记】Harmony学习:下载安装 DevEco Studio 开发工具IDE

IDE 安装 从官网下载DevEco Studio 安装包后进行安装&#xff0c; 安装完毕后&#xff0c;本地环境可能要配置相关工具&#xff0c;可以通过下面的诊断检测一下本地环境&#xff0c;通过蓝色“Set it up now” 可以快速安装。 1. Node.js (for ohpm) 2. ohpm 下载op的包管理&a…

项目学习记录

项目开发 创建项目环境配置关联git新增模块项目启动打印地址日志使用httpclient进行idea内部控制台测试使用AOP拦截器打印日志 创建项目 创建一个空项目&#xff0c;并勾选下面选项 然后进入pom.xml中修改项目配置 根据这个链接选则&#xff0c;修改项目的支持版本 链接&#…

css2复合选择器

一.后代&#xff08;包含&#xff09;选择器&#xff08;一样的标签可以用class命名以分别&#xff09; 空格表示 全部后代 应用 二.子类选择器 >表示 只要子不要孙 应用 三.并集选择器 &#xff0c;表示 代表和 一般竖着写 应用 四.伪类选择器&#xff08;包括伪链接…

【C++】类和对象(2)

这篇博客继续学习类和对象~&#xff0c;主要介绍了类的6个默认成员函数。 目录 类的6个默认成员函数 构造函数 概念 特性 析构函数 概念 特性 拷贝构造函数 特性 赋值运算符重载 运算符重载 赋值运算符重载 前置和后置重载 日期类的实现 const成员 取地址及cons…

【十三】【C++】vector简单实现

代码实现 /*vector类简单实现*/ #if 1 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std; #include <vector> #include <algorithm> #include <crtdbg.h> #include <assert.h> #include <string.h>namespace MyVe…

SpringBoot循环依赖

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; SpringBoot循环依赖 循环依赖是指两个或多个模块或组件之间相互依赖形成闭环的情况。这种情况下&#xff0c;模块 A 依赖于模块 B&#xff0c;同时模块 B 也依赖于模块…