5-19泰额版.md 11.9 KB

5-19 泰额版 — 多租户(独立库)与接口说明

本文档说明 泰额版后端泰额版/Food Labeling Management Code/Yi.Abp.Net8)在 2026-05-19 起的多租户改造:每个租户独立 MySQL 业务库,平台主库仅存 yitenant;以及泰额专用登录、租户开通相关接口。

与美国版业务接口(food-labeling-us)共用同一宿主时,调用业务 API 须携带租户上下文(见 租户上下文)。


目录


架构概览

用途 连接来源
平台主库 antis-foodlabeling-host yitenant(租户元数据) appsettingsDbConnOptions.Url
租户业务库antis-foodlabeling-us fl_*locationuser yitenant.TenantConnectionString
antis-foodlabeling-host (主库)
  └── yitenant
        ├── Default  → antis-foodlabeling-us(迁移期默认租户 / 现有数据)
        └── 新租户    → antis-foodlabeling-{tenant}(Provision 自动建库)
  • 不做 业务表 TenantId 行级隔离(勿执行给 fl_*TenantId 的 ALTER)。
  • 切换租户:请求头 __tenant 和/或 JWT 中的 TenantId Claim。
  • 无租户上下文时:连接平台主库(用于租户 CRUD、开通租户等)。

默认租户(迁移期)

Id 11111111-1111-1111-1111-111111111111
Name Default
业务库 antis-foodlabeling-us(连接串写在 yitenant.TenantConnectionString

数据库与 SQL 脚本

脚本目录:泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling/scripts/

执行顺序

顺序 文件 说明
1 create_platform_host_database.sql 创建主库 antis-foodlabeling-hostyitenant
2 migrate_yitenant_to_host.sql 可选:若曾在业务库 antis-foodlabeling-us 中写过 yitenant,迁移到主库
3 separate_database_bootstrap.sql 在主库登记默认租户,TenantConnectionString 指向 antis-foodlabeling-us

勿执行

以下 不要 在业务库执行(共享库 + 行级 TenantId 方案已废弃):

-- 勿执行
ALTER TABLE fl_product ADD COLUMN TenantId ...
UPDATE fl_product SET TenantId = ...

自检 SQL

USE antis-foodlabeling-host;

SELECT Id, Name, LEFT(TenantConnectionString, 80) AS conn
FROM yitenant
WHERE Id = '11111111-1111-1111-1111-111111111111';

应有一条 Default,且 conn 中含 database=antis-foodlabeling-us


应用配置

文件:泰额版/.../src/Yi.Abp.Web/appsettings.json

配置项 说明
DbConnOptions.Url 平台主库,如 database=antis-foodlabeling-host
DbConnOptions.EnabledSaasMultiTenancy true
FoodLabeling:MultiTenancy:Mode SeparateDatabase
FoodLabeling:TenantDatabase 新租户库名模板、RDS 账号等
FoodLabeling:LegacyTenant 默认租户 Id / Name

新租户库名模板示例:antis-foodlabeling-{tenant}{tenant} 为规范化后的租户名)。


租户上下文

业务请求须让后端解析到 租户 Id,任选其一(推荐登录后仅用 Bearer Token):

方式 说明
请求头 __tenant: {租户Guid}
JWT Claim:TenantIdTokenTypeConst.TenantId)及 AbpClaimTypes.TenantId

泰额登录签发的 Token 已写入上述 Claim;JwtClaimTenantResolveContributor 会自动解析。

租户解析顺序(YiAbpWebModule):

  1. HeaderTenantResolveContributor__tenant
  2. JwtClaimTenantResolveContributor(JWT)

独立库模式下不会自动回落到默认租户;未传租户时走主库连接,业务表可能查不到数据。


泰额专用接口

Swagger 分组:泰额版-食品标签FoodLabeling.Th.Application

基础路径前缀:/api/app/(ABP 动态 API 约定,以 Swagger 为准)

POST /api/app/th-app-auth/login

应用服务ThAppAuthAppService
鉴权:匿名

说明

  1. 平台主库校验 tenantId 是否存在且已配置 TenantConnectionString
  2. 切换到该租户业务库,按邮箱 + 密码校验 user(盐值哈希与美国版一致)。
  3. 签发 JWT(含 TenantId)、RefreshToken,并返回绑定门店列表。

入参 ThAppLoginInputVo

字段 类型 必填 说明
tenantId Guid 平台主库 yitenant.Id
email string 登录邮箱(user.Email / 邮箱形 UserName
password string 密码
uuid string 图形验证码 UUID(系统开启验证码时必填)
code string 图形验证码

请求示例

POST /api/app/th-app-auth/login
Content-Type: application/json
{
  "tenantId": "11111111-1111-1111-1111-111111111111",
  "email": "admin@example.com",
  "password": "YourPassword1!"
}

出参 ThAppLoginOutputDto

字段 类型 说明
token string 访问令牌(含 TenantId Claim)
refreshToken string 刷新令牌
tenantId Guid 当前租户 Id
tenantName string 租户名称
locations array 绑定门店(结构同美国版 UsAppBoundLocationDto

出参示例

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "...",
  "tenantId": "11111111-1111-1111-1111-111111111111",
  "tenantName": "Default",
  "locations": [
    {
      "id": "...",
      "locationCode": "LOC001",
      "locationName": "Store A",
      "fullAddress": "...",
      "state": true
    }
  ]
}

JWT Claims(节选)

Claim 说明
TenantId / tenantid 租户 Guid 字符串
client_kind th_app
sub / UserId 用户 Id

错误说明

提示 原因
请输入租户、邮箱与密码 参数缺失
租户不存在或已停用 主库无该 yitenant 记录
租户未配置业务库连接串 TenantConnectionString 为空
登录失败!邮箱不存在 租户库中无该用户
用户名或密码错误 密码校验失败

GET /api/app/th-app-auth/my-locations

应用服务ThAppAuthAppService
鉴权:Bearer Token

说明:在当前 JWT 租户上下文下,查询 userlocation + location 绑定门店。

GET /api/app/th-app-auth/my-locations
Authorization: Bearer {token}

可选同时带:__tenant: 11111111-1111-1111-1111-111111111111(与 Token 中租户一致即可)。


ThMulti-tenancy

应用服务ThMultiTenancyAppService

GET /api/app/th-multi-tenancy/tenant-select(方法名以 Swagger 为准,一般为 get-tenant-select

租户下拉列表(供登录页选择租户)。

出参ThTenantSelectDto[]

字段 类型 说明
id Guid 租户 Id
name string 租户名称

GET /api/app/th-multi-tenancy/current-tenantget-current-tenant

当前请求解析到的租户(调试用)。

出参ThCurrentTenantDto

字段 类型 说明
tenantId Guid? 当前租户 Id
tenantName string? 当前租户名称

POST /api/app/th-tenant-provisioning/provision

应用服务ThTenantProvisioningAppService
鉴权:需登录(平台管理员)

说明

  1. 平台主库写入 yitenant(含 TenantConnectionString)。
  2. 若未传连接串,按 FoodLabeling:TenantDatabase 生成,如 antis-foodlabeling-{tenant}
  3. initializeDatabase=true 时调用 InitAsync:建库 + CodeFirst 业务表(不含 yitenant)。

入参 ThProvisionTenantInputVo

字段 类型 必填 说明
name string 租户名称(用于生成库名)
tenantConnectionString string 自定义连接串;空则按模板生成
dbType int SqlSugar.DbType,默认 0 = MySql
initializeDatabase bool 默认 true,是否立即建库建表

请求示例

POST /api/app/th-tenant-provisioning/provision
Content-Type: application/json
Authorization: Bearer {token}
{
  "name": "acme",
  "initializeDatabase": true
}

出参 ThProvisionTenantOutputDto

字段 类型 说明
tenantId Guid 新租户 Id
name string 租户名称
databaseName string 业务库名
tenantConnectionString string 完整连接串
databaseInitialized bool 是否已执行 Init

POST /api/app/th-tenant-provisioning/initialize-tenant-database

对已有租户补执行建库建表(入参:租户 tenantId,以 Swagger 为准)。


框架租户管理(补充)

分组:租户管理接口Yi.Framework.TenantManagement.Application

方法 路径 说明
POST /api/app/tenant 创建租户(需 tenantConnectionString
PUT /api/app/tenant/init/{id} 租户业务库 CodeFirst 初始化

泰额推荐使用 th-tenant-provisioning/provision 一步完成登记 + 建库。


业务接口联调

宿主同时加载 FoodLabeling.Application(美国版业务)与 FoodLabeling.Th.Application

调用任意业务 API(如 /api/app/product/api/app/location)时:

GET /api/app/product?SkipCount=1&MaxResultCount=10
Authorization: Bearer {泰额登录返回的 token}

或:

GET /api/app/product?SkipCount=1&MaxResultCount=10
Authorization: Bearer {token}
__tenant: 11111111-1111-1111-1111-111111111111

分页SkipCount1-based 页码(与美国版一致,见 5-18接口优化.md)。


代码与脚本路径

类型 路径
泰额应用层 module/food-labeling/FoodLabeling.Th.Application/
泰额契约 module/food-labeling/FoodLabeling.Th.Application.Contracts/
美国版业务(共用) module/food-labeling-us/FoodLabeling.Application/
SQL 脚本 module/food-labeling/scripts/
多租户常量 module/food-labeling-us/FoodLabeling.Domain.Shared/MultiTenancy/FoodLabelingMultiTenancyConsts.cs
JWT 租户解析 module/food-labeling-us/FoodLabeling.Application/MultiTenancy/JwtClaimTenantResolveContributor.cs
Web 配置 src/Yi.Abp.Web/appsettings.jsonYiAbpWebModule.cs

常见问题

现象 处理
启动报连库失败 确认已执行 create_platform_host_database.sql,且 DbConnOptions.Url 指向 antis-foodlabeling-host
登录报租户不存在 在主库执行 separate_database_bootstrap.sql
登录成功但业务列表为空 检查 Token 是否带 TenantId;或请求头补 __tenant
业务接口查到主库无数据 未解析租户,误连主库;须登录泰额接口或传 __tenant
新租户无表 调用 provisioninitializeDatabase=true,或 PUT tenant/init/{id}
误加了 TenantId 列 独立库模式不需要;可保留列但不使用,建议勿加

变更记录

日期 内容
2026-05-19 泰额版多租户独立库方案;平台主库分离;ThAppAuth 登录写 JWT TenantId;租户开通与 SQL 脚本说明