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((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 { @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 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 logout() async { await ref.read(authServiceProvider).logout(); SessionOrgScope.instance.clear(); await _clearPersistedSession(); state = const AuthState(); } /// 清除本地持久化的登录态。 Future _clearPersistedSession() async { await CredentialStorage.instance.clearSession(); } } /// 登录状态提供者。 final authProvider = NotifierProvider( AuthNotifier.new, );