docs(readme): 同步 commit 1-7 的功能和数据模型
README 在 commit 3 时只加过 API Push 的"关键特性"和"API 概览"两段,
但漏了以下 5 处,本次统一补上:
1. 数据模型表:
- sources.kind 加 api_push(commit 1 alembic 0008 + commit 3 worker 跳过)
- articles 加 is_short_news / external_id / source_ref / content_hash(commit 1)
- articles.translation_status 加 'n/a' 状态值
- api_tokens 加 purpose + source_id(commit 1 + commit 2)
- ER 关系: sources 1:N api_tokens(ingest token 绑定 source)
- 新增"短新闻(API Push)特性"子节,描述 content_hash 算法
和 url 合成策略
2. API 概览:
- POST /admin/refresh/{source_id} 注明对 api_push 源无效
- POST /admin/translation/rerun 注明对短新闻/中文源返 400(commit 4)
- 新增"端到端测试"小节,引用 scripts/smoke_ingest.py(commit 5)
3. 故障排查:
- 新增 3 个 Q:API Push 相关 / 中文 RSS 详情页 / alembic 部署顺序
- 最后一个 Q 引用 agent memory "FastAPI + alembic 部署 SOP"(commit 部署)
4. 路线图:
- 新增 Phase 1.6 (API Push 短新闻) ✅ 2026-06-14
- 新增 Phase 1.7 (中文源头 RSS 优化) ✅ 2026-06-14
无代码改动,纯文档同步。
This commit is contained in:
51
README.md
51
README.md
@@ -415,18 +415,28 @@ diary-news/
|
|||||||
| 表 | 关键字段 | 说明 |
|
| 表 | 关键字段 | 说明 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **users** | role(enum: owner/member), password_hash | 用户 + 角色 |
|
| **users** | role(enum: owner/member), password_hash | 用户 + 角色 |
|
||||||
| **sources** | slug(uniq), kind(rss/html_list/tg_channel), priority, fetch_interval_min, consecutive_failures | 采集源 |
|
| **sources** | slug(uniq), kind(rss/html_list/tg_channel/**api_push**), priority, fetch_interval_min, consecutive_failures, blocklist_tags | 采集源(`api_push` 是被动接收,不被 worker 调度) |
|
||||||
| **articles** | url_hash(uniq), translation_status(pending/ok/partial/failed), category, commentary, body_zh_formatted, image_ai_url, *_status, category, entities(JSONB), sentiment, topic_id, bias | 文章 + 译文 + LLM 增强 |
|
| **articles** | url_hash(uniq), translation_status(pending/ok/partial/failed/n/a), category, commentary, body_zh_formatted, image_ai_url, *_status, entities(JSONB), sentiment, topic_id, bias, **is_short_news**, **external_id**, **source_ref**, **content_hash(uniq)** | 文章 + 译文 + LLM 增强;短新闻 4 字段为 API Push 接入 |
|
||||||
| **bookmarks** | (user_id, article_id) UNIQUE | 收藏 |
|
| **bookmarks** | (user_id, article_id) UNIQUE | 收藏 |
|
||||||
| **subscriptions** | keyword, match_in(any/title/body), channel | 关键词订阅 |
|
| **subscriptions** | keyword, match_in(any/title/body), channel | 关键词订阅 |
|
||||||
| **api_tokens** | token_hash(sha256), expires_at, revoked_at | Android 预留 |
|
| **api_tokens** | token_hash(sha256), purpose(mobile/ingest), source_id(ingest 专用), expires_at, revoked_at | Android + API Push ingest 双用途 |
|
||||||
| **llm_settings** | format_prompt, classify_prompt, commentary_prompt, image_prompt_template, image_size, chat_model, image_model, interval_sec, enabled | LLM 提示词(单行) |
|
| **llm_settings** | format_prompt, classify_prompt, commentary_prompt, image_prompt_template, image_size, chat_model, image_model, interval_sec, enabled | LLM 提示词(单行) |
|
||||||
|
|
||||||
ER 关系:
|
ER 关系:
|
||||||
- `users` 1:N → `bookmarks` / `subscriptions` / `api_tokens`
|
- `users` 1:N → `bookmarks` / `subscriptions` / `api_tokens`
|
||||||
- `sources` 1:N → `articles`(cascade delete)
|
- `sources` 1:N → `articles`(cascade delete), 1:N → `api_tokens`(purpose=ingest 时)
|
||||||
- `articles` 1:N → `bookmarks`, self-ref `duplicate_of`(去重链)
|
- `articles` 1:N → `bookmarks`, self-ref `duplicate_of`(去重链)
|
||||||
|
|
||||||
|
### 短新闻(API Push)特性
|
||||||
|
|
||||||
|
- `articles.is_short_news=true` 标记(其余文章默认 false)
|
||||||
|
- `articles.content_hash` UNIQUE 索引 — 内容指纹,API Push 三层去重的核心 key
|
||||||
|
- external_id 存在时:`sha1("ext:" + external_id)`
|
||||||
|
- external_id 缺失时:`sha1(title + "|" + body[:500])`
|
||||||
|
- `articles.url` 对短新闻合成占位 `api-push://{source_slug}/{content_hash[:16]}`
|
||||||
|
- 入库后 `translation/format/image_ai_status='n/a'`,enrichment_loop 只跑 classify + 双 provider commentary
|
||||||
|
- 中文源头的长新闻(RSS 抓的中文源)在前端详情页不显示译文板块,translation_loop 跳过省 TMT 配额
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
@@ -633,13 +643,23 @@ WHERE translation_status='ok';
|
|||||||
- `POST /admin/sources/{source_id}/ingest-tokens` — 为 api_push 源生成 ingest token(raw_token 仅一次性返回)
|
- `POST /admin/sources/{source_id}/ingest-tokens` — 为 api_push 源生成 ingest token(raw_token 仅一次性返回)
|
||||||
- `GET /admin/sources/{source_id}/ingest-tokens` — 列出某个 source 的 ingest token
|
- `GET /admin/sources/{source_id}/ingest-tokens` — 列出某个 source 的 ingest token
|
||||||
- `DELETE /admin/ingest-tokens/{token_id}` — 撤销 ingest token
|
- `DELETE /admin/ingest-tokens/{token_id}` — 撤销 ingest token
|
||||||
- `POST /admin/refresh/{source_id}` — 立即触发抓取
|
- `POST /admin/refresh/{source_id}` — 立即触发抓取(对 api_push 源无效,返回 OK 但 worker 不调度)
|
||||||
- `POST /admin/translation/rerun/{article_id}` — 重译
|
- `POST /admin/translation/rerun/{article_id}` — 重译;**对短新闻/中文源文章返 400**(commit 4)
|
||||||
- `GET /admin/health` — 源健康看板
|
- `GET /admin/health` — 源健康看板
|
||||||
- `POST /admin/translation/quota/reset` — 重置本月配额
|
- `POST /admin/translation/quota/reset` — 重置本月配额
|
||||||
- `GET /admin/llm/settings` / `PUT` / `POST /reset` / `POST /test` — LLM 设置
|
- `GET /admin/llm/settings` / `PUT` / `POST /reset` / `POST /test` — LLM 设置
|
||||||
- `POST /admin/llm/enrich/{article_id}` — 手动触发某篇 enrich
|
- `POST /admin/llm/enrich/{article_id}` — 手动触发某篇 enrich
|
||||||
|
|
||||||
|
### 端到端测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/smoke_ingest.py --token <RAW_TOKEN>
|
||||||
|
# 期望输出:ALL PASS (6/6)
|
||||||
|
```
|
||||||
|
|
||||||
|
详见 [`scripts/smoke_ingest.py`](./scripts/smoke_ingest.py) — 验证 /api/v1/ingest 的
|
||||||
|
"创建/重复 external_id/内容去重/错误 token/body 超长"6 步链路。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 开发-部署工作流
|
## 开发-部署工作流
|
||||||
@@ -750,12 +770,31 @@ ssh hknews "cd /root/diary-news && git reset --hard <sha>"
|
|||||||
### Q: 中文用户名乱码?
|
### Q: 中文用户名乱码?
|
||||||
PowerShell 默认 GBK,运行前先 `chcp 65001` 切 UTF-8。
|
PowerShell 默认 GBK,运行前先 `chcp 65001` 切 UTF-8。
|
||||||
|
|
||||||
|
### Q: 短新闻 / API Push 相关?
|
||||||
|
- 短新闻入库后停在 enrich 阶段:看 worker 日志 `docker compose logs worker | grep enrichment`
|
||||||
|
- ingest 接口 401:`X-Ingest-Token` 无效/吊销/过期;回 `/admin/sources/{id}/ingest-tokens` 重新生成
|
||||||
|
- ingest 接口 429:1 秒内推超 2 篇(默认 `INGEST_RATE_PER_SEC=2`);退避 1 秒重试
|
||||||
|
- 短新闻 Feed 看不到:先 `SELECT * FROM articles WHERE is_short_news=true` 查是否入库
|
||||||
|
- 想清理 smoke 测试残留:`DELETE FROM articles WHERE source_ref='smoke';`
|
||||||
|
|
||||||
|
### Q: 中文 RSS 长新闻详情页仍显示"译文"板块?
|
||||||
|
- 确认 `articles.lang_src` 是 `'zh'` / `'zh-CN'` 等(前端判断标准)
|
||||||
|
- 详情页逻辑在 `ArticleDetail.vue` `isChineseSource` computed:`isShort || lang_src.startsWith('zh')`
|
||||||
|
- 详情页还会显示"原文"卡片代替"译文"卡(commit 6)
|
||||||
|
|
||||||
|
### Q: 加了 alembic 迁移但容器看不到新文件?
|
||||||
|
- 永远先 `docker compose up -d --no-deps --force-recreate api worker` 再 `alembic upgrade head`
|
||||||
|
- 容器在 bind mount 之前用镜像层启动,旧镜像层会被覆盖但只有 recreate 才生效
|
||||||
|
- 详细踩坑记录见 agent memory "FastAPI + alembic 部署 SOP"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 路线图
|
## 路线图
|
||||||
|
|
||||||
- [x] **Phase 1 (MVP)**:5 RSS 源 + 翻译 + 网页 + admin CRUD
|
- [x] **Phase 1 (MVP)**:5 RSS 源 + 翻译 + 网页 + admin CRUD
|
||||||
- [x] **Phase 1.5**:LLM 智能增强(排版/分类/插图/点评) ✅ 2026-06-08
|
- [x] **Phase 1.5**:LLM 智能增强(排版/分类/插图/点评) ✅ 2026-06-08
|
||||||
|
- [x] **Phase 1.6**:API Push 短新闻(`POST /api/v1/ingest` + 三层去重 + per-token 限速) ✅ 2026-06-14
|
||||||
|
- [x] **Phase 1.7**:中文源头 RSS 优化(详情页隐藏译文 + 翻译循环跳过省配额) ✅ 2026-06-14
|
||||||
- [ ] **Phase 2**:PWA 离线缓存 / 关键词订阅推送(Telegram)
|
- [ ] **Phase 2**:PWA 离线缓存 / 关键词订阅推送(Telegram)
|
||||||
- [ ] **Phase 3**:Android 客户端(API Token 已预留)
|
- [ ] **Phase 3**:Android 客户端(API Token 已预留)
|
||||||
- [ ] **Phase 4**:自动分类/点评/实体识别(目前是 LLM 一次性,无 ML pipeline)
|
- [ ] **Phase 4**:自动分类/点评/实体识别(目前是 LLM 一次性,无 ML pipeline)
|
||||||
|
|||||||
Reference in New Issue
Block a user