diff --git a/src/user/ticket/index.tsx b/src/user/ticket/index.tsx index 6b854bb..4e6a374 100644 --- a/src/user/ticket/index.tsx +++ b/src/user/ticket/index.tsx @@ -37,6 +37,7 @@ const UserTicketList = () => { const [orderHasMore, setOrderHasMore] = useState(true); const [orderPage, setOrderPage] = useState(1); const [orderTotal, setOrderTotal] = useState(0); + const [orderCancelLoadingById, setOrderCancelLoadingById] = useState>({}); const [activeTab, setActiveTab] = useState<'ticket' | 'order'>(() => { const tab = Taro.getCurrentInstance().router?.params?.tab @@ -320,40 +321,85 @@ const UserTicketList = () => { return Number.isFinite(computed) ? computed : 0; }; + const getTicketUsedQty = (t?: Partial | null) => { + if (!t) return 0; + const anyT: any = t; + const raw = anyT.usedQty ?? anyT.usedNum ?? anyT.usedCount; + const n = Number(raw); + return Number.isFinite(n) ? n : 0; + }; + const rollbackUserTicketAfterOrderCancel = async (order: GltTicketOrder, before?: GltUserTicket | null) => { + const orderId = Number(order?.id); const ticketId = Number(order?.userTicketId); const qty = Math.max(0, Math.floor(Number((order as any)?.totalNum ?? 0))); + if (!Number.isFinite(orderId) || orderId <= 0) return; if (!Number.isFinite(ticketId) || ticketId <= 0) return; if (!Number.isFinite(qty) || qty <= 0) return; + const rollbackKey = `glt_ticket_order_rollback:${orderId}`; + if (Taro.getStorageSync(rollbackKey)) return; + const after = await getGltUserTicket(ticketId); if (!after?.id) return; const beforeAvail = before ? getTicketAvailableQty(before) : undefined; const afterAvail = getTicketAvailableQty(after); + const beforeUsed = before ? getTicketUsedQty(before) : undefined; + const afterUsed = getTicketUsedQty(after); - let need = qty; + let needAvail = qty; if (typeof beforeAvail === 'number') { const delta = afterAvail - beforeAvail; - if (delta >= qty) return; // backend already rolled back - if (delta > 0) need = Math.max(0, qty - delta); + if (delta >= qty) { + Taro.setStorageSync(rollbackKey, Date.now()); + return; // backend already rolled back + } + if (delta > 0) needAvail = Math.max(0, qty - delta); + } + let needUsed = qty; + if (typeof beforeUsed === 'number') { + const delta = beforeUsed - afterUsed; + if (delta >= qty) { + needUsed = 0; // backend already rolled back used qty + } else if (delta > 0) { + needUsed = Math.max(0, qty - delta); + } + } + + if (needAvail <= 0 && needUsed <= 0) { + Taro.setStorageSync(rollbackKey, Date.now()); + return; } - if (need <= 0) return; const currentAvailRaw = Number((after as any)?.availableQty); const baseAvail = Number.isFinite(currentAvailRaw) ? currentAvailRaw : afterAvail; - const nextAvail = (Number.isFinite(baseAvail) ? baseAvail : 0) + need; + const safeBaseAvail = Number.isFinite(baseAvail) ? baseAvail : 0; + const totalRaw = Number((after as any)?.totalQty ?? 0); + const total = Number.isFinite(totalRaw) ? totalRaw : undefined; const frozenRaw = Number((after as any)?.frozenQty ?? 0); const frozen = Number.isFinite(frozenRaw) ? frozenRaw : 0; - const reduceFrozen = Math.min(frozen, need); - const nextFrozen = reduceFrozen > 0 ? Math.max(0, frozen - reduceFrozen) : undefined; + + const currentUsedRaw = Number((after as any)?.usedQty); + const baseUsed = Number.isFinite(currentUsedRaw) ? currentUsedRaw : afterUsed; + const safeBaseUsed = Number.isFinite(baseUsed) ? baseUsed : 0; + let nextUsed = safeBaseUsed - needUsed; + if (nextUsed < 0) nextUsed = 0; + + const maxAvail = typeof total === 'number' ? Math.max(0, total - frozen - nextUsed) : undefined; + + let nextAvail = safeBaseAvail + needAvail; + if (typeof maxAvail === 'number' && Number.isFinite(maxAvail) && nextAvail > maxAvail) nextAvail = maxAvail; + if (nextAvail < 0) nextAvail = 0; await updateGltUserTicket({ ...after, availableQty: nextAvail, - ...(nextFrozen !== undefined ? { frozenQty: nextFrozen } : {}) + usedQty: nextUsed }); + + Taro.setStorageSync(rollbackKey, Date.now()); }; // Allow users to modify/cancel before delivery starts (e.g. 待派单 / 待配送). @@ -391,6 +437,7 @@ const UserTicketList = () => { Taro.showToast({ title: '仅配送未开始的订单可取消', icon: 'none' }); return; } + if (orderCancelLoadingById[order.id]) return; const modal = await Taro.showModal({ title: '取消订单', @@ -400,6 +447,7 @@ const UserTicketList = () => { if (!modal.confirm) return; try { + setOrderCancelLoadingById((prev) => ({ ...prev, [order.id as number]: true })); Taro.showLoading({ title: '取消中...' }); let beforeTicket: GltUserTicket | null = null; if (order.userTicketId) { @@ -427,6 +475,7 @@ const UserTicketList = () => { Taro.showToast({ title: '取消失败,请重试', icon: 'none' }); } finally { Taro.hideLoading(); + setOrderCancelLoadingById((prev) => ({ ...prev, [order.id as number]: false })); } }; @@ -703,7 +752,10 @@ const UserTicketList = () => {