新增完整的 channels 限界上下文模块,包含契约层、领域核心层、应用服务、管道编排、插件体系、基础设施组合根等全层级代码,新增飞书与微信 iLink 渠道插件基础结构,补充各类 DTO、端口协议与领域服务实现。
120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
"""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)
|