it0/it0_app/lib/features/referral/domain/models/referral_info.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}月)';
}