feat: 集成错误处理与网络状态管理

- 在 Providers 组件中引入 ErrorProvider 以处理全局错误状态
- 更新 PlayerAppShell 组件的注释,说明网络状态横幅的用途
- 在 lotteryHttp 中添加对 500、502、503 错误的处理,更新全局错误状态
- 导出 useNetworkStatus 和 useIsOffline 钩子以支持网络状态管理
This commit is contained in:
2026-05-13 15:14:02 +08:00
parent 1e7a06dc86
commit c8f8f90515
12 changed files with 629 additions and 10 deletions

View File

@@ -12,6 +12,7 @@ import {
LotteryApiEnvelopeError,
} from "@/types/api/errors";
import { isApiEnvelope } from "@/types/api/envelope";
import { useErrorStore } from "@/stores/error-store";
const baseURL = process.env.NEXT_PUBLIC_LOTTERY_API_BASE_URL?.trim();
@@ -25,18 +26,45 @@ export const lotteryHttp = axios.create({
});
/** 站内接口 401清本地会话并回入口与 {@link EntryGate} `session=expired` 衔接 */
/** 500 错误:更新全局服务器错误状态 */
lotteryHttp.interceptors.response.use(
(response) => response,
(error: unknown) => {
if (
isAxiosError(error) &&
error.response?.status === 401 &&
typeof window !== "undefined"
) {
clearPersistedPlayerBearerToken();
setPlayerBearerToken(null);
if (window.location.pathname !== "/") {
window.location.replace("/?session=expired");
if (isAxiosError(error) && typeof window !== "undefined") {
const status = error.response?.status;
// 401: 会话过期,清除令牌并重定向
if (status === 401) {
clearPersistedPlayerBearerToken();
setPlayerBearerToken(null);
if (window.location.pathname !== "/") {
window.location.replace("/?session=expired");
}
}
// 500/502/503: 服务器错误,更新全局错误状态
if (status === 500 || status === 502 || status === 503) {
const setServerError = useErrorStore.getState().setServerError;
let message = "服务器暂时不可用,请稍后重试";
// 尝试从响应中获取更详细的错误信息
const responseData = error.response?.data;
if (
typeof responseData === "object" &&
responseData !== null &&
"msg" in responseData &&
typeof responseData.msg === "string"
) {
message = responseData.msg;
}
setServerError(true, message);
}
// 网络错误 (无响应): 检查网络状态
if (!error.response && error.message?.includes("Network Error")) {
const setIsOffline = useErrorStore.getState().setIsOffline;
setIsOffline(true);
}
}
return Promise.reject(error);