feat: 完成外部服务解耦与租户级 Provider 模块化,移除 Supabase 依赖
This commit is contained in:
141
docs/migration/phase-4-external-provider-decoupling.md
Normal file
141
docs/migration/phase-4-external-provider-decoupling.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 第四阶段:外部服务解耦与租户级 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 验证。
|
||||
Reference in New Issue
Block a user