Files
lotteryFront/src/hooks/use-wallet-polling.ts
kang ab81da3199 feat: 增强注单处理逻辑并优化用户反馈体验
新增注单错误处理逻辑,支持已退款及待确认状态的异常场景处理。
更新 HallBetResultDialog:针对部分失败与已退款订单显示对应提示信息。
优化注单分组逻辑,新增订单状态处理,提升整体订单管理能力。
新增订单状态与用户通知相关多语言翻译,进一步提升用户体验。
2026-05-26 16:32:53 +08:00

183 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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"));
}
}