forked from wangziqi/gongxue-base
feat: scaffold supabase multi-tenant backend
This commit is contained in:
28
docs/refactor/README.md
Normal file
28
docs/refactor/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# SaaS 重构工作区
|
||||
|
||||
这个目录记录从 PocketBase 单体项目迁移到 Supabase/PostgreSQL + 新 API + Taro 学生端的重构过程。
|
||||
|
||||
当前第一阶段目标:
|
||||
|
||||
- 本地 Supabase 能启动并创建多租户 PostgreSQL schema。
|
||||
- 新 API 能连接数据库并解析租户。
|
||||
- PocketBase 的 `docs/pb_schema.json` 能被解析,后续真实数据导出后可导入到新库。
|
||||
- 先保留旧 React Web 项目,逐步把学生端和后台接到新 API。
|
||||
|
||||
关键文件:
|
||||
|
||||
- `supabase/config.toml`:本地 Supabase 配置。
|
||||
- `supabase/migrations/202606210001_core_multitenant_schema.sql`:第一版商用级多租户 schema。
|
||||
- `apps/api`:新业务 API 服务,内部按 `src/core` 和 `src/features` 分层。
|
||||
- `packages/config`、`packages/db`、`packages/domain`:新系统共享基础包。
|
||||
- `scripts/import-pocketbase`:PocketBase schema/数据导入工具。
|
||||
- `docker-compose.api.yml`、`apps/api/Dockerfile`:本地 Docker API 运行入口。
|
||||
- `docs/refactor/architecture.md`:新重构目录边界和工程规范。
|
||||
|
||||
下一步优先级:
|
||||
|
||||
1. 导出 PocketBase 真实数据到 `pb_export/*.json`。
|
||||
2. 执行 `npm run pb:import:json` 和 `npm run pb:import:validate`。
|
||||
3. 按学生端页面逐步从 PocketBase SDK 切换到 `src/services/supabaseApi.ts`。
|
||||
4. 为订单、支付、权益开通补齐 API 写入流程和 webhook 幂等处理。
|
||||
5. 新建 Taro 学生端时复用同一套租户解析和业务 API,不另起一套后端。
|
||||
70
docs/refactor/api-structure.md
Normal file
70
docs/refactor/api-structure.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# API 目录规范
|
||||
|
||||
`apps/api` 是 Web、Taro 小程序、管理后台共用的业务 API。所有复杂业务写入都进入这里,前端不直接写 Supabase 表。
|
||||
|
||||
## 当前结构
|
||||
|
||||
```text
|
||||
apps/api/src/
|
||||
server.ts HTTP 服务入口,只负责请求生命周期
|
||||
core/
|
||||
config.ts 环境变量和运行配置
|
||||
db.ts PostgreSQL 连接池和查询封装
|
||||
http.ts CORS、JSON 响应、统一错误
|
||||
router.ts 汇总注册各业务域路由
|
||||
features/
|
||||
auth/ 短信验证码、迁移期 session、OAuth provider 预留
|
||||
health/ 健康检查
|
||||
tenant/ 租户解析、品牌配置、域名识别
|
||||
catalog/ 公开题库、科目、手册、商城、资料资源只读接口
|
||||
learning/ 答题、错题、收藏、练习 session
|
||||
commerce/ 订单、支付确认、激活码、权益
|
||||
referral/ 销售/代理客资追踪、首绑保护、团队关系、CRM 队列
|
||||
platform-admin/ 平台方 SaaS 租户、订阅、账单、使用量
|
||||
tenant-admin/ 租户品牌、域名、公开设置、登录/商户配置、成员权限、活动/兑换码运营
|
||||
tenant-content/ 租户后台内容维护:题目、视频、分数线、单词、知识手册、资料资源、批量导入
|
||||
```
|
||||
|
||||
## 新业务域落位
|
||||
|
||||
后续按下面方式增加目录:
|
||||
|
||||
```text
|
||||
features/
|
||||
auth/ 登录、绑定手机、OAuth 回调、会话换取
|
||||
learning/ 答题记录、错题、收藏、学习进度
|
||||
commerce/ 商品、订单、支付、退款、权益开通
|
||||
referral/ 销售/代理增长链路、客资归属、分佣依据、CRM 入队
|
||||
platform-admin/ 平台租户管理、年费、服务费、账务审计
|
||||
tenant-admin/ 合作商后台配置、品牌、域名、收款账户、登录 provider、密钥掩码、成员权限、审计、活动、兑换码、优惠券
|
||||
tenant-content/ 合作商内容维护、批量导入、资源绑定、内容审计
|
||||
```
|
||||
|
||||
每个 feature 默认包含:
|
||||
|
||||
```text
|
||||
index.ts 导出 RouteDefinition[]
|
||||
routes.ts HTTP handler
|
||||
service.ts 业务编排和事务
|
||||
repository.ts SQL 查询和写入
|
||||
types.ts 仅本领域使用的类型
|
||||
```
|
||||
|
||||
## 规则
|
||||
|
||||
- `server.ts` 不直接 import 业务 handler,只 import `createRouter()`。
|
||||
- `features/*/index.ts` 只注册路由,不写 SQL。
|
||||
- `routes.ts` 做参数解析、鉴权上下文、HTTP 错误,不写复杂事务。
|
||||
- `service.ts` 承接订单、支付、权益、答题判定等业务规则。
|
||||
- `repository.ts` 才写 SQL,所有 SQL 必须带明确租户边界。
|
||||
- 可预期错误用 `HttpError`,生产环境不向前端暴露内部异常。
|
||||
- 写接口必须考虑幂等、审计和租户隔离;支付 webhook 必须先设计幂等键。
|
||||
- 迁移期接口可用 `x-user-id` 标识学生用户;接 Supabase Auth 后统一替换为 JWT 解析。
|
||||
- 登录类接口先使用 `Authorization: Bearer tk_*` 迁移期 session;session 明文只返回客户端,数据库只保存 hash。
|
||||
- 平台运营接口使用 `x-platform-admin-key` 作为临时保护;正式上线前要迁到平台管理员 JWT 和审计日志。
|
||||
- `platform-admin` 管平台与合作商之间的 SaaS 账务,`tenant-admin` 管合作商自己的品牌、域名、公开配置、登录/商户配置、活动和兑换码,`tenant-content` 管合作商自己的题库和学习内容维护。
|
||||
- `tenant-admin` 的敏感配置必须拆分:公开字段进入 `config_public`,商户密钥、短信密钥、OAuth app secret 进入 `app_private.tenant_secrets` 或生产 KMS/Vault;对前端只返回 `secretRef` 和掩码状态。
|
||||
- `tenant-admin` 权限由 `tenant_memberships.role` 的默认权限和 `permissions` JSON 覆盖共同决定;后端接口必须校验具体权限点,不能只依赖前端菜单隐藏。
|
||||
- `referral` 是增长/客资业务域,负责邀请码、扫码事件、首绑保护、销售/代理团队归属和 CRM 入队;真实 CRM webhook 发送应由 worker 处理,API 只负责幂等入队。
|
||||
- 资料、PDF、视频等对象存储资源必须先进入 `content_assets` 台账,再通过 API 做权限校验和签名 URL 下发;前端不能直接拼 OSS/COS/Supabase Storage 地址。
|
||||
- 批量导入必须先写 `content_import_jobs/items/issues`,保留原始 payload、规范化 payload、逐行问题和审计记录;同步 API 当前支持题目 JSON,Excel/CSV 和其它内容类型应接入同一管线。
|
||||
97
docs/refactor/architecture.md
Normal file
97
docs/refactor/architecture.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# 重构工程结构
|
||||
|
||||
新系统按 SaaS 商用架构组织,不再把 PocketBase 旧项目作为长期主结构。旧 React/PocketBase 代码先保留为兼容层,新的后端、数据库、导入器和共享包独立放置。
|
||||
|
||||
## 目录边界
|
||||
|
||||
```text
|
||||
apps/
|
||||
api/ 新业务 API,前端和小程序都通过它访问业务数据
|
||||
src/core/ 配置、HTTP、错误响应、路由注册、数据库访问等基础层
|
||||
src/features/ 领域模块,按 catalog、tenant、health 等拆分
|
||||
src/features/platform-admin/
|
||||
平台方管理合作商租户、订阅、账单和使用量
|
||||
|
||||
packages/
|
||||
config/ 环境变量、默认租户、默认数据库连接等共享配置
|
||||
db/ PostgreSQL 连接池和 query/queryOne
|
||||
domain/ 租户角色、订单状态、权益范围等领域常量
|
||||
|
||||
supabase/
|
||||
config.toml 本地 Supabase 配置
|
||||
migrations/ PostgreSQL schema、RLS、触发器、索引
|
||||
seed.sql 本地主租户 seed
|
||||
|
||||
scripts/
|
||||
import-pocketbase/ PocketBase schema 风险分析、JSON 导入、导入后校验
|
||||
smoke-seed.js 本地 reset 后的最小业务烟测数据
|
||||
|
||||
src/
|
||||
services/supabaseApi.ts 旧 Web 前端迁向新 API 的兼容客户端
|
||||
tenant.config.ts 租户解析配置,优先读新 API
|
||||
```
|
||||
|
||||
## 设计原则
|
||||
|
||||
- `apps/api` 是业务 API 层,复杂交易、支付、权益、租户解析都应该在这里做,不让前端直接操作表。
|
||||
- 平台方与合作商之间的 SaaS 收费,使用 `platform_saas_plans`、`tenant_subscriptions`、`tenant_invoices`、`tenant_invoice_payments`;学生 C 端会员订单仍使用 `orders/payments/entitlements`。
|
||||
- `apps/api/src/server.ts` 只负责 HTTP 生命周期;业务路由统一放在 `features/*`,由 `core/router.ts` 汇总注册。
|
||||
- API 对外错误必须走 `HttpError` 或统一错误响应,生产环境不向前端泄露数据库异常和内部栈信息。
|
||||
- `packages/*` 放可复用基础能力,后续 Taro 小程序、管理后台 API、异步 worker 都复用这里。
|
||||
- `supabase/migrations` 是数据库事实来源,旧 PB 字段不能绕过迁移规范直接进正式表。
|
||||
- `scripts/import-pocketbase` 是一次性和可重复迁移工具,必须保持幂等,导入后必须跑验证。
|
||||
- `src/services/pocketbase.ts` 和 `src/services/mockBackend.ts` 属于旧兼容层,后续按页面逐步替换到 `src/services/supabaseApi.ts`。
|
||||
|
||||
## 本地开发顺序
|
||||
|
||||
```bash
|
||||
npm run supabase:start
|
||||
npm run supabase:reset
|
||||
npm run db:smoke-seed
|
||||
npm run dev:api
|
||||
```
|
||||
|
||||
也可以只把 API 放进 Docker 容器运行。Supabase 仍由 Supabase CLI 管理,API 容器通过宿主机端口连接本地 PostgreSQL:
|
||||
|
||||
```bash
|
||||
npm run supabase:start
|
||||
npm run docker:api:build
|
||||
npm run docker:api:up
|
||||
```
|
||||
|
||||
如果 Docker 拉取 `node:20-alpine` 超时,先配置 Docker Desktop 镜像源或代理,再重试 `npm run docker:api:build`。
|
||||
|
||||
验证 API:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8787/health
|
||||
curl "http://127.0.0.1:8787/api/tenant/resolve?host=localhost"
|
||||
curl "http://127.0.0.1:8787/api/catalog/regions?tenantId=00000000-0000-0000-0000-000000000001"
|
||||
```
|
||||
|
||||
导入旧数据:
|
||||
|
||||
```bash
|
||||
npm run pb:import:json
|
||||
npm run pb:import:validate
|
||||
```
|
||||
|
||||
## 当前已验证
|
||||
|
||||
- Docker Desktop 可用。
|
||||
- Supabase 本地容器可启动。
|
||||
- `supabase db reset` 可完整执行三份 migration 和 seed。
|
||||
- `supabase db reset` 可完整执行全部 migration 和 seed。
|
||||
- `npm run db:smoke-seed` 可恢复最小业务烟测数据。
|
||||
- `platform-admin` 可完成平台概览、租户创建、订阅、账单生成、人工收款确认、使用量记录。
|
||||
- API `/health` 可连 PostgreSQL 并返回 `db: ok`。
|
||||
- API `/api/tenant/resolve?host=localhost` 可解析主租户。
|
||||
- API 构建产物入口 `apps/api/dist/apps/api/src/server.js` 已验证可启动。
|
||||
- 空库执行 `pb:import:validate` 为 0 failures、0 warnings。
|
||||
|
||||
## 下一阶段拆分
|
||||
|
||||
- `apps/api/src/features` 继续按业务域扩展:真实支付 provider、真实 OAuth provider、平台审计和 worker。
|
||||
- `src/services/supabaseApi.ts` 逐页替换旧 PB 只读接口,优先学生端和小程序共用页面。
|
||||
- 新增 `apps/worker` 承接 CRM webhook、支付补偿、日报统计、导入后异步检查。
|
||||
- 新增 `apps/taro` 后,所有租户解析和公开业务读取都复用 API,不单独维护另一套后端逻辑。
|
||||
94
docs/refactor/auth-payment-provider-plan.md
Normal file
94
docs/refactor/auth-payment-provider-plan.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# 国内认证与支付接入方案
|
||||
|
||||
## Supabase 边界
|
||||
|
||||
Supabase 适合承担 PostgreSQL、RLS、Auth、Edge Functions、Webhook/Hooks 等底座能力,但它不是中国大陆支付网关,也不会内置微信支付、支付宝、阿里云短信、腾讯云短信这一整套商用配置。
|
||||
|
||||
对本项目更稳妥的落位是:
|
||||
|
||||
- Supabase/PostgreSQL:保存多租户、订单、支付事件、权益、审计、登录事件。
|
||||
- `apps/api`:实现业务 API、短信 provider、OAuth provider、支付 provider、回调验签和幂等。
|
||||
- `app_private.tenant_secrets` 或生产 Vault/KMS:保存租户级密钥。
|
||||
- `tenant_auth_providers`、`tenant_payment_accounts`:只保存非敏感公开配置。
|
||||
|
||||
Supabase Auth 可继续作为最终 JWT 用户体系目标;本地重构期先用 `app_private.auth_sessions` 签发 `tk_` session,保证 Web/Taro 能跑通端到端流程。
|
||||
|
||||
## 当前已实现
|
||||
|
||||
- `POST /api/auth/sms/send`:手机号验证码发送,验证码只保存 HMAC hash。
|
||||
- `POST /api/auth/sms/verify`:验证码登录,自动创建或复用 `platform_users`。
|
||||
- `GET /api/auth/me`:通过 Bearer token 获取当前用户。
|
||||
- `POST /api/auth/logout`:吊销迁移期 session。
|
||||
- `POST /api/auth/oauth/wechat`、`/wechat-miniapp`、`/qq`:provider 占位,已固定错误码 `PROVIDER_NOT_CONFIGURED`。
|
||||
- `tenant_auth_providers`:租户级公开认证配置。
|
||||
- `sms_verification_codes`:验证码审计表,不保存明文 code。
|
||||
- `auth_login_events`:登录事件审计。
|
||||
- `app_private.auth_sessions`:迁移期 session token hash。
|
||||
|
||||
## 短信 Provider
|
||||
|
||||
本地默认是 `AUTH_SMS_PROVIDER=mock`,仅开发环境返回 `debugCode`。生产环境如果仍为 mock,会直接拒绝发送。
|
||||
|
||||
后续真实 provider:
|
||||
|
||||
- `aliyun`:接阿里云短信 `SendSms`,需要 AccessKey、签名、模板 ID。
|
||||
- `tencent`:接腾讯云短信 `SendSms`,需要 SecretId、SecretKey、SdkAppId、签名、模板 ID。
|
||||
|
||||
密钥策略:
|
||||
|
||||
- AccessKey/SecretKey 不进入 `tenant_settings.public_config`。
|
||||
- 租户级密钥写 `app_private.tenant_secrets(secret_scope='sms')` 或生产 Vault。
|
||||
- 前端只能看到 provider 是否启用、签名展示名、隐私协议链接等非敏感配置。
|
||||
|
||||
## 微信/QQ 登录
|
||||
|
||||
微信小程序登录应由前端传 `wx.login` code 到 `/api/auth/oauth/wechat-miniapp`,后端调用微信 `code2Session` 换取 openid/session_key/unionid,再落 `user_identities`。
|
||||
|
||||
微信网页 OAuth 和 QQ OAuth 也必须在后端完成 code 换 token、获取 openid/unionid、验错、账号合并和登录事件审计。旧 PocketBase hooks 中的邀请码/销售归属逻辑后续应拆到 `referral` feature,不继续堆在 auth 模块里。
|
||||
|
||||
## 支付 Provider
|
||||
|
||||
支付不走 Supabase 内置能力。推荐继续扩展 `commerce`:
|
||||
|
||||
- `POST /api/commerce/orders` 只负责创建订单,金额以后端套餐为准。
|
||||
- `POST /api/commerce/payments/:provider/create` 后续按 provider 创建支付参数或收银台地址。
|
||||
- `POST /api/commerce/payments/:provider/notify` 统一落 `payment_events`,先验签、再幂等、再更新订单和权益。
|
||||
- 支付成功继续复用 `grantSvipEntitlement`,避免微信/支付宝/XPay 各写一套开通逻辑。
|
||||
|
||||
B 端合作商年费、服务费、服务器资源费不走学生端 `orders`,而是走平台账务:
|
||||
|
||||
- `platform_saas_plans`:平台售卖给合作商的 SaaS 套餐。
|
||||
- `tenant_subscriptions`:合作商当前订阅状态。
|
||||
- `tenant_invoices`、`tenant_invoice_items`:合作商账单与明细。
|
||||
- `tenant_invoice_payments`:合作商账单收款记录。
|
||||
- `tenant_usage_records`:学生数、题量、存储等用量指标。
|
||||
|
||||
支持策略:
|
||||
|
||||
- 平台代收:平台商户号收款,再给租户结算。
|
||||
- 租户自收:每个租户配置自己的商户号和密钥。
|
||||
- 服务商模式:平台服务商统一管理子商户。
|
||||
|
||||
真实接入前需要先明确微信支付、支付宝或聚合支付是否允许你们销售的题库会员形态,以及小程序端是否涉及虚拟支付限制。
|
||||
|
||||
## 需要准备的资料
|
||||
|
||||
- 阿里云或腾讯云短信:签名、模板 ID、AccessKey/SecretKey、短信用途文案。
|
||||
- 微信小程序:AppID、AppSecret、主体信息、合法域名、用户手机号授权能力。
|
||||
- 微信网页/公众号:AppID、AppSecret、授权回调域名。
|
||||
- QQ 互联:AppID、AppKey、回调域名。
|
||||
- 微信支付:商户号、API v3 key、商户证书/平台证书、回调域名、AppID 绑定关系。
|
||||
- 支付宝:AppID、应用私钥、支付宝公钥、回调地址、网页/手机网站/当面付产品开通情况。
|
||||
- 每个合作商租户的收款模式:平台代收、租户自收或服务商子商户。
|
||||
|
||||
## 参考资料
|
||||
|
||||
- Supabase Phone Login: https://supabase.com/docs/guides/auth/phone-login
|
||||
- Supabase Auth Hooks: https://supabase.com/docs/guides/auth/auth-hooks
|
||||
- Supabase Social Login: https://supabase.com/docs/guides/auth/social-login
|
||||
- 阿里云短信 SendSms: https://help.aliyun.com/zh/sms/developer-reference/api-dysmsapi-2017-05-25-sendsms
|
||||
- 腾讯云短信 SendSms: https://cloud.tencent.com/document/api/382/55981
|
||||
- 微信小程序登录 code2Session: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
|
||||
- QQ 互联 OAuth: https://wiki.connect.qq.com/oauth2-0简介
|
||||
- 微信支付 API v3: https://pay.weixin.qq.com/doc/v3/merchant/4012791855
|
||||
- 支付宝开放平台: https://opendocs.alipay.com/
|
||||
177
docs/refactor/backend-progress.md
Normal file
177
docs/refactor/backend-progress.md
Normal 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_` session,token 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/Vault,API 只返回 `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. 增加真实支付 provider:XPay、微信支付、支付宝,并完善 webhook 幂等。
|
||||
5. 增加 `apps/worker`:支付补偿、CRM webhook、日报统计、导入后检查。
|
||||
6. 开始 Taro scaffold,把 `supabaseApi` 抽到跨端包或适配层。
|
||||
|
||||
## 测试命令
|
||||
|
||||
```text
|
||||
npm run test:api
|
||||
npm run check:refactor
|
||||
```
|
||||
43
docs/refactor/blueprint-coverage.md
Normal file
43
docs/refactor/blueprint-coverage.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# SaaS 蓝图覆盖矩阵
|
||||
|
||||
更新时间:2026-06-21 21:42
|
||||
|
||||
## 目标定位
|
||||
|
||||
新项目要覆盖旧 PocketBase 项目全部功能,同时升级为多租户 SaaS:
|
||||
|
||||
- 平台超级管理员:管理所有租户、SaaS 套餐、年费/服务费、公共/地区题库披露。
|
||||
- 租户公司:拥有自己的品牌、域名、支付/登录/CRM 配置、成员角色、题库内容、销售/代理体系。
|
||||
- 学生端:刷题、错题、收藏、背单词、知识手册、分数线、视频解析、会员权益。
|
||||
- 跨端前端:后续 Taro 一套代码输出 H5 和小程序,统一调用 `apps/api`。
|
||||
|
||||
## 当前覆盖情况
|
||||
|
||||
| 蓝图模块 | 当前状态 | 已落地内容 | 待补内容 |
|
||||
| --- | --- | --- | --- |
|
||||
| 平台超级管理员 | 部分完成 | 租户管理、SaaS 套餐、订阅、账单、服务费收款、用量记录 | 公共题库披露策略、地区/全国套餐权限、平台侧主题模板库、平台审计 |
|
||||
| 租户品牌和域名 | 基础完成 | 品牌、Logo、主题 JSON、公开资源、域名、租户公开配置 | 三套默认主题、主题可视化编辑、图标/图片上传 |
|
||||
| 租户成员权限 | 基础完成 | owner/admin/operator/teacher/sales/agent/student,权限矩阵,成员启停,审计查询 | 前端权限 UI、自定义角色模板、菜单级可见配置 |
|
||||
| 题库内容维护 | 基础完成 | 题目录入/更新、题目 JSON 预览/导入、视频绑定、分数线、单词、知识手册后台 API | Excel/CSV 批量导入、分类/节点完整管理、公题库采纳/复制/授权 |
|
||||
| 学生刷题 | 基础完成 | 题目列表、练习 session、答题、错题本、收藏夹 | 模考、专项练习策略、错题复习计划、题型统计深度分析 |
|
||||
| 背单词 | 基础完成 | 单词单元、单词、进度、收藏、统计 | 复习算法、每日计划、排行榜 |
|
||||
| 知识手册 | 基础完成 | 科目、章节、条目只读与后台维护 | 富文本资源、版本管理、附件/PDF 关联 |
|
||||
| 分数线 | 基础完成 | 字段、院校、专业、记录、趋势、年份 | 复杂动态筛选、批量导入、AI 择校数据上下文 |
|
||||
| 视频解析会员 | 部分完成 | 题目视频、批量查询、后台绑定 | SVIP 权限、播放次数扣减、签名 URL、防盗链、水印、播放统计 |
|
||||
| 资料下载/PDF | 基础完成 | `content_assets` 资源台账、后台资源管理、上传/下载签名占位、学生端列表、SVIP 下载权限 | 真实 OSS/COS/Supabase Storage 签名、PDF 预览渲染、防盗链、资料前端管理页 |
|
||||
| 营销中心 | 基础完成 | SVIP 套餐、激活码批次、激活码生成、优惠券、Banner/FAQ/公告 | 勋章自动发放、复杂活动规则、核销报表 |
|
||||
| 销售/代理客资 | 基础完成 | 邀请码、扫码/分享事件、首绑保护、销售统计、客资明细、团队关系、手动补绑、小程序码占位 | 真实微信小程序码、分佣结算单、销售团队看板、代理费结算 |
|
||||
| CRM 系统 | 基础完成 | CRM 配置、密钥私密存储、客资入队、队列查询 | worker 发送、钉钉/飞书/企微 adapter、重试签名、定向/轮询分配 |
|
||||
| 数据看板 | 数据表部分具备 | `dashboard_daily_stats`、`revenue_daily_stats` 表、旧 backfill 脚本 | API 聚合、24h 活跃、收入趋势、题型/科目/套餐销售看板 |
|
||||
| 登录认证 | 迁移期可用 | 短信 mock、迁移期 session、OAuth 配置表 | 阿里云/腾讯云短信、微信/QQ 登录真实 adapter、Supabase Auth/JWT |
|
||||
| 支付 | 迁移期可用 | 订单、支付记录、手动确认、权益发放、租户商户配置 | 微信支付/支付宝/XPay adapter、webhook 幂等、退款 |
|
||||
| AI 择校推荐 | 未开始 | 暂无 | 数据上下文、AI provider、JSON 报告 schema、PDF 报告生成 |
|
||||
| Taro 跨端 | 未开始 | 旧 Web 新 API 适配开始 | `apps/taro`、共享 API client、H5/小程序统一构建 |
|
||||
|
||||
## 接下来优先级
|
||||
|
||||
1. 完善内容导入和对象存储:Excel/CSV、单词/手册/分数线/视频导入,真实 OSS/COS/Supabase Storage 签名。
|
||||
2. 公共题库/地区题库授权:平台题库向租户披露、租户采纳、按 SaaS 套餐限制地区。
|
||||
3. 视频会员控制:视频资源签名 URL、防盗链、水印、播放次数和会员权益。
|
||||
4. 数据看板 API:把旧 dashboard/revenue 统计迁到新 API。
|
||||
5. 真实 provider:短信、微信/QQ 登录、微信支付/支付宝、CRM worker。
|
||||
65
docs/refactor/data-governance.md
Normal file
65
docs/refactor/data-governance.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# 数据治理与安全规范
|
||||
|
||||
这次重构不按 PocketBase 旧字段原样搬迁。旧集合只作为历史输入源,正式业务表按商用 SaaS 规范重新建模。
|
||||
|
||||
## 默认原则
|
||||
|
||||
- 旧数据先进入 `pb_raw_records`,且默认脱敏。
|
||||
- 密钥、token、session、private key、AppSecret、支付密钥不得进入 `public` schema。
|
||||
- 私密配置进入 `app_private.tenant_secrets`,生产环境再接云厂商 KMS/Vault。
|
||||
- `settings` 不再作为业务表使用,必须拆成公开配置、支付账户、短信配置、OAuth 配置、存储配置。
|
||||
- `users` 不再作为万能表,拆成用户、身份、租户成员、学生资料、权益、学习记录。
|
||||
- `users.isSvip`、`svipExpiry`、`svipRegions` 不作为新系统权限源,统一迁移为 `entitlements`。
|
||||
- `users.stats.favorites/wrongBook` 不继续留在 JSON 中,统一迁移为收藏表和错题表。
|
||||
- `crm_config`、支付、短信、OAuth、对象存储等配置不得在 `public` schema 中保存明文密钥;公共表只保留可展示配置或 secret 引用。
|
||||
- 导入后的上线闸门是 `npm run pb:import:validate`:有 `FAIL` 不上线,`WARN` 必须由业务确认并记录。
|
||||
|
||||
## 禁止直接复制的字段类型
|
||||
|
||||
字段名包含以下关键词时默认视为敏感:
|
||||
|
||||
```text
|
||||
password
|
||||
token
|
||||
secret
|
||||
privateKey
|
||||
sessionKey
|
||||
accessKey
|
||||
appKey
|
||||
apiKey
|
||||
openid
|
||||
unionid
|
||||
aesKey
|
||||
notifyToken
|
||||
```
|
||||
|
||||
这些字段默认在导入原始区时写入 `[REDACTED]`。如果确实需要迁移到私有表,必须显式设置:
|
||||
|
||||
```bash
|
||||
IMPORT_SECRET_VALUES=true npm run import:json
|
||||
```
|
||||
|
||||
并且只能进入 `app_private.tenant_secrets`。
|
||||
|
||||
## 导入质量报告
|
||||
|
||||
导入时会写入 `pb_import_issues`,用于记录:
|
||||
|
||||
- 敏感字段来源
|
||||
- 旧 JSON 字段需要拆表
|
||||
- `settings` 大杂烩配置风险
|
||||
- `users` 会员状态需要转权益
|
||||
|
||||
查看旧 schema 风险:
|
||||
|
||||
```bash
|
||||
npm run pb:schema:risk
|
||||
```
|
||||
|
||||
## 后续硬性验收
|
||||
|
||||
- RLS 覆盖所有带 `tenant_id` 的表。
|
||||
- 租户间数据隔离测试必须自动化。
|
||||
- 支付 webhook 必须幂等。
|
||||
- 订单金额、支付流水、权益开通必须可审计。
|
||||
- 管理员操作必须写审计日志。
|
||||
240
docs/refactor/implementation-status.md
Normal file
240
docs/refactor/implementation-status.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Supabase 重构功能进度矩阵
|
||||
|
||||
更新时间:2026-06-21 21:42
|
||||
|
||||
## 当前结论
|
||||
|
||||
当前重构已经完成了 Supabase/PostgreSQL 多租户底座、核心业务表、PocketBase 数据导入器雏形、部分学生端 API、租户后台 API、平台后台 SaaS 账务 API、内容资产/题目 JSON 批量导入基础闭环,以及本地 Docker/API 构建验证。
|
||||
|
||||
但这还不是完整商用交付状态,也不能说旧项目核心功能已经全部重构完成。现在更准确的状态是:后端商用架构骨架已经立住,核心业务正在按模块补齐。部分功能已经有可调用 API,部分功能只有数据模型和导入映射,部分功能还没有前端/自动化测试闭环。
|
||||
|
||||
## 新旧项目位置
|
||||
|
||||
| 范围 | 路径 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| 旧 PocketBase/React 项目 | `F:\project\src`、`F:\project\pb_hooks`、`F:\project\pb_migrations` | 保留作为功能参照和迁移来源 |
|
||||
| 新 Node API | `F:\project\apps\api` | 已按 `core/features` 分层重构 |
|
||||
| Supabase/PostgreSQL 迁移 | `F:\project\supabase\migrations` | 已建立多租户和业务域表 |
|
||||
| PocketBase 数据导入 | `F:\project\scripts\import-pocketbase` | 已支持多类旧数据归一化导入与校验 |
|
||||
| 共享包 | `F:\project\packages\config`、`F:\project\packages\db`、`F:\project\packages\domain` | 已建立基础共享层 |
|
||||
| 旧 Web 到新 API 适配 | `F:\project\src\services\supabaseApi.ts` | 已开始抽象,后续应迁到 Taro 共享 API 包 |
|
||||
|
||||
## 功能完成度
|
||||
|
||||
| 模块 | 数据模型 | PocketBase 导入 | API | 自动化测试 | 当前状态 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 多租户隔离 | 已建 `tenants`、`tenant_domains`、`tenant_branding`、`tenant_settings`、RLS 基础 | 部分支持 | 租户解析、品牌、域名、支付账户、登录 provider、平台建租户已实现 | 核心 API 集成测试含租户隔离断言 | 基础可用,正式 JWT/RLS 权限闭环未完成 |
|
||||
| 刷题题库 | 已建题库、题目、题目版本、分类、地区、科目、导入任务台账 | 已支持核心映射 | 题目列表、练习 session、答题提交、租户后台题目录入/更新、JSON 预览/导入已实现 | 核心 API 集成测试含导入断言 | 基础刷题链路、后台题目录入和 JSON 批量导入可跑,专项练习/模考/Excel 导入仍需补齐 |
|
||||
| 错题本 | 已建 `wrong_questions` | 已支持旧错题归一化 | 错题列表、答题自动入错题、移出错题已实现 | 仅烟测 | 基础功能已实现,复习计划和统计未完成 |
|
||||
| 收藏夹 | 已建 `favorite_questions` | 已支持旧收藏归一化 | 收藏/取消收藏、收藏列表已实现 | 仅烟测 | 基础功能已实现 |
|
||||
| 用户订阅/题库会员/SVIP | 已建 `orders`、`payments`、`entitlements`、`svip_plans`、激活码 | 已映射旧 SVIP/会员权益 | 下单、手动支付确认、激活码兑换、权益查询已实现 | 仅烟测 | 业务骨架可跑,真实微信/支付宝支付和 webhook 未完成 |
|
||||
| 背单词 | 已建单词单元、单词、进度、收藏表 | 已支持内容和部分用户状态映射 | 单元/单词只读、进度、收藏、统计、租户后台单词维护 API 已实现 | 核心 API 集成测试 | 学生端基础学习状态和后台单词维护已实现,复习算法和后台统计待完善 |
|
||||
| 知识手册 | 已建手册科目、章节、条目 | 已支持内容导入 | 只读 API、租户后台手册科目/章节/条目维护 API 已实现 | 核心 API 集成测试 | 学生端阅读和后台维护基础可用,富文本资源/版本管理待补 |
|
||||
| 分数线 | 已建院校、专业、字段、记录表 | 已支持导入映射 | 字段、院校、专业、记录、趋势、年份、租户后台维护 API 已实现 | 核心 API 集成测试 | 查询和后台维护基础闭环已实现,复杂动态筛选/批量导入待补 |
|
||||
| 题目视频讲解 | 已建 `video_explanations`、`question_videos` | 已支持导入映射 | 单题视频、批量预加载、通用视频搜索、租户后台视频创建绑定 API 已实现 | 核心 API 集成测试 | 播放数据和后台绑定链路已实现,会员权限、签名 URL、播放统计待补 |
|
||||
| 资料下载/PDF | 已扩展 `content_assets`,新增资源台账和导入任务表 | 旧 `app_assets/images` 兼容导入 | 租户后台资源管理、上传/下载签名占位、学生端资料列表/下载权限已实现 | 核心 API 集成测试含 SVIP 资料下载 | 资料资源基础闭环可跑,真实 OSS/COS 签名、PDF 预览渲染、资料下载前端待补 |
|
||||
| 个人中心 | 已建 `student_profiles`、会员权益、订单、练习记录 | 已支持部分用户资料导入 | 个人资料、目标院校/专业、会员状态、最近练习、统计聚合 API 已实现 | 核心 API 烟测 | 学生端基础个人中心已实现,签到/任务/更细统计待补 |
|
||||
| 活动/优惠 | 已建优惠券、激活码、激活码批次、banner、FAQ、公告等基础表 | 部分支持 | banner/FAQ/公告只读与租户后台维护、激活码兑换、激活码批次、批量生成激活码、优惠券维护已实现 | 核心 API 集成测试 | 基础运营后台可用,复杂活动规则、营销自动化、核销报表待补 |
|
||||
| 销售/代理客资追踪 | 已建推荐码、首绑客资、团队关系、小程序码缓存、CRM 队列 | 旧 `referral_tracks` 已有映射基础 | 邀请码、扫码/分享事件、首绑保护、销售统计、客资明细、手动补绑、团队关系、CRM 配置/队列已实现 | 核心 API 集成测试 | 增长链路基础可用,真实微信小程序码、分佣结算单、CRM worker 推送待补 |
|
||||
| 租户后台 | 已建品牌、域名、设置、支付账户、登录 provider、私密密钥表、成员、审计日志、资源台账、导入台账 | 不适用 | 概览、品牌、设置、域名、支付账户、登录配置、密钥掩码、活动内容、兑换码/优惠券、成员管理、权限矩阵、审计查询、内容维护、资源管理、题目 JSON 导入已实现 | 核心 API 集成测试含角色/权限/租户隔离/密钥不泄露/资源与导入断言 | 租户配置与运营闭环可用,前端权限 UI、Excel 导入、真实对象存储签名待补 |
|
||||
| 平台后台 | 已建 SaaS 套餐、订阅、账单、服务费、用量 | 不适用 | 租户管理、账单、收款确认、用量记录已实现 | 仅烟测 | 平台收费链路骨架可用,正式鉴权/审计/自动计费未完成 |
|
||||
| 登录认证 | 已建短信验证码、会话、OAuth provider 配置表 | 旧用户映射已预留 | 短信 mock 登录、迁移期 session、OAuth 占位已实现 | 仅烟测 | 本地可测,真实短信/微信/QQ 登录未完成 |
|
||||
| 数据导入 | 已建立 importer、risk report、validate | 已覆盖多类旧集合 | 命令行导入/校验 | `pb:import:validate` | 基础工具可用,需用真实完整数据做多轮 dry-run |
|
||||
| 测试体系 | 不适用 | 不适用 | 不适用 | 已新增核心 API 集成测试、租户隔离测试、权限矩阵测试、资源/题目导入测试、导入校验 | 还不是完整覆盖,支付幂等、真实导入回归、前端端到端测试仍需补 |
|
||||
|
||||
## 已实现 API 范围
|
||||
|
||||
```text
|
||||
auth:
|
||||
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
|
||||
|
||||
tenant:
|
||||
GET /api/tenant/resolve
|
||||
|
||||
catalog:
|
||||
GET /api/catalog/regions
|
||||
GET /api/catalog/region-modules
|
||||
GET /api/catalog/module-nodes
|
||||
GET /api/catalog/schools
|
||||
GET /api/catalog/majors
|
||||
GET /api/catalog/subjects
|
||||
GET /api/catalog/categories
|
||||
GET /api/catalog/questions
|
||||
GET /api/catalog/assets
|
||||
GET /api/catalog/assets/download
|
||||
GET /api/catalog/vocabulary-units
|
||||
GET /api/catalog/vocabulary-words
|
||||
GET /api/catalog/handbook-subjects
|
||||
GET /api/catalog/handbook-chapters
|
||||
GET /api/catalog/handbook-entries
|
||||
GET /api/catalog/banners
|
||||
GET /api/catalog/faqs
|
||||
GET /api/catalog/announcements
|
||||
GET /api/catalog/products
|
||||
GET /api/catalog/timelines
|
||||
GET /api/catalog/svip-plans
|
||||
|
||||
learning:
|
||||
POST /api/learning/practice-sessions
|
||||
POST /api/learning/answers
|
||||
GET /api/learning/favorites/questions
|
||||
POST /api/learning/favorites/questions
|
||||
GET /api/learning/wrong-questions
|
||||
POST /api/learning/wrong-questions/resolve
|
||||
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
|
||||
|
||||
profile:
|
||||
GET /api/profile/me
|
||||
PATCH /api/profile/me
|
||||
|
||||
scoreline:
|
||||
GET /api/scoreline/fields
|
||||
GET /api/scoreline/schools
|
||||
GET /api/scoreline/majors
|
||||
GET /api/scoreline/records
|
||||
GET /api/scoreline/trend
|
||||
GET /api/scoreline/years
|
||||
|
||||
video:
|
||||
GET /api/questions/{questionId}/videos
|
||||
GET /api/questions/videos?questionId=...
|
||||
POST /api/questions/videos/batch
|
||||
GET /api/videos/search
|
||||
|
||||
tenant-content:
|
||||
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
|
||||
GET /api/tenant-content/videos
|
||||
PUT /api/tenant-content/videos
|
||||
POST /api/tenant-content/question-videos
|
||||
GET /api/tenant-content/scoreline/schools
|
||||
PUT /api/tenant-content/scoreline/schools
|
||||
GET /api/tenant-content/scoreline/majors
|
||||
PUT /api/tenant-content/scoreline/majors
|
||||
GET /api/tenant-content/scoreline/fields
|
||||
PUT /api/tenant-content/scoreline/fields
|
||||
GET /api/tenant-content/scoreline/records
|
||||
PUT /api/tenant-content/scoreline/records
|
||||
GET /api/tenant-content/vocabulary-units
|
||||
PUT /api/tenant-content/vocabulary-units
|
||||
GET /api/tenant-content/vocabulary-words
|
||||
PUT /api/tenant-content/vocabulary-words
|
||||
GET /api/tenant-content/handbook-subjects
|
||||
PUT /api/tenant-content/handbook-subjects
|
||||
GET /api/tenant-content/handbook-chapters
|
||||
PUT /api/tenant-content/handbook-chapters
|
||||
GET /api/tenant-content/handbook-entries
|
||||
PUT /api/tenant-content/handbook-entries
|
||||
|
||||
commerce:
|
||||
POST /api/commerce/orders
|
||||
GET /api/commerce/orders
|
||||
GET /api/commerce/entitlements
|
||||
GET /api/commerce/entitlements/check
|
||||
POST /api/commerce/payments/manual-confirm
|
||||
POST /api/commerce/activation-codes/redeem
|
||||
|
||||
referral/crm:
|
||||
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
|
||||
|
||||
tenant-admin:
|
||||
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
|
||||
|
||||
platform-admin:
|
||||
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
|
||||
```
|
||||
|
||||
## 商用交付缺口
|
||||
|
||||
上线前至少还需要完成:
|
||||
|
||||
1. 正式鉴权:迁移期 `x-tenant-id`、`x-user-id`、`x-platform-admin-key` 要替换为 Supabase Auth/JWT/服务端 session,并逐表验证 RLS。
|
||||
2. 国内能力接入:短信、微信登录、微信小程序登录、QQ 登录、微信支付、支付宝支付的租户级配置入口已具备,但真实 provider adapter、回调验签和 webhook 幂等仍需实现。
|
||||
3. 核心缺口 API:学生端个人中心、分数线、题目视频详情、背单词进度/收藏已补基础 API;下一步重点是后台维护、权限、统计和真实业务验收。
|
||||
4. 后台能力:题库录入、JSON 批量导入、资源台账、视频绑定、知识手册维护、分数线维护、品牌/商户/登录/活动/兑换码配置、销售客资、CRM 队列、成员权限、审计查询已补 API;Excel 导入、真实对象存储签名和前端操作台待补。
|
||||
5. 自动化测试:已建立核心 API、租户隔离、权限矩阵、后台维护、资源/导入集成测试;仍需真实数据导入回归、支付幂等、前端端到端测试。
|
||||
6. Taro 前端:建立 `apps/taro` 或等价跨端应用,把 H5 和小程序统一走同一套 API client。
|
||||
7. 运维交付:生产环境变量、备份恢复、日志监控、异常告警、数据库迁移流程、灰度发布、回滚预案。
|
||||
|
||||
## 下一步优先级
|
||||
|
||||
为了先把旧项目核心业务补齐,再进入支付/短信等商用关键模块,建议按下面顺序继续:
|
||||
|
||||
1. 完善内容导入和文件上传:Excel/CSV、单词、手册、分数线、视频导入,接真实 OSS/COS/Supabase Storage 签名。
|
||||
2. 补地区/公共题库披露策略、租户套餐地区限制、主题模板系统。
|
||||
3. 补学习统计:练习历史、正确率趋势、错题复习计划、单词复习算法。
|
||||
4. 补视频商用控制:SVIP 权限、签名 URL、防盗链、水印、播放次数扣减。
|
||||
5. 补 AI 择校推荐报告、排行榜、勋章自动发放。
|
||||
6. 接真实支付、短信、微信/QQ 登录 provider adapter,并开始 Taro scaffold。
|
||||
127
docs/refactor/local-supabase.md
Normal file
127
docs/refactor/local-supabase.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# 本地 Supabase 开发环境
|
||||
|
||||
## 前置依赖
|
||||
|
||||
Supabase 本地开发需要:
|
||||
|
||||
- Docker Desktop
|
||||
- Supabase CLI
|
||||
- Node.js 20+
|
||||
|
||||
当前机器如果出现下面错误,说明 Docker Desktop 未启动或 Docker daemon 不可访问:
|
||||
|
||||
```text
|
||||
failed to inspect container health
|
||||
open //./pipe/docker_engine: The system cannot find the file specified
|
||||
```
|
||||
|
||||
先启动 Docker Desktop,再执行 Supabase 命令。
|
||||
|
||||
当前仓库已经加入 `supabase/config.toml` 和第一版 migration。安装依赖后执行:
|
||||
|
||||
```bash
|
||||
npm run supabase:start
|
||||
npm run supabase:status
|
||||
```
|
||||
|
||||
本地默认端口:
|
||||
|
||||
```text
|
||||
API: http://127.0.0.1:54321
|
||||
DB: postgresql://postgres:postgres@127.0.0.1:54322/postgres
|
||||
Studio: http://127.0.0.1:54323
|
||||
Inbucket: http://127.0.0.1:54324
|
||||
```
|
||||
|
||||
重置数据库:
|
||||
|
||||
```bash
|
||||
npm run supabase:reset
|
||||
npm run db:smoke-seed
|
||||
```
|
||||
|
||||
## API 服务
|
||||
|
||||
```bash
|
||||
npm run dev:api
|
||||
```
|
||||
|
||||
健康检查:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8787/health
|
||||
```
|
||||
|
||||
租户解析:
|
||||
|
||||
```bash
|
||||
curl "http://127.0.0.1:8787/api/tenant/resolve?host=localhost"
|
||||
curl "http://127.0.0.1:8787/api/tenant/resolve?tenantCode=master"
|
||||
```
|
||||
|
||||
公开题库数据接口示例:
|
||||
|
||||
```bash
|
||||
curl "http://127.0.0.1:8787/api/catalog/regions?tenantId=00000000-0000-0000-0000-000000000001"
|
||||
curl "http://127.0.0.1:8787/api/catalog/questions?tenantId=00000000-0000-0000-0000-000000000001&limit=20"
|
||||
```
|
||||
|
||||
本地短信登录 smoke 可用 mock provider。开发环境接口会返回 `debugCode`:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://127.0.0.1:8787/api/auth/sms/send" \
|
||||
-H "content-type: application/json" \
|
||||
-H "x-tenant-id: 00000000-0000-0000-0000-000000000001" \
|
||||
-d "{\"phone\":\"13900000001\",\"purpose\":\"login\"}"
|
||||
```
|
||||
|
||||
平台运营接口迁移期使用 `x-platform-admin-key`。本地默认值来自 `PLATFORM_ADMIN_API_KEY`,未设置时为 `local-platform-admin-key`:
|
||||
|
||||
```bash
|
||||
curl "http://127.0.0.1:8787/api/platform-admin/overview" \
|
||||
-H "x-platform-admin-key: local-platform-admin-key"
|
||||
|
||||
curl "http://127.0.0.1:8787/api/platform-admin/tenants?limit=20" \
|
||||
-H "x-platform-admin-key: local-platform-admin-key"
|
||||
```
|
||||
|
||||
## Docker 运行 API
|
||||
|
||||
本地 Supabase 继续由 Supabase CLI 启动,API 可以单独进入 Docker 容器:
|
||||
|
||||
```bash
|
||||
npm run supabase:start
|
||||
npm run docker:api:build
|
||||
npm run docker:api:up
|
||||
```
|
||||
|
||||
Compose 文件是 `docker-compose.api.yml`。API 容器默认使用:
|
||||
|
||||
```text
|
||||
DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:54322/postgres
|
||||
PORT=8787
|
||||
```
|
||||
|
||||
如果本机已有非容器 API 占用 `8787`,先关闭旧进程或临时修改 `docker-compose.api.yml` 的端口映射。当前机器 Docker Desktop 可用,Supabase 容器已可运行;若构建 API 镜像时报 Docker Hub 或 ECR 拉取超时,需要先处理 Docker 镜像源或网络代理。
|
||||
|
||||
## PocketBase JSON 导入
|
||||
|
||||
把 PocketBase 导出的集合 JSON 放到仓库根目录的 `pb_export` 文件夹后执行:
|
||||
|
||||
```bash
|
||||
npm run pb:import:json
|
||||
npm run pb:import:validate
|
||||
```
|
||||
|
||||
导入真实密钥时必须明确打开开关,且只允许进入 `app_private.tenant_secrets`:
|
||||
|
||||
```bash
|
||||
set IMPORT_SECRET_VALUES=true
|
||||
npm run pb:import:json
|
||||
```
|
||||
|
||||
上线前 `pb:import:validate` 不能有 `FAIL`。`WARN` 通常代表旧数据缺失关联,需要业务确认后记录处理结论。
|
||||
|
||||
## 说明
|
||||
|
||||
Supabase Custom Domain 不用于合作商多域名绑定。合作商域名在你们自己的 Web/API 网关层解析,然后从 `tenant_domains` 表得到 `tenant_id`。
|
||||
89
docs/refactor/pocketbase-to-supabase-mapping.md
Normal file
89
docs/refactor/pocketbase-to-supabase-mapping.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# PocketBase 到 Supabase 迁移映射
|
||||
|
||||
## 迁移原则
|
||||
|
||||
- 保留 PocketBase 旧 ID 到 `legacy_id`,新系统主键统一用 UUID。
|
||||
- 所有租户内业务表都带 `tenant_id`。
|
||||
- 旧表原始 JSON 进入 `pb_raw_records` 前默认脱敏,保证可追溯但不泄露密钥。
|
||||
- 旧集合里不符合商用规范的字段不得直接映射到正式表,必须经过清洗和拆表。
|
||||
- 第一阶段标准化导入覆盖核心商用链路:租户、用户、题库目录、题目、订单、支付、权益、激活码、优惠券、词库、手册、运营内容、分数线、视频解析、CRM 和推广关系。未覆盖集合先 raw import,并在 `pb_import_issues` 里记录风险。
|
||||
- `users.isSvip`、`svipExpiry`、`svipRegions` 最终迁移为 `entitlements`。
|
||||
- `users.stats.favorites`、`wrongBook` 最终迁移为 `favorite_questions`、`wrong_questions`。
|
||||
- `settings` 中的密钥类配置必须进入 `app_private.tenant_secrets` 或外部 Vault,不进入 `public` schema。
|
||||
|
||||
## 核心映射
|
||||
|
||||
| PocketBase | PostgreSQL | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `tenant_config` | `tenants` / `tenant_branding` / `tenant_settings` | 从单实例配置升级为平台租户配置 |
|
||||
| `users` | `platform_users` / `tenant_memberships` / `student_profiles` / `user_identities` | 用户身份、租户角色、学生资料拆分 |
|
||||
| `regions` | `regions` | 增加 `tenant_id` |
|
||||
| `region_modules` | `region_modules` | 增加 `tenant_id` |
|
||||
| `module_nodes` | `module_nodes` | 作为后续题库层级主结构 |
|
||||
| `subjects` | `subjects` | 保留旧结构兼容,逐步和 `module_nodes` 对齐 |
|
||||
| `categories` | `categories` | 保留旧结构兼容 |
|
||||
| `questions` | `questions` / `question_versions` | 题目实体和题目内容版本拆分 |
|
||||
| `orders` | `orders` / `order_items` / `payments` / `payment_events` | 订单与支付流水分离 |
|
||||
| `svip_plans` | `svip_plans` | 金额改为分 |
|
||||
| `codes` | `activation_codes` | 激活码表 |
|
||||
| `code_batches` | `code_batches` | 批次表 |
|
||||
| `coupons` | `coupons` | 优惠券 |
|
||||
| `coupon_redemptions` | `coupon_redemptions` | 兑换流水 |
|
||||
| `vocabulary_units` | `vocabulary_units` | 背单词单元 |
|
||||
| `vocabulary` | `vocabulary_words` | 单词表 |
|
||||
| `handbook_*` | `handbook_*` | 手册内容 |
|
||||
| `banners` / `faqs` / `announcements` | 同名表 | 运营内容 |
|
||||
| `settings` | `tenant_settings` / `tenant_payment_accounts` | 拆分公开配置、私密配置、支付配置 |
|
||||
| `crm_config.secret` | `app_private.tenant_secrets` | 公共表只保留 `secret_ref` |
|
||||
| `users.stats.favorites` | `favorite_questions` | 迁移为关系表 |
|
||||
| `users.stats.wrongBook` | `wrong_questions` | 迁移为关系表 |
|
||||
| `users.isSvip` / `svipExpiry` / `svipRegions` | `entitlements` | 迁移为租户/地区范围权益 |
|
||||
| `user_word_progress` / `user_word_favorites` | 同名规范表 | 关联到 `platform_users` 与 `vocabulary_words` |
|
||||
| `app_assets` / `images` | `content_assets` | 统一素材索引 |
|
||||
|
||||
## 导入命令
|
||||
|
||||
先安装导入器依赖:
|
||||
|
||||
```bash
|
||||
cd scripts/import-pocketbase
|
||||
copy .env.example .env
|
||||
npm install
|
||||
```
|
||||
|
||||
查看 schema 摘要:
|
||||
|
||||
```bash
|
||||
npm run schema:summary
|
||||
npm run schema:risk
|
||||
```
|
||||
|
||||
把 PocketBase 导出的集合 JSON 放到 `pb_export`:
|
||||
|
||||
```text
|
||||
pb_export/
|
||||
users.json
|
||||
regions.json
|
||||
questions.json
|
||||
```
|
||||
|
||||
执行导入:
|
||||
|
||||
```bash
|
||||
npm run import:json
|
||||
```
|
||||
|
||||
导入后执行验证:
|
||||
|
||||
```bash
|
||||
npm run import:validate
|
||||
```
|
||||
|
||||
根目录也可以执行:
|
||||
|
||||
```bash
|
||||
npm run pb:import:json
|
||||
npm run pb:import:validate
|
||||
```
|
||||
|
||||
导入器会先把所有 JSON 放入 `pb_raw_records`,并默认对敏感字段脱敏;然后按依赖顺序标准化导入业务表。上线前必须处理 `FAIL` 项;`WARN` 项通常表示旧数据关系缺失,例如旧题目引用了不存在的章节,需要业务确认是否可接受。
|
||||
Reference in New Issue
Block a user