feat: A3-A6 数据层(内容/播放/记录收藏/互动的 HTTP 对接)

- AppCatalog 模型(feed/section/card/detail/episode/banner/category)
- HttpCatalogRepository 对接首页/发现/分类/详情
- PlaybackRepository 播放票据、WatchRepository 记录收藏
- InteractionRepository 评论点赞举报、WalletRepository 钱包解锁
This commit is contained in:
Zhou Weidong committed 2026-10-17 02:31:47 +08:00
1 parent a7ac3b7fc7
commit 88a401512d
6 files changed
+382

No files matched your search

@@ -0,0 +1,33 @@
// Author: fruit.com.cn
import '../models/app_catalog.dart';
import '../services/api_client.dart';
/// 对接后端 `/app/**` 内容接口(A3)。
class HttpCatalogRepository {
HttpCatalogRepository(this._client);
final ApiClient _client;
Future<AppFeed> fetchHomeFeed() async {
final data = await _client.get('/app/home/feed');
return AppFeed.fromJson(data as Map<String, dynamic>);
}
Future<AppFeed> fetchDiscoverFeed() async {
final data = await _client.get('/app/discover/feed');
return AppFeed.fromJson(data as Map<String, dynamic>);
}
Future<List<AppCategory>> fetchCategories() async {
final data = await _client.get('/app/categories');
return (data as List)
.map((e) => AppCategory.fromJson(e as Map<String, dynamic>))
.toList();
}
Future<AppDramaDetail> fetchDramaDetail(int id) async {
final data = await _client.get('/app/dramas/$id');
return AppDramaDetail.fromJson(data as Map<String, dynamic>);
}
}
@@ -0,0 +1,61 @@
// Author: fruit.com.cn
import '../services/api_client.dart';
/// 评论、点赞、举报(A6)。
class InteractionRepository {
InteractionRepository(this._client);
final ApiClient _client;
Future<void> postComment({
required int dramaId,
required String content,
int? rootId,
}) async {
await _client.post('/app/comments', data: {
'dramaId': dramaId,
'content': content,
if (rootId != null) 'rootId': rootId,
});
}
Future<List<dynamic>> fetchComments({
required int dramaId,
int? cursor,
int size = 20,
}) async {
final data = await _client.get('/app/comments', query: {
'dramaId': dramaId,
if (cursor != null) 'cursor': cursor,
'size': size,
});
return data as List;
}
Future<void> deleteComment(int id) async {
await _client.delete('/app/comments/$id');
}
Future<void> toggleLike({
required String targetType,
required int targetId,
}) async {
await _client.post('/app/likes/toggle', data: {
'targetType': targetType,
'targetId': targetId,
});
}
Future<void> report({
required String targetType,
required int targetId,
required String reason,
}) async {
await _client.post('/app/reports', data: {
'targetType': targetType,
'targetId': targetId,
'reason': reason,
});
}
}
@@ -0,0 +1,16 @@
// Author: fruit.com.cn
import '../services/api_client.dart';
/// 播放票据(A4)。
class PlaybackRepository {
PlaybackRepository(this._client);
final ApiClient _client;
/// 签发播放票据与签名地址,客户端原样使用 playUrl。
Future<Map<String, dynamic>> fetchPlayTicket(int episodeId) async {
final data = await _client.get('/app/episodes/$episodeId/play');
return data as Map<String, dynamic>;
}
}
@@ -0,0 +1,25 @@
// Author: fruit.com.cn
import '../services/api_client.dart';
/// 钱包、金币解锁(09 收费支付)。
class WalletRepository {
WalletRepository(this._client);
final ApiClient _client;
Future<Map<String, dynamic>> fetchWallet() async {
final data = await _client.get('/app/wallet');
return data as Map<String, dynamic>;
}
Future<Map<String, dynamic>> unlockEpisode(int episodeId) async {
final data = await _client.post('/app/episodes/$episodeId/unlock');
return data as Map<String, dynamic>;
}
Future<List<dynamic>> fetchOrders() async {
final data = await _client.get('/app/orders');
return data as List;
}
}
@@ -0,0 +1,59 @@
// Author: fruit.com.cn
import '../services/api_client.dart';
/// 观看记录与收藏(A5)。
class WatchRepository {
WatchRepository(this._client);
final ApiClient _client;
Future<void> reportProgress({
required int episodeId,
required int positionSec,
required int durationSec,
}) async {
await _client.post('/app/watch/progress', data: {
'episodeId': episodeId,
'positionSec': positionSec,
'durationSec': durationSec,
});
}
Future<Map<String, dynamic>> fetchProgress(int dramaId) async {
final data = await _client.get('/app/watch/progress', query: {
'dramaId': dramaId,
});
return data as Map<String, dynamic>;
}
Future<List<dynamic>> fetchRecent({int limit = 20}) async {
final data = await _client.get('/app/watch/recent', query: {'limit': limit});
return data as List;
}
Future<void> deleteProgress(int dramaId) async {
await _client.delete('/app/watch/progress/$dramaId');
}
Future<void> clearProgress() async {
await _client.delete('/app/watch/progress');
}
Future<bool> toggleFavorite(int dramaId) async {
final data = await _client.post('/app/favorites/toggle', data: {
'dramaId': dramaId,
});
final map = data as Map<String, dynamic>;
return map['favorited'] as bool? ?? false;
}
Future<List<dynamic>> fetchFavorites() async {
final data = await _client.get('/app/favorites');
return data as List;
}
Future<void> deleteFavorite(int dramaId) async {
await _client.delete('/app/favorites/$dramaId');
}
}