Initial project

This commit is contained in:
Cody
2026-03-06 14:56:17 +08:00
parent 977b627b15
commit bf9e099747
1180 changed files with 50973 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
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.
///
/// ## 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) => ...);
///
/// // Handle errors
/// messaging.errorStream.listen((error) => ...);
///
/// // Lifecycle management
/// messaging.onEnterForeground();
/// messaging.onEnterBackground();
///
/// // Cleanup
/// await messaging.disconnect();
/// await messaging.dispose();
/// ```
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
Future<bool> send(Map<String, dynamic> message);
/// Send a raw string message
Future<bool> sendString(String message);
/// Stream of incoming parsed JSON messages
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<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();
}

View File

@@ -0,0 +1,19 @@
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/networks_sdk_wiring.dart';
/// SDK API
abstract class NetworksSdkApi
{
factory NetworksSdkApi() => NetworksSdkWiring.build();
Future<String?> platformVersion();
void initialize(ApiConfig aApiConfig);
Future<T?> executeRequest<T>(ApiRequestable<T> request);
}

View File

@@ -0,0 +1,108 @@
import 'network_callbacks.dart';
/// API 配置
/// 非单例,由 App 层构造并注入到 ApiClient
class ApiConfig {
/// 基础 URL来自 config.json → AppConfig.apiBaseUrl
String baseURL;
/// 当前 token内存持有App 层负责持久化)
String? token;
/// 平台相关 headersApp 层注入version、platform、channel 等)
Map<String, String> platformHeaders;
/// Token 过期时的刷新回调
final OnTokenRefresh? onTokenRefresh;
/// 需要强制登出时的回调
final OnForceLogout? onForceLogout;
/// 日志输出回调(不设置则不输出日志)
final OnLog? onLog;
/// 网络可用性查询App 层注入,请求前调用)
///
/// 与 [SocketConfig.onCheckNetworkAvailable] 对称。
/// 返回 true 表示网络可用,可以发起请求;
/// 返回 false 则直接抛 [ApiError.noNetworkConnection],不走网络。
final OnCheckNetworkAvailable? onCheckNetworkAvailable;
/// App 层定义的 Token 过期错误码集合
final Set<int> tokenExpiredCodes;
/// App 层定义的强制登出错误码集合
final Set<int> forceLogoutCodes;
/// 瞬态错误最大重试次数5xx / 超时 / 连接失败)
///
/// 0 = 不重试(默认),设为 3 启用重试。
/// 与 Token 刷新重试独立,两者可叠加。
final int maxRetries;
/// 重试基础延迟(指数退避起点)
///
/// 实际延迟 = min(baseDelay * 2^attempt, 30s) + jitter
final Duration retryBaseDelay;
ApiConfig({
required this.baseURL,
this.token,
this.platformHeaders = const {},
this.onTokenRefresh,
this.onForceLogout,
this.onLog,
this.onCheckNetworkAvailable,
this.tokenExpiredCodes = const {},
this.forceLogoutCodes = const {},
this.maxRetries = 0,
this.retryBaseDelay = const Duration(seconds: 1),
});
/// 构建默认 headers
/// [includeToken] — 是否注入 token
/// [customHeaders] — 单次请求自定义 header优先级最高
Map<String, String> defaultHeaders({
bool includeToken = true,
Map<String, String>? customHeaders,
}) {
final headers = <String, String>{
'Content-Type': 'application/json; charset=utf-8',
'Accept': 'application/json',
'Timestamp': '${DateTime.now().millisecondsSinceEpoch ~/ 1000}',
'APP-Request-ID': _generateRequestId(),
};
// 合并平台 headersApp 层注入的 version、platform 等)
headers.addAll(platformHeaders);
// Token 注入
if (includeToken && token != null && token!.isNotEmpty) {
headers['token'] = token!;
}
// 单次请求自定义 header覆盖默认值
if (customHeaders != null) {
headers.addAll(customHeaders);
}
return headers;
}
/// 更新 token
void updateToken(String? newToken) {
token = newToken;
}
/// 更新 base URL
void updateBaseURL(String newBaseURL) {
baseURL = newBaseURL;
}
/// 生成请求 ID用于幂等性
String _generateRequestId() {
final now = DateTime.now().microsecondsSinceEpoch;
return '$now${Object().hashCode}';
}
}

View File

@@ -0,0 +1,13 @@
/// 网络层回调类型定义,由 App 层注入 SDK避免 SDK 直接依赖外部实现。
library;
typedef OnTokenRefresh = Future<String?> Function();
/// 強制登出回調
typedef OnForceLogout = void Function();
/// 日誌輸出回調
typedef OnLog = void Function(String message, {String? tag});
/// 網路可用性查詢App 層注入SDK 在請求前調用)
typedef OnCheckNetworkAvailable = Future<bool> Function();

View File

@@ -0,0 +1,106 @@
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';
import 'package:networks_sdk/src/domain/repositories/networks_messaging_repository.dart';
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]
class NetworksMessagingApiImpl implements NetworksMessagingApi {
NetworksMessagingRepository? _repository;
@override
void initialize(SocketConfig config) {
_repository = NetworksMessagingRepositoryImpl();
_repository!.initialize(config);
}
void _checkInitialized() {
if (_repository == null) {
throw StateError(
'NetworksMessagingApi not initialized. Call initialize() first.',
);
}
}
@override
Future<bool> connect(String url, {String? token}) {
_checkInitialized();
return _repository!.connect(url, token: token);
}
@override
Future<void> disconnect() {
_checkInitialized();
return _repository!.disconnect();
}
@override
bool get isConnected {
_checkInitialized();
return _repository!.isConnected;
}
@override
SocketConnectionState get connectionState {
_checkInitialized();
return _repository!.connectionState;
}
@override
Future<bool> send(Map<String, dynamic> message) {
_checkInitialized();
return _repository!.send(message);
}
@override
Future<bool> sendString(String message) {
_checkInitialized();
return _repository!.sendString(message);
}
@override
Stream<Map<String, dynamic>> get messageStream {
_checkInitialized();
return _repository!.messageStream;
}
@override
Stream<String> get rawMessageStream {
_checkInitialized();
return _repository!.rawMessageStream;
}
@override
Stream<SocketConnectionState> get connectionStateStream {
_checkInitialized();
return _repository!.connectionStateStream;
}
@override
Stream<SocketError> get errorStream {
_checkInitialized();
return _repository!.errorStream;
}
@override
void onEnterForeground() {
_checkInitialized();
_repository!.onEnterForeground();
}
@override
void onEnterBackground() {
_checkInitialized();
_repository!.onEnterBackground();
}
@override
Future<void> dispose() async {
if (_repository != null) {
await _repository!.dispose();
_repository = null;
}
}
}

View File

@@ -0,0 +1,19 @@
import '../../../networks_sdk.dart';
import 'networks_sdk_core.dart';
/// SDK API Implementation
class NetworksSdkApiImpl implements NetworksSdkApi {
final NetworksSdkCore _core;
NetworksSdkApiImpl({required NetworksSdkCore core}) : _core = core;
@override
Future<String?> platformVersion() => _core.repo.platformVersion();
@override
void initialize(ApiConfig apiConfig) => _core.repo.initialize(apiConfig);
@override
Future<T?> executeRequest<T>(ApiRequestable<T> request) => _core.repo.executeRequest(request);
}

View File

@@ -0,0 +1,14 @@
import '../../../networks_sdk_platform_interface.dart';
import '../../domain/repositories/networks_sdk_repository.dart';
class NetworksSdkCore
{
final NetworksSdkPlatform platform;
final NetworksSdkRepository repo;
NetworksSdkCore({
required this.platform,
required this.repo,
});
}

View File

@@ -0,0 +1,37 @@
import '../../../networks_sdk.dart';
import '../../../networks_sdk_method_channel.dart';
import '../../../networks_sdk_platform_interface.dart';
import '../../data/datasources/networks_sdk_method_channel_datasource.dart';
import '../../data/repositories/networks_sdk_repository_impl.dart';
import 'networks_sdk_core.dart';
import 'networks_sdk_api_impl.dart';
/// SDK Wiring - builds all SDK components
class NetworksSdkWiring
{
/// Builds the HTTP API
static NetworksSdkApi buildApi() {
// platform instancemethod channel
final platform = NetworksSdkPlatform.instance;
if (platform is MethodChannelNetworksSdk) {
// platform.init(); // or defer to NotificationApiImpl.init
}
// data layer
final ds = NetworksSdkMethodChannelDataSource(platform);
final repo = NetworksSdkRepositoryImpl(ds);
final core = NetworksSdkCore(platform: platform, repo: repo,);
return NetworksSdkApiImpl(core: core);
}
/// Builds the messaging API (WebSocket)
static NetworksMessagingApi buildMessagingApi() {
return NetworksMessagingApiImpl();
}
/// Builds the default SDK instance (HTTP API)
/// Use [buildMessagingApi()] separately for messaging features
static NetworksSdkApi build() => buildApi();
}

View File

@@ -0,0 +1,46 @@
/// WebSocket 配置
/// 非单例,由 App 层构造并注入到 SocketClient
///
/// 与 [ApiConfig] 设计一致SDK 不依赖 Flutter
/// 网络检测、生命周期等业务逻辑通过回调注入。
class SocketConfig {
/// 应用层心跳间隔(定时发送 "ping" 字符串)
final Duration heartbeatInterval;
/// 底层 WebSocket ping 间隔Dart WebSocket 自动管理)
final Duration pingInterval;
/// Pong 超时(超过此时间未收到 pong 则判定连接断开)
final Duration pongTimeout;
/// 连接超时
final Duration connectTimeout;
/// 最大重连次数0 = 不重连)
final int maxReconnectAttempts;
/// 最大重连延迟(指数退避上限)
final Duration maxReconnectDelay;
/// 是否自动重连
final bool autoReconnect;
/// 日志输出回调(与 ApiConfig.onLog 同签名)
final void Function(String message, {String? tag})? onLog;
/// 网络可用性查询App 层注入SDK 在重连前调用)
/// 返回 true 表示网络可用,可以尝试重连
final Future<bool> Function()? onCheckNetworkAvailable;
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.maxReconnectAttempts = 5,
this.maxReconnectDelay = const Duration(seconds: 30),
this.autoReconnect = true,
this.onLog,
this.onCheckNetworkAvailable,
});
}