Files
gongxue-base/docs/superpowers/specs/2026-07-13-binary-course-attendance-design.md

64 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 课程二态考勤与截止自动结算设计
## 目标
课程考勤仅向教师展示“已打卡 / 未打卡”。迟到属于已打卡。每节课程到达截止时间后,系统自动从钉钉做最后一次拉取并将最终结果写入课程考勤记录;截止仍无实际打卡的学生记为缺勤。
## 范围
- 仅调整排课关联的课程考勤。
- 不迁移历史记录。
- 不改变后台其他考勤来源、迟到统计或钉钉原始数据。
- 不引入队列或新依赖,复用 NestJS Schedule 与现有导入、匹配、课程考勤服务。
## 状态规则
### 教师当前课程页面
- 存在课程时间窗口内的实际打卡时间:显示“已打卡”。
- 不存在实际打卡时间:显示“未打卡”。
- `Late``SeriousLate``Normal` 均显示为“已打卡”。
- 页面汇总仅显示已打卡数、未打卡数和总人数。
### 最终记录
- 截止时存在实际打卡时间:`present`
- 截止时不存在实际打卡时间:`absent`
- 钉钉原始记录继续保留 `timeResult`,因此不会丢失迟到信息。
- 自动结算完成后,课程考勤场次状态改为 `completed`,不再被后续拉取覆盖。
## 自动结算
后台任务每分钟扫描:
1. 当天有效的内部课程;
2. 当前时间已经达到课程 `endTime`
3. 对应日期的课程考勤场次尚未完成或尚未创建。
对每节符合条件的课程:
1. 获取该班在读学生的钉钉用户 ID
2. 拉取当天最终钉钉考勤并自动匹配;
3. 创建或刷新课程考勤记录;
4. 将有实际打卡的记录归为 `present`,其余归为 `absent`
5. 将场次标记为 `completed`
任务按课程独立处理。单节课拉取失败只记录错误,其他课程继续;下一分钟继续补偿失败课程。现有 `(scheduleId, lessonDate)` 唯一约束和完成状态保证重复扫描幂等。
跨午夜课程以结束时间不晚于开始时间判断为次日截止;扫描同时覆盖昨日跨午夜课程。
## 手动查看
课程开始后,教师点击“查看当前考勤”仍会拉取最新数据。课程截止前结果是临时二态视图;课程截止后读取自动结算的最终记录。若自动任务尚未成功,手动查看可继续拉取,但只有自动结算或明确完成操作会冻结最终结果。
## 测试
- 迟到且存在实际打卡时间时,当前课程视图为“已打卡”。
- 无实际打卡时间时,当前课程视图为“未打卡”。
- 截止结算把迟到和正常打卡写为 `present`
- 截止结算把无打卡写为 `absent` 并完成场次。
- 未截止课程不结算。
- 重复扫描已完成课程不重复拉取或写入。
- 单节课程失败不阻断其他课程,后续扫描可补偿。
- 跨午夜课程在次日截止后结算。