Ruth.Net8.Core.Services
2026.11.10
dotnet add package Ruth.Net8.Core.Services --version 2026.11.10
NuGet\Install-Package Ruth.Net8.Core.Services -Version 2026.11.10
<PackageReference Include="Ruth.Net8.Core.Services" Version="2026.11.10" />
<PackageVersion Include="Ruth.Net8.Core.Services" Version="2026.11.10" />
<PackageReference Include="Ruth.Net8.Core.Services" />
paket add Ruth.Net8.Core.Services --version 2026.11.10
#r "nuget: Ruth.Net8.Core.Services, 2026.11.10"
#:package Ruth.Net8.Core.Services@2026.11.10
#addin nuget:?package=Ruth.Net8.Core.Services&version=2026.11.10
#tool nuget:?package=Ruth.Net8.Core.Services&version=2026.11.10
Ruth.Net8.Core.Services 说明
核心业务服务层(用户模块),提供用户/角色/菜单/系统/组织机构/租户等业务服务的实现, 以及数据库自动迁移。Controller(Ruth.Net8.Core.Controllers)调用本层接口,本层依赖 Ruth.Net8.Core.Data(实体/EF 上下文)与 Ruth.Net8.Core.Models(DTO)。
目录结构
Ruth.Net8.Core.Services/
├── ServiceCollectionExtensions.cs // 服务注册入口(AddServices),反射扫描 Service 目录
├── ModuleInitializer.cs // 模块初始化器约定(反射自动注册)
├── RunMigrations.cs // 启动任务:自动执行 EF 迁移(MigrateAsync)
└── Service/
├── 用户Service.cs // 用户 CRUD / 登录 / 角色授权 / 密码等
├── 租户Service.cs // 多租户:租户 CRUD / 用户-租户授权 / 切租户校验
├── 角色Service.cs // 角色 CRUD / 角色菜单授权
├── 菜单Service.cs // 菜单 CRUD / 树
├── 系统Service.cs // 系统 CRUD / 用户-系统菜单
├── 组织机构Service.cs // 部门树
├── 日志Service.cs // 操作日志
├── CustomTreeService.cs // 通用自定义树
├── WeChatWorkService.cs // 企业微信
├── OpcServerConfigService.cs // OPC 服务器配置
├── OpcGroupConfigService.cs // OPC 分组配置
├── OpcDeviceConfigService.cs // OPC 设备配置
├── OpcTagConfigService.cs // OPC 标签配置
├── OpcTagGroupTemplateService.cs // OPC 标签组模板
└── ...
服务注册机制
ServiceCollectionExtensions.AddServices(services, configuration):反射扫描本程序集 命名空间Ruth.Net8.Core.Services.Service下、实现*Service结尾接口的类,AddScoped注册(接口 → 实现)。ModuleInitializer:模块初始化器约定(moduleinitializer 约定注入)。- 新增 Service 的写法:新建
类名Service.cs+I类名Service.cs(Ruth.Net8.Core.Interfaces), 无需手工改注册代码,重启即注册。
Service 通用模式
public class XxxService : JYC.Net6.CustomCLS.EFCORE.BaseDbService<XxxEntity, XxxDto, RuthCoreContext>, IXxxService
{
public XxxService(IDbContextFactory<RuthCoreContext> dbContextFactory, IMapper mapper) : base(dbContextFactory, mapper) { }
}
- 通用 CRUD(增删改查/分页)由
BaseDbService提供; - 业务方法内部
using var db = _dbContextFactory.CreateDbContext()创建短生命周期上下文; - DTO 映射:实体标注
[CustomEFCoreAutoMapper]反射自动映射。
多租户改造(用户可访问多个租户)
改造日期:2026-11-02 | 编译:0 错误(64 个 Lib 存量 nullable 警告)
一、需求与方案
需求:user 模块支持多租户,实现一个用户可以访问多个租户。
已确认方案: | 决策点 | 结论 | | --- | --- | | 租户粒度 | 项目/矿点(同一系统下按项目/矿点分租户) | | 数据隔离 | 共享表 + TenantId 行过滤(EF 全局查询过滤器,一个库一张表) | | 租户切换 | 登录返回租户列表 + JWT 携带/切换租户 |
二、本层改动(Ruth.Net8.Core.Services)
| 文件 | 说明 |
|---|---|
新增 Service\租户Service.cs |
租户 CRUD + 用户-租户授权 + 切租户校验 |
修改 Service\用户Service.cs |
LoginAsync 成功时填充 data.Tenants(可访问租户列表) |
三、配套改动(跨项目)
| 项目 | 文件 | 说明 |
|---|---|---|
| Ruth.Net8.Core.Data | Entity\租户Entity.cs(新) |
租户实体:TenantCode / TenantName / Description / Status |
| Ruth.Net8.Core.Data | Config\租户Config.cs(新) |
表名 租户,schema RuthCore |
| Ruth.Net8.Core.Data | TenantContext.cs(新) |
AsyncLocal 当前租户上下文(随异步流传播) |
| Ruth.Net8.Core.Data | RuthCoreContext.cs(改) |
CurrentTenantId 属性 + ApplyTenantQueryFilters 全局查询过滤器 |
| Ruth.Net8.Core.Models | 租户Dto.cs / UserTenantDto.cs / SwitchTenantDto.cs / SwitchTenantResultDto.cs(新) |
租户相关 DTO |
| Ruth.Net8.Core.Models | 用户Dto.cs(改) |
加 List<租户Dto>? Tenants |
| Ruth.Net8.Core.Models | Define.cs(改) |
加 USERTENANT = "用户租户" |
| Ruth.Net8.Core.Interfaces | I租户Service.cs(新) |
租户服务接口 |
| Ruth.Net8.Core.Controllers | 租户sController.cs(新) |
租户管理接口 |
| Ruth.Net8.Core.WebAPIService | Classes\租户s.cs(新) |
管理端 HTTP 调用封装 |
| Ruth.Net8.Core.WebAPI | Controllers\TenantController.cs(新) |
SwitchTenantAsync 切租户签发 JWT |
| Ruth.Net8.Core.WebAPI | Program.cs(改) |
AddJwtBearer.OnTokenValidated 写入 TenantContext |
| SqlServerMigrations / SqliteMigrations | 20261102083228_coresql20261102_tenant / 20261102083250_sqlite20261102_tenant(新) |
建 租户 表 |
四、核心设计
4.1 "用户-租户"关系:复用泛化关系表
与现有 用户角色(USERROLE)/ 角色菜单(ROLEMENU)/ 部门用户(DepartMentUSER)同机制:
关系表一条记录:FirstId = 用户ID,SecondId = 租户ID,Key = "用户租户"(Define.USERTENANT)
一个用户授权 N 条 → 可访问 N 个租户
4.2 数据隔离链路
登录 LoginAsync ──► 返回用户 + Tenants(可访问租户列表)
前端选租户 ──► SwitchTenantAsync ──► 校验授权 ──► 签发 JWT(TenantId Claim,30分钟)
后续请求 Authorization: Bearer <token>
└─► JWT OnTokenValidated ──► TenantContext.CurrentTenantId = TenantId
└─► 工厂创建 DbContext ──► RuthCoreContext.CurrentTenantId
└─► EF 全局查询过滤器 ──► 只查当前租户数据
4.3 全局查询过滤器(RuthCoreContext.ApplyTenantQueryFilters)
- 凡带
TenantId属性的业务实体自动应用:CurrentTenantId == null || e.TenantId == CurrentTenantId - 当前租户为空 → 不过滤(返回全部,兼容现有场景);
- 有当前租户 → 只返回该租户数据;
- 用户 / 角色 / 菜单 / 关系 / 租户等共享基础表不带 TenantId,不受过滤。
4.4 租户Service 接口方法
| 方法 | 说明 |
|---|---|
SetUserTenantsAsync(List<UserTenantDto>) |
保存用户-租户授权(先删后插,同 SetUserRolesAsync) |
GetTenantsByUserIdAsync(用户Dto) |
用户可访问租户(超管返回全部启用租户) |
GetUsersByTenantIdAsync(租户Dto) |
租户下用户列表(授权界面反查) |
CheckUserTenantAsync(SwitchTenantDto) |
切换前校验(用户存在 + 租户启用 + 已授权;超管免授权校验) |
4.5 超管规则
IsSuperMan == true:可访问全部启用租户,切租户免授权校验。
五、接口一览
| 接口 | 方法 | 说明 |
|---|---|---|
/api/租户s/... |
通用 CRUD | 租户增删改查(BaseController) |
/api/租户s/SetUserTenantsAsync |
POST | 保存用户-租户授权 |
/api/租户s/GetTenantsByUserId |
POST | 用户可访问租户列表 |
/api/租户s/GetUsersByTenantId |
POST | 租户下用户列表 |
/api/租户s/CheckUserTenant |
POST | 切换前校验 |
/api/TenantController/SwitchTenantAsync |
POST | 切租户并签发 JWT(Body: {"UserId":"...","TenantId":"..."}) |
六、业务表启用租户隔离(后续步骤,本轮未执行)
本轮只搭好多租户基础设施,未给任何现有业务表加 TenantId(现有表保持全局,不受影响)。 需要隔离某张业务表时:
- 业务实体加属性:
public string? TenantId { get; set; }; - 生成迁移(
dotnet ef migrations add或手写),迁移里UPDATE 业务表 SET TenantId = '<默认租户ID>'回填存量数据; - 重启 WebAPI → 自动迁移 → 过滤器自动生效,业务 Service 查询代码一行不用改。
七、数据库迁移说明
- 迁移项目:
Ruth.Net8.Core.SqlServerMigrations(SqlServer)与Ruth.Net8.Core.SqliteMigrations(Sqlite)两套同步; - 应用方式:WebAPI 启动时
RunMigrations.ExecuteAsync→db.Database.MigrateAsync()自动应用,无需手工执行; - 新增迁移命令(示例,dotnet-ef 9.0.5 全局工具,需先把
C:\Program Files\dotnet加入 PATH):
dotnet ef migrations add <迁移名> --project Ruth.Net8.Core.SqlServerMigrations --startup-project Ruth.Net8.Core.SqlServerMigrations --context RuthCoreContext
dotnet ef migrations add <迁移名> --project Ruth.Net8.Core.SqliteMigrations --startup-project Ruth.Net8.Core.SqliteMigrations --context RuthCoreContext
八、前端对接要点
- 登录:
LoginAsync返回值新增data.Tenants(数组,空则未授权任何租户); - 切换:调
/api/TenantController/SwitchTenantAsync,Body{"UserId": 用户Id, "TenantId": 目标租户Id},返回data.Token(携带租户信息的 JWT); - 访问:之后的请求带
Authorization: Bearer <Token>,服务端自动按该租户过滤数据; - 授权管理界面:租户管理页维护租户;用户管理页或租户页调
SetUserTenantsAsync分配可访问租户。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- AutoMapper (>= 14.0.0)
- Ruth.JYC.CustomCLS (>= 2026.3.20)
- Ruth.Net8.Core.Controllers (>= 2026.11.10)
- Ruth.Net8.Core.Data (>= 2026.11.10)
- Ruth.Net8.Core.Interfaces (>= 2026.11.10)
- Ruth.Net8.Core.SqliteMigrations (>= 2026.11.10)
- Ruth.Net8.Core.SqlServerMigrations (>= 2026.11.10)
-
net9.0
- AutoMapper (>= 14.0.0)
- Ruth.JYC.CustomCLS (>= 2026.3.20)
- Ruth.Net8.Core.Controllers (>= 2026.11.10)
- Ruth.Net8.Core.Data (>= 2026.11.10)
- Ruth.Net8.Core.Interfaces (>= 2026.11.10)
- Ruth.Net8.Core.SqliteMigrations (>= 2026.11.10)
- Ruth.Net8.Core.SqlServerMigrations (>= 2026.11.10)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.11.10 | 73 | 11/10/2026 |
| 2026.11.2 | 100 | 11/3/2026 |
| 2026.8.10 | 149 | 8/10/2026 |
| 2026.6.6 | 144 | 6/5/2026 |
| 2026.4.26 | 135 | 4/26/2026 |
| 2026.1.29 | 162 | 1/29/2026 |
| 2026.1.8 | 147 | 1/8/2026 |
| 2025.10.4 | 194 | 10/4/2025 |
| 2025.5.18 | 212 | 5/18/2025 |
| 2025.2.21 | 217 | 2/21/2025 |
| 2024.12.9 | 223 | 12/9/2024 |
| 2024.11.25 | 215 | 11/26/2024 |
| 2024.11.21 | 210 | 11/21/2024 |
| 2024.10.14 | 229 | 10/14/2024 |
| 2024.8.21 | 265 | 8/21/2024 |
| 8.0.4 | 238 | 5/7/2024 |
| 8.0.1 | 260 | 1/26/2024 |