Skip to content

[v0.9] 1.1 raw_items 采集管道 #2

@jessie-coco

Description

@jessie-coco

背景

当前架构中,采集与 Digest 生成耦合在一起:每次调用采集逻辑就直接生成 Digest,同一个 Source 如果有多个用户订阅,会重复抓取。这在多用户场景下既浪费资源,又难以扩展。

kevinho/clawfeed PR #15feat: raw_items collection pipeline)已 MERGED,本 issue 跟踪将其合并到 coco-xyz/clawfeed 并完成后续集成工作。

目标

将 Source 级采集与 Digest 生成完全解耦。同一个 Source 无论有多少人订阅,只采集一次,结果写入 raw_items 表,供所有订阅者共享。

技术方案

新增数据表

CREATE TABLE IF NOT EXISTS raw_items (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  source_id INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
  title TEXT NOT NULL DEFAULT '',
  url TEXT NOT NULL DEFAULT '',
  author TEXT DEFAULT '',
  content TEXT NOT NULL DEFAULT '',
  fetched_at TEXT NOT NULL DEFAULT (datetime('now')),
  published_at TEXT,
  dedup_key TEXT NOT NULL,            -- source_id + ":" + url,无 URL 时用 content hash
  metadata TEXT DEFAULT '{}',         -- 点赞数、评论数等原始信号
  UNIQUE(source_id, dedup_key)
);
CREATE INDEX idx_raw_items_source_fetched ON raw_items(source_id, fetched_at DESC);
CREATE INDEX idx_raw_items_fetched ON raw_items(fetched_at DESC);

核心改动

  • db/migrations/006_raw_items.sql — DDL migration
  • src/db.mjs — 新增 raw_items CRUD 函数:insertRawItem(), getRawItemsBySource(), pruneOldRawItems()
  • 重构现有采集逻辑:采集结果写入 raw_items 而非直接生成 Digest
  • Digest 生成改为从 raw_items 读取(为 1.2 个性化做准备)
  • 并发采集池:多个 Source 并行采集,互不阻塞

去重策略

  • dedup_key = source_id + ":" + url
  • 无 URL 内容使用 SHA256(content) 作为 dedup_key
  • 通过 UNIQUE(source_id, dedup_key) + INSERT OR IGNORE 实现零代码去重

安全与稳定性

  • SSRF 防护:校验 RSS/RSS Feed URL 不指向内网地址
  • 错误隔离:单个 Source 采集失败不影响其他 Source
  • 自动暂停:连续失败 N 次(如 5 次)自动将 Source 标记为 is_active = 0

验收标准

  • raw_items 表创建成功,包含正确索引
  • 采集任意 Source,结果写入 raw_items,不再直接触发 Digest 生成
  • 重复采集同一内容(相同 URL)不产生重复记录(INSERT OR IGNORE 验证)
  • 多 Source 并发采集,单个失败不影响整体
  • 连续失败的 Source 自动 is_active = 0 暂停
  • 现有 66 个 E2E assertions 全部通过
  • GET /api/raw-items/stats 返回各 Source 的采集统计(条数、最近采集时间)

依赖

无(此为 Phase 1 基础,其他三个 issue 均依赖此 issue)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions