RuoVea.OmiApi.SystemApp
10.0.0.6
dotnet add package RuoVea.OmiApi.SystemApp --version 10.0.0.6
NuGet\Install-Package RuoVea.OmiApi.SystemApp -Version 10.0.0.6
<PackageReference Include="RuoVea.OmiApi.SystemApp" Version="10.0.0.6" />
<PackageVersion Include="RuoVea.OmiApi.SystemApp" Version="10.0.0.6" />
<PackageReference Include="RuoVea.OmiApi.SystemApp" />
paket add RuoVea.OmiApi.SystemApp --version 10.0.0.6
#r "nuget: RuoVea.OmiApi.SystemApp, 10.0.0.6"
#:package RuoVea.OmiApi.SystemApp@10.0.0.6
#addin nuget:?package=RuoVea.OmiApi.SystemApp&version=10.0.0.6
#tool nuget:?package=RuoVea.OmiApi.SystemApp&version=10.0.0.6
RuoVea.OmiApi.SystemApp
系统应用管理 — 用户、角色、菜单、机构、职位、权限管理 API
目录
概览
RuoVea.OmiApi.SystemApp 是基于 .NET 8.0 / .NET 10.0 构建的企业级系统应用管理组件,提供开箱即用的用户、角色、菜单、机构、职位、权限管理能力。基于 SqlSugar ORM,支持 MySql、SqlServer、PostgreSQL、SQLite、Oracle 等主流数据库,内置多租户、数据权限过滤、Redis 缓存、国际化 i18n 与种子数据初始化。
功能矩阵
| 模块 | 功能 | 说明 |
|---|---|---|
| 用户管理 | CRUD / 密码修改与重置 / 状态管理 / 角色授权 / 登录解锁 | 支持 AdminType 分级、多机构兼职 |
| 角色管理 | CRUD / 菜单授权 / 数据范围授权 / 分页查询 | 支持 DataScope 全部/自定义/本部门/本部门及以下/仅本人/个人 六种数据范围 |
| 菜单管理 | 树形 CRUD / 按钮权限 / 登录菜单树 / 一键生成 | 支持菜单类型(目录/菜单/按钮)、外链、iframe、缓存 |
| 机构管理 | 树形 CRUD / 数据权限过滤 / 级联删除保护 | 部门树天然支持多级嵌套 |
| 职位管理 | CRUD / 在职人员查询 | 职位与用户通过 SysUserExtOrg 多对多关联 |
| 权限管理 | 角色-菜单、用户-角色、角色-机构 多对多关联 | 完整 RBAC 模型 |
| 缓存管理 | Redis 缓存 / 按前缀删除/查询 / 用户权限与数据范围缓存 | 自动缓存失效 |
| 数据权限 | SqlSugarFilter / EntityBaseData / 机构级-个人级隔离 | AOP 切面自动注入过滤条件 |
| 多租户 | ITenantEntity / TenantAttribute | 租户数据天然隔离 |
| 种子数据 | 用户/角色/菜单/机构/职位 预设数据 | 一键初始化数据库表结构与业务数据 |
| 国际化 | i18n 多语言资源文件(zh-CN / en-US / ja-JP / fr-FR / vi-VN / zh-HK / zh-TW) | 中文错误提示开箱即用 |
架构总览
┌──────────────────────────────────────────────────────────────────────────┐
│ Client (Web / Mobile / Third-Party) │
└────────────────────────────────┬─────────────────────────────────────────┘
│ HTTP (REST)
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ RuoVea.DynamicWebApi (Auto API Mapping) │
│ /openapi/api/SysUser/GetPagesAsync ... │
└────────────────────────────────┬─────────────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ SysUserService │ │ SysRoleService │ │ SysMenuService │
│ 依赖: │ │ 依赖: │ │ 依赖: │
│ IPasswordServer│ │ SysRoleOrgSvc │ │ SysRoleMenuSvc │
│ ICurrentUser │ │ SysRoleMenuSvc │ │ SysUserRoleSvc │
│ SysOrgService │ │ SysOrgService │ │ SysCacheService│
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────────┐
│ SqlSugar ORM (SugarRepository<T>) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ SqlSugar │ │ SqlSugar │ │ SqlSugar │ │
│ │ Filter │ │ Filter │ │ Filter │ │
│ │ (数据权限) │ │ (数据权限) │ │ (数据权限) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└────────────────────────────────┬─────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ MySql │ SqlServer │ PostgreSQL │ SQLite │ Oracle │ ... │
└──────────────────────────────────────────────────────────────────────────┘
安装
NuGet 包管理器
# .NET 8.0
dotnet add package RuoVea.OmiApi.SystemApp --version 8.0.2.21
# .NET 10.0
dotnet add package RuoVea.OmiApi.SystemApp --version 10.0.0.4
或通过 Visual Studio 包管理器控制台:
# .NET 8.0
Install-Package RuoVea.OmiApi.SystemApp -Version 8.0.2.21
# .NET 10.0
Install-Package RuoVea.OmiApi.SystemApp -Version 10.0.0.4
依赖项
| 包名 | 用途 |
|---|---|
| RuoVea.ExFilter | 异常过滤器与请求拦截 |
| RuoVea.ExJwtBearer | JWT Bearer 认证配置 |
| RuoVea.ExPws | 密码加密服务 (IPasswordServer) |
| RuoVea.OmiApi.Config | 数据库初始化配置 (DbInitConfig) |
| RuoVea.DynamicWebApi | 类库自动映射为 REST API 控制器 |
| RuoVea.ExSugar | SqlSugar 数据库连接配置 |
30 秒快速开始
1. 配置文件 (appsettings.json)
{
// <summary>JWT 认证配置</summary>
"Jwt": {
"ValidateIssuerSigningKey": true, // 是否验证密钥,默认 true
"IssuerSigningKey": "3c1cbc3f546eda35168c3aa3cb91780fbe703f0996c6d123ea96dc85c70bbc0a",
"ValidateIssuer": true,
"ValidIssuer": "RuoVea.OmiApi",
"ValidateAudience": true,
"ValidAudience": "omiApiAudience",
"ValidateLifetime": true,
"ExpiredTime": 1440, // 过期时间(分钟),默认 1440 (24h)
"ClockSkew": 5 // 过期时间容错值(秒)
},
// <summary>数据库连接配置(第一个为默认库)</summary>
"ConnectionConfigs": [
{
"DbType": "Sqlite",
"ConnectionString": "DataSource=./ruovea.db"
}
],
// <summary>Swagger API 版本配置</summary>
"Swagger": {
"ApiVersions": [
{
"Title": "系统应用",
"Version": "system"
}
]
},
// <summary>数据库初始化与种子数据配置</summary>
"DbInitConfig": {
"InitTable": true, // 是否自动建表
"InitSeedData": true // 是否初始化种子数据
},
// <summary>密码加密配置</summary>
"PasswordConfig": {
"DefaultPassword": "123qwe",
"EncryptTimes": 3
}
}
2. Program.cs
// <summary>构建 Web 应用并注入 SystemApp 服务</summary>
var builder = WebApplication.CreateBuilder(args);
// <summary>1. DynamicWebApi:自动将 Service 类映射为 API 控制器</summary>
builder.Services.AddDynamicWebApi(options =>
{
options.DefaultApiPrefix = "/openapi/api";
options.RemoveControllerPostfixes = new List<string> { "AppService", "Service" };
options.RemovePrefix = new List<string> { "get", "post" };
});
// <summary>2. 注入用户/角色/菜单/机构/职位/权限全套服务</summary>
builder.Services.AddOmiSystemSetup();
// <summary>3. HttpContext 用户上下文</summary>
builder.Services.AddHttpContextSetup<AspNetUser>();
// <summary>4. SqlSugar 数据库连接 + 数据权限过滤器</summary>
builder.Services.AddSqlSugarSetup(moreFilter: (provider) =>
{
SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider);
});
// <summary>5. 初始化数据库表结构和种子数据(isWeb: true=Web菜单, false=API菜单)</summary>
builder.Services.AddSystemInitSetup(isWeb: false);
// <summary>6. 请求拦截器(操作日志、参数验证、统一结果、异常处理)</summary>
builder.Services
.RequestActionSetup()
.ResultSetup()
.ExceptionSetup();
// <summary>7. Swagger 文档</summary>
builder.Services.AddSwaggerSetup();
// <summary>8. 跨域</summary>
builder.Services.AddCors(option =>
{
option.AddDefaultPolicy(b =>
b.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
var app = builder.Build();
// <summary>中间件管道</summary>
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseSwaggerSetup();
app.MapControllers();
app.Run();
3. 访问 Swagger
启动项目后,访问 Swagger UI,选择 "系统应用 (system)" 分组即可查看全部 API:
https://localhost:5001/swagger/index.html
核心场景
场景一:用户 CRUD 与生命周期管理
Client SysUserService Database
│ │ │
│ POST /openapi/api/SysUser/AddUser │ │
│────────────────────────────────────────>│ │
│ │ IPasswordServer.Encrypt(pwd) │
│ │ SugarRepository<SysUser> │
│ │──────────────────────────────>│
│ │ INSERT INTO SysUser │
│ │<──────────────────────────────│
│ { userId, account, ... } │ │
│<────────────────────────────────────────│ │
│ │ │
│ PUT /openapi/api/SysUser/ResetPwd │ │
│────────────────────────────────────────>│ │
│ │ 验证权限 (SupAdmin 绕过) │
│ │ IPasswordServer.Encrypt(new) │
│ │ UPDATE SysUser SET Password │
│ │──────────────────────────────>│
│ 200 OK │ │
│<────────────────────────────────────────│ │
创建用户
// <summary>异步创建用户示例</summary>
public async Task<long> CreateUserAsync(SysUserService userService)
{
var input = new AddUserInput
{
Account = "zhangsan",
Password = "Abc@123456",
NickName = "张三",
RealName = "张三",
Email = "zhangsan@example.com",
Phone = "13800138000",
OrgId = 1,
PosId = 1
};
return await userService.AddUser(input);
}
// <summary>同步调用示例(❗仅限控制台/非 Web 上下文使用)</summary>
public long CreateUserSync(SysUserService userService)
{
var input = new AddUserInput
{
Account = "zhangsan",
Password = "Abc@123456",
NickName = "张三",
RealName = "张三",
Email = "zhangsan@example.com",
Phone = "13800138000",
OrgId = 1,
PosId = 1
};
// ❗ 同步调用会阻塞线程,生产环境请使用 async/await
return userService.AddUser(input).GetAwaiter().GetResult();
}
修改密码
// <summary>异步修改当前用户密码</summary>
public async Task<bool> ChangePasswordAsync(SysUserService userService)
{
var input = new ChangePwdInput
{
OldPassword = "Abc@123456",
NewPassword = "Xyz@654321"
};
// <summary>内部验证旧密码 + 新密码加密 + 更新数据库</summary>
return await userService.ChangePwd(input);
}
// <summary>同步修改密码(⚠️ 同步方法不支持异步上下文传播)</summary>
public bool ChangePasswordSync(SysUserService userService)
{
var input = new ChangePwdInput
{
OldPassword = "Abc@123456",
NewPassword = "Xyz@654321"
};
return userService.ChangePwd(input).GetAwaiter().GetResult();
}
解除登录锁定
// <summary>管理员异步解除指定用户的登录锁定</summary>
public async Task<bool> UnlockUserAsync(SysUserService userService, long userId)
{
var input = new UnlockLoginInput { UserId = userId };
return await userService.UnlockLogin(input);
}
场景二:角色授权与数据范围隔离
Client SysRoleService Database
│ │ │
│ POST .../SysRole/GrantMenu │ │
│ { roleId, menuIdList } │ │
│────────────────────────────────────>│ │
│ │ DELETE + INSERT │
│ │ SysRoleMenu (批量替换) │
│ │──────────────────────────────>│
│ │ SysCacheService.Remove │
│ │ (清除角色权限缓存) │
│ 200 OK │ │
│<────────────────────────────────────│ │
│ │ │
│ POST .../SysRole/GrantDataScope │ │
│ { roleId, scope, orgIdList } │ │
│────────────────────────────────────>│ │
│ │ UPDATE SysRole.DataScope │
│ │ DELETE + INSERT SysRoleOrg │
│ │──────────────────────────────>│
│ 200 OK │ │
│<────────────────────────────────────│ │
创建角色并授权菜单
// <summary>异步创建角色并授权菜单</summary>
public async Task<long> CreateRoleWithMenusAsync(
SysRoleService roleService, SysMenuService menuService)
{
// <summary>创建角色</summary>
var addRoleInput = new AddRoleInput
{
Name = "业务主管",
Code = "biz_manager",
Sort = 100,
DataScope = DataScopeType.Custom,
Remark = "业务部门主管角色"
};
long roleId = await roleService.AddRole(addRoleInput);
// <summary>授权菜单</summary>
var grantInput = new RoleMenuInput
{
RoleId = roleId,
MenuIdList = new List<long> { 1, 2, 3, 4, 5 }
};
await roleService.GrantMenu(grantInput);
return roleId;
}
授权数据范围
// <summary>异步授权角色数据范围(自定义机构列表)</summary>
public async Task<bool> GrantDataScopeAsync(SysRoleService roleService, long roleId)
{
var input = new RoleOrgInput
{
RoleId = roleId,
DataScope = DataScopeType.Custom,
OrgIdList = new List<long> { 101, 102, 103 }
};
// <summary>Custom 模式下写入 SysRoleOrg 表,用于查询时过滤</summary>
await roleService.GrantDataScope(input);
return true;
}
// <summary>同步授权数据范围</summary>
public bool GrantDataScopeSync(SysRoleService roleService, long roleId)
{
var input = new RoleOrgInput
{
RoleId = roleId,
DataScope = DataScopeType.Custom,
OrgIdList = new List<long> { 101, 102, 103 }
};
roleService.GrantDataScope(input).GetAwaiter().GetResult();
return true;
}
场景三:菜单树与按钮权限
┌─────────────────────────────────────────────────────────┐
│ SysMenu 树形结构 │
│ │
│ 系统管理 (目录) Type=Dir │
│ ├─ 用户管理 (菜单) Type=Menu │
│ │ ├─ 新增用户 (按钮) Type=Btn Permission=user:add │
│ │ ├─ 编辑用户 (按钮) Type=Btn Permission=user:edit │
│ │ └─ 删除用户 (按钮) Type=Btn Permission=user:delete │
│ ├─ 角色管理 (菜单) Type=Menu │
│ │ ├─ 新增角色 (按钮) Type=Btn │
│ │ └─ 授权菜单 (按钮) Type=Btn │
│ └─ 菜单管理 (菜单) Type=Menu │
│ │
└─────────────────────────────────────────────────────────┘
获取登录菜单树
// <summary>异步获取当前登录用户的菜单树(含按钮权限)</summary>
public async Task<List<MenuTreeOutput>> GetLoginMenuAsync(SysMenuService menuService)
{
// <summary>根据用户角色过滤,返回树形菜单,按钮权限挂载在对应菜单下</summary>
var menuTree = await menuService.GetLoginMenuTree();
return menuTree;
}
// <summary>同步获取登录菜单树</summary>
public List<MenuTreeOutput> GetLoginMenuSync(SysMenuService menuService)
{
return menuService.GetLoginMenuTree().GetAwaiter().GetResult();
}
一键创建含增删改查按钮的菜单
// <summary>异步一键生成菜单及标准 CRUD 按钮权限</summary>
public async Task<bool> CreateMenuWithPermissionAsync(SysMenuService menuService)
{
var input = new AddMenuInput
{
Pid = 1,
Type = MenuType.Menu,
Name = "UserManagement",
Path = "/system/user",
Component = "system/user/index",
Title = "用户管理",
Icon = "user",
Sort = 1,
// <summary>勾选将自动生成增/删/改/查四个按钮子菜单</summary>
GenerateCRUD = true
};
await menuService.AddMenu(input);
return true;
}
// <summary>同步一键创建菜单</summary>
public bool CreateMenuWithPermissionSync(SysMenuService menuService)
{
var input = new AddMenuInput
{
Pid = 1,
Type = MenuType.Menu,
Name = "UserManagement",
Path = "/system/user",
Component = "system/user/index",
Title = "用户管理",
Icon = "user",
Sort = 1,
GenerateCRUD = true
};
menuService.AddMenu(input).GetAwaiter().GetResult();
return true;
}
获取按钮权限集合
// <summary>异步获取当前用户的按钮权限标识集合</summary>
/// <remarks>返回如 ["user:add", "user:edit", "user:delete", 前端据此控制按钮显隐</remarks>
public async Task<List<string>> GetButtonPermissionsAsync(SysMenuService menuService)
{
return await menuService.GetOwnBtnPermList();
}
场景四:机构树与职位管理
机构树 职位
┌─────────────────┐ ┌──────────┐
│ 集团总公司 │ │ 总经理 │ SysPosition
│ ├─ 技术部 │ │ 技术总监 │
│ │ ├─ 前端组 │ │ 前端组长 │
│ │ └─ 后端组 │ │ 后端组长 │
│ ├─ 市场部 │ │ 市场经理 │
│ └─ 财务部 │ │ 财务经理 │
└─────────────────┘ └──────────┘
│ │
└──────────┬───────────────┘
▼
SysUserExtOrg (用户-机构-职位 关联表)
┌────────────────────────────────┐
│ UserId │ OrgId │ PosId │Default│
│ 1 │ 101 │ 1 │ Y │
│ 1 │ 102 │ 5 │ N │ ← 用户可兼职多机构多职位
│ 2 │ 101 │ 3 │ Y │
└────────────────────────────────┘
创建机构
// <summary>异步创建组织机构</summary>
public async Task<long> CreateOrgAsync(SysOrgService orgService)
{
var input = new AddOrgInput
{
Pid = 1,
Name = "研发中心",
Code = "RD-CENTER",
Contacts = "李四",
Tel = "010-88888888",
Sort = 10
};
return await orgService.AddOrg(input);
}
删除机构(级联保护)
// <summary>异步删除机构 — 系统自动校验子机构与关联用户</summary>
/// <remarks>
/// 删除前校验链:
/// 1. i18n.organization_has_users — 当前机构下有用户时禁止删除
/// 2. i18n.subordinate_org_has_users — 下级机构有关联用户时禁止删除
/// 3. 仅当机构及所有下级均无用户时方可删除
/// </remarks>
public async Task<bool> DeleteOrgSafeAsync(SysOrgService orgService, long orgId)
{
try
{
var input = new DeleteOrgInput { Id = orgId };
await orgService.DeleteOrg(input);
return true;
}
catch (Exception ex)
{
// <summary>根据 i18n 错误码提示具体失败原因</summary>
Console.WriteLine($"删除失败: {ex.Message}");
return false;
}
}
场景五:缓存管理与数据权限过滤
请求到达
│
▼
┌──────────────────────┐
│ SqlSugarFilter │ 数据权限过滤器(AOP 切面自动注入)
│ .SetCustomEntityFilter() │
└──────────┬───────────┘
│
▼
┌──────────────────────┐ ┌──────────────────────┐
│ 用户数据范围缓存 │ │ 数据范围类型 │
│ Redis Key: │ │ All → 全部 │
│ user:datascope:{id} │ │ Custom → 自定义机构 │
│ │ │ Dept → 本部门 │
│ SysCacheService │ │ DeptWithChildren │
│ ├─ Remove(key) │ │ → 本部门及以下│
│ ├─ RemoveByPrefix │ │ Personal → 仅本人 │
│ ├─ GetKeysByPrefix │ │ Self → 个人 │
│ └─ GetValue │ └──────────────────────┘
└──────────────────────┘
缓存操作
// <summary>异步根据键名前缀删除缓存</summary>
/// <remarks>
/// ❗ RemoveByPrefixKey 依赖 Redis 的 SCAN 命令遍历键,键量极大时可能耗时。
/// 建议在生产环境中控制前缀粒度。
/// </remarks>
public async Task RemoveCacheByPrefixAsync(SysCacheService cacheService, string prefix)
{
await cacheService.RemoveByPrefixKey(new BaseIdInput { Value = prefix });
}
// <summary>异步获取缓存值</summary>
public async Task<string> GetCacheValueAsync(SysCacheService cacheService, string key)
{
var input = new BaseIdInput { Value = key };
return await cacheService.GetValue(input);
}
// <summary>同步清空所有缓存(⚠️ 生产环境慎用)</summary>
public void ClearAllCache(SysCacheService cacheService)
{
// ⚠️ 清空全部缓存可能引发瞬时性能尖峰,建议仅在维护窗口执行
cacheService.Clear().GetAwaiter().GetResult();
}
数据权限过滤原理
// <summary>数据权限过滤在 SqlSugar 查询中自动生效</summary>
/// <remarks>
/// SqlSugarFilter.SetCustomEntityFilter 在每次查询前动态注入 WHERE 条件:
/// - SuperAdmin 角色: 不添加任何过滤(查看全部数据)
/// - All 数据范围: 不添加过滤
/// - Dept 数据范围: 添加 WHERE OrgId = @CurrentOrgId
/// - DeptWithChildren: 添加 WHERE OrgId IN (当前机构及所有子机构ID)
/// - Personal: 添加 WHERE CreateUserId = @CurrentUserId
///
/// 无需在业务代码中手动拼接过滤条件。
/// </remarks>
public async Task<List<SysUser>> QueryWithAutoFilterAsync(SugarRepository<SysUser> repo)
{
// <summary>查询自动带上了当前用户的数据权限过滤条件</summary>
return await repo.GetListAsync();
}
配置选项详解
JWT 配置 (Jwt)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ValidateIssuerSigningKey |
bool | true |
是否验证签名密钥 |
IssuerSigningKey |
string | — | HMAC-SHA256 签名密钥,建议长度 >= 32 |
ValidateIssuer |
bool | true |
是否验证签发方 (iss) |
ValidIssuer |
string | — | 合法的签发方标识 |
ValidateAudience |
bool | true |
是否验证接收方 (aud) |
ValidAudience |
string | — | 合法的接收方标识 |
ValidateLifetime |
bool | true |
是否验证 Token 过期时间 |
ExpiredTime |
long | 1440 |
Token 有效期(分钟),默认 24 小时 |
ClockSkew |
long | 5 |
时钟偏移容错(秒),用于服务端时间不一致场景 |
数据库连接配置 (ConnectionConfigs)
| 配置项 | 类型 | 说明 |
|---|---|---|
DbType |
string | 数据库类型:MySql、SqlServer、Sqlite、Oracle、PostgreSQL 等 |
ConnectionString |
string | 数据库连接字符串 |
ConfigId |
string | 多库标识(不填即默认库) |
EnableUnderLine |
bool | 启用驼峰转下划线(表/列名) |
IsDeleteFilter |
bool | 软删除过滤(实体需继承 IDeletedEntity) |
IsTenantIdFilter |
bool | 多租户过滤(实体需继承 ITenantIdFilter) |
CommandTimeOut |
int | SQL 命令超时(秒),默认 30 |
数据库初始化配置 (DbInitConfig)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
InitTable |
bool | true |
启动时自动检测并创建不存在的数据库表(CodeFirst) |
InitSeedData |
bool | true |
初始化种子数据(管理员/基础角色/菜单等) |
密码加密配置 (PasswordConfig)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DefaultPassword |
string | 123qwe |
种子数据用户及重置密码时的默认密码 |
EncryptTimes |
int | 3 |
密码加密迭代次数 |
Swagger 版本配置 (Swagger.ApiVersions)
| 配置项 | 类型 | 说明 |
|---|---|---|
Title |
string | Swagger UI 中显示的 API 分组标题 |
Version |
string | API 版本标识,GroupName 固定为 system |
API 接口速览
默认路由前缀:
/openapi/api| Swagger GroupName:system
SysUserService — 用户管理
| HTTP | 路由 | 方法 | 说明 |
|---|---|---|---|
GET |
/SysUser/GetUserById |
GetUserById |
根据用户 Id 获取用户 |
GET |
/SysUser/GetDataAsync |
GetDataAsync |
根据主键查询 |
GET |
/SysUser/GetAllAsync |
GetAllAsync |
获取全部用户简要列表(仅 ID、账号、姓名) |
GET |
/SysUser/GetPagesAsync |
GetPagesAsync |
获取用户分页列表 |
POST |
/SysUser/AddUser |
AddUser |
增加用户 |
GET |
/SysUser/UserList |
UserList |
获取用户列表 |
PUT |
/SysUser/UpdateUser |
UpdateUser |
更新用户 |
DELETE |
/SysUser/DeleteUser |
DeleteUser |
删除用户 |
GET |
/SysUser/GetBaseInfo |
GetBaseInfo |
查看用户基本信息 |
PUT |
/SysUser/UpdateBaseInfo |
UpdateBaseInfo |
更新用户基本信息 |
PUT |
/SysUser/SetStatus |
SetStatus |
设置用户状态 (启用/禁用) |
POST |
/SysUser/GrantRole |
GrantRole |
授权用户角色 |
PUT |
/SysUser/ChangePwd |
ChangePwd |
修改用户密码 |
PUT |
/SysUser/ResetPwd |
ResetPwd |
重置用户密码 |
PUT |
/SysUser/UnlockLogin |
UnlockLogin |
解除登录锁定 |
GET |
/SysUser/GetOwnRoleList |
GetOwnRoleList |
获取用户拥有角色集合 |
GET |
/SysUser/GetOwnExtOrgList |
GetOwnExtOrgList |
获取用户扩展机构集合 |
GET |
/SysUser/GetRoleUserListByRoleId |
GetRoleUserListByRoleId |
获取角色用户相关信息 |
SysRoleService — 角色管理
| HTTP | 路由 | 方法 | 说明 |
|---|---|---|---|
GET |
/SysRole/GetPagesAsync |
GetPagesAsync |
获取角色分页列表 |
GET |
/SysRole/GetDataAsync |
GetDataAsync |
根据主键查询 |
GET |
/SysRole/GetList |
GetList |
获取角色列表 |
POST |
/SysRole/AddRole |
AddRole |
增加角色 |
PUT |
/SysRole/UpdateRole |
UpdateRole |
更新角色 |
DELETE |
/SysRole/DeleteRole |
DeleteRole |
删除角色 |
POST |
/SysRole/GrantMenu |
GrantMenu |
授权角色菜单 |
POST |
/SysRole/GrantDataScope |
GrantDataScope |
授权角色数据范围 |
GET |
/SysRole/GetOwnMenuList |
GetOwnMenuList |
根据角色 Id 获取菜单 Id 集合 |
GET |
/SysRole/GetOwnOrgList |
GetOwnOrgList |
根据角色 Id 获取机构 Id 集合 |
PUT |
/SysRole/SetStatus |
SetStatus |
设置角色状态 (启用/禁用) |
SysMenuService — 菜单管理
| HTTP | 路由 | 方法 | 说明 |
|---|---|---|---|
GET |
/SysMenu/GetLoginMenuTree |
GetLoginMenuTree |
获取登录菜单树 |
GET |
/SysMenu/GetDataAsync |
GetDataAsync |
根据主键查询 |
GET |
/SysMenu/GetList |
GetList |
获取菜单列表 |
POST |
/SysMenu/AddMenu |
AddMenu |
增加菜单 |
POST |
/SysMenu/AddMenus |
AddMenus |
增加菜单和权限(一键生成 CRUD) |
PUT |
/SysMenu/UpdateMenu |
UpdateMenu |
更新菜单 |
DELETE |
/SysMenu/DeleteMenu |
DeleteMenu |
删除菜单 |
GET |
/SysMenu/GetOwnBtnPermList |
GetOwnBtnPermList |
获取按钮权限集合 |
SysOrgService — 机构管理
| HTTP | 路由 | 方法 | 说明 |
|---|---|---|---|
GET |
/SysOrg/GetList |
GetList |
获取机构列表 |
GET |
/SysOrg/GetDataAsync |
GetDataAsync |
根据主键查询 |
POST |
/SysOrg/AddOrg |
AddOrg |
增加机构 |
PUT |
/SysOrg/UpdateOrg |
UpdateOrg |
更新机构 |
DELETE |
/SysOrg/DeleteOrg |
DeleteOrg |
删除机构 |
SysPosService — 职位管理
| HTTP | 路由 | 方法 | 说明 |
|---|---|---|---|
GET |
/SysPos/GetList |
GetList |
获取职位列表 |
GET |
/SysPos/GetDataAsync |
GetDataAsync |
根据主键查询 |
POST |
/SysPos/AddPos |
AddPos |
增加职位 |
PUT |
/SysPos/UpdatePos |
UpdatePos |
更新职位 |
DELETE |
/SysPos/DeletePos |
DeletePos |
删除职位 |
SysCacheService — 缓存管理
| HTTP | 路由 | 方法 | 说明 |
|---|---|---|---|
DELETE |
/SysCache/Remove |
Remove |
删除缓存 |
DELETE |
/SysCache/Clear |
Clear |
清空所有缓存 |
DELETE |
/SysCache/RemoveByPrefixKey |
RemoveByPrefixKey |
根据键名前缀删除缓存 |
GET |
/SysCache/GetKeysByPrefixKey |
GetKeysByPrefixKey |
根据键名前缀获取键名集合 |
GET |
/SysCache/GetValue |
GetValue |
获取缓存值 |
错误处理与日志
错误码表
| 错误码 | 说明 | 触发场景 |
|---|---|---|
ErrorEnum.D4000 |
请求参数验证失败 | 入参不合法(必填项为空、格式错误等) |
i18n.account_exists |
账号已存在 | 创建用户时账号重复 |
i18n.account_not_exists |
账号不存在 | 登录/查询不存在的账号 |
i18n.prohibit_delete_super_admin |
禁止删除超级管理员 | 尝试删除 SupAdmin 角色用户 |
i18n.illegal_operation_self |
非法操作自身 | 用户对自己执行禁止操作 |
i18n.prohibit_modify_self_status |
禁止修改自身状态 | 用户尝试启用/禁用自己 |
i18n.prohibit_modify_super_admin_status |
禁止修改超级管理员状态 | 操作用户尝试修改管理员状态 |
i18n.dict_status_error |
字典状态异常 | 依赖字典项已禁用或不存在 |
i18n.new_password_same_as_old |
新密码与旧密码相同 | 修改密码时新旧密码一致 |
i18n.password_error |
密码错误 | 修改密码时旧密码验证失败 |
i18n.data_exists |
数据已存在 | 唯一约束冲突(名称/编码重复) |
i18n.record_not_exists |
记录不存在 | 更新/删除不存在的记录 |
i18n.route_name_duplicate |
路由名称重复 | 创建菜单时路由名冲突 |
i18n.parent_node_cannot_be_button |
父节点不能是按钮 | 将菜单/目录挂载到按钮类型节点下 |
i18n.permission_id_format_empty |
权限标识不能为空 | 按钮类型菜单未填写权限标识 |
i18n.permission_id_format_error |
权限标识格式错误 | 按钮权限标识不符合规范 |
i18n.prohibit_same_node_as_parent |
禁止选择自己作为父节点 | 移动节点到自身或子节点下 |
i18n.prohibit_add_root_organization |
禁止添加根机构 | 创建一级机构(根据业务策略) |
i18n.organization_exists |
机构已存在 | 创建机构时名称/编码重复 |
i18n.no_permission_to_operate_organization |
无权限操作机构 | 非管理员操作受保护机构 |
i18n.current_org_id_cannot_match_parent |
当前机构 Id 不能与父级相同 | 更新机构时设置了循环引用 |
i18n.organization_has_users |
机构下存在用户 | 删除有用户的机构 |
i18n.affiliated_org_has_users |
附属机构下存在用户 | 删除机构时其扩展关联有用户 |
i18n.subordinate_org_has_users |
下级机构存在用户 | 删除机构时其子机构有关联用户 |
i18n.position_exists |
职位已存在 | 创建职位时编码重复 |
i18n.position_not_exists |
职位不存在 | 更新/删除不存在的职位 |
i18n.no_permission_to_modify_position |
无权限修改职位 | 非管理员操作受保护职位 |
i18n.position_has_users |
职位下存在用户 | 删除有用户关联的职位 |
i18n.prohibit_delete_admin |
禁止删除管理员 | 尝试删除管理员角色 |
i18n.role_has_accounts |
角色下存在账号 | 删除有用户关联的角色 |
i18n.no_permission |
无权限 | 用户缺少操作所需权限 |
i18n.org_select |
请选择机构 | 创建用户/配置时未选择机构 |
i18n.position_select |
请选择职位 | 创建用户时未选择职位 |
异常处理示例
// <summary>异步方式 — 带完整错误码识别与日志记录的异常处理</summary>
public async Task<object> CreateUserWithErrorHandlingAsync(SysUserService userService, ILogger logger)
{
try
{
var input = new AddUserInput
{
Account = "testuser",
Password = "Test@123",
NickName = "测试用户",
OrgId = 1,
PosId = 1
};
long userId = await userService.AddUser(input);
logger.LogInformation("用户创建成功: {UserId}, {Account}", userId, input.Account);
return new { Success = true, UserId = userId };
}
catch (Exception ex) when (ex.Message.Contains("i18n.account_exists"))
{
// <summary>账号已存在的业务异常</summary>
logger.LogWarning("创建用户失败 — 账号已存在: {Message}", ex.Message);
return new { Success = false, Error = "ACCOUNT_EXISTS", Message = "账号已存在,请更换账号名" };
}
catch (Exception ex) when (ex.Message.Contains("i18n.org_select"))
{
// <summary>未选择机构的业务异常</summary>
logger.LogWarning("创建用户失败 — 未选择机构: {Message}", ex.Message);
return new { Success = false, Error = "ORG_REQUIRED", Message = "请选择所属机构" };
}
catch (Exception ex)
{
// <summary>未预期的系统异常</summary>
logger.LogError(ex, "创建用户时发生系统异常");
return new { Success = false, Error = "SYSTEM_ERROR", Message = "系统异常,请稍后重试" };
}
}
// <summary>同步方式 — 带错误处理的异常捕获(⚠️ 仅在非 Web 场景使用)</summary>
public object CreateUserWithErrorHandlingSync(SysUserService userService)
{
try
{
var input = new AddUserInput
{
Account = "testuser",
Password = "Test@123",
NickName = "测试用户",
OrgId = 1,
PosId = 1
};
long userId = userService.AddUser(input).GetAwaiter().GetResult();
return new { Success = true, UserId = userId };
}
⚠️ // 同步等待会丢失异步上下文中的 ICurrentUser 等 Scoped 依赖信息
catch (Exception ex)
{
return new { Success = false, Error = ex.Message };
}
}
版本迁移指南
从早期版本迁移至 8.0.x / 10.0.x
重大变更:包结构从单 TFM 变更为双 TFM 支持(net8.0 / net10.0)。
升级 NuGet 引用
<PackageReference Include="RuoVea.OmiApi.SystemApp" Version="7.x.x" /> <PackageReference Include="RuoVea.OmiApi.SystemApp" Version="8.0.2.21" /> <PackageReference Include="RuoVea.OmiApi.SystemApp" Version="10.0.0.4" />AddSystemInitSetup参数变更// <summary>旧版本 — 无参数</summary> builder.Services.AddSystemInitSetup(); // <summary>新版本 — 新增 isWeb 参数控制种子菜单类型</summary> // isWeb = true → 使用 Web 菜单种子数据(适合前后端分离项目) // isWeb = false → 使用 API 菜单种子数据(默认,适合纯 API 项目) builder.Services.AddSystemInitSetup(isWeb: false);AddSqlSugarSetup数据权限过滤器// <summary>新版本中必须显式注册数据权限过滤器</summary> // 否则多租户和数据范围过滤不会生效 builder.Services.AddSqlSugarSetup(moreFilter: (provider) => { SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider); });Swagger 版本配置必须显式声明
// <summary>appsettings.json 中必须添加 Swagger 节点</summary> { "Swagger": { "ApiVersions": [ { "Title": "系统应用", "Version": "system" } ] } }
常见问题
Q: 启动后 Swagger 中看不到 system 分组?
原因:未配置
Swagger.ApiVersions或AddSwaggerSetup()未调用。
// <summary>appsettings.json 中添加 Swagger 版本配置</summary>
{
"Swagger": {
"ApiVersions": [
{ "Title": "系统应用", "Version": "system" }
]
}
}
Q: 数据库表未自动创建?
原因:
DbInitConfig.InitTable未设为true,或AddSystemInitSetup()未调用。
// <summary>确保 DbInitConfig 配置正确</summary>
{
"DbInitConfig": {
"InitTable": true, // 自动建表
"InitSeedData": true // 初始化种子数据
}
}
⚠️ 注意:AddOmiSystemSetup() 必须在 AddSystemInitSetup() 之前调用,否则 DbInitConfig 无法正确传递。
Q: 如何切换数据库类型?
修改
ConnectionConfigs[0].DbType和对应的ConnectionString:
// <summary>MySQL 示例</summary>
{
"ConnectionConfigs": [
{
"DbType": "MySql",
"ConnectionString": "Server=localhost;Database=ruovea;Uid=root;Pwd=123456;"
}
]
}
// <summary>PostgreSQL 示例</summary>
{
"ConnectionConfigs": [
{
"DbType": "PostgreSQL",
"ConnectionString": "Host=localhost;Database=ruovea;Username=postgres;Password=123456"
}
]
}
Q: 超级管理员密码是什么?如何修改?
种子数据中超级管理员账号为
superAdmin,默认密码由PasswordConfig.DefaultPassword控制(默认为123qwe)。修改方式:
// <summary>在 appsettings.json 中修改默认密码</summary>
{
"PasswordConfig": {
"DefaultPassword": "YourSecurePassword@2024",
"EncryptTimes": 5
}
}
修改后需重新初始化种子数据(删除已有数据库文件或执行清表操作后重启)。
Q: 数据权限过滤不生效?
❗ 请检查以下三项:
// <summary>1. 确保 AddSqlSugarSetup 中注册了 moreFilter</summary>
builder.Services.AddSqlSugarSetup(moreFilter: (provider) =>
{
SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider); // ← 必须
});
// <summary>2. 实体须继承 EntityBase(内含 CreateUserId/OrgId 等过滤字段)</summary>
// <summary>3. 当前登录用户的角色 DataScope 配置正确(非 All)</summary>
Q: 缓存清除后权限未立即生效?
角色/菜单授权变更后,组件内部会自动调用
SysCacheService.Remove清除相关缓存。如果使用外部 Redis 管理工具手动清除,请删除user:permission:*和user:datascope:*前缀的键。
Q: AddOmiSystemSetup 的三个重载如何选择?
| 重载 | 适用场景 |
|---|---|
AddOmiSystemSetup() |
使用 appsettings.json 中配置(默认推荐) |
AddOmiSystemSetup(IConfiguration config) |
动态/内存配置、多环境配置 |
AddOmiSystemSetup(Action<DbInitConfig> config) |
代码内构建配置、复杂初始化逻辑 |
Q: ⚠️ 同步调用 .GetAwaiter().GetResult() 有什么风险?
在 ASP.NET Core 的同步上下文中调用异步方法并使用
.GetAwaiter().GetResult()可能导致死锁(deadlock),因为SynchronizationContext会尝试将后续代码封送回原线程,而该线程正在被阻塞。始终推荐在 Web 应用中使用 async/await。
| Product | Versions 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. |
-
net10.0
- RuoVea.DynamicWebApi (>= 10.0.0)
- RuoVea.ExFilter (>= 10.0.0.6)
- RuoVea.ExJwtBearer (>= 10.0.0.5)
- RuoVea.ExPws (>= 10.0.0.4)
- RuoVea.ExSugar (>= 10.0.0.8)
- RuoVea.OmiApi.Config (>= 10.0.0.4)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on RuoVea.OmiApi.SystemApp:
| Package | Downloads |
|---|---|
|
RuoVea.OmiSystem
角色用户菜单机构职位管理 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.0.6 | 45 | 7/7/2026 |
| 10.0.0.5 | 108 | 6/26/2026 |
| 10.0.0.4 | 113 | 6/24/2026 |
| 10.0.0.3 | 121 | 5/28/2026 |
| 10.0.0.2 | 110 | 5/28/2026 |
| 9.0.0.3 | 124 | 5/28/2026 |
| 9.0.0.2 | 123 | 5/28/2026 |
| 9.0.0.1 | 174 | 3/23/2026 |
| 8.0.2.23 | 40 | 7/7/2026 |
| 8.0.2.22 | 111 | 6/26/2026 |
| 8.0.2.21 | 136 | 6/24/2026 |
| 8.0.2.20 | 128 | 5/28/2026 |
| 8.0.1.19 | 120 | 5/28/2026 |
| 8.0.1.18 | 136 | 3/23/2026 |
| 7.0.2.20 | 124 | 5/28/2026 |
| 7.0.1.19 | 121 | 5/28/2026 |
| 7.0.1.18 | 132 | 3/23/2026 |
| 6.0.2.20 | 131 | 5/28/2026 |
| 6.0.2.19 | 126 | 5/28/2026 |
| 6.0.2.18 | 139 | 3/23/2026 |