import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/network/dio_client.dart'; import '../data/services/spot_service.dart'; /// 单币种配置(来自 spot/v1/config/coin/list) class SpotCoinConfig { final String coin; final String icon; final int scale; // 余额显示小数位数(coinScale/scale/minScale) final int volumePre; // 数量精度(volumePre,覆盖 scale) /// 前台展示小数位(后端 showPrecision,与 ConfigCoinSymbol 一致) final int showPrecision; const SpotCoinConfig({ required this.coin, this.icon = '', this.scale = 8, this.volumePre = 8, this.showPrecision = 0, }); /// 资产列表等「按币种展示数量」的小数位:仅用后端 showPrecision;未配置(≤0)时默认 2 int get assetDisplayDecimals => showPrecision > 0 ? showPrecision.clamp(0, 18) : 2; factory SpotCoinConfig.fromJson(Map json) { final coin = (json['symbol'] ?? json['coinId'] ?? json['coin'] ?? '') .toString() .toUpperCase(); final scale = ((json['coinScale'] ?? json['scale'] ?? json['minScale']) as num?) ?.toInt() ?? 8; final volumePre = (json['volumePre'] as num?)?.toInt() ?? scale; final showPrecision = (json['showPrecision'] as num?)?.toInt() ?? 0; return SpotCoinConfig( coin: coin, icon: json['icon'] as String? ?? '', scale: scale, volumePre: volumePre, showPrecision: showPrecision, ); } } /// 全局现货币种缓存 — 缓存 spot/v1/config/coin/list 返回的所有币种信息 /// 键为大写币种符号(如 "BTC", "USDT"),供现货资产列表查询图标和余额精度 class SpotCoinCacheNotifier extends Notifier> { @override Map build() { Future.microtask(_load); return {}; } Future _load() async { try { final dio = ref.read(dioClientProvider); final coins = await SpotService(dio).getCoins(); final configs = coins .map(SpotCoinConfig.fromJson) .where((c) => c.coin.isNotEmpty); state = {for (final c in configs) c.coin: c}; } catch (_) {} } Future refresh() => _load(); } final spotCoinCacheProvider = NotifierProvider>( SpotCoinCacheNotifier.new, ); /// 键统一为大写,与 [SpotCoinCacheNotifier] 中 map 一致 SpotCoinConfig? lookupSpotCoinConfig( Map map, String coin, ) { final k = coin.trim().toUpperCase(); if (k.isEmpty) return null; return map[k]; } /// 仅使用现货币种配置里的 icon(去空白);空串表示只显示字母占位,与「资产→现货」一致 String spotCoinIconUrl(Map map, String coin) { final raw = lookupSpotCoinConfig(map, coin)?.icon ?? ''; return raw.trim(); }