aury-boot 0.0.19__tar.gz → 0.0.29__tar.gz

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 (200) hide show
  1. {aury_boot-0.0.19 → aury_boot-0.0.29}/PKG-INFO +3 -8
  2. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/_version.py +2 -2
  3. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/app/base.py +6 -2
  4. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/app/components.py +46 -0
  5. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/config/settings.py +32 -2
  6. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/constants/components.py +3 -0
  7. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/errors/handlers.py +40 -16
  8. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/pkg.py +3 -3
  9. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/AGENTS.md.tpl +13 -1
  10. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/02-repository.md.tpl +31 -0
  11. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/03-service.md.tpl +60 -0
  12. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/08-scheduler.md.tpl +77 -24
  13. aury_boot-0.0.29/aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl +152 -0
  14. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/messaging.tpl +8 -5
  15. aury_boot-0.0.29/aury/boot/commands/templates/project/modules/schedules.py.tpl +38 -0
  16. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/repository/impl.py +164 -0
  17. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/transaction/__init__.py +57 -0
  18. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/channel/backends/memory.py +43 -0
  19. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/channel/backends/redis.py +42 -0
  20. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/channel/base.py +15 -0
  21. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/channel/manager.py +41 -11
  22. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/backends/redis.py +9 -2
  23. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/manager.py +9 -5
  24. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/scheduler/manager.py +198 -37
  25. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/tasks/manager.py +37 -17
  26. {aury_boot-0.0.19 → aury_boot-0.0.29}/pyproject.toml +2 -7
  27. aury_boot-0.0.19/aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl +0 -104
  28. aury_boot-0.0.19/aury/boot/commands/templates/project/modules/schedules.py.tpl +0 -21
  29. {aury_boot-0.0.19 → aury_boot-0.0.29}/.gitignore +0 -0
  30. {aury_boot-0.0.19 → aury_boot-0.0.29}/README.md +0 -0
  31. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/__init__.py +0 -0
  32. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/__init__.py +0 -0
  33. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/adapter/__init__.py +0 -0
  34. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/adapter/base.py +0 -0
  35. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/adapter/config.py +0 -0
  36. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/adapter/decorators.py +0 -0
  37. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/adapter/exceptions.py +0 -0
  38. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/adapter/http.py +0 -0
  39. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/app/__init__.py +0 -0
  40. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/app/middlewares.py +0 -0
  41. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/app/startup.py +0 -0
  42. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/config/__init__.py +0 -0
  43. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/config/multi_instance.py +0 -0
  44. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/constants/__init__.py +0 -0
  45. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/constants/scheduler.py +0 -0
  46. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/constants/service.py +0 -0
  47. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/errors/__init__.py +0 -0
  48. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/errors/chain.py +0 -0
  49. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/errors/codes.py +0 -0
  50. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/errors/exceptions.py +0 -0
  51. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/errors/response.py +0 -0
  52. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/interfaces/__init__.py +0 -0
  53. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/interfaces/egress.py +0 -0
  54. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/interfaces/ingress.py +0 -0
  55. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/middleware/__init__.py +0 -0
  56. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/middleware/logging.py +0 -0
  57. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/migrations/__init__.py +0 -0
  58. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/migrations/manager.py +0 -0
  59. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/migrations/setup.py +0 -0
  60. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/rpc/__init__.py +0 -0
  61. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/rpc/base.py +0 -0
  62. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/rpc/client.py +0 -0
  63. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/rpc/discovery.py +0 -0
  64. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/scheduler/__init__.py +0 -0
  65. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/scheduler/runner.py +0 -0
  66. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/application/server/__init__.py +0 -0
  67. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/__init__.py +0 -0
  68. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/add.py +0 -0
  69. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/app.py +0 -0
  70. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/config.py +0 -0
  71. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/docker.py +0 -0
  72. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/docs.py +0 -0
  73. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/generate.py +0 -0
  74. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/init.py +0 -0
  75. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/migrate/__init__.py +0 -0
  76. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/migrate/app.py +0 -0
  77. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/migrate/commands.py +0 -0
  78. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/scheduler.py +0 -0
  79. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/server/__init__.py +0 -0
  80. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/server/app.py +0 -0
  81. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/generate/api.py.tpl +0 -0
  82. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/generate/model.py.tpl +0 -0
  83. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/generate/repository.py.tpl +0 -0
  84. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/generate/schema.py.tpl +0 -0
  85. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/generate/service.py.tpl +0 -0
  86. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/README.md.tpl +0 -0
  87. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/admin_console_init.py.tpl +0 -0
  88. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/00-overview.md.tpl +0 -0
  89. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/01-model.md.tpl +0 -0
  90. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/04-schema.md.tpl +0 -0
  91. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/05-api.md.tpl +0 -0
  92. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/06-exception.md.tpl +0 -0
  93. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/07-cache.md.tpl +0 -0
  94. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/09-tasks.md.tpl +0 -0
  95. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/10-storage.md.tpl +0 -0
  96. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/11-logging.md.tpl +0 -0
  97. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/12-admin.md.tpl +0 -0
  98. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/14-mq.md.tpl +0 -0
  99. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/15-events.md.tpl +0 -0
  100. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/16-adapter.md.tpl +0 -0
  101. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/aury_docs/99-cli.md.tpl +0 -0
  102. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/config.py.tpl +0 -0
  103. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/conftest.py.tpl +0 -0
  104. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/_header.tpl +0 -0
  105. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/admin.tpl +0 -0
  106. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/cache.tpl +0 -0
  107. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/database.tpl +0 -0
  108. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/log.tpl +0 -0
  109. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/rpc.tpl +0 -0
  110. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/scheduler.tpl +0 -0
  111. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/service.tpl +0 -0
  112. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/storage.tpl +0 -0
  113. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/env_templates/third_party.tpl +0 -0
  114. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/gitignore.tpl +0 -0
  115. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/main.py.tpl +0 -0
  116. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/modules/api.py.tpl +0 -0
  117. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/modules/exceptions.py.tpl +0 -0
  118. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/templates/project/modules/tasks.py.tpl +0 -0
  119. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/commands/worker.py +0 -0
  120. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/__init__.py +0 -0
  121. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/exceptions/__init__.py +0 -0
  122. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/i18n/__init__.py +0 -0
  123. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/i18n/translator.py +0 -0
  124. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/logging/__init__.py +0 -0
  125. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/logging/context.py +0 -0
  126. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/logging/decorators.py +0 -0
  127. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/logging/format.py +0 -0
  128. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/common/logging/setup.py +0 -0
  129. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/contrib/__init__.py +0 -0
  130. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/contrib/admin_console/__init__.py +0 -0
  131. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/contrib/admin_console/auth.py +0 -0
  132. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/contrib/admin_console/discovery.py +0 -0
  133. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/contrib/admin_console/install.py +0 -0
  134. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/contrib/admin_console/utils.py +0 -0
  135. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/__init__.py +0 -0
  136. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/exceptions/__init__.py +0 -0
  137. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/models/__init__.py +0 -0
  138. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/models/base.py +0 -0
  139. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/models/mixins.py +0 -0
  140. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/models/models.py +0 -0
  141. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/pagination/__init__.py +0 -0
  142. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/repository/__init__.py +0 -0
  143. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/repository/interceptors.py +0 -0
  144. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/repository/interface.py +0 -0
  145. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/repository/query_builder.py +0 -0
  146. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/service/__init__.py +0 -0
  147. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/domain/service/base.py +0 -0
  148. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/__init__.py +0 -0
  149. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/cache/__init__.py +0 -0
  150. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/cache/backends.py +0 -0
  151. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/cache/base.py +0 -0
  152. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/cache/exceptions.py +0 -0
  153. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/cache/factory.py +0 -0
  154. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/cache/manager.py +0 -0
  155. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/channel/__init__.py +0 -0
  156. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/channel/backends/__init__.py +0 -0
  157. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/clients/__init__.py +0 -0
  158. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/clients/rabbitmq/__init__.py +0 -0
  159. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/clients/rabbitmq/config.py +0 -0
  160. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/clients/rabbitmq/manager.py +0 -0
  161. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/clients/redis/__init__.py +0 -0
  162. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/clients/redis/config.py +0 -0
  163. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/clients/redis/manager.py +0 -0
  164. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/database/__init__.py +0 -0
  165. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/database/config.py +0 -0
  166. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/database/exceptions.py +0 -0
  167. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/database/manager.py +0 -0
  168. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/database/query_tools/__init__.py +0 -0
  169. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/database/strategies/__init__.py +0 -0
  170. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/di/__init__.py +0 -0
  171. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/di/container.py +0 -0
  172. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/__init__.py +0 -0
  173. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/backends/__init__.py +0 -0
  174. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/backends/memory.py +0 -0
  175. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/backends/rabbitmq.py +0 -0
  176. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/base.py +0 -0
  177. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/events/middleware.py +0 -0
  178. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/monitoring/__init__.py +0 -0
  179. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/mq/__init__.py +0 -0
  180. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/mq/backends/__init__.py +0 -0
  181. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/mq/backends/rabbitmq.py +0 -0
  182. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/mq/backends/redis.py +0 -0
  183. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/mq/base.py +0 -0
  184. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/mq/manager.py +0 -0
  185. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/scheduler/__init__.py +0 -0
  186. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/scheduler/exceptions.py +0 -0
  187. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/storage/__init__.py +0 -0
  188. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/storage/base.py +0 -0
  189. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/storage/exceptions.py +0 -0
  190. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/storage/factory.py +0 -0
  191. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/tasks/__init__.py +0 -0
  192. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/tasks/config.py +0 -0
  193. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/tasks/constants.py +0 -0
  194. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/infrastructure/tasks/exceptions.py +0 -0
  195. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/testing/__init__.py +0 -0
  196. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/testing/base.py +0 -0
  197. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/testing/client.py +0 -0
  198. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/testing/factory.py +0 -0
  199. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/toolkit/__init__.py +0 -0
  200. {aury_boot-0.0.19 → aury_boot-0.0.29}/aury/boot/toolkit/http/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aury-boot
3
- Version: 0.0.19
3
+ Version: 0.0.29
4
4
  Summary: Aury Boot - 基于 FastAPI 生态的企业级 API 开发框架
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: alembic>=1.17.2
@@ -24,13 +24,11 @@ Requires-Dist: sqladmin>=0.16.0; extra == 'admin'
24
24
  Provides-Extra: all
25
25
  Requires-Dist: aiomysql>=0.3.2; extra == 'all'
26
26
  Requires-Dist: aiosqlite>=0.21.0; extra == 'all'
27
- Requires-Dist: amqp>=5.3.1; extra == 'all'
28
27
  Requires-Dist: apscheduler>=3.11.1; extra == 'all'
29
28
  Requires-Dist: asyncpg>=0.31.0; extra == 'all'
30
29
  Requires-Dist: aury-sdk-storage[aws]>=0.0.1; extra == 'all'
31
- Requires-Dist: dramatiq-kombu-broker>=0.2.2; extra == 'all'
32
30
  Requires-Dist: dramatiq>=1.18.0; extra == 'all'
33
- Requires-Dist: kombu>=5.6.1; extra == 'all'
31
+ Requires-Dist: pika>=1.3.2; extra == 'all'
34
32
  Requires-Dist: redis>=7.1.0; extra == 'all'
35
33
  Provides-Extra: dev
36
34
  Requires-Dist: mypy>=1.19.0; extra == 'dev'
@@ -53,9 +51,7 @@ Requires-Dist: aiosqlite>=0.21.0; extra == 'recommended'
53
51
  Requires-Dist: apscheduler>=3.11.1; extra == 'recommended'
54
52
  Requires-Dist: asyncpg>=0.31.0; extra == 'recommended'
55
53
  Requires-Dist: aury-sdk-storage[aws]>=0.0.1; extra == 'recommended'
56
- Requires-Dist: dramatiq-kombu-broker>=0.2.2; extra == 'recommended'
57
54
  Requires-Dist: dramatiq>=1.18.0; extra == 'recommended'
58
- Requires-Dist: kombu>=5.6.1; extra == 'recommended'
59
55
  Requires-Dist: redis>=7.1.0; extra == 'recommended'
60
56
  Provides-Extra: redis
61
57
  Requires-Dist: redis>=7.1.0; extra == 'redis'
@@ -66,9 +62,8 @@ Requires-Dist: apscheduler>=3.11.1; extra == 'scheduler'
66
62
  Provides-Extra: sqlite
67
63
  Requires-Dist: aiosqlite>=0.21.0; extra == 'sqlite'
68
64
  Provides-Extra: tasks
69
- Requires-Dist: dramatiq-kombu-broker>=0.2.2; extra == 'tasks'
70
65
  Requires-Dist: dramatiq>=1.18.0; extra == 'tasks'
71
- Requires-Dist: kombu>=5.6.1; extra == 'tasks'
66
+ Requires-Dist: redis>=7.1.0; extra == 'tasks'
72
67
  Description-Content-Type: text/markdown
73
68
 
74
69
  # Aury Boot
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.19'
32
- __version_tuple__ = version_tuple = (0, 0, 19)
31
+ __version__ = version = '0.0.29'
32
+ __version_tuple__ = version_tuple = (0, 0, 29)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -12,7 +12,9 @@ import sys
12
12
  from typing import Any, ClassVar
13
13
 
14
14
  from fastapi import FastAPI, Request, status
15
+ from fastapi.exceptions import RequestValidationError
15
16
  from fastapi.responses import JSONResponse
17
+ from starlette.exceptions import HTTPException
16
18
  from starlette.middleware import Middleware as StarletteMiddleware
17
19
 
18
20
  from aury.boot.application.config import BaseConfig
@@ -279,8 +281,10 @@ class FoundationApp(FastAPI):
279
281
  **kwargs,
280
282
  )
281
283
 
282
- # 异常处理
283
- self.add_exception_handler(Exception, global_exception_handler)
284
+ # 异常处理:显式注册以覆盖 FastAPI/Starlette 默认处理器,确保统一响应格式
285
+ self.add_exception_handler(RequestValidationError, global_exception_handler) # 422 参数校验
286
+ self.add_exception_handler(HTTPException, global_exception_handler) # 4xx/5xx HTTP 异常
287
+ self.add_exception_handler(Exception, global_exception_handler) # 其他未处理异常
284
288
 
285
289
  # 设置路由
286
290
  self.setup_routes()
@@ -15,6 +15,7 @@ from aury.boot.application.constants import ComponentName, ServiceType
15
15
  from aury.boot.application.migrations import MigrationManager
16
16
  from aury.boot.common.logging import logger
17
17
  from aury.boot.infrastructure.cache import CacheManager
18
+ from aury.boot.infrastructure.channel import ChannelManager
18
19
  from aury.boot.infrastructure.database import DatabaseManager
19
20
  from aury.boot.infrastructure.events import EventBusManager
20
21
  from aury.boot.infrastructure.mq import MQManager
@@ -505,6 +506,49 @@ class MessageQueueComponent(Component):
505
506
  logger.warning(f"消息队列 [{name}] 关闭失败: {e}")
506
507
 
507
508
 
509
+ class ChannelComponent(Component):
510
+ """流式通道组件。
511
+
512
+ 用于 SSE(Server-Sent Events)和实时通信场景,支持 memory 和 redis 后端。
513
+ """
514
+
515
+ name = ComponentName.CHANNEL
516
+ enabled = True
517
+ depends_on: ClassVar[list[str]] = []
518
+
519
+ def can_enable(self, config: BaseConfig) -> bool:
520
+ """当配置了 Channel 实例时启用。"""
521
+ return self.enabled and bool(config.get_channels())
522
+
523
+ async def setup(self, app: FoundationApp, config: BaseConfig) -> None:
524
+ """初始化流式通道。"""
525
+ channel_configs = config.get_channels()
526
+ if not channel_configs:
527
+ logger.debug("未配置 Channel 实例,跳过通道初始化")
528
+ return
529
+
530
+ for name, ch_config in channel_configs.items():
531
+ try:
532
+ channel_manager = ChannelManager.get_instance(name)
533
+ if not channel_manager.is_initialized:
534
+ await channel_manager.initialize(
535
+ backend=ch_config.backend,
536
+ url=ch_config.url,
537
+ )
538
+ except Exception as e:
539
+ logger.warning(f"通道 [{name}] 初始化失败(非关键): {e}")
540
+
541
+ async def teardown(self, app: FoundationApp) -> None:
542
+ """关闭所有通道实例。"""
543
+ for name in list(ChannelManager._instances.keys()):
544
+ try:
545
+ channel_manager = ChannelManager.get_instance(name)
546
+ if channel_manager.is_initialized:
547
+ await channel_manager.cleanup()
548
+ except Exception as e:
549
+ logger.warning(f"通道 [{name}] 关闭失败: {e}")
550
+
551
+
508
552
  class EventBusComponent(Component):
509
553
  """事件总线组件。
510
554
 
@@ -555,6 +599,7 @@ FoundationApp.components = [
555
599
  StorageComponent,
556
600
  TaskComponent,
557
601
  MessageQueueComponent,
602
+ ChannelComponent,
558
603
  EventBusComponent,
559
604
  SchedulerComponent,
560
605
  ]
@@ -563,6 +608,7 @@ FoundationApp.components = [
563
608
  __all__ = [
564
609
  "AdminConsoleComponent",
565
610
  "CacheComponent",
611
+ "ChannelComponent",
566
612
  "DatabaseComponent",
567
613
  "EventBusComponent",
568
614
  "MessageQueueComponent",
@@ -246,6 +246,27 @@ class CacheSettings(BaseModel):
246
246
  )
247
247
 
248
248
 
249
+ class ChannelSettings(BaseModel):
250
+ """流式通道配置(单实例)。
251
+
252
+ 环境变量格式: CHANNEL__{FIELD}
253
+ 示例: CHANNEL__BACKEND, CHANNEL__URL
254
+
255
+ 支持的后端类型:
256
+ - memory: 内存后端(默认,单进程)
257
+ - redis: Redis Pub/Sub(多进程/分布式)
258
+ """
259
+
260
+ backend: str = Field(
261
+ default="",
262
+ description="通道后端 (memory/redis),空字符串表示不启用"
263
+ )
264
+ url: str | None = Field(
265
+ default=None,
266
+ description="Redis URL(当 backend=redis 时需要)"
267
+ )
268
+
269
+
249
270
  class StorageSettings(BaseModel):
250
271
  """对象存储组件接入配置(Application 层)。
251
272
 
@@ -507,10 +528,9 @@ class MessageQueueSettings(BaseModel):
507
528
  - Task: 基于 Dramatiq,用于异步任务处理(API + Worker 模式)
508
529
  - MQ: 通用消息队列,用于服务间通信、事件驱动架构
509
530
 
510
- 支持的后端(通过 Kombu):
531
+ 支持的后端:
511
532
  - Redis: redis://localhost:6379/0
512
533
  - RabbitMQ: amqp://guest:guest@localhost:5672//
513
- - Amazon SQS: sqs://
514
534
  """
515
535
 
516
536
  enabled: bool = Field(
@@ -780,6 +800,7 @@ class BaseConfig(BaseSettings):
780
800
  # ========== 数据与缓存 ==========
781
801
  database: DatabaseSettings = Field(default_factory=DatabaseSettings)
782
802
  cache: CacheSettings = Field(default_factory=CacheSettings)
803
+ channel: ChannelSettings = Field(default_factory=ChannelSettings)
783
804
  storage: StorageSettings = Field(default_factory=StorageSettings)
784
805
 
785
806
  # 迁移配置
@@ -880,10 +901,18 @@ class BaseConfig(BaseSettings):
880
901
  """获取所有通道实例配置。
881
902
 
882
903
  从环境变量解析 CHANNEL__{INSTANCE}__{FIELD} 格式的配置。
904
+ 如果没有配置多实例,返回从单实例配置转换的 default 实例。
883
905
  """
884
906
  if self._channels is None:
885
907
  loader = MultiInstanceConfigLoader("CHANNEL", ChannelInstanceConfig)
886
908
  self._channels = loader.load()
909
+ if not self._channels and self.channel.backend:
910
+ self._channels = {
911
+ "default": ChannelInstanceConfig(
912
+ backend=self.channel.backend,
913
+ url=self.channel.url,
914
+ )
915
+ }
887
916
  return self._channels
888
917
 
889
918
  def get_mqs(self) -> dict[str, MQInstanceConfig]:
@@ -930,6 +959,7 @@ __all__ = [
930
959
  "CacheInstanceConfig",
931
960
  "CacheSettings",
932
961
  "ChannelInstanceConfig",
962
+ "ChannelSettings",
933
963
  "DatabaseInstanceConfig",
934
964
  "DatabaseSettings",
935
965
  "EventInstanceConfig",
@@ -41,6 +41,9 @@ class ComponentName(str, Enum):
41
41
  # 事件总线组件
42
42
  EVENT_BUS = "event_bus"
43
43
 
44
+ # 流式通道组件
45
+ CHANNEL = "channel"
46
+
44
47
  # 迁移组件
45
48
  MIGRATIONS = "migrations"
46
49
 
@@ -8,11 +8,12 @@ from __future__ import annotations
8
8
  from abc import ABC, abstractmethod
9
9
  from typing import TYPE_CHECKING
10
10
 
11
- from fastapi import HTTPException, Request, status
11
+ from fastapi import Request, status
12
12
  from fastapi.exceptions import RequestValidationError
13
13
  from fastapi.responses import JSONResponse
14
14
  from pydantic import ValidationError
15
15
  from sqlalchemy.exc import IntegrityError, SQLAlchemyError
16
+ from starlette.exceptions import HTTPException
16
17
 
17
18
  from aury.boot.common.exceptions import FoundationError
18
19
  from aury.boot.common.logging import logger
@@ -149,23 +150,31 @@ class BaseErrorHandler(ErrorHandler):
149
150
 
150
151
 
151
152
  class HTTPExceptionHandler(ErrorHandler):
152
- """FastAPI HTTP异常处理器。"""
153
+ """HTTP 异常处理器。
154
+
155
+ 处理 FastAPI 和 Starlette 的 HTTPException(包括 404、401、403 等)。
156
+ """
153
157
 
154
158
  def can_handle(self, exception: Exception) -> bool:
155
- """判断是否为HTTP异常。"""
159
+ """判断是否为 HTTP 异常。"""
160
+ # Starlette HTTPException 是 FastAPI HTTPException 的基类
156
161
  return isinstance(exception, HTTPException)
157
162
 
158
163
  async def handle(self, exception: HTTPException, request: Request) -> JSONResponse:
159
- """处理HTTP异常。"""
160
- logger.warning(f"HTTP异常: {exception.status_code} - {exception.detail}")
164
+ """处理 HTTP 异常。"""
165
+ # 获取错误信息:Starlette detail,FastAPI 也用 detail
166
+ detail = getattr(exception, "detail", str(exception))
167
+ status_code = exception.status_code
168
+
169
+ logger.warning(f"HTTP 异常 [{request.method} {request.url.path}]: {status_code} - {detail}")
161
170
 
162
171
  response = ResponseBuilder.fail(
163
- message=exception.detail,
164
- code=exception.status_code,
172
+ message=detail if isinstance(detail, str) else str(detail),
173
+ code=status_code,
165
174
  )
166
175
 
167
176
  return JSONResponse(
168
- status_code=exception.status_code,
177
+ status_code=status_code,
169
178
  content=response.model_dump(mode="json"),
170
179
  )
171
180
 
@@ -174,6 +183,7 @@ class ValidationErrorHandler(ErrorHandler):
174
183
  """验证异常处理器。
175
184
 
176
185
  处理 Pydantic ValidationError 和 FastAPI RequestValidationError。
186
+ 返回 422 Unprocessable Entity(符合 FastAPI 规范)。
177
187
  """
178
188
 
179
189
  def can_handle(self, exception: Exception) -> bool:
@@ -182,24 +192,38 @@ class ValidationErrorHandler(ErrorHandler):
182
192
 
183
193
  async def handle(self, exception: Exception, request: Request) -> JSONResponse:
184
194
  """处理验证异常。"""
185
- logger.warning(f"数据验证失败: {exception}")
186
-
187
195
  errors = []
188
196
  for error in exception.errors():
197
+ # 构建友好的字段路径:跳过 body 前缀
198
+ loc = error.get("loc", ())
199
+ # FastAPI 会在 loc 前加 'body'/'query'/'path' 等,保留第一个作为来源
200
+ source = str(loc[0]) if loc else ""
201
+ field_path = ".".join(str(part) for part in loc[1:]) if len(loc) > 1 else str(loc[0]) if loc else ""
202
+
189
203
  errors.append({
190
- "field": ".".join(str(loc) for loc in error["loc"]),
191
- "message": error["msg"],
192
- "type": error["type"],
204
+ "field": field_path,
205
+ "source": source, # body / query / path / header
206
+ "message": error.get("msg", ""),
207
+ "type": error.get("type", ""),
208
+ "input": error.get("input"), # 实际输入值(便于调试)
193
209
  })
194
210
 
211
+ # 详细日志:方便开发调试
212
+ error_summary = "; ".join(
213
+ f"{e['source']}.{e['field']}({e['type']}): {e['message']}" for e in errors
214
+ )
215
+ logger.warning(
216
+ f"参数校验失败 [{request.method} {request.url.path}]: {error_summary}"
217
+ )
218
+
195
219
  response = ResponseBuilder.fail(
196
- message="数据验证失败",
197
- code=400,
220
+ message="参数校验失败",
221
+ code=422,
198
222
  errors=errors,
199
223
  )
200
224
 
201
225
  return JSONResponse(
202
- status_code=status.HTTP_400_BAD_REQUEST,
226
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
203
227
  content=response.model_dump(mode="json"),
204
228
  )
205
229
 
@@ -97,16 +97,16 @@ MODULES: dict[str, ModuleInfo] = {
97
97
  "tasks": ModuleInfo(
98
98
  name="tasks",
99
99
  desc="Dramatiq 任务队列",
100
- usage="TaskManager 异步任务时需要",
100
+ usage="TaskManager 异步任务时需要(默认使用 Redis Broker)",
101
101
  category=Category.TASK,
102
- deps=["dramatiq", "kombu", "dramatiq-kombu-broker"],
102
+ deps=["dramatiq", "redis"],
103
103
  ),
104
104
  "rabbitmq": ModuleInfo(
105
105
  name="rabbitmq",
106
106
  desc="RabbitMQ 消息队列后端",
107
107
  usage="TaskManager/EventBus 使用 RabbitMQ 时需要(需配合 tasks)",
108
108
  category=Category.TASK,
109
- deps=["amqp"],
109
+ deps=["pika"],
110
110
  ),
111
111
  # 调度器
112
112
  "scheduler": ModuleInfo(
@@ -75,6 +75,8 @@ mypy {package_name}/
75
75
  2. **[aury_docs/02-repository.md](./aury_docs/02-repository.md)** - Repository 使用
76
76
  - BaseRepository API
77
77
  - Filters 语法(__gt, __like 等)
78
+ - Cursor 分页(推荐,性能更优)
79
+ - 流式查询(大数据处理)
78
80
  - 自动提交机制
79
81
 
80
82
  3. **[aury_docs/03-service.md](./aury_docs/03-service.md)** - Service 编写与事务
@@ -162,15 +164,25 @@ class User(Base): ...
162
164
  - 写操作**必须**使用 `@transactional` 装饰器
163
165
  - 只读操作可以不加事务装饰器
164
166
  - 跨 Service 调用通过共享 session 实现事务共享
167
+ - **后台任务必须**使用 `@isolated_task` 装饰器
165
168
 
166
169
  ```python
167
- from aury.boot.domain.transaction import transactional
170
+ from aury.boot.domain.transaction import transactional, isolated_task
168
171
 
169
172
  class UserService(BaseService):
170
173
  @transactional
171
174
  async def create(self, data: UserCreate) -> User:
172
175
  # 自动事务管理
173
176
  return await self.repo.create(data.model_dump())
177
+
178
+
179
+ # 后台任务必须加 @isolated_task,否则事务不会提交
180
+ @isolated_task
181
+ async def background_upload(space_id: int, url: str):
182
+ async with db.session() as session:
183
+ async with transactional_context(session):
184
+ repo = SpaceRepository(session, Space)
185
+ await repo.update(...)
174
186
  ```
175
187
 
176
188
  ### Manager API 规范
@@ -72,6 +72,37 @@ result = await repo.paginate(
72
72
  # - result.has_next: bool # 是否有下一页
73
73
  # - result.has_prev: bool # 是否有上一页
74
74
 
75
+ # === Cursor 分页(推荐,性能更优) ===
76
+ from aury.boot.domain.pagination import CursorPaginationParams
77
+
78
+ # 第一页
79
+ result = await repo.cursor_paginate(
80
+ CursorPaginationParams(limit=20),
81
+ is_active=True,
82
+ )
83
+
84
+ # 下一页(带上 cursor)
85
+ result = await repo.cursor_paginate(
86
+ CursorPaginationParams(cursor=result.next_cursor, limit=20),
87
+ is_active=True,
88
+ )
89
+
90
+ # CursorPaginationResult 结构:
91
+ # - result.items: list[T] # 数据列表
92
+ # - result.next_cursor: str | None # 下一页游标
93
+ # - result.prev_cursor: str | None # 上一页游标
94
+ # - result.has_next: bool # 是否有下一页
95
+ # - result.has_prev: bool # 是否有上一页
96
+
97
+ # === 流式查询(大数据处理) ===
98
+ # 逐条流式处理,不会一次性加载到内存
99
+ async for user in repo.stream(batch_size=1000, is_active=True):
100
+ await process(user)
101
+
102
+ # 批量流式处理
103
+ async for batch in repo.stream_batches(batch_size=1000):
104
+ await bulk_sync_to_es(batch)
105
+
75
106
  # === 创建 ===
76
107
  user = await repo.create({{"name": "Alice", "email": "a@b.com"}})
77
108
  users = await repo.batch_create([{{"name": "A"}}, {{"name": "B"}}]) # 返回实体
@@ -396,3 +396,63 @@ DATABASE_ISOLATION_LEVEL=REPEATABLE READ
396
396
  - 大多数场景:`READ COMMITTED`(平衡性能和一致性)
397
397
  - 报表/统计查询:`REPEATABLE READ`(保证读取一致性)
398
398
  - 金融交易:`SERIALIZABLE`(最强一致性,性能较低)
399
+
400
+ ### 3.12 后台任务事务隔离(重要)
401
+
402
+ 在 `@transactional` 装饰的 Service 方法中 spawn 后台任务时,**必须**使用 `@isolated_task` 或 `isolated_context`,否则事务不会提交。
403
+
404
+ **问题背景**:
405
+ `asyncio.create_task()` 会继承父协程的 `contextvars`。如果父协程在 `@transactional` 中,子任务会继承事务深度标记,导致:
406
+ - `auto_commit` 失效
407
+ - `transactional_context` 也不会提交
408
+ - session 关闭时数据被 rollback
409
+
410
+ **解决方案 1:装饰器(推荐)**
411
+
412
+ ```python
413
+ import asyncio
414
+ from aury.boot.domain.transaction import isolated_task, transactional_context
415
+ from aury.boot.infrastructure.database import DatabaseManager
416
+
417
+ db = DatabaseManager.get_instance()
418
+
419
+
420
+ @isolated_task
421
+ async def upload_cover(space_id: int, cover_url: str):
422
+ """后台任务:上传封面。"""
423
+ async with db.session() as session:
424
+ async with transactional_context(session):
425
+ repo = SpaceRepository(session, Space)
426
+ space = await repo.get(space_id)
427
+ if space:
428
+ await repo.update(space, {{"cover": cover_url}})
429
+ # 现在会正常 commit
430
+
431
+
432
+ class SpaceService(BaseService):
433
+ @transactional
434
+ async def create(self, data: SpaceCreate) -> Space:
435
+ space = await self.repo.create(data.model_dump())
436
+
437
+ # spawn 后台任务
438
+ asyncio.create_task(upload_cover(space.id, data.cover_url))
439
+
440
+ return space
441
+ ```
442
+
443
+ **解决方案 2:上下文管理器**
444
+
445
+ ```python
446
+ from aury.boot.domain.transaction import isolated_context
447
+
448
+ async def background_job():
449
+ async with isolated_context():
450
+ async with db.session() as session:
451
+ async with transactional_context(session):
452
+ # 正常的事务处理
453
+ ...
454
+ ```
455
+
456
+ **注意事项**:
457
+ - 后台任务必须新开 session(`db.session()`),不能复用主请求的 `self.session`
458
+ - 后台任务的事务与主请求独立,主请求回滚不影响后台任务