88 lines
4.5 KiB
Markdown
88 lines
4.5 KiB
Markdown
# 第二阶段:.NET 工程底座
|
||
|
||
完成日期:2026-07-27。
|
||
|
||
本阶段按 greenfield 项目建设,不承担旧 Supabase / NestJS 数据兼容。数据库结构以 EF Core 实体、Fluent Configuration 和 Migration 为唯一权威来源;API 运行时不自动修改数据库。
|
||
|
||
## 数据库开发与发布边界
|
||
|
||
- 开发采用 code first:修改实体或 Fluent Configuration 后生成 EF Core Migration。
|
||
- Migration 统一保存在 `Tiku.Infrastructure/Persistence/Migrations/`。
|
||
- `Tiku.DbMigrator` 是执行 Migration 的独立入口。
|
||
- `Tiku.Api` 和 `Tiku.Worker` 不调用 `Database.Migrate()`,避免多实例启动时争抢 DDL,也避免应用进程持有结构变更权限。
|
||
- 发布前生成并审查 SQL;生产环境由部署流程显式执行 DbMigrator 或审核后的 SQL。
|
||
- Development 未配置连接串时默认连接本机 `tiku` 数据库,并使用当前系统用户名,不内置密码。
|
||
- 非 Development 环境必须显式配置 `ConnectionStrings:Database` 或 `DATABASE_URL`,缺失时启动失败。
|
||
|
||
常用命令:
|
||
|
||
```bash
|
||
dotnet ef migrations add <MigrationName> \
|
||
--project Tiku.Infrastructure \
|
||
--startup-project Tiku.DbMigrator
|
||
|
||
dotnet ef migrations script \
|
||
--project Tiku.Infrastructure \
|
||
--startup-project Tiku.DbMigrator
|
||
|
||
dotnet run --project Tiku.DbMigrator
|
||
```
|
||
|
||
## 真实 PostgreSQL 测试基线
|
||
|
||
`Tiku.IntegrationTests` 已移除 EF Core InMemory provider,全部集成测试连接真实 PostgreSQL:
|
||
|
||
- 管理连接可通过 `TIKU_TEST_POSTGRES_ADMIN` 覆盖。
|
||
- 默认管理连接为 `Host=localhost;Database=postgres;Username=<当前系统用户>`。
|
||
- 测试进程创建一次已执行完整 Migration 的模板数据库。
|
||
- 每个 `ApiTestFactory` 从模板克隆独立 `tiku_it_*` 数据库,测试之间不共享业务数据。
|
||
- Factory 释放时终止连接并删除克隆库;进程退出时删除模板库。
|
||
- 测试只允许管理 `tiku_it_*` 命名空间,不访问或清理已有 `tiku` 业务数据库。
|
||
|
||
真实关系型测试暴露并修正了以下 InMemory 无法可靠验证的问题:
|
||
|
||
- 测试夹具缺失的 Region、School、Subject、Category、Question 等外键实体。
|
||
- `Question` 与 `QuestionVersion` 当前版本引用形成的插入循环,改为事务内分阶段保存。
|
||
- `ReferralLead.FirstTrackId` 与 `ReferralTrack.LeadId` 形成的插入循环,改为事务内分阶段保存。
|
||
- PostgreSQL JSON 映射不能将未定义的 `default(JsonElement)` 生成为 SQL literal,缺省数组改为有效的 `[]`。
|
||
- 徽章发放不再伪造超长 legacy ID;通知使用新记录 ID 生成独立、稳定的去重键。
|
||
|
||
## 配置与密钥安全
|
||
|
||
- 根配置不再提交可用于生产的 JWT signing key。
|
||
- Development 只使用明确标识的开发 JWT key;Production 拒绝该 key。
|
||
- 租户第三方凭据使用 AES-256-GCM envelope 保存。
|
||
- 每条密文使用 12-byte nonce、16-byte authentication tag。
|
||
- AAD 绑定 `tenantId`、`secretRef` 和 `keyId`,密文不能跨租户或跨引用替换。
|
||
- 主密钥必须是 Base64 编码的 32 字节值;Production 拒绝仓库内的开发测试 key。
|
||
- 运行时配置键为 `Security:TenantSecrets:KeyId` 和 `Security:TenantSecrets:MasterKey`;环境变量可使用 `TIKU_TENANT_SECRET_KEY_ID` 与 `TIKU_TENANT_SECRET_MASTER_KEY`。
|
||
- 新 schema 只保留 `encryption_key_id`、`encrypted_payload`、`encryption_nonce`、`encryption_tag`,不保留明文字段或明文回退读取路径。
|
||
|
||
本阶段的 `EncryptTenantSecretPayloads` Migration 会直接删除旧 `secret_payload` 字段。这是 greenfield 决策,不提供旧数据转换或兼容窗口。
|
||
|
||
## 验证结果
|
||
|
||
2026-07-27 在本机 PostgreSQL 18.4、.NET SDK 10.0.301 上完成:
|
||
|
||
```text
|
||
dotnet build:通过,0 warning / 0 error
|
||
dotnet test:通过,265/265
|
||
UnitTests:16/16
|
||
IntegrationTests(真实 PostgreSQL):249/249
|
||
dotnet format --verify-no-changes:通过
|
||
git diff --check:通过
|
||
EF Core migration script:通过,生成 4184 行 SQL
|
||
测试数据库清理:通过,无 tiku_it_* 残留
|
||
```
|
||
|
||
## 第二阶段退出条件
|
||
|
||
- [x] EF Core Migration 成为 code-first schema 的唯一版本历史。
|
||
- [x] API 与 Worker 不在启动时自动执行 Migration。
|
||
- [x] 生产数据库、JWT 和租户密钥配置缺失时 fail fast。
|
||
- [x] 租户第三方凭据只保存 AES-GCM 密文 envelope。
|
||
- [x] 集成测试完全切换到隔离的真实 PostgreSQL 数据库。
|
||
- [x] 修复真实数据库暴露的外键、循环依赖、JSON 和字段长度问题。
|
||
- [x] 全量构建、测试、格式与 Migration SQL 验证通过。
|
||
|