Xiaomai Sum (小麦助教 课时自动统计)
Overview
自动读取小麦助教(xiaomai5.com)的点课记录并按「扣课时」口径统计课时,无需手动复制粘贴。
难点(密码 MD5 非 RSA、默认 API 域名是内网域名、认证必须带 HTTP 头、扣课时与实际扣课时两个字段)
均已固化,详见 references/api_reference.md。
统计口径(2026-08-10 与用户最终确认)
- 数据源 = 点课记录(
rollCall/queryInstRollCalls,一次课一条)。 - 时间维度 = 上课时间(
classStartDate),区间[当月1号00:00, 次月1号00:00),不是点课/课消时间。 - 课时数 = 逐学员相加,两个字段:
- 「扣课时」=planCheckedClassHour,当学员到课(classState 非 ABSENCE/LEAVE)
或实际扣课时 > 0 时计入,否则记 0
—— 与平台导出「学员点名记录.xls」的『扣课时』列完全一致(2026-07 实测 202 行逐条对上),默认口径;
- 「实际扣课时」=checkedClassHour—— 对应导出表『实际扣课时』列(--hour-field checked切换)。
两列可能不同。实测 2026-07 差 19 课时,来自 5 条到课记录(伊铭轩 7/29-31、崔思涵 7/24、杨竺鑫 7/5),
均为到课但实际扣课时更少或未扣;另有请假/未到学员 plan>0 但未实际扣课时(已按规则置 0)。 - 仅统计本人点名的课(
rollCallTeacherId === 登录者 teacherId);共带班里他人点名的排除并列出。 - 分类 = 看班级名:含
集训/csp/信奥/c++/cpp/c++→ C++;
含python→ Python;其余 → 其他。
When to use
- "统计某老师 7 月份的课时,按 C++/Python 分类,算一下多少钱(C++ 12 元、Python 7.5 元 ×0.75)"
- "统计 XX、YY 几个学员在 7月6日以后我上的课时"
- "读取小麦助教里的学员课消 / 排课 / 考勤 / 点名记录"
- 结算类需求:课时 × 单价、Python 换算系数、手工追加营地等课时行
Quick start
Node 18+,无第三方依赖。凭据用环境变量 XIAOMAI_ACCOUNT / XIAOMAI_PASSWORD
(或 CLI 参数,推荐环境变量,避免密码出现在进程列表)。脚本不内置任何账号。
按月统计教师课时 + 账单 (scripts/query_hours.js)
# 基础:扣课时口径,2026年7月,单价 C++12 / Python7.5,Python ×0.75
node scripts/query_hours.js 138xxxxxxxx "密码" 赵宇琦 2026 7 \
--extra "营地,784,cpp"
# 等价写法(全部选项显式给出)
node scripts/query_hours.js --account 138xxxxxxxx --password "密码" --teacher 赵宇琦 \
--year 2026 --month 7 --hour-field plan --price-cpp 12 --price-python 7.5 \
--python-rate 0.75 --extra "营地,784,cpp"
# 切实际扣课时口径核对
node scripts/query_hours.js 138xxxxxxxx "密码" 赵宇琦 2026 7 --hour-field checked
输出:控制台汇总 + 账单(小计/总计)+ JSON + CSV(逐学员行,含 扣课时/实际扣课时 两列,
与平台导出的「学员点名记录」可直接对账)。
按学员名单 + 日期范围 (scripts/query_student_hours.js)
node scripts/query_student_hours.js --students "张三,李四,王五" --from 2026-07-07 \
[--to 2026-08-01] [--hour-field plan|checked]
node scripts/query_student_hours.js --students students.txt --from 2026-07-07
- "X日以后"默认按次日 00:00 起算(如"7月6日以后"→
--from 2026-07-07),向用户说明该假设。 - 0 课时学员自动做相近姓名核对,防止 API 返回名与用户名单有出入。
Billing(账单计算)
- C++ 小计 = C++扣课时 ×
--price-cpp(默认 12) - Python 小计 = Python扣课时 ×
--python-rate(默认 0.75)×--price-python(默认 7.5) - 总计 = 两项小计之和;
--extra "名称,课时[,分类]"可追加手工课时(营地等,默认按 C++) - 已验证样例(2026-07 赵宇琦,与用户对账一致):
C++/CSP 781 × 12 = 9,372 元 + 营地 784 × 12 = 9,408 元 + Python 27×0.75=20.25 × 7.5 = 151.88 元
→ 总计 18,931.88 元
How it works (the non-obvious bits)
完整 API 契约见 references/api_reference.md,核心四点:
- 登录:
POST {GATEWAY}/business/anon/b/login,密码是 MD5 32 位小写(不是 RSA)。 - 网关:用公网可达的
https://gateway.xiaomai5.com;前端默认api-b.xiaomai5.com是内网域名,公网无法解析。 - 认证:HTTP 头 + query 双重携带(token/xmtoken/uid/userid/tid/instid/bizaccountid/aid + 静态头),只带 query 会返回「请重新登录!」。
- 课时:点课记录一次课一条,会话级
checkedClassHour不能直接相加,必须按signId
调getStuRollCallsBySignId逐学员累加;用planCheckedClassHour(扣课时)或
checkedClassHour(实际扣课时),字段含义见上。
Resources
scripts/query_hours.js—— 按月统计 + 分类 + 账单 + CSV(推荐入口)scripts/query_student_hours.js—— 按学员名单 + 日期范围统计references/api_reference.md—— 逆向得到的完整 API 契约、认证表、字段映射与已验证样例
Notes / caveats
- 登录可能限流("登录过于频繁"),脚本自动重试 5 次、间隔 8s。
instUserInfos多机构时脚本用第一个;统计其他老师需传其teacherId(或换账号登录),姓名参数仅作标签。stateList:[1]取有效记录,与页面默认导出一致。- 上课时间(
classStartDate) 与点课时间(signDate) 通常不同;口径严格按上课时间。 - 集训/CSP/信奥一律归 C++;如客户另有归类口径,改
classify()即可。 - 营地等非点课课时用
--extra手工追加并保留来源说明。 - 凭据属用户;脚本不落盘、不打日志,避免把密码/token 打到共享面。
本仓库对接(SkillHub 1.1.0 + 本地补丁)
仓库里本来就有 ops/guide/ 下的小麦取数指南(路径/坑),脚本以前只在 WorkBuddy 技能目录。现已把 SkillHub @user_3bd3856c/xiaomai-classhour-sum 收进 .cursor/skills/xiaomai-classhour-sum/。
相邻能力不要混:
| 东西 | 边界 |
|---|---|
| 本技能 | 小麦助教 xiaomai5.com 点课 / 课消 / 名单 |
OA hours / take-name |
琦琦 OA 自己的课时账本,生产还没接小麦点课全量 |
usaco-import |
OJ 题库 DFS/BFS 专项,不是「DFS/BFS暑期集训」这个班 |
查某 OJ 班学员在小麦的课时:
- 192
group+group_member+user_info拉花名册(例:DFS/BFS暑期集训gid 1017),去掉老师号「七七」。 - 小麦按上课时间拉
queryInstRollCalls→getStuRollCallsBySignId。问「这些人所有点课」时不要按登录者rollCallTeacherId过滤(默认教师账单脚本会滤)。 - 主列用「扣课时」
planCheckedClassHour(到课或已扣才计)。超点 =oweClassHour(oweState=OWE),已经含在扣课时里,不要再加一遍。实际扣课时是checkedClassHour。 - 凭据只用环境变量
XIAOMAI_ACCOUNT/XIAOMAI_PASSWORD,可放本机~/.config/xiaomai.env(勿提交)。
综合技能(OJ 花名册 + 小麦点课 + OA 账本对账)还没写,后续单独做。