/// ============================================================ /// 【UserModel】用户相关模型 /// /// CurrentUserExtension:GET /api/v1/User/GetCurrentUserExtension 的响应体, /// 对齐 Vue 端 api/user/type.ts 的 CurrentUserExtension 全部字段(17 个)。 /// /// 一、为什么字段全都要: /// 登录后要缓存这套信息(user_storage.dart 里存 JSON), /// "我的"页 / 任务页都可能用到,缺一个就得多调一次接口。 /// /// 二、高频坑: /// 1. fromJson 全部 ?? 兜底,服务端缺字段/类型漂移不会闪崩; /// 2. 数字统一 (json['x'] as num?)?.toInt(),防 int/double 不一致; /// 3. toJson 与 fromJson 必须成对,否则缓存/回填会丢字段。 /// ============================================================ class CurrentUserExtension { const CurrentUserExtension({ this.userId = 0, this.userName = '', this.userPoints = 0.0, this.levelName = '', this.avatar = '', this.className = '', this.classId = 0, this.gradeLevel = '', this.graduationYear = 0, this.targetConfigId = 0, this.targetConfigName = '', this.userAvatarStatus = 0, this.userAvatarReason = '', this.isShowGuide = false, this.isShowReport = false, this.isShowAfterReport = false, this.id = 0, }); final int userId; final String userName; final double userPoints; final String levelName; final String avatar; final String className; final int classId; final String gradeLevel; final int graduationYear; final int targetConfigId; final String targetConfigName; final int userAvatarStatus; final String userAvatarReason; final bool isShowGuide; final bool isShowReport; /// 是否显示出门测试报告(默认 false,后端置 true 后,用户完成报告查看后置回 false) final bool isShowAfterReport; final int id; /// JSON → 对象(服务器响应) /// /// 两个兼容点(对齐接口真实返回 {code, message, data: {...}}): /// 1. data 包装:data 存在就取 data,直接返回 {userId,...} 也支持; /// 2. 字符串数字:后端 userId/classId 返回的是字符串 /// ("820466383020136"),不能直接 as num,用 _toInt 统一转换。 factory CurrentUserExtension.fromJson(Map json) { final payload = (json['data'] as Map?) ?? json; return CurrentUserExtension( userId: _toInt(payload['userId']), userName: payload['userName'] as String? ?? '', userPoints: _toDouble(payload['userPoints']), levelName: payload['levelName'] as String? ?? '', avatar: payload['avatar'] as String? ?? '', className: payload['className'] as String? ?? '', classId: _toInt(payload['classId']), gradeLevel: payload['gradeLevel'] as String? ?? '', graduationYear: _toInt(payload['graduationYear']), targetConfigId: _toInt(payload['targetConfigId']), targetConfigName: payload['targetConfigName'] as String? ?? '', userAvatarStatus: _toInt(payload['userAvatarStatus']), userAvatarReason: payload['userAvatarReason'] as String? ?? '', isShowGuide: payload['isShowGuide'] as bool? ?? false, isShowReport: payload['isShowReport'] as bool? ?? false, isShowAfterReport: payload['isShowAfterReport'] as bool? ?? false, id: _toInt(payload['id']), ); } /// 对象 → JSON(写入本地缓存) Map toJson() { return { 'userId': userId, 'userName': userName, 'userPoints': userPoints, 'levelName': levelName, 'avatar': avatar, 'className': className, 'classId': classId, 'gradeLevel': gradeLevel, 'graduationYear': graduationYear, 'targetConfigId': targetConfigId, 'targetConfigName': targetConfigName, 'userAvatarStatus': userAvatarStatus, 'userAvatarReason': userAvatarReason, 'isShowGuide': isShowGuide, 'isShowReport': isShowReport, 'isShowAfterReport': isShowAfterReport, 'id': id, }; } /// 局部更新(目前只用得上 avatar / isShowReport / isShowAfterReport) CurrentUserExtension copyWith({ String? avatar, bool? isShowReport, bool? isShowAfterReport, }) { return CurrentUserExtension( userId: userId, userName: userName, userPoints: userPoints, levelName: levelName, avatar: avatar ?? this.avatar, className: className, classId: classId, gradeLevel: gradeLevel, graduationYear: graduationYear, targetConfigId: targetConfigId, targetConfigName: targetConfigName, userAvatarStatus: userAvatarStatus, userAvatarReason: userAvatarReason, isShowGuide: isShowGuide, isShowReport: isShowReport ?? this.isShowReport, isShowAfterReport: isShowAfterReport ?? this.isShowAfterReport, id: id, ); } } /// 用户积分等级(GET /api/v1/User/GetUserPointLevel,备用) class UserPointLevel { const UserPointLevel({ this.userId = 0, this.totalPoints = 0, this.level = 0, this.levelPoints = 0, this.levelMaxPoints = 0, this.levelUpPoints = 0, }); final int userId; final int totalPoints; final int level; final int levelPoints; final int levelMaxPoints; final int levelUpPoints; factory UserPointLevel.fromJson(Map json) { return UserPointLevel( userId: (json['userId'] as num?)?.toInt() ?? 0, totalPoints: (json['totalPoints'] as num?)?.toInt() ?? 0, level: (json['level'] as num?)?.toInt() ?? 0, levelPoints: (json['levelPoints'] as num?)?.toInt() ?? 0, levelMaxPoints: (json['levelMaxPoints'] as num?)?.toInt() ?? 0, levelUpPoints: (json['levelUpPoints'] as num?)?.toInt() ?? 0, ); } } /// 兼容"字符串数字"(后端 userId/classId 返回 "820466383020136") int _toInt(dynamic v) { if (v is num) return v.toInt(); if (v is String) return int.tryParse(v) ?? 0; return 0; } /// 兼容小数积分(后端 userPoints 返回 105.5) double _toDouble(dynamic v) { if (v is num) return v.toDouble(); if (v is String) return double.tryParse(v) ?? 0; return 0; }