| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- import 'package:dio/dio.dart';
- import 'package:flutter_riverpod/flutter_riverpod.dart';
- import '../../core/network/dio_client.dart';
- import '../services/futures_service.dart';
- /// 合约跟单 Repository — 对应 Android FollowDefService + FollowService
- class CopyTradingRepository {
- CopyTradingRepository(this._dio);
- final Dio _dio;
- /// 获取交易员列表
- /// [orderBy] 排序:10=综合, 20=14日收益率, 30=14日胜率, 40=资金实力
- /// [traderType] ""=全部, "1"=普通跟单, "2"=无损跟单
- Future<List<Map<String, dynamic>>> getTraderList({
- required String orderBy,
- String traderType = '',
- String nickName = '',
- int page = 1,
- int pageSize = 20,
- }) async {
- final resp = await _dio.post(
- 'follow/customer/trader-list',
- data: {
- 'orderBy': int.tryParse(orderBy) ?? 10,
- 'traderType': traderType.isEmpty ? null : int.tryParse(traderType),
- 'nickName': nickName,
- 'currentPage': '$page',
- 'pageSize': '$pageSize',
- },
- );
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map) {
- final records = data['records'];
- if (records is List) {
- return records.whereType<Map<String, dynamic>>().toList();
- }
- }
- }
- return [];
- }
- /// 获取公开交易员列表(无需登录,支持 traderType / orderBy 过滤)
- Future<List<Map<String, dynamic>>> getPublicTraderList({
- int page = 1,
- int pageSize = 20,
- String traderType = '',
- String orderBy = '10',
- }) async {
- final Map<String, dynamic> params = {
- 'pageNum': page,
- 'pageSize': pageSize,
- 'orderBy': int.tryParse(orderBy) ?? 10
- };
- if (traderType.isNotEmpty) params['traderType'] = int.tryParse(traderType);
- final resp = await _dio.get(
- 'follow/customer/top-trader-list',
- queryParameters: params,
- );
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map) {
- final records = data['records'];
- if (records is List) {
- return records.whereType<Map<String, dynamic>>().toList();
- }
- }
- if (data is List) return data.whereType<Map<String, dynamic>>().toList();
- }
- return [];
- }
- /// 获取跟单账户权益(balance、availableBalance、currentRevenue、totalRevenue)
- Future<Map<String, dynamic>?> getFollowWallet() async {
- final resp = await _dio.get('swap/follow-wallet/get');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map<String, dynamic>) return data;
- }
- return null;
- }
- /// 获取合约账户信息(currentCapital = 合约账户权益,balance = 可用余额)
- Future<Map<String, dynamic>?> getContractWallet() async {
- final resp = await _dio.get('swap/wallet-new/get');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map<String, dynamic>) return data;
- }
- return null;
- }
- /// 获取当前用户跟单/交易员信息
- /// 关键字段:traderLevel("20"=交易员,其他=跟单员)、id(交易员ID)
- Future<Map<String, dynamic>?> getFollowerInfo() async {
- final resp = await _dio.get('follow/customer/');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map<String, dynamic>) return data;
- if (data is Map) return Map<String, dynamic>.from(data);
- if (data is List && data.isNotEmpty) {
- final first = data.first;
- if (first is Map<String, dynamic>) return first;
- if (first is Map) return Map<String, dynamic>.from(first);
- }
- }
- if (body is List && body.isNotEmpty) {
- final first = body.first;
- if (first is Map<String, dynamic>) return first;
- if (first is Map) return Map<String, dynamic>.from(first);
- }
- return null;
- }
- /// 获取交易员自己的带单统计信息
- /// 字段:currentClearedProfit(本期结算收益)、totalFollowProfit(累计分润收益)、
- /// following(当前跟单人数)、maxFollow(最大跟单人数)
- Future<Map<String, dynamic>?> getTraderInfo(String traderId) async {
- final resp = await _dio.put('follow/customer/trader-info/$traderId');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map<String, dynamic>) return data;
- }
- return null;
- }
- /// 提交交易员申请
- Future<bool> applyTrader() async {
- await _dio.get('follow/customer/apply-trader');
- return true;
- }
- /// 获取我的跟单用户列表(带单员视角)
- Future<List<Map<String, dynamic>>> getMyFollowers(
- {int page = 1, int pageSize = 20}) async {
- final resp = await _dio.post(
- 'follow/follow/followers',
- data: {'currentPage': page, 'pageSize': pageSize},
- );
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map) {
- final records = data['records'] ?? data['list'];
- if (records is List)
- return records.whereType<Map<String, dynamic>>().toList();
- }
- // 有些接口直接 data 就是 list
- if (data is List) return data.whereType<Map<String, dynamic>>().toList();
- }
- return [];
- }
- /// 获取带单仓位(当前/历史)
- /// [type] "current" = 当前带单, "history" = 历史带单
- Future<List<Map<String, dynamic>>> getTraderOrders({
- required String traderId,
- required String type,
- int page = 1,
- int pageSize = 20,
- }) async {
- final resp = await _dio.post(
- 'follow/position/lead',
- data: {
- 'traderId': traderId,
- 'type': type,
- 'currentPage': page,
- 'pageSize': pageSize,
- },
- );
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is Map) {
- final records = data['records'] ?? data['list'];
- if (records is List)
- return records.whereType<Map<String, dynamic>>().toList();
- }
- if (data is List) return data.whereType<Map<String, dynamic>>().toList();
- }
- return [];
- }
- /// 移除跟单用户
- Future<bool> removeFollower(String followId) async {
- await _dio.put('follow/follow/remove-follow/$followId');
- return true;
- }
- // ── 带单设置 ────────────────────────────────────────────
- /// 获取所有可选标签(GET follow/tag/)
- Future<List<Map<String, dynamic>>> getAllTags() async {
- final resp = await _dio.get('follow/tag/');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is List) return data.whereType<Map<String, dynamic>>().toList();
- }
- return [];
- }
- /// 获取交易员当前已选标签(GET follow/trader-tag/)
- Future<List<Map<String, dynamic>>> getMyTags() async {
- final resp = await _dio.get('follow/trader-tag/');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is List) return data.whereType<Map<String, dynamic>>().toList();
- }
- return [];
- }
- /// 获取交易员可选合约(PUT follow/trader-symbol/{traderId})
- Future<List<Map<String, dynamic>>> getTraderSymbols(String traderId) async {
- final resp = await _dio.put('follow/trader-symbol/$traderId');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data is List) return data.whereType<Map<String, dynamic>>().toList();
- }
- return [];
- }
- /// 更新昵称 / 签名(POST follow/customer/update)
- Future<bool> updateTraderProfile(
- {String? nickname, String? description}) async {
- final Map<String, dynamic> data = {};
- if (nickname != null) data['nickName'] = nickname;
- if (description != null) data['description'] = description;
- await _dio.post('follow/customer/update', data: data);
- return true;
- }
- /// 更新标签(POST follow/trader-tag/update),传标签 id 数组
- Future<bool> updateTraderTags(List<String> tagIds) async {
- await _dio.post('follow/trader-tag/update', data: tagIds);
- return true;
- }
- /// 更新带单合约(POST follow/trader-symbol/update),传 symbol 名称数组
- Future<bool> updateTraderSymbols(List<String> symbols) async {
- await _dio.post('follow/trader-symbol/update', data: symbols);
- return true;
- }
- /// 关注交易员(PUT follow/favorite/{traderId})
- /// 拦截器已对 code≠0 抛异常,此处不抛即表示成功
- Future<bool> favoriteTrader(String traderId) async {
- await _dio.put('follow/favorite/$traderId');
- return true;
- }
- /// 取消关注交易员(PUT follow/favorite/unfavorite/{traderId})
- Future<bool> unfavoriteTrader(String traderId) async {
- await _dio.put('follow/favorite/unfavorite/$traderId');
- return true;
- }
- /// 收藏的交易员列表(POST follow/favorite/list)
- Future<List<Map<String, dynamic>>> getFavoriteList({
- int currentPage = 1,
- int pageSize = 200,
- }) async {
- final resp = await _dio.post(
- 'follow/favorite/list',
- data: {
- 'currentPage': currentPage,
- 'pageSize': pageSize,
- },
- );
- return _extractRecords(resp.data);
- }
- /// 开始跟单(POST follow/follow/)
- Future<bool> followTrader(Map<String, dynamic> params) async {
- await _dio.post('follow/follow/', data: params);
- return true;
- }
- /// 取消跟单(PUT follow/follow/unfollow/{traderId})
- Future<bool> unfollowTrader(String traderId) async {
- await _dio.put('follow/follow/unfollow/$traderId');
- return true;
- }
- /// 获取邀请链接(uc/member/my-info → promotionPrefix + promotionCode)
- Future<String?> getInviteUrl() async {
- try {
- final resp = await _dio.post('uc/member/my-info');
- final data = resp.data is Map ? resp.data['data'] : null;
- if (data is Map) {
- final prefix = data['promotionPrefix']?.toString() ?? '';
- final code = data['promotionCode']?.toString() ?? '';
- if (prefix.isNotEmpty || code.isNotEmpty) return '$prefix$code';
- }
- } catch (_) {}
- return null;
- }
- /// 更新头像(POST follow/customer/update-avatar,multipart/form-data,字段名 "avatar")
- /// 返回新头像 URL
- Future<String?> updateAvatar(String filePath) async {
- final formData = FormData.fromMap({
- 'avatar': await MultipartFile.fromFile(filePath, filename: 'avatar.jpg'),
- });
- final resp =
- await _dio.post('follow/customer/update-avatar', data: formData);
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data != null) return data.toString();
- }
- return null;
- }
- /// 获取合约持仓列表(GET swap/wallet-new/get-with-positions),用于与带单仓位交叉匹配保证金比率
- Future<List<Map<String, dynamic>>> getFuturesPositions() async {
- try {
- final data = await FuturesService(_dio).getWithPositions();
- final list = data['currentPositionWithCutList'];
- if (list is List) return list.whereType<Map<String, dynamic>>().toList();
- } catch (_) {}
- return [];
- }
- /// 申请取消交易员资格(GET follow/customer/cancel-trader)
- Future<bool> cancelTraderQualification() async {
- await _dio.get('follow/customer/cancel-trader');
- return true;
- }
- /// 获取当前带单员的跟单用户总数(GET follow/follow/follower-count)
- Future<int> getMyFollowerCount() async {
- final resp = await _dio.get('follow/follow/follower-count');
- final body = resp.data;
- if (body is Map) {
- final data = body['data'];
- if (data != null) return int.tryParse(data.toString()) ?? 0;
- }
- if (body != null) return int.tryParse(body.toString()) ?? 0;
- return 0;
- }
- // ── 我的跟单 3 个 Tab ────────────────────────────────────
- /// 当前跟单仓位(POST follow/position/current-follow)
- Future<List<Map<String, dynamic>>> getCurrentCopyPositions(
- {int page = 1, int pageSize = 20}) async {
- final resp = await _dio.post(
- 'follow/position/current-follow',
- data: {'currentPage': '$page', 'pageSize': '$pageSize'},
- );
- return _extractRecords(resp.data);
- }
- /// 我的交易员列表(POST follow/follow/following)
- Future<List<Map<String, dynamic>>> getFollowingTraders(
- {int page = 1, int pageSize = 20}) async {
- final resp = await _dio.post(
- 'follow/follow/following',
- data: {'currentPage': '$page', 'pageSize': '$pageSize'},
- );
- return _extractRecords(resp.data);
- }
- /// 历史跟单仓位(POST follow/position/history-follow)
- Future<List<Map<String, dynamic>>> getHistoryCopyPositions(
- {int page = 1, int pageSize = 20}) async {
- final resp = await _dio.post(
- 'follow/position/history-follow',
- data: {'currentPage': '$page', 'pageSize': '$pageSize'},
- );
- return _extractRecords(resp.data);
- }
- /// 手动平仓(POST swap/order/close-all,市价全平)
- /// 服务端用 @RequestParam 读取,必须以 application/x-www-form-urlencoded 发送
- Future<bool> closeCopyPosition(String positionId) async {
- await _dio.post(
- 'swap/order/close-all',
- data: {'positionId': positionId},
- options: Options(contentType: Headers.formUrlEncodedContentType),
- );
- return true;
- }
- List<Map<String, dynamic>> _extractRecords(dynamic body) {
- if (body is! Map) return [];
- final data = body['data'];
- if (data is Map) {
- final records = data['records'] ?? data['list'];
- if (records is List)
- return records.whereType<Map<String, dynamic>>().toList();
- }
- if (data is List) return data.whereType<Map<String, dynamic>>().toList();
- return [];
- }
- }
- final copyTradingRepositoryProvider = Provider<CopyTradingRepository>((ref) {
- return CopyTradingRepository(ref.read(dioClientProvider));
- });
|