import 'package:dio/dio.dart'; typedef PageResult = ({List> items, bool hasMore}); // type: 0=市价,1=限价,2=计划委托 // direction: 0=做多,1=做空 // 撤单传 entrustId(数字 ID) class FuturesService { const FuturesService(this._dio); final Dio _dio; static final _form = Options( contentType: 'application/x-www-form-urlencoded', ); // ── 合约信息 ───────────────────────────────────────────────── Future> getSymbolInfo(String symbol) async { final resp = await _dio.post>( 'swap/symbol-info', data: {'symbol': symbol}, options: _form, ); final data = resp.data?['data']; if (data is Map) return data; return resp.data ?? {}; } // ── 杠杆 ───────────────────────────────────────────────────── Future getLeverage(int contractId) async { final resp = await _dio.post>( 'swap/wallet-new/getLeverage', data: {'contractId': contractId}, options: _form, ); final data = resp.data?['data']; return (data is num ? data.toInt() : int.tryParse('$data')) ?? 20; } Future modifyLeverage(int contractId, int leverage) async { await _dio.post( 'swap/wallet-new/modifyLeverage', data: {'contractId': contractId, 'leverage': leverage}, options: _form, ); } /// 切换仓位模式:0=合仓(全仓)1=分仓 /// 后端要求有持仓/委托时禁止切换,失败会抛异常 Future modifyPositionType(int positionType) async { await _dio.post( 'swap/wallet-new/modifyType', data: {'positionType': positionType}, options: _form, ); } // ── 账户与仓位 ─────────────────────────────────────────────── Future> getWithPositions([String symbol = '']) async { final resp = await _dio.get>( 'swap/wallet-new/get-with-positions', queryParameters: symbol.isNotEmpty ? {'symbol': symbol} : null, ); return (resp.data?['data'] as Map?) ?? {}; } // ── 委托单 ─────────────────────────────────────────────────── Future getCurrentOrders({ int pageNo = 1, int pageSize = 10, }) async { final resp = await _dio.get>( 'swap/order/current', queryParameters: {'pageNo': pageNo, 'pageSize': pageSize}, ); return _extractPage(resp.data, pageSize); } static PageResult _extractPage(Map? body, int pageSize) { if (body == null) return (items: [], hasMore: false); List> items = []; final direct = body['content'] ?? body['records'] ?? body['list']; if (direct is List) { items = direct.map((e) => Map.from(e as Map)).toList(); final last = body['last']; final hasMore = last is bool ? !last : items.length >= pageSize; return (items: items, hasMore: hasMore); } final nested = body['data']; if (nested is Map) { final content = nested['content'] ?? nested['records'] ?? nested['list']; if (content is List) { items = content.map((e) => Map.from(e as Map)).toList(); final last = nested['last']; final hasMore = last is bool ? !last : items.length >= pageSize; return (items: items, hasMore: hasMore); } } if (nested is List) { items = nested.map((e) => Map.from(e as Map)).toList(); return (items: items, hasMore: items.length >= pageSize); } return (items: [], hasMore: false); } // ── 开仓 ───────────────────────────────────────────────────── Future openOrder({ required int contractCoinId, required int type, required int direction, required double volume, required int leverage, double? entrustPrice, double? triggerPrice, double? profitPrice, double? lossPrice, int? isPercentage, // 1=百分比模式 int? positionType, // 0=全仓 1=分仓,由前端传递 }) async { // 计划委托无 entrustPrice 时传 0(市价) final effectiveEntrustPrice = (type == 2 && (entrustPrice == null || entrustPrice <= 0)) ? 0.0 : entrustPrice; await _dio.post( 'swap/order/open/v3', data: { 'contractCoinId': contractCoinId, 'type': type, 'direction': direction, 'volume': volume.toString(), 'leverage': leverage, 'entrustPrice': effectiveEntrustPrice?.toString() ?? '', 'triggerPrice': triggerPrice?.toString() ?? '', 'profitPrice': profitPrice?.toString() ?? '', 'lossPrice': lossPrice?.toString() ?? '', if (isPercentage != null) 'isPercentage': isPercentage, if (positionType != null) 'positionType': positionType, }, options: _form, ); } // ── 平仓 ───────────────────────────────────────────────────── Future closeMarket({ required String positionId, required double volume, }) async { await _dio.post( 'swap/order/close', data: { 'positionId': positionId, 'volume': volume.toString(), 'type': 0, 'entrustPrice': '0', 'triggerPrice': '0', }, options: _form, ); } Future closeLimit({ required String positionId, required double price, required double volume, }) async { await _dio.post( 'swap/order/close', data: { 'positionId': positionId, 'entrustPrice': price.toString(), 'volume': volume.toString(), 'type': 1, 'triggerPrice': '0', }, options: _form, ); } Future closeAllPositions() async { await _dio.post('swap/order/close-all-coins', options: _form); } // entrustPrice=0 → 计划市价;>0 → 计划限价 Future closeConditional({ required String positionId, required double triggerPrice, required double volume, double entrustPrice = 0, }) async { await _dio.post( 'swap/order/close', data: { 'positionId': positionId, 'entrustPrice': entrustPrice.toString(), 'volume': volume.toString(), 'type': 2, 'triggerPrice': triggerPrice.toString(), }, options: _form, ); } Future reverseOpenPosition(String positionId) async { await _dio.post( 'swap/order/close-and-open', data: {'positionId': positionId}, options: _form, ); } // ── 撤单 ───────────────────────────────────────────────────── Future cancelOrder(int entrustId) async { await _dio.post( 'swap/order/cancel', data: {'entrustId': entrustId}, options: _form, ); } // entrustIds 以 JSON 数组字符串传递,如 "[1,2,3]" Future cancelOrdersByIds(List entrustIds) async { await _dio.post( 'swap/order/cancel-by-ids', data: {'entrustIds': entrustIds.toString()}, options: _form, ); } // ── 止盈止损 ───────────────────────────────────────────────── // 首次设置(无 cutEntrustId) Future setTpsl({ required String positionId, double? profitPrice, double? lossPrice, }) async { await _dio.post( 'swap/order/cut-position', data: { 'positionId': positionId, 'profitPrice': profitPrice?.toString() ?? '0', 'profitEntrustPrice': '0.0', 'lossPrice': lossPrice?.toString() ?? '0', 'lossEntrustPrice': '0.0', }, options: _form, ); } // 修改已有止盈止损委托(有 cutEntrustId) Future modifyCutOrder({ required int entrustId, double? profitPrice, double? lossPrice, }) async { await _dio.post( 'swap/order/modify-cut', data: { 'entrustId': entrustId, 'profitPrice': profitPrice?.toString() ?? '0', 'lossPrice': lossPrice?.toString() ?? '0', }, options: _form, ); } // ── 历史记录 ───────────────────────────────────────────────── Future getPositionHistory({ int pageNo = 1, int pageSize = 10, }) async { final resp = await _dio.get>( 'swap/position/history', queryParameters: {'pageNo': pageNo, 'pageSize': pageSize}, ); return _extractPage(resp.data, pageSize); } Future getOrderHistoryAll({ int pageNo = 1, int pageSize = 10, }) async { final resp = await _dio.get>( 'swap/order/history-all', queryParameters: {'pageNo': pageNo, 'pageSize': pageSize}, ); return _extractPage(resp.data, pageSize); } }