fix: 登录失败自动自检三个服务端口并展示 SDK 原始错误(B-58)
- IMService 记录 onConnectFailed 的 code/error(lastConnectError) - SDK 未初始化成功时 login 直接抛错,不再无限等待 - 登录页失败提示下附自检结果:账号服务/消息接口/消息长连接 通不通 - 版本号 1.0.2+3
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import '../config.dart';
|
||||||
import '../services/auth_api.dart';
|
import '../services/auth_api.dart';
|
||||||
import '../services/call_service.dart';
|
import '../services/call_service.dart';
|
||||||
import '../services/im_service.dart';
|
import '../services/im_service.dart';
|
||||||
@@ -92,14 +94,52 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
MaterialPageRoute(builder: (_) => const HomeScreen()),
|
MaterialPageRoute(builder: (_) => const HomeScreen()),
|
||||||
);
|
);
|
||||||
} on AuthException catch (e) {
|
} on AuthException catch (e) {
|
||||||
setState(() => _error = e.message);
|
await _fail(e.message);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
setState(() => _error = '登录出错了,请稍后再试');
|
await _fail('登录出错了,请稍后再试');
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _logging = false);
|
if (mounted) setState(() => _logging = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 登录失败:在提示下方附上服务器自检结果,方便远程定位是哪一段不通
|
||||||
|
Future<void> _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<String> _diagnoseServer() async {
|
||||||
|
final dio = Dio(BaseOptions(
|
||||||
|
connectTimeout: const Duration(seconds: 5),
|
||||||
|
receiveTimeout: const Duration(seconds: 5),
|
||||||
|
));
|
||||||
|
Future<String> 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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ class IMService extends ChangeNotifier {
|
|||||||
/// 与服务器的连接状态(断网横幅用)
|
/// 与服务器的连接状态(断网横幅用)
|
||||||
ConnectStatus connectStatus = ConnectStatus.idle;
|
ConnectStatus connectStatus = ConnectStatus.idle;
|
||||||
|
|
||||||
|
/// 最近一次连接失败的原始错误(诊断展示用)
|
||||||
|
String? lastConnectError;
|
||||||
|
|
||||||
/// 是否已登录
|
/// 是否已登录
|
||||||
bool loggedIn = false;
|
bool loggedIn = false;
|
||||||
|
|
||||||
@@ -108,12 +111,14 @@ class IMService extends ChangeNotifier {
|
|||||||
connectStatus = ConnectStatus.connecting;
|
connectStatus = ConnectStatus.connecting;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
},
|
},
|
||||||
onConnectSuccess: () {
|
|
||||||
connectStatus = ConnectStatus.success;
|
|
||||||
notifyListeners();
|
|
||||||
},
|
|
||||||
onConnectFailed: (code, error) {
|
onConnectFailed: (code, error) {
|
||||||
connectStatus = ConnectStatus.failed;
|
connectStatus = ConnectStatus.failed;
|
||||||
|
lastConnectError = '$code $error';
|
||||||
|
notifyListeners();
|
||||||
|
},
|
||||||
|
onConnectSuccess: () {
|
||||||
|
connectStatus = ConnectStatus.success;
|
||||||
|
lastConnectError = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
},
|
},
|
||||||
onKickedOffline: _forceLogout,
|
onKickedOffline: _forceLogout,
|
||||||
@@ -191,6 +196,10 @@ class IMService extends ChangeNotifier {
|
|||||||
/// 登录(init 之后调用)。userID/token 来自公司账号登录接口。
|
/// 登录(init 之后调用)。userID/token 来自公司账号登录接口。
|
||||||
Future<void> login({required String userID, required String token}) async {
|
Future<void> login({required String userID, required String token}) async {
|
||||||
await init();
|
await init();
|
||||||
|
if (!sdkReady) {
|
||||||
|
// initSDK 返回 false:SDK 没初始化成功,直接登录会无限等待
|
||||||
|
throw StateError('消息组件初始化失败');
|
||||||
|
}
|
||||||
final user = await OpenIM.iMManager.login(
|
final user = await OpenIM.iMManager.login(
|
||||||
userID: userID,
|
userID: userID,
|
||||||
token: token,
|
token: token,
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
name: changlian
|
name: changlian
|
||||||
description: 畅联 —— 公司内部通讯 App(手机端)
|
description: 畅联 —— 公司内部通讯 App(手机端)
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 1.0.1+2
|
version: 1.0.2+3
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.6.0 <4.0.0"
|
sdk: ">=3.6.0 <4.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user