# Taro 前端对接指南 更新时间:2026-06-28 目标:用一套 Taro 工程同时服务微信小程序和 H5 Web 题库,并采用“Supabase Auth/JWT + `apps/api` 业务 API 优先”的混合架构,支持多租户、品牌主题、地区题库、会员权益、销售追踪和对象存储资源。 建议新建: ```text F:\project\apps\taro ``` 旧前端参考: ```text F:\project\参考\旧题库项目\src ``` ## 启动流程 ### H5 1. 从 `window.location.host` 获取当前域名。 2. 调用 `GET /api/tenant/resolve?host=`。 3. 保存 `tenant.id`、`tenant.slug`、`branding`、`features`、`publicConfig`。 4. 初始化主题、Logo、页面标题、功能开关。 5. 检查本地 session token,调用 `GET /api/auth/me`。 6. 如果未登录,进入登录页;如果已登录,加载个人中心和首页数据。 ### 微信小程序 1. 从编译环境或小程序启动参数读取 `tenantCode`。 2. 推广码、销售码、分享码从 `options` 或 `scene` 中解析。 3. 调用 `GET /api/tenant/resolve?tenantCode=`。 4. 如存在 referral 参数,先调用 `/api/referral/resolve` 和 `/api/referral/track-event`。 5. 登录后再调用 `/api/referral/bind` 完成首绑保护。 ## 请求封装 本项目不采用“前端直接写 Supabase 表替代业务命令层”的模式。Supabase 官方允许前端在 RLS 和最小权限下使用 Data API,但本系统的订单、支付、权益、租户后台、内容导入、CRM、对象存储签名等都需要服务端事务、密钥、审计和幂等,所以复杂业务命令默认调用 RPC、`apps/api`、Edge Function 或 worker。 前端可以使用 Supabase client 的范围: - H5 Auth session/JWT。 - 小程序端在兼容性验证通过后的 Auth session/JWT。 - 低风险公开只读数据,且必须已经有 RLS、grant、跨租户测试。 - Realtime 非敏感通知。 前端必须调用 `apps/api` 的范围: - 题库练习、答题、错题、收藏。 - 订单、支付、激活码、优惠券、权益。 - 私有 PDF、资料、视频、对象存储签名。 - 租户后台、平台后台、内容导入、CRM、销售/代理。 前端应封装一个统一 API client,所有页面禁止直接散写 `Taro.request`。 本地迁移期仍可兼容旧请求头,但新的 Taro 请求封装必须按下面目标实现: ```text Authorization: Bearer x-tenant-id: # 仅作为登录前/公开目录租户上下文;登录后必须与 session 租户一致 ``` 生产目标: ```text Authorization: Bearer ``` 前端不应再传 `x-user-id`、query/body `userId` 来表示当前用户。后端已经实现 session 优先解析:如果 Authorization 存在,用户态接口以 session 用户为准;如果请求里伪造了不同的 `userId` 会返回 `AUTH_USER_MISMATCH`,伪造不同租户会返回 `AUTH_TENANT_MISMATCH`。 生产或云端测试建议设置: ```text ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false ``` 这样旧式 `x-user-id` 和平台管理 key 会被拒绝,前端可以提前发现未按 session 接入的页面。 前端环境变量只允许包含: ```text TARO_APP_API_BASE_URL TARO_APP_SUPABASE_URL TARO_APP_SUPABASE_PUBLISHABLE_KEY ``` 禁止把 Supabase secret key、service role key、数据库连接串、对象存储密钥、支付私钥放进 Taro。 统一错误处理: | HTTP | 前端动作 | | --- | --- | | 400 | 展示表单错误或参数错误 | | 401 | 清 session,跳登录 | | 403 | 展示无权限或会员升级 | | 404 | 展示空状态 | | 409 | 展示业务冲突,例如激活码已用 | | 413 | 提示上传/导入文件过大 | | 429 | 倒计时重试,例如短信冷却 | | 500 | 展示系统异常并上报日志 | ## 全局状态建议 | Store | 内容 | | --- | --- | | tenantStore | tenant、branding、theme、features、publicConfig | | authStore | session、user、roles、permissions、loginState | | regionStore | 当前地区、可选地区、地区权益 | | catalogStore | content entries、nodes、collections、blueprints | | entitlementStore | SVIP 权益、视频权益、资料下载权益 | | referralStore | inviteCode、referrer、scene、bindState | | uiStore | 当前主题、tab、loading、toast、modal | 注意缓存必须带租户维度,例如: ```text tenant::catalog:entries tenant::profile tenant::theme ``` 切换租户或切换小程序环境时必须清理旧租户缓存。 ## 页面/API 映射 | 页面 | 主要接口 | | --- | --- | | 启动页 | `GET /api/tenant/resolve` | | 登录页 | `POST /api/auth/sms/send`、`POST /api/auth/sms/verify`、`POST /api/auth/oauth/wechat-miniapp`、后续微信网页/QQ provider | | 首页 | `/api/catalog/content-entries`、`/api/catalog/banners`、`/api/catalog/announcements`、`/api/profile/me` | | 选地区 | `/api/catalog/regions`、`/api/commerce/entitlements/check` | | 题库入口 | `/api/catalog/content-entries` | | 分类树 | `/api/catalog/content-nodes?entryId=...&parentId=root` | | 题目列表 | `/api/catalog/question-collections`、`/api/catalog/question-collections/questions` | | 开始练习 | `POST /api/learning/practice-sessions` | | 提交答案 | `POST /api/learning/answers` | | 错题本 | `GET /api/learning/wrong-questions`、`POST /api/learning/wrong-questions/resolve` | | 收藏夹 | `GET/POST /api/learning/favorites/questions` | | 题目视频 | `GET /api/questions/{questionId}/videos`、`POST /api/questions/videos/batch` | | 背单词 | `/api/catalog/vocabulary-units`、`/api/catalog/vocabulary-words` | | 单词进度 | `/api/learning/vocabulary/progress`、`/api/learning/vocabulary/stats` | | 单词收藏 | `/api/learning/vocabulary/favorites` | | 知识手册 | `/api/catalog/handbook-subjects`、`handbook-chapters`、`handbook-entries` | | 分数线 | `/api/scoreline/fields`、`schools`、`majors`、`records`、`trend`、`years` | | 资料下载 | `/api/catalog/assets`、`/api/catalog/assets/download` | | 商城 | `/api/catalog/svip-plans`、`POST /api/commerce/orders`、`POST /api/commerce/payments/create` | | 订单/权益 | `/api/commerce/orders`、`/api/commerce/entitlements` | | 激活码兑换 | `POST /api/commerce/activation-codes/redeem` | | 个人中心 | `GET/PATCH /api/profile/me` | | 销售分享 | `/api/referral/resolve`、`track-event`、`bind` | ## 题库新模型接入方式 旧项目常按“地区 -> 科目 -> 章节/试卷”固定层级处理。新项目不要写死层级,按下面模型渲染: ```text content_entries -> content_nodes 任意深度分类树 -> question_collections 题目列表/试卷/章节/题型集合 -> practice_blueprints 顺序/随机/全真模拟规则 ``` 前端建议: - `entryType=question_practice` 渲染为刷题入口。 - `entryType=vocabulary` 渲染为背单词入口。 - `entryType=handbook` 渲染为知识手册入口。 - `markerType=school` 或 `markerType=exam_track` 可作为学生目标院校/专业意向采集。 - 不同地区节点层级可以不同,页面组件必须支持递归树和面包屑。 ## 多租户前端优化 - Logo、标题、主题色、客服信息全部来自 `tenant/resolve`。 - 功能开关控制菜单显示,但接口权限仍以后端为准。 - 私有图片、PDF、视频不要直接拼 URL,一律通过后端签名。 - 支付渠道从后端返回或租户配置读取,不在页面硬编码。 - 小程序分享路径必须带 tenantCode 和 referral code。 - 用户首绑归属由后端保护,前端不要提供“换绑销售”入口。 - 管理后台菜单按 `GET /api/tenant-admin/permissions` 和用户权限渲染。 - H5 自定义域名下要注意缓存隔离,不能把 A 租户主题缓存用到 B 租户。 ## 登录对接 ### 短信登录 开发环境可以先使用 mock 短信,接口会返回 `debugCode`。生产环境禁止依赖 `debugCode`。 ```text POST /api/auth/sms/send body: { "phone": "13800000000", "purpose": "login" } POST /api/auth/sms/verify body: { "phone": "13800000000", "code": "123456", "purpose": "login" } ``` 成功后保存: ```text session.token session.expiresAt user ``` 后续请求统一带: ```text Authorization: Bearer x-tenant-id: ``` ### 微信小程序登录 微信小程序端调用 `Taro.login()` 获取 code,然后交给后端: ```text POST /api/auth/oauth/wechat-miniapp body: { "code": "", "profile": { "nickName": "...", "avatarUrl": "..." } } ``` 成功响应包含: ```text provider user isNewUser session.token session.expiresAt identity.openId identity.unionId ``` 注意: - 前端不接触 `appSecret`。 - 前端不会拿到微信 `session_key`。 - 如果登录前已经解析到推广码,登录成功后再调用 `/api/referral/bind` 完成首绑保护。 - 手机号授权后续应走独立的“绑定手机号”接口,不要把微信手机号解密逻辑写在页面里。 ## 支付对接 支付流程必须以后端订单金额和后端回调为准,前端只负责拉起支付。 ### 创建订单 ```text POST /api/commerce/orders body: { "planId": "", "quantity": 1, "payProvider": "wechat_pay | alipay", "payMethod": "jsapi | wap", "regionId": "" } ``` 返回 `orderNo` 后,再创建支付参数: ```text POST /api/commerce/payments/create body: { "orderNo": "", "provider": "wechat_pay", "openId": "<微信小程序登录后的 openId>" } ``` 微信小程序返回的 `paymentParams` 可直接映射到 `Taro.requestPayment`: ```text appId timeStamp nonceStr package signType paySign ``` 支付宝 H5/WAP 返回: ```text paymentParams.url ``` H5 可以跳转到该 URL。小程序端如果后续要接支付宝小程序,需要新增独立 provider/method,不要复用 H5 WAP URL。 支付完成后前端不要自行开通会员。前端应轮询或重新请求: ```text GET /api/commerce/orders GET /api/commerce/entitlements ``` 后端支付回调地址由租户支付账户配置: ```text /api/commerce/payments/notify/wechat_pay?tenantId= /api/commerce/payments/notify/alipay?tenantId= ``` 前端禁止: - 传入自定义金额。 - 伪造支付成功状态。 - 保存商户号私钥、API v3 key、支付宝应用私钥。 - 在页面里实现 webhook 验签或权益开通。 ## 第一阶段页面建议 1. `pages/bootstrap/index` - 租户解析、主题初始化、登录态恢复。 2. `pages/login/index` - 先接短信登录;后续接微信小程序登录。 3. `pages/home/index` - Banner、公告、题库入口、会员入口、资料入口。 4. `pages/region/index` - 地区选择和权益提示。 5. `pages/catalog/index` - entry/node/collection/blueprint 通用导航。 6. `pages/practice/index` - 刷题、答题、解析、错题、收藏。 7. `pages/vocabulary/index` - 单词单元、学习、收藏。 8. `pages/handbook/index` - 手册目录和阅读。 9. `pages/scoreline/index` - 动态字段筛选和趋势。 10. `pages/profile/index` - 会员、订单、激活码、学习数据。 ## 租户后台前端建议 租户后台可以先做 H5 管理台,也可以后续使用 Taro H5 复用部分组件。优先页面: - 概览:`/api/tenant-admin/overview` - 品牌/主题/域名/公开设置 - 支付账户/登录 provider/密钥引用 - 用户与成员权限 - 内容入口/分类树/题目集合/练习蓝图 - 题目/单词/知识手册/分数线/视频维护 - JSON 导入 preview/import/issues - Banner/FAQ/公告/激活码/优惠券 - 销售/代理/CRM 队列 租户后台不应在前端自行决定权限;隐藏菜单只是体验优化,接口仍会校验权限。 ## 联调顺序 1. 启动页和租户解析。 2. 短信登录和 `auth/me`。 3. 首页、地区、内容入口、题库树。 4. 练习 session、答题、错题、收藏。 5. 背单词、知识手册、分数线。 6. 会员套餐、订单、激活码。 7. 资料下载、视频解析。 8. 销售追踪和分享链路。 9. 租户后台内容维护和导入。 10. 正式鉴权、真实支付、对象存储生产联调。 ## Supabase Client 验证任务 前端 scaffold 后先做一个最小兼容性验证: - H5:`@supabase/supabase-js` 初始化、session 持久化、token refresh、logout。 - 微信小程序:验证自定义 storage/fetch/URL polyfill 是否稳定。 - API:用 Supabase access token 调 `apps/api`,后端解析出可信用户。 - 安全:确认前端 bundle 中不存在 secret/service role/database/payment/storage 私钥。 如果微信小程序端 `supabase-js` 兼容性不稳定,小程序端改走 `apps/api/auth/*` 登录适配层,H5 继续使用 Supabase client 管理 Auth。