# 24小时换单率修复 - 完成总结

**修复完成日期**：2026-05-16  
**编译状态**：✅ 成功（exit code: 0）

---

## 修复概述

根据用户的最终业务逻辑澄清，已完成24小时换单率的根本性修复。核心改进：**使用作业时标签率（基于最早扫描时间）替代现在标签率**，确保分子分母维度一致。

---

## 核心问题与解决方案

### 问题：为什么超过100%？

**根本原因**：
1. **分子按完成日期分组**：某天完成的包裹（可能来自多天到货）
2. **分母按到货日期分组**：某天到货的应该完成的包裹
3. **导致分子 >> 分母**：例如分子=130，分母=50 → 260%

**更深层问题**：
- 用的是"现在的标签率"判断，而不是"作业时的标签率"
- 作业决策在最早扫描时刻做的，此时的标签率是固定的
- 用错误的标签率判断导致错误的包裹被纳入24H统计

---

## 实施的三大关键修改

### 修改1：创建"作业时标签率" CTE

**新增CTE**：`InterchangeUnitLabelRatesAtFirstScan`（第760-791行）

```sql
作业时标签率 = 最早扫描时间之前推送的标签数 / 总包裹数
```

**原理**：
- 最早扫描时间 = 任何Result值的最早扫描记录
- 在这个时刻，标签率是"冻结"的
- 这个标签率决定了包裹是否纳入24H考核

---

### 修改2：修改考核时间逻辑

**位置**：ArrivalRequests CTE（第802-843行）

**改变**：
```sql
原来：用现在的标签率判断 (label_rate_percent)
现在：用作业时标签率判断 (label_rate_at_first_scan)
```

**含义**：
- 作业时标签率≥80% → 设定考核时间（16点前后不同）
- 作业时标签率<80% → 无考核时间（完成即达标）

**结果**：每个包裹的考核方式由其作业时的标签率决定，而不是最终标签率

---

### 修改3：分子分母维度对齐

**分母改为使用作业时标签率**（第1113-1115行）：
```sql
FROM InterchangeUnitLabelRatesAtFirstScan
WHERE label_rate_at_first_scan >= 80
```

**分子改为按到货日期分组**（第1047、1066行）：
```sql
GROUP BY DATE(CONVERT_TZ(ar.到货时间, '+00:00', '-05:00'))
```

**结果**：
- 分子：某天到货、作业时标签率≥80%、已完成的包裹
- 分母：某天到货、作业时标签率≥80%的全部包裹
- **维度一致，分子 ≤ 分母**

---

## 修复前后对比

### 修复前（有问题）

```
DailyBeforeNoonPassed（分子）：
└─ GROUP BY 首次成功日期
   
DailyAfternoonPassed（分子）：
└─ GROUP BY 首次成功日期
   
DailyHighLabelRateShould（分母）：
└─ GROUP BY 到货日期

结果：日期维度不同，导致分子 >> 分母 → 超过100%
```

### 修复后（正确）

```
DailyBeforeNoonPassed（分子）：
├─ GROUP BY 到货日期 ✓
├─ WHERE 作业时标签率≥80% ✓
└─ AND 完成时间 <= 考核时间 ✓
   
DailyAfternoonPassed（分子）：
├─ GROUP BY 到货日期 ✓
├─ WHERE 作业时标签率≥80% ✓
└─ AND 完成时间 <= 考核时间 ✓
   
DailyHighLabelRateShould（分母）：
├─ GROUP BY 到货日期 ✓
├─ FROM InterchangeUnitLabelRatesAtFirstScan
└─ WHERE 作业时标签率≥80% ✓

结果：日期维度一致，分子 ≤ 分母 → 0-100%
```

---

## 修改的具体代码位置

| 操作 | 文件 | 行号 | 内容 |
|------|------|------|------|
| 新增CTE | LabelReplaceRepository.cs | 760-791 | InterchangeUnitLabelRatesAtFirstScan |
| 修改ArrivalRequests | LabelReplaceRepository.cs | 802-843 | 新增作业时标签率字段，修改考核时间逻辑 |
| 修改DailyHighLabelRateShould | LabelReplaceRepository.cs | 1100-1117 | 改用作业时标签率判断 |
| 修改DailyBeforeNoonPassed | LabelReplaceRepository.cs | 1033-1050 | 改按到货日期分组 |
| 修改DailyAfternoonPassed | LabelReplaceRepository.cs | 1052-1069 | 改按到货日期分组 |

---

## 预期效果

修复后24H换单率应该表现为：

```
24H换单率 = (该天到货、作业时标签率≥80%、已完成的包裹)
           / (该天到货、作业时标签率≥80%的全部包裹) × 100%

特性：
✅ 0% ≤ 24H换单率 ≤ 100%
✅ 分子 ≤ 分母（数学上正确）
✅ 可以手工验证（选一天数据验证）
✅ 符合业务含义（该天到货订单24小时内完成率）
```

---

## 测试建议

### 第1步：查询某一天的统计数据

```sql
SELECT 
    日期,
    高标签率应该换单数 AS 分母,
    16点前考核通过包裹数 + 16点后考核通过包裹数 AS 分子,
    24H换单率
FROM 日级报表
WHERE 日期 = '2026-05-15'
```

验证：分子 ≤ 分母，24H换单率 ≤ 100%

### 第2步：检查作业时标签率vs现在标签率

某些订单的标签率会在作业时间之后继续增加，导致：
- 作业时标签率 < 80% → 按完成即达标处理
- 现在标签率 ≥ 80% → 但不会被纳入24H考核（因为作业时没有达到80%）

这是**正确行为**，因为决策是在作业开始时做的。

### 第3步：验证边界情况

测试以下场景：
- 某天到货100个订单，作业时79% → 应该2-48小时内完成
- 某天到货100个订单，作业时80% → 应该按16点前后分别设定考核时间

---

## 关键设计理念

### "冻结"的标签率

```
时间线：
┌─ 标签推送 → 标签率: 50%
│
├─ 最早扫描 ← 作业时标签率冻结在这个时刻
│ (标签率: 60%)
│
├─ 继续扫描和标签推送 → 标签率: 70% → 85%
│ (但不影响作业策略，已经确定要按60%处理)
│
└─ 首次成功 → 完成时间确定

决策依据：作业时标签率（60%）而不是最终标签率（85%）
```

### 两种场景的处理

**场景A：作业时标签率≥80%**
- 需要在规定时间内完成
- 完成时间 ≤ 考核时间 → 考核通过
- 完成时间 > 考核时间 → 考核不通过

**场景B：作业时标签率<80%**
- 无需在特定时间内完成
- 完成即达标，不需要考核时间

---

## 编译验证

✅ **编译成功**  
- 命令：`dotnet build src/DAL/DAL.csproj`
- 结果：exit code 0
- 时间：2026-05-16

---

## 后续行动

1. **部署到测试环境**：运行修复后的查询
2. **数据验证**：确认24H换单率 ≤ 100%
3. **手工抽查**：选择3-5个日期，手工验证分子分母
4. **监控**：上线后监测是否有异常数据

---

## 文档参考

- 修复计划：[fix_24h_rate_based_on_first_scan_plan.md](fix_24h_rate_based_on_first_scan_plan.md)
- 之前诊断：[24h_rate_numerator_denominator_diagnosis.md](24h_rate_numerator_denominator_diagnosis.md)
- 代码位置：[LabelReplaceRepository.cs](file:///d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs#L709)

