北京科技大学(USTB)课表 App:WebView 登录本研一体化教务系统一键导入课表与成绩,本地周视图展示,上课与考前提醒。
课表
- v2.1.0 AI 小宠物:支持 MiMo、DeepSeek、自定义 OpenAI 兼容 API;点击宠物打开可展开的半屏聊天,可联网聊热点、按分项授权分析课表/考试/成绩/日程等信息,自定义人格并管理本机长期记忆。设置统一收进“我的 → 课表小宠物”;API Key 与记录独立加密保存。
- v2.0.1 课表小宠物:默认开启,可在“我的 → 课表小宠物”关闭。“大肥鱼 · deepseek酱”可自由拖动、随机走动,点击说话;新增同形象趴边、坐靠动作,可在今日课程顶部、底部导航上沿和边缘停靠。自动走动优先空白区域,路线和气泡尽量避开色块,尤其今天与临近的课程。默认打开或切回课表时打招呼,支持每次、间隔 1/5/30 分钟或关闭;久别有专门欢迎词。60 条离线闲聊,安静/适中/活泼三档频率,默认称呼“同学”可自定义;课程、考试、闲聊与欢迎独立设置。切页、弹层、翻周和退后台暂停,不改变系统通知
- 一次扫码跑完三件事:内置 WebView 完成统一身份认证登录(App 不接触学号密码)后,同一次扫码自动导入学期课表 + 抓取成绩/考试/学业进度 + 登录云账号;课表抓完直接入库(保留隐藏状态与自定义课),末尾给出逐项结果与失败项重试。教务会话还在时连扫码都不需要——进入即静默跑完
- 官方教学周日历:导入时同步教务校历,教学周↔日期精确映射——国庆等长假周不占教学周序号(第 4 周 = 10/5,而非 9/28);假期中自动提示并定位到假期后第一个教学周
- 假期与调休补课:教务校历的天级放假标记随导入生效——放假日(如国庆 10/5-10/7、中秋)课表整列清空标「休」、顶栏「假期中」、假期里不排上课提醒、「下一节课」图钉隐藏;调休补课日(如「10月10日补周三的课」)教务系统查不到,口径随版本内置(
SchoolAdjustments),补课列按生效星期渲染并带「补周X」角标,提醒照排,隐藏周末时周末补课列仍显示 - 班级实验/上机安排自动同步:实验安排只在班级群通知、教务系统拿不到(教务里这些课只有「无固定时间课程」条目)。服务端按班级下发(当前 2026-2027-1 学期覆盖机243班:工程数值计算上机 2 次 + 电工实验 5 次),每次「登录并同步」自动拉取并整组替换,落到对应周次的节次上(带地点与备注,参与上课提醒);教务里同名的「无固定时间课程」条目自动隐藏。安排改动只需更新服务端数据、不用发版;其他班级的账号看不到本班安排
- 周视图课表:按"大节"排版(1-2 节 = 第一大节 … 第 11-12 节 = 第六大节,个别 13 节加课也归入第六大节),支持滑动切周、周次下拉跳转、单双周标注、隐藏周末开关、隐藏本周不上的课开关(关闭时这些课淡化显示)、同周冲突课程并排窄列、今天实心胶囊高亮、顶栏日期+学期状态(未开学/第 N 周/假期中)
- 课程管理:手动增删改课程(含多时段),教务导入数据、班级实验安排与手动课程共存;四类课程(教务导入 / 班级实验安排 / 自定义 / 示例)都可隐藏,可选“仅本次 / 整个学期”,在「学期设置」→ 隐藏的课程里恢复(同时停止对应上课提醒);教务导入课与班级实验安排只能隐藏不能删除(前者下次导入、后者每次同步都会原样回来),自定义课与示例课可直接删除;同名调课/单双周拆分行自动合并
- 深色模式:跟随系统 / 浅色 / 深色三态切换;浅色暖渐变 + 暗色暗渐变双主题背景
教务(无课教室 | 日程 | 成绩 | 考试)
- 无课教室:按楼栋查看今天各大节的空教室(教室名 + 座位数 + 空座率),教室名自然排序(1002 排在 503 之后)、当前大节自动展开、已结束的大节淡化并标"已结束"、顶部交代"今天几号 · 数据何时更新"、下拉刷新。数据来自贝壳教学平台(
ustb.smartclass.cn),无需登录教务账号,请求里不含学号/姓名 - 日程:个人待办日程,支持每天重复 / 每周固定几天 / 一次性三种模式;“近期”按日期展示未来两周,“全部日程”可查看远期与已完成事项,保存后自动定位。重复事项当天可打卡、次日重新开始,未来日期只预览;过期的一次性事项可补打卡并持续保留完成状态。每周重复可触摸选择星期,至少选择一天
- 日程时间选择:日期使用日历,时间(时/分双列)和提前分钟(1–120)使用滚轮,支持拖动吸附与点选居中。高亮、确定后的表单和保存记录共用滚轮当前位置,快速确定也保持一致;取消保留原值,屏幕重建恢复滚动位置
- 成绩:按学期分组的成绩单,单科排名(如 排名 5/128),点击弹详情(排名/考核方式/学分/开课学院等),不及格标红、学期组头"N 门未通过"
- 加权成绩:只看必修课的加权平均分,学年/学期双列筛选,可自定义排除课程
- GPA:本地 4.0 制计算(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,补考/重修覆盖正考)
- 学分修读进度:毕业总进度条(已修/要求)+ 17 类课程性质的要求 vs 已完成进度条,口径与教务网一致
- 成绩隐私:小眼睛一键隐藏/显示全部分数
- 考试安排:按日期分组、D-N 倒计时、座位号、考点展示
- 考试独立刷新:考试页只获取当前学期考试,复用教务登录会话;登录过期时重新登录后继续获取,不触发课表、成绩、实验安排或云账号登录。考试更新时间单独记录,获取失败保留已有考试与提醒。
- 按页刷新:成绩页只更新成绩、学籍和学业进度,考试页只更新考试。直接点刷新,复用登录会话,成功自动返回并提示结果,失败保留重试入口;课表和云同步入口继续完整同步。
- 页面状态保留:底部页面和教务分区切换时保留各自滚动、展开状态;在教务或我的按系统返回先回课表,弹层优先关闭。
课程隐藏与周次标注
- 点课程详情的“隐藏”可选“仅本次 / 整个学期”;仅本次按实际日期隐藏当前卡片这一堂课并停用对应提醒,其他日期和时段继续显示。重新导入、云备份和恢复会保留匹配课次的隐藏设置。
- 学期设置的“隐藏的课程”完整显示名称、来源、日期 / 周次及节次;教务拆行合并为一个恢复项。恢复单次仅恢复该日期,恢复整个学期会同时清除该课程的单次隐藏。
- 卡片仅对连续隔周上课的安排标注“单周 / 双周”;仅上一次标注“第 N 周”,其他周次在详情查看。
提醒
- 上课提醒:每门课程块开始前 N 分钟通知(默认 15 分钟可调),精确闹钟 + 开机自动重排
- 考前提醒:考试前一天 20:00 与开考前 1 小时各提醒一次(含地点与座位号)
- 日程提醒:每个日程事项按自定义提前量提醒(1–120 分钟),随事项增删改即时重排
我的
- 云同步(opt-in):与课表/成绩共用同一次扫码登录(账号 = 学号,无密码);服务端会拿本次登录的教务会话向教务系统复核学号(不是只认"学号+姓名",详见下文隐私一节)。启用后课表/自定义课程/日程/成绩/考试/学业进度与相关设置整包自动备份到云端(改动后 8 秒去抖上传)。换机/重装场景会自动识别:云端有备份而本机没有数据时直接恢复;两边都有数据,首次关联、云端版本变化或缺少本机确认记录时,弹窗让你选“从云端恢复 / 用本机覆盖云端 / 暂不上传”。同账号已开启同步且云端版本未变时沿用备份方向,减少重复询问;恢复前强制校验快照签名,恢复后提醒自动重排
- 学籍信息卡(姓名/学号/学院/专业/班级),主题三态切换,检查更新,外部系统快捷入口(评教/大创),清除成绩缓存,联系开发者
当前版本 2.1.2(versionCode 69)。最新 Release 提供正式签名 APK,可直接覆盖安装升级;也可在 App 内“我的 → 检查更新”获取。v1.0.14 及更早版本因包名变更需卸载重装。
本次发布宠物活力优化:主动搭话按主题轮换并避开最近说过的内容,欢迎语不再反复重复;开启联网后宠物会定时播报公开热点(首次进入课表约 2–3 分钟,之后按设置间隔);宠物走动更自然、会原地换姿态,翻周时滑出屏幕并从分界线或屏幕边缘走入,不再闪现。v2.1.0 的 AI 首发功能(MiMo/DeepSeek/自定义 API、联网热点、分项授权只读分析、人格与本机记忆、半屏聊天)延续保留,无需迁移数据库。
本版从合并后的 main 发布(codex/pet-ai-polish 小修批次)。
在“我的 → 课表小宠物 → 模型与联网”填写 Key 并选择搜索来源,再到“记忆与数据”打开需要的数据授权:
- “我的 → 课表小宠物”是统一入口,详细设置分为互动与外观、模型与联网、人格与称呼、记忆与数据。
- 内置小米 MiMo、DeepSeek 预设,填 API Key 即可使用;自定义 OpenAI 兼容供应商可填写 HTTPS 服务地址、Key 和模型名,也可读取模型列表并测试连接。手机直接连接所选供应商,模型和联网消耗用户自己的供应商额度。
- 点击宠物进入半屏连续聊天,可展开全屏;支持自定义名字、人设、称呼、本机聊天历史,以及可查看、修改、删除和关闭的长期记忆。未配置 API 或请求失败时保留离线陪伴。
- 课表、考试、日程、成绩、学分进度、学籍分别授权,默认全部关闭,按问题需要读取。只做分析和建议,不修改学习数据;日期、隐藏课程、打卡和统计沿用原业务口径。
- MiMo 使用其官方联网插件(需先在控制台开通),DeepSeek 使用官方 Search;也可给其他聊天模型单独配置 DeepSeek 搜索 Key。热点保留来源与时间,查询失败不生成虚假的“实时结果”。
- 自动搭话只在课表前台可见且没有弹层时运行,切页、后台、翻周和弹层会停止主动请求;搭话间隔与热点刷新频率独立设置。
API Key、人格、聊天和记忆使用独立 AndroidKeyStore 密钥加密,原子存入本机 noBackupFilesDir,不参与云备份或系统备份;加密失败不会改存明文。只向所选供应商发送当前问题所需的已授权内容,不读取或发送学校密码、学校登录会话、云账号凭证。更换授权会开启新会话,旧记录只留在本机历史中,带已撤销数据来源标记的回复和记忆不再进入后续请求。
App 内「我的 → 检查更新」走自有更新源(/api/bs/app/latest):响应带 Ed25519 签名、并覆盖安装包 SHA-256,验签不通过则整体忽略该源(不拿未验签的强更或下载链接),装包前再核对一次摘要。
- Kotlin + Jetpack Compose(Material 3)
- Room(课程 / 节次时间 / 成绩 / 考试 / 日程)+ DataStore(学期配置 / 教学周日历 / 提醒 / 主题 / 隐私开关)
- AlarmManager(上课 / 考试 / 日程提醒调度)
- OkHttp + kotlinx.serialization(云同步 REST,api.caeamer.com 的 /api/bs 模块;无 Retrofit)
- 云同步全链路 HTTPS:源站不暴露任何公网端口,由 Cloudflare Tunnel(cloudflared 主动出连)承接
api.caeamer.com;客户端不再有明文 HTTP 通道 - 教务适配层:WebView 注入 JS 复用登录会话,直接调教务结构化 JSON 接口(无需解析 HTML)
- AndroidKeyStore(AES-GCM)加密保存云 token;Ed25519 验签(自有更新源元数据 + 云备份快照)
- 云登录:教务统一认证 → SSO(
sso.ustb.edu.cn/ac-h5)扫码 → 微认证二维码(sis.ustb.edu.cn/connect/qrpage|qrimg|state)。App 用shouldInterceptRequest原生捕获二维码 sid 并自行长轮询,不依赖页面自带轮询(慢网下它会超时→reload→换码)
- 课表:
/xszykb/queryxszykbzong(学期总课表,含 32 位周次位图)、/component/queryKbjg(节次时间) - 成绩:
/cjgl/grcjcx/grcjcx(成绩+排名/考核方式,JSON POST)、/cjgl/grcjcx/getgpa(GPA 概览) - 学业进度:
/cjgl/cjzhtjcx/cjcx/queryXflbyq(学分类别要求)、queryBxkqk(毕业总进度) - 考试:
/kscxtj/queryXsksByxhList(pxn/pxq/ppylx 参数带 p 前缀) - 校历:
/Xiaoli/queryMonthList(全量教学周↔日期映射,需RoleCode头),失败时逐周/component/queryRlZcSj兜底 - 无课教室(贝壳教学平台,与教务系统相互独立):
/general/api/open/building/listBuildings(楼栋)、/general/api/open/teachingCycle/listNodeTypes(节次类型)、/general/api/classroom/freeClassRooms(POST,nodeId传空串=全部时段)。每个请求都要带csrkToken签名:密钥在/config.json的加密domainConfig里(AES-256-CBC),时间戳必须用服务器时间——直接取/config.json的 HTTPDate响应头,详见docs/JWXT_API.md附录 - 登录页是 PC 布局且其 meta viewport 会覆盖 WebView 宽视口设置,注入脚本改写
width=1440并修补100vh高度坍缩(详见JwWebView.kt注释) - 站内跳转按域名白名单收口:
ustb.edu.cn子域 http 与 https 同等放行(统一认证授权成功后的回调 302 会指向http://sso.ustb.edu.cn/idp/thirdAuth/...,拒绝非 https 会把这次跳转静默丢弃,界面永久卡在"授权成功"),站外一律交系统浏览器,无 host/非 http(s) 一律不加载(详见JwWebView.kt的jwNavPolicy) - 全量接口参考:docs/JWXT_API.md(后续开发勿再扒站)
./gradlew assembleRelease # 需要 keystore.properties(签名配置,未入库)
./gradlew testDebugUnitTest # 解析器/周次逻辑单测(fixture 为真实接口样本,见 docs/samples/)
node --test app/src/test/js/*.test.cjs # 成绩/考试脚本:请求范围、空结果与失败语义交互回归覆盖滚轮初始化、拖动与快速确定、保存重开、提醒边界、日程查看与打卡、页面状态及刷新返回。需启动模拟器或连接 Android 设备:
./gradlew connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.caeamer.beikeschedule.PetBubbleTouchTest,com.caeamer.beikeschedule.PetInteractionTest,com.caeamer.beikeschedule.PetWelcomeInteractionTest,com.caeamer.beikeschedule.PetPoseRenderTest,com.caeamer.beikeschedule.TodoInteractionTest,com.caeamer.beikeschedule.WheelPickerInteractionTest,com.caeamer.beikeschedule.NavigationInteractionTest,com.caeamer.beikeschedule.PetAiSettingsInteractionTest,com.caeamer.beikeschedule.PetAiChatInteractionTest更多技术细节见 docs/TECH_DESIGN.md 与 docs/JWXT_API.md。
学号密码只在系统 WebView 中由本人输入给学校统一认证页面,App 不读取、不存储密码;成绩默认隐藏展示需点小眼睛查看。
本机数据:课表/成绩/日程默认仅保存在本机,不上传云端。登录会话由系统 WebView 保存,学号等学籍信息会随成绩抓取缓存在本机用于离线展示;如需终止会话,可在「我的」→「退出教务登录」清除。
云同步(opt-in,默认关闭):在「我的」→「云同步」用教务统一认证(扫码或账密)登录后才会开启。账号即学号、无密码——统一认证成功本身即是身份验证。开启后课表、日程、成绩、考试与相关设置会整包备份到开发者自有服务器(仅用于备份恢复,不做任何分析或共享),全程 HTTPS:https://api.caeamer.com 由 Cloudflare 承接,源站不开放任何公网入站端口(cloudflared 从服务器主动向 Cloudflare 出连,请求经隧道回到本机 127.0.0.1:3000)。关闭开关或退出云账号即停止上传;云端备份在退出云账号后保留,重新登录即可找回。
云账号的身份校验:登录时 App 会把本次教务会话(SESSION cookie)用服务端 RSA 公钥密封(RSA-OAEP-SHA256,密文上传,链路上拿不到明文,也不会被记录),服务端解开后立即向教务系统 /user/me 核实"请求方确实持有该学号的教务会话",核实完即丢弃;只凭"学号+姓名"无法再换到 token(这两项在班级名单/群文件里总是成对出现)。云 token 本身用 AndroidKeyStore 的 AES-256-GCM 加密后落盘,换设备无法解密复用。
云端数据完整性:GET /backup 返回的快照带服务端 Ed25519 签名,恢复前强制验签(无签名/验签失败一律拒绝恢复),避免明文 HTTP 上被整包替换成投毒快照。
查看「无课教室」时 App 会直接访问贝壳教学平台(ustb.smartclass.cn)的公开接口,该请求只携带一个由平台公开配置算出的签名参数与设备网络出口 IP,不包含学号、姓名或任何本机数据。
问题反馈:caeamer@163.com | GitHub Issues
© 2026 caeamer. All rights reserved.