优化配置,修复 demo bug
1,network 框架完善 2,websocket 机制完善 3,设计文档整理到架构文档 4,脚本,配置完善
This commit is contained in:
@@ -1,92 +1,75 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:networks_sdk/src/domain/entities/socket_connection_state.dart';
|
||||
import 'package:networks_sdk/src/domain/entities/socket_error.dart';
|
||||
import 'package:networks_sdk/src/presentation/wiring/networks_sdk_wiring.dart';
|
||||
import 'package:networks_sdk/src/presentation/wiring/socket_config.dart';
|
||||
|
||||
/// Messaging API for real-time communication
|
||||
/// 实时通信公开接口
|
||||
///
|
||||
/// This abstract class provides a technology-agnostic interface for
|
||||
/// real-time messaging. The actual implementation may use WebSocket
|
||||
/// or other transport mechanisms.
|
||||
/// 底层基于 WebSocket,支持 JSON / 字符串 / 二进制消息、
|
||||
/// 自动重连(含无限重连)、Token 热更新、消息加密/解密钩子。
|
||||
///
|
||||
/// ## Usage
|
||||
/// ## 使用方式
|
||||
///
|
||||
/// ```dart
|
||||
/// final messaging = NetworksMessagingApi();
|
||||
/// await messaging.initialize(SocketConfig(...));
|
||||
///
|
||||
/// // Connect to messaging server
|
||||
/// await messaging.connect('wss://api.example.com/ws', token: 'xxx');
|
||||
///
|
||||
/// // Listen for messages
|
||||
/// messaging.messageStream.listen((msg) => print(msg));
|
||||
///
|
||||
/// // Send messages
|
||||
/// await messaging.send({'type': 'chat', 'data': {...}});
|
||||
///
|
||||
/// // Handle connection state
|
||||
/// messaging.connectionStateStream.listen((state) => ...);
|
||||
/// // Token 热更新(不断连)
|
||||
/// messaging.updateToken('new_token');
|
||||
///
|
||||
/// // Handle errors
|
||||
/// messaging.errorStream.listen((error) => ...);
|
||||
/// // 发送二进制
|
||||
/// await messaging.sendBytes(Uint8List.fromList([0x01, 0x02]));
|
||||
///
|
||||
/// // Lifecycle management
|
||||
/// messaging.onEnterForeground();
|
||||
/// messaging.onEnterBackground();
|
||||
///
|
||||
/// // Cleanup
|
||||
/// await messaging.disconnect();
|
||||
/// await messaging.dispose();
|
||||
/// ```
|
||||
abstract class NetworksMessagingApi
|
||||
{
|
||||
abstract class NetworksMessagingApi {
|
||||
factory NetworksMessagingApi() => NetworksSdkWiring.buildMessagingApi();
|
||||
|
||||
/// Initialize the messaging service with configuration
|
||||
void initialize(SocketConfig config);
|
||||
|
||||
/// Connect to the messaging server
|
||||
///
|
||||
/// [url] - WebSocket URL (e.g., 'wss://api.example.com/ws')
|
||||
/// [token] - Optional authentication token
|
||||
Future<bool> connect(String url, {String? token});
|
||||
|
||||
/// Disconnect from the messaging server
|
||||
///
|
||||
/// Manual disconnect does not trigger auto-reconnect
|
||||
Future<void> disconnect();
|
||||
|
||||
/// Check if currently connected
|
||||
bool get isConnected;
|
||||
|
||||
/// Current connection state
|
||||
SocketConnectionState get connectionState;
|
||||
|
||||
/// Send a JSON message
|
||||
/// Token 热更新(不断开连接)
|
||||
///
|
||||
/// 仅更新内部 token,下次重连自动使用新 token。
|
||||
void updateToken(String token);
|
||||
|
||||
Future<bool> send(Map<String, dynamic> message);
|
||||
|
||||
/// Send a raw string message
|
||||
Future<bool> sendString(String message);
|
||||
|
||||
/// Stream of incoming parsed JSON messages
|
||||
/// 发送二进制数据
|
||||
Future<bool> sendBytes(List<int> bytes);
|
||||
|
||||
Stream<Map<String, dynamic>> get messageStream;
|
||||
|
||||
/// Stream of raw string messages (including failed JSON parses)
|
||||
Stream<String> get rawMessageStream;
|
||||
|
||||
/// Stream of connection state changes
|
||||
/// 二进制消息流
|
||||
Stream<Uint8List> get binaryMessageStream;
|
||||
|
||||
Stream<SocketConnectionState> get connectionStateStream;
|
||||
|
||||
/// Stream of errors
|
||||
Stream<SocketError> get errorStream;
|
||||
|
||||
/// Called when app enters foreground
|
||||
void onEnterForeground();
|
||||
|
||||
/// Called when app enters background
|
||||
void onEnterBackground();
|
||||
|
||||
/// Dispose all resources
|
||||
Future<void> dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,69 @@
|
||||
|
||||
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:networks_sdk/src/data/dto/api_requestable.dart';
|
||||
import 'package:networks_sdk/src/presentation/wiring/api_config.dart';
|
||||
import 'package:networks_sdk/src/presentation/wiring/network_callbacks.dart';
|
||||
import 'package:networks_sdk/src/presentation/wiring/networks_sdk_wiring.dart';
|
||||
|
||||
|
||||
/// SDK API
|
||||
abstract class NetworksSdkApi
|
||||
{
|
||||
/// Networks SDK 公开接口
|
||||
///
|
||||
/// 提供两种请求入口:
|
||||
/// - [executeRequest] — 统一请求入口(标准 / Upload / 流式)
|
||||
/// - [executeDownload] — 带进度的文件下载(支持断点续传)
|
||||
///
|
||||
/// 流式请求(SSE)也走 [executeRequest],由业务 Request 类 override
|
||||
/// `decodeResponse` 处理 SSE 解析。SDK 根据 `requestType == stream`
|
||||
/// 自动切换响应类型。
|
||||
///
|
||||
/// 使用方式:
|
||||
/// ```dart
|
||||
/// final sdk = NetworksSdkApi();
|
||||
/// sdk.initialize(apiConfig);
|
||||
///
|
||||
/// // 标准请求
|
||||
/// final data = await sdk.executeRequest(LoginRequest(...));
|
||||
///
|
||||
/// // 流式请求(SSE)— 同一入口,Request 类 override decodeResponse
|
||||
/// final result = await sdk.executeRequest(VoiceTranscribeRequest(...));
|
||||
///
|
||||
/// // 文件下载
|
||||
/// await sdk.executeDownload(
|
||||
/// url: '/files/report.pdf',
|
||||
/// savePath: '/tmp/report.pdf',
|
||||
/// onProgress: (received, total) => print('$received / $total'),
|
||||
/// );
|
||||
/// ```
|
||||
abstract class NetworksSdkApi {
|
||||
factory NetworksSdkApi() => NetworksSdkWiring.build();
|
||||
|
||||
Future<String?> platformVersion();
|
||||
|
||||
void initialize(ApiConfig aApiConfig);
|
||||
void initialize(ApiConfig apiConfig);
|
||||
|
||||
Future<T?> executeRequest<T>(ApiRequestable<T> request);
|
||||
/// 执行 API 请求 — 统一入口
|
||||
///
|
||||
/// 支持标准请求、登录、上传、流式(SSE),由 `request.requestType` 控制。
|
||||
/// 流式请求由业务 Request 类 override `decodeResponse` 处理 SSE 解析。
|
||||
///
|
||||
/// [cancelToken] — 可选,用于取消正在进行的请求
|
||||
Future<T?> executeRequest<T>(
|
||||
ApiRequestable<T> request, {
|
||||
CancelToken? cancelToken,
|
||||
});
|
||||
|
||||
/// 下载文件到本地路径
|
||||
///
|
||||
/// [url] — 下载 URL(完整路径或相对路径)
|
||||
/// [savePath] — 本地保存路径
|
||||
/// [onProgress] — 下载进度回调
|
||||
/// [cancelToken] — 取消令牌
|
||||
/// [resume] — 是否断点续传
|
||||
/// [headers] — 额外请求头
|
||||
Future<void> executeDownload({
|
||||
required String url,
|
||||
required String savePath,
|
||||
OnDownloadProgress? onProgress,
|
||||
CancelToken? cancelToken,
|
||||
bool resume = false,
|
||||
Map<String, String>? headers,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'network_callbacks.dart';
|
||||
|
||||
/// API 配置
|
||||
@@ -13,12 +12,22 @@ class ApiConfig {
|
||||
/// 平台相关 headers(App 层注入:version、platform、channel 等)
|
||||
Map<String, String> platformHeaders;
|
||||
|
||||
// ── 认证回调 ──
|
||||
|
||||
/// Token 过期时的刷新回调
|
||||
final OnTokenRefresh? onTokenRefresh;
|
||||
|
||||
/// 需要强制登出时的回调
|
||||
final OnForceLogout? onForceLogout;
|
||||
|
||||
/// Token 更新后的通知回调
|
||||
///
|
||||
/// 在 [updateToken] 被调用且新 token 非空时触发。
|
||||
/// App 层用于同步 token 到 WebSocket 等其他模块。
|
||||
final void Function(String newToken)? onTokenUpdated;
|
||||
|
||||
// ── 基础回调 ──
|
||||
|
||||
/// 日志输出回调(不设置则不输出日志)
|
||||
final OnLog? onLog;
|
||||
|
||||
@@ -29,12 +38,39 @@ class ApiConfig {
|
||||
/// 返回 false 则直接抛 [ApiError.noNetworkConnection],不走网络。
|
||||
final OnCheckNetworkAvailable? onCheckNetworkAvailable;
|
||||
|
||||
// ── 加密回调(预留给 cipher_guard_sdk)──
|
||||
|
||||
/// 请求体加密回调,null 时不加密
|
||||
final OnEncryptRequest? onEncryptRequest;
|
||||
|
||||
/// 响应体解密回调,null 时不解密
|
||||
final OnDecryptResponse? onDecryptResponse;
|
||||
|
||||
// ── 业务错误回调 ──
|
||||
|
||||
/// 业务错误拦截回调
|
||||
///
|
||||
/// 在 token 过期 / 强制登出判断之后执行。
|
||||
/// 返回 true = App 层已处理,SDK 正常传递响应;
|
||||
/// 返回 false = 未处理,SDK 继续正常流程。
|
||||
final OnBusinessError? onBusinessError;
|
||||
|
||||
/// 响应变换回调
|
||||
///
|
||||
/// 在 `executeRequest` 解码前调用,App 层可以统一解包
|
||||
/// `{ code, data, message }` 结构。返回 null 表示不变换。
|
||||
final OnTransformResponse? onTransformResponse;
|
||||
|
||||
// ── 错误码集合 ──
|
||||
|
||||
/// App 层定义的 Token 过期错误码集合
|
||||
final Set<int> tokenExpiredCodes;
|
||||
|
||||
/// App 层定义的强制登出错误码集合
|
||||
final Set<int> forceLogoutCodes;
|
||||
|
||||
// ── 重试配置 ──
|
||||
|
||||
/// 瞬态错误最大重试次数(5xx / 超时 / 连接失败)
|
||||
///
|
||||
/// 0 = 不重试(默认),设为 3 启用重试。
|
||||
@@ -46,18 +82,50 @@ class ApiConfig {
|
||||
/// 实际延迟 = min(baseDelay * 2^attempt, 30s) + jitter
|
||||
final Duration retryBaseDelay;
|
||||
|
||||
// ── Token 刷新配置 ──
|
||||
|
||||
/// Token 刷新超时时间,防止 onTokenRefresh 卡住导致请求永远阻塞
|
||||
final Duration tokenRefreshTimeout;
|
||||
|
||||
/// Token 刷新时间窗口:刷新成功后此时间内再次收到过期码直接返回成功,
|
||||
/// 避免服务端同步延迟导致的误判
|
||||
final Duration tokenReuseWindow;
|
||||
|
||||
// ── 主动刷新配置 ──
|
||||
|
||||
/// Token 过期时间解析回调
|
||||
///
|
||||
/// App 层解析 JWT `exp` claim,用于主动刷新判断。
|
||||
/// 未注入时不启用主动刷新。
|
||||
final OnGetTokenExpiry? onGetTokenExpiry;
|
||||
|
||||
/// 主动刷新阈值:距过期不足此时间时提前刷新
|
||||
///
|
||||
/// 默认 1 小时。WebSocket 重连前、App 回前台时
|
||||
/// 自动检查并刷新即将过期的 token,避免带过期 token 发起请求。
|
||||
final Duration proactiveRefreshThreshold;
|
||||
|
||||
ApiConfig({
|
||||
required this.baseURL,
|
||||
this.token,
|
||||
this.platformHeaders = const {},
|
||||
this.onTokenRefresh,
|
||||
this.onForceLogout,
|
||||
this.onTokenUpdated,
|
||||
this.onLog,
|
||||
this.onCheckNetworkAvailable,
|
||||
this.onEncryptRequest,
|
||||
this.onDecryptResponse,
|
||||
this.onBusinessError,
|
||||
this.onTransformResponse,
|
||||
this.tokenExpiredCodes = const {},
|
||||
this.forceLogoutCodes = const {},
|
||||
this.maxRetries = 0,
|
||||
this.retryBaseDelay = const Duration(seconds: 1),
|
||||
this.tokenRefreshTimeout = const Duration(seconds: 10),
|
||||
this.tokenReuseWindow = const Duration(seconds: 3),
|
||||
this.onGetTokenExpiry,
|
||||
this.proactiveRefreshThreshold = const Duration(hours: 1),
|
||||
});
|
||||
|
||||
/// 构建默认 headers
|
||||
@@ -70,6 +138,8 @@ class ApiConfig {
|
||||
final headers = <String, String>{
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'Accept': 'application/json',
|
||||
'Keep-Alive': 'timeout=60',
|
||||
// Unix 时间戳(秒),整数值,非格式化日期字符串
|
||||
'Timestamp': '${DateTime.now().millisecondsSinceEpoch ~/ 1000}',
|
||||
'APP-Request-ID': _generateRequestId(),
|
||||
};
|
||||
@@ -91,8 +161,13 @@ class ApiConfig {
|
||||
}
|
||||
|
||||
/// 更新 token
|
||||
///
|
||||
/// 同时触发 [onTokenUpdated] 通知其他模块(如 WebSocket)同步 token。
|
||||
void updateToken(String? newToken) {
|
||||
token = newToken;
|
||||
if (newToken != null && newToken.isNotEmpty) {
|
||||
onTokenUpdated?.call(newToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新 base URL
|
||||
@@ -105,4 +180,4 @@ class ApiConfig {
|
||||
final now = DateTime.now().microsecondsSinceEpoch;
|
||||
return '$now${Object().hashCode}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,105 @@
|
||||
/// 网络层回调类型定义,由 App 层注入 SDK,避免 SDK 直接依赖外部实现。
|
||||
library;
|
||||
|
||||
import 'package:networks_sdk/src/domain/entities/encrypted_request.dart';
|
||||
|
||||
// ── 认证 ──
|
||||
|
||||
/// Token 刷新回调,返回新 token;返回 null 表示刷新失败
|
||||
typedef OnTokenRefresh = Future<String?> Function();
|
||||
|
||||
/// 強制登出回調
|
||||
/// 强制登出回调
|
||||
typedef OnForceLogout = void Function();
|
||||
|
||||
/// 日誌輸出回調
|
||||
// ── Token 生命周期 ──
|
||||
|
||||
/// 获取 token 过期时间
|
||||
///
|
||||
/// App 层解析 JWT 的 `exp` claim 返回过期时间。
|
||||
/// 返回 null 表示无法解析(非 JWT 或格式错误)。
|
||||
typedef OnGetTokenExpiry = DateTime? Function(String token);
|
||||
|
||||
// ── 基础 ──
|
||||
|
||||
/// 日志输出回调
|
||||
typedef OnLog = void Function(String message, {String? tag});
|
||||
|
||||
/// 網路可用性查詢(App 層注入,SDK 在請求前調用)
|
||||
typedef OnCheckNetworkAvailable = Future<bool> Function();
|
||||
/// 网络可用性查询(App 层注入,SDK 在请求前调用)
|
||||
typedef OnCheckNetworkAvailable = Future<bool> Function();
|
||||
|
||||
// ── 加密(预留给 cipher_guard_sdk)──
|
||||
|
||||
/// HTTP 请求加密回调
|
||||
///
|
||||
/// 接收原始路径、headers、请求体,返回 [EncryptedRequest]。
|
||||
/// 拦截器根据返回值中的非 null 字段覆盖原始请求。
|
||||
///
|
||||
/// 参数说明:
|
||||
/// - [path] — 原始请求路径(如 `/api/v1/auth/login`)
|
||||
/// - [headers] — 当前请求的全部 headers(含 token、platform headers 等)
|
||||
/// - [body] — 原始请求体(可能是 Map、String、null 等)
|
||||
///
|
||||
/// App 层实现示例(X25519 + AES-256-CBC 模式):
|
||||
/// - 加密 path → hex 编码 → 替换路径
|
||||
/// - 加密 body → base64 编码 → 替换请求体
|
||||
/// - 加密 token → 放入 X-Token header
|
||||
/// - Ed25519 签名 → 放入 X-Signature header
|
||||
/// - Content-Type → text/plain
|
||||
typedef OnEncryptRequest =
|
||||
Future<EncryptedRequest> Function(
|
||||
String path,
|
||||
Map<String, String> headers,
|
||||
Object? body,
|
||||
);
|
||||
|
||||
/// HTTP 响应解密回调
|
||||
///
|
||||
/// 输入是原始响应数据(加密后可能是 String、`List<int>`、或 Map),
|
||||
/// 返回解密后的 Map 供业务层使用。
|
||||
///
|
||||
/// [responseData] 的实际类型取决于服务端响应格式:
|
||||
/// - 加密模式下通常是 base64 字符串
|
||||
/// - 非加密模式下是 `Map<String, dynamic>`
|
||||
typedef OnDecryptResponse =
|
||||
Future<Map<String, dynamic>> Function(Object responseData);
|
||||
|
||||
// ── 业务错误 ──
|
||||
|
||||
/// 业务错误拦截回调
|
||||
///
|
||||
/// App 层统一处理特定错误码,返回 true = 已处理(SDK 不再抛错),
|
||||
/// 返回 false = 未处理(SDK 继续正常流程)。
|
||||
typedef OnBusinessError = bool Function(int code, String message, String path);
|
||||
|
||||
/// 响应变换回调
|
||||
///
|
||||
/// App 层自定义响应解包逻辑(如统一解包 `{ code, data, message }` 结构)。
|
||||
/// 返回 null 表示不变换,使用原始响应。
|
||||
typedef OnTransformResponse =
|
||||
Map<String, dynamic>? Function(Map<String, dynamic> raw);
|
||||
|
||||
// ── 下载 ──
|
||||
|
||||
/// 下载进度回调
|
||||
typedef OnDownloadProgress = void Function(int received, int total);
|
||||
|
||||
// ── WebSocket 加密(预留给 cipher_guard_sdk)──
|
||||
|
||||
/// WebSocket 连接 URL 构建回调
|
||||
///
|
||||
/// 建立连接前调用,接收原始 URL 和 token,返回最终的连接 URL 字符串。
|
||||
/// WS 握手本质是 HTTP GET 升级请求,只需控制 URL(路径 + 查询参数)。
|
||||
///
|
||||
/// App 层可在此(通过调用 cipher_guard_sdk):
|
||||
/// - 加密 URL 路径(如 `/ws` → `/hex(encrypt(ws))`)
|
||||
/// - 加密 token 参数(明文 token 不出现在 URL 中)
|
||||
/// - 添加加密模式协商参数(如 `cipher=true&type=mode3`)
|
||||
///
|
||||
/// null 时使用默认行为:在 URL 后追加 `?token=xxx`。
|
||||
typedef OnBuildConnectUrl = String Function(String url, String? token);
|
||||
|
||||
/// WebSocket 发送前加密回调
|
||||
typedef OnEncryptMessage = Future<String> Function(String plainText);
|
||||
|
||||
/// WebSocket 收到后解密回调
|
||||
typedef OnDecryptMessage = Future<String> Function(String cipherText);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:networks_sdk/src/data/repositories/networks_messaging_repository_impl.dart';
|
||||
import 'package:networks_sdk/src/domain/entities/socket_connection_state.dart';
|
||||
import 'package:networks_sdk/src/domain/entities/socket_error.dart';
|
||||
@@ -5,7 +7,7 @@ import 'package:networks_sdk/src/domain/repositories/networks_messaging_reposito
|
||||
import 'package:networks_sdk/src/presentation/facade/networks_messaging_api.dart';
|
||||
import 'package:networks_sdk/src/presentation/wiring/socket_config.dart';
|
||||
|
||||
/// Implementation of [NetworksMessagingApi] using [NetworksMessagingRepository]
|
||||
/// [NetworksMessagingApi] 的实现,透传给 [NetworksMessagingRepository]
|
||||
class NetworksMessagingApiImpl implements NetworksMessagingApi {
|
||||
NetworksMessagingRepository? _repository;
|
||||
|
||||
@@ -47,6 +49,12 @@ class NetworksMessagingApiImpl implements NetworksMessagingApi {
|
||||
return _repository!.connectionState;
|
||||
}
|
||||
|
||||
@override
|
||||
void updateToken(String token) {
|
||||
_checkInitialized();
|
||||
_repository!.updateToken(token);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> send(Map<String, dynamic> message) {
|
||||
_checkInitialized();
|
||||
@@ -59,6 +67,12 @@ class NetworksMessagingApiImpl implements NetworksMessagingApi {
|
||||
return _repository!.sendString(message);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> sendBytes(List<int> bytes) {
|
||||
_checkInitialized();
|
||||
return _repository!.sendBytes(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Map<String, dynamic>> get messageStream {
|
||||
_checkInitialized();
|
||||
@@ -71,6 +85,12 @@ class NetworksMessagingApiImpl implements NetworksMessagingApi {
|
||||
return _repository!.rawMessageStream;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Uint8List> get binaryMessageStream {
|
||||
_checkInitialized();
|
||||
return _repository!.binaryMessageStream;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<SocketConnectionState> get connectionStateStream {
|
||||
_checkInitialized();
|
||||
@@ -103,4 +123,3 @@ class NetworksMessagingApiImpl implements NetworksMessagingApi {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import '../../../networks_sdk.dart';
|
||||
import 'networks_sdk_core.dart';
|
||||
|
||||
/// SDK API Implementation
|
||||
/// [NetworksSdkApi] 的实现,透传给 Repository
|
||||
class NetworksSdkApiImpl implements NetworksSdkApi {
|
||||
final NetworksSdkCore _core;
|
||||
|
||||
@@ -14,6 +14,29 @@ class NetworksSdkApiImpl implements NetworksSdkApi {
|
||||
void initialize(ApiConfig apiConfig) => _core.repo.initialize(apiConfig);
|
||||
|
||||
@override
|
||||
Future<T?> executeRequest<T>(ApiRequestable<T> request) => _core.repo.executeRequest(request);
|
||||
Future<T?> executeRequest<T>(
|
||||
ApiRequestable<T> request, {
|
||||
CancelToken? cancelToken,
|
||||
}) {
|
||||
return _core.repo.executeRequest(request, cancelToken: cancelToken);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> executeDownload({
|
||||
required String url,
|
||||
required String savePath,
|
||||
OnDownloadProgress? onProgress,
|
||||
CancelToken? cancelToken,
|
||||
bool resume = false,
|
||||
Map<String, String>? headers,
|
||||
}) {
|
||||
return _core.repo.executeDownload(
|
||||
url: url,
|
||||
savePath: savePath,
|
||||
onProgress: onProgress,
|
||||
cancelToken: cancelToken,
|
||||
resume: resume,
|
||||
headers: headers,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'package:networks_sdk/src/presentation/wiring/network_callbacks.dart';
|
||||
|
||||
/// WebSocket 配置
|
||||
/// 非单例,由 App 层构造并注入到 SocketClient
|
||||
///
|
||||
/// 与 [ApiConfig] 设计一致:SDK 不依赖 Flutter,
|
||||
/// 网络检测、生命周期等业务逻辑通过回调注入。
|
||||
class SocketConfig {
|
||||
// ── 心跳 ──
|
||||
|
||||
/// 应用层心跳间隔(定时发送 "ping" 字符串)
|
||||
final Duration heartbeatInterval;
|
||||
|
||||
@@ -13,10 +17,19 @@ class SocketConfig {
|
||||
/// Pong 超时(超过此时间未收到 pong 则判定连接断开)
|
||||
final Duration pongTimeout;
|
||||
|
||||
// ── 连接 ──
|
||||
|
||||
/// 连接超时
|
||||
final Duration connectTimeout;
|
||||
|
||||
/// 是否启用 WebSocket 压缩(permessage-deflate)
|
||||
final bool enableCompression;
|
||||
|
||||
// ── 重连 ──
|
||||
|
||||
/// 最大重连次数(0 = 不重连)
|
||||
///
|
||||
/// 当 [unlimitedReconnect] 为 true 时此字段无效。
|
||||
final int maxReconnectAttempts;
|
||||
|
||||
/// 最大重连延迟(指数退避上限)
|
||||
@@ -25,22 +38,65 @@ class SocketConfig {
|
||||
/// 是否自动重连
|
||||
final bool autoReconnect;
|
||||
|
||||
/// 无限重连模式
|
||||
///
|
||||
/// IM 场景建议开启:连接断开后始终尝试重连,不受
|
||||
/// [maxReconnectAttempts] 限制。退避延迟仍受
|
||||
/// [maxReconnectDelay] 约束。
|
||||
final bool unlimitedReconnect;
|
||||
|
||||
// ── 回调 ──
|
||||
|
||||
/// 日志输出回调(与 ApiConfig.onLog 同签名)
|
||||
final void Function(String message, {String? tag})? onLog;
|
||||
final OnLog? onLog;
|
||||
|
||||
/// 网络可用性查询(App 层注入,SDK 在重连前调用)
|
||||
/// 返回 true 表示网络可用,可以尝试重连
|
||||
final Future<bool> Function()? onCheckNetworkAvailable;
|
||||
final OnCheckNetworkAvailable? onCheckNetworkAvailable;
|
||||
|
||||
/// 重连前回调
|
||||
///
|
||||
/// 每次自动重连前调用(心跳超时、连接断开等触发的内部重连)。
|
||||
/// App 层用于:
|
||||
/// - 检查并刷新即将过期的 token(通过 [SocketClient.updateToken])
|
||||
/// - 其他重连前准备工作
|
||||
///
|
||||
/// 回调完成后才发起实际连接。如果回调抛出异常,本次重连跳过,
|
||||
/// 等下一轮退避定时器触发。
|
||||
final Future<void> Function()? onBeforeReconnect;
|
||||
|
||||
// ── 加密回调(预留给 cipher_guard_sdk)──
|
||||
|
||||
/// 连接 URL 构建回调
|
||||
///
|
||||
/// 建立连接前调用,接收原始 URL 和 token,返回最终连接 URL 字符串。
|
||||
/// null 时使用默认行为(URL 后追加 `?token=xxx`)。
|
||||
///
|
||||
/// App 层注入 cipher_guard_sdk 的加密逻辑:路径/token 加密、
|
||||
/// 添加 `cipher=true` 参数等。
|
||||
final OnBuildConnectUrl? onBuildConnectUrl;
|
||||
|
||||
/// 发送前加密回调,null 时不加密
|
||||
final OnEncryptMessage? onEncryptMessage;
|
||||
|
||||
/// 收到后解密回调,null 时不解密
|
||||
final OnDecryptMessage? onDecryptMessage;
|
||||
|
||||
SocketConfig({
|
||||
this.heartbeatInterval = const Duration(seconds: 10),
|
||||
this.pingInterval = const Duration(seconds: 5),
|
||||
this.pongTimeout = const Duration(seconds: 10),
|
||||
this.connectTimeout = const Duration(seconds: 15),
|
||||
this.enableCompression = false,
|
||||
this.maxReconnectAttempts = 5,
|
||||
this.maxReconnectDelay = const Duration(seconds: 30),
|
||||
this.autoReconnect = true,
|
||||
this.unlimitedReconnect = false,
|
||||
this.onLog,
|
||||
this.onCheckNetworkAvailable,
|
||||
this.onBeforeReconnect,
|
||||
this.onBuildConnectUrl,
|
||||
this.onEncryptMessage,
|
||||
this.onDecryptMessage,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user