120 lines
3.5 KiB
Dart
120 lines
3.5 KiB
Dart
import 'package:dio/dio.dart';
|
||
|
||
import '../core/network/api_client.dart';
|
||
import '../core/network/endpoints.dart';
|
||
import '../models/user_model.dart';
|
||
import '../models/user_profile.dart';
|
||
import '../models/video_item.dart';
|
||
|
||
/// 用户服务(《03-接口文档》第 4 章 + 6.6/6.7)
|
||
class UserService {
|
||
UserService._();
|
||
|
||
/// 4.1 获取个人信息
|
||
static Future<UserModel> fetchMe() async {
|
||
final data = await ApiClient.get<Map<String, dynamic>>(Endpoints.me);
|
||
return UserModel.fromJson(data);
|
||
}
|
||
|
||
/// 4.2 修改资料(返回更新后的完整用户对象)
|
||
static Future<UserModel> updateMe({
|
||
String? nickname,
|
||
String? bio,
|
||
int? gender,
|
||
}) async {
|
||
final data = await ApiClient.put<Map<String, dynamic>>(
|
||
Endpoints.me,
|
||
data: {
|
||
if (nickname != null) 'nickname': nickname,
|
||
if (bio != null) 'bio': bio,
|
||
if (gender != null) 'gender': gender,
|
||
},
|
||
);
|
||
return UserModel.fromJson(data);
|
||
}
|
||
|
||
/// 4.3 上传头像(multipart,字段名 file;仅支持 jpg/png,≤5MB)
|
||
static Future<String> uploadAvatar({
|
||
required String filePath,
|
||
required String fileName,
|
||
}) async {
|
||
final data = await ApiClient.post<Map<String, dynamic>>(
|
||
Endpoints.meAvatar,
|
||
data: FormData.fromMap({
|
||
'file': await MultipartFile.fromFile(filePath, filename: fileName),
|
||
}),
|
||
);
|
||
return (data['avatar_url'] as String?) ?? '';
|
||
}
|
||
|
||
/// 4.7 用户公开主页(游客可)
|
||
static Future<UserPublicProfile> fetchPublicProfile(int userId) async {
|
||
final data =
|
||
await ApiClient.get<Map<String, dynamic>>(Endpoints.userDetail(userId));
|
||
return UserPublicProfile.fromJson(data);
|
||
}
|
||
|
||
/// 4.8 他人作品列表(仅已发布)
|
||
static Future<PagedVideos> fetchUserVideos(
|
||
int userId, {
|
||
int page = 1,
|
||
int pageSize = 50,
|
||
}) =>
|
||
_fetchVideos(Endpoints.userVideos(userId), page: page, pageSize: pageSize);
|
||
|
||
/// 我的作品(需登录,含各状态)
|
||
static Future<PagedVideos> fetchMyVideos({
|
||
int page = 1,
|
||
int pageSize = 50,
|
||
}) =>
|
||
_fetchVideos(Endpoints.meVideos, page: page, pageSize: pageSize);
|
||
|
||
/// 6.6 我的收藏(需登录)
|
||
static Future<PagedVideos> fetchMyFavorites({
|
||
int page = 1,
|
||
int pageSize = 50,
|
||
}) =>
|
||
_fetchVideos(Endpoints.meFavorites, page: page, pageSize: pageSize);
|
||
|
||
/// 6.7 观看历史(需登录):video + watched_at + cost_type
|
||
static Future<List<HistoryItem>> fetchMyHistory({
|
||
int page = 1,
|
||
int pageSize = 50,
|
||
}) async {
|
||
final data = await ApiClient.get<Map<String, dynamic>>(
|
||
Endpoints.meHistory,
|
||
queryParameters: {'page': page, 'page_size': pageSize},
|
||
);
|
||
return ((data['list'] as List?) ?? const [])
|
||
.whereType<Map<String, dynamic>>()
|
||
.map(HistoryItem.fromJson)
|
||
.toList();
|
||
}
|
||
|
||
static Future<PagedVideos> _fetchVideos(
|
||
String path, {
|
||
required int page,
|
||
required int pageSize,
|
||
}) async {
|
||
final data = await ApiClient.get<Map<String, dynamic>>(
|
||
path,
|
||
queryParameters: {'page': page, 'page_size': pageSize},
|
||
);
|
||
return PagedVideos(
|
||
total: (data['total'] as num?)?.toInt() ?? 0,
|
||
items: ((data['list'] as List?) ?? const [])
|
||
.whereType<Map<String, dynamic>>()
|
||
.map(VideoItem.fromJson)
|
||
.toList(),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 视频分页结果(普通 page/page_size 分页)
|
||
class PagedVideos {
|
||
PagedVideos({required this.total, required this.items});
|
||
|
||
final int total;
|
||
final List<VideoItem> items;
|
||
}
|