ops-console v1.0.2 (versionCode 8) 源码入库
This commit is contained in:
158
lib/providers/auth_provider.dart
Normal file
158
lib/providers/auth_provider.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../models/user.dart';
|
||||
import '../services/api_client.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../utils/credential_storage.dart';
|
||||
import '../utils/session_org_scope.dart';
|
||||
|
||||
/// AuthService 提供者。
|
||||
final authServiceProvider = Provider<AuthService>((ref) => AuthService());
|
||||
|
||||
/// 登录状态。
|
||||
class AuthState {
|
||||
const AuthState({
|
||||
this.user,
|
||||
this.isLoading = false,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
/// 当前登录用户;为 null 表示未登录。
|
||||
final User? user;
|
||||
|
||||
/// 是否正在执行登录请求。
|
||||
final bool isLoading;
|
||||
|
||||
/// 最近一次登录失败的错误信息。
|
||||
final String? errorMessage;
|
||||
|
||||
/// 是否已登录。
|
||||
bool get isLoggedIn => user != null;
|
||||
|
||||
AuthState copyWith({
|
||||
User? user,
|
||||
bool? isLoading,
|
||||
String? errorMessage,
|
||||
bool clearUser = false,
|
||||
bool clearError = false,
|
||||
}) {
|
||||
return AuthState(
|
||||
user: clearUser ? null : (user ?? this.user),
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 登录状态管理。
|
||||
class AuthNotifier extends Notifier<AuthState> {
|
||||
@override
|
||||
AuthState build() {
|
||||
// 注册会话失效回调:401 刷新失败 → 清空登录态(含本地持久化),
|
||||
// 路由 redirect 监听本状态,自动跳转登录页。
|
||||
ApiClient.instance.onUnauthorized = () {
|
||||
SessionOrgScope.instance.clear();
|
||||
unawaited(_clearPersistedSession());
|
||||
state = const AuthState();
|
||||
};
|
||||
|
||||
// 尝试从本地存储恢复登录态(免登录)。
|
||||
return _tryRestoreSession();
|
||||
}
|
||||
|
||||
/// 从本地存储恢复登录态。
|
||||
AuthState _tryRestoreSession() {
|
||||
final cs = CredentialStorage.instance;
|
||||
final token = cs.token;
|
||||
final userMap = cs.savedUserMap;
|
||||
|
||||
if (token != null && token.isNotEmpty && userMap != null) {
|
||||
// 恢复 TokenStore(供 ApiInterceptor 使用)。
|
||||
TokenStore.instance.save(
|
||||
token: token,
|
||||
refreshToken: cs.refreshToken,
|
||||
);
|
||||
try {
|
||||
final user = User.fromJson(userMap);
|
||||
// 恢复数据权限范围:仅当登录用户 orgId 非空时收敛。
|
||||
SessionOrgScope.instance.setCurrentOrg(user.orgId);
|
||||
return AuthState(user: user);
|
||||
} catch (_) {
|
||||
// JSON 解析失败,清除残留数据。
|
||||
_clearPersistedSession();
|
||||
}
|
||||
}
|
||||
return const AuthState();
|
||||
}
|
||||
|
||||
/// 执行登录;成功返回 true,失败返回 false 并写入错误信息。
|
||||
///
|
||||
/// [captcha]/[uuid] 仅在后端要求验证码时传值;当前各环境已关闭登录验证码,
|
||||
/// 调用方(登录页)可不传,由 [AuthService.login] 决定是否写入请求体。
|
||||
Future<bool> login({
|
||||
required String account,
|
||||
required String password,
|
||||
String captcha = '',
|
||||
String uuid = '',
|
||||
bool rememberPassword = false,
|
||||
}) async {
|
||||
state = state.copyWith(isLoading: true, clearError: true);
|
||||
try {
|
||||
final user = await ref.read(authServiceProvider).login(
|
||||
account: account,
|
||||
password: password,
|
||||
captcha: captcha,
|
||||
uuid: uuid,
|
||||
);
|
||||
|
||||
// 持久化登录态(token + 用户信息),用于免登录恢复。
|
||||
await CredentialStorage.instance.saveSession(
|
||||
token: user.token,
|
||||
refreshToken: TokenStore.instance.refreshToken,
|
||||
userJson: jsonEncode(user.toJson()),
|
||||
);
|
||||
|
||||
// 记住密码。
|
||||
if (rememberPassword) {
|
||||
await CredentialStorage.instance.saveCredentials(
|
||||
account: account,
|
||||
password: password,
|
||||
);
|
||||
} else {
|
||||
await CredentialStorage.instance.clearCredentials();
|
||||
}
|
||||
|
||||
state = AuthState(user: user, isLoading: false);
|
||||
// 登录成功后写入数据权限范围,供 OrgService 客户端收敛组织/门店数据。
|
||||
SessionOrgScope.instance.setCurrentOrg(user.orgId);
|
||||
return true;
|
||||
} on AuthException catch (e) {
|
||||
state = state.copyWith(isLoading: false, errorMessage: e.message);
|
||||
return false;
|
||||
} catch (_) {
|
||||
state = state.copyWith(isLoading: false, errorMessage: '登录失败,请稍后重试');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 登出并清空登录态。
|
||||
Future<void> logout() async {
|
||||
await ref.read(authServiceProvider).logout();
|
||||
SessionOrgScope.instance.clear();
|
||||
await _clearPersistedSession();
|
||||
state = const AuthState();
|
||||
}
|
||||
|
||||
/// 清除本地持久化的登录态。
|
||||
Future<void> _clearPersistedSession() async {
|
||||
await CredentialStorage.instance.clearSession();
|
||||
}
|
||||
}
|
||||
|
||||
/// 登录状态提供者。
|
||||
final authProvider = NotifierProvider<AuthNotifier, AuthState>(
|
||||
AuthNotifier.new,
|
||||
);
|
||||
Reference in New Issue
Block a user