数据权限的设计与实现系列11——前端筛选器组件Everright-filter集成功能完善2

筛选条件数据类型完善

文本类

筛选器组件给了一个文本类操作的范例,如下:

            Text: [
                {
                  label: '等于',
                  en_label: 'Equal',
                  style: 'noop'
                },
                {
                  label: '等于其中之一',
                  en_label: 'Equal to one of',
                  value: 'one_of',
                  style: 'tags'
                },
                {
                  label: '不等于',
                  en_label: 'Not equal',
                  value: 'not_equal',
                  style: 'noop'
                },
                {
                  label: '包含',
                  en_label: 'Contains',
                  value: 'contains',
                  style: 'noop'
                },
                {
                  label: '不包含',
                  en_label: 'Not contain',
                  value: 'not_contain',
                  style: 'noop'
                },
                {
                  label: '为空',
                  en_label: 'Empty',
                  value: 'empty',
                  style: 'none'
                },
                {
                  label: '不为空',
                  en_label: 'Not empty',
                  value: 'not_empty',
                  style: 'none'
                }
              ]

即以下几种情况:

  • 等于
  • 等于其中之一
  • 不等于
  • 包含
  • 不包含
  • 为空
  • 不为空

平台认为不是太合理,做了以下调整:

  1. 等于其中之一很少用,并且可以等价转换一个条件组,内含多个等于关系的条件,组内逻辑关系为或,因此去除
  2. 对于字符串,常用的主要是模糊匹配、以……开始、以……结束,因此将包含调整为模糊匹配、去除不包含,增加以……开始、以……结束两种,其他保留不变,同时对操作符的编码也做了相应调整。

调整完成后如下:

              Text: [
                {
                  label: '等于',
                  value: 'EQ',
                  style: 'noop'
                },
                {
                  label: '模糊匹配',
                  value: 'LK',
                  style: 'noop'
                },

                {
                  label: '以…开始',
                  value: 'RL',
                  style: 'noop'
                },
                {
                  label: '以…结束',
                  value: 'LL',
                  style: 'noop'
                },
                {
                  label: '不等于',
                  value: 'NE',
                  style: 'noop'
                },
                {
                  label: '为空',
                  value: 'NL',
                  style: 'none'
                },
                {
                  label: '不为空',
                  value: 'NN',
                  style: 'none'
                }
              ]

后端将规则转换成SQL片段的功能方法也要进行相应调整,这里参考了平台原本的通用查询功能的部分处理,改造如下:

@Override
    public String generateSqlPart(String id, String rule) {
        String result = "";
        // 转换数据
        DataFilterRuleVO dataFilterRule = JSON.parseObject(rule, DataFilterRuleVO.class);
        // 获取组集合
        List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();
        // 存放各组对应的条件sql片段
        List<String> groupSqlPartList = new ArrayList<>(dataFilterGroupList.size());
        // 遍历组集合
        for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {
            // 获取条件集合
            List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();
            if (CollectionUtils.isNotEmpty(conditionList)) {
                // 存放各条件对应的条件sql片段
                List<String> conditionSqlPartList = new ArrayList<>(conditionList.size());
                // 遍历条件集合
                for (DataFilterConditionVO condition : conditionList) {
                    // 获取字段名,命名风格驼峰转换成下划线
                    String fieldName = CommonUtil.camelToUnderline(condition.getProperty());
                    Object value = condition.getValue();
                    // 获取操作
                    String operator = condition.getOperator();
                    QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);
                    QueryWrapper<?> queryWrapper = new QueryWrapper<>();
                    addEasyQuery(queryWrapper, fieldName, queryRule, value);

                    log.info(queryWrapper.getSqlSegment());
                    log.info(queryWrapper.getParamNameValuePairs().toString());
                    // 放入结果列表缓存                 
                    conditionSqlPartList.add(queryWrapper.getTargetSql());
                }
                // 完成各条件的处理后,进行组内拼接
                if (conditionList.size() > 1) {
                    String logicalOperator = dataFilterGroup.getLogicalOperator();
                    StringBuffer sb = new StringBuffer();
                    sb.append("(");
                    conditionSqlPartList.stream().forEach(sqlPart -> {
                        sb.append(sqlPart);
                        sb.append(" " + logicalOperator + " ");
                    });
                    sb.delete(sb.length() - logicalOperator.length() - 1, sb.length());
                    sb.append(")");
                    groupSqlPartList.add(sb.toString());

                } else {
                    groupSqlPartList.add("(" + conditionSqlPartList.get(0) + ")");

                }
            }

        }

这里调用了一个方法,addEasyQuery,借助了MybatisPlus组件的条件构造器功能,如下:

/**
     * 根据规则走不同的查询
     *
     * @param queryWrapper QueryWrapper
     * @param name         字段名字
     * @param rule         查询规则
     * @param value        查询条件值
     */
    private void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {

        if (value == null || rule == null || ObjectUtils.isEmpty(value)) {
            return;
        }
        name = CommonUtil.camelToUnderline(name);
        switch (rule) {
            case GT:
                queryWrapper.gt(name, value);
                break;
            case GE:
                queryWrapper.ge(name, value);
                break;
            case LT:
                queryWrapper.lt(name, value);
                break;
            case LE:
                queryWrapper.le(name, value);
                break;
            case EQ:
                queryWrapper.eq(name, value);
                break;
            case NE:
                queryWrapper.ne(name, value);
                break;
            case IN:
                if (value instanceof String) {
                    queryWrapper.in(name, (Object[]) value.toString().split(COMMA));
                } else if (value instanceof String[]) {
                    queryWrapper.in(name, (Object[]) value);
                } else {
                    queryWrapper.in(name, value);
                }
                break;
            case LK:
                queryWrapper.like(name, value);
                break;
            case LL:
                queryWrapper.likeLeft(name, value);
                break;
            case RL:
                queryWrapper.likeRight(name, value);
                break;
            default:
                log.info("--查询规则未匹配到---");
                break;
        }
    }

如何获取完整SQL片段

此处遇到了一个棘手的问题。
我们需要获取到完整的SQL片段,类似这种:((param_value = ‘10’ and param_name like ‘%用户%’ ) or (param_key like ‘%password%’) )。
但是MybatisPlus组件的条件构造器QueryWrapper拿不到这种完整的sql片段,而是将sql语句参数和值分开保存的,通过getSqlSegment可以获取到参数化的sql语句,通过getParamNameValuePairs可以获取到参数值,如下:

查了半天资料,也没找到现成的方法可用,将这二者处理成我们期望的完整SQL片段。
于是有两种方案可选,一种是放弃借助QueryWrapper,自己来拼接SQL语句;一种是仍然使用QueryWrapper,自己来实现转换功能。
综合考虑了下,后一种的扩展性更好一些,只是写个单条件的字符串替换处理,复杂度不高,需要注意区分数值类和非数值类类型,相应的处理时要不要加单引号。

转换方法行不复杂,如下:

 /**
     * 将参数化的SQL填充值处理为完整的SQL片段
     *
     * @param sqlSegment          参数化的SQL片段
     * @param paramNameValuePairs 参数值对
     * @param numberFlag          是否为数值类型
     * @return 完整的SQL片段
     */
    private String handleSqlPart(String sqlSegment, Map<String, Object> paramNameValuePairs, boolean numberFlag) {

        for (int i = 0; i < paramNameValuePairs.size(); i++) {

            String value = paramNameValuePairs.get("MPGENVAL" + (i + 1)).toString();
            // 若为非数值类,则加上单引号
            if (!numberFlag) {
                value = "'" + value + "'";
            }
            sqlSegment = StringUtils.replace(sqlSegment, "#{ew.paramNameValuePairs.MPGENVAL" + (i + 1) + "}", value);

        }
        return sqlSegment;
    }

是否为数值类型,则是通过实体模型配置,获取当前模型属性的数据类型来判断的,如下:

 @Override
    public String generateSqlPart(String id, String rule) {
        String result = "";

        List<EntityModelProperty> modelPropertyList = entityModelPropertyService.getFullPropertyByEntityModelId(id);
        String[] numberDataType = {"INTEGER", "LONG", "DOUBLE", "DECIMAL"};

        // 转换数据
        DataFilterRuleVO dataFilterRule = JSON.parseObject(rule, DataFilterRuleVO.class);
        // 获取组集合
        List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();
        // 存放各组对应的条件sql片段
        List<String> groupSqlPartList = new ArrayList<>(dataFilterGroupList.size());
        // 遍历组集合
        for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {
            // 获取条件集合
            List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();
            if (CollectionUtils.isNotEmpty(conditionList)) {
                // 存放各条件对应的条件sql片段
                List<String> conditionSqlPartList = new ArrayList<>(conditionList.size());
                // 遍历条件集合
                for (DataFilterConditionVO condition : conditionList) {
                    // 获取字段名,命名风格驼峰转换成下划线
                    String fieldName = CommonUtil.camelToUnderline(condition.getProperty());
                    Object value = condition.getValue();
                    // 获取操作
                    String operator = condition.getOperator();
                    QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);
                    QueryWrapper<?> queryWrapper = new QueryWrapper<>();
                    addEasyQuery(queryWrapper, fieldName, queryRule, value);

                    log.info(queryWrapper.getSqlSegment());
                    log.info(queryWrapper.getParamNameValuePairs().toString());
                    AtomicBoolean numberFlag = new AtomicBoolean(false);
                    modelPropertyList.stream().filter(modelProperty -> modelProperty.getCode().equals(condition.getProperty())).findFirst()
                            .ifPresent(modelProperty -> {
                                String dataType = modelProperty.getDataType();
                                if (ArrayUtils.contains(numberDataType, dataType)) {
                                    numberFlag.set(true);
                                }

                            });


                    String sqlPart = handleSqlPart(queryWrapper.getSqlSegment(), queryWrapper.getParamNameValuePairs(), numberFlag.get());
                    // 放入结果列表缓存
                    conditionSqlPartList.add(sqlPart);
                }
                // 完成各条件的处理后,进行组内拼接
                if (conditionList.size() > 1) {
                    String logicalOperator = dataFilterGroup.getLogicalOperator();
                    StringBuffer sb = new StringBuffer();
                    sb.append("(");
                    conditionSqlPartList.stream().forEach(sqlPart -> {
                        sb.append(sqlPart);
                        sb.append(" " + logicalOperator + " ");
                    });
                    sb.delete(sb.length() - logicalOperator.length() - 1, sb.length());
                    sb.append(")");
                    groupSqlPartList.add(sb.toString());

                } else {
                    groupSqlPartList.add("(" + conditionSqlPartList.get(0) + ")");

                }
            }

        }

实现效果如下:

数值类未加单引号,非数值类附加了单引号,符合预期。

开源平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:[csdn专栏]
开源地址:[Gitee]
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学海无涯,行者无疆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值