上传源代码版本

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,221 @@
# 数据库视图逻辑修复计划
## 问题分析
### 当前问题表现
- 视图查询结果大多为0
-`DailyShouldReplaceCount` = 3592 和 `CumulativeTotalReplaceCount` = 3315 有数据
- 其他指标新增数、完成数、到仓数等全为0
### 根本原因
当前视图SQL存在以下关键问题
1. **硬编码NOW()比较问题** 第17、24、34、42、49、56、62、69、76、83、91、100行
- 使用 `CAST(CONVERT_TZ(NOW(), '+00:00', '-05:00') AS DATE)``NOW()` 进行比较
- 每次查询都与当前时间比较,导致查询特定历史日期时大部分条件都不满足
- 应该改为接受参数化的查询日期
2. **JOIN逻辑混乱**第109-115行
-`label_scan_history`的JOIN使用子查询且索引方式错误
-`arrival_handover_forms`的JOIN条件不合理`r.BillOfLadingNumber = ahf.HandoverNumber OR r.MasterPackageNumber = ahf.HandoverNumber`
- 应该支持多种匹配方式,但需要明确的业务逻辑
3. **时区转换过度** 多个CONVERT_TZ调用
- 重复的CONVERT_TZ导致性能下降
- 应该在应用层处理时区转换,简化视图逻辑
4. **指标计算与业务逻辑不一致**
- `DailyShouldReplaceCount` 应该基于 `arrival_handover_forms` 表的接收时间
- 当前逻辑:基于 `label_replace_requests` 的创建时间判断
- 业务逻辑源自MetricsCalculationService
- 先获取当日到仓的交接单按ReceiptTime
- 计算每个交接单的标签率≥80%时才计为应换单)
- 统计满足条件的订单数
## 修复方案
### 方案1修改为参数化查询的视图推荐
**思路**:不在视图中使用 `NOW()`,改为在应用层传入查询日期,从而支持灵活的日期范围查询
**优点**
- 支持查询任意日期的指标
- 简化SQL逻辑提高性能
- 易于调试和验证
**缺点**
- 不能直接在SQL中使用 `SELECT * FROM view WHERE date = '2026-05-15'`
- 需要使用存储过程或改为表函数
### 方案2创建存储过程更优
**思路**:使用存储过程接受 `QueryDate` 参数,动态生成查询语句
**优点**
- 完全灵活,支持任意日期查询
- 保持SQL优化和性能
- 易于维护和扩展
**缺点**
- 需要修改应用层调用方式
### 方案3直接修复当前视图临时方案
**思路**
1. 在视图中使用 `CURDATE()` 代替 `NOW()`
2. 简化JOIN逻辑
3. 修正指标计算
**缺点**
- 只能查询当前日期数据
- 不符合长期需求
## 选择方案2存储过程
### 原因
1. 最符合实际业务需求
2. 应用层已有 `db.SqlQueryable<dynamic>(sql)` 的调用方式
3. 易于与C#应用集成
### 实现步骤
#### 步骤1创建存储过程 `sp_GetDailyMetricsSummary`
存储过程需要接受一个 `QueryDate` 参数YYYY-MM-DD格式返回当天的所有指标。
**核心逻辑**
```sql
DELIMITER //
CREATE PROCEDURE sp_GetDailyMetricsSummary(IN p_date DATE)
BEGIN
-- 时间范围定义UTC-5时区
-- 开始时间:查询日期 00:00 (UTC-5)
-- 结束时间:查询日期 23:59:59 (UTC-5)
SELECT
p_date AS MetricsDate,
-- 1. DailyNewReplaceCount当天新增应换单数
-- 来源当日到仓的交接单中标签率≥80%的订单数
-- 2. DailyShouldReplaceCount应该换单数累计
-- 来源所有到仓的交接单直到今天标签率≥80%的订单数
-- 3. DailySuccessCount当天完成数
-- 来源:当日扫描成功(Result=0)的订单数
-- 4. CumulativeTotalReplaceCount累计未完成数
-- 来源:历史到仓记录中,未扫描成功的订单数
-- 5. DailyStopCount当日冻结数
-- 来源:当日扫描结果中包含"STOP"的订单数
-- 6. DailyLabelPushCount当日标签推送数
-- 来源:当日新增标签的订单数(Label不为空)
-- 7. DailyScanCount当日扫描总数
-- 8. BeforeNoonArrivedCount16点前到仓数
-- 来源:当日到仓时间(ReceiptTime) < 16:00 (UTC-5)的订单数
-- 9. AfternoonArrivedCount16点后到仓数
-- 10. BeforeNoonPassedCount16点前完成数
-- 来源16点前到仓的订单中在次日16:00前扫描成功的订单数
-- 11. AfternoonPassedCount16点后完成数
-- 12. DailyFailureCount当日失败数
NOW() AS DataFetchTime
FROM (
-- 基础数据集:当日及历史到仓记录
SELECT
r.Id AS OrderId,
r.NeutralWaybillNumber,
r.Label,
ahf.HandoverNumber,
ahf.ReceiptTime,
s.Id AS ScanId,
s.Result,
s.Description,
s.CreatedAt AS ScanCreatedAt
FROM label_replace_requests r
LEFT JOIN arrival_handover_forms ahf ON
r.BillOfLadingNumber = ahf.HandoverNumber
OR r.MasterPackageNumber = ahf.HandoverNumber
LEFT JOIN (
SELECT * FROM label_scan_history
WHERE CAST(DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00'))) AS DATE) >= DATE_SUB(p_date, INTERVAL 90 DAY)
) s ON r.NeutralWaybillNumber = s.NeutralWaybillNumber
WHERE r.Label IS NOT NULL
) base_data
GROUP BY p_date;
END //
DELIMITER ;
```
**关键业务规则**基于MetricsCalculationService
1. **DailyShouldReplaceCount / BeforeNoonArrivedCount / AfternoonArrivedCount**
- 获取当日接收的交接单ReceiptTime在p_date这一天UTC-5
- 计算每个交接单的标签率(标签订单数 / 总订单数)
- 只统计标签率≥80%的订单
- 按16点划分
2. **DailySuccessCount / BeforeNoonPassedCount / AfternoonPassedCount**
- 对于16点前到仓的订单统计次日16:00前扫描成功的数量
- 对于16点后到仓的订单统计本日16:00前扫描成功的数量
- Result = 0 表示扫描成功
3. **CumulativeTotalReplaceCount**
- 统计所有历史到仓记录(直到昨天)中,未扫描成功的订单
4. **DailyFailureCount**
- DailyShouldReplaceCount - DailySuccessCount
#### 步骤2修改应用层调用
`MetricsCalculationService.GetDailySummaryAsync()` 中:
```csharp
string sql = $"CALL sp_GetDailyMetricsSummary('{date:yyyy-MM-dd}')";
var summaryList = await db.SqlQueryable<dynamic>(sql).ToListAsync();
```
#### 步骤3验证和测试
1. 在数据库中执行 `CALL sp_GetDailyMetricsSummary('2026-05-15')`
2. 验证返回的各项指标是否大于0符合实际数据
3. 对比原始的11个异步查询结果
4. 性能测试:确保<1秒完成
## 风险评估
| 风险项 | 概率 | 影响 | 缓解方案 |
|--------|------|------|---------|
| 存储过程语法错误 | | | 保留原始视图作为备份逐行测试 |
| 业务逻辑理解偏差 | | | 与用户确认每个指标的定义对比原始结果 |
| 性能不达预期 | | | 添加必要索引优化查询计划 |
| 时区转换错误 | | | 充分测试UTC-5转换逻辑验证样本数据 |
## 实现时间表
1. **编写存储过程SQL** - 验证语法正确
2. **在数据库创建存储过程** - 确保无错误
3. **用样本数据测试** - 对比原始结果
4. **修改应用层代码** - 调整GetDailySummaryAsync方法
5. **集成测试** - 前端调用验证
6. **性能验证** - 确保<1秒目标
7. **部署** - 发布到生产环境
## 备选方案
如果存储过程方案遇到困难改为直接优化视图
1. 移除所有 `NOW()` 比较
2. 在应用层计算日期范围UTC-5
3. 使用 `BETWEEN` 比较日期范围而非精确日期
4. 简化JOIN逻辑分离查询