初始化
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hgdj/hj_model/drama/drama_models.dart';
|
||||
import 'package:hgdj/hj_model/drama_media_info.dart';
|
||||
import 'package:hgdj/hj_model/list_base_model.dart';
|
||||
import 'package:hgdj/hj_model/media_content.dart';
|
||||
import 'package:hgdj/tools_base/net/base_resp_bean.dart';
|
||||
import 'package:hgdj/tools_base/net/http_manager.dart';
|
||||
|
||||
/// 短剧接口。金币解锁已并入 PayManager/BuyService,这里只剩读操作 + 两个计数型写操作:
|
||||
/// 信息流翻页去重、分享互动计数,都必须带 `X-Request-ID`——同一次业务的重试要复用同一个 id,
|
||||
/// 换新 id 就会重复计数
|
||||
class DramaService {
|
||||
static Options _idempotent(String requestId) =>
|
||||
Options(headers: {'X-Request-ID': requestId});
|
||||
|
||||
/// AI短剧信息流:一条 = 一部剧 + 该剧第 1 集。
|
||||
/// 排序、去重、已看沉底全在服务端,客户端不得二次排序,也不分页页码——每次请求都是下一批
|
||||
static Future<ListBaseModel<DramaFeedItem>?> fetchFeed(
|
||||
{int size = 20, String? requestId}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/drama/feed',
|
||||
param: {'pageSize': size},
|
||||
options: requestId == null ? null : _idempotent(requestId),
|
||||
jsonTransformation: (json) => ListBaseModel<DramaFeedItem>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 短剧专题列表(热门短剧 Tab,按 sort 倒序已由服务端排好)
|
||||
static Future<List<DramaTopic>?> fetchTopics() async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/drama/topics',
|
||||
jsonTransformation: (json) =>
|
||||
(json['list'] as List?)
|
||||
?.map((e) => DramaTopic.fromJson(e))
|
||||
.toList() ??
|
||||
<DramaTopic>[],
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 专题作品列表(热门短剧橱窗)
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchTopicWorks(
|
||||
String topicId, {
|
||||
int page = 1,
|
||||
int size = 20,
|
||||
}) async {
|
||||
if (topicId.isEmpty) return null;
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/drama/topic/works',
|
||||
param: {
|
||||
'topicId': topicId,
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
},
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 搜索短剧:`GET /media/search`,[kind] 固定 4
|
||||
/// [sortType] 1 最新上架、2 最多观看;默认 1
|
||||
static Future<DramaSearchResult?> search({
|
||||
required String keyword,
|
||||
int page = 1,
|
||||
int size = 20,
|
||||
int sortType = 1,
|
||||
}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/search',
|
||||
param: {
|
||||
'keyword': keyword,
|
||||
'kind': 4,
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
'sortType': sortType,
|
||||
},
|
||||
jsonTransformation: (json) => DramaSearchResult.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 短剧详情(剧信息 + resume 续播位置,不含分集)
|
||||
static Future<DramaMediaInfo?> fetchDetail(String? dramaId) async {
|
||||
if (dramaId?.isNotEmpty != true) return null;
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/info',
|
||||
param: {'id': dramaId},
|
||||
jsonTransformation: (json) => DramaMediaInfo.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 短剧片库搜索:标签页用它按标签取剧,走的是和 ACG 片库同一个接口。
|
||||
/// [tagIds] 空/不传 = 不按标签筛;[sortType] 1热门推荐 2最新上架 4最多收藏
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> searchLibrary({
|
||||
int page = 1,
|
||||
int size = 20,
|
||||
List<String>? tagIds,
|
||||
int? sortType,
|
||||
}) async {
|
||||
final param = <String, dynamic>{
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
'mediaType': 'drama',
|
||||
'sortType': sortType,
|
||||
'tagIds': tagIds,
|
||||
}..removeWhere((k, v) => v == null || (v is List && v.isEmpty));
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/media/library/search',
|
||||
param: param,
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 分集列表(选集面板用) [sortType] 0-正序 1-倒序
|
||||
static Future<ListBaseModel<MediaContent>?> fetchEpisodeList(
|
||||
String dramaId, int page, int size) async {
|
||||
if (dramaId.isEmpty) return null; // 与 fetchDetail 一致:没有剧 id 就别发空请求
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media_content/list',
|
||||
param: {
|
||||
'mediaId': dramaId,
|
||||
'pageNumber': page,
|
||||
'pageSize': size,
|
||||
'sortType': 0
|
||||
},
|
||||
jsonTransformation: (json) => ListBaseModel<MediaContent>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 分集详情:播放地址 + canPlay + 未解锁时的 paywall。
|
||||
/// 列表里的播放地址会过期,每次起播都得重新拉这个接口,不能跨时长缓存
|
||||
static Future<MediaContent?> fetchEpisode(String? contentId) async {
|
||||
if (contentId?.isNotEmpty != true) return null;
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media_content/info',
|
||||
param: {'id': contentId},
|
||||
jsonTransformation: (json) => MediaContent.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 我的喜欢-短剧
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchLikes(int page,
|
||||
{int size = 20}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/mine/like',
|
||||
param: {'likeType': 'drama', 'pageNumber': page, 'pageSize': size},
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 我的收藏-短剧 [sortType] 0-最新收藏 1-最近更新
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchFavorites(int page,
|
||||
{int size = 20, int sortType = 0}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media_bookshelf/list',
|
||||
param: {
|
||||
'type': 'drama',
|
||||
'sortType': sortType,
|
||||
'pageNumber': page,
|
||||
'pageSize': size
|
||||
},
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 我的已购-短剧(按剧去重,点进去看每集的 hasBuy)
|
||||
static Future<ListBaseModel<DramaMediaInfo>?> fetchPurchased(int page,
|
||||
{int size = 20}) async {
|
||||
final result = await httpManager.fetchResponseByGET(
|
||||
'/media/my_buy',
|
||||
param: {'mediaType': 'drama', 'pageNumber': page, 'pageSize': size},
|
||||
jsonTransformation: (json) =>
|
||||
ListBaseModel<DramaMediaInfo>.fromJson(json),
|
||||
);
|
||||
return result.isSuccess ? result.data : null;
|
||||
}
|
||||
|
||||
/// 短剧单集下载授权:**下载链路唯一入口**。
|
||||
/// 登录态、上下架、短剧权益、钱包下载次数校验 + 扣 1 次 + 幂等,服务端一次做完并回下载地址,
|
||||
/// 前端不要再调 `/mine/privilege/consume` 或 `/mine/download/use`——那两个只扣次不给资源,会重复扣。
|
||||
/// [requestId] 同一次下载(含超时重试、刷新过期地址)必须复用,换新的会再扣一次;
|
||||
/// 返回整个 [BaseRespBean]:失败要按 code 分流引导,不能只看有没有数据
|
||||
static Future<BaseRespBean> authorizeDownload({
|
||||
required String mediaId,
|
||||
required String contentId,
|
||||
required String requestId,
|
||||
}) {
|
||||
return httpManager.fetchResponseByPOST(
|
||||
'/media/drama/download/authorize',
|
||||
param: {'mediaId': mediaId, 'contentId': contentId},
|
||||
options: _idempotent(requestId),
|
||||
jsonTransformation: (json) => DramaDownloadAuth.fromJson(json),
|
||||
);
|
||||
}
|
||||
|
||||
/// 分享落地:服务端按 [eventId] 幂等累计分享互动数,返回二维码 PNG 的 base64
|
||||
static Future<String?> shareOutput({
|
||||
required String content,
|
||||
required String? mediaId,
|
||||
String? contentId,
|
||||
required String eventId,
|
||||
}) async {
|
||||
final result = await httpManager.fetchResponseByPOST(
|
||||
'/share/output',
|
||||
param: {
|
||||
'content': content,
|
||||
'objType': 'drama',
|
||||
'mediaID': mediaId,
|
||||
'contentID': contentId ?? '',
|
||||
'eventId': eventId,
|
||||
},
|
||||
options: _idempotent(eventId),
|
||||
);
|
||||
return result.isSuccess
|
||||
? (result.data is Map ? result.data['qrCode'] : null)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user