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

12 KiB
Raw Permalink Blame History

合并版场馆小程序设计

背景

当前有两套独立系统:

  • 冰场馆小程序:前端为 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/体育场馆/场馆小程序 下新增合并版小程序项目。

两套旧小程序当前都配置同一个微信小程序 appidwx516b3e0d9940c788。合并版继续沿用该 appid,不引入新的微信小程序身份。

目标

构建一个合并版 uni-app 微信小程序:

  • 用户首次进入时选择“冰场馆”或“综合场馆”。
  • 小程序内可以切换当前场馆。
  • 两个场馆的业务页面、登录态、API 请求和后端数据完全隔离。
  • 不合并、不改造两个 Django 后端、数据库、Django admin 和后端 CI/CD。
  • 新项目能独立安装依赖并构建微信小程序产物。

非目标

  • 不统一两个后端接口协议。
  • 不迁移或合并会员数据、订单数据、课程数据、会员卡数据。
  • 不改造 Django admin。
  • 不新增统一后台或网关服务。
  • 不强制保留旧小程序的底部四个 Tab 结构。
  • 不重做业务页面视觉设计,只做必要的入口、切换和隔离改造。

推荐方案

采用“模块隔离合并”方案。

新项目保留两个业务模块:

  • ice:冰场馆模块。
  • comprehensive:综合场馆模块。

每个模块保留自己的页面、API、状态管理、业务工具和资源。公共层只负责场馆选择、当前场馆持久化、跨模块跳转和基础应用配置。

该方案的核心取舍是:牺牲一部分目录重复,换取后端隔离、登录态隔离、支付隔离和后续维护清晰度。

工程结构

新项目目录:

/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 的第一个页面为:

pages/venue-select/index

旧页面迁移时统一加场馆前缀:

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.navigateTouni.redirectTouni.reLaunchuni.switchTab 目标都改为带模块前缀的路径。

由于用户没有要求保留旧 Tab,合并版可以先不配置全局 tabBar。模块内通过页面入口和按钮跳转完成导航。这样能避免一个小程序只能有一套全局 tabBar 带来的耦合。

场馆选择与切换

公共场馆状态使用 Pinia store 和本地存储:

CURRENT_VENUE_KEY = "CURRENT_VENUE"

可选值:

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

VITE_ICE_API_BASE_URL=https://venue.test.chatgqt.top
VITE_COMPREHENSIVE_API_BASE_URL=https://venue2.test.chatgqt.top

冰场馆请求封装放在:

src/modules/ice/api/request.js

综合场馆请求封装放在:

src/modules/comprehensive/api/request.js

两个模块都可以继续使用后端现有响应协议 { code, data, message },但错误归一化逻辑不共用。这样可以保留冰场馆对 40002 资料完善、40003 绑定冲突等错误码的处理,也保留综合场馆上传、支付、订单相关的现有处理。

Vite H5 开发代理可配置两个前缀:

/ice-api -> VITE_ICE_API_BASE_URL
/comprehensive-api -> VITE_COMPREHENSIVE_API_BASE_URL

微信小程序构建时直接使用完整后端 URL。

登录态隔离

冰场馆保留:

ICE_MEMBER_TOKEN
ICE_MEMBER_TOKEN_EXPIRES_AT

综合场馆保留:

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、固定底部操作栏等公共规则。页面级样式随页面复制,减少重写成本。

静态资源按模块隔离:

src/static/ice/tab/*
src/static/comprehensive/tab/*

如果不配置全局 tabBar,旧 tab 图标只作为后续模块内导航备用资源。

文件迁移策略

复制时排除:

  • node_modules
  • dist
  • .git
  • .idea
  • .DS_Store
  • 后端目录
  • 旧项目 .env 中的临时 frp 地址

优先以综合场馆小程序为工程基线,因为它已有 Pinia store、上传、支付、订单等更完整能力。冰场馆页面和 API 迁入独立模块。

所有旧的 @/api/<module> 导入都改为模块路径:

@/modules/ice/api/<module>
@/modules/comprehensive/api/<module>

综合场馆 defineStore("session") 改为专属 ID

defineStore("comprehensiveVenueSession")

如果冰场馆新增 store,也使用 iceVenueSessioniceVenueProfile 这类 iceVenue 前缀。

错误处理

场馆选择页:

  • 本地值不是 icecomprehensive 时视为未选择。
  • 跳转失败时展示 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,不新增后端微信登录配置变更。

交付结果

完成后应得到一个新的合并版小程序项目:

/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram

它可以独立构建微信小程序。用户首次进入选择场馆,后续可在小程序内切换;两个场馆后端、登录态、支付和业务数据仍完全隔离。