Files
dataClean/API.md
T
congsh 778ccefb22 feat: 任务进度实时展示、接口测试、暗色主题重构及多项 bug 修复
后端
- 新增 app/task_progress.py 线程安全进度注册表
- 任务改为后台线程异步执行(_run_task_background),手动触发立即返回 task_key
- 6 个任务函数(summarizer/tagger/scorer/deduplicator/brief/taxonomy)循环内上报进度
- scheduler 定时任务同步上报进度(trigger=scheduled)
- 新增 GET /api/tasks/progress 与 POST /api/tasks/progress/reset 接口
- 新增 POST /api/test-connection 接口连通性测试(独立短超时客户端)
- 修复 ai_client/rss_client 配置在 import 时固化的 bug(改为 property 运行时读取 settings),
  导致实际任务用 .env 假 key 调 LLM 401
- 修复 ai_client 对 reasoning 模型(MiniMax-M3 等)输出 <think> 块的 JSON 解析失败
- 修复 taxonomy bootstrap:LLM 超时(改用 300s 专用 client)、MiniMax 输出审查
  (精简样本仅标题 + 约束生成中性类目名)、失败误报 success(改抛异常如实标记)
- 修复 models.py 双外键关系映射启动崩溃(显式 foreign_keys)
- 修复 main.py SPA 路由 404、ArticleOut.published_at 序列化 500
- 移除 lifespan 同步 bootstrap 阻塞启动,改由 scheduler 后台异步执行

前端
- Deep Ink 高对比度暗色主题重构,修复 Element Plus 暗色模式对比度问题
- Tasks 页面任务进度实时展示(进度条/阶段/计数/状态/触发来源)+ 1.5s 轮询
- 接口测试面板(rssKeeper / LLM 连通性 + 延迟)
- 修复 nextJobs jobId 映射 bug

部署与文档
- Dockerfile 优化(BuildKit 缓存挂载、预编译 wheel、去 gcc、阿里云镜像源)
- 新增 API.md 接口文档

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-14 15:14:40 +08:00

435 lines
10 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` | 1200 |
| `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,范围 1100
**响应** `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 |
间隔参数可通过[配置接口](#配置管理接口)修改,修改后需重启服务生效。