13 KiB
Design Document: Admin System Backend
Overview
本设计文档描述了为 adminSystem 前端提供后端支持的 API 设计。后端将扩展现有的 C# ASP.NET Core 项目,添加用户认证、用户管理、角色管理和动态菜单功能。
Architecture
graph TB
subgraph Frontend["adminSystem Frontend"]
Login[登录页面]
Dashboard[控制台]
UserMgmt[用户管理]
RoleMgmt[角色管理]
MenuMgmt[菜单管理]
AMT[AMT Scanner 功能]
end
subgraph Backend["C# Backend API"]
AuthController[AuthController]
UserController[UserController]
RoleController[RoleController]
MenuController[MenuController]
AuthService[AuthService]
UserService[UserService]
RoleService[RoleService]
MenuService[MenuService]
JwtMiddleware[JWT 中间件]
end
subgraph Database["MySQL Database"]
Users[(Users)]
Roles[(Roles)]
UserRoles[(UserRoles)]
Menus[(Menus)]
RoleMenus[(RoleMenus)]
end
Login --> AuthController
Dashboard --> MenuController
UserMgmt --> UserController
RoleMgmt --> RoleController
MenuMgmt --> MenuController
AMT --> |现有 API| Backend
AuthController --> AuthService
UserController --> UserService
RoleController --> RoleService
MenuController --> MenuService
AuthService --> Users
UserService --> Users
UserService --> UserRoles
RoleService --> Roles
RoleService --> RoleMenus
MenuService --> Menus
MenuService --> RoleMenus
Components and Interfaces
1. AuthController
负责用户认证相关的 API 端点。
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
// POST /api/auth/login - 用户登录
[HttpPost("login")]
public async Task<ActionResult<ApiResponse<LoginResponse>>> Login(LoginRequest request);
// POST /api/auth/refresh - 刷新 Token
[HttpPost("refresh")]
public async Task<ActionResult<ApiResponse<LoginResponse>>> RefreshToken(RefreshTokenRequest request);
// POST /api/auth/logout - 退出登录
[HttpPost("logout")]
public async Task<ActionResult<ApiResponse>> Logout();
}
2. UserController
负责用户信息和用户管理的 API 端点。
[ApiController]
[Route("api/user")]
public class UserController : ControllerBase
{
// GET /api/user/info - 获取当前用户信息
[HttpGet("info")]
[Authorize]
public async Task<ActionResult<ApiResponse<UserInfo>>> GetUserInfo();
// GET /api/user/list - 获取用户列表(分页)
[HttpGet("list")]
[Authorize(Roles = "R_SUPER,R_ADMIN")]
public async Task<ActionResult<ApiResponse<PaginatedResponse<UserListItem>>>> GetUserList([FromQuery] UserSearchParams query);
// POST /api/user - 创建用户
[HttpPost]
[Authorize(Roles = "R_SUPER,R_ADMIN")]
public async Task<ActionResult<ApiResponse<UserListItem>>> CreateUser(CreateUserRequest request);
// PUT /api/user/{id} - 更新用户
[HttpPut("{id}")]
[Authorize(Roles = "R_SUPER,R_ADMIN")]
public async Task<ActionResult<ApiResponse<UserListItem>>> UpdateUser(int id, UpdateUserRequest request);
// DELETE /api/user/{id} - 删除用户
[HttpDelete("{id}")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse>> DeleteUser(int id);
}
3. RoleController
负责角色管理的 API 端点。
[ApiController]
[Route("api/role")]
public class RoleController : ControllerBase
{
// GET /api/role/list - 获取角色列表(分页)
[HttpGet("list")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse<PaginatedResponse<RoleListItem>>>> GetRoleList([FromQuery] RoleSearchParams query);
// POST /api/role - 创建角色
[HttpPost]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse<RoleListItem>>> CreateRole(CreateRoleRequest request);
// PUT /api/role/{id} - 更新角色
[HttpPut("{id}")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse<RoleListItem>>> UpdateRole(int id, UpdateRoleRequest request);
// DELETE /api/role/{id} - 删除角色
[HttpDelete("{id}")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse>> DeleteRole(int id);
// PUT /api/role/{id}/menus - 分配菜单权限
[HttpPut("{id}/menus")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse>> AssignMenus(int id, AssignMenusRequest request);
}
4. MenuController
负责动态菜单的 API 端点。
[ApiController]
[Route("api")]
public class MenuController : ControllerBase
{
// GET /api/v3/system/menus/simple - 获取用户菜单(adminSystem 使用的接口)
[HttpGet("v3/system/menus/simple")]
[Authorize]
public async Task<ActionResult<ApiResponse<List<MenuTreeItem>>>> GetUserMenus();
// GET /api/menu/list - 获取所有菜单(管理用)
[HttpGet("menu/list")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse<List<MenuTreeItem>>>> GetAllMenus();
// POST /api/menu - 创建菜单
[HttpPost("menu")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse<MenuTreeItem>>> CreateMenu(CreateMenuRequest request);
// PUT /api/menu/{id} - 更新菜单
[HttpPut("menu/{id}")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse<MenuTreeItem>>> UpdateMenu(int id, UpdateMenuRequest request);
// DELETE /api/menu/{id} - 删除菜单
[HttpDelete("menu/{id}")]
[Authorize(Roles = "R_SUPER")]
public async Task<ActionResult<ApiResponse>> DeleteMenu(int id);
}
Data Models
数据库实体
// 用户表
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
public string? NickName { get; set; }
public string? Email { get; set; }
public string? Phone { get; set; }
public string? Avatar { get; set; }
public string Gender { get; set; } = "0"; // 0-未知, 1-男, 2-女
public string Status { get; set; } = "1"; // 1-启用, 2-禁用
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public string? CreatedBy { get; set; }
public string? UpdatedBy { get; set; }
public bool IsDeleted { get; set; } = false;
public ICollection<UserRole> UserRoles { get; set; }
}
// 角色表
public class Role
{
public int Id { get; set; }
public string RoleName { get; set; }
public string RoleCode { get; set; } // R_SUPER, R_ADMIN, R_USER
public string? Description { get; set; }
public bool Enabled { get; set; } = true;
public DateTime CreatedAt { get; set; }
public ICollection<UserRole> UserRoles { get; set; }
public ICollection<RoleMenu> RoleMenus { get; set; }
}
// 用户-角色关联表
public class UserRole
{
public int UserId { get; set; }
public User User { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
}
// 菜单表
public class Menu
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Name { get; set; } // 路由名称
public string Path { get; set; } // 路由路径
public string? Component { get; set; } // 组件路径
public string? Title { get; set; } // 菜单标题 (i18n key)
public string? Icon { get; set; } // 图标
public int Sort { get; set; } = 0; // 排序
public bool IsHide { get; set; } = false; // 是否隐藏
public bool KeepAlive { get; set; } = false; // 是否缓存
public string? Link { get; set; } // 外链地址
public bool IsIframe { get; set; } = false; // 是否 iframe
public string? Roles { get; set; } // 角色限制 (JSON 数组)
public DateTime CreatedAt { get; set; }
public Menu? Parent { get; set; }
public ICollection<Menu> Children { get; set; }
public ICollection<RoleMenu> RoleMenus { get; set; }
}
// 角色-菜单关联表
public class RoleMenu
{
public int RoleId { get; set; }
public Role Role { get; set; }
public int MenuId { get; set; }
public Menu Menu { get; set; }
}
API 请求/响应模型
// 统一响应格式
public class ApiResponse<T>
{
public int Code { get; set; } = 200;
public string Msg { get; set; } = "success";
public T? Data { get; set; }
}
public class ApiResponse : ApiResponse<object> { }
// 登录请求
public class LoginRequest
{
public string UserName { get; set; }
public string Password { get; set; }
}
// 登录响应
public class LoginResponse
{
public string Token { get; set; }
public string RefreshToken { get; set; }
}
// 用户信息响应
public class UserInfo
{
public int UserId { get; set; }
public string UserName { get; set; }
public string? Email { get; set; }
public string? Avatar { get; set; }
public List<string> Roles { get; set; }
public List<string> Buttons { get; set; }
}
// 分页响应
public class PaginatedResponse<T>
{
public List<T> Records { get; set; }
public int Current { get; set; }
public int Size { get; set; }
public int Total { get; set; }
}
// 菜单树项
public class MenuTreeItem
{
public string Path { get; set; }
public string Name { get; set; }
public string? Component { get; set; }
public MenuMeta Meta { get; set; }
public List<MenuTreeItem>? Children { get; set; }
}
public class MenuMeta
{
public string Title { get; set; }
public string? Icon { get; set; }
public bool? IsHide { get; set; }
public bool? KeepAlive { get; set; }
public string? Link { get; set; }
public bool? IsIframe { get; set; }
public List<string>? Roles { get; set; }
}
Correctness Properties
A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.
Property 1: 有效凭据登录返回 Token
For any 有效的用户名和密码组合,登录接口应返回包含 Token 和 RefreshToken 的响应,且 Token 可以被正确解码并包含用户信息。
Validates: Requirements 1.1, 1.5
Property 2: 无效凭据登录返回 401
For any 无效的用户名或密码,登录接口应返回 401 状态码。
Validates: Requirements 1.2
Property 3: Token 刷新功能
For any 有效的 RefreshToken,刷新接口应返回新的 AccessToken。
Validates: Requirements 1.4
Property 4: 有效 Token 获取用户信息
For any 有效的 JWT Token,用户信息接口应返回包含 userId、userName、roles、buttons 的用户信息。
Validates: Requirements 2.1, 2.2, 2.3
Property 5: 无效 Token 返回 401
For any 无效或过期的 Token,受保护的 API 应返回 401 状态码。
Validates: Requirements 2.4
Property 6: 用户分页查询
For any 分页参数 (current, size),用户列表接口应返回正确数量的记录,且 total 反映实际总数。
Validates: Requirements 3.1
Property 7: 用户名唯一性验证
For any 已存在的用户名,创建用户接口应返回错误。
Validates: Requirements 3.2
Property 8: 用户搜索过滤
For any 搜索条件,返回的用户列表应只包含符合条件的用户。
Validates: Requirements 3.3
Property 9: 角色编码唯一性验证
For any 已存在的角色编码,创建角色接口应返回错误。
Validates: Requirements 4.2
Property 10: 菜单角色过滤
For any 用户角色,菜单接口应只返回该角色有权限访问的菜单项。
Validates: Requirements 5.1, 5.4
Property 11: 统一响应格式
For any API 请求,响应应包含 code、msg、data 字段。
Validates: Requirements 7.1
Error Handling
| 错误场景 | HTTP 状态码 | 响应 Code | 错误消息 |
|---|---|---|---|
| 用户名或密码错误 | 401 | 401 | 用户名或密码错误 |
| Token 无效或过期 | 401 | 401 | 未授权访问 |
| 权限不足 | 403 | 403 | 权限不足 |
| 资源不存在 | 404 | 404 | 资源不存在 |
| 参数验证失败 | 400 | 400 | 具体验证错误信息 |
| 用户名已存在 | 400 | 400 | 用户名已存在 |
| 角色编码已存在 | 400 | 400 | 角色编码已存在 |
| 服务器内部错误 | 500 | 500 | 服务器内部错误 |
Testing Strategy
单元测试
- 测试 AuthService 的密码哈希和验证逻辑
- 测试 JWT Token 的生成和解析
- 测试用户、角色、菜单的 CRUD 操作
- 测试权限过滤逻辑
集成测试
- 测试完整的登录流程
- 测试 Token 刷新流程
- 测试用户管理 API
- 测试角色管理 API
- 测试菜单获取和过滤
属性测试
使用 FsCheck 或类似库进行属性测试:
- 验证 Token 格式和内容
- 验证分页逻辑
- 验证搜索过滤逻辑
- 验证权限控制逻辑