diff --git a/mobile/lib/screens/login_screen.dart b/mobile/lib/screens/login_screen.dart index 2618147..66301dc 100644 --- a/mobile/lib/screens/login_screen.dart +++ b/mobile/lib/screens/login_screen.dart @@ -1,6 +1,8 @@ +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../config.dart'; import '../services/auth_api.dart'; import '../services/call_service.dart'; import '../services/im_service.dart'; @@ -92,14 +94,52 @@ class _LoginScreenState extends State { MaterialPageRoute(builder: (_) => const HomeScreen()), ); } on AuthException catch (e) { - setState(() => _error = e.message); + await _fail(e.message); } catch (_) { - setState(() => _error = '登录出错了,请稍后再试'); + await _fail('登录出错了,请稍后再试'); } finally { if (mounted) setState(() => _logging = false); } } + /// 登录失败:在提示下方附上服务器自检结果,方便远程定位是哪一段不通 + Future _fail(String msg) async { + final parts = [msg]; + try { + parts.add(await _diagnoseServer()); + } catch (_) { + // 自检本身失败不影响原提示 + } + final sdkErr = IMService.instance.lastConnectError; + if (sdkErr != null && sdkErr.isNotEmpty) parts.add('消息组件报告:$sdkErr'); + if (mounted) setState(() => _error = parts.join('\n')); + } + + /// 逐个探测三个服务端口:能拿到任何 HTTP 响应就算通 + Future _diagnoseServer() async { + final dio = Dio(BaseOptions( + connectTimeout: const Duration(seconds: 5), + receiveTimeout: const Duration(seconds: 5), + )); + Future probe(String label, String url) async { + try { + await dio.get(url); + return '$label通'; + } on DioException catch (e) { + return e.response != null ? '$label通' : '$label不通'; + } catch (_) { + return '$label不通'; + } + } + + final results = await Future.wait([ + probe('账号服务', '$authApiBase/api/health'), + probe('消息接口', '$apiAddr/'), + probe('消息长连接', 'http://$serverHost:$wsPort/'), + ]); + return '自检:${results.join(',')}'; + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/mobile/lib/services/im_service.dart b/mobile/lib/services/im_service.dart index 2bd398d..404ec60 100644 --- a/mobile/lib/services/im_service.dart +++ b/mobile/lib/services/im_service.dart @@ -36,6 +36,9 @@ class IMService extends ChangeNotifier { /// 与服务器的连接状态(断网横幅用) ConnectStatus connectStatus = ConnectStatus.idle; + /// 最近一次连接失败的原始错误(诊断展示用) + String? lastConnectError; + /// 是否已登录 bool loggedIn = false; @@ -108,12 +111,14 @@ class IMService extends ChangeNotifier { connectStatus = ConnectStatus.connecting; notifyListeners(); }, - onConnectSuccess: () { - connectStatus = ConnectStatus.success; - notifyListeners(); - }, onConnectFailed: (code, error) { connectStatus = ConnectStatus.failed; + lastConnectError = '$code $error'; + notifyListeners(); + }, + onConnectSuccess: () { + connectStatus = ConnectStatus.success; + lastConnectError = null; notifyListeners(); }, onKickedOffline: _forceLogout, @@ -191,6 +196,10 @@ class IMService extends ChangeNotifier { /// 登录(init 之后调用)。userID/token 来自公司账号登录接口。 Future login({required String userID, required String token}) async { await init(); + if (!sdkReady) { + // initSDK 返回 false:SDK 没初始化成功,直接登录会无限等待 + throw StateError('消息组件初始化失败'); + } final user = await OpenIM.iMManager.login( userID: userID, token: token, diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index fc48522..517aab5 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -1,7 +1,7 @@ name: changlian description: 畅联 —— 公司内部通讯 App(手机端) publish_to: "none" -version: 1.0.1+2 +version: 1.0.2+3 environment: sdk: ">=3.6.0 <4.0.0"