fix: harden ticket model edge cases
This commit is contained in:
@@ -15,8 +15,9 @@ const toMoney = (value) => Number(Number(value || 0).toFixed(2))
|
||||
|
||||
const formatTimestamp = (date) => {
|
||||
const pad = (value) => String(value).padStart(2, '0')
|
||||
const chinaDate = new Date(date.getTime() + 8 * 60 * 60 * 1000)
|
||||
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||
return `${chinaDate.getUTCFullYear()}-${pad(chinaDate.getUTCMonth() + 1)}-${pad(chinaDate.getUTCDate())} ${pad(chinaDate.getUTCHours())}:${pad(chinaDate.getUTCMinutes())}:${pad(chinaDate.getUTCSeconds())}`
|
||||
}
|
||||
|
||||
const createCredential = (prefix, orderNo) => ({
|
||||
@@ -91,7 +92,7 @@ export const createBookingDraft = (product) => ({
|
||||
export const validateBooking = ({ product, draft, travelers }) => {
|
||||
if (!draft?.visitDate) return '请选择使用日期'
|
||||
if (!draft.timeSlot) return '请选择使用场次'
|
||||
if (!Number.isInteger(draft.quantity) || draft.quantity < 1 || (draft.maxQuantity && draft.quantity > draft.maxQuantity)) return '购买数量无效'
|
||||
if (!Number.isInteger(draft.quantity) || draft.quantity < 1 || (draft.maxQuantity !== undefined && draft.maxQuantity !== null && draft.quantity > draft.maxQuantity)) return '购买数量无效'
|
||||
|
||||
const selectedTravelers = [...new Set(draft.travelerIds || [])]
|
||||
.map((id) => travelers.find((traveler) => traveler.id === id))
|
||||
@@ -121,7 +122,7 @@ export const createPaidOrder = ({ product, draft, travelers, now = new Date() })
|
||||
...entitlement,
|
||||
id: `${orderId}-${entitlement.id || 'entitlement'}-${index + 1}`,
|
||||
sourceId: entitlement.id,
|
||||
quantity: (Number(entitlement.quantity) || 1) * draft.quantity,
|
||||
quantity: Number(entitlement.quantity ?? 1) * draft.quantity,
|
||||
usedQuantity: 0,
|
||||
status: 'pending',
|
||||
}))
|
||||
@@ -165,6 +166,9 @@ export const applyRefund = (order, entitlementIds, reason, now = new Date()) =>
|
||||
const refundableIds = order.entitlements
|
||||
.filter((entitlement) => entitlement.status === 'pending' && selectedIds.has(entitlement.id))
|
||||
.map((entitlement) => entitlement.id)
|
||||
|
||||
if (!refundableIds.length) return { order, refund: null }
|
||||
|
||||
const amount = calculateRefundAmount(order, entitlementIds)
|
||||
const entitlements = order.entitlements.map((entitlement) => (
|
||||
entitlement.status === 'pending' && selectedIds.has(entitlement.id)
|
||||
|
||||
@@ -125,6 +125,7 @@ test('validates booking draft fields and selected travelers in order', () => {
|
||||
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, timeSlot: '' }, travelers: [eligibleTraveler] }), '请选择使用场次')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, quantity: 0 }, travelers: [eligibleTraveler] }), '购买数量无效')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, maxQuantity: 0 }, travelers: [eligibleTraveler] }), '购买数量无效')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, travelerIds: [] }, travelers: [eligibleTraveler] }), '请选择足够数量的出行人')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: { ...completeDraft, quantity: 2, travelerIds: ['t-1', 't-1'] }, travelers: [eligibleTraveler] }), '请选择足够数量的出行人')
|
||||
assert.equal(validateBooking({ product: adultProduct, draft: completeDraft, travelers: [incompleteTraveler] }), '请完善出行人实名信息')
|
||||
@@ -213,6 +214,15 @@ test('creates a paid order with entitlements and credentials', () => {
|
||||
{ id: 'shuttle', name: '观光车票', fulfillment: 'self', quantity: 1, unit: '张', refundAllocation: 40 },
|
||||
])
|
||||
assert.deepEqual(insuredOrder.insurance, { name: '出行意外险', price: 5, quantity: 2, status: 'active' })
|
||||
|
||||
const zeroQuantityOrder = createPaidOrder({
|
||||
product: { ...adultProduct, entitlements: [{ ...adultProduct.entitlements[0], quantity: 0 }] },
|
||||
draft: { ...createBookingDraft(adultProduct), quantity: 2 },
|
||||
travelers: [],
|
||||
now,
|
||||
})
|
||||
|
||||
assert.equal(zeroQuantityOrder.entitlements[0].quantity, 0)
|
||||
})
|
||||
|
||||
test('applies refunds and pops view history without mutation', () => {
|
||||
@@ -230,6 +240,7 @@ test('applies refunds and pops view history without mutation', () => {
|
||||
})
|
||||
const entitlementId = order.entitlements[0].id
|
||||
const history = ['home', 'detail', 'booking']
|
||||
const originalOrder = structuredClone(order)
|
||||
|
||||
assert.equal(calculateRefundAmount(order, [entitlementId]), 120)
|
||||
|
||||
@@ -237,10 +248,16 @@ test('applies refunds and pops view history without mutation', () => {
|
||||
assert.equal(result.order.status, 'refunding')
|
||||
assert.equal(result.refund.amount, 120)
|
||||
assert.equal(result.order.entitlements[0].status, 'refunding')
|
||||
assert.deepEqual(order, originalOrder)
|
||||
assert.notEqual(result.order.entitlements, order.entitlements)
|
||||
assert.notEqual(result.order.logs, order.logs)
|
||||
assert.deepEqual(result.order.logs.at(-1), { time: '2026-07-30 10:01:00', text: '已申请退款', operator: '用户' })
|
||||
const repeatedRefund = applyRefund(result.order, [entitlementId, 'missing-entitlement'], '重复申请')
|
||||
assert.equal(repeatedRefund.refund.amount, 0)
|
||||
assert.deepEqual(repeatedRefund.refund.entitlementIds, [])
|
||||
assert.equal(repeatedRefund.order, result.order)
|
||||
assert.equal(repeatedRefund.refund, null)
|
||||
const emptyRefund = applyRefund(order, [], '空申请')
|
||||
assert.equal(emptyRefund.order, order)
|
||||
assert.equal(emptyRefund.refund, null)
|
||||
assert.deepEqual(popViewHistory(history), { view: 'booking', history: ['home', 'detail'] })
|
||||
assert.deepEqual(history, ['home', 'detail', 'booking'])
|
||||
assert.deepEqual(popViewHistory([]), { view: 'home', history: [] })
|
||||
|
||||
Reference in New Issue
Block a user