Java 反射+递归 实现数据聚合发布的配置化

发布时间 2023-11-25 10:33:04作者: tT9/wY

大致是GraphQL的思路
分开配置接口数据结构和数据实体的元数据
支持列表查询,支持多层级的数据聚合
参数选叶子节点就行,后续可以把参数用JS实现一个选择树状结构的UI,生成出查询字符串来,或者按照字段分配权限给租户
异常处理的不太好,有待继续调试
不支持数据权限,只支持根据聚合根向下查,相当于跨端的JPA/hibernate多表查询,好处是依赖很简单,用项目现存的Entity就行。

// 接口
@RestController
@RequestMapping("/open")
public class OpenSearchController {
    @Resource
    private ApplicationContext applicationContext;

    /**
     * 车辆查询
     */
    @GetMapping("/search/vehicle/{no}")
    public String search(@PathVariable("no") String no, @RequestParam String propertys) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
        // 需要查询的接口定义(叶子节点)
        List<OpenSearchDifination> propertyLeafList = Arrays.stream(propertys.split(","))
                .map(x -> EnumUtil.fromString(OpenSearchDifination.class, x))
                .collect(Collectors.toList());
        // 需要查询的接口定义(所有路径节点)
        Set<OpenSearchDifination> propertySet = new HashSet<>();
        propertyLeafList.forEach(x -> {
            do {
                propertySet.add(x);
                x = x.getParent();
            } while (x != null);
        });
        // 查询
        Map<String, Object> param = new HashMap<>(1);
        param.put("no", no);
        return JSONUtil.toJsonStr(search(OpenSearchDifination.VEHICLE, propertySet, param));
    }

    /**
     * 递归查询并构建结果
     *
     * @param root        查询节点
     * @param propertySet 需要查询的接口定义
     * @param parentData  父节点数据(用于子节点的查询)
     * @return 查询结果
     */
    private Object search(OpenSearchDifination root, Set<OpenSearchDifination> propertySet, Map<String, Object> parentData) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        if (!propertySet.contains(root)) {
            // 不属于请求参数声明的查询范围
            return null;
        }
        if (root.getType().equals(OpenSearchDifinationType.ARRAY)) {
            // 当前节点是数组【需要查询出结果集给到子节点】
            // 取元数据
            OpenSearchMetaData metaData = EnumUtil.getBy(OpenSearchMetaData::getDifination, root);
            // 反射取到ORM层实例
            ResolvableType entityType = ResolvableType.forClass(metaData.getEntity());
            ResolvableType mapperType = ResolvableType.forClassWithGenerics(BaseMapper.class, entityType);
            Object mapper = applicationContext.getBean(applicationContext.getBeanNamesForType(mapperType)[0]);
            // 构建查询条件
            Object selectParam = metaData.getEntity().newInstance();
            // 构建查询条件:父节点关联字段值
            Object relevanceParentProperty = parentData.get(metaData.getRelevanceParentProperty());
            // 构建查询条件:赋值方法名
            String setMethodName = "set" + metaData.getRelevanceChildProperty().substring(0, 1).toUpperCase() + metaData.getRelevanceChildProperty().substring(1);
            // 构建查询条件:赋值(反射工具类,忽略参数)
            ReflectUtil.getMethodByName(metaData.getEntity(), setMethodName).invoke(selectParam, relevanceParentProperty);
            // 查询
            List<?> result = (List<?>) ReflectUtil.getMethodByName(mapper.getClass(), "select").invoke(mapper, selectParam);
            return result.stream().map(x -> {
                OpenSearchDifination child = root.getChild().stream()
                        .filter(y -> y.getType().equals(OpenSearchDifinationType.OBJECT))
                        .findFirst().orElse(null);
                try {
                    return search(child, propertySet, BeanUtil.beanToMap(x));
                } catch (Exception e) {
                    e.printStackTrace();
                    return e.getMessage();
                }
            }).collect(Collectors.toList());
        } else if (root.getType().equals(OpenSearchDifinationType.OBJECT)) {
            // 当前节点是对象
            List<OpenSearchDifination> childs = root.getChild();
            Map<String, Object> result = new HashMap<>(childs.size());
            childs.stream()
                    .filter(x -> propertySet.contains(root))
                    .forEach(x -> {
                        try {
                            // 元数据
                            OpenSearchMetaData metaData = EnumUtil.getBy(OpenSearchMetaData::getDifination, x);
                            if (x.getType().equals(OpenSearchDifinationType.OBJECT) && metaData != null) {
                                // 子节点是对象【需要查询出单条结果给到子节点】
                                // 反射取到ORM层实例
                                ResolvableType entityType = ResolvableType.forClass(metaData.getEntity());
                                ResolvableType mapperType = ResolvableType.forClassWithGenerics(BaseMapper.class, entityType);
                                Object mapper = applicationContext.getBean(applicationContext.getBeanNamesForType(mapperType)[0]);
                                // 构建查询条件
                                Object selectParam = metaData.getEntity().newInstance();
                                // 构建查询条件:父节点关联字段
                                Object relevanceParentProperty = parentData.get(metaData.getRelevanceParentProperty());
                                // 构建查询条件:赋值方法名
                                String setMethodName = "set" + metaData.getRelevanceChildProperty().substring(0, 1).toUpperCase() + metaData.getRelevanceChildProperty().substring(1);
                                ReflectUtil.getMethodByName(metaData.getEntity(), setMethodName).invoke(selectParam, relevanceParentProperty);
                                // 查询
                                Object searchResult = ReflectUtil.getMethodByName(mapper.getClass(), "selectOne").invoke(mapper, selectParam);
                                // 附带主键
                                result.put("id", ReflectUtil.getMethodByName(AutoIncrementKeyBaseDomain.class, "getId").invoke(searchResult));
                                // 递归
                                result.put(x.getName(), search(x, propertySet, BeanUtil.beanToMap(searchResult)));
                            } else {
                                // 附带主键
                                result.put("id", parentData.get("id"));
                                // 子节点不是对象【递归】
                                result.put(x.getName(), search(x, propertySet, parentData));
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            result.put(x.getName(), e.getMessage());
                        }
                    });
            return result;
        } else {
            // 当前节点是叶子节点
            // 元数据
            OpenSearchMetaData metaData = EnumUtil.getBy(OpenSearchMetaData::getDifination, root);
            return parentData.get(metaData.getPropertyName());
        }
    }
}

// 接口数据结构定义枚举
@Getter
public enum OpenSearchDifination {
    /**
     * 车辆
     */
    VEHICLE("vehicle", OpenSearchDifinationType.ARRAY, null),
    /**
     * 车辆-列表项
     */
    VEHICLE_I(null, OpenSearchDifinationType.OBJECT, VEHICLE),
    /**
     * 车辆-列表项-车牌号
     */
    VEHICLE_I_NO("no", OpenSearchDifinationType.STRING, VEHICLE_I),;


    /**
     * 对象名
     */
    private final String name;
    /**
     * 对象类型
     */
    private final OpenSearchDifinationType type;
    /**
     * 子元素
     */
    private final OpenSearchDifination parent;

    OpenSearchDifination(String name, OpenSearchDifinationType type, OpenSearchDifination parent) {
        this.name = name;
        this.type = type;
        this.parent = parent;
    }

    public List<OpenSearchDifination> getChild() {
        List<OpenSearchDifination> result = new ArrayList<>();
        for (OpenSearchDifination x : OpenSearchDifination.values()) {
            if (this.equals(x.getParent())) {
                result.add(x);
            }
        }
        return result;
    }
}
// 数据类型
@Getter
public enum OpenSearchDifinationType {
    /**
     * 字符串
     */
    STRING(true),
    /**
     * 整型
     */
    INT(true),
    /**
     * 列表
     */
    ARRAY(false),
    /**
     * 对象
     */
    OBJECT(false);

    /**
     * 是否叶子节点
     */
    private final boolean isLeaf;

    OpenSearchDifinationType(boolean isLeaf) {
        this.isLeaf = isLeaf;
    }
}

// 元数据
@Getter
public enum OpenSearchMetaData {
    /**
     * 车辆
     */
    VEHICLE(OpenSearchDifination.VEHICLE, null, DppVehicleInfo.class, "no", "no"),
    /**
     * 车辆-列表项-车牌号
     */
    VEHICLE_I_NO(OpenSearchDifination.VEHICLE_I_NO, "no", null, null, null);

    /**
     * 定义
     */
    private final OpenSearchDifination difination;
    /**
     * 属性名
     */
    private final String propertyName;
    /**
     * OBJECT:实体类
     */
    private final Class<? extends AutoIncrementKeyBaseDomain<Long>> entity;
    /**
     * OBJECT:关联关系
     * 父节点属性(OpenSearchDifination接口定义中的name)
     */
    private final String relevanceParentProperty;
    /**
     * OBJECT:关联关系
     * 当前节点属性(Entity中的属性)
     */
    private final String relevanceChildProperty;


    OpenSearchMetaData(OpenSearchDifination difination, String propertyName, Class<? extends AutoIncrementKeyBaseDomain<Long>> entity, String relevanceParentProperty, String relevanceChildProperty) {
        this.difination = difination;
        this.entity = entity;
        this.propertyName = propertyName;
        this.relevanceParentProperty = relevanceParentProperty;
        this.relevanceChildProperty = relevanceChildProperty;
    }
}