上传源代码版本

This commit is contained in:
Im-Jenisson
2026-06-01 16:30:29 +08:00
commit b2a9b7d3c2
462 changed files with 104365 additions and 0 deletions

View File

@@ -0,0 +1,251 @@
# SQL 指标优化计划
## 一、当前SQL逻辑分析
### 现有指标计算
1. **当日新增换单数**:当天到货并且推送了标签数据的订单数量
2. **累计要换的总单数**:历史未完成换单数 + 当日新增换单数(通过滚动求和计算)
3. **当日标签推送数**:通过标签推送时间计算的当天标签推送数量
4. **当日换单成功数**当天完成的换单包裹数Result = 0
5. **当天换单完成率**:当日完成数/(当日新增换单数+累计要换的总单数)
6. **24H换单率**24小时内完成的包裹数/当日完成数
### 现有逻辑中的关键CTE
- **ArrivalFormsWithDate**:获取所有到货交接单基础信息
- **ArrivalRequests**:关联到货单与换单请求,计算考核时间(目前:取标签推送时间和到仓时间的较晚时间)
- **OverallScanStatus**:获取每个订单的首次成功信息
- **DailyBase**:每日基础统计
---
## 二、新需求分析与实现方案
### 需求1修改包裹考核时间逻辑
**原逻辑**:取标签推送时间与到仓时间的较晚时间
**新逻辑**:根据标签率判断
- **标签率 >= 80%**对客承诺90%
- 到仓时间 < 16:00考核时间截止为**次日16:00**
- 到仓时间 >= 16:00考核时间截止为**次日23:59**
- **标签率 < 80%**对客承诺90%
- 考核时间 = **包裹换单完成时间**
**实现方案**
1. `ArrivalRequests` CTE 中需要
- 计算每个订单所属客户的标签率
- 根据标签率和到仓时间计算新的考核时间
2. 需要新增CTE计算客户的标签率
```
CustomerLabelRate: 计算每个客户的标签率 = 有标签的订单数/总订单数
```
3. 修改 `ArrivalRequests` 中的考核时间计算逻辑
### 需求2修改24小时换单完成率
**原逻辑**24H内完成数/当日完成数
**新逻辑**(包裹换单完成时间 - 包裹考核时间 <= 0 的包裹数) / 当天应该换单数
**说明**
- 包裹换单完成时间 <= 考核时间 的包裹视为24小时内完成
- 分母改为"当天应该换单数"而不是"当日完成数"
**实现方案**
1. 创建新CTE计算每日24小时内完成的包裹数
2. 修改分母为当天应该换单数(历史未完成数+当日新增数)
### 需求3新增指标 - 16:00前到仓的包裹数量
**定义**当日到仓时间在16:00之前的包裹数量
**实现方案**
在每日统计中新增计数:
```sql
COUNT(DISTINCT CASE
WHEN ar.到货日期 = dd.日期 AND HOUR(ar.到货时间) < 16
THEN ar.RequestId
END) AS 16点前到仓包裹数
```
### 需求4新增指标 - 16:00后到仓的包裹数量
**定义**当日到仓时间在16:00之后的包裹数量
**实现方案**
在每日统计中新增计数:
```sql
COUNT(DISTINCT CASE
WHEN ar.到货日期 = dd.日期 AND HOUR(ar.到货时间) >= 16
THEN ar.RequestId
END) AS 16点后到仓包裹数
```
---
## 二.五、实现方式评估SQL实现 vs 代码实现
### 方案对比
#### 方案A直接在SQL中完整实现推荐
**优点**
- 数据库层面完成所有计算,性能最优
- 减少应用层数据传输和处理
- 逻辑清晰,便于维护和调试
- 数据一致性更好
**缺点**
- SQL复杂度高维护难度大
- 调试相对困难
#### 方案BSQL + C#代码混合实现
**优点**
- 分离关注点,部分逻辑在应用层更清晰
- 便于测试和调试
**缺点**
- 性能相对较差(多次数据传输)
- 代码复杂度反而更高
- 数据一致性难以保证
### 最终决策采用方案ASQL完整实现
**理由**
1. 虽然SQL复杂但逻辑清晰且一次性完成
2. 涉及大量的CASE WHEN计算在数据库层完成更高效
3. 新增的标签率计算本质上是CTE级别的操作适合SQL实现
---
## 三、实现步骤
### 步骤1分析当前代码结构
- [x] 已分析 `DailyLabelStatsChineseDto` 类结构
- [x] 已确认SQL所在文件位置
### 步骤2修改DTO类添加新字段
- 在 `DailyLabelStatsChineseDto` 类中添加:
- `LabelRate`(标签率 %- 用于下推到前端展示整体标签率
- `BeforeNoonArrivedCount`16:00前到仓包裹数
- `AfternoonArrivedCount`16:00后到仓包裹数
- `ShouldReplaceCount`(当天应该换单数 = 历史未完成+当日新增)
- `Rate24HourModified`修改后的24小时换单率分母为当天应该换单数
### 步骤3修改SQL实现新逻辑
SQL文件位置`d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs` (L709-1040)
#### 3.1 新增 `CustomerLabelRate` CTE
- 计算每个客户的标签率 = (有标签的订单数) / (总订单数)
#### 3.2 修改 `ArrivalRequests` CTE
- 添加客户标签率信息
- 根据标签率和到仓时间重新计算考核时间:
```
CASE
WHEN 标签率 >= 0.8 THEN
CASE
WHEN HOUR(到仓时间) < 16 THEN DATE_ADD(DATE(到仓时间), INTERVAL 1 DAY) 16:00:00
ELSE DATE_ADD(DATE(到仓时间), INTERVAL 1 DAY) 23:59:59
END
ELSE
包裹换单完成时间
END AS 考核时间
```
#### 3.3 修改 `DailyBase` CTE
- 添加16:00前和16:00后到仓包裹数的统计
#### 3.4 新增/修改24小时完成数计算CTE
- 重新计算基于新考核时间的24小时内完成数
#### 3.5 修改最终SELECT语句
- 添加新的指标列输出
- 修改24小时换单率的分母
### 步骤4修改C#代码映射新字段
- 在 `GetDailyLabelStatsChineseAsync()` 方法中添加新列的映射
### 步骤5验证和测试
- 检查SQL语法
- 验证数据准确性
- 测试边界情况
---
## 四、新增DTO字段映射与标签率计算
### SQL输出新列
1. `标签率` → 系统整体的标签率(有标签的订单数/总订单数)
2. `16点前到仓包裹数` → `BeforeNoonArrivedCount`
3. `16点后到仓包裹数` → `AfternoonArrivedCount`
4. `当天应该换单数` → `ShouldReplaceCount`
5. `修改后的24小时换单率` → `Rate24HourModified` 或保持原 `Rate24Hour` 字段
### 标签率计算方式
**概念澄清**
- **交接单(Handover)**:到货交接单,由 HandoverNumber 标识
- **订单(Request)**:换单请求记录(label_replace_requests)
- **关系**:一个交接单可以关联多个订单(通过 BillOfLadingNumber 或 MasterPackageNumber 匹配)
- **总订单数**:一个交接单关联的所有 label_replace_requests 的总数
**客户标签率计算逻辑**
- 每个交接单所属一个客户
- 客户标签率 = 该客户下有标签的订单总数 / 该客户下的总订单数
- 系统标签率 = 全系统有标签的订单总数 / 全系统总订单数
**在SQL中计算系统标签率**在最终SELECT时新增
```sql
CONCAT(ROUND(
(SELECT COUNT(DISTINCT l.Id) FROM label_replace_requests l
INNER JOIN arrival_handover_forms a
ON l.BillOfLadingNumber = a.HandoverNumber OR l.MasterPackageNumber = a.HandoverNumber
WHERE l.Label IS NOT NULL AND l.Label != '')
/
(SELECT COUNT(DISTINCT l.Id) FROM label_replace_requests l
INNER JOIN arrival_handover_forms a
ON l.BillOfLadingNumber = a.HandoverNumber OR l.MasterPackageNumber = a.HandoverNumber)
* 100, 2
), '%') AS 系统标签率
```
### C#映射代码
在 `GetDailyLabelStatsChineseAsync()` 方法的reader映射中添加新字段
```csharp
// 注意:系统标签率是常数(不随日期变化),在每行数据中值相同
LabelRate = reader["系统标签率"] as string ?? "0.00%",
BeforeNoonArrivedCount = reader["16点前到仓包裹数"] != DBNull.Value ? Convert.ToInt32(reader["16点前到仓包裹数"]) : 0,
AfternoonArrivedCount = reader["16点后到仓包裹数"] != DBNull.Value ? Convert.ToInt32(reader["16点后到仓包裹数"]) : 0,
ShouldReplaceCount = reader["当天应该换单数"] != DBNull.Value ? Convert.ToInt32(reader["当天应该换单数"]) : 0,
```
---
## 五、关键数据表结构回顾
- **arrival_handover_forms**: 到货交接单表
- `ReceiptTime`: 到仓时间UTC-5
- `HandoverNumber`: 交接单号
- **label_replace_requests**: 换单请求表
- `NeutralWaybillNumber`: 中性运单号
- `Label`: 标签
- `LabelRetrievedAt`: 标签推送时间
- `CustomerId`: 客户ID
- **label_scan_history**: 扫描历史表
- `CreatedAt`: 扫描时间UTC
- `Result`: 扫描结果0=成功)
---
## 六、实现注意事项
1. **时区处理**确保所有时间比较统一使用UTC-5时区
2. **标签率计算**需要明确如何定义"总订单数"所有订单还是特定条件的订单
3. **考核时间精确性**新逻辑涉及时间戳的精确比较需谨慎处理
4. **向后兼容性**可能需要在Excel导出功能中也添加新列
5. **性能考虑**大量的CASE WHEN和时间转换可能影响性能需监控