马良AI写作初始化仓库

This commit is contained in:
邓滨杰
2025-09-10 00:07:52 +08:00
parent 3c06bb1a03
commit 39c0f8840f
1309 changed files with 318528 additions and 0 deletions

View File

@@ -0,0 +1,429 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import '../../utils/date_time_parser.dart';
part 'subscription_models.g.dart';
/// 订阅计划模型
@JsonSerializable()
class SubscriptionPlan extends Equatable {
final String? id;
final String planName;
final String? description;
final double price;
final String currency;
final BillingCycle billingCycle;
final String? roleId;
final int? creditsGranted;
final bool active;
final bool recommended;
final int priority;
final Map<String, dynamic>? features;
final int trialDays;
final int maxUsers;
final DateTime? createdAt;
final DateTime? updatedAt;
const SubscriptionPlan({
this.id,
required this.planName,
this.description,
required this.price,
required this.currency,
required this.billingCycle,
this.roleId,
this.creditsGranted,
this.active = true,
this.recommended = false,
this.priority = 0,
this.features,
this.trialDays = 0,
this.maxUsers = -1,
this.createdAt,
this.updatedAt,
});
factory SubscriptionPlan.fromJson(Map<String, dynamic> json) {
// 兼容后端字段可能为空或类型不一致(如 BigDecimal 序列化为字符串)
final dynamic priceRaw = json['price'];
final double parsedPrice = priceRaw is num
? priceRaw.toDouble()
: (priceRaw is String ? double.tryParse(priceRaw) ?? 0.0 : 0.0);
final dynamic priorityRaw = json['priority'];
final int parsedPriority = priorityRaw is num
? priorityRaw.toInt()
: (priorityRaw is String ? int.tryParse(priorityRaw) ?? 0 : 0);
final dynamic creditsRaw = json['creditsGranted'];
final int? parsedCredits = creditsRaw == null
? null
: (creditsRaw is num
? creditsRaw.toInt()
: (creditsRaw is String ? int.tryParse(creditsRaw) : null));
final dynamic activeRaw = json['active'];
final bool parsedActive = activeRaw is bool
? activeRaw
: (activeRaw is String ? activeRaw.toLowerCase() == 'true' : true);
final dynamic recommendedRaw = json['recommended'];
final bool parsedRecommended = recommendedRaw is bool
? recommendedRaw
: (recommendedRaw is String ? recommendedRaw.toLowerCase() == 'true' : false);
final featuresRaw = json['features'];
final Map<String, dynamic>? parsedFeatures =
featuresRaw is Map<String, dynamic> ? featuresRaw : null;
return SubscriptionPlan(
id: json['id'] as String?,
planName: (json['planName'] as String?) ?? '未命名套餐',
description: json['description'] as String?,
price: parsedPrice,
currency: (json['currency'] as String?) ?? 'CNY',
billingCycle: _parseBillingCycle(json['billingCycle']),
roleId: json['roleId'] as String?,
creditsGranted: parsedCredits,
active: parsedActive,
recommended: parsedRecommended,
priority: parsedPriority,
features: parsedFeatures,
trialDays: ((json['trialDays'] is String)
? int.tryParse(json['trialDays'])
: (json['trialDays'] as num?))
?.toInt() ?? 0,
maxUsers: ((json['maxUsers'] is String)
? int.tryParse(json['maxUsers'])
: (json['maxUsers'] as num?))
?.toInt() ?? -1,
createdAt: json['createdAt'] != null ? parseBackendDateTime(json['createdAt']) : null,
updatedAt: json['updatedAt'] != null ? parseBackendDateTime(json['updatedAt']) : null,
);
}
Map<String, dynamic> toJson() => _$SubscriptionPlanToJson(this);
@override
List<Object?> get props => [
id,
planName,
description,
price,
currency,
billingCycle,
roleId,
creditsGranted,
active,
recommended,
priority,
features,
trialDays,
maxUsers,
createdAt,
updatedAt,
];
/// 获取月度等价价格
double get monthlyEquivalentPrice {
switch (billingCycle) {
case BillingCycle.monthly:
return price;
case BillingCycle.quarterly:
return price / 3;
case BillingCycle.yearly:
return price / 12;
case BillingCycle.lifetime:
return price / 120; // 假设10年使用期
}
}
/// 获取计费周期显示文本
String get billingCycleText {
switch (billingCycle) {
case BillingCycle.monthly:
return '月付';
case BillingCycle.quarterly:
return '季付';
case BillingCycle.yearly:
return '年付';
case BillingCycle.lifetime:
return '终身';
}
}
/// 获取格式化价格
String get formattedPrice {
return '$currency ${price.toStringAsFixed(2)}';
}
/// 解析BillingCycle枚举
static BillingCycle _parseBillingCycle(dynamic value) {
if (value == null) return BillingCycle.monthly;
final stringValue = value.toString().toUpperCase();
switch (stringValue) {
case 'MONTHLY':
return BillingCycle.monthly;
case 'QUARTERLY':
return BillingCycle.quarterly;
case 'YEARLY':
return BillingCycle.yearly;
case 'LIFETIME':
return BillingCycle.lifetime;
default:
return BillingCycle.monthly;
}
}
/// 创建副本
SubscriptionPlan copyWith({
String? id,
String? planName,
String? description,
double? price,
String? currency,
BillingCycle? billingCycle,
String? roleId,
int? creditsGranted,
bool? active,
bool? recommended,
int? priority,
Map<String, dynamic>? features,
int? trialDays,
int? maxUsers,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return SubscriptionPlan(
id: id ?? this.id,
planName: planName ?? this.planName,
description: description ?? this.description,
price: price ?? this.price,
currency: currency ?? this.currency,
billingCycle: billingCycle ?? this.billingCycle,
roleId: roleId ?? this.roleId,
creditsGranted: creditsGranted ?? this.creditsGranted,
active: active ?? this.active,
recommended: recommended ?? this.recommended,
priority: priority ?? this.priority,
features: features ?? this.features,
trialDays: trialDays ?? this.trialDays,
maxUsers: maxUsers ?? this.maxUsers,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
/// 计费周期枚举
enum BillingCycle {
@JsonValue('MONTHLY')
monthly,
@JsonValue('QUARTERLY')
quarterly,
@JsonValue('YEARLY')
yearly,
@JsonValue('LIFETIME')
lifetime,
}
/// 用户订阅模型
@JsonSerializable()
class UserSubscription extends Equatable {
final String? id;
final String userId;
final String planId;
final DateTime? startDate;
final DateTime? endDate;
final SubscriptionStatus status;
final bool autoRenewal;
final String? paymentMethod;
final String? transactionId;
final int creditsUsed;
final int totalCredits;
final DateTime? canceledAt;
final String? cancelReason;
final DateTime? trialEndDate;
final bool isTrial;
final DateTime? createdAt;
final DateTime? updatedAt;
const UserSubscription({
this.id,
required this.userId,
required this.planId,
this.startDate,
this.endDate,
required this.status,
this.autoRenewal = false,
this.paymentMethod,
this.transactionId,
this.creditsUsed = 0,
this.totalCredits = 0,
this.canceledAt,
this.cancelReason,
this.trialEndDate,
this.isTrial = false,
this.createdAt,
this.updatedAt,
});
factory UserSubscription.fromJson(Map<String, dynamic> json) {
return UserSubscription(
id: json['id'] as String?,
userId: json['userId'] as String,
planId: json['planId'] as String,
startDate: json['startDate'] != null ? parseBackendDateTime(json['startDate']) : null,
endDate: json['endDate'] != null ? parseBackendDateTime(json['endDate']) : null,
status: _parseSubscriptionStatus(json['status']),
autoRenewal: json['autoRenewal'] as bool? ?? false,
paymentMethod: json['paymentMethod'] as String?,
transactionId: json['transactionId'] as String?,
creditsUsed: (json['creditsUsed'] as num?)?.toInt() ?? 0,
totalCredits: (json['totalCredits'] as num?)?.toInt() ?? 0,
canceledAt: json['canceledAt'] != null ? parseBackendDateTime(json['canceledAt']) : null,
cancelReason: json['cancelReason'] as String?,
trialEndDate: json['trialEndDate'] != null ? parseBackendDateTime(json['trialEndDate']) : null,
isTrial: json['isTrial'] as bool? ?? false,
createdAt: json['createdAt'] != null ? parseBackendDateTime(json['createdAt']) : null,
updatedAt: json['updatedAt'] != null ? parseBackendDateTime(json['updatedAt']) : null,
);
}
Map<String, dynamic> toJson() => _$UserSubscriptionToJson(this);
@override
List<Object?> get props => [
id,
userId,
planId,
startDate,
endDate,
status,
autoRenewal,
paymentMethod,
transactionId,
creditsUsed,
totalCredits,
canceledAt,
cancelReason,
trialEndDate,
isTrial,
createdAt,
updatedAt,
];
/// 获取剩余积分
int get remainingCredits => (totalCredits - creditsUsed).clamp(0, totalCredits);
/// 检查订阅是否有效
bool get isValid {
final now = DateTime.now();
return (status == SubscriptionStatus.active || status == SubscriptionStatus.trial) &&
(endDate == null || endDate!.isAfter(now));
}
/// 检查是否即将过期7天内
bool get isExpiringSoon {
if (endDate == null) return false;
final now = DateTime.now();
final sevenDaysLater = now.add(const Duration(days: 7));
return endDate!.isBefore(sevenDaysLater) && endDate!.isAfter(now);
}
/// 解析SubscriptionStatus枚举
static SubscriptionStatus _parseSubscriptionStatus(dynamic value) {
if (value == null) return SubscriptionStatus.active;
final stringValue = value.toString().toUpperCase();
switch (stringValue) {
case 'ACTIVE':
return SubscriptionStatus.active;
case 'TRIAL':
return SubscriptionStatus.trial;
case 'CANCELED':
return SubscriptionStatus.canceled;
case 'EXPIRED':
return SubscriptionStatus.expired;
case 'SUSPENDED':
return SubscriptionStatus.suspended;
case 'REFUNDED':
return SubscriptionStatus.refunded;
default:
return SubscriptionStatus.active;
}
}
/// 获取状态显示文本
String get statusText {
switch (status) {
case SubscriptionStatus.active:
return '活跃';
case SubscriptionStatus.trial:
return '试用期';
case SubscriptionStatus.canceled:
return '已取消';
case SubscriptionStatus.expired:
return '已过期';
case SubscriptionStatus.suspended:
return '暂停';
case SubscriptionStatus.refunded:
return '已退款';
}
}
}
/// 订阅状态枚举
enum SubscriptionStatus {
@JsonValue('ACTIVE')
active,
@JsonValue('TRIAL')
trial,
@JsonValue('CANCELED')
canceled,
@JsonValue('EXPIRED')
expired,
@JsonValue('SUSPENDED')
suspended,
@JsonValue('REFUNDED')
refunded,
}
/// 订阅统计信息
@JsonSerializable()
class SubscriptionStatistics extends Equatable {
final int totalPlans;
final int activePlans;
final int totalSubscriptions;
final int activeSubscriptions;
final int trialSubscriptions;
final double monthlyRevenue;
final double yearlyRevenue;
const SubscriptionStatistics({
required this.totalPlans,
required this.activePlans,
required this.totalSubscriptions,
required this.activeSubscriptions,
required this.trialSubscriptions,
required this.monthlyRevenue,
required this.yearlyRevenue,
});
factory SubscriptionStatistics.fromJson(Map<String, dynamic> json) =>
_$SubscriptionStatisticsFromJson(json);
Map<String, dynamic> toJson() => _$SubscriptionStatisticsToJson(this);
@override
List<Object?> get props => [
totalPlans,
activePlans,
totalSubscriptions,
activeSubscriptions,
trialSubscriptions,
monthlyRevenue,
yearlyRevenue,
];
}