Skip to content

考勤系统模块 ​

attendance 模块封装西安交通大学考勤系统接口,提供考勤系统登录、考勤流水查询、课程考勤状态查询和考勤系统课表查询能力。GUI 中的“考勤流水”页面和“课表与考勤”页面都会使用这个模块。

本科生与研究生考勤系统接口结构基本一致,但部署在不同域名下。模块通过 is_postgraduate 参数选择访问本科生或研究生系统。

模块职责 ​

考勤模块支持以下能力:

  • 本科生考勤系统访问。
  • 研究生考勤系统访问。
  • 普通访问与 WebVPN 访问。
  • 用户名密码登录与二维码登录。
  • 登录后提取并维护 X-Business-Token header。
  • 查询刷卡流水。
  • 查询课程维度的考勤状态。
  • 查询考勤系统中的课表数据。

代码位置 ​

文件职责
attendance/attendance.py考勤系统登录类、数据结构、API 封装
app/sessions/attendance_session.pyGUI 层考勤站点 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.cnATTENDANCE_URL / ATTENDANCE_WEBVPN_URL
研究生yjs-kq.xjtu.edu.cnPOSTGRADUATE_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普通访问否
AttendanceNewWebVPNLoginWebVPN否
AttendanceNewQRCodeLogin普通访问是
AttendanceNewWebVPNQRCodeLoginWebVPN是

这些登录类复用 auth 模块的统一认证状态机。普通登录类继承 NewLogin,WebVPN 登录类继承 NewWebVPNLogin,二维码登录类通过 QRCodeLoginMixin 复用扫码登录流程。

考勤系统的 token 换取逻辑位于 _AttendanceTokenMixin.postLogin():

  1. 统一认证完成后,浏览器会跳转到 <考勤系统域名>/cas/callback?loginRequestId=...&ticket=...。
  2. 从最终跳转 URL 中读取 loginRequestId 与 ticket。
  3. 向 <考勤系统域名>/sa/auth/cas/exchange 提交这两个参数,换取 data.tokenValue。
  4. 写入 self.session.headers["X-Business-Token"]。

这个 header 是 AttendanceSession.validate_login() 判断考勤站点状态的前置条件,也是后续所有考勤接口请求的认证凭据。

AttendanceSession ​

GUI 程序通过 AttendanceSession 接入 Session 管理层。它的关键配置如下:

字段值含义
site_keyattendanceSessionManager 中的注册名称
site_name考勤系统展示给用户的站点名称
supports_webvpnTrue支持 WebVPN 访问
use_webvpn_when_off_campusTrue自动探测为校外时使用 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 包装器。

简化代码示例:

python
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 的流程:

  1. 使用当前账号获取 AttendanceSession。
  2. 调用 ensure_login() 确保考勤系统已登录。
  3. 创建 Attendance 包装器。
  4. 调用 getFlowRecordWithPage() 查询分页流水。
  5. 通过 flowRecord 信号返回 UI。

ScheduleAttendanceThread 的流程:

  1. 确认当前账号和日期范围。
  2. 验证或登录考勤系统。
  3. 查询考勤流水。
  4. 查询课程考勤状态详情。
  5. 通过 result(records, water_page) 返回课程状态和流水。

ScheduleAttendanceMonitorThread 用于处理考勤流水接口响应慢的场景。它监视 ScheduleAttendanceThread,在查询时间较长时提示用户可以取消,并在被监视线程强制结束后尽量返回已获得的部分结果。

与 UI 层的关系 ​

当前有两个 UI 入口使用考勤模块。

AttendanceInterface 是独立考勤流水页面。它创建 AttendanceFlowThread,接收 flowRecord 后把 AttendanceFlow 列表显示到表格中。表格中会将 FlowRecordType 映射为“有效”“无效”“未知”。

ScheduleInterface 在课表页面中集成考勤能力。它创建 ScheduleAttendanceThread 和 ScheduleAttendanceMonitorThread,接收 AttendanceWaterRecord 与 AttendanceFlow 后,把课程考勤结果合并到课表显示和本地课表数据库状态中。

自动重试与取消 ​

考勤系统部分接口响应较慢或偶发返回异常。当前实现中,cfg.autoRetryAttendance 控制部分查询失败后的自动重试行为。

自动重试主要捕获:

  • ServerError
  • json.JSONDecodeError
  • requests.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。

继续阅读 ​

Released under the GPL-3.0 License.