springbootAI 2.1.0__tar.gz → 2.2.0__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 (290) hide show
  1. {springbootai-2.1.0 → springbootai-2.2.0}/CHANGELOG.md +30 -0
  2. {springbootai-2.1.0/springbootAI.egg-info → springbootai-2.2.0}/PKG-INFO +33 -15
  3. {springbootai-2.1.0 → springbootai-2.2.0}/README.md +26 -8
  4. {springbootai-2.1.0 → springbootai-2.2.0}/pyproject.toml +7 -7
  5. {springbootai-2.1.0 → springbootai-2.2.0}/requirements-ai.txt +3 -3
  6. {springbootai-2.1.0 → springbootai-2.2.0}/spring/__init__.py +1 -1
  7. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/__init__.py +1 -1
  8. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/__init__.py +18 -0
  9. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/core.py +110 -0
  10. springbootai-2.2.0/spring/aop/__init__.py +10 -0
  11. springbootai-2.2.0/spring/aop/aspect.py +510 -0
  12. {springbootai-2.1.0 → springbootai-2.2.0}/spring/aop/comprehensive_aop.py +24 -22
  13. {springbootai-2.1.0 → springbootai-2.2.0}/spring/context/bean_factory.py +14 -0
  14. {springbootai-2.1.0 → springbootai-2.2.0}/spring/csv/__init__.py +1 -1
  15. {springbootai-2.1.0 → springbootai-2.2.0}/spring/data/__init__.py +1 -1
  16. {springbootai-2.1.0 → springbootai-2.2.0}/spring/datasource/__init__.py +1 -1
  17. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/__init__.py +1 -1
  18. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/__init__.py +1 -1
  19. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/__init__.py +1 -1
  20. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/__init__.py +11 -0
  21. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/ddl_auto.py +220 -2
  22. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/__init__.py +1 -1
  23. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/version.py +1 -1
  24. {springbootai-2.1.0 → springbootai-2.2.0}/spring/retry/__init__.py +3 -1
  25. springbootai-2.2.0/spring/retry/recovery.py +149 -0
  26. {springbootai-2.1.0 → springbootai-2.2.0}/spring/retry/retry_decorator.py +12 -18
  27. {springbootai-2.1.0 → springbootai-2.2.0}/spring/security/__init__.py +3 -1
  28. springbootai-2.2.0/spring/security/security_aop.py +375 -0
  29. {springbootai-2.1.0 → springbootai-2.2.0}/spring/test/__init__.py +1 -1
  30. {springbootai-2.1.0 → springbootai-2.2.0}/spring/tx/__init__.py +1 -1
  31. {springbootai-2.1.0 → springbootai-2.2.0}/spring/utils/banner.py +1 -1
  32. {springbootai-2.1.0 → springbootai-2.2.0}/spring/validation/__init__.py +1 -1
  33. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/swagger.py +2 -2
  34. {springbootai-2.1.0 → springbootai-2.2.0}/spring/websocket/__init__.py +1 -1
  35. {springbootai-2.1.0 → springbootai-2.2.0/springbootAI.egg-info}/PKG-INFO +33 -15
  36. {springbootai-2.1.0 → springbootai-2.2.0}/springbootAI.egg-info/SOURCES.txt +4 -0
  37. {springbootai-2.1.0 → springbootai-2.2.0}/springbootAI.egg-info/requires.txt +6 -6
  38. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_annotations_contract.py +13 -0
  39. springbootai-2.2.0/tests/test_audit_time.py +242 -0
  40. springbootai-2.2.0/tests/test_declarative_aop_post_authorize_recover.py +535 -0
  41. springbootai-2.1.0/spring/aop/__init__.py +0 -4
  42. springbootai-2.1.0/spring/security/security_aop.py +0 -248
  43. {springbootai-2.1.0 → springbootai-2.2.0}/LICENSE +0 -0
  44. {springbootai-2.1.0 → springbootai-2.2.0}/MANIFEST.in +0 -0
  45. {springbootai-2.1.0 → springbootai-2.2.0}/SECURITY.md +0 -0
  46. {springbootai-2.1.0 → springbootai-2.2.0}/requirements-langgraph.txt +0 -0
  47. {springbootai-2.1.0 → springbootai-2.2.0}/requirements-mcp.txt +0 -0
  48. {springbootai-2.1.0 → springbootai-2.2.0}/requirements.txt +0 -0
  49. {springbootai-2.1.0 → springbootai-2.2.0}/setup.cfg +0 -0
  50. {springbootai-2.1.0 → springbootai-2.2.0}/setup.py +0 -0
  51. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/advisors.py +0 -0
  52. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/annotations.py +0 -0
  53. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/autoconfig.py +0 -0
  54. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/core.py +0 -0
  55. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/etl.py +0 -0
  56. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/memory.py +0 -0
  57. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/observability.py +0 -0
  58. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/providers.py +0 -0
  59. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/resilience.py +0 -0
  60. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/tools.py +0 -0
  61. {springbootai-2.1.0 → springbootai-2.2.0}/spring/ai/vectorstore.py +0 -0
  62. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/cache.py +0 -0
  63. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/cloud.py +0 -0
  64. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/conditional.py +0 -0
  65. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/langchain.py +0 -0
  66. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/langgraph.py +0 -0
  67. {springbootai-2.1.0 → springbootai-2.2.0}/spring/annotations/messaging.py +0 -0
  68. {springbootai-2.1.0 → springbootai-2.2.0}/spring/aop/cloud_aop.py +0 -0
  69. {springbootai-2.1.0 → springbootai-2.2.0}/spring/aop/method_interceptor.py +0 -0
  70. {springbootai-2.1.0 → springbootai-2.2.0}/spring/aop/proxy_factory.py +0 -0
  71. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/__init__.py +0 -0
  72. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/discovery.py +0 -0
  73. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/feign.py +0 -0
  74. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/gateway.py +0 -0
  75. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/load_balancer.py +0 -0
  76. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/seata.py +0 -0
  77. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/seata_bridge.py +0 -0
  78. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/sentinel.py +0 -0
  79. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/tracer.py +0 -0
  80. {springbootai-2.1.0 → springbootai-2.2.0}/spring/cloud/transaction_store.py +0 -0
  81. {springbootai-2.1.0 → springbootai-2.2.0}/spring/config/__init__.py +0 -0
  82. {springbootai-2.1.0 → springbootai-2.2.0}/spring/config/binding.py +0 -0
  83. {springbootai-2.1.0 → springbootai-2.2.0}/spring/config/config_loader.py +0 -0
  84. {springbootai-2.1.0 → springbootai-2.2.0}/spring/context/__init__.py +0 -0
  85. {springbootai-2.1.0 → springbootai-2.2.0}/spring/context/application_context.py +0 -0
  86. {springbootai-2.1.0 → springbootai-2.2.0}/spring/context/bean_definition.py +0 -0
  87. {springbootai-2.1.0 → springbootai-2.2.0}/spring/context/registry.py +0 -0
  88. {springbootai-2.1.0 → springbootai-2.2.0}/spring/context/scanner.py +0 -0
  89. {springbootai-2.1.0 → springbootai-2.2.0}/spring/core/__init__.py +0 -0
  90. {springbootai-2.1.0 → springbootai-2.2.0}/spring/core/graceful_shutdown.py +0 -0
  91. {springbootai-2.1.0 → springbootai-2.2.0}/spring/core/typing_utils.py +0 -0
  92. {springbootai-2.1.0 → springbootai-2.2.0}/spring/csv/annotations.py +0 -0
  93. {springbootai-2.1.0 → springbootai-2.2.0}/spring/csv/converters.py +0 -0
  94. {springbootai-2.1.0 → springbootai-2.2.0}/spring/csv/easy_csv.py +0 -0
  95. {springbootai-2.1.0 → springbootai-2.2.0}/spring/csv/exceptions.py +0 -0
  96. {springbootai-2.1.0 → springbootai-2.2.0}/spring/csv/reader.py +0 -0
  97. {springbootai-2.1.0 → springbootai-2.2.0}/spring/csv/writer.py +0 -0
  98. {springbootai-2.1.0 → springbootai-2.2.0}/spring/data/page.py +0 -0
  99. {springbootai-2.1.0 → springbootai-2.2.0}/spring/data/repository.py +0 -0
  100. {springbootai-2.1.0 → springbootai-2.2.0}/spring/data/specification.py +0 -0
  101. {springbootai-2.1.0 → springbootai-2.2.0}/spring/datasource/annotations.py +0 -0
  102. {springbootai-2.1.0 → springbootai-2.2.0}/spring/datasource/context.py +0 -0
  103. {springbootai-2.1.0 → springbootai-2.2.0}/spring/datasource/dynamic.py +0 -0
  104. {springbootai-2.1.0 → springbootai-2.2.0}/spring/event/__init__.py +0 -0
  105. {springbootai-2.1.0 → springbootai-2.2.0}/spring/event/publisher.py +0 -0
  106. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/annotations.py +0 -0
  107. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/converters.py +0 -0
  108. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/easy_excel.py +0 -0
  109. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/exceptions.py +0 -0
  110. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/reader.py +0 -0
  111. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/style.py +0 -0
  112. {springbootai-2.1.0 → springbootai-2.2.0}/spring/excel/writer.py +0 -0
  113. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/accessor.py +0 -0
  114. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/auto_config.py +0 -0
  115. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/holder.py +0 -0
  116. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/locale.py +0 -0
  117. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/locale_resolver.py +0 -0
  118. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/message_source.py +0 -0
  119. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/middleware.py +0 -0
  120. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/properties.py +0 -0
  121. {springbootai-2.1.0 → springbootai-2.2.0}/spring/i18n/sources.py +0 -0
  122. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/adapters.py +0 -0
  123. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/agents/__init__.py +0 -0
  124. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/agents/services.py +0 -0
  125. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/annotations.py +0 -0
  126. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/autoconfig.py +0 -0
  127. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/callbacks/__init__.py +0 -0
  128. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/callbacks/handlers.py +0 -0
  129. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/chains/__init__.py +0 -0
  130. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/chains/services.py +0 -0
  131. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/core.py +0 -0
  132. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/indexes/__init__.py +0 -0
  133. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/indexes/index.py +0 -0
  134. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/loaders/__init__.py +0 -0
  135. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/loaders/loaders.py +0 -0
  136. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/memory/__init__.py +0 -0
  137. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/memory/memory.py +0 -0
  138. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/parsers/__init__.py +0 -0
  139. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/parsers/parsers.py +0 -0
  140. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/partners.py +0 -0
  141. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/prompts/__init__.py +0 -0
  142. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/prompts/templates.py +0 -0
  143. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/retrievers/__init__.py +0 -0
  144. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/retrievers/retrievers.py +0 -0
  145. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/tools/__init__.py +0 -0
  146. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/tools/tools.py +0 -0
  147. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/utilities/__init__.py +0 -0
  148. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/utilities/utils.py +0 -0
  149. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/vectorstores/__init__.py +0 -0
  150. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langchain/vectorstores/stores.py +0 -0
  151. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langgraph/__init__.py +0 -0
  152. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langgraph/annotation_runtime.py +0 -0
  153. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langgraph/annotations.py +0 -0
  154. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langgraph/autoconfig.py +0 -0
  155. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langgraph/checkpoint.py +0 -0
  156. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langgraph/config.py +0 -0
  157. {springbootai-2.1.0 → springbootai-2.2.0}/spring/langgraph/runtime.py +0 -0
  158. {springbootai-2.1.0 → springbootai-2.2.0}/spring/logging/__init__.py +0 -0
  159. {springbootai-2.1.0 → springbootai-2.2.0}/spring/logging/loguru_logger.py +0 -0
  160. {springbootai-2.1.0 → springbootai-2.2.0}/spring/main.py +0 -0
  161. {springbootai-2.1.0 → springbootai-2.2.0}/spring/mcp/__init__.py +0 -0
  162. {springbootai-2.1.0 → springbootai-2.2.0}/spring/mcp/annotation_runtime.py +0 -0
  163. {springbootai-2.1.0 → springbootai-2.2.0}/spring/mcp/annotations.py +0 -0
  164. {springbootai-2.1.0 → springbootai-2.2.0}/spring/mcp/autoconfig.py +0 -0
  165. {springbootai-2.1.0 → springbootai-2.2.0}/spring/mcp/client.py +0 -0
  166. {springbootai-2.1.0 → springbootai-2.2.0}/spring/mcp/config.py +0 -0
  167. {springbootai-2.1.0 → springbootai-2.2.0}/spring/mcp/server.py +0 -0
  168. {springbootai-2.1.0 → springbootai-2.2.0}/spring/messaging/__init__.py +0 -0
  169. {springbootai-2.1.0 → springbootai-2.2.0}/spring/messaging/rabbitmq.py +0 -0
  170. {springbootai-2.1.0 → springbootai-2.2.0}/spring/monitoring/__init__.py +0 -0
  171. {springbootai-2.1.0 → springbootai-2.2.0}/spring/monitoring/prometheus.py +0 -0
  172. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/database.py +0 -0
  173. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/migration.py +0 -0
  174. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/mybatis_integration.py +0 -0
  175. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/annotations/__init__.py +0 -0
  176. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/annotations/annotations.py +0 -0
  177. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/cache/__init__.py +0 -0
  178. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/cache/cache.py +0 -0
  179. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/cache/redis_cache.py +0 -0
  180. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/circuit_breaker/__init__.py +0 -0
  181. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +0 -0
  182. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/configuration.py +0 -0
  183. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/core/__init__.py +0 -0
  184. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/core/sql_session.py +0 -0
  185. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/core/sql_session_factory.py +0 -0
  186. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/dialect/__init__.py +0 -0
  187. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/dialect/dialect.py +0 -0
  188. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/dynamic_sql/__init__.py +0 -0
  189. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +0 -0
  190. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/interceptor/__init__.py +0 -0
  191. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/interceptor/interceptor.py +0 -0
  192. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/mapper/__init__.py +0 -0
  193. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/mapper/mapper.py +0 -0
  194. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/metrics/__init__.py +0 -0
  195. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/metrics/metrics.py +0 -0
  196. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/pool/__init__.py +0 -0
  197. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/pool/connection_pool.py +0 -0
  198. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/security/__init__.py +0 -0
  199. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/security/access_control.py +0 -0
  200. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/security/password_encoder.py +0 -0
  201. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/security/sensitive_data_masker.py +0 -0
  202. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/security/sql_injection_detector.py +0 -0
  203. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/transaction/__init__.py +0 -0
  204. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/transaction/transaction.py +0 -0
  205. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/type_handler/__init__.py +0 -0
  206. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/type_handler/type_handler.py +0 -0
  207. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/xml_parser/__init__.py +0 -0
  208. {springbootai-2.1.0 → springbootai-2.2.0}/spring/orm/pymybatis/xml_parser/xml_parser.py +0 -0
  209. {springbootai-2.1.0 → springbootai-2.2.0}/spring/retry/retry_annotations.py +0 -0
  210. {springbootai-2.1.0 → springbootai-2.2.0}/spring/scheduling/__init__.py +0 -0
  211. {springbootai-2.1.0 → springbootai-2.2.0}/spring/scheduling/scheduler.py +0 -0
  212. {springbootai-2.1.0 → springbootai-2.2.0}/spring/security/jwt_utils.py +0 -0
  213. {springbootai-2.1.0 → springbootai-2.2.0}/spring/security/replay_protection.py +0 -0
  214. {springbootai-2.1.0 → springbootai-2.2.0}/spring/security/secret_manager.py +0 -0
  215. {springbootai-2.1.0 → springbootai-2.2.0}/spring/security/security_context.py +0 -0
  216. {springbootai-2.1.0 → springbootai-2.2.0}/spring/test/slicing.py +0 -0
  217. {springbootai-2.1.0 → springbootai-2.2.0}/spring/tracing/__init__.py +0 -0
  218. {springbootai-2.1.0 → springbootai-2.2.0}/spring/tracing/skywalking.py +0 -0
  219. {springbootai-2.1.0 → springbootai-2.2.0}/spring/tx/events.py +0 -0
  220. {springbootai-2.1.0 → springbootai-2.2.0}/spring/tx/synchronization.py +0 -0
  221. {springbootai-2.1.0 → springbootai-2.2.0}/spring/utils/__init__.py +0 -0
  222. {springbootai-2.1.0 → springbootai-2.2.0}/spring/utils/bean_utils.py +0 -0
  223. {springbootai-2.1.0 → springbootai-2.2.0}/spring/utils/logger.py +0 -0
  224. {springbootai-2.1.0 → springbootai-2.2.0}/spring/utils/redis_client.py +0 -0
  225. {springbootai-2.1.0 → springbootai-2.2.0}/spring/validation/aop.py +0 -0
  226. {springbootai-2.1.0 → springbootai-2.2.0}/spring/validation/constraints.py +0 -0
  227. {springbootai-2.1.0 → springbootai-2.2.0}/spring/validation/exceptions.py +0 -0
  228. {springbootai-2.1.0 → springbootai-2.2.0}/spring/validation/validator.py +0 -0
  229. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/__init__.py +0 -0
  230. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/actuator.py +0 -0
  231. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/exception_handler.py +0 -0
  232. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/health.py +0 -0
  233. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/interceptor.py +0 -0
  234. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/result.py +0 -0
  235. {springbootai-2.1.0 → springbootai-2.2.0}/spring/web/web_context.py +0 -0
  236. {springbootai-2.1.0 → springbootai-2.2.0}/spring/websocket/annotations.py +0 -0
  237. {springbootai-2.1.0 → springbootai-2.2.0}/spring/websocket/broker.py +0 -0
  238. {springbootai-2.1.0 → springbootai-2.2.0}/spring/websocket/exceptions.py +0 -0
  239. {springbootai-2.1.0 → springbootai-2.2.0}/spring/websocket/handler.py +0 -0
  240. {springbootai-2.1.0 → springbootai-2.2.0}/spring/websocket/router.py +0 -0
  241. {springbootai-2.1.0 → springbootai-2.2.0}/spring/websocket/session.py +0 -0
  242. {springbootai-2.1.0 → springbootai-2.2.0}/springbootAI.egg-info/dependency_links.txt +0 -0
  243. {springbootai-2.1.0 → springbootai-2.2.0}/springbootAI.egg-info/entry_points.txt +0 -0
  244. {springbootai-2.1.0 → springbootai-2.2.0}/springbootAI.egg-info/top_level.txt +0 -0
  245. {springbootai-2.1.0 → springbootai-2.2.0}/sql/seata_undo_log.sql +0 -0
  246. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_actuator.py +0 -0
  247. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_ai_comprehensive.py +0 -0
  248. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_ai_declarative_annotations.py +0 -0
  249. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_ai_module.py +0 -0
  250. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_annotation_combinations.py +0 -0
  251. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_aop_annotations_full.py +0 -0
  252. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_bean_utils.py +0 -0
  253. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_cache_annotations.py +0 -0
  254. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_cloud_embedded_full.py +0 -0
  255. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_component_init_error.py +0 -0
  256. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_conditional_annotations.py +0 -0
  257. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_config_binding.py +0 -0
  258. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_config_fixes_185.py +0 -0
  259. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_connection_resilience.py +0 -0
  260. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_core_annotations_full.py +0 -0
  261. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_csv_module.py +0 -0
  262. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_data_repository.py +0 -0
  263. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_datasource_routing.py +0 -0
  264. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_ddl_auto.py +0 -0
  265. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_di_config_event_full.py +0 -0
  266. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_excel_module.py +0 -0
  267. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_i18n_module.py +0 -0
  268. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_jpa_version_transient.py +0 -0
  269. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_langchain_comprehensive.py +0 -0
  270. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_langchain_ext.py +0 -0
  271. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_langchain_module.py +0 -0
  272. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_langgraph_comprehensive.py +0 -0
  273. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_langgraph_module.py +0 -0
  274. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_logging_config.py +0 -0
  275. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_mcp_module.py +0 -0
  276. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_new_features.py +0 -0
  277. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_orm_pymybatis_full.py +0 -0
  278. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_production_readiness.py +0 -0
  279. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_pymybatis_contract.py +0 -0
  280. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_seata_bridge.py +0 -0
  281. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_seata_durable_store.py +0 -0
  282. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_security.py +0 -0
  283. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_security_full.py +0 -0
  284. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_security_hardening.py +0 -0
  285. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_swagger_module.py +0 -0
  286. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_test_slicing.py +0 -0
  287. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_transactional_events.py +0 -0
  288. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_validation_module.py +0 -0
  289. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_web_annotations_full.py +0 -0
  290. {springbootai-2.1.0 → springbootai-2.2.0}/tests/test_websocket_module.py +0 -0
@@ -2,6 +2,34 @@
2
2
 
3
3
  本项目从 `2.1.0` 开始按 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 记录面向使用者的变化,并使用语义化版本号。
4
4
 
5
+ ## [2.2.0] - 2026-08-12
6
+
7
+ ### 新增
8
+
9
+ - 新增声明式 AOP:`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@Around`、`@AfterReturning`、`@AfterThrowing`。支持受管 Bean 自动代理、同步/异步通知、`JoinPoint` / `ProceedingJoinPoint`,以及 `execution`、`within`、`bean`、`@annotation` 和切点引用。
10
+ - 新增 `@PostAuthorize`,方法成功返回后可使用 `returnObject` / `#returnObject`、当前认证信息、角色和权限进行授权。
11
+ - 新增 `@Recover`,在 `@Retryable` 重试耗尽后按异常类型选择最具体的恢复方法;兼容原有 `recover="method"` 写法和独立重试装饰器入口。
12
+ - 新增面向新手的 [`AOP_SECURITY_RETRY.md`](doc/AOP_SECURITY_RETRY.md),包含完整示例、执行顺序、表达式速查、安全边界和常见错误。
13
+
14
+ ### 安全与兼容性
15
+
16
+ - 安全表达式改为白名单 AST 求值,不执行任意 Python 代码;不支持的语法按授权失败处理。
17
+ - 声明式切面作为受管 Bean 的最外层扩展,可观察内置 AOP、重试和安全包装后的最终结果;手工实例化对象不会自动代理。
18
+ - 旧式命名恢复方法继续保持原签名;显式命名的方法同时标记 `@Recover` 时使用包含最终异常的新签名。
19
+
20
+ ### 测试
21
+
22
+ - 新增 26 个声明式 AOP、后置鉴权和重试恢复专项用例,并将 9 个新公共注解加入 172 项注解契约检查。
23
+ - Conda Python 3.10.20 完整回归:2372 passed、4 skipped、172 subtests passed,覆盖率 68.63%。
24
+ - Docker 真实集成测试:MySQL、Redis、RabbitMQ、Nacos、Seata TCC 共 5 项通过;Redis 和 Seata bridge 停机失败关闭 2 项通过。
25
+
26
+ ## [2.1.1] - 2026-08-12
27
+
28
+ ### 修复
29
+
30
+ - 锁定兼容的 `langchain-core==1.5.4` 与 `langchain-openai==1.4.2`,避免可选 AI 依赖解析冲突。
31
+ - CI 中间件恢复命令改用 `docker compose up -d --wait <service>`,兼容不支持 `start --wait` 的 Compose 版本。
32
+
5
33
  ## [2.1.0] - 2026-08-12
6
34
 
7
35
  ### 新增
@@ -34,4 +62,6 @@
34
62
  - Seata `distributed` 提供真实 TC + TCC 协调,不是 Python AT 数据源代理,不会自动生成 `undo_log`。
35
63
  - 内存 LangGraph checkpointer 仅用于测试;多 worker 生产环境必须注入共享存储后端。
36
64
 
65
+ [2.2.0]: https://github.com/YUCONGGEN/springbootAI/compare/v2.1.1...v2.2.0
66
+ [2.1.1]: https://github.com/YUCONGGEN/springbootAI/compare/v2.1.0...v2.1.1
37
67
  [2.1.0]: https://github.com/YUCONGGEN/springbootAI/compare/v2.0.2...v2.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: springbootAI
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Spring-style Python framework with Web, ORM, Cloud and AI integrations
5
5
  Author-email: YuConggen <1516933915@qq.com>
6
6
  License-Expression: MIT
@@ -56,16 +56,16 @@ Requires-Dist: sqlglot==27.28.1; extra == "ast"
56
56
  Provides-Extra: excel
57
57
  Requires-Dist: openpyxl==3.1.5; extra == "excel"
58
58
  Provides-Extra: ai
59
- Requires-Dist: langchain-openai==1.4.1; extra == "ai"
60
- Requires-Dist: langchain-core==1.5.1; extra == "ai"
59
+ Requires-Dist: langchain-openai==1.4.2; extra == "ai"
60
+ Requires-Dist: langchain-core==1.5.4; extra == "ai"
61
61
  Requires-Dist: langchain-classic==1.0.8; extra == "ai"
62
62
  Requires-Dist: langchain-text-splitters==1.1.2; extra == "ai"
63
63
  Requires-Dist: langchain-community==0.4.2; extra == "ai"
64
64
  Requires-Dist: numpy==2.2.6; extra == "ai"
65
65
  Requires-Dist: pydantic==2.13.4; extra == "ai"
66
66
  Provides-Extra: langchain
67
- Requires-Dist: langchain-openai==1.4.1; extra == "langchain"
68
- Requires-Dist: langchain-core==1.5.1; extra == "langchain"
67
+ Requires-Dist: langchain-openai==1.4.2; extra == "langchain"
68
+ Requires-Dist: langchain-core==1.5.4; extra == "langchain"
69
69
  Requires-Dist: langchain-classic==1.0.8; extra == "langchain"
70
70
  Requires-Dist: langchain-text-splitters==1.1.2; extra == "langchain"
71
71
  Requires-Dist: langchain-community==0.4.2; extra == "langchain"
@@ -108,8 +108,8 @@ Requires-Dist: defusedxml==0.7.1; extra == "full"
108
108
  Requires-Dist: requests==2.34.2; extra == "full"
109
109
  Requires-Dist: httpx==0.28.1; extra == "full"
110
110
  Requires-Dist: openpyxl==3.1.5; extra == "full"
111
- Requires-Dist: langchain-openai==1.4.1; extra == "full"
112
- Requires-Dist: langchain-core==1.5.1; extra == "full"
111
+ Requires-Dist: langchain-openai==1.4.2; extra == "full"
112
+ Requires-Dist: langchain-core==1.5.4; extra == "full"
113
113
  Requires-Dist: langchain-classic==1.0.8; extra == "full"
114
114
  Requires-Dist: langchain-text-splitters==1.1.2; extra == "full"
115
115
  Requires-Dist: langchain-community==0.4.2; extra == "full"
@@ -151,7 +151,7 @@ SpringBootAI 是一个采用 Spring 风格注解和分层结构的 Python 应用
151
151
  | AI 与编排 | 模型调用、Tools、RAG、Chain、状态图、MCP client/server | LangChain / LangGraph / 官方 MCP SDK |
152
152
  | 生产治理 | 健康检查、Prometheus、限流熔断、追踪、Swagger | prometheus-client / OpenTelemetry / OpenAPI |
153
153
 
154
- 当前版本是 `2.1.0`;支持 Python 3.10、3.11 和 3.12,许可证为 MIT。项目仍标记为 Beta。用于公网高并发、合规敏感或支付/订单/库存等核心系统前,必须完成目标数据库、流量模型、故障恢复和安全基线验证。内嵌 Gateway 适合内部路由,不替代公网 Nginx/Kong/WAF;Seata `distributed` 当前验证的是官方 TC + TCC 回调,不会为 Python 数据库操作自动生成 AT `undo_log`。
154
+ 当前版本是 `2.2.0`;支持 Python 3.10、3.11 和 3.12,许可证为 MIT。项目仍标记为 Beta。用于公网高并发、合规敏感或支付/订单/库存等核心系统前,必须完成目标数据库、流量模型、故障恢复和安全基线验证。内嵌 Gateway 适合内部路由,不替代公网 Nginx/Kong/WAF;Seata `distributed` 当前验证的是官方 TC + TCC 回调,不会为 Python 数据库操作自动生成 AT `undo_log`。
155
155
 
156
156
  [新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md) | [全部文档](https://github.com/YUCONGGEN/springbootAI/tree/master/doc) | [变更日志](https://github.com/YUCONGGEN/springbootAI/blob/master/CHANGELOG.md) | [安全报告](https://github.com/YUCONGGEN/springbootAI/blob/master/SECURITY.md) | [发布检查](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/RELEASE_CHECKLIST.md)
157
157
 
@@ -246,6 +246,7 @@ curl http://127.0.0.1:8080/api/hello/Alice
246
246
  |------|------|----------|-----------|
247
247
  | ✅ 新手入门 | [BEGINNER_GUIDE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md) | 随核心包 | 从零安装、创建项目、运行接口、打开 Swagger |
248
248
  | ✅ 常用注解模块 | [ANNOTATION_MODULES.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/ANNOTATION_MODULES.md) | 随核心包 | Bean Validation / 条件装配 / 缓存增强 / CSV / `@Version` / `@Transient` |
249
+ | ✅ AOP / 后置鉴权 / 重试恢复 | [AOP_SECURITY_RETRY.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AOP_SECURITY_RETRY.md) | 随核心包 | `@Aspect` 通知 / `@PostAuthorize` / `@Recover`,含小白示例与常见错误 |
249
250
  | 📦 AI(对接大模型) | [AI_MODULE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AI_MODULE.md) | `pip install springbootAI[ai]` | ChatClient / Advisor / Tools / RAG / Function Calling / 多厂商适配 |
250
251
  | 📦 LangChain | [LANGCHAIN_MODULE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/LANGCHAIN_MODULE.md) | `pip install springbootAI[langchain]` | Chains / Agents / Memory / Retrievers / VectorStores / 30+ 提供商 |
251
252
  | 📦 LangGraph | [LANGGRAPH_MODULE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/LANGGRAPH_MODULE.md) | `pip install springbootAI[langgraph]` | 状态图 / 条件路由 / 人工中断 / 注解式工作流 |
@@ -314,10 +315,10 @@ SpringBootAI 是一个 **Python Web 框架**。它把 Java Spring Boot 的"注
314
315
 
315
316
  | 组件 | 当前版本 |
316
317
  |------|----------|
317
- | `spring` 框架 API | 2.1.0 |
318
- | `spring.orm.pymybatis` | 2.1.0 |
319
- | `spring.ai` AI 模块 | 2.1.0 |
320
- | `spring.langchain` LangChain 模块 | 2.1.0 |
318
+ | `spring` 框架 API | 2.2.0 |
319
+ | `spring.orm.pymybatis` | 2.2.0 |
320
+ | `spring.ai` AI 模块 | 2.2.0 |
321
+ | `spring.langchain` LangChain 模块 | 2.2.0 |
321
322
  | Python | 3.10+ |
322
323
 
323
324
  ### 1.4 适合什么场景
@@ -361,7 +362,7 @@ SpringBootAI 注解会先把元数据放到 `__spring_annotations__`。之后是
361
362
  | 应用事件 | ✅ 可用 | `ApplicationEvent`、`@EventListener`、同步有序发布和异步监听 |
362
363
  | 内嵌 ORM + DDL Auto | ✅ 可用 | PyMyBatis + JPA ddl-auto 自动建表(create/update/validate),支持 XML/注解 SQL、事务、缓存 |
363
364
  | 本地事务 | ✅ 可用 | `@Transactional` 支持七种 Spring 传播模式 |
364
- | JWT 与方法安全 | ✅ 可用 | access/refresh token、`@Authenticate`、角色/权限授权、401/403 映射 |
365
+ | JWT 与方法安全 | ✅ 可用 | access/refresh token、`@Authenticate`、前置/后置授权、401/403 映射 |
365
366
  | 重试/异步 | ✅ 可用 | 受管 Bean 的退避重试、恢复方法和 Future/Task 异步调度 |
366
367
  | Redis/缓存 | ✅ 可用 | 分布式锁、KV/Hash/List/Set/Counter,需要 Redis 服务 |
367
368
  | RabbitMQ | ✅ 可用 | `@RabbitListener` 自动注册并后台消费,`RabbitTemplate` 发送 |
@@ -372,7 +373,7 @@ SpringBootAI 注解会先把元数据放到 `__spring_annotations__`。之后是
372
373
  | API Gateway | ✅ 可用 | 轻量 ASGI/WSGI 网关,路由转发、路径重写、过滤器链、负载均衡 |
373
374
  | Prometheus 监控 | ✅ 可用 | Counter/Gauge/Histogram 指标暴露 |
374
375
  | Feign 声明式 HTTP | ✅ 可用 | 声明式接口、Fallback 降级、自动传播 XID 和 trace 头 |
375
- | 高级 AOP | ✅ 可用 | 限流、熔断、幂等、审计、锁、指标、追踪、缓存 |
376
+ | 高级 AOP | ✅ 可用 | 声明式切面、限流、熔断、幂等、审计、锁、指标、追踪、缓存 |
376
377
  | AI 模块 | ✅ 可用 | ChatClient/ChatModel/EmbeddingModel/Advisor/Tools,OpenAI/Ollama/DeepSeek/Moonshot 适配 |
377
378
  | LangChain 模块 | ✅ 可用 | Chains/Agents(6 种)/Memory/Retrievers/VectorStores + 30+ 提供商,双向适配器 |
378
379
  | LangGraph 模块 | ✅ 可选 | 官方 LangGraph 状态图、条件路由、人工中断、持久化 checkpointer 和注解工作流 |
@@ -1005,7 +1006,7 @@ class ScheduledTasks:
1005
1006
  print("Current time:", time.time())
1006
1007
  ```
1007
1008
 
1008
- **边界要点**:`max_retries=3` 包含首次调用;`@Async` 同步方法返回 `Future`;`@Scheduled` 多 worker 会重复执行。
1009
+ **边界要点**:`max_retries=3` 包含首次调用;重试耗尽可用 `@Recover` 按异常类型兜底;`@Async` 同步方法返回 `Future`;`@Scheduled` 多 worker 会重复执行。声明式切面、后置鉴权和恢复方法的完整小白示例见 [AOP_SECURITY_RETRY.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AOP_SECURITY_RETRY.md)。
1009
1010
 
1010
1011
  ### 5.10 安全、Cloud 与消息注解
1011
1012
 
@@ -1013,6 +1014,7 @@ class ScheduledTasks:
1013
1014
  |------|----------|--------------|
1014
1015
  | `@Authenticate` | 校验 JWT 并建立安全上下文 | 受管 Bean 实际执行;HTTP 控制器自动读取 `Authorization: Bearer ...` |
1015
1016
  | `@PreAuthorize` | 按角色/权限表达式授权 | 受管 Bean 实际执行;未认证返回 401,权限不足返回 403 |
1017
+ | `@PostAuthorize` | 根据方法返回值授权 | 受管 Bean 返回后执行;支持 `returnObject` / `#returnObject` |
1016
1018
  | `@Secured` | 按任一角色授权 | 受管 Bean 实际执行 |
1017
1019
  | `@SentinelResource` | 限流、业务异常 fallback | 受管 Bean 方法会包装;已内嵌限流熔断引擎 |
1018
1020
  | `@GlobalTransactional` | 通过 Seata 管理全局事务 | 受管 Bean 方法调用 Seata manager |
@@ -1365,6 +1367,22 @@ def call_remote(self):
1365
1367
 
1366
1368
  **只对幂等操作开启自动重试**(如读操作)。写操作必须先设计幂等键。
1367
1369
 
1370
+ 重试耗尽后可以使用 `@Recover`:
1371
+
1372
+ ```python
1373
+ from spring.annotations import Recover, Retryable
1374
+
1375
+ @Retryable(value=(ConnectionError,), max_attempts=3, backoff=200)
1376
+ def call_remote(self, key):
1377
+ raise ConnectionError("offline")
1378
+
1379
+ @Recover(ConnectionError)
1380
+ def recover_remote(self, error, key):
1381
+ return {"key": key, "status": "degraded", "reason": str(error)}
1382
+ ```
1383
+
1384
+ 恢复方法选择、异步用法和常见错误见 [AOP / 后置鉴权 / 重试恢复指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AOP_SECURITY_RETRY.md)。
1385
+
1368
1386
  ### 11.3 @Async & @Scheduled
1369
1387
 
1370
1388
  ```python
@@ -17,7 +17,7 @@ SpringBootAI 是一个采用 Spring 风格注解和分层结构的 Python 应用
17
17
  | AI 与编排 | 模型调用、Tools、RAG、Chain、状态图、MCP client/server | LangChain / LangGraph / 官方 MCP SDK |
18
18
  | 生产治理 | 健康检查、Prometheus、限流熔断、追踪、Swagger | prometheus-client / OpenTelemetry / OpenAPI |
19
19
 
20
- 当前版本是 `2.1.0`;支持 Python 3.10、3.11 和 3.12,许可证为 MIT。项目仍标记为 Beta。用于公网高并发、合规敏感或支付/订单/库存等核心系统前,必须完成目标数据库、流量模型、故障恢复和安全基线验证。内嵌 Gateway 适合内部路由,不替代公网 Nginx/Kong/WAF;Seata `distributed` 当前验证的是官方 TC + TCC 回调,不会为 Python 数据库操作自动生成 AT `undo_log`。
20
+ 当前版本是 `2.2.0`;支持 Python 3.10、3.11 和 3.12,许可证为 MIT。项目仍标记为 Beta。用于公网高并发、合规敏感或支付/订单/库存等核心系统前,必须完成目标数据库、流量模型、故障恢复和安全基线验证。内嵌 Gateway 适合内部路由,不替代公网 Nginx/Kong/WAF;Seata `distributed` 当前验证的是官方 TC + TCC 回调,不会为 Python 数据库操作自动生成 AT `undo_log`。
21
21
 
22
22
  [新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md) | [全部文档](https://github.com/YUCONGGEN/springbootAI/tree/master/doc) | [变更日志](https://github.com/YUCONGGEN/springbootAI/blob/master/CHANGELOG.md) | [安全报告](https://github.com/YUCONGGEN/springbootAI/blob/master/SECURITY.md) | [发布检查](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/RELEASE_CHECKLIST.md)
23
23
 
@@ -112,6 +112,7 @@ curl http://127.0.0.1:8080/api/hello/Alice
112
112
  |------|------|----------|-----------|
113
113
  | ✅ 新手入门 | [BEGINNER_GUIDE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md) | 随核心包 | 从零安装、创建项目、运行接口、打开 Swagger |
114
114
  | ✅ 常用注解模块 | [ANNOTATION_MODULES.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/ANNOTATION_MODULES.md) | 随核心包 | Bean Validation / 条件装配 / 缓存增强 / CSV / `@Version` / `@Transient` |
115
+ | ✅ AOP / 后置鉴权 / 重试恢复 | [AOP_SECURITY_RETRY.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AOP_SECURITY_RETRY.md) | 随核心包 | `@Aspect` 通知 / `@PostAuthorize` / `@Recover`,含小白示例与常见错误 |
115
116
  | 📦 AI(对接大模型) | [AI_MODULE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AI_MODULE.md) | `pip install springbootAI[ai]` | ChatClient / Advisor / Tools / RAG / Function Calling / 多厂商适配 |
116
117
  | 📦 LangChain | [LANGCHAIN_MODULE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/LANGCHAIN_MODULE.md) | `pip install springbootAI[langchain]` | Chains / Agents / Memory / Retrievers / VectorStores / 30+ 提供商 |
117
118
  | 📦 LangGraph | [LANGGRAPH_MODULE.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/LANGGRAPH_MODULE.md) | `pip install springbootAI[langgraph]` | 状态图 / 条件路由 / 人工中断 / 注解式工作流 |
@@ -180,10 +181,10 @@ SpringBootAI 是一个 **Python Web 框架**。它把 Java Spring Boot 的"注
180
181
 
181
182
  | 组件 | 当前版本 |
182
183
  |------|----------|
183
- | `spring` 框架 API | 2.1.0 |
184
- | `spring.orm.pymybatis` | 2.1.0 |
185
- | `spring.ai` AI 模块 | 2.1.0 |
186
- | `spring.langchain` LangChain 模块 | 2.1.0 |
184
+ | `spring` 框架 API | 2.2.0 |
185
+ | `spring.orm.pymybatis` | 2.2.0 |
186
+ | `spring.ai` AI 模块 | 2.2.0 |
187
+ | `spring.langchain` LangChain 模块 | 2.2.0 |
187
188
  | Python | 3.10+ |
188
189
 
189
190
  ### 1.4 适合什么场景
@@ -227,7 +228,7 @@ SpringBootAI 注解会先把元数据放到 `__spring_annotations__`。之后是
227
228
  | 应用事件 | ✅ 可用 | `ApplicationEvent`、`@EventListener`、同步有序发布和异步监听 |
228
229
  | 内嵌 ORM + DDL Auto | ✅ 可用 | PyMyBatis + JPA ddl-auto 自动建表(create/update/validate),支持 XML/注解 SQL、事务、缓存 |
229
230
  | 本地事务 | ✅ 可用 | `@Transactional` 支持七种 Spring 传播模式 |
230
- | JWT 与方法安全 | ✅ 可用 | access/refresh token、`@Authenticate`、角色/权限授权、401/403 映射 |
231
+ | JWT 与方法安全 | ✅ 可用 | access/refresh token、`@Authenticate`、前置/后置授权、401/403 映射 |
231
232
  | 重试/异步 | ✅ 可用 | 受管 Bean 的退避重试、恢复方法和 Future/Task 异步调度 |
232
233
  | Redis/缓存 | ✅ 可用 | 分布式锁、KV/Hash/List/Set/Counter,需要 Redis 服务 |
233
234
  | RabbitMQ | ✅ 可用 | `@RabbitListener` 自动注册并后台消费,`RabbitTemplate` 发送 |
@@ -238,7 +239,7 @@ SpringBootAI 注解会先把元数据放到 `__spring_annotations__`。之后是
238
239
  | API Gateway | ✅ 可用 | 轻量 ASGI/WSGI 网关,路由转发、路径重写、过滤器链、负载均衡 |
239
240
  | Prometheus 监控 | ✅ 可用 | Counter/Gauge/Histogram 指标暴露 |
240
241
  | Feign 声明式 HTTP | ✅ 可用 | 声明式接口、Fallback 降级、自动传播 XID 和 trace 头 |
241
- | 高级 AOP | ✅ 可用 | 限流、熔断、幂等、审计、锁、指标、追踪、缓存 |
242
+ | 高级 AOP | ✅ 可用 | 声明式切面、限流、熔断、幂等、审计、锁、指标、追踪、缓存 |
242
243
  | AI 模块 | ✅ 可用 | ChatClient/ChatModel/EmbeddingModel/Advisor/Tools,OpenAI/Ollama/DeepSeek/Moonshot 适配 |
243
244
  | LangChain 模块 | ✅ 可用 | Chains/Agents(6 种)/Memory/Retrievers/VectorStores + 30+ 提供商,双向适配器 |
244
245
  | LangGraph 模块 | ✅ 可选 | 官方 LangGraph 状态图、条件路由、人工中断、持久化 checkpointer 和注解工作流 |
@@ -871,7 +872,7 @@ class ScheduledTasks:
871
872
  print("Current time:", time.time())
872
873
  ```
873
874
 
874
- **边界要点**:`max_retries=3` 包含首次调用;`@Async` 同步方法返回 `Future`;`@Scheduled` 多 worker 会重复执行。
875
+ **边界要点**:`max_retries=3` 包含首次调用;重试耗尽可用 `@Recover` 按异常类型兜底;`@Async` 同步方法返回 `Future`;`@Scheduled` 多 worker 会重复执行。声明式切面、后置鉴权和恢复方法的完整小白示例见 [AOP_SECURITY_RETRY.md](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AOP_SECURITY_RETRY.md)。
875
876
 
876
877
  ### 5.10 安全、Cloud 与消息注解
877
878
 
@@ -879,6 +880,7 @@ class ScheduledTasks:
879
880
  |------|----------|--------------|
880
881
  | `@Authenticate` | 校验 JWT 并建立安全上下文 | 受管 Bean 实际执行;HTTP 控制器自动读取 `Authorization: Bearer ...` |
881
882
  | `@PreAuthorize` | 按角色/权限表达式授权 | 受管 Bean 实际执行;未认证返回 401,权限不足返回 403 |
883
+ | `@PostAuthorize` | 根据方法返回值授权 | 受管 Bean 返回后执行;支持 `returnObject` / `#returnObject` |
882
884
  | `@Secured` | 按任一角色授权 | 受管 Bean 实际执行 |
883
885
  | `@SentinelResource` | 限流、业务异常 fallback | 受管 Bean 方法会包装;已内嵌限流熔断引擎 |
884
886
  | `@GlobalTransactional` | 通过 Seata 管理全局事务 | 受管 Bean 方法调用 Seata manager |
@@ -1231,6 +1233,22 @@ def call_remote(self):
1231
1233
 
1232
1234
  **只对幂等操作开启自动重试**(如读操作)。写操作必须先设计幂等键。
1233
1235
 
1236
+ 重试耗尽后可以使用 `@Recover`:
1237
+
1238
+ ```python
1239
+ from spring.annotations import Recover, Retryable
1240
+
1241
+ @Retryable(value=(ConnectionError,), max_attempts=3, backoff=200)
1242
+ def call_remote(self, key):
1243
+ raise ConnectionError("offline")
1244
+
1245
+ @Recover(ConnectionError)
1246
+ def recover_remote(self, error, key):
1247
+ return {"key": key, "status": "degraded", "reason": str(error)}
1248
+ ```
1249
+
1250
+ 恢复方法选择、异步用法和常见错误见 [AOP / 后置鉴权 / 重试恢复指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/AOP_SECURITY_RETRY.md)。
1251
+
1234
1252
  ### 11.3 @Async & @Scheduled
1235
1253
 
1236
1254
  ```python
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
12
12
  [project]
13
13
  # 发行名 springbootAI(PyPI 包名,pip install springbootAI);导入包名仍为 spring
14
14
  name = "springbootAI"
15
- version = "2.1.0"
15
+ version = "2.2.0"
16
16
  description = "Spring-style Python framework with Web, ORM, Cloud and AI integrations"
17
17
  readme = "README.md"
18
18
  license = "MIT"
@@ -101,8 +101,8 @@ excel = ["openpyxl==3.1.5"]
101
101
  # 依赖清单同步自 requirements-ai.txt(所有版本号 == 已测试验证)
102
102
  # ========================================
103
103
  ai = [
104
- "langchain-openai==1.4.1",
105
- "langchain-core==1.5.1",
104
+ "langchain-openai==1.4.2",
105
+ "langchain-core==1.5.4",
106
106
  "langchain-classic==1.0.8",
107
107
  "langchain-text-splitters==1.1.2",
108
108
  "langchain-community==0.4.2",
@@ -117,8 +117,8 @@ ai = [
117
117
  # 详见 doc/LANGCHAIN_MODULE.md
118
118
  # ========================================
119
119
  langchain = [
120
- "langchain-openai==1.4.1",
121
- "langchain-core==1.5.1",
120
+ "langchain-openai==1.4.2",
121
+ "langchain-core==1.5.4",
122
122
  "langchain-classic==1.0.8",
123
123
  "langchain-text-splitters==1.1.2",
124
124
  "langchain-community==0.4.2",
@@ -191,8 +191,8 @@ full = [
191
191
  # Excel
192
192
  "openpyxl==3.1.5",
193
193
  # AI 模块(对齐 springbootAI[ai])
194
- "langchain-openai==1.4.1",
195
- "langchain-core==1.5.1",
194
+ "langchain-openai==1.4.2",
195
+ "langchain-core==1.5.4",
196
196
  "langchain-classic==1.0.8",
197
197
  "langchain-text-splitters==1.1.2",
198
198
  "langchain-community==0.4.2",
@@ -7,21 +7,21 @@
7
7
  #
8
8
  # 安装方式(按需):
9
9
  # pip install -r requirements-ai.txt # 全量
10
- # pip install langchain-openai==1.4.1 # 仅 OpenAI 适配
10
+ # pip install langchain-openai==1.4.2 # 仅 OpenAI 适配
11
11
  #
12
12
  # 推荐 Python: 3.10+(CI 测试 3.10 / 3.11 / 3.12)
13
13
  # 所有固定版本均经过本仓库测试与依赖漏洞扫描
14
14
  # ========================================
15
15
 
16
16
  # LangChain 核心(1.x 架构,BaseChatModel / Embeddings / Runnable 接口)
17
- langchain-core==1.5.1
17
+ langchain-core==1.5.4
18
18
 
19
19
  # LangChain classic(LLMChain / ConversationChain / initialize_agent 等经典 API)
20
20
  # langchain 1.x 把经典 Chain/Agent 迁到了 langchain-classic 包
21
21
  langchain-classic==1.0.8
22
22
 
23
23
  # OpenAI 兼容模型适配(OpenAI / Azure / DeepSeek / Moonshot)
24
- langchain-openai==1.4.1
24
+ langchain-openai==1.4.2
25
25
 
26
26
  # 文档切分器(RecursiveCharacterTextSplitter / CharacterTextSplitter)
27
27
  langchain-text-splitters==1.1.2
@@ -5,7 +5,7 @@ from .config import *
5
5
  from .utils import *
6
6
  from .main import create_app, run, SpringApplication, run_cli
7
7
 
8
- __version__ = "2.1.0"
8
+ __version__ = "2.2.0"
9
9
  __author__ = "yuconggen"
10
10
  __license__ = "MIT"
11
11
 
@@ -47,7 +47,7 @@ from spring.ai.resilience import (
47
47
  from spring.ai.observability import AIMetrics, ai_metrics
48
48
  from spring.ai.autoconfig import AIProperties, bind_ai_config, configure_ai
49
49
 
50
- __version__ = "2.1.0"
50
+ __version__ = "2.2.0"
51
51
 
52
52
  __all__ = [
53
53
  # core
@@ -13,6 +13,13 @@ from .core import (
13
13
  DeleteMapping,
14
14
  Service,
15
15
  Component,
16
+ Aspect,
17
+ Pointcut,
18
+ Before,
19
+ After,
20
+ Around,
21
+ AfterReturning,
22
+ AfterThrowing,
16
23
  Repository,
17
24
  Autowired,
18
25
  Qualifier,
@@ -42,6 +49,7 @@ from .core import (
42
49
  Transactional,
43
50
  Cacheable,
44
51
  Retryable,
52
+ Recover,
45
53
  Async,
46
54
  Scheduled,
47
55
  AsyncResult,
@@ -56,6 +64,7 @@ from .core import (
56
64
  Validate,
57
65
  Trace,
58
66
  PreAuthorize,
67
+ PostAuthorize,
59
68
  Secured,
60
69
  Authenticate,
61
70
  )
@@ -127,6 +136,13 @@ __all__ = [
127
136
  "DeleteMapping",
128
137
  "Service",
129
138
  "Component",
139
+ "Aspect",
140
+ "Pointcut",
141
+ "Before",
142
+ "After",
143
+ "Around",
144
+ "AfterReturning",
145
+ "AfterThrowing",
130
146
  "Repository",
131
147
  "Autowired",
132
148
  "Qualifier",
@@ -154,6 +170,7 @@ __all__ = [
154
170
  "Transactional",
155
171
  "Cacheable",
156
172
  "Retryable",
173
+ "Recover",
157
174
  "Async",
158
175
  "Scheduled",
159
176
  "AsyncResult",
@@ -169,6 +186,7 @@ __all__ = [
169
186
  "Trace",
170
187
  # 安全注解
171
188
  "PreAuthorize",
189
+ "PostAuthorize",
172
190
  "Secured",
173
191
  "Authenticate",
174
192
  # Spring Cloud 注解
@@ -230,6 +230,76 @@ class Component(SpringAnnotation):
230
230
  super().__init__(value=value)
231
231
 
232
232
 
233
+ class Aspect(Component):
234
+ """Declare a component whose methods contain AOP advice."""
235
+
236
+ _annotation_type = "aspect"
237
+
238
+
239
+ class Pointcut(SpringAnnotation):
240
+ """Declare a reusable pointcut expression on an aspect method."""
241
+
242
+ _annotation_type = "pointcut"
243
+
244
+ def __init__(self, value: str):
245
+ if not isinstance(value, str) or not value.strip():
246
+ raise ValueError("Pointcut value must be a non-empty string")
247
+ super().__init__(value=value.strip())
248
+
249
+
250
+ class _AdviceAnnotation:
251
+ def _init_advice(self, value: str = "", *, pointcut: Optional[str] = None):
252
+ expression = pointcut if pointcut is not None else value
253
+ if not isinstance(expression, str) or not expression.strip():
254
+ raise ValueError("Advice pointcut must be a non-empty string")
255
+ SpringAnnotation.__init__(self, value=expression.strip())
256
+
257
+
258
+ class Before(_AdviceAnnotation, SpringAnnotation):
259
+ """Run advice before a matched method."""
260
+
261
+ _annotation_type = "advice"
262
+
263
+ def __init__(self, value: str = "", *, pointcut: Optional[str] = None):
264
+ self._init_advice(value, pointcut=pointcut)
265
+
266
+
267
+ class After(Before):
268
+ """Run advice after a matched method, including exceptional completion."""
269
+
270
+
271
+ class Around(Before):
272
+ """Wrap a matched method with a ``ProceedingJoinPoint``."""
273
+
274
+
275
+ class AfterReturning(Before):
276
+ """Run advice after a matched method returns successfully."""
277
+
278
+ def __init__(
279
+ self,
280
+ value: str = "",
281
+ *,
282
+ pointcut: Optional[str] = None,
283
+ returning: str = "result",
284
+ ):
285
+ self._init_advice(value, pointcut=pointcut)
286
+ self.returning = returning
287
+
288
+
289
+ class AfterThrowing(Before):
290
+ """Run advice after a matched method raises an exception."""
291
+
292
+ def __init__(
293
+ self,
294
+ value: str = "",
295
+ *,
296
+ pointcut: Optional[str] = None,
297
+ throwing: str = "exception",
298
+ ):
299
+ self._init_advice(value, pointcut=pointcut)
300
+ self.throwing = throwing
301
+
302
+
233
303
  class Repository(SpringAnnotation):
234
304
  _annotation_type = "component"
235
305
 
@@ -595,6 +665,35 @@ class Retryable(SpringAnnotation):
595
665
  )
596
666
 
597
667
 
668
+ class Recover(SpringAnnotation):
669
+ """Mark a method as a fallback for an exhausted ``@Retryable`` call."""
670
+
671
+ _annotation_type = "recover"
672
+
673
+ def __new__(cls, *args, **kwargs):
674
+ # ``@Recover(SomeError)`` uses an exception class as configuration,
675
+ # not as the decorator target understood by SpringAnnotation.__new__.
676
+ if args and isinstance(args[0], type) and issubclass(args[0], Exception):
677
+ return object.__new__(cls)
678
+ return super().__new__(cls, *args, **kwargs)
679
+
680
+ def __init__(
681
+ self,
682
+ value: Optional[Union[Type[Exception], Tuple[Type[Exception], ...]]] = None,
683
+ ):
684
+ if value is None:
685
+ exception_types = None
686
+ elif isinstance(value, type) and issubclass(value, Exception):
687
+ exception_types = (value,)
688
+ elif isinstance(value, tuple) and value and all(
689
+ isinstance(item, type) and issubclass(item, Exception) for item in value
690
+ ):
691
+ exception_types = value
692
+ else:
693
+ raise TypeError("Recover value must be an exception type or non-empty tuple")
694
+ super().__init__(value=exception_types)
695
+
696
+
598
697
  class Async(SpringAnnotation):
599
698
  _annotation_type = "aop"
600
699
 
@@ -832,6 +931,17 @@ class PreAuthorize(SpringAnnotation):
832
931
  super().__init__(value=value)
833
932
 
834
933
 
934
+ class PostAuthorize(SpringAnnotation):
935
+ """Authorize a method after evaluating its return value."""
936
+
937
+ _annotation_type = "security"
938
+
939
+ def __init__(self, value: str):
940
+ if not isinstance(value, str) or not value.strip():
941
+ raise ValueError("PostAuthorize value must be a non-empty expression")
942
+ super().__init__(value=value.strip())
943
+
944
+
835
945
  class Secured(SpringAnnotation):
836
946
  """
837
947
  角色权限控制注解
@@ -0,0 +1,10 @@
1
+ from .proxy_factory import ProxyFactory
2
+ from .method_interceptor import MethodInterceptor
3
+ from .aspect import JoinPoint, ProceedingJoinPoint
4
+
5
+ __all__ = [
6
+ "ProxyFactory",
7
+ "MethodInterceptor",
8
+ "JoinPoint",
9
+ "ProceedingJoinPoint",
10
+ ]