refactor: 推进后端分层架构治理

This commit is contained in:
jxxghp
2026-08-18 00:29:14 +08:00
parent 5d0bacabd5
commit 5128ae9e1e
363 changed files with 34179 additions and 6019 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
from threading import Lock
from typing import Any, Dict, List, Optional, Protocol, Sequence, Tuple, Union
from app.schemas import Message
from app.schemas.message import Message
from app.schemas.notification import ChannelCapabilityManager
from app.schemas.types import NotificationChannel
+1 -1
View File
@@ -6,7 +6,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
from app.application.messaging.interaction import InteractionContext, MessageGateway
from app.runtime.events import EventManager
from app.schemas import Message
from app.schemas.message import Message
from app.schemas.types import EventType, NotificationChannel
+91
View File
@@ -0,0 +1,91 @@
"""消息入口的用户会话状态用例。"""
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Callable, MutableMapping, Optional, Union
UserId = Union[str, int]
SessionEntry = tuple[str, datetime]
ExpiredSessionHandler = Callable[[str, UserId], None]
Clock = Callable[[], datetime]
SessionIdFactory = Callable[[UserId, datetime], str]
@dataclass(frozen=True, slots=True)
class SessionResolution:
"""描述用户会话解析结果及是否复用了旧会话。"""
session_id: str
reused: bool
inactive_minutes: float = 0.0
class MessageSessionService:
"""管理消息用户到 Agent 会话的绑定、复用和过期清理。"""
def __init__(
self,
*,
sessions: MutableMapping[UserId, SessionEntry],
timeout_minutes: int,
expired_handler: ExpiredSessionHandler,
clock: Clock = datetime.now,
session_id_factory: Optional[SessionIdFactory] = None,
) -> None:
"""保存共享会话映射和由 Chain 提供的 Agent 清理端口。"""
self._sessions = sessions
self._timeout = timedelta(minutes=timeout_minutes)
self._expired_handler = expired_handler
self._clock = clock
self._session_id_factory = session_id_factory or self._default_session_id
@staticmethod
def _default_session_id(user_id: UserId, now: datetime) -> str:
"""按历史格式生成新的用户会话 ID。"""
return f"user_{user_id}_{int(now.timestamp())}"
def cleanup(self, now: Optional[datetime] = None) -> None:
"""移除超时绑定,并通知拥有者释放对应 Agent 会话。"""
current_time = now or self._clock()
for user_id, (session_id, last_time) in list(self._sessions.items()):
if current_time - last_time <= self._timeout:
continue
self._sessions.pop(user_id, None)
self._expired_handler(session_id, user_id)
def resolve(self, user_id: UserId) -> SessionResolution:
"""复用有效绑定或为用户创建新会话。"""
current_time = self._clock()
self.cleanup(current_time)
current = self._sessions.get(user_id)
if current:
session_id, last_time = current
inactive = current_time - last_time
if inactive <= self._timeout:
self._sessions[user_id] = (session_id, current_time)
return SessionResolution(
session_id=session_id,
reused=True,
inactive_minutes=inactive.total_seconds() / 60,
)
session_id = self._session_id_factory(user_id, current_time)
self._sessions[user_id] = (session_id, current_time)
return SessionResolution(session_id=session_id, reused=False)
def bind(self, user_id: UserId, session_id: str) -> None:
"""绑定指定会话,并在替换时释放旧会话。"""
current = self._sessions.get(user_id)
if current and current[0] != session_id:
self._expired_handler(current[0], user_id)
self._sessions[user_id] = (session_id, self._clock())
def clear(self, user_id: UserId) -> Optional[str]:
"""清除用户绑定并返回被移除的会话 ID。"""
current = self._sessions.pop(user_id, None)
return current[0] if current else None
def get(self, user_id: UserId) -> Optional[SessionEntry]:
"""读取用户当前会话绑定,不改变最后活动时间。"""
return self._sessions.get(user_id)
+1 -1
View File
@@ -15,7 +15,7 @@ from app.application.messaging.interaction import (
update_or_post_message,
)
from app.runtime.log import logger
from app.schemas import Message
from app.schemas.message import Message
from app.schemas.types import NotificationChannel
+1 -1
View File
@@ -13,7 +13,7 @@ from app.application.messaging.interaction import (
supports_interaction_buttons,
update_or_post_message,
)
from app.schemas import Message
from app.schemas.message import Message
from app.schemas.types import NotificationChannel
+1 -1
View File
@@ -14,7 +14,7 @@ from app.application.messaging.interaction import (
)
from app.db.models.subscribe import Subscribe
from app.db.oper.subscribe import SubscribeOper
from app.schemas import Message
from app.schemas.message import Message
from app.schemas.types import NotificationChannel, MediaType