aury-boot 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aury/boot/__init__.py +66 -0
- aury/boot/_version.py +2 -2
- aury/boot/application/__init__.py +120 -0
- aury/boot/application/app/__init__.py +39 -0
- aury/boot/application/app/base.py +511 -0
- aury/boot/application/app/components.py +434 -0
- aury/boot/application/app/middlewares.py +101 -0
- aury/boot/application/config/__init__.py +44 -0
- aury/boot/application/config/settings.py +663 -0
- aury/boot/application/constants/__init__.py +19 -0
- aury/boot/application/constants/components.py +50 -0
- aury/boot/application/constants/scheduler.py +28 -0
- aury/boot/application/constants/service.py +29 -0
- aury/boot/application/errors/__init__.py +55 -0
- aury/boot/application/errors/chain.py +80 -0
- aury/boot/application/errors/codes.py +67 -0
- aury/boot/application/errors/exceptions.py +238 -0
- aury/boot/application/errors/handlers.py +320 -0
- aury/boot/application/errors/response.py +120 -0
- aury/boot/application/interfaces/__init__.py +76 -0
- aury/boot/application/interfaces/egress.py +224 -0
- aury/boot/application/interfaces/ingress.py +98 -0
- aury/boot/application/middleware/__init__.py +22 -0
- aury/boot/application/middleware/logging.py +451 -0
- aury/boot/application/migrations/__init__.py +13 -0
- aury/boot/application/migrations/manager.py +685 -0
- aury/boot/application/migrations/setup.py +237 -0
- aury/boot/application/rpc/__init__.py +63 -0
- aury/boot/application/rpc/base.py +108 -0
- aury/boot/application/rpc/client.py +294 -0
- aury/boot/application/rpc/discovery.py +218 -0
- aury/boot/application/scheduler/__init__.py +13 -0
- aury/boot/application/scheduler/runner.py +123 -0
- aury/boot/application/server/__init__.py +296 -0
- aury/boot/commands/__init__.py +30 -0
- aury/boot/commands/add.py +76 -0
- aury/boot/commands/app.py +105 -0
- aury/boot/commands/config.py +177 -0
- aury/boot/commands/docker.py +367 -0
- aury/boot/commands/docs.py +284 -0
- aury/boot/commands/generate.py +1277 -0
- aury/boot/commands/init.py +890 -0
- aury/boot/commands/migrate/__init__.py +37 -0
- aury/boot/commands/migrate/app.py +54 -0
- aury/boot/commands/migrate/commands.py +303 -0
- aury/boot/commands/scheduler.py +124 -0
- aury/boot/commands/server/__init__.py +21 -0
- aury/boot/commands/server/app.py +541 -0
- aury/boot/commands/templates/generate/api.py.tpl +105 -0
- aury/boot/commands/templates/generate/model.py.tpl +17 -0
- aury/boot/commands/templates/generate/repository.py.tpl +19 -0
- aury/boot/commands/templates/generate/schema.py.tpl +29 -0
- aury/boot/commands/templates/generate/service.py.tpl +48 -0
- aury/boot/commands/templates/project/CLI.md.tpl +92 -0
- aury/boot/commands/templates/project/DEVELOPMENT.md.tpl +1397 -0
- aury/boot/commands/templates/project/README.md.tpl +111 -0
- aury/boot/commands/templates/project/admin_console_init.py.tpl +50 -0
- aury/boot/commands/templates/project/config.py.tpl +30 -0
- aury/boot/commands/templates/project/conftest.py.tpl +26 -0
- aury/boot/commands/templates/project/env.example.tpl +213 -0
- aury/boot/commands/templates/project/gitignore.tpl +128 -0
- aury/boot/commands/templates/project/main.py.tpl +41 -0
- aury/boot/commands/templates/project/modules/api.py.tpl +19 -0
- aury/boot/commands/templates/project/modules/exceptions.py.tpl +84 -0
- aury/boot/commands/templates/project/modules/schedules.py.tpl +18 -0
- aury/boot/commands/templates/project/modules/tasks.py.tpl +20 -0
- aury/boot/commands/worker.py +143 -0
- aury/boot/common/__init__.py +35 -0
- aury/boot/common/exceptions/__init__.py +114 -0
- aury/boot/common/i18n/__init__.py +16 -0
- aury/boot/common/i18n/translator.py +272 -0
- aury/boot/common/logging/__init__.py +716 -0
- aury/boot/contrib/__init__.py +10 -0
- aury/boot/contrib/admin_console/__init__.py +18 -0
- aury/boot/contrib/admin_console/auth.py +137 -0
- aury/boot/contrib/admin_console/discovery.py +69 -0
- aury/boot/contrib/admin_console/install.py +172 -0
- aury/boot/contrib/admin_console/utils.py +44 -0
- aury/boot/domain/__init__.py +79 -0
- aury/boot/domain/exceptions/__init__.py +132 -0
- aury/boot/domain/models/__init__.py +51 -0
- aury/boot/domain/models/base.py +69 -0
- aury/boot/domain/models/mixins.py +135 -0
- aury/boot/domain/models/models.py +96 -0
- aury/boot/domain/pagination/__init__.py +279 -0
- aury/boot/domain/repository/__init__.py +23 -0
- aury/boot/domain/repository/impl.py +423 -0
- aury/boot/domain/repository/interceptors.py +47 -0
- aury/boot/domain/repository/interface.py +106 -0
- aury/boot/domain/repository/query_builder.py +348 -0
- aury/boot/domain/service/__init__.py +11 -0
- aury/boot/domain/service/base.py +73 -0
- aury/boot/domain/transaction/__init__.py +404 -0
- aury/boot/infrastructure/__init__.py +104 -0
- aury/boot/infrastructure/cache/__init__.py +31 -0
- aury/boot/infrastructure/cache/backends.py +348 -0
- aury/boot/infrastructure/cache/base.py +68 -0
- aury/boot/infrastructure/cache/exceptions.py +37 -0
- aury/boot/infrastructure/cache/factory.py +94 -0
- aury/boot/infrastructure/cache/manager.py +274 -0
- aury/boot/infrastructure/database/__init__.py +39 -0
- aury/boot/infrastructure/database/config.py +71 -0
- aury/boot/infrastructure/database/exceptions.py +44 -0
- aury/boot/infrastructure/database/manager.py +317 -0
- aury/boot/infrastructure/database/query_tools/__init__.py +164 -0
- aury/boot/infrastructure/database/strategies/__init__.py +198 -0
- aury/boot/infrastructure/di/__init__.py +15 -0
- aury/boot/infrastructure/di/container.py +393 -0
- aury/boot/infrastructure/events/__init__.py +33 -0
- aury/boot/infrastructure/events/bus.py +362 -0
- aury/boot/infrastructure/events/config.py +52 -0
- aury/boot/infrastructure/events/consumer.py +134 -0
- aury/boot/infrastructure/events/middleware.py +51 -0
- aury/boot/infrastructure/events/models.py +63 -0
- aury/boot/infrastructure/monitoring/__init__.py +529 -0
- aury/boot/infrastructure/scheduler/__init__.py +19 -0
- aury/boot/infrastructure/scheduler/exceptions.py +37 -0
- aury/boot/infrastructure/scheduler/manager.py +478 -0
- aury/boot/infrastructure/storage/__init__.py +38 -0
- aury/boot/infrastructure/storage/base.py +164 -0
- aury/boot/infrastructure/storage/exceptions.py +37 -0
- aury/boot/infrastructure/storage/factory.py +88 -0
- aury/boot/infrastructure/tasks/__init__.py +24 -0
- aury/boot/infrastructure/tasks/config.py +45 -0
- aury/boot/infrastructure/tasks/constants.py +37 -0
- aury/boot/infrastructure/tasks/exceptions.py +37 -0
- aury/boot/infrastructure/tasks/manager.py +490 -0
- aury/boot/testing/__init__.py +24 -0
- aury/boot/testing/base.py +122 -0
- aury/boot/testing/client.py +163 -0
- aury/boot/testing/factory.py +154 -0
- aury/boot/toolkit/__init__.py +21 -0
- aury/boot/toolkit/http/__init__.py +367 -0
- {aury_boot-0.0.2.dist-info → aury_boot-0.0.3.dist-info}/METADATA +3 -2
- aury_boot-0.0.3.dist-info/RECORD +137 -0
- aury_boot-0.0.2.dist-info/RECORD +0 -5
- {aury_boot-0.0.2.dist-info → aury_boot-0.0.3.dist-info}/WHEEL +0 -0
- {aury_boot-0.0.2.dist-info → aury_boot-0.0.3.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""数据库事务管理工具。
|
|
2
|
+
|
|
3
|
+
提供多种事务管理方式:
|
|
4
|
+
1. @transactional - 通用装饰器,自动从参数中获取session
|
|
5
|
+
2. @requires_transaction - Repository 事务检查装饰器
|
|
6
|
+
3. transactional_context - 上下文管理器(支持 on_commit 回调)
|
|
7
|
+
4. TransactionManager - 手动控制事务(支持 Savepoints)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import contextvars
|
|
13
|
+
from collections.abc import AsyncGenerator, Awaitable, Callable
|
|
14
|
+
from contextlib import asynccontextmanager
|
|
15
|
+
from functools import wraps
|
|
16
|
+
from inspect import signature
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
20
|
+
|
|
21
|
+
from aury.boot.common.logging import logger
|
|
22
|
+
from aury.boot.domain.exceptions import TransactionRequiredError
|
|
23
|
+
|
|
24
|
+
# 用于跟踪嵌套事务的上下文变量
|
|
25
|
+
_transaction_depth: contextvars.ContextVar[int] = contextvars.ContextVar("transaction_depth", default=0)
|
|
26
|
+
|
|
27
|
+
# 用于存储 on_commit 回调的上下文变量
|
|
28
|
+
_on_commit_callbacks: contextvars.ContextVar[list[Callable[[], Any] | Callable[[], Awaitable[Any]]]] = (
|
|
29
|
+
contextvars.ContextVar("on_commit_callbacks", default=[])
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def on_commit(callback: Callable[[], Any] | Callable[[], Awaitable[Any]]) -> None:
|
|
34
|
+
"""注册事务提交后执行的回调函数。
|
|
35
|
+
|
|
36
|
+
类似 Django 的 transaction.on_commit(),回调会在事务成功提交后执行。
|
|
37
|
+
如果事务回滚,回调不会执行。
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
callback: 回调函数,可以是同步或异步函数
|
|
41
|
+
|
|
42
|
+
用法:
|
|
43
|
+
async with transactional_context(session):
|
|
44
|
+
order = await repo.create({...})
|
|
45
|
+
on_commit(lambda: send_notification.delay(order.id))
|
|
46
|
+
# 只有事务提交成功后,才会执行 send_notification
|
|
47
|
+
|
|
48
|
+
注意:
|
|
49
|
+
- 回调在事务提交后、上下文退出前执行
|
|
50
|
+
- 如果回调抛出异常,不会影响事务(事务已提交)
|
|
51
|
+
- 嵌套事务中注册的回调,只在最外层事务提交后执行
|
|
52
|
+
"""
|
|
53
|
+
callbacks = _on_commit_callbacks.get()
|
|
54
|
+
# 创建新列表避免修改默认值
|
|
55
|
+
if not callbacks:
|
|
56
|
+
callbacks = []
|
|
57
|
+
_on_commit_callbacks.set(callbacks)
|
|
58
|
+
callbacks.append(callback)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def _execute_on_commit_callbacks() -> None:
|
|
62
|
+
"""执行所有 on_commit 回调。"""
|
|
63
|
+
callbacks = _on_commit_callbacks.get()
|
|
64
|
+
if not callbacks:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
# 清空回调列表
|
|
68
|
+
_on_commit_callbacks.set([])
|
|
69
|
+
|
|
70
|
+
for callback in callbacks:
|
|
71
|
+
try:
|
|
72
|
+
result = callback()
|
|
73
|
+
# 如果是协程,await 它
|
|
74
|
+
if hasattr(result, "__await__"):
|
|
75
|
+
await result
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
# 回调异常不影响事务(事务已提交),只记录日志
|
|
78
|
+
logger.error(f"on_commit 回调执行失败: {exc}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@asynccontextmanager
|
|
82
|
+
async def transactional_context(session: AsyncSession, auto_commit: bool = True) -> AsyncGenerator[AsyncSession]:
|
|
83
|
+
"""
|
|
84
|
+
事务上下文管理器,自动处理提交和回滚。
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
session: 数据库会话
|
|
88
|
+
auto_commit: 是否自动提交(默认True)
|
|
89
|
+
|
|
90
|
+
特性:
|
|
91
|
+
- 支持嵌套调用:只有最外层会 commit/rollback
|
|
92
|
+
- 兼容 SQLAlchemy 2.0 autobegin 模式
|
|
93
|
+
- 支持 on_commit 回调
|
|
94
|
+
|
|
95
|
+
用法:
|
|
96
|
+
async with transactional_context(session):
|
|
97
|
+
await repo1.create(...)
|
|
98
|
+
await repo2.update(...)
|
|
99
|
+
on_commit(lambda: print("事务已提交"))
|
|
100
|
+
# 成功自动提交,异常自动回滚
|
|
101
|
+
"""
|
|
102
|
+
# 使用 contextvars 跟踪嵌套深度,避免依赖 in_transaction()
|
|
103
|
+
depth = _transaction_depth.get()
|
|
104
|
+
is_outermost = depth == 0
|
|
105
|
+
_transaction_depth.set(depth + 1)
|
|
106
|
+
|
|
107
|
+
# 最外层事务初始化回调列表
|
|
108
|
+
if is_outermost:
|
|
109
|
+
_on_commit_callbacks.set([])
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
yield session
|
|
113
|
+
# 只有最外层且 auto_commit=True 时才提交
|
|
114
|
+
if auto_commit and is_outermost:
|
|
115
|
+
await session.commit()
|
|
116
|
+
logger.debug("事务提交成功")
|
|
117
|
+
# 提交成功后执行 on_commit 回调
|
|
118
|
+
await _execute_on_commit_callbacks()
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
# 只有最外层才回滚
|
|
121
|
+
if is_outermost:
|
|
122
|
+
await session.rollback()
|
|
123
|
+
# 回滚时清空回调列表(不执行)
|
|
124
|
+
_on_commit_callbacks.set([])
|
|
125
|
+
logger.error(f"事务回滚: {exc}")
|
|
126
|
+
raise
|
|
127
|
+
finally:
|
|
128
|
+
_transaction_depth.set(depth)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def transactional[T](func: Callable[..., T]) -> Callable[..., T]:
|
|
132
|
+
"""
|
|
133
|
+
通用事务装饰器,自动从函数参数中查找session并管理事务。
|
|
134
|
+
|
|
135
|
+
支持多种用法:
|
|
136
|
+
1. 参数名为 session 的函数
|
|
137
|
+
2. 参数名为 db 的函数
|
|
138
|
+
3. 类方法中有 self.session 属性
|
|
139
|
+
|
|
140
|
+
特性:
|
|
141
|
+
- 自动识别session参数
|
|
142
|
+
- 成功时自动提交
|
|
143
|
+
- 异常时自动回滚
|
|
144
|
+
- 支持嵌套(内层事务不会重复提交)
|
|
145
|
+
|
|
146
|
+
用法示例:
|
|
147
|
+
@transactional
|
|
148
|
+
async def create_user(session: AsyncSession, name: str):
|
|
149
|
+
# 操作会在事务中执行
|
|
150
|
+
user = User(name=name)
|
|
151
|
+
session.add(user)
|
|
152
|
+
await session.flush()
|
|
153
|
+
return user
|
|
154
|
+
|
|
155
|
+
# 或者在类方法中
|
|
156
|
+
class UserService:
|
|
157
|
+
def __init__(self, session: AsyncSession):
|
|
158
|
+
self.session = session
|
|
159
|
+
|
|
160
|
+
@transactional
|
|
161
|
+
async def create_with_profile(self, name: str):
|
|
162
|
+
# 自动使用 self.session
|
|
163
|
+
user = await self.create_user(name)
|
|
164
|
+
profile = await self.create_profile(user.id)
|
|
165
|
+
return user, profile
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
@wraps(func)
|
|
169
|
+
async def wrapper(*args, **kwargs):
|
|
170
|
+
session: AsyncSession | None = None
|
|
171
|
+
|
|
172
|
+
# 策略1: 从kwargs中获取session或db
|
|
173
|
+
if "session" in kwargs:
|
|
174
|
+
session = kwargs["session"]
|
|
175
|
+
elif "db" in kwargs:
|
|
176
|
+
session = kwargs["db"]
|
|
177
|
+
else:
|
|
178
|
+
# 策略2: 从args中获取 (检查类型注解)
|
|
179
|
+
sig = signature(func)
|
|
180
|
+
params = list(sig.parameters.keys())
|
|
181
|
+
|
|
182
|
+
for i, param_name in enumerate(params):
|
|
183
|
+
if i < len(args):
|
|
184
|
+
param_value = args[i]
|
|
185
|
+
# 检查是否是AsyncSession类型
|
|
186
|
+
if isinstance(param_value, AsyncSession):
|
|
187
|
+
session = param_value
|
|
188
|
+
break
|
|
189
|
+
# 检查参数名
|
|
190
|
+
if param_name in ("session", "db"):
|
|
191
|
+
session = param_value
|
|
192
|
+
break
|
|
193
|
+
|
|
194
|
+
# 策略3: 从self.session获取 (类方法)
|
|
195
|
+
if session is None and args and hasattr(args[0], "session"):
|
|
196
|
+
session = args[0].session
|
|
197
|
+
|
|
198
|
+
if session is None:
|
|
199
|
+
raise ValueError(
|
|
200
|
+
f"无法找到session参数。请确保函数 {func.__name__} 有一个名为 'session' 或 'db' 的参数,"
|
|
201
|
+
"或者类有 'session' 属性。"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# 使用事务上下文管理器,它会自动处理嵌套情况
|
|
205
|
+
logger.debug(f"执行事务 {func.__name__}")
|
|
206
|
+
async with transactional_context(session):
|
|
207
|
+
return await func(*args, **kwargs)
|
|
208
|
+
|
|
209
|
+
return wrapper
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class TransactionManager:
|
|
213
|
+
"""
|
|
214
|
+
事务管理器,提供更细粒度的事务控制。
|
|
215
|
+
|
|
216
|
+
支持功能:
|
|
217
|
+
- 手动控制事务 begin/commit/rollback
|
|
218
|
+
- Savepoints(保存点)
|
|
219
|
+
- on_commit 回调
|
|
220
|
+
|
|
221
|
+
用法:
|
|
222
|
+
tm = TransactionManager(session)
|
|
223
|
+
|
|
224
|
+
await tm.begin()
|
|
225
|
+
try:
|
|
226
|
+
await repo1.create(data)
|
|
227
|
+
|
|
228
|
+
# 使用 Savepoint
|
|
229
|
+
sid = await tm.savepoint()
|
|
230
|
+
try:
|
|
231
|
+
await repo2.update(data)
|
|
232
|
+
except Exception:
|
|
233
|
+
await tm.savepoint_rollback(sid)
|
|
234
|
+
|
|
235
|
+
await tm.commit()
|
|
236
|
+
except Exception:
|
|
237
|
+
await tm.rollback()
|
|
238
|
+
raise
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
def __init__(self, session: AsyncSession):
|
|
242
|
+
self.session = session
|
|
243
|
+
self._transaction = None
|
|
244
|
+
self._savepoints: dict[str, Any] = {} # savepoint_id -> nested transaction
|
|
245
|
+
self._savepoint_counter = 0
|
|
246
|
+
self._on_commit_callbacks: list[Callable[[], Any] | Callable[[], Awaitable[Any]]] = []
|
|
247
|
+
|
|
248
|
+
async def begin(self):
|
|
249
|
+
"""开始事务。"""
|
|
250
|
+
if self._transaction is None:
|
|
251
|
+
self._transaction = await self.session.begin()
|
|
252
|
+
logger.debug("手动开启事务")
|
|
253
|
+
|
|
254
|
+
async def commit(self):
|
|
255
|
+
"""提交事务。"""
|
|
256
|
+
if self._transaction:
|
|
257
|
+
await self.session.commit()
|
|
258
|
+
self._transaction = None
|
|
259
|
+
logger.debug("手动提交事务")
|
|
260
|
+
# 执行 on_commit 回调
|
|
261
|
+
await self._execute_callbacks()
|
|
262
|
+
|
|
263
|
+
async def rollback(self):
|
|
264
|
+
"""回滚事务。"""
|
|
265
|
+
if self._transaction:
|
|
266
|
+
await self.session.rollback()
|
|
267
|
+
self._transaction = None
|
|
268
|
+
self._savepoints.clear()
|
|
269
|
+
self._on_commit_callbacks.clear() # 回滚时清空回调
|
|
270
|
+
logger.debug("手动回滚事务")
|
|
271
|
+
|
|
272
|
+
async def savepoint(self, name: str | None = None) -> str:
|
|
273
|
+
"""创建保存点。
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
name: 保存点名称,可选。如果不提供,自动生成。
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
str: 保存点 ID,用于后续 commit 或 rollback
|
|
280
|
+
|
|
281
|
+
用法:
|
|
282
|
+
sid = await tm.savepoint()
|
|
283
|
+
try:
|
|
284
|
+
await risky_operation()
|
|
285
|
+
await tm.savepoint_commit(sid)
|
|
286
|
+
except Exception:
|
|
287
|
+
await tm.savepoint_rollback(sid)
|
|
288
|
+
"""
|
|
289
|
+
self._savepoint_counter += 1
|
|
290
|
+
savepoint_id = name or f"sp_{self._savepoint_counter}"
|
|
291
|
+
|
|
292
|
+
# 使用 begin_nested() 创建 savepoint
|
|
293
|
+
nested = await self.session.begin_nested()
|
|
294
|
+
self._savepoints[savepoint_id] = nested
|
|
295
|
+
logger.debug(f"创建保存点: {savepoint_id}")
|
|
296
|
+
return savepoint_id
|
|
297
|
+
|
|
298
|
+
async def savepoint_commit(self, savepoint_id: str) -> None:
|
|
299
|
+
"""提交保存点。
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
savepoint_id: savepoint() 返回的 ID
|
|
303
|
+
"""
|
|
304
|
+
if savepoint_id not in self._savepoints:
|
|
305
|
+
logger.warning(f"保存点不存在: {savepoint_id}")
|
|
306
|
+
return
|
|
307
|
+
|
|
308
|
+
# SQLAlchemy 的 begin_nested() 在退出时自动提交
|
|
309
|
+
# 这里只需要从字典中移除
|
|
310
|
+
del self._savepoints[savepoint_id]
|
|
311
|
+
logger.debug(f"确认保存点: {savepoint_id}")
|
|
312
|
+
|
|
313
|
+
async def savepoint_rollback(self, savepoint_id: str) -> None:
|
|
314
|
+
"""回滚到保存点。
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
savepoint_id: savepoint() 返回的 ID
|
|
318
|
+
"""
|
|
319
|
+
if savepoint_id not in self._savepoints:
|
|
320
|
+
logger.warning(f"保存点不存在: {savepoint_id}")
|
|
321
|
+
return
|
|
322
|
+
|
|
323
|
+
nested = self._savepoints.pop(savepoint_id)
|
|
324
|
+
await nested.rollback()
|
|
325
|
+
logger.debug(f"回滚到保存点: {savepoint_id}")
|
|
326
|
+
|
|
327
|
+
def on_commit(self, callback: Callable[[], Any] | Callable[[], Awaitable[Any]]) -> None:
|
|
328
|
+
"""注册事务提交后执行的回调。
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
callback: 回调函数,可以是同步或异步函数
|
|
332
|
+
"""
|
|
333
|
+
self._on_commit_callbacks.append(callback)
|
|
334
|
+
|
|
335
|
+
async def _execute_callbacks(self) -> None:
|
|
336
|
+
"""执行所有 on_commit 回调。"""
|
|
337
|
+
callbacks = self._on_commit_callbacks
|
|
338
|
+
self._on_commit_callbacks = []
|
|
339
|
+
|
|
340
|
+
for callback in callbacks:
|
|
341
|
+
try:
|
|
342
|
+
result = callback()
|
|
343
|
+
if hasattr(result, "__await__"):
|
|
344
|
+
await result
|
|
345
|
+
except Exception as exc:
|
|
346
|
+
logger.error(f"on_commit 回调执行失败: {exc}")
|
|
347
|
+
|
|
348
|
+
async def __aenter__(self):
|
|
349
|
+
await self.begin()
|
|
350
|
+
return self
|
|
351
|
+
|
|
352
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
353
|
+
if exc_type is None:
|
|
354
|
+
await self.commit()
|
|
355
|
+
else:
|
|
356
|
+
await self.rollback()
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def ensure_transaction(session: AsyncSession) -> bool:
|
|
360
|
+
"""
|
|
361
|
+
检查当前会话是否在事务中。
|
|
362
|
+
|
|
363
|
+
用于在Repository中进行安全检查。
|
|
364
|
+
"""
|
|
365
|
+
return session.in_transaction()
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def requires_transaction(func: Callable) -> Callable:
|
|
369
|
+
"""事务必需装饰器。
|
|
370
|
+
|
|
371
|
+
确保方法在事务中执行,如果不在事务中则抛出 TransactionRequiredError。
|
|
372
|
+
|
|
373
|
+
用于 Repository 方法,强制要求在事务中调用。
|
|
374
|
+
|
|
375
|
+
用法:
|
|
376
|
+
class UserRepository(BaseRepository):
|
|
377
|
+
@requires_transaction
|
|
378
|
+
async def update_user(self, user, data):
|
|
379
|
+
# 此方法必须在事务中执行
|
|
380
|
+
return await self.update(user, data)
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
@wraps(func)
|
|
384
|
+
async def wrapper(self, *args, **kwargs):
|
|
385
|
+
if not self._session.in_transaction():
|
|
386
|
+
raise TransactionRequiredError(
|
|
387
|
+
f"方法 {func.__name__} 需要在事务中执行,但当前会话不在事务中"
|
|
388
|
+
)
|
|
389
|
+
return await func(self, *args, **kwargs)
|
|
390
|
+
|
|
391
|
+
return wrapper
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
__all__ = [
|
|
395
|
+
"TransactionManager",
|
|
396
|
+
"TransactionRequiredError",
|
|
397
|
+
"_transaction_depth", # 内部使用,不对外文档化
|
|
398
|
+
"ensure_transaction",
|
|
399
|
+
"on_commit",
|
|
400
|
+
"requires_transaction",
|
|
401
|
+
"transactional",
|
|
402
|
+
"transactional_context",
|
|
403
|
+
]
|
|
404
|
+
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""基础设施层模块。
|
|
2
|
+
|
|
3
|
+
提供外部依赖的实现,包括:
|
|
4
|
+
- 数据库管理
|
|
5
|
+
- 缓存管理
|
|
6
|
+
- 存储管理
|
|
7
|
+
- 调度器
|
|
8
|
+
- 任务队列
|
|
9
|
+
- 日志
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# 数据库
|
|
13
|
+
# 缓存
|
|
14
|
+
from .cache import (
|
|
15
|
+
CacheBackend,
|
|
16
|
+
CacheFactory,
|
|
17
|
+
CacheManager,
|
|
18
|
+
ICache,
|
|
19
|
+
MemcachedCache,
|
|
20
|
+
MemoryCache,
|
|
21
|
+
RedisCache,
|
|
22
|
+
)
|
|
23
|
+
from .database import DatabaseManager
|
|
24
|
+
|
|
25
|
+
# 日志(已迁移到 common 层)
|
|
26
|
+
# 从 common.logging 导入
|
|
27
|
+
|
|
28
|
+
# 调度器(可选依赖)
|
|
29
|
+
try:
|
|
30
|
+
from .scheduler import SchedulerManager
|
|
31
|
+
except ImportError:
|
|
32
|
+
SchedulerManager = None # type: ignore[assignment, misc]
|
|
33
|
+
|
|
34
|
+
# 存储(基于 aury-sdk-storage)
|
|
35
|
+
from .storage import (
|
|
36
|
+
IStorage,
|
|
37
|
+
LocalStorage,
|
|
38
|
+
S3Storage,
|
|
39
|
+
StorageBackend,
|
|
40
|
+
StorageConfig,
|
|
41
|
+
StorageFactory,
|
|
42
|
+
StorageFile,
|
|
43
|
+
StorageManager,
|
|
44
|
+
UploadResult,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# 任务队列(可选依赖)
|
|
48
|
+
try:
|
|
49
|
+
from .tasks import TaskManager, TaskProxy, conditional_actor
|
|
50
|
+
except ImportError:
|
|
51
|
+
TaskManager = None # type: ignore[assignment, misc]
|
|
52
|
+
TaskProxy = None # type: ignore[assignment, misc]
|
|
53
|
+
conditional_actor = None # type: ignore[assignment, misc]
|
|
54
|
+
|
|
55
|
+
# 事件总线
|
|
56
|
+
# 依赖注入
|
|
57
|
+
from .di import Container, Lifetime, Scope, ServiceDescriptor
|
|
58
|
+
from .events import (
|
|
59
|
+
EventBus,
|
|
60
|
+
EventConsumer,
|
|
61
|
+
EventLoggingMiddleware,
|
|
62
|
+
EventMiddleware,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
"CacheBackend",
|
|
67
|
+
"CacheFactory",
|
|
68
|
+
# 缓存
|
|
69
|
+
"CacheManager",
|
|
70
|
+
# 依赖注入
|
|
71
|
+
"Container",
|
|
72
|
+
# 数据库
|
|
73
|
+
"DatabaseManager",
|
|
74
|
+
# 事件总线
|
|
75
|
+
"EventBus",
|
|
76
|
+
"EventConsumer",
|
|
77
|
+
"EventLoggingMiddleware",
|
|
78
|
+
"EventMiddleware",
|
|
79
|
+
"ICache",
|
|
80
|
+
"IStorage",
|
|
81
|
+
"Lifetime",
|
|
82
|
+
"LocalStorage",
|
|
83
|
+
"MemcachedCache",
|
|
84
|
+
"MemoryCache",
|
|
85
|
+
"RedisCache",
|
|
86
|
+
"S3Storage",
|
|
87
|
+
# 调度器
|
|
88
|
+
"SchedulerManager",
|
|
89
|
+
"Scope",
|
|
90
|
+
"ServiceDescriptor",
|
|
91
|
+
"StorageBackend",
|
|
92
|
+
"StorageConfig",
|
|
93
|
+
"StorageFactory",
|
|
94
|
+
"StorageFile",
|
|
95
|
+
# 存储
|
|
96
|
+
"StorageManager",
|
|
97
|
+
"UploadResult",
|
|
98
|
+
# 任务队列
|
|
99
|
+
"TaskManager",
|
|
100
|
+
"TaskProxy",
|
|
101
|
+
"conditional_actor",
|
|
102
|
+
# 日志(已迁移到 common 层,请从 common.logging 导入)
|
|
103
|
+
]
|
|
104
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""缓存系统模块。
|
|
2
|
+
|
|
3
|
+
支持多种缓存后端:
|
|
4
|
+
- Redis
|
|
5
|
+
- Memory(内存)
|
|
6
|
+
- Memcached(可选)
|
|
7
|
+
- 可扩展其他类型
|
|
8
|
+
|
|
9
|
+
使用策略模式,可以轻松切换缓存后端。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .backends import MemcachedCache, MemoryCache, RedisCache
|
|
13
|
+
from .base import CacheBackend, ICache
|
|
14
|
+
from .exceptions import CacheBackendError, CacheError, CacheMissError
|
|
15
|
+
from .factory import CacheFactory
|
|
16
|
+
from .manager import CacheManager
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"CacheBackend",
|
|
20
|
+
"CacheBackendError",
|
|
21
|
+
# 异常
|
|
22
|
+
"CacheError",
|
|
23
|
+
"CacheFactory",
|
|
24
|
+
"CacheManager",
|
|
25
|
+
"CacheMissError",
|
|
26
|
+
"ICache",
|
|
27
|
+
"MemcachedCache",
|
|
28
|
+
"MemoryCache",
|
|
29
|
+
"RedisCache",
|
|
30
|
+
]
|
|
31
|
+
|