Compare commits

...

1 Commits

Author SHA1 Message Date
xiaoxia-saas-bot f9a8757c92 fix(config): system_config_service 运行时读取 SessionLocal 修复后台配置接口500 #2246
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 6s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m3s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m14s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m35s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m1s
AI Code Review / AI Code Review (pull_request) Successful in 8m36s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 11m8s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 11m41s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 11m52s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 13m47s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m48s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 33s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m46s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 31m14s
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
模块 import 时 session.SessionLocal 还是 None,initialize_database 之后才赋值;
原写法在 import 时绑定到旧 None,导致 GET /api/v1/admin/ditto-emotion/config
抛 RuntimeError(数据库会话工厂未初始化) 返回 500。

改为 import 模块、运行时读取 session.SessionLocal,并补 2 个回归用例
(启动顺序绑定 / 无工厂抛错),test_system_config 29 passed。
2026-10-08 22:42:34 +08:00
2 changed files with 35 additions and 2 deletions
@@ -12,7 +12,7 @@ import logging
import threading
from typing import Any
from packages.adapters.sqlalchemy_impl.session import SessionLocal
from packages.adapters.sqlalchemy_impl import session as db_session
from packages.adapters.sqlalchemy_impl.system_setting_repository import (
SQLAlchemySystemSettingRepository,
)
@@ -35,7 +35,9 @@ class SystemConfigService:
# ── 会话 ─────────────────────────────────────────────────────
def _get_session_factory(self):
factory = self._session_factory or SessionLocal
# 必须运行时读取模块属性:模块导入时 SessionLocal 还是 None,
# initialize_database() 之后才被赋值,import 时绑定会拿到旧值。
factory = self._session_factory or db_session.SessionLocal
if factory is None:
raise RuntimeError("数据库会话工厂未初始化")
return factory
+31
View File
@@ -91,6 +91,37 @@ class TestConfigService:
def test_default_when_missing(self, config_service):
assert config_service.get_config("not_exist", "fallback") == "fallback"
def test_uses_module_session_factory_assigned_after_import(self):
# 回归:模块导入时 session.SessionLocal 为 None,initialize_database()
# 之后才赋值;服务必须运行时读取模块属性,而不是 import 时绑定旧值。
from packages.adapters.sqlalchemy_impl import session as db_session
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
factory = sessionmaker(bind=engine)
writer = SystemConfigService(session_factory=factory)
writer.set_config("late_key", 7, setting_type=SETTING_TYPE_INT)
reader = SystemConfigService() # 不注入工厂,依赖模块级 SessionLocal
old = db_session.SessionLocal
try:
db_session.SessionLocal = factory
assert reader.get_config("late_key", 0) == 7
finally:
db_session.SessionLocal = old
def test_raises_when_no_session_factory(self):
from packages.adapters.sqlalchemy_impl import session as db_session
svc = SystemConfigService()
old = db_session.SessionLocal
try:
db_session.SessionLocal = None
with pytest.raises(RuntimeError):
svc.get_config("anything", 1)
finally:
db_session.SessionLocal = old
def test_db_overrides_default(self, config_service):
config_service.set_config("k", 20, setting_type=SETTING_TYPE_INT)
# 再次读取应命中 DB 值,而非传入的默认