目的:后续开发直接查本文档,不再重复扒站。 标注说明:✅ 已实测接入 App | 🔬 已实测/字段实锤但未接入 | 📝 来自 2026-08-28 调研,未逐项实测 |
⚠️ 已弃用/有坑 实测时间:2026-08-28 / 2026-08-29(真实会话)。样例统一在docs/samples/。
| 项 | 说明 |
|---|---|
| Base URL | https://byyt.ustb.edu.cn |
| 鉴权 | Cookie SESSION=<base64(uuid)>(Spring Security 风格),登录后全站通用 |
| UA | 跨 UA 直连不会使会话失效(2026-08-29 curl 实测);App 内仍统一 WebView 同源 fetch,不维护 Cookie/UA |
| 编码 | 全站 UTF-8(Windows 终端显示乱码是 codepage 问题,非接口问题) |
| 请求体 | 两种:form(application/x-www-form-urlencoded)与 JSON(application/json,传 form 会被拒 415)。逐接口标注 |
| 响应 | 两种形态:包装 {code,msg,msg_en,content} 或裸 JSON/数组(逐接口标注) |
| 分页 | PageHelper 结构 {total,list,pageNum,pageSize,size,pages,isFirstPage,...} |
| 学期代码 | xnxq="2025-20262"(xn+xq 拼接)、xnxqmc="2025-2026-2";进度接口的 jzxnxq 同为 xn+xq 拼接 |
| 字段命名 | form 系接口字段多为大写(XN/XQ/KCMC),JSON 系接口小写(kcmc/xf);同一数据两种接口字段名不同 |
| 页面规律 | 功能页 HTML 尾部引 /pub/<模块>/...js 与 /component/inco/...js,数据接口路径藏在 JS 里;表格列定义 JS(*Column*.js)= 响应字段名的权威来源 |
| 接口 | 方式/参数 | 响应 | 状态 |
|---|---|---|---|
/component/querydangqianxnxq |
POST form,空体 | 裸 {XN,XQ,XNXQ,XNXQ_EN}(XNXQ="2026-2027-1") |
✅ |
/component/queryRlZcSj |
POST form xn,xq,djz(周次) |
包装 content:[{xqj:1..7,rq:"yyyy-MM-dd"}] 该周 7 天日期 |
✅ |
/component/getXnxqByRq |
GET ?rq=yyyy-MM-dd |
裸 学期+教学周 zc | 📝 |
/Xiaoli/queryMonthList |
POST form xn,xq,需 header RoleCode: 01 |
裸 {xlList:[...],xnxqList,monlist};每周 7 条(天级),每行仅一个星期字段非空(周一那条 MON=该周周一日期)并带同名字段的调课标记 MON1..SUN1:工作日 '1'=该日放假不上课(周末恒为 '1',无信息量),XNXQ=xn+xq 拼接,ZC 1..18 教学周、99 假期(整周)。样例 queryMonthList-2026-2027-1.json |
✅ |
/component/queryzclist |
POST form xn,xq |
包装 content:[{ZC}] 周次列表(1-18+99) |
✅ |
/component/querydangqianzc |
POST form | 当前教学周(假期返回空) | 📝 |
教学周≠日期周:长假周(国庆)不占教学周序号,一切周映射以 Xiaoli/queryMonthList 为准。
放假/补课(2026-10 实测结论):校历是天级的——xlList 每行一个日期,ZC=99 整周假期,工作日的 *1 标记 '1' 即单日放假(本学期国庆:9/25 中秋、9/28-10/4 整周、10/5-10/7 共 9 个工作日,10/8 恢复上课)。补课安排不在教务数据里(课表无周末行、教务通知系统搜"放假/调课"只有 2012-2020 旧文、课表页无备注、getXnxqByRq 只回周级 zc)——补课日按哪周几上课只能靠教务处/班级通知,App 把每学期口径内置在 model/SchoolAdjustments.kt(键 xn-xq,随版本发布更新),与 SemesterConfig.makeups 存量字段在 WeekResolver.daySchedule 合并、内置优先;设置页无手动入口。
| 接口 | 方式/参数 | 响应 | 状态 |
|---|---|---|---|
/xszykb/querykbsffb |
POST form xn,xq |
裸 "0"=未发布 |
✅ |
/xszykb/queryxszykbzong |
POST form xn,xq |
包装 content:[...] 总课表;字段 RWH 任务号、KEY("xq2_jc1"=周2第1节;"bz"=备注行)、SKSJ 多行文本(名/师/周/【校区】地/节)、ZC 周位图(34 字符,index=周次,索引 0 为占位符)、XB 色号(99999=无固定时间) |
✅ |
/xszykb/queryxszykbzhou |
POST form | 单周课表(含备注) | 📝 |
/component/queryKbjg |
POST form xn,xq,pylx |
包装 content:[{xj,kssj,jssj}] 节次时间(1..13 节) |
✅ |
/xszykb/queryxszytjkb |
POST form | 推荐课表视图 | 📝 |
/component/queryKssjFb |
POST form | 节次时间发布状态 | 📝 |
/component/queryxskbbzb / queryxskbbzb2 |
POST form | 课表备注 | 📝 |
/xskb/queryrwbewm |
POST form | 课程任务二维码 | 📝 |
/cjgl/grcjcx/grcjcx — POST JSON {xn,xq,kcmc:null,cxbj:"-1",pylx,current:1,pageSize:500,xscjlb:null,sffx:null,yhdm}(yhdm/pylx 取自 /user/me)→ 包装 content:{total,list:[...]} ✅
list 字段(实测,docs/samples/grcjcx-all.json):kcdm kcmc xnxq xnxqmc kcxz(必修/任选) kclb(通识课程/实验/美育(素质拓展)…) xf 学分 zzcj 总评(可能为"优/良") bkcx(正考/补考) yxmc 开课学院 sffx 辅修 pm 排名 zrs 该课总人数 khfs 考核方式 xs 学时 zpcj xscjlb(主修) rwh。
kclb → zzcj → … → xf(xf 在 zzcj 之后),按名取值不受影响。
/cjgl/grcjcx/getgpa — POST form 空体 → 裸 {BL:GPA值, HDXF:已获学分, TGKC:通过门数, PM:专业排名, ZRS:专业总人数, PJXFJ_PM:平均学分绩排名, PJXFJ_PM_FW} ✅
queryBxkqk 带 sfcxxfj:"1" 只多返回 XFJ(学分绩排名标识)与 PM/ZYRS,教务网无 4.0 制绩点接口。
App 的 4.0 制 GPA 由 GpaCalculator 本地计算:换算表 90-100=4.0 / 85-89=3.7 / 80-84=3.4 / 75-79=3.0 / 70-74=2.4 / 65-69=2.0 / 60-64=1.0 / <60=0;纳入全部有数字成绩课程(等级制排除);同 kcdm 有补考/重修行只取补考/重修(多行取最高);挂科计 0 绩点、学分进分母。
调用链:先 getXss 拿标识,再 JSON POST 查询。
| 接口 | 请求 | 响应 |
|---|---|---|
/cjgl/cjzhtjcx/cjcx/getXss |
POST form 空体 | 包装 content:[{xh,nj,pylx,xjid,zyfxdm,fah,…}](fah=培养方案号) |
/cjgl/cjzhtjcx/cjcx/queryqxnxq |
POST form 空体 | 裸 {XN,XQ} 当前学年学期 |
/cjgl/cjzhtjcx/cjcx/queryBxkqk |
POST JSON {xh,pylx,nj,jzxnxq:xn+xq,xjid,fah,sfcxxfj:"0"} |
包装 content:{yqmsxf:{YQXF要求学分,YQMS要求门数}, ywcxf已修学分, wwcxf未完成学分, ywcms已过门数, wwcms未过门数}(实测 125.5/89.5/36.0/58/43) |
/cjgl/cjzhtjcx/cjcx/queryXflbyq |
POST JSON 同上(可加 zyfxdm:"0") |
包装 content:{total:17,list:[{kclbmc学分类别,kcxzmc课程性质,kclbdm,kcxzdm,yqwcxf要求学分,yzhxf已转移,dzhxf待转移,moochdxf,moocsjhdxf}]}(17 行与教务网页"学分类别要求"表一致) |
口径警告(重要):
queryXflbyq的ywcxf是转移/认定口径(如创新学分 14.2),≠已完成学分,勿用作已完成;网页"已完成学分"实为前端按成绩单kclb分组汇总已通过课程xf(已数值验证:学科平台 23.5、实验 5.0、基础实习 3.0、专业实习 2.0 与网页一致)。App 内同样本地汇总,可离线。- 本地 kclb → Xflbyq 类别行的匹配规则:全等 或 行名以本地名结尾(如 "素质拓展—美育(素质拓展)".endsWith("美育(素质拓展)"))。
| 其他同模块接口 | 说明 | 状态 |
|---|---|---|
/cjgl/cjzhtjcx/cjcx/queryMkyq |
JSON {xjid,zyfxdm,fah,pylx} 模块要求树(含每模块要求/完成学分门数学时) |
🔬 |
/cjgl/cjzhtjcx/cjcx/querybyyq |
JSON {xh,zyfx,pylx,fah} 毕业要求 |
🔬 |
/cjgl/cjzhtjcx/cjcx/queryFaKzkc |
方案可修课程 | 📝 |
/cjgl/cjzhtjcx/cjcx/querysfxsbyyq |
是否显示毕业要求 | 📝 |
/cjgl/cjzhtjcx/cjcx/queryZyfxTjinfo |
JSON 专业方向统计 | 📝 |
/cjgl/cjzhtjcx/cjcx/queryInfo |
JSON(页面注释代码中出现,勿依赖) | 📝 |
/cjgl/grcjcx/bkcxbjList |
补考/重修班级 | 📝 |
/cjgl/grcjcx/seefx |
辅修成绩 | 📝 |
/xjgl/xyyj/*(如 queryXyyjXshd) |
学业警示/学期成绩/挂科情况(菜单"学业警示核查")——App 选择本地算挂科,不依赖 | 📝 |
/UserManager/queryXsxkqk |
/kscxtj/queryXsksByxhList — POST form:pxn/pxq/ppylx(pageNum/pageSize → 裸 PageHelper 分页 {total,list:[...]} 🔬
list 字段(权威来源:列定义 JS /pub/gly/ksgl/cxtj/XskscxByXhColumn-*.js,已存 docs/samples/XskscxByXhColumn.js):
XH XM KCDM KCMC 课程名、KSSJDMC 考试类型("期末考试")、KSSJMS 考试时间描述文本(需解析出日期/起止时间)、ZWH 座位号、CDDM/CDXX 地点、JKJSBZ 进考场标志、KKYXMC 开课学院、ZYMC/ZYFXMC/NJMC。
docs/samples/exams-empty.json。排考后需用真数据核对 KSSJMS 格式一次。
考试查询页面:GET /kscxtj/queryXskscxByXh(HTML)。
/Xsxk/*:queryXkdqXnxq 当前选课学期、queryKkxqList 开课学期列表、queryKxrw 可选任务、addXuanke 选课、tuike 退课、queryXkgwc 购物车、queryYxkc 已选课程、queryXsxkrzList 选课日志、queryJiaofei 缴费查询;/Xsxktz/queryXlctzList 休复学;页面 /Xsxk/query/1。
课表查询(选课系统内):/Xskbcx/queryXskbcxList(个人) / queryBjkbcxList(班级) / queryGwckbcxList(公选课)。
| 接口 | 说明 | 状态 |
|---|---|---|
/user/me |
POST form;完整档案+25 权限码+roleAuth['01'] 完整功能菜单树(qxmc/url/fqxdm)+学籍快照 |
✅ |
/UserManager/queryxsxx |
学籍信息(学院/专业/班级/年级) | ✅ |
/UserManager/querywhbsxsxx |
含身份证号 |
📝 |
/UserManager/queryArrears |
欠费查询 | 📝 |
/UserManager/xgmm |
改密码 | 📝 |
/UserManager/upddzyx / updlxdh |
改邮箱/电话 | 📝 |
/common/queryGjlist / queryMzlist |
国籍/民族代码表(页面下拉用) | 📝 |
/Zdpyfa/*18 个接口(基本信息/课程要求/毕业要求/实践环节/专业方向统计等)📝——部分需页面上下文,直接调用 403;页面/xspyyjsxsjh/xsjhBgd/1。/Jkgrjhpp/qeuryXspyfaList📝(注意原拼写 qeury)。- 交流生业务
/j1jh/gnjls/*📝;AI 助手/incoai/go📝;收藏/shouCang/qxshouCang📝。 - 外部系统:评教
https://pingjiao.ustb.edu.cn、大创/SRTPhttps://srtp.ustb.edu.cn、北科学堂/雨课堂/毕业/实践教学平台(URL 待补)。
| 文件 | 内容 |
|---|---|
grcjcx-all.json |
全量成绩单(含 pm/zrs/khfs) |
getgpa.json |
GPA 概览 |
xflbyq.json |
学分类别要求 17 行(要求学分实锤) |
bxkqk.json |
毕业总进度 |
xsxkqk.json |
本学期选课情况(弃用留档) |
exams-empty.json |
考试分页空态(参数正确性验证) |
XskscxByXhColumn.js |
考试列定义(响应字段名依据) |
queryxszykbzong-2026-2027-1.json |
总课表 |
queryKbjg-section-times.json |
节次时间 |
queryRlZcSj-week1-dates.json |
第 1 周日期 |
与上面 1~8 节的教务系统(byyt)无关,是另一个站点:
https://ustb.smartclass.cn, 对应页面/ustb/ClassRoom.aspx("今日无课教室")。本节于 2026-09 对照站点自身前端 与真实响应核实,勿再重新扒站。
| 接口 | 方法 | 用途 |
|---|---|---|
/config.json |
GET | 站点配置;domainConfig 字段是加密的签名密钥段,csrkTime(300000 = 5 分钟)是 token 有效窗口。不需要签名 |
/Home/GettimeDif |
GET | 服务器当前时间,绝对毫秒时间戳(不是时间差)。需要签名 |
/general/api/open/building/listBuildings |
GET | 楼栋列表 [{id,name}] |
/general/api/open/teachingCycle/listNodeTypes |
GET | 节次类型 [{id,name}],实测有"默认节次"(6 大节)与"小节次"(12 小节) |
/general/api/classroom/freeClassRooms |
POST | 空教室。体 {buildingId, cycleTypeId, nodeId},nodeId 传空串=全部时段 |
响应统一为 {"code":0,"msg":"success","data":[…]};code != 0 时 msg 即错误原因。
freeClassRooms 的 data 为 [{nodeId,nodeName,startTime,endTime,classroomItems:[{classroomId,classroomName,noSeatRate,seatCount}]}],
startTime/endTime 的日期部分是固定占位 2000-01-01,只取时分;
noSeatRate 可能为 null(无数据),必须与 0(真的 0% 空座)区分。
- 算法:AES-256-CBC/PKCS7 解密
/config.json的domainConfig(hex)得到明文配置,取其中的csrkKey。 - token:把毫秒时间戳的每一位数字当作下标去
csrkKey里取字符(13 位时间戳 → 13 字符 token)。 - 时间戳必须落在服务端的
[现在, 现在+csrkTime]窗口内,且必须用服务器时间(设备时钟快 3 分钟就会全量失败)。 站点自己的取法是TimeDif = parseInt(/Home/GettimeDif 响应) - Date.now(); App 改用/config.json的 HTTPDate响应头(该请求不需要签名,没有"先有鸡还是先有蛋"的问题)。 - 服务端密钥会轮换:轮换后请求返回
csrf key validate error,正确做法是丢弃本地缓存的 key、 重新拉/config.json后重试一次(每个接口都要这么做,只覆盖一半会让自愈整条失效)。 - 实现与单测:
data/remote/SmartClassCrypto.kt、SmartClassKeyProvider.kt、SmartClassApi.kt, fixture 见app/src/test/resources/smartclass-config.json。
- 站点页面上的"全天 / 上午 / 下午 / 晚上"筛选是同一份数据的界面分组,接口没有对应的日期或半天参数: 只能查"今天"。App 因此在页面顶部明示"今天 · 更新时间",并把已结束的时段淡化。
- 站点会在本地存储里放
csrkDate版本号,版本变化时清空csrkKey重新拉取——与 App 的"密钥被拒即重取"同源。