| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import '../../../core/utils/avatar_urls.dart';
- /// 交易员(跟单广场列表项)
- class Trader {
- final String id;
- final String name;
- final String avatarLetter;
- final String? avatarUrl; // 真实头像 URL(来自 API)
- final int followers;
- final int? maxFollowers; // 最大跟随人数
- final double roi30d; // 近14日收益率 %
- final double profit30d; // 近30日收益 USDT
- final double profitShare; // 分润比例 %(来自 dividendPercent)
- final double winRate; // 近14日胜率 %
- final bool isNoLoss; // 是否无损跟单
- final bool isFollowing; // 是否已跟随
- /// 当前用户是否已收藏(跟单广场「我的收藏」与列表星标)
- final bool isFavorited;
- final List<double> trendData; // 走势迷你图数据
- final String? levelName; // 交易员等级名称(如 L3、L5)
- // ── 我的交易员 tab 额外字段 ──────────────────────────────
- final String? description; // 个人介绍
- final List<String> tags; // 标签列表(如超短、中长、稳健型)
- final double profitAmount; // 累计收益 USDT(对跟单员是从该交易员获得的收益)
- final int tradingDays; // 累计交易天数
- final int followCustomer; // 累计跟单人数(总人数,非当前)
- const Trader({
- required this.id,
- required this.name,
- required this.avatarLetter,
- this.avatarUrl,
- required this.followers,
- this.maxFollowers,
- required this.roi30d,
- required this.profit30d,
- required this.profitShare,
- required this.winRate,
- this.isNoLoss = false,
- this.isFollowing = false,
- this.isFavorited = false,
- this.trendData = const [],
- this.levelName,
- this.description,
- this.tags = const [],
- this.profitAmount = 0,
- this.tradingDays = 0,
- this.followCustomer = 0,
- });
- /// 是否与 Web 一致的布尔解析(支持 1/true/"true")。
- static bool parseFavoriteFlag(dynamic value) {
- if (value == null) {
- return false;
- }
- if (value is bool) {
- return value;
- }
- if (value is num) {
- return value != 0;
- }
- final s = value.toString().trim().toLowerCase();
- if (s.isEmpty || s == '0' || s == 'false') {
- return false;
- }
- if (s == '1' || s == 'true' || s == 'yes') {
- return true;
- }
- return false;
- }
- Trader copyWith({
- bool? isFavorited,
- bool? isFollowing,
- }) {
- return Trader(
- id: id,
- name: name,
- avatarLetter: avatarLetter,
- avatarUrl: avatarUrl,
- followers: followers,
- maxFollowers: maxFollowers,
- roi30d: roi30d,
- profit30d: profit30d,
- profitShare: profitShare,
- winRate: winRate,
- isNoLoss: isNoLoss,
- isFollowing: isFollowing ?? this.isFollowing,
- isFavorited: isFavorited ?? this.isFavorited,
- trendData: trendData,
- levelName: levelName,
- description: description,
- tags: tags,
- profitAmount: profitAmount,
- tradingDays: tradingDays,
- followCustomer: followCustomer,
- );
- }
- /// 从 API 返回的 FollowTrader / TraderVo Map 构建
- factory Trader.fromApi(
- Map<String, dynamic> json, {
- bool isNoLoss = false,
- bool? isFavoritedOverride,
- }) {
- final nickname =
- (json['nickname'] ?? json['nickName'])?.toString().trim() ?? '';
- final letter = nickname.isNotEmpty ? nickname[0].toUpperCase() : '?';
- // 将 yield14List 数组转为 double 列表(用于走势图)
- List<double> trendData = [];
- final yields = json['yield14List'];
- if (yields is List) {
- trendData = yields
- .map((e) {
- if (e is Map) {
- // 安卓 Yield14 model: @SerializedName("dayYield14")
- final v =
- e['dayYield14'] ?? e['yield'] ?? e['rate'] ?? e['value'] ?? 0;
- return (v is num)
- ? v.toDouble()
- : double.tryParse(v.toString()) ?? 0.0;
- }
- return (e is num)
- ? e.toDouble()
- : double.tryParse(e.toString()) ?? 0.0;
- })
- .toList()
- .reversed // 后台按 date DESC 返回(最新在前),翻转为时间正序(旧→新,左→右)
- .toList();
- }
- double _d(String key) =>
- double.tryParse((json[key] ?? '0').toString()) ?? 0.0;
- int _i(String key) => int.tryParse((json[key] ?? '0').toString()) ?? 0;
- // 解析标签列表(可能是 List<String> 或 List<Map>)
- List<String> tags = [];
- final rawTags = json['tags'];
- if (rawTags is List) {
- tags = rawTags
- .map((e) {
- if (e is String) return e;
- if (e is Map) return (e['name'] ?? '').toString();
- return '';
- })
- .where((s) => s.isNotEmpty)
- .toList();
- }
- // 分润比例:不同接口可能用不同字段名
- double resolvedProfitShare = 0.0;
- for (final key in [
- 'dividendPercent',
- 'profitRatio',
- 'dividendRatio',
- 'sharePercent',
- 'profitPercent'
- ]) {
- final v = double.tryParse((json[key] ?? '').toString());
- if (v != null && v != 0.0) {
- resolvedProfitShare = v;
- break;
- }
- }
- return Trader(
- id: json['id']?.toString() ?? '',
- name: nickname,
- avatarLetter: letter,
- avatarUrl: resolvedAvatarUrlFromRecord(json),
- followers: _i('following'),
- maxFollowers: json['maxFollow'] != null ? _i('maxFollow') : null,
- roi30d: _d('dayYield14'),
- profit30d: _d('profitAmount'),
- profitShare: resolvedProfitShare,
- winRate: _d('winRate14'),
- isNoLoss: isNoLoss || json['specialTrader'] == true,
- isFollowing: parseFavoriteFlag(json['isFollow'] ?? json['follow']),
- isFavorited: isFavoritedOverride ??
- parseFavoriteFlag(
- json['isFavorite'] ?? json['isFavorited'] ?? json['favorite']),
- trendData: trendData,
- levelName: json['levelName'] as String?,
- description: json['description'] as String?,
- tags: tags,
- profitAmount: _d('profitAmount'),
- tradingDays: _i('tradingDays'),
- followCustomer: _i('defaultFollowCustomer') + _i('followCustomer'),
- );
- }
- }
|