You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.6 KiB
4.6 KiB
| comet_change | role | canonical_spec |
|---|---|---|
| config-to-mysql | technical-design | openspec |
Config to MySQL - Technical Design
1. 架构概览
1.1 目标架构
业务代码 / API
↓
app/config_store.py (统一入口)
↓
MySQL 可用?
↓ 是 ↓ 否
读/写 app_config 读/写 JSON 文件
1.2 核心组件
| 组件 | 职责 |
|---|---|
AppConfig 模型 |
MySQL 配置表 ORM 映射 |
ConfigStore |
统一配置读写入口,封装降级逻辑 |
config_migration.py |
JSON → MySQL 一次性迁移 |
| JSON 文件 | MySQL 不可用时降级使用 |
2. 数据模型
2.1 AppConfig 表
class AppConfig(Base):
__tablename__ = "app_config"
id = Column(Integer, primary_key=True, autoincrement=True)
config_key = Column(String(64), unique=True, nullable=False, index=True)
config_value = Column(JSON, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
created_at = Column(DateTime, default=datetime.utcnow)
2.2 配置键
| config_key | 对应文件 | 默认值 |
|---|---|---|
symbols |
config/symbols_config.json |
{"futures": {}, "stock": {}} |
ai |
config/ai_config.json |
{"models": [], "active_model": None, "analysis_settings": {...}} |
3. 降级策略
3.1 读取流程
- 检查 MySQL 可用性(复用
StorageManager.check_mysql()) - MySQL 可用:
- 查询
app_config - 命中则返回
- 未命中则读取 JSON 文件并回填数据库
- 查询
- MySQL 不可用:
- 直接读取 JSON 文件
3.2 写入流程
- 检查 MySQL 可用性
- MySQL 可用时先写入
app_config;失败时保留 JSON 旧值并返回 False - MySQL 写入成功后,再原子写入 JSON 文件
- MySQL 不可用时只写入 JSON 文件
3.3 降级检测
复用 StorageManager.check_mysql() 惰性检测,避免每次配置读写都尝试连接数据库。
4. ConfigStore 接口
class ConfigStore:
def get_config(self, key: str, fallback: Optional[dict] = None) -> dict: ...
def set_config(self, key: str, value: dict) -> bool: ...
def load_from_json(self, key: str, fallback: Optional[dict] = None) -> dict: ...
def save_to_json(self, key: str, value: dict) -> None: ...
4.1 单例获取
_config_store = None
def get_config_store() -> ConfigStore:
global _config_store
if _config_store is None:
_config_store = ConfigStore()
return _config_store
5. 数据迁移
5.1 迁移时机
应用启动时(lifespan),在 MySQL 表结构初始化后执行。
5.2 迁移逻辑
def migrate_configs_to_mysql():
for key, file_path, default in CONFIGS:
if _config_exists_in_mysql(key):
continue
value = _load_json(file_path, default)
_save_to_mysql(key, value)
5.3 幂等性
- 检测
app_config是否已有对应 key - 有则跳过
6. 使用方修改
6.1 品种配置读取
替换以下文件中的 _load_symbols_config() / load_symbols_config():
app/api/config.pyapp/api/futures_analysis.pyapp/api/trade_review.pyapp/services/trade_parser.pyapp/services/plan_generator.py
统一使用:
from app.config_store import get_config_store
symbols = get_config_store().get_config("symbols", {"futures": {}, "stock": {}})
6.2 AI 配置读取
替换 app/api/ai_config.py 和 app/services/ai_analysis.py 中的 _load_ai_config():
ai_config = get_config_store().get_config("ai", {...default...})
6.3 写入接口
app/api/config.py 和 app/api/ai_config.py 的保存接口改为:
get_config_store().set_config("symbols", data)
7. 兼容性
- 保持 JSON 文件路径和 schema 不变
- API 接口签名不变
- 默认值与现有 JSON 默认值一致
8. 测试策略
8.1 单元测试
| 场景 | 测试 |
|---|---|
| MySQL 命中 | get_config 从数据库返回值 |
| MySQL 未命中 | get_config 读取 JSON 并回填 |
| MySQL 不可用 | get_config 读取 JSON |
| 写入 | set_config 双写数据库和文件 |
| 迁移幂等 | 数据库已有数据时跳过 |
8.2 集成测试
- 启动应用验证配置表创建
- 验证 API 读写正常
- 运行全部测试套件确保无回归
9. 风险与缓解
| 风险 | 缓解措施 |
|---|---|
| MySQL 不可用时配置无法写入数据库 | 始终同步写入 JSON 文件 |
| 多处修改引入回归 | 统一 ConfigStore 接口 + 全面测试 |
| JSON 文件与数据库不一致 | 写入时双写;读取时优先数据库 |