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

77 lines
4.3 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.

# 课程二态考勤与截止自动结算 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 教师课程考勤只显示已打卡/未打卡,并在课程截止后自动最终拉取、落库和完成场次。
**Architecture:** 保留钉钉原始迟到状态;课程服务根据“是否存在实际打卡时间”生成临时二态结果和最终 `present/absent`。新增 Attendance 模块内的 NestJS 定时结算服务,每分钟扫描到期课程并复用导入与课程考勤服务,失败留待下一轮补偿。
**Tech Stack:** NestJS 11、@nestjs/schedule 6、TypeORM 0.3、Jest、React 19、Ant Design 6。
## Global Constraints
- 仅调整排课关联的课程考勤。
- 不迁移历史记录,不改变钉钉原始记录。
- 不新增依赖、队列、兼容层或重复状态模型。
- 课程截止后有实际打卡写 `present`,无实际打卡写 `absent`
- 单节失败不得阻断其他课程,后续扫描必须可补偿。
---
### Task 1: 课程二态映射
**Files:**
- Modify: `apps/server/src/attendance/attendance.service.ts`
- Test: `apps/server/src/attendance/attendance.lesson-session.spec.ts`
**Interfaces:**
- Produces: `createLessonAttendanceFromDingTalk(scheduleId, lessonDate, userId, finalize?)``finalize=false` 返回临时二态,`finalize=true` 写最终二态并完成场次。
- [ ] 添加失败测试:`Late` 且有 `checkInTime` 应为 `present`;无实际时间应为 `pending`;最终结算时无时间应为 `absent` 且 session 为 `completed`
- [ ] 运行 `npm test -- attendance.lesson-session.spec.ts --runInBand`,确认新增断言按预期失败。
- [ ] 将课程状态映射改为只检查课程窗口内是否存在 `checkInTime``checkOutTime`;最终结算参数控制无打卡为 `absent`,并在同一事务完成场次。
- [ ] 再次运行相同测试,确认通过。
### Task 2: 截止自动结算
**Files:**
- Create: `apps/server/src/attendance/attendance-settlement.service.ts`
- Create: `apps/server/src/attendance/attendance-settlement.service.spec.ts`
- Modify: `apps/server/src/attendance/attendance.module.ts`
- Modify: `apps/server/src/app.module.ts`
**Interfaces:**
- Consumes: `AttendanceImportService.importFromDingTalk(...)``AttendanceService.getTeacherClassDingUserIds(...)``AttendanceService.createLessonAttendanceFromDingTalk(..., true)`
- Produces: `AttendanceSettlementService.settleEndedLessons(now?: Date): Promise<void>`,由 `@Cron('* * * * *')` 调用。
- [ ] 添加失败测试:未截止不处理、已完成不处理、到期课程最终拉取并结算、一个课程失败后继续处理下一个、昨日跨午夜课程可结算。
- [ ] 运行 `npm test -- attendance-settlement.service.spec.ts --runInBand`,确认因服务不存在而失败。
- [ ] 实现每分钟扫描今天普通到期课程及昨日跨午夜到期课程;逐课程捕获异常并记录;使用课程 `teacherId` 作为自动导入审计用户。
- [ ]`AttendanceModule` 注册服务,在根模块启用 `ScheduleModule.forRoot()`
- [ ] 再次运行相同测试,确认通过。
### Task 3: 教师二态界面
**Files:**
- Modify: `apps/admin/src/pages/Attendance/attendance-workspace.ts`
- Modify: `apps/admin/src/pages/Attendance/index.tsx`
- Test: `apps/admin/src/pages/Attendance/attendance-workspace.test.ts`
**Interfaces:**
- Produces: `summarizeLessonCheckins(records)`,将 `present`/`late` 归为已打卡,将其余归为未打卡。
- [ ] 添加失败测试:`present` 与遗留 `late` 均计入已打卡,`pending`/`absent` 计入未打卡。
- [ ] 运行 admin 的定向测试命令并确认失败。
- [ ] 教师抽屉改为“已打卡 / 未打卡”标签、汇总和手动修改选项;管理员档案保持原五态。
- [ ] 再次运行定向测试,确认通过。
### Task 4: 聚焦验证
**Files:**
- Verify only; no planned production edits.
- [ ] 运行 server 两个定向 Jest 测试文件。
- [ ] 运行 server `npm run typecheck`
- [ ] 运行 admin 定向测试与 `npm run typecheck`
- [ ] 用现有数据库场景确认王子琪的 `Late` 在教师视图归为已打卡、陈浩无记录归为未打卡;不修改数据库数据。