初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
import 'package:hgdj/hj_model/drama_media_info.dart';
import 'package:hgdj/hj_model/media_content.dart';
/// 一条短剧观看记录:续播位置 + 列表展示用的剧信息快照。本地记录,不走后端,见 [DramaResumeStore]。
/// 落库时整条存成 model_data,所以 [mediaId] 必须写进 json——
/// 基类只回 model_data 一列,读盘时全靠它认领是哪部剧
class DramaResume {
final String mediaId; //哪部剧,同时是行主键
final String contentId; //上次看的分集。定位只认它,别再存「第几集」——分页深度靠边翻边找就够了
final int progressSeconds; //看到第几秒
/// 剧信息快照(封面/剧名/集数)。续播定位用不到,是历史记录页拿它画卡片——
/// 只存 id 的话列表除了 id 什么都渲染不出来
final DramaMediaInfo? drama;
/// 本条写入时刻(毫秒)。历史记录页按它倒序,不看 db 的 create_time——
/// 老数据没这个字段,读出来是 0,排在最后
final int watchedAt;
const DramaResume({
required this.mediaId,
required this.contentId,
required this.progressSeconds,
this.drama,
this.watchedAt = 0,
});
DramaResume.fromJson(dynamic json)
: mediaId = json?['mediaId'] ?? '',
contentId = json?['contentId'] ?? '',
progressSeconds = json?['progressSeconds'] ?? 0,
drama = json?['drama'] == null
? null
: DramaMediaInfo.fromJson(json['drama']),
watchedAt = json?['watchedAt'] ?? 0;
Map<String, dynamic> toJson() => {
'mediaId': mediaId,
'contentId': contentId,
'progressSeconds': progressSeconds,
'drama': drama?.toJson(),
'watchedAt': watchedAt,
};
}
/// 付费墙:文案/价格/余额全部服务端下发,前端不自己拼
class DramaPaywall {
String? checkoutContextId; //本次付费墙上下文,充值/开卡归因和下单都要带
String? title;
int? coinBalance;
int? unlockCoin;
String? coinButtonText;
String? cardButtonText;
DramaPaywall.fromJson(dynamic json) {
json ??= {};
checkoutContextId = json['checkoutContextId'];
title = json['title'];
coinBalance = json['coinBalance'];
unlockCoin = json['unlockCoin'];
coinButtonText = json['coinButtonText'];
cardButtonText = json['cardButtonText'];
}
String get titleUI => title?.isNotEmpty == true ? title! : '更多精彩解锁即享';
String get coinButtonUI => coinButtonText?.isNotEmpty == true
? coinButtonText!
: '${unlockCoin ?? 0}金币解锁';
String get cardButtonUI =>
cardButtonText?.isNotEmpty == true ? cardButtonText! : '开通短剧卡免费看';
}
/// AI短剧 Feed 的一条:某部剧 + 该剧第一集。整页走 ListBaseModel<DramaFeedItem>
class DramaFeedItem {
DramaMediaInfo? media;
MediaContent? content;
DramaFeedItem.fromJson(dynamic json) {
json ??= {};
//缺 media 就不造空模型,上层按 null 过滤掉这条,别让信息流多出一个没剧名没地址的空位
if (json['media'] != null) media = DramaMediaInfo.fromJson(json['media']);
if (json['content'] != null)
content = MediaContent.fromJson(json['content']);
}
}
/// 短剧专题(热门短剧 Tab
class DramaTopic {
String? topicId;
String? name;
String? topicType;
String? systemKey;
int? sort;
int? workCount;
DramaTopic({
this.topicId,
this.name,
this.topicType,
this.systemKey,
this.sort,
this.workCount,
});
DramaTopic.fromJson(dynamic json) {
json ??= {};
topicId = json['topicId']?.toString();
name = json['name']?.toString();
topicType = json['topicType']?.toString();
systemKey = json['systemKey']?.toString();
sort = json['sort'] is int
? json['sort'] as int
: int.tryParse('${json['sort'] ?? ''}');
workCount = json['workCount'] is int
? json['workCount'] as int
: int.tryParse('${json['workCount'] ?? ''}');
}
}
/// 短剧搜索结果(`/media/search` kind=4
class DramaSearchResult {
List<DramaMediaInfo> list = [];
List<DramaMediaInfo> tagMediaList = [];
String? tagID;
bool hasNext = false;
DramaSearchResult.fromJson(dynamic json) {
json ??= {};
hasNext = json['hasNext'] == true;
tagID = json['tagID']?.toString();
list = (json['list'] as List?)
?.map((e) => DramaMediaInfo.fromJson(e))
.toList() ??
[];
tagMediaList = (json['tagMediaList'] as List?)
?.map((e) => DramaMediaInfo.fromJson(e))
.toList() ??
[];
}
}
/// 单集金币解锁的下单结果
class DramaUnlockResult {
String? orderId;
String? mediaId;
String? contentId;
int? paidCoin;
int? coinBalance;
DramaUnlockResult.fromJson(Map<String, dynamic>? json) {
json ??= {};
orderId = json['orderId'];
mediaId = json['mediaId'];
contentId = json['contentId'];
paidCoin = json['paidCoin'];
coinBalance = json['coinBalance'];
}
}
/// 短剧单集下载授权结果。权益/上下架/次数校验和扣次都在服务端一次做完,
/// 客户端只负责拿地址开下载,不再自己算次数
class DramaDownloadAuth {
String? mediaId;
String? contentId;
int? episodeNumber;
String? name;
String? cover;
String? downloadUrl; // H.264 m3u8,可能是相对地址
String? h265DownloadUrl; // H.265 m3u8,没有时为空串
int? mediaSize; // 字节
String? expiresAt; // 本次地址失效时间(签发后 6 小时)
int? remainingDownloadCount; // 扣完之后钱包剩余下载次数,直接用它顶掉本地值
bool? charged; // false = 幂等重试命中,没有重复扣次
DramaDownloadAuth.fromJson(Map<String, dynamic>? json) {
json ??= {};
mediaId = json['mediaId'];
contentId = json['contentId'];
episodeNumber = json['episodeNumber'];
name = json['name'];
cover = json['cover'];
downloadUrl = json['downloadUrl'];
h265DownloadUrl = json['h265DownloadUrl'];
mediaSize = json['mediaSize'];
expiresAt = json['expiresAt'];
remainingDownloadCount = json['remainingDownloadCount'];
charged = json['charged'] == true;
}
}