feat: complete SaaS commercial delivery workflows

This commit is contained in:
2026-08-01 15:26:21 +08:00
parent 46abf4d62f
commit d58bcd97e9
56 changed files with 27505 additions and 181 deletions

View File

@@ -65,7 +65,7 @@ API 不自动迁移数据库。
### Worker 后台处理
`Tiku.Worker` 独立注册个 Hosted ServiceAPI 进程不注册后台循环:
`Tiku.Worker` 独立注册个 Hosted ServiceAPI 进程不注册后台循环:
| Worker | 周期 | 当前职责 |
| --- | --- | --- |
@@ -73,10 +73,12 @@ API 不自动迁移数据库。
| `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 实例而不重复执行同一周期循环。
即时任务与 `RunAfter` 延时任务统一写入 `background_jobs`。后台任务用 `FOR UPDATE SKIP LOCKED` 和五分钟租约协调;个周期处理器使用 PostgreSQL advisory lock允许部署多个 Worker 实例而不重复执行同一周期循环。商业续费、应收、退款和催缴投递仍以 PostgreSQL 表为权威,不引入消息代理。
## 数据与持久化

View File

@@ -7,7 +7,7 @@
| 进程 | PostgreSQL | Redis | 说明 |
| --- | --- | --- | --- |
| `Tiku.Api` | 必需 | Development 可选Production 必需 | 提供 HTTP API、认证、授权和缓存 |
| `Tiku.Worker` | 必需 | 不需要 | 承载周期任务、任务队列、租户导出和安全扫描 |
| `Tiku.Worker` | 必需 | Development 可选Production 必需 | 承载周期任务、任务队列、商业账务和授权缓存失效重试 |
| `Tiku.DbMigrator` | 必需 | 不需要 | 执行 Migration、内置目录 seed 和管理员引导 |
Development 未配置 Redis 时,安全服务使用进程内/数据库防线。Production 不允许 Redis 降级;后台任务在所有环境统一使用 PostgreSQL。
@@ -84,13 +84,18 @@ Redis key 使用环境前缀;配置解析会强制 `AbortOnConnectFail=false`
"Enabled": true,
"BatchSize": 100,
"IntervalMinutes": 60
},
"CommercialBilling": {
"Enabled": true,
"BatchSize": 100,
"AllowedWebhookHosts": []
}
}
```
域名只有在 `AllowedCnameTargets`、DNS JSON endpoint、Gateway URL 和 API key 配置完成后,才可能从 Pending/Failed 进入 Active。仅 DNS 验证成功不代表 TLS 已就绪。
后台任务状态和 `RunAfter` 存在 PostgreSQL。Worker 使用 `FOR UPDATE SKIP LOCKED`、五分钟租约和有限重试处理即时、延时及失败待重试任务;周期循环使用 PostgreSQL advisory lock 防止多实例重复执行。`Worker:Enabled=false` 会关闭全部四个后台循环,通常只用于测试或维护。
后台任务状态和 `RunAfter` 存在 PostgreSQL。Worker 使用 `FOR UPDATE SKIP LOCKED`、五分钟租约和有限重试处理即时、延时及失败待重试任务;周期循环使用 PostgreSQL advisory lock 防止多实例重复执行。当前六个循环分别处理域名、订阅生命周期、Feature 用量、通用任务、授权缓存失效和商业账务。商业账务会生成续费应收、提醒、外部催缴投递和已审批退款Webhook 必须使用 HTTPS、Host allowlist、签名和私网地址拒绝。`Worker:Enabled=false` 会关闭全部循环,通常只用于测试或维护。
API 和 Worker 必须使用同一 PostgreSQL 数据库与一致的对象存储配置。迁移必须在两者启动前由 `Tiku.DbMigrator` 单独执行。
@@ -101,7 +106,21 @@ docker build -f Tiku.Api/Dockerfile -t tiku-api .
docker build -f Tiku.Worker/Dockerfile -t tiku-worker .
```
API 和 Worker 应独立设置副本数与资源限制。先完成 Migration再启动 Worker最后开放 API 流量;不要在容器入口自动执行 Migration。
API 和 Worker 应独立设置副本数与资源限制。发布顺序固定为 `Tiku.DbMigrator``Tiku.Worker``Tiku.Api``Tiku.PlatformAdmin.Web`;不要在容器入口自动执行 Migration。
## 发布门禁、备份恢复和告警
`.github/workflows/ci.yml` 执行 restore、build、完整测试、format、EF 模型漂移、幂等 Migration SQL、平台前端检查、OpenAPI 生成漂移、API/Worker 镜像构建和 `git diff --check`。本地发布前应执行相同检查。
PostgreSQL 恢复演练使用标准 `PGHOST``PGPORT``PGUSER``PGPASSWORD` 环境变量,并通过 `TIKU_BACKUP_SOURCE_DATABASE` 指定源数据库:
```bash
TIKU_BACKUP_SOURCE_DATABASE=tiku scripts/rehearse-postgres-restore.sh
```
脚本只创建并删除名称受限的 `tiku_restore_drill_*` 临时数据库,校验 Migration 历史与租户表后自动清理。生产演练仍应在隔离实例上执行,并把耗时、备份大小和恢复点记录到变更单。
`deploy/monitoring/postgres-exporter-queries.yaml` 提供 Worker stale、最老 Pending Job、支付回调失败、逾期应收和催缴死信指标`deploy/monitoring/commercial-alerts.yaml` 提供对应 Prometheus 告警。部署时必须让 PostgreSQL exporter 使用只读监控账号,并先在预发布验证规则名称与采集前缀。
## 安全与网络配置
@@ -177,8 +196,6 @@ Readiness 为绿色不等于认证授权、跨租户隔离或后台任务恢复
- Owner 转移目标必须是已有 Active 成员,并在同一事务内同步成员角色和后台角色绑定。
- 发布前至少演练一次导出可下载、归档阻断条件、归档、恢复和 Owner 转移。
## 从 RabbitMQ 版本切换
## 后台任务权威边界
移除消息表的 Migration 与旧 API/Worker 不兼容。发布时使用维护窗口:停止旧 API 和 Worker确认 RabbitMQ Consumer 已退出并备份 PostgreSQL运行 `Tiku.DbMigrator`,再部署新 API 与新 Worker。未消费的后台任务消息可丢弃因为对应任务记录已经写入 `background_jobs`;安全消息不保存授权真相
切换时不强制重置 `processing` 任务。旧租约最多五分钟后由新 Worker 接管。回滚需要先停止新 API 与 Worker执行 Migration Down 重建空 inbox/outbox 表,再恢复 RabbitMQ 配置和旧 API/Worker历史消息不会恢复。
当前运行时不依赖 RabbitMQ也没有消息 inbox/outbox 发布链路。即时、延时、重试和商业任务均以 PostgreSQL 状态为准Redis 不保存任务或商业状态真相。回滚时不得通过恢复旧消息表绕过当前数据库状态机