13 KiB
Raw Blame History

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 格式和内容
  • 验证分页逻辑
  • 验证搜索过滤逻辑
  • 验证权限控制逻辑