217 lines
6.9 KiB
Dart
217 lines
6.9 KiB
Dart
class ReferralInfo {
|
|
final String referralCode;
|
|
final String shareUrl;
|
|
final int directCount;
|
|
final int activeCount;
|
|
final int pendingCreditCents;
|
|
final int totalEarnedCents;
|
|
final int totalAppliedCents;
|
|
|
|
const ReferralInfo({
|
|
required this.referralCode,
|
|
required this.shareUrl,
|
|
required this.directCount,
|
|
required this.activeCount,
|
|
required this.pendingCreditCents,
|
|
required this.totalEarnedCents,
|
|
required this.totalAppliedCents,
|
|
});
|
|
|
|
factory ReferralInfo.fromJson(Map<String, dynamic> json) => ReferralInfo(
|
|
referralCode: json['referralCode'] as String? ?? '',
|
|
shareUrl: json['shareUrl'] as String? ?? '',
|
|
directCount: json['directCount'] as int? ?? 0,
|
|
activeCount: json['activeCount'] as int? ?? 0,
|
|
pendingCreditCents: json['pendingCreditCents'] as int? ?? 0,
|
|
totalEarnedCents: json['totalEarnedCents'] as int? ?? 0,
|
|
totalAppliedCents: json['totalAppliedCents'] as int? ?? 0,
|
|
);
|
|
|
|
String get pendingCreditFormatted =>
|
|
'\$${(pendingCreditCents / 100).toStringAsFixed(2)}';
|
|
|
|
String get totalEarnedFormatted =>
|
|
'\$${(totalEarnedCents / 100).toStringAsFixed(2)}';
|
|
}
|
|
|
|
class ReferralItem {
|
|
final String id;
|
|
final String referredTenantId;
|
|
final String referralCode;
|
|
final String status; // PENDING | ACTIVE | REWARDED | EXPIRED
|
|
final int level;
|
|
final DateTime registeredAt;
|
|
final DateTime? activatedAt;
|
|
|
|
const ReferralItem({
|
|
required this.id,
|
|
required this.referredTenantId,
|
|
required this.referralCode,
|
|
required this.status,
|
|
required this.level,
|
|
required this.registeredAt,
|
|
this.activatedAt,
|
|
});
|
|
|
|
factory ReferralItem.fromJson(Map<String, dynamic> json) => ReferralItem(
|
|
id: json['id'] as String,
|
|
referredTenantId: json['referredTenantId'] as String? ?? '',
|
|
referralCode: json['referralCode'] as String? ?? '',
|
|
status: json['status'] as String? ?? 'PENDING',
|
|
level: json['level'] as int? ?? 1,
|
|
registeredAt: DateTime.parse(json['registeredAt'] as String),
|
|
activatedAt: json['activatedAt'] != null
|
|
? DateTime.parse(json['activatedAt'] as String)
|
|
: null,
|
|
);
|
|
|
|
bool get isActive => status == 'ACTIVE' || status == 'REWARDED';
|
|
}
|
|
|
|
// ── User-level personal circle models ─────────────────────────────────────
|
|
|
|
class UserReferralInfo {
|
|
final String code;
|
|
final String shareUrl;
|
|
final int circleSize;
|
|
final int activeCount;
|
|
final int pointsBalance;
|
|
final int totalEarned;
|
|
final int totalSpent;
|
|
|
|
const UserReferralInfo({
|
|
required this.code,
|
|
required this.shareUrl,
|
|
required this.circleSize,
|
|
required this.activeCount,
|
|
required this.pointsBalance,
|
|
required this.totalEarned,
|
|
required this.totalSpent,
|
|
});
|
|
|
|
factory UserReferralInfo.fromJson(Map<String, dynamic> json) => UserReferralInfo(
|
|
code: json['code'] as String? ?? '',
|
|
shareUrl: json['shareUrl'] as String? ?? '',
|
|
circleSize: json['circleSize'] as int? ?? 0,
|
|
activeCount: json['activeCount'] as int? ?? 0,
|
|
pointsBalance: json['pointsBalance'] as int? ?? 0,
|
|
totalEarned: json['totalEarned'] as int? ?? 0,
|
|
totalSpent: json['totalSpent'] as int? ?? 0,
|
|
);
|
|
}
|
|
|
|
class CircleMember {
|
|
final String relationshipId;
|
|
final String referredUserId;
|
|
final int level;
|
|
final String status;
|
|
final DateTime joinedAt;
|
|
final DateTime? activatedAt;
|
|
|
|
const CircleMember({
|
|
required this.relationshipId,
|
|
required this.referredUserId,
|
|
required this.level,
|
|
required this.status,
|
|
required this.joinedAt,
|
|
this.activatedAt,
|
|
});
|
|
|
|
factory CircleMember.fromJson(Map<String, dynamic> json) => CircleMember(
|
|
relationshipId: json['relationshipId'] as String,
|
|
referredUserId: json['referredUserId'] as String,
|
|
level: json['level'] as int? ?? 1,
|
|
status: json['status'] as String? ?? 'PENDING',
|
|
joinedAt: DateTime.parse(json['joinedAt'] as String),
|
|
activatedAt: json['activatedAt'] != null
|
|
? DateTime.parse(json['activatedAt'] as String)
|
|
: null,
|
|
);
|
|
|
|
bool get isActive => status == 'ACTIVE' || status == 'REWARDED';
|
|
}
|
|
|
|
class PointTransaction {
|
|
final String id;
|
|
final int delta;
|
|
final String type;
|
|
final String? note;
|
|
final DateTime createdAt;
|
|
|
|
const PointTransaction({
|
|
required this.id,
|
|
required this.delta,
|
|
required this.type,
|
|
this.note,
|
|
required this.createdAt,
|
|
});
|
|
|
|
factory PointTransaction.fromJson(Map<String, dynamic> json) => PointTransaction(
|
|
id: json['id'] as String,
|
|
delta: json['delta'] as int? ?? 0,
|
|
type: json['type'] as String? ?? '',
|
|
note: json['note'] as String?,
|
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
|
);
|
|
|
|
bool get isEarned => delta > 0;
|
|
|
|
String get typeLabel {
|
|
switch (type) {
|
|
case 'REFERRAL_FIRST_PAYMENT': return '推荐首次订阅奖励';
|
|
case 'REFERRAL_RECURRING': return '推荐续订奖励';
|
|
case 'REFERRAL_L2': return '二级推荐奖励';
|
|
case 'REFERRAL_WELCOME': return '加入欢迎积分';
|
|
case 'REDEMPTION_QUOTA': return '兑换使用配额';
|
|
case 'REDEMPTION_UNLOCK': return '兑换智能体解锁';
|
|
case 'ADMIN_GRANT': return '平台赠送';
|
|
case 'EXPIRY': return '积分过期';
|
|
default: return type;
|
|
}
|
|
}
|
|
}
|
|
|
|
class RewardItem {
|
|
final String id;
|
|
final int amountCents;
|
|
final String amountFormatted;
|
|
final String rewardType;
|
|
final String triggerType;
|
|
final String status; // PENDING | APPLIED | EXPIRED
|
|
final String? sourceInvoiceId;
|
|
final int? recurringMonth;
|
|
final DateTime createdAt;
|
|
final DateTime? appliedAt;
|
|
|
|
const RewardItem({
|
|
required this.id,
|
|
required this.amountCents,
|
|
required this.amountFormatted,
|
|
required this.rewardType,
|
|
required this.triggerType,
|
|
required this.status,
|
|
this.sourceInvoiceId,
|
|
this.recurringMonth,
|
|
required this.createdAt,
|
|
this.appliedAt,
|
|
});
|
|
|
|
factory RewardItem.fromJson(Map<String, dynamic> json) => RewardItem(
|
|
id: json['id'] as String,
|
|
amountCents: json['amountCents'] as int? ?? 0,
|
|
amountFormatted: json['amountFormatted'] as String? ?? '\$0.00',
|
|
rewardType: json['rewardType'] as String? ?? 'CREDIT',
|
|
triggerType: json['triggerType'] as String? ?? 'FIRST_PAYMENT',
|
|
status: json['status'] as String? ?? 'PENDING',
|
|
sourceInvoiceId: json['sourceInvoiceId'] as String?,
|
|
recurringMonth: json['recurringMonth'] as int?,
|
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
|
appliedAt: json['appliedAt'] != null
|
|
? DateTime.parse(json['appliedAt'] as String)
|
|
: null,
|
|
);
|
|
|
|
String get triggerLabel =>
|
|
triggerType == 'FIRST_PAYMENT' ? '首次付款奖励' : '续订奖励(第${recurringMonth ?? 1}月)';
|
|
}
|