OPENAPI · V1
通过本接口,已授权的服务端应用可以查询和发布官网知识库文章与资讯,并查询知识库页面的访问统计。新增内容直接发布到 COS 内容快照,经官网现有接口和页面对外提供。
生产 API 地址:https://project.xiaotunqifu.com。请求使用 HTTPS、POST 和 Content-Type: application/json。
| 接口 | 路径 | 说明 |
|---|---|---|
| 知识库文章列表 | /api/openapi/knowledge/list | 分页查询已发布知识库文章元数据 |
| 新增知识库文章 | /api/openapi/knowledge/create | 发布中英双语知识库文章(/faq) |
| 知识库访问统计 | /api/openapi/knowledge/statistics | 按时间段查询知识库页面访问表现 |
| 资讯列表 | /api/openapi/news/list | 分页查询资讯元数据 |
| 新增资讯 | /api/openapi/news/create | 发布官网资讯(/article、/info) |
| 更新资讯状态 | /api/openapi/news/update | 撤稿、撤下列表、分端显示与恢复上架 |
所有接口使用同一套凭证:分配的 KNOWLEDGE_RELEASE_SECRET_ID 和 KNOWLEDGE_RELEASE_SECRET_KEY。Secret Key 是 64 位十六进制字符串,解码为 32 字节密钥。凭证应在调用方服务端配置;网页端不需要持有凭证。
请求和成功响应使用 AES-256-GCM。UTF-8 编码的 JSON 为明文,每次加密生成新的 12 字节随机 IV,认证标签为 16 字节。iv、ciphertext、tag 使用标准 Base64 编码(带 padding),标签独立于密文。
请求 JSON 包含以下字段:
| 字段 | 类型与含义 |
|---|---|
| secretId | 分配的 Secret ID |
| timestamp | Unix 时间戳,整数,单位秒;允许前后 300 秒偏差 |
| nonce | 每次请求唯一的 16–128 位字符串,仅字母、数字、下划线、短横线;推荐 24 字节随机数的 Base64URL 编码 |
| iv | 12 字节随机 IV 的 Base64 |
| ciphertext | 加密后 JSON 的 Base64 |
| tag | 16 字节 GCM 认证标签的 Base64 |
附加认证数据 AAD 是以下各行用 \n 连接后的 UTF-8 字节,末尾没有换行。path 为第 1 节表中本次调用的完整路径,不包含域名、查询参数或末尾斜杠;每个接口的 AAD 绑定各自路径,为一个接口加密的请求不能用于另一个接口:
BW-RELEASE-V1
request
POST
{path}
{secretId}
{timestamp}
{nonce}
成功响应为 {"code":0,"status":200,"data":{...加密信封...}}。取出 data,使用同一密钥解密;AAD 第二行改为 response,时间戳使用响应中的值,其余规则相同。响应 nonce 与本次请求一致。必须校验 GCM tag,并确认 Secret ID、nonce 和时间戳后再使用结果。HTTP 错误返回未加密的错误对象,含 message 和 status。
每个 nonce 仅可使用一次。超时或重试时重新生成 nonce、时间戳和 IV,文章 ID 与文章内容保持一致。Node.js Crypto 文档提供 GCM、AAD 和认证标签的实现说明。
| HTTP 状态 | 含义与处理 |
|---|---|
| 401 | 凭证、加密认证、时间戳错误,或 nonce 已使用;检查配置并重新加密 |
| 409 | 正在发布、ID 内容冲突、URL 冲突或分页版本变化;按错误信息处理 |
| 422 | 文章字段、资讯字段或分页参数无效;修正明文后重新加密 |
| 503 | 依赖服务不可用或发布结果待确认;稍后用相同 ID 和内容重试 |
POST /api/openapi/knowledge/list入参(加密前的 JSON):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | integer | 否 | 默认 1,范围 1–100000 |
| size | integer | 否 | 默认 20,范围 1–100 |
| version | string | 续页必填 | 传入第一页返回的 version;发布版本变化时返回 409,需从第一页重新查询 |
{"page":1,"size":20}
解密后返回 version、page、size、total 和 records。每条记录包含 id、routeSlug、aliases、hash 及 zh、en 元数据(标题、摘要、分类、标签、更新时间),列表不含正文。中文地址为 /faq/{routeSlug},英文地址为 /en/faq/{routeSlug}。
POST /api/openapi/knowledge/create入参:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 调用方生成并固定;faq-api- 加小写字母、数字及单个短横线分隔的词,总长不超过 150 |
| zh / en | object | 是 | 中英双语内容,字段相同,见下 |
| zh.q / en.q | string | 是 | 标题,最多 200 字符;英文标题须用英文字符,系统据此生成稳定英文 URL |
| zh.summary / en.summary | string | 是 | 摘要,最多 2000 字符 |
| zh.category / en.category | string | 是 | 分类,最多 100 字符 |
| zh.updatedAt / en.updatedAt | string | 是 | ISO 日期时间 |
| zh.a / en.a | string | 是 | Markdown 正文,最多 200000 字符 |
| zh.tags / en.tags | string[] | 是 | 最多 20 个非空字符串,每项最多 80 字符,可传空数组 |
{
"id": "faq-api-crm-approval-guide",
"zh": {
"q": "CRM 审批流程如何设计?",
"summary": "从审批角色、权限和记录追溯规划 CRM 审批。",
"category": "软件定制",
"tags": ["CRM", "审批流程"],
"updatedAt": "2026-09-24T10:00:00+08:00",
"a": "## 审批角色\n\n先确定发起人、审批人和可见范围。"
},
"en": {
"q": "How to design CRM approval workflows",
"summary": "Plan approval roles, permissions and an auditable history for your CRM.",
"category": "Custom Software",
"tags": ["CRM", "Approvals"],
"updatedAt": "2026-09-24T10:00:00+08:00",
"a": "## Approval roles\n\nDefine who submits, who approves and who can view each request."
}
}
本接口提供新增功能。相同 ID、相同内容可安全重试;相同 ID、不同内容返回 409;URL 与现有文章或专题冲突时返回 409。
成功解密后的结果示例:
{
"id": "faq-api-crm-approval-guide",
"version": "64位发布版本摘要",
"created": true,
"published": true,
"status": "complete",
"pending": [],
"urls": {
"zh": "/faq/how-to-design-crm-approval-workflows",
"en": "/en/faq/how-to-design-crm-approval-workflows"
}
}
published: true 表示 COS 已发布;status: complete 表示搜索与网页缓存同步完成。status: pending 时,pending 会包含 search 或 website-cache,可再次提交相同文章恢复同步。重复提交时 created: false。若请求超时或返回 503,发布结果可能尚不确定,同样使用原 ID 和原内容重试。
POST /api/openapi/knowledge/statistics查询官网 boilingwater.cn 知识库页面在所选时间段内的访问表现,中文和英文路径分别统计。
入参(传入 {} 使用默认条件):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| startDate / endDate | string | 否 | 同时传入 YYYY-MM-DD,按上海时区包含首尾日期;最多 366 天,不允许未来日期。省略时查询含今天的最近 30 天 |
| locale | string | 否 | all(默认)、zh 或 en |
| path | string | 否 | 精确页面路径,例如 /faq/example 或 /en/faq/example,不含域名、查询参数或末尾斜杠 |
| keyword | string | 否 | 标题或路径的包含匹配,忽略大小写,最多 500 字符 |
| sortBy | string | 否 | views(默认,访问量)、visitors(去重访客数)、durationMs(总可见停留毫秒数)、averageMs(每次访问平均可见停留毫秒数) |
| sortOrder | string | 否 | desc(默认)或 asc |
| page | integer | 否 | 默认 1,范围 1–100000 |
| size | integer | 否 | 默认 20,范围 1–100 |
返回示例(解密后):
{
"generatedAt": "2026-09-27T02:00:00.000Z",
"site": "boilingwater.cn",
"timezone": "Asia/Shanghai",
"startDate": "2026-09-24",
"endDate": "2026-09-26",
"startedAt": "2026-09-01T01:00:00.000Z",
"visitorsStartedAt": "2026-09-23T01:00:00.000Z",
"visitorsComplete": true,
"page": 1,
"size": 10,
"sortBy": "views",
"sortOrder": "desc",
"total": 1,
"totalPages": 1,
"records": [{
"path": "/faq/example", "title": "示例文章", "locale": "zh",
"views": 12, "visitors": 8, "durationMs": 240000, "averageMs": 20000
}]
}
total 是筛选后的页面数;超过总页数返回空 records。先聚合完整日期范围,再筛选、排序、分页;同分按路径排序,缺失指标始终排在最后。统计会随访问继续更新,跨页查询不是冻结快照。
访问量来自官网浏览器统计采集,不合并旧版文章访问计数;心跳和重试不会增加访问量。停留时间只累计页面可见时上报的时长,按访问开始日归属;averageMs = durationMs / views,四舍五入到毫秒,没有访问样本时为 null。统计包含已采集的全部流量,未进行机器人过滤。
visitors 按浏览器标识跨所选日期去重,同一访客在同一页面多次访问只计一次;它不等于实名人数,也不能通过累加不同页面访客数得到全站人数。页面人数从本功能上线后的采集起点开始记录;visitorsStartedAt 为起点,无采集时为 null。若查询包含起点当日或更早日期,visitorsComplete=false 且 visitors=null,历史缺失不作零处理。选择起点之后的完整日期范围可按人数比较;今天的数据仍会增长。startedAt 为整体浏览器统计采集起点,早于该起点的数据不完整。
本接口仅返回时间段内有采集记录的知识库路径,可能包含曾经访问的历史页面,不代表当前已发布文章清单。需要文章元数据时另用 list。筛选字段非法返回 HTTP 400;日期范围非法返回 HTTP 422。查询不会发布或修改文章。
POST /api/openapi/news/list分页查询官网资讯(资讯中心)元数据,按 createTime 与 id 倒序。
入参:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | integer | 否 | 默认 1,范围 1–100000 |
| size | integer | 否 | 默认 20,范围 1–100 |
| version | string | 续页必填 | 传入第一页返回的 version;快照版本变化时返回 409,需从第一页重新查询 |
解密后返回 version、page、size、total 和 records。每条记录为一条资讯的元数据:id、title、description、image、type、flag、status、siteStatus、siteHiddenStatus、wxStatus、appStatus、createTime、updateTime、labels,以及可选的 url、feedId、contentType 等历史字段;列表不含正文 content。flag=0 的记录为已删除(保留但不展示),status=0 为已撤下列表。
POST /api/openapi/news/create入参:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 数字字符串,使用 8 开头的独立区间(16–19 位),与历史 ID 隔离;示例生成:node -e 'console.log(String(8000000000000000000n + BigInt(Date.now()) * 1000n))' |
| type | integer | 是 | 栏目类型:1 资讯速递、2 行业智汇、3 视频号、4 专题 |
| title | string | 是 | 标题,非空 |
| description | string | 是 | 摘要,非空 |
| image | string | 是 | 封面完整 HTTPS 地址。图片须先经既有上传通道落地(如 POST /api/file/upload),本接口不上传图片;发布前服务端实际 GET 校验:可公开访问、类型为 PNG/JPEG/WebP/AVIF/GIF、不超过 10 MiB |
| markdown | string | 二选一 | Markdown 正文,发布时转换为 HTML 入库 |
| content | string | null | 二选一 | 可信 HTML 正文;type=3 且提供 url 时可省略(纯外链/视频资讯正文为 null) |
| labels | string[] | 否 | 标签,默认 [],最多 20 个非空字符串 |
| flag | integer | 否 | 有效标记,默认 1;0 表示已删除(保留在快照但不展示) |
| status | integer | 否 | 发布状态,默认 1 上架;0 撤下列表 |
| siteStatus | integer | 否 | 官网显示,默认 1 |
| siteHiddenStatus | integer | 否 | 官网隐藏,默认 0 |
| wxStatus | integer | 否 | 小程序显示,默认 0 |
| appStatus | integer | 否 | App 显示,默认 0 |
| createTime | string | 否 | 创建时间,带时区的 ISO 时间;默认为当前上海时间(+08:00)。发布后不可变 |
| updateTime | string | 否 | 更新时间,同上;默认为当前上海时间 |
| url | string | 否 | 外链地址(type 3 视频/外链) |
| feedId | string | 否 | 视频号 feedId |
| contentType | integer | 否 | 内容类型(历史语义) |
| createBy / updateBy | string | 否 | 作者字段(历史迁移兼容) |
{
"id": "8001790000000000000",
"type": 1,
"title": "【今日资讯】示例主标题",
"description": "本期 5 条,覆盖平台规则与产品动态。",
"image": "https://file.boilingwater.cn/website/news/2026-09-27-example-cover.png",
"labels": ["日资讯"],
"markdown": "## 主资讯\n\n正文内容。"
}
本接口提供新增功能。相同 ID、相同内容可安全重试(返回 created: false);相同 ID、不同内容返回 409;正在发布时返回 409,稍后用相同内容重试。请求超时或返回 503 时发布结果可能尚不确定,使用原 ID 和原内容重试。
成功解密后的结果示例:
{
"id": "8001790000000000000",
"version": "64位快照版本摘要",
"created": true,
"published": true,
"urls": { "zh": "/article/8001790000000000000/1", "en": "/en/article/8001790000000000000/1" },
"record": { "id": "8001790000000000000", "title": "…", "content": "<h2>主资讯</h2>…" }
}
urls 规则:type 1/2/3 为 /article/{id}/{type},type 4 为 /info/{id};英文地址加 /en 前缀。record 是完整入库记录(含转换后的 HTML 正文)。
发布后须知:
/api/news 与资讯中心页面对外提供;官网 sitemap 动态读取资讯接口,无需额外更新。record 保存为 website/src/content/news/{id}.json(该目录为资讯源文件备份目录)。news.update,不要直接改动 COS 快照。POST /api/openapi/news/update更新已发布资讯的展示与撤稿状态。只接受状态字段;标题、正文、类型、创建时间等内容字段不可通过本接口修改。典型用途:撤稿、撤下列表、分端显示控制与恢复上架。
入参:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 已发布资讯 ID(历史 ID 与 8 开头新 ID 均可) |
| flag | integer | 至少一项 | 有效标记;0 撤稿(保留在快照但不展示) |
| status | integer | 至少一项 | 1 上架,0 撤下列表 |
| siteStatus | integer | 至少一项 | 官网显示 |
| siteHiddenStatus | integer | 至少一项 | 官网隐藏 |
| wxStatus | integer | 至少一项 | 小程序显示 |
| appStatus | integer | 至少一项 | App 显示 |
| updateTime | string | 否 | 带时区的 ISO 时间;默认为当前上海时间(+08:00) |
flag、status、siteStatus、siteHiddenStatus、wxStatus、appStatus 六个状态字段至少提供一个,不接受其他字段。完整撤稿通常同时传 flag: 0、status: 0、siteStatus: 0;仅隐藏列表传 status: 0(详情仍可按地址访问)。flag 是共享内容删除状态,记录同时面向小程序/App 时,仅撤官网不要顺带把多端状态置 0。
{"id":"8001790000000000000","flag":0,"status":0,"siteStatus":0}
成功解密后的结果示例:
{
"id": "8001790000000000000",
"version": "64位快照版本摘要",
"updated": true,
"published": true,
"record": { "id": "8001790000000000000", "flag": 0, "status": 0, "updateTime": "2026-09-28T09:00:00+08:00" }
}
updated: false 表示目标状态与现状一致,为空操作,不产生新版本;相同请求可安全重试。未知 ID 返回 404;正在发布时返回 409,稍后重试;超时或 503 时用相同请求重试(状态修改幂等)。状态变更最迟约 30 秒经 /api/news 列表与详情生效。
下载 release-client.mjs,它包含请求加密、HTTP 调用和响应解密,适用于 Node.js 24。将凭证设置到环境变量后:
import { readFile } from 'node:fs/promises';
import { releaseRequest } from './release-client.mjs';
// 知识库接口:action 为 list / create / statistics;
// 资讯接口:action 为 news.list / news.create。
console.log(await releaseRequest('list', { page: 1, size: 20 }));
console.log(await releaseRequest('news.list', { page: 1, size: 20 }));
// article.json 使用第 5 节结构,news.json 使用第 8 节结构;仅在准备发布时执行。
const article = JSON.parse(await readFile('./article.json', 'utf8'));
console.log(await releaseRequest('create', article));
const news = JSON.parse(await readFile('./news.json', 'utf8'));
console.log(await releaseRequest('news.create', news));
可通过 KNOWLEDGE_RELEASE_BASE_URL 指定其他已配置的 API 环境。调用函数也支持第三个参数 { baseUrl, secretId, secretKey }。