@@ -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:remo ve') " )
@PreAuthorize ( " hasAuthority('sys:menu:sa ve') " )
@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 < Menu > undeletedMenus = menuService . list ( new LambdaQueryWrapper < Menu > ( ) . 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 < Menu > ( ) . eq ( Menu : : getDeleted , 1 ) ) ;
boolean deleteResult = menuService . remove ( new LambdaQueryWrapper < Menu > ( ) . eq ( Menu : : getDeleted , 1 ) ) ;
System . out . println ( " 已永久删除标记为deleted=1的菜单记录, 结果: " + deleteResult ) ;
// 第二步: 将现有未删除的记录( deleted=0) 标记为 deleted=1
if ( ! undeletedMenus . isEmpty ( ) ) {
LambdaUpdateWrapper < Menu > updateWrapper = new LambdaUpdateWrapper < > ( ) ;
updateWrapper . eq ( Menu : : getDeleted , 0 ) ;
updateWrapper . set ( Menu : : getDeleted , 1 ) ;
menuService . update ( updateWrapper ) ;
boolean updateResult = menuService . update ( updateWrapper ) ;
System . out . println ( " 更新未删除菜单记录的结果: " + updateResult ) ;
}
// 检查更新后的菜单数据
long afterCleanupCount = menuService . count ( new LambdaQueryWrapper < Menu > ( ) . eq ( Menu : : getDeleted , 0 ) ) ;
System . out . println ( " 清理后未标记删除的菜单数: " + afterCleanupCount ) ;
// 第三步: 导入XLS文件的内容
List < MenuImportParam > 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
System . out . println ( " 从Excel文件中读取到 " + list . size ( ) + " 条菜单记录 " ) ;
// 存储原始parentId到菜单列表的映射关系
Map < Integer , List < MenuImportParam > > menuGroups = new HashMap < > ( ) ;
// 存储原始ID到新菜单对象的映射关系( 用于后续设置正确的parentId)
Map < Integer , Menu > 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 ) ;
}
if ( item . getSortNumber ( ) = = null ) {
item . setSortNumber ( 100 ) ;
System . out . println ( " 菜单分组情况: " ) ;
for ( Map . Entry < Integer , List < MenuImportParam > > entry : menuGroups . entrySet ( ) ) {
System . out . println ( " parentId= " + entry . getKey ( ) + " 的菜单数: " + entry . getValue ( ) . size ( ) ) ;
}
if ( item . getParentId ( ) = = null ) {
item . setParentId ( 0 ) ;
// 先创建所有父级菜单( parentId为0的菜单)
List < MenuImportParam > rootMenus = menuGroups . getOrDefault ( 0 , new ArrayList < > ( ) ) ;
List < Menu > 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 ) ;
}
menuService . save ( item ) ;
}
} ) ;
// 递归创建子级菜单( 注意: 这里不再处理parentId=0的菜单, 因为已经在上面处理过了)
System . out . println ( " 开始创建子级菜单(跳过根菜单) " ) ;
// 只处理非根菜单的子级菜单
for ( Map . Entry < Integer , List < MenuImportParam > > entry : menuGroups . entrySet ( ) ) {
Integer parentId = entry . getKey ( ) ;
if ( parentId ! = 0 ) { // 跳过根菜单( parentId=0)
System . out . println ( " 处理parentId= " + parentId + " 的子菜单 " ) ;
createChildMenus ( menuGroups , tempIdMapping , parentId ) ;
}
}
// 获取所有导入的菜单ID
List < Integer > allImportedMenuIds = new ArrayList < > ( ) ;
for ( Menu menu : tempIdMapping . values ( ) ) {
allImportedMenuIds . add ( menu . getMenuId ( ) ) ;
}
System . out . println ( " 总共导入了 " + allImportedMenuIds . size ( ) + " 个菜单 " ) ;
// 显示导入的菜单详情
if ( ! allImportedMenuIds . isEmpty ( ) ) {
List < Menu > allImportedMenus = menuService . list ( new LambdaQueryWrapper < Menu > ( )
. 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 < Menu > allImportedMenus = menuService . list ( new LambdaQueryWrapper < Menu > ( )
. 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 < Integer , List < MenuImportParam > > menuGroups ,
Map < Integer , Menu > 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 < MenuImportParam > 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 < Menu > importedMenus ) {
try {
// 1.查找当前租户的超管权限的roleId
final Role superAdmin = roleService . getOne ( new LambdaQueryWrapper < Role > ( ) . 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 ( " 导入失败 " ) ;
}
}