ForcePilot/backend/package/yuxi/channels/adapters/sqlalchemy_transaction_adapter.py
Kris b88c0ae29e feat(channels): 批量新增多渠道网关限界上下文基础代码与契约
新增完整的 channels 限界上下文模块,包含契约层、领域核心层、应用服务、管道编排、插件体系、基础设施组合根等全层级代码,新增飞书与微信 iLink 渠道插件基础结构,补充各类 DTO、端口协议与领域服务实现。
2026-07-02 03:22:12 +08:00

120 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""SQLAlchemy 事务适配器。
实现 ``TransactionPort`` 契约,基于 SQLAlchemy ``AsyncSession`` 提供事务
边界控制能力。事务边界由应用层(管道或用例编排器)显式调用,被驱动适配
器通过构造时共享的 ``AsyncSession`` 加入同一事务,**不得** 自主提交。
事务共享机制:``create_driven_adapters(db)`` 将同一 ``AsyncSession``
注入到 ``ChannelPersistenceAdapter`` / ``ConversationAdapter`` /
``SqlAlchemyTransactionAdapter````begin()`` 在共享 session 上开启
事务,所有适配器的写操作自动加入。
"""
from __future__ import annotations
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from yuxi.channels.contract.ports.driven.transaction_port import (
TransactionPort,
)
__all__ = ["SqlAlchemyTransactionAdapter", "SqlAlchemyTransactionContext"]
class SqlAlchemyTransactionContext:
"""SQLAlchemy 事务上下文。
封装 ``AsyncSession`` 与其事务。被驱动适配器在构造时共享同一
``AsyncSession````begin()`` 在共享 session 上开启事务,所有适配器
的写操作自动加入。上下文管理器退出时自动提交(无异常)或回滚
(有异常)。
被驱动适配器 **不得** 调用 ``commit`` / ``rollback``,仅由应用层
通过 ``TransactionPort`` 控制。
"""
def __init__(self, session: AsyncSession) -> None:
"""初始化事务上下文。
Args:
session: SQLAlchemy 异步会话,事务边界由本上下文控制。
"""
self._session = session
self._txn: Any = None
async def commit(self) -> None:
"""提交当前事务。
仅由应用层调用,被驱动适配器 **不得** 调用。提交后清空事务对象,
避免 ``__aexit__`` 重复提交。
"""
if self._txn is not None:
await self._txn.commit()
self._txn = None
async def rollback(self) -> None:
"""回滚当前事务。
仅由应用层调用,被驱动适配器 **不得** 调用。回滚后清空事务对象,
避免 ``__aexit__`` 重复回滚。
"""
if self._txn is not None:
await self._txn.rollback()
self._txn = None
async def __aenter__(self) -> SqlAlchemyTransactionContext:
"""进入事务上下文,开启 SQLAlchemy 事务。"""
self._txn = await self._session.begin()
return self
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
"""退出事务上下文。
若事务未被显式提交/回滚(``self._txn`` 非空),则按异常状态自动
提交(无异常)或回滚(有异常);若已被显式提交/回滚(``self._txn``
为空),则跳过,避免双重操作。事务对象退出后释放引用,避免泄漏。
"""
try:
if self._txn is not None:
if exc is None:
await self._txn.commit()
else:
await self._txn.rollback()
finally:
self._txn = None
class SqlAlchemyTransactionAdapter(TransactionPort):
"""SQLAlchemy 事务适配器。
实现 ``TransactionPort`` 契约,基于共享的 ``AsyncSession`` 提供事务
边界控制。事务边界由应用层显式调用 ``begin()`` 开启,被驱动适配器
通过构造时共享的 session 加入同一事务。
关键约束:
- 事务边界 **必须** 由应用层控制。
- 被驱动适配器 **不得** 自主调用 ``commit`` / ``rollback``。
- 事务范围 **必须** 由应用层显式声明。
"""
def __init__(self, session: AsyncSession) -> None:
"""初始化事务适配器。
Args:
session: SQLAlchemy 异步会话,与被驱动适配器共享以保证事务
一致性。
"""
self._session = session
def begin(self) -> SqlAlchemyTransactionContext:
"""开启一个新事务,返回 SQLAlchemy 事务上下文。
Returns:
SQLAlchemy 事务上下文,被驱动适配器通过构造时共享的 session
加入同一事务。上下文管理器退出时自动提交(无异常)或回滚
(有异常)。
"""
return SqlAlchemyTransactionContext(self._session)