RuoVea.OmiApi.SystemApp 10.0.1.16

dotnet add package RuoVea.OmiApi.SystemApp --version 10.0.1.16
                    
NuGet\Install-Package RuoVea.OmiApi.SystemApp -Version 10.0.1.16
                    
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.1.16" />
                    
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.1.16" />
                    
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.1.16
                    
#r "nuget: RuoVea.OmiApi.SystemApp, 10.0.1.16"
                    
#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.1.16
                    
#: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.1.16
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.OmiApi.SystemApp&version=10.0.1.16
                    
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(全局 AOP)/ DataScopeService(按需调用)/ EntityBase 机构级 + 个人级隔离,支持全局自动过滤或 Manual 精确控制
多租户 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.3.16

# .NET 10.0
dotnet add package RuoVea.OmiApi.SystemApp --version 10.0.1.14

或通过 Visual Studio 包管理器控制台:

# .NET 8.0
Install-Package RuoVea.OmiApi.SystemApp -Version 8.0.3.16

# .NET 10.0
Install-Package RuoVea.OmiApi.SystemApp -Version 10.0.1.14

依赖项

包名 用途
RuoVea.ExFilter 异常过滤器与请求拦截
RuoVea.ExJwtBearer JWT Bearer 认证配置
RuoVea.ExPws 密码加密服务 (IPasswordServer)
RuoVea.ExConfig 全局配置与 AppSettings 读取
RuoVea.OmiApi.Config 数据库初始化配置 (DbInitConfig) 与系统参数表
RuoVea.DynamicWebApi 类库自动映射为 REST API 控制器
RuoVea.ExSugar SqlSugar 数据库连接配置

⚠️ 基座包互斥:RuoVea.OmiApi.SystemApp 与 RuoVea.OmiApi.User / RuoVea.OmiApi.UserRole / RuoVea.OmiApi.UserRoleMenu 属于同一套权限体系的四个互斥基座规格,业务系统只能引用其中一个。若编译时同时引用了其它基座包,build\RuoVea.OmiApi.SystemApp.targets 会抛出 RUV001 编译错误,请移除多余的 PackageReference 后重试。


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.SetOrgEntityFilter(builder.Services, provider);       // 数据范围过滤(机构/用户维度)
    SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider);    // 自定义业务过滤(IEntityFilter)
});

// <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(开启多租户后,另有一个 "tenant" 分组):

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
    {
        PasswordOld = "Abc@123456",
        PasswordNew = "Xyz@654321"
    };
    // <summary>内部验证旧密码 + 新密码加密 + 更新数据库</summary>
    return await userService.ChangePwd(input) > 0;
}

// <summary>同步修改密码(⚠️ 同步方法不支持异步上下文传播)</summary>
public bool ChangePasswordSync(SysUserService userService)
{
    var input = new ChangePwdInput
    {
        PasswordOld = "Abc@123456",
        PasswordNew = "Xyz@654321"
    };
    return userService.ChangePwd(input).GetAwaiter().GetResult() > 0;
}
解除登录锁定
// <summary>管理员异步解除指定用户的登录锁定</summary>
public async Task<bool> UnlockUserAsync(SysUserService userService, long userId)
{
    var input = new UnlockLoginInput { Id = userId };
    await userService.UnlockLogin(input);
    return true;
}

场景二:角色授权与数据范围隔离

 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.DEFINE,
        Remark = "业务部门主管角色"
    };
    long roleId = await roleService.AddRole(addRoleInput);

    // <summary>授权菜单</summary>
    var grantInput = new RoleMenuInput
    {
        Id = 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
    {
        Id = roleId,
        DataScope = (int)DataScopeType.DEFINE,
        OrgIdList = new List<long> { 101, 102, 103 }
    };
    // <summary>DEFINE 模式下写入 SysRoleOrg 表,用于查询时过滤</summary>
    await roleService.GrantDataScope(input);
    return true;
}

// <summary>同步授权数据范围</summary>
public bool GrantDataScopeSync(SysRoleService roleService, long roleId)
{
    var input = new RoleOrgInput
    {
        Id = roleId,
        DataScope = (int)DataScopeType.DEFINE,
        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<MenuOutput>> GetLoginMenuAsync(SysMenuService menuService)
{
    // <summary>根据用户角色过滤,返回树形菜单,按钮权限挂载在对应菜单下</summary>
    var menuTree = await menuService.GetLoginMenuTree();
    return menuTree;
}

// <summary>同步获取登录菜单树</summary>
public List<MenuOutput> 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>AddMenus 会自动生成"新增/编辑/删除"三个按钮子菜单</summary>
    };
    await menuService.AddMenus(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
    };
    menuService.AddMenus(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       → 全部          │
 │  sys_user_org:{userId}        │     │  Custom    → 自定义机构      │
 │  sys_role_maxDataScope:{userId}│     │  Dept      → 本部门         │
 │  sys_role_dataScope_orgIds:{userId}  │  DeptWithChildren          │
 │  sys_role_dataScope_userIds:{userId} │       → 本部门及以下        │
 │                               │     │  Personal  → 仅本人         │
 │  SysCacheService              │     │  Self      → 个人           │
 │  ├─ Remove(key)               │     └────────────────────────────┘
 │  ├─ RemoveByPrefixKey(prefix) │
 │  ├─ GetKeysByPrefixKey(prefix)│
 │  ├─ GetValue(key)             │
 │  └─ Clear()                   │
 └───────────────────────────────┘
缓存操作
// <summary>异步根据键名前缀删除缓存</summary>
/// <remarks>
/// ❗ RemoveByPrefixKey 依赖 Redis 的 SCAN 命令遍历键,键量极大时可能耗时。
///    建议在生产环境中控制前缀粒度。
/// </remarks>
public async Task RemoveCacheByPrefixAsync(SysCacheService cacheService, string prefix)
{
    await cacheService.RemoveByPrefixKey(prefix);
}

// <summary>异步获取缓存值</summary>
public async Task<object> GetCacheValueAsync(SysCacheService cacheService, string key)
{
    return await Task.FromResult(cacheService.GetValue(key));
}

// <summary>同步清空所有缓存(⚠️ 生产环境慎用)</summary>
public void ClearAllCache(SysCacheService cacheService)
{
    // ⚠️ 清空全部缓存可能引发瞬时性能尖峰,建议仅在维护窗口执行
    cacheService.Clear().GetAwaiter().GetResult();
}
数据权限过滤原理
// <summary>数据权限过滤在 SqlSugar 查询中自动生效</summary>
/// <remarks>
/// SqlSugarFilter.SetCustomEntityFilter 在每次查询前动态注入 WHERE 条件:
/// - SuperAdmin 角色: 不添加任何过滤(查看全部数据)
/// - All 数据范围: 不添加过滤
/// - Dept 数据范围: 添加 WHERE OrgId IN (用户有权限的机构ID集合)
/// - DeptWithChildren: 添加 WHERE OrgId IN (本部门及所有子机构ID)
/// - Define(自定义): 添加 WHERE OrgId IN (角色配置的自定义机构ID集合)
/// - Self(仅本人): 添加 WHERE CreateUserId = @CurrentUserId
///
/// 无需在业务代码中手动拼接过滤条件。
/// </remarks>
public async Task<List<SysUser>> QueryWithAutoFilterAsync(SugarRepository<SysUser> repo)
{
    // <summary>查询自动带上了当前用户的数据权限过滤条件</summary>
    return await repo.GetListAsync();
}
数据权限双维度过滤(v8.0.2.23+)

组件支持两种数据权限过滤维度,在 SqlSugarFilter 中自动识别:

业务表基类 过滤条件 适用场景
EntityBaseData(含 CreateOrgId) orgIds.Contains(CreateOrgId ?? 0) 业务表存储了创建者所属机构
EntityBase(仅含 Creator,无 CreateOrgId) userIds.Contains(Creator) 业务表只记录了创建者用户Id,通过机构反查用户集合

场景一:业务表仅存储 UserId(无 OrgId 字段)

实体继承 EntityBase 即可,过滤器会通过以下链路自动计算可访问的用户范围:

当前用户 → 获取可访问的机构Id集合
        → SysUserExtOrg: WHERE OrgId IN (orgIds) → userIds
        → 自动注入: userIds.Contains(Creator)
// <summary>场景一示例:仅记录创建者的业务实体</summary>
public class MyBusinessLog : EntityBase
{
    public string Content { get; set; }
    // 注意:不继承 EntityBaseData,没有 CreateOrgId
    // 数据权限过滤器会自动按 Creator 进行用户维度过滤
}

// <summary>查询时自动生效,无需手动拼接 WHERE</summary>
public async Task<List<MyBusinessLog>> QueryLogsAsync(SugarRepository<MyBusinessLog> repo)
{
    return await repo.GetListAsync();
}

场景二:业务表存储了 OrgId

实体继承 EntityBaseData 即可,过滤器会按机构Id直接过滤(原有行为不变):

// <summary>场景二示例:记录了创建者所属机构的业务实体</summary>
public class MyBusinessOrder : EntityBaseData
{
    public string OrderNo { get; set; }
    // 继承自 EntityBaseData 的 CreateOrgId 字段
    // 数据权限过滤器会自动按 CreateOrgId 进行机构维度过滤
}

// <summary>查询时自动生效</summary>
public async Task<List<MyBusinessOrder>> QueryOrdersAsync(SugarRepository<MyBusinessOrder> repo)
{
    return await repo.GetListAsync();
}

手动调用数据范围 API

如果需要在前端或业务逻辑中主动获取数据范围:

// <summary>获取机构维度数据范围(返回机构Id集合)</summary>
public async Task<List<long>> GetOrgScopeAsync(SysUserService userService, long userId)
{
    return await userService.GetUserDataScopeIdList(userId);
}

// <summary>批量获取机构维度数据范围</summary>
public async Task<Dictionary<long, List<long>>> GetOrgsScopeAsync(SysUserService userService, List<long> userIds)
{
    return await userService.GetUsersDataScope(userIds);
}

// <summary>获取用户维度数据范围(返回用户Id集合)</summary>
public async Task<List<long>> GetUserScopeAsync(SysUserService userService, long userId)
{
    return await userService.GetUserDataScopeUserIdList(userId);
}

// <summary>批量获取用户维度数据范围</summary>
public async Task<Dictionary<long, List<long>>> GetUsersScopeAsync(SysUserService userService, List<long> userIds)
{
    return await userService.GetUsersDataScopeUserIdList(userIds);
}
DataScopeService — 按需数据权限过滤(v8.0.2.26+)

DataScopeService 是比 SqlSugarFilter.SetOrgEntityFilter 更灵活的数据权限过滤方式。前者通过 moreFilter 全局 AOP 注入,对所有实体生效且可能影响未登录接口;后者由业务代码按需调用,开发人员显式决定哪些查询需要数据范围过滤。

与 SetOrgEntityFilter 的区别:

维度 SetOrgEntityFilter(全局) DataScopeService(按需)
生效方式 moreFilter 回调 → 全局 AOP 注入 Service 注入后链式调用 BuildFilterAsync<T>()
过滤粒度 所有 EntityBase / EntityBaseData 子类 由开发人员在每个查询上显式调用
未登录请求 可能因类型不匹配报错 返回 u => true,不受影响
控制力 无差别全局生效 逐查询精确控制

使用方式:

  1. DI 自动注册(无需手动 AddScoped,已在 AddOmiSystemSetup 中完成)
  2. 在 Service 构造函数中注入 DataScopeService
  3. 在查询链中加一行 .Where(await _dataScopeService.BuildFilterAsync<T>())
// <summary>示例:注入 DataScopeService 并按需添加数据范围过滤</summary>
public partial class MyService : IApplicationService
{
    private readonly SugarRepository<MyEntity> _repo;
    private readonly DataScopeService _dataScopeService;

    public MyService(SugarRepository<MyEntity> repo, DataScopeService dataScopeService)
    { _repo = repo; _dataScopeService = dataScopeService; }

    public async Task<PageResult<MyEntityDto>> GetPagesAsync(MyEntityQuery parm)
    {
        return await _repo.AsQueryable()
            .Where(await _dataScopeService.BuildFilterAsync<MyEntity>())  // ← 数据范围过滤
            .Where(predicate.ToExpression())
            .ToPageAsync<MyEntity, MyEntityDto>(parm);
    }
}

BuildFilterAsync<T>() 返回 Expression<Func<T, bool>>(永远非 null):

场景 返回值
超管 / 未登录 u => true(无过滤)
ALL 数据范围 u => true(无过滤)
SELF(仅本人) u => u.Creator == userId
机构 / 自定义 u => u.Creator.HasValue && userIds.Contains(u.Creator.Value)
自定义业务过滤器(IEntityFilter)

SetCustomEntityFilter 是一个扩展点,允许下游业务模块为特定实体添加自定义的全局过滤条件,无需修改框架源码。

和 SetOrgEntityFilter 的区别:

方法 过滤维度 适用范围 适用场景
SetOrgEntityFilter 数据权限范围 所有 EntityBase / EntityBaseData 子类 按用户/机构隔离数据
SetCustomEntityFilter 自定义业务规则 指定实体类型 按业务状态、类型等维度过滤

实现步骤:

  1. 创建一个类,实现 IEntityFilter 接口
  2. 在 AddEntityFilter() 方法中返回自定义的 TableFilterItem 过滤表达式
  3. 在 moreFilter 回调中注册 SetCustomEntityFilter

示例:

// <summary>1. 实现 IEntityFilter 接口 — 例如:只查已发布的文章</summary>
public class ArticleFilter : IEntityFilter
{
    public IEnumerable<TableFilterItem<object>> AddEntityFilter()
    {
        Expression<Func<Article, bool>> dynamicExpression = u => u.Status == "published";
        var tableFilterItem = new TableFilterItem<object>(typeof(Article), dynamicExpression);

        return new[] { tableFilterItem };
    }
}

// <summary>2. 在 Program.cs 中注册(moreFilter 回调中)</summary>
// <remarks>SetCustomEntityFilter 会自动扫描并加载所有 IEntityFilter 实现</remarks>
builder.Services.AddSqlSugarSetup(moreFilter: (provider) =>
{
    SqlSugarFilter.SetOrgEntityFilter(builder.Services, provider);       // 数据范围
    SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider);    // 自定义业务过滤
});

// <summary>3. 查询时自动生效,无需手动拼接 WHERE</summary>
// 以下查询会自动带上 WHERE Status = 'published'
public async Task<List<Article>> GetPublishedArticlesAsync(SugarRepository<Article> repo)
{
    return await repo.GetListAsync();
}

多个过滤条件:一个类可以返回多个 TableFilterItem,一个项目可以有多个 IEntityFilter 实现类,它们都会被加载和应用。

缓存:过滤表达式会缓存到 Redis,键为 db:{configId}:custom:{userId},避免每次请求都反射扫描。


配置选项详解

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)。主库 ConnectionConfigs[0] 开启该选项后,AddOmiSystemSetup() 才会注册租户服务、初始化租户表与默认租户种子数据
CommandTimeOut int SQL 命令超时(秒),默认 30

数据库初始化配置 (DbInitConfig)

配置项 类型 默认值 说明
InitTable bool true 启动时自动检测并创建不存在的数据库表(CodeFirst)
InitSeedData bool true 初始化种子数据(管理员/基础角色/菜单等)

密码加密配置 (PasswordConfig)

配置项 类型 默认值 说明
DefaultPassword string 123qwe 种子数据用户及重置密码时的默认密码
EncryptTimes int 3 密码加密迭代次数
StrongPassword bool — 修改密码时是否校验密码强度(IPasswordServer.Validate)

Swagger 版本配置 (Swagger.ApiVersions)

配置项 类型 说明
Title string Swagger UI 中显示的 API 分组标题
Version string API 版本标识,普通接口 GroupName 固定为 system,多租户接口为 tenant

API 接口速览

默认路由前缀: /openapi/api | Swagger GroupName: system(多租户服务为 tenant,仅开启 IsTenantIdFilter 时注册)

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 获取角色用户相关信息
GET /SysUser/GetUserDataScopeIdList GetUserDataScopeIdList 获取用户数据范围(机构Id集合)
GET /SysUser/GetUsersDataScope GetUsersDataScope 批量获取用户数据权限范围(机构维度)
GET /SysUser/GetUserDataScopeUserIdList GetUserDataScopeUserIdList 获取用户数据范围(用户Id维度)
GET /SysUser/GetUsersDataScopeUserIdList GetUsersDataScopeUserIdList 批量获取用户数据权限范围(用户Id维度)

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 获取缓存值

多租户 API(仅开启 IsTenantIdFilter 时注册)

⚠️ 当主库 ConnectionConfigs[0].IsTenantIdFilter = true 时,AddOmiSystemSetup() 才会注册租户相关服务并初始化租户表(SysTenant、SysTenantPackage、SysTenantPackageRel)与默认租户种子数据;未开启时不注册,Swagger 中也看不到以下接口。两个服务使用 [ApiExplorerSettings(GroupName = "tenant")] 分组。

SysTenantService — 租户管理

HTTP 路由 方法 说明
GET /SysTenant/GetPagesAsync GetPagesAsync 分页查询租户(按名称/联系人/状态/套餐过滤)
GET /SysTenant/GetDataAsync GetDataAsync 根据 Id 获取租户详情(含套餐信息)
GET /SysTenant/GetListAsync GetListAsync 获取启用的租户列表
GET /SysTenant/GetTenantByName GetTenantByName 根据名称获取租户
GET /SysTenant/GetTenantByWebsite GetTenantByWebsite 根据绑定域名获取租户
POST /SysTenant/AddDataAsync AddDataAsync 新增租户(自动初始化租户管理员 + 默认角色 + 套餐授权)
PUT /SysTenant/UpdateDataAsync UpdateDataAsync 更新租户(同步重新分配套餐)
DELETE /SysTenant/DeleteDataAsync DeleteDataAsync 删除租户(软删,级联删除套餐关联;默认租户禁止删除)
PUT /SysTenant/ChangeStatus ChangeStatus 更新租户状态(启用/禁用;默认租户禁止停用)
GET /SysTenant/GetTenantPackageIds GetTenantPackageIds 获取租户的套餐 Id 集合
GET /SysTenant/GetTenantPackages GetTenantPackages 获取租户的套餐列表
POST /SysTenant/GrantPackages GrantPackages 分配租户套餐(全量覆盖,同步角色菜单)

SysTenantPackageService — 租户套餐管理

HTTP 路由 方法 说明
GET /SysTenantPackage/GetPagesAsync GetPagesAsync 分页查询租户套餐(按名称/编码/状态过滤)
GET /SysTenantPackage/GetDataAsync GetDataAsync 根据 Id 获取租户套餐详情
GET /SysTenantPackage/GetListAsync GetListAsync 获取启用的租户套餐列表
POST /SysTenantPackage/AddDataAsync AddDataAsync 新增租户套餐
PUT /SysTenantPackage/UpdateDataAsync UpdateDataAsync 更新租户套餐(菜单变更时同步关联租户角色菜单)
DELETE /SysTenantPackage/DeleteDataAsync DeleteDataAsync 删除租户套餐(软删,同步裁剪关联租户的角色菜单)
PUT /SysTenantPackage/ChangeStatus ChangeStatus 更新租户套餐状态(启用/禁用)

错误处理与日志

错误码表

错误码 说明 触发场景
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 字典状态错误 状态值不属于 YesOrNot 枚举
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 权限标识格式错误 如xxx:xxx 按钮权限标识不符合规范
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不能与父机构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 请选择职位 创建用户时未选择职位

多租户相关错误码(仅开启 IsTenantIdFilter 时生效):

错误码 说明 触发场景
i18n.error_message_tenant_name_exists 已有相同名称的租户 新增/更新租户时名称重复
i18n.error_message_tenant_not_found 指定的租户不存在 更新/删除/停用不存在的租户
i18n.error_message_tenant_package_exists 已有相同名称或编码的租户套餐 新增/更新套餐时名称或编码重复
i18n.error_message_tenant_package_not_found 指定的租户套餐不存在 更新/删除/停用不存在的套餐
i18n.error_message_tenant_package_has_tenant 租户套餐已被租户使用,无法删除 存在引用关系的套餐被删除
i18n.error_message_tenant_default_cannot_delete 系统默认租户不允许删除或停用 删除/停用内置默认租户
i18n.error_message_invalid_status 状态值无效 状态值不属于 YesOrNot 枚举
i18n.error_message_tenant_admin_account_required 请填写租户管理员账号 新增租户时未填写管理员账号
i18n.error_message_tenant_admin_account_exists 租户管理员账号已存在 管理员账号全局重复
i18n.error_message_tenant_expire_time_required 请填写租户过期时间 新增/更新租户时未填写过期时间
i18n.error_message_tenant_account_count_required 请填写租户账号配额 新增/更新租户时未填写账号配额

异常处理示例

// <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.3.16" />    
    <PackageReference Include="RuoVea.OmiApi.SystemApp" Version="10.0.1.14" />   
    
  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.SetOrgEntityFilter(builder.Services, provider);       // 数据范围过滤(机构/用户维度)
        SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider);    // 自定义业务过滤(IEntityFilter)
    });
    
  4. 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.SetOrgEntityFilter(builder.Services, provider);       // 数据范围过滤(机构/用户维度)← 必须
    SqlSugarFilter.SetCustomEntityFilter(builder.Services, provider);    // 自定义业务过滤(IEntityFilter)
});

// <summary>2. 实体须继承 EntityBase(含 Creator)或 EntityBaseData(含 CreateOrgId 等过滤字段)</summary>
// <summary>3. 当前登录用户的角色 DataScope 配置正确(非 All)</summary>

Q: 缓存清除后权限未立即生效?

角色/菜单授权变更后,组件内部会自动调用 SysCacheService.Remove 清除相关缓存。如果使用外部 Redis 管理工具手动清除,请删除以下前缀的键:

  • sys_user_button:* —— 用户按钮权限集合
  • sys_user_org:* —— 用户机构集合
  • sys_role_maxDataScope:* —— 角色最大数据范围
  • sys_role_dataScope_orgIds:* —— 角色数据范围(机构Id维度)
  • sys_role_dataScope_userIds:* —— 角色数据范围(用户Id维度)

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.1.16 100 8/20/2026
10.0.1.15 96 8/18/2026
10.0.1.14 104 8/17/2026
10.0.1.13 93 8/17/2026
10.0.1.12 111 8/16/2026
10.0.1.11 95 8/16/2026
10.0.1.10 107 8/16/2026
10.0.1.9 105 8/16/2026
10.0.1.8 103 8/16/2026
10.0.1 95 8/16/2026
8.0.3.18 93 8/20/2026
8.0.3.17 101 8/18/2026
8.0.3.16 93 8/17/2026
8.0.3.15 95 8/17/2026
8.0.3.12 99 8/16/2026
8.0.3.11 91 8/16/2026
8.0.3.10 95 8/16/2026
8.0.3.9 96 8/16/2026
8.0.3.8 92 8/16/2026
8.0.3 97 8/16/2026
Loading failed