feat(ticket): 添加水票可用数量计算和订单取消后水票回退功能

- 引入 getGltUserTicket 和 updateGltUserTicket API 接口
- 实现 getTicketAvailableQty 函数用于计算水票可用数量
- 添加 rollbackUserTicketAfterOrderCancel 函数处理订单取消后的水票回退逻辑
- 在订单取消时获取取消前的水票状态并进行数量对比
- 订单取消成功后自动回退相应的水票数量
- 添加水票回退失败时的错误提示和用户通知
- 更新取消订单的成功提示信息为"订单已取消,水票已退回"
This commit is contained in:
2026-03-10 11:36:59 +08:00
parent 80d4db4156
commit 0c9a03d656

View File

@@ -14,7 +14,7 @@ import {
Tag Tag
} from '@nutui/nutui-react-taro'; } from '@nutui/nutui-react-taro';
import { View, Text, Image } from '@tarojs/components'; 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 type { GltUserTicket } from '@/api/glt/gltUserTicket/model';
import { pageGltTicketOrder, removeGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder'; import { pageGltTicketOrder, removeGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder';
import type { GltTicketOrder } from '@/api/glt/gltTicketOrder/model'; import type { GltTicketOrder } from '@/api/glt/gltTicketOrder/model';
@@ -261,6 +261,65 @@ const UserTicketList = () => {
return d.isValid() ? d.format('YYYY年MM月DD日') : v; return d.isValid() ? d.format('YYYY年MM月DD日') : v;
}; };
const getTicketAvailableQty = (t?: Partial<GltUserTicket> | 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. 待派单 / 待配送). // Allow users to modify/cancel before delivery starts (e.g. 待派单 / 待配送).
const isTicketOrderPendingDelivery = (order: GltTicketOrder) => { const isTicketOrderPendingDelivery = (order: GltTicketOrder) => {
if (!order?.id) return false; if (!order?.id) return false;
@@ -306,12 +365,26 @@ const UserTicketList = () => {
try { try {
Taro.showLoading({ title: '取消中...' }); Taro.showLoading({ title: '取消中...' });
let beforeTicket: GltUserTicket | null = null;
if (order.userTicketId) {
beforeTicket = await getGltUserTicket(Number(order.userTicketId)).catch(() => null);
}
try { try {
await updateGltTicketOrder({ id: order.id, deleted: 1 }); await updateGltTicketOrder({ id: order.id, deleted: 1 });
} catch (e) { } catch (e) {
await removeGltTicketOrder(order.id); 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); await reloadOrders(true);
} catch (e) { } catch (e) {
console.error('取消送水订单失败:', e); console.error('取消送水订单失败:', e);