/** * 维护模式检测服务 * * 功能: * 1. 检测后端服务是否可用 * 2. 连续失败达到阈值时判定为维护模式 * 3. 自动轮询检测服务恢复 */ class MaintenanceDetector { private failureCount = 0; private readonly FAILURE_THRESHOLD = 3; // 连续失败3次判定为维护 private readonly RESET_INTERVAL = 60000; // 60秒后重置计数 private resetTimer: NodeJS.Timeout | null = null; private recoveryCheckTimer: NodeJS.Timeout | null = null; /** * 记录请求失败 */ recordFailure() { this.failureCount++; console.log(`[MaintenanceDetector] 请求失败次数: ${this.failureCount}/${this.FAILURE_THRESHOLD}`); // 清除之前的重置计时器 if (this.resetTimer) { clearTimeout(this.resetTimer); } // 60秒后自动重置计数(避免误判网络抖动) this.resetTimer = setTimeout(() => { console.log('[MaintenanceDetector] 重置失败计数'); this.failureCount = 0; }, this.RESET_INTERVAL); // 达到阈值,判定为维护模式 if (this.failureCount >= this.FAILURE_THRESHOLD) { this.enterMaintenanceMode(); } } /** * 记录请求成功 */ recordSuccess() { if (this.failureCount > 0) { console.log('[MaintenanceDetector] 请求成功,重置失败计数'); this.failureCount = 0; } // 清除重置计时器 if (this.resetTimer) { clearTimeout(this.resetTimer); this.resetTimer = null; } } /** * 进入维护模式 */ private enterMaintenanceMode() { console.log('[MaintenanceDetector] ========== 进入维护模式 =========='); console.log('[MaintenanceDetector] 当前路径:', window.location.pathname); // 标记维护状态到 sessionStorage sessionStorage.setItem('maintenance_mode', 'true'); sessionStorage.setItem('maintenance_start_time', Date.now().toString()); console.log('[MaintenanceDetector] sessionStorage 已设置'); // 跳转到维护页面 if (window.location.pathname !== '/maintenance') { console.log('[MaintenanceDetector] 准备跳转到 /maintenance'); console.log('[MaintenanceDetector] 使用 window.location.replace 跳转...'); // 使用 setTimeout 确保跳转执行 setTimeout(() => { window.location.replace('/maintenance'); }, 100); } else { console.log('[MaintenanceDetector] 已在维护页面,无需跳转'); } // 开始轮询检测恢复 this.startRecoveryCheck(); } /** * 定期检测服务恢复 */ private startRecoveryCheck() { // 避免重复启动 if (this.recoveryCheckTimer) { return; } console.log('[MaintenanceDetector] 开始轮询检测服务恢复(每30秒)'); this.recoveryCheckTimer = setInterval(async () => { try { // 尝试请求 Spring Boot Actuator 健康检查端点(无需认证) // 使用原生fetch避免触发axios拦截器 const response = await fetch('/actuator/health', { method: 'GET', cache: 'no-cache', signal: AbortSignal.timeout(3000) }); // 只有返回 200-299 才算真正恢复 // 500+ 错误不算恢复 if (response.ok) { // 服务恢复 console.log('[MaintenanceDetector] 服务已恢复!'); this.exitMaintenanceMode(); } else { console.log(`[MaintenanceDetector] 服务仍不可用 (状态码: ${response.status})`); } } catch (error) { console.log('[MaintenanceDetector] 服务检测失败,继续等待...'); } }, 30000); // 每30秒检查一次 } /** * 退出维护模式 */ private exitMaintenanceMode() { console.log('[MaintenanceDetector] 退出维护模式'); // 清除维护状态 sessionStorage.removeItem('maintenance_mode'); sessionStorage.removeItem('maintenance_start_time'); // 停止轮询 if (this.recoveryCheckTimer) { clearInterval(this.recoveryCheckTimer); this.recoveryCheckTimer = null; } // 重置失败计数 this.failureCount = 0; // 刷新页面(返回应用) window.location.href = '/'; } /** * 手动检测恢复(供维护页面调用) */ async checkRecovery(): Promise { try { const response = await fetch('/actuator/health', { method: 'GET', cache: 'no-cache', signal: AbortSignal.timeout(3000) }); // 只有返回 200-299 才算真正恢复 if (response.ok) { this.exitMaintenanceMode(); return true; } return false; } catch { return false; } } /** * 获取维护开始时间 */ getMaintenanceStartTime(): number | null { const startTime = sessionStorage.getItem('maintenance_start_time'); return startTime ? parseInt(startTime, 10) : null; } /** * 检查是否在维护模式 */ isInMaintenanceMode(): boolean { return sessionStorage.getItem('maintenance_mode') === 'true'; } } // 导出单例 export const maintenanceDetector = new MaintenanceDetector();