Files
railwaysystem/docs/archive/shared-trial-baseline-20260920/07-analytics-attendance-semantics.md
T
wangzhibiao 8ad0f5172e 文档架构:建立唯一现行系统真理文档门户 (Current Truth Bootstrap)
docs: bootstrap canonical current truth portal (00-START-HERE through 08-NEXT-ACTIONS)
2026-09-23 23:50:37 +08:00

72 lines
7.6 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.
# 07. 考勤统计语义与指标口径规范 (Attendance Analytics Semantics)
> **版本**:V1.0 (Package C 闭环基线)
> **更新日期**:2026-09-21
> **适用模块**:管理驾驶舱 (Dashboard)、考勤专题分析 (Attendance Analytics)、学员 360 画像 (Student360)、考勤异常 Top 10
> **核心原则**:统一底层 Expected Attendance Universe 聚合语义,严格隔离未来课次,严格区分缺勤与未签到,技术语义守恒闭环。
---
## 1. 冻结技术语义 (Frozen Technical Semantics)
| 技术语义项 | 规范定义 | 技术落地点 | 解决问题 |
| :--- | :--- | :--- | :--- |
| **已开始课次 (Started Lesson)** | `lesson.start_time <= CURRENT_TIMESTAMP`<br>未来课次打卡记录严格隔离,不得进入任何当前历史统计。 | `TrainAnalysisMapper.xml` 所有考勤聚合统一条件 `l.start_time <= CURRENT_TIMESTAMP` | **C07 关闭**:消除 Dashboard 状态分布泄漏 46 条未来课次打卡记录缺陷。 |
| **应到基准集 (Expected Attendance Universe)** | `STARTED_LESSONS × PLAN_COURSES × ELIGIBLE_ENROLLMENTS LEFT JOIN CHECKIN`<br>由计划已开课次与计划参训学员关系形成应到基准,不得由打卡流水作为驱动表。 | 共享查询基准 `expectedAttendanceBase` / `filteredAttendanceConditions` | **C08 关闭**:消除异常 Top 10 仅从打卡流水统计导致未打卡学员不可见缺陷。 |
| **显式缺勤 vs 未签到** | - **显式缺勤 (Explicit Absence)**:打卡流水存在且 `attendance_status = 0`。<br>- **未签到 (No Checkin)**:无打卡流水行(`checkin_id IS NULL`)。<br>两者在状态分布、Top 10、学员画像中严格互斥,不得隐式混淆。 | 状态派生:`CASE WHEN c.checkin_id IS NULL THEN 'NO_CHECKIN' ELSE CAST(c.attendance_status AS VARCHAR) END` | 杜绝将无流水学员擅自改写为 status=0,客观反映教务未核验状态。 |
| **趋势时间轴 (Trend Time Axis)** | 每日考勤打卡走势时间轴必须锚定 `lesson.start_time`,严禁锚定 `sign_in_time`。<br>确保正常、迟到、早退、请假、明确缺勤、未签到均落入课次排课日期。 | `TO_CHAR(l.start_time, 'YYYY-MM-DD') AS stat_date` 统一分组 | **C06 关闭**:消除因 `sign_in_time IS NULL` 导致缺勤/请假/未签到无法进入走势的缺陷。 |
| **状态分布守恒律** | `EXPECTED_PERSON_TIMES = NORMAL + LATE + EARLY + LEAVE + EXPLICIT_ABSENCE + NO_CHECKIN` | Dashboard 与 Attendance 共享 `buildStatusDistribution` | 全局与专题分析在无筛选或相同筛选条件下统计口径严格 1:1 相等。 |
| **走势守恒律** | 筛选范围内各日期走势累加 `SUM(normal_count + abnormal_count)` 严格等于该范围内的 `EXPECTED_PERSON_TIMES`。 | `selectFilteredDailyTrend` | 走势无漏项、无未来虚假漂移。 |
---
## 2. 待确认业务定义清单 (Business Definitions Pending Confirmation)
以下规则由于缺乏业务需求权威定论或底层数据表时序字段支撑,代码中均以策略开关或明确常量集中隔离,**严禁作为已定案的最终甲方规则**:
### 2.1 PENDING-01: 待报到参训学员的应到基准 (`WAITING_ENROLLMENT_EXPECTED_POLICY`)
- **状态**:`BUSINESS_DEFINITION_REQUIRED`
- **问题说明**:学员参训记录为 `status=0`(待报到)且无报到核验流水时,是否从该培训班开班的第一节课起就计入应到基准分母。
- **当前处理**:V1 默认不以 `EXISTS(train_class_report)` 过滤应到集合,确保未到场报到人员可进入缺勤/未签到统计;待甲方确认是否仅针对开班前已完成报名审核的名单起算。
### 2.2 PENDING-02: 考勤请假是否计入出勤率分母 (`LEAVE_DENOMINATOR_POLICY`)
- **状态**:`BUSINESS_DEFINITION_REQUIRED`
- **问题说明**:学员在某课次拥有 `attendance_status=4`(请假)流水时,该人次是否从出勤率分母(应到人次)中扣除,还是保留在分母但计为非正常出勤。
- **当前处理**:当前系统出勤率公式为 `正常出勤人次 / 应到总人次`(请假保留在分母,但非正常)。页面与 API 返回均携带 `metricDefinitionNotice = BUSINESS_METRIC_DEFINITION_PENDING_CONFIRMATION` 声明。
### 2.3 PENDING-03: 放弃参训学员的历史课次有效性 (`ABANDON_HISTORICAL_POLICY`)
- **状态**:`BUSINESS_DEFINITION_REQUIRED`
- **问题说明**:学员参训状态为 `status=3`(放弃)时,由于 `train_enrollment` 表无状态变更生效时间区间(`HISTORICAL_ENROLLMENT_ELIGIBILITY_EXACT = NO`),无法精准切分放弃前与放弃后的课次。
- **当前处理**:当前统计按所属计划完整名单统计,不利用当前最终状态逆向重写历史;待建立学员流转审计历史表后重构精确时间切片。
### 2.4 PENDING-04: 请假是否计入考勤异常排行 (`LEAVE_COUNTS_AS_ABNORMAL`)
- **状态**:`BUSINESS_DEFINITION_PENDING_CONFIRMATION`
- **问题说明**:请假属于审批合规事项,部分管理制度认为请假不属于“违规异常考勤”,不应计入 Top 10 预警排行榜。
- **当前处理**:集中在 `TrainAnalysisServiceImpl.LEAVE_COUNTS_AS_ABNORMAL_DEFAULT = true`(当前预览保持系统现有计入异常口径),并通过 `AttendanceFilterDto.includeLeaveAsAbnormal` 支持单点无缝切换,未散落硬编码。
---
## 3. 跨模块考勤统计语义对齐矩阵
| 功能界面 / API | 基础驱动集 | 正常出勤 | 异常考勤构成 | 未签到 (无打卡) | 未来课次防护 |
| :--- | :--- | :---: | :--- | :---: | :---: |
| **管理驾驶舱 状态分布**<br>`/train/analysis/dashboard/charts` | 已开始课次 × 班级学员 | 正常出勤 (`1`) | 迟到(`2`)、早退(`3`)、请假(`4`)、缺勤(`0`) | 未签到 (`NO_CHECKIN`) | 隔离(0 泄漏) |
| **管理驾驶舱 30天走势**<br>`/train/analysis/dashboard/charts` | 已开始课次 × 班级学员 | 正常人次 (`1`) | 异常人次 (`0,2,3,4,NO_CHECKIN`) | 计入异常人次 | 隔离(0 泄漏) |
| **考勤专题 状态构成**<br>`/train/analysis/attendance/charts` | 已开始课次 × 班级学员 | 正常出勤 (`1`) | 迟到(`2`)、早退(`3`)、请假(`4`)、缺勤(`0`) | 未签到 (`NO_CHECKIN`) | 隔离(0 泄漏) |
| **考勤专题 每日趋势**<br>`/train/analysis/attendance/charts` | 已开始课次 × 班级学员 | 正常人次 (`1`) | 异常人次 (`0,2,3,4,NO_CHECKIN`) | 计入异常人次 | 隔离(0 泄漏) |
| **考勤异常 Top 10**<br>`/train/analysis/attendance/charts` | 已开始课次 × 班级学员 | 不参与排行 | 缺勤(`absentCount`)、迟到(`lateCount`)、早退(`earlyLeaveCount`)、请假(`leaveCount`) | 未签到 (`noCheckinCount`) | 隔离(0 泄漏) |
| **学员 360 画像 考勤卡片**<br>`/train/analysis/portrait/student-360/{id}` | 该学员所属计划已开始课次 | 正常出勤 (`normalCount`) | 迟到、早退、请假、明确缺勤 | 未签到 (`noCheckinCount`) | 隔离(0 泄漏) |
---
## 4. 数据库只读验证事实 (Kingbase QA Live Snapshot)
在 `railwaysystem_integrated_qa_20260916` 数据库实例上的只读验证结果:
- 课次总数:118,其中已开始课次:1(Lesson 90),未来课次:117。
- 考勤打卡记录总数:77,其中已开始课次打卡:31,未来课次打卡:46。
- 已开始课次应到人次:31,打卡匹配人次:31,未签到人次:0。
- 31 人次考勤分布:正常 31,迟到 0,早退 0,请假 0,缺勤 0,未签到 0。
- 守恒断言:`EXPECTED_PERSON_TIMES (31) = NORMAL (31) + ABNORMAL (0)`,`PASS`。
- 未来打卡泄漏:`FUTURE_LESSON_ANALYTICS_LEAK_COUNT = 0`,`PASS`。
- `DUPLICATE_CHECKIN_KEY_COUNT = 0`,无重复打卡键。