From 55660ba18bb3b8b8cecc1c979aa71cde5b4b39d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=A6=E4=B8=AD=E8=87=AA=E6=9C=89=E9=A2=9C=E5=A6=82?= =?UTF-8?q?=E7=8E=89?= <1206770390@qq.com> Date: Tue, 22 Jul 2025 11:30:35 +0000 Subject: [PATCH] =?UTF-8?q?refactor(extension/crud):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=A0=91=E5=88=97=E8=A1=A8=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E9=87=8D=E8=BD=BD=E6=96=B9=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E6=9E=84=E5=BB=BA=E5=8D=95=E4=B8=AA?= =?UTF-8?q?=E6=A0=B9=E8=8A=82=E7=82=B9=E6=88=96=E8=80=85=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E6=A0=B9=E8=8A=82=E7=82=B9=E7=9A=84=E6=A0=91=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extension/crud/service/CrudService.java | 15 ++- .../crud/service/CrudServiceImpl.java | 113 +++++++++++++++--- .../crud/service/CrudServiceImpl.java | 111 ++++++++++++----- 3 files changed, 193 insertions(+), 46 deletions(-) diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CrudService.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CrudService.java index b42dbed5..5dc8d6d6 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CrudService.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CrudService.java @@ -60,15 +60,26 @@ public interface CrudService { List list(@Valid Q query, @Valid SortQuery sortQuery); /** - * 查询树列表 + * 查询树列表(多个根节点) * * @param query 查询条件 * @param sortQuery 排序查询条件 - * @param isSimple 是否为简单树结构(不包含基本树结构之外的扩展字段,简单树(下拉列表)使用全局配置结构,复杂树(表格)使用 @DictField 局部配置) + * @param isSimple 是否为简单树结构(不包含基本树结构之外的扩展字段,简单树(下拉列表)使用全局配置结构,复杂树(表格)使用 @TreeField 局部配置) * @return 树列表信息 */ List> tree(@Valid Q query, @Valid SortQuery sortQuery, boolean isSimple); + /** + * 查询树列表 + * + * @param query 查询条件 + * @param sortQuery 排序查询条件 + * @param isSimple 是否为简单树结构(不包含基本树结构之外的扩展字段,简单树(下拉列表)使用全局配置结构,复杂树(表格)使用 @TreeField 局部配置) + * @param isSingleRoot 是否为单个根节点 + * @return 树列表信息 + */ + List> tree(@Valid Q query, @Valid SortQuery sortQuery, boolean isSimple, boolean isSingleRoot); + /** * 查询详情 * diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java index 8cca7183..4a090af6 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java @@ -27,15 +27,20 @@ import cn.hutool.core.util.ReflectUtil; import cn.hutool.extra.spring.SpringUtil; import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.query.QueryWrapper; +import jakarta.el.MethodNotFoundException; import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Sort; +import org.springframework.lang.NonNull; import org.springframework.transaction.annotation.Transactional; import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.exception.BusinessException; import top.continew.starter.core.util.ReflectUtils; +import top.continew.starter.core.util.TreeBuildUtils; import top.continew.starter.core.util.validation.ValidationUtils; import top.continew.starter.data.base.BaseMapper; import top.continew.starter.data.service.impl.ServiceImpl; import top.continew.starter.data.util.QueryWrapperHelper; +import top.continew.starter.excel.util.ExcelUtils; import top.continew.starter.extension.crud.annotation.TreeField; import top.continew.starter.extension.crud.autoconfigure.CrudProperties; import top.continew.starter.extension.crud.autoconfigure.CrudTreeProperties; @@ -44,12 +49,14 @@ import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.query.SortQuery; import top.continew.starter.extension.crud.model.resp.LabelValueResp; import top.continew.starter.extension.crud.model.resp.PageResp; -import top.continew.starter.excel.util.ExcelUtils; +import java.lang.invoke.MethodHandleProxies; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; -import java.util.ArrayList; +import java.lang.reflect.Method; import java.util.List; import java.util.Optional; +import java.util.function.Function; /** * CRUD 业务实现基类 @@ -89,9 +96,14 @@ public class CrudServiceImpl, T extends BaseIdDO, L, D, @Override public List> tree(Q query, SortQuery sortQuery, boolean isSimple) { + return this.tree(query, sortQuery, true, false); + } + + @Override + public List> tree(Q query, SortQuery sortQuery, boolean isSimple, boolean isSingleRoot) { List list = this.list(query, sortQuery); if (CollUtil.isEmpty(list)) { - return new ArrayList<>(0); + return CollUtil.newArrayList(); } CrudProperties crudProperties = SpringUtil.getBean(CrudProperties.class); CrudTreeProperties treeProperties = crudProperties.getTree(); @@ -106,21 +118,88 @@ public class CrudServiceImpl, T extends BaseIdDO, L, D, treeNodeConfig = treeProperties.genTreeNodeConfig(treeField); rootId = treeField.rootId(); } - // 构建树 - return TreeUtil.build(list, rootId, treeNodeConfig, (node, tree) -> { - tree.setId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.value()))); - tree.setParentId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.parentIdKey()))); - tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey()))); - tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey()))); - // 如果构建简单树结构,则不包含扩展字段 - if (!isSimple) { - List fieldList = ReflectUtils.getNonStaticFields(listClass); - fieldList.removeIf(f -> CharSequenceUtil.equalsAnyIgnoreCase(f.getName(), treeField.value(), treeField - .parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey())); - fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f - .getName())))); + if (isSingleRoot) { + // 构建单根节点树 + return TreeUtil.build(list, rootId, treeNodeConfig, (node, + tree) -> buildTreeField(isSimple, node, tree, treeField)); + } else { + Function getId = createMethodReference(listClass, CharSequenceUtil.genGetter(treeField.value())); + Function getParentId = createMethodReference(listClass, CharSequenceUtil.genGetter(treeField + .parentIdKey())); + // 构建多根节点树 + return TreeBuildUtils.buildMultiRoot(list, getId, getParentId, treeNodeConfig, (node, + tree) -> buildTreeField(isSimple, node, tree, treeField)); + } + } + + /** + * 构建树字段 + * + * @param isSimple 是否简单树结构 + * @param node 节点 + * @param tree 树 + * @param treeField 树字段 + */ + private void buildTreeField(boolean isSimple, L node, Tree tree, TreeField treeField) { + tree.setId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.value()))); + tree.setParentId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.parentIdKey()))); + tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey()))); + tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey()))); + // 如果构建简单树结构,则不包含扩展字段 + if (!isSimple) { + List fieldList = ReflectUtils.getNonStaticFields(listClass); + fieldList.removeIf(f -> CharSequenceUtil.equalsAnyIgnoreCase(f.getName(), treeField.value(), treeField + .parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey())); + fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f + .getName())))); + } + } + + /** + * 通过反射创建方法引用,支持在父类中查找方法 + * + * @param clazz 实体类类型 + * @param methodName 方法名 + * @param 实体类类型 + * @param 返回值类型 + * @return Function 方法引用 + * @throws IllegalArgumentException 如果参数不合法 + * @throws MethodNotFoundException 如果在类层次结构中找不到指定方法 + */ + @SuppressWarnings("unchecked") + public static Function createMethodReference(@NonNull Class clazz, @NonNull String methodName) { + Method method = getMethod(clazz, methodName); + try { + // 设置访问权限并返回方法引用 + method.setAccessible(true); + return MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.lookup().unreflect(method)); + } catch (Exception e) { + throw new BusinessException("创建方法引用失败: " + clazz.getName() + "." + methodName, e); + } + } + + /** + * 获取方法(包括父类) + * + * @param clazz 实体类 + * @param methodName 方法名 + * @param 实体类 + * @return 方法 + */ + private static Method getMethod(Class clazz, String methodName) { + Class currentClass = clazz; + Method method = null; + // 查找方法(包括父类) + while (currentClass != null) { + try { + method = currentClass.getDeclaredMethod(methodName); + break; + } catch (NoSuchMethodException e) { + // 继续查找父类 + currentClass = currentClass.getSuperclass(); } - }); + } + return method; } @Override diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java index afb368cc..e699ad4d 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/service/CrudServiceImpl.java @@ -22,6 +22,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.lang.tree.TreeUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ReflectUtil; @@ -29,10 +30,13 @@ import cn.hutool.extra.spring.SpringUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.el.MethodNotFoundException; import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Sort; +import org.springframework.lang.NonNull; import org.springframework.transaction.annotation.Transactional; import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.exception.BusinessException; import top.continew.starter.core.util.ClassUtils; import top.continew.starter.core.util.ReflectUtils; import top.continew.starter.core.util.TreeBuildUtils; @@ -97,58 +101,111 @@ public class CrudServiceImpl, T extends BaseIdDO, L, D, @Override public List> tree(Q query, SortQuery sortQuery, boolean isSimple) { + return this.tree(query, sortQuery, true, false); + } + + @Override + public List> tree(Q query, SortQuery sortQuery, boolean isSimple, boolean isSingleRoot) { List list = this.list(query, sortQuery); if (CollUtil.isEmpty(list)) { - return new ArrayList<>(0); + return CollUtil.newArrayList(); } CrudProperties crudProperties = SpringUtil.getBean(CrudProperties.class); CrudTreeProperties treeProperties = crudProperties.getTree(); TreeField treeField = listClass.getDeclaredAnnotation(TreeField.class); + TreeNodeConfig treeNodeConfig; + Long rootId; // 简单树(下拉列表)使用全局配置结构,复杂树(表格)使用局部配置 - TreeNodeConfig treeNodeConfig = isSimple - ? treeProperties.genTreeNodeConfig() - : treeProperties.genTreeNodeConfig(treeField); - String valueGetter = CharSequenceUtil.genGetter(treeField.value()); - String parentIdKeyGetter = CharSequenceUtil.genGetter(treeField.parentIdKey()); - Function getId = createMethodReference(listClass, valueGetter); - Function getParentId = createMethodReference(listClass, parentIdKeyGetter); - // 构建树 - return TreeBuildUtils.buildMultiRoot(list, getId, getParentId, treeNodeConfig, (node, tree) -> { - tree.setId(ReflectUtil.invoke(node, valueGetter)); - tree.setParentId(ReflectUtil.invoke(node, parentIdKeyGetter)); - tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey()))); - tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey()))); - // 如果构建简单树结构,则不包含扩展字段 - if (!isSimple) { - List fieldList = ReflectUtils.getNonStaticFields(listClass); - fieldList.removeIf(f -> CharSequenceUtil.equalsAnyIgnoreCase(f.getName(), treeField.value(), treeField - .parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey())); - fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f - .getName())))); - } - }); + if (isSimple) { + treeNodeConfig = treeProperties.genTreeNodeConfig(); + rootId = treeProperties.getRootId(); + } else { + treeNodeConfig = treeProperties.genTreeNodeConfig(treeField); + rootId = treeField.rootId(); + } + if (isSingleRoot) { + // 构建单根节点树 + return TreeUtil.build(list, rootId, treeNodeConfig, (node, + tree) -> buildTreeField(isSimple, node, tree, treeField)); + } else { + Function getId = createMethodReference(listClass, CharSequenceUtil.genGetter(treeField.value())); + Function getParentId = createMethodReference(listClass, CharSequenceUtil.genGetter(treeField + .parentIdKey())); + // 构建多根节点树 + return TreeBuildUtils.buildMultiRoot(list, getId, getParentId, treeNodeConfig, (node, + tree) -> buildTreeField(isSimple, node, tree, treeField)); + } } /** - * 通过反射创建方法引用 + * 构建树字段 + * + * @param isSimple 是否简单树结构 + * @param node 节点 + * @param tree 树 + * @param treeField 树字段 + */ + private void buildTreeField(boolean isSimple, L node, Tree tree, TreeField treeField) { + tree.setId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.value()))); + tree.setParentId(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.parentIdKey()))); + tree.setName(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.nameKey()))); + tree.setWeight(ReflectUtil.invoke(node, CharSequenceUtil.genGetter(treeField.weightKey()))); + // 如果构建简单树结构,则不包含扩展字段 + if (!isSimple) { + List fieldList = ReflectUtils.getNonStaticFields(listClass); + fieldList.removeIf(f -> CharSequenceUtil.equalsAnyIgnoreCase(f.getName(), treeField.value(), treeField + .parentIdKey(), treeField.nameKey(), treeField.weightKey(), treeField.childrenKey())); + fieldList.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, CharSequenceUtil.genGetter(f + .getName())))); + } + } + + /** + * 通过反射创建方法引用,支持在父类中查找方法 * * @param clazz 实体类类型 * @param methodName 方法名 * @param 实体类类型 * @param 返回值类型 * @return Function 方法引用 + * @throws IllegalArgumentException 如果参数不合法 + * @throws MethodNotFoundException 如果在类层次结构中找不到指定方法 */ @SuppressWarnings("unchecked") - public static Function createMethodReference(Class clazz, String methodName) { + public static Function createMethodReference(@NonNull Class clazz, @NonNull String methodName) { try { - Method method = clazz.getDeclaredMethod(methodName); + Method method = getMethod(clazz, methodName); method.setAccessible(true); return MethodHandleProxies.asInterfaceInstance(Function.class, MethodHandles.lookup().unreflect(method)); } catch (Exception e) { - throw new RuntimeException("Failed to create method reference for " + methodName, e); + throw new BusinessException("创建方法引用失败:" + clazz.getName() + StringConstants.DOT + methodName, e); } } + /** + * 获取方法(包括父类) + * + * @param clazz 实体类 + * @param methodName 方法名 + * @param 实体类 + * @return 方法 + */ + private static Method getMethod(Class clazz, String methodName) { + Class currentClass = clazz; + Method method = null; + // 查找方法(包括父类) + while (currentClass != null) { + try { + method = currentClass.getDeclaredMethod(methodName); + break; + } catch (NoSuchMethodException e) { + // 继续查找父类 + currentClass = currentClass.getSuperclass(); + } + } + return method; + } + @Override public D get(Long id) { T entity = super.getById(id, false);