feat(achievement): 添加判定核心、通知模块并接入判题链路

This commit is contained in:
2026-08-04 01:02:29 -06:00
parent 9e0bc1fa8c
commit 95bf02efbd
4 changed files with 185 additions and 0 deletions

59
achievement/notify.py Normal file
View File

@@ -0,0 +1,59 @@
"""解锁通知。
通知走推拉结合UserAchievement.notified 是唯一的真相来源:
- 拉(主):前端在布局层拉 /api/achievements/pending覆盖全部场景
- 推增强WebSocket 只负责把"当场那一下"的延迟压到几百毫秒
必须推拉结合的原因:前端 WebSocket 不是常驻连接useSubmissionWebSocket
只在问题页且有提交监听时建连,纯推会丢消息。
"""
import logging
from utils.websocket import push_to_user
logger = logging.getLogger(__name__)
def notify_achievements(user_id, records):
"""records: list[UserAchievement],已带 select_related('achievement')。"""
if not records:
return
payload = [
{
"id": r.achievement_id,
"name": r.achievement.name,
"description": r.achievement.description,
"icon": r.achievement.icon,
"rarity": r.achievement.rarity,
"kind": "achievement",
}
for r in records
]
_push(user_id, payload)
def notify_badges(user_id, badges):
"""badges: list[ProblemSetBadge]。题单奖章复用同一个弹窗组件。"""
if not badges:
return
payload = [
{
"id": b.id,
"name": b.name,
"description": b.description,
"icon": b.icon,
"rarity": "bronze",
"kind": "badge",
}
for b in badges
]
_push(user_id, payload)
def _push(user_id, payload):
# 推送失败不影响已入库的解锁记录,前端下次拉 pending 时仍会补弹
try:
push_to_user(user_id, "achievement_unlocked", {"achievements": payload})
except Exception as e:
logger.error(f"Failed to push achievement notification: user_id={user_id}, error={e}")