## Context 当前系统有两条独立的发布路径: 1. `scheduler.py` → `auto_publish_once()` → 直接生成 + 直接发布到小红书(绕过队列) 2. `queue_ops.py` → `generate_to_queue()` → `PublishQueue` SQLite 持久化 → `QueuePublisher._loop()` → 按排期/审核状态发布 `AnalyticsService` 已在 `content_weights.json` 中保存 `time_weights`(3小时段权重,如 `"18-21时": {"weight": 85, "count": 12}`),但这些数据没有被排期逻辑使用。用户排期必须手动输入时间字符串。 ## Goals / Non-Goals **Goals:** - G1: 基于 `time_weights` 自动计算最优发布时段,为入队内容分配 `scheduled_time` - G2: 消除调度器绕过队列的直接发布路径,统一为队列驱动 - G3: 支持内容间距控制(同一时段不超过 N 篇),分散到多天 - G4: UI 增加自动排期开关和排期建议展示 **Non-Goals:** - 不改动 `PublishQueue` 的 SQLite 表结构(不新增列) - 不增加 A/B 测试或基于实时反馈的动态调整 - 不修改 `QueuePublisher` 的发布执行逻辑(仅改其上游输入) - 不改变评论/点赞/收藏等非发布类自动化任务 ## Decisions ### D1: 智能排期引擎放在 `publish_queue.py` 中作为 `PublishQueue` 的方法 **决定**: 在 `PublishQueue` 类上新增 `suggest_schedule_time()` 和 `auto_schedule_item()` 方法。 **理由**: 排期引擎需要查询现有队列排期(避免时段冲突),`PublishQueue` 已持有 SQLite 连接和查询方法,放在此处可避免跨模块传递 db 连接。 **备选方案**: 独立模块 `schedule_engine.py` — 但会增加新文件,且仍需注入 `PublishQueue` 实例来查询已排期项。 ### D2: `time_weights` 通过 `AnalyticsService.get_time_weights()` 新方法获取 **决定**: 在 `AnalyticsService` 上新增 `get_time_weights() -> dict` 方法,返回 `time_weights` 字典。无数据时返回默认值。 **理由**: 封装内部 `_weights` 结构,提供干净的 API 供排期引擎调用。 **默认时段**: 无分析数据时 fallback 到: `{"08-11时": 70, "12-14时": 60, "18-21时": 85, "21-24时": 75}`(小红书高流量经验值)。 ### D3: 统一发布路径 — 调度器 publish 分支改为 generate_to_queue + auto_approve **决定**: `_scheduler_loop` 的自动发布分支改为调用 `generate_to_queue(auto_schedule=True, auto_approve=True)`,不再调用 `auto_publish_once` 内的发布逻辑。`auto_publish_once` 保留但重构为仅生成内容入队。 **理由**: - 统一发布路径,所有内容都走队列审核流程 - `QueuePublisher` 已有重试、错误处理、日志记录能力 - 调度器生成的内容也会出现在队列表格和日历中,可追溯 **行为变化**: 调度器发布从「立即生成并发布」变为「生成入队 → QueuePublisher 下一轮 check(最多 60s)发布」。延迟可接受。 ### D4: 时段冲突检测基于 SQLite 查询 **决定**: `suggest_schedule_time()` 查询 `queue` 表中 `scheduled_time` 列,统计每个时段已排队的数量,优先选择高权重且低负载的时段。 **规则**: - 每个3小时段最多 `max_per_slot`(默认 2)篇 - 同一天内最多 `max_per_day`(默认 5)篇 - 优先当天还有空余的高权重时段,当天满则顺延到次日 - 最远排到7天后 ### D5: `generate_to_queue` 新增 `auto_schedule` 和 `auto_approve` 参数 **决定**: `generate_to_queue()` 签名增加 `auto_schedule: bool = False` 和 `auto_approve: bool = False`: - `auto_schedule=True` → 调用 `PublishQueue.suggest_schedule_time()` 为每篇内容分配时间 - `auto_approve=True` → 入队后自动调用 `approve()`,状态从 draft 直接变为 scheduled/approved **理由**: 最小改动,对现有手动流程无影响;调度器场景两者都开启,UI 场景用户可选。 ### D6: UI 增加自动排期复选框和排期建议面板 **决定**: - 在「批量生成到队列」区域增加 `auto_schedule` 复选框,勾选后隐藏手动排期输入框 - 在日历视图旁增加「📊 推荐时段」Markdown 面板,展示 `time_weights` top 时段 **理由**: 最小 UI 变更,不重构现有布局。 ## Risks / Trade-offs - **[排期延迟]** 调度器发布不再即时,会有最多 60s 延迟(QueuePublisher check 间隔)→ 对社交媒体发布场景可接受,且可通过缩短 `check_interval` 缓解 - **[无数据 fallback]** 首次使用时 `time_weights` 为空,排期基于经验默认值 → 运行一段时间后数据学习会逐步优化 - **[时段冲突查询性能]** 每次入队都需查询队列排期 → 队列规模通常 < 100 项,SQLite WAL 模式下查询性能无忧 - **[auto_approve 安全性]** 调度器自动审核跳过人工审核 → 仅在调度器自动模式下启用,用户手动入队仍需审核