aury-boot 0.0.4__py3-none-any.whl → 0.0.7__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 (122) hide show
  1. aury/boot/__init__.py +2 -2
  2. aury/boot/_version.py +2 -2
  3. aury/boot/application/__init__.py +60 -36
  4. aury/boot/application/adapter/__init__.py +112 -0
  5. aury/boot/application/adapter/base.py +511 -0
  6. aury/boot/application/adapter/config.py +242 -0
  7. aury/boot/application/adapter/decorators.py +259 -0
  8. aury/boot/application/adapter/exceptions.py +202 -0
  9. aury/boot/application/adapter/http.py +325 -0
  10. aury/boot/application/app/__init__.py +12 -8
  11. aury/boot/application/app/base.py +12 -0
  12. aury/boot/application/app/components.py +137 -44
  13. aury/boot/application/app/middlewares.py +9 -4
  14. aury/boot/application/app/startup.py +249 -0
  15. aury/boot/application/config/__init__.py +36 -1
  16. aury/boot/application/config/multi_instance.py +216 -0
  17. aury/boot/application/config/settings.py +398 -149
  18. aury/boot/application/constants/components.py +6 -0
  19. aury/boot/application/errors/handlers.py +17 -3
  20. aury/boot/application/middleware/logging.py +21 -120
  21. aury/boot/application/rpc/__init__.py +2 -2
  22. aury/boot/commands/__init__.py +30 -10
  23. aury/boot/commands/app.py +131 -1
  24. aury/boot/commands/docs.py +104 -17
  25. aury/boot/commands/generate.py +22 -22
  26. aury/boot/commands/init.py +68 -17
  27. aury/boot/commands/server/app.py +2 -3
  28. aury/boot/commands/templates/project/AGENTS.md.tpl +221 -0
  29. aury/boot/commands/templates/project/README.md.tpl +2 -2
  30. aury/boot/commands/templates/project/aury_docs/00-overview.md.tpl +59 -0
  31. aury/boot/commands/templates/project/aury_docs/01-model.md.tpl +184 -0
  32. aury/boot/commands/templates/project/aury_docs/02-repository.md.tpl +206 -0
  33. aury/boot/commands/templates/project/aury_docs/03-service.md.tpl +398 -0
  34. aury/boot/commands/templates/project/aury_docs/04-schema.md.tpl +95 -0
  35. aury/boot/commands/templates/project/aury_docs/05-api.md.tpl +116 -0
  36. aury/boot/commands/templates/project/aury_docs/06-exception.md.tpl +118 -0
  37. aury/boot/commands/templates/project/aury_docs/07-cache.md.tpl +122 -0
  38. aury/boot/commands/templates/project/aury_docs/08-scheduler.md.tpl +32 -0
  39. aury/boot/commands/templates/project/aury_docs/09-tasks.md.tpl +38 -0
  40. aury/boot/commands/templates/project/aury_docs/10-storage.md.tpl +115 -0
  41. aury/boot/commands/templates/project/aury_docs/11-logging.md.tpl +131 -0
  42. aury/boot/commands/templates/project/aury_docs/12-admin.md.tpl +56 -0
  43. aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl +104 -0
  44. aury/boot/commands/templates/project/aury_docs/14-mq.md.tpl +102 -0
  45. aury/boot/commands/templates/project/aury_docs/15-events.md.tpl +147 -0
  46. aury/boot/commands/templates/project/aury_docs/16-adapter.md.tpl +403 -0
  47. aury/boot/commands/templates/project/{CLI.md.tpl → aury_docs/99-cli.md.tpl} +19 -19
  48. aury/boot/commands/templates/project/config.py.tpl +10 -10
  49. aury/boot/commands/templates/project/env_templates/_header.tpl +10 -0
  50. aury/boot/commands/templates/project/env_templates/admin.tpl +49 -0
  51. aury/boot/commands/templates/project/env_templates/cache.tpl +14 -0
  52. aury/boot/commands/templates/project/env_templates/database.tpl +22 -0
  53. aury/boot/commands/templates/project/env_templates/log.tpl +18 -0
  54. aury/boot/commands/templates/project/env_templates/messaging.tpl +46 -0
  55. aury/boot/commands/templates/project/env_templates/rpc.tpl +28 -0
  56. aury/boot/commands/templates/project/env_templates/scheduler.tpl +18 -0
  57. aury/boot/commands/templates/project/env_templates/service.tpl +18 -0
  58. aury/boot/commands/templates/project/env_templates/storage.tpl +38 -0
  59. aury/boot/commands/templates/project/env_templates/third_party.tpl +43 -0
  60. aury/boot/commands/templates/project/modules/tasks.py.tpl +1 -1
  61. aury/boot/common/logging/__init__.py +26 -674
  62. aury/boot/common/logging/context.py +132 -0
  63. aury/boot/common/logging/decorators.py +118 -0
  64. aury/boot/common/logging/format.py +315 -0
  65. aury/boot/common/logging/setup.py +214 -0
  66. aury/boot/contrib/admin_console/auth.py +2 -3
  67. aury/boot/contrib/admin_console/install.py +1 -1
  68. aury/boot/domain/models/mixins.py +48 -1
  69. aury/boot/domain/pagination/__init__.py +94 -0
  70. aury/boot/domain/repository/impl.py +1 -1
  71. aury/boot/domain/repository/interface.py +1 -1
  72. aury/boot/domain/transaction/__init__.py +8 -9
  73. aury/boot/infrastructure/__init__.py +86 -29
  74. aury/boot/infrastructure/cache/backends.py +102 -18
  75. aury/boot/infrastructure/cache/base.py +12 -0
  76. aury/boot/infrastructure/cache/manager.py +153 -91
  77. aury/boot/infrastructure/channel/__init__.py +24 -0
  78. aury/boot/infrastructure/channel/backends/__init__.py +9 -0
  79. aury/boot/infrastructure/channel/backends/memory.py +83 -0
  80. aury/boot/infrastructure/channel/backends/redis.py +88 -0
  81. aury/boot/infrastructure/channel/base.py +92 -0
  82. aury/boot/infrastructure/channel/manager.py +203 -0
  83. aury/boot/infrastructure/clients/__init__.py +22 -0
  84. aury/boot/infrastructure/clients/rabbitmq/__init__.py +9 -0
  85. aury/boot/infrastructure/clients/rabbitmq/config.py +46 -0
  86. aury/boot/infrastructure/clients/rabbitmq/manager.py +288 -0
  87. aury/boot/infrastructure/clients/redis/__init__.py +28 -0
  88. aury/boot/infrastructure/clients/redis/config.py +51 -0
  89. aury/boot/infrastructure/clients/redis/manager.py +264 -0
  90. aury/boot/infrastructure/database/config.py +7 -16
  91. aury/boot/infrastructure/database/manager.py +16 -38
  92. aury/boot/infrastructure/events/__init__.py +18 -21
  93. aury/boot/infrastructure/events/backends/__init__.py +11 -0
  94. aury/boot/infrastructure/events/backends/memory.py +86 -0
  95. aury/boot/infrastructure/events/backends/rabbitmq.py +193 -0
  96. aury/boot/infrastructure/events/backends/redis.py +162 -0
  97. aury/boot/infrastructure/events/base.py +127 -0
  98. aury/boot/infrastructure/events/manager.py +224 -0
  99. aury/boot/infrastructure/mq/__init__.py +24 -0
  100. aury/boot/infrastructure/mq/backends/__init__.py +9 -0
  101. aury/boot/infrastructure/mq/backends/rabbitmq.py +179 -0
  102. aury/boot/infrastructure/mq/backends/redis.py +167 -0
  103. aury/boot/infrastructure/mq/base.py +143 -0
  104. aury/boot/infrastructure/mq/manager.py +239 -0
  105. aury/boot/infrastructure/scheduler/manager.py +7 -3
  106. aury/boot/infrastructure/storage/__init__.py +9 -9
  107. aury/boot/infrastructure/storage/base.py +17 -5
  108. aury/boot/infrastructure/storage/factory.py +0 -1
  109. aury/boot/infrastructure/tasks/__init__.py +2 -2
  110. aury/boot/infrastructure/tasks/config.py +5 -13
  111. aury/boot/infrastructure/tasks/manager.py +55 -33
  112. {aury_boot-0.0.4.dist-info → aury_boot-0.0.7.dist-info}/METADATA +20 -2
  113. aury_boot-0.0.7.dist-info/RECORD +197 -0
  114. aury/boot/commands/templates/project/DEVELOPMENT.md.tpl +0 -1397
  115. aury/boot/commands/templates/project/env.example.tpl +0 -213
  116. aury/boot/infrastructure/events/bus.py +0 -362
  117. aury/boot/infrastructure/events/config.py +0 -52
  118. aury/boot/infrastructure/events/consumer.py +0 -134
  119. aury/boot/infrastructure/events/models.py +0 -63
  120. aury_boot-0.0.4.dist-info/RECORD +0 -137
  121. {aury_boot-0.0.4.dist-info → aury_boot-0.0.7.dist-info}/WHEEL +0 -0
  122. {aury_boot-0.0.4.dist-info → aury_boot-0.0.7.dist-info}/entry_points.txt +0 -0
@@ -183,7 +183,7 @@ MODEL_BASE_CLASSES = {
183
183
  "has_timestamps": True,
184
184
  },
185
185
  "AuditableStateModel": {
186
- "desc": "标准模型 + 软删除",
186
+ "desc": "int 主键 + 软删除(推荐)",
187
187
  "features": ["id: int", "created_at", "updated_at", "deleted_at"],
188
188
  "id_type": "int",
189
189
  "has_timestamps": True,
@@ -195,7 +195,7 @@ MODEL_BASE_CLASSES = {
195
195
  "has_timestamps": True,
196
196
  },
197
197
  "UUIDAuditableStateModel": {
198
- "desc": "UUID 主键 + 软删除(推荐)",
198
+ "desc": "UUID 主键 + 软删除",
199
199
  "features": ["id: UUID", "created_at", "updated_at", "deleted_at"],
200
200
  "id_type": "uuid",
201
201
  "has_timestamps": True,
@@ -219,13 +219,13 @@ MODEL_BASE_CLASSES = {
219
219
  "has_timestamps": True,
220
220
  },
221
221
  "FullFeaturedModel": {
222
- "desc": "完整功能 int版",
222
+ "desc": "int 主键 + 全功能",
223
223
  "features": ["id: int", "created_at", "updated_at", "deleted_at", "version"],
224
224
  "id_type": "int",
225
225
  "has_timestamps": True,
226
226
  },
227
227
  "FullFeaturedUUIDModel": {
228
- "desc": "完整功能 UUID版",
228
+ "desc": "UUID 主键 + 全功能",
229
229
  "features": ["id: UUID", "created_at", "updated_at", "deleted_at", "version"],
230
230
  "id_type": "uuid",
231
231
  "has_timestamps": True,
@@ -250,9 +250,9 @@ class ModelDefinition:
250
250
  def id_type(self) -> str:
251
251
  """获取 id 类型:'int' 或 'uuid'。"""
252
252
  if self.base_class:
253
- return MODEL_BASE_CLASSES.get(self.base_class, {}).get("id_type", "uuid")
254
- # 默认通过 soft_delete/timestamps 推断时使用 UUID
255
- return "uuid"
253
+ return MODEL_BASE_CLASSES.get(self.base_class, {}).get("id_type", "int")
254
+ # 默认使用 int 主键
255
+ return "int"
256
256
 
257
257
  @property
258
258
  def id_py_type(self) -> str:
@@ -373,14 +373,14 @@ def _collect_base_class_interactive() -> str:
373
373
  info = MODEL_BASE_CLASSES[name]
374
374
  # 推荐的加标记
375
375
  desc = info["desc"]
376
- if name == "UUIDAuditableStateModel":
376
+ if name == "AuditableStateModel":
377
377
  desc = f"[bold green]★ {desc}[/bold green]"
378
378
  table.add_row(str(i), name, desc, ", ".join(info["features"]))
379
379
 
380
380
  console.print(table)
381
381
  console.print()
382
382
 
383
- # 默认选择 UUIDAuditableStateModel(第 4 个)
383
+ # 默认选择 AuditableStateModel(第 4 个)
384
384
  choice = Prompt.ask(
385
385
  "请选择基类序号",
386
386
  default="4",
@@ -556,16 +556,16 @@ def _generate_model_content(model: ModelDefinition) -> str:
556
556
  features = base_info.get("features", [])
557
557
  base_doc = f"继承 {base_class} 自动获得:\n - " + "\n - ".join(features) if features else f"继承 {base_class} 基类。"
558
558
  elif model.soft_delete and model.timestamps:
559
- base_class = "UUIDAuditableStateModel"
560
- base_doc = """继承 UUIDAuditableStateModel 自动获得:
561
- - id: UUID 主键
559
+ base_class = "AuditableStateModel"
560
+ base_doc = """继承 AuditableStateModel 自动获得:
561
+ - id: int 自增主键
562
562
  - created_at: 创建时间
563
563
  - updated_at: 更新时间
564
564
  - deleted_at: 软删除时间戳"""
565
565
  elif model.timestamps:
566
- base_class = "UUIDModel"
567
- base_doc = """继承 UUIDModel 自动获得:
568
- - id: UUID 主键
566
+ base_class = "Model"
567
+ base_doc = """继承 Model 自动获得:
568
+ - id: int 自增主键
569
569
  - created_at: 创建时间
570
570
  - updated_at: 更新时间"""
571
571
  else:
@@ -773,7 +773,7 @@ def generate_model(
773
773
  ),
774
774
  base: str | None = typer.Option(
775
775
  None, "--base", "-b",
776
- help="模型基类(Model/UUIDModel/UUIDAuditableStateModel/VersionedModel 等)"
776
+ help="模型基类(AuditableStateModel/Model/FullFeaturedModel 等)"
777
777
  ),
778
778
  force: bool = typer.Option(False, "--force", "-f", help="强制覆盖"),
779
779
  no_soft_delete: bool = typer.Option(False, "--no-soft-delete", help="禁用软删除"),
@@ -803,9 +803,9 @@ def generate_model(
803
803
  (length) - 字符串长度,如 str(100)
804
804
 
805
805
  可用基类:
806
- Model, AuditableStateModel, UUIDModel, UUIDAuditableStateModel,
807
- VersionedModel, VersionedTimestampedModel, VersionedUUIDModel,
808
- FullFeaturedModel, FullFeaturedUUIDModel
806
+ AuditableStateModel, Model, FullFeaturedModel,
807
+ UUIDModel, UUIDAuditableStateModel, FullFeaturedUUIDModel,
808
+ VersionedModel, VersionedTimestampedModel, VersionedUUIDModel
809
809
 
810
810
  示例:
811
811
  aury generate model user
@@ -1104,7 +1104,7 @@ def generate_crud(
1104
1104
  ),
1105
1105
  base: str | None = typer.Option(
1106
1106
  None, "--base", "-b",
1107
- help="模型基类(Model/UUIDModel/UUIDAuditableStateModel/VersionedModel 等)"
1107
+ help="模型基类(AuditableStateModel/Model/FullFeaturedModel 等)"
1108
1108
  ),
1109
1109
  force: bool = typer.Option(False, "--force", "-f", help="强制覆盖"),
1110
1110
  no_soft_delete: bool = typer.Option(False, "--no-soft-delete", help="禁用软删除"),
@@ -1122,8 +1122,8 @@ def generate_crud(
1122
1122
 
1123
1123
  示例:
1124
1124
  aury generate crud user
1125
- aury generate crud user --base Model # 使用 int 主键
1126
- aury generate crud user --base UUIDAuditableStateModel # 使用 UUID 主键(推荐)
1125
+ aury generate crud user --base AuditableStateModel # int 主键 + 软删除(推荐)
1126
+ aury generate crud user --base Model # int 主键 + 时间戳
1127
1127
  aury generate crud user email:str:unique age:int? --force
1128
1128
  aury generate crud article title:str(200) content:text status:str=draft
1129
1129
  """
@@ -153,15 +153,29 @@ dev = [
153
153
  TEMPLATE_FILE_MAP = {
154
154
  "main.py": "main.py.tpl",
155
155
  "config.py": "config.py.tpl",
156
- ".env.example": "env.example.tpl",
156
+ ".env.example": "env_templates", # 特殊处理:拼接 env_templates/ 目录下的所有 .tpl 文件
157
157
  ".gitignore": "gitignore.tpl",
158
158
  "README.md": "README.md.tpl",
159
- "DEVELOPMENT.md": "DEVELOPMENT.md.tpl",
160
- "CLI.md": "CLI.md.tpl",
159
+ "AGENTS.md": "AGENTS.md.tpl",
161
160
  "conftest.py": "conftest.py.tpl",
162
161
  "admin_console/__init__.py": "admin_console_init.py.tpl",
163
162
  }
164
163
 
164
+ # env 模板拼接顺序
165
+ ENV_TEMPLATE_ORDER = [
166
+ "_header.tpl",
167
+ "service.tpl",
168
+ "database.tpl",
169
+ "cache.tpl",
170
+ "log.tpl",
171
+ "admin.tpl",
172
+ "scheduler.tpl",
173
+ "messaging.tpl",
174
+ "storage.tpl",
175
+ "third_party.tpl",
176
+ "rpc.tpl",
177
+ ]
178
+
165
179
  # 模块 __init__.py 模板映射
166
180
  MODULE_TEMPLATE_MAP = {
167
181
  "api": "api.py.tpl",
@@ -171,8 +185,25 @@ MODULE_TEMPLATE_MAP = {
171
185
  }
172
186
 
173
187
 
188
+ def _read_env_template() -> str:
189
+ """读取并拼接 env_templates/ 目录下的所有模板文件。"""
190
+ env_dir = TEMPLATES_DIR / "env_templates"
191
+ parts = []
192
+
193
+ for tpl_name in ENV_TEMPLATE_ORDER:
194
+ tpl_path = env_dir / tpl_name
195
+ if tpl_path.exists():
196
+ parts.append(tpl_path.read_text(encoding="utf-8"))
197
+
198
+ return "\n".join(parts)
199
+
200
+
174
201
  def _read_template(name: str) -> str:
175
202
  """读取模板文件。"""
203
+ # 特殊处理 .env.example:拼接 env_templates/ 目录
204
+ if name == ".env.example":
205
+ return _read_env_template()
206
+
176
207
  # 先尝试从映射中查找 .tpl 文件
177
208
  tpl_name = TEMPLATE_FILE_MAP.get(name)
178
209
  if tpl_name:
@@ -245,22 +276,22 @@ def init_admin_console_module(
245
276
  dest.write_text(content, encoding="utf-8")
246
277
  result["file_created"] = True
247
278
 
248
- # 2) 尝试在 .env.example 中开启 ADMIN_* 配置
279
+ # 2) 尝试在 .env.example 中开启 ADMIN__* 配置
249
280
  if enable_env:
250
281
  env_example = base_path / ".env.example"
251
282
  if env_example.exists():
252
283
  try:
253
284
  s = env_example.read_text(encoding="utf-8")
254
285
  s2 = (
255
- s.replace("# ADMIN_ENABLED=false", "ADMIN_ENABLED=true")
256
- .replace("# ADMIN_PATH=/api/admin-console", "ADMIN_PATH=/api/admin-console")
257
- .replace("# ADMIN_AUTH_MODE=basic", "ADMIN_AUTH_MODE=basic")
286
+ s.replace("# ADMIN__ENABLED=false", "ADMIN__ENABLED=true")
287
+ .replace("# ADMIN__PATH=/api/admin-console", "ADMIN__PATH=/api/admin-console")
288
+ .replace("# ADMIN__AUTH__MODE=basic", "ADMIN__AUTH__MODE=basic")
258
289
  .replace(
259
- "# ADMIN_AUTH_SECRET_KEY=CHANGE_ME_TO_A_RANDOM_SECRET",
260
- "ADMIN_AUTH_SECRET_KEY=CHANGE_ME_TO_A_RANDOM_SECRET",
290
+ "# ADMIN__AUTH__SECRET_KEY=CHANGE_ME_TO_A_RANDOM_SECRET",
291
+ "ADMIN__AUTH__SECRET_KEY=CHANGE_ME_TO_A_RANDOM_SECRET",
261
292
  )
262
- .replace("# ADMIN_AUTH_BASIC_USERNAME=admin", "ADMIN_AUTH_BASIC_USERNAME=admin")
263
- .replace("# ADMIN_AUTH_BASIC_PASSWORD=change_me", "ADMIN_AUTH_BASIC_PASSWORD=change_me")
293
+ .replace("# ADMIN__AUTH__BASIC_USERNAME=admin", "ADMIN__AUTH__BASIC_USERNAME=admin")
294
+ .replace("# ADMIN__AUTH__BASIC_PASSWORD=change_me", "ADMIN__AUTH__BASIC_PASSWORD=change_me")
264
295
  )
265
296
  if s2 != s:
266
297
  env_example.write_text(s2, encoding="utf-8")
@@ -712,8 +743,7 @@ def init(
712
743
  (code_root / "admin_console" / "__init__.py", "admin_console/__init__.py", False),
713
744
  (base_path / "tests" / "conftest.py", "conftest.py", False), # tests 放在项目根目录
714
745
  (base_path / "README.md", "README.md", True), # 覆盖 uv init 创建的默认 README
715
- (base_path / "DEVELOPMENT.md", "DEVELOPMENT.md", False), # 开发指南
716
- (base_path / "CLI.md", "CLI.md", False), # CLI 命令参考
746
+ (base_path / "AGENTS.md", "AGENTS.md", False), # AI 编程助手上下文
717
747
  (base_path / ".gitignore", ".gitignore", False), # Git 忽略文件
718
748
  ]
719
749
 
@@ -806,13 +836,34 @@ def init(
806
836
  else:
807
837
  console.print(" [dim]ℹ️ migrations/ 目录已存在,跳过[/dim]")
808
838
 
809
- # 5. 生成 Docker 配置
839
+ # 5. 生成开发文档 (aury_docs/) - 动态扫描模板目录
840
+ console.print("\n[bold]📚 生成开发文档...[/bold]")
841
+ aury_docs_tpl_dir = TEMPLATES_DIR / "aury_docs"
842
+ aury_docs_dir = base_path / "aury_docs"
843
+ aury_docs_dir.mkdir(parents=True, exist_ok=True)
844
+ docs_count = 0
845
+ if aury_docs_tpl_dir.exists():
846
+ for tpl_path in sorted(aury_docs_tpl_dir.glob("*.md.tpl")):
847
+ output_name = tpl_path.stem # 去掉 .tpl 后缀,保留 .md
848
+ output_path = aury_docs_dir / output_name
849
+ if output_path.exists() and not force:
850
+ continue
851
+ try:
852
+ content = tpl_path.read_text(encoding="utf-8")
853
+ content = content.format(**template_vars)
854
+ output_path.write_text(content, encoding="utf-8")
855
+ docs_count += 1
856
+ except Exception:
857
+ pass
858
+ console.print(f" [green]✅ 已生成 {docs_count} 个文档到 aury_docs/[/green]")
859
+
860
+ # 6. 生成 Docker 配置
810
861
  if with_docker:
811
862
  console.print("\n[bold]🐳 生成 Docker 配置...[/bold]")
812
863
  from .docker import docker_init
813
864
  docker_init(force=force)
814
865
 
815
- # 6. 显示结果
866
+ # 7. 显示结果
816
867
  console.print("\n")
817
868
 
818
869
  tree = Tree(f"[bold cyan]{project_name}/[/bold cyan]")
@@ -821,8 +872,8 @@ def init(
821
872
  tree.add("[dim]alembic.ini[/dim]")
822
873
  tree.add("[dim]pyproject.toml[/dim]")
823
874
  tree.add("[dim]README.md[/dim]")
824
- tree.add("[dim]DEVELOPMENT.md[/dim]")
825
- tree.add("[dim]CLI.md[/dim]")
875
+ tree.add("[dim]AGENTS.md[/dim]")
876
+ tree.add("[blue]aury_docs/[/blue]")
826
877
  if with_docker:
827
878
  tree.add("[dim]Dockerfile[/dim]")
828
879
  tree.add("[dim]docker-compose.yml[/dim]")
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import contextlib
5
6
  import os
6
7
  from pathlib import Path
7
8
  import sys
@@ -339,13 +340,11 @@ def dev(
339
340
  typer.echo(f" 应用模块: {app_module_path}")
340
341
 
341
342
  # 在应用启动完成后打印一次服务地址
342
- try:
343
+ with contextlib.suppress(Exception):
343
344
  app_instance.add_event_handler(
344
345
  "startup",
345
346
  lambda: typer.echo(f"✅ 服务已就绪: http://{server_host}:{server_port}"),
346
347
  )
347
- except Exception:
348
- pass
349
348
 
350
349
  # 默认包含/排除规则(watchfiles 支持)
351
350
  reload_includes = [
@@ -0,0 +1,221 @@
1
+ # AGENTS.md
2
+
3
+ > 这是给 AI 编程助手阅读的项目上下文文件。根据开发任务类型,阅读下方「开发任务文档索引」中对应的文档。
4
+
5
+ ## 项目概述
6
+
7
+ - **项目名称**: {project_name}
8
+ - **包名**: {package_name}
9
+ - **框架**: [Aury Boot](https://github.com/AuriMyth/aury-boot)(基于 FastAPI + SQLAlchemy 2.0 的微服务框架)
10
+ - **Python 版本**: >= 3.13
11
+ - **包管理**: uv(推荐)或 pip
12
+
13
+ ## 常用命令
14
+
15
+ ```bash
16
+ # 开发服务器(热重载)
17
+ aury server dev
18
+
19
+ # 数据库迁移
20
+ aury migrate make -m "描述" # 生成迁移
21
+ aury migrate up # 执行迁移
22
+ aury migrate down # 回滚迁移
23
+ aury migrate status # 查看状态
24
+ aury migrate show # 查看历史
25
+
26
+ # 测试
27
+ pytest
28
+ pytest tests/test_xxx.py -v
29
+
30
+ # 代码检查
31
+ ruff check .
32
+ mypy {package_name}/
33
+ ```
34
+
35
+ > ⚠️ **重要**:数据库迁移文件**必须**使用 `aury migrate make` 命令生成,**禁止**手写迁移文件!
36
+
37
+ ## 项目结构
38
+
39
+ ```
40
+ {project_name}/
41
+ ├── {package_name}/ # 主代码包
42
+ │ ├── models/ # SQLAlchemy ORM 模型
43
+ │ ├── repositories/ # 数据访问层(Repository 模式)
44
+ │ ├── services/ # 业务逻辑层
45
+ │ ├── schemas/ # Pydantic 请求/响应模型
46
+ │ ├── api/ # FastAPI 路由
47
+ │ ├── exceptions/ # 业务异常
48
+ │ ├── tasks/ # 异步任务(Dramatiq)
49
+ │ └── schedules/ # 定时任务
50
+ ├── aury_docs/ # 开发文档(由 aury docs dev 生成)
51
+ ├── tests/ # 测试
52
+ ├── migrations/ # 数据库迁移
53
+ └── main.py # 应用入口
54
+ ```
55
+
56
+ ## 重要提醒
57
+
58
+ > **修改代码前,必须先阅读对应的文档!**
59
+ >
60
+ > 框架的 API 可能与常见框架不同,不要猜测。请查看 `aury_docs/` 下对应的文档。
61
+
62
+ ## 开发任务文档索引
63
+
64
+ 根据你要开发的功能类型,**必须阅读**对应的文档:
65
+
66
+ ### CRUD / 数据库相关(最常见)
67
+
68
+ 开发 CRUD 功能时,按顺序阅读以下文档:
69
+
70
+ 1. **[aury_docs/01-model.md](./aury_docs/01-model.md)** - Model 定义规范
71
+ - 基类选择(AuditableStateModel 等)
72
+ - 字段类型映射
73
+ - 约束定义(软删除模型的复合唯一约束)
74
+
75
+ 2. **[aury_docs/02-repository.md](./aury_docs/02-repository.md)** - Repository 使用
76
+ - BaseRepository API
77
+ - Filters 语法(__gt, __like 等)
78
+ - 自动提交机制
79
+
80
+ 3. **[aury_docs/03-service.md](./aury_docs/03-service.md)** - Service 编写与事务
81
+ - 事务装饰器 @transactional
82
+ - 跨 Service 调用
83
+ - 事务传播 / Savepoints / on_commit 回调
84
+ - SELECT FOR UPDATE
85
+
86
+ 4. **[aury_docs/04-schema.md](./aury_docs/04-schema.md)** - Pydantic Schema
87
+ - 请求/响应模型
88
+ - 常用验证
89
+
90
+ 5. **[aury_docs/05-api.md](./aury_docs/05-api.md)** - API 路由
91
+ - 路由编写示例
92
+ - 依赖注入模式
93
+
94
+ 6. **[aury_docs/06-exception.md](./aury_docs/06-exception.md)** - 异常处理
95
+ - 内置异常
96
+ - 自定义异常
97
+
98
+ ### 异步任务 / 定时任务
99
+
100
+ - **[aury_docs/08-scheduler.md](./aury_docs/08-scheduler.md)** - 定时任务(APScheduler)
101
+ - **[aury_docs/09-tasks.md](./aury_docs/09-tasks.md)** - 异步任务(Dramatiq)
102
+
103
+ ### 基础设施
104
+
105
+ - **[aury_docs/07-cache.md](./aury_docs/07-cache.md)** - 缓存
106
+ - **[aury_docs/10-storage.md](./aury_docs/10-storage.md)** - 对象存储(S3/COS/OSS)
107
+ - **[aury_docs/11-logging.md](./aury_docs/11-logging.md)** - 日志
108
+ - **[aury_docs/12-admin.md](./aury_docs/12-admin.md)** - 管理后台(SQLAdmin)
109
+ - **[aury_docs/13-channel.md](./aury_docs/13-channel.md)** - 流式通道(SSE)
110
+ - **[aury_docs/14-mq.md](./aury_docs/14-mq.md)** - 消息队列
111
+ - **[aury_docs/15-events.md](./aury_docs/15-events.md)** - 事件总线
112
+
113
+ ### 第三方集成
114
+
115
+ - **[aury_docs/16-adapter.md](./aury_docs/16-adapter.md)** - 第三方接口适配器(Mock/真实切换)
116
+
117
+ ### 配置 / CLI / 环境变量
118
+
119
+ - **[aury_docs/00-overview.md](./aury_docs/00-overview.md)** - 项目概览与最佳实践
120
+ - **[aury_docs/99-cli.md](./aury_docs/99-cli.md)** - CLI 命令参考
121
+ - **[.env.example](./.env.example)** - 所有可用环境变量
122
+
123
+ ## 代码规范
124
+
125
+ ### Model 规范
126
+
127
+ - **必须**继承框架预定义基类,**不要**直接继承 `Base`
128
+ - **推荐**使用 `AuditableStateModel`(int 主键 + 时间戳 + 软删除)
129
+ - 软删除模型**必须**使用复合唯一约束(包含 `deleted_at`),不能单独使用 `unique=True`
130
+ - **不建议**使用数据库外键(`ForeignKey`),通过程序控制关系,便于分库分表和微服务拆分
131
+
132
+ ```python
133
+ # ✅ 正确
134
+ from aury.boot.domain.models import AuditableStateModel
135
+
136
+ class User(AuditableStateModel):
137
+ __tablename__ = "users"
138
+ email: Mapped[str] = mapped_column(String(255), index=True)
139
+ __table_args__ = (
140
+ UniqueConstraint("email", "deleted_at", name="uq_users_email_deleted"),
141
+ )
142
+
143
+ # ❌ 错误:直接继承 Base
144
+ from aury.boot.domain.models.base import Base
145
+ class User(Base): ...
146
+ ```
147
+
148
+ ### Service 规范
149
+
150
+ - 写操作**必须**使用 `@transactional` 装饰器
151
+ - 只读操作可以不加事务装饰器
152
+ - 跨 Service 调用通过共享 session 实现事务共享
153
+
154
+ ```python
155
+ from aury.boot.domain.transaction import transactional
156
+
157
+ class UserService(BaseService):
158
+ @transactional
159
+ async def create(self, data: UserCreate) -> User:
160
+ # 自动事务管理
161
+ return await self.repo.create(data.model_dump())
162
+ ```
163
+
164
+ ### Manager API 规范
165
+
166
+ 所有基础设施 Manager 统一使用 `initialize()` 方法初始化:
167
+
168
+ ```python
169
+ # ✅ 正确
170
+ from aury.boot.infrastructure.cache import CacheManager
171
+ cache = CacheManager.get_instance()
172
+ await cache.initialize(backend="redis", url="redis://localhost:6379")
173
+
174
+ from aury.boot.infrastructure.storage import StorageManager, StorageConfig
175
+ storage = StorageManager.get_instance()
176
+ await storage.initialize(StorageConfig(...))
177
+
178
+ from aury.boot.infrastructure.events import EventBusManager
179
+ events = EventBusManager.get_instance()
180
+ await events.initialize(backend="memory")
181
+
182
+ # ❗ 注意:没有 configure() 方法,配置直接传入 initialize()
183
+ ```
184
+
185
+ ### 异常规范
186
+
187
+ - **必须**继承框架异常类,**不要**直接继承 `Exception`
188
+ - 使用框架内置异常:`NotFoundError`, `AlreadyExistsError`, `UnauthorizedError` 等
189
+
190
+ ```python
191
+ # ✅ 正确
192
+ from aury.boot.application.errors import NotFoundError
193
+ raise NotFoundError("用户不存在", resource=user_id)
194
+
195
+ # ❌ 错误
196
+ raise Exception("用户不存在")
197
+ ```
198
+
199
+ ### 响应规范
200
+
201
+ - **不要**自定义通用响应 Schema
202
+ - 使用框架内置:`BaseResponse`, `PaginationResponse`
203
+
204
+ ```python
205
+ from aury.boot.application.interfaces.egress import BaseResponse
206
+ return BaseResponse(code=200, message="成功", data=user)
207
+ ```
208
+
209
+ ## 禁止操作
210
+
211
+ - ❌ 不要直接修改 `aury/boot/` 框架代码
212
+ - ❌ 不要在 Model 中直接使用 `unique=True`(软删除模型)
213
+ - ❌ 不要自定义通用响应 Schema
214
+ - ❌ 不要在 Repository 之外直接操作 session
215
+ - ❌ 不要提交 `.env` 文件到版本库
216
+
217
+ ## 需要确认的操作
218
+
219
+ - ⚠️ 添加新的 pip 依赖前请确认
220
+ - ⚠️ 修改数据库迁移前请确认
221
+ - ⚠️ 删除文件前请确认
@@ -106,6 +106,6 @@ aury worker
106
106
 
107
107
  ## 文档
108
108
 
109
- - [DEVELOPMENT.md](./DEVELOPMENT.md) - 开发指南(代码组织与规范)
110
- - [CLI.md](./CLI.md) - CLI 命令参考
109
+ - [AGENTS.md](./AGENTS.md) - AI 编程助手上下文
110
+ - [aury_docs/](./aury_docs/) - 开发文档(包含 Model/Service/API 等指南)
111
111
  - [Aury Boot 文档](https://github.com/AuriMyth/aury-boot)
@@ -0,0 +1,59 @@
1
+ # {project_name} 开发指南
2
+
3
+ 本文档基于 [Aury Boot](https://github.com/AuriMyth/aury-boot) 框架。
4
+
5
+ CLI 命令参考请查看 [99-cli.md](./99-cli.md)。
6
+
7
+ ---
8
+
9
+ ## 目录结构
10
+
11
+ ```
12
+ {project_name}/
13
+ ├── {package_name}/ # 代码包(默认 app,可通过 aury init <pkg> 自定义)
14
+ │ ├── models/ # SQLAlchemy ORM 模型
15
+ │ ├── repositories/ # 数据访问层
16
+ │ ├── services/ # 业务逻辑层
17
+ │ ├── schemas/ # Pydantic 请求/响应模型
18
+ │ ├── api/ # FastAPI 路由
19
+ │ ├── exceptions/ # 业务异常
20
+ │ ├── tasks/ # 异步任务(Dramatiq)
21
+ │ └── schedules/ # 定时任务(Scheduler)
22
+ ├── tests/ # 测试
23
+ ├── migrations/ # 数据库迁移
24
+ └── main.py # 应用入口
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 最佳实践
30
+
31
+ 1. **分层架构**:API → Service → Repository → Model
32
+ 2. **事务管理**:在 Service 层使用 `@transactional`,只读操作可不加
33
+ 3. **错误处理**:使用框架异常类,全局异常处理器统一处理
34
+ 4. **配置管理**:使用 `.env` 文件,不提交到版本库
35
+ 5. **日志记录**:使用框架 logger,支持结构化日志和链路追踪
36
+ 6. **多实例配置**:环境变量格式 `{{PREFIX}}_{{INSTANCE}}_{{FIELD}}`
37
+
38
+ ---
39
+
40
+ ## 文档索引
41
+
42
+ ### CRUD / 数据库
43
+ - [01-model.md](./01-model.md) - Model 定义
44
+ - [02-repository.md](./02-repository.md) - Repository 使用
45
+ - [03-service.md](./03-service.md) - Service 与事务
46
+ - [04-schema.md](./04-schema.md) - Pydantic Schema
47
+ - [05-api.md](./05-api.md) - API 路由
48
+ - [06-exception.md](./06-exception.md) - 异常处理
49
+
50
+ ### 基础设施
51
+ - [07-cache.md](./07-cache.md) - 缓存
52
+ - [08-scheduler.md](./08-scheduler.md) - 定时任务
53
+ - [09-tasks.md](./09-tasks.md) - 异步任务
54
+ - [10-storage.md](./10-storage.md) - 对象存储
55
+ - [11-logging.md](./11-logging.md) - 日志
56
+ - [12-admin.md](./12-admin.md) - 管理后台
57
+ - [13-channel.md](./13-channel.md) - 流式通道(SSE)
58
+ - [14-mq.md](./14-mq.md) - 消息队列
59
+ - [15-events.md](./15-events.md) - 事件总线