From f3f8aee9a0023d4eb555085b3975d4c451a8604a Mon Sep 17 00:00:00 2001 From: gqt <3217233537@qq.com> Date: Sun, 12 Jul 2026 20:31:07 +0800 Subject: [PATCH] feat: migrate comprehensive courses to sessions --- miniprogram/package.json | 1 + .../verify-comprehensive-session-contract.mjs | 34 +++++++++++ .../src/modules/comprehensive/api/booking.js | 36 +++++++----- .../src/modules/comprehensive/api/content.js | 4 +- .../src/modules/comprehensive/api/course.js | 57 ++++++++++--------- .../comprehensive/bookings/course-detail.vue | 8 +-- .../pages/comprehensive/bookings/index.vue | 6 +- .../pages/comprehensive/courses/confirm.vue | 24 ++++---- .../pages/comprehensive/courses/detail.vue | 26 ++++----- .../src/pages/comprehensive/courses/index.vue | 38 ++++++------- 10 files changed, 139 insertions(+), 95 deletions(-) create mode 100644 miniprogram/scripts/verify-comprehensive-session-contract.mjs diff --git a/miniprogram/package.json b/miniprogram/package.json index 744f6d0..5674bbb 100644 --- a/miniprogram/package.json +++ b/miniprogram/package.json @@ -12,6 +12,7 @@ "verify:startup": "node scripts/verify-venue-startup.mjs", "verify:home-switch": "node scripts/verify-home-venue-switch.mjs", "verify:comprehensive-auth": "node scripts/verify-comprehensive-auth-state.mjs", + "verify:comprehensive-session": "node scripts/verify-comprehensive-session-contract.mjs", "verify:auth-agreements": "node scripts/verify-auth-agreements.mjs" }, "dependencies": { diff --git a/miniprogram/scripts/verify-comprehensive-session-contract.mjs b/miniprogram/scripts/verify-comprehensive-session-contract.mjs new file mode 100644 index 0000000..4742895 --- /dev/null +++ b/miniprogram/scripts/verify-comprehensive-session-contract.mjs @@ -0,0 +1,34 @@ +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { dirname, resolve } from 'node:path' + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const srcRoot = resolve(root, 'src') +const failures = [] + +const expectIncludes = (content, expected, label) => { + if (!content.includes(expected)) failures.push(`${label} is missing: ${expected}`) +} + +const expectExcludes = (content, unexpected, label) => { + if (content.includes(unexpected)) failures.push(`${label} should not contain: ${unexpected}`) +} + +const courseApi = readFileSync(resolve(srcRoot, 'modules/comprehensive/api/course.js'), 'utf8') +const bookingApi = readFileSync(resolve(srcRoot, 'modules/comprehensive/api/booking.js'), 'utf8') + +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') + +if (failures.length) { + console.error(`Comprehensive session contract verification failed with ${failures.length} issue(s):`) + for (const failure of failures) console.error(`- ${failure}`) + process.exit(1) +} + +console.log('Comprehensive session contract verification passed.') diff --git a/miniprogram/src/modules/comprehensive/api/booking.js b/miniprogram/src/modules/comprehensive/api/booking.js index b0b105e..6a150b9 100644 --- a/miniprogram/src/modules/comprehensive/api/booking.js +++ b/miniprogram/src/modules/comprehensive/api/booking.js @@ -2,18 +2,26 @@ import { request } from './request' import { toArray, toNumber, toPageQuery, toPagination } from './mappers' import { toOrderBrief } from './order' -const toBookingCourse = (backendCourse = {}) => ({ - id: backendCourse.id ?? null, - title: backendCourse.title || '', - cover_image: backendCourse.cover_image || '', - coach_name: backendCourse.coach_name || '', - start_at: backendCourse.start_at || '', - end_at: backendCourse.end_at || '', - capacity: toNumber(backendCourse.capacity), - booked_count: toNumber(backendCourse.booked_count), - remaining_count: toNumber(backendCourse.remaining_count), - price: backendCourse.price || '0.00', - courts: toArray(backendCourse.courts) +const toBookingCourt = (backendCourt = {}) => ({ + id: backendCourt.id ?? null, + name: backendCourt.name || '', + court_type: backendCourt.court_type || '', + court_type_label: backendCourt.court_type_label || '' +}) + +const toBookingSession = (backendSession = {}) => ({ + id: backendSession.id ?? null, + course_id: backendSession.course_id ?? null, + title: backendSession.title || '', + cover_image: backendSession.cover_image || '', + 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', + courts: toArray(backendSession.courts).map(toBookingCourt) }) const toPrivateCourt = (backendCourt = {}) => ({ @@ -25,7 +33,7 @@ const toPrivateCourt = (backendCourt = {}) => ({ const toCourseBooking = (backendBooking = {}) => ({ id: backendBooking.id ?? null, - course_id: backendBooking.course_id ?? null, + session_id: backendBooking.session_id ?? null, order_id: backendBooking.order_id ?? null, status: backendBooking.status || '', status_label: backendBooking.status_label || backendBooking.status || '', @@ -37,7 +45,7 @@ const toCourseBooking = (backendBooking = {}) => ({ can_cancel: backendBooking.can_cancel === true, cancel_deadline_at: backendBooking.cancel_deadline_at || null, created_at: backendBooking.created_at || '', - course: toBookingCourse(backendBooking.course), + session: toBookingSession(backendBooking.session), order: toOrderBrief(backendBooking.order) }) diff --git a/miniprogram/src/modules/comprehensive/api/content.js b/miniprogram/src/modules/comprehensive/api/content.js index fa367c6..7862ede 100644 --- a/miniprogram/src/modules/comprehensive/api/content.js +++ b/miniprogram/src/modules/comprehensive/api/content.js @@ -1,6 +1,6 @@ import { request } from './request' import { compactParams, toArray, toNumber, toPageQuery, toPagination } from './mappers' -import { toCourse } from './course' +import { toSession } from './course' const toBanner = (backendBanner = {}) => ({ id: backendBanner.id ?? null, @@ -22,7 +22,7 @@ const toArticle = (backendArticle = {}) => ({ const toHomeContent = (backendData = {}) => ({ banners: toArray(backendData.banners).map(toBanner), articles: toArray(backendData.articles).map(toArticle), - upcoming_courses: toArray(backendData.upcoming_courses).map(toCourse), + upcoming_courses: toArray(backendData.upcoming_courses).map(toSession), venue: { name: backendData.venue?.name || '' } diff --git a/miniprogram/src/modules/comprehensive/api/course.js b/miniprogram/src/modules/comprehensive/api/course.js index 738f073..70bf863 100644 --- a/miniprogram/src/modules/comprehensive/api/course.js +++ b/miniprogram/src/modules/comprehensive/api/course.js @@ -8,27 +8,28 @@ const toCourseCourt = (backendCourt = {}) => ({ court_type_label: backendCourt.court_type_label || '' }) -export const toCourse = (backendCourse = {}) => ({ - id: backendCourse.id ?? null, - title: backendCourse.title || '', - cover_image: backendCourse.cover_image || '', - coach_id: backendCourse.coach_id ?? null, - coach_name: backendCourse.coach_name || '', - start_at: backendCourse.start_at || '', - end_at: backendCourse.end_at || '', - capacity: toNumber(backendCourse.capacity), - booked_count: toNumber(backendCourse.booked_count), - remaining_count: toNumber(backendCourse.remaining_count), - price: backendCourse.price || '0.00', - status: backendCourse.status || '', - status_label: backendCourse.status_label || backendCourse.status || '', - courts: toArray(backendCourse.courts).map(toCourseCourt), - detail_html: backendCourse.detail_html || '', - created_at: backendCourse.created_at || '', - updated_at: backendCourse.updated_at || '' +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 || '' }) -const toCourseQuery = (params = {}) => compactParams({ +const toSessionQuery = (params = {}) => compactParams({ ...toPageQuery(params), date: params.date, coach_id: params.coachId || params.coach_id, @@ -36,22 +37,22 @@ const toCourseQuery = (params = {}) => compactParams({ }) export const courseApi = { - getCourses(params = {}) { + getSessions(params = {}) { return request({ - url: '/api/courses', - data: toCourseQuery(params) - }).then((data) => toPagination(data, toCourse)) + url: '/api/sessions', + data: toSessionQuery(params) + }).then((data) => toPagination(data, toSession)) }, - getCourseDetail(id) { + getSessionDetail(id) { return request({ - url: `/api/courses/${id}` - }).then(toCourse) + url: `/api/sessions/${id}` + }).then(toSession) }, - createCourseBooking(courseId) { + createCourseBooking(sessionId) { return request({ url: '/api/bookings/course', method: 'POST', - data: { course_id: courseId } + data: { session_id: sessionId } }) } } diff --git a/miniprogram/src/pages/comprehensive/bookings/course-detail.vue b/miniprogram/src/pages/comprehensive/bookings/course-detail.vue index b2ba88a..7b7ef7a 100644 --- a/miniprogram/src/pages/comprehensive/bookings/course-detail.vue +++ b/miniprogram/src/pages/comprehensive/bookings/course-detail.vue @@ -6,11 +6,11 @@ - + - {{ booking.course.title }} - {{ formatTimeRange(booking.course.start_at, booking.course.end_at) }} - {{ booking.course.coach_name }} · {{ booking.course.courts.map(item => item.name).join('、') }} + {{ booking.session.title }} + {{ formatTimeRange(booking.session.start_at, booking.session.end_at) }} + {{ booking.session.coach_name }} · {{ booking.session.courts.map(item => item.name).join('、') }} diff --git a/miniprogram/src/pages/comprehensive/bookings/index.vue b/miniprogram/src/pages/comprehensive/bookings/index.vue index 83e2ddc..df50cc7 100644 --- a/miniprogram/src/pages/comprehensive/bookings/index.vue +++ b/miniprogram/src/pages/comprehensive/bookings/index.vue @@ -99,19 +99,19 @@ const loadItems = async () => { } const getItemTitle = (item) => { - if (businessType.value === 'course') return item.course?.title || '课程预约' + if (businessType.value === 'course') return item.session?.title || '课程预约' if (businessType.value === 'private') return item.courts?.map((court) => court.name).join('、') || '包场预约' return `篮球门票 ${item.order_no}` } const getItemTime = (item) => { - if (businessType.value === 'course') return formatTimeRange(item.course?.start_at, item.course?.end_at) + if (businessType.value === 'course') return formatTimeRange(item.session?.start_at, item.session?.end_at) if (businessType.value === 'private') return formatTimeRange(item.start_at, item.end_at) return item.checked_in_at ? `核销时间 ${formatDateTime(item.checked_in_at)}` : `生成时间 ${formatDateTime(item.created_at)}` } const getItemDesc = (item) => { - if (businessType.value === 'course') return `${item.course?.coach_name || ''} · ${item.course?.courts?.map((court) => court.name).join('、') || ''}` + if (businessType.value === 'course') return `${item.session?.coach_name || ''} · ${item.session?.courts?.map((court) => court.name).join('、') || ''}` if (businessType.value === 'private') return `共 ${item.courts?.length || 0} 个场地` return item.status === 'available' ? '可到馆核销使用' : item.status_label } diff --git a/miniprogram/src/pages/comprehensive/courses/confirm.vue b/miniprogram/src/pages/comprehensive/courses/confirm.vue index 3d692a4..866ac65 100644 --- a/miniprogram/src/pages/comprehensive/courses/confirm.vue +++ b/miniprogram/src/pages/comprehensive/courses/confirm.vue @@ -1,29 +1,29 @@