111
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user