Files
tiku-backend.net/docs/architecture/overview.md

123 lines
6.7 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.

# 系统架构与业务边界
本文描述当前仓库的实际代码结构和运行时职责。接口路径、DTO 和响应模型以运行时 OpenAPI 为准。
## 分层与依赖
```text
+------------------+ +------------------+ +-----------------+
| Tiku.Api | | Tiku.Worker | | Tiku.DbMigrator |
| HTTP API | | Background loops | | Migrate + Seed |
+--------+---------+ +--------+---------+ +--------+--------+
| | |
+---------------------+---------------------+
v
+---------------------+
| Tiku.Infrastructure |
+----------+----------+
v
+---------------------+
| Tiku.Application |
+----------+----------+
v
+---------------------+
| Tiku.Domain |
+---------------------+
```
- `Tiku.Domain` 保存领域实体、枚举和基础类型。除 Identity stores 抽象外,不依赖持久化或 Provider SDK。
- `Tiku.Application` 定义用例契约、Provider 接口、安全上下文和业务目录,依赖 Domain。
- `Tiku.Infrastructure` 实现 EF Core、PostgreSQL、Identity、外部 Provider 和后台任务,依赖 Application 与 Domain。
- `Tiku.Api``Tiku.Worker` 是独立运行时入口,`Tiku.DbMigrator` 是部署时迁移和 seed 入口。
## 运行时组件
### API
`Tiku.Api/Program.cs` 只负责组合服务、构建应用和启用请求管线。管线的关键顺序是:
```text
Forwarded Headers
-> HTTPS / 压缩 / 静态文件
-> Routing / CORS
-> Host 租户解析
-> 浏览器 CSRF
-> JWT 认证 / 认证专用限流 / 全局限流
-> 当前用户上下文 / 授权
-> SaaS Feature 校验
-> Output Cache
-> Controllers
```
OpenAPI 和 Scalar 只在 Development 映射。平台管理端位于独立的 `Tiku.PlatformAdmin.Web` React 工程,由 OpenAPI 生成接口契约并单独构建、部署;`Tiku.Api` 不再托管平台前端静态文件。
### DbMigrator
`Tiku.DbMigrator` 是唯一迁移入口,执行顺序为:
1. 解析 `ConnectionStrings:Database``DATABASE_URL`
2. 进入带审计原因的 System Scope。
3. 执行 `Database.MigrateAsync()`
4. seed 内置 SaaS Feature、PermissionModule、BackendPermission 和 BackendMenu 目录。
5. Development 全新数据库自动 seed 平台管理员;非 Development 仅在显式传入 `--bootstrap-platform-admin` 时创建管理员。
API 不自动迁移数据库。
### Worker 后台处理
`Tiku.Worker` 独立注册六个 Hosted ServiceAPI 进程不注册后台循环:
| Worker | 周期 | 当前职责 |
| --- | --- | --- |
| `TenantDomainWorker` | `TenantDomains:PollSeconds`,限制为 103600 秒 | 校验自定义域名 CNAME/TXT调用网关 TLS 接口并失效租户缓存 |
| `SaasSubscriptionWorker` | 60 秒 | 处理到期、宽限期等 SaaS 订阅生命周期 |
| `FeatureUsageWorker` | `FeatureUsageReconciliation:IntervalMinutes`,限制为 11440 分钟 | 按真实业务数据校准租户 Feature 用量 |
| `BackgroundJobsWorker` | 默认 2 秒、4 个分区 | 使用租约处理 PostgreSQL 中的即时、延时和待重试任务 |
| `AuthorizationCacheInvalidationWorker` | 默认 2 秒 | 重试 PostgreSQL 中待处理的 Redis 授权缓存失效事件 |
| `CommercialBillingWorker` | 60 秒 | 生成续费应收与催缴提醒、投递启用渠道并执行已审批退款 |
后台任务支持 `content_import``content_export``asset_security_scan``tenant_export``statistics_aggregation``commerce_reconciliation``tenant_domain_recheck`。安全扫描通过 ClamAV `INSTREAM` 协议流式处理对象;未通过扫描或扫描不可用时资源访问 fail-closed。
即时任务与 `RunAfter` 延时任务统一写入 `background_jobs`。后台任务用 `FOR UPDATE SKIP LOCKED` 和五分钟租约协调;六个周期处理器使用 PostgreSQL advisory lock允许部署多个 Worker 实例而不重复执行同一周期循环。商业续费、应收、退款和催缴投递仍以 PostgreSQL 表为权威,不引入消息代理。
## 数据与持久化
- 数据库使用标准 PostgreSQL普通 schema 由 EF Core entity、Fluent Configuration 和 Migration 管理。
- 当前模型启用 `citext``ltree``pg_trgm` 扩展,并统一映射为 `snake_case`
- Data Protection key ring 由 API 持久化到 PostgreSQL非 Development 必须使用 X509 证书保护。
- 后台任务状态、执行时间、重试和结果由 `background_jobs` 持久化。
- PostgreSQL 不启用 RLS租户隔离由应用和数据库多层共同保证详见[认证、授权与租户隔离](security-and-tenancy.md)。
## 当前业务模块
### 平台端
- 租户、Owner、域名、状态、员工、角色和审计告警。租户开通在单一事务中创建 Owner、Pending 主域名、默认试用和 v1 前端配置;域名 DNS/TLS Active 后才允许平台领取一次性 Owner 激活链接。
- 平台公共题库、分类节点、题目、导入和资源上传。
- SaaS Feature、额度定义、套餐版本、报价、订单、支付、退款、订阅、发票和催缴。
- 平台级 CRM、短信渠道/模板和支付应用配置。
### 租户端
- 员工、角色、权限、菜单、DataScope 和租户设置。
- 私有题库、公共题库引用、内容目录、词汇、手册、视频、分数线、站点内容、导入导出和资源。
- 学生、班级、CRM 跟进、监管规则、报表和审计。
- 学生商城、订单、支付、退款、优惠券、积分、推广和分佣。
- 租户 SaaS 目录、账务、订阅、用量、发票和 onboarding 状态。
- 身份、短信、对象存储、支付、通知和 AI 的租户 Provider 配置边界。
### 学生端
- Host 对应的运行时品牌、导航、Feature 和登录方式 bootstrap。
- 账号登录、个人资料、通知、签到和积分。
- 题目目录、练习会话、作答、收藏、错题、视频播放和进度。
- 学生商品、订单、支付、优惠券、权益和推广关系。
是否存在某个具体操作,应以 Controller 和 OpenAPI 为准,不能仅凭本节的模块名称推断。
## 外部服务边界
Application 通过接口表达身份、短信、对象存储、支付、通知、域名和 AI 能力Infrastructure 当前包含自托管身份、阿里云短信/OSS、微信、支付宝、站内通知、DNS JSON 查询和 HTTP 网关实现。
租户级 Provider 元数据和密钥分别存入 `TenantExternalProvider``TenantSecret`。密钥由 32 字节 master key 加密API 不应把明文、`SecretRef` 或 Provider 内部 payload 返回给客户端。