ForcePilot/backend/package/yuxi/channels/adapters/sqlalchemy_transaction_adapter.py
Kris c7eb196a7e chore: 批量完成多模块迭代优化与功能完善
本次提交涵盖了近百处代码优化与功能补全,包括:
1. 完善配置与数据模型:新增expired_at配对记录字段、路由绑定乐观锁版本控制、会话路由信息追踪字段
2. 优化业务流程:添加幂等记录操作人审计、会话合并领域服务文档更新、媒体处理异步化改造
3. 新增功能能力:健康检查时间更新、会话路由信息更新接口、内容审核/幂等记录清理定时任务
4. 修复与简化:移除废弃的max_message_length属性、修复微信iLink适配器配置读取路径、简化配对过期扫描逻辑
5. 代码规范优化:统一敏感词检测工具导入、完善事务上下文处理注释、调整wechat_woc入站适配器sender回退逻辑
2026-07-08 03:57:05 +08:00

132 lines
5.0 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 事务。
SQLAlchemy 2.0 autobegin 语义下,前置读操作(如 ``getChannelSessionByPeer``
等 SELECT 查询)会在 session 上隐式开启只读事务。若不处理,
``begin()`` 会抛 ``InvalidRequestError: A transaction is already
begun on this Session.``。
此处检测并提交隐式事务后再开启显式事务。安全性保证:按适配器契约,
``tx=None`` 的写操作自主提交autobegin 事务仅含读操作,提交不会
产生副作用数据落库。应用层显式事务边界§10.1)由此方法独占控制。
"""
if self._session.in_transaction():
await self._session.commit()
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)