diff --git a/guerlab-cloud-api/src/main/java/net/guerlab/cloud/api/feign/ResultDecoder.java b/guerlab-cloud-api/src/main/java/net/guerlab/cloud/api/feign/ResultDecoder.java index 1ec06c4ba3a35ce5f919b9ec853700b6fc3859b0..9e64e9de516ace5d78b8f1fa4a0aa3a47e219fe3 100644 --- a/guerlab-cloud-api/src/main/java/net/guerlab/cloud/api/feign/ResultDecoder.java +++ b/guerlab-cloud-api/src/main/java/net/guerlab/cloud/api/feign/ResultDecoder.java @@ -16,6 +16,7 @@ package net.guerlab.cloud.api.feign; import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; +import java.util.Collection; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.type.TypeReference; @@ -29,6 +30,9 @@ import feign.codec.Decoder; import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + import net.guerlab.cloud.core.result.Result; import net.guerlab.commons.exception.ApplicationException; @@ -40,6 +44,8 @@ import net.guerlab.commons.exception.ApplicationException; @Slf4j public class ResultDecoder implements Decoder { + private static final MediaType TEXT_MEDIA_TYPE = MediaType.parseMediaType("text/*"); + private final ObjectMapper objectMapper; /** @@ -60,8 +66,15 @@ public class ResultDecoder implements Decoder { } String resultBody = Util.toString(body.asReader(StandardCharsets.UTF_8)); - if (!isJson(resultBody) && type instanceof Class && String.class.isAssignableFrom((Class) type)) { - return resultBody; + if (type instanceof Class && String.class.isAssignableFrom((Class) type)) { + if (!isJson(resultBody)) { + return resultBody; + } + + MediaType contentType = getContentType(response); + if (contentType != null && TEXT_MEDIA_TYPE.includes(contentType)) { + return resultBody; + } } return parse0(resultBody, type); @@ -74,6 +87,26 @@ public class ResultDecoder implements Decoder { return resultBody.startsWith("{") && resultBody.endsWith("}"); } + @Nullable + private MediaType getContentType(Response response) { + Collection headerValues = response.headers().get(HttpHeaders.CONTENT_TYPE); + if (headerValues == null) { + return null; + } + + for (String headerValue : headerValues) { + if (headerValue != null) { + try { + return MediaType.parseMediaType(headerValue); + } + catch (Exception ignored) { + // ignore this exception + } + } + } + return null; + } + @Nullable private Object parse0(String resultBody, Type type) throws IOException, FeignException { TypeReference typeReference = new TypeReference<>() { diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/AbstractSearchParamsUtilInstance.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/AbstractSearchParamsUtilInstance.java index c6db3216639b2dd96c0bfda38f8b8bfbb6153da2..a50b44f0774c46dec69e03f2f31258d1626a117a 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/AbstractSearchParamsUtilInstance.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/AbstractSearchParamsUtilInstance.java @@ -85,13 +85,15 @@ public abstract class AbstractSearchParamsUtilInstance { @Nullable public SearchParamsHandler getHandler(Class type) { Optional optional = handlers.stream() - .filter(wrapper -> Objects.equals(type, wrapper.type)).findFirst(); + .filter(wrapper -> Objects.equals(type, wrapper.type)) + .min(SearchParamsHandlerWrapper::compareTo); if (optional.isPresent()) { return optional.get().handler; } - optional = handlers.stream().filter(wrapper -> wrapper.type.isAssignableFrom(type)).findFirst(); + optional = handlers.stream().filter(wrapper -> wrapper.type.isAssignableFrom(type)) + .min(SearchParamsHandlerWrapper::compareTo); if (optional.isPresent()) { return optional.get().handler; @@ -113,7 +115,7 @@ public abstract class AbstractSearchParamsUtilInstance { /** * 搜索参数处理器包装. */ - private static class SearchParamsHandlerWrapper { + private static class SearchParamsHandlerWrapper implements Comparable { /** * 类型. @@ -141,6 +143,11 @@ public abstract class AbstractSearchParamsUtilInstance { public int hashCode() { return Objects.hash(type, handler); } + + @Override + public int compareTo(SearchParamsHandlerWrapper o) { + return handler.compareTo(o.handler); + } } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsHandler.java index 93983dc742e61893ff84da18edb4db9cac50c875..68f6fe555749497cdec3bd84034a7146b1a0d6c9 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsHandler.java @@ -13,27 +13,41 @@ package net.guerlab.cloud.searchparams; +import java.lang.reflect.Field; + import jakarta.annotation.Nullable; +import org.springframework.core.Ordered; + /** * SearchParams参数处理接口. * * @author guer */ @FunctionalInterface -public interface SearchParamsHandler { +public interface SearchParamsHandler extends Ordered, Comparable { /** * 设置参数值. * * @param object 输出对象 - * @param fieldName 类字段名 + * @param field 类字段 * @param columnName 数据库字段名 * @param value 参数值 * @param searchModelType 搜索模式类型 * @param customSql 自定义sql * @param jsonField json字段信息 */ - void setValue(Object object, String fieldName, String columnName, Object value, SearchModelType searchModelType, + void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField); + + @Override + default int getOrder() { + return 0; + } + + @Override + default int compareTo(SearchParamsHandler o) { + return getOrder() - o.getOrder(); + } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsUtils.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsUtils.java index ffe83d15b943db089dab7d0a4520a7de9a18ea2d..41a51d84c7d66af6775c1744a3a351e4690be89d 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsUtils.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SearchParamsUtils.java @@ -193,11 +193,11 @@ public final class SearchParamsUtils { } List sqlProviders = getCustomerSqlProviders(searchModel) - .filter(provider -> provider.accept(object, value)) + .filter(provider -> provider.accept(object, value, field)) .toList(); if (!sqlProviders.isEmpty()) { - sqlProviders.forEach(provider -> provider.apply(object, value)); + sqlProviders.forEach(provider -> provider.apply(object, value, field)); return; } @@ -210,7 +210,7 @@ public final class SearchParamsUtils { JsonField jsonField = field.getAnnotation(JsonField.class); - handler.setValue(object, field.getName(), columnName, value, searchModelType, + handler.setValue(object, field, columnName, value, searchModelType, StringUtils.trimToNull(getCustomSql(searchModel)), jsonField); } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SqlProvider.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SqlProvider.java index 219839e3e22bfb507877776b9636b2262f13724e..9c670d0b26dbfdb3af5612f673672d3231d54ae2 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SqlProvider.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SqlProvider.java @@ -13,6 +13,8 @@ package net.guerlab.cloud.searchparams; +import java.lang.reflect.Field; + import org.springframework.core.Ordered; /** @@ -27,17 +29,19 @@ public interface SqlProvider extends Ordered { * * @param object 处理对象 * @param value 参数值 + * @param field 参数值字段 * @return 是否处理 */ - boolean accept(Object object, Object value); + boolean accept(Object object, Object value, Field field); /** * 处理. * * @param object 处理对象 * @param value 参数值 + * @param field 参数值字段 */ - void apply(Object object, Object value); + void apply(Object object, Object value, Field field); @Override default int getOrder() { diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SubQuerySupport.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SubQuerySupport.java new file mode 100644 index 0000000000000000000000000000000000000000..ee7d166b4208aabd451ab8f1debb26e5c145fc01 --- /dev/null +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/SubQuerySupport.java @@ -0,0 +1,61 @@ +/* + * Copyright 2018-2025 guerlab.net and other contributors. + * + * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/lgpl-3.0.html + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.guerlab.cloud.searchparams; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 子查询支持. + * + * @author guer + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface SubQuerySupport { + + /** + * 结果字段名称. + * + * @return 结果字段名称 + */ + String resultFieldName() default ""; + + /** + * 关联类型. + * + * @return 关联类型 + */ + String refType() default "IN"; + + /** + * 实体类型. + * + * @return 实体类型 + */ + Class entityClass(); + + /** + * 表名称. + * + * @return 表名称 + */ + String tableName() default ""; +} diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/map/MapSearchParamsUtilInstance.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/map/MapSearchParamsUtilInstance.java index 75a936103721d47118ee926afd7207b909f2715d..c2a9c0af9f80cdd1292c321d9bfa6aa7abf37e26 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/map/MapSearchParamsUtilInstance.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/main/java/net/guerlab/cloud/searchparams/map/MapSearchParamsUtilInstance.java @@ -13,6 +13,7 @@ package net.guerlab.cloud.searchparams.map; +import java.lang.reflect.Field; import java.util.Map; import jakarta.annotation.Nullable; @@ -47,9 +48,14 @@ public class MapSearchParamsUtilInstance extends AbstractSearchParamsUtilInstanc @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public void setValue(Object object, String fieldName, String columnName, Object value, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { - ((Map) object).put(fieldName, value); + ((Map) object).put(field.getName(), value); + } + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; } } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/BeanTestSqlProvider.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/BeanTestSqlProvider.java index 22702d658172d9736e00267365facfa630af59cf..011adcbf49351b84eafead56a8fbb50dabe6dfcd 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/BeanTestSqlProvider.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/BeanTestSqlProvider.java @@ -13,6 +13,8 @@ package net.guerlab.cloud.searchparams; +import java.lang.reflect.Field; + /** * 测试自定义sql提供器. * @@ -21,12 +23,12 @@ package net.guerlab.cloud.searchparams; public class BeanTestSqlProvider implements TestSqlProvider { @Override - public boolean accept(Object object, Object value) { + public boolean accept(Object object, Object value, Field field) { return false; } @Override - public void apply(Object object, Object value) { + public void apply(Object object, Object value, Field field) { } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ClassTestSqlProvider.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ClassTestSqlProvider.java index 7a5cc82da848950658b0d009611e24e88746a2ae..7f2b1ea0ebc5aa23bc3928c0fedfc00214c5d99f 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ClassTestSqlProvider.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ClassTestSqlProvider.java @@ -13,6 +13,8 @@ package net.guerlab.cloud.searchparams; +import java.lang.reflect.Field; + /** * 测试自定义sql提供器. * @@ -21,12 +23,12 @@ package net.guerlab.cloud.searchparams; public class ClassTestSqlProvider implements TestSqlProvider { @Override - public boolean accept(Object object, Object value) { + public boolean accept(Object object, Object value, Field field) { return false; } @Override - public void apply(Object object, Object value) { + public void apply(Object object, Object value, Field field) { } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ServiceLoadTestSqlProvider.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ServiceLoadTestSqlProvider.java index eb672cb86d234a1e0c1802a17cb2c81322d40de7..3441f6df624255646ba53c9de14606eacade55bd 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ServiceLoadTestSqlProvider.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-core/src/test/java/net/guerlab/cloud/searchparams/ServiceLoadTestSqlProvider.java @@ -13,6 +13,8 @@ package net.guerlab.cloud.searchparams; +import java.lang.reflect.Field; + /** * 测试自定义sql提供器. * @@ -21,12 +23,12 @@ package net.guerlab.cloud.searchparams; public class ServiceLoadTestSqlProvider implements TestSqlProvider { @Override - public boolean accept(Object object, Object value) { + public boolean accept(Object object, Object value, Field field) { return false; } @Override - public void apply(Object object, Object value) { + public void apply(Object object, Object value, Field field) { } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/BoolQueryBuilderDefaultHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/BoolQueryBuilderDefaultHandler.java index 7762487f343c4a4157d00b08cd46a06734c266ce..f43349568b6355c35d55d7cb78f90d417ee1b803 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/BoolQueryBuilderDefaultHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/BoolQueryBuilderDefaultHandler.java @@ -13,6 +13,7 @@ package net.guerlab.cloud.searchparams.elasticsearch; +import java.lang.reflect.Field; import java.time.temporal.Temporal; import java.util.ArrayList; import java.util.Arrays; @@ -67,7 +68,7 @@ public class BoolQueryBuilderDefaultHandler implements SearchParamsHandler { } @Override - public void setValue(Object object, String fieldName, String columnName, Object value, SearchModelType searchModelType, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { if (value instanceof OrderBys) { return; diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/SortOptionsBuilderDefaultHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/SortOptionsBuilderDefaultHandler.java index 86babc2f176999b5c1d6b88655ee03f5e75ab24c..77aa13c824a73d1a5e7515479befcad8844a7843 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/SortOptionsBuilderDefaultHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-elasticsearch/src/main/java/net/guerlab/cloud/searchparams/elasticsearch/SortOptionsBuilderDefaultHandler.java @@ -13,6 +13,7 @@ package net.guerlab.cloud.searchparams.elasticsearch; +import java.lang.reflect.Field; import java.util.List; import co.elastic.clients.elasticsearch._types.SortOrder; @@ -35,7 +36,7 @@ import net.guerlab.cloud.searchparams.SearchParamsHandler; public class SortOptionsBuilderDefaultHandler implements SearchParamsHandler { @Override - public void setValue(Object object, String fieldName, String columnName, Object value, SearchModelType searchModelType, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { if (!(value instanceof OrderBys)) { return; diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/BetweenHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/BetweenHandler.java index bde5b309a123b79f6641e680e686f806084aa7c1..bb12f56ee6b53d0853b362dcaab84226e55493e0 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/BetweenHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/BetweenHandler.java @@ -13,6 +13,8 @@ package net.guerlab.cloud.searchparams.mybatisplus; +import java.lang.reflect.Field; + import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; @@ -35,7 +37,7 @@ public class BetweenHandler extends AbstractMyBatisPlusSearchParamsHandler { } @Override - public void setValue(Object object, String fieldName, String columnName, Object value, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { Between between = (Between) value; if (between.isInvalid()) { diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/CollectionHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/CollectionHandler.java index 4cd59d7ca17675d19fea448c40ee20e08a65c237..e89fbff2fc4633e510ee7e9dd3d809945afdf867 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/CollectionHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/CollectionHandler.java @@ -13,6 +13,7 @@ package net.guerlab.cloud.searchparams.mybatisplus; +import java.lang.reflect.Field; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -50,7 +51,7 @@ public class CollectionHandler extends AbstractMyBatisPlusSearchParamsHandler { @SuppressWarnings("unchecked") @Override - public void setValue(Object object, String fieldName, String columnName, Object value, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { Collection collection = (Collection) value; diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/DefaultHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/DefaultHandler.java index 6f6e038c41fe9abf8a5ce5e50d7887a445a00979..7f0e7c5c48b57b3878124f0ccdb79c38ededd8d3 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/DefaultHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/DefaultHandler.java @@ -13,6 +13,8 @@ package net.guerlab.cloud.searchparams.mybatisplus; +import java.lang.reflect.Field; + import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; @@ -29,7 +31,7 @@ import net.guerlab.cloud.searchparams.SearchParamsHandler; public class DefaultHandler implements SearchParamsHandler { @Override - public void setValue(Object object, String fieldName, String columnName, Object value, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { QueryWrapper wrapper = (QueryWrapper) object; columnName = ColumnNameGetter.getColumnName(columnName, wrapper.getEntityClass()); @@ -114,4 +116,9 @@ public class DefaultHandler implements SearchParamsHandler { default -> wrapper.eq(columnName, value); } } + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; + } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/OrderBysHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/OrderBysHandler.java index 58f52ab09a4bd0b661bf0d9588f5ba63bfd647e3..f778f6ebf07d54fa3d9192bbe7c7ad1eee55d257 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/OrderBysHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/OrderBysHandler.java @@ -13,6 +13,7 @@ package net.guerlab.cloud.searchparams.mybatisplus; +import java.lang.reflect.Field; import java.util.List; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -37,7 +38,7 @@ public class OrderBysHandler extends AbstractMyBatisPlusSearchParamsHandler { } @Override - public void setValue(Object object, String fieldName, String columnName, Object value, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { QueryWrapper wrapper = (QueryWrapper) object; List orderBys = (OrderBys) value; diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/StringHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/StringHandler.java index 86781410c5b61ac12e116eecf0b497e42954f53e..74c93d77fed09e7a7f01c86eb9688dff6148f37a 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/StringHandler.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/StringHandler.java @@ -13,6 +13,8 @@ package net.guerlab.cloud.searchparams.mybatisplus; +import java.lang.reflect.Field; + import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; @@ -33,7 +35,7 @@ public class StringHandler extends AbstractMyBatisPlusSearchParamsHandler { } @Override - public void setValue(Object object, String fieldName, String columnName, Object value, + public void setValue(Object object, Field field, String columnName, Object value, SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { String str = StringUtils.trimToNull((String) value); diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/SubQueryHandler.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/SubQueryHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..05a657734725bf072d2cc56cfcaee029571f0d1e --- /dev/null +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/java/net/guerlab/cloud/searchparams/mybatisplus/SubQueryHandler.java @@ -0,0 +1,95 @@ +/* + * Copyright 2018-2025 guerlab.net and other contributors. + * + * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/lgpl-3.0.html + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.guerlab.cloud.searchparams.mybatisplus; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import jakarta.annotation.Nullable; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import net.guerlab.cloud.searchparams.JsonField; +import net.guerlab.cloud.searchparams.SearchModelType; +import net.guerlab.cloud.searchparams.SearchParams; +import net.guerlab.cloud.searchparams.SearchParamsUtils; +import net.guerlab.cloud.searchparams.SubQuerySupport; +import net.guerlab.commons.collection.CollectionUtil; + +/** + * 子查询处理. + * + * @author guer + */ +@Slf4j +public class SubQueryHandler extends AbstractMyBatisPlusSearchParamsHandler { + + private static final AtomicInteger SUB_QUERY_ATOMIC = new AtomicInteger(); + + @Override + public Class acceptClass() { + return SearchParams.class; + } + + @Override + public void setValue(Object object, Field field, String columnName, Object value, + SearchModelType searchModelType, @Nullable String customSql, @Nullable JsonField jsonField) { + SubQuerySupport subQuerySupport = field.getAnnotation(SubQuerySupport.class); + if (subQuerySupport == null) { + return; + } + String resultFieldName = StringUtils.trimToNull(subQuerySupport.resultFieldName()); + String refType = StringUtils.trimToNull(subQuerySupport.refType()); + if (refType == null) { + return; + } + Class entityClass = subQuerySupport.entityClass(); + + String tableName = StringUtils.trimToNull(subQuerySupport.tableName()); + if (tableName == null) { + TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass); + if (tableInfo != null) { + tableName = tableInfo.getTableName(); + } + } + if (tableName == null) { + log.warn("subQuery miss tableName, entityClass is {}", entityClass.getName()); + return; + } + + String subQuerySql = "SELECT %S FROM %s".formatted(resultFieldName, tableName); + + QueryWrapper wrapper = (QueryWrapper) object; + QueryWrapper subQueryWrapper = new QueryWrapper<>(entityClass); + SearchParamsUtils.handler((SearchParams) value, subQueryWrapper); + + String sqlSegment = subQueryWrapper.getSqlSegment(); + Map valuePairs = subQueryWrapper.getParamNameValuePairs(); + if (!StringUtils.isBlank(sqlSegment) && !CollectionUtil.isEmpty(valuePairs)) { + String subQueryKind = "subQuery" + SUB_QUERY_ATOMIC.incrementAndGet(); + sqlSegment = sqlSegment.replace("#{ew.paramNameValuePairs.MPGENVAL", "#{ew.paramNameValuePairs." + subQueryKind + "_MPGENVAL"); + + valuePairs.forEach((k, v) -> wrapper.getParamNameValuePairs().put(subQueryKind + "_" + k, v)); + + subQuerySql += " WHERE " + sqlSegment; + } + + columnName = ColumnNameGetter.getColumnName(columnName, wrapper.getEntityClass()); + wrapper.apply("%s %s (%s)".formatted(columnName, refType, subQuerySql)); + } +} diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/resources/META-INF/services/net.guerlab.cloud.searchparams.mybatisplus.AbstractMyBatisPlusSearchParamsHandler b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/resources/META-INF/services/net.guerlab.cloud.searchparams.mybatisplus.AbstractMyBatisPlusSearchParamsHandler index 2c5b598d78df7a68495694fcf95ca7aaa78dab1a..064bd2b5de3c44a3a0dcbf2ce5fbbcf5bc87c848 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/resources/META-INF/services/net.guerlab.cloud.searchparams.mybatisplus.AbstractMyBatisPlusSearchParamsHandler +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/main/resources/META-INF/services/net.guerlab.cloud.searchparams.mybatisplus.AbstractMyBatisPlusSearchParamsHandler @@ -15,3 +15,4 @@ net.guerlab.cloud.searchparams.mybatisplus.BetweenHandler net.guerlab.cloud.searchparams.mybatisplus.CollectionHandler net.guerlab.cloud.searchparams.mybatisplus.OrderBysHandler net.guerlab.cloud.searchparams.mybatisplus.StringHandler +net.guerlab.cloud.searchparams.mybatisplus.SubQueryHandler diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/MybatisPlusSearchParamsTest.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/MybatisPlusSearchParamsTest.java index f74776de4adda8aadb33aafe6d8fbd040ddd1324..921566f15494e9fe6483287de61664d348cf9fa2 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/MybatisPlusSearchParamsTest.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/MybatisPlusSearchParamsTest.java @@ -341,4 +341,35 @@ class MybatisPlusSearchParamsTest { Assertions.assertEquals("(ID NOT IN (#{ew.paramNameValuePairs.MPGENVAL1},#{ew.paramNameValuePairs.MPGENVAL2},#{ew.paramNameValuePairs.MPGENVAL3}) AND ID NOT IN (#{ew.paramNameValuePairs.MPGENVAL4}))", queryWrapper.getSqlSegment()); } + + @Test + @Order(19) + void testWithSubQueryHasValue() { + TestSubSearchParams subSearchParams = new TestSubSearchParams(); + subSearchParams.setNameLike("abcd"); + + TestSearchParams searchParams = new TestSearchParams(); + searchParams.setT2(List.of("1", "2")); + searchParams.setSubSearchParams(subSearchParams); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + + SearchParamsUtils.handler(searchParams, queryWrapper); + + Assertions.assertEquals("(('column' in (#{ew.paramNameValuePairs.MPGENVAL1}, #{ew.paramNameValuePairs.MPGENVAL2})) AND ID IN (SELECT ID FROM TEST_SUB_ENTITY WHERE (name LIKE #{ew.paramNameValuePairs.subQuery1_MPGENVAL1})))", queryWrapper.getSqlSegment()); + } + + @Test + @Order(20) + void testWithSubQueryNotHasValue() { + TestSearchParams searchParams = new TestSearchParams(); + searchParams.setT2(List.of("1", "2")); + searchParams.setSubSearchParams(new TestSubSearchParams()); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + + SearchParamsUtils.handler(searchParams, queryWrapper); + + Assertions.assertEquals("(('column' in (#{ew.paramNameValuePairs.MPGENVAL1}, #{ew.paramNameValuePairs.MPGENVAL2})) AND ID IN (SELECT ID FROM TEST_SUB_ENTITY))", queryWrapper.getSqlSegment()); + } } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSearchParams.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSearchParams.java index 88687c9839139cc4d9192ec5f0dfd0540e9009fe..2413ae47fc41444baf07bfdcab1e8bf9657217be 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSearchParams.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSearchParams.java @@ -25,6 +25,7 @@ import net.guerlab.cloud.searchparams.JsonField; import net.guerlab.cloud.searchparams.SearchModel; import net.guerlab.cloud.searchparams.SearchModelType; import net.guerlab.cloud.searchparams.SearchParams; +import net.guerlab.cloud.searchparams.SubQuerySupport; /** * @author guer @@ -77,4 +78,8 @@ public class TestSearchParams implements SearchParams { @Column(name = "ID") @SearchModel(SearchModelType.NOT_IN) private List notBigList; + + @Column(name = "ID") + @SubQuerySupport(resultFieldName = "ID", entityClass = TestSubEntity.class, tableName = "TEST_SUB_ENTITY") + private TestSubSearchParams subSearchParams; } diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSqlProvider.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSqlProvider.java index b7b7ae707a0c20d41651e75d4a6082bd746a7208..71e8e2c31661426ea16670e52b9e0dc7b7da157a 100644 --- a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSqlProvider.java +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSqlProvider.java @@ -13,6 +13,7 @@ package net.guerlab.cloud.searchparams.mybatisplus; +import java.lang.reflect.Field; import java.util.Collection; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -27,12 +28,12 @@ import net.guerlab.cloud.searchparams.SqlProvider; public class TestSqlProvider implements SqlProvider { @Override - public boolean accept(Object object, Object value) { + public boolean accept(Object object, Object value, Field field) { return object instanceof QueryWrapper && value instanceof Collection; } @Override - public void apply(Object object, Object value) { + public void apply(Object object, Object value, Field field) { QueryWrapper queryWrapper = (QueryWrapper) object; Collection values = (Collection) value; queryWrapper.and((sw) -> values.forEach(v -> sw.or().eq("_v", v))); diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSubEntity.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSubEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..f673e23bdb2d6a54d2d79d351be927ab2431704e --- /dev/null +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSubEntity.java @@ -0,0 +1,28 @@ +/* + * Copyright 2018-2025 guerlab.net and other contributors. + * + * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/lgpl-3.0.html + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.guerlab.cloud.searchparams.mybatisplus; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +@Data +@TableName("TEST_SUB_ENTITY") +public class TestSubEntity { + + @TableId + private Long id; + + private String name; +} diff --git a/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSubSearchParams.java b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSubSearchParams.java new file mode 100644 index 0000000000000000000000000000000000000000..8137da124ee5d16f427b1fdbd764506f6c71a37a --- /dev/null +++ b/guerlab-cloud-searchparams/guerlab-cloud-searchparams-mybatisplus/src/test/java/net/guerlab/cloud/searchparams/mybatisplus/TestSubSearchParams.java @@ -0,0 +1,35 @@ +/* + * Copyright 2018-2025 guerlab.net and other contributors. + * + * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/lgpl-3.0.html + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.guerlab.cloud.searchparams.mybatisplus; + +import lombok.Getter; +import lombok.Setter; + +import net.guerlab.cloud.searchparams.Column; +import net.guerlab.cloud.searchparams.SearchModel; +import net.guerlab.cloud.searchparams.SearchModelType; +import net.guerlab.cloud.searchparams.SearchParams; + +/** + * @author guer + */ +@Setter +@Getter +@SuppressWarnings("unused") +public class TestSubSearchParams implements SearchParams { + + @Column(name = "name") + @SearchModel(SearchModelType.LIKE) + private String nameLike; +} diff --git a/pom.xml b/pom.xml index c7523ef0c9ae976dfc566828ec075cf51e2a7c68..e1c91400afa1e36dd0fe5ac062ae21162a71fdf8 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ - 2024.11.1 + 2025.0.0 ${basedir} 17 @@ -346,11 +346,6 @@ guerlab-cloud-security-core ${revision} - - net.guerlab.cloud - guerlab-cloud-security-provider - ${revision} - net.guerlab.cloud guerlab-cloud-security-webflux