mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 12:52:26 +00:00
此次提交进行了大规模的架构重构: 1. 移除所有平台租户相关模块和代码,包括租户管理、套餐、订单、发票等功能 2. 将菜单模块从platform迁移到system模块,统一系统功能入口 3. 移除租户隔离相关的模型混入、中间件和配置 4. 简化文件上传、SSE事件总线、定时任务等模块的租户逻辑 5. 重构所有业务schema和模型,移除租户相关字段和关联 6. 清理初始化脚本、模板和常量中的租户相关代码 7. 简化认证和权限控制逻辑,移除数据范围检查相关代码
67 lines
2.2 KiB
Python
67 lines
2.2 KiB
Python
"""SSE 事件推送端点"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
from collections.abc import AsyncIterable
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Depends, Header, Query
|
|
from fastapi.sse import EventSourceResponse, ServerSentEvent
|
|
|
|
from app.core.base_schema import AuthSchema
|
|
from app.core.dependencies import get_current_user_ws
|
|
from app.core.event_bus import EventBus
|
|
from app.core.logger import logger
|
|
|
|
SSERouter = APIRouter(prefix="/events", tags=["SSE 事件推送"])
|
|
|
|
|
|
@SSERouter.get("", response_class=EventSourceResponse)
|
|
async def sse_event_stream(
|
|
token: Annotated[str, Query(..., description="认证 token")],
|
|
auth: Annotated[AuthSchema, Depends(get_current_user_ws)],
|
|
last_event_id: Annotated[str | None, Header()] = None,
|
|
) -> AsyncIterable[ServerSentEvent]:
|
|
"""SSE 事件流端点
|
|
|
|
客户端通过 EventSource 连接后可持续接收服务端推送的事件。
|
|
连接断开后浏览器自动重连,并通过 Last-Event-ID 恢复断连期间的事件。
|
|
|
|
## 事件类型
|
|
- `notification` — 系统通知
|
|
- `payment_success` — 支付成功
|
|
- `ticket_reply` — 工单回复
|
|
|
|
连接保活由 FastAPI 自动处理(每 15 秒发送 ping 注释)。
|
|
"""
|
|
user_id = auth.user.id
|
|
|
|
queue = EventBus.subscribe(user_id)
|
|
logger.info(f"SSE 连接建立: user_id={user_id}")
|
|
|
|
# 解析断连前最后收到的事件 ID,用于恢复
|
|
event_id = int(last_event_id) if last_event_id and last_event_id.isdigit() else 0
|
|
|
|
try:
|
|
while True:
|
|
payload = await queue.get()
|
|
event_id += 1
|
|
yield _to_sse_event(payload, str(event_id))
|
|
except asyncio.CancelledError:
|
|
pass
|
|
finally:
|
|
EventBus.unsubscribe(user_id)
|
|
logger.info(f"SSE 连接断开: user_id={user_id}")
|
|
|
|
|
|
def _to_sse_event(payload: str, event_id: str) -> ServerSentEvent:
|
|
"""将 EventBus JSON 消息转换为 ServerSentEvent"""
|
|
try:
|
|
data = json.loads(payload)
|
|
event_type = data.pop("type", "message")
|
|
return ServerSentEvent(data=data, event=event_type, id=event_id)
|
|
except json.JSONDecodeError:
|
|
return ServerSentEvent(raw_data=payload, id=event_id, event="raw")
|