22 KiB
综合场馆新增业务前端对接 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: 完成综合场馆排课接口迁移、商城储蓄卡与次卡购买、已有次卡可约排课、个人中心入口和支付规则,并移除两个场馆首页的近期课程区块。
Architecture: 延续现有 uni-app Vue 3 的 pages/comprehensive 页面层与 modules/comprehensive/api 接口映射层。会员端课程统一使用排课 session,商城复用现有订单支付页,普通课程预约与次卡预约保持独立数据模型;不新增依赖、不改后端、不重构冰场馆其他业务。
Tech Stack: uni-app、Vue 3 <script setup>、Pinia、JavaScript、Node.js 静态验证脚本、npm
Global Constraints
- 有效前端目录仅为
场馆小程序/miniprogram,不得修改两个已废弃的冰场馆小程序/miniprogram、综合场馆小程序/miniprogram。 - 产品界面使用“储蓄卡”称谓;代码、接口和后端模型沿用
storedValue、stored_value和“储值卡”。 - 排课详情和预约使用
session.id;course_id仅表示课程模板,不能用于预约。 - 储蓄卡订单仅允许微信支付;次卡套餐订单允许余额支付和微信支付。
- 次卡预约不创建支付订单,预约时不扣次数,后台核销时才扣减。
- 普通预约和次卡预约的嵌套
session不保证包含courts,相关页面不得依赖该字段。 - 综合场馆与冰场馆首页均不再展示近期课程,但课程列表和详情入口必须保留。
- 不修改场地页面视觉、不实现后台锁场、不修改冰场馆其他业务。
- 不新增 npm 依赖;继续使用现有请求封装、登录 store、样式变量和分页约定。
文件结构与职责
新建文件
| 文件 | 职责 |
|---|---|
src/modules/comprehensive/api/membership.js |
次卡套餐、持卡、流水、可约排课和次卡预约的请求与映射 |
src/modules/comprehensive/api/storedValue.js |
储蓄卡商品和购买订单的请求与映射 |
src/pages/comprehensive/mall/index.vue |
商城分区展示次卡套餐和储蓄卡,创建订单并跳转支付 |
src/pages/comprehensive/membership/cards.vue |
我的次卡分页列表 |
src/pages/comprehensive/membership/card-detail.vue |
次卡信息、适用课程和使用流水 |
src/pages/comprehensive/membership/available-sessions.vue |
当前已有次卡能够预约的排课列表与选卡预约 |
src/pages/comprehensive/membership/booking-detail.vue |
次卡预约详情 |
scripts/verify-comprehensive-session-contract.mjs |
验证排课 URL、session_id、booking.session 和首页移除课程 |
scripts/verify-comprehensive-commerce.mjs |
验证商城、次卡页面、支付限制、个人中心入口和路由注册 |
修改文件
| 文件 | 修改职责 |
|---|---|
src/modules/comprehensive/api/course.js |
/api/sessions 与 { session_id } |
src/modules/comprehensive/api/content.js |
首页不再映射近期排课 |
src/modules/comprehensive/api/booking.js |
普通预约改读 session_id 和 session |
src/pages/comprehensive/courses/*.vue |
页面变量和提交语义改为排课 |
src/pages/comprehensive/bookings/index.vue |
普通预约/次卡预约切换展示 |
src/pages/comprehensive/bookings/course-detail.vue |
普通预约详情读取 booking.session,不依赖场地字段 |
src/pages/comprehensive/profile/index.vue |
余额、充值、我的次卡、流水和消费记录入口 |
src/pages/comprehensive/profile/orders.vue |
新增会员卡、储值卡订单筛选 |
src/pages/comprehensive/pay/index.vue |
储值卡订单强制微信支付并复查订单状态 |
src/pages/comprehensive/home/index.vue |
移除近期课程区块和相关状态 |
src/pages/ice/home/index.vue |
移除近期课程区块和相关状态 |
src/modules/ice/api/home.js |
首页请求不再传课程参数、不再映射近期课程 |
src/pages.json |
注册商城和次卡页面 |
package.json |
注册两条新的验证命令 |
Task 1: 排课接口与普通课程预约迁移
Files:
- Create:
scripts/verify-comprehensive-session-contract.mjs - Modify:
src/modules/comprehensive/api/course.js - Modify:
src/modules/comprehensive/api/booking.js - Modify:
src/pages/comprehensive/courses/index.vue - Modify:
src/pages/comprehensive/courses/detail.vue - Modify:
src/pages/comprehensive/courses/confirm.vue - Modify:
src/pages/comprehensive/bookings/index.vue - Modify:
src/pages/comprehensive/bookings/course-detail.vue - Modify:
package.json
Interfaces:
-
Produces:
toSession(backendSession): SessionViewModel -
Produces:
courseApi.getSessions(params)、courseApi.getSessionDetail(sessionId)、courseApi.createCourseBooking(sessionId) -
Produces:
toCourseBooking()返回{ session_id, session, order, ...statusFields } -
Consumes: 现有
request()、toPagination()、toPageQuery()、日期和金额工具 -
Step 1: 先建立会失败的排课契约验证
新增验证脚本,至少断言:
expectIncludes(courseApi, "url: '/api/sessions'", 'session list endpoint')
expectIncludes(courseApi, 'url: `/api/sessions/${id}`', 'session detail endpoint')
expectIncludes(courseApi, 'data: { session_id: sessionId }', 'booking payload')
expectExcludes(courseApi, "url: '/api/courses'", 'legacy course endpoint')
expectIncludes(bookingApi, 'session_id: backendBooking.session_id', 'booking session id')
expectIncludes(bookingApi, 'session: toBookingSession(backendBooking.session)', 'booking session mapper')
expectExcludes(bookingApi, 'course: toBookingCourse(backendBooking.course)', 'legacy booking course')
在 package.json 增加:
"verify:comprehensive-session": "node scripts/verify-comprehensive-session-contract.mjs"
运行 npm run verify:comprehensive-session,预期因旧 /api/courses、course_id 和 booking.course 而失败。
- Step 2: 完成 API 映射与页面迁移
course.js 的核心接口固定为:
export const toSession = (backendSession = {}) => ({
id: backendSession.id ?? null,
course_id: backendSession.course_id ?? null,
title: backendSession.title || '',
cover_image: backendSession.cover_image || '',
coach_id: backendSession.coach_id ?? null,
coach_name: backendSession.coach_name || '',
start_at: backendSession.start_at || '',
end_at: backendSession.end_at || '',
capacity: toNumber(backendSession.capacity),
booked_count: toNumber(backendSession.booked_count),
remaining_count: toNumber(backendSession.remaining_count),
price: backendSession.price || '0.00',
status: backendSession.status || '',
status_label: backendSession.status_label || backendSession.status || '',
courts: toArray(backendSession.courts).map(toCourseCourt),
detail_html: backendSession.detail_html || '',
created_at: backendSession.created_at || '',
updated_at: backendSession.updated_at || ''
})
export const courseApi = {
getSessions(params = {}) {
return request({ url: '/api/sessions', data: toSessionQuery(params) })
.then((data) => toPagination(data, toSession))
},
getSessionDetail(id) {
return request({ url: `/api/sessions/${id}` }).then(toSession)
},
createCourseBooking(sessionId) {
return request({
url: '/api/bookings/course',
method: 'POST',
data: { session_id: sessionId }
})
}
}
普通预约映射改读 backendBooking.session;嵌套 session 映射允许 courts 缺失并返回空数组。课程列表、详情、确认页内部变量改为 session/sessions,确认页提交 session.id。预约列表和详情改读 item.session、booking.session,并删除对预约嵌套对象 courts.map(...) 的强依赖。
- Step 3: 验证并提交这一条完整链路
运行:
npm run verify:comprehensive-session
npm run build:mp-weixin
预期:验证脚本打印 Comprehensive session contract verification passed.;微信小程序构建退出码为 0。随后提交:
git add package.json scripts/verify-comprehensive-session-contract.mjs src/modules/comprehensive/api/course.js src/modules/comprehensive/api/booking.js src/pages/comprehensive/courses src/pages/comprehensive/bookings
git commit -m "feat: migrate comprehensive courses to sessions"
Task 2: 商城、次卡套餐与储蓄卡购买
Files:
- Create:
src/modules/comprehensive/api/membership.js - Create:
src/modules/comprehensive/api/storedValue.js - Create:
src/pages/comprehensive/mall/index.vue - Create:
scripts/verify-comprehensive-commerce.mjs - Modify:
src/pages.json - Modify:
package.json
Interfaces:
-
Produces:
membershipApi.getPlans()、membershipApi.createMembershipOrder(planId) -
Produces:
storedValueApi.getCards()、storedValueApi.createOrder(cardId) -
Produces: 商城购买成功统一跳转
/pages/comprehensive/pay/index?order_id={id} -
Consumes:
request()、authStorage、综合场馆 session store、现有支付页 -
Step 1: 扩展静态验证,先覆盖商城路由和接口契约
验证脚本检查以下精确契约:
expectIncludes(membershipApi, "url: '/api/membership/plans'", 'membership plans')
expectIncludes(membershipApi, "url: '/api/membership/orders'", 'membership order')
expectIncludes(membershipApi, 'data: { plan_id: planId }', 'membership payload')
expectIncludes(storedValueApi, "url: '/api/stored-value/cards'", 'stored value cards')
expectIncludes(storedValueApi, "url: '/api/stored-value/orders'", 'stored value order')
expectIncludes(storedValueApi, 'data: { card_id: cardId }', 'stored value payload')
expectIncludes(pagesJson, 'pages/comprehensive/mall/index', 'mall route')
expectIncludes(mallPage, '储蓄卡', 'stored value UI copy')
expectIncludes(mallPage, 'face_value', 'face value display')
增加命令:
"verify:comprehensive-commerce": "node scripts/verify-comprehensive-commerce.mjs"
运行 npm run verify:comprehensive-commerce,预期因文件和路由尚不存在而失败。
- Step 2: 实现两个 API 模块和商城页面
membership.js 的套餐和订单接口返回稳定视图模型:
const toPlan = (item = {}) => ({
id: item.id ?? null,
name: item.name || '',
price: item.price || '0.00',
total_count: toNumber(item.total_count),
is_active: item.is_active === true,
courses: toArray(item.courses).map((course) => ({
id: course.id ?? null,
title: course.title || ''
})),
created_at: item.created_at || ''
})
storedValue.js 映射 id/name/price/face_value/is_active/created_at。商城并行加载套餐和储蓄卡,分区展示;储蓄卡卡片必须同时显示“支付 ¥price”和“到账 ¥face_value”。购买前检查登录,创建请求期间禁用对应按钮,成功后只使用后端返回的 order.id 跳转支付页。
在 pages.json 注册商城路由,标题使用“商城”。
- Step 3: 验证商城并提交
运行:
npm run verify:comprehensive-commerce
npm run build:mp-weixin
预期两条命令退出码均为 0。提交:
git add package.json scripts/verify-comprehensive-commerce.mjs src/modules/comprehensive/api/membership.js src/modules/comprehensive/api/storedValue.js src/pages/comprehensive/mall/index.vue src/pages.json
git commit -m "feat: add comprehensive membership and stored value mall"
Task 3: 我的次卡、可约排课和次卡预约
Files:
- Create:
src/pages/comprehensive/membership/cards.vue - Create:
src/pages/comprehensive/membership/card-detail.vue - Create:
src/pages/comprehensive/membership/available-sessions.vue - Create:
src/pages/comprehensive/membership/booking-detail.vue - Modify:
src/modules/comprehensive/api/membership.js - Modify:
src/pages/comprehensive/bookings/index.vue - Modify:
src/pages.json - Modify:
scripts/verify-comprehensive-commerce.mjs
Interfaces:
-
Produces:
getCards(params)、getUsageRecords(cardId, params)、getAvailableSessions(params) -
Produces:
createCardBooking({ cardId, sessionId })、getCardBookings(params)、getCardBookingDetail(bookingId) -
Produces:
AvailableSession.available_card_ids: number[] -
Consumes: Task 2 的
getPlans(),用于按plan_id在次卡详情补充适用课程;不得调用不存在的单卡详情接口 -
Step 1: 先把持卡和预约契约加入验证脚本
加入以下断言后运行验证,预期失败:
expectIncludes(membershipApi, "url: '/api/membership/cards'", 'member cards')
expectIncludes(membershipApi, 'cards/${cardId}/usage-records', 'card usage records')
expectIncludes(membershipApi, "url: '/api/membership/available-courses'", 'available sessions')
expectIncludes(membershipApi, "url: '/api/membership/bookings'", 'card bookings')
expectIncludes(membershipApi, 'data: { card_id: cardId, session_id: sessionId }', 'card booking payload')
expectIncludes(availablePage, 'available_card_ids', 'available card selection')
expectExcludes(bookingDetail, 'booking.order', 'card booking has no order')
- Step 2: 实现次卡 API 与四个页面
membership.js 统一输出:
createCardBooking({ cardId, sessionId }) {
return request({
url: '/api/membership/bookings',
method: 'POST',
data: { card_id: cardId, session_id: sessionId }
}).then(toCardBooking)
}
页面规则:
cards.vue分页显示plan_name/total_count/remaining_count/status_label。card-detail.vue并行请求getPlans()和该卡使用流水,以plan_id匹配套餐并展示适用课程;找不到套餐时仍展示卡和流水。available-sessions.vue只展示/available-courses返回的数据。从卡详情带card_id进入时过滤不含该 ID 的排课;未指定卡时,单个available_card_ids自动选中,多个 ID 让用户选择。- 创建次卡预约后直接跳
membership/booking-detail?id={booking.id},不进入支付页。 booking-detail.vue展示booking.session、booking.card和状态时间,不展示订单与自行取消按钮,也不依赖session.courts。bookings/index.vue在课程业务下增加“普通预约/次卡预约”切换,分别请求/bookings/courses和/membership/bookings;次卡预约金额区域显示“次卡预约”。
在 pages.json 注册四个页面及明确的导航标题。
- Step 3: 验证持卡预约全链路并提交
运行:
npm run verify:comprehensive-commerce
npm run verify:comprehensive-session
npm run build:mp-weixin
预期全部退出码为 0。提交:
git add scripts/verify-comprehensive-commerce.mjs src/modules/comprehensive/api/membership.js src/pages/comprehensive/membership src/pages/comprehensive/bookings/index.vue src/pages.json
git commit -m "feat: add membership card course booking flow"
Task 4: 个人中心、消费记录与储蓄卡支付规则
Files:
- Modify:
src/pages/comprehensive/profile/index.vue - Modify:
src/pages/comprehensive/profile/orders.vue - Modify:
src/pages/comprehensive/pay/index.vue - Modify:
scripts/verify-comprehensive-commerce.mjs
Interfaces:
-
Consumes:
memberApi.getBalance()、商城路由、我的次卡路由、现有订单 API -
Produces: 储值卡订单
business_type === 'stored_value'时只能调用payOrderByWechat() -
Produces: 订单筛选增加
membership和stored_value -
Step 1: 先增加个人中心和支付限制验证
expectIncludes(profilePage, '/pages/comprehensive/mall/index', 'mall entry')
expectIncludes(profilePage, '/pages/comprehensive/membership/cards', 'my cards entry')
expectIncludes(profilePage, '/pages/comprehensive/profile/balance', 'balance records entry')
expectIncludes(profilePage, '/pages/comprehensive/profile/orders', 'consumption records entry')
expectIncludes(orderPage, "value: 'membership'", 'membership filter')
expectIncludes(orderPage, "value: 'stored_value'", 'stored value filter')
expectIncludes(payPage, "order.value?.business_type === 'stored_value'", 'stored value payment guard')
运行 npm run verify:comprehensive-commerce,预期新断言失败。
- Step 2: 扩展个人中心、订单和支付页
个人中心登录后请求余额并展示,菜单增加:
{ title: '充值', url: '/pages/comprehensive/mall/index' }
{ title: '我的次卡', url: '/pages/comprehensive/membership/cards' }
{ title: '余额流水', url: '/pages/comprehensive/profile/balance' }
{ title: '消费记录', url: '/pages/comprehensive/profile/orders' }
订单筛选增加“会员卡”和“储值卡”。支付页定义:
const isStoredValueOrder = computed(() => order.value?.business_type === 'stored_value')
const canUseBalance = computed(() => {
if (isStoredValueOrder.value) return false
return toAmountNumber(balance.value.balance) >= toAmountNumber(order.value?.amount)
})
储值卡订单隐藏余额支付项、默认 wechat,selectMethod('balance') 和 pay() 都必须有储值卡保护。微信 uni.requestPayment 成功后重新调用 loadPage() 获取后端订单状态;不在前端直接增加余额。
- Step 3: 验证并提交个人中心与支付改造
运行:
npm run verify:comprehensive-commerce
npm run build:mp-weixin
预期全部通过。提交:
git add scripts/verify-comprehensive-commerce.mjs src/pages/comprehensive/profile/index.vue src/pages/comprehensive/profile/orders.vue src/pages/comprehensive/pay/index.vue
git commit -m "feat: connect comprehensive profile and stored value payment"
Task 5: 移除首页近期课程并完成整体回归
Files:
- Modify:
src/pages/comprehensive/home/index.vue - Modify:
src/modules/comprehensive/api/content.js - Modify:
src/pages/ice/home/index.vue - Modify:
src/modules/ice/api/home.js - Modify:
scripts/verify-comprehensive-session-contract.mjs - Modify:
scripts/verify-home-venue-switch.mjs(仅在原断言受页面结构变化影响时调整,保留场馆切换验证)
Interfaces:
-
综合场馆首页继续消费
banners/articles/venue,忽略后端upcoming_sessions -
冰场馆首页继续消费
banners/activities/venue,不请求或映射upcoming_courses -
两个场馆课程 Tab 与课程页面不变
-
Step 1: 先加入首页移除课程的失败验证
expectExcludes(comprehensiveHome, '近期课程', 'comprehensive recent courses section')
expectExcludes(comprehensiveHome, 'upcomingCourses', 'comprehensive recent course state')
expectExcludes(comprehensiveContentApi, 'upcoming_courses', 'legacy comprehensive home mapping')
expectExcludes(comprehensiveContentApi, 'upcoming_sessions', 'unused comprehensive home mapping')
expectExcludes(iceHome, '近期课程', 'ice recent courses section')
expectExcludes(iceHome, 'upcomingCourses', 'ice recent course state')
expectExcludes(iceHomeApi, 'course_limit', 'ice recent course request')
expectExcludes(iceHomeApi, 'upcoming_courses', 'ice recent course mapping')
运行 npm run verify:comprehensive-session,预期因两个首页仍包含近期课程而失败。
- Step 2: 删除两个首页的近期课程展示和无用数据流
综合场馆首页删除近期课程模板、upcomingCourses 状态、formatTimeRange 导入和课程相关样式;content.js 的首页模型只返回:
{
banners: toArray(backendData.banners).map(toBanner),
articles: toArray(backendData.articles).map(toArticle),
venue: { name: backendData.venue?.name || '' }
}
冰场馆首页删除近期课程模板、状态、格式化函数和相关样式;home.js 不再导入课程映射、不再发送 course_limit/course_date,首页模型只保留 banners/activities/venue。不要删除 VenueTabBar 的课程 Tab,也不要修改两个场馆的课程列表和详情页面。
- Step 3: 执行完整验证、人工联调清单并提交
自动验证:
npm run verify:tabbar
npm run verify:startup
npm run verify:home-switch
npm run verify:comprehensive-auth
npm run verify:auth-agreements
npm run verify:comprehensive-session
npm run verify:comprehensive-commerce
npm run build:mp-weixin
npm run build:h5
预期所有命令退出码为 0。随后在微信开发者工具连接综合场馆后端,人工验证:
- 排课列表、详情、普通预约和支付使用同一
session.id。 - 商城同时展示次卡套餐和储蓄卡,储蓄卡明确显示售价与到账面值。
- 次卡支付后出现在我的次卡;“去预约”只展示当前已有次卡能约的排课。
- 次卡预约成功不进入支付页,后台核销/取消后刷新状态正确。
- 储蓄卡订单只能微信支付,成功后余额和充值流水刷新正确。
- 个人中心可进入充值、我的次卡、余额流水和消费记录。
- 两个场馆首页均无近期课程,课程 Tab 仍可正常进入。
- 包场、门票、订单详情、登录和场馆切换无回归。
最后提交:
git add src/pages/comprehensive/home/index.vue src/modules/comprehensive/api/content.js src/pages/ice/home/index.vue src/modules/ice/api/home.js scripts/verify-comprehensive-session-contract.mjs scripts/verify-home-venue-switch.mjs
git commit -m "feat: finalize comprehensive frontend integration"
完成定义
- 五个 Task 均有独立提交且验证通过。
npm run build:mp-weixin和npm run build:h5均成功。- 新增 API 没有旧
/api/courses或预约{ course_id }残留。 - 商城、持卡、可约排课、次卡预约、储蓄卡充值和个人中心形成完整可操作链路。
- 综合场馆与冰场馆首页均不展示近期课程。
- 工作区中用户原有的无关改动未被提交或覆盖。