From 8eb03ddedcad2d276ad1f2d4aa68252f963633c2 Mon Sep 17 00:00:00 2001 From: haosicheng Date: Fri, 17 Oct 2025 17:38:02 +0800 Subject: [PATCH] =?UTF-8?q?[patch]=20=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=99=E9=A2=9D=E4=B8=8D=E8=B6=B3=20=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebSocket/BackendClient.py | 35 +++++-- WebSocket/backend_singleton.py | 96 ++++++++++++++++--- config.py | 2 +- main.py | 169 +++++++++++++++++++++++---------- 4 files changed, 225 insertions(+), 77 deletions(-) diff --git a/WebSocket/BackendClient.py b/WebSocket/BackendClient.py index b0ad4bb..ad05b33 100644 --- a/WebSocket/BackendClient.py +++ b/WebSocket/BackendClient.py @@ -32,6 +32,7 @@ class BackendClient: self.token_error_callback: Optional[Callable] = None # 新增:token错误回调 self.version_callback: Optional[Callable] = None # 新增:版本检查回调 self.disconnect_callback: Optional[Callable] = None # 新增:被踢下线回调 + self.balance_insufficient_callback: Optional[Callable] = None # 新增:余额不足回调 self.log_callback: Optional[Callable] = None # 新增:日志回调 self.is_connected = False @@ -52,7 +53,7 @@ class BackendClient: """Unified logging method that works in both dev and packaged environments""" # Always print to console (visible in dev mode) print(f"[{level}] {message}") - + # Also call the log callback if available (for file logging) if self.log_callback: try: @@ -130,10 +131,10 @@ class BackendClient: self._log("已禁用心跳机制", "WARNING") self.is_connected = True - + # 🔥 在重置之前记录是否是重连(用于后续上报平台状态) was_reconnecting = self.reconnect_attempts > 0 - + self.reconnect_attempts = 0 # 重置重连计数 self.is_reconnecting = False self._log("后端WebSocket连接成功", "SUCCESS") @@ -285,6 +286,7 @@ class BackendClient: token_error: Callable = None, version: Callable = None, disconnect: Callable = None, + balance_insufficient: Callable = None, log: Callable = None): """设置各种消息类型的回调函数""" if store_list: @@ -309,6 +311,8 @@ class BackendClient: self.version_callback = version if disconnect: self.disconnect_callback = disconnect + if balance_insufficient: + self.balance_insufficient_callback = balance_insufficient if log: self.log_callback = log @@ -333,11 +337,11 @@ class BackendClient: if hasattr(manager, 'platform_listeners') and manager.platform_listeners: platform_count = len(manager.platform_listeners) self._log(f"🔄 检测到 {platform_count} 个活跃平台连接,准备重新上报状态", "INFO") - + # 延迟1秒,确保后端完全准备好 import time time.sleep(1.0) - + for platform_key, listener_info in manager.platform_listeners.items(): store_id = listener_info.get('store_id') platform = listener_info.get('platform') @@ -353,7 +357,7 @@ class BackendClient: "status": True, "cookies": "" # 重连时无需再次发送cookies } - + # 同步发送,确保发送成功 future = asyncio.run_coroutine_threadsafe( self._send_to_backend(reconnect_message), @@ -361,7 +365,7 @@ class BackendClient: ) # 等待发送完成(最多2秒) future.result(timeout=2) - + self._log(f"✅ 已重新上报 {platform} 平台状态: {store_display}", "SUCCESS") except Exception as e: self._log(f"❌ 上报 {platform} 平台状态失败: {e}", "ERROR") @@ -386,7 +390,7 @@ class BackendClient: try: from WebSocket.backend_singleton import get_websocket_manager manager = get_websocket_manager() - + # 从 platform_listeners 获取活跃平台 if hasattr(manager, 'platform_listeners') and manager.platform_listeners: # 获取第一个活跃平台的 store_id @@ -475,6 +479,8 @@ class BackendClient: self._handle_version_response(message) elif msg_type == 'disconnect': # 新增:被踢下线 self._handle_disconnect(message) + elif msg_type == 'balance_insufficient': # 新增:余额不足 + self._handle_balance_insufficient(message) else: print(f"未知消息类型: {msg_type}") @@ -1203,7 +1209,8 @@ class BackendClient: self.login_callback(platform_name, store_id, full_message, store_name) else: # 普通Cookie模式 - print(f"收到登录指令: 平台={platform_name}, 店铺={store_name or store_id}, cookies_len={len(cookies) if cookies else 0}") + print( + f"收到登录指令: 平台={platform_name}, 店铺={store_name or store_id}, cookies_len={len(cookies) if cookies else 0}") if self.login_callback: self.login_callback(platform_name, store_id, cookies, store_name) @@ -1275,6 +1282,16 @@ class BackendClient: if self.disconnect_callback: self.disconnect_callback(disconnect_message) + def _handle_balance_insufficient(self, message: Dict[str, Any]): + """处理余额不足消息""" + balance_message = message.get('content', '所有平台已断开。请充值条数后,重新连接') + + self._log(f"⚠️ 余额不足: {balance_message}", "WARNING") + + # 触发余额不足回调(在主线程中处理断开逻辑) + if self.balance_insufficient_callback: + self.balance_insufficient_callback(balance_message) + # ==================== 辅助方法 ==================== def set_token(self, token: str): diff --git a/WebSocket/backend_singleton.py b/WebSocket/backend_singleton.py index 9f7dda3..da44776 100644 --- a/WebSocket/backend_singleton.py +++ b/WebSocket/backend_singleton.py @@ -60,7 +60,8 @@ class WebSocketManager: def set_callbacks(self, log: Callable = None, success: Callable = None, error: Callable = None, platform_connected: Callable = None, platform_disconnected: Callable = None, - token_error: Callable = None, disconnect: Callable = None): + token_error: Callable = None, disconnect: Callable = None, + balance_insufficient: Callable = None): """设置回调函数""" if log: self.callbacks['log'] = log @@ -76,6 +77,8 @@ class WebSocketManager: self.callbacks['token_error'] = token_error if disconnect: self.callbacks['disconnect'] = disconnect + if balance_insufficient: + self.callbacks['balance_insufficient'] = balance_insufficient def _log(self, message: str, level: str = "INFO"): """内部日志方法""" @@ -104,15 +107,16 @@ class WebSocketManager: except Exception as e: self._log(f"通知平台连接失败: {e}", "ERROR") - def notify_platform_kicked(self, platform_name: str, store_name: str, reason: str = "账号在其他设备登录", store_id: str = None): + def notify_platform_kicked(self, platform_name: str, store_name: str, reason: str = "账号在其他设备登录", + store_id: str = None): """通知GUI平台被踢下线(供平台监听器调用)""" try: self._log(f"⚠️ 平台被踢下线: {platform_name} - {store_name}, 原因: {reason}", "WARNING") - + # 从连接列表中移除 if platform_name in self.connected_platforms: self.connected_platforms.remove(platform_name) - + # 🔥 发送平台断开消息给后端(status=false) if store_id and self.backend_client: try: @@ -126,13 +130,63 @@ class WebSocketManager: self._log(f"✅ 已通知后端 {platform_name} 平台断开: {store_id}", "INFO") except Exception as send_error: self._log(f"❌ 发送平台断开消息失败: {send_error}", "ERROR") - + # 通知GUI显示弹窗 if self.callbacks['platform_disconnected']: self.callbacks['platform_disconnected'](platform_name, store_name, reason) except Exception as e: self._log(f"通知平台断开失败: {e}", "ERROR") + def disconnect_all_async(self): + """异步断开所有连接(余额不足时调用)- 不阻塞当前线程""" + try: + self._log("🔴 收到余额不足通知,开始断开所有连接...", "ERROR") + + # 1. 断开所有平台连接 + if self.platform_listeners: + platform_count = len(self.platform_listeners) + self._log(f"正在断开 {platform_count} 个平台连接...", "INFO") + + # 复制键列表,避免遍历时修改字典 + platform_keys = list(self.platform_listeners.keys()) + + for key in platform_keys: + try: + listener_info = self.platform_listeners.get(key) + if listener_info: + platform_type = listener_info.get('platform', '') + self._log(f"断开 {platform_type} 平台连接: {key}", "INFO") + + # 移除连接记录 + self.platform_listeners.pop(key, None) + except Exception as e: + self._log(f"断开平台连接失败: {e}", "ERROR") + + self._log(f"✅ 已断开所有 {platform_count} 个平台连接", "INFO") + + # 清空连接列表 + self.connected_platforms.clear() + + # 2. 延迟断开后端连接(在新线程中执行,避免阻塞) + import threading + def _delayed_backend_disconnect(): + try: + import time + time.sleep(0.5) # 延迟0.5秒,确保上面的操作完成 + + if self.backend_client: + self.backend_client.should_stop = True + self.backend_client.is_connected = False + self._log("✅ 已标记后端连接为断开状态", "INFO") + except Exception as e: + self._log(f"断开后端连接失败: {e}", "ERROR") + + disconnect_thread = threading.Thread(target=_delayed_backend_disconnect, daemon=True) + disconnect_thread.start() + + except Exception as e: + self._log(f"断开所有连接失败: {e}", "ERROR") + def connect_backend(self, token: str) -> bool: """连接后端WebSocket""" try: @@ -188,12 +242,18 @@ class WebSocketManager: if self.callbacks['disconnect']: self.callbacks['disconnect'](disconnect_msg) + def _on_balance_insufficient(balance_msg: str): + """余额不足回调""" + if self.callbacks['balance_insufficient']: + self.callbacks['balance_insufficient'](balance_msg) + def _on_log(message: str, level: str = "INFO"): """Backend client log callback""" self._log(message, level) backend.set_callbacks(success=_on_backend_success, login=_on_backend_login, token_error=_on_token_error, disconnect=_on_disconnect, + balance_insufficient=_on_balance_insufficient, log=_on_log) if not backend.is_connected: @@ -235,12 +295,18 @@ class WebSocketManager: if self.callbacks['disconnect']: self.callbacks['disconnect'](disconnect_msg) + def _on_balance_insufficient(balance_msg: str): + """余额不足回调""" + if self.callbacks['balance_insufficient']: + self.callbacks['balance_insufficient'](balance_msg) + def _on_log(message: str, level: str = "INFO"): """Backend client log callback""" self._log(message, level) backend.set_callbacks(login=_on_backend_login, success=_on_backend_success, token_error=_on_token_error, disconnect=_on_disconnect, + balance_insufficient=_on_balance_insufficient, log=_on_log) backend.connect() @@ -261,15 +327,15 @@ class WebSocketManager: # 🔥 检查并断开当前店铺的旧连接(策略B:先断开旧连接,再建立新连接) store_key_pattern = f":{store_id}" # 匹配 "平台名:store_id" 格式 keys_to_remove = [key for key in self.platform_listeners.keys() if key.endswith(store_key_pattern)] - + if keys_to_remove: self._log(f"🔄 检测到店铺 {store_id} 重复登录,断开 {len(keys_to_remove)} 个旧连接", "INFO") - + for key in keys_to_remove: listener_info = self.platform_listeners.get(key) if listener_info: platform_type = listener_info.get('platform', '') - + # 从各平台的 WebsocketManager 中获取连接并关闭WebSocket try: if platform_type == "京东": @@ -289,7 +355,7 @@ class WebSocketManager: pass jd_mgr.remove_connection(key) self._log(f"✅ 已从京东管理器移除连接: {key}", "DEBUG") - + elif platform_type == "抖音": from Utils.Dy.DyUtils import DouYinWebsocketManager as DYWSManager dy_mgr = DYWSManager() @@ -307,13 +373,13 @@ class WebSocketManager: pass dy_mgr.remove_connection(key) self._log(f"✅ 已从抖音管理器移除连接: {key}", "DEBUG") - + elif platform_type == "千牛": from Utils.QianNiu.QianNiuUtils import QianNiuWebsocketManager as QNWSManager qn_mgr = QNWSManager() qn_mgr.remove_connection(key) self._log(f"✅ 已从千牛管理器移除连接: {key}", "DEBUG") - + elif platform_type == "拼多多": from Utils.Pdd.PddUtils import WebsocketManager as PDDWSManager pdd_mgr = PDDWSManager() @@ -332,17 +398,17 @@ class WebSocketManager: self._log(f"⚠️ 关闭WebSocket时出错: {ws_e}", "DEBUG") pdd_mgr.remove_connection(key) self._log(f"✅ 已从拼多多管理器移除连接: {key}", "DEBUG") - + except Exception as e: self._log(f"⚠️ 移除{platform_type}连接时出错: {e}", "WARNING") - + # 从监听器字典中移除 self.platform_listeners.pop(key, None) - + # 给WebSocket一点时间完全关闭 import time time.sleep(0.5) - + self._log(f"✅ 旧连接已全部断开,准备建立新连接", "INFO") # 平台名称映射 diff --git a/config.py b/config.py index f321566..794d341 100644 --- a/config.py +++ b/config.py @@ -52,7 +52,7 @@ PLATFORMS = { "ws_url": "wss://dongdong.jd.com/workbench/websocket" }, "DOUYIN": { - "name": "抖音", + "name": "抖音", "ws_url": None # 动态获取 }, "QIANNIU": { diff --git a/main.py b/main.py index 52216ed..9673db3 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,6 @@ from WebSocket.backend_singleton import get_websocket_manager from windows_taskbar_fix import setup_windows_taskbar_icon import os - # ===================== 文件日志系统 - 生产环境启用 ===================== # 重定向所有输出到文件,确保有日志记录 from exe_file_logger import setup_file_logging, log_to_file # 开发环境启用 @@ -33,6 +32,12 @@ class DisconnectSignals(QObject): disconnected = pyqtSignal(str) # (disconnect_message) +# 新增: 余额不足信号类(用于线程安全的余额不足提示) +class BalanceInsufficientSignals(QObject): + """余额不足信号""" + balance_insufficient = pyqtSignal(str) # (balance_message) + + # 新增: 用户名密码输入对话框类 class LoginWindow(QMainWindow): def __init__(self): @@ -62,6 +67,10 @@ class LoginWindow(QMainWindow): self.disconnect_signals = DisconnectSignals() self.disconnect_signals.disconnected.connect(self._show_disconnect_dialog) + # 创建余额不足信号对象(线程安全) + self.balance_insufficient_signals = BalanceInsufficientSignals() + self.balance_insufficient_signals.balance_insufficient.connect(self._show_balance_insufficient_dialog) + # 横幅相关 self.promo_banner = None self.banner_shadow = None # 阴影效果引用 @@ -170,7 +179,7 @@ class LoginWindow(QMainWindow): # 系统初始化日志输出到终端 print("[INFO] 系统初始化完成") - + # 让窗口自适应内容高度(最小化) self.adjustSize() print("[INFO] 窗口已自适应内容大小") @@ -182,41 +191,41 @@ class LoginWindow(QMainWindow): banner_frame = QFrame() banner_frame.setObjectName("promoBanner") banner_frame.setCursor(Qt.PointingHandCursor) # 鼠标变手型 - + # 保存横幅引用,用于动画 self.promo_banner = banner_frame - + # 使用图片标签 banner_image = QLabel() banner_image.setObjectName("bannerImage") - + # 加载横幅图片 from windows_taskbar_fix import get_resource_path image_path = get_resource_path("static/hengfu.jpg") - + if os.path.exists(image_path): from PyQt5.QtGui import QPixmap pixmap = QPixmap(image_path) - + # 设置固定的横幅尺寸 target_width = 400 # 固定宽度 target_height = 70 # 固定高度(调整为70px,更紧凑) - + # 缩放图片以适应目标尺寸 scaled_pixmap = pixmap.scaled( - target_width, + target_width, target_height, Qt.IgnoreAspectRatio, # 忽略宽高比,拉伸填充 Qt.SmoothTransformation # 平滑缩放,提高图片质量 ) - + # 设置图片 banner_image.setPixmap(scaled_pixmap) banner_image.setScaledContents(False) - + banner_frame.setFixedHeight(target_height) banner_image.setFixedSize(target_width, target_height) - + # 🔧 使用CSS圆角来处理边角(更简单稳定) banner_image.setStyleSheet(""" QLabel#bannerImage { @@ -224,7 +233,7 @@ class LoginWindow(QMainWindow): background-color: transparent; } """) - + print(f"[INFO] 横幅图片已加载: {image_path}, 尺寸: {target_width}x{target_height}") else: # 图片不存在时的备用方案 @@ -241,14 +250,14 @@ class LoginWindow(QMainWindow): """) banner_frame.setFixedHeight(60) print(f"[WARNING] 横幅图片未找到: {image_path},使用备用样式") - + # 创建布局 banner_layout = QHBoxLayout() banner_layout.setContentsMargins(0, 0, 0, 0) banner_layout.setSpacing(0) banner_frame.setLayout(banner_layout) banner_layout.addWidget(banner_image) - + # 设置圆角和边框 banner_frame.setStyleSheet(""" QFrame#promoBanner { @@ -256,7 +265,7 @@ class LoginWindow(QMainWindow): background: transparent; } """) - + # 初始阴影效果 self.banner_shadow = QGraphicsDropShadowEffect() self.banner_shadow.setBlurRadius(15) @@ -264,7 +273,7 @@ class LoginWindow(QMainWindow): self.banner_shadow.setYOffset(3) self.banner_shadow.setColor(QColor(0, 0, 0, 60)) banner_frame.setGraphicsEffect(self.banner_shadow) - + # 点击事件 - 跳转官网 def open_official_website(): try: @@ -274,9 +283,9 @@ class LoginWindow(QMainWindow): self.add_log(f"已打开官网: {official_url}", "INFO") except Exception as e: self.add_log(f"打开官网失败: {e}", "ERROR") - + banner_frame.mousePressEvent = lambda event: open_official_website() - + # 🎯 添加鼠标悬停事件(动态效果) def on_enter(event): """鼠标进入时的动画效果""" @@ -285,30 +294,30 @@ class LoginWindow(QMainWindow): self.banner_shadow.setBlurRadius(25) self.banner_shadow.setYOffset(5) self.banner_shadow.setColor(QColor(0, 0, 0, 100)) - + # 轻微放大动画 animation = QPropertyAnimation(banner_frame, b"geometry") original_rect = banner_frame.geometry() - + expanded_rect = QRect( original_rect.x() - 3, original_rect.y() - 2, original_rect.width() + 6, original_rect.height() + 4 ) - + animation.setDuration(200) animation.setStartValue(original_rect) animation.setEndValue(expanded_rect) animation.setEasingCurve(QEasingCurve.OutCubic) animation.start() - + # 保存动画引用避免被垃圾回收 banner_frame.hover_animation = animation - + except Exception as e: print(f"[DEBUG] 悬停动画错误: {e}") - + def on_leave(event): """鼠标离开时的动画效果""" try: @@ -316,42 +325,42 @@ class LoginWindow(QMainWindow): self.banner_shadow.setBlurRadius(15) self.banner_shadow.setYOffset(3) self.banner_shadow.setColor(QColor(0, 0, 0, 60)) - + # 恢复原始大小 animation = QPropertyAnimation(banner_frame, b"geometry") current_rect = banner_frame.geometry() - + original_rect = QRect( current_rect.x() + 3, current_rect.y() + 2, current_rect.width() - 6, current_rect.height() - 4 ) - + animation.setDuration(200) animation.setStartValue(current_rect) animation.setEndValue(original_rect) animation.setEasingCurve(QEasingCurve.InCubic) animation.start() - + # 保存动画引用避免被垃圾回收 banner_frame.leave_animation = animation - + except Exception as e: print(f"[DEBUG] 离开动画错误: {e}") - + # 安装事件过滤器 banner_frame.enterEvent = on_enter banner_frame.leaveEvent = on_leave - + # 添加到主布局 layout.addWidget(banner_frame) - + print("[INFO] 宣传横幅已创建(使用图片,带悬停动画)") - + except Exception as e: print(f"[WARNING] 创建宣传横幅失败: {e}") - + # 横幅颜色动画相关方法已移除(改用图片横幅) def apply_modern_styles(self): @@ -617,7 +626,7 @@ class LoginWindow(QMainWindow): """处理平台被踢下线 - 显示弹窗警告""" try: self.add_log(f"⚠️ {platform_name}平台被踢下线: {store_name}, 原因: {reason}", "WARNING") - + # 显示弹窗提示 message_text = ( f"【{store_name}】连接已断开\n\n" @@ -634,12 +643,12 @@ class LoginWindow(QMainWindow): message_text, QMessageBox.Ok ) - + # 更新状态显示 self.status_label.setText(f"⚠️ {platform_name}已断开") self.status_label.setStyleSheet( "color: #ff6b6b; background: rgba(255, 107, 107, 0.1); border-radius: 12px; padding: 5px 10px;") - + except Exception as e: self.add_log(f"显示平台断开提示失败: {e}", "ERROR") @@ -678,6 +687,18 @@ class LoginWindow(QMainWindow): import traceback self.add_log(f"详细错误: {traceback.format_exc()}", "ERROR") + def on_balance_insufficient(self, balance_msg: str): + """处理余额不足 - 发射信号到主线程(可在任何线程中调用)""" + try: + self.add_log(f"📡 收到余额不足通知,准备发射信号: {balance_msg}", "INFO") + # 发射信号(Qt 自动调度到主线程) + self.balance_insufficient_signals.balance_insufficient.emit(balance_msg) + self.add_log(f"✅ 余额不足信号已发射", "DEBUG") + except Exception as e: + self.add_log(f"❌ 发射余额不足信号失败: {e}", "ERROR") + import traceback + self.add_log(f"详细错误: {traceback.format_exc()}", "ERROR") + def _show_disconnect_dialog(self, disconnect_msg: str): """显示断开连接提示框(信号槽函数,始终在主线程中执行)""" try: @@ -714,6 +735,49 @@ class LoginWindow(QMainWindow): import traceback self.add_log(f"详细错误: {traceback.format_exc()}", "ERROR") + def _show_balance_insufficient_dialog(self, balance_msg: str): + """显示余额不足提示框(信号槽函数,始终在主线程中执行)""" + try: + self.add_log(f"🎯 主线程收到余额不足信号: {balance_msg}", "INFO") + + # 1. 断开所有连接(异步,不阻塞) + ws_manager = get_websocket_manager() + if ws_manager: + self.add_log("🔴 开始断开所有连接...", "INFO") + ws_manager.disconnect_all_async() + self.add_log("✅ 断开连接指令已发送", "INFO") + + # 2. 更新UI状态 + self.status_label.setText("🔴 余额不足") + self.status_label.setStyleSheet( + "color: #dc3545; background: rgba(220, 53, 69, 0.1); border-radius: 12px; padding: 5px 10px; font-weight: bold;") + + # 3. 重置按钮状态 + self.login_btn.setEnabled(True) + self.login_btn.setText("重新连接") + self.login_btn.setObjectName("loginButton") + self.login_btn.setStyleSheet(self.login_btn.styleSheet()) # 刷新样式 + + # 4. 清空已连接平台列表 + self.connected_platforms.clear() + + # 5. 显示弹窗提示(主线程中执行,不会卡顿) + QMessageBox.critical( + self, + "余额不足", + f"{balance_msg}\n\n" + f"已断开所有平台连接和后端连接。\n\n" + f"请充值后,点击「连接服务」按钮重新连接。", + QMessageBox.Ok + ) + + self.add_log("✅ 余额不足提示已显示,所有连接已断开", "INFO") + + except Exception as e: + self.add_log(f"❌ 显示余额不足提示框失败: {e}", "ERROR") + import traceback + self.add_log(f"详细错误: {traceback.format_exc()}", "ERROR") + def delayed_platform_summary(self): """定时器触发的汇总显示更新""" try: @@ -818,16 +882,16 @@ class LoginWindow(QMainWindow): try: # 清空现有菜单 self.tray_menu.clear() - + # 获取WebSocket管理器 ws_manager = get_websocket_manager() - + # 1. 显示后端连接状态 if ws_manager and ws_manager.backend_client and ws_manager.backend_client.is_connected: backend_status = QAction("✅ 后端已连接", self) backend_status.setEnabled(False) # 不可点击 self.tray_menu.addAction(backend_status) - + # 2. 显示已连接的平台信息 platform_listeners = ws_manager.platform_listeners if platform_listeners: @@ -837,18 +901,18 @@ class LoginWindow(QMainWindow): platform = info.get('platform', '') store_name = info.get('store_name', '') store_id = info.get('store_id', '') - + if platform not in platform_stores: platform_stores[platform] = [] - + # 优先显示店铺名称,如果没有则显示 store_id 前8位 if store_name: display_name = store_name else: display_name = store_id[:8] + "..." if len(store_id) > 8 else store_id - + platform_stores[platform].append(display_name) - + # 显示每个平台的店铺 for platform, stores in platform_stores.items(): stores_text = ", ".join(stores) @@ -865,23 +929,23 @@ class LoginWindow(QMainWindow): backend_disconnected = QAction("❌ 后端未连接", self) backend_disconnected.setEnabled(False) self.tray_menu.addAction(backend_disconnected) - + # 添加分隔线 self.tray_menu.addSeparator() - + # 3. 显示主窗口 show_action = QAction("显示主窗口", self) show_action.triggered.connect(self.show_window) self.tray_menu.addAction(show_action) - + # 添加分隔线 self.tray_menu.addSeparator() - + # 4. 退出程序 quit_action = QAction("退出程序", self) quit_action.triggered.connect(self.quit_application) self.tray_menu.addAction(quit_action) - + except Exception as e: print(f"[ERROR] 更新托盘菜单失败: {e}") # 如果更新失败,至少保证基本功能可用 @@ -889,7 +953,7 @@ class LoginWindow(QMainWindow): show_action = QAction("显示主窗口", self) show_action.triggered.connect(self.show_window) self.tray_menu.addAction(show_action) - + quit_action = QAction("退出程序", self) quit_action.triggered.connect(self.quit_application) self.tray_menu.addAction(quit_action) @@ -1004,7 +1068,8 @@ class LoginWindow(QMainWindow): platform_connected=self.on_platform_connected, # 新增:平台连接回调 platform_disconnected=self.on_platform_kicked, # 新增:平台被踢回调 token_error=self.on_token_error, # 新增:token错误回调 - disconnect=self.on_disconnect # 新增:被踢下线回调 + disconnect=self.on_disconnect, # 新增:被踢下线回调 + balance_insufficient=self.on_balance_insufficient # 新增:余额不足回调 ) # 连接后端