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,348 @@
|
|
|
1
|
+
"""查询构建器。
|
|
2
|
+
|
|
3
|
+
提供链式查询和复杂条件构建功能。
|
|
4
|
+
|
|
5
|
+
注意:此模块定义在 domain 层(而非 infrastructure),因为查询构建是领域层的通用能力,
|
|
6
|
+
与特定的数据库实现无关。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from sqlalchemy import Select, and_, not_, or_, select
|
|
15
|
+
from sqlalchemy.orm import joinedload, selectinload
|
|
16
|
+
|
|
17
|
+
from aury.boot.domain.models import Base
|
|
18
|
+
|
|
19
|
+
# 操作符处理函数字典(函数式编程)
|
|
20
|
+
_OPERATOR_HANDLERS: dict[str, Callable[[Any, Any], Any]] = {
|
|
21
|
+
"gt": lambda field, value: field > value,
|
|
22
|
+
"lt": lambda field, value: field < value,
|
|
23
|
+
"gte": lambda field, value: field >= value,
|
|
24
|
+
"lte": lambda field, value: field <= value,
|
|
25
|
+
"in": lambda field, value: field.in_(value),
|
|
26
|
+
"like": lambda field, value: field.like(value),
|
|
27
|
+
"ilike": lambda field, value: field.ilike(value),
|
|
28
|
+
"ne": lambda field, value: field != value,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _handle_isnull_operator(field: Any, value: bool) -> Any:
|
|
33
|
+
"""处理 isnull 操作符。
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
field: 字段对象
|
|
37
|
+
value: 是否为空(True=为空,False=不为空)
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
SQLAlchemy 条件表达式
|
|
41
|
+
"""
|
|
42
|
+
if value:
|
|
43
|
+
return field.is_(None)
|
|
44
|
+
return field.isnot(None)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class QueryBuilder[ModelType: Base]:
|
|
48
|
+
"""查询构建器。
|
|
49
|
+
|
|
50
|
+
提供链式查询和复杂条件构建功能。
|
|
51
|
+
支持操作符:__gt、__lt、__gte、__lte、__in、__like、__ilike、__isnull、__ne
|
|
52
|
+
支持复杂条件:and_()、or_()、not_()
|
|
53
|
+
支持关系查询:joinedload()、selectinload()
|
|
54
|
+
支持悲观锁:for_update()
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, model_class: type[ModelType]) -> None:
|
|
58
|
+
"""初始化查询构建器。
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
model_class: 模型类
|
|
62
|
+
"""
|
|
63
|
+
self._model_class = model_class
|
|
64
|
+
self._query = select(model_class)
|
|
65
|
+
self._filters = []
|
|
66
|
+
self._order_by = []
|
|
67
|
+
self._load_options = []
|
|
68
|
+
self._limit = None
|
|
69
|
+
self._offset = None
|
|
70
|
+
self._for_update: dict[str, bool] | None = None
|
|
71
|
+
|
|
72
|
+
def filter(self, **kwargs) -> QueryBuilder[ModelType]:
|
|
73
|
+
"""添加过滤条件。
|
|
74
|
+
|
|
75
|
+
支持操作符:
|
|
76
|
+
- __gt: 大于
|
|
77
|
+
- __lt: 小于
|
|
78
|
+
- __gte: 大于等于
|
|
79
|
+
- __lte: 小于等于
|
|
80
|
+
- __in: 包含于
|
|
81
|
+
- __like: 模糊匹配(区分大小写)
|
|
82
|
+
- __ilike: 模糊匹配(不区分大小写)
|
|
83
|
+
- __isnull: 是否为空
|
|
84
|
+
- __ne: 不等于
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
**kwargs: 过滤条件
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
QueryBuilder: 查询构建器实例
|
|
91
|
+
"""
|
|
92
|
+
for key, value in kwargs.items():
|
|
93
|
+
if "__" in key:
|
|
94
|
+
field_name, operator = key.rsplit("__", 1)
|
|
95
|
+
if not hasattr(self._model_class, field_name):
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
field = getattr(self._model_class, field_name)
|
|
99
|
+
|
|
100
|
+
# 使用函数式编程处理操作符
|
|
101
|
+
if operator == "isnull":
|
|
102
|
+
# isnull 需要特殊处理(需要判断 value 的布尔值)
|
|
103
|
+
self._filters.append(_handle_isnull_operator(field, value))
|
|
104
|
+
elif operator in _OPERATOR_HANDLERS:
|
|
105
|
+
# 使用函数字典处理其他操作符
|
|
106
|
+
handler = _OPERATOR_HANDLERS[operator]
|
|
107
|
+
self._filters.append(handler(field, value))
|
|
108
|
+
else:
|
|
109
|
+
if hasattr(self._model_class, key) and value is not None:
|
|
110
|
+
self._filters.append(getattr(self._model_class, key) == value)
|
|
111
|
+
|
|
112
|
+
return self
|
|
113
|
+
|
|
114
|
+
def filter_by(self, *conditions) -> QueryBuilder[ModelType]:
|
|
115
|
+
"""添加自定义过滤条件。
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
*conditions: SQLAlchemy 条件表达式
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
QueryBuilder: 查询构建器实例
|
|
122
|
+
"""
|
|
123
|
+
self._filters.extend(conditions)
|
|
124
|
+
return self
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def and_(*conditions) -> Any:
|
|
128
|
+
"""创建 AND 条件。
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
*conditions: 条件列表
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
SQLAlchemy AND 表达式
|
|
135
|
+
"""
|
|
136
|
+
return and_(*conditions)
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def or_(*conditions) -> Any:
|
|
140
|
+
"""创建 OR 条件。
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
*conditions: 条件列表
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
SQLAlchemy OR 表达式
|
|
147
|
+
"""
|
|
148
|
+
return or_(*conditions)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def not_(condition) -> Any:
|
|
152
|
+
"""创建 NOT 条件。
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
condition: 条件表达式
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
SQLAlchemy NOT 表达式
|
|
159
|
+
"""
|
|
160
|
+
return not_(condition)
|
|
161
|
+
|
|
162
|
+
def order_by(self, *fields) -> QueryBuilder[ModelType]:
|
|
163
|
+
"""添加排序条件。
|
|
164
|
+
|
|
165
|
+
支持字符串字段名,使用 "-" 前缀表示降序。
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
*fields: 排序字段列表
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
QueryBuilder: 查询构建器实例
|
|
172
|
+
"""
|
|
173
|
+
for field in fields:
|
|
174
|
+
if isinstance(field, str):
|
|
175
|
+
if field.startswith("-"):
|
|
176
|
+
field_name = field[1:]
|
|
177
|
+
if hasattr(self._model_class, field_name):
|
|
178
|
+
self._order_by.append(getattr(self._model_class, field_name).desc())
|
|
179
|
+
else:
|
|
180
|
+
if hasattr(self._model_class, field):
|
|
181
|
+
self._order_by.append(getattr(self._model_class, field))
|
|
182
|
+
else:
|
|
183
|
+
self._order_by.append(field)
|
|
184
|
+
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
def limit(self, limit: int) -> QueryBuilder[ModelType]:
|
|
188
|
+
"""设置返回记录数限制。
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
limit: 记录数
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
QueryBuilder: 查询构建器实例
|
|
195
|
+
"""
|
|
196
|
+
self._limit = limit
|
|
197
|
+
return self
|
|
198
|
+
|
|
199
|
+
def offset(self, offset: int) -> QueryBuilder[ModelType]:
|
|
200
|
+
"""设置跳过记录数。
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
offset: 跳过记录数
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
QueryBuilder: 查询构建器实例
|
|
207
|
+
"""
|
|
208
|
+
self._offset = offset
|
|
209
|
+
return self
|
|
210
|
+
|
|
211
|
+
def joinedload(self, *relationships) -> QueryBuilder[ModelType]:
|
|
212
|
+
"""添加关联加载(JOIN)。
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
*relationships: 关联关系列表
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
QueryBuilder: 查询构建器实例
|
|
219
|
+
"""
|
|
220
|
+
for rel in relationships:
|
|
221
|
+
if isinstance(rel, str):
|
|
222
|
+
if hasattr(self._model_class, rel):
|
|
223
|
+
self._load_options.append(joinedload(getattr(self._model_class, rel)))
|
|
224
|
+
else:
|
|
225
|
+
self._load_options.append(joinedload(rel))
|
|
226
|
+
|
|
227
|
+
return self
|
|
228
|
+
|
|
229
|
+
def selectinload(self, *relationships) -> QueryBuilder[ModelType]:
|
|
230
|
+
"""添加关联加载(SELECT IN)。
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
*relationships: 关联关系列表
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
QueryBuilder: 查询构建器实例
|
|
237
|
+
"""
|
|
238
|
+
for rel in relationships:
|
|
239
|
+
if isinstance(rel, str):
|
|
240
|
+
if hasattr(self._model_class, rel):
|
|
241
|
+
self._load_options.append(selectinload(getattr(self._model_class, rel)))
|
|
242
|
+
else:
|
|
243
|
+
self._load_options.append(selectinload(rel))
|
|
244
|
+
|
|
245
|
+
return self
|
|
246
|
+
|
|
247
|
+
def for_update(
|
|
248
|
+
self,
|
|
249
|
+
*,
|
|
250
|
+
nowait: bool = False,
|
|
251
|
+
skip_locked: bool = False,
|
|
252
|
+
of: list[str] | None = None,
|
|
253
|
+
) -> QueryBuilder[ModelType]:
|
|
254
|
+
"""添加悲观锁(SELECT ... FOR UPDATE)。
|
|
255
|
+
|
|
256
|
+
用于在事务中锁定记录,防止并发修改。
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
nowait: 是否立即返回,不等待锁释放。如果记录被锁定,立即抛出异常。
|
|
260
|
+
skip_locked: 是否跳过已锁定的记录。适用于队列场景。
|
|
261
|
+
of: 指定要锁定的列(部分数据库支持)。
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
QueryBuilder: 查询构建器实例
|
|
265
|
+
|
|
266
|
+
用法:
|
|
267
|
+
# 基本用法:锁定记录
|
|
268
|
+
query = repo.query().filter(id=1).for_update()
|
|
269
|
+
result = await session.execute(query.build())
|
|
270
|
+
product = result.scalar_one()
|
|
271
|
+
product.stock -= 1 # 安全修改,其他事务会等待
|
|
272
|
+
|
|
273
|
+
# 不等待:如果被锁定立即抛出异常
|
|
274
|
+
query = repo.query().filter(id=1).for_update(nowait=True)
|
|
275
|
+
|
|
276
|
+
# 跳过已锁定记录:适用于任务队列
|
|
277
|
+
query = repo.query().filter(status="pending").for_update(skip_locked=True)
|
|
278
|
+
|
|
279
|
+
注意:
|
|
280
|
+
- 必须在事务中使用
|
|
281
|
+
- nowait 和 skip_locked 不能同时使用
|
|
282
|
+
- 不同数据库支持的参数不同(PostgreSQL 支持所有,MySQL 部分支持,SQLite 不支持)
|
|
283
|
+
"""
|
|
284
|
+
if nowait and skip_locked:
|
|
285
|
+
raise ValueError("nowait 和 skip_locked 不能同时使用")
|
|
286
|
+
|
|
287
|
+
self._for_update = {
|
|
288
|
+
"nowait": nowait,
|
|
289
|
+
"skip_locked": skip_locked,
|
|
290
|
+
}
|
|
291
|
+
if of:
|
|
292
|
+
self._for_update["of"] = of
|
|
293
|
+
|
|
294
|
+
return self
|
|
295
|
+
|
|
296
|
+
def build(self) -> Select:
|
|
297
|
+
"""构建查询对象。
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
Select: SQLAlchemy 查询对象
|
|
301
|
+
"""
|
|
302
|
+
query = self._query
|
|
303
|
+
|
|
304
|
+
# 应用过滤条件
|
|
305
|
+
if self._filters:
|
|
306
|
+
query = query.where(and_(*self._filters))
|
|
307
|
+
|
|
308
|
+
# 应用排序
|
|
309
|
+
if self._order_by:
|
|
310
|
+
query = query.order_by(*self._order_by)
|
|
311
|
+
|
|
312
|
+
# 应用关联加载
|
|
313
|
+
if self._load_options:
|
|
314
|
+
query = query.options(*self._load_options)
|
|
315
|
+
|
|
316
|
+
# 应用分页
|
|
317
|
+
if self._offset is not None:
|
|
318
|
+
query = query.offset(self._offset)
|
|
319
|
+
|
|
320
|
+
if self._limit is not None:
|
|
321
|
+
query = query.limit(self._limit)
|
|
322
|
+
|
|
323
|
+
# 应用悲观锁
|
|
324
|
+
if self._for_update is not None:
|
|
325
|
+
# 构建 for_update 参数
|
|
326
|
+
of_columns = None
|
|
327
|
+
if "of" in self._for_update:
|
|
328
|
+
of_list = self._for_update["of"]
|
|
329
|
+
of_columns = [
|
|
330
|
+
getattr(self._model_class, col)
|
|
331
|
+
for col in of_list
|
|
332
|
+
if hasattr(self._model_class, col)
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
query = query.with_for_update(
|
|
336
|
+
nowait=self._for_update.get("nowait", False),
|
|
337
|
+
skip_locked=self._for_update.get("skip_locked", False),
|
|
338
|
+
of=of_columns if of_columns else None,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
return query
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
__all__ = [
|
|
345
|
+
"QueryBuilder",
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""领域服务基类。
|
|
2
|
+
|
|
3
|
+
提供业务服务层的基础功能和通用逻辑。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from abc import ABC
|
|
9
|
+
|
|
10
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
11
|
+
|
|
12
|
+
from aury.boot.common.logging import logger
|
|
13
|
+
from aury.boot.infrastructure.monitoring import monitor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseService(ABC): # noqa: B024
|
|
17
|
+
"""领域服务抽象基类。
|
|
18
|
+
|
|
19
|
+
职责:
|
|
20
|
+
1. 管理数据库会话
|
|
21
|
+
2. 协调多个Repository
|
|
22
|
+
3. 实现业务逻辑
|
|
23
|
+
4. 事务管理
|
|
24
|
+
|
|
25
|
+
设计原则:
|
|
26
|
+
- 单一职责:每个Service处理一个业务领域
|
|
27
|
+
- 依赖注入:通过构造函数注入依赖
|
|
28
|
+
- 事务管理:使用装饰器管理事务边界
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
session: 数据库会话
|
|
32
|
+
|
|
33
|
+
使用示例:
|
|
34
|
+
from aury.boot.domain.transaction import transactional
|
|
35
|
+
|
|
36
|
+
class UserService(BaseService):
|
|
37
|
+
def __init__(self, session: AsyncSession):
|
|
38
|
+
super().__init__(session)
|
|
39
|
+
self.user_repo = UserRepository(session)
|
|
40
|
+
self.profile_repo = ProfileRepository(session)
|
|
41
|
+
|
|
42
|
+
@transactional
|
|
43
|
+
async def create_user_with_profile(self, data: dict):
|
|
44
|
+
user = await self.user_repo.create(data["user"])
|
|
45
|
+
profile = await self.profile_repo.create({
|
|
46
|
+
"user_id": user.id,
|
|
47
|
+
**data["profile"]
|
|
48
|
+
})
|
|
49
|
+
return user, profile
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, session: AsyncSession) -> None:
|
|
53
|
+
"""初始化Service。
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
session: 数据库会话
|
|
57
|
+
"""
|
|
58
|
+
self._session = session
|
|
59
|
+
logger.debug(f"初始化 {self.__class__.__name__}")
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def session(self) -> AsyncSession:
|
|
63
|
+
"""获取数据库会话。
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
AsyncSession: 数据库会话
|
|
67
|
+
"""
|
|
68
|
+
return self._session
|
|
69
|
+
|
|
70
|
+
def __repr__(self) -> str:
|
|
71
|
+
"""字符串表示。"""
|
|
72
|
+
return f"<{self.__class__.__name__}>"
|
|
73
|
+
|