springbootAI 1.8.7__tar.gz → 1.8.8__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.
- {springbootai-1.8.7/springbootAI.egg-info → springbootai-1.8.8}/PKG-INFO +4 -4
- {springbootai-1.8.7 → springbootai-1.8.8}/README.md +3 -3
- {springbootai-1.8.7 → springbootai-1.8.8}/pyproject.toml +1 -1
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/__init__.py +1 -1
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/autoconfig.py +24 -10
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/annotations/cloud.py +9 -7
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/aop/cloud_aop.py +19 -8
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/__init__.py +2 -1
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/gateway.py +93 -20
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/seata.py +145 -98
- springbootai-1.8.8/spring/cloud/seata_bridge.py +142 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/config/config_loader.py +59 -1
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/logging/loguru_logger.py +99 -32
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/cache/redis_cache.py +93 -42
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/security/jwt_utils.py +27 -13
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/actuator.py +115 -13
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/health.py +67 -46
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/web_context.py +6 -1
- {springbootai-1.8.7 → springbootai-1.8.8/springbootAI.egg-info}/PKG-INFO +4 -4
- {springbootai-1.8.7 → springbootai-1.8.8}/springbootAI.egg-info/SOURCES.txt +2 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_actuator.py +7 -1
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_ai_module.py +16 -6
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_di_config_event_full.py +82 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_logging_config.py +128 -7
- springbootai-1.8.8/tests/test_seata_bridge.py +175 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_security.py +4 -1
- {springbootai-1.8.7 → springbootai-1.8.8}/LICENSE +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/MANIFEST.in +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/requirements.txt +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/setup.cfg +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/setup.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/advisors.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/core.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/etl.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/memory.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/observability.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/providers.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/resilience.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/tools.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/ai/vectorstore.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/annotations/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/annotations/cache.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/annotations/conditional.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/annotations/core.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/annotations/messaging.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/aop/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/aop/comprehensive_aop.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/aop/method_interceptor.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/aop/proxy_factory.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/discovery.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/feign.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/load_balancer.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/sentinel.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/tracer.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/cloud/transaction_store.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/config/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/config/binding.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/context/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/context/application_context.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/context/bean_definition.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/context/bean_factory.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/context/registry.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/context/scanner.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/core/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/core/graceful_shutdown.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/core/typing_utils.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/csv/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/csv/annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/csv/converters.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/csv/easy_csv.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/csv/exceptions.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/csv/reader.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/csv/writer.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/data/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/data/page.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/data/repository.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/data/specification.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/datasource/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/datasource/annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/datasource/context.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/datasource/dynamic.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/event/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/event/publisher.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/converters.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/easy_excel.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/exceptions.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/reader.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/style.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/excel/writer.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/accessor.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/auto_config.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/holder.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/locale.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/locale_resolver.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/message_source.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/middleware.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/properties.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/i18n/sources.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/logging/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/main.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/messaging/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/messaging/rabbitmq.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/monitoring/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/monitoring/prometheus.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/database.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/ddl_auto.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/migration.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/mybatis_integration.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/annotations/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/annotations/annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/cache/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/cache/cache.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/circuit_breaker/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/configuration.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/core/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/core/sql_session.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/core/sql_session_factory.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/dialect/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/dialect/dialect.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/dynamic_sql/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/interceptor/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/interceptor/interceptor.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/mapper/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/mapper/mapper.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/metrics/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/metrics/metrics.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/pool/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/pool/connection_pool.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/security/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/security/access_control.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/security/password_encoder.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/security/sensitive_data_masker.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/security/sql_injection_detector.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/transaction/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/transaction/transaction.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/type_handler/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/type_handler/type_handler.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/version.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/xml_parser/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/orm/pymybatis/xml_parser/xml_parser.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/retry/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/retry/retry_annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/retry/retry_decorator.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/scheduling/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/scheduling/scheduler.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/security/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/security/replay_protection.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/security/secret_manager.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/security/security_aop.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/security/security_context.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/test/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/test/slicing.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/tracing/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/tracing/skywalking.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/tx/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/tx/events.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/tx/synchronization.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/utils/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/utils/banner.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/utils/bean_utils.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/utils/logger.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/utils/redis_client.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/validation/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/validation/aop.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/validation/constraints.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/validation/exceptions.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/validation/validator.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/exception_handler.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/interceptor.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/result.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/web/swagger.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/websocket/__init__.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/websocket/annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/websocket/broker.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/websocket/exceptions.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/websocket/handler.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/websocket/router.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/spring/websocket/session.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/springbootAI.egg-info/dependency_links.txt +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/springbootAI.egg-info/entry_points.txt +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/springbootAI.egg-info/requires.txt +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/springbootAI.egg-info/top_level.txt +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/sql/seata_undo_log.sql +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_annotation_combinations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_annotations_contract.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_aop_annotations_full.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_bean_utils.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_cache_annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_cloud_embedded_full.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_component_init_error.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_conditional_annotations.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_config_binding.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_config_fixes_185.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_connection_resilience.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_core_annotations_full.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_csv_module.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_data_repository.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_datasource_routing.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_ddl_auto.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_excel_module.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_i18n_module.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_jpa_version_transient.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_new_features.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_orm_pymybatis_full.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_production_readiness.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_pymybatis_contract.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_seata_durable_store.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_security_full.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_swagger_module.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_test_slicing.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_transactional_events.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_validation_module.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_web_annotations_full.py +0 -0
- {springbootai-1.8.7 → springbootai-1.8.8}/tests/test_websocket_module.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: springbootAI
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.8
|
|
4
4
|
Summary: Python版Spring Boot框架,集成PyMyBatis ORM
|
|
5
5
|
Author-email: YuConggen <1516933915@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -86,7 +86,7 @@ Dynamic: license-file
|
|
|
86
86
|
|
|
87
87
|
SpringBootAI 是一个借鉴 Spring Boot 编程模型的 Python Web 框架,提供装饰器式组件扫描、依赖注入、FastAPI 路由、配置加载、安全能力、内嵌的 PyMyBatis ORM,以及企业级 AI 模块(对齐 Spring AI 2.0:ChatClient/Advisor/Tools/RAG/Function Calling)。本指南为框架核心综合使用文档;**AI / ORM / Cloud / Excel 等模块的完整注解与功能说明已分离为独立文档**(见下方“模块文档”),本指南相应章节保留概览与跳转链接。
|
|
88
88
|
|
|
89
|
-
- SpringBootAI 版本:`1.8.
|
|
89
|
+
- SpringBootAI 版本:`1.8.8`
|
|
90
90
|
- 内嵌 PyMyBatis 版本:`1.4.0`
|
|
91
91
|
- Python:3.10+
|
|
92
92
|
- 状态:Beta(企业试点)
|
|
@@ -153,7 +153,7 @@ SpringBootAI 借鉴了 Spring Boot 的注解和分层习惯,但运行时是 Py
|
|
|
153
153
|
|
|
154
154
|
| 组件 | 当前版本 |
|
|
155
155
|
|------|----------|
|
|
156
|
-
| `spring` 框架 API | 1.8.
|
|
156
|
+
| `spring` 框架 API | 1.8.8 |
|
|
157
157
|
| `spring.orm.pymybatis` | 1.4.0 |
|
|
158
158
|
| Python | 3.10+ |
|
|
159
159
|
|
|
@@ -442,7 +442,7 @@ CORS_ALLOW_ORIGINS CORS_ALLOW_CREDENTIALS
|
|
|
442
442
|
LOG_LEVEL LOG_DIR
|
|
443
443
|
```
|
|
444
444
|
|
|
445
|
-
|
|
445
|
+
`SPRING_PROFILES_ACTIVE` 用于 `@Profile` 组件筛选、生产安全校验,以及**自动加载并深度合并** `application-{profile}.yml`(v1.8.5 起实现,对齐 Spring Boot 语义)。Profile 文件与主 `application.yml` 同目录,加载顺序为:主配置 → profile 配置深度合并(profile 覆盖主配置的同名键,未涉及的键保留),合并后再解析 `${ENV:default}` 占位符和环境变量覆盖。例如 `SPRING_PROFILES_ACTIVE=prod` 会自动合并 `application-prod.yml`,无需部署流程生成最终 `application.yml`。
|
|
446
446
|
|
|
447
447
|
### 4.4 Docker 容器 IP 自动检测(开发环境)
|
|
448
448
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
SpringBootAI 是一个借鉴 Spring Boot 编程模型的 Python Web 框架,提供装饰器式组件扫描、依赖注入、FastAPI 路由、配置加载、安全能力、内嵌的 PyMyBatis ORM,以及企业级 AI 模块(对齐 Spring AI 2.0:ChatClient/Advisor/Tools/RAG/Function Calling)。本指南为框架核心综合使用文档;**AI / ORM / Cloud / Excel 等模块的完整注解与功能说明已分离为独立文档**(见下方“模块文档”),本指南相应章节保留概览与跳转链接。
|
|
4
4
|
|
|
5
|
-
- SpringBootAI 版本:`1.8.
|
|
5
|
+
- SpringBootAI 版本:`1.8.8`
|
|
6
6
|
- 内嵌 PyMyBatis 版本:`1.4.0`
|
|
7
7
|
- Python:3.10+
|
|
8
8
|
- 状态:Beta(企业试点)
|
|
@@ -69,7 +69,7 @@ SpringBootAI 借鉴了 Spring Boot 的注解和分层习惯,但运行时是 Py
|
|
|
69
69
|
|
|
70
70
|
| 组件 | 当前版本 |
|
|
71
71
|
|------|----------|
|
|
72
|
-
| `spring` 框架 API | 1.8.
|
|
72
|
+
| `spring` 框架 API | 1.8.8 |
|
|
73
73
|
| `spring.orm.pymybatis` | 1.4.0 |
|
|
74
74
|
| Python | 3.10+ |
|
|
75
75
|
|
|
@@ -358,7 +358,7 @@ CORS_ALLOW_ORIGINS CORS_ALLOW_CREDENTIALS
|
|
|
358
358
|
LOG_LEVEL LOG_DIR
|
|
359
359
|
```
|
|
360
360
|
|
|
361
|
-
|
|
361
|
+
`SPRING_PROFILES_ACTIVE` 用于 `@Profile` 组件筛选、生产安全校验,以及**自动加载并深度合并** `application-{profile}.yml`(v1.8.5 起实现,对齐 Spring Boot 语义)。Profile 文件与主 `application.yml` 同目录,加载顺序为:主配置 → profile 配置深度合并(profile 覆盖主配置的同名键,未涉及的键保留),合并后再解析 `${ENV:default}` 占位符和环境变量覆盖。例如 `SPRING_PROFILES_ACTIVE=prod` 会自动合并 `application-prod.yml`,无需部署流程生成最终 `application.yml`。
|
|
362
362
|
|
|
363
363
|
### 4.4 Docker 容器 IP 自动检测(开发环境)
|
|
364
364
|
|
|
@@ -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 = "1.8.
|
|
15
|
+
version = "1.8.8"
|
|
16
16
|
description = "Python版Spring Boot框架,集成PyMyBatis ORM"
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
license = "MIT"
|
|
@@ -60,10 +60,24 @@ from spring.context.registry import BeanRegistry
|
|
|
60
60
|
|
|
61
61
|
logger = logging.getLogger("Spring.AI")
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
|
|
64
|
+
def _ai_allow_fake() -> bool:
|
|
65
|
+
"""读取 ``AI_ALLOW_FAKE`` 环境变量,决定 api_key 缺失时是否降级 ``FakeChatModel``。
|
|
66
|
+
|
|
67
|
+
安全默认:``false``。开发/测试环境需**显式**设置 ``AI_ALLOW_FAKE=true``
|
|
68
|
+
才允许降级,防止生产环境配错时无声返回测试数据。
|
|
69
|
+
|
|
70
|
+
设计要点:
|
|
71
|
+
- 每次调用实时读取环境变量(非模块导入时一次性读取),便于 ``config_loader``
|
|
72
|
+
在生产 profile 下运行时强制 ``AI_ALLOW_FAKE=false``,避免导入时序导致的绕过。
|
|
73
|
+
- 接受 ``true/1/yes/on``(大小写不敏感)为 True,其余为 False,与框架
|
|
74
|
+
``_to_bool`` 规则一致。
|
|
75
|
+
- 生产环境由 ``config_loader._validate_config`` 双重加固:
|
|
76
|
+
(1) 强制 ``AI_ALLOW_FAKE=false``;
|
|
77
|
+
(2) 校验默认 provider 的 api-key 已配置,缺失直接抛 ``ConfigurationError``。
|
|
78
|
+
"""
|
|
79
|
+
return os.environ.get("AI_ALLOW_FAKE", "false").strip().lower() in (
|
|
80
|
+
"true", "1", "yes", "on")
|
|
67
81
|
|
|
68
82
|
|
|
69
83
|
# ==================== 类型化配置 dataclass ====================
|
|
@@ -258,7 +272,7 @@ def _build_chat_model(props: AIProperties, redis_client=None) -> ChatModel:
|
|
|
258
272
|
|
|
259
273
|
if provider == "openai":
|
|
260
274
|
if not props.openai.api_key:
|
|
261
|
-
if not
|
|
275
|
+
if not _ai_allow_fake():
|
|
262
276
|
raise ValueError(
|
|
263
277
|
"AI_ALLOW_FAKE=false 但 spring.ai.openai.api-key 未配置。"
|
|
264
278
|
" 请设置 OPENAI_API_KEY 环境变量或 application.yml 的 api-key。")
|
|
@@ -295,7 +309,7 @@ def _build_chat_model(props: AIProperties, redis_client=None) -> ChatModel:
|
|
|
295
309
|
if provider in _COMPAT_SPECS:
|
|
296
310
|
pname, lc_mod, lc_cls, cfg = _COMPAT_SPECS[provider]
|
|
297
311
|
if not cfg.api_key:
|
|
298
|
-
if not
|
|
312
|
+
if not _ai_allow_fake():
|
|
299
313
|
raise ValueError(
|
|
300
314
|
f"AI_ALLOW_FAKE=false 但 spring.ai.{provider}.api-key 未配置。"
|
|
301
315
|
f" 请设置 {provider.upper()}_API_KEY 环境变量。")
|
|
@@ -309,7 +323,7 @@ def _build_chat_model(props: AIProperties, redis_client=None) -> ChatModel:
|
|
|
309
323
|
)
|
|
310
324
|
|
|
311
325
|
logger.warning("未知 AI provider: %s", provider)
|
|
312
|
-
if not
|
|
326
|
+
if not _ai_allow_fake():
|
|
313
327
|
raise ValueError(
|
|
314
328
|
f"AI_ALLOW_FAKE=false 但未知 provider: {provider}。"
|
|
315
329
|
" 请检查 application.yml 的 spring.ai.default-provider 配置。")
|
|
@@ -323,7 +337,7 @@ def _build_embedding_model(props: AIProperties, redis_client=None):
|
|
|
323
337
|
|
|
324
338
|
if provider == "openai":
|
|
325
339
|
if not props.openai.api_key:
|
|
326
|
-
if not
|
|
340
|
+
if not _ai_allow_fake():
|
|
327
341
|
raise ValueError(
|
|
328
342
|
"AI_ALLOW_FAKE=false 但 Embedding 未配置 api-key。"
|
|
329
343
|
" 请设置 OPENAI_API_KEY 环境变量。")
|
|
@@ -351,7 +365,7 @@ def _build_embedding_model(props: AIProperties, redis_client=None):
|
|
|
351
365
|
if provider in ("deepseek", "moonshot", "zhipu"):
|
|
352
366
|
cfg = getattr(props, provider)
|
|
353
367
|
if not cfg.api_key:
|
|
354
|
-
if not
|
|
368
|
+
if not _ai_allow_fake():
|
|
355
369
|
raise ValueError(
|
|
356
370
|
f"AI_ALLOW_FAKE=false 但 Embedding 未配置 {provider} api-key。")
|
|
357
371
|
logger.warning("Embedding 未配置 %s api-key,降级 FakeEmbeddingModel",
|
|
@@ -364,7 +378,7 @@ def _build_embedding_model(props: AIProperties, redis_client=None):
|
|
|
364
378
|
circuit_breaker=cb,
|
|
365
379
|
)
|
|
366
380
|
|
|
367
|
-
if not
|
|
381
|
+
if not _ai_allow_fake():
|
|
368
382
|
raise ValueError(
|
|
369
383
|
"AI_ALLOW_FAKE=false 但未知 Embedding provider。"
|
|
370
384
|
" 请检查 application.yml 的 spring.ai.default-provider 配置。")
|
|
@@ -178,16 +178,18 @@ class LoadBalanced(SpringAnnotation):
|
|
|
178
178
|
|
|
179
179
|
class GlobalTransactional(SpringAnnotation):
|
|
180
180
|
"""
|
|
181
|
-
Seata
|
|
182
|
-
|
|
181
|
+
Seata 分布式事务注解。
|
|
182
|
+
|
|
183
|
+
仅事务发起入口方法添加。distributed 模式使用官方 Java 客户端桥接和
|
|
184
|
+
Seata TCC;参与服务通过 ``register_branch`` 注册业务分支。
|
|
183
185
|
|
|
184
186
|
使用注意:
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
187
|
+
- 必须配置 Seata Server、bridge、共享令牌和事务组
|
|
188
|
+
- TCC 分支必须实现持久化资源预留和幂等的 prepare/commit/rollback
|
|
189
|
+
- 回调要处理重复提交、空回滚和悬挂,不能只修改进程内变量
|
|
188
190
|
- 不支持嵌套事务
|
|
189
|
-
-
|
|
190
|
-
-
|
|
191
|
+
- 业务方法抛出异常时会触发全局回滚
|
|
192
|
+
- 不要把 ContextVar 事务上下文直接复制到独立后台任务中继续使用
|
|
191
193
|
"""
|
|
192
194
|
_annotation_type = "aop"
|
|
193
195
|
|
|
@@ -140,16 +140,27 @@ def global_transactional_decorator(annotation: GlobalTransactional):
|
|
|
140
140
|
if seata_manager.is_in_transaction():
|
|
141
141
|
logger.warning("[GlobalTransactional] Nested transaction detected, skipping")
|
|
142
142
|
return await func(*args, **kwargs)
|
|
143
|
-
|
|
143
|
+
mode = seata_manager.get_mode()
|
|
144
|
+
if mode in {"http", "distributed"}:
|
|
144
145
|
tx_id = await asyncio.to_thread(begin_transaction)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if transaction_context is None:
|
|
149
|
-
seata_manager._cleanup_context()
|
|
150
|
-
raise RuntimeError(
|
|
151
|
-
f"Unable to bind durable HTTP transaction context: {tx_id}"
|
|
146
|
+
if mode == "http":
|
|
147
|
+
transaction_context = await asyncio.to_thread(
|
|
148
|
+
seata_manager.get_transaction_context, tx_id
|
|
152
149
|
)
|
|
150
|
+
if transaction_context is None:
|
|
151
|
+
seata_manager._cleanup_context()
|
|
152
|
+
raise RuntimeError(
|
|
153
|
+
f"Unable to bind durable HTTP transaction context: {tx_id}"
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
transaction_context = {
|
|
157
|
+
'in_transaction': True,
|
|
158
|
+
'tx_id': tx_id,
|
|
159
|
+
'status': 'Begin',
|
|
160
|
+
'timeout': annotation.timeout,
|
|
161
|
+
'start_time': time.time(),
|
|
162
|
+
'name': annotation.name or func.__name__,
|
|
163
|
+
}
|
|
153
164
|
seata_manager.bind_transaction_context(transaction_context)
|
|
154
165
|
else:
|
|
155
166
|
tx_id = begin_transaction()
|
|
@@ -19,6 +19,7 @@ from spring.cloud.seata import (
|
|
|
19
19
|
BranchStatus,
|
|
20
20
|
)
|
|
21
21
|
from spring.cloud.transaction_store import SQLiteTransactionStore
|
|
22
|
+
from spring.cloud.seata_bridge import SeataBridgeClient, SeataBridgeError
|
|
22
23
|
from spring.cloud.sentinel import (
|
|
23
24
|
sentinel_engine,
|
|
24
25
|
SentinelEngine,
|
|
@@ -64,7 +65,7 @@ __all__ = [
|
|
|
64
65
|
'FeignClientProxy', 'FeignClientFactory', 'create_feign_client', 'create_declared_feign_client',
|
|
65
66
|
# 分布式事务
|
|
66
67
|
'seata_manager', 'init_seata', 'SeataTransactionManager', 'BranchStatus',
|
|
67
|
-
'SQLiteTransactionStore',
|
|
68
|
+
'SQLiteTransactionStore', 'SeataBridgeClient', 'SeataBridgeError',
|
|
68
69
|
# 熔断限流
|
|
69
70
|
'sentinel_engine', 'SentinelEngine', 'FlowRule', 'DegradeRule',
|
|
70
71
|
'SystemRule', 'HotParamRule', 'BlockException', 'sentinel_protect',
|
|
@@ -198,6 +198,7 @@ class GatewayRouter:
|
|
|
198
198
|
|
|
199
199
|
def __init__(self, discovery_client=None, default_filters: List[GatewayFilter] = None,
|
|
200
200
|
timeout: float = 10.0, max_body_size: int = 10 * 1024 * 1024,
|
|
201
|
+
max_response_size: int = 50 * 1024 * 1024,
|
|
201
202
|
transport: Optional[httpx.AsyncBaseTransport] = None):
|
|
202
203
|
self.discovery = discovery_client
|
|
203
204
|
self.routes: List[Route] = []
|
|
@@ -210,6 +211,9 @@ class GatewayRouter:
|
|
|
210
211
|
self._path_pattern_cache: Dict[str, re.Pattern] = {}
|
|
211
212
|
self.timeout = float(timeout)
|
|
212
213
|
self.max_body_size = int(max_body_size)
|
|
214
|
+
# 上游响应大小上限(字节):防止大文件下载/并发请求整体载入内存耗尽 worker。
|
|
215
|
+
# 旧版本直接 upstream.content 整体载入,无大小限制。0 表示禁用限制(不推荐)。
|
|
216
|
+
self.max_response_size = int(max_response_size)
|
|
213
217
|
self._transport = transport
|
|
214
218
|
self._client: Optional[httpx.AsyncClient] = None
|
|
215
219
|
self._client_lock = threading.Lock()
|
|
@@ -410,33 +414,102 @@ class GatewayRouter:
|
|
|
410
414
|
}
|
|
411
415
|
try:
|
|
412
416
|
target_url = target_uri + forward_path
|
|
413
|
-
|
|
417
|
+
# 使用 stream=True 避免上游响应整体载入内存:
|
|
418
|
+
# 旧版本 upstream.content 一次性载入全部响应体,大文件下载或并发请求
|
|
419
|
+
# 可能耗尽 worker 内存。流式读取配合 max_response_size 可在超限时提前中止。
|
|
420
|
+
req = self._get_client().build_request(
|
|
414
421
|
method,
|
|
415
422
|
target_url,
|
|
416
423
|
params=query_items,
|
|
417
424
|
headers=request_headers,
|
|
418
425
|
content=body,
|
|
419
426
|
)
|
|
427
|
+
upstream = await self._get_client().send(req, stream=True)
|
|
420
428
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
429
|
+
try:
|
|
430
|
+
ctx.response_status = upstream.status_code
|
|
431
|
+
response_headers = {
|
|
432
|
+
key: value for key, value in upstream.headers.items()
|
|
433
|
+
if key.lower() not in self._HOP_BY_HOP_HEADERS | {'content-length'}
|
|
434
|
+
}
|
|
435
|
+
ctx.response_headers.update(response_headers)
|
|
436
|
+
|
|
437
|
+
# 执行后置过滤器
|
|
438
|
+
for flt in self.filters:
|
|
439
|
+
try:
|
|
440
|
+
await self._run_filter(flt, 'post_filter', ctx)
|
|
441
|
+
except Exception:
|
|
442
|
+
logger.exception("[Gateway] post filter failed")
|
|
443
|
+
|
|
444
|
+
# 响应大小限制:
|
|
445
|
+
# (1) 先检查 Content-Length 头(快速路径,无需读取响应体即可拒绝)
|
|
446
|
+
# (2) 流式读取时累计字节数,超限立即中止并返回 502
|
|
447
|
+
if self.max_response_size > 0:
|
|
448
|
+
content_length = upstream.headers.get('content-length')
|
|
449
|
+
if content_length:
|
|
450
|
+
try:
|
|
451
|
+
if int(content_length) > self.max_response_size:
|
|
452
|
+
logger.warning(
|
|
453
|
+
"[Gateway] Upstream response Content-Length %s exceeds limit %d",
|
|
454
|
+
content_length, self.max_response_size,
|
|
455
|
+
)
|
|
456
|
+
return JSONResponse(
|
|
457
|
+
{"error": "Upstream response too large",
|
|
458
|
+
"limit_bytes": self.max_response_size},
|
|
459
|
+
status_code=502,
|
|
460
|
+
)
|
|
461
|
+
except ValueError:
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
# is_stream_consumed=True 表示响应体已预载(如 MockTransport 或
|
|
465
|
+
# 非 stream 模式);此时 aiter_raw() 会抛 StreamConsumed,
|
|
466
|
+
# 直接用 .content 检查大小即可。
|
|
467
|
+
if upstream.is_stream_consumed:
|
|
468
|
+
response_body = upstream.content
|
|
469
|
+
if len(response_body) > self.max_response_size:
|
|
470
|
+
logger.warning(
|
|
471
|
+
"[Gateway] Upstream response %d bytes exceeds limit %d",
|
|
472
|
+
len(response_body), self.max_response_size,
|
|
473
|
+
)
|
|
474
|
+
return JSONResponse(
|
|
475
|
+
{"error": "Upstream response too large",
|
|
476
|
+
"limit_bytes": self.max_response_size},
|
|
477
|
+
status_code=502,
|
|
478
|
+
)
|
|
479
|
+
else:
|
|
480
|
+
chunks = []
|
|
481
|
+
response_size = 0
|
|
482
|
+
too_large = False
|
|
483
|
+
async for chunk in upstream.aiter_raw():
|
|
484
|
+
response_size += len(chunk)
|
|
485
|
+
if response_size > self.max_response_size:
|
|
486
|
+
logger.warning(
|
|
487
|
+
"[Gateway] Upstream response exceeded %d bytes (got %d+), aborting",
|
|
488
|
+
self.max_response_size, response_size,
|
|
489
|
+
)
|
|
490
|
+
too_large = True
|
|
491
|
+
break
|
|
492
|
+
chunks.append(chunk)
|
|
493
|
+
|
|
494
|
+
if too_large:
|
|
495
|
+
return JSONResponse(
|
|
496
|
+
{"error": "Upstream response too large",
|
|
497
|
+
"limit_bytes": self.max_response_size},
|
|
498
|
+
status_code=502,
|
|
499
|
+
)
|
|
500
|
+
response_body = b''.join(chunks)
|
|
501
|
+
else:
|
|
502
|
+
# 无限制:直接读取完整响应(向后兼容,不推荐在生产使用)
|
|
503
|
+
response_body = await upstream.aread()
|
|
504
|
+
|
|
505
|
+
return Response(
|
|
506
|
+
content=response_body,
|
|
507
|
+
status_code=upstream.status_code,
|
|
508
|
+
headers=ctx.response_headers,
|
|
509
|
+
)
|
|
510
|
+
finally:
|
|
511
|
+
# stream=True 模式下必须显式关闭响应,否则连接不会归还连接池
|
|
512
|
+
await upstream.aclose()
|
|
440
513
|
except httpx.TimeoutException as exc:
|
|
441
514
|
logger.warning(f"[Gateway] Upstream timeout: {exc}")
|
|
442
515
|
return JSONResponse({"error": "Gateway timeout"}, status_code=504)
|