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
                    
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.SystemApp" Version="10.0.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RuoVea.OmiApi.SystemApp" Version="10.0.0.6" />
                    
Directory.Packages.props
<PackageReference Include="RuoVea.OmiApi.SystemApp" />
                    
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.SystemApp --version 10.0.0.6
                    
#r "nuget: RuoVea.OmiApi.SystemApp, 10.0.0.6"
                    
#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.SystemApp@10.0.0.6
                    
#: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.SystemApp&version=10.0.0.6
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.OmiApi.SystemApp&version=10.0.0.6
                    
Install as a Cake Tool

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 数据库类型:MySqlSqlServerSqliteOraclePostgreSQL
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)。

  1. 升级 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" />   
    
  2. AddSystemInitSetup 参数变更

    // <summary>旧版本 — 无参数</summary>
    builder.Services.AddSystemInitSetup();
    
    // <summary>新版本 — 新增 isWeb 参数控制种子菜单类型</summary>
    // isWeb = true  → 使用 Web 菜单种子数据(适合前后端分离项目)
    // isWeb = false → 使用 API 菜单种子数据(默认,适合纯 API 项目)
    builder.Services.AddSystemInitSetup(isWeb: false);
    
  3. AddSqlSugarSetup 数据权限过滤器

    // <summary>新版本中必须显式注册数据权限过滤器</summary>
    // 否则多租户和数据范围过滤不会生效
    builder.Services.AddSqlSugarSetup(moreFilter: (provider) =>
    {
        SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider);
    });
    
  4. Swagger 版本配置必须显式声明

    // <summary>appsettings.json 中必须添加 Swagger 节点</summary>
    {
      "Swagger": {
        "ApiVersions": [
          { "Title": "系统应用", "Version": "system" }
        ]
      }
    }
    

常见问题

Q: 启动后 Swagger 中看不到 system 分组?

原因:未配置 Swagger.ApiVersionsAddSwaggerSetup() 未调用。

// <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 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.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
Loading failed