import 'dart:async'; import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart'; import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart'; import 'package:maibu_satabot_v2/core/network/net_message_dispatcher.dart'; import '../../../../core/app/app_user_cubit.dart'; import '../../../../core/infrastructure/logging/sentry_logger_impl.dart'; import '../../../../core/logging/i_logger_service.dart'; import '../../../../core/network/tcp/tcp_client.dart'; import '../../../../core/storage/user_storage.dart'; import '../../../devices/domain/usecases/get_user_device_usecase.dart'; import '../../../devices/domain/usecases/switch_device_usecase.dart'; 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 '../../../../features/v2/site/presentation/cubit/site_cubit.dart'; import '../../data/datasources/auth_tcp_datasource.dart'; import '../../data/datasources/impl/auth_tcp_datasource_impl.dart'; import 'auth_state.dart'; /// emit做的事情: /// 1.修改状态标志:通过改变类的类型(从 Initial 变成 Authenticated) /// 2.携带数据:把 user 对象塞进了状态里,让外部能拿到 /// 3.发送通知:自动触发监听(如 GoRouter 的刷新)。 class AuthCubit extends Cubit { final UserStorage storage; final TcpClient tcp; final AppUserCubit appCubit; final NetMessageDispatcher dispatcher; final AuthTcpDatasource _authTcpDatasource; final ILoggerService _logger = GetIt.I(); StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期 AuthCubit( this.storage, this.tcp, this.appCubit, this.dispatcher, this._authTcpDatasource, ) : super(AuthInitial()) { // Cubit 一启动就开始监听 TCP 的“自动逻辑” _listenToAuthResponse(); } /// App 启动时检查本地缓存 Future appStarted() async { final logger = GetIt.I() as SentryLoggerImpl; try { final user = await storage.getUser(); logger.logWithLevel( '启动时检查本地缓存', level: 'INFO', data: {'user': user != null ? '找到用户: ${user.username}' : '未找到用户'}, ); if (user != null) { // 🔥 冷启动时重新初始化 TCP 连接 await tcp.initializeTcp( host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT, ); // 2. 同步全局 App 状态 appCubit.setAuth(user); // 3. 进入已登录状态 emit(AuthAuthenticated(user)); logger.logWithLevel('✅ [AUTH] 应用启动 - 已恢复登录状态并建立TCP连接', level: 'INFO'); } else { logger.logWithLevel('⚠️ [AUTH] 应用启动 - 无本地缓存,进入未登录状态', level: 'INFO'); emit(AuthUnauthenticated()); } } catch (e) { logger.logWithLevel('❌ [AUTH] 应用启动检查失败: $e', level: 'ERROR'); emit(AuthUnauthenticated()); } } /// 当 HTTP 登录/注册成功后调用 Future loginSuccess(UserEntity user) async { await storage.saveUser(user); // 🔥 使用封装的TCP初始化方法:连接 + 认证 + 心跳 await tcp.initializeTcp(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT); appCubit.setAuth(user); emit(AuthAuthenticated(user)); } /// 退出登录 (主动或被动) Future logout() async { //print("退出登录"); // 🔥 关键修复:在断开TCP前,先清空所有业务状态,避免重连触发旧数据 _clearAllBusinessState(); await storage.deleteUser(); tcp.disconnect(); // 断开后会自动重连 appCubit.clearAuth(); emit(AuthUnauthenticated()); } /// 🔥 新增:清空所有业务状态,防止数据泄露到新账户 void _clearAllBusinessState() { try { final devicesCubit = GetIt.I(); final deviceStatusBloc = GetIt.I(); final siteCubit = GetIt.I(); // 1. 清空设备列表和选中设备 devicesCubit.emit(const DevicesState()); debugPrint('✅ [AUTH] 已清空 DevicesCubit 状态'); _logger.logWithLevel('✅ [AUTH] 已清空 DevicesCubit 状态'); // 2. 清空设备实时状态 deviceStatusBloc.add(DeviceStatusReset()); debugPrint('✅ [AUTH] 已重置 DeviceStatusBloc 状态'); _logger.logWithLevel('✅ [AUTH] 已重置 DeviceStatusBloc 状态'); // 3. 清空全局选中的场站 siteCubit.clearSelectedSite(); debugPrint('✅ [AUTH] 已清空 SiteCubit 选中状态'); _logger.logWithLevel('✅ [AUTH] 已清空 SiteCubit 选中状态'); debugPrint('✅ [AUTH] 所有业务状态已清空'); _logger.logWithLevel('✅ [AUTH] 所有业务状态已清空'); } catch (e) { debugPrint('❌ [AUTH] 清空业务状态失败: $e'); _logger.logWithLevel('❌ [AUTH] 清空业务状态失败: $e', level: 'ERROR'); } } /// TCP 指令监听 void _listenToAuthResponse() { debugPrint('>>> [AUTH] _listenToAuthResponse() 被调用,开始监听 0x12 指令'); _kickOutSub?.cancel(); // 防止重复监听 // 直接监听原始数据包,自己处理 JSON 解析(去掉 CRC 字节) _kickOutSub = dispatcher.onCommand(0x12).listen((packet) { debugPrint( '>>> [AUTH] 收到 0x12 原始包,payload 长度=${packet.payload.length}, 内容=${packet.payload}', ); try { // 🔥 关键:手动去掉最后 2 个 CRC 字节 String jsonString; if (packet.payload.length > 2) { jsonString = utf8.decode( packet.payload.sublist(0, packet.payload.length - 2), ); } else { jsonString = utf8.decode(packet.payload); } debugPrint('>>> [AUTH] 获取 JSON: "$jsonString"'); final jsonMap = jsonDecode(jsonString); debugPrint('>>> [AUTH] 获取 JSON Map: $jsonMap'); final respond = jsonMap['respond'] ?? ''; debugPrint('>>> [AUTH] respond 字段值: "$respond"'); _logger.logWithLevel( '>>> [AUTH] respond 字段值: "$respond"', shouldLog: true, ); debugPrint('>>> [AUTH] respond 类型: ${respond.runtimeType}'); debugPrint( '>>> [AUTH] respond == "have_logged_in": ${respond == "have_logged_in"}', ); if (respond == 'have_logged_in') { debugPrint('>>> [AUTH] ⚠️ 检测到异地登录 (respond=have_logged_in),开始退出...'); _logger.logWithLevel( '⚠️ [AUTH] 检测到异地登录 (respond=have_logged_in),开始退出...', level: 'INFO', ); logout(); } else { debugPrint('>>> [AUTH] ℹ️ 收到 0x12 消息,respond="$respond",不处理'); } } catch (e) { debugPrint('>>> [AUTH] ❌ JSON 解析失败:$e'); } }); _logger.logWithLevel('[AUTH] 监听 TCP 0x12 指令...', level: 'INFO'); } @override Future close() { _kickOutSub?.cancel(); // 销毁 Cubit 时关闭监听 return super.close(); } /// 🔥 息屏/后台后恢复到前台时的重连方法 Future reconnectAfterResume() async { _logger.logWithLevel('[AUTH] 检测到应用恢复到前台,检查 TCP 连接状态...', level: 'INFO'); // 如果 TCP 未连接,则执行重连 if (!tcp.isConnected) { _logger.logWithLevel('[AUTH] TCP 未连接,开始重连...', level: 'INFO'); try { await tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT); tcp.startHeartbeat(interval: const Duration(seconds: 4)); _logger.logWithLevel('[AUTH] TCP 重连成功!', level: 'INFO'); } catch (e) { _logger.logWithLevel('[AUTH] TCP 重连失败:$e', level: 'ERROR'); } } else { _logger.logWithLevel('[AUTH] TCP 已连接,无需重连', level: 'INFO'); // 可选:发送一个心跳包确认连接有效 tcp.sendHeartbeat(); } } }