Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions app/common/lottery/lottery_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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 ""
Expand All @@ -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
]
Comment on lines +1158 to +1161
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

draw_random() now builds selected_prizes using p.get("student_id", 0) for the tuple's num. When get_random_items() returns prizes without a student_id (e.g., student-assignment disabled, or candidates is empty and it returns selected_prizes directly), this will force the displayed number to 0/00 for all animation frames and changes prior behavior (previously used p["id"]). Consider falling back to the prize id when student_id is missing/0 (or using None to suppress the number), so non-assignment / empty-candidate cases continue to display correctly while still using real student ids when available.

Suggested change
selected_prizes = [
(p.get("student_id", 0) or 0, p["name"], p.get("exist", True))
for p in prizes
]
selected_prizes = []
for p in prizes:
try:
display_num = int(p.get("student_id", 0) or 0)
except Exception:
display_num = 0
if not display_num:
try:
display_num = int(p.get("id", 0) or 0)
except Exception:
display_num = 0
selected_prizes.append(
(display_num, p["name"], p.get("exist", True))
)

Copilot uses AI. Check for mistakes.

display_result_animated(
widget,
Expand Down
63 changes: 59 additions & 4 deletions app/core/single_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,62 @@ 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
Comment on lines 20 to +27
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本 PR 代码中还删除了 requirements-windows.txtrequirements-linux.txt 两个依赖文件,但 PR 描述里未提及这一变更。若这是有意切换到 pyproject.toml/uv.lock 作为唯一依赖来源,建议在描述中补充说明(或拆分为独立 PR),以便评审者确认对打包/CI/文档没有影响。

Copilot uses AI. Check for mistakes.
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
Comment on lines +42 to +54
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_check_server_alive()的注释写的是“服务器是否有响应”,但当前实现只检查是否能在超时内建立连接(并未验证对端能处理/回应任何数据)。建议要么把注释/日志措辞改为“是否可连接/是否在监听”,要么实现一个简单的 ping(写入并等待对端读/回)来匹配“有响应”的语义。

Copilot uses AI. Check for mistakes.


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:
"""激活已有实例

Expand Down Expand Up @@ -74,8 +123,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)
Expand Down
55 changes: 0 additions & 55 deletions requirements-linux.txt

This file was deleted.

59 changes: 0 additions & 59 deletions requirements-windows.txt

This file was deleted.

Loading