功能(auth):实现用户注册并分配默认角色

- 添加AppConfig类以启用事务管理
- 在application.yaml中配置默认角色代码
- 在AuthServiceImpl中注入RoleMapper和defRoleCode
- 在注册过程中为新用户分配默认角色
- 添加用户注册和登录事件的日志记录
- 在登录过程中处理异常并进行适当的错误日志记录
- 创建用户、角色、权限及关联关系的数据库表
- 添加用户角色实体类和用户-角色关系映射器
- 实现用户角色服务以管理用户角色分配
- 添加通过SetUserRoleRequest设置用户角色的API端点
- 更新用户实体类并添加适当的字段和注解
- 实现包含角色信息的用户信息查询方法
- 将用户转换逻辑重构为可复用方法
- 为SetUserRoleRequest DTO添加校验约束}```
This commit is contained in:
Chaos
2025-11-20 17:19:52 +08:00
parent f3f92b52b8
commit e46b820fca
18 changed files with 411 additions and 34 deletions

View File

@@ -32,6 +32,8 @@ public enum ErrorCode {
USER_UPDATE_USERNAME_FAILED(400,"USER-111" , "用户名更新失败"), USER_UPDATE_USERNAME_FAILED(400,"USER-111" , "用户名更新失败"),
ROLE_ID_INVALID(400, "ROLE-100" , "角色ID无效" ),
// ================== 论坛内容相关 (200-299) ================== // ================== 论坛内容相关 (200-299) ==================
POST_NOT_FOUND(404, "POST-201", "帖子不存在"), POST_NOT_FOUND(404, "POST-201", "帖子不存在"),
POST_DELETED(410, "POST-202", "帖子已被删除"), POST_DELETED(410, "POST-202", "帖子已被删除"),
@@ -45,7 +47,7 @@ public enum ErrorCode {
CAPTCHA_ERROR(400, "SYS-301", "验证码错误"), CAPTCHA_ERROR(400, "SYS-301", "验证码错误"),
SMS_SEND_FAILED(500, "SYS-302", "短信发送失败"), SMS_SEND_FAILED(500, "SYS-302", "短信发送失败"),
FILE_UPLOAD_FAILED(500, "SYS-303", "文件上传失败"), FILE_UPLOAD_FAILED(500, "SYS-303", "文件上传失败"),
RATE_LIMIT_EXCEEDED(429, "SYS-304", "操作过于频繁"); RATE_LIMIT_EXCEEDED(429, "SYS-304", "操作过于频繁"),;
private final int httpStatus; private final int httpStatus;
private final String code; // 业务错误码(领域-编号) private final String code; // 业务错误码(领域-编号)

View File

@@ -15,8 +15,6 @@ public class UserConverter {
user.setAccountNonExpired(true); user.setAccountNonExpired(true);
user.setCredentialsNonExpired(true); user.setCredentialsNonExpired(true);
user.setAccountNonLocked(true); user.setAccountNonLocked(true);
user.setCreateTime(java.time.LocalDateTime.now());
user.setUpdateTime(java.time.LocalDateTime.now());
return user; return user;
} }
} }

View File

@@ -3,8 +3,16 @@ package cn.nopj.chaos_api.mapper;
import cn.nopj.chaos_api.domain.entity.Role; import cn.nopj.chaos_api.domain.entity.Role;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper @Mapper
public interface RoleMapper extends BaseMapper<Role> { public interface RoleMapper extends BaseMapper<Role> {
/**
* 根据角色编码查询角色信息
* @param code 角色编码
* @return 角色信息
*/
@Select("SELECT * FROM t_role WHERE code = #{code}")
Role selectByCode(String code);
} }

View File

@@ -4,6 +4,7 @@ import cn.nopj.chaos_api.domain.entity.Role;
import cn.nopj.chaos_api.domain.entity.User; import cn.nopj.chaos_api.domain.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.*; import org.apache.ibatis.annotations.*;
import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
@@ -34,6 +35,7 @@ public interface UserMapper extends BaseMapper<User> {
* @param id 用户id * @param id 用户id
* @param i 角色id * @param i 角色id
*/ */
@Select("INSERT INTO t_user_role (user_id, role_id) VALUES (#{userId}, #{roleId})") @Select("INSERT INTO t_user_role (user_id, role_id) VALUES (#{userId}, #{roleId})")
void insertUserRole(@Param("userId") Long id, @Param("roleId") Long i); void insertUserRole(@Param("userId") Long id, @Param("roleId") Long i);
@@ -54,7 +56,7 @@ public interface UserMapper extends BaseMapper<User> {
@Result(property = "roles", column = "id", @Result(property = "roles", column = "id",
many = @Many(select = "findRolesByUserId")) many = @Many(select = "findRolesByUserId"))
}) })
User findUserWithRoles(Long id); User findUserWithRolesById(Long id);
/** /**
@@ -83,4 +85,16 @@ public interface UserMapper extends BaseMapper<User> {
WHERE ur.user_id = #{userId} WHERE ur.user_id = #{userId}
""") """)
List<Role> findRolesByUserId(@Param("userId") Long userId); List<Role> findRolesByUserId(@Param("userId") Long userId);
@Select("SELECT * FROM t_user WHERE username = #{username} ")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "nickname", column = "nickname"),
@Result(property = "roles", column = "id",
many = @Many(select = "findRolesByUserId"))
})
User findUserWithRolesByUsername(String username);
} }

View File

@@ -0,0 +1,7 @@
package cn.nopj.chaos_api.mapper;
import cn.nopj.chaos_api.domain.entity.UserRole;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserRoleMapper extends BaseMapper<UserRole> {
}

View File

@@ -1,57 +1,85 @@
package cn.nopj.chaos_api.domain.entity; package cn.nopj.chaos_api.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable; import java.util.Date;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/**
* 系统管理-用户表
* @TableName t_user
*/
@Data @Data
@TableName("t_user") @TableName(value ="t_user")
public class User implements Serializable {
public class User {
/** /**
* 用户id * 主键ID
*/ */
@TableId @TableId
private Long id; private Long id;
/** /**
* 用户名 * 用户名/登录名
*/ */
private String username; private String username;
/** /**
* 密码 * 密码加密存储建议BCrypt
*/ */
private String password; private String password;
/** /**
* 账号是否启 * 用户昵称
*/
private String nickname;
/**
* 状态 (1:启用, 0:禁用)
*/ */
private Boolean enabled; private Boolean enabled;
/** /**
* 账号是否未过期 * 账号未过期 (1:是, 0:否)
*/ */
private Boolean accountNonExpired; private Boolean accountNonExpired;
/** /**
* 密码是否未过期 * 凭证未过期 (1:是, 0:否)
*/ */
private Boolean credentialsNonExpired; private Boolean credentialsNonExpired;
/** /**
* 账号是否未锁定 * 账号未锁定 (1:是, 0:否)
*/ */
private Boolean accountNonLocked; private Boolean accountNonLocked;
/**
* 逻辑删除 (1:已删除, 0:未删除)
*/
private Boolean deleted;
/**
* 备注信息
*/
private String remark;
/** /**
* 创建时间 * 创建时间
*/ */
private LocalDateTime createTime; private Date createTime;
/** /**
* 更新时间 * 更新时间
*/ */
private LocalDateTime updateTime; private Date updateTime;
@TableField(exist = false) @TableField(exist = false)
private List<Role> roles; private List<Role> roles;
} }

View File

@@ -0,0 +1,13 @@
package cn.nopj.chaos_api.domain.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@TableName("t_user_role")
@AllArgsConstructor
@NoArgsConstructor
public class UserRole {
private Long userId;
private Long roleId;
}

View File

@@ -0,0 +1,14 @@
package cn.nopj.chaos_api.dto.request;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import java.util.List;
@Data
public class SetUserRoleRequest {
@Pattern(regexp = "^[0-9]+$", message = "用户id格式错误")
private Long userId;
@Pattern(regexp = "^[0-9]+$", message = "角色id格式错误")
private List<Long> rolesId;
}

View File

@@ -42,4 +42,12 @@ public interface UserInfoService {
* @param newUsername 新用户名 * @param newUsername 新用户名
*/ */
void updateUsername(String username, String newUsername); void updateUsername(String username, String newUsername);
/**
* 根据用户名查询用户信息
*
* @param username 用户名
* @return 用户信息
*/
UserinfoResponse findUserWithRoles(String username);
} }

View File

@@ -0,0 +1,23 @@
package cn.nopj.chaos_api.service;
import cn.nopj.chaos_api.dto.request.SetUserRoleRequest;
import java.util.List;
/**
* 用户角色服务
*/
public interface UserRoleService {
/**
* 为用户设置角色
* @param userId 用户id
* @param roles_id 角色id组
*/
void setUserRole(Long userId, List<Long> roles_id);
/**
* 为用户设置角色
* @param request 请求参数
*/
void setUserRole(SetUserRoleRequest request);
}

View File

@@ -46,18 +46,7 @@ public class UserInfoServiceImpl implements UserInfoService {
} }
return allUserWithRoles.stream().map(user -> { return allUserWithRoles.stream().map(user -> {
UserinfoResponse userinfoResponse = new UserinfoResponse(); return userConverterUserinfo(user);
userinfoResponse.setId(user.getId());
userinfoResponse.setUsername(user.getUsername());
userinfoResponse.setRoles(
user.getRoles().stream().map(role -> {
RoleResponse roleResponse = new RoleResponse();
roleResponse.setId(role.getId());
roleResponse.setName(role.getName());
return roleResponse;
}).toList()
);
return userinfoResponse;
}).toList(); }).toList();
} }
@@ -89,5 +78,42 @@ public class UserInfoServiceImpl implements UserInfoService {
} }
@Override
public UserinfoResponse findUserWithRoles(String username) {
if (username == null || username.isEmpty()){
throw new BizException(ErrorCode.USER_NOT_EXISTS);
}
log.info("查询用户名: {}", username);
User user = userMapper.findUserWithRolesByUsername(username);
log.info("查询用户名: {} 结果: {}", username, user);
if (user == null) {
throw new BizException(ErrorCode.USER_NOT_EXISTS);
}
return userConverterUserinfo(user);
}
/**
* User 转 UserinfoResponse
* @param user
* @return
*/
private UserinfoResponse userConverterUserinfo(User user) {
UserinfoResponse userinfoResponse = new UserinfoResponse();
userinfoResponse.setId(user.getId());
userinfoResponse.setUsername(user.getUsername());
userinfoResponse.setRoles(
user.getRoles().stream().map(role -> {
RoleResponse roleResponse = new RoleResponse();
roleResponse.setId(role.getId());
roleResponse.setName(role.getName());
return roleResponse;
}).toList()
);
return userinfoResponse;
}
} }

View File

@@ -0,0 +1,50 @@
package cn.nopj.chaos_api.service.impl;
import cn.nopj.chaos_api.common.constants.ErrorCode;
import cn.nopj.chaos_api.common.exceotion.BizException;
import cn.nopj.chaos_api.domain.entity.UserRole;
import cn.nopj.chaos_api.dto.request.SetUserRoleRequest;
import cn.nopj.chaos_api.mapper.UserRoleMapper;
import cn.nopj.chaos_api.service.UserRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Slf4j
@Service
public class UserRoleServiceImpl implements UserRoleService {
@Autowired
private UserRoleMapper userRoleMapper;
@Override
public void setUserRole(Long userId, List<Long> roles_id) {
for (Long role_id : roles_id) {
if (role_id == null || role_id == 0L) {
throw new BizException(ErrorCode.ROLE_ID_INVALID);
}else {
userRoleMapper.insert(new UserRole(userId, role_id));
}
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void setUserRole(SetUserRoleRequest request) {
if (request == null || request.getUserId() == null ){
throw new BizException(ErrorCode.USER_ID_INVALID);
}
if (request.getRolesId() == null || request.getRolesId().isEmpty()){
throw new BizException(ErrorCode.ROLE_ID_INVALID);
}
Long userId = request.getUserId();
List<Long> roles_id = request.getRolesId();
setUserRole(userId, roles_id);
}
}

View File

@@ -0,0 +1,9 @@
package cn.nopj.chaos_api.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class AppConfig {
}

View File

@@ -1,7 +1,13 @@
package cn.nopj.chaos_api.controller; package cn.nopj.chaos_api.controller;
import cn.nopj.chaos_api.dto.request.SetUserRoleRequest;
import cn.nopj.chaos_api.model.ApiResult;
import cn.nopj.chaos_api.service.UserRoleService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -10,7 +16,21 @@ import org.springframework.web.bind.annotation.RestController;
*/ */
@Slf4j @Slf4j
@RestController @RestController
@RequestMapping("/api/role")
public class RoleController { public class RoleController {
@Autowired
private UserRoleService userRoleService;
/**
* 设置用户角色
* @param request 请求参数
* @return 处理结果
*/
@RequestMapping("/setUserRole")
public ApiResult<String> setUserRole(@RequestBody SetUserRoleRequest request) {
userRoleService.setUserRole(request);
return ApiResult.success("用户角色设置成功");
}
} }

View File

@@ -60,4 +60,14 @@ public class UserController {
userInfoService.updateUsername(username, request.getUsername()); userInfoService.updateUsername(username, request.getUsername());
return ApiResult.success("用户名更新成功"); return ApiResult.success("用户名更新成功");
} }
/**
* 获取当前登录用户信息
* @return 用户信息
*/
@GetMapping("/info")
ApiResult<UserinfoResponse> getCurrentUserInfo(@RequestAttribute("currentUsername") String username){
return ApiResult.success(userInfoService.findUserWithRoles(username));
}
} }

View File

@@ -3,8 +3,10 @@ package cn.nopj.chaos_api.service.impl;
import cn.nopj.chaos_api.common.constants.ErrorCode; import cn.nopj.chaos_api.common.constants.ErrorCode;
import cn.nopj.chaos_api.common.exceotion.BizException; import cn.nopj.chaos_api.common.exceotion.BizException;
import cn.nopj.chaos_api.domain.entity.Role;
import cn.nopj.chaos_api.domain.entity.User; import cn.nopj.chaos_api.domain.entity.User;
import cn.nopj.chaos_api.dto.response.AuthTokenResponse; import cn.nopj.chaos_api.dto.response.AuthTokenResponse;
import cn.nopj.chaos_api.mapper.RoleMapper;
import cn.nopj.chaos_api.mapper.UserMapper; import cn.nopj.chaos_api.mapper.UserMapper;
import cn.nopj.chaos_api.service.AuthService; import cn.nopj.chaos_api.service.AuthService;
import cn.nopj.chaos_api.util.JwtTokenUtil; import cn.nopj.chaos_api.util.JwtTokenUtil;
@@ -20,6 +22,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service @Service
@Slf4j @Slf4j
@@ -36,7 +39,13 @@ public class AuthServiceImpl implements AuthService {
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
@Value("${jwt.tokenHead}") @Value("${jwt.tokenHead}")
private String tokenHead; private String tokenHead;
@Value("${auth.def-role-code}")
private String defRoleCode;
@Autowired
private RoleMapper roleMapper;
@Override @Override
@Transactional(rollbackFor = Exception.class)
public User register(User user) { public User register(User user) {
// 检查用户名是否已存在 // 检查用户名是否已存在
@@ -48,13 +57,17 @@ public class AuthServiceImpl implements AuthService {
user.setPassword(passwordEncoder.encode(user.getPassword())); user.setPassword(passwordEncoder.encode(user.getPassword()));
userMapper.insert(user); userMapper.insert(user);
// 你可以在这里为新用户分配默认角色 // 你可以在这里为新用户分配默认角色
userMapper.insertUserRole(user.getId(), 2L);
Role role = roleMapper.selectByCode(defRoleCode);
userMapper.insertUserRole(user.getId(), role.getId());
log.info("用户注册成功: {}", user.getUsername());
return user; return user;
} }
@Override @Override
public AuthTokenResponse login(String username, String password) { public AuthTokenResponse login(String username, String password) {
log.info("用户登录: {}", username);
// 尝试进行身份验证 // 尝试进行身份验证
try{ try{
Authentication authentication = authenticationManager.authenticate( Authentication authentication = authenticationManager.authenticate(
@@ -79,6 +92,7 @@ public class AuthServiceImpl implements AuthService {
} catch (DisabledException e) { } catch (DisabledException e) {
throw new BizException(ErrorCode.USER_NOT_ENABLED); throw new BizException(ErrorCode.USER_NOT_ENABLED);
} catch (Exception e) { } catch (Exception e) {
log.error("用户登录异常: {}", e.getMessage());
throw new BizException(ErrorCode.SYSTEM_ERROR); throw new BizException(ErrorCode.SYSTEM_ERROR);
} }
} }

View File

@@ -39,4 +39,5 @@ jwt:
secret: zHANgcHao@1995!20250506 secret: zHANgcHao@1995!20250506
expiration: 604800 expiration: 604800
auth:
def-role-code: user

View File

@@ -0,0 +1,132 @@
-- ----------------------------
-- 1. 系统用户表 (t_user)
-- ----------------------------
DROP TABLE IF EXISTS chaos.t_user;
CREATE TABLE chaos.t_user (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键ID',
username VARCHAR(64) NOT NULL COMMENT '用户名/登录名',
password VARCHAR(255) NOT NULL COMMENT '密码加密存储建议BCrypt',
nickname VARCHAR(64) DEFAULT '' COMMENT '用户昵称',
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态 (1:启用, 0:禁用)',
account_non_expired TINYINT(1) NOT NULL DEFAULT 1 COMMENT '账号未过期 (1:是, 0:否)',
credentials_non_expired TINYINT(1) NOT NULL DEFAULT 1 COMMENT '凭证未过期 (1:是, 0:否)',
account_non_locked TINYINT(1) NOT NULL DEFAULT 1 COMMENT '账号未锁定 (1:是, 0:否)',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除 (1:已删除, 0:未删除)',
remark VARCHAR(500) DEFAULT '' COMMENT '备注信息',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统管理-用户表';
-- ----------------------------
-- 2. 角色/用户组表 (t_role)
-- ----------------------------
DROP TABLE IF EXISTS chaos.t_role;
CREATE TABLE chaos.t_role (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(64) NOT NULL COMMENT '角色名称 (如: 管理员)',
code VARCHAR(64) NOT NULL COMMENT '角色标识 (如: admin)',
status TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态 (1:正常, 0:停用)',
remark VARCHAR(500) DEFAULT '' COMMENT '备注',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统管理-角色表';
-- ----------------------------
-- 3. 权限表 (t_permission)
-- ----------------------------
DROP TABLE IF EXISTS chaos.t_permission;
CREATE TABLE chaos.t_permission (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键ID',
parent_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父权限ID (0为顶级)',
name VARCHAR(64) NOT NULL COMMENT '权限名称',
code VARCHAR(128) NOT NULL COMMENT '权限标识/资源路径',
type TINYINT(1) NOT NULL DEFAULT 1 COMMENT '类型 (1:目录, 2:菜单, 3:按钮)',
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序 (数值越小越靠前)',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_code (code),
INDEX idx_parent_id (parent_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统管理-权限表';
-- ----------------------------
-- 4. 用户-角色关联表 (t_user_role)
-- ----------------------------
DROP TABLE IF EXISTS chaos.t_user_role;
CREATE TABLE chaos.t_user_role (
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
role_id BIGINT UNSIGNED NOT NULL COMMENT '角色ID',
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_ur_user_id FOREIGN KEY (user_id) REFERENCES chaos.t_user (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_ur_role_id FOREIGN KEY (role_id) REFERENCES chaos.t_role (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统管理-用户角色关联表';
-- ----------------------------
-- 5. 角色-权限关联表 (t_role_permission)
-- ----------------------------
DROP TABLE IF EXISTS chaos.t_role_permission;
CREATE TABLE chaos.t_role_permission (
role_id BIGINT UNSIGNED NOT NULL COMMENT '角色ID',
permission_id BIGINT UNSIGNED NOT NULL COMMENT '权限ID',
PRIMARY KEY (role_id, permission_id),
CONSTRAINT fk_rp_role_id FOREIGN KEY (role_id) REFERENCES chaos.t_role (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_rp_permission_id FOREIGN KEY (permission_id) REFERENCES chaos.t_permission (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统管理-角色权限关联表';
-- ----------------------------
-- 6. 基础设备表 (t_device)
-- ----------------------------
DROP TABLE IF EXISTS chaos.t_device;
CREATE TABLE chaos.t_device (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(128) NOT NULL COMMENT '设备名称',
model VARCHAR(128) DEFAULT NULL COMMENT '设备型号',
type_id BIGINT UNSIGNED DEFAULT NULL COMMENT '设备类型ID (关联类型表)',
location_id BIGINT UNSIGNED DEFAULT NULL COMMENT '位置ID (关联位置表)',
snmp_community VARCHAR(128) DEFAULT NULL COMMENT 'SNMP团体名 (加密存储建议)',
manufacturer VARCHAR(128) DEFAULT NULL COMMENT '设备制造商',
purchase_date DATE DEFAULT NULL COMMENT '采购日期',
status TINYINT DEFAULT 1 COMMENT '设备状态 (1:在线, 0:离线, 2:维护中)',
remark VARCHAR(500) DEFAULT '' COMMENT '备注',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
INDEX idx_type_id (type_id),
INDEX idx_location_id (location_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资产管理-设备信息表';
-- ----------------------------
-- 7. 网络接口表 (t_network_interface)
-- ----------------------------
DROP TABLE IF EXISTS chaos.t_network_interface;
CREATE TABLE chaos.t_network_interface (
id BIGINT UNSIGNED AUTO_INCREMENT COMMENT '主键ID',
device_id BIGINT UNSIGNED NOT NULL COMMENT '设备ID',
parent_id BIGINT UNSIGNED DEFAULT NULL COMMENT '父接口ID (用于子接口/聚合口)',
name VARCHAR(64) NOT NULL COMMENT '接口名称 (如: eth0, GE0/0/1)',
mac_address VARCHAR(17) DEFAULT NULL COMMENT 'MAC地址 (格式: AA:BB:CC:DD:EE:FF)',
ip_address VARCHAR(45) DEFAULT NULL COMMENT 'IP地址 (支持IPv4/IPv6)',
subnet_mask VARCHAR(45) DEFAULT NULL COMMENT '子网掩码/前缀长度',
gateway_ip VARCHAR(45) DEFAULT NULL COMMENT '网关IP地址',
vlan_id INT DEFAULT NULL COMMENT 'VLAN ID (1-4094)',
port_speed INT DEFAULT 0 COMMENT '端口速率 (Mbps)',
status TINYINT DEFAULT 1 COMMENT '接口状态 (1:UP, 0:DOWN)',
remark VARCHAR(500) DEFAULT '' COMMENT '备注',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
CONSTRAINT fk_ni_device_id FOREIGN KEY (device_id) REFERENCES chaos.t_device (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_ni_parent_id FOREIGN KEY (parent_id) REFERENCES chaos.t_network_interface (id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资产管理-网络接口表';