import 'dart:developer'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:dio/dio.dart'; import '../core/api_client.dart'; import '../core/local_database.dart'; class UserInfo { final String id; final String phone; final String role; final String? name; final String? avatarUrl; final String? gender; final String? birthDate; UserInfo({ required this.id, required this.phone, this.role = 'User', this.name, this.avatarUrl, this.gender, this.birthDate, }); } class AuthState { final UserInfo? user; final bool isLoggedIn; final bool isLoading; const AuthState({this.user, this.isLoggedIn = false, this.isLoading = true}); } final authProvider = NotifierProvider( AuthNotifier.new, ); final localDbProvider = Provider( (ref) => LocalDatabase.instance, ); final authExpiredNotifierProvider = Provider((ref) { return AuthExpiredNotifier(); }); final apiClientProvider = Provider((ref) { return ApiClient( db: ref.watch(localDbProvider), authExpiredNotifier: ref.watch(authExpiredNotifierProvider), ); }); class AuthNotifier extends Notifier { @override AuthState build() { final removeListener = ref.read(authExpiredNotifierProvider).addListener( () { state = const AuthState(isLoggedIn: false, isLoading: false); }, ); ref.onDispose(removeListener); _checkAuth(); // 初始为"加载中",让启动闸门显示 Splash 盖住登录页/首页初始态 return const AuthState(isLoggedIn: false, isLoading: true); } Future _checkAuth() async { final db = ref.read(localDbProvider); final refresh = await db.read('refresh_token'); if (refresh == null) { // 无 token:判定完成、未登录 state = const AuthState(isLoggedIn: false, isLoading: false); return; } state = const AuthState(isLoading: true); try { final response = await Dio( BaseOptions(baseUrl: baseUrl), ).post('/api/auth/refresh', data: {'refreshToken': refresh}); final data = response.data['data']; if (data != null) { await db.write('access_token', data['accessToken']); await db.write('refresh_token', data['refreshToken']); final u = data['user'] as Map?; state = AuthState( isLoggedIn: true, isLoading: false, user: UserInfo(id: '', phone: '', role: u?['role'] ?? 'User'), ); _loadProfile(); } else { state = const AuthState(isLoggedIn: false, isLoading: false); } } catch (_) { state = const AuthState(isLoggedIn: false, isLoading: false); } } Future _loadProfile() async { // Admin 不查 profile(无 User 记录) if (state.user?.role == 'Admin') return; try { final api = ref.read(apiClientProvider); final response = await api.get('/api/user/profile'); final user = response.data['data']; if (user != null) { state = AuthState( isLoggedIn: true, isLoading: false, user: UserInfo( id: user['id'] ?? '', phone: user['phone'] ?? '', role: user['role'] ?? state.user?.role ?? 'User', name: user['name'], avatarUrl: user['avatarUrl'], gender: user['gender'], birthDate: user['birthDate']?.toString(), ), ); } } catch (e) { log('[Auth] loadProfile: $e'); } } Future refreshProfile() => _loadProfile(); /// 发送验证码 Future<({String? error, String? devCode})> sendSms(String phone) async { try { final api = ref.read(apiClientProvider); final response = await api.post( '/api/auth/send-sms', data: {'phone': phone}, ); return ( error: null, devCode: response.data['data']?['devCode'] as String?, ); } catch (e) { return (error: '发送失败: $e', devCode: null); } } /// 注册(新用户,需选身份) Future register( String phone, String code, String name, String doctorId, ) async { try { final api = ref.read(apiClientProvider); final response = await api.post( '/api/auth/register', data: { 'phone': phone, 'smsCode': code, 'name': name, 'doctorId': doctorId, }, ); final data = response.data['data']; if (data == null) return response.data['message'] ?? '注册失败'; await api.saveTokens(data['accessToken'], data['refreshToken']); final user = data['user']; state = AuthState( isLoggedIn: true, isLoading: false, user: UserInfo( id: user['id'] ?? '', phone: user['phone'] ?? '', role: user['role'] ?? 'User', ), ); return null; } catch (e) { return '注册失败: $e'; } } /// 登录(已有账号) Future login(String phone, String code) async { try { final api = ref.read(apiClientProvider); final response = await api.post( '/api/auth/login', data: {'phone': phone, 'smsCode': code}, ); final data = response.data['data']; if (data == null) return response.data['message'] ?? '登录失败'; await api.saveTokens(data['accessToken'], data['refreshToken']); final user = data['user']; state = AuthState( isLoggedIn: true, isLoading: false, user: UserInfo( id: user['id'] ?? '', phone: user['phone'] ?? '', role: user['role'] ?? 'User', name: user['name'], avatarUrl: user['avatarUrl'], ), ); return null; } catch (e) { return '登录失败: $e'; } } /// Apple 登录 Future appleLogin( String identityToken, String? authorizationCode, String? name, ) async { try { final api = ref.read(apiClientProvider); final response = await api.post( '/api/auth/apple-login', data: { 'identityToken': identityToken, 'authorizationCode': authorizationCode, 'name': name, }, ); final data = response.data['data']; if (data == null) return response.data['message'] ?? 'Apple 登录失败'; await api.saveTokens(data['accessToken'], data['refreshToken']); final user = data['user']; state = AuthState( isLoggedIn: true, isLoading: false, user: UserInfo( id: user['id'] ?? '', phone: user['phone'] ?? '', role: user['role'] ?? 'User', name: user['name'], avatarUrl: user['avatarUrl'], ), ); return null; } catch (_) { return 'Apple 登录失败,请稍后重试'; } } /// 登出 Future logout() async { final api = ref.read(apiClientProvider); final db = ref.read(localDbProvider); final refresh = await db.read('refresh_token'); if (refresh != null) { try { await api.post('/api/auth/logout', data: {'refreshToken': refresh}); } catch (e) { log('[Auth] logout: $e'); } } await api.clearTokens(); state = const AuthState(isLoggedIn: false, isLoading: false); } } /// 便捷:当前用户角色 final userRoleProvider = Provider((ref) { return ref.watch(authProvider).user?.role ?? 'User'; });