diff --git a/src/user/ticket/index.tsx b/src/user/ticket/index.tsx index e28b919..dc7d7b0 100644 --- a/src/user/ticket/index.tsx +++ b/src/user/ticket/index.tsx @@ -14,7 +14,7 @@ import { Tag } from '@nutui/nutui-react-taro'; import { View, Text, Image } from '@tarojs/components'; -import { pageGltUserTicket } from '@/api/glt/gltUserTicket'; +import { getGltUserTicket, pageGltUserTicket, updateGltUserTicket } from '@/api/glt/gltUserTicket'; import type { GltUserTicket } from '@/api/glt/gltUserTicket/model'; import { pageGltTicketOrder, removeGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder'; import type { GltTicketOrder } from '@/api/glt/gltTicketOrder/model'; @@ -261,6 +261,65 @@ const UserTicketList = () => { return d.isValid() ? d.format('YYYY年MM月DD日') : v; }; + const getTicketAvailableQty = (t?: Partial | null) => { + if (!t) return 0; + const anyT: any = t; + const raw = + anyT.availableQty ?? + anyT.availableNum ?? + anyT.availableCount ?? + anyT.remainQty ?? + anyT.remainNum ?? + anyT.remainCount; + const n = Number(raw); + if (Number.isFinite(n)) return n; + + const total = Number(anyT.totalQty ?? anyT.totalNum ?? anyT.totalCount ?? 0); + const used = Number(anyT.usedQty ?? anyT.usedNum ?? anyT.usedCount ?? 0); + const frozen = Number(anyT.frozenQty ?? anyT.frozenNum ?? anyT.frozenCount ?? 0); + const computed = + (Number.isFinite(total) ? total : 0) - + (Number.isFinite(used) ? used : 0) - + (Number.isFinite(frozen) ? frozen : 0); + return Number.isFinite(computed) ? computed : 0; + }; + + const rollbackUserTicketAfterOrderCancel = async (order: GltTicketOrder, before?: GltUserTicket | null) => { + const ticketId = Number(order?.userTicketId); + const qty = Math.max(0, Math.floor(Number((order as any)?.totalNum ?? 0))); + if (!Number.isFinite(ticketId) || ticketId <= 0) return; + if (!Number.isFinite(qty) || qty <= 0) return; + + const after = await getGltUserTicket(ticketId); + if (!after?.id) return; + + const beforeAvail = before ? getTicketAvailableQty(before) : undefined; + const afterAvail = getTicketAvailableQty(after); + + let need = 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 (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 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; + + await updateGltUserTicket({ + ...after, + availableQty: nextAvail, + ...(nextFrozen !== undefined ? { frozenQty: nextFrozen } : {}) + }); + }; + // Allow users to modify/cancel before delivery starts (e.g. 待派单 / 待配送). const isTicketOrderPendingDelivery = (order: GltTicketOrder) => { if (!order?.id) return false; @@ -306,12 +365,26 @@ const UserTicketList = () => { try { Taro.showLoading({ title: '取消中...' }); + let beforeTicket: GltUserTicket | null = null; + if (order.userTicketId) { + beforeTicket = await getGltUserTicket(Number(order.userTicketId)).catch(() => null); + } try { await updateGltTicketOrder({ id: order.id, deleted: 1 }); } catch (e) { await removeGltTicketOrder(order.id); } - Taro.showToast({ title: '订单已取消', icon: 'success' }); + try { + await rollbackUserTicketAfterOrderCancel(order, beforeTicket); + Taro.showToast({ title: '订单已取消,水票已退回', icon: 'success' }); + } catch (e) { + console.error('取消订单后退回水票失败:', e); + await Taro.showModal({ + title: '取消成功', + content: '订单已取消,但水票退回失败,请稍后刷新“我的水票”确认,或联系客服处理。', + showCancel: false + }); + } await reloadOrders(true); } catch (e) { console.error('取消送水订单失败:', e);