feat: A1 网络层与鉴权 + A2 账号登录对接
- 引入 dio,封装统一 ApiClient(BaseUrl/Token 注入/401 自动刷新/{code,message,data} 解包)
- TokenStorage 令牌持久化,AuthController 登录状态管理
- AuthRepository 封装 otp/login/logout 接口
- 登录页对接手机号/邮箱验证码登录(Google 登录待 OAuth client 配置)
This commit is contained in:
1 parent
015677d1a8
commit
a7ac3b7fc7
9 files changed
+602
-78
No files matched your search
@@ -0,0 +1,91 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'api_client.dart';
|
||||
import 'token_storage.dart';
|
||||
|
||||
/// 登录/刷新成功后返回的会话令牌。
|
||||
class AuthSession {
|
||||
AuthSession({
|
||||
required this.accessToken,
|
||||
required this.refreshToken,
|
||||
required this.expiresIn,
|
||||
required this.newUser,
|
||||
});
|
||||
|
||||
final String accessToken;
|
||||
final String refreshToken;
|
||||
final int expiresIn;
|
||||
final bool newUser;
|
||||
|
||||
factory AuthSession.fromJson(Map<String, dynamic> json) {
|
||||
return AuthSession(
|
||||
accessToken: json['accessToken'] as String,
|
||||
refreshToken: json['refreshToken'] as String,
|
||||
expiresIn: (json['expiresIn'] as num?)?.toInt() ?? 7200,
|
||||
newUser: json['newUser'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 账号与鉴权接口封装。
|
||||
class AuthRepository {
|
||||
AuthRepository(this._client, this._tokenStorage);
|
||||
|
||||
final ApiClient _client;
|
||||
final TokenStorage _tokenStorage;
|
||||
|
||||
/// 发送验证码。
|
||||
Future<void> sendOtp({
|
||||
required String scene,
|
||||
required String targetType,
|
||||
required String target,
|
||||
}) async {
|
||||
await _client.post('/app/auth/otp', data: {
|
||||
'scene': scene,
|
||||
'targetType': targetType,
|
||||
'target': target,
|
||||
});
|
||||
}
|
||||
|
||||
/// 验证码登录。
|
||||
Future<AuthSession> loginWithCode({
|
||||
required String targetType,
|
||||
required String target,
|
||||
required String code,
|
||||
}) async {
|
||||
final data = await _client.post('/app/auth/login', data: {
|
||||
'targetType': targetType,
|
||||
'target': target,
|
||||
'code': code,
|
||||
});
|
||||
return AuthSession.fromJson(data as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
/// 密码登录。
|
||||
Future<AuthSession> loginWithPassword({
|
||||
required String targetType,
|
||||
required String target,
|
||||
required String password,
|
||||
}) async {
|
||||
final data = await _client.post('/app/auth/login', data: {
|
||||
'targetType': targetType,
|
||||
'target': target,
|
||||
'password': password,
|
||||
});
|
||||
return AuthSession.fromJson(data as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
/// 保存会话令牌。
|
||||
Future<void> saveSession(AuthSession session) async {
|
||||
await _tokenStorage.saveTokens(
|
||||
access: session.accessToken,
|
||||
refresh: session.refreshToken,
|
||||
);
|
||||
}
|
||||
|
||||
/// 登出并清理本地令牌。
|
||||
Future<void> logout() async {
|
||||
await _client.post('/app/auth/logout');
|
||||
await _tokenStorage.clear();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user