---
type: pointer
title: 全站页面接口设计
summary: 按当前页面读取与提交路径复用合同，明确输入输出、分页、授权、失败恢复及不新增的接口
updated: 2026-10-08
---

# 全站页面接口设计

视觉与页面需求以Figma原稿为准；当前生成API只描述已实现合同。本轮重建前端，不修改后端端点或生成客户端。以下先定义需要承接的最小合同；标“拟议”的路径仅供后续实施，不是可调用API。旧页面依赖清单继续用于兼容核查。

## Figma页面的服务缺口与最小合同

所有聚合响应含 `snapshot_at`、`window_start/end`、`coverage`、`available`；未知字段为null，零值必须有完成覆盖依据。公开端只返回许可范围，个人端owner只取会话。无分析样本不返回虚构比例。

| 拟议合同 | 页面/输入 | 输出与数量 | 失败/一致性 |
|---|---|---|---|
| `GET /api/publication/overview` | 首页；window固定24h，category可选 | tracking_count/new_count/platform_count/negative_spike_count，前5上升事件 | 同一快照，不用50条热榜代替全量；不可用字段null，局部availability |
| 扩展 `GET /api/publication/stories/{event_id}` | 事件；固定revision | sentiment三类数量+sample_count、覆盖、代表观点最多6条、24h序列最多24点 | revision一致；证据撤权后重投影；48h现值不能改标签冒充24h |
| `GET /api/publication/search` | 探索；q/type/source_keys/window/sentiment/order/cursor，limit<=40 | 类型化结果，total仅有精确查询时返回，next_cursor | cursor绑定全部过滤和snapshot；类型未开放返回明确能力状态，非空结果中混入私有数据禁止 |
| 扩展公开刊期投影 | 日报；kind/key/revision | 固定社媒趋势<=10、重点事件、入刊观点/舆情、引用id | 使用入刊快照，不能把今天数据拼进历史刊期；按许可脱敏 |
| 扩展榜单投影 | 榜单；board/run | comparable_previous_run、rank_change、新入榜标志、最多3变化摘要 | 方法/样本口径不同不可比较；长上下文须独立发布，不改映射 |
| `GET /api/topics/{topic_id}/overview` | 监控；24h窗口 | 24小时命中桶、平台最近成功/计数/状态 | 计数基于冻结规则版本与时间；未覆盖!=0，跨owner404 |
| `PUT/DELETE /api/reading/follows/{event_id}` | 事件关注 | 关注状态、last_seen_revision、未读进展数 | 自然键幂等；不创建新事件副本，重复PUT不重置已读位置 |
| `PATCH /api/reading/alerts/{evaluation_id}` | 最近告警；read=true | read_at、version | 验证评估归属；重复已读幂等；全部已读使用snapshot截止，不能吞新告警 |
| 多类型收藏本机合同 | 收藏；kind/id/note/saved_at | 当前仅资讯实现；未来类型由可公开读取投影验证 | 先扩本机结构与迁移，不默认云服务；跨设备另立明确用户故事 |

首批实施可复用当前查询扩字段，不要求同时引入上述所有路径。每个服务切片必须先更新OpenAPI，再从同一后端生成客户端，最后接UI；文档中的路径不能手写到生成文件。409保留草稿并重读，401/403区分无会话/拒绝，429保留查询不自动重试写入，5xx给重试并保留可读旧数据标过期。

## 从页面到接口的边界

| 当前交互 | 请求/响应中必须保留 | 存储与最小服务职责 |
|---|---|---|
| 首页加载 | stories最多50、daily最新1期 | 两路读取并行、局部降级；4项全站指标目前未知，待聚合合同 |
| 探索筛选/翻页 | q/mode/by/window/category/source_key/tag/topic/order；items或cards、next_cursor、snapshot_at | 查询URL为真源；普通30/搜索40/时间线20；换条件清游标，不拉全库 |
| 公开正文 | content_id → id/revision、许可、正文可读模式、媒体、来源、关联 | 由服务端固定发布范围解释ID；禁止客户端owner参数决定可读范围 |
| 收藏 | 本机ID/日期 → 每页20项公开详情，逐项处理撤回 | 无服务器收藏写入；如有瓶颈先验证批读需求，不建同步服务 |
| 主题创建/编辑 | name/三组关键词/source_keys/editorial_profile_ids/频率/report_time/周报/通知；编辑expected_version | 新建只保存，更新比较版本；服务返回timezone，页面不是任意时区编辑器 |
| 主题运行 | topic_id + operation_id + source_keys → topic_version、各来源job_ids或skip | 受理不是采集成功；重试保留相同输入/键；不用前端定时器调度外部采集 |
| 内容/评论读取 | content_id、root_id或parent_id、cursor/limit → 可读内容、父关系、next_cursor | 每页20；父缺失有状态；身份/版本/观察分开 |
| 评论刷新 | 单内容readiness → 明确run请求 → job_id | 沿用任务受理与恢复；查看旧评论不是刷新成功 |
| 告警保存 | operation_id/expected_revision/name/topic+rule_version/metric/threshold/cooldown/target+revision/enabled；heat_increment需事件 | 规则、评估与投递分离；没有已读状态合同 |
| 私有事件修订 | event_id+预期revision+操作/对象/原因 | 复用固定成员与revision_operations，不用页面覆盖原事实 |
| 文档草稿/发布 | path、base/source/draft版本、expected_revision、operation_id、snapshot_id | 允许清单分读/写/发布；源文件和快照已有独立流程，不复用业务content库 |

## 现有调用合同

每页的字段裁剪是展示模型，不应手写一份与后端脱节的新API类型。以下类型的完整定义在[生成类型](../../../../frontend/src/api/typings.d.ts)，约束/默认值/错误见对应后端。`${param0}`等是生成客户端模板路径，正式HTTP路径参数语义以其params类型为准。

### 首页与探索

| 函数 | 方法 / 路径 | 输入 → 输出 | 合同源 |
|---|---|---|---|
| `listPublicItems` | `GET /api/publication/items` | `listPublicItemsParams` → `PublicItemsPage` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicHotStories` | `GET /api/publication/hot` | `getPublicHotStoriesParams` → `PublicStoriesPage` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicTopicDirectory` | `GET /api/publication/topics` | `无业务入参` → `PublicTopicDirectoryView` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicTopicPage` | `GET /api/publication/topics/{slug}` | `getPublicTopicPageParams` → `PublicTopicPageView` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicReadingTimeline` | `GET /api/publication/timeline` | `getPublicReadingTimelineParams` → `PublicTimelinePage` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicStory` | `GET /api/publication/stories/{event_id}` | `getPublicStoryParams` → `PublicStoryView` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicStoryDevelopments` | `GET /api/publication/stories/{event_id}/developments` | `getPublicStoryDevelopmentsParams` → `PublicDevelopmentsPage` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicFactReports` | `GET /api/publication/facts/{fact_id}/reports` | `getPublicFactReportsParams` → `PublicFactReportsPage` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getSitePublicationItem` | `GET /api/publication/items/{content_id}/site` | `getSitePublicationItemParams` → `PublicItemDetailView` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |

### 刊物与榜单

| 函数 | 方法 / 路径 | 输入 → 输出 | 合同源 |
|---|---|---|---|
| `listPublicEditionCatalogue` | `GET /api/publication/catalogue/editions` | `listPublicEditionCatalogueParams` → `PublicEditionCatalogueView` | [生成函数](../../../../frontend/src/api/gongkaikanwumulu.ts) · [后端](../../../../backend/app/api/routers/publication_editions.py) |
| `getPublicEdition` | `GET /api/publication/editions/{kind}/{key}` | `getPublicEditionParams` → `PublicEditionView` | [生成函数](../../../../frontend/src/api/gongkaifabu.ts) · [后端](../../../../backend/app/api/routers/publication.py) |
| `getPublicEditionNavigation` | `GET /api/publication/catalogue/editions/{kind}/navigation/{key}` | `getPublicEditionNavigationParams` → `PublicEditionNavigationView` | [生成函数](../../../../frontend/src/api/gongkaikanwumulu.ts) · [后端](../../../../backend/app/api/routers/publication_editions.py) |
| `getPublicDailyCalendar` | `GET /api/publication/catalogue/editions/daily/months/{month}` | `getPublicDailyCalendarParams` → `PublicDailyCalendarView` | [生成函数](../../../../frontend/src/api/gongkaikanwumulu.ts) · [后端](../../../../backend/app/api/routers/publication_editions.py) |
| `getLeaderboardBoard` | `GET /api/leaderboard/boards/{board}` | `getLeaderboardBoardParams` → `BoardView` | [生成函数](../../../../frontend/src/api/moxingbang.ts) · [后端](../../../../backend/app/api/routers/leaderboard.py) |
| `getLeaderboardModel` | `GET /api/leaderboard/models/{slug}` | `getLeaderboardModelParams` → `ModelDetailView` | [生成函数](../../../../frontend/src/api/moxingbang.ts) · [后端](../../../../backend/app/api/routers/leaderboard.py) |
| `listLeaderboardSources` | `GET /api/leaderboard/sources` | `无业务入参` → `SourcesView` | [生成函数](../../../../frontend/src/api/moxingbang.ts) · [后端](../../../../backend/app/api/routers/leaderboard.py) |
| `getLeaderboardSource` | `GET /api/leaderboard/sources/{source_key}` | `getLeaderboardSourceParams` → `SourceDetailView` | [生成函数](../../../../frontend/src/api/moxingbang.ts) · [后端](../../../../backend/app/api/routers/leaderboard.py) |
| `getLeaderboardRules` | `GET /api/leaderboard/rules` | `无业务入参` → `RulesView` | [生成函数](../../../../frontend/src/api/moxingbang.ts) · [后端](../../../../backend/app/api/routers/leaderboard.py) |

### 身份与主题

| 函数 | 方法 / 路径 | 输入 → 输出 | 合同源 |
|---|---|---|---|
| `getLoginOptions` | `GET /api/identity/options` | `无业务入参` → `LoginOptionsView` | [生成函数](../../../../frontend/src/api/identity.ts) · [后端](../../../../backend/app/api/routers/identity.py) |
| `createIdentitySession` | `POST /api/identity/sessions` | `IdentityPasswordLoginInput` → `IdentitySessionView` | [生成函数](../../../../frontend/src/api/identity.ts) · [后端](../../../../backend/app/api/routers/identity.py) |
| `sendEmailLoginCode` | `POST /api/identity/email/challenges` | `EmailCodeInput` → `EmailChallengeView` | [生成函数](../../../../frontend/src/api/identity.ts) · [后端](../../../../backend/app/api/routers/identity.py) |
| `verifyEmailLoginCode` | `POST /api/identity/email/sessions` | `VerifyEmailCodeInput` → `IdentitySessionView` | [生成函数](../../../../frontend/src/api/identity.ts) · [后端](../../../../backend/app/api/routers/identity.py) |
| `startGithubLogin` | `POST /api/identity/github/authorize` | `GithubAuthorizationInput` → `GithubAuthorizationView` | [生成函数](../../../../frontend/src/api/identity.ts) · [后端](../../../../backend/app/api/routers/identity.py) |
| `listMonitorTopics` | `GET /api/topics` | `listMonitorTopicsParams` → `PageViewMonitorTopicView_` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `getMonitorTopic` | `GET /api/topics/{topic_id}` | `getMonitorTopicParams` → `MonitorTopicView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `createMonitorTopic` | `POST /api/topics` | `MonitorTopicCreateInput` → `MonitorTopicView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `updateMonitorTopic` | `PATCH /api/topics/{topic_id}` | `updateMonitorTopicParams, MonitorTopicUpdateInput` → `MonitorTopicView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `previewMonitorTopic` | `POST /api/topics/preview` | `MonitorTopicPreviewInput` → `MonitorTopicPreviewView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `previewMonitorTopicSamples` | `POST /api/topics/sample-preview` | `ContentSamplePreviewInput` → `ContentSamplePreviewView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `runMonitorTopic` | `POST /api/topics/{topic_id}/runs` | `runMonitorTopicParams, MonitorTopicRunInput` → `MonitorTopicRunView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `pauseMonitorTopic` | `POST /api/topics/{topic_id}/pause` | `pauseMonitorTopicParams` → `MonitorTopicView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `resumeMonitorTopic` | `POST /api/topics/{topic_id}/resume` | `resumeMonitorTopicParams` → `MonitorTopicView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `archiveMonitorTopic` | `POST /api/topics/{topic_id}/archive` | `archiveMonitorTopicParams` → `MonitorTopicView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |
| `cloneMonitorTopic` | `POST /api/topics/{topic_id}/clone` | `cloneMonitorTopicParams` → `MonitorTopicView` | [生成函数](../../../../frontend/src/api/jiankongzhuti.ts) · [后端](../../../../backend/app/api/routers/monitor_topics.py) |

### 结果、评论、来源和恢复

| 函数 | 方法 / 路径 | 输入 → 输出 | 合同源 |
|---|---|---|---|
| `listContentRecords` | `GET /api/contents` | `listContentRecordsParams` → `PageViewContentRecordSummaryView_` | [生成函数](../../../../frontend/src/api/zuopinziliao.ts) · [后端](../../../../backend/app/api/routers/content_records.py) |
| `getContentRecord` | `GET /api/contents/{content_id}` | `getContentRecordParams` → `ContentRecordDetailView` | [生成函数](../../../../frontend/src/api/zuopinziliao.ts) · [后端](../../../../backend/app/api/routers/content_records.py) |
| `listContentComments` | `GET /api/contents/{content_id}/comments` | `listContentCommentsParams` → `PageViewContentCommentView_` | [生成函数](../../../../frontend/src/api/zuopinziliao.ts) · [后端](../../../../backend/app/api/routers/content_records.py) |
| `getContentCommentRunReadiness` | `GET /api/contents/{content_id}/comment-run-readiness` | `getContentCommentRunReadinessParams` → `CommentRunReadinessView` | [生成函数](../../../../frontend/src/api/zuopinziliao.ts) · [后端](../../../../backend/app/api/routers/content_records.py) |
| `runContentComments` | `POST /api/contents/{content_id}/comment-runs` | `runContentCommentsParams, CommentManualRunInput` → `JobAcceptedView` | [生成函数](../../../../frontend/src/api/zuopinziliao.ts) · [后端](../../../../backend/app/api/routers/content_records.py) |
| `listSourceCapabilities` | `GET /api/source-capabilities` | `无业务入参` → `PageViewSourcePlatformView_` | [生成函数](../../../../frontend/src/api/laiyuannengli.ts) · [后端](../../../../backend/app/api/routers/source_capabilities.py) |
| `updateSourceConnection` | `PUT /api/source-connections/{source_key}` | `updateSourceConnectionParams, SourceConnectionUpdateInput` → `SourceConnectionView` | [生成函数](../../../../frontend/src/api/laiyuannengli.ts) · [后端](../../../../backend/app/api/routers/source_connections.py) |
| `connectBilibiliChrome` | `POST /api/source-connections/bilibili/chrome` | `无业务入参` → `SourcePresetApplyView` | [生成函数](../../../../frontend/src/api/laiyuannengli.ts) · [后端](../../../../backend/app/api/routers/source_connections.py) |
| `listCollectionJobs` | `GET /api/jobs` | `listCollectionJobsParams` → `PageViewJobHistoryItemView_` | [生成函数](../../../../frontend/src/api/caijirenwu.ts) · [后端](../../../../backend/app/api/routers/collection_jobs.py) |
| `getCollectionJob` | `GET /api/jobs/{job_id}` | `getCollectionJobParams` → `JobStatusView` | [生成函数](../../../../frontend/src/api/caijirenwu.ts) · [后端](../../../../backend/app/api/routers/collection_jobs.py) |
| `cancelCollectionJob` | `POST /api/jobs/{job_id}/cancel` | `cancelCollectionJobParams` → `JobStatusView` | [生成函数](../../../../frontend/src/api/caijirenwu.ts) · [后端](../../../../backend/app/api/routers/collection_jobs.py) |
| `retryCollectionJob` | `POST /api/jobs/{job_id}/retry` | `retryCollectionJobParams` → `JobStatusView` | [生成函数](../../../../frontend/src/api/caijirenwu.ts) · [后端](../../../../backend/app/api/routers/collection_jobs.py) |
| `listCollectionCoverage` | `GET /api/collection-coverage` | `listCollectionCoverageParams` → `PageViewCollectionCoverageView_` | [生成函数](../../../../frontend/src/api/caijifugai.ts) · [后端](../../../../backend/app/api/routers/collection_coverage.py) |
| `getCollectionCoverage` | `GET /api/collection-coverage/{window_id}` | `getCollectionCoverageParams` → `CollectionCoverageView` | [生成函数](../../../../frontend/src/api/caijifugai.ts) · [后端](../../../../backend/app/api/routers/collection_coverage.py) |
| `getCollectionCoverageMetrics` | `GET /api/collection-coverage/metrics` | `getCollectionCoverageMetricsParams` → `CollectionCoverageMetricsView` | [生成函数](../../../../frontend/src/api/caijifugai.ts) · [后端](../../../../backend/app/api/routers/collection_coverage.py) |

### 告警、报告与文档

| 函数 | 方法 / 路径 | 输入 → 输出 | 合同源 |
|---|---|---|---|
| `listAlerts` | `GET /api/alerts` | `无业务入参` → `AlertRuleView[]` | [生成函数](../../../../frontend/src/api/gerentufagaojing.ts) · [后端](../../../../backend/app/api/routers/alerts.py) |
| `createAlert` | `POST /api/alerts` | `AlertRuleInput` → `AlertRuleView` | [生成函数](../../../../frontend/src/api/gerentufagaojing.ts) · [后端](../../../../backend/app/api/routers/alerts.py) |
| `updateAlert` | `PUT /api/alerts/{rule_id}` | `updateAlertParams, AlertRuleInput` → `AlertRuleView` | [生成函数](../../../../frontend/src/api/gerentufagaojing.ts) · [后端](../../../../backend/app/api/routers/alerts.py) |
| `listAlertHistory` | `GET /api/alerts/{rule_id}/history` | `listAlertHistoryParams` → `AlertEvaluationView[]` | [生成函数](../../../../frontend/src/api/gerentufagaojing.ts) · [后端](../../../../backend/app/api/routers/alerts.py) |
| `listReports` | `GET /api/reports` | `listReportsParams` → `PageViewReportSummaryView_` | [生成函数](../../../../frontend/src/api/ribao.ts) · [后端](../../../../backend/app/api/routers/reports.py) |
| `getReport` | `GET /api/reports/{report_id}` | `getReportParams` → `ReportDetailView` | [生成函数](../../../../frontend/src/api/ribao.ts) · [后端](../../../../backend/app/api/routers/reports.py) |
| `listReportEditions` | `GET /api/editions` | `listReportEditionsParams` → `EditionSummaryView[]` | [生成函数](../../../../frontend/src/api/rizhouyuekan.ts) · [后端](../../../../backend/app/api/routers/report_editions.py) |
| `requestReportEdition` | `POST /api/editions` | `EditionRequestInput` → `EditionDetailView` | [生成函数](../../../../frontend/src/api/rizhouyuekan.ts) · [后端](../../../../backend/app/api/routers/report_editions.py) |
| `getReportEdition` | `GET /api/editions/{edition_id}` | `getReportEditionParams` → `EditionDetailView` | [生成函数](../../../../frontend/src/api/rizhouyuekan.ts) · [后端](../../../../backend/app/api/routers/report_editions.py) |
| `correctReportEdition` | `POST /api/editions/{edition_id}/corrections` | `correctReportEditionParams, EditionCorrectionInput` → `EditionDetailView` | [生成函数](../../../../frontend/src/api/rizhouyuekan.ts) · [后端](../../../../backend/app/api/routers/report_editions.py) |
| `listWorkspaceDocuments` | `GET /api/workspace/documents` | `listWorkspaceDocumentsParams` → `WorkspaceCatalogView` | [生成函数](../../../../frontend/src/api/xiangmuwendang.ts) · [后端](../../../../backend/app/api/routers/workspace_documents.py) |
| `searchWorkspaceDocuments` | `GET /api/workspace/documents/search` | `searchWorkspaceDocumentsParams` → `WorkspaceSearchView` | [生成函数](../../../../frontend/src/api/xiangmuwendang.ts) · [后端](../../../../backend/app/api/routers/workspace_documents.py) |
| `getWorkspaceDocument` | `GET /api/workspace/documents/document` | `getWorkspaceDocumentParams` → `WorkspaceDocumentView` | [生成函数](../../../../frontend/src/api/xiangmuwendang.ts) · [后端](../../../../backend/app/api/routers/workspace_documents.py) |
| `getWorkspaceDocumentDraft` | `GET /api/workspace/documents/draft` | `getWorkspaceDocumentDraftParams` → `WorkspaceDraftView` | [生成函数](../../../../frontend/src/api/xiangmuwendang.ts) · [后端](../../../../backend/app/api/routers/workspace_documents.py) |
| `saveWorkspaceDocumentDraft` | `PUT /api/workspace/documents/draft` | `WorkspaceSaveInput` → `WorkspaceDraftView` | [生成函数](../../../../frontend/src/api/xiangmuwendang.ts) · [后端](../../../../backend/app/api/routers/workspace_documents.py) |
| `publishWorkspaceDocument` | `POST /api/workspace/documents/publish` | `WorkspacePublishInput` → `WorkspaceOperationView` | [生成函数](../../../../frontend/src/api/xiangmuwendang.ts) · [后端](../../../../backend/app/api/routers/workspace_documents.py) |
| `getWorkspaceDocumentOperation` | `GET /api/workspace/documents/operations/{operation_id}` | `getWorkspaceDocumentOperationParams` → `WorkspaceOperationView` | [生成函数](../../../../frontend/src/api/xiangmuwendang.ts) · [后端](../../../../backend/app/api/routers/workspace_documents.py) |

## 核心写入的字段和校验

| 操作 | 必填/关键字段 | 页面校验与服务器判断 | 成功/失败恢复 |
|---|---|---|---|
| 创建主题 | name、match_any/match_all/exclude；来源与频率/报告配置按现有默认 | 词条使用KeywordInput；名字和规则限制沿用schema；来源必须实际准入 | 返回MonitorTopicView再跳详情。当前创建合同没有operation_id，不承诺自动幂等；响应丢失先重读列表核对，不能盲目重发 |
| 修改主题 | 创建字段+expected_version | 服务比较当前版本；主题owner从会话取得 | 返回新current_version；409保留草稿并提示重读，不静默覆盖 |
| 立即运行 | operation_id（UUID）、source_keys；topic_id路径参数 | 主题状态/来源版本/预算/准入以服务器为准 | 返回每个来源job_ids或skip；同一次操作复用原键；刷新后的完整批次回执目前没有独立读取合同，列为缺口 |
| 保存告警 | operation_id、expected_revision、topic_id/topic_rule_version、metric、threshold、cooldown_seconds、target_id/target_revision | metric为negative_count或heat_increment；前者计数阈值应为整数，后者关联事件；具体范围由已有合同校验 | 返回AlertRuleView；冲突重读规则/主题/目标；不要借保存动作重复发送通知 |
| 反馈 | operation_id、content；email/page_url/screenshot可选 | 当前UI正文2–5000字符、邮箱≤200、页面URL≤500、截图≤8MiB且类型受限；服务器仍必须验证 | 返回编号；失败留输入和原操作号；不自动转发到外部应用 |
| 取消/恢复Job | job_id与现有控制输入 | 服务器判断状态、manual_retry_allowed、租约和冻结来源条件 | 成功后重读原任务；取消只能阻止后续步骤，不等于撤销已发外部请求 |

账户密码和验证码、运营令牌均不属于页面持久字段；只通过既有认证合同传递，不写URL、日志、导出和本机长期存储。

## 错误、授权与重试

统一使用`ApiRequestError`的HTTP状态和code分支，message仅为显示文案。各接口实际错误集以路由定义为准，不给每个端点机械添加相同状态码。

| 情况 | 页面行为 |
|---|---|
| 401/会话失效 | 清除当前私有视图，安全站内回跳登录；不靠缓存继续展示敏感详情 |
| 403/独立管理权限不足 | 显示无权限或所需配置，普通登录不等于运营权限 |
| 404/资源不存在或不可读 | 不暴露另一owner的标题；公开撤回不能回退到旧正文 |
| 409/版本或操作冲突 | 保留用户输入，重读服务版本；同键异参不当成成功 |
| 422/输入不合规则 | 关联具体表单字段；页面校验不能代替服务校验 |
| 429/限流或冷却 | 展示实际等待/停止原因，不循环自动重试写入 |
| 503/数据库、来源或服务不可用 | 与无记录区分；独立区块单独失败，已有合法数据标记过期 |
| 网络断开、超时、请求取消 | 读取允许重试，旧响应不能覆盖新筛选；写入可能已提交，按原合同查结果，不假定“没发出” |

私有读取不进入公开缓存。公开读也必须复核许可/修订。所有生成请求经过现有request.ts，业务不直接fetch/Axios；仅调整页面字段使用时不改变网络层或创建第二套SDK。

## 避免重复建设

全站统计、情感、关注和告警已读按上方原稿需求承接；优先扩充现有读取投影，不为每个控件新建服务。不新增通用操作账本、独立运行批次/来源批次状态机；静态说明不建CRUD。本机备注不需要云写入，跨设备同步需独立用户故事。

增量必须附页面、控件、输入输出、统计口径、现有接口不足、最小存储与可复跑验收。后端合同变更后生成客户端，不手改生成物；真实来源与送达另行验收。
