From d17b646ae05899c9961ebae39f8e6b92647656ed Mon Sep 17 00:00:00 2001 From: gqt <3217233537@qq.com> Date: Sun, 12 Jul 2026 19:57:37 +0800 Subject: [PATCH] docs: relocate docs into miniprogram/docs --- docs/merged-miniapp-verification.md | 77 - .../plans/2026-06-08-merged-venue-miniapp.md | 1683 ----------------- .../2026-06-08-merged-venue-miniapp-design.md | 412 ---- 3 files changed, 2172 deletions(-) delete mode 100644 docs/merged-miniapp-verification.md delete mode 100644 docs/superpowers/plans/2026-06-08-merged-venue-miniapp.md delete mode 100644 docs/superpowers/specs/2026-06-08-merged-venue-miniapp-design.md diff --git a/docs/merged-miniapp-verification.md b/docs/merged-miniapp-verification.md deleted file mode 100644 index 4f9aa25..0000000 --- a/docs/merged-miniapp-verification.md +++ /dev/null @@ -1,77 +0,0 @@ -# 合并版场馆小程序验证记录 - -## 构建 - -- 命令:`npm run build:mp-weixin` -- 结果:通过,命令退出码为 0 -- 产物:`miniprogram/dist/build/mp-weixin` -- 备注:构建日志包含 uni-app 版本提示和 uview-plus/Sass 弃用警告,未出现编译错误。 - -## 路由隔离 - -- 检查旧的无前缀页面路径:通过 -- 冰场馆页面前缀:`pages/ice` -- 综合场馆页面前缀:`pages/comprehensive` -- `pages.json` 首个页面:`pages/venue-select/index` -- `pages.json` 页面数量:30 -- 综合场馆页面数量:19 -- 冰场馆页面数量:10 -- 全局 `tabBar`:未配置 - -## API 隔离 - -- 冰场馆 baseURL:`VITE_ICE_API_BASE_URL` -- 综合场馆 baseURL:`VITE_COMPREHENSIVE_API_BASE_URL` -- 旧 `VITE_API_BASE_URL`:未使用 -- 旧全局 API import:未发现 - -## 登录态隔离 - -- 冰场馆 token:`ICE_MEMBER_TOKEN` -- 冰场馆 token 过期时间:`ICE_MEMBER_TOKEN_EXPIRES_AT` -- 综合场馆 token:`COMPREHENSIVE_VENUE_TOKEN` - -## 支付隔离 - -- `uni.requestPayment` 仅出现在 `pages/comprehensive/pay/index.vue` -- 综合场馆支付接口仅出现在 `modules/comprehensive/api/order.js` -- 冰场馆模块未引用综合场馆支付页或支付 API - -## 自动化检查命令 - -```bash -cd /Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram -npm run build:mp-weixin -``` - -```bash -cd /Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序 -rg -n "@/api|@/stores/session|@/utils/(date|money|status)|@/components/StatePanel.vue" miniprogram/src/pages miniprogram/src/modules -rg -n '\x27/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|"/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|`/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)' miniprogram/src/pages -rg -n "VITE_API_BASE_URL" miniprogram/src/modules miniprogram/.env miniprogram/.env.example miniprogram/vite.config.js -node -e "const fs=require('fs'); const p=JSON.parse(fs.readFileSync('miniprogram/src/pages.json','utf8')); const paths=p.pages.map(x=>x.path); console.log(JSON.stringify({first: paths[0], total: paths.length, comprehensive: paths.filter(x=>x.startsWith('pages/comprehensive/')).length, ice: paths.filter(x=>x.startsWith('pages/ice/')).length, hasTabBar: Boolean(p.tabBar)}, null, 2));" -``` - -## 微信开发者工具烟测 - -当前终端环境未直接打开微信开发者工具执行人工烟测。上线或提审前需要在微信开发者工具中打开: - -```text -/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/dist/build/mp-weixin -``` - -人工检查项: - -- 首次进入展示“选择场馆”。 -- 选择“冰场馆”进入 `pages/ice/home/index`。 -- 选择“综合场馆”进入 `pages/comprehensive/home/index`。 -- 两个模块的“会员中心/我的”可返回 `pages/venue-select/index` 切换场馆。 -- 冰场馆页面请求域名为 `https://venue.test.chatgqt.top`。 -- 综合场馆页面请求域名为 `https://venue2.test.chatgqt.top`。 -- 综合场馆支付页仍由综合场馆后端生成支付参数。 - -## 上线前人工检查 - -- 微信 request 合法域名包含 `venue.test.chatgqt.top`。 -- 微信 request 合法域名包含 `venue2.test.chatgqt.top`。 -- 综合场馆支付商户、回调和综合场馆后端配置保持现状。 diff --git a/docs/superpowers/plans/2026-06-08-merged-venue-miniapp.md b/docs/superpowers/plans/2026-06-08-merged-venue-miniapp.md deleted file mode 100644 index 33850ac..0000000 --- a/docs/superpowers/plans/2026-06-08-merged-venue-miniapp.md +++ /dev/null @@ -1,1683 +0,0 @@ -# Merged Venue Miniapp Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Build a new merged uni-app WeChat mini program under `场馆小程序/miniprogram` that lets users choose and switch between ice venue and comprehensive venue while keeping both Django backends, auth tokens, APIs, and business pages isolated. - -**Architecture:** The app uses one public venue selection shell and two isolated modules: `src/modules/ice` and `src/modules/comprehensive`. Route pages live under `src/pages/ice/*` and `src/pages/comprehensive/*`, while each module imports only its own API client and state. No backend code is changed. - -**Tech Stack:** uni-app, Vue 3, Vite 5, Pinia, uview-plus, dayjs, WeChat mini program target. - ---- - -## File Structure Map - -- Create `miniprogram/package.json`: merged project dependency and script manifest copied from the existing miniapp projects. -- Create `miniprogram/package-lock.json`: copied from the comprehensive project first, then refreshed by `npm install` if needed. -- Create `miniprogram/vite.config.js`: Vite + uni plugin config with two API env variables and two H5 proxy prefixes. -- Create `miniprogram/.env`: production-like default backend URLs for both venues. -- Create `miniprogram/.env.example`: local development URL examples for both venues. -- Create `miniprogram/src/main.js`: Vue app bootstrap with Pinia and uview-plus. -- Create `miniprogram/src/App.vue`: merged global styles and app lifecycle logging. -- Create `miniprogram/src/manifest.json`: WeChat app manifest using `wx516b3e0d9940c788`. -- Create `miniprogram/src/uni.scss`: shared SCSS imports. -- Create `miniprogram/src/styles/variables.scss`: shared design variables and mixins. -- Create `miniprogram/src/pages.json`: merged page registry, no global `tabBar` in first version. -- Create `miniprogram/src/stores/venue.js`: public Pinia store for current venue selection. -- Create `miniprogram/src/utils/venue.js`: venue constants, storage helpers, target home paths, and navigation helpers. -- Create `miniprogram/src/pages/venue-select/index.vue`: first-entry venue selector and switch page. -- Create `miniprogram/src/modules/comprehensive/api/*`: comprehensive backend API modules. -- Create `miniprogram/src/modules/comprehensive/stores/session.js`: comprehensive session store with dedicated store id. -- Create `miniprogram/src/modules/comprehensive/utils/*`: comprehensive business utility modules. -- Create `miniprogram/src/pages/comprehensive/*`: comprehensive venue route pages. -- Create `miniprogram/src/modules/ice/api/*`: ice backend API modules. -- Create `miniprogram/src/components/ice/StatePanel.vue`: ice module state panel component. -- Create `miniprogram/src/pages/ice/*`: ice venue route pages. -- Create `miniprogram/src/static/comprehensive/tab/*`: comprehensive tab image assets for future module navigation. -- Create `miniprogram/src/static/ice/tab/*`: ice tab image assets for future module navigation. - -## Source Roots - -- Existing comprehensive miniapp: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram` -- Existing ice miniapp: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram` -- New merged miniapp: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram` - -## Implementation Tasks - -### Task 1: Scaffold the Merged Miniapp Shell - -**Files:** -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package.json` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package-lock.json` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/vite.config.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/.env` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/.env.example` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/main.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/App.vue` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/manifest.json` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/uni.scss` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/styles/variables.scss` - -- [ ] **Step 1: Copy baseline project metadata** - -Run: - -```bash -cp '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/package.json' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package.json' -cp '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/package-lock.json' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package-lock.json' -``` - -Expected: both files exist in `场馆小程序/miniprogram`. - -- [ ] **Step 2: Update merged package metadata** - -Edit `miniprogram/package.json` to: - -```json -{ - "name": "merged-venue-miniapp", - "version": "0.1.0", - "description": "冰场馆与综合场馆合并版微信小程序端", - "type": "module", - "scripts": { - "dev:mp-weixin": "uni -p mp-weixin", - "build:mp-weixin": "uni build -p mp-weixin", - "dev:h5": "uni", - "build:h5": "uni build" - }, - "dependencies": { - "@dcloudio/uni-app": "3.0.0-alpha-5000120260211001", - "@dcloudio/uni-components": "3.0.0-alpha-5000120260211001", - "@dcloudio/uni-h5": "3.0.0-alpha-5000120260211001", - "@dcloudio/uni-mp-weixin": "3.0.0-alpha-5000120260211001", - "dayjs": "^1.11.21", - "pinia": "^2.3.1", - "uview-plus": "^3.8.37", - "vue": "^3.4.0" - }, - "devDependencies": { - "@dcloudio/uni-automator": "3.0.0-alpha-5000120260211001", - "@dcloudio/uni-cli-shared": "3.0.0-alpha-5000120260211001", - "@dcloudio/uni-stacktracey": "3.0.0-alpha-5000120260211001", - "@dcloudio/vite-plugin-uni": "3.0.0-alpha-5000120260211001", - "sass": "^1.83.0", - "vite": "^5.2.0" - } -} -``` - -Expected: package name and description identify the merged app; dependencies remain identical to the two existing miniapps. - -- [ ] **Step 3: Create environment files** - -Create `miniprogram/.env`: - -```dotenv -VITE_ICE_API_BASE_URL=https://venue.test.chatgqt.top -VITE_COMPREHENSIVE_API_BASE_URL=https://venue2.test.chatgqt.top -``` - -Create `miniprogram/.env.example`: - -```dotenv -VITE_ICE_API_BASE_URL=http://127.0.0.1:8000 -VITE_COMPREHENSIVE_API_BASE_URL=http://127.0.0.1:8001 -``` - -Expected: no old single `VITE_API_BASE_URL` remains in these files. - -- [ ] **Step 4: Create Vite config** - -Create `miniprogram/vite.config.js`: - -```js -import { fileURLToPath, URL } from 'node:url' -import { defineConfig, loadEnv } from 'vite' -import uniPlugin from '@dcloudio/vite-plugin-uni' - -const uni = typeof uniPlugin === 'function' ? uniPlugin : uniPlugin.default - -export default defineConfig(({ mode }) => { - const env = loadEnv(mode, process.cwd(), '') - const iceApiBase = env.VITE_ICE_API_BASE_URL || 'http://127.0.0.1:8000' - const comprehensiveApiBase = env.VITE_COMPREHENSIVE_API_BASE_URL || 'http://127.0.0.1:8001' - - return { - plugins: [uni()], - optimizeDeps: { - include: ['uview-plus'], - }, - resolve: { - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)), - }, - }, - css: { - preprocessorOptions: { - scss: { - api: 'modern-compiler', - }, - }, - }, - server: { - proxy: { - '/ice-api': { - target: iceApiBase, - changeOrigin: true, - secure: false, - rewrite: (path) => path.replace(/^\/ice-api/, ''), - }, - '/comprehensive-api': { - target: comprehensiveApiBase, - changeOrigin: true, - secure: false, - rewrite: (path) => path.replace(/^\/comprehensive-api/, ''), - }, - }, - }, - } -}) -``` - -Expected: H5 development can proxy both backends; WeChat builds still use env URLs directly. - -- [ ] **Step 5: Create app bootstrap** - -Create `miniprogram/src/main.js`: - -```js -import { createSSRApp } from 'vue' -import App from './App.vue' -import * as Pinia from 'pinia' -import uviewPlus from 'uview-plus' -import 'uview-plus/index.scss' - -export function createApp() { - const app = createSSRApp(App) - app.use(Pinia.createPinia()) - app.use(uviewPlus) - return { - app, - Pinia - } -} -``` - -Expected: Pinia and uview-plus are globally registered. - -- [ ] **Step 6: Create manifest** - -Create `miniprogram/src/manifest.json`: - -```json -{ - "name": "场馆服务", - "appid": "", - "description": "冰场馆与综合场馆合并版会员服务小程序", - "versionName": "0.1.0", - "versionCode": "100", - "transformPx": false, - "mp-weixin": { - "appid": "wx516b3e0d9940c788", - "setting": { - "urlCheck": false, - "es6": true, - "postcss": true, - "minified": true - }, - "usingComponents": true - }, - "vueVersion": "3" -} -``` - -Expected: WeChat appid is `wx516b3e0d9940c788`. - -- [ ] **Step 7: Create global SCSS files** - -Create `miniprogram/src/uni.scss`: - -```scss -@use "@/styles/variables.scss" as *; -@import "uview-plus/theme.scss"; -``` - -Create `miniprogram/src/styles/variables.scss`: - -```scss -$bg-color-main: #FFFFFF; -$bg-color-soft: #F8FAFC; -$bg-color-hover: #F1F5F9; -$bg-color-panel: #EFF6FF; - -$text-color-main: #0F172A; -$text-color-regular: #334155; -$text-color-muted: #64748B; -$text-color-light: #94A3B8; - -$color-primary: #0EA5E9; -$color-primary-light: #E0F2FE; -$color-success: #10B981; -$color-success-light: #D1FAE5; -$color-warning: #F59E0B; -$color-warning-light: #FEF3C7; -$color-danger: #EF4444; -$color-danger-light: #FEE2E2; - -$border-color: #E2E8F0; -$border-radius-sm: 6rpx; -$border-radius-base: 12rpx; -$border-radius-lg: 20rpx; - -$spacing-xs: 8rpx; -$spacing-sm: 16rpx; -$spacing-md: 24rpx; -$spacing-lg: 32rpx; -$spacing-xl: 48rpx; - -@mixin minimal-card { - background: $bg-color-main; - border: 1rpx solid $border-color; - border-radius: $border-radius-base; - padding: $spacing-md; - box-shadow: 0 2rpx 8rpx rgba(15, 23, 42, 0.03); -} - -@mixin text-ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} -``` - -Expected: SCSS variables used by both old projects are available. - -- [ ] **Step 8: Create global App component** - -Create `miniprogram/src/App.vue`: - -```vue - - - -``` - -Expected: common global classes used by the comprehensive project remain available, and ice pages still get the shared variables. - -- [ ] **Step 9: Commit shell** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git add miniprogram/package.json miniprogram/package-lock.json miniprogram/vite.config.js miniprogram/.env miniprogram/.env.example miniprogram/src/main.js miniprogram/src/App.vue miniprogram/src/manifest.json miniprogram/src/uni.scss miniprogram/src/styles/variables.scss -git commit -m "feat: scaffold merged miniapp shell" -``` - -Expected: commit succeeds with only shell files staged. - -### Task 2: Add Public Venue Selection State and Page - -**Files:** -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/utils/venue.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/stores/venue.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/venue-select/index.vue` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages.json` - -- [ ] **Step 1: Create venue utility module** - -Create `miniprogram/src/utils/venue.js`: - -```js -export const CURRENT_VENUE_KEY = 'CURRENT_VENUE' - -export const VENUE_KEYS = Object.freeze({ - ICE: 'ice', - COMPREHENSIVE: 'comprehensive' -}) - -export const VENUE_OPTIONS = Object.freeze([ - { - key: VENUE_KEYS.ICE, - name: '冰场馆', - description: '课程预约、会员卡与滑冰服务', - homePath: '/pages/ice/home/index' - }, - { - key: VENUE_KEYS.COMPREHENSIVE, - name: '综合场馆', - description: '课程、包场、门票、订单与余额', - homePath: '/pages/comprehensive/home/index' - } -]) - -export const isVenueKey = (value) => { - return value === VENUE_KEYS.ICE || value === VENUE_KEYS.COMPREHENSIVE -} - -export const getVenueOption = (venueKey) => { - return VENUE_OPTIONS.find((item) => item.key === venueKey) || null -} - -export const getStoredVenue = () => { - const venue = uni.getStorageSync(CURRENT_VENUE_KEY) - return isVenueKey(venue) ? venue : '' -} - -export const setStoredVenue = (venueKey) => { - if (!isVenueKey(venueKey)) { - uni.removeStorageSync(CURRENT_VENUE_KEY) - return '' - } - - uni.setStorageSync(CURRENT_VENUE_KEY, venueKey) - return venueKey -} - -export const navigateToVenueHome = (venueKey, options = {}) => { - const venue = getVenueOption(venueKey) - if (!venue) { - uni.reLaunch({ url: '/pages/venue-select/index' }) - return - } - - const navigate = options.redirect === true ? uni.redirectTo : uni.reLaunch - navigate({ url: venue.homePath }) -} -``` - -Expected: utility has a single source of truth for venue keys and home paths. - -- [ ] **Step 2: Create venue Pinia store** - -Create `miniprogram/src/stores/venue.js`: - -```js -import { defineStore } from 'pinia' -import { getStoredVenue, getVenueOption, navigateToVenueHome, setStoredVenue } from '@/utils/venue' - -export const useVenueStore = defineStore('currentVenue', { - state: () => ({ - currentVenue: '' - }), - getters: { - currentVenueOption: (state) => getVenueOption(state.currentVenue), - hasSelectedVenue: (state) => Boolean(getVenueOption(state.currentVenue)) - }, - actions: { - init() { - this.currentVenue = getStoredVenue() - return this.currentVenue - }, - selectVenue(venueKey) { - this.currentVenue = setStoredVenue(venueKey) - return this.currentVenue - }, - enterVenue(venueKey) { - const selectedVenue = this.selectVenue(venueKey) - navigateToVenueHome(selectedVenue) - } - } -}) -``` - -Expected: selection persists and can navigate to module home. - -- [ ] **Step 3: Create venue selection page** - -Create `miniprogram/src/pages/venue-select/index.vue`: - -```vue - - - - - -``` - -Expected: first page gives two concrete choices and persists selection before navigation. - -- [ ] **Step 4: Create initial pages registry** - -Create `miniprogram/src/pages.json`: - -```json -{ - "easycom": { - "autoscan": true, - "custom": { - "^u--(.*)": "uview-plus/components/u-$1/u-$1.vue", - "^up-(.*)": "uview-plus/components/u-$1/u-$1.vue", - "^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue" - } - }, - "pages": [ - { - "path": "pages/venue-select/index", - "style": { - "navigationBarTitleText": "选择场馆", - "navigationBarBackgroundColor": "#FFFFFF", - "navigationBarTextStyle": "black" - } - } - ], - "globalStyle": { - "navigationBarTextStyle": "black", - "navigationBarBackgroundColor": "#FFFFFF", - "backgroundColor": "#F8FAFC", - "backgroundColorBottom": "#F8FAFC", - "backgroundColorTop": "#FFFFFF" - } -} -``` - -Expected: app has a valid first page before business modules are migrated. - -- [ ] **Step 5: Verify selection shell builds** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -npm install -npm run build:mp-weixin -``` - -Expected: build succeeds or fails only because `/pages/ice/home/index` and `/pages/comprehensive/home/index` do not exist yet after tapping; compile-time should not require those target files until pages are registered. If compile fails for missing dependencies, fix dependency installation before continuing. - -- [ ] **Step 6: Commit selection shell** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git add miniprogram/src/utils/venue.js miniprogram/src/stores/venue.js miniprogram/src/pages/venue-select/index.vue miniprogram/src/pages.json miniprogram/package-lock.json -git commit -m "feat: add venue selection shell" -``` - -Expected: commit succeeds with public shell files and any refreshed lockfile. - -### Task 3: Migrate Comprehensive Venue Module - -**Files:** -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/api/*.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/stores/session.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/utils/*.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/comprehensive/**` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/comprehensive/tab/*.png` -- Modify: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages.json` - -- [ ] **Step 1: Copy comprehensive source files into namespaced locations** - -Run: - -```bash -mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive' -mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/comprehensive' -mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/comprehensive' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/api' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/api' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/stores' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/stores' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/utils' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/utils' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/pages/.' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/comprehensive/' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/static/tab' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/comprehensive/tab' -``` - -Expected: comprehensive pages exist under `src/pages/comprehensive`, and API/store/utils exist under `src/modules/comprehensive`. - -- [ ] **Step 2: Update comprehensive request base URL** - -Edit `miniprogram/src/modules/comprehensive/api/request.js` so the top constants are: - -```js -const KEY_TOKEN = 'COMPREHENSIVE_VENUE_TOKEN' - -const API_BASE_URL = import.meta.env.VITE_COMPREHENSIVE_API_BASE_URL || 'http://127.0.0.1:8001' -``` - -Expected: `VITE_API_BASE_URL` is not used in this module. - -- [ ] **Step 3: Rename comprehensive session store id and imports** - -Edit `miniprogram/src/modules/comprehensive/stores/session.js`: - -```js -import { defineStore } from 'pinia' -import { authApi, authStorage } from '@/modules/comprehensive/api/auth' -import { memberApi } from '@/modules/comprehensive/api/member' - -export const useComprehensiveSessionStore = defineStore('comprehensiveVenueSession', { - state: () => ({ - isReady: false, - isLoggedIn: false, - member: null - }), - actions: { - async init() { - this.isLoggedIn = authStorage.isLoggedIn() - if (this.isLoggedIn) { - try { - this.member = await memberApi.getMemberMe() - } catch (error) { - if ([40001, 40002, 401, 403].includes(error.code) || [401, 403].includes(error.statusCode)) { - authStorage.clear() - this.member = null - this.isLoggedIn = false - } - } - } - this.isReady = true - }, - async login() { - const loginResult = await loginByWechat() - const data = await authApi.wechatLogin({ code: loginResult.code }) - this.member = data.member - this.isLoggedIn = true - }, - logout() { - authApi.logout() - this.member = null - this.isLoggedIn = false - }, - async refreshMember() { - if (!authStorage.isLoggedIn()) return - this.member = await memberApi.getMemberMe() - this.isLoggedIn = true - } - } -}) - -const loginByWechat = () => new Promise((resolve, reject) => { - uni.login({ - provider: 'weixin', - success: (res) => { - if (!res.code) { - reject(new Error('微信登录未返回 code')) - return - } - resolve(res) - }, - fail: reject - }) -}) -``` - -Expected: no `@/api` imports and no `defineStore('session')` remain in this file. - -- [ ] **Step 4: Rewrite comprehensive page imports** - -Run these replacements from `miniprogram`: - -```bash -perl -pi -e "s#@/api/#@/modules/comprehensive/api/#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#@/stores/session#@/modules/comprehensive/stores/session#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#@/utils/#@/modules/comprehensive/utils/#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#useSessionStore#useComprehensiveSessionStore#g" src/pages/comprehensive/**/*.vue -``` - -Expected: comprehensive pages import from `@/modules/comprehensive/*`. - -- [ ] **Step 5: Rewrite comprehensive route strings** - -Run these replacements from `miniprogram`: - -```bash -perl -pi -e "s#/pages/home#/pages/comprehensive/home#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/courses#/pages/comprehensive/courses#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/bookings#/pages/comprehensive/bookings#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/profile#/pages/comprehensive/profile#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/activities#/pages/comprehensive/activities#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/private#/pages/comprehensive/private#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/tickets#/pages/comprehensive/tickets#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/orders#/pages/comprehensive/orders#g" src/pages/comprehensive/**/*.vue -perl -pi -e "s#/pages/pay#/pages/comprehensive/pay#g" src/pages/comprehensive/**/*.vue -``` - -Expected: comprehensive pages no longer navigate to unprefixed old routes. - -- [ ] **Step 6: Add comprehensive switch entry to profile menu** - -Edit `miniprogram/src/pages/comprehensive/profile/index.vue`. - -Add one item to `menuItems` after the personal info item: - -```js -{ title: '切换场馆', url: '/pages/venue-select/index', theme: 'profile-theme', icon: 'profile' } -``` - -Expected: logged-in comprehensive users can navigate to the venue selection page. If the page shows no menu when logged out, the venue selector remains available by manually opening the first page; Task 7 will also validate direct switching. - -- [ ] **Step 7: Register comprehensive pages** - -Modify `miniprogram/src/pages.json` by appending these page entries after `pages/venue-select/index`: - -```json -{ - "path": "pages/comprehensive/home/index", - "style": { - "navigationBarTitleText": "首页", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/comprehensive/courses/index", - "style": { - "navigationBarTitleText": "课程", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/comprehensive/bookings/index", - "style": { - "navigationBarTitleText": "我的预约", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/comprehensive/profile/index", - "style": { - "navigationBarTitleText": "会员中心" - } -}, -{ - "path": "pages/comprehensive/activities/index", - "style": { - "navigationBarTitleText": "活动公告", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/comprehensive/activities/detail", - "style": { - "navigationBarTitleText": "活动详情" - } -}, -{ - "path": "pages/comprehensive/courses/detail", - "style": { - "navigationBarTitleText": "课程详情" - } -}, -{ - "path": "pages/comprehensive/courses/confirm", - "style": { - "navigationBarTitleText": "确认课程预约" - } -}, -{ - "path": "pages/comprehensive/private/index", - "style": { - "navigationBarTitleText": "包场预约" - } -}, -{ - "path": "pages/comprehensive/private/confirm", - "style": { - "navigationBarTitleText": "确认包场" - } -}, -{ - "path": "pages/comprehensive/tickets/index", - "style": { - "navigationBarTitleText": "篮球门票" - } -}, -{ - "path": "pages/comprehensive/pay/index", - "style": { - "navigationBarTitleText": "订单支付" - } -}, -{ - "path": "pages/comprehensive/orders/detail", - "style": { - "navigationBarTitleText": "订单详情" - } -}, -{ - "path": "pages/comprehensive/bookings/course-detail", - "style": { - "navigationBarTitleText": "课程预约详情" - } -}, -{ - "path": "pages/comprehensive/bookings/private-detail", - "style": { - "navigationBarTitleText": "包场预约详情" - } -}, -{ - "path": "pages/comprehensive/profile/balance", - "style": { - "navigationBarTitleText": "余额流水", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/comprehensive/profile/tickets", - "style": { - "navigationBarTitleText": "我的门票", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/comprehensive/profile/orders", - "style": { - "navigationBarTitleText": "我的订单", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/comprehensive/profile/detail", - "style": { - "navigationBarTitleText": "个人信息" - } -} -``` - -Expected: all comprehensive pages from the old `pages.json` are registered with the `pages/comprehensive` prefix. - -- [ ] **Step 8: Verify comprehensive module references** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -rg -n "@/api|@/stores/session|@/utils/" src/pages/comprehensive src/modules/comprehensive -rg -n "'/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|\"/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)" src/pages/comprehensive -rg -n "VITE_API_BASE_URL" src/modules/comprehensive -``` - -Expected: all three commands return no matches. - -- [ ] **Step 9: Build after comprehensive migration** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -npm run build:mp-weixin -``` - -Expected: build succeeds, or fails only because ice pages have not been registered yet. If the failure references comprehensive imports, fix those references before continuing. - -- [ ] **Step 10: Commit comprehensive module** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git add miniprogram/src/modules/comprehensive miniprogram/src/pages/comprehensive miniprogram/src/static/comprehensive miniprogram/src/pages.json -git commit -m "feat: migrate comprehensive venue module" -``` - -Expected: commit succeeds with comprehensive module files. - -### Task 4: Migrate Ice Venue Module - -**Files:** -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/ice/api/*.js` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/components/ice/StatePanel.vue` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/ice/**` -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/ice/tab/*.png` -- Modify: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages.json` - -- [ ] **Step 1: Copy ice source files into namespaced locations** - -Run: - -```bash -mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/ice' -mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/ice' -mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/components/ice' -mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/ice' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/api' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/ice/api' -cp '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/components/StatePanel.vue' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/components/ice/StatePanel.vue' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/pages/.' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/ice/' -cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/static/tab' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/ice/tab' -``` - -Expected: ice pages exist under `src/pages/ice`, API under `src/modules/ice/api`, and state panel under `src/components/ice`. - -- [ ] **Step 2: Update ice request base URL** - -Edit `miniprogram/src/modules/ice/api/request.js` so the base URL constant is: - -```js -const API_BASE_URL = import.meta.env.VITE_ICE_API_BASE_URL || 'http://127.0.0.1:8000' -``` - -Expected: `VITE_API_BASE_URL` is not used in this module. - -- [ ] **Step 3: Rewrite ice page imports** - -Run these replacements from `miniprogram`: - -```bash -perl -pi -e "s#@/api/#@/modules/ice/api/#g" src/pages/ice/**/*.vue -perl -pi -e "s#@/components/StatePanel.vue#@/components/ice/StatePanel.vue#g" src/pages/ice/**/*.vue -``` - -Expected: ice pages import from `@/modules/ice/api/*` and `@/components/ice/StatePanel.vue`. - -- [ ] **Step 4: Rewrite ice route strings** - -Run these replacements from `miniprogram`: - -```bash -perl -pi -e "s#/pages/home#/pages/ice/home#g" src/pages/ice/**/*.vue -perl -pi -e "s#/pages/courses#/pages/ice/courses#g" src/pages/ice/**/*.vue -perl -pi -e "s#/pages/bookings#/pages/ice/bookings#g" src/pages/ice/**/*.vue -perl -pi -e "s#/pages/profile#/pages/ice/profile#g" src/pages/ice/**/*.vue -perl -pi -e "s#/pages/activities#/pages/ice/activities#g" src/pages/ice/**/*.vue -``` - -Expected: ice pages no longer navigate to unprefixed old routes. - -- [ ] **Step 5: Add ice switch entry to profile page** - -Edit `miniprogram/src/pages/ice/profile/index.vue`. - -Add a menu item in the logged-in menu group before the logout item: - -```vue - - - - - - - - - 切换场馆 - - - - - -``` - -If the page does not define `.profile-theme` or `.svg-profile-minimal`, use the existing `card-theme` icon container classes instead: - -```vue - - - - - -``` - -Expected: logged-in ice users can navigate to the venue selection page. - -- [ ] **Step 6: Register ice pages** - -Modify `miniprogram/src/pages.json` by appending these page entries after the comprehensive entries: - -```json -{ - "path": "pages/ice/home/index", - "style": { - "navigationBarTitleText": "首页", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/ice/courses/index", - "style": { - "navigationBarTitleText": "近期课程", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/ice/bookings/index", - "style": { - "navigationBarTitleText": "我的预约", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/ice/profile/index", - "style": { - "navigationBarTitleText": "会员中心" - } -}, -{ - "path": "pages/ice/bookings/detail", - "style": { - "navigationBarTitleText": "预约详情" - } -}, -{ - "path": "pages/ice/courses/detail", - "style": { - "navigationBarTitleText": "课程详情" - } -}, -{ - "path": "pages/ice/profile/cards", - "style": { - "navigationBarTitleText": "我的会员卡", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/ice/profile/card-history", - "style": { - "navigationBarTitleText": "会员卡使用流水", - "enablePullDownRefresh": true - } -}, -{ - "path": "pages/ice/profile/detail", - "style": { - "navigationBarTitleText": "个人信息" - } -}, -{ - "path": "pages/ice/activities/detail", - "style": { - "navigationBarTitleText": "活动详情" - } -} -``` - -Expected: all ice pages from the old `pages.json` are registered with the `pages/ice` prefix. - -- [ ] **Step 7: Verify ice module references** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -rg -n "@/api|@/components/StatePanel.vue" src/pages/ice src/modules/ice -rg -n "'/pages/(home|courses|bookings|profile|activities)|\"/pages/(home|courses|bookings|profile|activities)" src/pages/ice -rg -n "VITE_API_BASE_URL" src/modules/ice -``` - -Expected: all three commands return no matches. - -- [ ] **Step 8: Build after ice migration** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -npm run build:mp-weixin -``` - -Expected: build succeeds. If build fails, fix the reported import, route registration, or SCSS variable error before continuing. - -- [ ] **Step 9: Commit ice module** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git add miniprogram/src/modules/ice miniprogram/src/pages/ice miniprogram/src/components/ice miniprogram/src/static/ice miniprogram/src/pages.json -git commit -m "feat: migrate ice venue module" -``` - -Expected: commit succeeds with ice module files. - -### Task 5: Finalize Cross-Module Navigation and Safety Checks - -**Files:** -- Modify: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/venue-select/index.vue` -- Modify: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/utils/venue.js` -- Modify: module pages only if checks reveal stale routes or stale imports. - -- [ ] **Step 1: Ensure venue selector can return to stored current venue** - -Update `miniprogram/src/pages/venue-select/index.vue` to keep the existing card UI and add a continue action when a venue is already selected: - -```vue - -``` - -Add script code: - -```js -const selectedVenue = computed(() => venueStore.currentVenueOption) - -const continueCurrentVenue = () => { - if (!venueStore.currentVenue) return - venueStore.enterVenue(venueStore.currentVenue) -} -``` - -Add styles: - -```scss -.current-card { - display: flex; - flex-direction: column; - gap: 12rpx; - margin-bottom: 32rpx; - padding: 28rpx; - border-radius: $border-radius-lg; - background: $bg-color-main; - border: 1rpx solid $border-color; -} - -.current-label { - color: $text-color-muted; - font-size: 22rpx; - font-weight: 700; -} - -.current-name { - color: $text-color-main; - font-size: 32rpx; - font-weight: 900; -} - -.continue-btn { - margin-top: 12rpx; -} -``` - -Expected: users who return to the selector can continue into the stored venue without changing it. - -- [ ] **Step 2: Run stale import checks** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -rg -n "@/api|@/stores/session|@/utils/(date|money|status)|@/components/StatePanel.vue" src/pages src/modules -``` - -Expected: no matches. If matches appear, rewrite them to module-specific imports: - -```text -@/modules/ice/api/ -@/modules/comprehensive/api/ -@/modules/comprehensive/utils/ -@/components/ice/StatePanel.vue -``` - -- [ ] **Step 3: Run stale route checks** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -rg -n "'/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|\"/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|`/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)" src/pages -``` - -Expected: no matches. If matches appear, prefix with `ice` or `comprehensive` based on the file path. - -- [ ] **Step 4: Run API base URL checks** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -rg -n "VITE_API_BASE_URL|const API_BASE_URL" src/modules -``` - -Expected output includes only: - -```text -src/modules/ice/api/request.js:const API_BASE_URL = import.meta.env.VITE_ICE_API_BASE_URL || 'http://127.0.0.1:8000' -src/modules/comprehensive/api/request.js:const API_BASE_URL = import.meta.env.VITE_COMPREHENSIVE_API_BASE_URL || 'http://127.0.0.1:8001' -``` - -- [ ] **Step 5: Run token isolation checks** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -rg -n "ICE_MEMBER_TOKEN|ICE_MEMBER_TOKEN_EXPIRES_AT|COMPREHENSIVE_VENUE_TOKEN" src/modules -``` - -Expected output includes: - -```text -src/modules/ice/api/request.js:const KEY_TOKEN = 'ICE_MEMBER_TOKEN' -src/modules/ice/api/request.js:const KEY_TOKEN_EXPIRES_AT = 'ICE_MEMBER_TOKEN_EXPIRES_AT' -src/modules/comprehensive/api/request.js:const KEY_TOKEN = 'COMPREHENSIVE_VENUE_TOKEN' -``` - -- [ ] **Step 6: Build final merged app** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -npm run build:mp-weixin -``` - -Expected: build succeeds and outputs `dist/build/mp-weixin`. - -- [ ] **Step 7: Commit safety pass** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git add miniprogram -git commit -m "fix: finalize merged venue navigation" -``` - -Expected: commit succeeds if files changed. If no files changed after checks, skip this commit and record that no safety-pass commit was needed. - -### Task 6: Manual WeChat Mini Program Smoke Test - -**Files:** -- No source files should be modified unless the smoke test finds a defect. - -- [ ] **Step 1: Open generated mini program in WeChat DevTools** - -Open this directory in WeChat DevTools: - -```text -/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/dist/build/mp-weixin -``` - -Expected: project opens with appid `wx516b3e0d9940c788`. - -- [ ] **Step 2: Smoke test venue selector** - -In the simulator: - -```text -1. Open first page. -2. Confirm "选择场馆" appears. -3. Tap "冰场馆". -4. Confirm URL is pages/ice/home/index. -5. Navigate back to pages/venue-select/index. -6. Tap "综合场馆". -7. Confirm URL is pages/comprehensive/home/index. -``` - -Expected: both venue cards navigate to the correct module home page. - -- [ ] **Step 3: Smoke test ice module navigation** - -Open these paths in WeChat DevTools page path input: - -```text -pages/ice/home/index -pages/ice/courses/index -pages/ice/bookings/index -pages/ice/profile/index -pages/ice/profile/cards -``` - -Expected: each page loads without a JavaScript import error. Network errors from unavailable backend are acceptable during local smoke testing if request URLs point to `https://venue.test.chatgqt.top`. - -- [ ] **Step 4: Smoke test comprehensive module navigation** - -Open these paths in WeChat DevTools page path input: - -```text -pages/comprehensive/home/index -pages/comprehensive/courses/index -pages/comprehensive/bookings/index -pages/comprehensive/profile/index -pages/comprehensive/private/index -pages/comprehensive/tickets/index -pages/comprehensive/pay/index?order_id=1 -``` - -Expected: each page loads without a JavaScript import error. The pay page may show an order loading error for `order_id=1`; it must not call the ice backend. - -- [ ] **Step 5: Inspect request domains** - -Use WeChat DevTools network panel: - -```text -1. Open pages/ice/home/index. -2. Confirm requests go to https://venue.test.chatgqt.top. -3. Open pages/comprehensive/home/index. -4. Confirm requests go to https://venue2.test.chatgqt.top. -``` - -Expected: requests never cross backend domains. - -- [ ] **Step 6: Commit smoke-test fixes** - -If source fixes were required, run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git add miniprogram -git commit -m "fix: address merged miniapp smoke test issues" -``` - -Expected: commit succeeds only if smoke testing found and fixed defects. - -### Task 7: Final Verification and Delivery Notes - -**Files:** -- Create: `/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/docs/merged-miniapp-verification.md` - -- [ ] **Step 1: Run final command verification** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram' -npm run build:mp-weixin -rg -n "@/api|@/stores/session|@/utils/(date|money|status)|@/components/StatePanel.vue" src/pages src/modules -rg -n "'/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|\"/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|`/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)" src/pages -rg -n "VITE_API_BASE_URL" src/modules .env .env.example vite.config.js -``` - -Expected: - -```text -npm run build:mp-weixin exits 0 -stale import rg exits 1 with no matches -stale route rg exits 1 with no matches -VITE_API_BASE_URL rg exits 1 with no matches -``` - -- [ ] **Step 2: Write verification note** - -Create `docs/merged-miniapp-verification.md`: - -```markdown -# 合并版场馆小程序验证记录 - -## 构建 - -- 命令:`npm run build:mp-weixin` -- 结果:通过 -- 产物:`miniprogram/dist/build/mp-weixin` - -## 路由隔离 - -- 检查旧的无前缀页面路径:通过 -- 冰场馆页面前缀:`pages/ice` -- 综合场馆页面前缀:`pages/comprehensive` - -## API 隔离 - -- 冰场馆 baseURL:`VITE_ICE_API_BASE_URL` -- 综合场馆 baseURL:`VITE_COMPREHENSIVE_API_BASE_URL` -- 旧 `VITE_API_BASE_URL`:未使用 - -## 登录态隔离 - -- 冰场馆 token:`ICE_MEMBER_TOKEN` -- 冰场馆 token 过期时间:`ICE_MEMBER_TOKEN_EXPIRES_AT` -- 综合场馆 token:`COMPREHENSIVE_VENUE_TOKEN` - -## 上线前人工检查 - -- 微信 request 合法域名包含 `venue.test.chatgqt.top` -- 微信 request 合法域名包含 `venue2.test.chatgqt.top` -- 综合场馆支付仍由综合场馆后端生成支付参数 -``` - -If any command fails, replace `结果:通过` with the exact failing command and actual status, then fix the issue before final delivery. - -- [ ] **Step 3: Commit verification note** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git add docs/merged-miniapp-verification.md -git commit -m "docs: record merged miniapp verification" -``` - -Expected: commit succeeds with the verification record. - -- [ ] **Step 4: Final git status check** - -Run: - -```bash -cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序' -git status --short -``` - -Expected: no uncommitted files except intentionally ignored build output if `.gitignore` is added later. If build output appears and should not be committed, add a project `.gitignore` that ignores: - -```gitignore -miniprogram/node_modules/ -miniprogram/dist/ -.DS_Store -``` - -Commit `.gitignore` with: - -```bash -git add .gitignore -git commit -m "chore: ignore generated miniapp files" -``` - -Expected: working tree is clean or only contains intentionally untracked files reported to the user. - -## Plan Self-Review - -Spec coverage: - -- New project under `场馆小程序/miniprogram`: Task 1. -- First-entry venue choice and in-app switching: Tasks 2, 5, and 6. -- Isolated Django backends and API clients: Tasks 1, 3, 4, and 5. -- Token isolation: Tasks 3, 4, 5, and 7. -- Route prefixing: Tasks 3, 4, and 5. -- No backend/admin/CI merge: all tasks operate only under `场馆小程序`. -- Build and smoke verification: Tasks 5, 6, and 7. - -Placeholder scan: the plan intentionally avoids unspecified implementation slots. Each migration step includes exact copy commands, rewrite commands, file paths, expected checks, and commit commands. - -Type consistency: - -- Venue key values are `ice` and `comprehensive` across `VENUE_KEYS`, storage, routes, and page prefixes. -- Store exports are `useVenueStore` and `useComprehensiveSessionStore`. -- Environment variables are `VITE_ICE_API_BASE_URL` and `VITE_COMPREHENSIVE_API_BASE_URL`. -- Token keys are `ICE_MEMBER_TOKEN`, `ICE_MEMBER_TOKEN_EXPIRES_AT`, and `COMPREHENSIVE_VENUE_TOKEN`. diff --git a/docs/superpowers/specs/2026-06-08-merged-venue-miniapp-design.md b/docs/superpowers/specs/2026-06-08-merged-venue-miniapp-design.md deleted file mode 100644 index 4f9d3a6..0000000 --- a/docs/superpowers/specs/2026-06-08-merged-venue-miniapp-design.md +++ /dev/null @@ -1,412 +0,0 @@ -# 合并版场馆小程序设计 - -## 背景 - -当前有两套独立系统: - -- 冰场馆小程序:前端为 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/` 导入都改为模块路径: - -```text -@/modules/ice/api/ -@/modules/comprehensive/api/ -``` - -综合场馆 `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 -``` - -它可以独立构建微信小程序。用户首次进入选择场馆,后续可在小程序内切换;两个场馆后端、登录态、支付和业务数据仍完全隔离。