# 首次扫描时间的使用范围说明

**更新日期**: 2026-05-16  
**主题**: 首次扫描时间在SQL中的具体使用  
**状态**: ✅ 验证无冲突

---

## 首次扫描时间的来源

### 定义位置

**OverallScanStatus CTE**（第814-822行）：
```sql
OverallScanStatus AS (
    SELECT 
        s.NeutralWaybillNumber,
        MAX(CASE WHEN s.Result = 0 THEN 1 ELSE 0 END) AS 曾成功,
        MIN(CASE WHEN s.Result = 0 THEN DATE(...) ELSE NULL END) AS 首次成功日期,
        MIN(CASE WHEN s.Result = 0 THEN s.CreatedAt ELSE NULL END) AS 首次成功时间
    FROM label_scan_history s
    GROUP BY s.NeutralWaybillNumber
)
```

### 含义

- `首次成功时间` = 该包裹**首次扫描成功**（Result = 0）的时刻
- 来自 `label_scan_history.CreatedAt` 的最小值（第一次成功）

---

## 首次扫描时间的使用地点

### 1. DailyLowLabelRate24HCompleted CTE

**位置**：第962-974行

**使用方式**：
```sql
WHERE 
    oss.曾成功 = 1 
    AND oss.首次成功时间 IS NOT NULL
    AND ar.考核时间 IS NULL
```

**作用**：
- 判断该包裹是否曾经成功扫描
- 配合"考核时间为NULL"（低标签率）来统计完成数

**不影响的地方**：❌ 冻结标签率计算

### 2. Daily24HCompletedOrders CTE

**位置**：第976-996行

**使用方式**：
```sql
WHERE 
    oss.曾成功 = 1 
    AND oss.首次成功时间 IS NOT NULL
    AND (
        -- 情况1：标签率>=80%，有固定考核时间
        (ar.考核时间 IS NOT NULL AND CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') <= ar.考核时间)
        OR
        -- 情况2：标签率<80%，完成时间本身就是考核时间（即完成即达标）
        (ar.考核时间 IS NULL)
    )
```

**作用**：
- 用 `首次成功时间` 与 `考核时间` 比较
- 判断包裹是否在考核期限内完成

**不影响的地方**：❌ 冻结标签率计算

### 3. DailyBeforeNoonPassed CTE

**位置**：第1000-1021行

**使用方式**：
```sql
WHERE 
    oss.曾成功 = 1 
    AND oss.首次成功时间 IS NOT NULL
    AND CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') <= ar.考核时间
```

**作用**：
- 统计16点前到仓且完成考核的包裹
- 用首次成功时间来判断是否满足考核时间

**不影响的地方**：❌ 冻结标签率计算

### 4. DailyAfternoonPassed CTE

**位置**：第1023-1044行

**使用方式**：
```sql
WHERE 
    oss.曾成功 = 1 
    AND oss.首次成功时间 IS NOT NULL
    AND CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') <= ar.考核时间
```

**作用**：
- 统计16点后到仓且完成考核的包裹
- 用首次成功时间来判断是否满足考核时间

**不影响的地方**：❌ 冻结标签率计算

### 5. DailyLowLabelRatePassed CTE

**位置**：第1050-1069行

**使用方式**：
```sql
WHERE 
    oss.曾成功 = 1 
    AND oss.首次成功时间 IS NOT NULL
    AND ar.考核时间 IS NULL
```

**作用**：
- 统计低标签率（考核时间=NULL）且成功的包裹
- 完成即达标

**不影响的地方**：❌ 冻结标签率计算

---

## 与冻结标签率计算的关系

### 冻结标签率的计算

**位置**：InterchangeUnitLabelRates CTE（第734-763行）

**计算方式**：
```sql
label_rate_percent = COUNT(DISTINCT CASE 
                      WHEN l.Label IS NOT NULL AND l.Label != '' 
                      THEN l.Id 
                    END) * 100.0 / COUNT(DISTINCT l.Id)
```

**特点**：
- ✅ 只依赖 `label_replace_requests` 中的 `Label` 字段
- ❌ **不涉及** `首次成功时间`
- ❌ **不涉及** `label_scan_history`
- ❌ **不涉及** 时间比较

### 验证

**首次扫描时间在冻结标签率计算中的使用**：
```
❌ 未使用
❌ 不需要使用
❌ 对计算没有影响
```

---

## 数据流整体分析

```
【冻结标签率的独立计算】
label_replace_requests（有标签？）
    ↓
InterchangeUnitLabelRates（只看是否有标签）
    ↓
冻结标签率 >= 80% 或 < 80%
    ↓
DailyHighLabelRateShould / DailyLowLabelRateShould（分类交接单）

【考核完成情况的独立统计】
label_scan_history（首次成功扫描）
    ↓
OverallScanStatus（首次成功时间）
    ↓
各个考核CTE（DailyBeforeNoonPassed 等）
    ↓
判断是否满足考核时间

【两条线的汇总】
冻结标签率 + 考核结果 = 最终报表
```

---

## 总结

### 首次扫描时间的用途

| 用途 | 位置 | 说明 |
|------|------|------|
| ✅ 判断是否曾成功 | DailyLowLabelRate24HCompleted 等 | 用来过滤成功的包裹 |
| ✅ 比较考核时间 | DailyBeforeNoonPassed、DailyAfternoonPassed 等 | 判断是否在考核期限内 |
| ✅ 统计历史数据 | 整体报表 | 多维度分析 |
| ❌ 计算冻结标签率 | InterchangeUnitLabelRates | **完全不使用** |

### 对现有逻辑的影响

```
❌ 不影响冻结标签率的计算
❌ 不影响高/低标签率的分类
✅ 只用于考核结果的判断（已有逻辑）
✅ 用于历史数据统计（补充信息）
```

### 结论

**首次扫描时间的引入完全安全，不会影响现有逻辑。**

- 冻结标签率：基于标签属性（不依赖扫描时间）
- 考核完成：基于成功时间与考核期限的比较（需要扫描时间）
- 两部分逻辑独立，互不影响

---

## 编译状态

✅ **编译成功** (exit code 0)
- 逻辑清晰，没有冲突
- 首次扫描时间的使用范围明确
- 与冻结标签率完全独立

