125 lines
6.0 KiB
Markdown
125 lines
6.0 KiB
Markdown
# 系统架构与业务边界
|
||
|
||
本文描述当前仓库的实际代码结构和运行时职责。接口路径、DTO 和响应模型以运行时 OpenAPI 为准。
|
||
|
||
## 分层与依赖
|
||
|
||
```text
|
||
+------------------+
|
||
| Tiku.Contracts |
|
||
+--------^---------+
|
||
|
|
||
+-----------+ +---------------+---------------+
|
||
| Tiku.Api | | Tiku.Worker / Tiku.DbMigrator |
|
||
+-----+-----+ +---------------+---------------+
|
||
| |
|
||
+-------------+-------------+
|
||
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 与 Contracts。
|
||
- `Tiku.Contracts` 保存 API 与 Worker 使用的版本化消息 DTO,不引用 HTTP、EF Core 或 Provider SDK。
|
||
- `Tiku.Api`、`Tiku.Worker` 和 `Tiku.DbMigrator` 是独立运行入口。
|
||
|
||
## 运行时组件
|
||
|
||
### API
|
||
|
||
`Tiku.Api/Program.cs` 只负责组合服务、构建应用和启用请求管线。管线的关键顺序是:
|
||
|
||
```text
|
||
Forwarded Headers
|
||
-> HTTPS / 压缩 / 静态文件
|
||
-> Routing / CORS
|
||
-> Host 租户解析
|
||
-> 浏览器 CSRF
|
||
-> JWT 认证 / 认证专用限流 / 全局限流
|
||
-> 当前用户上下文 / 授权
|
||
-> SaaS Feature 校验
|
||
-> Output Cache
|
||
-> Controllers
|
||
```
|
||
|
||
OpenAPI 和 Scalar 只在 Development 映射。平台管理静态文件由 `Tiku.Api/wwwroot` 同源托管,默认入口是 `/platform-admin/`。
|
||
|
||
### 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 都不自动迁移数据库。
|
||
|
||
### Worker
|
||
|
||
`Tiku.Worker` 当前注册四个独立 Hosted Service:
|
||
|
||
| Worker | 周期 | 当前职责 |
|
||
| --- | --- | --- |
|
||
| `TenantDomainWorker` | `TenantDomains:PollSeconds`,限制为 10~3600 秒 | 校验自定义域名 CNAME/TXT,调用网关 TLS 接口并失效租户缓存 |
|
||
| `SaasSubscriptionWorker` | 60 秒 | 处理到期、宽限期等 SaaS 订阅生命周期 |
|
||
| `FeatureUsageWorker` | `FeatureUsageReconciliation:IntervalMinutes`,限制为 1~1440 分钟 | 按真实业务数据校准租户 Feature 用量 |
|
||
| `BackgroundJobsWorker` | 2 秒,4 个分区 | 租约处理 PostgreSQL 中的延时/待执行后台任务;未配置 RabbitMQ 时也处理即时任务 |
|
||
|
||
后台任务当前支持 `content_import`、`content_export`、`statistics_aggregation`、`commerce_reconciliation` 和 `tenant_domain_recheck`。`asset_security_scan` 会明确失败,直到配置实际扫描 Provider;不能把它描述为已接通扫描服务。
|
||
|
||
配置 RabbitMQ 后,即时安全事件和后台任务请求使用 MassTransit;API 使用 EF Bus Outbox,Worker Consumer 使用 EF inbox/outbox。延时任务仍由 PostgreSQL `RunAfter` 和租约 Worker 处理。
|
||
|
||
## 数据与持久化
|
||
|
||
- 数据库使用标准 PostgreSQL,普通 schema 由 EF Core entity、Fluent Configuration 和 Migration 管理。
|
||
- 当前模型启用 `citext`、`ltree` 和 `pg_trgm` 扩展,并统一映射为 `snake_case`。
|
||
- Data Protection key ring 由 API 持久化到 PostgreSQL;非 Development 必须使用 X509 证书保护。
|
||
- MassTransit inbox/outbox 表与业务表处于同一 `TikuDbContext`。
|
||
- PostgreSQL 不启用 RLS;租户隔离由应用和数据库多层共同保证,详见[认证、授权与租户隔离](security-and-tenancy.md)。
|
||
|
||
## 当前业务模块
|
||
|
||
### 平台端
|
||
|
||
- 租户、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 返回给客户端。
|