优化配置,修复 demo bug

1,network 框架完善
2,websocket 机制完善
3,设计文档整理到架构文档
4,脚本,配置完善
This commit is contained in:
Cody
2026-03-07 14:58:10 +08:00
parent f8a118af73
commit 0ee2c8c63c
82 changed files with 2704 additions and 1045 deletions

View File

@@ -6,6 +6,8 @@ import 'package:networks_sdk/networks_sdk.dart';
import '../../core/foundation/api_paths.dart';
import '../../core/foundation/config.dart';
import '../../core/foundation/constants.dart';
import '../../core/foundation/errors.dart';
import '../../core/foundation/utils.dart';
import '../../core/services/network_monitor.dart';
import '../../core/services/socket_manager.dart';
@@ -47,6 +49,21 @@ final networkMonitorProvider = Provider<NetworkMonitor>((ref) {
return monitor;
});
// ── Token 更新事件流 ─────────────────────────────────────────────────────────
/// Token 更新事件流
///
/// apiConfigProvider.onTokenUpdated → 推送新 token 到此流
/// socketManagerProvider → 监听此流 → 同步 token 到 WebSocket
/// onBeforeReconnect 中刷新 token 后调用 apiConfig.updateToken → tokenStream.add
/// 需要同步传播到 socketManager.updateToken → socketClient._currentToken
/// 确保随后的 _doConnect() 使用新 token。异步模式下 _doConnect 会在 stream
final _tokenUpdateStreamProvider = Provider<StreamController<String>>((ref) {
final controller = StreamController<String>.broadcast(sync: true);
ref.onDispose(controller.close);
return controller;
});
// ── HTTP 基础设施 ─────────────────────────────────────────────────────────────
/// API 配置 Provider全局单例
@@ -58,15 +75,18 @@ final networkMonitorProvider = Provider<NetworkMonitor>((ref) {
/// 请求前先判断网络状态,无网络时直接抛 [ApiError.noNetworkConnection]。
final apiConfigProvider = Provider<ApiConfig>((ref) {
final networkMonitor = ref.read(networkMonitorProvider);
final tokenStream = ref.read(_tokenUpdateStreamProvider);
return ApiConfig(
baseURL: AppConfig.apiBaseUrl,
platformHeaders: {
'Platform': 'Android', // TODO: 运行时从平台 API 获取
'Platform': 'Android', // TODO: 运行时从 platform API 获取
'client-version': '1.0.0', // TODO: 运行时从 package_info 获取
'Channel': '', // TODO: 从 AppConfig 读取渠道标识
'lang': 'zh-CN', // TODO: 从 l10n_sdk 或系统 locale 动态获取
},
tokenExpiredCodes: {30002, 30003, 30124},
forceLogoutCodes: {30125},
tokenExpiredCodes: ApiErrorCodes.tokenExpiredCodes,
forceLogoutCodes: ApiErrorCodes.forceLogoutCodes,
onForceLogout: () {
// TODO: 清除登录态,跳转登录页
},
@@ -74,7 +94,17 @@ final apiConfigProvider = Provider<ApiConfig>((ref) {
// TODO: App 层刷新 token 逻辑
return null;
},
onTokenUpdated: (newToken) {
// 通过事件流同步到 WebSocket避免直接引用 socketManagerProvider 造成循环依赖
tokenStream.add(newToken);
},
onCheckNetworkAvailable: () async => networkMonitor.isConnected,
onEncryptRequest: null, // TODO: 接入 cipher_guard_sdk 后注入请求加密回调
onDecryptResponse: null, // TODO: 接入 cipher_guard_sdk 后注入响应解密回调
onBusinessError: null, // TODO: 接入业务错误统一处理(弹窗 / Toast / 跳转等)
onTransformResponse:
null, // TODO: 如后端信封结构非标准,在此归一化为 { code, data, message }
onGetTokenExpiry: parseJwtExpiry,
maxRetries: AppConstants.maxRetries,
retryBaseDelay: AppConstants.retryBaseDelay,
onLog: (message, {tag}) {
@@ -94,16 +124,47 @@ final networkSdkApiProvider = Provider<NetworksSdkApi>((ref) {
// ── WebSocket 基础设施 ────────────────────────────────────────────────────────
/// SocketConfig Provider全局单例
/// SocketConfig Provider内部使用,不对外暴露
///
/// 与 apiConfigProvider 对称,通过回调注入 App 层能力,
/// SDK 内部不调用其他 SDK。
final socketConfigProvider = Provider<SocketConfig>((ref) {
final _socketConfigProvider = Provider<SocketConfig>((ref) {
final networkMonitor = ref.read(networkMonitorProvider);
return SocketConfig(
maxReconnectAttempts: AppConstants.maxRetries,
maxReconnectDelay: AppConstants.maxReconnectDelay,
unlimitedReconnect: true, // IM 场景始终保持连接
onBuildConnectUrl:
null, // TODO: 接入 cipher_guard_sdk 后注入 WS URL 加密(路径/token/cipher 参数)
onEncryptMessage: null, // TODO: 接入 cipher_guard_sdk 后注入消息加密回调
onDecryptMessage: null, // TODO: 接入 cipher_guard_sdk 后注入消息解密回调
onBeforeReconnect: () async {
// SocketClient 内部重连心跳超时、stream onDone前调用。
// 与 SocketManager.onBeforeReconnect 职责相同:检查 token 并按需刷新。
// 刷新后通过 sync stream 同步传播到 SocketClient._currentToken
// 确保随后的 _doConnect() 使用新 token。
final apiConfig = ref.read(apiConfigProvider);
final currentToken = apiConfig.token;
if (currentToken == null || apiConfig.onGetTokenExpiry == null) return;
final expiry = apiConfig.onGetTokenExpiry!(currentToken);
if (expiry == null) return;
final remaining = expiry.difference(DateTime.now());
if (remaining > apiConfig.proactiveRefreshThreshold) return;
// ignore: avoid_print
print(
'[Socket] Token expiring in ${remaining.inMinutes}min, refreshing before reconnect',
);
final newToken = await apiConfig.onTokenRefresh?.call();
if (newToken != null && newToken.isNotEmpty) {
// updateToken → onTokenUpdated → sync stream → manager.updateToken
// → _client.updateToken → socketClient._currentToken 同步更新
apiConfig.updateToken(newToken);
}
},
onLog: (message, {tag}) {
// ignore: avoid_print
print('[${tag ?? 'Socket'}] $message');
@@ -114,12 +175,11 @@ final socketConfigProvider = Provider<SocketConfig>((ref) {
);
});
/// SocketClient Provider全局单例
/// SocketClient Provider内部使用,不对外暴露
///
/// 与 apiClientProvider 对称。
final socketClientProvider = Provider<NetworksMessagingApi>((ref)
{
final config = ref.read(socketConfigProvider);
/// 与 networkSdkApiProvider 对称。
final _socketClientProvider = Provider<NetworksMessagingApi>((ref) {
final config = ref.read(_socketConfigProvider);
return NetworksMessagingApi()..initialize(config);
});
@@ -139,17 +199,43 @@ final socketClientProvider = Provider<NetworksMessagingApi>((ref)
/// 网络状态变化由 [networkMonitorProvider](公共服务)驱动,
/// 自动触发断连/重连。
///
/// Token 更新由 [_tokenUpdateStreamProvider] 事件流驱动,
/// HTTP 层刷新 token 后自动同步到 WebSocket。
///
/// onMessageTransform 参考 HTTP 层 onTokenRefresh 的回调模式:
/// 后续接入加解密 SDK 时,在此注入解密回调,
/// SDK 内部不调用其他 SDK。
final socketManagerProvider = Provider<SocketManager>((ref) {
final client = ref.read(socketClientProvider);
final client = ref.read(_socketClientProvider);
final networkMonitor = ref.read(networkMonitorProvider);
final apiConfig = ref.read(apiConfigProvider);
final tokenStream = ref.read(_tokenUpdateStreamProvider);
final manager = SocketManager(
client: client,
wsUrl: _buildWsUrl(AppConfig.apiBaseUrl),
onMessageTransform: null, // TODO: 接入加解密 SDK 后注入解密回调
onBeforeReconnect: () async {
// 重连前检查 token 是否即将过期,是则主动刷新
final currentToken = apiConfig.token;
if (currentToken == null || apiConfig.onGetTokenExpiry == null) return;
final expiry = apiConfig.onGetTokenExpiry!(currentToken);
if (expiry == null) return;
final remaining = expiry.difference(DateTime.now());
if (remaining > apiConfig.proactiveRefreshThreshold) return;
// ignore: avoid_print
print(
'[SocketManager] Token expiring in ${remaining.inMinutes}min, refreshing before reconnect',
);
final newToken = await apiConfig.onTokenRefresh?.call();
if (newToken != null && newToken.isNotEmpty) {
// updateToken 触发 onTokenUpdated → tokenStream → socketManager.updateToken
apiConfig.updateToken(newToken);
}
},
onCheckNetworkAvailable: () async => networkMonitor.isConnected,
onLog: (message, {tag}) {
// ignore: avoid_print
@@ -157,13 +243,19 @@ final socketManagerProvider = Provider<SocketManager>((ref) {
},
);
// 监听 token 更新事件 → 同步到 WebSocket
final tokenSub = tokenStream.stream.listen((newToken) {
manager.updateToken(newToken);
});
// 监听网络状态变化 → 驱动 SocketManager 断连/重连
final subscription = networkMonitor.onStatusChanged.listen((isAvailable) {
final networkSub = networkMonitor.onStatusChanged.listen((isAvailable) {
manager.handleNetworkStatusChanged(isAvailable: isAvailable);
});
ref.onDispose(() {
subscription.cancel();
tokenSub.cancel();
networkSub.cancel();
unawaited(manager.dispose());
});
@@ -215,23 +307,55 @@ String _buildWsUrl(String httpBaseUrl) {
// Provider 链路:
//
// networkMonitorProvider公共服务HTTP + WS 共用)
// ├── apiConfigProvider → apiClientProvider ← HTTP 层
// └── socketConfigProvider → socketClientProvider ← WS 层
// ├── apiConfigProvider → networkSdkApiProvider ← HTTP 层
// └── _socketConfigProvider → _socketClientProvider ← WS 层(内部)
// → socketManagerProvider
//
// _tokenUpdateStreamProvider打破循环引用的中间层
// ← apiConfigProvider.onTokenUpdated 推送
// → socketManagerProvider 监听 → socketManager.updateToken()
//
// 网络事件驱动链路:
//
// connectivity_plus平台网络事件
// → NetworkMonitor.onStatusChangedtrue / false
// → SocketManager.handleNetworkStatusChanged()
// → 断网: disconnect()
// → 恢复: connect(token: lastToken)
// → 恢复: onBeforeReconnect → connect(token: lastToken)
//
// 前后台事件驱动链路:
//
// WidgetsBindingObserverApp 层 app.dart
// → SocketManager.onEnterBackground() → disconnect
// → SocketManager.onEnterForeground() → reconnect
// → SocketManager.onEnterBackground()
// disconnectInBackground=true → disconnect默认移动端省电
// disconnectInBackground=false → 完全保活,不断连不暂停心跳(桌面端)
// → SocketManager.onEnterForeground() → onBeforeReconnect → reconnect
//
// Token 刷新 → WebSocket 同步链路:
//
// RetryInterceptor 检测 token 过期
// → TokenRefreshManager.refreshIfNeeded()
// → apiConfig.updateToken(newToken)
// → onTokenUpdated(newToken)
// → _tokenUpdateStream.add(newToken)
// → socketManager.updateToken(newToken) // 不断连,下次重连自动用新 token
//
// 主动 token 刷新(重连前,两个层级):
//
// SocketManager 层(前台恢复 / 网络恢复触发):
// SocketManager.onBeforeReconnect()
// → 解析 JWT exp → 距过期 < 阈值
// → apiConfig.onTokenRefresh() → 刷新
// → apiConfig.updateToken(newToken)
// → sync stream → manager.updateToken → _lastToken 更新
// → _client.connect(token: _lastToken) 使用新 token
//
// SocketClient 层(心跳超时 / stream onDone 触发):
// SocketConfig.onBeforeReconnect()
// → 同上逻辑:检查 JWT exp → 刷新 → apiConfig.updateToken
// → sync stream → manager.updateToken → _client.updateToken
// → socketClient._currentToken 同步更新
// → _doConnect() 使用新 token
//
// Repository 直接注入 ApiClient通过回调注入其他 SDK 能力:
//
@@ -313,7 +437,7 @@ String _buildWsUrl(String httpBaseUrl) {
// final authRepositoryProvider = Provider((ref) {
// final apiConfig = ref.read(apiConfigProvider);
// return AuthRepositoryImpl(
// client: ref.read(apiClientProvider), // 直接注入
// client: ref.read(networkSdkApiProvider), // 注入 Facade 接口
// onTokenUpdate: (token) {
// apiConfig.updateToken(token); // 内存network_sdk
// // secureStorage.saveToken(token); // 持久化crypto_sdk