From e42aa86fb06c0ea6e94cbefda37ea4f67edf1278 Mon Sep 17 00:00:00 2001 From: Supercmd Date: Sat, 4 Apr 2026 17:05:03 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(=E6=8A=BD=E5=A5=96=E7=AE=A1=E7=90=86):?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E6=8A=BD=E5=A5=96=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E4=B8=AD=E5=AD=A6=E7=94=9FID=E6=9C=AA=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E4=BC=A0=E9=80=92=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 确保抽奖结果中的学生ID能够正确从候选数据中提取并传递到奖品信息中 同时修正抽奖结果显示时学生ID的获取逻辑 --- app/common/lottery/lottery_manager.py | 27 ++++++++++-- requirements-linux.txt | 55 ------------------------- requirements-windows.txt | 59 --------------------------- 3 files changed, 23 insertions(+), 118 deletions(-) delete mode 100644 requirements-linux.txt delete mode 100644 requirements-windows.txt diff --git a/app/common/lottery/lottery_manager.py b/app/common/lottery/lottery_manager.py index f328a311..edab1527 100644 --- a/app/common/lottery/lottery_manager.py +++ b/app/common/lottery/lottery_manager.py @@ -450,8 +450,10 @@ def get_random_items(self, count): group_name = "" student_name = "" + student_id = 0 if self.current_group_index == 1: - raw_group = system_random.choice(candidates).get("name", "") + selected_candidate = system_random.choice(candidates) + raw_group = selected_candidate.get("name", "") include_group = show_random in (0, 1, 2, 5, 6, 7, 8, 9) include_name = show_random in (0, 1, 2, 3, 4, 7, 8, 9, 10, 11) @@ -464,13 +466,23 @@ def get_random_items(self, count): if group_members: selected_member = system_random.choice(group_members) student_name = (selected_member or {}).get("name", "") + try: + student_id = int((selected_member or {}).get("id", 0) or 0) + except Exception: + student_id = 0 if not student_name: student_name = raw_group else: - student_name = system_random.choice(candidates).get("name", "") + selected_candidate = system_random.choice(candidates) + student_name = selected_candidate.get("name", "") + try: + student_id = int(selected_candidate.get("id", 0) or 0) + except Exception: + student_id = 0 prize_copy["ipc_group_name"] = str(group_name or "") prize_copy["ipc_student_name"] = str(student_name or "") + prize_copy["student_id"] = student_id if group_name or student_name: prize_copy["name"] = self._format_prize_student_text( prize_name, group_name, student_name, show_random @@ -1125,9 +1137,13 @@ def draw_random(widget): for p in prizes or []: if not isinstance(p, dict): continue + try: + sid = int(p.get("student_id", 0) or 0) + except Exception: + sid = 0 ipc_selected_students.append( { - "student_id": 0, + "student_id": sid, "student_name": str(p.get("ipc_student_name", "") or ""), "display_text": str( p.get("ipc_display_text", p.get("name", "")) or "" @@ -1139,7 +1155,10 @@ def draw_random(widget): ), } ) - selected_prizes = [(p["id"], p["name"], p.get("exist", True)) for p in prizes] + selected_prizes = [ + (p.get("student_id", 0) or 0, p["name"], p.get("exist", True)) + for p in prizes + ] display_result_animated( widget, diff --git a/requirements-linux.txt b/requirements-linux.txt deleted file mode 100644 index 5a6b12da..00000000 --- a/requirements-linux.txt +++ /dev/null @@ -1,55 +0,0 @@ -# Linux 依赖配置 - 支持 Python 3.8.10 - -# === GUI框架 === -PySide6==6.7.1 -PySide6-Qt6==6.7.3 -PySide6-Fluent-Widgets==1.9.1 -PySide6-Frameless-Window==0.7.4 -darkdetect==0.8.0 - -# === 核心库 === -asyncio~=3.4.3 -loguru==0.7.3 -colorama==0.4.6 -packaging==25.0 - -# === 数据处理 === -numpy~=1.24.4 -pandas~=2.0.3 -pillow~=10.4.0 -openpyxl==3.1.5 -xlrd>=2.0.1 - -# === 网络与通信 === -requests==2.32.4 -edge-tts==7.0.2 - -# === 音频处理 === -pyttsx3==2.98 -sounddevice==0.5.2 -soundfile==0.13.1 - -# === 系统工具 === -psutil~=7.0.0 -keyboard==0.13.5 - -# === 加密与安全 === -pyotp==2.9.0 -pycryptodome==3.23.0 - -# === 二维码处理 === -pyqrcode~=1.2.1 -pypng~=0.20220715.0 -colorthief==0.2.1 - -# === Linux特定依赖 === -# Linux音频控制替代pycaw -pulsectl==24.8.0; platform_system == "Linux" - -# === 其他依赖 === -sip~=6.8.6 -jinja2~=3.1.6 -pyyaml>=6.0.1 - -# === 通知工具 === -plyer>=2.1.0 diff --git a/requirements-windows.txt b/requirements-windows.txt deleted file mode 100644 index 5bd1e2d3..00000000 --- a/requirements-windows.txt +++ /dev/null @@ -1,59 +0,0 @@ -# Windows 依赖配置 - 支持 Python 3.8.10 - -# === GUI框架 === -PySide6==6.7.1 -PySide6-Qt6==6.7.3 -PySide6-Fluent-Widgets==1.9.1 -PySide6-Frameless-Window==0.7.4 -darkdetect==0.8.0 - -# === 核心库 === -asyncio~=3.4.3 -loguru==0.7.3 -colorama==0.4.6 -packaging==25.0 - -# === 数据处理 === -numpy~=1.24.4 -pandas~=2.0.3 -pillow~=10.4.0 -openpyxl==3.1.5 -xlrd>=2.0.1 - -# === 网络与通信 === -requests==2.32.4 -edge-tts==7.0.2 - -# === 音频处理 === -pyttsx3==2.98 -sounddevice==0.5.2 -soundfile==0.13.1 - -# === 系统工具 === -psutil~=7.0.0 -keyboard==0.13.5 - -# === 加密与安全 === -pyotp==2.9.0 -pycryptodome==3.23.0 - -# === 二维码处理 === -pyqrcode~=1.2.1 -pypng~=0.20220715.0 -colorthief==0.2.1 - -# === Windows特定依赖 === -pywin32==310; platform_system == "Windows" -win32_setctime==1.2.0; platform_system == "Windows" -winshell==0.6; platform_system == "Windows" -comtypes==1.4.10 -wmi==1.5.1 -pycaw==20240210; platform_system == "Windows" - -# === 其他依赖 === -sip~=6.8.6 -jinja2~=3.1.6 -pyyaml>=6.0.1 - -# === 通知工具 === -plyer>=2.1.0 From 3251e05a168c488a22cc2ba4442baf921c508180 Mon Sep 17 00:00:00 2001 From: Supercmd Date: Sat, 4 Apr 2026 17:39:44 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(=E5=8D=95=E5=AE=9E=E4=BE=8B):=20?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=85=B1=E4=BA=AB=E5=86=85=E5=AD=98=E5=92=8C?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E6=9C=8D=E5=8A=A1=E5=99=A8=E7=9A=84=E5=81=A5?= =?UTF-8?q?=E5=A3=AE=E6=80=A7=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当无法附加到共享内存时,增加服务器状态检测和资源清理逻辑 修复本地服务器启动失败时未清理残留socket的问题 --- app/core/single_instance.py | 48 +++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/app/core/single_instance.py b/app/core/single_instance.py index e216b7ac..4b43d69e 100644 --- a/app/core/single_instance.py +++ b/app/core/single_instance.py @@ -20,13 +20,47 @@ def check_single_instance() -> Tuple[Optional[QSharedMemory], bool]: _activate_existing_instance() return shared_memory, False else: - logger.exception("无法附加到共享内存") - return shared_memory, False + logger.warning("无法附加到共享内存,尝试检测服务器状态") + if _check_server_alive(): + logger.info("检测到活跃的服务器,已有实例正在运行") + _activate_existing_instance() + return shared_memory, False + else: + logger.warning("服务器无响应,清理残留资源后重新启动") + _cleanup_stale_resources() + if shared_memory.create(1): + logger.info("单实例检查通过(清理后重新创建)") + return shared_memory, True + else: + logger.exception("清理后仍无法创建共享内存") + return shared_memory, False logger.info("单实例检查通过,可以安全启动程序") return shared_memory, True +def _check_server_alive() -> bool: + """检查本地服务器是否有响应 + + Returns: + bool: 服务器是否有响应 + """ + local_socket = QLocalSocket() + local_socket.connectToServer(SHARED_MEMORY_KEY) + connected = local_socket.waitForConnected(500) + if connected: + local_socket.disconnectFromServer() + return True + return False + + +def _cleanup_stale_resources() -> None: + """清理残留的单实例资源 + """ + QLocalServer.removeServer(SHARED_MEMORY_KEY) + logger.debug("已清理残留的socket资源") + + def _activate_existing_instance() -> bool: """激活已有实例 @@ -74,8 +108,14 @@ def setup_local_server( """ server = QLocalServer() if not server.listen(SHARED_MEMORY_KEY): - logger.exception(f"无法启动本地服务器: {server.errorString()}") - return None + error_string = server.errorString() + logger.warning(f"本地服务器启动失败,尝试清理残留socket: {error_string}") + + QLocalServer.removeServer(SHARED_MEMORY_KEY) + + if not server.listen(SHARED_MEMORY_KEY): + logger.exception(f"无法启动本地服务器: {server.errorString()}") + return None server.newConnection.connect( lambda: _handle_new_connection(server, main_window, float_window, url_handler) From db5c6cf7661a303f2639a087a54935b150fac26a Mon Sep 17 00:00:00 2001 From: Supercmd Date: Sat, 4 Apr 2026 18:06:08 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(=E5=8D=95=E5=AE=9E=E4=BE=8B):=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0POSIX=E7=B3=BB=E7=BB=9F=E4=B8=8B=E5=85=B1?= =?UTF-8?q?=E4=BA=AB=E5=86=85=E5=AD=98=E6=AE=8B=E7=95=99=E6=B8=85=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在POSIX系统上,QSharedMemory在进程崩溃后不会自动清理残留的共享内存段 添加_cleanup_stale_shared_memory函数来检测并清理这些残留资源 --- app/core/single_instance.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/core/single_instance.py b/app/core/single_instance.py index 4b43d69e..1fa7cd83 100644 --- a/app/core/single_instance.py +++ b/app/core/single_instance.py @@ -60,6 +60,21 @@ def _cleanup_stale_resources() -> None: QLocalServer.removeServer(SHARED_MEMORY_KEY) logger.debug("已清理残留的socket资源") + _cleanup_stale_shared_memory() + + +def _cleanup_stale_shared_memory() -> None: + """清理残留的共享内存段(POSIX系统需要) + + 在POSIX系统上,QSharedMemory在进程崩溃后不会自动清理。 + 需要先attach再detach来触发清理。 + """ + stale_memory = QSharedMemory(SHARED_MEMORY_KEY) + if stale_memory.attach(): + logger.debug("检测到残留的共享内存段,正在清理") + stale_memory.detach() + logger.debug("已清理残留的共享内存段") + def _activate_existing_instance() -> bool: """激活已有实例