Files
app/lib/services/auth_service.dart
gongyun 1d9e0a831f
Build Android Release / Build Android (push) Failing after 7s
Android Feature Optimization and Updates
2026-05-21 10:31:43 +08:00

290 lines
7.8 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 '../data/models/login_config_model.dart';
import '../data/models/user_model.dart';
import 'api_service.dart';
import '../core/utils/app_logger.dart';
import '../core/exceptions/app_exception.dart';
/// 认证服务
class AuthService {
// 私有化构造函数,防止在外部被直接实例化
AuthService._internal();
// 存储单例的静态私有变量
static final AuthService _instance = AuthService._internal();
// 公开一个 getter 叫 instance (或者使用工厂构造函数)
static AuthService get instance => _instance;
/// 准备登录
Future<Map<String, bool>> prepareLogin(String email) async {
final response = await ApiService.instance.get<Map<String, dynamic>>(
'/session/prepare',
queryParameters: {'email': email},
noAuth: true,
);
return response as Map<String, bool>;
}
/// 获取登录配置(是否需要验证码、是否允许注册等)
Future<LoginConfigModel> getLoginConfig() async {
final response = await ApiService.instance.get<Map<String, dynamic>>(
'/site/config/login',
noAuth: true,
);
final data = response['data'];
if (data is Map<String, dynamic>) {
return LoginConfigModel.fromJson(data);
}
if (data is Map) {
return LoginConfigModel.fromJson(Map<String, dynamic>.from(data));
}
return LoginConfigModel.fromJson(response);
}
/// 获取图形验证码
Future<Map<String, String>> getCaptcha() async {
final response = await ApiService.instance.get<Map<String, dynamic>>(
'/site/captcha',
noAuth: true,
);
final data = response['data'] is Map
? Map<String, dynamic>.from(response['data'] as Map)
: response;
return {
'image': data['image'] as String? ?? '',
'ticket': data['ticket'] as String? ?? '',
};
}
/// 获取站点基础配置,用于判断验证码类型。
Future<Map<String, dynamic>> getBasicSiteConfig() async {
final response = await ApiService.instance.get<Map<String, dynamic>>(
'/site/config/basic',
noAuth: true,
);
AppLogger.d('AuthService -> 站点基础配置响应: $response');
final data = response['data'];
if (data is Map<String, dynamic>) {
return data;
}
if (data is Map) {
return Map<String, dynamic>.from(data);
}
return response;
}
/// 密码登录
Future<LoginResponseModel> passwordLogin({
required String email,
required String password,
String? captcha,
String? ticket,
}) async {
final data = <String, dynamic>{
'email': email,
'password': password,
if (captcha != null && captcha.isNotEmpty) 'captcha': captcha,
if (ticket != null && ticket.isNotEmpty) 'ticket': ticket,
};
final response = await ApiService.instance.post<Map<String, dynamic>>(
'/session/token',
data: data,
noAuth: true,
);
AppLogger.d('AuthService -> 登录响应: $response');
// code 203 表示需要两步验证
final code = response['code'] as int?;
if (code == 203) {
final sessionId = response['data'] as String;
throw TwoFactorRequiredException(sessionId);
}
return LoginResponseModel.fromJson(response);
}
/// 2FA登录
Future<LoginResponseModel> twoFactorLogin({
required String otp,
required String sessionId,
}) async {
final data = <String, dynamic>{'otp': otp, 'session_id': sessionId};
final response = await ApiService.instance.post<Map<String, dynamic>>(
'/session/token/2fa',
data: data,
noAuth: true,
);
return LoginResponseModel.fromJson(response);
}
/// 刷新Token
/// 这个方法现在由 ApiService 调用,传入当前的 refreshToken
Future<TokenModel> refreshToken(String refreshToken) async {
final data = <String, dynamic>{'refresh_token': refreshToken};
final response = await ApiService.instance.post<Map<String, dynamic>>(
'/session/token/refresh',
data: data,
noAuth: true,
);
return TokenModel.fromJson(response);
}
/// 登出
/// 现在由 ServerService 和 AuthProvider 负责清除本地数据
Future<void> logout() async {
try {
// 登出需要调用 API,但 refreshToken 由调用方提供
// 这个方法现在主要用于调用登出 API
await ApiService.instance.delete<void>(
'/session/token',
data: <String, dynamic>{},
noAuth: true,
);
} catch (e) {
// 登出失败也要清除本地数据(由调用方处理)
rethrow;
}
}
/// 获取当前用户信息
Future<UserModel> getCurrentUser() async {
final response = await ApiService.instance.get<Map<String, dynamic>>(
'/user/me',
);
return UserModel.fromJson(response);
}
/// 获取用户容量
Future<CapacityModel> getUserCapacity() async {
final response = await ApiService.instance.get<Map<String, dynamic>>(
'/user/capacity',
);
return CapacityModel.fromJson(response);
}
/// 发送重置密码邮件
Future<void> sendResetPasswordEmail({
required String email,
String? captcha,
String? ticket,
}) async {
final data = <String, dynamic>{
'email': email,
...captcha != null ? {'captcha': captcha} : {},
...ticket != null ? {'ticket': ticket} : {},
};
final response = await ApiService.instance.post<Map<String, dynamic>>(
'/user/reset',
data: data,
noAuth: true,
isNoData: true,
);
final code = response['code'] as int?;
if (code != 0) {
final msg = response['msg'] as String? ?? '发送失败';
throw Exception(msg);
}
}
/// 用户注册
Future<SignUpResponse> signUp({
required String email,
required String password,
String? language,
String? captcha,
String? ticket,
}) async {
final data = <String, dynamic>{
'email': email,
'password': password,
...language != null ? {'language': language} : {},
...captcha != null ? {'captcha': captcha} : {},
...ticket != null ? {'ticket': ticket} : {},
};
final response = await ApiService.instance.post<Map<String, dynamic>>(
'/user',
data: data,
noAuth: true,
);
final code = response['code'] as int?;
final msg = response['msg'] as String?;
if (code != 0 && code != 203) {
throw Exception('注册失败: $msg');
}
return SignUpResponse(
code: code ?? 0,
msg: msg,
requiresEmailActivation: code == 203,
);
}
}
/// 注册响应
class SignUpResponse {
final int code;
final String? msg;
final bool requiresEmailActivation;
SignUpResponse({
required this.code,
this.msg,
required this.requiresEmailActivation,
});
}
/// 登录响应模型
/// 这个模型现在将 token 合并到 user 中返回
class LoginResponseModel {
final UserModel user;
LoginResponseModel({required this.user});
factory LoginResponseModel.fromJson(Map<String, dynamic> json) {
// 检查是否为错误响应(包含 code 和 msg 但没有 data 或 user
final code = json['code'] as int?;
final msg = json['msg'] as String?;
final Map<String, dynamic>? data = json['data'] as Map<String, dynamic>?;
// 如果 code 不是 0,说明是错误响应
if (code != null && code != 0) {
throw Exception(msg ?? '登录失败');
}
// 如果没有 data 且也没有 user,说明是错误响应
if (data == null && json['user'] == null) {
throw Exception(msg ?? '登录失败');
}
final Map<String, dynamic> userData = data ?? json;
final userJson = userData['user'] as Map<String, dynamic>;
// 将 token 合并到 user 中
userJson['token'] = userData['token'];
return LoginResponseModel(user: UserModel.fromJson(userJson));
}
Map<String, dynamic> toJson() {
return {'user': user.toJson()};
}
}