数据权限的设计与实现系列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'
}
]
即以下几种情况:
- 等于
- 等于其中之一
- 不等于
- 包含
- 不包含
- 为空
- 不为空
平台认为不是太合理,做了以下调整:
- 等于其中之一很少用,并且可以等价转换一个条件组,内含多个等于关系的条件,组内逻辑关系为或,因此去除
- 对于字符串,常用的主要是模糊匹配、以……开始、以……结束,因此将包含调整为模糊匹配、去除不包含,增加以……开始、以……结束两种,其他保留不变,同时对操作符的编码也做了相应调整。
调整完成后如下:
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
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!