This commit is contained in:
sion
2026-04-06 18:45:04 +08:00
parent 2e34072f45
commit ee8979f471
37 changed files with 5 additions and 2500 deletions

View File

@@ -1,183 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../data/models/kline_candle.dart';
import '../data/services/kline_service.dart';
import '../data/services/kline_websocket_service.dart';
/// K线状态管理
class KlineProvider extends ChangeNotifier {
final KlineService _klineService;
final KlineWebSocketService _wsService;
KlineProvider(this._klineService, this._wsService);
List<KlineCandle> _candles = [];
KlineCandle? _currentCandle;
String _interval = '1h';
String _coinCode = '';
bool _isLoading = false;
bool _isLoadingMore = false;
bool _isConnected = false;
String? _error;
StreamSubscription<KlineCandle>? _wsSubscription;
Timer? _pollingTimer;
// Getters
List<KlineCandle> get candles => _candles;
KlineCandle? get currentCandle => _currentCandle;
String get interval => _interval;
String get coinCode => _coinCode;
bool get isLoading => _isLoading;
bool get isLoadingMore => _isLoadingMore;
bool get isConnected => _isConnected;
String? get error => _error;
/// 加载某个币种的K线数据
Future<void> loadCoin(String coinCode, {String? interval}) async {
_coinCode = coinCode;
if (interval != null) _interval = interval;
_candles = [];
_currentCandle = null;
_error = null;
_isLoading = true;
notifyListeners();
try {
// 1. 获取历史K线
final response = await _klineService.fetchHistory(
coinCode: _coinCode,
interval: _interval,
limit: 200,
);
if (response.success && response.data != null) {
_candles = response.data!;
}
// 2. 获取当前K线
final currentResponse = await _klineService.fetchCurrentCandle(
coinCode: _coinCode,
interval: _interval,
);
if (currentResponse.success && currentResponse.data != null) {
_currentCandle = currentResponse.data;
}
// 3. 连接 WebSocket
_connectWebSocket();
_isLoading = false;
notifyListeners();
} catch (e) {
_error = '加载K线数据失败: $e';
_isLoading = false;
notifyListeners();
}
}
/// 切换周期
Future<void> changeInterval(String newInterval) async {
if (newInterval == _interval) return;
_interval = newInterval;
_candles = [];
_currentCandle = null;
// 重新加载
await loadCoin(_coinCode, interval: newInterval);
}
/// 加载更多历史数据(分页)
Future<void> loadMore() async {
if (_isLoadingMore || _candles.isEmpty) return;
_isLoadingMore = true;
notifyListeners();
try {
final oldestTime = _candles.first.closeTime;
final response = await _klineService.fetchHistory(
coinCode: _coinCode,
interval: _interval,
limit: 200,
before: oldestTime,
);
if (response.success && response.data != null) {
_candles = [...response.data!, ..._candles];
}
} catch (_) {}
_isLoadingMore = false;
notifyListeners();
}
void _connectWebSocket() {
// 取消之前的订阅
_wsSubscription?.cancel();
_wsService.unsubscribe(_coinCode);
// 订阅新币种
_wsService.connect();
_wsSubscription = _wsService.subscribe(_coinCode).listen(
_onTick,
onError: (_) => _startPolling(),
onDone: () => _startPolling(),
);
_isConnected = _wsService.isConnected;
notifyListeners();
}
void _onTick(KlineCandle tick) {
if (tick.interval != _interval) return;
_isConnected = true;
if (tick.isClosed) {
// 收盘 tick → 添加到历史列表
_candles.add(tick);
_currentCandle = null;
// 停止轮询(如果之前在轮询)
_pollingTimer?.cancel();
_pollingTimer = null;
} else {
// 进行中的 tick → 更新当前K线
_currentCandle = tick;
}
notifyListeners();
}
/// WebSocket 断连时降级为 HTTP 轮询
void _startPolling() {
_isConnected = false;
notifyListeners();
_pollingTimer?.cancel();
_pollingTimer = Timer.periodic(
const Duration(seconds: 5),
(_) => _pollCurrentCandle(),
);
}
Future<void> _pollCurrentCandle() async {
try {
final response = await _klineService.fetchCurrentCandle(
coinCode: _coinCode,
interval: _interval,
);
if (response.success && response.data != null) {
_currentCandle = response.data;
notifyListeners();
}
} catch (_) {}
}
@override
void dispose() {
_wsSubscription?.cancel();
_wsService.unsubscribe(_coinCode);
_pollingTimer?.cancel();
super.dispose();
}
}