2.3 KiB
2.3 KiB
第三阶段:租户隔离、共享题库与前端运行时
状态:已完成主线设计和实现。
目标
- 普通业务代码默认只能读取和写入当前租户数据。
- 公共题库由平台主体拥有,有效租户可访问。
- 租户私题只属于本租户。
- 公共题和私题可混合组卷、答题、收藏、错题和统计。
- 租户自定义域名安全解析到统一前端运行时配置。
结果
ITenantContext只读化,请求租户由中间件解析。- EF Core Query Filter 自动按租户过滤。
- SaveChanges 拦截器自动写入当前租户并拒绝跨租户写入。
- DbContext 使用 scoped
AddDbContext,避免池化串租户状态。 TenantResolutionMiddleware在认证前按可信 Host 解析租户。- JWT tenant claim 与 Host 解析不一致时返回 403。
- 未知、Pending、禁用域名在业务前返回 404。
- 唯一
PlatformOwned租户拥有公共题库、公共题、公共题版本和公共分类主干。 TenantQuestionReference作为公共题/私题消费引用。PracticeSessionQuestion锁定题目版本,历史答题按原版本回放。TenantFrontendConfig支持 Draft / Preview / Publish。GET /api/runtime/bootstrap仅根据当前 Host 返回公开配置。
数据库边界
EF Core 负责实体、索引、外键和普通约束。以下跨表租户不变量由集中 PostgreSQL guard 管理:
TenantQuestionReference只能引用平台公共题或当前租户私题。TaxonomyNode父节点只能属于平台主体或当前租户。
维护规则见根目录 README 的“数据库与 ORM 分工”。
验收
- A 租户不能读取、修改、删除或 Attach B 租户数据。
- A 可访问公共题和 A 私题,不能构造 B 私题引用。
- 公共题 V1 创建会话后发布 V2,旧会话仍使用 V1,新会话使用 V2。
- Host A 携带 Tenant B JWT 返回 403。
- Pending/未知域名和 Suspended 租户无法获取 runtime bootstrap。
- 新增租户实体漏写 filter、组合外键或租户索引时模型测试失败。
dotnet build TIKU-BACKEND.slnx --no-restore
dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-build
dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-build
dotnet ef migrations script --project Tiku.Infrastructure --startup-project Tiku.DbMigrator
git diff --check