新增注单错误处理逻辑,支持已退款及待确认状态的异常场景处理。 更新 HallBetResultDialog:针对部分失败与已退款订单显示对应提示信息。 优化注单分组逻辑,新增订单状态处理,提升整体订单管理能力。 新增订单状态与用户通知相关多语言翻译,进一步提升用户体验。
183 lines
5.6 KiB
TypeScript
183 lines
5.6 KiB
TypeScript
"use client";
|
||
|
||
import { useCallback, useEffect, useRef } from "react";
|
||
|
||
import { getWalletBalance } from "@/api/wallet";
|
||
import { getActivePlayerCurrencyFromStore } from "@/lib/player-currency";
|
||
import { useNetworkConnectionStore } from "@/stores/network-connection-store";
|
||
|
||
const POLLING_INTERVAL_MS = 30_000; // 30秒轮询间隔
|
||
const LIMITED_POLLING_DURATION_MS = 2 * 60 * 1000; // 2分钟限时轮询
|
||
|
||
export type UseWalletPollingReturn = {
|
||
/** 开始钱包轮询(持续或限时) */
|
||
startWalletPolling: (options?: { limitedDuration?: boolean }) => void;
|
||
/** 停止钱包轮询 */
|
||
stopWalletPolling: () => void;
|
||
/** 立即刷新钱包余额 */
|
||
refreshWallet: () => Promise<void>;
|
||
/** 是否正在轮询中 */
|
||
isPolling: boolean;
|
||
};
|
||
|
||
/**
|
||
* 钱包余额轮询 Hook
|
||
* 用于:
|
||
* 1. WebSocket 降级模式下的余额同步
|
||
* 2. 下注后的限时轮询(2分钟)
|
||
* 3. 开奖结果后的限时轮询(2分钟)
|
||
*/
|
||
export function useWalletPolling(): UseWalletPollingReturn {
|
||
const store = useNetworkConnectionStore();
|
||
const {
|
||
walletPollingIntervalId,
|
||
walletPollingExpiryAt,
|
||
setWalletPollingIntervalId,
|
||
setWalletPollingExpiryAt,
|
||
clearWalletPolling,
|
||
} = store;
|
||
|
||
const intervalIdRef = useRef<number | null>(walletPollingIntervalId);
|
||
|
||
// 同步 ref 和 store 状态
|
||
useEffect(() => {
|
||
intervalIdRef.current = walletPollingIntervalId;
|
||
}, [walletPollingIntervalId]);
|
||
|
||
// 刷新钱包余额
|
||
const refreshWallet = useCallback(async () => {
|
||
try {
|
||
await getWalletBalance({ currency: getActivePlayerCurrencyFromStore() });
|
||
// 触发全局刷新事件,让所有监听组件更新
|
||
window.dispatchEvent(new Event("lottery-wallet-refresh"));
|
||
} catch {
|
||
// 静默处理错误,避免频繁报错
|
||
}
|
||
}, []);
|
||
|
||
// 开始钱包轮询
|
||
const startWalletPolling = useCallback(
|
||
(options?: { limitedDuration?: boolean }) => {
|
||
const { limitedDuration = false } = options ?? {};
|
||
|
||
// 先停止现有的轮询
|
||
if (intervalIdRef.current) {
|
||
window.clearInterval(intervalIdRef.current);
|
||
intervalIdRef.current = null;
|
||
}
|
||
|
||
// 立即执行一次刷新
|
||
void refreshWallet();
|
||
|
||
// 设置轮询间隔
|
||
const intervalId = window.setInterval(() => {
|
||
// 检查限时轮询是否过期
|
||
if (limitedDuration && walletPollingExpiryAt) {
|
||
if (Date.now() > walletPollingExpiryAt) {
|
||
// 过期,停止轮询
|
||
clearWalletPolling();
|
||
intervalIdRef.current = null;
|
||
return;
|
||
}
|
||
}
|
||
void refreshWallet();
|
||
}, POLLING_INTERVAL_MS);
|
||
|
||
intervalIdRef.current = intervalId;
|
||
setWalletPollingIntervalId(intervalId);
|
||
|
||
// 设置限时轮询的过期时间
|
||
if (limitedDuration) {
|
||
const expiryAt = Date.now() + LIMITED_POLLING_DURATION_MS;
|
||
setWalletPollingExpiryAt(expiryAt);
|
||
|
||
// 设置一个定时器在过期时清理
|
||
window.setTimeout(() => {
|
||
if (intervalIdRef.current === intervalId) {
|
||
clearWalletPolling();
|
||
intervalIdRef.current = null;
|
||
}
|
||
}, LIMITED_POLLING_DURATION_MS + 1000); // 稍微延迟确保interval先执行检查
|
||
}
|
||
},
|
||
[
|
||
walletPollingExpiryAt,
|
||
refreshWallet,
|
||
setWalletPollingIntervalId,
|
||
setWalletPollingExpiryAt,
|
||
clearWalletPolling,
|
||
],
|
||
);
|
||
|
||
// 停止钱包轮询
|
||
const stopWalletPolling = useCallback(() => {
|
||
if (intervalIdRef.current) {
|
||
window.clearInterval(intervalIdRef.current);
|
||
intervalIdRef.current = null;
|
||
}
|
||
clearWalletPolling();
|
||
}, [clearWalletPolling]);
|
||
|
||
// 组件卸载时清理
|
||
useEffect(() => {
|
||
return () => {
|
||
if (intervalIdRef.current) {
|
||
window.clearInterval(intervalIdRef.current);
|
||
}
|
||
};
|
||
}, []);
|
||
|
||
return {
|
||
startWalletPolling,
|
||
stopWalletPolling,
|
||
refreshWallet,
|
||
isPolling: walletPollingIntervalId !== null,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 全局钱包轮询触发器
|
||
* 用于在非 React 上下文(如事件监听)中触发钱包轮询
|
||
*/
|
||
export function triggerWalletPollingAfterBet(): void {
|
||
const store = useNetworkConnectionStore.getState();
|
||
|
||
// 如果是降级模式,立即刷新并启动限时轮询
|
||
if (store.mode === "polling" || store.mode === "offline") {
|
||
// 立即刷新一次
|
||
void getWalletBalance({ currency: getActivePlayerCurrencyFromStore() }).then(() => {
|
||
window.dispatchEvent(new Event("lottery-wallet-refresh"));
|
||
});
|
||
|
||
// 清除现有轮询
|
||
if (store.walletPollingIntervalId) {
|
||
window.clearInterval(store.walletPollingIntervalId);
|
||
}
|
||
|
||
// 启动限时轮询
|
||
const intervalId = window.setInterval(() => {
|
||
const live = useNetworkConnectionStore.getState();
|
||
if (live.walletPollingExpiryAt && Date.now() > live.walletPollingExpiryAt) {
|
||
window.clearInterval(intervalId);
|
||
live.setWalletPollingIntervalId(null);
|
||
return;
|
||
}
|
||
void getWalletBalance({ currency: getActivePlayerCurrencyFromStore() }).then(() => {
|
||
window.dispatchEvent(new Event("lottery-wallet-refresh"));
|
||
});
|
||
}, POLLING_INTERVAL_MS);
|
||
|
||
useNetworkConnectionStore.getState().setWalletPollingIntervalId(intervalId);
|
||
useNetworkConnectionStore.getState().setWalletPollingExpiryAt(Date.now() + LIMITED_POLLING_DURATION_MS);
|
||
|
||
// 2分钟后自动清理
|
||
window.setTimeout(() => {
|
||
window.clearInterval(intervalId);
|
||
store.setWalletPollingIntervalId(null);
|
||
}, LIMITED_POLLING_DURATION_MS + 1000);
|
||
} else {
|
||
// WebSocket 模式下,只触发一次刷新
|
||
window.dispatchEvent(new Event("lottery-wallet-refresh"));
|
||
}
|
||
}
|