/// ============================================================ /// 【class 模块模型】班级模块实体 —— 对标 Vue3 api/class/type.ts + api/userPoint/type.ts /// /// 内容(合并自原 features/class/models/): /// 班级动态:UserPostCommonDto / UserPostDto / UserPostResponse / ClassUser /// 积分排行:UserRank / UserRankPagedList / UserCountResult /// /// 序列化:手动 fromJson(响应壳 {code,message,data:{...}} 先解包 data,再逐字段取), /// 不依赖 build_runner。数字用 _toInt 兜底字符串数字。 /// /// 说明:UserPostDto 提供 copyWith(点赞/评论乐观更新需要在列表内替换单条)。 /// ============================================================ library; // ============ 班级动态 ============ class UserPostCommonDto { const UserPostCommonDto({ this.userId = 0, this.userName = '', this.content = '', this.createTime = '', this.id = 0, this.avatar = '', }); final int userId; final String userName; final String content; final String createTime; final int id; final String avatar; factory UserPostCommonDto.fromJson(Map json) => UserPostCommonDto( userId: _toInt(json['userId']), userName: json['userName'] as String? ?? '', content: json['content'] as String? ?? '', createTime: json['createTime'] as String? ?? '', id: _toInt(json['id']), // 详情接口的留言项带评论者头像 avatar: json['avatar'] as String? ?? '', ); } class UserPostDto { const UserPostDto({ this.userId = 0, this.userName = '', this.avatar = '', this.classId = 0, this.content = '', this.likeCount = 0, this.replyCount = 0, this.subjectName = '', this.createTime = '', this.userLikeStatus = false, this.commons = const [], this.id = 0, this.urls = const [], this.type = 0, // 1 攻克任务 2 试题笔记 3 讲义笔记 this.userLikeAvatars = const [], }); final int userId; final String userName; final String avatar; final int classId; final String content; final int likeCount; final int replyCount; final String subjectName; final String createTime; final bool userLikeStatus; final List commons; final int id; final List urls; final int type; final List userLikeAvatars; factory UserPostDto.fromJson(Map json) => UserPostDto( userId: _toInt(json['userId']), userName: json['userName'] as String? ?? '', avatar: json['avatar'] as String? ?? '', classId: _toInt(json['classId']), content: json['content'] as String? ?? '', likeCount: _toInt(json['likeCount']), replyCount: _toInt(json['replyCount']), subjectName: json['subjectName'] as String? ?? '', createTime: json['createTime'] as String? ?? '', userLikeStatus: json['userLikeStatus'] as bool? ?? false, commons: (json['commons'] as List? ?? const []) .whereType>() .map(UserPostCommonDto.fromJson) .toList(), id: _toInt(json['id']), urls: (json['urls'] as List? ?? const []) .whereType() .toList(), type: _toInt(json['type']), userLikeAvatars: (json['userLikeAvatars'] as List? ?? const []) .whereType() .toList(), ); UserPostDto copyWith({ int? userId, String? userName, String? avatar, int? classId, String? content, int? likeCount, int? replyCount, String? subjectName, String? createTime, bool? userLikeStatus, List? commons, int? id, List? urls, int? type, List? userLikeAvatars, }) { return UserPostDto( userId: userId ?? this.userId, userName: userName ?? this.userName, avatar: avatar ?? this.avatar, classId: classId ?? this.classId, content: content ?? this.content, likeCount: likeCount ?? this.likeCount, replyCount: replyCount ?? this.replyCount, subjectName: subjectName ?? this.subjectName, createTime: createTime ?? this.createTime, userLikeStatus: userLikeStatus ?? this.userLikeStatus, commons: commons ?? this.commons, id: id ?? this.id, urls: urls ?? this.urls, type: type ?? this.type, userLikeAvatars: userLikeAvatars ?? this.userLikeAvatars, ); } } /// 班级动态分页响应 /// /// 响应壳:{code, message, data: {data: UserPostDto[], total, ...}} /// 外层 data 是分页对象,内层 data 是动态数组。 class UserPostResponse { const UserPostResponse({ this.data = const [], this.total = 0, this.pageIndex = 1, this.pageSize = 10, this.id = 0, }); final List data; final int total; final int pageIndex; final int pageSize; final int id; factory UserPostResponse.fromJson(Map json) { final payload = json['data'] is Map ? json['data'] as Map : json; final list = payload['data']; return UserPostResponse( data: list is List ? list .whereType>() .map(UserPostDto.fromJson) .toList() : const [], total: _toInt(payload['total']), pageIndex: _toInt(payload['pageIndex']), pageSize: _toInt(payload['pageSize']), id: _toInt(payload['id']), ); } } class ClassUser { const ClassUser({ this.userId = 0, this.userName = '', this.avatar = '', this.isOnline = false, }); final int userId; final String userName; final String avatar; final bool isOnline; factory ClassUser.fromJson(Map json) => ClassUser( userId: _toInt(json['userId']), userName: json['userName'] as String? ?? '', avatar: json['avatar'] as String? ?? '', isOnline: json['isOnline'] as bool? ?? false, ); } // ============ 积分排行 ============ class UserRank { const UserRank({ this.userName = '', this.userId = 0, this.userAvatar = '', this.userClassName = '', this.rank = 0, this.points = 0, }); final String userName; final int userId; final String userAvatar; final String userClassName; final int rank; final double points; factory UserRank.fromJson(Map json) => UserRank( userName: json['userName'] as String? ?? '', userId: _toInt(json['userId']), userAvatar: json['userAvatar'] as String? ?? '', userClassName: json['userClassName'] as String? ?? '', rank: _toInt(json['rank']), points: _toDouble(json['points']), ); } class UserRankPagedList { const UserRankPagedList({ this.myRank, this.userRanks = const [], this.total = 0, this.pageIndex = 1, this.pageSize = 20, }); final UserRank? myRank; final List userRanks; final int total; final int pageIndex; final int pageSize; factory UserRankPagedList.fromJson(Map json) { final payload = json['data'] is Map ? json['data'] as Map : json; // 兼容 userRanks / data 两种返回字段 final ranksList = payload['userRanks'] is List ? payload['userRanks'] as List : (payload['data'] is List ? payload['data'] as List : const []); return UserRankPagedList( myRank: payload['myRank'] is Map ? UserRank.fromJson(payload['myRank'] as Map) : null, userRanks: ranksList .whereType>() .map(UserRank.fromJson) .toList(), total: _toInt(payload['total']), pageIndex: _toInt(payload['pageIndex']), pageSize: _toInt(payload['pageSize']), ); } } class UserCountResult { const UserCountResult({ this.classUserCount, this.gradeUserCount, this.cloudSchoolUserCount, }); final int? classUserCount; final int? gradeUserCount; final int? cloudSchoolUserCount; factory UserCountResult.fromJson(Map json) { final payload = json['data'] is Map ? json['data'] as Map : json; return UserCountResult( classUserCount: _toIntOrNull(payload['classUserCount']), gradeUserCount: _toIntOrNull(payload['gradeUserCount']), cloudSchoolUserCount: _toIntOrNull(payload['cloudSchoolUserCount']), ); } } // ============ 类型转换工具 ============ int _toInt(dynamic v) { if (v is num) return v.toInt(); if (v is String) return int.tryParse(v) ?? 0; return 0; } double _toDouble(dynamic v) { if (v is num) return v.toDouble(); if (v is String) return double.tryParse(v) ?? 0; return 0; } int? _toIntOrNull(dynamic v) { if (v == null) return null; if (v is num) return v.toInt(); if (v is String) return int.tryParse(v); return null; }