package com.ecep.contract; import java.util.List; import com.ecep.contract.constant.ParamConstant; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; 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; /** * 实体服务基类 * 提供基础的CRUD操作和查询方法 * * @param 实体类型 * @param VO类型 * @param 主键类型 */ @Slf4j public abstract class EntityService, VO, ID> { /** * 获取实体数据访问层接口 * 子类必须实现此方法,提供具体的实体数据访问层实例 * * @return 实体数据访问层接口 */ protected abstract MyRepository getRepository(); public T getById(ID id) { return getRepository().findById(id).orElse(null); } /** * 创建新实体实例 * 设置默认值或必要的属性 * 实例是游离态的,未存储到数据库 * * @return 新实体实例 */ public abstract T createNewEntity(); /** * 统计所有实体数量 * * @return 实体数量 */ public long count() { return getRepository().count(); } /** * 根据查询规范统计实体数量 * * @param spec 查询规范 * @return 符合规范的实体数量 */ public long count(Specification spec) { return getRepository().count(spec); } /** * 根据JSON参数节点统计实体数量 * * @param paramsNode JSON参数节点 * @return 符合参数节点规范的实体数量 */ public long count(JsonNode paramsNode) { return count(applyJsonParameter(paramsNode)); } /** * 保存实体到数据库 * * @param entity 要保存的实体 * @return 保存后的实体 */ public T save(T entity) { return getRepository().save(entity); } /** * 删除实体 * * @param entity 要删除的实体 */ public void delete(T entity) { getRepository().delete(entity); } @Deprecated protected abstract Specification buildParameterSpecification(JsonNode paramsNode); /** * 应用JSON参数节点到查询规范 * * @param node JSON参数节点 * @return 应用参数节点后的查询规范 */ protected Specification applyJsonParameter(JsonNode node) { Specification spec = SpecificationUtils.applySearchText(node, this::getSearchSpecification); JsonNode filterNode = node.get(ParamConstant.KEY_FILTER); if (filterNode != null) { Specification childSpec = buildFilterCondition(filterNode); if (childSpec != null) { spec = SpecificationUtils.and(spec, childSpec); } } return spec; } /** * 根据JSON参数节点查询所有实体 * * @param paramsNode JSON参数节点 * @param pageable 分页信息 * @return 符合参数节点规范的实体分页结果 */ public Page findAll(JsonNode paramsNode, Pageable pageable) { return findAll(applyJsonParameter(paramsNode), pageable).map(T::toVo); } /** * 根据查询规范查询所有实体 * * @param spec 查询规范 * @param pageable 分页信息 * @return 符合规范的实体分页结果 */ public Page findAll(Specification spec, Pageable pageable) { return getRepository().findAll(spec, pageable); } /** * 根据查询规范查询所有实体 * * @param spec 查询规范 * @param sort 排序信息 * @return 符合规范的实体列表 */ public List findAll(Specification spec, Sort sort) { return getRepository().findAll(spec, sort); } /** * 根据搜索文本查询所有实体 * * @param searchText 搜索文本 * @return 符合搜索文本规范的实体列表 */ public List search(String searchText) { Specification spec = getSearchSpecification(searchText); return getRepository().findAll(spec, Pageable.ofSize(10)).getContent(); } /** * 根据搜索文本构建查询规范 * * @param searchText 搜索文本 * @return 符合搜索文本规范的查询规范 */ public Specification getSearchSpecification(String searchText) { if (!StringUtils.hasText(searchText)) { return null; } return SpecificationUtils.andWith(searchText, this::buildSearchSpecification); } /** * 构建搜索规范 * * @param searchText 搜索文本,非空 * @return 符合搜索文本规范的查询规范 */ protected abstract Specification buildSearchSpecification(String searchText); /** * 构建过滤条件规范 * * @param filterNode 过滤条件节点 * @return 过滤条件规范 */ 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; } field = aliasFor(field, filterNode); 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)) { return buildLikeSpecification(field, filterNode); } if (ParamConstant.KEY_in.equals(operatorStr)) { return buildInSpecification(field, filterNode); } if (ParamConstant.KEY_notIn.equals(operatorStr)) { return buildNotInSpecification(field, filterNode); } if (ParamConstant.KEY_greaterThan.equals(operatorStr)) { return buildGreaterThanSpecification(field, filterNode); } if (ParamConstant.KEY_lessThan.equals(operatorStr)) { return buildLessThanSpecification(field, filterNode); } return null; } /** * 为查询字段添加别名 * * @param field * @param filterNode * @return */ protected String aliasFor(String field, JsonNode filterNode) { return field; } private > Specification buildLessThanSpecification(String field, JsonNode filterNode) { JsonNode valueNode = filterNode.get(ParamConstant.KEY_VALUE); if (valueNode == null || valueNode.isNull()) { log.debug("lessThan 操作符需要 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 value = objectMapper.convertValue(valueNode, clz); return cb.lessThan(path.as(clz), value); }; } private Specification buildGreaterThanSpecification(String field, JsonNode filterNode) { // TODO 实现大于操作符的逻辑 throw new UnsupportedOperationException("Unimplemented method 'buildGreaterThanSpecification'"); } private Specification buildNotInSpecification(String field, JsonNode filterNode) { // TODO 实现 NOT IN 操作符的逻辑 throw new UnsupportedOperationException("Unimplemented method 'buildNotInSpecification'"); } private Specification buildInSpecification(String field, JsonNode filterNode) { // TODO 实现 IN 操作符的逻辑 throw new UnsupportedOperationException("Unimplemented method 'buildInSpecification'"); } private Specification buildLikeSpecification(String field, JsonNode filterNode) { JsonNode valueNode = filterNode.get(ParamConstant.KEY_VALUE); if (valueNode == null || !valueNode.isTextual()) { log.debug("LIKE 操作符需要 value 为字符串"); return null; } ParamConstant.Mode mode = ParamConstant.Mode.CONTAINS; if (filterNode.has(ParamConstant.KEY_MODE)) { mode = ParamConstant.Mode.valueOf(filterNode.get(ParamConstant.KEY_MODE).asText()); } String likeValue; switch (mode) { case STARTS_WITH: likeValue = valueNode.asText() + "%"; break; case ENDS_WITH: likeValue = "%" + valueNode.asText(); break; default: likeValue = "%" + valueNode.asText() + "%"; break; } return (root, query, cb) -> { // 支持 company.name 这种嵌套属性路径 String[] fieldPath = field.split("\\."); Path path = root; for (String segment : fieldPath) { path = path.get(segment); } return cb.like(path.as(String.class), likeValue); }; } /** * 等于操作符的逻辑 * * @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)) { if (valueNode.isNumber()) { return cb.equal(path.get("id"), valueNode.asInt()); } if (valueNode.isObject() && valueNode.has("id")) { JsonNode identity = valueNode.get("id"); if (identity.isNumber()) { return cb.equal(path.get("id"), identity.asInt()); } } } if (clz == java.lang.Enum.class) { // 将字符串转换为对应的枚举值 clz = ((SqmBasicValuedSimplePath) path).getExpressibleJavaType().getJavaTypeClass(); } ObjectMapper objectMapper = SpringApp.getBean(ObjectMapper.class); Object value = null; try { value = objectMapper.convertValue(valueNode, clz); } catch (Exception e) { throw new RuntimeException("field=" + field + ", clz=" + clz.getName() + ", value=" + valueNode, e); } // 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); JsonNode lowNode, highNode; boolean includeBegin = false, includeEnd = false; if (valueNode == null || valueNode.isNull()) { throw new IllegalArgumentException(field + " 的 BETWEEN 操作符需要参数"); } if (valueNode.isArray()) { if (valueNode.size() != 2) { throw new IllegalArgumentException(field + " 的 BETWEEN 操作符的 value 数组长度必须为 2"); } lowNode = valueNode.get(0); highNode = valueNode.get(1); } else if (valueNode.isObject()) { lowNode = valueNode.get(ParamConstant.KEY_between_begin); highNode = valueNode.get(ParamConstant.KEY_between_end); if (valueNode.has(ParamConstant.KEY_INCLUDE_BEGIN)) { includeBegin = valueNode.get(ParamConstant.KEY_INCLUDE_BEGIN).asBoolean(); } if (valueNode.has(ParamConstant.KEY_INCLUDE_END)) { includeEnd = valueNode.get(ParamConstant.KEY_INCLUDE_END).asBoolean(); } } else { throw new IllegalArgumentException(field + " 的 BETWEEN 操作符的 value 必须为数组或对象"); } if (lowNode == null || highNode == null || lowNode.isNull() || highNode.isNull()) { throw new IllegalArgumentException(field + " 的 BETWEEN 操作符的 value 数组元素不能为空"); } 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); } }