# dataClean 接口文档 > 服务地址:`http://:7331` > 所有 `/api/*` 接口(除 `/health`)在配置了 `API_TOKEN` 时需要在请求头携带 `Authorization: Bearer `。 ## 目录 - [鉴权](#鉴权) - [健康检查](#健康检查) - [文章接口](#文章接口) - [简报接口](#简报接口) - [分类体系接口](#分类体系接口) - [任务接口](#任务接口) - [任务进度](#任务进度) - [接口连通性测试](#接口连通性测试) - [配置管理接口](#配置管理接口) - [仪表盘统计](#仪表盘统计) - [错误码](#错误码) --- ## 鉴权 若服务端配置了 `API_TOKEN`,除 `/health` 外所有接口需要在请求头携带: ``` Authorization: Bearer ``` 未携带或 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 | 间隔参数可通过[配置接口](#配置管理接口)修改,修改后需重启服务生效。