feat(huya): 协作台账增加 WebSocket 实时同步

- 后端新增进程内事件总线与 /api/huya/external-orders/ws\n- 所有写接口成功后发布轻量失效事件\n- 前端自动重连、静默刷新、断线兜底轮询
This commit is contained in:
yml2213
2026-10-17 13:44:27 +08:00
parent 26b8d6b054
commit cad06ae789
6 changed files with 519 additions and 12 deletions
+61
View File
@@ -0,0 +1,61 @@
"""协作台账实时事件总线:revision、跨线程投递、队列满时保留最新事件。"""
from __future__ import annotations
import asyncio
import threading
from web.backend.services.huya_collab_events import CollabEventHub
def test_publish_increments_revision_without_subscribers():
hub = CollabEventHub()
first = hub.publish(scope="orders", action="order.create", actor_id=7)
second = hub.publish(scope="shops", action="shop.create", actor_id=8)
assert first["revision"] == 1
assert second["revision"] == 2
assert hub.ready_event() == {"type": "collab.ready", "revision": 2}
assert hub.ping_event() == {"type": "collab.ping", "revision": 2}
def test_publish_delivers_from_worker_thread():
async def scenario():
hub = CollabEventHub()
queue = hub.subscribe()
worker = threading.Thread(
target=lambda: hub.publish(
scope="orders", action="order.update", actor_id=9
)
)
worker.start()
worker.join(timeout=2)
event = await asyncio.wait_for(queue.get(), timeout=2)
assert event["type"] == "collab.invalidate"
assert event["scope"] == "orders"
assert event["action"] == "order.update"
assert event["actor_id"] == 9
assert event["revision"] == 1
asyncio.run(scenario())
def test_queue_overflow_keeps_latest_event():
async def scenario():
hub = CollabEventHub()
queue = hub.subscribe()
for _ in range(101):
hub.publish(scope="orders", action="order.update", actor_id=1)
await asyncio.sleep(0.05)
assert queue.qsize() <= queue.maxsize
latest = await asyncio.wait_for(queue.get(), timeout=2)
# 队列满时丢掉旧事件,但最后一条一定是 revision 最大的
while not queue.empty():
latest = queue.get_nowait()
assert latest["revision"] == 101
asyncio.run(scenario())
+28
View File
@@ -28,7 +28,10 @@ from web.backend.models import (
User,
)
from web.backend.permissions import ROLE_PERMISSIONS
from web.backend.routers.huya import collab as collab_api
from web.backend.schemas import HuyaExternalOrderUpdate
from web.backend.services import huya_collab_service as collab
from web.backend.services.huya_collab_events import collab_events
def fake_ck(uid: str, *, web: bool = True) -> str:
@@ -1460,6 +1463,31 @@ class TestCollabGiftFilter(TestCollabBase):
)
class TestCollabRealtimeEvents(TestCollabBase):
def test_update_order_publishes_invalidate_event(self, monkeypatch):
order = self._create(order_no="RT-1")
captured: dict = {}
def fake_publish(**kwargs):
captured.update(kwargs)
return kwargs
monkeypatch.setattr(collab_events, "publish", fake_publish)
updated = collab_api.update_external_order(
order.id,
HuyaExternalOrderUpdate(remark="实时更新", expected_version=order.version),
self.db,
self.user,
)
assert updated["remark"] == "实时更新"
assert captured == {
"scope": "orders",
"action": "order.update",
"actor_id": self.user.id,
}
class TestCollabBuyerCluster(TestCollabBase):
"""同一买家的多单:按「绑定账号 + 绑定角色」软识别(只读派生,不动 uid 唯一性)。