Files
gongxue-base/docs/refactor/web-launch-acceptance-checklist.md
2026-07-12 19:26:57 +08:00

220 lines
16 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.

# Web 版上线前验收清单
更新时间2026-07-11
这份清单用于先上线 H5 Web 题库。Taro 仍然是前端工程,后端以 Supabase Auth/JWT、PostgreSQL/RLS、`apps/api`、worker 为主。前端视觉和交互可以参考 `F:\project\参考\旧题库小程序前端文件` 和旧 Web 版,但不能继承旧 PocketBase 直连、旧鉴权或旧字段模型。
## 前端接入原则
- H5 登录态优先使用 Supabase Auth access token。
- 复杂业务统一调用 `apps/api`,不要让页面直接写 Supabase 表。
- `runtime-config.json` 只能放 `portal``apiBaseUrl``supabaseUrl``supabasePublishableKey``tenantCode`
- 前端禁止出现 service role key、secret key、数据库连接串、OSS/COS 密钥、支付私钥、短信密钥。
- 页面层禁止手写 `x-user-id``Authorization``x-tenant-id`,统一使用 `apps/taro/src/services/api.ts`
- 私有图片、PDF、视频只消费后端短签名和 `content_assets` 权限结果,不能拼对象存储 URL。
- 学生头像只做男女预设,不做上传、裁剪或第三方头像同步。
- 排行榜默认不请求、不展示;只有租户购买/开启活动且完成专项压测后再接独立页面。
- H5 构建目录必须包含 `index.html``js/``css/`,并且每个上线目录根部必须由部署方放置对应 `runtime-config.json`
- 三套 H5 只注册本 portal 页面;学生微信小程序使用独立 `dist/weapp-student` 目录和学生分包,不能覆盖 `dist/h5-student`
- 租户、会话、权限和主题由全局 App Provider 管理;业务缓存键按 portal、域名或 tenantCode、tenantId、userId 隔离,跨标签账号变化会触发重验,页面不得自行维护另一套长期身份状态。
## 首个平台超级管理员
生产库没有可登录平台管理员时,先在 Supabase Auth 创建或确认你的账号并取得 `auth.users.id`。只在生产运维终端运行服务器 CLI先 dry-run再使用精确确认短语写入
```bash
DATABASE_URL='<production-database-url>' \
BOOTSTRAP_PLATFORM_ADMIN_AUTH_USER_ID='<auth.users UUID>' \
BOOTSTRAP_PLATFORM_ADMIN_USERNAME='<operator username>' \
BOOTSTRAP_PLATFORM_ADMIN_NAME='<display name>' \
npm run bootstrap:platform-admin
npm run bootstrap:platform-admin -- --apply --confirm BOOTSTRAP_FIRST_PLATFORM_ADMIN
```
该命令不是公开 API不接收密码、验证码或 service role key不输出完整 Auth UUID、邮箱或手机号。写入后立即运行 `npm run readiness:production:db`,确认 active 超管、Auth 绑定和 `{"*":true}` 权限门禁通过;已有可登录超管时命令必须拒绝。
## 学生端验收旅程
用普通学生账号在 H5 走一遍:
1. 进入域名后能解析租户品牌、主题、公开配置。
2. 登录后 `GET /api/profile/me` 返回当前用户,不能靠页面传 userId。
3. 查看题库入口、地区、分类树、合集和练习蓝图。
4. 开通或确认 SVIP 权益后创建顺序/随机/模考 session。
5. 拉取 session detail刷新页面后仍能按后端 session 续练。
6. 提交答案、交卷、查看报告、逐题复盘。
7. 收藏题目,在个人中心或复习页进入收藏练习。
8. 产生错题后查看错题本、复习计划和错题复习 session。
9. 背单词:今日计划、单元学习、收藏练习、发音、进度上报。
10. 知识手册:章节、搜索、公式/图片/RichContent 安全渲染。
11. 分数线:地区、院校、专业、年份和动态字段筛选。
12. 资料下载:预览/下载前必须看到短签名、水印 traceId 或权限提示。
13. 视频解析:未授权提示、授权后短签名播放、水印上下文、次数扣减。
14. 订单:套餐、优惠券、下单、支付参数、订单详情、状态轮询、售后入口。
15. 个人中心:权益、学习统计、勋章、积分任务、兑换、站内通知。
自动烟测命令:
```bash
npm run smoke:launch-persona
```
该命令会写入少量测试练习和收藏记录,生产只在灰度/演练租户执行。
## 租户后台验收旅程
用租户管理员账号在 H5 走一遍:
1. 工作台模块按权限显示,学生账号访问后台必须 403。
2. 数据看板能按 7/30/90 天、地区读取收益、注册、学习、内容、活跃、反馈、激活码。
3. 学生运营列表、状态、批量导入、分班、备注、跟进任务、督导规则、CRM 推送。
4. 题库内容:入口、分类、题目集合、蓝图、题目/单词/手册/分数线/视频导入预览、异步 job、复检。
5. 营销中心:激活码、优惠券规则、核销报表、积分任务、兑换、勋章、用户通知。
6. CRM/分佣CRM 配置、队列、失败池、日志脱敏、重试/忽略、结算、凭证复核、销售转化报表。
7. 财务运营:退款、官方账单下载任务、对账异常、差错工单、人工调整凭证。
8. 设置:品牌、主题模板/草稿/发布、域名、支付账户、登录 provider、角色模板、成员绑定。
9. 跨租户访问必须拒绝,字段权限如手机号脱敏要按角色生效。
## 平台后台验收旅程
用平台管理员账号在 H5 走一遍:
1. 工作台、租户列表、租户详情、状态变更、账务资料维护。
2. SaaS 套餐、订阅、订阅账单候选、批量开票、收款、逾期催缴。
3. 用量记录、用量超额候选、超额账单生成。
4. 公共题库授权、租户可见范围、采纳/同步/冲突运营摘要。
5. 平台员工、权限点、禁用/恢复、受限员工越权拒绝。
6. 审计日志、CSV/JSON 导出、审计告警、外部通知渠道。
7. 学生或租户管理员访问平台后台必须 403。
## 安全与配置门禁
上线前至少执行:
```bash
npm run test:readiness
npm run test:auth:foundation
npm run audit:runtime
npm run security:repo
npm run check:api
npm run check:worker
npm run check:taro
npm run build:taro:h5:student
npm run build:taro:h5:tenant
npm run build:taro:h5:platform
npm run build:taro:weapp:student
npm run readiness:production
npm run readiness:production:db
npm run smoke:taro:h5
npm run smoke:taro:h5:interaction
node scripts/taro-h5-release-guardrails-test.js --require-dist
npm run smoke:launch-persona -- --write docs/refactor/launch-artifacts/launch-persona-smoke.json --write-md docs/refactor/launch-artifacts/launch-persona-smoke.md
```
`test:rls` 和完整 API/worker 集成测试是会写入数据的测试套件只允许连接本地、CI 或生产 schema/脱敏快照的隔离克隆库。克隆库必须在 `app_private.environment_safety` 显式标记 `environment='test'|'ci'``allow_destructive_tests=true`,然后单独留存动态 RLS 证据:
```bash
DATABASE_URL='<isolated-rls-clone-url>' \
npm run test:rls > docs/refactor/launch-artifacts/rls-tenant-isolation.log
```
禁止在 `source /etc/tiku-saas/api.env` 后直接运行 `test:rls``test:api``test:worker:*`。真实生产数据库只执行 `readiness:production:db` 等只读门禁;真实 API 验收使用无 seed 的 `smoke:auth:remote`、受控灰度租户 `smoke:launch-persona` 等专用脚本。
`smoke:launch-persona` 是真实 API 角色旅程烟测,必须进入生产上线证据;正式证据必须使用 `LAUNCH_SMOKE_AUTH_MODE=app_session`,通过 Bearer `tk_` session 验证身份,不使用旧 `x-user-id` 或平台本地 key。普通学生要能在 SVIP 权益下创建练习、答题、收藏题目、进入收藏复习和错题复习入口;租户管理员要能读取看板/主题/学生/销售转化并拒绝学生或跨租户访问;平台管理员要能读取租户/套餐/审计入口并拒绝学生访问平台后台。生产证据命令必须带 `--write docs/refactor/launch-artifacts/launch-persona-smoke.json`,确保 `production-launch-evidence.json` 引用的 artifact 是稳定路径,不是只存在带时间戳的本地报告。
`smoke:taro:h5` 会启动临时静态服务器和 mock API验证三套 H5 的 `index.html`、JS/CSS 资源、history fallback、公开 runtime config 和 `/api/tenant/resolve` 契约。`smoke:taro:h5:interaction` 会在真实 Chrome/Edge 中点击学生、租户后台、平台后台关键路径,覆盖静态烟测发现不了的 JS 运行时、直接 history 路由刷新和 Taro 点击事件问题;当前脚本覆盖 33 项检查,包括学生未登录 401、首页、题库、答题、收藏、错题/收藏复习、背单词、知识手册、资料短签名和水印、视频播放授权、分数线、AI 择校、消息中心、会员收银台下单/支付参数/订单状态,租户后台内容导入、公共题库采纳/同步/冲突处理、学生运营、营销/CRM/分佣、主题/角色/成员写操作以及平台后台租户、账务、公共题库授权和员工写操作另有跨门户移动视口、Input 挂载竞态和 Button loading 200 次稳定节点探针。`taro-h5-release-guardrails-test` 会扫描源码、三套 H5 产物和 runtime-config 边界,防止旧 PocketBase、`x-user-id``x-platform-admin-key`、数据库连接串和服务端密钥形态进入前端发布目录。若刚构建完但未放入真实 `runtime-config.json`,脚本允许 warning正式部署目录必须补齐。
写入生产上线证据时,三套正式发布目录必须先放入真实公开 `runtime-config.json`,再运行严格模式:
```bash
npm --silent run smoke:taro:h5 -- --json > docs/refactor/launch-artifacts/taro-h5-static-smoke.json
npm --silent run smoke:taro:h5:interaction -- --json > docs/refactor/launch-artifacts/taro-h5-interaction-smoke.json
node scripts/taro-h5-release-guardrails-test.js --require-dist --require-runtime-config --json > docs/refactor/launch-artifacts/taro-h5-release-guardrails.json
npm run smoke:launch-persona -- --write docs/refactor/launch-artifacts/launch-persona-smoke.json --write-md docs/refactor/launch-artifacts/launch-persona-smoke.md > docs/refactor/launch-artifacts/launch-persona-smoke.log
```
每个 `checks[].artifact` 都必须同时填写真实 `artifactSha256`。三端静态目录已经切换到正式域名后,部署脚本应显式再执行严格线上校验:
```bash
LAUNCH_GATE_VERIFY_LIVE_H5=true \
npm run launch:gate -- --evidence /etc/tiku-saas/production-launch-evidence.json --verify-live-h5
```
证据中的 `liveH5.releaseManifestArtifact` 指向本次候选 `taro-h5-release-manifest.json``liveH5.releaseManifestSha256` 记录该 manifest 文件的 SHA-256。严格模式会请求学生端、租户后台和平台后台各自的 `index.html``runtime-config.json` 和主 app bundle要求 HTTP 成功、portal 正确、`apiBaseUrl` 与证据中的生产 HTTPS API 一致,并验证线上 index/app 哈希与候选发布目录一致。默认 launch gate 保持离线,不带显式开关时不会访问公网。
PNVS 短信登录上线前要用真实手机号跑一次远程 smoke。脚本不会读取或输出密钥它只调用公网 API发送验证码后在终端输入收到的短信验证码再确认 `/api/auth/me` 可用:
```bash
set -a
source /etc/tiku-saas/api.env
set +a
PNVS_TENANT_ID=00000000-0000-0000-0000-000000000001 \
npm run diagnose:aliyun-pnvs > docs/refactor/launch-artifacts/sms-pnvs-diagnostics.json
SMS_SMOKE_API_BASE_URL=https://api.tjszsb.com \
SMS_SMOKE_TENANT_ID=00000000-0000-0000-0000-000000000001 \
SMS_SMOKE_PHONE=13800138000 \
SMS_SMOKE_ORIGIN=https://admin.tjszsb.com \
npm run smoke:sms-login:remote -- --write docs/refactor/launch-artifacts/sms-pnvs-remote-smoke.json
```
`smoke:sms-login:remote` 默认要求短信发送接口返回 `provider=aliyun-pnvs`;如果生产仍走传统 `aliyun`/`tencent` provider脚本会直接失败。
PNVS 只验收手机号登录/换绑验证码链路。催缴、营销、CRM 等非验证码短信不应接 PNVS平台催缴通知仍按 webhook 类通知渠道验收。
`security:repo` 是仓库自带的静态安全扫描,会拦截密钥形态、前端旧鉴权头、真实 runtime-config 和生产证据误入 Git。它不能替代真实 `@codex-security`;如插件在当前 Codex 环境暴露扫描工具,再补插件扫描结果。若工具不可用,不能把该项标记为已完成,只能在上线证据里标记为待补。
`readiness:production``readiness:production:db` 是生产阻断门禁:会拒绝 mock/未知短信 provider、弱密钥、`CORS=*`、旧身份头、local_dev 存储、非 HTTPS 对象存储公开 URL、阿里云 OSS 内网直签、未接外部资源扫描、localhost webhook以及生产库残留 `local/test/ci``allow_destructive_tests=true` 标记、运行角色未通过 superuser bootstrap、租户公开配置缺字段/混入密钥、OAuth redirectUri/支付 notifyUrl 非 HTTPS、active provider 缺私密 `tenant_secrets`、active 租户公开 URL 指向 localhost/HTTP、缺少可登录平台管理员等问题。尚未发布主题只 warning 并回退平台默认主题,正式上线前仍要逐租户确认。
首次应用数据库迁移前,由 self-hosted PostgreSQL/Supabase 的真正 superuser 执行一次:
```bash
DATABASE_ADMIN_URL='<secret-managed-superuser-url>' \
npm run bootstrap:db-runtime-roles -- \
--apply --confirm=BOOTSTRAP_BACKEND_RUNTIME_ROLES
```
该管理员连接只用于集群角色和官方 Supabase 基础镜像 `public` 扩展函数 ACL bootstrap不得进入 API/Worker 配置或发布证据。普通 migration 会验证 `tiku_api/tiku_worker` 已是 `LOGIN NOINHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION BYPASSRLS` 且无父角色,并在 `anon/authenticated` 仍可执行任何 `public` 函数时直接阻断。每次 Supabase 镜像或扩展升级后都要重跑该幂等 bootstrap 与数据库 readiness。
微信小程序产物还必须在微信开发者工具和至少一台真机验证:打开 `apps/taro/dist/weapp-student`确认主包只保留启动页、学生页面位于分包、tenantCode 能解析正确租户、短信/微信登录能建立当前租户会话,并完成刷题、支付调起、文件预览、音视频和分享能力。租户后台与平台后台本阶段只发布桌面 H5。
生产 API 推荐:
```text
ALLOW_LEGACY_AUTH_HEADERS=false
ALLOW_PLATFORM_ADMIN_KEY=false
CORS_ORIGIN=https://student.example.com,https://tenant-admin.example.com,https://platform-admin.example.com
CORS_TENANT_DOMAINS_ENABLED=true
CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000
CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000
CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000
AUTH_SMS_PROVIDER=aliyun-pnvs
```
`CORS_ORIGIN` 中的学生/租户后台域名只适用于平台自营的少量固定 Origin合作租户自定义域名必须由 `active tenant_domains + active tenants` 动态准入。上线验收必须同时验证 active Origin 通过inactive/unknown Origin 的 OPTIONS 和普通请求返回 `403 CORS_ORIGIN_DENIED`,伪造 Host 无法绕过,且无 Origin 的 `/health` 仍可用。
生产 worker 推荐:
```text
WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http
WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false
STORAGE_DEFAULT_PROVIDER=aliyun_oss 或 tencent_cos 或 supabase_storage
STORAGE_REQUIRE_TENANT_PREFIX=true
ALIYUN_OSS_INTERNAL=false
```
## 性能与数据库
本地真实迁移库已能跑题库读写压测,但正式容量必须在目标 4 核 16G 云服务器复测。
推荐顺序:
1. `npm run perf:postgres:evidence` 采集默认 PostgreSQL 参数。
2.`docs/refactor/postgresql-4c16g-tuning.md` 应用 shared-host 起步值。
3. 重启 PostgreSQL 后执行 `PG_TUNING_PROFILE=shared-host npm run perf:postgres:evidence -- --strict --json`,确认 profile 达标、`pending_restart=0``pg_stat_statements` 可用。
4.`npm run perf:api:local` 的 6/30/50/100 阶梯,只读和少量写入各一组。
5.`postgres.tuning-evidence` 和 API 压测摘要写入本地 `production-launch-evidence.json`,执行 `npm run launch:gate`
容量折算不要直接把压测 worker 当在线人数。真实学生有读题和思考时间,应结合 H5 埋点估算单人平均 RPS再按成功 RPS 折算在线容量。