远程发布知识点、热点内容、课程和题目 · API Key 认证 · OpenAPI 3.0
/admin/user/api-keys),点击「生成密钥」,填写名称与有效期。ems_ 开头的随机字符串,明文仅在生成时展示一次,请立即复制保存。X-Api-Key: ems_a1b2c3d4…):X-Api-Key: {your_api_key}
https://bixuebihui.com/apihttp://localhost:8888/apiX-Api-Key: ems_xxx(与内容/题目/组卷接口相同,无需额外 Token)。
API 在入库前会自动过滤危险标签和属性,防止 XSS 注入。只有白名单内的标签和属性会被保留,其余内容将被静默移除。
| 类别 | 允许标签 | 说明 |
|---|---|---|
| 段落/标题 | p, br, h1~h6 | 正文段落、换行、标题 |
| 文本格式 | strong, b, em, i, u, s, del, ins | 加粗、斜体、删除线等 |
| 列表 | ul, ol, li | 无序/有序列表 |
| 引用/代码 | blockquote, pre, code | 引用块、代码块(配合 data-language)、行内代码 |
| 扩展块 | div(ql-math-block / ql-chem-block) | KaTeX 公式、化学结构;见「富文本扩展块」 |
| 链接/图片 | a, img | 链接(限 href,title,target)图片(限 src,alt,width,height) |
| 表格 | table, thead, tbody, tr, th, td | 结构化数据展示 |
| 通用 | span, div | 布局容器 |
| 禁用项 | 示例 | 原因 |
|---|---|---|
| 脚本/嵌入式内容 | script, iframe, object, embed | XSS 攻击、任意网页嵌入 |
| 表单控件 | form, input, button, textarea, select | 钓鱼/伪造表单提交 |
| 样式/元数据 | style, link, meta, base | CSS 注入、页面劫持 |
| 事件处理器 | onclick, onerror, onload 等所有 on* 属性 | JavaScript 执行 |
| javascript: 伪协议 | href="javascript:alert(1)" | 链接型 XSS |
⚠️ 过滤在后端(Java OpenApiController)执行。扩展块使用的 div / pre 及 data-* 属性在白名单内,详见下方「富文本扩展块」。
通过 Open API 发布内容时,字段 text(及题目的 questionDescribe)除常规 HTML 外,可嵌入与管理后台富文本编辑器相同的扩展块。
前台阅读页会自动渲染语法高亮、KaTeX 公式与化学结构图。
使用 <pre> + data-language,块内写纯文本源码(勿预置 highlight 的 span 标签)。
<pre data-language="python" class="ql-syntax">def hello():
print("world")</pre>
常见 data-language:plaintext, javascript, typescript, python, java, c, cpp, csharp, go, rust, php, html, css, json, yaml, markdown, sql, bash, latex, r, matlab 等。
独立行间公式,data-latex 为 LaTeX 字符串(JSON 中反斜杠需转义)。
<div class="ql-math-block" data-latex="E=mc^2"></div>
<div class="ql-math-block" data-latex="\int_0^1 x^2 \, dx = \frac{1}{3}"></div>
| data-chem-format | 说明 | data-chem-source |
|---|---|---|
smiles | 推荐;自动绘制 2D 结构 | SMILES 字符串,如 CCO(乙醇)、c1ccccc1(苯) |
mol | MOL 文件(V2000/V3000) | 文件全文(ChemDraw 可导出) |
cdxml | ChemDraw 原生 XML | 存原文;前台可能仅展示源码,建议优先 SMILES/MOL |
可选属性 data-chem-caption:结构说明或化合物名称。
<div class="ql-chem-block"
data-chem-format="smiles"
data-chem-source="CCO"
data-chem-caption="乙醇"></div>
<h2>乙醇氧化反应</h2>
<p>结构式如下:</p>
<div class="ql-chem-block" data-chem-format="smiles" data-chem-source="CCO"></div>
<p>反应式:</p>
<div class="ql-math-block" data-latex="C_2H_5OH + \frac{3}{2}O_2 \rightarrow 2CO_2 + 3H_2O"></div>
<p>示例代码:</p>
<pre data-language="python" class="ql-syntax">print("CCO")</pre>
在管理后台用富文本编辑器(工具栏:代码块 </>、公式 ∑、化学式 ⬡)编辑后保存,也可直接 GET /open/content/{id} 查看返回 HTML 作为模板。
内容的增删改查均限定为当前 API Key 所属用户创建的记录(content_user_id)。
查询/修改/删除他人内容时统一返回 404。
| 方法 | 路径 | 说明 | 需要 Key |
|---|---|---|---|
| POST | /open/upload | 上传封面图(multipart,字段 file) | 是 |
| POST | /open/content | 发布知识点或热点文章(归属当前 Key 用户) | 是 |
| GET | /open/content/{id} | 按 ID 查询内容(仅本人创建) | 是 |
| PUT | /open/content/{id} | 更新内容(仅本人创建,部分更新) | 是 |
| DELETE | /open/content/{id} | 删除内容(软删,仅本人创建) | 是 |
| 方法 | 路径 | 说明 | 需要 Key |
|---|---|---|---|
| GET | /open/questions | 题目列表(分页、关键词、难度、题型、knowsId 知识点过滤) | 是 |
| POST | /open/questions/batch | 批量发布题目(最多 100 道) | 是 |
| POST | /open/questions/batch-knows-id | 按题号批量设置 knowsId(组卷知识点关联) | 是 |
| DELETE | /open/questions/{id} | 软删除题目 | 是 |
组卷器按 x2_knows 知识点从题库抽题。典型流程:
查知识点 ID → 导入/已有题目 → 批量打 knowsId → 查库存验证。
以下接口均使用同一 X-Api-Key,不对外暴露管理后台 JWT。
| 方法 | 路径 | 说明 | 需要 Key |
|---|---|---|---|
| GET | /open/exam/knows | 按章节列出知识点(默认 sectionId=208 Java面试) | 是 |
| GET | /open/exam/knows/interview | IT基础(207) + Java面试(208) 全部组卷知识点 | 是 |
| GET | /open/exam/inventory | 各知识点下客观题库存(按难度分层) | 是 |
| POST | /open/questions/batch-knows-id | 批量设置题目的 question_knows_id | 是 |
Java 面试常用 knowsId(section 208):
2030 Java语言基础 · 2031 集合与IO · 2032 并发与JVM ·
2033 Spring · 2034 网络 · 2035 数据库。
以 GET /open/exam/knows?sectionId=208 返回为准。
面向访客的模拟面试流程,不使用 X-Api-Key,答案不在列表接口返回,判分走服务端。
页面入口:/practice。
| 方法 | 路径 | 说明 | 需要 Key |
|---|---|---|---|
| GET | /public/practice/knows | 组卷知识点列表 | 否 |
| GET | /public/practice/inventory | 题量库存(含关联 KU) | 否 |
| GET | /public/practice/questions | 脱敏题目列表 | 否 |
| POST | /public/practice/generate | 生成自测试卷 | 否 |
| POST | /public/practice/submit | 交卷判分 + 雷达图 | 否 |
课程介绍 HTML 使用 CourseHtmlSanitizer(与内容 Open API 不同):允许 B 站 / YouTube 等视频平台 iframe,仍禁止 script 等危险标签。
| 方法 | 路径 | 说明 | 需要 Key |
|---|---|---|---|
| GET | /open/course-categories | 查询课程分类与科目(获取 categoryId) | 是 |
| POST | /open/courses | 发布课程(标题、富文本 describe、封面、视频/PDF/附件) | 是 |
| PUT | /open/courses/{id} | 更新课程(字段同创建) | 是 |
categoryId 填响应中 subjects[].id(课程科目 ID),不是 groups[].id。常用科目:
1 C语言基础 · 2 Python 编程 · 3 生物信息学 ·
4 计算化学 · 5 AI/机器学习(以 GET /open/course-categories 为准)。
| 方法 | 路径 | 说明 | 需要 Key |
|---|---|---|---|
| GET | /open/status | 检查 API 是否正常运行 | 否 |
curl -X POST https://bixuebihui.com/api/open/content \
-H "X-Api-Key: ems_xxx" \
-H "Content-Type: application/json" \
-d '{
"catId": 14,
"title": "乙醇结构与反应式",
"text": "<h2>示例</h2><div class=\"ql-chem-block\" data-chem-format=\"smiles\" data-chem-source=\"CCO\" data-chem-caption=\"乙醇\"></div><div class=\"ql-math-block\" data-latex=\"E=mc^2\"></div><pre data-language=\"python\" class=\"ql-syntax\">print(\"CCO\")</pre>",
"status": 1
}'
# 1. 上传封面图
curl -X POST https://bixuebihui.com/api/open/upload \
-H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-F "file=@cover.jpg"
# 响应 data.path 或 data.thumb 用于下一步
# 2. 发布内容(将 thumb 设为上传返回的 path)
curl -X POST https://bixuebihui.com/api/open/content \
-H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"catId": 1,
"title": "甲醛安全剂量之争:一个分子,三个答案",
"tags": "化学,健康,甲醛",
"describe": "为什么不同机构对甲醛的安全剂量建议不一样?",
"thumb": "a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg",
"text": "<h2>引言</h2><p>甲醛是最常见的室内空气污染物之一…</p>",
"status": 1
}'
# 将 ems_xxx 替换为管理后台生成的完整密钥
curl -X POST https://bixuebihui.com/api/open/content \
-H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"catId": 1,
"title": "甲醛安全剂量之争:一个分子,三个答案",
"tags": "化学,健康,甲醛",
"describe": "为什么不同机构对甲醛的安全剂量建议不一样?本文从毒理学 LNT 模型讲起。",
"text": "<h2>引言</h2><p>甲醛是最常见的室内空气污染物之一…</p>",
"status": 1
}'
# id 为创建时返回的 data.id;只传需要修改的字段
curl -X PUT https://bixuebihui.com/api/open/content/12345 \
-H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "更新后的标题",
"text": "<h2>修订版</h2><p>正文内容…</p>"
}'
curl https://bixuebihui.com/api/open/content/12345 \ -H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
curl -X DELETE https://bixuebihui.com/api/open/content/12345 \ -H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
curl -X POST https://bixuebihui.com/api/open/questions/batch \
-H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"questions": [
{
"questionType": 1,
"questionText": "甲醛的化学式是?",
"questionSelect": "{\"A\":\"CH₂O\",\"B\":\"C₂H₅OH\",\"C\":\"H₂SO₄\",\"D\":\"NaCl\"}",
"questionAnswer": "A",
"questionDescribe": "甲醛是最简单的醛类化合物。",
"questionLevel": 1,
"questionKnowsId": "2030"
}
]
}'
# 1. 列出 Java 面试知识点(获取 knowsId)
curl https://bixuebihui.com/api/open/exam/knows/interview \
-H "X-Api-Key: ems_xxx"
# 2. dryRun 预览(逐题映射,适合 85 题分批标注)
curl -X POST https://bixuebihui.com/api/open/questions/batch-knows-id \
-H "X-Api-Key: ems_xxx" \
-H "Content-Type: application/json" \
-d '{
"dryRun": true,
"items": [
{"questionId": 13847, "knowsId": 2030},
{"questionId": 13848, "knowsId": 2031},
{"questionId": 13792, "knowsId": 2032}
]
}'
# 3. 正式写入(确认 preview 无误后 dryRun 改为 false)
curl -X POST https://bixuebihui.com/api/open/questions/batch-knows-id \
-H "X-Api-Key: ems_xxx" \
-H "Content-Type: application/json" \
-d '{
"dryRun": false,
"overwrite": true,
"items": [
{"questionId": 13847, "knowsId": 2030}
]
}'
# 4. 验证组卷库存(某知识点下有多少客观题)
curl "https://bixuebihui.com/api/open/exam/inventory?knowsIds=2030&knowsIds=2031" \
-H "X-Api-Key: ems_xxx"
同一 knowsId 的连续题号可用 ranges:{"fromId":13847,"toId":13876,"knowsId":2030}。
单次最多 500 题;overwrite:false 时跳过已有 knowsId 的题目。
# 0. 查询可选 categoryId(subjects[].id)
curl https://bixuebihui.com/api/open/course-categories \
-H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# 封面可先 POST /open/upload;describe 支持富文本,视频平台 iframe 会保留
curl -X POST https://bixuebihui.com/api/open/courses \
-H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "AlphaFold 入门精讲",
"categoryId": 1,
"describe": "<h2>课程简介</h2><p>从序列到结构…</p>",
"thumb": "a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg",
"youtuUrl": "https://www.bilibili.com/video/BV1xx411c7mD",
"pdfFile": "files/course/alphafold-slides.pdf",
"files": "files/course/exercise.zip"
}'
# 更新课程
curl -X PUT https://bixuebihui.com/api/open/courses/42 \
-H "X-Api-Key: ems_xxx" \
-H "Content-Type: application/json" \
-d '{"title": "AlphaFold 入门(修订版)", "describe": "<p>更新正文…</p>"}'
import requests
API_BASE = "https://bixuebihui.com/api"
API_KEY = "ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 管理后台生成
# 检查状态
r = requests.get(f"{API_BASE}/open/status")
print(r.json())
# 发布知识点
r = requests.post(f"{API_BASE}/open/content",
headers={"X-Api-Key": API_KEY},
json={
"catId": 1,
"title": "今日知识点",
"tags": "生物,分类学",
"describe": "摘要…",
"text": "<h2>正文…</h2>",
"status": 1
})
print(r.json())
# 批量发布题目
r = requests.post(f"{API_BASE}/open/questions/batch",
headers={"X-Api-Key": API_KEY},
json={"questions": [
{"questionType": 1, "questionText": "题干", "questionAnswer": "A",
"questionLevel": 2, "questionKnowsId": "2030"}
]})
print(r.json())
# 组卷:查知识点 → 批量打 knowsId → 查库存
r = requests.get(f"{API_BASE}/open/exam/knows/interview", headers={"X-Api-Key": API_KEY})
print(r.json())
r = requests.post(f"{API_BASE}/open/questions/batch-knows-id",
headers={"X-Api-Key": API_KEY},
json={"dryRun": True, "items": [
{"questionId": 13847, "knowsId": 2030},
{"questionId": 13792, "knowsId": 2031},
]})
print(r.json())
r = requests.get(f"{API_BASE}/open/exam/inventory",
headers={"X-Api-Key": API_KEY},
params={"knowsIds": [2030, 2031]})
print(r.json())
# 发布课程
r = requests.post(f"{API_BASE}/open/courses",
headers={"X-Api-Key": API_KEY},
json={
"title": "AlphaFold 入门",
"describe": "<h2>简介</h2><p>正文…</p>",
"youtuUrl": "https://www.bilibili.com/video/BV1xx411c7mD",
"pdfFile": "files/course/slides.pdf"
})
print(r.json())
下方为完整的 OpenAPI 交互式文档,可直接在此页面发送测试请求。