From 5d6fb961b61f74198df457ddc59b6ed65463c29d Mon Sep 17 00:00:00 2001 From: songqq Date: Sat, 13 Dec 2025 16:48:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=9F=A5=E8=AF=A2):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E9=80=9A=E7=94=A8=E8=BF=87=E6=BB=A4=E6=9D=A1=E4=BB=B6=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E4=B8=8E=E8=A7=A3=E6=9E=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增参数常量定义和查询条件构建工具,支持复杂条件组合 重构EntityService基类以支持通用过滤条件解析 优化SpecificationUtils工具类,增加搜索文本处理方法 --- .../com/ecep/contract/util/ParamUtils.java | 444 +++++++++++++++--- .../ecep/contract/constant/ParamConstant.java | 37 +- .../java/com/ecep/contract/EntityService.java | 157 ++++++- .../contract/cloud/rk/CloudRkService.java | 62 +-- .../contract/util/SpecificationUtils.java | 18 +- 5 files changed, 599 insertions(+), 119 deletions(-) diff --git a/client/src/main/java/com/ecep/contract/util/ParamUtils.java b/client/src/main/java/com/ecep/contract/util/ParamUtils.java index 27690e5..6fd52d0 100644 --- a/client/src/main/java/com/ecep/contract/util/ParamUtils.java +++ b/client/src/main/java/com/ecep/contract/util/ParamUtils.java @@ -4,7 +4,8 @@ import java.time.LocalDate; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Consumer; + +import org.springframework.util.StringUtils; import com.ecep.contract.constant.ParamConstant; @@ -16,64 +17,65 @@ public class ParamUtils { /** * 创建日期范围查询参数 * - * @param key 查询字段名 + * @param field 查询字段名 * @param begin 开始日期 * @param end 结束日期 * @return 包含日期范围的查询参数Map */ - public static Map between(String key, LocalDate begin, LocalDate end) { - return Map.of(key, Map.of( - "begin", begin, - "end", end)); + public static Map between(String field, LocalDate begin, LocalDate end) { + Builder builder = builder(); + builder.between(field, begin, end); + return builder.build(); } /** * 创建等于条件查询参数 * - * @param key 查询字段名 + * @param field 查询字段名 * @param value 查询值 * @return 包含等于条件的查询参数Map */ - public static Map equal(String key, Object value) { - return Map.of(key, value); + public static Map equal(String field, Object value) { + Builder builder = builder(); + builder.equals(field, value); + return builder.build(); } /** * 创建字符串模糊查询参数 * - * @param key 查询字段名 + * @param field 查询字段名 * @param value 模糊查询的字符串值 * @return 包含模糊查询条件的查询参数Map */ - public static Map like(String key, String value) { - Map params = new HashMap<>(); - params.put(key, "%" + value + "%"); - return params; + public static Map contains(String field, String value, boolean caseSensitive) { + Builder builder = builder(); + builder.and().like(field, value, ParamConstant.Mode.CONTAINS, caseSensitive, false); + return builder.build(); } - /** - * 创建日期模糊查询参数 - * - * @param key 查询字段名 - * @param value 模糊查询的日期值 - * @return 包含日期模糊查询条件的查询参数Map - */ - public static Map like(String key, LocalDate value) { - Map params = new HashMap<>(); - params.put(key, "%" + value + "%"); - return params; + public static Map startsWith(String field, String value, boolean caseSensitive) { + Builder builder = builder(); + builder.and().like(field, value, ParamConstant.Mode.STARTS_WITH, caseSensitive, false); + return builder.build(); + } + + public static Map endsWith(String field, String value, boolean caseSensitive) { + Builder builder = builder(); + builder.and().like(field, value, ParamConstant.Mode.ENDS_WITH, caseSensitive, false); + return builder.build(); } /** * 创建非空条件查询参数 * - * @param key 查询字段名 + * @param field 查询字段名 * @return 包含非空条件的查询参数Map */ - public static Map isNotNull(String key) { - Map params = new HashMap<>(); - params.put(key, Map.of("isNotNull", true)); - return params; + public static Map isNotNull(String field) { + Builder builder = builder(); + builder.isNotNull(field); + return builder.build(); } /** @@ -84,9 +86,22 @@ public class ParamUtils { * @return 包含小于条件的查询参数Map */ public static Map lessThan(String key, LocalDate value) { - Map params = new HashMap<>(); - params.put(key, Map.of("lessThan", value)); - return params; + Builder builder = builder(); + builder.lessThan(key, value); + return builder.build(); + } + + /** + * 创建大于条件的日期查询参数 + * + * @param key 查询字段名 + * @param value 比较的日期值 + * @return 包含大于条件的查询参数Map + */ + public static Map greaterThan(String key, LocalDate value) { + Builder builder = builder(); + builder.greaterThan(key, value); + return builder.build(); } /** @@ -105,7 +120,7 @@ public class ParamUtils { public static class Builder { // 存储构建的查询参数 private String searchText; - private Map params = new HashMap<>(); + private FilterGroup group; /** * 私有构造方法,防止外部直接实例化 @@ -114,18 +129,6 @@ public class ParamUtils { private Builder() { } - private void addParam(String field, String action, Object value) { - Object map = params.computeIfAbsent(field, k -> new HashMap<>()); - if (map instanceof Map) { - ((Map) map).put(action, value); - return; - } - HashMap m = new HashMap<>(); - m.put(action, value); - m.put(ParamConstant.KEY_equal, map); - params.put(field, m); - } - /** * 添加非空条件到构建器 * @@ -133,7 +136,7 @@ public class ParamUtils { * @return 当前Builder实例,支持链式调用 */ public Builder isNotNull(String key) { - addParam(key, ParamConstant.KEY_isNotNull, true); + getDefaultGroup().isNotNull(key); return this; } @@ -145,7 +148,7 @@ public class ParamUtils { * @return 当前Builder实例,支持链式调用 */ public Builder lessThan(String key, LocalDate value) { - addParam(key, ParamConstant.KEY_lessThan, value); + getDefaultGroup().lessThan(key, value); return this; } @@ -157,7 +160,7 @@ public class ParamUtils { * @return 当前Builder实例,支持链式调用 */ public Builder greaterThan(String key, LocalDate value) { - addParam(key, ParamConstant.KEY_greaterThan, value); + getDefaultGroup().greaterThan(key, value); return this; } @@ -169,7 +172,7 @@ public class ParamUtils { * @return 当前Builder实例,支持链式调用 */ public Builder equals(String key, Object value) { - addParam(key, ParamConstant.KEY_equal, value); + getDefaultGroup().equals(key, value); return this; } @@ -182,10 +185,16 @@ public class ParamUtils { * @return 当前Builder实例,支持链式调用 */ public Builder between(String key, LocalDate begin, LocalDate end) { - addParam(key, ParamConstant.KEY_between, new Object[]{begin, end}); + getDefaultGroup().between(key, begin, end); return this; } + private FilterGroup getDefaultGroup() { + if (group != null) { + return group; + } + return and(); + } /** * 添加全文搜索条件到构建器 @@ -200,56 +209,337 @@ public class ParamUtils { /** * 构建并返回查询参数Map + * 推荐的结构(仅为契约示例,服务端需配套解析): + * *
          * {
-         *   searchText: "搜索文本",
-         *   and: [
-         *     {field: "字段名", action: "equal", value: "值"},
-         *     {field: "字段名", action: "between", value: [begin, end]}
-         *     {field: "字段名", action: "or", value: [
-         *       {field: "字段名", action: "equal", value: "值"},
-         *     ]}
-         *   ],
-         *   or: [
-         *     {field: "字段名", action: "操作符", value: "值"}
-         *   ]
+         *   "searchText": "关键词",
+         *   "filter": {
+         *     "op": "and",
+         *     "conditions": [
+         *       { "field": "code", "op": "equal", "value": "V-0001" },
+         *       {
+         *         "field": "createdDate",
+         *         "op": "between",
+         *         "value": {
+         *           "begin": "2024-01-01",
+         *           "end": "2024-12-31",
+         *           "includeBegin": true,
+         *           "includeEnd": false
+         *         }
+         *       },
+         *       {
+         *          "field": "name",
+         *          "op": "like",
+         *          "value": "元素",
+         *          "mode": "contains",
+         *          "caseSensitive": false
+         *       },
+         *       {
+         *         "op": "or",
+         *         "conditions": [
+         *           { "field": "name", "op": "like", "value": "元素", "mode": "contains", "caseSensitive": false },
+         *           { "field": "abbName", "op": "like", "value": "元素", "mode": "contains", "caseSensitive": false }
+         *         ]
+         *       }
+         *     ]
+         *   }
          * }
          * 
* + * 约定说明: + * - 分组节点:{ op: "and"|"or", conditions: [...] } + * - 叶子条件:{ field: "路径", op: "equal"|"between"|..., value: ... } + * - like 扩展:支持 mode("contains"|"startsWith"|"endsWith") 和 + * caseSensitive(boolean) + * - between 扩展:value 为对象,支持包含边界 includeBegin/includeEnd + * * @return 包含所有添加条件的查询参数Map */ public Map build() { Map map = new HashMap<>(); - map.put(ParamConstant.KEY_SEARCH_TEXT, searchText); + if (StringUtils.hasText(searchText)) { + map.put(ParamConstant.KEY_SEARCH_TEXT, searchText); + } + if (group != null) { + Map filter = new HashMap<>(); + filter.put(ParamConstant.KEY_OPERATOR, group.operator.name().toLowerCase()); + filter.put(ParamConstant.KEY_CONDITIONS, group.conditions); + map.put(ParamConstant.KEY_FILTER, filter); + } return map; } /** * 添加AND逻辑条件组到构建器 * - * @param consumer Builder消费者,用于构建子条件 - * @return 当前Builder实例,支持链式调用 + * @return 新建的分组实例,支持链式调用添加子条件 */ - public Builder and(Consumer consumer) { - Builder builder = new Builder(); - consumer.accept(builder); - params.put(ParamConstant.KEY_AND, builder); - return this; + public FilterGroup and() { + if (group == null) { + group = new FilterGroup(ParamConstant.Operator.AND); + } else { + if (group.operator != ParamConstant.Operator.AND) { + throw new IllegalArgumentException(" 当前是 " + group.operator + " 条件组不允许修改为 AND 条件"); + } + } + return group; } /** * 添加OR逻辑条件组到构建器 * - * @param consumer Builder消费者,用于构建子条件 - * @return 当前Builder实例,支持链式调用 + * @return 新建的分组实例,支持链式调用添加子条件 */ - public Builder or(Consumer consumer) { - Builder builder = new Builder(); - consumer.accept(builder); - params.put(ParamConstant.KEY_OR, builder); - return this; + public FilterGroup or() { + if (group == null) { + group = new FilterGroup(ParamConstant.Operator.OR); + } else { + if (group.operator != ParamConstant.Operator.OR) { + throw new IllegalArgumentException(" 当前是 " + group.operator + " 条件组不允许修改为 OR 条件"); + } + } + return group; } } + /** + * 过滤条件组,支持 AND/OR 逻辑组合 + */ + public static class FilterGroup { + private ParamConstant.Operator operator = ParamConstant.Operator.AND; + List> conditions = new java.util.ArrayList<>(); + + public FilterGroup(ParamConstant.Operator operator) { + this.operator = operator; + } + + // 添加叶子条件:equal(支持 ignoreNull) + public FilterGroup equals(String field, Object value) { + return equals(field, value, false); + } + + public FilterGroup equals(String field, Object value, boolean ignoreNull) { + if (ignoreNull && isNullOrEmpty(value)) { + return this; + } + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_equal); + leaf.put(ParamConstant.KEY_VALUE, value); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 添加叶子条件:between(对象形式 + 边界 + ignoreNull) + public FilterGroup between(String field, LocalDate begin, LocalDate end) { + return between(field, begin, end, true, true, false); + } + + public FilterGroup between(String field, LocalDate begin, LocalDate end, + boolean includeBegin, boolean includeEnd, boolean ignoreNull) { + boolean noBegin = begin == null; + boolean noEnd = end == null; + if (ignoreNull && noBegin && noEnd) { + return this; + } + Map value = new HashMap<>(); + if (!noBegin) { + value.put(ParamConstant.KEY_between_begin, begin); + } + if (!noEnd) { + value.put(ParamConstant.KEY_between_end, end); + } + value.put(ParamConstant.KEY_INCLUDE_BEGIN, includeBegin); + value.put(ParamConstant.KEY_INCLUDE_END, includeEnd); + + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_BETWEEN); + leaf.put(ParamConstant.KEY_VALUE, value); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 添加叶子条件:lessThan(支持 ignoreNull) + public FilterGroup lessThan(String field, LocalDate value) { + return lessThan(field, value, false); + } + + public FilterGroup lessThan(String field, LocalDate value, boolean ignoreNull) { + if (ignoreNull && value == null) { + return this; + } + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_lessThan); + leaf.put(ParamConstant.KEY_VALUE, value); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 添加叶子条件:greaterThan(支持 ignoreNull) + public FilterGroup greaterThan(String field, LocalDate value) { + return greaterThan(field, value, false); + } + + public FilterGroup greaterThan(String field, LocalDate value, boolean ignoreNull) { + if (ignoreNull && value == null) { + return this; + } + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_greaterThan); + leaf.put(ParamConstant.KEY_VALUE, value); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 添加叶子条件:isNotNull + public FilterGroup isNotNull(String field) { + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_isNotNull); + leaf.put(ParamConstant.KEY_VALUE, true); + conditions.add(leaf); + return this; + } + + // 添加叶子条件:like(支持 mode/caseSensitive/ignoreNull) + public FilterGroup like(String field, String value) { + return like(field, value, ParamConstant.Mode.CONTAINS, false, false); + } + + public FilterGroup like(String field, String value, ParamConstant.Mode mode, boolean caseSensitive, + boolean ignoreNull) { + if (ignoreNull && isNullOrEmpty(value)) { + return this; + } + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_like); + leaf.put(ParamConstant.KEY_VALUE, value); + // 使用枚举值作为模式输出 + leaf.put(ParamConstant.KEY_MODE, mode.name()); + leaf.put(ParamConstant.KEY_CASE_SENSITIVE, caseSensitive); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 添加叶子条件:notLike(支持 mode/caseSensitive/ignoreNull) + public FilterGroup notLike(String field, String value) { + return notLike(field, value, ParamConstant.Mode.CONTAINS, false, false); + } + + public FilterGroup notLike(String field, String value, ParamConstant.Mode mode, boolean caseSensitive, + boolean ignoreNull) { + if (ignoreNull && isNullOrEmpty(value)) { + return this; + } + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_notLike); + leaf.put(ParamConstant.KEY_VALUE, value); + // 使用枚举值作为模式输出 + leaf.put(ParamConstant.KEY_MODE, mode.name()); + leaf.put(ParamConstant.KEY_CASE_SENSITIVE, caseSensitive); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 添加叶子条件:in(空集合防护 + ignoreNull) + public FilterGroup in(String field, java.util.Collection values) { + return in(field, values, false); + } + + public FilterGroup in(String field, java.util.Collection values, boolean ignoreNull) { + if (ignoreNull && (values == null || values.isEmpty())) { + return this; + } + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_in); + leaf.put(ParamConstant.KEY_VALUE, values); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 添加叶子条件:notIn(空集合防护 + ignoreNull) + public FilterGroup notIn(String field, java.util.Collection values) { + return notIn(field, values, false); + } + + public FilterGroup notIn(String field, java.util.Collection values, boolean ignoreNull) { + if (ignoreNull && (values == null || values.isEmpty())) { + return this; + } + Map leaf = new HashMap<>(); + leaf.put(ParamConstant.KEY_FIELD, field); + leaf.put(ParamConstant.KEY_OPERATOR, ParamConstant.KEY_notIn); + leaf.put(ParamConstant.KEY_VALUE, values); + if (ignoreNull) { + leaf.put(ParamConstant.KEY_IGNORE_NULL, true); + } + conditions.add(leaf); + return this; + } + + // 嵌套分组:and / or(保持原有行为) + public FilterGroup and() { + FilterGroup group = new FilterGroup(ParamConstant.Operator.AND); + Map child = new HashMap<>(); + child.put(ParamConstant.KEY_OPERATOR, ParamConstant.Operator.AND.name().toLowerCase()); + child.put(ParamConstant.KEY_CONDITIONS, group.conditions); + conditions.add(child); + return group; + } + + public FilterGroup or() { + FilterGroup group = new FilterGroup(ParamConstant.Operator.OR); + Map child = new HashMap<>(); + child.put(ParamConstant.KEY_OPERATOR, ParamConstant.Operator.OR.name().toLowerCase()); + child.put(ParamConstant.KEY_CONDITIONS, group.conditions); + conditions.add(child); + return group; + } + + // 辅助:空值判断(String/Collection/Array/null) + private boolean isNullOrEmpty(Object value) { + if (value == null) { + return true; + } + if (value instanceof String s) { + return !org.springframework.util.StringUtils.hasText(s); + } + if (value instanceof java.util.Collection c) { + return c.isEmpty(); + } + if (value.getClass().isArray()) { + return java.lang.reflect.Array.getLength(value) == 0; + } + return false; + } + } } diff --git a/common/src/main/java/com/ecep/contract/constant/ParamConstant.java b/common/src/main/java/com/ecep/contract/constant/ParamConstant.java index 697e80a..7936f0a 100644 --- a/common/src/main/java/com/ecep/contract/constant/ParamConstant.java +++ b/common/src/main/java/com/ecep/contract/constant/ParamConstant.java @@ -1,14 +1,43 @@ package com.ecep.contract.constant; public class ParamConstant { + // 操作符常量 + public static final String KEY_like = "like"; + public static final String KEY_notLike = "notLike"; + public static final String KEY_in = "in"; + public static final String KEY_notIn = "notIn"; public static final String KEY_isNotNull = "isNotNull"; public static final String KEY_lessThan = "lessThan"; public static final String KEY_greaterThan = "greaterThan"; - public static final String KEY_between = "between"; - + public static final String KEY_BETWEEN = "between"; + public static final String KEY_between_begin = "begin"; + public static final String KEY_between_end = "end"; + public static final String KEY_INCLUDE_BEGIN = "includeBegin"; + public static final String KEY_INCLUDE_END = "includeEnd"; public static final String KEY_equal = "equal"; - public static final String KEY_OR = "or"; - public static final String KEY_AND = "and"; public static final String KEY_SEARCH_TEXT = "searchText"; + public static final String KEY_FILTER = "filter"; + public static final String KEY_OPERATOR = "op"; + public static final String KEY_CONDITIONS = "conditions"; + // 新增:统一字段与值的键名、like 配置、between 边界、忽略空值 + public static final String KEY_FIELD = "field"; + public static final String KEY_VALUE = "value"; + // 新增:like 的模式键名 + public static final String KEY_MODE = "mode"; + public static final String KEY_CASE_SENSITIVE = "caseSensitive"; + + public static final String KEY_IGNORE_NULL = "ignoreNull"; + + + + public enum Operator { + OR, AND + } + // 新增:like 模式枚举 + public enum Mode { + CONTAINS, + STARTS_WITH, + ENDS_WITH + } } diff --git a/server/src/main/java/com/ecep/contract/EntityService.java b/server/src/main/java/com/ecep/contract/EntityService.java index 98b8dea..4c1a418 100644 --- a/server/src/main/java/com/ecep/contract/EntityService.java +++ b/server/src/main/java/com/ecep/contract/EntityService.java @@ -10,9 +10,14 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.util.StringUtils; import com.ecep.contract.ds.MyRepository; +import com.ecep.contract.model.IdentityEntity; import com.ecep.contract.model.Voable; import com.ecep.contract.util.SpecificationUtils; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.persistence.criteria.Path; +import lombok.extern.slf4j.Slf4j; /** * 实体服务基类 @@ -22,6 +27,7 @@ import com.fasterxml.jackson.databind.JsonNode; * @param VO类型 * @param 主键类型 */ +@Slf4j public abstract class EntityService, VO, ID> { /** * 获取实体数据访问层接口 @@ -52,11 +58,14 @@ public abstract class EntityService, VO, ID> { protected abstract Specification buildParameterSpecification(JsonNode paramsNode); public Page findAll(JsonNode paramsNode, Pageable pageable) { - Specification spec = null; - if (paramsNode.has(ParamConstant.KEY_SEARCH_TEXT)) { - spec = getSpecification(paramsNode.get(ParamConstant.KEY_SEARCH_TEXT).asText()); + Specification spec = SpecificationUtils.applySearchText(paramsNode, this::getSpecification); + JsonNode filterNode = paramsNode.get(ParamConstant.KEY_FILTER); + if (filterNode != null) { + Specification childSpec = buildFilterCondition(filterNode); + if (childSpec != null) { + spec = SpecificationUtils.and(spec, childSpec); + } } - spec = SpecificationUtils.and(spec, buildParameterSpecification(paramsNode)); return findAll(spec, pageable).map(T::toVo); } @@ -81,4 +90,144 @@ public abstract class EntityService, VO, ID> { Specification spec = getSpecification(searchText); return getRepository().findAll(spec, Pageable.ofSize(10)).getContent(); } + + private Specification buildFilterCondition(JsonNode filterNode) { + String operatorStr = filterNode.get(ParamConstant.KEY_OPERATOR).asText(); + + if (isAndOrOperator(operatorStr)) { + if (filterNode.has(ParamConstant.KEY_CONDITIONS)) { + JsonNode conditionsNode = filterNode.get(ParamConstant.KEY_CONDITIONS); + if (conditionsNode.isArray()) { + List> specs = new java.util.ArrayList<>(); + + for (JsonNode conditionNode : conditionsNode) { + Specification childSpec = buildFilterCondition(conditionNode); + if (childSpec != null) { + specs.add(childSpec); + } + } + if (!specs.isEmpty()) { + ParamConstant.Operator op = ParamConstant.Operator.AND; + if (ParamConstant.Operator.OR.name().equalsIgnoreCase(operatorStr)) { + op = ParamConstant.Operator.OR; + } + return (op == ParamConstant.Operator.AND) ? Specification.allOf(specs) + : Specification.anyOf(specs); + } + } else { + log.debug("filterNode 中没有 conditions 数组"); + } + } + return null; + } + + String field = filterNode.get(ParamConstant.KEY_FIELD).asText(); + if (!StringUtils.hasText(field)) { + log.debug("filterNode 中没有 field 字段"); + return null; + } + + if (ParamConstant.KEY_equal.equals(operatorStr)) { + return buildEqualSpecification(field, filterNode); + } + if (ParamConstant.KEY_BETWEEN.equals(operatorStr)) { + return buildBetweenSpecification(field, filterNode); + } + if (ParamConstant.KEY_like.equals(operatorStr)) { + // TODO 实现 LIKE 操作符的逻辑 + return null; + } + if (ParamConstant.KEY_in.equals(operatorStr)) { + // TODO 实现 IN 操作符的逻辑 + return null; + } + if (ParamConstant.KEY_notIn.equals(operatorStr)) { + // TODO 实现 NOT IN 操作符的逻辑 + return null; + } + if (ParamConstant.KEY_greaterThan.equals(operatorStr)) { + // TODO 实现大于操作符的逻辑 + return null; + } + if (ParamConstant.KEY_lessThan.equals(operatorStr)) { + // TODO 实现小于操作符的逻辑 + return null; + } + + return null; + } + + /** + * 等于操作符的逻辑 + * + * @param field + * @param filterNode + * @return + */ + private Specification buildEqualSpecification(String field, JsonNode filterNode) { + // 等于操作符的逻辑 + JsonNode valueNode = filterNode.get(ParamConstant.KEY_VALUE); + if (valueNode == null || valueNode.isNull()) { + return (root, query, cb) -> cb.isNull(root.get(field)); + } + return (root, query, cb) -> { + // 支持 company.id 这种嵌套属性路径 + String[] fieldPath = field.split("\\."); + Path path = root; + for (String segment : fieldPath) { + path = path.get(segment); + } + Class clz = path.getJavaType(); + if (IdentityEntity.class.isAssignableFrom(clz) && valueNode.isNumber()) { + return cb.equal(path.get("id"), valueNode.asInt()); + } + ObjectMapper objectMapper = SpringApp.getBean(ObjectMapper.class); + Object value = objectMapper.convertValue(valueNode, clz); + // Object value = valueNode.isTextual() ? valueNode.asText() + // : valueNode.isNumber() ? valueNode.numberValue() + // : valueNode.isBoolean() ? valueNode.asBoolean() : valueNode; + return cb.equal(path, value); + }; + } + + /** + * BETWEEN 操作符的逻辑 + * + * @param field + * @param filterNode + * @return + */ + private > Specification buildBetweenSpecification(String field, + JsonNode filterNode) { + // BETWEEN 操作符:要求 value 为数组,且长度=2 + JsonNode valueNode = filterNode.get(ParamConstant.KEY_VALUE); + if (valueNode == null || !valueNode.isArray() || valueNode.size() != 2) { + log.debug("BETWEEN 操作符需要 value 为包含两个元素的数组"); + return null; + } + JsonNode lowNode = valueNode.get(0); + JsonNode highNode = valueNode.get(1); + if (lowNode == null || highNode == null || lowNode.isNull() || highNode.isNull()) { + log.debug("BETWEEN 操作符的 value 数组元素不能为空"); + return null; + } + return (root, query, cb) -> { + // 支持嵌套属性路径,如 company.id + String[] fieldPath = field.split("\\."); + Path path = root; + for (String segment : fieldPath) { + path = path.get(segment); + } + Class clz = (Class) path.getJavaType(); + ObjectMapper objectMapper = SpringApp.getBean(ObjectMapper.class); + X lowVal = objectMapper.convertValue(lowNode, clz); + X highVal = objectMapper.convertValue(highNode, clz); + return cb.between(path.as(clz), lowVal, highVal); + }; + } + + private boolean isAndOrOperator(String str) { + return ParamConstant.Operator.AND.name().equalsIgnoreCase(str) + || ParamConstant.Operator.OR.name().equalsIgnoreCase(str); + } } diff --git a/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java b/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java index 9a1bfcf..c60b627 100644 --- a/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java +++ b/server/src/main/java/com/ecep/contract/cloud/rk/CloudRkService.java @@ -1,17 +1,11 @@ package com.ecep.contract.cloud.rk; -import java.io.File; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; -import com.ecep.contract.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -26,17 +20,20 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import com.ecep.contract.BlackReasonType; +import com.ecep.contract.EntityService; +import com.ecep.contract.IEntityService; +import com.ecep.contract.MessageHolder; +import com.ecep.contract.QueryService; +import com.ecep.contract.SpringApp; import com.ecep.contract.cloud.CloudInfo; +import com.ecep.contract.ds.MyRepository; import com.ecep.contract.ds.company.model.Company; import com.ecep.contract.ds.company.model.CompanyBlackReason; -import com.ecep.contract.ds.company.repository.CompanyBlackReasonRepository; -import com.ecep.contract.ds.company.repository.CompanyOldNameRepository; import com.ecep.contract.ds.company.service.CompanyService; import com.ecep.contract.ds.other.model.CloudRk; import com.ecep.contract.ds.other.service.SysConfService; import com.ecep.contract.service.VoableService; -import com.ecep.contract.util.FileUtils; -import com.ecep.contract.util.HttpJsonUtils; import com.ecep.contract.util.MyStringUtils; import com.ecep.contract.util.SpecificationUtils; import com.ecep.contract.vo.CloudRkVo; @@ -53,7 +50,8 @@ import lombok.Data; @Lazy @Service @CacheConfig(cacheNames = "cloud-rk") -public class CloudRkService implements IEntityService, QueryService, VoableService { +public class CloudRkService extends EntityService + implements IEntityService, QueryService, VoableService { private static final Logger logger = LoggerFactory.getLogger(CloudRkService.class); public static final String KEY_PROXY = "cloud.rk.proxy"; @@ -93,14 +91,12 @@ public class CloudRkService implements IEntityService, QueryService, QueryService findAll(JsonNode paramsNode, Pageable pageable) { + protected Specification buildParameterSpecification(JsonNode paramsNode) { Specification spec = null; - if (paramsNode.has("searchText")) { - spec = getSpecification(paramsNode.get("searchText").asText()); - } spec = SpecificationUtils.andParam(spec, paramsNode, "company"); - spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "cloudId", "customerGrade", "customerScore", "vendorGrade", "vendorScore", "active", "version", "rank", "description"); - // 可以根据需要添加更多参数处理 - return findAll(spec, pageable).map(CloudRk::toVo); - } - - public Page findAll(Specification spec, Pageable pageable) { - return cloudRKRepository.findAll(spec, pageable); + spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "cloudId", "customerGrade", "customerScore", + "vendorGrade", "vendorScore", "active", "version", "rank", "description"); + return spec; } @Override - public Specification getSpecification(String searchText) { - if (!StringUtils.hasText(searchText)) { - return null; - } + protected Specification buildSearchSpecification(String searchText) { return (root, query, builder) -> { Path company = root.get("company"); return builder.or( @@ -143,6 +129,10 @@ public class CloudRkService implements IEntityService, QueryService findAll(Specification spec, Pageable pageable) { + return cloudRKRepository.findAll(spec, pageable); + } + private void toCompanyBlackReasonList( Company company, BlackReasonType type, JsonNode reason, List dbReasons, @@ -386,4 +376,16 @@ public class CloudRkService implements IEntityService, QueryService getRepository() { + return cloudRKRepository; + } + + @Override + public CloudRk createNewEntity() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'createNewEntity'"); + } + } diff --git a/server/src/main/java/com/ecep/contract/util/SpecificationUtils.java b/server/src/main/java/com/ecep/contract/util/SpecificationUtils.java index c2b5cd5..1bd41fd 100644 --- a/server/src/main/java/com/ecep/contract/util/SpecificationUtils.java +++ b/server/src/main/java/com/ecep/contract/util/SpecificationUtils.java @@ -6,6 +6,7 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.util.StringUtils; import com.ecep.contract.SpringApp; +import com.ecep.contract.constant.ParamConstant; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -42,7 +43,7 @@ public class SpecificationUtils { * @return 搜索条件为空时返回null,否则返回一个Specification对象 */ public static Specification andWith(String searchText, - Function> buildSearchSpecification) { + Function> buildSearchSpecification) { Specification spec = null; String[] split = null; do { @@ -85,7 +86,8 @@ public class SpecificationUtils { return spec; } - public static Specification andFieldEqualParam(Specification spec, JsonNode paramsNode, String... fields) { + public static Specification andFieldEqualParam(Specification spec, JsonNode paramsNode, + String... fields) { for (String field : fields) { if (!paramsNode.has(field)) { continue; @@ -112,8 +114,8 @@ public class SpecificationUtils { return spec; } - - public static > Specification andFieldBetweenParam(Specification spec, JsonNode paramsNode, String field, Class valueType) { + public static > Specification andFieldBetweenParam(Specification spec, + JsonNode paramsNode, String field, Class valueType) { if (paramsNode.has(field)) { JsonNode param = paramsNode.get(field); if (param.isObject()) { @@ -131,4 +133,12 @@ public class SpecificationUtils { } return spec; } + + public static Specification applySearchText(JsonNode paramsNode, + Function> applySearchTextSpecification) { + if (paramsNode.has(ParamConstant.KEY_SEARCH_TEXT)) { + return applySearchTextSpecification.apply(paramsNode.get(ParamConstant.KEY_SEARCH_TEXT).asText()); + } + return null; + } } \ No newline at end of file