feat: establish dotnet engineering foundation

This commit is contained in:
2026-07-27 15:00:14 +08:00
parent 60a376ea0b
commit 28e9a9fa41
36 changed files with 17301 additions and 89 deletions

View File

@@ -6,6 +6,7 @@
- [`docs/adr/0001-authoritative-dotnet-backend.md`](adr/0001-authoritative-dotnet-backend.md)
- [`docs/migration/phase-1-repository-baseline.md`](migration/phase-1-repository-baseline.md)
- [`docs/migration/phase-2-engineering-foundation.md`](migration/phase-2-engineering-foundation.md)
迁移原则:

View File

@@ -0,0 +1,87 @@
# 第二阶段:.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 keyProduction 拒绝该 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
UnitTests16/16
IntegrationTests真实 PostgreSQL249/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 验证通过。