feat(admin-server): 将管理员超级管理员标识改造为角色系统,新增角色及菜单管理相关表结构

This commit is contained in:
FallingCliff 2025-06-16 13:04:29 +08:00
parent 41cae3cb1f
commit 9cdffe1f70
24 changed files with 113 additions and 975 deletions

View File

@ -1,86 +0,0 @@
package com.example.admin_server.controller.admin;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.admin_server.common.Result;
import com.example.admin_server.model.dto.MenuDTO;
import com.example.admin_server.model.entity.SysMenu;
import com.example.admin_server.service.ISysMenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 菜单管理控制器
*/
@RestController
@RequestMapping("/api/admin/menu")
@RequiredArgsConstructor
@Api(tags = {"菜单管理"})
public class SysMenuController {
private final ISysMenuService menuService;
@PostMapping("/add")
@ApiOperation("创建菜单")
public Result<?> createMenu(@Validated @RequestBody MenuDTO menuDTO) {
SysMenu menu = new SysMenu();
BeanUtil.copyProperties(menuDTO, menu);
return Result.ok(menuService.save(menu));
}
@PutMapping("/edit")
@ApiOperation("更新菜单")
public Result<?> updateMenu(@Validated @RequestBody MenuDTO menuDTO) {
SysMenu menu = new SysMenu();
BeanUtil.copyProperties(menuDTO, menu);
return Result.ok(menuService.updateById(menu));
}
@DeleteMapping("/delete")
@ApiOperation("删除菜单")
public Result<?> deleteMenu(@RequestParam Long id) {
// 检查是否有子菜单
int count = Math.toIntExact(menuService.count(new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getParentId, id)));
if (count > 0) {
return Result.fail("存在子菜单,无法删除");
}
return Result.ok(menuService.removeById(id));
}
@GetMapping("/detail")
@ApiOperation("获取菜单详情")
public Result<SysMenu> getMenuDetail(@RequestParam Long id) {
SysMenu menu = menuService.getById(id);
return menu != null ? Result.ok(menu) : Result.fail("菜单不存在");
}
@GetMapping("/tree")
@ApiOperation("获取菜单树")
public Result<List<SysMenu>> getMenuTree() {
List<SysMenu> menuTree = menuService.getAllMenuTree();
return Result.ok(menuTree);
}
@GetMapping("/current")
@ApiOperation("获取当前管理员的菜单树")
public Result<List<SysMenu>> getCurrentAdminMenus() {
// 这里应该从安全上下文中获取当前登录的管理员ID
// 为了演示这里使用一个固定的ID
Long adminId = 1L; // 实际应用中应该从SecurityContext获取
List<SysMenu> menuTree = menuService.getMenusByAdminId(adminId);
return Result.ok(menuTree);
}
@GetMapping("/list")
@ApiOperation("获取所有菜单列表")
public Result<List<SysMenu>> getMenuList() {
List<SysMenu> menus = menuService.list();
return Result.ok(menus);
}
}

View File

@ -1,72 +0,0 @@
package com.example.admin_server.controller.admin;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.admin_server.common.Result;
import com.example.admin_server.common.query.IPageRequest;
import com.example.admin_server.model.dto.IdListDTO;
import com.example.admin_server.model.dto.RoleDTO;
import com.example.admin_server.model.entity.SysRole;
import com.example.admin_server.service.ISysRoleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/admin/role")
@RequiredArgsConstructor
@Api(tags = {"角色管理"})
public class SysRoleController {
private final ISysRoleService roleService;
@PostMapping("/add")
@ApiOperation("创建角色")
public Result<?> createRole(@Validated @RequestBody RoleDTO roleDTO) {
SysRole role = BeanUtil.copyProperties(roleDTO, SysRole.class);
return Result.ok(roleService.save(role));
}
@PutMapping("/edit")
@ApiOperation("更新角色")
public Result<?> updateRole(@Validated @RequestBody RoleDTO roleDTO) {
SysRole role = BeanUtil.copyProperties(roleDTO, SysRole.class);
return Result.ok(roleService.updateById(role));
}
@DeleteMapping("/delete")
@ApiOperation("批量删除角色")
public Result<?> deleteRoles(@Validated @RequestBody IdListDTO idList) {
return Result.ok(roleService.removeByIds(idList.getIdList()));
}
@GetMapping("/detail")
@ApiOperation("获取角色详情")
public Result<SysRole> getRoleDetail(@RequestParam Long id) {
SysRole role = roleService.getById(id);
return role != null ? Result.ok(role) : Result.fail("角色不存在");
}
@GetMapping("/list")
@ApiOperation("获取角色列表")
public Result<List<SysRole>> getRoleList() {
return Result.ok(roleService.list());
}
@PostMapping("/menu/assign")
@ApiOperation("分配角色菜单")
public Result<?> assignRoleMenu(@RequestParam Long roleId, @RequestBody List<Long> menuIds) {
roleService.assignMenus(roleId, menuIds);
return Result.ok();
}
@GetMapping("/menu/list")
@ApiOperation("获取角色菜单")
public Result<List<Long>> getRoleMenus(@RequestParam Long roleId) {
return Result.ok(roleService.getMenuIdsByRoleId(roleId));
}
}

View File

@ -1,32 +0,0 @@
package com.example.admin_server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.admin_server.model.entity.SysAdminRole;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 管理员角色关联Mapper接口
*/
@Mapper
public interface SysAdminRoleMapper extends BaseMapper<SysAdminRole> {
/**
* 根据管理员ID查询角色ID列表
* @param adminId 管理员ID
* @return 角色ID列表
*/
@Select("SELECT role_id FROM sys_admin_role WHERE admin_id = #{adminId}")
List<Long> selectRoleIdsByAdminId(@Param("adminId") Long adminId);
/**
* 批量插入管理员角色关联
* @param adminId 管理员ID
* @param roleIds 角色ID列表
* @return 影响行数
*/
int batchInsert(@Param("adminId") Long adminId, @Param("roleIds") List<Long> roleIds);
}

View File

@ -1,36 +0,0 @@
package com.example.admin_server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.admin_server.model.entity.SysMenu;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 菜单Mapper接口
*/
@Mapper
public interface SysMenuMapper extends BaseMapper<SysMenu> {
/**
* 根据管理员ID查询菜单列表
* @param adminId 管理员ID
* @return 菜单列表
*/
@Select("SELECT DISTINCT m.* FROM sys_menu m " +
"INNER JOIN sys_role_menu rm ON m.id = rm.menu_id " +
"INNER JOIN sys_admin_role ar ON rm.role_id = ar.role_id " +
"WHERE ar.admin_id = #{adminId} AND m.status = 1 " +
"ORDER BY m.sort_order")
List<SysMenu> selectMenusByAdminId(@Param("adminId") Long adminId);
/**
* 根据角色ID查询菜单ID列表
* @param roleId 角色ID
* @return 菜单ID列表
*/
@Select("SELECT menu_id FROM sys_role_menu WHERE role_id = #{roleId}")
List<Long> selectMenuIdsByRoleId(@Param("roleId") Long roleId);
}

View File

@ -1,12 +0,0 @@
package com.example.admin_server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.admin_server.model.entity.SysRole;
import org.apache.ibatis.annotations.Mapper;
/**
* 角色Mapper接口
*/
@Mapper
public interface SysRoleMapper extends BaseMapper<SysRole> {
}

View File

@ -1,23 +0,0 @@
package com.example.admin_server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.admin_server.model.entity.SysRoleMenu;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 角色菜单关联Mapper接口
*/
@Mapper
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> {
/**
* 批量插入角色菜单关联
* @param roleId 角色ID
* @param menuIds 菜单ID列表
* @return 影响行数
*/
int batchInsert(@Param("roleId") Long roleId, @Param("menuIds") List<Long> menuIds);
}

View File

@ -59,9 +59,9 @@ public class Admin implements Serializable {
@TableField("status") @TableField("status")
private Integer status; private Integer status;
@ApiModelProperty(value = "是否为超级管理员1是0否") @ApiModelProperty(value = "角色")
@TableField("is_super") @TableField("role_id")
private Integer isSuper; private Integer roleId;
@ApiModelProperty(value = "上次登录IP") @ApiModelProperty(value = "上次登录IP")
@TableField("last_login_ip") @TableField("last_login_ip")

View File

@ -1,37 +0,0 @@
package com.example.admin_server.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 管理员角色关联实体类
*/
@Data
@TableName("sys_admin_role")
public class SysAdminRole {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 管理员ID
*/
private Long adminId;
/**
* 角色ID
*/
private Long roleId;
/**
* 创建时间
*/
private LocalDateTime createTime;
}

View File

@ -1,77 +1,78 @@
package com.example.admin_server.model.entity; package com.example.admin_server.model.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableField; import io.swagger.annotations.ApiModel;
import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.annotations.ApiModelProperty;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
/** /**
* 系统菜单实体类 * <p>
* 菜单表
* </p>
*
* @author FallingCliff
* @since 2025-05-24
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_menu") @TableName("sys_menu")
@ApiModel(value="Menu对象", description="后台管理员表")
public class SysMenu implements Serializable { public class SysMenu implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** @ApiModelProperty(value = "主键ID")
* 菜单ID
*/
@TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private Long id; private Long id;
/** @ApiModelProperty(value = "父菜单ID")
* 父菜单ID @TableField("parent_id")
*/
private Long parentId; private Long parentId;
/** @ApiModelProperty(value = "菜单标题")
* 菜单名称 @TableField("title")
*/ private String title;
private String menuName;
/** @ApiModelProperty(value = "菜单URL")
* 菜单URL @TableField("url")
*/ private String url;
private String menuUrl;
/** @ApiModelProperty(value = "权限标识")
* 权限标识 @TableField("perms")
*/
private String perms; private String perms;
/** @ApiModelProperty(value = "图标")
* 图标 @TableField("icon")
*/
private String icon; private String icon;
/** @ApiModelProperty(value = "类型 0:目录 1:菜单 2:按钮")
* 类型 0:目录 1:菜单 2:按钮 @TableField("type")
*/
private Integer type; private Integer type;
/** @ApiModelProperty(value = "排序")
* 排序 @TableField("sort")
*/ private Integer sort;
private Integer sortOrder;
/** @ApiModelProperty(value = "是否可见 0:不可见 1:可见")
* 是否可见 0:不可见 1:可见 @TableField("visible")
*/
private Integer visible; private Integer visible;
/** @ApiModelProperty(value = "状态 0:禁用 1:启用")
* 状态 0:禁用 1:启用 @TableField("status")
*/
private Integer status; private Integer status;
/** @ApiModelProperty(value = "子菜单列表")
* 子菜单列表
*/
@TableField(exist = false) @TableField(exist = false)
private List<SysMenu> children; private List<SysMenu> children;
@ApiModelProperty(value = "是否删除0未删除1已删除")
@TableField("deleted")
@TableLogic
private Integer deleted;
} }

View File

@ -1,52 +1,62 @@
package com.example.admin_server.model.entity; package com.example.admin_server.model.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.annotations.ApiModel;
import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/** /**
* 角色实体类 * <p>
* 角色表
* </p>
*
* @author FallingCliff
* @since 2025-06-16
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_role") @TableName("sys_role")
public class SysRole { @ApiModel(value = "SysRole对象", description = "角色表")
public class SysRole implements Serializable {
/** private static final long serialVersionUID = 1L;
* 角色ID
*/ @ApiModelProperty(value = "角色ID")
@TableId(type = IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private Long id; private Long id;
/** @ApiModelProperty(value = "角色名称")
* 角色名称 @TableField("role_name")
*/
private String roleName; private String roleName;
/** @ApiModelProperty(value = "角色描述")
* 角色编码 @TableField("description")
*/
private String roleCode;
/**
* 角色描述
*/
private String description; private String description;
/** @ApiModelProperty(value = "角色描述")
* 状态(0:禁用,1:启用) @TableField("permissions")
*/ private String permissions;
@ApiModelProperty(value = "状态(0:禁用,1:启用)")
@TableField("status")
private Integer status; private Integer status;
/** @ApiModelProperty(value = "创建时间")
* 创建时间 @TableField("create_time")
*/
private LocalDateTime createTime; private LocalDateTime createTime;
/** @ApiModelProperty(value = "更新时间")
* 更新时间 @TableField("update_time")
*/
private LocalDateTime updateTime; private LocalDateTime updateTime;
@ApiModelProperty(value = "是否删除0未删除1已删除")
@TableField("deleted")
@TableLogic
private Integer deleted;
} }

View File

@ -1,37 +0,0 @@
package com.example.admin_server.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 角色菜单关联实体类
*/
@Data
@TableName("sys_role_menu")
public class SysRoleMenu {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 角色ID
*/
private Long roleId;
/**
* 菜单ID
*/
private Long menuId;
/**
* 创建时间
*/
private LocalDateTime createTime;
}

View File

@ -1,34 +0,0 @@
package com.example.admin_server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.admin_server.model.entity.SysAdminRole;
import java.util.List;
/**
* 管理员角色关联服务接口
*/
public interface ISysAdminRoleService extends IService<SysAdminRole> {
/**
* 保存管理员角色关联
* @param adminId 管理员ID
* @param roleIds 角色ID列表
* @return 是否成功
*/
boolean saveAdminRoles(Long adminId, List<Long> roleIds);
/**
* 获取管理员的角色ID列表
* @param adminId 管理员ID
* @return 角色ID列表
*/
List<Long> getRoleIdsByAdminId(Long adminId);
/**
* 删除管理员的所有角色关联
* @param adminId 管理员ID
* @return 是否成功
*/
boolean removeByAdminId(Long adminId);
}

View File

@ -1,38 +0,0 @@
package com.example.admin_server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.admin_server.model.entity.SysMenu;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 菜单服务接口
*/
public interface ISysMenuService extends IService<SysMenu> {
/**
* 获取管理员的菜单列表
* @param adminId 管理员ID
* @return 菜单列表
*/
List<SysMenu> getMenusByAdminId(Long adminId);
/**
* 构建菜单树
* @param menus 菜单列表
* @return 菜单树
*/
List<SysMenu> buildMenuTree(List<SysMenu> menus);
/**
* 获取所有菜单树
* @return 菜单树
*/
List<SysMenu> getAllMenuTree();
@Transactional(rollbackFor = Exception.class)
@CacheEvict(allEntries = true)
boolean removeById(Long id);
}

View File

@ -1,27 +0,0 @@
package com.example.admin_server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.admin_server.model.entity.SysRoleMenu;
import java.util.List;
/**
* 角色菜单关联服务接口
*/
public interface ISysRoleMenuService extends IService<SysRoleMenu> {
/**
* 保存角色菜单关联
* @param roleId 角色ID
* @param menuIds 菜单ID列表
* @return 是否成功
*/
boolean saveRoleMenus(Long roleId, List<Long> menuIds);
/**
* 删除角色的所有菜单关联
* @param roleId 角色ID
* @return 是否成功
*/
boolean removeByRoleId(Long roleId);
}

View File

@ -1,51 +0,0 @@
package com.example.admin_server.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.admin_server.model.entity.SysRole;
import com.example.admin_server.model.query.RoleQuery;
import java.util.List;
/**
* 角色服务接口
*/
public interface ISysRoleService extends IService<SysRole> {
/**
* 创建角色
* @param role 角色信息
* @param menuIds 菜单ID列表
* @return 是否成功
*/
boolean createRole(SysRole role, List<Long> menuIds);
/**
* 更新角色
* @param role 角色信息
* @param menuIds 菜单ID列表
* @return 是否成功
*/
boolean updateRole(SysRole role, List<Long> menuIds);
/**
* 删除角色
* @param roleId 角色ID
* @return 是否成功
*/
boolean deleteRole(Long roleId);
/**
* 获取角色的菜单ID列表
* @param roleId 角色ID
* @return 菜单ID列表
*/
List<Long> getRoleMenuIds(Long roleId);
/**
* 角色分页查询
* @param query 查询参数
* @return 分页结果
*/
IPage<SysRole> pageList(RoleQuery query);
}

View File

@ -30,7 +30,6 @@ public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements
Page<Admin> page = pageRequest.toPage(); Page<Admin> page = pageRequest.toPage();
QueryWrapper<Admin> wrapper = QueryWrapperBuilder.build(query, Admin.class); QueryWrapper<Admin> wrapper = QueryWrapperBuilder.build(query, Admin.class);
IPage<Admin> adminPage = this.page(page, wrapper); IPage<Admin> adminPage = this.page(page, wrapper);
return BeanConvertUtil.convertPage(adminPage, AdminVO::new); return BeanConvertUtil.convertPage(adminPage, AdminVO::new);
} }
} }

View File

@ -1,53 +0,0 @@
package com.example.admin_server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.admin_server.mapper.SysAdminRoleMapper;
import com.example.admin_server.model.entity.SysAdminRole;
import com.example.admin_server.service.ISysAdminRoleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* 管理员角色关联服务实现类
*/
@Service
public class SysAdminRoleServiceImpl extends ServiceImpl<SysAdminRoleMapper, SysAdminRole> implements ISysAdminRoleService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean saveAdminRoles(Long adminId, List<Long> roleIds) {
// 先删除原有的关联
removeByAdminId(adminId);
// 如果roleIds为空则直接返回true表示只是清空关联
if (roleIds == null || roleIds.isEmpty()) {
return true;
}
// 批量插入新的关联
return baseMapper.batchInsert(adminId, roleIds) > 0;
}
@Override
public List<Long> getRoleIdsByAdminId(Long adminId) {
// 查询管理员的角色关联
List<SysAdminRole> adminRoles = list(new LambdaQueryWrapper<SysAdminRole>()
.eq(SysAdminRole::getAdminId, adminId));
// 提取角色ID
return adminRoles.stream()
.map(SysAdminRole::getRoleId)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean removeByAdminId(Long adminId) {
return remove(new LambdaQueryWrapper<SysAdminRole>()
.eq(SysAdminRole::getAdminId, adminId));
}
}

View File

@ -1,150 +0,0 @@
package com.example.admin_server.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.admin_server.common.exception.BusinessException;
import com.example.admin_server.mapper.SysMenuMapper;
import com.example.admin_server.model.entity.SysMenu;
import com.example.admin_server.service.ISysMenuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 菜单服务实现类
*/
@Slf4j
@Service
@CacheConfig(cacheNames = "sys_menu")
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
@Override
@Cacheable(key = "'admin_menu:' + #adminId")
public List<SysMenu> getMenusByAdminId(Long adminId) {
if (adminId == null) {
throw new BusinessException("管理员ID不能为空");
}
log.info("获取管理员[{}]的菜单列表", adminId);
// 获取管理员的菜单列表
List<SysMenu> menus = baseMapper.selectMenusByAdminId(adminId);
if (CollectionUtil.isEmpty(menus)) {
return new ArrayList<>();
}
// 构建菜单树并排序
return buildMenuTree(menus);
}
@Override
public List<SysMenu> buildMenuTree(List<SysMenu> menus) {
if (CollectionUtil.isEmpty(menus)) {
return new ArrayList<>();
}
// 将菜单列表转换为Map方便查找
Map<Long, SysMenu> menuMap = menus.stream()
.collect(Collectors.toMap(SysMenu::getId, menu -> menu));
List<SysMenu> rootMenus = new ArrayList<>();
// 遍历所有菜单将子菜单添加到父菜单的children列表中
menus.forEach(menu -> {
Long parentId = menu.getParentId();
if (parentId == null || parentId == 0) {
// 如果是根菜单直接添加到结果列表
rootMenus.add(menu);
} else {
// 如果是子菜单添加到父菜单的children列表
SysMenu parentMenu = menuMap.get(parentId);
if (parentMenu != null) {
if (parentMenu.getChildren() == null) {
parentMenu.setChildren(new ArrayList<>());
}
parentMenu.getChildren().add(menu);
// 对子菜单列表进行排序
parentMenu.getChildren().sort(Comparator.comparing(SysMenu::getSortOrder));
}
}
});
// 对根菜单进行排序
rootMenus.sort(Comparator.comparing(SysMenu::getSortOrder));
return rootMenus;
}
@Override
@Cacheable(key = "'all_menu_tree'")
public List<SysMenu> getAllMenuTree() {
log.info("获取所有菜单树");
// 获取所有菜单按照排序字段升序排列
List<SysMenu> allMenus = list(new LambdaQueryWrapper<SysMenu>()
.orderByAsc(SysMenu::getSortOrder));
// 构建菜单树
return buildMenuTree(allMenus);
}
@Override
@Transactional(rollbackFor = Exception.class)
@CacheEvict(allEntries = true)
public boolean save(SysMenu menu) {
validateMenu(menu);
return super.save(menu);
}
@Override
@Transactional(rollbackFor = Exception.class)
@CacheEvict(allEntries = true)
public boolean updateById(SysMenu menu) {
validateMenu(menu);
return super.updateById(menu);
}
@Transactional(rollbackFor = Exception.class)
@CacheEvict(allEntries = true)
@Override
public boolean removeById(Long id) {
// 检查是否存在子菜单
long count = count(new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getParentId, id));
if (count > 0) {
throw new BusinessException("存在子菜单,无法删除");
}
return super.removeById(id);
}
/**
* 验证菜单信息
*
* @param menu 菜单信息
*/
private void validateMenu(SysMenu menu) {
if (menu == null) {
throw new BusinessException("菜单信息不能为空");
}
if (menu.getParentId() != null && menu.getParentId() != 0) {
// 验证父菜单是否存在
SysMenu parentMenu = getById(menu.getParentId());
if (parentMenu == null) {
throw new BusinessException("父菜单不存在");
}
}
// 验证菜单名称是否重复
long count = count(new LambdaQueryWrapper<SysMenu>()
.eq(SysMenu::getMenuName, menu.getMenuName())
.ne(menu.getId() != null, SysMenu::getId, menu.getId()));
if (count > 0) {
throw new BusinessException("菜单名称已存在");
}
}
}

View File

@ -1,40 +0,0 @@
package com.example.admin_server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.admin_server.mapper.SysRoleMenuMapper;
import com.example.admin_server.model.entity.SysRoleMenu;
import com.example.admin_server.service.ISysRoleMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 角色菜单关联服务实现类
*/
@Service
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements ISysRoleMenuService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean saveRoleMenus(Long roleId, List<Long> menuIds) {
// 先删除原有的关联
removeByRoleId(roleId);
// 如果menuIds为空则直接返回true表示只是清空关联
if (menuIds == null || menuIds.isEmpty()) {
return true;
}
// 批量插入新的关联
return baseMapper.batchInsert(roleId, menuIds) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean removeByRoleId(Long roleId) {
return remove(new LambdaQueryWrapper<SysRoleMenu>()
.eq(SysRoleMenu::getRoleId, roleId));
}
}

View File

@ -1,80 +0,0 @@
package com.example.admin_server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.admin_server.mapper.SysRoleMapper;
import com.example.admin_server.model.entity.SysRole;
import com.example.admin_server.model.query.RoleQuery;
import com.example.admin_server.service.ISysRoleMenuService;
import com.example.admin_server.service.ISysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 角色服务实现类
*/
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
@Autowired
private ISysRoleMenuService roleMenuService;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean createRole(SysRole role, List<Long> menuIds) {
// 保存角色基本信息
boolean saved = save(role);
if (saved && menuIds != null && !menuIds.isEmpty()) {
// 保存角色菜单关联
return roleMenuService.saveRoleMenus(role.getId(), menuIds);
}
return saved;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateRole(SysRole role, List<Long> menuIds) {
// 更新角色基本信息
boolean updated = updateById(role);
if (updated) {
// 先删除原有的菜单关联
roleMenuService.removeByRoleId(role.getId());
// 如果有新的菜单关联则保存
if (menuIds != null && !menuIds.isEmpty()) {
return roleMenuService.saveRoleMenus(role.getId(), menuIds);
}
}
return updated;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteRole(Long roleId) {
// 先删除角色菜单关联
roleMenuService.removeByRoleId(roleId);
// 删除角色
return removeById(roleId);
}
@Override
public List<Long> getRoleMenuIds(Long roleId) {
return baseMapper.selectMenuIdsByRoleId(roleId);
}
@Override
public IPage<SysRole> pageList(RoleQuery query) {
Page<SysRole> page = new Page<>(query.getPageNum(), query.getPageSize());
LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
if (query.getKeyword() != null && !query.getKeyword().trim().isEmpty()) {
wrapper.like(SysRole::getName, query.getKeyword())
.or()
.like(SysRole::getCode, query.getKeyword());
}
wrapper.orderByAsc(SysRole::getSort);
return this.page(page, wrapper);
}
}

View File

@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS `admin` (
`phone` varchar(20) DEFAULT NULL COMMENT '手机号', `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像', `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`status` tinyint(1) DEFAULT 1 COMMENT '状态0禁用1正常', `status` tinyint(1) DEFAULT 1 COMMENT '状态0禁用1正常',
`is_super` tinyint(1) DEFAULT 0 COMMENT '是否为超级管理员1是0否', `roleId` int(11) DEFAULT 0 COMMENT '角色ID',
`last_login_ip` varchar(50) DEFAULT NULL COMMENT '上次登录IP', `last_login_ip` varchar(50) DEFAULT NULL COMMENT '上次登录IP',
`last_login_time` datetime DEFAULT NULL COMMENT '上次登录时间', `last_login_time` datetime DEFAULT NULL COMMENT '上次登录时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@ -39,6 +39,34 @@ CREATE TABLE IF NOT EXISTS `customer` (
UNIQUE KEY `uk_openid` (`openid`) UNIQUE KEY `uk_openid` (`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户表';
-- 创建sys_role表
CREATE TABLE IF NOT EXISTS `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(50) NOT NULL COMMENT '角色名称',
`description` varchar(255) DEFAULT NULL COMMENT '角色描述',
`permissions` varchar(255) DEFAULT NULL COMMENT '角色权限',
`status` tinyint(1) DEFAULT 1 COMMENT '状态(0:禁用,1:启用)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_name` (`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
-- 创建sys_menu表
CREATE TABLE IF NOT EXISTS `sys_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`parent_id` bigint(20) DEFAULT NULL COMMENT '父菜单ID',
`title` varchar(100) NOT NULL COMMENT '菜单标题',
`url` varchar(255) DEFAULT NULL COMMENT '菜单URL',
`perms` varchar(100) DEFAULT NULL COMMENT '权限标识',
`icon` varchar(100) DEFAULT NULL COMMENT '图标',
`order_num` int(11) DEFAULT 0 COMMENT '排序',
`visible` tinyint(1) DEFAULT 1 COMMENT '是否可见(1:可见,0:隐藏)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜单表';
-- 初始化超级管理员账号 -- 初始化超级管理员账号
INSERT INTO `admin` (`username`, `password`, `nickname`, `is_super`, `status`) INSERT INTO `admin` (`username`, `password`, `nickname`, `is_super`, `status`)
VALUES ('admin', 'e10adc3949ba59abbe56e057f20f883e', '超级管理员', 1, 1) VALUES ('admin', 'e10adc3949ba59abbe56e057f20f883e', '超级管理员', 1, 1)

View File

@ -1,64 +0,0 @@
-- 角色表
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '角色ID',
role_name VARCHAR(50) NOT NULL COMMENT '角色名称',
role_code VARCHAR(50) NOT NULL COMMENT '角色编码',
description VARCHAR(255) COMMENT '角色描述',
status TINYINT DEFAULT 1 COMMENT '状态(0:禁用,1:启用)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_role_code (role_code)
) COMMENT '角色表';
-- 菜单表
CREATE TABLE sys_menu (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '菜单ID',
parent_id BIGINT DEFAULT 0 COMMENT '父菜单ID',
menu_name VARCHAR(50) NOT NULL COMMENT '菜单名称',
path VARCHAR(200) COMMENT '路由路径',
component VARCHAR(255) COMMENT '组件路径',
redirect VARCHAR(255) COMMENT '重定向地址',
icon VARCHAR(50) COMMENT '菜单图标',
sort_order INT DEFAULT 0 COMMENT '排序',
keep_alive TINYINT DEFAULT 0 COMMENT '是否缓存(0:不缓存,1:缓存)',
hidden TINYINT DEFAULT 0 COMMENT '是否隐藏(0:显示,1:隐藏)',
type TINYINT NOT NULL COMMENT '菜单类型(1:目录,2:菜单,3:按钮)',
status TINYINT DEFAULT 1 COMMENT '状态(0:禁用,1:启用)',
permission VARCHAR(100) COMMENT '权限标识',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT '菜单表';
-- 角色菜单关联表
CREATE TABLE sys_role_menu (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID',
role_id BIGINT NOT NULL COMMENT '角色ID',
menu_id BIGINT NOT NULL COMMENT '菜单ID',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
UNIQUE KEY uk_role_menu (role_id, menu_id)
) COMMENT '角色菜单关联表';
-- 管理员角色关联表
CREATE TABLE sys_admin_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID',
admin_id BIGINT NOT NULL COMMENT '管理员ID',
role_id BIGINT NOT NULL COMMENT '角色ID',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
UNIQUE KEY uk_admin_role (admin_id, role_id)
) COMMENT '管理员角色关联表';
-- 初始化超级管理员角色
INSERT INTO sys_role (role_name, role_code, description) VALUES
('超级管理员', 'ROLE_SUPER_ADMIN', '系统超级管理员');
-- 初始化基础菜单数据
INSERT INTO sys_menu (parent_id, menu_name, path, component, icon, type, permission) VALUES
(0, '仪表盘', 'dashboard', 'Dashboard', 'dashboard', 2, 'dashboard:view'),
(0, '系统管理', 'system', null, 'setting', 1, 'system:view'),
(2, '用户管理', 'user', 'system/UserManagement', 'user', 2, 'system:user:view'),
(2, '角色管理', 'role', 'system/RoleManagement', 'team', 2, 'system:role:view'),
(2, '菜单管理', 'menu', 'system/MenuManagement', 'menu', 2, 'system:menu:view');
-- 为超级管理员分配所有菜单
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT 1, id FROM sys_menu;

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.admin_server.mapper.SysAdminRoleMapper">
<!-- 批量插入管理员角色关联 -->
<insert id="batchInsert">
INSERT INTO sys_admin_role (admin_id, role_id, create_time)
VALUES
<foreach collection="roleIds" item="roleId" separator=",">
(#{adminId}, #{roleId}, NOW())
</foreach>
</insert>
</mapper>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.admin_server.mapper.SysRoleMenuMapper">
<!-- 批量插入角色菜单关联 -->
<insert id="batchInsert">
INSERT INTO sys_role_menu (role_id, menu_id, create_time)
VALUES
<foreach collection="menuIds" item="menuId" separator=",">
(#{roleId}, #{menuId}, NOW())
</foreach>
</insert>
</mapper>