RuoVea.OmiApi.UserRoleMenu 10.0.0.5

dotnet add package RuoVea.OmiApi.UserRoleMenu --version 10.0.0.5
                    
NuGet\Install-Package RuoVea.OmiApi.UserRoleMenu -Version 10.0.0.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RuoVea.OmiApi.UserRoleMenu" Version="10.0.0.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RuoVea.OmiApi.UserRoleMenu" Version="10.0.0.5" />
                    
Directory.Packages.props
<PackageReference Include="RuoVea.OmiApi.UserRoleMenu" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RuoVea.OmiApi.UserRoleMenu --version 10.0.0.5
                    
#r "nuget: RuoVea.OmiApi.UserRoleMenu, 10.0.0.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RuoVea.OmiApi.UserRoleMenu@10.0.0.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RuoVea.OmiApi.UserRoleMenu&version=10.0.0.5
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.OmiApi.UserRoleMenu&version=10.0.0.5
                    
Install as a Cake Tool

RuoVea.OmiApi.UserRoleMenu

用户角色菜单管理 API 组件 —— 基于 .NET 构建的轻量级、跨平台 RBAC 权限管理系统后端。

RuoVea.OmiApi.UserRoleMenu 是一个开箱即用的用户-角色-菜单权限管理 NuGet 包,提供用户 CRUD、角色 CRUD、菜单树管理、角色菜单授权、用户角色授权、按钮权限控制、JWT 认证集成、缓存管理的完整能力。基于 SqlSugar ORMDynamicWebApi,注册即自动生成 RESTful API 端点,支持 MySql / SqlServer / PostgreSQL / SQLite / Oracle 等多种数据库。


目录


概览

功能特性

模块 功能
👤 用户管理 分页查询、增删改查、状态启用/停用、密码修改/重置、登录锁定解除、用户角色授权、基本信息管理
🔐 角色管理 分页查询、增删改查、状态控制、角色菜单授权、角色用户关联查询
📋 菜单管理 树形菜单 CRUD、按钮权限管理、菜单类型(目录/菜单/按钮)校验、登录菜单树动态构建
🔒 权限控制 基于按钮权限标识(xxx:xxx 格式)的细粒度权限控制,按钮权限缓存自动刷新
🌱 种子数据 自动建表、预置菜单/角色/用户种子数据(Web 菜单与 API 菜单双模式)
🗄️ 缓存管理 按钮权限缓存、黑名单缓存、密码错误次数缓存,支持按前缀批量删除与查询
🛡️ 安全防护 超级管理员禁止删除/修改状态、禁止操作本人账号状态、禁止删除含用户的角色
🏢 多租户 所有实体实现 ITenantEntity,天然支持租户隔离
🔑 JWT 集成 内置 JwtBearer 认证配置,与 RuoVea.ExJwtBearer 无缝对接
🗄️ 多库支持 MySql、SqlServer、PostgreSQL、SQLite、Oracle、Dm 等

架构一览

┌─────────────────────────────────────────────────────────────┐
│                      NuGet Package                           │
│  RuoVea.OmiApi.UserRoleMenu                                 │
├─────────────────────────────────────────────────────────────┤
│  Service Layer (7 Services)                                 │
│  ┌───────────────┐ ┌───────────────┐ ┌───────────────┐     │
│  │ SysUser       │ │ SysRole       │ │ SysMenu       │     │
│  │ Service       │ │ Service       │ │ Service       │     │
│  │ 14 endpoints  │ │ 8 endpoints   │ │ 8 endpoints   │     │
│  ├───────────────┤ ├───────────────┤ ├───────────────┤     │
│  │ SysUserRole   │ │ SysRoleMenu   │ │ SysCache      │     │
│  │ Service       │ │ Service       │ │ Service       │     │
│  │ (internal)    │ │ (internal)    │ │ 9 endpoints   │     │
│  └───────────────┘ └───────────────┘ └───────────────┘     │
│                                                             │
│  DI Extensions                                              │
│  ├─ AddOmiSystemSetup()       (3 overloads)                 │
│  └─ AddSystemInitSetup()      (种子数据 + 建表)             │
├─────────────────────────────────────────────────────────────┤
│  Domain Layer (5 Entities)                                  │
│  SysUser ──< SysUserRole >── SysRole                       │
│  SysRole ──< SysRoleMenu >── SysMenu                       │
│  SysMenu ─── SysMenu (Self-ref tree, Pid)                  │
├─────────────────────────────────────────────────────────────┤
│  Infrastructure                                             │
│  SqlSugar ORM · DynamicWebApi · ExSugar Repo               │
│  ExJwtBearer · ExFilter · ExPws · OmiApi.Config            │
└─────────────────────────────────────────────────────────────┘

支持的 .NET 版本

TFM NuGet 版本
net8.0 8.0.2.19
net10.0 10.0.0.4

安装

NuGet 包管理器

# .NET 8 项目
Install-Package RuoVea.OmiApi.UserRoleMenu -Version 8.0.2.19

# .NET 10 项目
Install-Package RuoVea.OmiApi.UserRoleMenu -Version 10.0.0.4

.NET CLI

dotnet add package RuoVea.OmiApi.UserRoleMenu --version 8.0.2.19

依赖项

本包依赖以下组件(安装时会自动引入):

包名 用途
RuoVea.DynamicWebApi 动态 API 控制器生成
RuoVea.ExSugar SqlSugar 仓储模式封装
RuoVea.ExJwtBearer JWT 认证集成
RuoVea.ExFilter 请求过滤与拦截
RuoVea.ExPws 密码加密服务
RuoVea.OmiApi.Config 系统配置管理

30 秒快速开始

1. 配置数据库连接 (appsettings.json)

{
  "ConnectionConfigs": [
    {
      "DbType": "Sqlite",
      "ConnectionString": "DataSource=./ruovea.db"
    }
  ],
  /* Jwt 配置 */
  "Jwt": {
    "ValidateIssuerSigningKey": true,
    "IssuerSigningKey": "3c1cbc3f546eda35168c3aa3cb91780fbe703f0996c6d123ea96dc85c70bbc0a",
    "ValidateIssuer": true,
    "ValidIssuer": "SecurityDemo.Authentication.JWT",
    "ValidateAudience": true,
    "ValidAudience": "jwtAudience",
    "ValidateLifetime": true,
    "ExpiredTime": 1440,
    "ClockSkew": 5
  },
  "Swagger": {
    "ApiVersions": [
      {
        "Title": "系统应用",
        "Version": "system"
      }
    ]
  }
}

支持的 DbType 值: MySqlSqlServerSqliteOraclePostgreSQLDmKdbndpOpenGaussClickHouse 等。

2. 注册服务 (Program.cs)

// <summary>
// 在 Program.cs 中注册 OmiApi.UserRoleMenu 组件服务
// </summary>
var builder = WebApplication.CreateBuilder(args);

// 注册动态 Web API(自动将 Service 映射为 REST 控制器)
builder.Services.AddDynamicWebApi(options =>
{
    options.RemoveControllerPostfixes = new List<string> { "AppService", "Service" };
    options.RemovePrefix = new List<string> { "get", "post" };
});

// 注册用户角色菜单模块服务(默认 Scoped 生命周期)
builder.Services.AddOmiSystemSetup();

// 注册 SqlSugar ORM
builder.Services.AddSqlSugarSetup();

// 注册 HttpContext 用户上下文
builder.Services.AddHttpContextSetup<AspNetUser>();

// 注册 JWT 认证
builder.Services.AddAuthenticationSetup(IdentifyEnum.Jwt, true);

// 初始化数据库表结构和种子数据(isWeb: true=Web菜单, false=API菜单)
builder.Services.AddSystemInitSetup(isWeb: true);

// 注册请求拦截、验证、异常处理
builder.Services
    .RequestActionSetup()
    .ResultSetup()
    .ExceptionSetup();

// 注册 Swagger
builder.Services.AddSwaggerSetup();

// 跨域配置
builder.Services.AddCors(option =>
{
    option.AddDefaultPolicy(builder =>
    {
        builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
    });
});

var app = builder.Build();

app.UseCors();
app.UseAuthentication();
app.UseAuthorization();

app.Run();

3. 启动并访问 Swagger

启动项目后,访问 https://localhost:xxxx/swagger,即可看到 "系统应用" 分组下的全部 RESTful API 端点。


核心场景

场景一:创建用户并授权角色

// <summary>
// 创建用户并授权角色 —— 异步写法。先创建用户,再为其分配角色集合。
// </summary>
public async Task<bool> CreateUserWithRolesAsync(
    SysUserService userService, AddUserInput input, List<long> roleIds)
{
    // 1. 创建用户(账号去重检查自动执行)
    var user = await userService.AddUser(input);

    // 2. 授权用户角色
    await userService.GrantRole(new UserRolesInput
    {
        UserId  = user.Id,
        RoleIds = roleIds
    });

    return true;
}

// <summary>
// 创建用户并授权角色 —— 同步写法(通过 .GetAwaiter().GetResult() 调用)
// ⚠️ 注意:在 ASP.NET 上下文中可能导致死锁,仅推荐在 Console/测试环境使用。
// </summary>
public bool CreateUserWithRoles(SysUserService userService, AddUserInput input, List<long> roleIds)
{
    var user = userService.AddUser(input).GetAwaiter().GetResult();
    userService.GrantRole(new UserRolesInput
    {
        UserId  = user.Id,
        RoleIds = roleIds
    }).GetAwaiter().GetResult();
    return true;
}
创建用户并授权流程:

  开始
    │
    ├─ 1. 校验账号是否已存在 → 存在则抛出 i18n.account_exists
    │
    ├─ 2. 密码加密(IPasswordServer)
    │
    ├─ 3. INSERT SysUser(用户主表)
    │
    └─ 4. DELETE + INSERT SysUserRole[](先清空旧角色,再批量写入新角色)
    │
  完成

场景二:角色菜单授权(完整权限配置)

// <summary>
// 为角色授予菜单权限 —— 异步写法。传入角色ID和菜单ID集合,先清空旧权限再批量写入。
// </summary>
public async Task GrantMenuToRoleAsync(SysRoleService roleService, long roleId, List<long> menuIds)
{
    await roleService.GrantMenu(new RoleMenuInput
    {
        RoleId = roleId,
        MenuIds = menuIds
    });
}

// <summary>
// 查询角色已有的菜单权限 —— 用于授权页面的回显
// </summary>
public async Task<List<long>> GetRoleMenuIdsAsync(SysRoleService roleService, long roleId)
{
    return await roleService.GetOwnMenuList(new RoleInput { Id = roleId });
}
角色菜单授权流程:

  开始事务
    │
    ├─ 1. DELETE FROM SysRoleMenu WHERE RoleId = @roleId(清空旧权限)
    │
    ├─ 2. INSERT SysRoleMenu[](批量写入新权限)
    │
    └─ 3. 清除按钮权限缓存(按前缀批量删除)
    │
  提交事务

场景三:获取登录用户菜单树(动态导航)

// <summary>
// 获取当前登录用户的菜单树 —— 根据用户角色动态构建,自动过滤禁用菜单和按钮类型节点。
// 异步写法:适用于 Controller 或 Service 中直接调用。
// </summary>
public async Task<List<MenuTreeOutput>> GetUserMenuTreeAsync(SysMenuService menuService)
{
    return await menuService.GetLoginMenuTree();
}

// <summary>
// 获取系统菜单树(用于角色授权时选择) —— 包含所有节点供管理员勾选。
// </summary>
public async Task<List<MenuTreeOutput>> GetGrantMenuTreeAsync(
    SysMenuService menuService, long roleId)
{
    return await menuService.TreeForGrant(
        new TreeForGrantInput { RoleId = roleId },
        includeButton: true,
        roleId: roleId
    );
}

// <summary>
// 获取登录用户菜单树 —— 同步写法
// ⚠️ 注意:在 ASP.NET 上下文中可能导致死锁,仅推荐在 Console/测试环境使用。
// </summary>
public List<MenuTreeOutput> GetUserMenuTree(SysMenuService menuService)
{
    return menuService.GetLoginMenuTree().GetAwaiter().GetResult();
}
登录菜单树构建流程:

  1. 获取当前用户信息(ICurrentUser)
    │
  2. 查询用户角色集合(SysUserRole)
    │
  3. 查询角色菜单集合(SysRoleMenu)
    │
  4. 查询所有菜单(SysMenu)
    │
  5. 过滤:
     ├─ 仅保留用户角色拥有的菜单
     ├─ 排除 IsDisable = Y(已禁用)
     └─ 排除 Type = 按钮类型(目录/菜单保留)
    │
  6. 递归构建树形结构(Pid 自引用)
    │
  返回树形菜单

场景四:按钮权限校验(细粒度权限控制)

// <summary>
// 获取当前用户的按钮权限标识集合 —— 支持缓存,用于前端按钮显隐控制。
// 异步写法:返回 List<string>,如 ["user:add", "user:delete", "role:grant"]。
// </summary>
public async Task<List<string>> GetUserButtonPermissionsAsync(SysMenuService menuService)
{
    return await menuService.GetOwnBtnPermList();
}

// <summary>
// 检查用户是否拥有特定按钮权限 —— 用于后端 API 鉴权。
// </summary>
public async Task<bool> HasPermissionAsync(SysMenuService menuService, string permission)
{
    var perms = await menuService.GetOwnBtnPermList();
    return perms.Contains(permission);
}

// <summary>
// 检查用户是否拥有特定按钮权限 —— 同步写法
// </summary>
public bool HasPermission(SysMenuService menuService, string permission)
{
    var perms = menuService.GetOwnBtnPermList().GetAwaiter().GetResult();
    return perms.Contains(permission);
}
按钮权限缓存流程:

  首次请求 GetOwnBtnPermList()
    │
    ├─ 缓存命中 → 直接返回
    │
    └─ 缓存未命中:
        │
        ├─ 1. 获取用户角色(SysUserRole)
        ├─ 2. 获取角色菜单(SysRoleMenu)
        ├─ 3. 查询按钮类型菜单(Type = 按钮)
        ├─ 4. 提取 Permission 字段
        ├─ 5. 过滤空值和非法格式
        ├─ 6. 写入缓存,设置过期时间
        │
        返回权限标识集合

  角色权限变更时:
    → GrantMenu() 自动调用 RemoveByPrefixKey() 清除按钮权限缓存

场景五:密码修改与重置(安全闭环)

// <summary>
// 用户自行修改密码 —— 需验证旧密码,且新密码不能与旧密码相同。
// 异步写法。
// </summary>
public async Task<bool> ChangePasswordAsync(
    SysUserService userService, ChangePwdInput input)
{
    // input 包含: OldPassword, NewPassword
    await userService.ChangePwd(input);
    return true;
}

// <summary>
// 管理员重置用户密码 —— 无需旧密码,直接设置为新密码。
// 异步写法。
// </summary>
public async Task<bool> ResetUserPasswordAsync(
    SysUserService userService, ResetPwdUserInput input)
{
    // input 包含: Id (用户ID), Password (新密码)
    await userService.ResetPwd(input);
    return true;
}

// <summary>
// 解除用户登录锁定 —— 清除密码错误次数缓存。
// 异步写法。
// </summary>
public async Task UnlockUserAsync(SysUserService userService, long userId)
{
    await userService.UnlockLogin(new UnlockLoginInput { Id = userId });
}

// <summary>
// 修改密码 —— 同步写法
// </summary>
public bool ChangePassword(SysUserService userService, ChangePwdInput input)
{
    userService.ChangePwd(input).GetAwaiter().GetResult();
    return true;
}
密码修改流程:

  1. 获取当前用户信息(ICurrentUser)
    │
  2. 验证旧密码(IPasswordServer.Verify)
    ├─ 不匹配 → 抛出 i18n.password_error
    │
  3. 校验新旧密码不能相同
    ├─ 相同 → 抛出 i18n.new_password_same_as_old
    │
  4. 加密新密码(IPasswordServer.Hash)
    │
  5. UPDATE SysUser SET Password = @newPwd WHERE Id = @userId
    │
  完成

配置选项详解

数据库连接配置 (ConnectionConfigs)

{
  "ConnectionConfigs": [
    {
      "DbType": "Sqlite",
      "ConnectionString": "DataSource=./ruovea.db",

      "EnableUnderLine": false,
      "EnableDiffLog": false,
      "IsEncrypt": false,
      "DbSecurity": "",
      "IsDeleteFilter": true,
      "IsUserIdFilter": false,
      "IsTenantIdFilter": false,
      "CommandTimeOut": 30
    }
  ]
}
参数 类型 默认值 说明
DbType string 必填 数据库类型
ConnectionString string 必填 连接字符串
EnableUnderLine bool false 驼峰转下划线
EnableDiffLog bool false 启用库表差异日志
IsEncrypt bool false 连接字符串是否加密
DbSecurity string "" 解密密钥(IsEncrypt=true 时使用)
IsDeleteFilter bool true ⚠️ 全局软删除过滤(实体需继承 IDeletedEntity
IsUserIdFilter bool false 按创建者过滤(实体需继承 ICreatorFilterEntityBase
IsTenantIdFilter bool false 按租户过滤(实体需继承 ITenantIdFilter
CommandTimeOut int 30 SQL 命令超时时间(秒)

JWT 认证配置 (Jwt)

{
  "Jwt": {
    "ValidateIssuerSigningKey": true,
    "IssuerSigningKey": "3c1cbc3f546eda35168c3aa3cb91780fbe703f0996c6d123ea96dc85c70bbc0a",
    "ValidateIssuer": true,
    "ValidIssuer": "SecurityDemo.Authentication.JWT",
    "ValidateAudience": true,
    "ValidAudience": "jwtAudience",
    "ValidateLifetime": true,
    "ExpiredTime": 1440,
    "ClockSkew": 5
  }
}
参数 类型 默认值 说明
ValidateIssuerSigningKey bool true 是否验证密钥
IssuerSigningKey string 必填 ⚠️ 密钥,长度必须大于 16 且足够复杂
ValidateIssuer bool true 是否验证签发方
ValidIssuer string 必填 签发方标识
ValidateAudience bool true 是否验证签收方
ValidAudience string 必填 签收方标识
ValidateLifetime bool true 是否验证过期时间
ExpiredTime int 1440 过期时间(分钟),默认 24 小时
ClockSkew int 5 ❗ 过期时间容错值(秒),默认 5 秒

Swagger API 版本配置

{
  "Swagger": {
    "ApiVersions": [
      {
        "Title": "系统应用",
        "Version": "system"
      }
    ]
  }
}
配置项 类型 说明
Title string API 版本显示标题,用于 Swagger UI 中展示
Version string API 版本标识,用于路由和文档区分

DI 注册配置

// <summary>
// AddOmiSystemSetup —— 三种重载,适应不同配置来源。
// </summary>

// 重载 1:自动从全局 AppSettings 读取配置
builder.Services.AddOmiSystemSetup();

// 重载 2:传入自定义 IConfiguration
builder.Services.AddOmiSystemSetup(configuration.GetSection("MySystem"));

// 重载 3:通过 Action 委托配置 DbInitConfig
builder.Services.AddOmiSystemSetup(options =>
{
    options.InitTable = true;
});

// 自定义服务生命周期
builder.Services.AddOmiSystemSetup(ServiceLifetime.Singleton);
方法 参数 默认值 说明
AddOmiSystemSetup() 从 AppSettings 自动绑定配置,Scoped 生命周期
AddOmiSystemSetup(IConfiguration config) config 通过 IConfiguration 传入配置节
AddOmiSystemSetup(Action<DbInitConfig> config) config 委托 代码内配置 DbInitConfig
serviceLifetime ServiceLifetime Scoped 注册所有服务的生命周期
// <summary>
// AddSystemInitSetup —— 初始化数据库表和种子数据。
// </summary>

// isWeb = true:初始化 Web 菜单种子数据
builder.Services.AddSystemInitSetup(isWeb: true);

// isWeb = false:初始化 API 菜单种子数据
builder.Services.AddSystemInitSetup(isWeb: false);
方法 参数 默认值 说明
AddSystemInitSetup(bool isWeb) isWeb false 异步初始化数据库表和种子数据,控制菜单种子类型

⚠️ 线程安全: 切换为 Singleton 生命周期时,确保注入的 SugarRepository<T>ISqlSugarClient 本身支持并发访问。SqlSugar 的 SqlSugarClient 是线程安全的,但仓储的某些操作依赖请求上下文(如 ICurrentUser 自动填充),单例模式下可能导致用户信息串扰。

性能提醒: AddSystemInitSetup 使用后台任务执行表结构检查和种子数据写入。生产环境首次启动后,已完成初始化的数据库无需重复执行,可通过 AddOmiSystemSetup(options => { options.InitTable = false; }) 关闭。


API 接口速览

所有接口自动归入 Swagger "system" 分组,默认路由前缀由 DynamicWebApi 配置决定。

SysUserService —— 用户管理

HTTP 方法 说明
GET GetPagesAsync(PageUserInput) 获取用户分页列表
POST GetRoleUserListByRoleId(long roleId) 获取角色用户相关信息
GET UserList() 获取用户列表
GET GetAllAsync() 获取全部用户简要列表(仅 ID、账号、姓名)
POST AddUser(AddUserInput) 增加用户
PUT UpdateUser(UpdateUserInput) 更新用户
DELETE DeleteUser(DeleteUserInput) 删除用户
GET GetBaseInfo() 查看用户基本信息
PUT UpdateBaseInfo(SysUser) 更新用户基本信息
POST SetStatus(UserInput) 设置用户状态(启用/停用)
POST GrantRole(UserRolesInput) 授权用户角色
POST ChangePwd(ChangePwdInput) 修改用户密码
POST ResetPwd(ResetPwdUserInput) 重置用户密码
POST UnlockLogin(UnlockLoginInput) 解除登录锁定
GET GetOwnRoleList(long userId) 获取用户拥有角色集合

SysRoleService —— 角色管理

HTTP 方法 说明
GET GetPagesAsync(PageRoleInput) 获取角色分页列表
GET GetList() 获取角色列表
POST AddRole(AddRoleInput) 增加角色
PUT UpdateRole(AddRoleInput) 更新角色
DELETE DeleteRole(DeleteRoleInput) 删除角色
POST GrantMenu(RoleMenuInput) 授权角色菜单
GET GetOwnMenuList(RoleInput) 根据角色 Id 获取菜单 Id 集合
POST SetStatus(RoleInput) 设置角色状态(启用/停用)

SysMenuService —— 菜单管理

HTTP 方法 说明
GET GetLoginMenuTree() 获取登录用户菜单树(含权限过滤)
GET GetList(MenuInput) 获取菜单列表
POST AddMenu(AddMenuInput) 增加菜单(含父节点校验)
PUT UpdateMenu(UpdateMenuInput) 更新菜单
DELETE DeleteMenu(DeleteMenuInput) 删除菜单
GET GetOwnBtnPermList() 获取用户按钮权限集合(缓存)
GET GetMenuTree() 获取系统菜单树(用于上级节点选择)
POST TreeForGrant(TreeForGrantInput, bool, long) 获取系统菜单树(用于角色授权选择)

SysCacheService —— 缓存管理

HTTP 方法 说明
DELETE Remove(string key) 删除指定缓存
DELETE Clear(string profix) 清空所有缓存
DELETE RemoveByPrefixKey(string prefixKey) 根据键名前缀批量删除缓存
GET GetKeysByPrefixKey(string prefixKey) 根据键名前缀获取键名集合
GET GetValue(string key) 获取缓存值
POST Set(string key, object value) 增加缓存(内部方法,NonAction)
POST Set(string key, object value, TimeSpan expire) 增加缓存并设置过期时间(内部方法)
GET Get<T>(string key) 获取泛型缓存(内部方法)
GET ExistKey(string key) 检查缓存是否存在(内部方法)

内部服务(不公开 API)

服务 依赖 说明
SysUserRoleService SugarRepository<SysUserRole>, SysCacheService 用户角色关联 CRUD(供 SysUserService 和 SysRoleService 内部调用)
SysRoleMenuService SugarRepository<SysRoleMenu>, SysCacheService 角色菜单关联 CRUD(供 SysRoleService 和 SysMenuService 内部调用)

错误处理与日志

错误码速查

组件内部使用 i18n 国际化资源管理错误信息,支持多语言切换:

错误码 含义 触发场景
i18n.account_exists 账号已存在 创建用户时账号重复
i18n.account_not_exists 账号不存在 登录或查询时账号未找到
i18n.data_exists 数据已存在 创建角色/菜单时 Code 或名称重复
i18n.dict_status_error 字典状态错误 字典数据状态异常
i18n.illegal_operation_self 非法操作,禁止删除自己 用户尝试删除自身账号
i18n.new_password_same_as_old 新密码不能与旧密码相同 修改密码时新旧密码一致
i18n.parent_node_cannot_be_button 父节点不能为按钮类型 创建菜单时选择了按钮类型作为父节点
i18n.password_error 旧密码输入错误 修改密码时旧密码校验失败
i18n.permission_id_format_empty 权限标识格式为空 按钮权限标识为空字符串
i18n.permission_id_format_error 权限标识格式错误 按钮权限标识不符合 xxx:xxx 格式
i18n.prohibit_delete_admin 禁止删除系统管理员角色 尝试删除系统管理员角色
i18n.prohibit_delete_super_admin 禁止删除超级管理员 尝试删除超级管理员账号
i18n.prohibit_modify_self_status 禁止修改本人账号状态 尝试启用/停用自己的账号
i18n.prohibit_modify_super_admin_status 禁止修改超级管理员状态 尝试修改超级管理员的启用状态
i18n.prohibit_same_node_as_parent 禁止本节点与父节点相同 菜单编辑时将自身设为父节点
i18n.record_not_exists 记录不存在 根据 ID 查询/更新时记录缺失
i18n.role_has_accounts 此角色下面存在账号禁止删除 删除角色时仍有用户关联
i18n.route_name_duplicate 路由名称重复 创建菜单时路由名称已存在
ErrorEnum.D4000 通用业务错误 参数校验失败等通用错误

异常处理示例

// <summary>
// 安全创建用户 —— 捕获参数、业务和数据库异常。
// 异步写法。
// </summary>
public async Task<(bool Success, string Message)> SafeCreateUserAsync(
    SysUserService userService, AddUserInput dto)
{
    try
    {
        await userService.AddUser(dto);
        return (true, "用户创建成功");
    }
    catch (Exception ex) when (ex.Message.Contains("account_exists"))
    {
        // 账号已存在
        return (false, $"账号 '{dto.Account}' 已存在,请更换账号名");
    }
    catch (ArgumentException ex)
    {
        // 参数校验失败(必填字段缺失、格式错误)
        return (false, $"参数校验失败: {ex.Message}");
    }
    catch (Exception ex) when (ex.Message.Contains("transaction", StringComparison.OrdinalIgnoreCase))
    {
        // 事务执行失败(数据库层面错误)
        return (false, $"数据操作失败,已自动回滚: {ex.Message}");
    }
}

// <summary>
// 安全删除角色 —— 含关联数据防护检查。
// 异步写法。
// </summary>
public async Task<(bool Success, string Message)> SafeDeleteRoleAsync(
    SysRoleService roleService, long roleId)
{
    try
    {
        await roleService.DeleteRole(new DeleteRoleInput { Id = roleId });
        return (true, "角色删除成功");
    }
    catch (Exception ex) when (ex.Message.Contains("prohibit_delete_admin"))
    {
        return (false, "系统管理员角色禁止删除");
    }
    catch (Exception ex) when (ex.Message.Contains("role_has_accounts"))
    {
        return (false, "该角色下仍有用户关联,请先解除用户角色关系");
    }
    catch (Exception ex) when (ex.Message.Contains("record_not_exists"))
    {
        return (false, "角色不存在或已被删除");
    }
}

// <summary>
// 安全删除角色 —— 同步写法(仅在非 ASP.NET 上下文使用)
// </summary>
public (bool Success, string Message) SafeDeleteRole(SysRoleService roleService, long roleId)
{
    try
    {
        roleService.DeleteRole(new DeleteRoleInput { Id = roleId }).GetAwaiter().GetResult();
        return (true, "角色删除成功");
    }
    catch (Exception ex)
    {
        return (false, ex.Message);
    }
}

日志集成

组件不直接输出日志,依赖调用方集成的日志框架。推荐在 Program.cs 中配置 Serilog 或 NLog 来捕获:

// SqlSugar 的 SQL 日志可通过 AOP 事件捕获
builder.Services.AddSqlSugarSetup(); // 内部配置了 SQL 执行日志

版本迁移指南

从 8.0.x 升级到 10.0.x

变更项 说明
TFM 升级 net8.0net10.0,需同步升级所有依赖包到 10.0.* 版本
包版本对齐 RuoVea.ExFilterRuoVea.ExJwtBearerRuoVea.ExPwsRuoVea.OmiApi.ConfigRuoVea.DynamicWebApiRuoVea.ExSugar 均需升至对应 10.0.*
API 兼容 所有公开 API 向后兼容,无需修改业务代码
数据库 表结构无变更,无需执行迁移脚本

API 变更历史

版本 变更
8.0.2.19 修复多字段查询时 SqlSugar 因重复参数 @value 键而报错的问题
8.0.2.x 组件版本升级,图表数据缓存
8.0.2.x 表结构初始化处理
更早版本 初始发布

常见问题

Q: 如何切换数据库?

修改 appsettings.json 中的 DbTypeConnectionString,然后重新运行。AddSystemInitSetup 会自动为新数据库创建表结构并写入种子数据。

// MySql 示例
{ "DbType": "MySql", "ConnectionString": "Server=localhost;Database=ruovea;Uid=root;Pwd=123456;" }

// SqlServer 示例
{ "DbType": "SqlServer", "ConnectionString": "Server=.;Database=ruovea;Trusted_Connection=True;" }

// PostgreSQL 示例
{ "DbType": "PostgreSQL", "ConnectionString": "Host=localhost;Database=ruovea;Username=postgres;Password=123456;" }

Q: 如何自定义 API 路由前缀?

AddDynamicWebApi 中配置 DefaultApiPrefix

builder.Services.AddDynamicWebApi(options =>
{
    options.DefaultApiPrefix = "/openapi/api";
});

Q: Web 菜单和 API 菜单有什么区别?

AddSystemInitSetup(isWeb: true) 初始化的是前端 Web 应用的菜单种子数据(含路由路径、组件、图标等),isWeb: false 初始化的是纯 API 接口的菜单种子数据。根据您的项目类型选择合适的模式。

Q: ⚠️ 超级管理员账号是什么?有哪些防护?

种子数据中预置的超级管理员账号在组件内部有严格防护:

  • 禁止删除超级管理员(i18n.prohibit_delete_super_admin
  • 禁止修改超级管理员状态(i18n.prohibit_modify_super_admin_status
  • 禁止修改本人账号状态(i18n.prohibit_modify_self_status
  • 禁止删除系统管理员角色(i18n.prohibit_delete_admin

这些防护确保系统始终至少有一个可用管理员。

Q: ❗ 按钮权限缓存何时失效?

按钮权限缓存在以下场景自动失效:

  1. 角色菜单授权变更(GrantMenu)时,自动调用 RemoveByPrefixKey 清除相关缓存
  2. 缓存过期时间到达后自动失效
  3. 手动调用 SysCacheService.RemoveByPrefixKey(prefixKey) 强制清除

如果需要立即刷新缓存,可通过 SysCacheServiceRemoveByPrefixKey 方法清除按钮权限缓存前缀。

Q: 为什么删除角色时提示"此角色下面存在账号"?

该角色仍有用户关联时,为防止权限失控,组件会抛出 i18n.role_has_accounts 错误。解决方案:通过 SysUserService.GrantRole() 先解除所有用户与该角色的关联,再执行删除。

Q: 菜单的三种类型(目录/菜单/按钮)各自用途是什么?

类型 说明 典型场景
目录 分组节点,无路由组件,仅用于组织菜单树 系统管理、内容管理等顶级目录
菜单 有路由和组件的页面节点,显示在左侧导航 用户管理、角色管理等具体页面
按钮 权限控制节点,不显示在菜单树中,仅用于权限标识 user:add、role:delete 等操作按钮

创建菜单时,父节点不能为按钮类型(i18n.parent_node_cannot_be_button),且路由名称不允许重复(i18n.route_name_duplicate)。

Q: ⚠️ 修改密码后需要重新登录吗?

修改密码操作不会使当前 JWT Token 失效。如果业务需要修改密码后强制重新登录,请在调用方额外实现 Token 失效逻辑(如将 Token 加入黑名单缓存)。

Q: ❗ 多租户场景下数据如何隔离?

所有实体(SysUserSysRoleSysMenu)均实现 ITenantEntity 接口。启用租户过滤后,查询和操作会自动按 TenantId 过滤。配置方式:

{
  "ConnectionConfigs": [
    {
      "IsTenantIdFilter": true
    }
  ]
}

许可证

本项目基于 Apache 2.0 License 开源发布。


Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on RuoVea.OmiApi.UserRoleMenu:

Package Downloads
RuoVea.OmiUserRoleMenu

字典管理

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0.5 48 6/26/2026
10.0.0.4 93 6/24/2026
10.0.0.3 116 5/28/2026
10.0.0.2 105 5/28/2026
10.0.0.1 123 3/23/2026
9.0.0.3 128 5/28/2026
9.0.0.2 113 5/28/2026
9.0.0.1 154 3/23/2026
9.0.0 138 1/27/2026
8.0.2.20 50 6/26/2026
8.0.2.19 107 6/24/2026
8.0.2.18 126 5/28/2026
8.0.1.17 122 5/28/2026
8.0.1.16 131 3/23/2026
7.0.2.18 113 5/28/2026
7.0.1.17 118 5/28/2026
7.0.1.16 126 3/23/2026
6.0.2.18 130 5/28/2026
6.0.2.17 131 5/28/2026
6.0.2.16 144 3/23/2026
Loading failed