Files
崔宝新 d5f73cfbb5 refactor(class): 优化接口请求与页面细节
1. 统一处理后端统一包结构,自动解析data字段
2. 新增点赞评论栏显示控制参数,详情页隐藏该栏
3. 简化详情页数据更新逻辑,对齐Vue版本实现
4. 重构确认弹窗组件,优化样式与弹窗尺寸
5. 为评论项添加水平边距,优化列表排版
2026-09-07 16:02:14 +08:00

205 lines
6.6 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:new_learn/core/api/class/model.dart';
import 'package:new_learn/core/network/dio_client.dart';
/// ============================================================
/// 【class 模块接口】班级模块请求 —— 对标 Vue3 端 api/class/index.ts + api/userPoint/index.ts
///
/// ClassApi:班级动态/成员/点赞/评论
/// getUserClassPost → 班级动态分页(游标 pageIndex + prevPageId)
/// getClassUsers → 班级成员在线状态
/// addUserLike → 点赞 / 取消点赞
/// addUserComment → 评论
///
/// UserRankApi:积分排行榜
/// getUserCount → 排行榜总人数(本班级 / 同年级 / 同云校)
/// getRankPage → 排行榜分页(游标分页:pageIndex + prevPageId)
///
/// 注意:页面只调这里,不直接碰 Dio;异常不在这里 catch,交页面统一处理。
/// ============================================================
class ClassApi {
ClassApi._();
static final ClassApi instance = ClassApi._();
/// 班级动态列表(queryType: 0 我的动态 / 1 班级动态)
Future<UserPostResponse> getUserClassPost({
int queryType = 1,
int pageIndex = 1,
int pageSize = 10,
int prevPageId = 0,
}) async {
final data = await DioClient.instance.get(
'/api/v1/UserPost/GetUserClassPost',
query: {
'queryType': queryType,
'pageIndex': pageIndex,
'pageSize': pageSize,
'prevPageId': prevPageId,
},
);
return UserPostResponse.fromJson(data as Map<String, dynamic>);
}
/// 班级成员(在线状态)
Future<List<ClassUser>> getClassUsers() async {
final data = await DioClient.instance.get('/api/v1/User/GetClassUsers');
final list = data is Map<String, dynamic> ? data['data'] : data;
if (list is List) {
return list
.whereType<Map<String, dynamic>>()
.map(ClassUser.fromJson)
.toList();
}
return const [];
}
/// 点赞 / 取消点赞(likeStatus: true 点赞 / false 取消)
Future<void> addUserLike({
required int userPostId,
required bool likeStatus,
}) async {
await DioClient.instance.post('/api/v1/UserLike/AddUserLike', data: {
'userPostId': userPostId,
'likeStatus': likeStatus,
});
}
/// 评论动态
Future<void> addUserComment({
required int userPostId,
required String content,
}) async {
await DioClient.instance.post('/api/v1/UserPost/AddUserCommon', data: {
'userPostId': userPostId,
'content': content,
});
}
/// 我的点赞列表 —— GET /api/v1/UserLike/GetUserLike(游标分页)
///
/// 后端每项包一层 {userPost: {...}},这里统一解包成 UserPostDto,
/// 页面拿到的是与班级动态一致的结构,直接复用 PostCard。
Future<UserPostResponse> getMyLikePage({
int pageIndex = 1,
int pageSize = 10,
int prevPageId = 0,
}) async {
final data = await DioClient.instance.get(
'/api/v1/UserLike/GetUserLike',
query: {
'pageIndex': pageIndex,
'pageSize': pageSize,
'prevPageId': prevPageId,
},
);
return _unwrapUserPostResponse(data);
}
/// 我的留言列表 —— GET /api/v1/UserPost/GetUserCommon(游标分页)
Future<UserPostResponse> getMyCommentPage({
int pageIndex = 1,
int pageSize = 10,
int prevPageId = 0,
}) async {
final data = await DioClient.instance.get(
'/api/v1/UserPost/GetUserCommon',
query: {
'pageIndex': pageIndex,
'pageSize': pageSize,
'prevPageId': prevPageId,
},
);
return _unwrapUserPostResponse(data);
}
/// 动态详情(含全量留言 / 点赞头像)—— GET /api/v1/UserPost/GetUserPostDetail/{id}
///
/// 后端统一包 {code, data} 结构,需取 data 内层再解析(对标 Vue request 拦截器自动解包)。
Future<UserPostDto> getUserPostDetail(int id) async {
final raw = await DioClient.instance.get(
'/api/v1/UserPost/GetUserPostDetail/$id',
);
final data = raw is Map<String, dynamic> && raw['data'] is Map<String, dynamic>
? raw['data'] as Map<String, dynamic>
: raw as Map<String, dynamic>;
return UserPostDto.fromJson(data);
}
/// 删除评论 —— DELETE /api/v1/UserPost/DeleteUserCommon/{id}
Future<void> deleteUserCommon(int commentId) async {
await DioClient.instance.delete(
'/api/v1/UserPost/DeleteUserCommon/$commentId',
);
}
}
/// 解包「我的点赞/留言」列表:每项若包了 userPost 则取内层,再统一转 UserPostDto
UserPostResponse _unwrapUserPostResponse(dynamic data) {
final raw = data is Map<String, dynamic> ? data : {'data': data};
final payload = raw['data'] is Map<String, dynamic>
? raw['data'] as Map<String, dynamic>
: raw;
final list = payload['data'];
final posts = <UserPostDto>[];
if (list is List) {
for (final e in list) {
if (e is! Map<String, dynamic>) continue;
final inner = e['userPost'] is Map<String, dynamic>
? e['userPost'] as Map<String, dynamic>
: e;
posts.add(UserPostDto.fromJson(inner));
}
}
return UserPostResponse(
data: posts,
total: _toInt(payload['total']),
pageIndex: _toInt(payload['pageIndex']),
pageSize: _toInt(payload['pageSize']),
);
}
int _toInt(dynamic v) {
if (v is num) return v.toInt();
if (v is String) return int.tryParse(v) ?? 0;
return 0;
}
/// 积分排行榜接口
class UserRankApi {
UserRankApi._();
static final UserRankApi instance = UserRankApi._();
/// 排行榜总人数(本班级 / 同年级 / 同云校)
Future<UserCountResult> getUserCount() async {
final data = await DioClient.instance.get(
'/api/v1/UserPointsRecord/GetUserCount',
);
return UserCountResult.fromJson(data as Map<String, dynamic>);
}
/// 排行榜分页(游标分页:pageIndex + prevPageId)
/// [userRankType] 0 总榜 / 1 本周 / 2 上周 / 3 今天 / 4 昨天
/// [userClass] 0 本班级 / 1 同年级 / 2 同云校
Future<UserRankPagedList> getRankPage({
required int pageIndex,
required int pageSize,
required int prevPageId,
required int userRankType,
required int userClass,
required bool desc,
}) async {
final data = await DioClient.instance.get(
'/api/v1/UserPointsRecord/GetUserRank',
query: {
'pageIndex': pageIndex,
'pageSize': pageSize,
'prevPageId': prevPageId,
'UserRankType': userRankType,
'UserClass': userClass,
'desc': desc,
},
);
return UserRankPagedList.fromJson(data as Map<String, dynamic>);
}
}