一次大的提交
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:maibu_satabot_v2/core/consts/http_api_consts.dart';
|
||||
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/net_message_dispatcher.dart';
|
||||
|
||||
@@ -20,6 +23,11 @@ import '../../../devices/presentation/bloc/devices_cubit.dart';
|
||||
import '../../../devices/presentation/bloc/device_status_bloc.dart';
|
||||
import '../../../devices/presentation/bloc/device_status_event.dart';
|
||||
import '../../../devices/presentation/bloc/devices_state.dart';
|
||||
import '../../../devices/presentation/bloc/device_task_cubit.dart';
|
||||
import '../../../home/presentation/bloc/permission_request_bloc.dart';
|
||||
import '../../../my/presentation/bloc/my_cubit.dart';
|
||||
import '../../../remote_control/presentation/bloc/remote_control_cubit.dart';
|
||||
import '../../../../core/network/mqtt/domain/interfaces/mqtt_client.dart';
|
||||
import '../../../../features/v2/site/presentation/cubit/site_cubit.dart';
|
||||
import '../../data/datasources/auth_tcp_datasource.dart';
|
||||
import '../../data/datasources/impl/auth_tcp_datasource_impl.dart';
|
||||
@@ -39,7 +47,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
|
||||
|
||||
|
||||
// 🔥 登录验证 Completer:用于等待登录阶段的 have_logged_in 推送
|
||||
Completer<bool>? _loginVerificationCompleter;
|
||||
|
||||
@@ -56,11 +64,57 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
) : super(AuthInitial()) {
|
||||
// Cubit 一启动就开始监听 TCP 的“自动逻辑”
|
||||
_listenToAuthResponse();
|
||||
// 🔥 设置重连耗尽回调:连续4次重连失败后弹窗提示用户退出登录
|
||||
tcp.onReconnectExhausted = _showReconnectFailedDialog;
|
||||
}
|
||||
|
||||
/// App 启动时检查本地缓存
|
||||
Future<void> appStarted() async {
|
||||
final logger = GetIt.I<ILoggerService>() as SentryLoggerImpl;
|
||||
|
||||
try {
|
||||
final prefs = GetIt.I<SharedPreferences>();
|
||||
|
||||
// Step 1: 先生成新的 session_id,保存旧的
|
||||
final oldSessionId = prefs.getString('current_session_id');
|
||||
final newSessionId = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
await prefs.setString('current_session_id', newSessionId);
|
||||
debugPrint('📱 [AUTH] 新会话: $newSessionId,旧会话: $oldSessionId');
|
||||
|
||||
// Step 2: 检查是否从后台被杀
|
||||
final pendingKillLogout = prefs.getBool('pending_kill_logout') ?? false;
|
||||
final savedSessionId = prefs.getString('saved_session_id');
|
||||
|
||||
if (pendingKillLogout && savedSessionId != null && oldSessionId != null) {
|
||||
if (savedSessionId == oldSessionId) {
|
||||
// saved == old_current → App 在后台被杀,从未恢复过
|
||||
logger.logWithLevel('🔄 [AUTH] 检测到 APP 被后台杀死,执行退出登录', level: 'INFO');
|
||||
await prefs.setBool('pending_kill_logout', false);
|
||||
await prefs.remove('saved_session_id');
|
||||
await storage.deleteUser();
|
||||
emit(AuthUnauthenticated());
|
||||
return;
|
||||
} else {
|
||||
// saved != old_current → App 被杀前已恢复过,清除标记
|
||||
logger.logWithLevel(
|
||||
'📱 [AUTH] pending_kill_logout=true 但会话已恢复过,清除标记',
|
||||
level: 'INFO',
|
||||
);
|
||||
await prefs.setBool('pending_kill_logout', false);
|
||||
await prefs.remove('saved_session_id');
|
||||
}
|
||||
} else if (pendingKillLogout) {
|
||||
logger.logWithLevel(
|
||||
'📱 [AUTH] pending_kill_logout=true 但无会话信息,清除标记',
|
||||
level: 'INFO',
|
||||
);
|
||||
await prefs.setBool('pending_kill_logout', false);
|
||||
await prefs.remove('saved_session_id');
|
||||
}
|
||||
} catch (e) {
|
||||
logger.logWithLevel('❌ [AUTH] 检查杀后台标记失败: $e', level: 'ERROR');
|
||||
}
|
||||
|
||||
try {
|
||||
final user = await storage.getUser();
|
||||
logger.logWithLevel(
|
||||
@@ -70,14 +124,21 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
);
|
||||
|
||||
if (user != null) {
|
||||
// 🔥 冷启动时不创建TCP连接,改为用户选择设备时再连接
|
||||
// 避免发送 0x03 触发服务端残留 session 的 have_logged_in
|
||||
|
||||
// 2. 同步全局 App 状态
|
||||
appCubit.setAuth(user);
|
||||
// 3. 进入已登录状态
|
||||
emit(AuthAuthenticated(user));
|
||||
logger.logWithLevel('✅ [AUTH] 应用启动 - 已恢复登录状态', level: 'INFO');
|
||||
final isValid = await _verifyToken(user);
|
||||
if (isValid) {
|
||||
logger.logWithLevel('✅ [AUTH] Token 有效,恢复登录状态', level: 'INFO');
|
||||
appCubit.setAuth(user);
|
||||
emit(AuthAuthenticated(user));
|
||||
logger.logWithLevel('✅ [AUTH] 应用启动 - 已恢复登录状态', level: 'INFO');
|
||||
} else {
|
||||
logger.logWithLevel('⚠️ [AUTH] Token 已过期,清除本地缓存', level: 'WARN');
|
||||
await storage.deleteUser();
|
||||
emit(AuthUnauthenticated());
|
||||
logger.logWithLevel(
|
||||
'⚠️ [AUTH] 应用启动 - Token 过期,进入未登录状态',
|
||||
level: 'INFO',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logger.logWithLevel('⚠️ [AUTH] 应用启动 - 无本地缓存,进入未登录状态', level: 'INFO');
|
||||
emit(AuthUnauthenticated());
|
||||
@@ -88,6 +149,42 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _verifyToken(UserEntity user) async {
|
||||
try {
|
||||
final verifyDio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: HttpApiConsts.baseUrl,
|
||||
headers: {'Authorization': 'Bearer ${user.token}'},
|
||||
connectTimeout: const Duration(seconds: 5),
|
||||
receiveTimeout: const Duration(seconds: 5),
|
||||
),
|
||||
);
|
||||
final response = await verifyDio.get(
|
||||
HttpApiConsts.getUserDevicesList,
|
||||
queryParameters: {'tenantName': user.username},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = response.data;
|
||||
if (data is Map<String, dynamic> && data['code'] == 401) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} on DioException catch (e) {
|
||||
final statusCode = e.response?.statusCode;
|
||||
debugPrint('>>> [AUTH] Token 验证失败: HTTP $statusCode');
|
||||
if (statusCode == 401 || statusCode == 403) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint('>>> [AUTH] Token 验证异常: $e');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// 当 HTTP 登录/注册成功后调用
|
||||
Future<void> loginSuccess(UserEntity user) async {
|
||||
await storage.saveUser(user);
|
||||
@@ -96,33 +193,33 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
try {
|
||||
debugPrint('>>> [AUTH] 登录成功,开始建立TCP连接...');
|
||||
_logger.logWithLevel('>>> [AUTH] 登录成功,开始建立TCP连接...', shouldLog: true);
|
||||
|
||||
|
||||
await tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
tcp.startHeartbeat(interval: const Duration(seconds: 4));
|
||||
|
||||
|
||||
// 🔥 关键:等待 2.5 秒,看是否收到 have_logged_in
|
||||
bool isKicked = await _waitForLoginVerification();
|
||||
|
||||
|
||||
if (isKicked) {
|
||||
// 🔥 不能进入 APP,必须清除所有登录信息
|
||||
debugPrint('>>> [AUTH] ⚠️ 登录验证失败,清除登录信息...');
|
||||
_logger.logWithLevel('>>> [AUTH] ⚠️ 登录验证失败,清除登录信息', shouldLog: true);
|
||||
|
||||
|
||||
// 1. 删除已保存的用户信息
|
||||
await storage.deleteUser();
|
||||
debugPrint('✅ [AUTH] 已删除用户信息');
|
||||
|
||||
|
||||
// 2. 断开 TCP 连接
|
||||
tcp.forceDisconnect();
|
||||
debugPrint('✅ [AUTH] 已断开 TCP 连接');
|
||||
|
||||
|
||||
// 3. 显示 Toast
|
||||
_showLoginFailedToast("账号已在其他设备登录");
|
||||
debugPrint('>>> [AUTH] ⚠️ 登录验证失败,停留在登录页');
|
||||
_logger.logWithLevel('>>> [AUTH] ⚠️ 登录验证失败,停留在登录页', shouldLog: true);
|
||||
return; // 停留在登录页
|
||||
return; // 停留在登录页
|
||||
}
|
||||
|
||||
|
||||
debugPrint('✅ [AUTH] TCP连接成功,验证通过');
|
||||
_logger.logWithLevel('✅ [AUTH] TCP连接成功,验证通过', shouldLog: true);
|
||||
} catch (e) {
|
||||
@@ -149,6 +246,42 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
|
||||
/// 🔥 Token 过期处理:弹出提示后退出登录
|
||||
Future<void> tokenExpired() async {
|
||||
_showTokenExpiredDialog();
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
logout();
|
||||
});
|
||||
}
|
||||
|
||||
void _showTokenExpiredDialog() {
|
||||
try {
|
||||
final context = navigatorKey.currentContext;
|
||||
if (context != null) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
title: const Text('登录已过期'),
|
||||
content: const Text('账号登录状态已过期,请重新登录'),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
},
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('>>> [AUTH] ❌ 显示 Token 过期弹窗失败:$e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 🔥 新增:显示登录失败 Toast(黑色背景,和登录错误提示一致)
|
||||
void _showLoginFailedToast(String message) {
|
||||
try {
|
||||
@@ -173,7 +306,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
try {
|
||||
debugPrint('>>> [AUTH] 📢 准备显示异地登录提示弹窗');
|
||||
_logger.logWithLevel('>>> [AUTH] 📢 准备显示异地登录提示弹窗', shouldLog: true);
|
||||
|
||||
|
||||
// 使用全局 navigatorKey 显示弹窗
|
||||
final context = navigatorKey.currentContext;
|
||||
if (context != null) {
|
||||
@@ -190,9 +323,12 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
);
|
||||
} else {
|
||||
debugPrint('>>> [AUTH] ⚠️ 无法获取 Navigator Context,将直接退出');
|
||||
_logger.logWithLevel('>>> [AUTH] ⚠️ 无法获取 Navigator Context,将直接退出', shouldLog: true);
|
||||
_logger.logWithLevel(
|
||||
'>>> [AUTH] ⚠️ 无法获取 Navigator Context,将直接退出',
|
||||
shouldLog: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 🔥 延迟 2 秒后执行退出,给用户时间看到提示
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
debugPrint('>>> [AUTH] ⏰ 延迟结束,开始执行退出登录');
|
||||
@@ -207,27 +343,142 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 🔥 TCP 重连耗尽弹窗:连续4次重连失败后提示用户退出登录
|
||||
void _showReconnectFailedDialog() {
|
||||
try {
|
||||
debugPrint('>>> [AUTH] 📢 重连耗尽,准备显示重连失败提示弹窗');
|
||||
_logger.logWithLevel('>>> [AUTH] 📢 重连耗尽,显示重连失败弹窗', shouldLog: true);
|
||||
|
||||
final context = navigatorKey.currentContext;
|
||||
if (context != null) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.wifi_off_rounded, size: 48, color: Color(0xFFF53F3F)),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'连接异常',
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1D2129),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'TCP重连认证无效请重新登陆',
|
||||
style: TextStyle(fontSize: 14, color: Color(0xFF86909C)),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
logout();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF165DFF),
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text('退出登录', style: TextStyle(fontSize: 15)),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
debugPrint('>>> [AUTH] ⚠️ 无法获取 Navigator Context,直接退出登录');
|
||||
logout();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('>>> [AUTH] ❌ 显示重连失败弹窗失败:$e');
|
||||
logout();
|
||||
}
|
||||
}
|
||||
|
||||
/// 🔥 新增:清空所有业务状态,防止数据泄露到新账户
|
||||
void _clearAllBusinessState() {
|
||||
try {
|
||||
final devicesCubit = GetIt.I<DevicesCubit>();
|
||||
final deviceStatusBloc = GetIt.I<DeviceStatusBloc>();
|
||||
final siteCubit = GetIt.I<SiteCubit>();
|
||||
// 1. 清空远程控制所有状态(targetDevice、权限、摇杆数据、电压电量等)
|
||||
final remoteControlCubit = GetIt.I<RemoteControlCubit>();
|
||||
remoteControlCubit.clearAll();
|
||||
debugPrint('✅ [AUTH] 已清空 RemoteControlCubit 状态');
|
||||
_logger.logWithLevel('✅ [AUTH] 已清空 RemoteControlCubit 状态');
|
||||
|
||||
// 1. 清空设备列表和选中设备
|
||||
// 2. 清空设备列表和选中设备
|
||||
final devicesCubit = GetIt.I<DevicesCubit>();
|
||||
devicesCubit.emit(const DevicesState());
|
||||
debugPrint('✅ [AUTH] 已清空 DevicesCubit 状态');
|
||||
_logger.logWithLevel('✅ [AUTH] 已清空 DevicesCubit 状态');
|
||||
|
||||
// 2. 清空设备实时状态
|
||||
// 3. 清空设备实时状态(图表数据等)
|
||||
final deviceStatusBloc = GetIt.I<DeviceStatusBloc>();
|
||||
deviceStatusBloc.add(DeviceStatusReset());
|
||||
debugPrint('✅ [AUTH] 已重置 DeviceStatusBloc 状态');
|
||||
_logger.logWithLevel('✅ [AUTH] 已重置 DeviceStatusBloc 状态');
|
||||
|
||||
// 3. 清空全局选中的场站
|
||||
siteCubit.clearSelectedSite();
|
||||
debugPrint('✅ [AUTH] 已清空 SiteCubit 选中状态');
|
||||
_logger.logWithLevel('✅ [AUTH] 已清空 SiteCubit 选中状态');
|
||||
// 4. 清空场站所有数据(列表、选中状态、_hasLoadedSites 标记)
|
||||
final siteCubit = GetIt.I<SiteCubit>();
|
||||
siteCubit.clearAll();
|
||||
debugPrint('✅ [AUTH] 已清空 SiteCubit 所有数据');
|
||||
_logger.logWithLevel('✅ [AUTH] 已清空 SiteCubit 所有数据');
|
||||
|
||||
// 5. 清空设备任务(taskPool、currentTask、currentTaskId 等)
|
||||
final deviceTaskCubit = GetIt.I<DeviceTaskCubit>();
|
||||
deviceTaskCubit.clearAll();
|
||||
debugPrint('✅ [AUTH] 已清空 DeviceTaskCubit 状态');
|
||||
_logger.logWithLevel('✅ [AUTH] 已清空 DeviceTaskCubit 状态');
|
||||
|
||||
// 6. 清空权限请求弹窗状态
|
||||
final permissionRequestBloc = GetIt.I<PermissionRequestBloc>();
|
||||
permissionRequestBloc.clearAll();
|
||||
debugPrint('✅ [AUTH] 已清空 PermissionRequestBloc 状态');
|
||||
_logger.logWithLevel('✅ [AUTH] 已清空 PermissionRequestBloc 状态');
|
||||
|
||||
// 7. 清空我的页面数据(昵称等个人信息)
|
||||
final myCubit = GetIt.I<MyCubit>();
|
||||
myCubit.clearAll();
|
||||
debugPrint('✅ [AUTH] 已清空 MyCubit 状态');
|
||||
_logger.logWithLevel('✅ [AUTH] 已清空 MyCubit 状态');
|
||||
|
||||
// 8. 断开 MQTT 连接(避免新用户收到上个用户的实时推送)
|
||||
try {
|
||||
final droneOsdClient = GetIt.I<MqttClient>(instanceName: 'droneOsdClient');
|
||||
if (droneOsdClient.isConnected) {
|
||||
droneOsdClient.disconnect();
|
||||
debugPrint('✅ [AUTH] 已断开 droneOsdClient MQTT');
|
||||
}
|
||||
} catch (_) {}
|
||||
try {
|
||||
final taskMessageClient = GetIt.I<MqttClient>(instanceName: 'taskMessageClient');
|
||||
if (taskMessageClient.isConnected) {
|
||||
taskMessageClient.disconnect();
|
||||
debugPrint('✅ [AUTH] 已断开 taskMessageClient MQTT');
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// 9. 清除 SharedPreferences 会话相关 key
|
||||
final prefs = GetIt.I<SharedPreferences>();
|
||||
prefs.remove('current_session_id');
|
||||
prefs.remove('pending_kill_logout');
|
||||
prefs.remove('saved_session_id');
|
||||
debugPrint('✅ [AUTH] 已清除 SharedPreferences 会话 key');
|
||||
|
||||
debugPrint('✅ [AUTH] 所有业务状态已清空');
|
||||
_logger.logWithLevel('✅ [AUTH] 所有业务状态已清空');
|
||||
@@ -240,15 +491,15 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
/// 🔥 等待登录验证结果(2.5 秒内看是否收到 have_logged_in)
|
||||
Future<bool> _waitForLoginVerification() async {
|
||||
_loginVerificationCompleter = Completer<bool>();
|
||||
|
||||
|
||||
// 等待 2.5 秒
|
||||
await Future.delayed(const Duration(milliseconds: 2500));
|
||||
|
||||
|
||||
// 如果 completer 还没完成,说明没收到 have_logged_in,返回 false(可以进入)
|
||||
if (!_loginVerificationCompleter!.isCompleted) {
|
||||
_loginVerificationCompleter!.complete(false);
|
||||
}
|
||||
|
||||
|
||||
return _loginVerificationCompleter!.future;
|
||||
}
|
||||
|
||||
@@ -290,35 +541,46 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
|
||||
if (respond == 'have_logged_in') {
|
||||
// 🔥 登录阶段策略:HTTP 已成功,TCP 认证阶段的推送视为服务端状态同步,直接放行
|
||||
if (_loginVerificationCompleter != null && !_loginVerificationCompleter!.isCompleted) {
|
||||
debugPrint('>>> [AUTH] 🛡️ 登录阶段收到 have_logged_in,视为服务端状态同步,允许进入 APP');
|
||||
if (_loginVerificationCompleter != null &&
|
||||
!_loginVerificationCompleter!.isCompleted) {
|
||||
debugPrint(
|
||||
'>>> [AUTH] 🛡️ 登录阶段收到 have_logged_in,视为服务端状态同步,允许进入 APP',
|
||||
);
|
||||
_logger.logWithLevel(
|
||||
'🛡️ [AUTH] 登录阶段收到 have_logged_in,视为服务端状态同步,允许进入',
|
||||
shouldLog: true,
|
||||
);
|
||||
_loginVerificationCompleter!.complete(false); // 标记为可以进入
|
||||
_loginVerificationCompleter!.complete(false); // 标记为可以进入
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 🔥 已登录阶段策略:检查是否处于安全模式
|
||||
if (_isSafeMode) {
|
||||
print('>>> [AUTH] 🛡️ 安全模式下拦截 have_logged_in 推送,防止控制中断');
|
||||
_logger.logWithLevel(
|
||||
'🛡️ [AUTH] 安全模式下拦截异地登录推送',
|
||||
level: 'WARN',
|
||||
);
|
||||
_logger.logWithLevel('🛡️ [AUTH] 安全模式下拦截异地登录推送', level: 'WARN');
|
||||
return; // 拦截退出逻辑
|
||||
}
|
||||
|
||||
|
||||
print('>>> [AUTH] ⚠️ 已登录状态下收到 TCP 0x12 指令 respond=have_logged_in');
|
||||
debugPrint('>>> [AUTH] ℹ️ 暂时忽略该推送,观察是否影响业务操作...');
|
||||
_logger.logWithLevel(
|
||||
'⚠️ [AUTH] 已登录状态收到 have_logged_in,暂不处理,观察业务影响',
|
||||
'⚠️ [AUTH] 已登录状态收到 have_logged_in,弹出异地登录提示',
|
||||
level: 'WARN',
|
||||
);
|
||||
|
||||
// 如果你希望依然保持严格的安全策略,可以取消下面注释恢复退出逻辑:
|
||||
// _showKickOutDialog();
|
||||
|
||||
// 🔥 关键:检查是否是自身 TCP 重连触发的 have_logged_in
|
||||
// 如果是自身刚发送 0x03 认证包引起的,忽略这次推送
|
||||
if (tcp.isOwnAuthTriggeredKick()) {
|
||||
debugPrint('>>> [AUTH] 🛡️ 检测到是自身认证触发的 have_logged_in,忽略');
|
||||
_logger.logWithLevel(
|
||||
'🛡️ [AUTH] 自身认证触发的 have_logged_in,忽略',
|
||||
level: 'INFO',
|
||||
);
|
||||
tcp.clearAuthTimestamp();
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔥 弹出"账号被顶下线"提示,2秒后执行退出登录
|
||||
_showKickOutDialog();
|
||||
} else {
|
||||
debugPrint('>>> [AUTH] ℹ️ 收到 0x12 消息,respond="$respond",不处理');
|
||||
}
|
||||
|
||||
@@ -28,15 +28,16 @@ class LoginCubit extends Cubit<LoginState> {
|
||||
// if (user != null) {
|
||||
// devicesCubit.fetchAllDevices(user.username);
|
||||
// }
|
||||
result.fold((failure) => emit(LoginFailure(failure.message)), (user) {
|
||||
result.fold((failure) => emit(LoginFailure(failure.message)), (user) async {
|
||||
// 先更新全局用户状态(确保首页能获取到 token)
|
||||
authCubit.appCubit.setAuth(user);
|
||||
// 发出登录成功状态(触发页面跳转)
|
||||
emit(LoginSuccess(user));
|
||||
// 后台异步执行 TCP 连接等初始化操作(不阻塞登录流程)
|
||||
authCubit.loginSuccess(user).catchError((e) {
|
||||
// 🔥 先等待 TCP 连接和认证完成,确保 AuthCubit 变为 AuthAuthenticated
|
||||
// 再触发页面导航,避免 GoRouter 拦截踢回登录页
|
||||
await authCubit.loginSuccess(user).catchError((e) {
|
||||
print('TCP 初始化失败: $e');
|
||||
});
|
||||
// 发出登录成功状态(触发页面跳转)
|
||||
emit(LoginSuccess(user));
|
||||
});
|
||||
} catch (e) {
|
||||
emit(LoginFailure(e.toString()));
|
||||
|
||||
Reference in New Issue
Block a user