From faa42e07e120043dff3324e7acb13d15437acbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=8C=AF=E5=8D=87?= <359059686@qq.com> Date: Fri, 29 Aug 2025 16:49:25 +0800 Subject: [PATCH] =?UTF-8?q?fix=20:=20=E7=99=BB=E5=BD=95=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E6=9C=AA=E7=9F=A5=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/providers/http_provider.dart | 122 +++++++----------- lib/data/repositories/auth_repository.dart | 59 +++------ .../auth/controllers/login_controller.dart | 11 +- lib/modules/my/controllers/my_controller.dart | 28 +++- lib/modules/my/views/my_page.dart | 48 ++++--- 5 files changed, 130 insertions(+), 138 deletions(-) diff --git a/lib/data/providers/http_provider.dart b/lib/data/providers/http_provider.dart index d5233b7..03a20cf 100644 --- a/lib/data/providers/http_provider.dart +++ b/lib/data/providers/http_provider.dart @@ -1,13 +1,15 @@ import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart' hide Response; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; +import 'package:problem_check_system/app/routes/app_routes.dart'; import 'package:problem_check_system/data/repositories/auth_repository.dart'; // DioProvider 是一个 GetxService,确保它在应用生命周期内是单例的。 // 它负责初始化和配置 Dio 实例,并添加所有拦截器。 class HttpProvider extends GetxService { - static const String _baseUrl = 'https://xh.anxincloud.cn'; + static const String _baseUrl = 'https://xhdev.anxincloud.cn'; late final Dio _dio; @@ -40,72 +42,7 @@ class HttpProvider extends GetxService { } List _getInterceptors() { - return [ - _getAuthInterceptor(), - _getErrorInterceptor(), - if (kDebugMode) _getLoggerInterceptor(), - ]; - } - - /// 认证拦截器:处理请求头中的 token 添加和 401 错误重试。 - Interceptor _getAuthInterceptor() { - return InterceptorsWrapper( - // 在请求发送前执行 - onRequest: (options, handler) async { - try { - // 尝试获取 AuthRepository 并添加 token 到请求头。 - final authRepository = Get.find(); - final token = authRepository.getToken(); - if (token != null && token.isNotEmpty) { - options.headers['Authorization'] = 'Bearer $token'; - } - } catch (e) { - // 如果 AuthRepository 尚未初始化(例如在登录或注册时),则跳过添加认证头。 - debugPrint('AuthRepository 未找到。跳过认证头。'); - } - return handler.next(options); - }, - // 在接收到错误时执行 - onError: (error, handler) async { - // 专门处理 401 Unauthorized 错误。 - if (error.response?.statusCode == 401) { - try { - final authRepository = Get.find(); - // 尝试刷新 token。 - await authRepository.refreshToken(); - - // 如果刷新成功,更新请求头并重试原始请求。 - final newOptions = Options( - method: error.requestOptions.method, - headers: error.requestOptions.headers - ..['Authorization'] = 'Bearer ${authRepository.getToken()}', - ); - - final response = await _dio.request( - error.requestOptions.path, - data: error.requestOptions.data, - queryParameters: error.requestOptions.queryParameters, - options: newOptions, - ); - - // 使用 handler.resolve() 返回重试的结果,阻止错误继续传递。 - return handler.resolve(response); - } on Exception catch (e) { - debugPrint('刷新 token 失败: $e'); - // 如果刷新 token 失败,清除认证数据并跳转到登录页。 - final authRepository = Get.find(); - authRepository.clearAuthData(); - if (Get.currentRoute != '/login') { - Get.offAllNamed('/login'); - } - // 传播原始错误,让下一个拦截器(通用错误拦截器)处理。 - return handler.next(error); - } - } - // 对于所有其他非 401 的错误,将错误传递给下一个拦截器。 - return handler.next(error); - }, - ); + return [_getErrorInterceptor(), if (kDebugMode) _getLoggerInterceptor()]; } /// 日志拦截器:在调试模式下打印详细的请求和响应日志。 @@ -124,20 +61,59 @@ class HttpProvider extends GetxService { /// 错误拦截器:处理通用的网络和服务器端错误,并显示 Snackbar 提示。 Interceptor _getErrorInterceptor() { return InterceptorsWrapper( + // 在请求发送前执行 + onRequest: (options, handler) async { + try { + // 尝试获取 AuthRepository 并添加 token 到请求头。 + final authRepository = Get.find(); + final token = authRepository.getToken(); + if (token != null && token.isNotEmpty) { + options.headers['Authorization'] = 'Bearer $token'; + } + } catch (e) { + // 如果 AuthRepository 尚未初始化(例如在登录或注册时),则跳过添加认证头。 + Get.snackbar( + '认证过期', + '请重新手动登录', + colorText: Colors.white, + backgroundColor: Colors.red, + snackPosition: SnackPosition.TOP, + ); + } + return handler.next(options); + }, onError: (error, handler) { // 处理网络连接超时或未知网络错误。 if (error.type == DioExceptionType.connectionTimeout || error.type == DioExceptionType.receiveTimeout || error.type == DioExceptionType.sendTimeout) { - Get.snackbar('网络超时', '请检查网络连接后重试'); + Get.snackbar( + '网络超时', + '请检查网络连接后重试', + colorText: Colors.white, + backgroundColor: Colors.red, + snackPosition: SnackPosition.TOP, + ); } else if (error.type == DioExceptionType.unknown) { - Get.snackbar('网络异常', '请检查网络连接后重试'); + Get.snackbar( + '网络异常', + '请检查网络连接后重试', + colorText: Colors.white, + backgroundColor: Colors.red, + snackPosition: SnackPosition.TOP, + ); } // 这部分代码只会在 AuthInterceptor 没有处理的错误(即非 401)时执行。 if (error.response != null) { final message = _handleStatusCode(error.response!); - Get.snackbar('请求错误', message); + Get.snackbar( + '请求错误', + message, + colorText: Colors.white, + backgroundColor: Colors.red, + snackPosition: SnackPosition.TOP, + ); } return handler.next(error); @@ -151,8 +127,10 @@ class HttpProvider extends GetxService { case 400: return response.data?['detail'] ?? '请求参数错误'; case 401: - return response.data?['detail'] ?? - '未授权,请重新登录'; // 注意:对于 401 错误,这行代码理想情况下不会被执行。 + final authRepository = Get.find(); + authRepository.clearAuthData(); + Get.offAllNamed(AppRoutes.login); + return '未授权,请重新登录'; case 403: return response.data?['detail'] ?? '访问被拒绝'; case 404: diff --git a/lib/data/repositories/auth_repository.dart b/lib/data/repositories/auth_repository.dart index 68a2cf8..6f1b4c0 100644 --- a/lib/data/repositories/auth_repository.dart +++ b/lib/data/repositories/auth_repository.dart @@ -1,4 +1,3 @@ -import 'package:dio/dio.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:problem_check_system/data/models/auth_model.dart'; @@ -86,56 +85,36 @@ class AuthRepository extends GetxService { /// Handles the user login process by calling the API and saving the response. Future login(LoginRequest request) async { - try { - final response = await httpProvider.post( - '/api/Accounts/SignIn', - data: request.toJson(), - ); - - final loginResponse = LoginResponse.fromJson(response.data); - return loginResponse; - } catch (e) { - throw Exception(e); - } + final response = await httpProvider.post( + '/api/Accounts/SignIn', + data: request.toJson(), + ); + + final loginResponse = LoginResponse.fromJson(response.data); + return loginResponse; } /// 从 API 获取用户个人资料 Future getUserProfile() async { - try { - final response = await httpProvider.get('/api/Accounts/Profile'); - - // 将 JSON 数据反序列化为 Profile 模型 - return User.fromJson(response.data); - } on DioException catch (e) { - // 专门处理网络错误,例如无网络连接、超时等 - // DioException 包含了更详细的错误信息 - throw Exception('Network error: ${e.message}'); - } catch (e) { - // 捕获所有其他未知错误 - rethrow; // 重新抛出异常,让调用者来处理 - } + final response = await httpProvider.get('/api/Accounts/Profile'); + + // 将 JSON 数据反序列化为 Profile 模型 + return User.fromJson(response.data); } /// Refreshes the authentication token using the refresh token. Future refreshToken() async { final refreshToken = getRefreshToken(); - if (refreshToken == null || refreshToken.isEmpty) { - throw Exception('没有可用的刷新token'); - } - try { - final response = await httpProvider.post( - '/auth/refresh', - data: {'refresh_token': refreshToken}, - ); + final response = await httpProvider.post( + '/auth/refresh', + data: {'refresh_token': refreshToken}, + ); - final authResponse = LoginResponse.fromJson(response.data); - saveToken(authResponse.token); - saveRefreshToken(authResponse.refreshToken); + final authResponse = LoginResponse.fromJson(response.data); + saveToken(authResponse.token); + saveRefreshToken(authResponse.refreshToken); - return authResponse; - } catch (e) { - throw Exception(e); - } + return authResponse; } } diff --git a/lib/modules/auth/controllers/login_controller.dart b/lib/modules/auth/controllers/login_controller.dart index a58cf6c..d2ac4f2 100644 --- a/lib/modules/auth/controllers/login_controller.dart +++ b/lib/modules/auth/controllers/login_controller.dart @@ -1,3 +1,4 @@ +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:problem_check_system/data/models/auth_model.dart'; @@ -59,6 +60,7 @@ class LoginController extends GetxController { /// 在线登录 Future _onlineLogin(LoginRequest loginRequest) async { try { + // 调用你的 post 方法 var loginResponse = await _authRepository.login(loginRequest); // 登录成功后 _authRepository.saveToken(loginResponse.token); @@ -71,8 +73,15 @@ class LoginController extends GetxController { _authRepository.removeLoginKey(); } Get.offAllNamed(AppRoutes.home); + // 登录成功,处理响应 + debugPrint('登录成功: $loginResponse'); + } on DioException catch (e) { + // 捕获由拦截器处理后抛出的 DioException + // 拦截器已经显示了 Snackbar,这里你可以做其他业务处理,例如清空表单等。 + debugPrint('登录失败,由DioException捕获: ${e.message}'); } catch (e) { - throw Exception(e); + // 捕获其他非 DioException 异常 + debugPrint('发生未知错误: $e'); } } diff --git a/lib/modules/my/controllers/my_controller.dart b/lib/modules/my/controllers/my_controller.dart index f75b842..71d20c3 100644 --- a/lib/modules/my/controllers/my_controller.dart +++ b/lib/modules/my/controllers/my_controller.dart @@ -1,3 +1,4 @@ +import 'package:dio/dio.dart'; import 'package:get/get.dart'; import 'package:problem_check_system/app/routes/app_routes.dart'; import 'package:problem_check_system/data/repositories/auth_repository.dart'; @@ -18,12 +19,29 @@ class MyController extends GetxController { _loadUserInfo(); } - // 从本地存储或API加载用户信息 + // 在你的 GetxController 中添加一个可观察的加载状态 + RxBool isLoading = false.obs; + + /// 从本地存储或API加载用户信息 Future _loadUserInfo() async { - var userProfile = await authRepository.getUserProfile(); - userName.value = userProfile.name ?? "无"; - userPhone.value = userProfile.email ?? '138****8547'; - userImage.value = userProfile.signatureImage.toString(); + // 设置加载状态为 true + isLoading.value = true; + try { + // 调用仓库层方法 + final userProfile = await authRepository.getUserProfile(); + + // 更新用户信息,如果字段为 null,则使用默认值 + userName.value = userProfile.name ?? "无"; + userPhone.value = userProfile.email ?? '138****8547'; + userImage.value = userProfile.signatureImage.toString(); + } on DioException catch (e) { + // 捕获由拦截器处理后抛出的 DioException + // 拦截器已经显示了 Snackbar,这里你可以做其他业务处理,例如清空表单等。 + } catch (e) { + // 捕获其他非 DioException 异常 + } finally { + isLoading.value = false; + } } void logout() { diff --git a/lib/modules/my/views/my_page.dart b/lib/modules/my/views/my_page.dart index d39f124..0b6b9ad 100644 --- a/lib/modules/my/views/my_page.dart +++ b/lib/modules/my/views/my_page.dart @@ -3,26 +3,39 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:problem_check_system/modules/my/controllers/my_controller.dart'; -import '../../../app/routes/app_routes.dart'; +import 'package:problem_check_system/app/routes/app_routes.dart'; class MyPage extends GetView { const MyPage({super.key}); @override Widget build(BuildContext context) { - // 获取 MyController 实例 - final MyController controller = Get.find(); - - return Scaffold( - body: Stack( - children: [ - // 顶部背景区域 - _buildBackground(), - // 内容区域 - _buildContent(controller), - ], - ), - ); + // Obx listens to changes in the controller's observable state, + // such as an isLoading flag, and rebuilds the widget accordingly. + return Obx(() { + // Check if the controller is in a loading state. + // Assuming MyController has a `isLoading` RxBool variable. + if (controller.isLoading.value) { + return const Scaffold( + body: Center( + // Display a CircularProgressIndicator while loading. + child: CircularProgressIndicator(), + ), + ); + } else { + // If not loading, show the main content. + return Scaffold( + body: Stack( + children: [ + // 顶部背景区域 + _buildBackground(), + // 内容区域 + _buildContent(), + ], + ), + ); + } + }); } /// 顶部背景和用户信息部分 @@ -45,7 +58,7 @@ class MyPage extends GetView { } /// 页面核心内容 - Widget _buildContent(MyController controller) { + Widget _buildContent() { return Positioned( top: 100.h, left: 20.w, @@ -94,11 +107,6 @@ class MyPage extends GetView { width: 1.w, ), ), - // child: const Icon( - // Icons.person, - // size: 40, - // color: Color(0xFFC8E0FF), - // ), child: Image.network( controller.userImage.value, // Show a CircularProgressIndicator while the image is loading