import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/api/api_client.dart'; import '../core/auth/auth_state.dart'; /// 登录态心跳:已登录时每 30s 打一次 POST /auth/ping。 /// 若会话已被撤销(被踢/管理员强制下线),后端返回 401, /// ApiClient 的拦截器会触发 refresh→失败→onAuthFailed→logout, /// 因此空闲用户也能在 ~30s 内感知到被下线。 final sessionHeartbeatProvider = Provider((ref) { final hb = SessionHeartbeat(ref); ref.onDispose(hb.dispose); hb.start(); return hb; }); class SessionHeartbeat { final Ref _ref; Timer? _timer; SessionHeartbeat(this._ref); void start() { _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 30), (_) => _ping()); } Future _ping() async { if (!_ref.read(authStateProvider).isLoggedIn) return; try { await _ref.read(apiClientProvider).post('/auth/ping'); } catch (e) { // 401 已由 ApiClient 拦截器处理(触发登出);其余错误忽略 debugPrint('[Heartbeat] ping failed: $e'); } } void dispose() => _timer?.cancel(); }