Files

167 lines
4.2 KiB
Dart
Raw Permalink Normal View History

2026-04-25 16:36:34 +08:00
class Invite {
final String id;
final String code;
final String salesId;
final String groupId;
final String? link;
final String? qrCodeUrl;
final DateTime? createdAt;
final DateTime? expiresAt;
final int clickCount;
final int scanCount;
final int joinCount;
final String status;
Invite({
required this.id,
required this.code,
required this.salesId,
required this.groupId,
this.link,
this.qrCodeUrl,
this.createdAt,
this.expiresAt,
this.clickCount = 0,
this.scanCount = 0,
this.joinCount = 0,
this.status = 'active',
});
factory Invite.fromJson(Map<String, dynamic> json) {
return Invite(
id: (json['_id'] ?? json['id'] ?? '').toString(),
code: json['code'] ?? '',
salesId: (json['salesId'] ?? '').toString(),
groupId: (json['groupId'] ?? '').toString(),
link: json['link'],
qrCodeUrl: json['qrCodeUrl'],
createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'].toString())
: null,
expiresAt: json['expiresAt'] != null
? DateTime.parse(json['expiresAt'].toString())
: null,
clickCount: json['clickCount'] ?? 0,
scanCount: json['scanCount'] ?? 0,
joinCount: json['joinCount'] ?? 0,
status: json['status'] ?? 'active',
);
}
Map<String, dynamic> toJson() {
return {
'_id': id,
'code': code,
'salesId': salesId,
'groupId': groupId,
'link': link,
'qrCodeUrl': qrCodeUrl,
'createdAt': createdAt?.toIso8601String(),
'expiresAt': expiresAt?.toIso8601String(),
'clickCount': clickCount,
'scanCount': scanCount,
'joinCount': joinCount,
'status': status,
};
}
bool get isExpired =>
expiresAt != null && DateTime.now().isAfter(expiresAt!);
bool get isActive => status == 'active' && !isExpired;
bool get isValid => isActive;
}
/// 单个邀请的统计数据,由 invite.getStats 返回
class InviteStats {
final String code;
final String? link;
final DateTime? createdAt;
final DateTime? expiresAt;
final InviteStatsDetail stats;
final List<AccessLog> recentAccess;
InviteStats({
required this.code,
this.link,
this.createdAt,
this.expiresAt,
required this.stats,
this.recentAccess = const [],
});
factory InviteStats.fromJson(Map<String, dynamic> json) {
return InviteStats(
code: json['code'] ?? '',
link: json['link'],
createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'].toString())
: null,
expiresAt: json['expiresAt'] != null
? DateTime.parse(json['expiresAt'].toString())
: null,
stats: InviteStatsDetail.fromJson(json['stats'] ?? {}),
recentAccess: (json['recentAccess'] as List?)
?.map((e) => AccessLog.fromJson(e))
.toList() ??
[],
);
}
}
class InviteStatsDetail {
final int clicks;
final int scans;
final int joins;
final String conversionRate;
InviteStatsDetail({
this.clicks = 0,
this.scans = 0,
this.joins = 0,
this.conversionRate = '0',
});
factory InviteStatsDetail.fromJson(Map<String, dynamic> json) {
return InviteStatsDetail(
clicks: json['clicks'] ?? 0,
scans: json['scans'] ?? 0,
joins: json['joins'] ?? 0,
conversionRate: json['conversionRate']?.toString() ?? '0',
);
}
double get conversionRateDouble => double.tryParse(conversionRate) ?? 0.0;
}
/// 来自后端的访问日志条目
class AccessLog {
final String? id;
final String inviteCode;
final String? visitorId;
final String accessType;
final DateTime? timestamp;
final String? ipAddress;
AccessLog({
this.id,
required this.inviteCode,
this.visitorId,
required this.accessType,
this.timestamp,
this.ipAddress,
});
factory AccessLog.fromJson(Map<String, dynamic> json) {
return AccessLog(
id: (json['_id'] ?? json['id'])?.toString(),
inviteCode: json['inviteCode'] ?? '',
visitorId: json['visitorId']?.toString(),
accessType: json['accessType'] ?? '',
timestamp: json['timestamp'] != null
? DateTime.parse(json['timestamp'].toString())
: null,
ipAddress: json['ipAddress'],
);
}
}