435 lines
10 KiB
Markdown
435 lines
10 KiB
Markdown
|
|
# dataClean 接口文档
|
|||
|
|
|
|||
|
|
> 服务地址:`http://<host>:7331`
|
|||
|
|
> 所有 `/api/*` 接口(除 `/health`)在配置了 `API_TOKEN` 时需要在请求头携带 `Authorization: Bearer <token>`。
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
|
|||
|
|
- [鉴权](#鉴权)
|
|||
|
|
- [健康检查](#健康检查)
|
|||
|
|
- [文章接口](#文章接口)
|
|||
|
|
- [简报接口](#简报接口)
|
|||
|
|
- [分类体系接口](#分类体系接口)
|
|||
|
|
- [任务接口](#任务接口)
|
|||
|
|
- [任务进度](#任务进度)
|
|||
|
|
- [接口连通性测试](#接口连通性测试)
|
|||
|
|
- [配置管理接口](#配置管理接口)
|
|||
|
|
- [仪表盘统计](#仪表盘统计)
|
|||
|
|
- [错误码](#错误码)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 鉴权
|
|||
|
|
|
|||
|
|
若服务端配置了 `API_TOKEN`,除 `/health` 外所有接口需要在请求头携带:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Authorization: Bearer <API_TOKEN>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
未携带或 token 无效时返回 `401` / `403`。未配置 `API_TOKEN` 时不启用鉴权(仅建议内网使用)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 健康检查
|
|||
|
|
|
|||
|
|
### `GET /health`
|
|||
|
|
|
|||
|
|
服务存活探针,无需鉴权。
|
|||
|
|
|
|||
|
|
**响应**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "status": "ok", "service": "dataClean" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 文章接口
|
|||
|
|
|
|||
|
|
### `GET /api/articles`
|
|||
|
|
|
|||
|
|
分页查询加工后的文章,支持按日期、分类、标签过滤。
|
|||
|
|
|
|||
|
|
**查询参数**
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必填 | 默认 | 说明 |
|
|||
|
|
|------|------|------|------|------|
|
|||
|
|
| `date` | string | 否 | - | 日期 `YYYY-MM-DD`,按 `fetched_at` 过滤当天 |
|
|||
|
|
| `category` | string | 否 | - | 精确分类名 |
|
|||
|
|
| `tag` | string | 否 | - | 标签名(JSON 数组精确匹配) |
|
|||
|
|
| `representative_only` | bool | 否 | `false` | 仅返回重复组代表文章 |
|
|||
|
|
| `limit` | int | 否 | `50` | 1–200 |
|
|||
|
|
| `offset` | int | 否 | `0` | 分页偏移 |
|
|||
|
|
|
|||
|
|
**响应** `ArticleListOut`
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"total": 200,
|
|||
|
|
"items": [
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"rk_article_id": 28124,
|
|||
|
|
"title": "文章标题",
|
|||
|
|
"link": "https://...",
|
|||
|
|
"feed_title": "来源",
|
|||
|
|
"category": "科技",
|
|||
|
|
"tags": ["AI", "芯片"],
|
|||
|
|
"heat_score": 45.2,
|
|||
|
|
"importance_score": 60.0,
|
|||
|
|
"duplication_score": 25.0,
|
|||
|
|
"composite_score": 52.56,
|
|||
|
|
"ai_summary": "AI 生成的摘要",
|
|||
|
|
"is_representative": false,
|
|||
|
|
"published_at": "2026-06-13T13:48:42"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `GET /api/articles/{article_id}`
|
|||
|
|
|
|||
|
|
获取单篇文章详情。
|
|||
|
|
|
|||
|
|
**路径参数** `article_id`:int
|
|||
|
|
|
|||
|
|
**响应** `ArticleOut`(同上 items 元素)
|
|||
|
|
|
|||
|
|
**错误** `404` 文章不存在
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 简报接口
|
|||
|
|
|
|||
|
|
### `GET /api/briefs`
|
|||
|
|
|
|||
|
|
列出每日简报(按日期倒序)。
|
|||
|
|
|
|||
|
|
**查询参数** `limit`:int,默认 30,范围 1–100
|
|||
|
|
|
|||
|
|
**响应** `List[BriefOut]`
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"brief_date": "2026-06-13",
|
|||
|
|
"total_articles": 200,
|
|||
|
|
"unique_articles": 150,
|
|||
|
|
"by_category": { "科技": [{...}], "财经": [{...}] },
|
|||
|
|
"markdown_path": "/app/data/briefs/2026-06-13/daily-brief.md"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `GET /api/briefs/{date}`
|
|||
|
|
|
|||
|
|
获取指定日期简报。
|
|||
|
|
|
|||
|
|
**路径参数** `date`:string,`YYYY-MM-DD`
|
|||
|
|
|
|||
|
|
**响应** `BriefOut` **错误** `404` 简报不存在
|
|||
|
|
|
|||
|
|
### `POST /api/briefs/{date}/regenerate`
|
|||
|
|
|
|||
|
|
强制重新生成指定日期简报。同步执行(需持任务锁)。
|
|||
|
|
|
|||
|
|
**响应**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "message": "简报已重新生成", "data": { ... } }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**错误** `409` 已有任务执行中
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 分类体系接口
|
|||
|
|
|
|||
|
|
### `GET /api/taxonomy`
|
|||
|
|
|
|||
|
|
列出分类/标签/打分规则。
|
|||
|
|
|
|||
|
|
**查询参数** `kind`:string,可选,过滤类型:`category` / `tag` / `heat_rule` / `importance_rule` / `duplication_rule`
|
|||
|
|
|
|||
|
|
**响应** `List[TaxonomyOut]`
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"name": "科技",
|
|||
|
|
"kind": "category",
|
|||
|
|
"description": "人工智能、芯片、互联网等",
|
|||
|
|
"keywords": ["AI", "芯片", "大模型"],
|
|||
|
|
"weight": 1.0,
|
|||
|
|
"created_by_ai": true
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `POST /api/taxonomy/bootstrap`
|
|||
|
|
|
|||
|
|
初始化或强制重建分类体系(后台异步执行)。
|
|||
|
|
|
|||
|
|
**查询参数** `force`:bool,默认 `false`。`true` 时清空后重建。
|
|||
|
|
|
|||
|
|
**响应**(立即返回)
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "message": "taxonomy 初始化已开始", "task_key": "bootstrap_taxonomy" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**错误** `409` 已有任务执行中。可通过 [`GET /api/tasks/progress`](#任务进度) 查看 `bootstrap_taxonomy` 进度。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 任务接口
|
|||
|
|
|
|||
|
|
所有任务接口均为**后台异步执行**:提交后立即返回 `task_key`,任务在线程池执行,通过[进度接口](#任务进度)轮询。
|
|||
|
|
|
|||
|
|
任务全局互斥(共享 `_task_lock`):同一时刻仅一个任务运行。
|
|||
|
|
|
|||
|
|
### `POST /api/tasks/summarize`
|
|||
|
|
|
|||
|
|
拉取 rssKeeper 最近 24 小时文章,为无摘要/短摘要文章生成 AI 摘要。
|
|||
|
|
|
|||
|
|
**响应**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "message": "摘要任务已开始", "task_key": "summarize" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**错误** `409` 已有任务执行中
|
|||
|
|
|
|||
|
|
### `POST /api/tasks/tag-score-dedup`
|
|||
|
|
|
|||
|
|
对当天文章执行:分类打标 → 去重 → 打分(三阶段,进度合并显示)。
|
|||
|
|
|
|||
|
|
**响应**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "message": "分类/去重/打分任务已开始", "task_key": "tag_score_dedup" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `POST /api/tasks/brief`
|
|||
|
|
|
|||
|
|
生成当天每日简报(force 重新生成)。
|
|||
|
|
|
|||
|
|
**响应**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "message": "简报生成任务已开始", "task_key": "generate_daily_brief" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 任务进度
|
|||
|
|
|
|||
|
|
### `GET /api/tasks/progress`
|
|||
|
|
|
|||
|
|
返回所有任务的实时进度快照(前端每 ~1.5 秒轮询)。
|
|||
|
|
|
|||
|
|
**响应**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"summarize": {
|
|||
|
|
"status": "running",
|
|||
|
|
"stage": "生成摘要",
|
|||
|
|
"current": 75,
|
|||
|
|
"total": 200,
|
|||
|
|
"message": null,
|
|||
|
|
"started_at": "2026-06-13T14:30:00+00:00",
|
|||
|
|
"updated_at": "2026-06-13T14:32:15+00:00",
|
|||
|
|
"finished_at": null,
|
|||
|
|
"trigger": "manual"
|
|||
|
|
},
|
|||
|
|
"tag_score_dedup": { "status": "idle", "stage": "", "current": 0, "total": 0, "message": null, "started_at": null, "updated_at": null, "finished_at": null, "trigger": null },
|
|||
|
|
"generate_daily_brief": { "..." : "同上结构" },
|
|||
|
|
"bootstrap_taxonomy": { "..." : "同上结构" }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**字段说明**
|
|||
|
|
|
|||
|
|
| 字段 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| `status` | `idle` / `running` / `success` / `error` |
|
|||
|
|
| `stage` | 当前阶段文案(如「生成摘要」「LLM 生成分类体系」) |
|
|||
|
|
| `current` / `total` | 进度计数,`total=0` 时为阶段型任务(用 indeterminate 进度条) |
|
|||
|
|
| `message` | 附加信息或错误详情(`status=error` 时为错误信息) |
|
|||
|
|
| `started_at` / `finished_at` | ISO 8601 时间戳 |
|
|||
|
|
| `trigger` | `manual`(手动触发)/ `scheduled`(定时触发) |
|
|||
|
|
|
|||
|
|
### `POST /api/tasks/progress/reset`
|
|||
|
|
|
|||
|
|
重置指定任务的进度为 idle(清除终态显示)。
|
|||
|
|
|
|||
|
|
**查询参数** `task_key`:string,必填
|
|||
|
|
|
|||
|
|
**响应** `{ "message": "已重置" }`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 接口连通性测试
|
|||
|
|
|
|||
|
|
### `POST /api/test-connection`
|
|||
|
|
|
|||
|
|
测试 rssKeeper 与 LLM API 连通性,返回状态与延迟。每个测试使用独立短超时客户端(10 秒、0 重试)。
|
|||
|
|
|
|||
|
|
**响应** `ConnectionTestResponse`
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"rss_keeper": {
|
|||
|
|
"name": "rssKeeper",
|
|||
|
|
"status": "ok",
|
|||
|
|
"latency_ms": 26.0,
|
|||
|
|
"error": null
|
|||
|
|
},
|
|||
|
|
"llm": {
|
|||
|
|
"name": "LLM",
|
|||
|
|
"status": "ok",
|
|||
|
|
"latency_ms": 6871.4,
|
|||
|
|
"error": null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`status` 为 `error` 时 `error` 字段含失败原因,`latency_ms` 为 `null`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 配置管理接口
|
|||
|
|
|
|||
|
|
> 配置修改保存到数据库,部分配置(调度间隔等)需重启服务生效。
|
|||
|
|
|
|||
|
|
### `GET /api/settings`
|
|||
|
|
|
|||
|
|
列出所有可编辑配置。敏感项(`OPENAI_API_KEY` / `API_TOKEN`)返回脱敏值。
|
|||
|
|
|
|||
|
|
**响应** `List[SettingOut]`
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"key": "OPENAI_API_KEY",
|
|||
|
|
"value": "sk-c...2R_8",
|
|||
|
|
"description": "LLM API Key",
|
|||
|
|
"is_sensitive": true,
|
|||
|
|
"is_masked": true,
|
|||
|
|
"updated_at": "2026-06-13T..."
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `PUT /api/settings/{key}`
|
|||
|
|
|
|||
|
|
更新单个配置项。
|
|||
|
|
|
|||
|
|
**请求体**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "value": "新值" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应** `{ "message": "配置已保存,重启服务后生效" }` **错误** `400` 无效配置项
|
|||
|
|
|
|||
|
|
### `PUT /api/settings`
|
|||
|
|
|
|||
|
|
批量更新配置。
|
|||
|
|
|
|||
|
|
**请求体**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "settings": { "OPENAI_MODEL": "gpt-4o-mini", "OPENAI_TIMEOUT": "60" } }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应** `{ "message": "配置已保存,重启服务后生效" }` **错误** `400` 列出无效配置项
|
|||
|
|
|
|||
|
|
### `POST /api/settings/reset`
|
|||
|
|
|
|||
|
|
将所有配置重置为环境变量默认值。
|
|||
|
|
|
|||
|
|
**响应** `{ "message": "配置已重置为环境变量默认值,重启服务后生效" }`
|
|||
|
|
|
|||
|
|
### 可编辑配置清单
|
|||
|
|
|
|||
|
|
| key | 说明 | 敏感 |
|
|||
|
|
|-----|------|------|
|
|||
|
|
| `RSSKEEPER_BASE_URL` | rssKeeper 服务地址 | 否 |
|
|||
|
|
| `OPENAI_API_KEY` | LLM API Key | 是 |
|
|||
|
|
| `OPENAI_BASE_URL` | LLM API 基础地址 | 否 |
|
|||
|
|
| `OPENAI_MODEL` | LLM 模型名 | 否 |
|
|||
|
|
| `OPENAI_TIMEOUT` | LLM 调用超时(秒) | 否 |
|
|||
|
|
| `OPENAI_MAX_RETRIES` | LLM 最大重试次数 | 否 |
|
|||
|
|
| `SUMMARIZE_INTERVAL_MINUTES` | 摘要任务间隔(分钟) | 否 |
|
|||
|
|
| `TAG_SCORE_INTERVAL_MINUTES` | 分类/打分/去重任务间隔(分钟) | 否 |
|
|||
|
|
| `DAILY_BRIEF_HOUR` | 每日简报生成小时 | 否 |
|
|||
|
|
| `DAILY_BRIEF_MINUTE` | 每日简报生成分钟 | 否 |
|
|||
|
|
| `TITLE_SIMILARITY_THRESHOLD` | 标题相似度阈值 | 否 |
|
|||
|
|
| `CONTENT_SIMILARITY_THRESHOLD` | 内容相似度阈值 | 否 |
|
|||
|
|
| `MAX_AI_SUMMARY_LENGTH` | AI 摘要最大长度 | 否 |
|
|||
|
|
| `MIN_ORIGINAL_SUMMARY_LENGTH` | 原始摘要最小长度 | 否 |
|
|||
|
|
| `BRIEF_TOP_N_PER_CATEGORY` | 简报每分类显示文章数 | 否 |
|
|||
|
|
| `LOG_LEVEL` | 日志级别 | 否 |
|
|||
|
|
| `API_TOKEN` | API 鉴权 Token(空则不启用) | 是 |
|
|||
|
|
| `CORS_ALLOWED_ORIGINS` | CORS 允许来源(逗号分隔) | 否 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 仪表盘统计
|
|||
|
|
|
|||
|
|
### `GET /api/stats`
|
|||
|
|
|
|||
|
|
返回仪表盘统计与下次定时任务时间。
|
|||
|
|
|
|||
|
|
**响应** `StatsOut`
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"total_articles": 200,
|
|||
|
|
"today_articles": 50,
|
|||
|
|
"ai_summarized": 180,
|
|||
|
|
"categories": 12,
|
|||
|
|
"tags": 43,
|
|||
|
|
"duplicate_groups": 5,
|
|||
|
|
"briefs": 1,
|
|||
|
|
"next_jobs": {
|
|||
|
|
"fetch_and_summarize": "2026-06-13T17:39:13+08:00",
|
|||
|
|
"tag_score_deduplicate": "2026-06-14T16:39:13+08:00",
|
|||
|
|
"generate_daily_brief": "2026-06-14T08:00:00+08:00"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 错误码
|
|||
|
|
|
|||
|
|
| 状态码 | 含义 | 触发场景 |
|
|||
|
|
|--------|------|----------|
|
|||
|
|
| `200` | 成功 | 正常请求 |
|
|||
|
|
| `400` | 参数错误 | 无效配置项 / 请求体格式错误 |
|
|||
|
|
| `401` | 未认证 | 未携带 Authorization 头(启用了鉴权时) |
|
|||
|
|
| `403` | 鉴权失败 | token 无效 |
|
|||
|
|
| `404` | 资源不存在 | 文章/简报不存在 |
|
|||
|
|
| `409` | 冲突 | 已有任务正在执行(任务全局互斥) |
|
|||
|
|
| `422` | 校验失败 | 响应模型序列化失败等 |
|
|||
|
|
| `500` | 服务器错误 | 内部异常 |
|
|||
|
|
|
|||
|
|
## 定时任务
|
|||
|
|
|
|||
|
|
服务启动后由 APScheduler 自动注册(时区 `Asia/Shanghai`):
|
|||
|
|
|
|||
|
|
| Job ID | 触发方式 | 默认 |
|
|||
|
|
|--------|----------|------|
|
|||
|
|
| `bootstrap_taxonomy` | 启动时一次(DateTrigger) | taxonomy 为空时生成 |
|
|||
|
|
| `fetch_and_summarize` | 间隔 | 每 60 分钟 |
|
|||
|
|
| `tag_score_deduplicate` | 间隔 | 每 1440 分钟(24 小时) |
|
|||
|
|
| `generate_daily_brief` | Cron | 每日 08:00 |
|
|||
|
|
|
|||
|
|
间隔参数可通过[配置接口](#配置管理接口)修改,修改后需重启服务生效。
|