上传源代码版本
This commit is contained in:
221
.trae/documents/view_logic_fix_plan.md
Normal file
221
.trae/documents/view_logic_fix_plan.md
Normal 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. BeforeNoonArrivedCount:16点前到仓数
|
||||
-- 来源:当日到仓时间(ReceiptTime) < 16:00 (UTC-5)的订单数
|
||||
|
||||
-- 9. AfternoonArrivedCount:16点后到仓数
|
||||
|
||||
-- 10. BeforeNoonPassedCount:16点前完成数
|
||||
-- 来源:16点前到仓的订单中,在次日16:00前扫描成功的订单数
|
||||
|
||||
-- 11. AfternoonPassedCount:16点后完成数
|
||||
|
||||
-- 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逻辑,分离查询
|
||||
|
||||
Reference in New Issue
Block a user