Files
FastapiAdmin/backend/app/api/v1/module_common/sse/controller.py
T
zhangtao a222cd9e43 refactor: 移除多租户相关代码,重构为单租户架构
此次提交进行了大规模的架构重构:
1.  移除所有平台租户相关模块和代码,包括租户管理、套餐、订单、发票等功能
2.  将菜单模块从platform迁移到system模块,统一系统功能入口
3.  移除租户隔离相关的模型混入、中间件和配置
4.  简化文件上传、SSE事件总线、定时任务等模块的租户逻辑
5.  重构所有业务schema和模型,移除租户相关字段和关联
6.  清理初始化脚本、模板和常量中的租户相关代码
7.  简化认证和权限控制逻辑,移除数据范围检查相关代码
2026-07-16 23:22:45 +08:00

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")