Files
tiku-backend.net/README.md
2026-07-26 13:29:44 +08:00

208 lines
9.0 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.

# TIKU Backend
题库 SaaS 后端,当前目标是从旧 PocketBase / Supabase 方案迁移到可控的 ASP.NET Core + PostgreSQL 架构。
这个仓库目前处在新后端初始化和安全底座建设阶段Domain 实体、EF Core Fluent 配置、PostgreSQL 初始建库 migration、模型约束测试已经建立API 层已经开始接入 ASP.NET Core 认证授权、本地登录、数据库 Session 和当前租户上下文。
## 为什么换掉 Supabase
旧方案的问题不只是“用了 Supabase”而是安全、权限、业务约束分散在太多地方
- 应用代码、Supabase RLS、SQL policy、Edge/脚本、前端约定之间边界不清。
- 多租户业务复杂后RLS policy 会越来越难审查,调试成本高。
- 团队现在已经有后端开发,继续把核心权限和业务一致性绑在 BaaS 规则上,反而增加维护难度。
- 复杂导入、对账、退款、CRM、资产安全扫描、平台账单等后台任务更适合由明确的 Worker + 应用层权限 + 数据库约束来承载。
- 新系统需要长期演进,直接掌控 PostgreSQL schema、迁移、索引、FK、事务边界会比依赖 Supabase 平台约束更可控。
新的方向不是“数据库裸奔”,而是把职责重新分层:
- ASP.NET Core 负责认证、授权、租户上下文、业务流程、审计记录。
- PostgreSQL 负责数据完整性、外键、唯一约束、检查约束、索引、JSONB/数组/ltree/citext 等原生能力。
- Worker 负责导入、对账、退款、通知、资产扫描、统计等后台任务。
- 测试负责持续验证 EF 模型和数据库约束没有退化。
## 技术栈
- .NET / ASP.NET Core
- Entity Framework Core
- PostgreSQL
- Npgsql
- xUnit
项目分层:
```text
Tiku.Api # HTTP API 入口
Tiku.Application # 应用服务、用例编排
Tiku.Domain # 领域实体、枚举、基础类型
Tiku.Infrastructure # EF Core、PostgreSQL 持久化配置、migration
Tiku.DbMigrator # 数据库迁移启动项目
Tiku.Worker # 后台任务
Tiku.UnitTests # 单元测试
Tiku.IntegrationTests # EF 模型/持久化约束测试
```
## 数据库策略
当前数据库以 PostgreSQL 为核心能力,而不是把 PostgreSQL 当成普通 KV 存储:
- JSON 字段统一使用 C# `JsonElement`,映射到 PostgreSQL `jsonb`
- 树形内容路径使用 `ltree`
- 大小写不敏感编码/域名/兑换码等使用 `citext`
- PostgreSQL array 使用 `text[]` / `uuid[]`,不把数组塞成字符串。
- 钱相关字段用整型 cents 或明确 decimal precision避免浮点误差。
- 多租户表默认有 `TenantId`
- 租户内跨表引用优先使用 composite FK例如 `(tenant_id, id)`
- 关键业务唯一性落数据库唯一索引或过滤唯一索引。
- 用 check constraint 保护金额、数量、状态范围等底线。
当前 migration 已压成一个 greenfield baseline
```text
Tiku.Infrastructure/Persistence/Migrations/20260725220742_InitialSchema.cs
```
因为新库还没正式上线,保留一堆开发过程 migration 意义不大。现在单个初始 migration 更适合作为新后端的起点。
## 新安全策略
这次迁移的核心安全思路是:不照搬 Supabase RLS而是在应用层建立明确、可测试、可审计的安全边界同时用 PostgreSQL 约束守住数据完整性底线。
### 已落地
- ASP.NET Core Controller API 已接入统一认证/授权管线。
- 本地认证不依赖 Supabase Auth
- 手机号 + 密码登录
- 短信验证码登录
- 微信网页 OAuth 登录
- 微信小程序 `wx.login` 登录
- JWT access token
- 数据库 `auth_sessions` refresh/session
- `auth_login_events` 登录事件
- JWT 不只验签,也会校验对应 `auth_sessions` 是否仍有效;登出或撤销 session 后,旧 access token 会被拒绝。
- 当前请求上下文已经拆成应用层抽象:
- `ICurrentUser`
- `ICurrentTenant`
- 第一批授权策略已经建立:
- 已登录用户
- 当前租户成员
- 租户管理员
- 第一批认证/租户接口已经建立:
- `GET /api/tenant/resolve`
- `GET /api/tenant/current-public`
- `POST /api/auth/login/password`
- `POST /api/auth/login/sms`
- `POST /api/auth/oauth/wechat`
- `POST /api/auth/oauth/wechat-miniapp`
- `POST /api/auth/refresh`
- `POST /api/auth/logout`
- `GET /api/me`
- `GET /api/tenants/current`
- `GET /api/health`
- 多租户数据表普遍包含 `TenantId`
- 大量租户内关系使用 composite FK避免只靠应用代码约定租户一致性。
- 不迁移 Supabase RLS、`app_private` schema、生产保护 policy。
- 敏感配置不原样落明文字段,例如 CRM / 支付 / webhook 只保留 `secret_ref` 或公开配置。
- PB 导入有专门审计表:
- `pb_import_runs`
- `pb_raw_records`
- `pb_import_issues`
- 关键业务流保留审计/事件表,例如:
- `audit_logs`
- `auth_login_events`
- `payment_events`
- `commerce_refund_events`
- `commerce_reconciliation_issue_events`
- `content_asset_access_events`
- `content_asset_security_scan_events`
- `platform_audit_alerts`
- 模型测试覆盖:
- 表数量和表名
- JSONB 映射
- `ltree` 映射
- 关键唯一索引
- decimal precision
- 租户关系 composite FK
- PB raw record 可追踪主键
- API / Auth 测试覆盖:
- 租户按 `tenantCode` / `host` 解析
- 公开品牌、主题、功能开关和 public config 返回
- health smoke test
- 未登录返回 401
- 无权限返回 403
- 密码登录成功/失败
- 短信验证码登录成功/失败
- 微信登录 upsert 用户、身份和租户成员
- 登录后访问当前用户和当前租户
- 登出后旧 access token / refresh token 被拒绝
### 计划落地
后续 API / Worker 实现时,需要继续把下面这些策略固化成代码和测试:
- 业务 API 不直接信任请求 body 的 `tenantId`;登录阶段可选择租户,登录后业务请求以 JWT/session 中解析出的当前租户为准。
- API 层继续基于用户、租户成员、角色模板、权限 JSON 扩展授权策略。
- 平台管理员、租户管理员、教师/运营、学生等权限边界继续细化到业务模块。
- Repository / Query 层默认带租户过滤,平台级查询必须显式声明。
- 所有跨租户资源访问必须走应用服务校验,不允许 controller 直查。
- 审计日志覆盖高风险操作:
- 登录、认证失败、短信验证
- 订单/支付/退款/对账调整
- 题库导入/导出/公库采纳
- 资产下载签名和安全扫描
- 租户配置、域名、品牌、支付账号变更
- 平台账单、催收、告警处理
- Secret 管理独立化,只在应用层通过安全服务解析 `secret_ref`
- 短信发送目前先完成验证码表、哈希校验和限流模型,真实供应商通过接口适配接入。
- 后台 Worker 使用最小权限的应用服务,不直接绕过业务规则写库。
- 对账、导入、批处理任务要求幂等键和可重跑设计。
- 生产环境连接串、密钥、对象存储凭据不进入仓库。
## 常用命令
```bash
dotnet build /home/xiong/Games/Anii/TIKU-BACKEND/TIKU-BACKEND.slnx
dotnet test /home/xiong/Games/Anii/TIKU-BACKEND/TIKU-BACKEND.slnx
dotnet format /home/xiong/Games/Anii/TIKU-BACKEND/TIKU-BACKEND.slnx --verify-no-changes
git -C /home/xiong/Games/Anii/TIKU-BACKEND diff --check
```
生成数据库 SQL
```bash
dotnet ef migrations script \
--project Tiku.Infrastructure \
--startup-project Tiku.DbMigrator
```
## API 迁移模板
后续从旧 Nest/Supabase 迁移 API 时,优先按当前认证和租户接口的模板推进:
1. DTO 放到 `Tiku.Api/Contracts`,只包含 HTTP 输入输出形状、校验属性和 OpenAPI 描述。
2. Controller 保持轻量只做路由、授权、DTO 到 Application request 的映射。
3. 业务流程放到 `Tiku.Application`EF/外部服务实现放到 `Tiku.Infrastructure`
4. 已登录业务接口默认从 `ICurrentTenant` / `ICurrentUser` 取上下文,不直接信任 body 里的 `tenantId`
5. 公开接口只返回 branding、feature flags、public config 等可暴露字段,不泄露 secret/refund/payment/internal metadata。
6. 每迁一个小闭环就补集成测试和 Scalar/OpenAPI 描述,测试通过后单独提交。
建议下一批迁移顺序:
```text
Catalog 基础只读 API
-> 地区 / 模块 / 学校 / 专业 / 科目 / 分类
Content Navigation 只读 API
-> 内容入口 / 内容树 / 题集 / 练习蓝图
Question Bank 只读 API
-> 题库列表 / 题目详情 / 题目版本
```
## 当前状态
- 数据库模型迁移已完成到初始 schema。
- migration 已整理为单个初始建库 migration。
- API 安全底座已建立JWT、Session、本地登录、当前用户、当前租户、基础授权策略。
- 租户公开入口已建立tenant resolve、public config、health。
- 当前模型测试、认证服务测试、API 认证/租户闭环测试通过。
- 下一步重点是继续把题库、内容、导入、订单等业务 API 接入这套安全轨道,而不是重新散写权限判断。