✨ feat(config): 更新模型配置与LLM提示词指南

- 将默认LLM模型从gemini-2.0-flash升级为gemini-3-flash-preview
- 将博主人设从"性感福利主播"更改为"二次元coser"
- 优化LLM生成SD提示词的指南,新增中国审美人物描述规则
- 为各SD模型添加颜值核心词、示范prompt和禁止使用的关键词
- 新增三维人物描述法(眼睛/肤色/气质)和专属光线词指导

📦 build(openspec): 归档旧规范并创建新规范

- 将improve-maintainability规范归档至2026-02-25目录
- 新增2026-02-26-improve-ui-layout规范,包含UI布局优化设计
- 新增2026-02-26-optimize-image-generation规范,包含图片生成优化设计
- 在根目录openspec/specs下新增图片质量、后处理、中国审美和LLM提示词规范

♻️ refactor(sd_service): 优化SD模型配置和图片后处理

- 为各SD模型添加中国审美特征词和欧美面孔排除词
- 新增高画质预设档,SDXL模型启用Hires Fix参数
- 将后处理拆分为beauty_enhance和anti_detect_postprocess两个独立函数
- 新增美化增强功能,支持通过enhance_level参数控制强度

♻️ refactor(services): 更新内容生成服务以支持美化增强

- 在generate_images函数中新增enhance_level参数
- 将美化强度参数传递至SDService.txt2img调用

♻️ refactor(ui): 优化UI布局和添加美化强度控件

- 注入自定义CSS主题层,优化字体、按钮和卡片样式
- 将全局设置迁移至独立的"⚙️ 配置"Tab,优化Tab顺序
- 在内容创作Tab的高级设置中添加美化强度滑块控件
- 优化自动运营Tab布局,改为2列卡片网格展示
This commit is contained in:
2026-02-26 22:58:05 +08:00
parent b635108b89
commit b5deafa2cc
53 changed files with 1578 additions and 387 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-24
@@ -0,0 +1,119 @@
## Context
`main.py` 是整个项目的单一入口,目前 4359 行,包含 10+ 个业务域的业务逻辑、全局状态、UI 组件和事件绑定。上一轮重构已将 Tab 1(内容创作)提取为 `ui/tab_create.py`,建立了 Tab 模块化的模式。本次设计延续该模式,将业务逻辑提取为 `services/` 目录,并完成剩余 Tab 的 UI 拆分。
已有外部服务层:`config_manager.py`、`llm_service.py`、`sd_service.py`、`mcp_client.py`、`analytics_service.py`、`publish_queue.py`。
`main.py` 中剩余的函数是对这些服务的**编排层**,应该独立成模块而非继续膨胀在入口文件里。
约束:
- Python 3.10+(当前环境)
- 不引入新的外部依赖
- 重构不改变任何业务行为
## Goals / Non-Goals
**Goals:**
- 将 `main.py` 瘦身至 ~300 行纯入口层(导入 + UI 组装 + `app.launch()`)
- 建立清晰的分层:`services/`(业务编排)→ `ui/`(Gradio 组件)→ `main.py`(入口)
- 消除模块间隐式依赖,所有依赖通过函数参数显式传递
- 保持所有函数签名、Gradio 回调绑定不变
**Non-Goals:**
- 不重构或重写任何现有业务逻辑(本次是纯搬迁)
- 不改变 `config_manager.py`、`llm_service.py` 等已有服务层
- 不引入类/面向对象重构(保持现有函数式风格)
- 不添加单元测试(独立变更)
## Decisions
### D1:分层架构 —— `services/` 不依赖 `ui/`,`ui/` 不依赖 `services/`
```
main.py (入口层:组装 + 启动)
├── services/ (业务编排层:纯 Python,无 Gradio)
│ ├── connection.py
│ ├── content.py
│ ├── hotspot.py
│ ├── engagement.py
│ ├── rate_limiter.py
│ ├── profile.py
│ ├── persona.py
│ ├── scheduler.py
│ ├── queue_ops.py
│ └── autostart.py
├── ui/ (UI 层:Gradio 组件 + 事件绑定)
│ ├── tab_create.py ← 已存在
│ ├── tab_hotspot.py
│ ├── tab_engage.py
│ ├── tab_profile.py
│ ├── tab_auto.py
│ ├── tab_queue.py
│ ├── tab_analytics.py
│ └── tab_settings.py
```
**为何不让 `ui/` 依赖 `services/`:** 每个 `build_tab()` 接收回调函数作为参数(已有 `tab_create.py` 的模式),而非直接 import service。这样 UI 层完全解耦,可独立测试或替换。
### D2:共享单例通过 `main.py` 初始化,作为参数传入
`cfg`、`mcp`、`analytics`、`pub_queue`、`queue_publisher` 仍在 `main.py` 顶层初始化。Service 函数需要它们时通过**函数参数**接收,不在 service 模块顶层 import。
**为何不在各 service 模块初始化单例:** 防止循环依赖、防止多次初始化、保持测试时可替换。
### D3:有状态模块使用模块级变量(不封装成类)
`rate_limiter`、`scheduler`、`engagement` 内部有 `threading.Event`、`_daily_stats` 等状态。保持现有模块级变量风格(不改成类),仅将变量和函数整体搬迁到对应模块。
**为何不改成类:** 本次目标是结构拆分,不是重构设计模式,避免引入额外变更风险。
### D4:迁移策略 —— 先提取后删除,不做重定向
每个域的迁移步骤:
1. 在 service 模块中写入函数(复制粘贴 + 调整 import)
2. 在 `main.py` 中删除对应函数,改为 `from services.xxx import ...`
3. 运行 `ast.parse()` 验证语法
4. 运行应用验证启动不报错
**为何不做 `main.py` 中的临时 re-export:** 简单场景直接删+导入更清晰,且 Gradio 回调绑定在 `main.py` 中通过变量名引用,只需保证同名变量在作用域内即可。
### D5:UI Tab 模块统一使用 `build_tab(fn_*, ...)` 签名
复用 `tab_create.py` 已建立的模式:
- 每个 `build_tab()` 接收所需的回调函数和共享 Gradio 组件作为参数
- 函数内部创建本 Tab 的所有 Gradio 组件及事件绑定
- 返回 `dict`,包含需要被其他 Tab 或 `app.load()` 引用的组件
### D6:`services/` 和 `ui/` 均需 `__init__.py`
使用空文件标记为 Python 包,与 `ui/__init__.py` 已有做法一致。
## Risks / Trade-offs
- **[风险] 大量 import 调整可能遗漏** → 每个模块完成后执行 `ast.parse()` + 应用启动验证,逐域推进
- **[风险] 全局状态的隐式共享** → 调度器、限流器的模块级变量在模块首次 import 时初始化,Python 模块单例语义保证只初始化一次,行为与当前一致
- **[权衡] `build_tab()` 参数列表长** → 与现有 `tab_create.py` 的做法一致,接受这种显式依赖的冗长性,可在后续变更中引入 dataclass 参数包
## Migration Plan
按以下顺序逐域提取,每步验证后再继续:
1. `services/rate_limiter.py` —— 无外部依赖,最安全的起点
2. `services/autostart.py` —— 独立,平台相关逻辑隔离
3. `services/persona.py` —— 仅依赖 `cfg`
4. `services/connection.py` —— 依赖 `cfg`、`llm_service`、`sd_service`、`mcp_client`
5. `services/profile.py` —— 依赖 `mcp_client`
6. `services/hotspot.py` —— 依赖 `llm_service`、`mcp_client`
7. `services/content.py` —— 依赖多个服务,最复杂
8. `services/engagement.py` —— 依赖 `rate_limiter`、`mcp_client`
9. `services/scheduler.py` —— 依赖 `engagement`、`content`
10. `services/queue_ops.py` —— 依赖 `content`、`pub_queue`
11. `ui/tab_hotspot.py` ~ `ui/tab_settings.py` —— 7 个 Tab UI 拆分
**回滚策略:** 所有修改通过 git 追踪;每个 service 提取为一个独立 commit,任意步骤可 `git revert`。
## Open Questions
- `_auto_log` 列表(被 `engagement` 和 `scheduler` 共同写入)归属哪个模块?
→ 暂定置于 `services/scheduler.py`,`engagement` 接收 `log_fn` 回调参数
- `queue_publisher` 的 callback 注册(`set_publish_callback`)在哪里调用?
→ 保留在 `main.py` 初始化段,callback 函数迁移到 `services/queue_ops.py`
@@ -0,0 +1,38 @@
## Why
`main.py` 目前共 4359 行,将连接管理、内容生成、自动化运营、调度、队列、UI 等 10+ 个业务域全部混入单一文件,导致阅读困难、修改风险高、模块间依赖不清晰。随着功能继续增长,维护成本将持续上升。现在是在文件进一步膨胀前完成结构化拆分的最佳时机。
## What Changes
- 按业务域将 `main.py` 中的函数提取为独立的 `services/` 模块
- 将剩余 UI Tab 提取为独立的 `ui/tab_*.py` 模块(`tab_create.py` 已完成,需继续完成其余 Tab)
- `main.py` 保留为**入口层**:仅负责组装 Gradio UI、注册事件、启动应用
- 所有模块保持向后兼容,不改变对外行为
## Capabilities
### New Capabilities
- `services-connection`: LLM / SD / MCP 连接管理(`connect_llm`、`connect_sd`、`check_mcp_status`、登录相关)
- `services-content`: 内容生成(`generate_copy`、`generate_images`、`publish_to_xhs`、`one_click_export`、face image 上传)
- `services-hotspot`: 热点探测(`search_hotspots`、`analyze_and_suggest`、`generate_from_hotspot`)
- `services-engagement`: 互动自动化(`auto_comment_once`、`auto_like_once`、`auto_favorite_once`、`auto_reply_once` 及对应 `_with_log` 包装)
- `services-rate-limiter`: 频率控制与每日限额(`_reset_daily_stats_if_needed`、`_check_daily_limit`、`_is_in_cooldown` 等)
- `services-profile`: 用户主页解析(`fetch_my_profile`、`_parse_profile_json`、`_parse_count`)
- `services-persona`: 人设管理(`_match_persona_pools`、`get_persona_topics`、`get_persona_keywords`、`on_persona_changed`)
- `services-scheduler`: 自动调度器(`_scheduler_loop`、`start_scheduler`、`stop_scheduler`、`get_scheduler_status`)
- `services-queue`: 内容排期队列(`generate_to_queue`、`queue_*` 系列函数、`_queue_publish_callback`)
- `services-autostart`: 开机自启管理(`enable_autostart`、`disable_autostart`、`toggle_autostart` 等)
- `ui-tabs-split`: 将其余 Gradio Tab(热点、互动、我的主页、自动运营、队列、数据分析、设置)提取为 `ui/tab_*.py`
### Modified Capabilities
(无需求层面变更,仅为实现重构)
## Impact
- **主要受影响文件**:`main.py`(从 4359 行缩减至 ~300 行入口层)
- **新增目录**:`services/`(10 个模块)、`ui/`(8 个 Tab 模块,`tab_create.py` 已存在)
- **依赖关系**:`services/` 模块之间通过函数参数传递依赖,避免循环导入;`main.py` 统一导入并组装
- **无 API 变更**:所有函数签名保持不变,Gradio 回调绑定不受影响
- **运行时影响**:零,重构不改变业务逻辑
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 开机自启管理函数迁移至独立模块
系统 SHALL 将开机自启相关常量和函数从 `main.py` 提取至 `services/autostart.py`,包括:`_APP_NAME`、`_STARTUP_REG_KEY`、`_get_startup_script_path`、`_get_startup_bat_path`、`_create_startup_scripts`、`is_autostart_enabled`、`enable_autostart`、`disable_autostart`、`toggle_autostart`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.autostart import toggle_autostart, is_autostart_enabled`
- **THEN** 函数可正常调用
#### Scenario: Windows 注册表操作行为不变
- **WHEN** `enable_autostart()` 在 Windows 系统上被调用
- **THEN** SHALL 向注册表 `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` 写入启动项,行为与迁移前完全一致
#### Scenario: 非 Windows 平台处理不变
- **WHEN** `enable_autostart()` 在非 Windows 系统上被调用
- **THEN** SHALL 返回与迁移前相同的平台不支持提示信息
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 连接管理函数迁移至独立模块
系统 SHALL 将所有 LLM / SD / MCP 连接管理及认证相关函数从 `main.py` 提取至 `services/connection.py`,包括:`_get_llm_config`、`connect_llm`、`add_llm_provider`、`remove_llm_provider`、`on_provider_selected`、`connect_sd`、`on_sd_model_change`、`check_mcp_status`、`get_login_qrcode`、`logout_xhs`、`_auto_fetch_xsec_token`、`check_login`、`save_my_user_id`、`upload_face_image`、`load_saved_face_image`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.connection import connect_llm, connect_sd` 等导入
- **THEN** 所有函数可正常调用,行为与迁移前完全一致
#### Scenario: 外部依赖通过参数传入
- **WHEN** `services/connection.py` 中的函数需要访问 `cfg`、`llm`(`LLMService`)、`sd`(`SDService`)、`mcp`(`MCPClient`)
- **THEN** 这些依赖 SHALL 通过函数参数接收,`services/connection.py` 模块顶层不创建单例实例
#### Scenario: 无循环导入
- **WHEN** Python 解释器加载 `services/connection.py`
- **THEN** 不产生 `ImportError` 或循环导入错误
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 内容生成函数迁移至独立模块
系统 SHALL 将内容生成、图片生成、发布及导出相关函数从 `main.py` 提取至 `services/content.py`,包括:`generate_copy`、`generate_images`、`one_click_export`、`publish_to_xhs`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.content import generate_copy, generate_images, publish_to_xhs, one_click_export`
- **THEN** 所有函数可正常调用,行为与迁移前完全一致
#### Scenario: 内容生成保留现有验证逻辑
- **WHEN** 调用 `publish_to_xhs` 时标题超过 20 字或图片数量不合法
- **THEN** 函数 SHALL 返回与迁移前相同的错误提示,不改变验证行为
#### Scenario: 临时文件清理逻辑保留
- **WHEN** `publish_to_xhs` 执行完毕(成功或失败)
- **THEN** `finally` 块中的 AI 临时文件清理逻辑 SHALL 正常执行
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 互动自动化函数迁移至独立模块
系统 SHALL 将评论、点赞、收藏、回复等互动自动化函数从 `main.py` 提取至 `services/engagement.py`,包括:`load_note_for_comment`、`ai_generate_comment`、`send_comment`、`fetch_my_notes`、`on_my_note_selected`、`fetch_my_note_comments`、`ai_reply_comment`、`send_reply`、`auto_comment_once`、`_auto_comment_with_log`、`auto_like_once`、`_auto_like_with_log`、`auto_favorite_once`、`_auto_favorite_with_log`、`auto_reply_once`、`_auto_reply_with_log`、`_auto_publish_with_log`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.engagement import auto_comment_once, auto_like_once` 等导入
- **THEN** 所有函数可正常调用
#### Scenario: 日志回调参数化
- **WHEN** `engagement.py` 中的 `_with_log` 函数需要追加日志时
- **THEN** 函数 SHALL 接收 `log_fn` 参数(callable)用于写入日志,不直接依赖外部 `_auto_log` 列表
#### Scenario: 频率限制集成
- **WHEN** `auto_comment_once` 等函数执行前需要检查每日限额和冷却状态
- **THEN** 通过调用 `rate_limiter` 模块中的函数实现,不在 `engagement.py` 内复制限流逻辑
@@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: 热点探测函数迁移至独立模块
系统 SHALL 将热点搜索与分析相关函数从 `main.py` 提取至 `services/hotspot.py`,包括:`search_hotspots`、`analyze_and_suggest`、`generate_from_hotspot`、`_set_cache`、`_get_cache`、`_fetch_and_cache`、`_pick_from_cache`、`fetch_proactive_notes`、`on_proactive_note_selected`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.hotspot import search_hotspots, analyze_and_suggest` 等导入
- **THEN** 所有函数可正常调用
#### Scenario: 线程安全缓存随模块迁移
- **WHEN** `_cache_lock`(`threading.RLock`)随函数一起迁移至 `services/hotspot.py`
- **THEN** `_set_cache` / `_get_cache` 的线程安全行为保持不变
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 人设管理函数及常量迁移至独立模块
系统 SHALL 将人设相关的常量和函数从 `main.py` 提取至 `services/persona.py`,包括:`DEFAULT_PERSONAS`、`RANDOM_PERSONA_LABEL`、`PERSONA_POOL_MAP`、`DEFAULT_TOPICS`、`DEFAULT_STYLES`、`DEFAULT_COMMENT_KEYWORDS`、`_match_persona_pools`、`get_persona_topics`、`get_persona_keywords`、`on_persona_changed`、`_resolve_persona`。
#### Scenario: 常量可从模块导入
- **WHEN** `main.py` 执行 `from services.persona import DEFAULT_PERSONAS, PERSONA_POOL_MAP`
- **THEN** 常量值 SHALL 与迁移前完全一致
#### Scenario: 人设解析正确处理随机人设标签
- **WHEN** `_resolve_persona(RANDOM_PERSONA_LABEL)` 被调用
- **THEN** SHALL 返回从人设池中随机选取的人设文本,行为与迁移前一致
#### Scenario: 人设变更回调正常触发
- **WHEN** `on_persona_changed(persona_text)` 被调用
- **THEN** SHALL 返回更新后的话题列表和关键词列表,供 Gradio UI 使用
@@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: 用户主页解析函数迁移至独立模块
系统 SHALL 将用户主页数据获取与解析相关函数从 `main.py` 提取至 `services/profile.py`,包括:`_parse_profile_json`、`_parse_count`、`fetch_my_profile`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.profile import fetch_my_profile`
- **THEN** 函数可正常调用,行为与迁移前一致
#### Scenario: 解析容错性保留
- **WHEN** `_parse_count` 接收到格式异常的数值字符串(如 "1.2万"、"--")
- **THEN** SHALL 返回与迁移前相同的浮点数或 0,不抛出异常
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 排期队列操作函数迁移至独立模块
系统 SHALL 将内容排期队列相关函数从 `main.py` 提取至 `services/queue_ops.py`,包括:`generate_to_queue`、`_queue_publish_callback`、`queue_refresh_table`、`queue_refresh_calendar`、`queue_preview_item`、`queue_approve_item`、`queue_reject_item`、`queue_delete_item`、`queue_retry_item`、`queue_publish_now`、`queue_start_processor`、`queue_stop_processor`、`queue_get_status`、`queue_batch_approve`、`queue_generate_and_refresh`。
#### Scenario: 模块导入成功
- **WHEN** `main.py` 执行 `from services.queue_ops import queue_generate_and_refresh, queue_refresh_table` 等导入
- **THEN** 所有函数可正常调用
#### Scenario: publish callback 在 main.py 完成注册
- **WHEN** 应用启动时 `main.py` 调用 `pub_queue.set_publish_callback(_queue_publish_callback)`(`_queue_publish_callback` 已迁移至 `queue_ops.py`)
- **THEN** 队列发布回调 SHALL 正常注册并在队列处理时触发
#### Scenario: 队列操作读写 pub_queue 单例
- **WHEN** `queue_ops.py` 中的函数需要访问 `pub_queue` 或 `queue_publisher`
- **THEN** 这些单例 SHALL 通过函数参数传入,不在 `queue_ops.py` 模块顶层初始化
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 频率控制与每日限额函数迁移至独立模块
系统 SHALL 将频率控制、每日限额及冷却相关的所有状态变量和函数从 `main.py` 提取至 `services/rate_limiter.py`,包括:`_auto_running`、`_op_history`、`_daily_stats`、`DAILY_LIMITS`、`_consecutive_errors`、`_error_cooldown_until`、`_reset_daily_stats_if_needed`、`_check_daily_limit`、`_increment_stat`、`_record_error`、`_clear_error_streak`、`_is_in_cooldown`、`_is_in_operating_hours`、`_get_stats_summary`。
#### Scenario: 模块级状态初始化一次
- **WHEN** Python 首次导入 `services/rate_limiter.py`
- **THEN** `_daily_stats`、`_op_history` 等模块级变量 SHALL 仅初始化一次(Python 模块单例语义)
#### Scenario: 每日限额检查正常工作
- **WHEN** `_check_daily_limit("comment")` 被调用
- **THEN** 返回值 SHALL 与迁移前行为完全一致
#### Scenario: 运营时段限制正常工作
- **WHEN** 当前时间不在 `start_hour` 至 `end_hour` 范围内时调用 `_is_in_operating_hours`
- **THEN** 返回 `False`,阻止自动化操作执行
@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 自动调度器函数迁移至独立模块
系统 SHALL 将调度器相关的状态变量和函数从 `main.py` 提取至 `services/scheduler.py`,包括:`_scheduler_next_times`、`_auto_log`(列表)、`_auto_log_append`、`_scheduler_loop`、`start_scheduler`、`stop_scheduler`、`get_auto_log`、`get_scheduler_status`、`_learn_running`、`_learn_scheduler_loop`、`start_learn_scheduler`、`stop_learn_scheduler`。
#### Scenario: 调度器启停正常工作
- **WHEN** `start_scheduler(...)` 被调用并传入合法参数
- **THEN** 调度器线程 SHALL 正常启动,`get_scheduler_status()` 返回运行中状态
#### Scenario: 日志追加线程安全
- **WHEN** 多个自动化任务并发调用 `_auto_log_append(msg)`
- **THEN** 日志条目 SHALL 正确追加,不丢失和乱序
#### Scenario: engagement 通过回调写日志
- **WHEN** `services/engagement.py` 中的函数需要写日志时
- **THEN** SHALL 通过 `log_fn` 参数(由 `scheduler.py` 传入 `_auto_log_append`)写入,不直接导入 `scheduler.py`
@@ -0,0 +1,30 @@
## ADDED Requirements
### Requirement: 剩余 Gradio Tab 提取为独立 UI 模块
系统 SHALL 将 `main.py` 中除 Tab 1(已完成)之外的 7 个 Gradio Tab 各自提取为 `ui/tab_*.py` 模块文件,每个文件暴露 `build_tab(...)` 函数:
| 模块文件 | Tab 名称 |
|---|---|
| `ui/tab_hotspot.py` | 🔥 热点探测 |
| `ui/tab_engage.py` | 💬 互动运营 |
| `ui/tab_profile.py` | 👤 我的主页 |
| `ui/tab_auto.py` | 🤖 自动运营 |
| `ui/tab_queue.py` | 📅 内容排期 |
| `ui/tab_analytics.py` | 📊 数据分析 |
| `ui/tab_settings.py` | ⚙️ 系统设置 |
#### Scenario: 每个 Tab 模块暴露 build_tab 函数
- **WHEN** `main.py` 执行 `from ui.tab_hotspot import build_tab as build_tab_hotspot`
- **THEN** 调用 `build_tab_hotspot(fn_*, ...)` 后 SHALL 返回包含需跨 Tab 共享组件的 dict
#### Scenario: build_tab 接收回调而非直接导入 services
- **WHEN** `build_tab(...)` 内部需要调用业务函数时
- **THEN** 业务函数 SHALL 通过 `fn_*` 参数传入(与 `tab_create.py` 已有模式一致),不在 `ui/tab_*.py` 内直接 `import services.*`
#### Scenario: 事件绑定在 build_tab 内完成
- **WHEN** `build_tab(...)` 被调用
- **THEN** 本 Tab 所有 Gradio 组件的 `.click()`、`.change()` 等事件绑定 SHALL 在函数内完成,`main.py` 不保留本 Tab 的事件绑定代码
#### Scenario: main.py 成为纯入口层
- **WHEN** 所有 11 个 capability 均完成迁移后
- **THEN** `main.py` 行数 SHALL 不超过 400 行,且不包含任何业务逻辑(仅含导入、单例初始化、UI 组装、`app.launch()`)
@@ -0,0 +1,96 @@
## 1. 基础结构搭建
- [x] 1.1 创建 `services/` 目录及 `services/__init__.py` 空文件
- [x] 1.2 确认 `ui/__init__.py` 已存在(上一轮已创建)
## 2. 迁移 services/rate_limiter.py
- [x] 2.1 创建 `services/rate_limiter.py`,迁移 `_auto_running`、`_op_history`、`_daily_stats`、`DAILY_LIMITS`、`_consecutive_errors`、`_error_cooldown_until` 等模块级变量
- [x] 2.2 迁移函数:`_reset_daily_stats_if_needed`、`_check_daily_limit`、`_increment_stat`、`_record_error`、`_clear_error_streak`、`_is_in_cooldown`、`_is_in_operating_hours`、`_get_stats_summary`
- [x] 2.3 在 `main.py` 中删除对应变量和函数,添加 `from services.rate_limiter import ...`
- [x] 2.4 运行 `ast.parse()` 验证 `main.py` 和 `services/rate_limiter.py` 语法正确
## 3. 迁移 services/autostart.py
- [x] 3.1 创建 `services/autostart.py`,迁移 `_APP_NAME`、`_STARTUP_REG_KEY` 及所有 autostart 函数(`_get_startup_script_path`、`_get_startup_bat_path`、`_create_startup_scripts`、`is_autostart_enabled`、`enable_autostart`、`disable_autostart`、`toggle_autostart`)
- [x] 3.2 在 `main.py` 中删除对应代码,添加 `from services.autostart import ...`
- [x] 3.3 运行 `ast.parse()` 验证语法正确
## 4. 迁移 services/persona.py
- [x] 4.1 创建 `services/persona.py`,迁移常量:`DEFAULT_PERSONAS`、`RANDOM_PERSONA_LABEL`、`PERSONA_POOL_MAP`、`DEFAULT_TOPICS`、`DEFAULT_STYLES`、`DEFAULT_COMMENT_KEYWORDS`
- [x] 4.2 迁移函数:`_match_persona_pools`、`get_persona_topics`、`get_persona_keywords`、`on_persona_changed`、`_resolve_persona`
- [x] 4.3 在 `main.py` 中删除对应代码,添加 `from services.persona import ...`
- [x] 4.4 运行 `ast.parse()` 验证语法正确
## 5. 迁移 services/connection.py
- [x] 5.1 创建 `services/connection.py`,迁移函数:`_get_llm_config`、`connect_llm`、`add_llm_provider`、`remove_llm_provider`、`on_provider_selected`
- [x] 5.2 迁移 SD 相关函数:`connect_sd`、`on_sd_model_change`
- [x] 5.3 迁移 MCP / 登录相关函数:`check_mcp_status`、`get_login_qrcode`、`logout_xhs`、`_auto_fetch_xsec_token`、`check_login`、`save_my_user_id`、`upload_face_image`、`load_saved_face_image`
- [x] 5.4 确保所有函数通过参数接收 `cfg`、`llm`、`sd`、`mcp` 等依赖,不在模块顶层初始化单例
- [x] 5.5 在 `main.py` 中删除对应函数,添加 `from services.connection import ...`
- [x] 5.6 运行 `ast.parse()` 验证语法正确
## 6. 迁移 services/profile.py
- [x] 6.1 创建 `services/profile.py`,迁移函数:`_parse_profile_json`、`_parse_count`、`fetch_my_profile`
- [x] 6.2 在 `main.py` 中删除对应函数,添加 `from services.profile import ...`
- [x] 6.3 运行 `ast.parse()` 验证语法正确
## 7. 迁移 services/hotspot.py
- [x] 7.1 创建 `services/hotspot.py`,迁移缓存相关:`_cache_lock`、`_set_cache`、`_get_cache`、`_fetch_and_cache`、`_pick_from_cache`
- [x] 7.2 迁移热点函数:`search_hotspots`、`analyze_and_suggest`、`generate_from_hotspot`、`fetch_proactive_notes`、`on_proactive_note_selected`
- [x] 7.3 在 `main.py` 中删除对应代码,添加 `from services.hotspot import ...`
- [x] 7.4 运行 `ast.parse()` 验证语法正确
## 8. 迁移 services/content.py
- [x] 8.1 创建 `services/content.py`,迁移函数:`generate_copy`、`generate_images`、`one_click_export`、`publish_to_xhs`
- [x] 8.2 确保 `publish_to_xhs` 的输入验证逻辑和 `finally` 临时文件清理逻辑完整保留
- [x] 8.3 在 `main.py` 中删除对应函数,添加 `from services.content import ...`
- [x] 8.4 运行 `ast.parse()` 验证语法正确
## 9. 迁移 services/engagement.py
- [x] 9.1 创建 `services/engagement.py`,迁移笔记/评论相关函数:`load_note_for_comment`、`ai_generate_comment`、`send_comment`、`fetch_my_notes`、`on_my_note_selected`、`fetch_my_note_comments`、`ai_reply_comment`、`send_reply`
- [x] 9.2 迁移自动化函数:`auto_comment_once`、`auto_like_once`、`auto_favorite_once`、`auto_reply_once` 及各 `_with_log` 包装
- [x] 9.3 将 `_with_log` 函数改为接收 `log_fn` 回调参数,不直接引用外部 `_auto_log`
- [x] 9.4 在 `main.py` 中删除对应函数,添加 `from services.engagement import ...`
- [x] 9.5 运行 `ast.parse()` 验证语法正确
## 10. 迁移 services/scheduler.py
- [x] 10.1 创建 `services/scheduler.py`,迁移状态变量和日志:`_auto_log`、`_scheduler_next_times`、`_auto_log_append`
- [x] 10.2 迁移调度器函数:`_scheduler_loop`、`start_scheduler`、`stop_scheduler`、`get_auto_log`、`get_scheduler_status`
- [x] 10.3 迁移学习调度器:`_learn_running`、`_learn_scheduler_loop`、`start_learn_scheduler`、`stop_learn_scheduler`
- [x] 10.4 确保 `_scheduler_loop` 调用 `engagement` 函数时传入 `log_fn=_auto_log_append`
- [x] 10.5 在 `main.py` 中删除对应代码,添加 `from services.scheduler import ...`
- [x] 10.6 运行 `ast.parse()` 验证语法正确
## 11. 迁移 services/queue_ops.py
- [x] 11.1 创建 `services/queue_ops.py`,迁移所有 queue 操作函数:`generate_to_queue`、`_queue_publish_callback`、`queue_refresh_table`、`queue_refresh_calendar`、`queue_preview_item`、`queue_approve_item`、`queue_reject_item`、`queue_delete_item`、`queue_retry_item`、`queue_publish_now`、`queue_start_processor`、`queue_stop_processor`、`queue_get_status`、`queue_batch_approve`、`queue_generate_and_refresh`
- [x] 11.2 确保 `pub_queue`、`queue_publisher` 通过参数传入各函数,不在模块顶层初始化
- [x] 11.3 在 `main.py` 中删除对应函数,添加 `from services.queue_ops import ...`;保留 `pub_queue.set_publish_callback(_queue_publish_callback)` 在 `main.py` 初始化段调用
- [x] 11.4 运行 `ast.parse()` 验证语法正确
## 12. 拆分 UI Tab 模块
- [x] 12.1 创建 `ui/tab_hotspot.py`,提取 Tab 2(🔥 热点探测)的所有 Gradio 组件和事件绑定,暴露 `build_tab(fn_*, ...)` 函数
- [x] 12.2 创建 `ui/tab_engage.py`,提取 Tab 3(💬 互动运营)的所有 Gradio 组件和事件绑定
- [x] 12.3 创建 `ui/tab_profile.py`,提取 Tab 4(👤 我的主页)的所有 Gradio 组件和事件绑定
- [x] 12.4 创建 `ui/tab_auto.py`,提取 Tab 5(🤖 自动运营)的所有 Gradio 组件和事件绑定
- [x] 12.5 创建 `ui/tab_queue.py`,提取 Tab 6(📅 内容排期)的所有 Gradio 组件和事件绑定
- [x] 12.6 创建 `ui/tab_analytics.py`,提取 Tab 7(📊 数据分析)的所有 Gradio 组件和事件绑定
- [x] 12.7 创建 `ui/tab_settings.py`,提取 Tab 8(⚙️ 系统设置)的所有 Gradio 组件和事件绑定
- [x] 12.8 在 `main.py` 中用相应的 `build_tab(...)` 调用替换各 Tab 代码块,完成后删除空白 Tab 块
- [x] 12.9 运行 `ast.parse()` 验证所有新建 UI 模块语法正确
## 13. 入口层清理与验证
- [x] 13.1 验证 `main.py` 行数不超过 400 行
- [x] 13.2 检查 `main.py` 不包含任何业务逻辑函数定义(除 lambda 内联外)
- [x] 13.3 运行应用 `python main.py`,确认启动无报错
- [x] 13.4 在浏览器中切换所有 Tab,确认 UI 正常渲染、事件响应正常