import 'package:flutter/foundation.dart'; import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// 用户认证服务 —— 登录 / 登出 / Token 持久化 class AuthService extends ChangeNotifier { // --------------- 状态 --------------- String _token = ''; String _username = ''; String _displayName = ''; String _avatarUrl = ''; bool _isLoading = false; String get token => _token; String get username => _username; String get displayName => _displayName.isNotEmpty ? _displayName : _username; String get avatarUrl => _avatarUrl; bool get isLoggedIn => _token.isNotEmpty; bool get isLoading => _isLoading; // --------------- Dio (内部使用,不依赖 ApiService 避免循环) --------------- final Dio _dio = Dio(BaseOptions( baseUrl: 'http://10.0.2.2:8080/api', connectTimeout: const Duration(seconds: 15), receiveTimeout: const Duration(seconds: 15), )); // --------------- 初始化(app 启动时恢复 session) --------------- Future init() async { final prefs = await SharedPreferences.getInstance(); _token = prefs.getString('token') ?? ''; _username = prefs.getString('username') ?? ''; _displayName = prefs.getString('displayName') ?? ''; _avatarUrl = prefs.getString('avatarUrl') ?? ''; notifyListeners(); } // --------------- 登录 --------------- Future login(String username, String password) async { _isLoading = true; notifyListeners(); try { final res = await _dio.post('/base/auth/login', data: { 'username': username, 'password': password, }); // 兼容两种响应格式 final data = res.data; if (data is Map && (data['code'] == 200 || data['code'] == 0)) { final payload = data['data'] ?? data; _token = payload is String ? payload : (payload['token'] ?? payload['accessToken'] ?? '').toString(); _username = payload is Map ? (payload['username'] ?? username).toString() : username; _displayName = payload is Map ? (payload['displayName'] ?? payload['nickName'] ?? '').toString() : ''; _avatarUrl = payload is Map ? (payload['avatar'] ?? '').toString() : ''; await _persist(); _isLoading = false; notifyListeners(); return true; } } catch (e) { debugPrint('Login error: $e'); } _isLoading = false; notifyListeners(); return false; } // --------------- 登出 --------------- Future logout() async { _token = ''; _username = ''; _displayName = ''; _avatarUrl = ''; final prefs = await SharedPreferences.getInstance(); await prefs.remove('token'); await prefs.remove('username'); await prefs.remove('displayName'); await prefs.remove('avatarUrl'); notifyListeners(); } // --------------- 刷新用户信息 --------------- Future refreshProfile() async { if (_token.isEmpty) return; try { final res = await _dio.get( '/base/user/info', options: Options(headers: {'Authorization': 'Bearer $_token'}), ); final data = res.data; if (data is Map && (data['code'] == 200 || data['code'] == 0)) { final payload = data['data'] ?? data; if (payload is Map) { _displayName = (payload['displayName'] ?? payload['nickName'] ?? _displayName).toString(); _avatarUrl = (payload['avatar'] ?? _avatarUrl).toString(); await _persist(); notifyListeners(); } } } catch (e) { debugPrint('refreshProfile error: $e'); } } // --------------- 持久化 --------------- Future _persist() async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('token', _token); await prefs.setString('username', _username); await prefs.setString('displayName', _displayName); await prefs.setString('avatarUrl', _avatarUrl); } }