90 lines
2.7 KiB
Dart
90 lines
2.7 KiB
Dart
/// 券数据模型 — 对应后端 GET /api/v1/coupons 返回结构
|
||
class CouponModel {
|
||
final String id;
|
||
final String issuerId;
|
||
final String name;
|
||
final String? description;
|
||
final String? imageUrl;
|
||
final double faceValue;
|
||
final double currentPrice;
|
||
final double? issuePrice;
|
||
final int totalSupply;
|
||
final int remainingSupply;
|
||
final String category;
|
||
final String status;
|
||
final String couponType;
|
||
final DateTime expiryDate;
|
||
final bool isTransferable;
|
||
final int resaleCount;
|
||
final int maxResaleCount;
|
||
|
||
// Joined issuer fields
|
||
final String? brandName;
|
||
final String? brandLogoUrl;
|
||
final String? creditRating;
|
||
|
||
const CouponModel({
|
||
required this.id,
|
||
required this.issuerId,
|
||
required this.name,
|
||
this.description,
|
||
this.imageUrl,
|
||
required this.faceValue,
|
||
required this.currentPrice,
|
||
this.issuePrice,
|
||
required this.totalSupply,
|
||
required this.remainingSupply,
|
||
required this.category,
|
||
required this.status,
|
||
required this.couponType,
|
||
required this.expiryDate,
|
||
required this.isTransferable,
|
||
this.resaleCount = 0,
|
||
this.maxResaleCount = 3,
|
||
this.brandName,
|
||
this.brandLogoUrl,
|
||
this.creditRating,
|
||
});
|
||
|
||
factory CouponModel.fromJson(Map<String, dynamic> json) {
|
||
final issuer = json['issuer'] as Map<String, dynamic>?;
|
||
return CouponModel(
|
||
id: json['id'] ?? '',
|
||
issuerId: json['issuerId'] ?? '',
|
||
name: json['name'] ?? '',
|
||
description: json['description'],
|
||
imageUrl: json['imageUrl'],
|
||
faceValue: _toDouble(json['faceValue']),
|
||
currentPrice: _toDouble(json['currentPrice']),
|
||
issuePrice: json['issuePrice'] != null ? _toDouble(json['issuePrice']) : null,
|
||
totalSupply: json['totalSupply'] ?? 0,
|
||
remainingSupply: json['remainingSupply'] ?? 0,
|
||
category: json['category'] ?? '',
|
||
status: json['status'] ?? '',
|
||
couponType: json['couponType'] ?? 'utility',
|
||
expiryDate: DateTime.tryParse(json['expiryDate']?.toString() ?? '') ?? DateTime.now(),
|
||
isTransferable: json['isTransferable'] ?? true,
|
||
resaleCount: json['resaleCount'] ?? 0,
|
||
maxResaleCount: json['maxResaleCount'] ?? 3,
|
||
brandName: issuer?['companyName'],
|
||
brandLogoUrl: issuer?['logoUrl'],
|
||
creditRating: issuer?['creditRating'],
|
||
);
|
||
}
|
||
|
||
/// 折扣率 (0~1)
|
||
double get discount => faceValue > 0 ? 1 - currentPrice / faceValue : 0;
|
||
|
||
/// 是否即将过期(7天内)
|
||
bool get isExpiringSoon => expiryDate.difference(DateTime.now()).inDays <= 7;
|
||
|
||
/// 是否已过期
|
||
bool get isExpired => expiryDate.isBefore(DateTime.now());
|
||
|
||
static double _toDouble(dynamic value) {
|
||
if (value == null) return 0;
|
||
if (value is num) return value.toDouble();
|
||
return double.tryParse(value.toString()) ?? 0;
|
||
}
|
||
}
|