feat: scaffold supabase multi-tenant backend

This commit is contained in:
Codex
2026-06-21 21:54:43 +08:00
commit c9c767c7bd
99 changed files with 36660 additions and 0 deletions

View File

@@ -0,0 +1,177 @@
# 后端重构进度
## 已完成
- Docker Desktop + Supabase local 已可用。
- API Docker 镜像 `tiku-saas-dev-api:latest` 已可构建,并可从容器连接宿主 Supabase PostgreSQL。
- API 已按 `core/features` 分层:
- `auth`:短信验证码登录、迁移期 session、OAuth provider 预留。
- `catalog`公开题库、地区、科目、手册、商品、SVIP 套餐、资料资源只读/下载接口。
- `learning`:练习 session、答题记录、错题、收藏、背单词进度/收藏/统计。
- `profile`:学生个人中心、目标院校/专业、会员状态、统计聚合、最近练习。
- `scoreline`:分数线字段、院校、专业、记录、趋势、年份。
- `video`:题目视频讲解、批量预加载、通用视频搜索。
- `commerce`:订单、支付确认、激活码兑换、权益查询。
- `referral`:销售/代理邀请码、首绑客资保护、销售统计、团队关系、CRM 队列。
- `platform-admin`平台方租户管理、SaaS 套餐、订阅、账单、服务费收款、使用量。
- `tenant-admin`:租户资料、品牌、公开设置、域名、支付账户、登录 provider、私密密钥掩码、活动内容、激活码批次、优惠券、成员管理、权限矩阵、审计查询。
- `tenant-content`:租户后台题目、视频、分数线、单词、知识手册、资料资源、题目 JSON 导入维护。
- `tenant`:域名/租户解析。
- `src/services/supabaseApi.ts` 已加入新 API 客户端方法,供旧 Web 逐步替换和后续 Taro 复用。
- 已新增 `npm run db:smoke-seed`,用于 `supabase:reset` 后恢复最小烟测数据。
- 已新增 `npm run smoke:core-api`,用于验证个人中心、分数线、题目视频、背单词进度/收藏等学生端核心 API。
- 已新增 `npm run test:api`,自动 seed、构建、启动临时 API并断言核心学生端接口、租户隔离、资源权限和题目导入。
## 已验证接口
```text
GET /health
POST /api/auth/sms/send
POST /api/auth/sms/verify
GET /api/auth/me
POST /api/auth/logout
POST /api/auth/oauth/wechat
POST /api/auth/oauth/wechat-miniapp
POST /api/auth/oauth/qq
GET /api/tenant/resolve
GET /api/platform-admin/overview
GET /api/platform-admin/plans
GET /api/platform-admin/tenants
POST /api/platform-admin/tenants
GET /api/platform-admin/tenants/detail
PATCH /api/platform-admin/tenants/status
PUT /api/platform-admin/tenants/billing-profile
POST /api/platform-admin/subscriptions
GET /api/platform-admin/invoices
POST /api/platform-admin/invoices
POST /api/platform-admin/invoices/from-subscription
POST /api/platform-admin/invoices/payments/manual-confirm
GET /api/platform-admin/usage
POST /api/platform-admin/usage
GET /api/catalog/*
GET /api/catalog/assets
GET /api/catalog/assets/download
POST /api/learning/answers
GET /api/learning/favorites/questions
POST /api/learning/favorites/questions
GET /api/learning/wrong-questions
GET /api/learning/vocabulary/progress
POST /api/learning/vocabulary/progress
GET /api/learning/vocabulary/favorites
POST /api/learning/vocabulary/favorites
GET /api/learning/vocabulary/stats
GET /api/profile/me
PATCH /api/profile/me
GET /api/scoreline/fields
GET /api/scoreline/schools
GET /api/scoreline/majors
GET /api/scoreline/records
GET /api/scoreline/trend
GET /api/scoreline/years
GET /api/questions/{questionId}/videos
POST /api/questions/videos/batch
GET /api/videos/search
POST /api/tenant-content/questions
PATCH /api/tenant-content/questions
GET /api/tenant-content/assets
PUT /api/tenant-content/assets
POST /api/tenant-content/assets/sign-upload
POST /api/tenant-content/assets/sign-download
POST /api/tenant-content/imports/preview/questions
POST /api/tenant-content/imports/questions
GET /api/tenant-content/imports
GET /api/tenant-content/imports/issues
PUT /api/tenant-content/videos
POST /api/tenant-content/question-videos
PUT /api/tenant-content/scoreline/schools
PUT /api/tenant-content/scoreline/majors
PUT /api/tenant-content/scoreline/fields
PUT /api/tenant-content/scoreline/records
PUT /api/tenant-content/vocabulary-units
PUT /api/tenant-content/vocabulary-words
PUT /api/tenant-content/handbook-subjects
PUT /api/tenant-content/handbook-chapters
PUT /api/tenant-content/handbook-entries
POST /api/commerce/orders
GET /api/commerce/orders
POST /api/commerce/payments/manual-confirm
POST /api/commerce/activation-codes/redeem
GET /api/commerce/entitlements
GET /api/commerce/entitlements/check
POST /api/referral/invite-code
POST /api/referral/resolve
POST /api/referral/track-event
POST /api/referral/bind
GET /api/referral/stats
GET /api/referral/sales-stats
GET /api/referral/sales-clients
POST /api/referral/manual-bind
GET /api/referral/team
PUT /api/referral/team
POST /api/referral/qrcode
GET /api/crm/config
PUT /api/crm/config
GET /api/crm/queue
GET /api/tenant-admin/permissions
GET /api/tenant-admin/overview
PUT /api/tenant-admin/branding
PUT /api/tenant-admin/settings
GET /api/tenant-admin/domains
POST /api/tenant-admin/domains
GET /api/tenant-admin/payment-accounts
PUT /api/tenant-admin/payment-accounts
GET /api/tenant-admin/auth-providers
PUT /api/tenant-admin/auth-providers
GET /api/tenant-admin/secrets
PUT /api/tenant-admin/secrets
GET /api/tenant-admin/banners
PUT /api/tenant-admin/banners
GET /api/tenant-admin/faqs
PUT /api/tenant-admin/faqs
GET /api/tenant-admin/announcements
PUT /api/tenant-admin/announcements
GET /api/tenant-admin/code-batches
PUT /api/tenant-admin/code-batches
GET /api/tenant-admin/activation-codes
PUT /api/tenant-admin/activation-codes
POST /api/tenant-admin/activation-codes/generate
GET /api/tenant-admin/coupons
PUT /api/tenant-admin/coupons
GET /api/tenant-admin/members
PUT /api/tenant-admin/members
POST /api/tenant-admin/members/disable
GET /api/tenant-admin/audit-logs
```
## 迁移期约定
- 当前写接口用 `x-tenant-id``x-user-id` 做迁移期上下文。
- `auth` 当前签发迁移期 `tk_` sessiontoken hash 存在 `app_private.auth_sessions`;后续接 Supabase Auth 后,`x-user-id` 要替换为 JWT 用户身份解析。
- 短信验证码只保存 HMAC hash不保存明文本地 `mock` provider 才会返回 `debugCode`
- `platform-admin` 当前用 `x-platform-admin-key` 做迁移期保护,生产后必须替换为平台管理员 JWT/服务端会话。
- B 端合作商年费/服务费使用 `tenant_invoices``tenant_invoice_items``tenant_invoice_payments`,不与 C 端学生订单混表。
- 订单金额以后端套餐价格为准,不信任前端传价。
- 激活码兑换和支付成功都走同一套 `grantSvipEntitlement` 权益开通逻辑。
- 租户支付账户、短信、OAuth 登录配置接口只保存公开配置;密钥进入 `app_private.tenant_secrets` 或生产 KMS/VaultAPI 只返回 `secretRef` 和掩码状态。
- `tenant-admin` 采用角色默认权限 + `tenant_memberships.permissions` 覆盖的权限矩阵。成员可进入后台,但每个接口会校验具体权限点;学生和跨租户成员会被拒绝。
- 当前默认角色:`tenant_owner`/`tenant_admin` 全权限,`tenant_operator` 可维护内容和活动,`teacher` 可维护内容,`sales` 可维护激活码和优惠券,`agent` 只读部分兑换码/优惠券。
- 销售/代理客资采用首绑保护:普通扫码/分享事件不会覆盖已有归属,只有具备 `referral:write` 的租户成员可手动强制补绑。
- CRM 当前完成配置、密钥入私密表、客资入队和队列查询;真实 webhook 发送、重试、签名在后续 `apps/worker` 中实现。
- 内容资源当前完成台账、租户后台维护、上传/下载签名占位和学生端 SVIP 下载权限真实对象存储签名、PDF 预览渲染和防盗链在 provider/worker 中实现。
- 题目批量导入当前支持 JSON 数组预览、逐行 issue、job/item 台账、执行导入和幂等跳过Excel/CSV、单词/手册/分数线导入会复用同一套 `content_import_jobs` 管线。
## 下一步
1. 完善内容导入和文件上传Excel/CSV、单词、手册、分数线、视频导入真实 OSS/COS/Supabase Storage 签名。
2. 接入真实短信 provider阿里云/腾讯云,密钥放 `app_private.tenant_secrets` 或生产 Vault。
3. 接入真实 OAuth provider微信网页、微信小程序、QQ并处理旧 PocketBase 身份映射。
4. 增加真实支付 providerXPay、微信支付、支付宝并完善 webhook 幂等。
5. 增加 `apps/worker`支付补偿、CRM webhook、日报统计、导入后检查。
6. 开始 Taro scaffold`supabaseApi` 抽到跨端包或适配层。
## 测试命令
```text
npm run test:api
npm run check:refactor
```