必学必会 · 开放 API

远程发布知识点、热点内容、课程和题目 · API Key 认证 · OpenAPI 3.0

快速上手 HTML 白名单 富文本扩展块 接口列表 组卷/知识点 调用示例 在线文档

🚀 快速上手

1
获取 API Key
登录管理后台 → 用户管理 → API 密钥/admin/user/api-keys),点击「生成密钥」,填写名称与有效期。
密钥格式为 ems_ 开头的随机字符串,明文仅在生成时展示一次,请立即复制保存。
可随时在列表中撤销不再使用的密钥。
2
在请求头中携带 Key
所有写接口均需在请求头中携带(示例:X-Api-Key: ems_a1b2c3d4…):
X-Api-Key: {your_api_key}
3
发送请求
生产环境 Base URL:https://bixuebihui.com/api
本地开发 Base URL:http://localhost:8888/api
所有需认证接口统一使用请求头:X-Api-Key: ems_xxx(与内容/题目/组卷接口相同,无需额外 Token)。

🛡️ HTML 白名单(安全防护)

API 在入库前会自动过滤危险标签和属性,防止 XSS 注入。只有白名单内的标签和属性会被保留,其余内容将被静默移除。

✅ 允许使用的标签

类别允许标签说明
段落/标题p, br, h1~h6正文段落、换行、标题
文本格式strong, b, em, i, u, s, del, ins加粗、斜体、删除线等
列表ul, ol, li无序/有序列表
引用/代码blockquote, pre, code引用块、代码块(配合 data-language)、行内代码
扩展块divql-math-block / ql-chem-blockKaTeX 公式、化学结构;见「富文本扩展块」
链接/图片a, img链接(限 href,title,target
图片(限 src,alt,width,height
表格table, thead, tbody, tr, th, td结构化数据展示
通用span, div布局容器

🚫 禁止的标签/属性

禁用项示例原因
脚本/嵌入式内容script, iframe, object, embedXSS 攻击、任意网页嵌入
表单控件form, input, button, textarea, select钓鱼/伪造表单提交
样式/元数据style, link, meta, baseCSS 注入、页面劫持
事件处理器onclick, onerror, onload 等所有 on* 属性JavaScript 执行
javascript: 伪协议href="javascript:alert(1)"链接型 XSS

⚠️ 过滤在后端(Java OpenApiController)执行。扩展块使用的 div / predata-* 属性在白名单内,详见下方「富文本扩展块」。

🧪 富文本扩展块(代码 / 公式 / 化学式)

通过 Open API 发布内容时,字段 text(及题目的 questionDescribe)除常规 HTML 外,可嵌入与管理后台富文本编辑器相同的扩展块。 前台阅读页会自动渲染语法高亮、KaTeX 公式与化学结构图。

1. 代码块(highlight.js)

使用 <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 等。

2. 数学公式块(KaTeX)

独立行间公式,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>

3. 化学结构块

data-chem-format说明data-chem-source
smiles推荐;自动绘制 2D 结构SMILES 字符串,如 CCO(乙醇)、c1ccccc1(苯)
molMOL 文件(V2000/V3000)文件全文(ChemDraw 可导出)
cdxmlChemDraw 原生 XML存原文;前台可能仅展示源码,建议优先 SMILES/MOL

可选属性 data-chem-caption:结构说明或化合物名称。

<div class="ql-chem-block"
     data-chem-format="smiles"
     data-chem-source="CCO"
     data-chem-caption="乙醇"></div>

组合示例(写入 text 字段)

<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/interviewIT基础(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 返回为准。

求职者公开自测(无需 API Key)

面向访客的模拟面试流程,不使用 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)

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
  }'

上传封面并发布(curl)

# 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
  }'

发布知识点(curl,无封面)

# 将 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
  }'

更新内容(curl)

# 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>"
  }'

按 ID 查询内容(curl)

curl https://bixuebihui.com/api/open/content/12345 \
  -H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

删除内容(curl)

curl -X DELETE https://bixuebihui.com/api/open/content/12345 \
  -H "X-Api-Key: ems_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

批量发布题目(curl)

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"
      }
    ]
  }'

组卷:查知识点 + 批量打 knowsId(curl)

# 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 的题目。

发布课程(curl)

# 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>"}'

Python 示例

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())

📖 在线文档(Swagger UI)

下方为完整的 OpenAPI 交互式文档,可直接在此页面发送测试请求。