feat(ticket): 添加水票可用数量计算和订单取消后水票回退功能
- 引入 getGltUserTicket 和 updateGltUserTicket API 接口 - 实现 getTicketAvailableQty 函数用于计算水票可用数量 - 添加 rollbackUserTicketAfterOrderCancel 函数处理订单取消后的水票回退逻辑 - 在订单取消时获取取消前的水票状态并进行数量对比 - 订单取消成功后自动回退相应的水票数量 - 添加水票回退失败时的错误提示和用户通知 - 更新取消订单的成功提示信息为"订单已取消,水票已退回"
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user