考勤系统模块
attendance 模块封装西安交通大学考勤系统接口,提供考勤系统登录、考勤流水查询、课程考勤状态查询和考勤系统课表查询能力。GUI 中的“考勤流水”页面和“课表与考勤”页面都会使用这个模块。
本科生与研究生考勤系统接口结构基本一致,但部署在不同域名下。模块通过 is_postgraduate 参数选择访问本科生或研究生系统。
模块职责
考勤模块支持以下能力:
- 本科生考勤系统访问。
- 研究生考勤系统访问。
- 普通访问与 WebVPN 访问。
- 用户名密码登录与二维码登录。
- 登录后提取并维护
X-Business-Tokenheader。 - 查询刷卡流水。
- 查询课程维度的考勤状态。
- 查询考勤系统中的课表数据。
代码位置
| 文件 | 职责 |
|---|---|
attendance/attendance.py | 考勤系统登录类、数据结构、API 封装 |
app/sessions/attendance_session.py | GUI 层考勤站点 Session |
app/threads/AttendanceFlowThread.py | 考勤流水查询线程 |
app/threads/ScheduleAttendanceThread.py | 课表界面中的考勤查询线程 |
app/threads/ScheduleAttendanceMonitorThread.py | 考勤查询监视线程 |
app/AttendanceInterface.py | “考勤流水”主界面 |
app/ScheduleInterface.py | “课表与考勤”界面中的考勤集成 |
系统入口与域名
考勤系统按用户类型分为两个站点,隶属于“电子考勤管理平台”(kq.xjtu.edu.cn):
| 用户类型 | 域名 | 登录入口常量 |
|---|---|---|
| 本科生 | bk-kq.xjtu.edu.cn | ATTENDANCE_URL / ATTENDANCE_WEBVPN_URL |
| 研究生 | yjs-kq.xjtu.edu.cn | POSTGRADUATE_ATTENDANCE_URL / POSTGRADUATE_ATTENDANCE_WEBVPN_URL |
两个站点的业务接口完全一致,统一位于 /sa 路径下。Attendance 类中的 _request() 会根据 is_postgraduate 选择域名,并把接口路径拼接为完整 URL,同时解析响应外壳({"code": 0, "data": ...})并返回其中的 data。新增 API 方法时,应继续使用 _get() / _post() 访问接口,让域名选择、错误处理保持一致。
新版系统只提供统一认证入口,普通访问与 WebVPN 访问使用同一个入口地址,两者仅在是否通过 webvpn.xjtu.edu.cn 转发上有区别。
登录与 X-Business-Token
考勤系统完成统一认证后,还需要在后续请求中携带 X-Business-Token header。模块通过专用登录类换取业务 token 并写入 session headers。
| 登录类 | 访问方式 | 二维码 |
|---|---|---|
AttendanceNewLogin | 普通访问 | 否 |
AttendanceNewWebVPNLogin | WebVPN | 否 |
AttendanceNewQRCodeLogin | 普通访问 | 是 |
AttendanceNewWebVPNQRCodeLogin | WebVPN | 是 |
这些登录类复用 auth 模块的统一认证状态机。普通登录类继承 NewLogin,WebVPN 登录类继承 NewWebVPNLogin,二维码登录类通过 QRCodeLoginMixin 复用扫码登录流程。
考勤系统的 token 换取逻辑位于 _AttendanceTokenMixin.postLogin():
- 统一认证完成后,浏览器会跳转到
<考勤系统域名>/cas/callback?loginRequestId=...&ticket=...。 - 从最终跳转 URL 中读取
loginRequestId与ticket。 - 向
<考勤系统域名>/sa/auth/cas/exchange提交这两个参数,换取data.tokenValue。 - 写入
self.session.headers["X-Business-Token"]。
这个 header 是 AttendanceSession.validate_login() 判断考勤站点状态的前置条件,也是后续所有考勤接口请求的认证凭据。
AttendanceSession
GUI 程序通过 AttendanceSession 接入 Session 管理层。它的关键配置如下:
| 字段 | 值 | 含义 |
|---|---|---|
site_key | attendance | SessionManager 中的注册名称 |
site_name | 考勤系统 | 展示给用户的站点名称 |
supports_webvpn | True | 支持 WebVPN 访问 |
use_webvpn_when_off_campus | True | 自动探测为校外时使用 WebVPN |
AttendanceSession._login() 会根据当前访问方式选择登录类:
AccessMode.NORMAL:使用AttendanceNewLogin或AttendanceNewQRCodeLogin。AccessMode.WEBVPN:使用AttendanceNewWebVPNLogin或AttendanceNewWebVPNQRCodeLogin。
同时,它会根据账号类型选择统一认证身份:
- 本科生账号使用
NewLogin.UNDERGRADUATE。 - 研究生账号使用
NewLogin.POSTGRADUATE。
validate_login() 使用 /sa/student/home 验证登录态。它会先检查 X-Business-Token 是否存在,再访问当前账号类型对应的考勤系统域名。
核心数据结构
考勤模块定义了两类状态枚举和两类数据对象。
| 类型 | 含义 |
|---|---|
FlowRecordType | 刷卡流水状态 |
WaterType | 课程的考勤状态 |
AttendanceFlow | 一条刷卡流水 |
AttendanceWaterRecord | 一节课的考勤结果 |
FlowRecordType 包含:
| 枚举 | 含义 |
|---|---|
VALID | 有效刷卡 |
INVALID | 无效刷卡 |
UNKNOWN | 未知状态 |
WaterType 包含:
| 枚举 | 含义 |
|---|---|
NORMAL | 正常 |
LATE | 迟到 |
ABSENCE | 缺勤 |
LEAVE | 请假 |
PENDING | 待考勤:该课次尚未产生考勤结果 |
NOT_REQUIRED | 不考勤:该课次无需考勤 |
UNKNOWN | 未知:服务端返回了未识别的状态 |
AttendanceFlow 的关键字段:
| 字段 | 含义 |
|---|---|
sbh | 流水编号 |
place | 刷卡地点 |
water_time | 刷卡时间 |
type_ | 刷卡状态 |
AttendanceWaterRecord 的关键字段:
| 字段 | 含义 |
|---|---|
sbh | 考勤记录编号 |
term_string | 学期字符串 |
start_time / end_time | 起止节次 |
week | 周数 |
location | 上课地点 |
teacher | 教师 |
status | 课程考勤状态 |
date | 上课日期 |
课程考勤状态与考勤流水
考勤模块里有两类容易混淆的数据:课程考勤状态和考勤流水。
| 类型 | 数据结构 | 代表问题 | 适合用途 | 与课程绑定 |
|---|---|---|---|---|
| 课程考勤状态 | AttendanceWaterRecord | 这节课最终算正常、迟到、缺勤、早退还是请假 | 课表考勤标记、课程维度统计 | 较容易 |
| 考勤流水 | AttendanceFlow | 我什么时候在哪个教室刷过卡,刷卡是否有效 | 考勤流水页面、确认是否打卡 | 较困难 |
课程考勤状态来自 attendanceDetailByTime()。它包含周数、节次、地点、教师、日期和最终考勤状态,因此可以较自然地绑定到课表中的某节课上。ScheduleAttendanceThread 会使用这类数据为课表更新考勤状态。
考勤流水来自 getFlowRecordWithPage() 或 getFlowRecordByTime()。它表示刷卡行为本身,适合回答“有没有刷卡、什么时候刷卡、在哪个教室刷卡、刷卡是否有效”。流水记录和课程的关联信息较少,直接绑定到某节课上需要额外推断。新版考勤系统只区分“有效流水”(effective=true)与“未匹配”(effective=false),因此 AttendanceFlow.type_ 只会是 VALID 或 INVALID。
如果功能需要把考勤结果显示到课表格子上,优先使用 attendanceDetailByTime() 返回的 AttendanceWaterRecord。如果功能需要展示刷卡历史或分页查询流水,使用 getFlowRecordWithPage() / getFlowRecordByTime() 返回的 AttendanceFlow。
Attendance API 封装
Attendance 类是考勤系统 API 包装器。它接收一个已经登录考勤系统的 session,并根据 is_postgraduate 选择本科生或研究生域名。
学期:
| 方法 | 用途 |
|---|---|
getNearTerm() | 获取当前学期信息(含学期编号 name、开始日期 startDate) |
考勤记录:
| 方法 | 用途 |
|---|---|
attendanceDetailByTime(start_date, end_date, current, page_size) | 获取时间段内课程考勤状态详情 |
课表:
| 方法 | 用途 |
|---|---|
getScheduleLessons(term_name) | 获取整个学期课表(jwxt 兼容格式) |
考勤流水:
| 方法 | 用途 |
|---|---|
getFlowRecordWithPage(current, page_size) | 分页获取流水,并返回总数、页码等信息 |
getFlowRecordByTime(start_date, end_date) | 按日期范围获取流水列表 |
响应中的 code != 0 时,方法会抛出 ServerError。HTTP 状态错误由 _request() 中的 raise_for_status() 抛出。
典型调用流程
GUI 线程通常通过当前账号的 SessionManager 获取考勤站点 Session,再创建 Attendance API 包装器。
简化代码示例:
from attendance import Attendance
session = account.session_manager.get_session("attendance")
session.ensure_login(
account.username,
account.password,
is_postgraduate=account.type == account.POSTGRADUATE,
account=account,
)
util = Attendance(
session,
is_postgraduate=account.type == account.POSTGRADUATE,
)
records = util.getFlowRecordWithPage(1, 10)Attendance 对象是轻量 API 包装器。调用时应按当前 session 和账号类型重新创建,避免旧包装器与新的登录状态不一致。
与线程层的关系
考勤模块在 GUI 中主要由三个线程使用:
| 线程 | 用途 |
|---|---|
AttendanceFlowThread | 独立“考勤流水”页面分页查询 |
ScheduleAttendanceThread | 课表页面查询一段日期内的考勤流水和课程考勤 |
ScheduleAttendanceMonitorThread | 监视长时间考勤查询,允许用户提前取消并保留部分结果 |
AttendanceFlowThread 的流程:
- 使用当前账号获取
AttendanceSession。 - 调用
ensure_login()确保考勤系统已登录。 - 创建
Attendance包装器。 - 调用
getFlowRecordWithPage()查询分页流水。 - 通过
flowRecord信号返回 UI。
ScheduleAttendanceThread 的流程:
- 确认当前账号和日期范围。
- 验证或登录考勤系统。
- 查询考勤流水。
- 查询课程考勤状态详情。
- 通过
result(records, water_page)返回课程状态和流水。
ScheduleAttendanceMonitorThread 用于处理考勤流水接口响应慢的场景。它监视 ScheduleAttendanceThread,在查询时间较长时提示用户可以取消,并在被监视线程强制结束后尽量返回已获得的部分结果。
与 UI 层的关系
当前有两个 UI 入口使用考勤模块。
AttendanceInterface 是独立考勤流水页面。它创建 AttendanceFlowThread,接收 flowRecord 后把 AttendanceFlow 列表显示到表格中。表格中会将 FlowRecordType 映射为“有效”“无效”“未知”。
ScheduleInterface 在课表页面中集成考勤能力。它创建 ScheduleAttendanceThread 和 ScheduleAttendanceMonitorThread,接收 AttendanceWaterRecord 与 AttendanceFlow 后,把课程考勤结果合并到课表显示和本地课表数据库状态中。
自动重试与取消
考勤系统部分接口响应较慢或偶发返回异常。当前实现中,cfg.autoRetryAttendance 控制部分查询失败后的自动重试行为。
自动重试主要捕获:
ServerErrorjson.JSONDecodeErrorrequests.Timeout
AttendanceFlowThread 查询流水失败时,会在开启自动重试后等待 2 秒继续查询。ScheduleAttendanceThread 查询考勤流水失败时,也会按同一配置重试。
课表页面的考勤查询还配有监视线程。监视线程会在流水查询完成后开始计时,如果后续课程考勤查询等待较久,就更新进度提示并缩短取消等待时间。用户取消后,如果已有流水或课程状态结果,监视线程会把已有部分返回给 UI。
维护注意事项
- 考勤系统依赖
X-Business-Token,登录后 header 丢失会导致验证失败。 - 本科生和研究生域名不同,新增接口时通过
_request()拼接地址。 - 新增 API 方法时统一检查
result["code"] != 0,失败时抛出ServerError。 - 日期参数通常使用
%Y-%m-%d。 - 课程维度结果优先解析为
AttendanceWaterRecord。 - 刷卡流水结果优先解析为
AttendanceFlow。 - WebVPN 访问方式由
AttendanceSession和CommonLoginSession处理,API 方法保持接口路径和普通域名逻辑。 - 线程中调用接口时,通过
ProcessThread信号反馈进度、错误和结果。
已知限制
- 考勤系统接口有时响应较慢。
- 部分字段命名来自学校接口,含义按当前功能使用场景解释。
- 考勤系统登录态和
X-Business-Token有时效,调用前应通过AttendanceSession.ensure_login()确认。 - 服务端只返回刷卡的布尔有效性,无法区分“重复刷卡”与“无效刷卡”;本地缓存中无法识别的状态会归为
UNKNOWN。
继续阅读
- 认证与登录系统:统一认证与
postLogin()扩展点。 - Session 管理设计:
AttendanceSession如何复用登录态和选择 WebVPN。 - 子线程与进度反馈设计:考勤查询线程如何向 GUI 汇报进度。
- 考勤流水用户手册:用户视角的考勤流水功能。
- 课表与考勤用户手册:用户视角的课表与考勤功能。