|
22 | 22 | import cn.hutool.core.convert.Convert; |
23 | 23 | import cn.hutool.core.lang.tree.Tree; |
24 | 24 | import cn.hutool.core.lang.tree.TreeNodeConfig; |
| 25 | +import cn.hutool.core.lang.tree.TreeUtil; |
25 | 26 | import cn.hutool.core.map.MapUtil; |
26 | 27 | import cn.hutool.core.text.CharSequenceUtil; |
27 | 28 | import cn.hutool.core.util.ReflectUtil; |
28 | 29 | import cn.hutool.extra.spring.SpringUtil; |
29 | 30 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
30 | 31 | import com.baomidou.mybatisplus.core.metadata.IPage; |
31 | 32 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| 33 | +import jakarta.el.MethodNotFoundException; |
32 | 34 | import jakarta.servlet.http.HttpServletResponse; |
33 | 35 | import org.springframework.data.domain.Sort; |
| 36 | +import org.springframework.lang.NonNull; |
34 | 37 | import org.springframework.transaction.annotation.Transactional; |
35 | 38 | import top.continew.starter.core.constant.StringConstants; |
| 39 | +import top.continew.starter.core.exception.BusinessException; |
36 | 40 | import top.continew.starter.core.util.ClassUtils; |
37 | 41 | import top.continew.starter.core.util.ReflectUtils; |
38 | 42 | import top.continew.starter.core.util.TreeBuildUtils; |
@@ -97,56 +101,109 @@ public List<L> list(Q query, SortQuery sortQuery) { |
97 | 101 |
|
98 | 102 | @Override |
99 | 103 | public List<Tree<Long>> tree(Q query, SortQuery sortQuery, boolean isSimple) { |
| 104 | + return this.tree(query, sortQuery, true, false); |
| 105 | + } |
| 106 | + |
| 107 | + @Override |
| 108 | + public List<Tree<Long>> tree(Q query, SortQuery sortQuery, boolean isSimple, boolean isSingleRoot) { |
100 | 109 | List<L> list = this.list(query, sortQuery); |
101 | 110 | if (CollUtil.isEmpty(list)) { |
102 | | - return new ArrayList<>(0); |
| 111 | + return CollUtil.newArrayList(); |
103 | 112 | } |
104 | 113 | CrudProperties crudProperties = SpringUtil.getBean(CrudProperties.class); |
105 | 114 | CrudTreeProperties treeProperties = crudProperties.getTree(); |
106 | 115 | TreeField treeField = listClass.getDeclaredAnnotation(TreeField.class); |
| 116 | + TreeNodeConfig treeNodeConfig; |
| 117 | + Long rootId; |
107 | 118 | // 简单树(下拉列表)使用全局配置结构,复杂树(表格)使用局部配置 |
108 | | - TreeNodeConfig treeNodeConfig = isSimple |
109 | | - ? treeProperties.genTreeNodeConfig() |
110 | | - : treeProperties.genTreeNodeConfig(treeField); |
111 | | - String valueGetter = CharSequenceUtil.genGetter(treeField.value()); |
112 | | - String parentIdKeyGetter = CharSequenceUtil.genGetter(treeField.parentIdKey()); |
113 | | - Function<L, Long> getId = createMethodReference(listClass, valueGetter); |
114 | | - Function<L, Long> getParentId = createMethodReference(listClass, parentIdKeyGetter); |
115 | | - // 构建树 |
116 | | - return TreeBuildUtils.buildMultiRoot(list, getId, getParentId, treeNodeConfig, (node, tree) -> { |
117 | | - tree.setId(ReflectUtil.invoke(node, valueGetter)); |
118 | | - tree.setParentId(ReflectUtil.invoke(node, parentIdKeyGetter)); |
119 | | - tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey()))); |
120 | | - tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey()))); |
121 | | - // 如果构建简单树结构,则不包含扩展字段 |
122 | | - if (!isSimple) { |
123 | | - List<Field> fieldList = ReflectUtils.getNonStaticFields(listClass); |
124 | | - fieldList.removeIf(f -> CharSequenceUtil.equalsAnyIgnoreCase(f.getName(), treeField.value(), treeField |
125 | | - .parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey())); |
126 | | - fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f |
127 | | - .getName())))); |
128 | | - } |
129 | | - }); |
| 119 | + if (isSimple) { |
| 120 | + treeNodeConfig = treeProperties.genTreeNodeConfig(); |
| 121 | + rootId = treeProperties.getRootId(); |
| 122 | + } else { |
| 123 | + treeNodeConfig = treeProperties.genTreeNodeConfig(treeField); |
| 124 | + rootId = treeField.rootId(); |
| 125 | + } |
| 126 | + if (isSingleRoot) { |
| 127 | + // 构建单根节点树 |
| 128 | + return TreeUtil.build(list, rootId, treeNodeConfig, (node, |
| 129 | + tree) -> buildTreeField(isSimple, node, tree, treeField)); |
| 130 | + } else { |
| 131 | + Function<L, Long> getId = createMethodReference(listClass, CharSequenceUtil.genGetter(treeField.value())); |
| 132 | + Function<L, Long> getParentId = createMethodReference(listClass, CharSequenceUtil.genGetter(treeField |
| 133 | + .parentIdKey())); |
| 134 | + // 构建多根节点树 |
| 135 | + return TreeBuildUtils.buildMultiRoot(list, getId, getParentId, treeNodeConfig, (node, |
| 136 | + tree) -> buildTreeField(isSimple, node, tree, treeField)); |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + /** |
| 141 | + * 构建树字段 |
| 142 | + * |
| 143 | + * @param isSimple 是否简单树结构 |
| 144 | + * @param node 节点 |
| 145 | + * @param tree 树 |
| 146 | + * @param treeField 树字段 |
| 147 | + */ |
| 148 | + private void buildTreeField(boolean isSimple, L node, Tree<Long> tree, TreeField treeField) { |
| 149 | + tree.setId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.value()))); |
| 150 | + tree.setParentId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.parentIdKey()))); |
| 151 | + tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey()))); |
| 152 | + tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey()))); |
| 153 | + // 如果构建简单树结构,则不包含扩展字段 |
| 154 | + if (!isSimple) { |
| 155 | + List<Field> fieldList = ReflectUtils.getNonStaticFields(listClass); |
| 156 | + fieldList.removeIf(f -> CharSequenceUtil.equalsAnyIgnoreCase(f.getName(), treeField.value(), treeField |
| 157 | + .parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey())); |
| 158 | + fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f |
| 159 | + .getName())))); |
| 160 | + } |
130 | 161 | } |
131 | 162 |
|
132 | 163 | /** |
133 | | - * 通过反射创建方法引用 |
| 164 | + * 通过反射创建方法引用,支持在父类中查找方法 |
134 | 165 | * |
135 | 166 | * @param clazz 实体类类型 |
136 | 167 | * @param methodName 方法名 |
137 | 168 | * @param <T> 实体类类型 |
138 | 169 | * @param <K> 返回值类型 |
139 | 170 | * @return Function<T, K> 方法引用 |
| 171 | + * @throws IllegalArgumentException 如果参数不合法 |
| 172 | + * @throws MethodNotFoundException 如果在类层次结构中找不到指定方法 |
140 | 173 | */ |
141 | 174 | @SuppressWarnings("unchecked") |
142 | | - public static <T, K> Function<T, K> createMethodReference(Class<T> clazz, String methodName) { |
| 175 | + public static <T, K> Function<T, K> createMethodReference(@NonNull Class<T> clazz, @NonNull String methodName) { |
143 | 176 | try { |
144 | | - Method method = clazz.getDeclaredMethod(methodName); |
| 177 | + Method method = getMethod(clazz, methodName); |
145 | 178 | method.setAccessible(true); |
146 | 179 | return MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.lookup().unreflect(method)); |
147 | 180 | } catch (Exception e) { |
148 | | - throw new RuntimeException("Failed to create method reference for " + methodName, e); |
| 181 | + throw new BusinessException("创建方法引用失败:" + clazz.getName() + StringConstants.DOT + methodName, e); |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + /** |
| 186 | + * 获取方法(包括父类) |
| 187 | + * |
| 188 | + * @param clazz 实体类 |
| 189 | + * @param methodName 方法名 |
| 190 | + * @param <T> 实体类 |
| 191 | + * @return 方法 |
| 192 | + */ |
| 193 | + private static <T> Method getMethod(Class<T> clazz, String methodName) { |
| 194 | + Class<?> currentClass = clazz; |
| 195 | + Method method = null; |
| 196 | + // 查找方法(包括父类) |
| 197 | + while (currentClass != null) { |
| 198 | + try { |
| 199 | + method = currentClass.getDeclaredMethod(methodName); |
| 200 | + break; |
| 201 | + } catch (NoSuchMethodException e) { |
| 202 | + // 继续查找父类 |
| 203 | + currentClass = currentClass.getSuperclass(); |
| 204 | + } |
149 | 205 | } |
| 206 | + return method; |
150 | 207 | } |
151 | 208 |
|
152 | 209 | @Override |
|
0 commit comments