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.
Files changed (138) hide show
  1. aury/boot/__init__.py +66 -0
  2. aury/boot/_version.py +2 -2
  3. aury/boot/application/__init__.py +120 -0
  4. aury/boot/application/app/__init__.py +39 -0
  5. aury/boot/application/app/base.py +511 -0
  6. aury/boot/application/app/components.py +434 -0
  7. aury/boot/application/app/middlewares.py +101 -0
  8. aury/boot/application/config/__init__.py +44 -0
  9. aury/boot/application/config/settings.py +663 -0
  10. aury/boot/application/constants/__init__.py +19 -0
  11. aury/boot/application/constants/components.py +50 -0
  12. aury/boot/application/constants/scheduler.py +28 -0
  13. aury/boot/application/constants/service.py +29 -0
  14. aury/boot/application/errors/__init__.py +55 -0
  15. aury/boot/application/errors/chain.py +80 -0
  16. aury/boot/application/errors/codes.py +67 -0
  17. aury/boot/application/errors/exceptions.py +238 -0
  18. aury/boot/application/errors/handlers.py +320 -0
  19. aury/boot/application/errors/response.py +120 -0
  20. aury/boot/application/interfaces/__init__.py +76 -0
  21. aury/boot/application/interfaces/egress.py +224 -0
  22. aury/boot/application/interfaces/ingress.py +98 -0
  23. aury/boot/application/middleware/__init__.py +22 -0
  24. aury/boot/application/middleware/logging.py +451 -0
  25. aury/boot/application/migrations/__init__.py +13 -0
  26. aury/boot/application/migrations/manager.py +685 -0
  27. aury/boot/application/migrations/setup.py +237 -0
  28. aury/boot/application/rpc/__init__.py +63 -0
  29. aury/boot/application/rpc/base.py +108 -0
  30. aury/boot/application/rpc/client.py +294 -0
  31. aury/boot/application/rpc/discovery.py +218 -0
  32. aury/boot/application/scheduler/__init__.py +13 -0
  33. aury/boot/application/scheduler/runner.py +123 -0
  34. aury/boot/application/server/__init__.py +296 -0
  35. aury/boot/commands/__init__.py +30 -0
  36. aury/boot/commands/add.py +76 -0
  37. aury/boot/commands/app.py +105 -0
  38. aury/boot/commands/config.py +177 -0
  39. aury/boot/commands/docker.py +367 -0
  40. aury/boot/commands/docs.py +284 -0
  41. aury/boot/commands/generate.py +1277 -0
  42. aury/boot/commands/init.py +890 -0
  43. aury/boot/commands/migrate/__init__.py +37 -0
  44. aury/boot/commands/migrate/app.py +54 -0
  45. aury/boot/commands/migrate/commands.py +303 -0
  46. aury/boot/commands/scheduler.py +124 -0
  47. aury/boot/commands/server/__init__.py +21 -0
  48. aury/boot/commands/server/app.py +541 -0
  49. aury/boot/commands/templates/generate/api.py.tpl +105 -0
  50. aury/boot/commands/templates/generate/model.py.tpl +17 -0
  51. aury/boot/commands/templates/generate/repository.py.tpl +19 -0
  52. aury/boot/commands/templates/generate/schema.py.tpl +29 -0
  53. aury/boot/commands/templates/generate/service.py.tpl +48 -0
  54. aury/boot/commands/templates/project/CLI.md.tpl +92 -0
  55. aury/boot/commands/templates/project/DEVELOPMENT.md.tpl +1397 -0
  56. aury/boot/commands/templates/project/README.md.tpl +111 -0
  57. aury/boot/commands/templates/project/admin_console_init.py.tpl +50 -0
  58. aury/boot/commands/templates/project/config.py.tpl +30 -0
  59. aury/boot/commands/templates/project/conftest.py.tpl +26 -0
  60. aury/boot/commands/templates/project/env.example.tpl +213 -0
  61. aury/boot/commands/templates/project/gitignore.tpl +128 -0
  62. aury/boot/commands/templates/project/main.py.tpl +41 -0
  63. aury/boot/commands/templates/project/modules/api.py.tpl +19 -0
  64. aury/boot/commands/templates/project/modules/exceptions.py.tpl +84 -0
  65. aury/boot/commands/templates/project/modules/schedules.py.tpl +18 -0
  66. aury/boot/commands/templates/project/modules/tasks.py.tpl +20 -0
  67. aury/boot/commands/worker.py +143 -0
  68. aury/boot/common/__init__.py +35 -0
  69. aury/boot/common/exceptions/__init__.py +114 -0
  70. aury/boot/common/i18n/__init__.py +16 -0
  71. aury/boot/common/i18n/translator.py +272 -0
  72. aury/boot/common/logging/__init__.py +716 -0
  73. aury/boot/contrib/__init__.py +10 -0
  74. aury/boot/contrib/admin_console/__init__.py +18 -0
  75. aury/boot/contrib/admin_console/auth.py +137 -0
  76. aury/boot/contrib/admin_console/discovery.py +69 -0
  77. aury/boot/contrib/admin_console/install.py +172 -0
  78. aury/boot/contrib/admin_console/utils.py +44 -0
  79. aury/boot/domain/__init__.py +79 -0
  80. aury/boot/domain/exceptions/__init__.py +132 -0
  81. aury/boot/domain/models/__init__.py +51 -0
  82. aury/boot/domain/models/base.py +69 -0
  83. aury/boot/domain/models/mixins.py +135 -0
  84. aury/boot/domain/models/models.py +96 -0
  85. aury/boot/domain/pagination/__init__.py +279 -0
  86. aury/boot/domain/repository/__init__.py +23 -0
  87. aury/boot/domain/repository/impl.py +423 -0
  88. aury/boot/domain/repository/interceptors.py +47 -0
  89. aury/boot/domain/repository/interface.py +106 -0
  90. aury/boot/domain/repository/query_builder.py +348 -0
  91. aury/boot/domain/service/__init__.py +11 -0
  92. aury/boot/domain/service/base.py +73 -0
  93. aury/boot/domain/transaction/__init__.py +404 -0
  94. aury/boot/infrastructure/__init__.py +104 -0
  95. aury/boot/infrastructure/cache/__init__.py +31 -0
  96. aury/boot/infrastructure/cache/backends.py +348 -0
  97. aury/boot/infrastructure/cache/base.py +68 -0
  98. aury/boot/infrastructure/cache/exceptions.py +37 -0
  99. aury/boot/infrastructure/cache/factory.py +94 -0
  100. aury/boot/infrastructure/cache/manager.py +274 -0
  101. aury/boot/infrastructure/database/__init__.py +39 -0
  102. aury/boot/infrastructure/database/config.py +71 -0
  103. aury/boot/infrastructure/database/exceptions.py +44 -0
  104. aury/boot/infrastructure/database/manager.py +317 -0
  105. aury/boot/infrastructure/database/query_tools/__init__.py +164 -0
  106. aury/boot/infrastructure/database/strategies/__init__.py +198 -0
  107. aury/boot/infrastructure/di/__init__.py +15 -0
  108. aury/boot/infrastructure/di/container.py +393 -0
  109. aury/boot/infrastructure/events/__init__.py +33 -0
  110. aury/boot/infrastructure/events/bus.py +362 -0
  111. aury/boot/infrastructure/events/config.py +52 -0
  112. aury/boot/infrastructure/events/consumer.py +134 -0
  113. aury/boot/infrastructure/events/middleware.py +51 -0
  114. aury/boot/infrastructure/events/models.py +63 -0
  115. aury/boot/infrastructure/monitoring/__init__.py +529 -0
  116. aury/boot/infrastructure/scheduler/__init__.py +19 -0
  117. aury/boot/infrastructure/scheduler/exceptions.py +37 -0
  118. aury/boot/infrastructure/scheduler/manager.py +478 -0
  119. aury/boot/infrastructure/storage/__init__.py +38 -0
  120. aury/boot/infrastructure/storage/base.py +164 -0
  121. aury/boot/infrastructure/storage/exceptions.py +37 -0
  122. aury/boot/infrastructure/storage/factory.py +88 -0
  123. aury/boot/infrastructure/tasks/__init__.py +24 -0
  124. aury/boot/infrastructure/tasks/config.py +45 -0
  125. aury/boot/infrastructure/tasks/constants.py +37 -0
  126. aury/boot/infrastructure/tasks/exceptions.py +37 -0
  127. aury/boot/infrastructure/tasks/manager.py +490 -0
  128. aury/boot/testing/__init__.py +24 -0
  129. aury/boot/testing/base.py +122 -0
  130. aury/boot/testing/client.py +163 -0
  131. aury/boot/testing/factory.py +154 -0
  132. aury/boot/toolkit/__init__.py +21 -0
  133. aury/boot/toolkit/http/__init__.py +367 -0
  134. {aury_boot-0.0.2.dist-info → aury_boot-0.0.3.dist-info}/METADATA +3 -2
  135. aury_boot-0.0.3.dist-info/RECORD +137 -0
  136. aury_boot-0.0.2.dist-info/RECORD +0 -5
  137. {aury_boot-0.0.2.dist-info → aury_boot-0.0.3.dist-info}/WHEEL +0 -0
  138. {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,11 @@
1
+ """服务模块。
2
+
3
+ 提供领域服务基类。
4
+ """
5
+
6
+ from .base import BaseService
7
+
8
+ __all__ = [
9
+ "BaseService",
10
+ ]
11
+
@@ -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
+