Files
xianyan/lib/features/auth/providers/auth_provider.dart
Developer 7564e8893d chore: 完成多平台适配与代码优化
此提交包含多项变更:
1. 新增鸿蒙平台支持,完善设备检测与数据库适配
2. 替换旧版分享插件API为SharePlus
3. 批量迁移StateNotifier到Notifier以适配新版Riverpod
4. 修复zip编码判断、图表API参数等bug
5. 更新应用图标、启动页资源与多尺寸适配图标
6. 调整Android最小SDK版本与应用名称
7. 优化日志打印与正则表达式使用
8. 修正编辑器画布样式初始化与配置逻辑
9. 更新依赖与CI插件配置
2026-05-17 07:17:07 +08:00

281 lines
7.9 KiB
Dart
Raw 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.
/// ============================================================
/// 闲言APP — 认证状态管理
/// 创建时间: 2026-04-28
/// 更新时间: 2026-05-15
/// 作用: 管理用户登录状态、Token、用户信息
/// 上次更新: v10.1.0 register增加密保参数; changePassword支持多验证方式
/// ============================================================
import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/network/api_exception.dart';
import '../../../core/services/device/device_info_service.dart';
import '../../../core/storage/app_kv_store.dart';
import '../../../core/storage/secure_storage.dart';
import '../../../core/utils/logger.dart';
import '../models/user_model.dart';
import '../services/auth_service.dart';
class AuthState {
const AuthState({
this.user,
this.isLoading = false,
this.isLoggedIn = false,
this.isInitialized = false,
this.error,
});
final UserModel? user;
final bool isLoading;
final bool isLoggedIn;
final bool isInitialized;
final String? error;
AuthState copyWith({
UserModel? user,
bool clearUser = false,
bool? isLoading,
bool? isLoggedIn,
bool? isInitialized,
String? error,
bool clearError = false,
}) {
return AuthState(
user: clearUser ? null : (user ?? this.user),
isLoading: isLoading ?? this.isLoading,
isLoggedIn: isLoggedIn ?? this.isLoggedIn,
isInitialized: isInitialized ?? this.isInitialized,
error: clearError ? null : (error ?? this.error),
);
}
}
class AuthNotifier extends Notifier<AuthState> {
@override
AuthState build() => _loadInitialState();
static const String _userCacheKey = 'cached_user_info';
AuthNotifier() {
_init();
}
static AuthState _loadInitialState() {
if (!AppKVStore.isReady) return const AuthState();
final cached = AppKVStore.getString(_userCacheKey);
if (cached != null) {
try {
final user = UserModel.fromJson(
jsonDecode(cached) as Map<String, dynamic>,
);
return AuthState(user: user, isLoggedIn: true);
} catch (_) {}
}
return const AuthState();
}
Future<void> _init() async {
final hasToken = await SecureStorage.isLoggedIn;
if (state.user != null && hasToken) {
state = state.copyWith(isInitialized: true);
_validateTokenInBackground();
} else if (state.user != null && !hasToken) {
await AuthService.clearCachedUser();
state = const AuthState(isInitialized: true);
} else if (hasToken) {
state = state.copyWith(isLoading: true);
final success = await AuthService.tryAutoLogin();
if (success) {
try {
final data = await AuthService.getUserInfo();
final user = UserModel.fromJson(data);
await _saveUserCache(user);
state = state.copyWith(
user: user,
isLoggedIn: true,
isLoading: false,
isInitialized: true,
clearError: true,
);
} catch (e) {
Log.w('自动登录后获取用户信息失败: $e');
state = state.copyWith(isLoading: false, isInitialized: true);
}
} else {
state = state.copyWith(
isLoading: false,
isInitialized: true,
clearUser: true,
);
}
} else {
state = state.copyWith(isLoading: false, isInitialized: true);
}
}
Future<void> _validateTokenInBackground() async {
try {
final valid = await AuthService.validateLocalToken();
if (!valid) {
final refreshed = await AuthService.refreshToken();
if (!refreshed) {
await AuthService.clearCachedUser();
state = state.copyWith(isLoggedIn: false, clearUser: true);
Log.i('Token已失效且刷新失败已清除登录状态');
} else {
try {
final data = await AuthService.getUserInfo();
final user = UserModel.fromJson(data);
await _saveUserCache(user);
state = state.copyWith(user: user);
} catch (_) {}
}
} else {
try {
final data = await AuthService.getUserInfo();
final user = UserModel.fromJson(data);
await _saveUserCache(user);
state = state.copyWith(user: user);
} catch (_) {}
}
} catch (e) {
Log.w('后台Token验证失败: $e');
}
}
Future<void> _saveUserCache(UserModel user) async {
try {
await AppKVStore.setString(_userCacheKey, jsonEncode(user.toJson()));
} catch (e) {
Log.w('保存用户缓存失败: $e');
}
}
Future<bool> login({
required String account,
required String password,
}) async {
state = state.copyWith(isLoading: true, clearError: true);
try {
final user = await AuthService.login(
account: account,
password: password,
);
await _saveUserCache(user);
state = state.copyWith(user: user, isLoggedIn: true, isLoading: false);
DeviceInfoService.registerDeviceIfNeeded();
return true;
} on ApiException catch (e) {
state = state.copyWith(isLoading: false, error: e.message);
return false;
} catch (e) {
state = state.copyWith(isLoading: false, error: '登录失败: $e');
return false;
}
}
Future<bool> register({
required String username,
required String password,
required String email,
String? mobile,
String? mobileCode,
int? secQuestion,
String? secAnswer,
}) async {
state = state.copyWith(isLoading: true, clearError: true);
try {
final user = await AuthService.register(
username: username,
password: password,
email: email,
mobile: mobile,
mobileCode: mobileCode,
secQuestion: secQuestion,
secAnswer: secAnswer,
);
await _saveUserCache(user);
state = state.copyWith(user: user, isLoggedIn: true, isLoading: false);
DeviceInfoService.registerDeviceIfNeeded();
return true;
} on ApiException catch (e) {
state = state.copyWith(isLoading: false, error: e.message);
return false;
} catch (e) {
state = state.copyWith(isLoading: false, error: '注册失败: $e');
return false;
}
}
Future<void> logout() async {
await DeviceInfoService.resetRegistration();
await AuthService.logout();
await AuthService.clearCachedUser();
state = const AuthState(isInitialized: true);
}
Future<void> refreshUser() async {
try {
final data = await AuthService.getUserInfo();
final user = UserModel.fromJson(data);
await _saveUserCache(user);
state = state.copyWith(user: user);
} catch (e) {
Log.e('刷新用户信息失败', e);
}
}
Future<bool> updateProfile({
String? nickname,
String? bio,
String? avatarUrl,
}) async {
try {
await AuthService.updateProfile(
nickname: nickname,
bio: bio,
avatarUrl: avatarUrl,
);
await refreshUser();
return true;
} on ApiException catch (e) {
state = state.copyWith(error: e.message);
return false;
}
}
Future<bool> changePassword({
required String newPassword,
String? oldPassword,
String? secAnswer,
String? userId,
String verifyMethod = 'password',
}) async {
try {
await AuthService.changePassword(
newPassword: newPassword,
oldPassword: oldPassword,
secAnswer: secAnswer,
userId: userId,
verifyMethod: verifyMethod,
);
return true;
} on ApiException catch (e) {
state = state.copyWith(error: e.message);
return false;
}
}
}
final authProvider = NotifierProvider<AuthNotifier, AuthState>(AuthNotifier.new);
final isLoggedInProvider = Provider<bool>((ref) {
return ref.watch(authProvider).isLoggedIn;
});
final currentUserProvider = Provider<UserModel?>((ref) {
return ref.watch(authProvider).user;
});