242 lines
6.7 KiB
Markdown
242 lines
6.7 KiB
Markdown
# 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)
|
||
|