Files
venue-miniapp/miniprogram/docs/superpowers/specs/2026-06-08-merged-venue-miniapp-design.md
T

413 lines
12 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.
# 合并版场馆小程序设计
## 背景
当前有两套独立系统:
- 冰场馆小程序:前端为 uni-app 微信小程序,后端为 Django,部署在 `https://venue.test.chatgqt.top/`
- 综合场馆小程序:前端为 uni-app 微信小程序,后端为 Django,部署在 `https://venue2.test.chatgqt.top/`
两个项目的后端、数据库、Django admin、CI/CD 都保持独立。本次只合并小程序端,在 `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序` 下新增合并版小程序项目。
两套旧小程序当前都配置同一个微信小程序 `appid``wx516b3e0d9940c788`。合并版继续沿用该 `appid`,不引入新的微信小程序身份。
## 目标
构建一个合并版 uni-app 微信小程序:
- 用户首次进入时选择“冰场馆”或“综合场馆”。
- 小程序内可以切换当前场馆。
- 两个场馆的业务页面、登录态、API 请求和后端数据完全隔离。
- 不合并、不改造两个 Django 后端、数据库、Django admin 和后端 CI/CD。
- 新项目能独立安装依赖并构建微信小程序产物。
## 非目标
- 不统一两个后端接口协议。
- 不迁移或合并会员数据、订单数据、课程数据、会员卡数据。
- 不改造 Django admin。
- 不新增统一后台或网关服务。
- 不强制保留旧小程序的底部四个 Tab 结构。
- 不重做业务页面视觉设计,只做必要的入口、切换和隔离改造。
## 推荐方案
采用“模块隔离合并”方案。
新项目保留两个业务模块:
- `ice`:冰场馆模块。
- `comprehensive`:综合场馆模块。
每个模块保留自己的页面、API、状态管理、业务工具和资源。公共层只负责场馆选择、当前场馆持久化、跨模块跳转和基础应用配置。
该方案的核心取舍是:牺牲一部分目录重复,换取后端隔离、登录态隔离、支付隔离和后续维护清晰度。
## 工程结构
新项目目录:
```text
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/
miniprogram/
package.json
vite.config.js
.env
.env.example
src/
App.vue
main.js
manifest.json
pages.json
uni.scss
pages/
venue-select/
index.vue
ice/
home/
courses/
bookings/
profile/
activities/
comprehensive/
home/
courses/
bookings/
profile/
activities/
private/
tickets/
orders/
pay/
modules/
ice/
api/
stores/
utils/
comprehensive/
api/
stores/
utils/
components/
stores/
venue.js
utils/
venue.js
styles/
variables.scss
static/
ice/
comprehensive/
```
`pages` 放小程序实际路由页面,`modules` 放模块专属 API、状态和工具。页面通过模块专属路径导入业务能力,避免复用同名全局 API。
## 路由设计
`pages.json` 的第一个页面为:
```text
pages/venue-select/index
```
旧页面迁移时统一加场馆前缀:
```text
pages/ice/home/index
pages/ice/courses/index
pages/ice/courses/detail
pages/ice/bookings/index
pages/ice/bookings/detail
pages/ice/profile/index
pages/ice/profile/cards
pages/ice/profile/card-history
pages/ice/profile/detail
pages/ice/activities/detail
pages/comprehensive/home/index
pages/comprehensive/courses/index
pages/comprehensive/courses/detail
pages/comprehensive/courses/confirm
pages/comprehensive/bookings/index
pages/comprehensive/bookings/course-detail
pages/comprehensive/bookings/private-detail
pages/comprehensive/profile/index
pages/comprehensive/profile/detail
pages/comprehensive/profile/orders
pages/comprehensive/profile/tickets
pages/comprehensive/profile/balance
pages/comprehensive/activities/index
pages/comprehensive/activities/detail
pages/comprehensive/private/index
pages/comprehensive/private/confirm
pages/comprehensive/tickets/index
pages/comprehensive/orders/detail
pages/comprehensive/pay/index
```
页面内所有 `uni.navigateTo``uni.redirectTo``uni.reLaunch``uni.switchTab` 目标都改为带模块前缀的路径。
由于用户没有要求保留旧 Tab,合并版可以先不配置全局 `tabBar`。模块内通过页面入口和按钮跳转完成导航。这样能避免一个小程序只能有一套全局 tabBar 带来的耦合。
## 场馆选择与切换
公共场馆状态使用 Pinia store 和本地存储:
```text
CURRENT_VENUE_KEY = "CURRENT_VENUE"
```
可选值:
```text
ice
comprehensive
```
流程:
1. 用户首次进入 `pages/venue-select/index`
2. 如果本地已有 `CURRENT_VENUE`,页面可以展示当前场馆并提供继续进入或切换。
3. 用户选择场馆后,写入 `CURRENT_VENUE`
4. 根据场馆跳转到对应首页:
- 冰场馆:`/pages/ice/home/index`
- 综合场馆:`/pages/comprehensive/home/index`
5. 小程序内的切换入口跳回 `pages/venue-select/index` 或直接调用公共切换方法。
6. 切换当前场馆不清除另一套 token,用户切回原场馆后保持该后端的登录态。
建议在两个模块的“我的/会员中心”页面添加“切换场馆”入口,跳转到 `/pages/venue-select/index`
## API 与环境变量
两个后端使用独立 baseURL
```text
VITE_ICE_API_BASE_URL=https://venue.test.chatgqt.top
VITE_COMPREHENSIVE_API_BASE_URL=https://venue2.test.chatgqt.top
```
冰场馆请求封装放在:
```text
src/modules/ice/api/request.js
```
综合场馆请求封装放在:
```text
src/modules/comprehensive/api/request.js
```
两个模块都可以继续使用后端现有响应协议 `{ code, data, message }`,但错误归一化逻辑不共用。这样可以保留冰场馆对 `40002` 资料完善、`40003` 绑定冲突等错误码的处理,也保留综合场馆上传、支付、订单相关的现有处理。
Vite H5 开发代理可配置两个前缀:
```text
/ice-api -> VITE_ICE_API_BASE_URL
/comprehensive-api -> VITE_COMPREHENSIVE_API_BASE_URL
```
微信小程序构建时直接使用完整后端 URL。
## 登录态隔离
冰场馆保留:
```text
ICE_MEMBER_TOKEN
ICE_MEMBER_TOKEN_EXPIRES_AT
```
综合场馆保留:
```text
COMPREHENSIVE_VENUE_TOKEN
```
两个模块各自调用 `uni.login({ provider: "weixin" })`。登录得到的 code 只提交给当前模块对应后端:
- 冰场馆:`https://venue.test.chatgqt.top/api/auth/wechat-login`
- 综合场馆:`https://venue2.test.chatgqt.top/api/auth/wechat-login`
因为合并版沿用旧项目共同的 `appid`,不需要为新 `appid` 改两个后端微信登录配置。
## 支付隔离
综合场馆的 `pages/comprehensive/pay/index` 继续只使用综合场馆模块 API。微信支付参数由综合场馆后端生成,前端只调用 `uni.requestPayment`
冰场馆模块不复用综合场馆订单和支付 API。任何支付相关跳转都必须保持在 `pages/comprehensive/*` 路由内。
## 样式与资源
全局 `styles/variables.scss` 采用两套项目共有变量,并以综合场馆版本为基础补齐冰场馆需要的变量和 mixin:
- `$bg-color-main`
- `$bg-color-soft`
- `$bg-color-hover`
- `$bg-color-panel`
- `$text-color-*`
- `$color-primary`
- `$color-success`
- `$color-warning`
- `$color-danger`
- `$border-*`
- `$spacing-*`
- `minimal-card`
- `text-ellipsis`
全局 `App.vue` 保留基础页面样式、按钮 reset、容器类、状态 badge、固定底部操作栏等公共规则。页面级样式随页面复制,减少重写成本。
静态资源按模块隔离:
```text
src/static/ice/tab/*
src/static/comprehensive/tab/*
```
如果不配置全局 tabBar,旧 tab 图标只作为后续模块内导航备用资源。
## 文件迁移策略
复制时排除:
- `node_modules`
- `dist`
- `.git`
- `.idea`
- `.DS_Store`
- 后端目录
- 旧项目 `.env` 中的临时 frp 地址
优先以综合场馆小程序为工程基线,因为它已有 Pinia store、上传、支付、订单等更完整能力。冰场馆页面和 API 迁入独立模块。
所有旧的 `@/api/<module>` 导入都改为模块路径:
```text
@/modules/ice/api/<module>
@/modules/comprehensive/api/<module>
```
综合场馆 `defineStore("session")` 改为专属 ID
```text
defineStore("comprehensiveVenueSession")
```
如果冰场馆新增 store,也使用 `iceVenueSession``iceVenueProfile` 这类 `iceVenue` 前缀。
## 错误处理
场馆选择页:
- 本地值不是 `ice``comprehensive` 时视为未选择。
- 跳转失败时展示 toast,并停留在选择页。
API 请求:
- 401 或后端登录过期错误只清理当前模块 token。
- 网络错误只影响当前模块页面。
- 切换场馆不触发任何后端请求,避免误清理另一模块登录态。
登录:
- `uni.login` 未返回 code 时提示用户重试。
- 后端返回绑定冲突、资料未完善等错误时沿用模块现有页面状态处理。
支付:
- 支付失败或取消只影响综合场馆订单页面。
- 不新增跨场馆支付状态。
## 测试与验收
基础验证:
-`场馆小程序/miniprogram` 执行 `npm install`
- 执行 `npm run build:mp-weixin`,确认能生成微信小程序产物。
路由验证:
- 首次进入展示场馆选择页。
- 选择冰场馆后进入 `/pages/ice/home/index`
- 选择综合场馆后进入 `/pages/comprehensive/home/index`
- 两个模块内主要页面跳转不再引用旧的 `/pages/home/index` 等无前缀路径。
API 验证:
- 冰场馆页面请求使用 `VITE_ICE_API_BASE_URL`
- 综合场馆页面请求使用 `VITE_COMPREHENSIVE_API_BASE_URL`
- 冰场馆 token 不会出现在综合场馆请求头中。
- 综合场馆 token 不会出现在冰场馆请求头中。
登录验证:
- 冰场馆登录后写入 `ICE_MEMBER_TOKEN`
- 综合场馆登录后写入 `COMPREHENSIVE_VENUE_TOKEN`
- 切换场馆不会删除另一场馆 token。
支付验证:
- 综合场馆支付页仍只调用综合场馆订单和支付 API。
- 冰场馆页面没有误跳到综合支付页。
微信后台上线前检查:
- 微信小程序 request 合法域名包含 `venue.test.chatgqt.top`
- 微信小程序 request 合法域名包含 `venue2.test.chatgqt.top`
- 如综合场馆支付上线,需要确认微信支付商户、回调和综合场馆后端配置保持现状。
## 难度
整体难度为中等偏上。
主要工作量不是新增业务,而是批量迁移:
- 页面路径加模块前缀。
- 页面内跳转路径同步修改。
- API import 路径改为模块专属路径。
- 两套样式和公共组件合并。
- 验证两个模块的登录态、请求、支付不会串线。
## 风险与缓解
路由漏改:
- 使用全文搜索检查旧路径:`/pages/home``/pages/courses``/pages/bookings``/pages/profile``/pages/pay`
- 构建后通过主要路径手动跳转验证。
API 串线:
- 禁止使用全局 `src/api/request.js`
- 每个模块保留独立 `request.js` 和独立 env 变量。
- 验证请求 URL 和 Authorization token。
登录态污染:
- 保留并强化 token key 命名空间。
- 401 只清理当前模块 token。
样式互相影响:
- 公共样式只放基础 reset 和共用工具类。
- 模块页面样式尽量保留在单文件组件内。
支付误接:
- 支付页面只存在于 `pages/comprehensive/pay/index`
- 支付 API 只从 `src/modules/comprehensive/api/order.js` 导入。
微信后台配置遗漏:
- 上线前将两个后端域名都加入合法域名。
- 因沿用旧 `appid`,不新增后端微信登录配置变更。
## 交付结果
完成后应得到一个新的合并版小程序项目:
```text
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram
```
它可以独立构建微信小程序。用户首次进入选择场馆,后续可在小程序内切换;两个场馆后端、登录态、支付和业务数据仍完全隔离。