const { request, ensureLogin, cents, clientRequestId, requestWechatPayment, } = require('../../utils/api.js') function defaultStartAt() { const date = new Date(Date.now() + 30 * 60 * 1000) date.setSeconds(0, 0) return date } function isoMinutesAfter(date, minutes) { return new Date(date.getTime() + minutes * 60 * 1000).toISOString() } Page({ data: { storeId: '', roomId: '', loading: false, errorMessage: '', successMessage: '', durationMinutes: 120, pricingMode: 'HOURLY', quote: null, order: null, payment: null, couponGrantId: '', packageHoldingId: '', packageMinutes: '', packageCreditYuan: '', benefitLoading: false, availableCoupons: [], availablePackages: [], selectedCouponName: '', selectedPackageName: '', }, onLoad(options) { this.setData({ storeId: options.storeId || '', roomId: options.roomId || '', }) this.loadBenefitOptions() }, async loadBenefitOptions() { this.setData({ benefitLoading: true }) try { await ensureLogin() const response = await request('/profile/benefits') this.setData({ availableCoupons: (response.data.coupons || []) .filter((item) => canUseCoupon(item, this.data.storeId, this.data.roomId)) .map(formatCouponOption), availablePackages: (response.data.packages || []) .filter((item) => canUsePackage(item, this.data.storeId, this.data.roomId)) .map(formatPackageOption), }) } catch (error) { this.setData({ errorMessage: error.message || '权益加载失败' }) } finally { this.setData({ benefitLoading: false }) } }, selectCoupon(event) { const couponGrantId = event.currentTarget.dataset.id || '' const current = this.data.couponGrantId === couponGrantId const selected = current ? null : this.data.availableCoupons.find((item) => item.couponGrantId === couponGrantId) this.setData({ couponGrantId: current ? '' : couponGrantId, selectedCouponName: selected ? selected.name : '', order: null, payment: null, successMessage: '', errorMessage: '', }) }, selectPackage(event) { const packageHoldingId = event.currentTarget.dataset.id || '' const current = this.data.packageHoldingId === packageHoldingId const selected = current ? null : this.data.availablePackages.find((item) => item.packageHoldingId === packageHoldingId) this.setData({ packageHoldingId: current ? '' : packageHoldingId, selectedPackageName: selected ? selected.name : '', packageMinutes: selected && selected.remainingMinutes > 0 ? String(Math.min(selected.remainingMinutes, this.data.durationMinutes)) : '', packageCreditYuan: selected && selected.remainingAmountCents > 0 ? yuanInput(Math.min( selected.remainingAmountCents, this.data.quote?.totalCents || selected.remainingAmountCents )) : '', order: null, payment: null, successMessage: '', errorMessage: '', }) }, updatePackageUsage(event) { const field = event.currentTarget.dataset.field if (!field) return this.setData({ [field]: event.detail.value, order: null, payment: null, successMessage: '', errorMessage: '', }) }, setDuration(event) { this.setData({ durationMinutes: Number(event.currentTarget.dataset.minutes), quote: null, order: null, payment: null, errorMessage: '', successMessage: '', }) }, async quote() { await this.withRequest(async () => { const payload = this.pricingPayload() const response = await request('/pricing/quote', { method: 'POST', data: payload }) this.setData({ quote: { ...response.data, totalText: cents(response.data.totalCents), depositText: cents(response.data.depositCents), }, successMessage: '报价已生成', }) }) }, async reserve() { await this.withRequest(async () => { const payload = this.pricingPayload() const response = await request('/orders/reserve', { method: 'POST', data: payload }) this.setData({ order: response.data, successMessage: response.data.benefitReservation ? '时段已预占,权益已冻结,请继续支付' : '时段已预占,请继续支付', }) }) }, async createTestPayment() { if (!this.data.order || !this.data.order.orderId) { this.setData({ errorMessage: '请先预占订单' }) return } function canUseCoupon(item, storeId, roomId) { return item.status === 'AVAILABLE' && Number(item.remainingUses || 0) > 0 && (!item.storeId || String(item.storeId) === String(storeId)) && (!item.roomId || String(item.roomId) === String(roomId)) } function canUsePackage(item, storeId, roomId) { return item.status === 'ACTIVE' && (Number(item.remainingMinutes || 0) > 0 || Number(item.remainingAmountCents || 0) > 0) && (!item.storeId || String(item.storeId) === String(storeId)) && (!item.roomId || String(item.roomId) === String(roomId)) } function formatCouponOption(item) { const isTime = item.couponType === 'TIME' return { ...item, valueText: isTime ? `${item.timeMinutes || 0}分钟` : cents(item.discountAmountCents), thresholdText: Number(item.minOrderAmountCents || 0) > 0 ? `满${cents(item.minOrderAmountCents)}可用` : '无门槛', scopeText: scopeText(item), } } function formatPackageOption(item) { return { ...item, remainingAmountText: cents(item.remainingAmountCents), scopeText: scopeText(item), } } function scopeText(item) { if (item.roomId) return `房间 ${item.roomId}` if (item.roomCategoryId) return `房型 ${item.roomCategoryId}` if (item.storeId) return `门店 ${item.storeId}` return item.holidayOnly ? '节假日' : '通用' } function yuanInput(centsValue) { return (Number(centsValue || 0) / 100).toFixed(2) } await this.withRequest(async () => { const response = await request('/payments', { method: 'POST', data: { orderId: this.data.order.orderId, provider: 'TEST', clientRequestId: clientRequestId('miniapp-pay'), }, }) this.setData({ payment: { ...response.data, amountText: cents(response.data.amountCents), }, successMessage: '测试支付单已创建', }) }) }, async createWechatPayment() { if (!this.data.order || !this.data.order.orderId) { this.setData({ errorMessage: '请先预占订单' }) return } await this.withRequest(async () => { const paymentResponse = await request('/payments', { method: 'POST', data: { orderId: this.data.order.orderId, provider: 'WECHAT', clientRequestId: clientRequestId('miniapp-wxpay'), }, }) const prepayResponse = await request('/pay/wechat/prepay', { method: 'POST', data: { paymentId: paymentResponse.data.paymentId }, }) await requestWechatPayment(prepayResponse.data) this.setData({ payment: { ...paymentResponse.data, amountText: cents(paymentResponse.data.amountCents), }, successMessage: '微信支付已提交,请稍后查看订单状态', }) }) }, async createBalancePayment() { if (!this.data.order || !this.data.order.orderId) { this.setData({ errorMessage: '请先预占订单' }) return } await this.withRequest(async () => { const response = await request('/payments', { method: 'POST', data: { orderId: this.data.order.orderId, provider: 'BALANCE', clientRequestId: clientRequestId('miniapp-balance'), }, }) this.setData({ payment: { ...response.data, amountText: cents(response.data.amountCents), }, successMessage: '余额支付已完成', }) }) }, async completeTestPayment() { if (!this.data.payment || !this.data.payment.paymentId) { this.setData({ errorMessage: '请先创建测试支付单' }) return } await this.withRequest(async () => { await request(`/payments/${encodeURIComponent(this.data.payment.paymentId)}/test-complete`, { method: 'POST', data: { callbackId: clientRequestId('miniapp-callback'), amountCents: this.data.payment.amountCents, }, }) this.setData({ successMessage: '测试支付已完成,生产环境将切换微信支付' }) }) }, openOrder(event) { const orderId = event.currentTarget.dataset.orderId if (!orderId) return wx.navigateTo({ url: `/pages/orders/detail?orderId=${encodeURIComponent(orderId)}` }) }, pricingPayload() { const start = defaultStartAt() return { roomId: this.data.roomId, startAt: start.toISOString(), endAt: isoMinutesAfter(start, this.data.durationMinutes), pricingMode: this.data.pricingMode, benefits: this.benefitsPayload(), } }, benefitsPayload() { const couponGrantId = String(this.data.couponGrantId || '').trim() const packageHoldingId = String(this.data.packageHoldingId || '').trim() if (!couponGrantId && !packageHoldingId) return undefined const packageMinutes = Number(this.data.packageMinutes || 0) const packageCreditCents = Math.round(Number(this.data.packageCreditYuan || 0) * 100) return { couponGrantId: couponGrantId || null, packageHoldingId: packageHoldingId || null, packageMinutes: packageMinutes > 0 ? packageMinutes : 0, packageCreditCents: packageCreditCents > 0 ? packageCreditCents : 0, clientRequestId: clientRequestId('miniapp-benefit'), } }, async withRequest(work) { this.setData({ loading: true, errorMessage: '', successMessage: '' }) try { await ensureLogin() await work() } catch (error) { this.setData({ errorMessage: error.message || '操作失败' }) } finally { this.setData({ loading: false }) } }, })