|
@@ -0,0 +1,315 @@
|
|
|
+package com.persagy.proxy.adm.utils;
|
|
|
+
|
|
|
+import cn.hutool.core.map.MapUtil;
|
|
|
+import cn.hutool.core.util.ArrayUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
+import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
|
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
|
|
+import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
|
+import com.persagy.proxy.adm.request.AdmQueryCriteria;
|
|
|
+import com.persagy.proxy.adm.request.OrderItem;
|
|
|
+import com.persagy.proxy.adm.request.QueryCriteria;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * ADM通用查询条件
|
|
|
+ * @author Charlie Yu
|
|
|
+ * @date 2021-08-16
|
|
|
+ */
|
|
|
+public class AdmQueryCriteriaHelper {
|
|
|
+
|
|
|
+ /** 条件匹配字符串 */
|
|
|
+ private static final String[] COND_STR = {" = "," != "," > "," >= "," < "," <= "," in "," isnull "," contain "," startwith "," endwith "};
|
|
|
+ /** 需要替换的字段对照 Map<AdmQueryCriteria.name, Map<AdmFieldName, DmpFieldName> */
|
|
|
+ private static Map<String, Map<String, String>> RENAME_MAP = new HashMap<>(16);
|
|
|
+ /** 与数据中台的操作参数对照 */
|
|
|
+ private static Map<String, String> OPERATOR_MAP = new HashMap<>();
|
|
|
+
|
|
|
+ static {
|
|
|
+ OPERATOR_MAP.put("!=", "$ne");
|
|
|
+ OPERATOR_MAP.put(">", "$gt");
|
|
|
+ OPERATOR_MAP.put(">=", "$gte");
|
|
|
+ OPERATOR_MAP.put("<", "$lt");
|
|
|
+ OPERATOR_MAP.put("<=", "$lte");
|
|
|
+ OPERATOR_MAP.put("in", "$in");
|
|
|
+ OPERATOR_MAP.put("contain", "$like");
|
|
|
+ OPERATOR_MAP.put("startwith", "$likeLeft");
|
|
|
+ OPERATOR_MAP.put("endwith", "$likeRight");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将ADM的查询条件转换为DMP的
|
|
|
+ * 调用前注意:
|
|
|
+ * - 调用前将name赋值,才会将条件中adm的属性替换为dmp的属性
|
|
|
+ * - 统计查询时将isOnlyCount设置为true
|
|
|
+ * 调用后注意(需要调用者自行处理的):
|
|
|
+ * - 不处理级联,级联应在调用者里处理级联调用转换后查询赋值
|
|
|
+ * - 不处理projection,查询后自行根据projection筛选返回属性。(dmp只能指定扩展属性,而adm的是针对所有属性的)
|
|
|
+ * @param admCriteria
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static QueryCriteria toDmpCriteria(AdmQueryCriteria admCriteria) {
|
|
|
+ QueryCriteria dmpCriteria = new QueryCriteria();
|
|
|
+ // 拷贝分页
|
|
|
+ copyPageInfo(admCriteria, dmpCriteria);
|
|
|
+ // 处理filters
|
|
|
+ dmpCriteria.setCriteria(toCriteria(admCriteria.getName(), admCriteria.getFilters()));
|
|
|
+ if(StringUtils.isNotEmpty(admCriteria.getName())){
|
|
|
+ // 处理name
|
|
|
+ dmpCriteria.getCriteria().put("objType", admCriteria.getName());
|
|
|
+ }
|
|
|
+ // 处理排序
|
|
|
+ dmpCriteria.setOrders(toOrderItem(admCriteria.getOrders()));
|
|
|
+ // 处理统计
|
|
|
+ dmpCriteria.setOnlyCount(admCriteria.isOnlyCount());
|
|
|
+ return dmpCriteria;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理分页信息
|
|
|
+ * @param admCriteria
|
|
|
+ * @param dmpCriteria
|
|
|
+ */
|
|
|
+ private static void copyPageInfo(AdmQueryCriteria admCriteria, QueryCriteria dmpCriteria) {
|
|
|
+ // 页码
|
|
|
+ Integer pageNumber = admCriteria.getPageNumber();
|
|
|
+ if(pageNumber == null || pageNumber < 1) {
|
|
|
+ pageNumber = 1;
|
|
|
+ }
|
|
|
+ dmpCriteria.setPage(pageNumber.longValue());
|
|
|
+ // 条数
|
|
|
+ Integer pageSize = admCriteria.getPageSize();
|
|
|
+ if(pageSize == null || pageSize > admCriteria.getMaxRow()) {
|
|
|
+ pageSize = admCriteria.getMaxRow();
|
|
|
+ }
|
|
|
+ dmpCriteria.setSize(pageSize.longValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 排序字符串转换为对象
|
|
|
+ * @param orderStr
|
|
|
+ */
|
|
|
+ private static List<OrderItem> toOrderItem(String orderStr) {
|
|
|
+ if(StrUtil.isBlank(orderStr)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 按,分隔
|
|
|
+ String[] orders = StrUtil.split(orderStr, ",");
|
|
|
+ List<OrderItem> orderList = new ArrayList<>();
|
|
|
+ for(String order:orders) {
|
|
|
+ // 按空格分隔
|
|
|
+ String[] items = StrUtil.split(order, " ");
|
|
|
+ // 去除空串
|
|
|
+ items = ArrayUtil.removeEmpty(items);
|
|
|
+ if(ArrayUtil.isEmpty(items)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ OrderItem orderItem = new OrderItem();
|
|
|
+ // 第一位为排序字段
|
|
|
+ orderItem.setColumn(items[0].trim());
|
|
|
+ // 只有一位 或 第二位不是desc 表示为升序
|
|
|
+ orderItem.setAsc(items.length <= 1 || !"desc".equalsIgnoreCase(items[1]));
|
|
|
+ orderList.add(orderItem);
|
|
|
+ }
|
|
|
+ return orderList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将ADM过滤条件转换为DMP查询
|
|
|
+ * 由于数据中台不支持or条件,因此不能转换为中台的KV条件形式,只能转换为sql
|
|
|
+ * @param filters
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static ObjectNode toCriteria(String name, String filters) {
|
|
|
+ ObjectNode criteria = JsonNodeFactory.instance.objectNode();
|
|
|
+ // filters格式参考:http://doc.sagacloud.cn/doc/service/dev/lib/web/filters.html#%E8%BF%87%E6%BB%A4%E6%9D%A1%E4%BB%B6
|
|
|
+ if(StrUtil.isBlank(filters)) {
|
|
|
+ return criteria;
|
|
|
+ }
|
|
|
+ // 替换逻辑运算符: && || ;
|
|
|
+ filters = StrUtil.replace(filters, "&&", " and ");
|
|
|
+ filters = StrUtil.replace(filters, "||", " or ");
|
|
|
+ filters = StrUtil.replace(filters, ";", " and ");
|
|
|
+ // 处理[]
|
|
|
+ filters = StrUtil.replace(filters, "[", " ( ");
|
|
|
+ filters = StrUtil.replace(filters, "]", " ) ");
|
|
|
+ // 查找下一个 连接点[and or为连接点]
|
|
|
+ ensureOrCondition(name, criteria, filters);
|
|
|
+ return criteria;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从or条件中截取表达式
|
|
|
+ * @param criteria
|
|
|
+ * @param filters
|
|
|
+ */
|
|
|
+ private static void ensureOrCondition(String name, ObjectNode criteria, String filters) {
|
|
|
+ int orIndex = StrUtil.indexOfIgnoreCase(filters, " or ", 0);
|
|
|
+ String orLeft = orIndex < 0 ? filters : StrUtil.subPre(filters, orIndex);
|
|
|
+ ensureAndCondition(name, criteria, orLeft);
|
|
|
+ // 有or条件
|
|
|
+ if(orIndex >= 0) {
|
|
|
+ String orRight = StrUtil.subSuf(filters, orIndex + 4);
|
|
|
+ ObjectNode node = criteria.putObject("$or");
|
|
|
+ // 右侧字符串继续递归处理
|
|
|
+ ensureOrCondition(name, node, orRight);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从and条件中截取表达式
|
|
|
+ * @param criteria
|
|
|
+ * @param filters
|
|
|
+ */
|
|
|
+ private static void ensureAndCondition(String name, ObjectNode criteria, String filters) {
|
|
|
+ int andIndex = StrUtil.indexOfIgnoreCase(filters, " and ", 0);
|
|
|
+ String andLeft = andIndex < 0 ? filters : StrUtil.subPre(filters, andIndex);
|
|
|
+ // 处理左侧条件
|
|
|
+ ensureAndExpress(name, criteria, andLeft);
|
|
|
+ // 处理右侧条件
|
|
|
+ if(andIndex >= 0) {
|
|
|
+ String andRight = StrUtil.subSuf(filters, andIndex+5);
|
|
|
+ // 递归处理and
|
|
|
+ ensureAndCondition(name, criteria, andRight);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理表达式
|
|
|
+ * @param criteria
|
|
|
+ * @param express
|
|
|
+ */
|
|
|
+ private static void ensureAndExpress(String name, ObjectNode criteria, String express) {
|
|
|
+
|
|
|
+ String cond = StrUtil.getContainsStr(express, COND_STR);
|
|
|
+ if(StrUtil.isBlank(cond)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ int condIndex = StrUtil.indexOfIgnoreCase(express, cond, 0);
|
|
|
+ String condLeft = StrUtil.subPre(express, condIndex).trim();
|
|
|
+ String condRight = StrUtil.subSuf(express, condIndex+cond.length()).trim();
|
|
|
+ addCondition(name, criteria, condLeft, cond.trim(), condRight);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 拼接条件
|
|
|
+ * @param criteria
|
|
|
+ * @param column
|
|
|
+ * @param operator
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ private static void addCondition(String name, ObjectNode criteria, String column, String operator, String value) {
|
|
|
+ // 替换列名
|
|
|
+ column = replaceColumns(name, column);
|
|
|
+ if("=".equals(operator)) {
|
|
|
+ criteria.set(column, getJsonNode(criteria, value));
|
|
|
+ } else if("in".equals(operator)) {
|
|
|
+ // 按,分隔
|
|
|
+ String[] splits = StrUtil.split(value, ",");
|
|
|
+ if(ArrayUtil.isEmpty(splits)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ArrayNode node = criteria.putArray(column);
|
|
|
+ for(String split:splits) {
|
|
|
+ // 去掉所有的() 即为值
|
|
|
+ split = StrUtil.removeAll(split, '(',')');
|
|
|
+ node.add(getJsonNode(criteria, split.trim()));
|
|
|
+ }
|
|
|
+ } else if("isnull".equals(operator)) {
|
|
|
+ ObjectNode node = criteria.putObject(column);
|
|
|
+ node.put("$null", true);
|
|
|
+ } else {
|
|
|
+ ObjectNode node = criteria.putObject(column);
|
|
|
+ node.set(OPERATOR_MAP.get(operator), getJsonNode(node, value));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取Json值对象
|
|
|
+ * @param criteria
|
|
|
+ * @param value
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static JsonNode getJsonNode(ObjectNode criteria, String value) {
|
|
|
+ if(StrUtil.isBlank(value)) {
|
|
|
+ return criteria.nullNode();
|
|
|
+ }
|
|
|
+ // 字符串
|
|
|
+ if(StrUtil.startWith(value, "'") && StrUtil.endWith(value, "'")) {
|
|
|
+ return criteria.textNode(StrUtil.sub(value, 1, value.length()-1));
|
|
|
+ }
|
|
|
+ // 数值
|
|
|
+ if (StrUtil.contains(value, ".")) {
|
|
|
+ try {
|
|
|
+ BigDecimal no = new BigDecimal(value);
|
|
|
+ return criteria.numberNode(no);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 整型
|
|
|
+ try {
|
|
|
+ Integer no = Integer.valueOf(value);
|
|
|
+ return criteria.numberNode(no);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+
|
|
|
+ }
|
|
|
+ return criteria.nullNode();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 替换属性称名
|
|
|
+ * @param name
|
|
|
+ * @param column
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static String replaceColumns(String name, String column) {
|
|
|
+ Map<String, String> fieldMap = getRenameFields(name);
|
|
|
+ // 没有对照则使用原本的
|
|
|
+ if(MapUtil.isEmpty(fieldMap) || !fieldMap.containsKey(column)) {
|
|
|
+ return column;
|
|
|
+ }
|
|
|
+ return fieldMap.get(column);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取更名对照
|
|
|
+ * @param name
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static Map<String, String> getRenameFields(String name) {
|
|
|
+ // 获取通用对照
|
|
|
+ Map<String, String> commonMap = getRenameMap().get("COMMON");
|
|
|
+ // 获取此类型的对照
|
|
|
+ Map<String, String> nameMap = getRenameMap().get(name);
|
|
|
+ if(MapUtil.isEmpty(nameMap)) {
|
|
|
+ return commonMap;
|
|
|
+ }
|
|
|
+ Map<String, String> rsMap = new HashMap<>();
|
|
|
+ rsMap.putAll(commonMap);
|
|
|
+ rsMap.putAll(nameMap);
|
|
|
+ return rsMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取通用Map
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static Map<String, Map<String, String>> getRenameMap() {
|
|
|
+ if(MapUtil.isEmpty(RENAME_MAP)) {
|
|
|
+ // COMMON表示通用对照
|
|
|
+ Map<String, String> commonMap = new HashMap<>();
|
|
|
+ commonMap.put("createTime", "creationTime");
|
|
|
+ commonMap.put("lastUpdate", "modifiedTime");
|
|
|
+ RENAME_MAP.put("COMMON", commonMap);
|
|
|
+ }
|
|
|
+ return RENAME_MAP;
|
|
|
+ }
|
|
|
+}
|