# Design: Config to MySQL

## 架构决策

### 决策 1：统一配置管理模块

**决策**：创建 `app/config_store.py` 作为所有配置读写的唯一入口。

**理由**：
- 消除多处重复的文件读取逻辑
- 集中管理降级策略
- 便于后续扩展（如缓存、审计）

### 决策 2：单表存储键值对

**决策**：使用单表 `app_config` 存储所有配置，以 `config_key` 区分 `symbols` 和 `ai`。

**表结构**：
```sql
CREATE TABLE app_config (
    id INT PRIMARY KEY AUTO_INCREMENT,
    config_key VARCHAR(64) UNIQUE NOT NULL,  -- 'symbols' 或 'ai'
    config_value JSON NOT NULL,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
```

**理由**：
- 与现有 JSON schema 兼容
- 减少表数量
- 未来新增配置无需改表结构

### 决策 3：MySQL + JSON 文件降级

**决策**：MySQL 可用时优先读写数据库；MySQL 不可用时回退到 JSON 文件。

**理由**：
- 用户明确要求无需 Redis
- JSON 文件作为 fallback 简单可靠
- 与现有配置读取方式兼容

## 数据流

```
业务代码
    ↓
app/config_store.py
    ↓
MySQL 可用？
    ↓ 是              ↓ 否
读/写 app_config   读/写 JSON 文件
```

## 配置键设计

| config_key | 对应文件 | 用途 |
|------------|----------|------|
| `symbols` | `config/symbols_config.json` | 品种合约映射 |
| `ai` | `config/ai_config.json` | AI 模型配置 |

## 降级策略

### 读取流程

1. 检查 MySQL 可用性
2. MySQL 可用则查询 `app_config`
3. 数据库无数据则读取 JSON 文件并回填数据库
4. MySQL 不可用则直接读取 JSON 文件

### 写入流程

1. 检查 MySQL 可用性
2. MySQL 可用则写入 `app_config`
3. 无论数据库是否成功，同步写入 JSON 文件（保持一致性）
4. MySQL 不可用则只写入 JSON 文件

## 兼容性

- 保持 `symbols_config.json` 和 `ai_config.json` 的 schema 不变
- API 接口签名不变
- 文件路径不变，作为 fallback 使用

## 数据迁移

启动时执行一次性迁移：

1. 检测 `app_config` 表是否已有 `symbols` 和 `ai` 配置
2. 缺失则从 JSON 文件读取并写入数据库
3. 迁移幂等：已存在则跳过

## 测试策略

1. 单元测试：
   - `ConfigStore.get_config` 从 MySQL 读取
   - `ConfigStore.get_config` 在 MySQL 不可用时读取 JSON
   - `ConfigStore.set_config` 写入 MySQL 和 JSON
   - 迁移幂等性
2. 集成测试：
   - 启动时自动创建表并迁移
   - API 接口正常工作
