Files
tiku-backend.net/docs/migration/phase-4-external-provider-decoupling.md

142 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 第四阶段:外部服务解耦与租户级 Provider 模块化
状态:已实施并通过构建、单元测试、真实 PostgreSQL 集成测试和空库迁移验收2026-07-27
本阶段按“完全不用 Supabase、无正式业务数据”的前提实施。不保留 Supabase Auth、Supabase Storage、旧 Provider 配置或旧 DTO 兼容层。目标是让业务层只表达身份、短信、对象存储、支付和通知的业务意图,第三方 SDK、账号、bucket、密钥和 claim 结构都收敛到 Infrastructure provider 边界。
## 设计结论
- Identity 默认使用自有 JWT、数据库 Session、密码、短信验证码和微信认证。
- Object Storage 默认使用阿里云 OSS`local_dev` 只允许本地测试。
- Database 固定为 EF Core + Npgsql + PostgreSQL。
- Provider 配置统一使用 `TenantExternalProvider` + `TenantSecret`
- 不实现 Supabase Auth 兼容层。
- 不实现 Supabase Storage provider。
- 不继续扩散 `TenantAuthProvider``TenantPaymentAccount` 这类专用配置表。
## 统一 Provider 配置
新增统一租户外部服务配置模型 `TenantExternalProvider`,核心字段包括:
- `TenantId`
- `Capability`
- `Provider`
- `Status`
- `DisplayName`
- `ConfigPublic`
- `SecretRef`
- `Priority`
- `Metadata`
- 审计字段
`Capability` 固定为:
- `Identity`
- `ObjectStorage`
- `Sms`
- `Payment`
- `Notification`
同一租户内 `Capability + Provider` 唯一。默认 Provider 选择规则是 `Status = Active` 且优先级最高;需要指定 provider code 的入口必须仍然受当前租户上下文约束。
`ConfigPublic` 只允许公开配置,例如:
- identity`appId`
- payment`merchantId`
- object_storage`region``endpoint``bucketAlias`
- sms / notification`templateCode`
密钥一律进入 `TenantSecret`,由 `SecretRef` 关联。公开配置写入路径必须拒绝 `secret``token``key``privateKey` 等敏感字段,避免把第三方凭据落到普通 JSON 配置里。
## Application 层接口边界
### `IIdentityProvider`
封装 password、sms、wechat_web、wechat_miniapp 等身份解析,不直接把第三方 claim 结构散落在业务服务里,也不负责绕过统一 Session 签发流程。
微信登录通过当前租户的 `TenantExternalProvider(Capability = Identity)` 读取 `appId``SecretRef`,再由基础设施层完成第三方交互。`UserIdentity.Provider` 只保存标准 provider code。
### `ISmsProvider`
只负责发送验证码或模板短信。验证码生成、哈希、频控、过期、校验和登录 Session 仍归自有业务服务负责。发送失败时记录失败状态,不能误判验证码可用。
### `IObjectStorageService`
业务输入不接受任意 bucket。对象存储 provider、bucket alias、endpoint、region 由当前租户的 `TenantExternalProvider(Capability = ObjectStorage)` 解析。
资产表可以保存 `StorageProvider``Bucket``ObjectKey` 作为落库事实,但这些字段只允许由存储服务写入,不对业务 DTO 暴露成任意可写参数。
### `IPaymentProvider`
支付账户配置从统一 Provider 配置加载。支付回调仍按 provider code 路由,但必须校验签名、租户、订单号和幂等事件。
`TenantPaymentAccount` 表和旧支付专用配置模型已删除,管理侧应用模型只保留面向租户后台的 provider 视图。
### `INotificationProvider`
默认实现为站内通知持久化。普通业务不直接跨模块 new `UserNotification`,后续 email、企微、短信模板等外发能力都应作为 Notification provider 扩展。
## 存储和认证的具体变化
- 删除生产代码中的 `SupabaseStorage` provider 枚举值。
- 删除应用层所有 Supabase provider 配置路径。
- 阿里云 OSS SDK 细节只保留在 Infrastructure 实现中。
- 上传签名和上传确认通过当前租户 object storage provider 解析目标位置。
- 微信登录不再从公开 JSON 直接读取密钥。
- 短信登录继续复用自有验证码表、频控和 Session 体系,发送动作改由 `ISmsProvider` 执行。
## 数据库基线
项目尚无正式业务数据,因此本阶段继续采用 greenfield migration
- 删除旧 `TenantAuthProvider` 表。
- 删除旧 `TenantPaymentAccount` 表。
- 新增 `TenantExternalProvider` 表。
- 保留并扩展 `TenantSecret` 用途。
- 重建唯一 `InitialSchema` migration。
- 使用空 PostgreSQL 通过 `Tiku.DbMigrator` 验证完整建库。
## 架构防线
架构测试需要阻止业务代码重新引入以下依赖:
- `Supabase`
- `supabase_storage`
- `SUPABASE_`
- `TenantAuthProvider`
- `TenantPaymentAccount`
- 业务层直接引用阿里云 OSS、微信、支付或短信 SDK namespace
- 业务层直接读取第三方密钥
- 业务 DTO 暴露任意 bucket / provider 细节
允许例外只应存在于:
- Infrastructure provider 实现
- EF Migration
- 测试 fake provider
- 明确说明历史迁移背景的文档
## 验收结果
本阶段完成时的验收口径:
- `dotnet build TIKU-BACKEND.slnx --no-restore`
- `dotnet test Tiku.UnitTests/Tiku.UnitTests.csproj --no-restore`
- `dotnet test Tiku.IntegrationTests/Tiku.IntegrationTests.csproj --no-restore`
- 空 PostgreSQL 数据库执行 `Tiku.DbMigrator`
- `git diff --check`
这些命令的目标不是证明“没有 Supabase 字符串”,而是证明新 Provider 模型、租户边界、迁移链和集成测试能共同支撑后续开发。
## 后续扩展规则
新增外部服务 provider 时,必须先判断它属于哪个 `Capability`,再补 Infrastructure provider 实现和租户配置解析。不要为每个第三方服务新建一套业务专用账号表;除非该能力已经形成独立领域模型,否则默认进入 `TenantExternalProvider`
Provider 扩展时还必须同时补:
- 租户 A/B 配置隔离测试。
- 密钥只通过 `TenantSecret` 读取的测试。
- `ConfigPublic` 敏感字段拒绝测试。
- 架构扫描允许列表。
- 空库 migration 验证。