1. 接入与鉴权
Base URL:http://127.0.0.1:8300所有端点都在 /api/v1 下,统一使用 uid + key 鉴权。密钥由平台方签发,格式 ak_ + 40 位随机串,服务端只存 sha256,创建时仅显示一次。
三种传参方式(任选其一)
- 请求头(推荐):
X-Uid+X-Key,避免密钥进入访问日志; - query 参数:
?uid=10001&key=ak_xxx; - form / JSON body:
{"uid":"10001","key":"ak_xxx", ...}; - 亦可使用
Authorization: Bearer <uid>:<key>。
方法约定
- 读接口(categories / class / class2 / expand-meta / balance / progress / log / work/query / orders)GET 与 POST 均支持;
- 写接口(query / create / update / refresh / stop / password / instant_brush / work/submit)仅 POST;
- 请求体支持
application/json与application/x-www-form-urlencoded,响应恒为application/json; charset=utf-8。
幂等
写接口建议带 Idempotency-Key 请求头;/create 另接受 out_trade_no(下游单号,同客户内唯一)。命中幂等/去重窗口时返回 code:1006 且 data.order_no 给出原订单号 —— 不要当失败处理。
curl -s "http://127.0.0.1:8300/api/v1/balance" \ -H "X-Uid: 10001" \ -H "X-Key: ak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
2. 统一响应包
成功判定:code === 0所有端点无一例外:code === 0 才是成功,不存在"查询返回 1、下单返回 0"的分裂。HTTP 状态码真实反映结果(400/401/402/403/404/409/429/500/502/503),可以只看 HTTP 状态做粗判,看 code 做精判。
// 成功
{ "code": 0, "msg": "ok", "request_id": "req_8f2c1d4e", "data": { } }
// 失败(业务数据一律在 data 内,不再散落顶层)
{ "code": 2005, "msg": "余额不足", "request_id": "req_8f2c1d4e",
"data": { "balance_cents": 10, "required_cents": 20, "gap_cents": 10 } }
msg给人看,code给机器看 —— 禁止用 msg 字符串匹配做逻辑判断;- 字段级错误在
data.fields: [{name, reason}]/data.detail; - 每个响应都带
request_id,报障时提供该值即可精确回放单次调用; - 限流响应带
X-RateLimit-Limit / -Remaining / -Reset与Retry-After。
3. 错误码表
code 与 HTTP 一一对应| code | HTTP | 含义 | 下游应对 |
|---|---|---|---|
| 0 | 200 | 成功 | 读 data |
| 1001 | 400 | 参数缺失或非法 | 按 data.detail 修正,不重试 |
| 1002 | 401 | UID 或 KEY 无效 | 立即停止重试,检查密钥 |
| 1003 | 403 | 账号已停用/已过期 | 联系平台方 |
| 1004 | 403 | 无权访问该订单 | 不重试(订单不属于本账号) |
| 1005 | 429 | 超过频率限制 | 按 Retry-After 退避重试 |
| 1006 | 409 | 重复提交(幂等或去重命中) | 读 data.order_no,不要当失败 |
| 1007 | 403 | IP 不在白名单 | 联系平台方 |
| 2001 | 404 | 分类/项目不存在或已下架 | 刷新项目列表 |
| 2002 | 400 | 学校未支持 | 读 data.candidates 修正 school |
| 2003 | 400 | 学习账号或密码错误 | 修正后重新下单 |
| 2004 | 400 | 所选课程不属于该账号 | 重新查课 |
| 2005 | 402 | 余额不足 | 充值后重试 |
| 2006 | 404 | 订单不存在 | 核对订单号 |
| 2007 | 409 | 当前订单状态不允许该操作 | 先查进度再决定 |
| 2008 | 400 | 高级参数校验失败 | 读 data.fields 按 schema 修正 |
| 2009 | 409 | 该项目不支持该能力 | 不重试,禁用该功能(见 capabilities) |
| 2010 | 429 | 补刷冷却中 | 读 data.retry_after 后再试 |
| 2011 | 400 | 课程名不合法(含协议分隔符) | 改名或用 kcid |
| 3001 | 500 | 服务内部错误 | 带 request_id 报障 |
| 3002 | 503 | 队列已满或引擎暂不可用 | 稍后重试 |
| 3003 | 502 | 依赖服务异常 | 稍后重试 |
HTTP 兼容开关:若你的 HTTP 客户端对非 2xx 处理有缺陷,可申请在客户维度开启 http_compat=200(body 中的 code 不变)。默认关闭,坚持真实状态码。
4. 端点清单(17 个)
路径与参数名与旧样本保持形状兼容以下示例统一省略鉴权(实际必传 uid + key),使用 -H "X-Uid: 10001" -H "X-Key: ak_xxx" 请求头方式。项目能力以 /class 返回的 capabilities 为准。
返回全部分类,data 为数组,含 product_count(空分类为 0,便于分类映射)。
curl -s "http://127.0.0.1:8300/api/v1/categories" \ -H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx"
参数 fenlei(分类 id,可逗号多选,不传返回全部)。返回 cid/name/price_cents/engine/capabilities/expand 摘要。计费以 price_cents 为准,浮点 price 仅展示。
curl -s "http://127.0.0.1:8300/api/v1/class?fenlei=1,2" \ -H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx"
普通商品返回自身;虚拟子商品带 is_virtual:true 与 mother_cid,其 expand 是固定预设,可直接下单,无需再拼参数。
curl -s "http://127.0.0.1:8300/api/v1/class2?fenlei=1" \ -H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx"
参数 platform(即 cid)。返回 fields[] 字段协议(name/aliases/type/label/target/required/options/min/max/max_len)与 submission,可据此动态渲染表单。type 支持 text|number|boolean|select|multi-select。校验以服务端为准。
curl -s "http://127.0.0.1:8300/api/v1/expand-meta?platform=1001" \ -H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx"
金额收入 data:{"uid","user","name","balance_cents","money","currency"}。balance_cents 为权威整数分。
curl -s "http://127.0.0.1:8300/api/v1/balance" \ -H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx"
按项目 + 学校 + 账号密码返回可下单课程列表。不落库、不扣费,但记 api_calls 审计。学校匹配失败 → 2002 + data.candidates。
curl -s "http://127.0.0.1:8300/api/v1/query" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"platform":1001,"school":"江苏农林职业技术学院","user":"20230101","pass":"123456"}'幂等 + 去重 + 余额校验 + 扣费 + 入队。返回 data.order_no(同值 data.id)与 state。kcname 与 kcid 数量需一致;课程名含半角逗号 → 2011。
curl -s "http://127.0.0.1:8300/api/v1/create" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3a9c21-0000-4000-8000-000000000000" \
-d '{"platform":1001,"school":"江苏农林职业技术学院","user":"20230101",
"pass":"123456","kcname":"高等数学,大学英语","kcid":"8801,8802",
"expand":{"remark":["重考一次"],"email":"a@b.com"},
"out_trade_no":"SHOP-20260924-0007"}'覆盖订单 expand,按 schema 校验(失败 2008 + data.fields)。若含需重跑字段(如 remark),data.requeued:true 表示已自动重新入队。
curl -s "http://127.0.0.1:8300/api/v1/update" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001","expand":{"remark":["重刷一次"],"target_score":90}}'可补刷状态:partial|failed|done|paused|cancelled;queued|running|refunded → 2007。冷却中 → 2010 + data.retry_after。对 paused/partial 是继续(断点续跑),对 done/failed 是重跑;默认不重复计费。
curl -s "http://127.0.0.1:8300/api/v1/refresh" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001"}'协作式取消:置 cancel_requested=1,引擎在课时之间退出。返回 paused 表示已受理。暂停不退款,恢复用 /refresh。
curl -s "http://127.0.0.1:8300/api/v1/stop" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001"}'更新学习账号密码(可选验证登录)。参数 pass,别名 xgmm。
curl -s "http://127.0.0.1:8300/api/v1/password" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001","pass":"newpass123"}'置 expand.speed_mode=instant 并以高优先级重新入队。
curl -s "http://127.0.0.1:8300/api/v1/instant_brush" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001"}'data 为数组,元素含 id/status/process/state/progress_percent/articles_done/articles_total/items[]。数据来自平台本地库,毫秒级返回,不依赖学校平台可用性。
curl -s "http://127.0.0.1:8300/api/v1/progress" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001"}'返回 data:[{time,ts,stage,course,status,process,remarks}] 与 total/truncated。stage ∈ login|query|learn|exam|certificate|mail|system,支持 limit/offset/stage/since。
curl -s "http://127.0.0.1:8300/api/v1/log" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001","limit":200}'content 长度 5~300,type ∈ order|recharge|agent|suggest|other(默认 order)。返回 data.work_id。
curl -s "http://127.0.0.1:8300/api/v1/work/submit" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"id":"CS20260924-000001","type":"order","content":"高等数学有两篇课时没通过,请协助补刷"}'传 work_id 查指定工单;只传 id(订单号)返回该订单最新工单,含 messages[] 往来消息。
curl -s "http://127.0.0.1:8300/api/v1/work/query" \
-H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"work_id":123}'参数 state/school/user/from_ts/to_ts/page/page_size。data 为 {list,total,page,page_size},仅返回本客户订单(客户隔离强制生效)。
curl -s "http://127.0.0.1:8300/api/v1/orders?state=partial&page=1&page_size=20" \ -H "X-Uid: 10001" -H "X-Key: ak_xxxxxxxx"
合计 17 个端点:categories、class、class2、expand-meta、balance、query、create、update、refresh、stop、password、instant_brush、progress、log、work/submit、work/query、orders。
5. 金额与时间约定
- 金额一律以
*_cents(整数分)为准:price_cents: 20就是 ¥0.20。浮点字段(price、money)为兼容保留,仅供展示,禁止用于计费与对账; - 时间返回
ts(Unix 毫秒)+time(YYYY-MM-DD HH:mm:ss,东八区),建议直接用time; - 请求涉及时间的参数(
from_ts/to_ts)传 Unix 毫秒; - 订单终态判定只看
state枚举,不要用中文state_text做判断。
// 正确:整数分运算 const total = items.reduce((s, it) => s + it.price_cents, 0); // 20 + 30 = 50 分 // 错误:浮点累加(0.2 + 0.3 = 0.5000000000000001) const bad = items.reduce((s, it) => s + it.price, 0);
6. 订单状态机与终态判定
| state | state_text | 终态 | 可补刷 | 说明 |
|---|---|---|---|---|
| pending | 待扣费 | 否 | 否 | 已建单,扣费未完成 |
| queued | 排队中 | 否 | 否 | 已扣费入队,等 worker 领取 |
| running | 进行中 | 否 | 否 | 引擎执行中,进度实时写库 |
| paused | 已暂停 | 否 | 是(继续) | 协作式取消,断点保留 |
| done | 已完成 | 是 | 是 | 全部课时通过 |
| partial | 部分完成 | 是 | 是(推荐) | 部分课时失败,建议补刷 |
| failed | 失败 | 是 | 是 | 登录失败/账号密码错,非客户责任会自动退款 |
| cancelled | 已取消 | 是 | 是 | — |
| refunded | 已退款 | 是 | 否 | 退款完成,不可补刷 |
终态判定:['done','partial','failed','cancelled','refunded'].includes(state)。轮询建议:非终态按 5~15 秒轮询 /progress,进入终态后停止轮询并调 /log 取完整日志归档。