## Context

当前项目是一个单机运行的小红书 AI 爆文自动化工具，使用 Gradio 作为 UI 框架，SQLite 为发布队列持久化，JSON 文件存储配置与分析数据。随着功能迭代至 V2.0，主文件 `main.py` 已超过 4400 行，并积累了多处生产风险：API Key 以明文写入 `config.json`、全局列表变量被多个回调线程无锁读写、`_temp_publish/` 目录的临时图片在发布后未清理、JSON 文件采用直接覆盖写入（电量耗尽或进程中断时会写出空文件）、发布前无标题/正文/图片数量的校验。

## Goals / Non-Goals

**Goals:**
- 消除 6 项已知的生产风险，使项目能安全、稳定地长期自动运营
- 保持所有 Gradio 回调函数签名不变（不破坏现有 UI 绑定）
- 每项改动独立可部署，不相互耦合
- 为后续功能拓展提供更清晰的代码结构基础

**Non-Goals:**
- 重写为 Web 服务或引入数据库 ORM
- 改变现有 UI 交互逻辑或视觉设计
- 实现性能优化或多账号支持
- 修改 MCP / SD / LLM 服务接口

## Decisions

### 决策 1：敏感配置存储方案选 keyring + 环境变量覆盖，放弃纯加密文件

**选项 A（选定）**: 使用 `keyring` 库将 API Key 存入系统凭证管理器（Windows Credential Manager / macOS Keychain / Linux Secret Service），`config.json` 中仅保留占位符 `"[keyring]"`；同时支持 `AUTOBOT_API_KEY_<NAME>` 环境变量在无 GUI 场景（Docker / CI）下覆盖。

**选项 B（放弃）**: 自行用 `Fernet` 加密后写入 `config.json`。缺点：密钥仍需本地存储，安全提升有限，且增加密钥管理复杂度。

**选项 C（放弃）**: 要求用户全部改用环境变量。对当前 Gradio UI 用户体验极差，用户每次重启需重新设置。

**结论**：`keyring` 方案对 Windows 单机用户最为透明，且 Docker 场景通过环境变量无缝降级。新增 `ConfigManager.get_secure(key)` / `set_secure(key, value)` 接口，内部优先读取环境变量，其次读 keyring，最后回退旧版明文（自动迁移一次）。

---

### 决策 2：全局缓存改用模块级 `threading.RLock` 保护，不引入新类

**选项 A（选定）**: 在 `main.py` 模块顶层声明一个 `_cache_lock = threading.RLock()`，所有读写 `_cached_proactive_entries` / `_cached_my_note_entries` 的函数用 `with _cache_lock:` 包裹。

**选项 B（放弃）**: 封装为 `CacheManager` 类。当前代码耦合 Gradio UI 较深，引入类会导致较大重构，收益不成比例。

**结论**：最小侵入方案，改动 5 处函数约 20 行，可在一个 PR 内完成。

---

### 决策 3：JSON 原子写使用 tempfile + os.replace

Python 标准库 `tempfile.NamedTemporaryFile` + `os.replace`（同目录）在 POSIX 和 Windows 均为原子操作（Windows Vista+ 支持）。无需引入新依赖。

适用范围：`ConfigManager.save()`、`AnalyticsService._save_analytics()`、`AnalyticsService._save_weights()`。

---

### 决策 4：临时文件清理在发布回调内同步执行

在 `publish_to_xhs()` 函数的 try/finally 块中清理 `_temp_publish/` 下以本次调用 `ai_N.jpg` 命名的文件。不使用全目录清空，以免并发发布时误删其他会话文件（Gradio 多用户虽少见，但更安全）。逐文件删除，删除失败仅打印 warning，不阻断流程。

---

### 决策 5：发布校验集中在 `publish_to_xhs()` 一处，不分散到 UI

校验逻辑写在业务函数中，而非 Gradio 的 `gr.Textbox` 校验器，保证逻辑可测试，且 UI 重构时不会遗失。

---

### 决策 6：UI 拆分采用渐进式迁移，不一次性重写

以 `ui/` 目录为目标，先提取最大的 Tab（内容创作 Tab）为 `ui/tab_create.py`，返回 `(components, callbacks)` 元组，`main.py` 调用并注册。其余 Tab 在后续迭代中逐步迁移。本次变更只迁移 `ui/tab_create.py` 作为示范，不强制完成全部拆分。

## Risks / Trade-offs

| 风险 | 缓解措施 |
|------|---------|
| `keyring` 在部分 Linux headless 环境无后端可用 | 检测到 `keyring.errors.NoKeyringError` 时降级为明文（打印警告），行为与改造前一致 |
| `os.replace` 在跨卷（不同磁盘分区）时失败 | 使用 `tempfile.mkstemp(dir=same_dir)` 确保临时文件与目标同卷 |
| 并发发布时 `ai_N.jpg` 命名冲突 | 当前 Gradio 为单进程单用户；若未来支持多用户，改用 UUID 命名 |
| UI 拆分期间双重维护 | 每次只迁移一个 Tab，迁移完成前旧代码仍有效 |

## Migration Plan

1. **无需数据迁移**：`config.json` 的旧明文 Key 在首次调用 `get_secure()` 时自动读取并写入 keyring，同时将 `config.json` 中对应字段替换为 `"[keyring]"` 占位符，单次完成。
2. **依赖安装**：`pip install keyring` 并更新 `requirements.txt`。
3. **无回滚风险**：各项改动均向后兼容，若 keyring 不可用则自动回退明文模式。

## Open Questions

- 是否需要在 UI 上增加「导出加密备份配置」功能，方便用户迁移设备？（本次范围外，记录待后续评估）
- `ui/` 拆分后是否需要引入 pytest-gradio 进行 UI 层单元测试？（本次不实施）
