[patch] 新增用户余额不足 交互模式代码

This commit is contained in:
2025-10-17 17:38:02 +08:00
parent a5c2a44512
commit 8eb03ddedc
4 changed files with 225 additions and 77 deletions

169
main.py
View File

@@ -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 # 新增:余额不足回调
)
# 连接后端