diff --git a/src/main/java/com/gxwebsoft/common/system/controller/MenuController.java b/src/main/java/com/gxwebsoft/common/system/controller/MenuController.java index 2d6e09c..9411cab 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/MenuController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/MenuController.java @@ -2,33 +2,40 @@ package com.gxwebsoft.common.system.controller; import cn.afterturn.easypoi.excel.ExcelImportUtil; import cn.afterturn.easypoi.excel.entity.ImportParams; -import cn.hutool.core.util.ObjectUtil; -import com.gxwebsoft.common.core.utils.JSONUtil; -import com.gxwebsoft.common.system.param.MenuImportParam; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - import cn.hutool.core.stream.CollectorUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.gxwebsoft.common.core.annotation.OperationLog; +import com.gxwebsoft.common.core.utils.JSONUtil; import com.gxwebsoft.common.core.web.*; import com.gxwebsoft.common.system.entity.*; +import com.gxwebsoft.common.system.param.MenuImportParam; import com.gxwebsoft.common.system.param.MenuParam; import com.gxwebsoft.common.system.param.VersionParam; import com.gxwebsoft.common.system.service.CompanyService; import com.gxwebsoft.common.system.service.MenuService; +import com.gxwebsoft.common.system.service.RoleMenuService; +import com.gxwebsoft.common.system.service.RoleService; import com.gxwebsoft.common.system.service.UserService; import com.gxwebsoft.common.system.service.VersionService; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Queue; import java.util.Set; import java.util.stream.Collectors; @@ -50,6 +57,10 @@ public class MenuController extends BaseController { private VersionService versionService; @Resource private UserService userService; + @Resource + private RoleService roleService; + @Resource + private RoleMenuService roleMenuService; @PreAuthorize("hasAuthority('sys:menu:list')") @Operation(summary = "分页查询菜单") @@ -238,45 +249,260 @@ public class MenuController extends BaseController { /** * excel批量导入菜单 */ - @PreAuthorize("hasAuthority('sys:menu:remove')") + @PreAuthorize("hasAuthority('sys:menu:save')") @Operation(summary = "批量导入菜单") @Transactional(rollbackFor = {Exception.class}) @PostMapping("/import") public ApiResult importBatch(MultipartFile file) { ImportParams importParams = new ImportParams(); try { + System.out.println("=== 开始菜单导入流程 ==="); + + // 检查导入前的菜单数据 + long beforeCount = menuService.count(); + System.out.println("导入前菜单总数: " + beforeCount); + + // 检查当前未删除的菜单 + List undeletedMenus = menuService.list(new LambdaQueryWrapper().eq(Menu::getDeleted, 0)); + System.out.println("当前未删除的菜单数: " + undeletedMenus.size()); + if (!undeletedMenus.isEmpty()) { + System.out.println("未删除菜单列表:"); + for (Menu menu : undeletedMenus) { + System.out.println(" ID: " + menu.getMenuId() + ", 名称: " + menu.getTitle() + + ", 父级ID: " + menu.getParentId() + ", deleted: " + menu.getDeleted()); + } + } + // 第一步:永久删除已标记为 deleted=1 的记录 - menuService.remove(new LambdaQueryWrapper().eq(Menu::getDeleted, 1)); + boolean deleteResult = menuService.remove(new LambdaQueryWrapper().eq(Menu::getDeleted, 1)); + System.out.println("已永久删除标记为deleted=1的菜单记录,结果: " + deleteResult); // 第二步:将现有未删除的记录(deleted=0)标记为 deleted=1 - LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); - updateWrapper.eq(Menu::getDeleted, 0); - updateWrapper.set(Menu::getDeleted, 1); - menuService.update(updateWrapper); + if (!undeletedMenus.isEmpty()) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Menu::getDeleted, 0); + updateWrapper.set(Menu::getDeleted, 1); + boolean updateResult = menuService.update(updateWrapper); + System.out.println("更新未删除菜单记录的结果: " + updateResult); + } + + // 检查更新后的菜单数据 + long afterCleanupCount = menuService.count(new LambdaQueryWrapper().eq(Menu::getDeleted, 0)); + System.out.println("清理后未标记删除的菜单数: " + afterCleanupCount); // 第三步:导入XLS文件的内容 List list = ExcelImportUtil.importExcel(file.getInputStream(), MenuImportParam.class, importParams); - list.forEach(d -> { - Menu item = JSONUtil.parseObject(JSONUtil.toJSONString(d), Menu.class); - assert item != null; - if (ObjectUtil.isNotEmpty(item)) { - // 设置默认值 - if (item.getDeleted() == null) { - item.setDeleted(0); // 新导入的数据deleted设为0 - } - if (item.getSortNumber() == null) { - item.setSortNumber(100); - } - if (item.getParentId() == null) { - item.setParentId(0); - } - menuService.save(item); + System.out.println("从Excel文件中读取到" + list.size() + "条菜单记录"); + + // 存储原始parentId到菜单列表的映射关系 + Map> menuGroups = new HashMap<>(); + // 存储原始ID到新菜单对象的映射关系(用于后续设置正确的parentId) + Map tempIdMapping = new HashMap<>(); + + // 按parentId分组(处理null值) + for (MenuImportParam param : list) { + Integer parentId = param.getParentId() != null ? param.getParentId() : 0; + menuGroups.computeIfAbsent(parentId, k -> new ArrayList<>()).add(param); + } + + System.out.println("菜单分组情况:"); + for (Map.Entry> entry : menuGroups.entrySet()) { + System.out.println(" parentId=" + entry.getKey() + " 的菜单数: " + entry.getValue().size()); + } + + // 先创建所有父级菜单(parentId为0的菜单) + List rootMenus = menuGroups.getOrDefault(0, new ArrayList<>()); + List createdRootMenus = new ArrayList<>(); + + System.out.println("开始创建" + rootMenus.size() + "个根菜单"); + for (MenuImportParam param : rootMenus) { + Menu menu = convertToMenu(param); + menu.setParentId(0); // 根菜单的parentId为0 + menuService.save(menu); + createdRootMenus.add(menu); + System.out.println("创建根菜单: " + menu.getTitle() + ", ID: " + menu.getMenuId() + ", 原始ID: " + param.getMenuId()); + + // 记录原始ID到新菜单的映射关系 + if (param.getMenuId() != null) { + tempIdMapping.put(param.getMenuId(), menu); } - }); + } + + // 递归创建子级菜单(注意:这里不再处理parentId=0的菜单,因为已经在上面处理过了) + System.out.println("开始创建子级菜单(跳过根菜单)"); + // 只处理非根菜单的子级菜单 + for (Map.Entry> entry : menuGroups.entrySet()) { + Integer parentId = entry.getKey(); + if (parentId != 0) { // 跳过根菜单(parentId=0) + System.out.println("处理parentId=" + parentId + "的子菜单"); + createChildMenus(menuGroups, tempIdMapping, parentId); + } + } + + // 获取所有导入的菜单ID + List allImportedMenuIds = new ArrayList<>(); + for (Menu menu : tempIdMapping.values()) { + allImportedMenuIds.add(menu.getMenuId()); + } + + System.out.println("总共导入了" + allImportedMenuIds.size() + "个菜单"); + + // 显示导入的菜单详情 + if (!allImportedMenuIds.isEmpty()) { + List allImportedMenus = menuService.list(new LambdaQueryWrapper() + .in(Menu::getMenuId, allImportedMenuIds) + .orderByAsc(Menu::getParentId, Menu::getSortNumber)); + System.out.println("导入的菜单详情:"); + for (Menu menu : allImportedMenus) { + System.out.println(" ID: " + menu.getMenuId() + ", 名称: " + menu.getTitle() + + ", 父级ID: " + menu.getParentId() + ", 类型: " + menu.getMenuType()); + } + } + + // 为超级管理员配置菜单权限 + if (!allImportedMenuIds.isEmpty()) { + List allImportedMenus = menuService.list(new LambdaQueryWrapper() + .in(Menu::getMenuId, allImportedMenuIds)); + System.out.println("为" + allImportedMenus.size() + "个菜单配置超级管理员权限"); + configureSuperAdminPermissionsForImportedMenus(allImportedMenus); + } + + // 最终检查 + long finalCount = menuService.count(); + System.out.println("导入后菜单总数: " + finalCount); + System.out.println("=== 菜单导入流程结束 ==="); + return success("成功导入" + list.size() + "条"); } catch (Exception e) { e.printStackTrace(); + return fail("导入失败: " + e.getMessage()); + } + } + + /** + * 递归创建子级菜单 + * @param menuGroups 菜单分组 + * @param tempIdMapping 临时ID映射关系 + * @param originalParentId 原始父级ID + */ + private void createChildMenus(Map> menuGroups, + Map tempIdMapping, + Integer originalParentId) { + System.out.println(">>> 进入createChildMenus方法,处理originalParentId=" + originalParentId); + + // 特殊处理:originalParentId=0的情况已经在主方法中处理过了,这里不应该再处理 + if (originalParentId == 0) { + System.out.println(" 跳过originalParentId=0的处理(已在主方法中处理)"); + System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId); + return; + } + + List childMenus = menuGroups.get(originalParentId); + if (childMenus == null || childMenus.isEmpty()) { + System.out.println(" 没有找到originalParentId=" + originalParentId + "的子菜单"); + System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId); + return; + } + + // 获取新的父级菜单对象 + Menu parentMenu = tempIdMapping.get(originalParentId); + if (parentMenu == null) { + System.out.println(" 未找到原始ID为" + originalParentId + "的父级菜单,跳过处理"); + System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId); + return; + } + + Integer newParentId = parentMenu.getMenuId(); + + System.out.println(" 创建父级ID为" + originalParentId + "(新ID:" + newParentId + ")的子菜单,共" + childMenus.size() + "个"); + + // 创建所有子菜单 + for (int i = 0; i < childMenus.size(); i++) { + MenuImportParam param = childMenus.get(i); + System.out.println(" 处理第" + (i+1) + "个子菜单,原始ID: " + param.getMenuId() + ", 原始ParentId: " + param.getParentId()); + + Menu menu = convertToMenu(param); + menu.setParentId(newParentId); + menuService.save(menu); + + System.out.println(" 创建子菜单: " + menu.getTitle() + ", ID: " + menu.getMenuId() + + ", 父级ID: " + menu.getParentId() + ", 原始ID: " + param.getMenuId()); + + // 记录原始ID到新菜单的映射关系 + if (param.getMenuId() != null) { + tempIdMapping.put(param.getMenuId(), menu); + } + + // 递归创建当前菜单的子级菜单(使用原始ID作为下一个递归的父级ID) + System.out.println(" 递归调用createChildMenus,处理原始ID=" + param.getMenuId() + "的子菜单"); + createChildMenus(menuGroups, tempIdMapping, param.getMenuId()); + } + + System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId); + } + + /** + * 将MenuImportParam转换为Menu实体 + * @param param MenuImportParam对象 + * @return Menu实体 + */ + private Menu convertToMenu(MenuImportParam param) { + Menu menu = new Menu(); + menu.setParentId(param.getParentId()); + menu.setTitle(param.getTitle()); + menu.setPath(param.getPath()); + menu.setComponent(param.getComponent()); + menu.setModules(param.getModules()); + menu.setModulesUrl(param.getModulesUrl()); + menu.setMenuType(param.getMenuType()); + menu.setSortNumber(param.getSortNumber() != null ? param.getSortNumber() : 0); + menu.setAuthority(param.getAuthority()); + menu.setIcon(param.getIcon()); + menu.setHide(param.getHide()); + menu.setAppId(param.getAppId()); + menu.setTenantId(param.getTenantId()); + menu.setDeleted(0); // 新导入的数据deleted设为0 + return menu; + } + + /** + * 为超级管理员配置导入菜单的权限 + * @param importedMenus 导入的菜单列表 + */ + private void configureSuperAdminPermissionsForImportedMenus(List importedMenus) { + try { + // 1.查找当前租户的超管权限的roleId + final Role superAdmin = roleService.getOne(new LambdaQueryWrapper().eq(Role::getRoleCode, "superAdmin")); + if (superAdmin == null) { + System.out.println("未找到superAdmin角色"); + return; + } + + final Integer roleId = superAdmin.getRoleId(); + final Integer tenantId = superAdmin.getTenantId(); + + // 为所有导入的菜单配置权限 + for (Menu menu : importedMenus) { + RoleMenu roleMenu = new RoleMenu(); + roleMenu.setRoleId(roleId); + roleMenu.setMenuId(menu.getMenuId()); + roleMenu.setTenantId(tenantId); + roleMenuService.save(roleMenu); + } + + // 调整根菜单的排序(如果有根菜单的话) + for (Menu menu : importedMenus) { + if (menu.getParentId() == 0) { + menu.setSortNumber(100); + menuService.updateById(menu); + break; + } + } + + System.out.println("为超级管理员配置菜单权限成功,共配置了" + importedMenus.size() + "个菜单"); + } catch (Exception e) { + System.err.println("为超级管理员配置菜单权限失败: " + e.getMessage()); + e.printStackTrace(); } - return fail("导入失败"); } } diff --git a/src/main/java/com/gxwebsoft/common/system/param/MenuImportParam.java b/src/main/java/com/gxwebsoft/common/system/param/MenuImportParam.java index d223b50..7245b24 100644 --- a/src/main/java/com/gxwebsoft/common/system/param/MenuImportParam.java +++ b/src/main/java/com/gxwebsoft/common/system/param/MenuImportParam.java @@ -15,16 +15,19 @@ import java.io.Serializable; public class MenuImportParam implements Serializable { private static final long serialVersionUID = 1L; - @Excel(name = "上级id, 0是顶级") + @Excel(name = "菜单ID") + private Integer menuId; + + @Excel(name = "父级ID") private Integer parentId; @Excel(name = "菜单名称") private String title; - @Excel(name = "菜单路由地址") + @Excel(name = "路由地址") private String path; - @Excel(name = "菜单组件地址") + @Excel(name = "组件路径") private String component; @Excel(name = "模块ID") @@ -33,7 +36,7 @@ public class MenuImportParam implements Serializable { @Excel(name = "模块API") private String modulesUrl; - @Excel(name = "菜单类型, 0菜单, 1按钮") + @Excel(name = "菜单类型") private Integer menuType; @Excel(name = "排序号") @@ -42,10 +45,10 @@ public class MenuImportParam implements Serializable { @Excel(name = "权限标识") private String authority; - @Excel(name = "菜单图标") + @Excel(name = "图标") private String icon; - @Excel(name = "是否隐藏, 0否, 1是") + @Excel(name = "是否隐藏") private Integer hide; @Excel(name = "关联应用") diff --git a/src/main/java/com/gxwebsoft/common/system/service/MenuService.java b/src/main/java/com/gxwebsoft/common/system/service/MenuService.java index db8e967..0b97e7c 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/MenuService.java +++ b/src/main/java/com/gxwebsoft/common/system/service/MenuService.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.gxwebsoft.common.system.entity.Menu; import com.gxwebsoft.common.system.param.MenuParam; +import java.util.List; + /** * 菜单Service * @@ -15,4 +17,11 @@ public interface MenuService extends IService { Boolean cloneMenu(MenuParam param); Boolean install(Integer id); -} + + /** + * 根据参数获取菜单列表 + * @param param 菜单参数 + * @return 菜单列表 + */ + List getMenuByClone(MenuParam param); +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/MenuServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/MenuServiceImpl.java index 4d1172b..726924f 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/MenuServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/MenuServiceImpl.java @@ -237,4 +237,9 @@ public class MenuServiceImpl extends ServiceImpl implements Me } } } + + @Override + public List getMenuByClone(MenuParam param) { + return baseMapper.getMenuByClone(param); + } }