fix: 修复菜单无法加载根节点问题

v1.4.1
Carina 4 years ago
parent 58e197e64f
commit 012473fe5c

@ -0,0 +1,33 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.common.constants;
/**
*
*
* @author Parker
* @date 202131015:50:16
*/
public interface TreeConstants {
/** 是否包含子集 */
String HAS_CHILDREN = "hasChildren";
/** 是否是叶子节点 */
String IS_LEAF = "isLeaf";
}

@ -17,13 +17,19 @@ package org.opsli.core.base.controller;
import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval; import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.opsli.api.base.result.ResultVo; import org.opsli.api.base.result.ResultVo;
@ -32,6 +38,7 @@ import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.common.annotation.RequiresPermissionsCus; import org.opsli.common.annotation.RequiresPermissionsCus;
import org.opsli.common.annotation.hotdata.EnableHotData; import org.opsli.common.annotation.hotdata.EnableHotData;
import org.opsli.common.constants.CacheConstants; import org.opsli.common.constants.CacheConstants;
import org.opsli.common.constants.TreeConstants;
import org.opsli.common.enums.ExcelOperate; import org.opsli.common.enums.ExcelOperate;
import org.opsli.common.exception.ServiceException; import org.opsli.common.exception.ServiceException;
import org.opsli.common.exception.TokenException; import org.opsli.common.exception.TokenException;
@ -40,6 +47,7 @@ import org.opsli.common.utils.OutputStreamUtil;
import org.opsli.common.utils.WrapperUtil; import org.opsli.common.utils.WrapperUtil;
import org.opsli.core.autoconfigure.properties.GlobalProperties; import org.opsli.core.autoconfigure.properties.GlobalProperties;
import org.opsli.core.base.entity.BaseEntity; import org.opsli.core.base.entity.BaseEntity;
import org.opsli.core.base.entity.HasChildren;
import org.opsli.core.base.service.interfaces.CrudServiceInterface; import org.opsli.core.base.service.interfaces.CrudServiceInterface;
import org.opsli.core.cache.local.CacheUtil; import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.msg.CoreMsg; import org.opsli.core.msg.CoreMsg;
@ -60,9 +68,8 @@ import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Date; import java.util.*;
import java.util.Iterator; import java.util.function.Function;
import java.util.List;
/** /**
* Service CRUD * Service CRUD
@ -353,6 +360,67 @@ public abstract class BaseRestController <T extends BaseEntity, E extends ApiWra
} }
} }
/**
*
* @param treeNodes
*/
protected List<Tree<Object>> handleTreeHasChildren(List<Tree<Object>> treeNodes,
Function<Set<String>, List<HasChildren>> callback) {
if(CollUtil.isEmpty(treeNodes) || callback == null){
return treeNodes;
}
Set<String> parentIds = Sets.newHashSet();
for (Tree<Object> treeNode : treeNodes) {
parentIds.add(Convert.toStr(treeNode.getId()));
}
// 数据排查是否存在下级
List<HasChildren> hasChildrenList = callback.apply(parentIds);
if(CollUtil.isEmpty(hasChildrenList)){
hasChildrenList = ListUtil.empty();
}
// 字典
Map<String, Boolean> hasChildrenDict = Maps.newHashMap();
for (HasChildren hasChildren : hasChildrenList) {
if (hasChildren.getCount() != null && hasChildren.getCount() > 0) {
hasChildrenDict.put(hasChildren.getParentId(), true);
}
}
// 处理节点
this.handleTreeHasChildren(treeNodes, hasChildrenDict);
return treeNodes;
}
/**
*
* @param treeNodes
* @param hasChildrenDict
*/
private void handleTreeHasChildren(List<Tree<Object>> treeNodes,
Map<String, Boolean> hasChildrenDict){
for (Tree<Object> treeNode : treeNodes) {
Boolean tmpFlag = hasChildrenDict.get(Convert.toStr(treeNode.getId()));
if (tmpFlag != null && tmpFlag) {
treeNode.putExtra(TreeConstants.IS_LEAF, false);
treeNode.putExtra(TreeConstants.HAS_CHILDREN, true);
}else {
treeNode.putExtra(TreeConstants.IS_LEAF, true);
treeNode.putExtra(TreeConstants.HAS_CHILDREN, false);
}
// 如果不为空 则继续递归处理
if(CollUtil.isNotEmpty(treeNode.getChildren())){
handleTreeHasChildren(treeNode.getChildren(), hasChildrenDict);
}
}
}
// ================================================= // =================================================
@PostConstruct @PostConstruct

@ -25,7 +25,6 @@ import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -41,10 +40,10 @@ import org.opsli.common.annotation.EnableLog;
import org.opsli.common.annotation.RequiresPermissionsCus; import org.opsli.common.annotation.RequiresPermissionsCus;
import org.opsli.common.constants.MenuConstants; import org.opsli.common.constants.MenuConstants;
import org.opsli.common.constants.MyBatisConstants; import org.opsli.common.constants.MyBatisConstants;
import org.opsli.common.enums.DictType;
import org.opsli.common.utils.FieldUtil; import org.opsli.common.utils.FieldUtil;
import org.opsli.common.utils.WrapperUtil; import org.opsli.common.utils.WrapperUtil;
import org.opsli.core.base.controller.BaseRestController; import org.opsli.core.base.controller.BaseRestController;
import org.opsli.core.base.entity.HasChildren;
import org.opsli.core.general.StartPrint; import org.opsli.core.general.StartPrint;
import org.opsli.core.persistence.Page; import org.opsli.core.persistence.Page;
import org.opsli.core.persistence.querybuilder.GenQueryBuilder; import org.opsli.core.persistence.querybuilder.GenQueryBuilder;
@ -63,7 +62,6 @@ import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
@ -83,10 +81,6 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
/** 排序字段 */ /** 排序字段 */
private static final String SORT_FIELD = "order"; private static final String SORT_FIELD = "order";
/** 是否包含子集 */
private static final String HAS_CHILDREN = "hasChildren";
/** 是否是叶子节点 */
private static final String IS_LEAF = "isLeaf";
/** 虚拟总节点 ID */ /** 虚拟总节点 ID */
private static final String VIRTUAL_TOTAL_NODE = "-1"; private static final String VIRTUAL_TOTAL_NODE = "-1";
@ -114,7 +108,8 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
// 这里有坑 如果 为 菜单数据 且 组件(Component)地址为空 不会跳转到主页 也不报错 // 这里有坑 如果 为 菜单数据 且 组件(Component)地址为空 不会跳转到主页 也不报错
// 修复菜单问题导致无法跳转主页 // 修复菜单问题导致无法跳转主页
menuModelList.removeIf(menuModel -> MenuConstants.MENU.equals(menuModel.getType()) && menuModelList.removeIf(
menuModel -> MenuConstants.MENU.equals(menuModel.getType()) &&
(StringUtils.isEmpty(menuModel.getComponent()) || (StringUtils.isEmpty(menuModel.getComponent()) ||
StringUtils.isEmpty(menuModel.getUrl()) StringUtils.isEmpty(menuModel.getUrl())
)); ));
@ -142,7 +137,8 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
// 这里有坑 如果 为 菜单数据 且 组件(Component)地址为空 不会跳转到主页 也不报错 // 这里有坑 如果 为 菜单数据 且 组件(Component)地址为空 不会跳转到主页 也不报错
// 修复菜单问题导致无法跳转主页 // 修复菜单问题导致无法跳转主页
menuModelList.removeIf(menuModel -> MenuConstants.MENU.equals(menuModel.getType()) && menuModelList.removeIf(
menuModel -> MenuConstants.MENU.equals(menuModel.getType()) &&
(StringUtils.isEmpty(menuModel.getComponent()) || (StringUtils.isEmpty(menuModel.getComponent()) ||
StringUtils.isEmpty(menuModel.getUrl()) StringUtils.isEmpty(menuModel.getUrl())
)); ));
@ -167,14 +163,9 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
List<MenuModel> menuModelList; List<MenuModel> menuModelList;
if(StringUtils.isEmpty(parentId)){ if(StringUtils.isEmpty(parentId)){
menuModelList = Lists.newArrayList(); menuModelList = Lists.newArrayList();
parentId = VIRTUAL_TOTAL_NODE; // 生成根节点菜单
MenuModel model = new MenuModel(); MenuModel model = getGenMenuModel();
model.setId(MenuConstants.GEN_ID); parentId = model.getParentId();
model.setMenuName("根节点");
model.setHidden("0");
model.setSortNo(-1);
model.setType("1");
model.setParentId(parentId);
menuModelList.add(model); menuModelList.add(model);
}else{ }else{
// 只查菜单 // 只查菜单
@ -182,7 +173,7 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
QueryWrapper<SysMenu> queryWrapper = queryBuilder.build(); QueryWrapper<SysMenu> queryWrapper = queryBuilder.build();
queryWrapper.eq( queryWrapper.eq(
FieldUtil.humpToUnderline(MyBatisConstants.FIELD_PARENT_ID), parentId); FieldUtil.humpToUnderline(MyBatisConstants.FIELD_PARENT_ID), parentId);
queryWrapper.eq("type", "1"); queryWrapper.eq("type", MenuConstants.MENU);
// 如果传入ID 则不包含自身 // 如果传入ID 则不包含自身
if(StringUtils.isNotEmpty(id)){ if(StringUtils.isNotEmpty(id)){
@ -200,7 +191,8 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
List<Tree<Object>> treeNodes = getMenuTrees(menuModelList, parentId,1); List<Tree<Object>> treeNodes = getMenuTrees(menuModelList, parentId,1);
// 处理是否包含子集 // 处理是否包含子集
this.handleTreeIsLeafByChoose(treeNodes); super.handleTreeHasChildren(treeNodes,
(parentIds)-> IService.hasChildrenByChoose(parentIds));
return ResultVo.success(treeNodes); return ResultVo.success(treeNodes);
} }
@ -216,14 +208,9 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
List<MenuModel> menuModelList; List<MenuModel> menuModelList;
if(StringUtils.isEmpty(parentId)){ if(StringUtils.isEmpty(parentId)){
menuModelList = Lists.newArrayList(); menuModelList = Lists.newArrayList();
parentId = VIRTUAL_TOTAL_NODE; // 生成根节点菜单
MenuModel model = new MenuModel(); MenuModel model = getGenMenuModel();
model.setId(MenuConstants.GEN_ID); parentId = model.getParentId();
model.setMenuName("根节点");
model.setHidden("0");
model.setSortNo(-1);
model.setType("1");
model.setParentId(parentId);
menuModelList.add(model); menuModelList.add(model);
}else{ }else{
QueryBuilder<SysMenu> queryBuilder = new GenQueryBuilder<>(); QueryBuilder<SysMenu> queryBuilder = new GenQueryBuilder<>();
@ -238,7 +225,8 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
List<Tree<Object>> treeNodes = getMenuTrees(menuModelList, parentId,1); List<Tree<Object>> treeNodes = getMenuTrees(menuModelList, parentId,1);
// 处理是否包含子集 // 处理是否包含子集
this.handleTreeHasChildren(treeNodes); super.handleTreeHasChildren(treeNodes,
(parentIds)-> IService.hasChildren(parentIds));
return ResultVo.success(treeNodes); return ResultVo.success(treeNodes);
} }
@ -251,11 +239,9 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
@RequiresPermissions("system_menu_select") @RequiresPermissions("system_menu_select")
@Override @Override
public ResultVo<?> findMenuTreePage(HttpServletRequest request) { public ResultVo<?> findMenuTreePage(HttpServletRequest request) {
QueryBuilder<SysMenu> queryBuilder = new WebQueryBuilder<>(entityClazz, QueryBuilder<SysMenu> queryBuilder = new WebQueryBuilder<>(entityClazz,
request.getParameterMap()); request.getParameterMap());
// 获得菜单 // 获得菜单
List<SysMenu> menuList = IService.findList(queryBuilder.build()); List<SysMenu> menuList = IService.findList(queryBuilder.build());
List<MenuModel> menuModelList = WrapperUtil.transformInstance(menuList, MenuModel.class); List<MenuModel> menuModelList = WrapperUtil.transformInstance(menuList, MenuModel.class);
@ -292,11 +278,8 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
public ResultVo<MenuModel> get(MenuModel model) { public ResultVo<MenuModel> get(MenuModel model) {
if(model != null){ if(model != null){
if(StringUtils.equals(MenuConstants.GEN_ID, model.getId())){ if(StringUtils.equals(MenuConstants.GEN_ID, model.getId())){
model.setMenuName("根节点"); // 生成根节点菜单
model.setHidden("0"); model = getGenMenuModel();
model.setSortNo(-1);
model.setType("1");
model.setParentId(VIRTUAL_TOTAL_NODE);
}else{ }else{
// 如果系统内部调用 则直接查数据库 // 如果系统内部调用 则直接查数据库
if (model.getIzApi() != null && model.getIzApi()){ if (model.getIzApi() != null && model.getIzApi()){
@ -589,72 +572,19 @@ public class MenuRestController extends BaseRestController<SysMenu, MenuModel, I
return TreeBuildUtil.INSTANCE.build(beanMapList, treeNodeConfig); return TreeBuildUtil.INSTANCE.build(beanMapList, treeNodeConfig);
} }
/** /**
* *
* @param treeNodes * @return MenuModel
*/ */
private void handleTreeHasChildren(List<Tree<Object>> treeNodes) { private MenuModel getGenMenuModel() {
if(CollUtil.isEmpty(treeNodes)){ MenuModel model = new MenuModel();
return; model.setId(MenuConstants.GEN_ID);
} model.setMenuName("根菜单");
model.setHidden(DictType.NO_YES_NO.getValue());
Set<String> parentIds = Sets.newHashSet(); model.setSortNo(-1);
for (Tree<Object> treeNode : treeNodes) { model.setType(MenuConstants.MENU);
parentIds.add(Convert.toStr(treeNode.getId())); model.setParentId(VIRTUAL_TOTAL_NODE);
} return model;
// 数据排查是否存在下级
List<HasChildren> hasChildrenList = IService.hasChildren(parentIds);
if (CollUtil.isNotEmpty(hasChildrenList)) {
Map<String, Boolean> tmp = Maps.newHashMap();
for (HasChildren hasChildren : hasChildrenList) {
if (hasChildren.getCount() != null && hasChildren.getCount() > 0) {
tmp.put(hasChildren.getParentId(), true);
}
}
for (Tree<Object> treeNode : treeNodes) {
Boolean tmpFlag = tmp.get(Convert.toStr(treeNode.getId()));
if (tmpFlag != null && tmpFlag) {
treeNode.putExtra(HAS_CHILDREN, true);
}
}
}
}
/**
*
* @param treeNodes
*/
private void handleTreeIsLeafByChoose(List<Tree<Object>> treeNodes) {
if(CollUtil.isEmpty(treeNodes)){
return;
}
Set<String> parentIds = Sets.newHashSet();
for (Tree<Object> treeNode : treeNodes) {
parentIds.add(Convert.toStr(treeNode.getId()));
}
// 数据排查是否存在下级
List<HasChildren> hasChildrenList = IService.hasChildrenByChoose(parentIds);
Map<String, Boolean> tmp = Maps.newHashMap();
for (HasChildren hasChildren : hasChildrenList) {
if (hasChildren.getCount() != null && hasChildren.getCount() > 0) {
tmp.put(hasChildren.getParentId(), false);
}
}
for (Tree<Object> treeNode : treeNodes) {
Boolean tmpFlag = tmp.get(Convert.toStr(treeNode.getId()));
if (tmpFlag == null || tmpFlag) {
treeNode.putExtra(IS_LEAF, true);
}else {
treeNode.putExtra(IS_LEAF, false);
}
}
} }
/** /**

@ -33,9 +33,9 @@ spring:
#primary: master #primary: master
datasource: datasource:
master: master:
url: jdbc:mysql://127.0.0.1:3306/opsli-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai url: jdbc:mysql://10.0.0.28:3306/opsli-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
username: root username: opsli-boot
password: 12345678 password: asRtHGtxSZYGEtmJ
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
# 多数据源配置 # 多数据源配置
#slave-datasource: #slave-datasource:

Loading…
Cancel
Save