动态路由

This commit is contained in:
dengqichen 2025-10-31 22:29:21 +08:00
parent b3c6d705b0
commit 563c189d8d
14 changed files with 520 additions and 66 deletions

View File

@ -4,13 +4,16 @@ import com.qqchen.deploy.backend.deploy.dto.TeamDTO;
import com.qqchen.deploy.backend.deploy.entity.Team;
import com.qqchen.deploy.backend.deploy.query.TeamQuery;
import com.qqchen.deploy.backend.deploy.service.ITeamService;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.framework.security.annotation.CheckPermission;
import com.qqchen.deploy.backend.framework.security.annotation.PermissionPrefix;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@ -20,15 +23,38 @@ import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/v1/teams")
@PermissionPrefix("deploy:team") // 定义权限前缀
@Tag(name = "团队管理", description = "团队的增删改查接口")
public class TeamApiController extends BaseController<Team, TeamDTO, Long, TeamQuery> {
@Resource
private ITeamService teamService;
// 需要 "deploy:team:create" 权限
@Override
@CheckPermission("create")
public Response<TeamDTO> create(@RequestBody @Valid TeamDTO dto) {
return super.create(dto);
}
// 需要 "deploy:team:update" 权限
@Override
@CheckPermission("update")
public Response<TeamDTO> update(@PathVariable Long id, @RequestBody @Valid TeamDTO dto) {
return super.update(id, dto);
}
// 需要 "deploy:team:delete" 权限
@Override
@CheckPermission("delete")
public Response<Void> delete(@PathVariable Long id) {
return super.delete(id);
}
@Override
protected void exportData(HttpServletResponse response, List<TeamDTO> data) {
// TODO: 实现导出功能
}
}

View File

@ -0,0 +1,41 @@
package com.qqchen.deploy.backend.framework.security.annotation;
import java.lang.annotation.*;
/**
* 权限检查注解
* 用于Controller方法级别定义该方法需要的操作权限
*
* 使用示例
* <pre>
* @RestController
* @PermissionPrefix("deploy:team")
* public class TeamApiController {
* // 需要 "deploy:team:create" 权限
* @CheckPermission("create")
* public Response<TeamDTO> create(@RequestBody TeamDTO dto) {
* ...
* }
*
* // 需要 "deploy:team:update" 权限
* @CheckPermission("update")
* public Response<TeamDTO> update(@PathVariable Long id, @RequestBody TeamDTO dto) {
* ...
* }
* }
* </pre>
*
* @author qqchen
* @date 2025-10-31
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckPermission {
/**
* 操作名称 "create""update""delete""list""view"
* 将与类级别的 @PermissionPrefix 拼接成完整权限点
*/
String value();
}

View File

@ -0,0 +1,34 @@
package com.qqchen.deploy.backend.framework.security.annotation;
import java.lang.annotation.*;
/**
* 权限前缀注解
* 用于Controller类级别定义该Controller所有权限的前缀
*
* 使用示例
* <pre>
* @RestController
* @PermissionPrefix("deploy:team")
* public class TeamApiController {
* // 方法需要 "deploy:team:create" 权限
* @CheckPermission("create")
* public Response<TeamDTO> create(@RequestBody TeamDTO dto) {
* ...
* }
* }
* </pre>
*
* @author qqchen
* @date 2025-10-31
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionPrefix {
/**
* 权限前缀 "deploy:team""system:user"
*/
String value();
}

View File

@ -0,0 +1,114 @@
package com.qqchen.deploy.backend.framework.security.aspect;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.security.annotation.CheckPermission;
import com.qqchen.deploy.backend.framework.security.annotation.PermissionPrefix;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Collection;
/**
* 权限检查切面
* 基于 Spring Security结合自定义注解实现细粒度权限控制
*
* 工作原理
* 1. 拦截所有带有 @CheckPermission 注解的方法
* 2. 从类级别获取 @PermissionPrefix 注解得到权限前缀
* 3. 拼接权限前缀和操作名称得到完整权限点
* 4. SecurityContextHolder 获取当前用户的权限列表
* 5. 判断用户是否拥有该权限如果没有则抛出异常
*
* @author qqchen
* @date 2025-10-31
*/
@Aspect
@Component
@Order(1)
@Slf4j
public class PermissionCheckAspect {
@Around("@annotation(com.qqchen.deploy.backend.framework.security.annotation.CheckPermission)")
public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
// 1. 获取方法注解信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CheckPermission annotation = method.getAnnotation(CheckPermission.class);
String operation = annotation.value();
// 2. 获取类级别的权限前缀
Class<?> controllerClass = joinPoint.getTarget().getClass();
PermissionPrefix prefixAnnotation = findPermissionPrefix(controllerClass);
if (prefixAnnotation == null) {
log.warn("Controller {} 未添加 @PermissionPrefix 注解,跳过权限检查",
controllerClass.getSimpleName());
return joinPoint.proceed();
}
String prefix = prefixAnnotation.value();
String requiredPermission = prefix + ":" + operation;
// 3. SecurityContext 获取当前用户权限
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
log.warn("用户未认证,权限检查失败: {}", requiredPermission);
throw new BusinessException(ResponseCode.UNAUTHORIZED);
}
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
log.debug("权限检查: user={}, required={}, userPermissions={}",
authentication.getName(), requiredPermission, authorities.size());
// 4. 判断是否拥有权限
boolean hasPermission = authorities.stream()
.anyMatch(auth -> auth.getAuthority().equals(requiredPermission));
if (!hasPermission) {
log.warn("权限检查失败: user={}, required={}",
authentication.getName(), requiredPermission);
throw new BusinessException(ResponseCode.FORBIDDEN,
new Object[]{"缺少权限: " + requiredPermission});
}
log.debug("权限检查通过: {}", requiredPermission);
return joinPoint.proceed();
}
/**
* 查找类或父类上的 @PermissionPrefix 注解
* 处理 CGLIB 代理类的情况
*/
private PermissionPrefix findPermissionPrefix(Class<?> clazz) {
PermissionPrefix annotation = clazz.getAnnotation(PermissionPrefix.class);
if (annotation != null) {
return annotation;
}
// 检查父类处理 CGLIB 代理
Class<?> superclass = clazz.getSuperclass();
while (superclass != null && superclass != Object.class) {
annotation = superclass.getAnnotation(PermissionPrefix.class);
if (annotation != null) {
return annotation;
}
superclass = superclass.getSuperclass();
}
return null;
}
}

View File

@ -57,22 +57,40 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
try {
String jwt = getJwtFromRequest(request);
log.debug("JWT从请求中提取: {}", jwt != null ? "存在" : "不存在");
if (StringUtils.hasText(jwt)) {
String username = jwtTokenUtil.getUsernameFromToken(jwt);
log.debug("从Token中解析出用户名: {}", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
log.debug("加载用户详情成功, 权限数量: {}", userDetails.getAuthorities().size());
if (jwtTokenUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("JWT认证成功, 用户: {}, 权限: {}", username, userDetails.getAuthorities().size());
} else {
log.warn("Token验证失败, 用户: {}", username);
}
} else {
if (username == null) {
log.warn("无法从Token中解析用户名");
}
if (SecurityContextHolder.getContext().getAuthentication() != null) {
log.debug("Security Context中已存在认证信息");
}
}
}
chain.doFilter(request, response);
} catch (ExpiredJwtException e) {
log.error("JWT Token已过期", e);
handleAuthenticationException(response, JwtAuthenticationException.expired());
} catch (SignatureException | MalformedJwtException e) {
log.error("JWT Token签名无效或格式错误", e);
handleAuthenticationException(response, JwtAuthenticationException.invalid());
} catch (Exception e) {
log.error("JWT authentication failed", e);
@ -104,16 +122,22 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private String getJwtFromRequest(HttpServletRequest request) {
// 1. 先从Authorization header获取
String bearerToken = request.getHeader("Authorization");
log.debug("Authorization Header: {}", bearerToken != null ? bearerToken.substring(0, Math.min(30, bearerToken.length())) + "..." : "null");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
String token = bearerToken.substring(7);
log.debug("从Header中提取Token成功, 长度: {}", token.length());
return token;
}
// 2. 如果header中没有则尝试从URL参数获取
String token = request.getParameter("token");
if (StringUtils.hasText(token)) {
log.debug("从URL参数中提取Token成功");
return token;
}
log.debug("未找到JWT Token");
return null;
}
}

View File

@ -14,18 +14,27 @@ public interface UserConverter extends BaseConverter<User, UserDTO> {
// MapStruct 会自动实现所有方法
@Mapping(target = "token", ignore = true)
@Mapping(target = "roles", ignore = true)
@Mapping(target = "permissions", ignore = true)
@Mapping(target = "menus", ignore = true)
@Mapping(target = "id", ignore = true)
@Mapping(target = "departmentId", source = "department.id")
@Mapping(target = "departmentName", source = "department.name")
LoginResponse toLoginResponse(User user);
@Mapping(target = "token", source = "token")
@Mapping(target = "roles", ignore = true)
@Mapping(target = "permissions", ignore = true)
@Mapping(target = "menus", ignore = true)
@Mapping(target = "id", ignore = true)
@Mapping(target = "departmentId", source = "user.department.id")
@Mapping(target = "departmentName", source = "user.department.name")
LoginResponse toLoginResponse(User user, String token);
@Mapping(target = "token", ignore = true)
@Mapping(target = "roles", ignore = true)
@Mapping(target = "permissions", ignore = true)
@Mapping(target = "menus", ignore = true)
@Mapping(target = "id", ignore = true)
LoginResponse toLoginResponse(UserDTO userDTO);

View File

@ -45,7 +45,7 @@ public class Permission extends Entity<Long> {
)
private Set<Menu> menus = new HashSet<>();
// @ManyToMany(mappedBy = "permissions")
// private Set<Role> roles = new HashSet<>();
// 不使用 JPA 关系映射,改用原生 SQL 查询
// @ManyToMany(mappedBy = "permissions", fetch = FetchType.LAZY)
// private Set<Role> roles = new HashSet<>();
}

View File

@ -1,12 +1,27 @@
package com.qqchen.deploy.backend.system.model.response;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "登录响应")
public class LoginResponse extends UserResponse {
@Schema(description = "JWT Token")
private String token;
}
@Schema(description = "角色代码列表", example = "[\"ROLE_ADMIN\", \"ROLE_DEV\"]")
private List<String> roles;
@Schema(description = "权限代码列表", example = "[\"system:user:list\", \"deploy:team:create\"]")
private List<String> permissions;
@Schema(description = "菜单树")
private List<Object> menus;
}

View File

@ -1,41 +1,55 @@
package com.qqchen.deploy.backend.system.repository;
import com.qqchen.deploy.backend.system.entity.Permission;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.system.entity.Permission;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 权限Repository
*
* @author qqchen
* @date 2025-10-31
*/
@Repository
public interface IPermissionRepository extends IBaseRepository<Permission, Long> {
List<Permission> findByMenuIdAndEnabledTrue(Long menuId);
@Query("SELECT p FROM Permission p WHERE p.enabled = true ORDER BY p.menuId, p.sort")
List<Permission> findAllEnabledOrderByMenuAndSort();
/**
* 根据角色ID列表查询权限列表使用原生SQL避免JPA关系映射
*/
@Query(value = "SELECT DISTINCT p.* FROM sys_permission p " +
"INNER JOIN sys_role_permission rp ON p.id = rp.permission_id " +
"WHERE rp.role_id IN :roleIds AND p.deleted = false",
nativeQuery = true)
List<Permission> findByRoleIds(@Param("roleIds") List<Long> roleIds);
/**
* 根据ID列表查询权限
*/
List<Permission> findByIdIn(List<Long> ids);
/**
* 查询所有未删除的权限按排序字段排序
*
* @return 权限列表
* 查询所有启用的权限按菜单和排序排列
*/
@Query("SELECT p FROM Permission p WHERE p.deleted = false ORDER BY p.menuId, p.sort")
List<Permission> findAllEnabledOrderByMenuAndSort();
/**
* 根据菜单ID查询启用的权限
*/
List<Permission> findByMenuIdAndDeletedFalse(Long menuId);
/**
* 根据菜单ID查询启用的权限使用enabled字段
*/
@Query("SELECT p FROM Permission p WHERE p.menuId = :menuId AND p.deleted = false")
List<Permission> findByMenuIdAndEnabledTrue(@Param("menuId") Long menuId);
/**
* 查询所有未删除的权限按排序
*/
List<Permission> findAllByDeletedFalseOrderBySort();
/**
* 根据菜单ID查询权限列表
*
* @param menuId 菜单ID
* @return 权限列表
*/
List<Permission> findByMenuIdAndDeletedFalseOrderBySort(Long menuId);
/**
* 检查权限编码是否存在
*
* @param code 权限编码
* @return 是否存在
*/
boolean existsByCodeAndDeletedFalse(String code);
}
}

View File

@ -51,4 +51,25 @@ public interface IUserService extends IBaseService<User, UserDTO, UserQuery, Lon
*/
@Transactional(readOnly = false)
void assignDepartment(Long userId, Long departmentId);
/**
* 获取用户角色代码列表
* @param userId 用户ID
* @return 角色代码列表
*/
List<String> getUserRoleCodes(Long userId);
/**
* 获取用户权限代码列表
* @param userId 用户ID
* @return 权限代码列表
*/
List<String> getUserPermissions(Long userId);
/**
* 获取用户菜单树
* @param userId 用户ID
* @return 菜单树列表
*/
List<Object> getUserMenuTree(Long userId);
}

View File

@ -5,13 +5,16 @@ import com.qqchen.deploy.backend.system.entity.User;
import com.qqchen.deploy.backend.system.repository.IUserRepository;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.hibernate.Hibernate;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户详情服务实现
@ -28,17 +31,31 @@ public class UserDetailsServiceImpl implements UserDetailsService {
private final IUserRepository userRepository;
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsernameAndDeletedFalse(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
// 返回自定义 UserDetails包含用户ID等扩展信息
// 从数据库加载用户实际权限
Hibernate.initialize(user.getRoles()); // 加载角色
List<SimpleGrantedAuthority> authorities = user.getRoles().stream()
.flatMap(role -> {
Hibernate.initialize(role.getPermissions()); // 加载权限
return role.getPermissions().stream();
})
.map(permission -> new SimpleGrantedAuthority(permission.getCode()))
.distinct()
.collect(Collectors.toList());
// 返回自定义 UserDetails包含用户ID等扩展信息
return new CustomUserDetails(
user.getId(), // 用户ID
user.getUsername(), // 用户名
user.getPassword(), // 密码
user.getEnabled(), // 是否启用
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) // 权限TODO: 从数据库加载实际权限
authorities // 实际权限
);
}
}
}

View File

@ -35,6 +35,7 @@ import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.qqchen.deploy.backend.framework.annotation.ServiceType.Type.DATABASE;
@ -103,6 +104,12 @@ public class UserServiceImpl extends BaseServiceImpl<User, UserDTO, UserQuery, L
.orElseThrow(() -> new BusinessException(ResponseCode.USER_NOT_FOUND));
LoginResponse response = userConverter.toLoginResponse(user, token);
// 补充角色权限菜单数据
response.setRoles(getUserRoleCodes(user.getId()));
response.setPermissions(getUserPermissions(user.getId()));
response.setMenus(getUserMenuTree(user.getId()));
log.info("用户 {} ({}) 登录成功", user.getUsername(), user.getNickname());
return response;
}
@ -155,7 +162,17 @@ public class UserServiceImpl extends BaseServiceImpl<User, UserDTO, UserQuery, L
public LoginResponse getCurrentUserResponse() {
// 复用getCurrentUser方法直接转换DTO为LoginResponse
UserDTO userDTO = getCurrentUser();
return userConverter.toLoginResponse(userDTO);
LoginResponse response = userConverter.toLoginResponse(userDTO);
// 补充角色权限菜单数据
Long userId = SecurityUtils.getCurrentUserId();
if (userId != null) {
response.setRoles(getUserRoleCodes(userId));
response.setPermissions(getUserPermissions(userId));
response.setMenus(getUserMenuTree(userId));
}
return response;
}
@Override
@ -224,4 +241,40 @@ public class UserServiceImpl extends BaseServiceImpl<User, UserDTO, UserQuery, L
}
return result;
}
@Override
@Transactional(readOnly = true)
public List<String> getUserRoleCodes(Long userId) {
User user = findEntityById(userId);
Hibernate.initialize(user.getRoles());
return user.getRoles().stream()
.map(role -> role.getCode())
.collect(Collectors.toList());
}
@Override
@Transactional(readOnly = true)
public List<String> getUserPermissions(Long userId) {
User user = findEntityById(userId);
Hibernate.initialize(user.getRoles());
return user.getRoles().stream()
.flatMap(role -> {
Hibernate.initialize(role.getPermissions());
return role.getPermissions().stream();
})
.map(permission -> permission.getCode())
.distinct()
.collect(Collectors.toList());
}
@Override
@Transactional(readOnly = true)
public List<Object> getUserMenuTree(Long userId) {
// TODO: 实现菜单树查询逻辑
// 1. 查询用户角色
// 2. 查询角色菜单
// 3. 构建菜单树
return List.of();
}
}

View File

@ -98,12 +98,16 @@ VALUES
-- 部门管理
(5, '部门管理', '/system/departments', 'System/Department/List', 'ApartmentOutlined', 2, 1, 40, FALSE, TRUE, 'system', '2024-01-01 00:00:00', 0, FALSE);
-- 初始化角色数据
-- ==================== 初始化角色数据 ====================
DELETE FROM sys_role WHERE id < 100;
INSERT INTO sys_role (id, create_time, code, name, type, description, sort)
VALUES
(1, NOW(), 'SUPER_ADMIN', '超级管理员', 1, '系统超级管理员,拥有所有权限', 1),
(2, NOW(), 'SYSTEM_ADMIN', '系统管理员', 1, '系统管理员,拥有大部分系统管理权限', 2),
(3, NOW(), 'COMMON_USER', '普通用户', 2, '普通用,仅拥有基本操作权限', 3);
(1, NOW(), 'ROLE_ADMIN', '管理员', 1, '系统管理员,拥有所有权限', 1),
(2, NOW(), 'ROLE_OPS', '运维', 2, '运维人员,负责服务器、部署等运维工作', 2),
(3, NOW(), 'ROLE_DEV', '开发', 2, '开发人员,负责应用开发和部署', 3),
(4, NOW(), 'ROLE_HR', 'HR', 2, '人力资源,负责人员管理', 4),
(5, NOW(), 'ROLE_BA', 'BA/产品', 2, '业务分析/产品经理,负责需求和产品管理', 5);
-- 初始化角色标签
INSERT INTO sys_role_tag (id, create_time, name, color)
@ -130,12 +134,106 @@ VALUES
-- 初始化角色菜单关联
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT 1, id FROM sys_menu; -- 超级管理员拥有所有菜单权限
SELECT 1, id FROM sys_menu; -- 管理员拥有所有菜单权限
INSERT INTO sys_role_menu (role_id, menu_id)
VALUES
(2, 1), (2, 2), (2, 3), (2, 4), (2, 5), -- 系统管理员拥有系统管理相关权限
(3, 304); -- 普通用户拥有三方系统权限
(2, 200), (2, 201), (2, 202), (2, 203), (2, 204), (2, 300), (2, 301), (2, 302), (2, 303), (2, 304), -- 运维拥有运维管理和资源管理权限
(3, 100), (3, 101), (3, 102), (3, 104), (3, 200), (3, 202); -- 开发拥有工作流和应用管理权限
-- ==================== 初始化权限数据 ====================
DELETE FROM sys_permission WHERE id < 10000;
-- 系统管理权限
INSERT INTO sys_permission (id, create_time, menu_id, code, name, type, sort) VALUES
-- 用户管理 (menu_id=2)
(1, NOW(), 2, 'system:user:list', '用户查询', 'FUNCTION', 1),
(2, NOW(), 2, 'system:user:view', '用户详情', 'FUNCTION', 2),
(3, NOW(), 2, 'system:user:create', '用户创建', 'FUNCTION', 3),
(4, NOW(), 2, 'system:user:update', '用户修改', 'FUNCTION', 4),
(5, NOW(), 2, 'system:user:delete', '用户删除', 'FUNCTION', 5),
-- 角色管理 (menu_id=3)
(11, NOW(), 3, 'system:role:list', '角色查询', 'FUNCTION', 1),
(12, NOW(), 3, 'system:role:view', '角色详情', 'FUNCTION', 2),
(13, NOW(), 3, 'system:role:create', '角色创建', 'FUNCTION', 3),
(14, NOW(), 3, 'system:role:update', '角色修改', 'FUNCTION', 4),
(15, NOW(), 3, 'system:role:delete', '角色删除', 'FUNCTION', 5),
-- 菜单管理 (menu_id=4)
(21, NOW(), 4, 'system:menu:list', '菜单查询', 'FUNCTION', 1),
(22, NOW(), 4, 'system:menu:view', '菜单详情', 'FUNCTION', 2),
(23, NOW(), 4, 'system:menu:create', '菜单创建', 'FUNCTION', 3),
(24, NOW(), 4, 'system:menu:update', '菜单修改', 'FUNCTION', 4),
(25, NOW(), 4, 'system:menu:delete', '菜单删除', 'FUNCTION', 5),
-- 部门管理 (menu_id=5)
(31, NOW(), 5, 'system:department:list', '部门查询', 'FUNCTION', 1),
(32, NOW(), 5, 'system:department:view', '部门详情', 'FUNCTION', 2),
(33, NOW(), 5, 'system:department:create', '部门创建', 'FUNCTION', 3),
(34, NOW(), 5, 'system:department:update', '部门修改', 'FUNCTION', 4),
(35, NOW(), 5, 'system:department:delete', '部门删除', 'FUNCTION', 5),
-- 运维管理权限
-- 团队管理 (menu_id=201)
(101, NOW(), 201, 'deploy:team:list', '团队查询', 'FUNCTION', 1),
(102, NOW(), 201, 'deploy:team:view', '团队详情', 'FUNCTION', 2),
(103, NOW(), 201, 'deploy:team:create', '团队创建', 'FUNCTION', 3),
(104, NOW(), 201, 'deploy:team:update', '团队修改', 'FUNCTION', 4),
(105, NOW(), 201, 'deploy:team:delete', '团队删除', 'FUNCTION', 5),
-- 应用管理 (menu_id=202)
(111, NOW(), 202, 'deploy:application:list', '应用查询', 'FUNCTION', 1),
(112, NOW(), 202, 'deploy:application:view', '应用详情', 'FUNCTION', 2),
(113, NOW(), 202, 'deploy:application:create', '应用创建', 'FUNCTION', 3),
(114, NOW(), 202, 'deploy:application:update', '应用修改', 'FUNCTION', 4),
(115, NOW(), 202, 'deploy:application:delete', '应用删除', 'FUNCTION', 5),
-- 定时任务管理 (menu_id=203)
(121, NOW(), 203, 'deploy:schedule-job:list', '任务查询', 'FUNCTION', 1),
(122, NOW(), 203, 'deploy:schedule-job:view', '任务详情', 'FUNCTION', 2),
(123, NOW(), 203, 'deploy:schedule-job:create', '任务创建', 'FUNCTION', 3),
(124, NOW(), 203, 'deploy:schedule-job:update', '任务修改', 'FUNCTION', 4),
(125, NOW(), 203, 'deploy:schedule-job:delete', '任务删除', 'FUNCTION', 5),
(126, NOW(), 203, 'deploy:schedule-job:pause', '暂停任务', 'FUNCTION', 6),
(127, NOW(), 203, 'deploy:schedule-job:resume', '恢复任务', 'FUNCTION', 7),
(128, NOW(), 203, 'deploy:schedule-job:disable', '禁用任务', 'FUNCTION', 8),
(129, NOW(), 203, 'deploy:schedule-job:enable', '启用任务', 'FUNCTION', 9),
(130, NOW(), 203, 'deploy:schedule-job:trigger', '手动触发', 'FUNCTION', 10),
-- 环境管理 (menu_id=204)
(141, NOW(), 204, 'deploy:environment:list', '环境查询', 'FUNCTION', 1),
(142, NOW(), 204, 'deploy:environment:view', '环境详情', 'FUNCTION', 2),
(143, NOW(), 204, 'deploy:environment:create', '环境创建', 'FUNCTION', 3),
(144, NOW(), 204, 'deploy:environment:update', '环境修改', 'FUNCTION', 4),
(145, NOW(), 204, 'deploy:environment:delete', '环境删除', 'FUNCTION', 5);
-- ==================== 分配权限给角色 ====================
DELETE FROM sys_role_permission;
-- 管理员拥有所有权限
INSERT INTO sys_role_permission (role_id, permission_id)
SELECT 1, id FROM sys_permission WHERE id < 10000;
-- 运维角色权限(运维管理权限)
INSERT INTO sys_role_permission (role_id, permission_id)
SELECT 2, id FROM sys_permission WHERE code LIKE 'deploy:%';
-- 开发角色权限(应用管理权限)
INSERT INTO sys_role_permission (role_id, permission_id)
SELECT 3, id FROM sys_permission WHERE code LIKE 'deploy:application:%';
-- ==================== 分配角色给用户 ====================
DELETE FROM sys_user_role;
-- admin 用户拥有管理员角色
INSERT INTO sys_user_role (user_id, role_id) VALUES (1, 1);
-- ops_manager 拥有运维角色
INSERT INTO sys_user_role (user_id, role_id) VALUES (4, 2);
-- dev_manager 拥有开发角色
INSERT INTO sys_user_role (user_id, role_id) VALUES (3, 3);
-- 初始化权限模板
INSERT INTO sys_permission_template (id, create_time, code, name, type, description, enabled)
@ -150,29 +248,6 @@ SELECT 1, id FROM sys_menu; -- 完整权限模板关联所有菜单
INSERT INTO sys_template_menu (template_id, menu_id)
VALUES (2, 304); -- 基础权限模板关联三方系统菜单
-- 初始化权限数据
INSERT INTO sys_permission (id, create_time, menu_id, code, name, type, sort)
VALUES
-- 用户管理权限
(1, NOW(), 2, 'system:user:list', '用户列表', 'FUNCTION', 1),
(2, NOW(), 2, 'system:user:create', '用户创建', 'FUNCTION', 2),
(3, NOW(), 2, 'system:user:update', '用户修改', 'FUNCTION', 3),
(4, NOW(), 2, 'system:user:delete', '用户删除', 'FUNCTION', 4),
-- 角色管理权限
(5, NOW(), 3, 'system:role:list', '角色列表', 'FUNCTION', 1),
(6, NOW(), 3, 'system:role:create', '角色创建', 'FUNCTION', 2),
(7, NOW(), 3, 'system:role:update', '角色修改', 'FUNCTION', 3),
(8, NOW(), 3, 'system:role:delete', '角色删除', 'FUNCTION', 4),
-- 外部服务权限
(9, NOW(), 304, 'system:external:list', '三方系统列表', 'FUNCTION', 1),
(10, NOW(), 304, 'system:external:create', '三方系统创建', 'FUNCTION', 2),
(11, NOW(), 304, 'system:external:update', '三方系统修改', 'FUNCTION', 3),
(12, NOW(), 304, 'system:external:delete', '三方系统删除', 'FUNCTION', 4),
(13, NOW(), 304, 'system:external:test', '连接测试', 'FUNCTION', 5),
(14, NOW(), 304, 'system:external:sync', '数据同步', 'FUNCTION', 6);
-- --------------------------------------------------------------------------------------
-- 初始化外部系统数据
-- --------------------------------------------------------------------------------------

View File

@ -241,6 +241,17 @@ CREATE TABLE sys_permission
INDEX IDX_menu_id (menu_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统权限表';
-- 角色权限关联表
CREATE TABLE sys_role_permission
(
role_id BIGINT NOT NULL COMMENT '角色ID',
permission_id BIGINT NOT NULL COMMENT '权限ID',
PRIMARY KEY (role_id, permission_id),
CONSTRAINT FK_role_permission_role FOREIGN KEY (role_id) REFERENCES sys_role (id),
CONSTRAINT FK_role_permission_permission FOREIGN KEY (permission_id) REFERENCES sys_permission (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';
-- --------------------------------------------------------------------------------------
-- 外部系统集成相关表
-- --------------------------------------------------------------------------------------