import 'package:dio/dio.dart'; /// 现货 API 服务 — 对接 /spot/v1/... 接口体系 /// /// 接口对照(全部带 spot/ 前缀): /// GET spot/v1/config/coin/list 已开启币种列表(含图标/精度) /// GET spot/v1/config/symbol/list 已开启交易对列表(含精度/手续费/最小量) /// GET spot/v1/account/asset/list 用户现货资产列表(含今日盈亏) /// GET spot/v1/account/transfer/symbols 可划转币种列表 /// POST spot/v1/account/transfer 资金账户 ↔ 现货账户划转 /// POST spot/v1/order/create 创建订单 /// POST spot/v1/order/cancel 撤单 /// POST spot/v1/order/current 当前委托 /// POST spot/v1/order/history 历史委托 /// POST spot/v1/order/trades 成交记录 /// POST spot/v1/account_records 资金流水 class SpotService { const SpotService(this._dio); final Dio _dio; // ── 配置 ──────────────────────────────────────────── /// 获取已开启的现货币种列表(含图标/显示精度/标签) Future>> getCoins() async { final resp = await _dio.get>('spot/v1/config/coin/list'); return _extractList(resp.data); } /// 获取已开启且已发布的交易对列表(含精度/手续费/最小下单量/排序权重) Future>> getSymbols() async { final resp = await _dio.get>('spot/v1/config/symbol/list'); return _extractList(resp.data); } // ── 资产 ──────────────────────────────────────────── /// 获取用户现货资产列表(含 totalAmount/todayPnl/todayPnlRate) /// /// [hideZero] 传 true 时服务端过滤 0 余额币种(对齐 Web hideZero 逻辑) Future> getAssets({bool? hideZero}) async { final query = {}; if (hideZero != null) { query['hideZero'] = hideZero; } final resp = await _dio.get>( 'spot/v1/account/asset/list', queryParameters: query.isEmpty ? null : query, ); final data = resp.data?['data']; if (data is Map) return data; return {}; } /// 获取可划转的开放币种列表 Future>> getTransferSymbols() async { final resp = await _dio .get>('spot/v1/account/transfer/symbols'); return _extractList(resp.data); } /// 账户间划转:资金账户 ↔ 现货账户 /// /// [direction] 1=资金账户→现货账户,2=现货账户→资金账户 Future transfer({ required String symbol, required double amount, required int direction, }) async { await _dio.post( 'spot/v1/account/transfer', data: {'symbol': symbol, 'amount': amount, 'direction': direction}, ); } // ── 订单 ──────────────────────────────────────────── /// 创建订单 /// /// [type] 1=限价单,2=市价单 /// [side] BUY / SELL /// [price] 限价单传价格;市价单传 0 /// [volume] 限价单传基础币数量;市价买传 USDT 金额;市价卖传基础币数量 Future> placeOrder({ required String symbol, required String side, required int type, required double price, required double volume, }) async { final resp = await _dio.post>( 'spot/v1/order/create', data: { 'symbol': symbol, 'side': side, 'type': type, 'price': price, 'volume': volume, 'source': 2, 'orderType': 1, }, ); final data = resp.data?['data']; if (data is Map) return data; return {}; } /// 撤单 Future cancelOrder(int orderId) async { await _dio.post( 'spot/v1/order/cancel', data: {'orderId': orderId}, ); } /// 查询当前委托(未完成挂单) Future> getCurrentOrders({ String? symbol, String? side, int page = 1, int size = 50, int? ctimeBegin, int? ctimeEnd, }) async { final body = {'page': page, 'size': size}; if (symbol != null && symbol.isNotEmpty) body['symbol'] = symbol; if (side != null && side.isNotEmpty) body['side'] = side; if (ctimeBegin != null) body['ctimeBegin'] = ctimeBegin; if (ctimeEnd != null) body['ctimeEnd'] = ctimeEnd; final resp = await _dio.post>('spot/v1/order/current', data: body); return _extractPageData(resp.data); } /// 查询历史委托(已完成或已撤单) Future> getHistoryOrders({ String? symbol, String? side, int? type, int? status, int page = 1, int size = 20, int? ctimeBegin, int? ctimeEnd, }) async { final body = {'page': page, 'size': size}; if (symbol != null && symbol.isNotEmpty) body['symbol'] = symbol; if (side != null && side.isNotEmpty) body['side'] = side; if (type != null) body['type'] = type; if (status != null) body['status'] = status; if (ctimeBegin != null) body['ctimeBegin'] = ctimeBegin; if (ctimeEnd != null) body['ctimeEnd'] = ctimeEnd; final resp = await _dio.post>('spot/v1/order/history', data: body); return _extractPageData(resp.data); } /// 查询成交记录(有成交量的订单) Future> getTrades({ String? symbol, String? side, int? type, int? status, int page = 1, int size = 20, int? ctimeBegin, int? ctimeEnd, }) async { final body = {'page': page, 'size': size}; if (symbol != null && symbol.isNotEmpty) body['symbol'] = symbol; if (side != null && side.isNotEmpty) body['side'] = side; if (type != null) body['type'] = type; if (status != null) body['status'] = status; if (ctimeBegin != null) body['ctimeBegin'] = ctimeBegin; if (ctimeEnd != null) body['ctimeEnd'] = ctimeEnd; final resp = await _dio.post>('spot/v1/order/trades', data: body); return _extractPageData(resp.data); } // ── 资金流水 ───────────────────────────────────────── /// 分页查询资金流水 /// /// [type] 流水类型:1=划转转入,2=划转转出,5=成交,12=手续费 Future> getAccountRecords({ String? symbol, int? type, int page = 1, int size = 20, int? startTime, int? endTime, }) async { final body = {'page': page, 'size': size}; if (symbol != null && symbol.isNotEmpty) body['symbol'] = symbol; if (type != null) body['type'] = type; if (startTime != null) body['startTime'] = startTime; if (endTime != null) body['endTime'] = endTime; final resp = await _dio .post>('spot/v1/account_records', data: body); return _extractPageData(resp.data); } // ── 工具 ──────────────────────────────────────────── static List> _extractList(Map? body) { final raw = body?['data']; if (raw is List) return raw.whereType>().toList(); return []; } static Map _extractPageData(Map? body) { final raw = body?['data']; if (raw is Map) return raw; return {}; } }