97 lines
3.8 KiB
Dart
97 lines
3.8 KiB
Dart
import 'dart:convert';
|
||
|
||
import 'package:crypto/crypto.dart';
|
||
import 'package:new_learn/core/api/auth/model.dart';
|
||
import 'package:new_learn/core/models/user_model.dart';
|
||
import 'package:new_learn/core/network/dio_client.dart';
|
||
import 'package:new_learn/core/storage/user_storage.dart';
|
||
|
||
/// ============================================================
|
||
/// 【UserApi】用户相关接口(对齐 Vue 端 api/user/index.ts)
|
||
///
|
||
/// 分层职责(像 Vue 项目把接口收敛到 api 目录一样):
|
||
/// Page(UI) → UserApi(业务方法) → DioClient(Dio 封装) → 服务器
|
||
///
|
||
/// 一、已实现:
|
||
/// login → POST /api/v1/User/Login
|
||
/// getCurrentUserExtension → GET /api/v1/User/GetCurrentUserExtension
|
||
/// (登录后拿完整用户信息:昵称/头像/班级/等级等)
|
||
/// getCurrentUser → 统一入口(本地缓存优先)
|
||
/// updateUserAvatar → 更改用户头像
|
||
///
|
||
/// 二、后续继续写(对齐 Vue,用到再加):
|
||
/// checkStudyPackStatus / setChangeUserOnline
|
||
/// getUserPointLevel / UpdateGuideStatus / UpdateReportStatus
|
||
///
|
||
/// 三、高频坑:
|
||
/// 1. 登录密码 MD5 加密(对齐 Vue stringToMd5),提交前在 Api 层完成;
|
||
/// 2. getCurrentUserExtension 必须带 Token 才有效 ——
|
||
/// 登录成功已把 Token 写进 DioClient.accessToken,自动带上;
|
||
/// 3. 不要在这里 catch ApiException —— 交给页面统一处理展示。
|
||
/// ============================================================
|
||
class UserApi {
|
||
// ==================== 登录 ====================
|
||
|
||
/// 登录 → POST /api/v1/User/Login
|
||
/// 密码规则对齐 Vue 端 stringToMd5:MD5 加密后再提交
|
||
static Future<LoginResponse> login({
|
||
required String account,
|
||
required String password,
|
||
}) async {
|
||
final request = LoginRequest(
|
||
account: account.trim(),
|
||
password: _md5(password.trim()),
|
||
);
|
||
final data = await DioClient.instance.post(
|
||
'/api/v1/User/Login',
|
||
data: request.toJson(),
|
||
);
|
||
return LoginResponse.fromJson(data as Map<String, dynamic>);
|
||
}
|
||
|
||
/// 登录后拉取完整用户信息 → GET /api/v1/User/GetCurrentUserExtension
|
||
static Future<CurrentUserExtension> getCurrentUserExtension() async {
|
||
final data = await DioClient.instance.get(
|
||
'/api/v1/User/GetCurrentUserExtension',
|
||
);
|
||
return CurrentUserExtension.fromJson(data as Map<String, dynamic>);
|
||
}
|
||
|
||
/// 获取当前用户(统一入口,缓存优先):
|
||
/// 1. 有本地缓存 → 直接返回(不费流量不转圈);
|
||
/// 2. 无缓存或 forceRefresh → 调 GetCurrentUserExtension 拉最新
|
||
/// (Token 由 Dio 拦截器自动带进 header)并写回缓存;
|
||
/// 3. 接口失败 → 回退返回本地缓存,缓存也没有才抛异常。
|
||
/// 页面展示用户信息一律走这里,别直接调 getCurrentUserExtension。
|
||
static Future<CurrentUserExtension> getCurrentUser({
|
||
bool forceRefresh = false,
|
||
}) async {
|
||
final cached = await UserStorage.instance.readUserInfo();
|
||
if (!forceRefresh && cached != null) return cached;
|
||
|
||
try {
|
||
final ext = await getCurrentUserExtension();
|
||
await UserStorage.instance.saveUserInfo(ext);
|
||
return ext;
|
||
} catch (_) {
|
||
if (cached != null) return cached; // 网络失败回退本地缓存
|
||
rethrow;
|
||
}
|
||
}
|
||
|
||
/// 更改用户头像 → POST /api/v1/User/UpdateUserAvatar
|
||
///
|
||
/// 对齐 UniApp:头像先传 OSS,拿到 url 后再把 {avatar: url} 提交给业务接口。
|
||
static Future<void> updateUserAvatar(String avatar) async {
|
||
await DioClient.instance.post(
|
||
'/api/v1/User/UpdateUserAvatar',
|
||
data: {'avatar': avatar},
|
||
);
|
||
}
|
||
|
||
/// MD5 加密(UniApp stringToMd5 的等价实现)
|
||
static String _md5(String text) {
|
||
return md5.convert(utf8.encode(text)).toString();
|
||
}
|
||
}
|