feat(查询): 实现通用过滤条件构建与解析功能
新增参数常量定义和查询条件构建工具,支持复杂条件组合 重构EntityService基类以支持通用过滤条件解析 优化SpecificationUtils工具类,增加搜索文本处理方法
This commit is contained in:
@@ -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> VO类型
|
||||
* @param <ID> 主键类型
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class EntityService<T extends Voable<VO>, VO, ID> {
|
||||
/**
|
||||
* 获取实体数据访问层接口
|
||||
@@ -52,11 +58,14 @@ public abstract class EntityService<T extends Voable<VO>, VO, ID> {
|
||||
protected abstract Specification<T> buildParameterSpecification(JsonNode paramsNode);
|
||||
|
||||
public Page<VO> findAll(JsonNode paramsNode, Pageable pageable) {
|
||||
Specification<T> spec = null;
|
||||
if (paramsNode.has(ParamConstant.KEY_SEARCH_TEXT)) {
|
||||
spec = getSpecification(paramsNode.get(ParamConstant.KEY_SEARCH_TEXT).asText());
|
||||
Specification<T> spec = SpecificationUtils.applySearchText(paramsNode, this::getSpecification);
|
||||
JsonNode filterNode = paramsNode.get(ParamConstant.KEY_FILTER);
|
||||
if (filterNode != null) {
|
||||
Specification<T> 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<T extends Voable<VO>, VO, ID> {
|
||||
Specification<T> spec = getSpecification(searchText);
|
||||
return getRepository().findAll(spec, Pageable.ofSize(10)).getContent();
|
||||
}
|
||||
|
||||
private Specification<T> 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<Specification<T>> specs = new java.util.ArrayList<>();
|
||||
|
||||
for (JsonNode conditionNode : conditionsNode) {
|
||||
Specification<T> 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<T> 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 <X extends Comparable<? super X>> Specification<T> 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<X> clz = (Class<X>) 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user