# 客户维度监控报表实现计划 ## 一、需求分析 ### 1.1 新报表概述 基于现有日级报表 `GetDailyLabelStatsChineseAsync()` 的逻辑,创建一个**客户维度**的监控报表 ### 1.2 报表核心维度 - **第一维度**:客户(CustomerId / CustomerCode / CustomerName) - **第二维度**:日期(可选:按日期聚合) ### 1.3 新增数据字段 1. **客户标签率**:该客户的有标签订单数 / 总订单数 2. **分段统计**:各客户在16点前后的到仓和考核通过情况 3. **核心指标**:每个客户的换单成功率、完成率等 ### 1.4 数据粒度 - **按客户日期统计**(粒度最细):CustomerId + Date - **按客户汇总**(粗粒度):CustomerId(可选) --- ## 二、现有系统分析 ### 2.1 现有表结构涉及 - `label_replace_requests` - 换单请求表,包含CustomerId、Label等 - `arrival_handover_forms` - 到货交接单表 - `label_scan_history` - 扫描历史表 ### 2.2 现有SQL逻辑复用 从 `GetDailyLabelStatsChineseAsync()` 中复用: - CustomerLabelRates CTE(客户标签率计算) - ArrivalRequests CTE(考核时间逻辑) - Daily24HCompletedOrders CTE(考核通过判断) - 16点分段统计逻辑 --- ## 三、新报表DTO定义 ### 3.1 新建DTO类 **类名**:`CustomerDailyLabelStatsDto` **字段清单**: ```csharp // 基础信息 public int CustomerId { get; set; } public string CustomerCode { get; set; } public string CustomerName { get; set; } public string Date { get; set; } // yyyy-MM-dd // 客户标签率相关 public string CustomerLabelRate { get; set; } // "xx.xx%" public int TotalRequests { get; set; } // 该客户该日期的总订单数 public int LabeledRequests { get; set; } // 有标签的订单数 // 到仓分布 public int BeforeNoonArrivedCount { get; set; } // 16点前到仓 public int AfternoonArrivedCount { get; set; } // 16点后到仓 // 考核通过 public int BeforeNoonPassedCount { get; set; } // 16点前考核通过 public int AfternoonPassedCount { get; set; } // 16点后考核通过 // 核心指标 public int DailyNewReplaceCount { get; set; } // 当日新增换单数 public int DailySuccessCount { get; set; } // 当日换单成功数 public int DailyFailureCount { get; set; } // 当日换单失败 public int DailyCompletedCount { get; set; } // 当日完成数 // 完成率 public string DailyCompletionRate { get; set; } // "xx.xx%" public string Rate24Hour { get; set; } // 24小时完成率 // 其他 public DateTime DataFetchTime { get; set; } // 数据拉取时间 ``` --- ## 四、新Repository方法实现 ### 4.1 新增方法签名 ```csharp /// /// 获取客户维度的每日标签换单统计数据 /// public async Task> GetCustomerDailyLabelStatsAsync() ``` ### 4.2 SQL结构设计 #### 步骤1:获取客户基础信息和标签率 ``` CustomerInfo + CustomerLabelRates ├─ 客户ID、代码、名称 ├─ 客户标签率 = 有标签订单数 / 总订单数 └─ 总订单数、有标签订单数 ``` #### 步骤2:按客户日期分组的到仓统计 ``` CustomerDailyArrival ├─ 按 CustomerId + DATE(ReceiptTime) 分组 ├─ 16点前到仓数 └─ 16点后到仓数 ``` #### 步骤3:按客户日期分组的考核通过统计 ``` CustomerDailyBeforeNoonPassed CustomerDailyAfternoonPassed ├─ 16点前考核通过数 └─ 16点后考核通过数 ``` #### 步骤4:按客户日期分组的换单完成统计 ``` CustomerDailyCompletion ├─ 当日新增换单数 ├─ 当日成功数 ├─ 当日失败数 └─ 当日完成数 ``` #### 步骤5:最终聚合与输出 ``` 最终SELECT ├─ 关联CustomerInfo(客户信息) ├─ 关联CustomerDailyArrival(到仓分布) ├─ 关联CustomerDailyXxxPassed(考核通过) ├─ 关联CustomerDailyCompletion(换单完成) ├─ 计算完成率、24H率 └─ 输出所有字段 ``` --- ## 五、SQL查询框架 ### 5.1 基础框架结构 ```sql WITH -- 步骤1:客户信息和标签率 CustomerInfo AS ( SELECT CustomerId, CustomerCode, CustomerName, 标签率计算... ), -- 步骤2:客户日期的到仓分布 CustomerDailyArrival AS ( SELECT CustomerId, DATE(ReceiptTime) AS 日期, COUNT(CASE WHEN HOUR(ReceiptTime) < 16...) COUNT(CASE WHEN HOUR(ReceiptTime) >= 16...) ), -- 步骤3:客户日期的16点前考核通过 CustomerDailyBeforeNoonPassed AS ( SELECT CustomerId, 日期, COUNT(...) ), -- 步骤4:客户日期的16点后考核通过 CustomerDailyAfternoonPassed AS ( SELECT CustomerId, 日期, COUNT(...) ), -- 步骤5:客户日期的换单完成统计 CustomerDailyCompletion AS ( SELECT CustomerId, 日期, COUNT(新增), COUNT(成功), COUNT(失败), COUNT(完成) ), -- 步骤6:最终聚合 最终SELECT ``` --- ## 六、关键SQL逻辑 ### 6.1 客户标签率计算 ```sql CustomerLabelRate = (SELECT COUNT(DISTINCT id) FROM label_replace_requests l WHERE l.CustomerId = ci.CustomerId AND l.Label IS NOT NULL AND l.Label != '') / (SELECT COUNT(DISTINCT id) FROM label_replace_requests l WHERE l.CustomerId = ci.CustomerId) * 100 ``` ### 6.2 按客户分组的到仓统计 ```sql -- 需要关联 ArrivalRequests 以获取 CustomerId -- 按 CustomerId + DATE(到货时间) 分组 -- 统计16点前后到仓数量(去重) ``` ### 6.3 按客户分组的考核通过统计 ```sql -- 复用Daily24HCompletedOrders的逻辑 -- 额外按 CustomerId 分组 -- 分别统计16点前和16点后的通过数 ``` --- ## 七、实现步骤 ### 步骤1:创建新DTO类 **文件**:`d:\EPproject\LabelReplaceServer\src\MDL\DTOs\CustomerDailyLabelStatsDto.cs` - 定义所有字段 - 添加SugarColumn注解 ### 步骤2:在Repository中新增方法 **文件**:`d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs` - 新增 `GetCustomerDailyLabelStatsAsync()` 方法 - 实现完整SQL查询 - 添加reader映射逻辑 ### 步骤3:创建对应的Service方法(可选) **文件**:`BLL/Services/` 中的相关Service - 封装数据处理逻辑 - 提供业务层接口 ### 步骤4:创建Controller端点(可选) **文件**:`CONTROLLER/Controllers/` 中的相关Controller - 新增API端点 - 处理请求参数(如客户ID、日期范围) ### 步骤5:验证与测试 - SQL语法检查 - 编译无错误 - 数据准确性验证 --- ## 八、关键技术点 ### 8.1 去重问题 - 客户标签率需要用 `COUNT(DISTINCT l.Id)` 避免重复计算 - 到仓分布需要用 `COUNT(DISTINCT ar.RequestId)` 避免重复 - 完成数需要用 `COUNT(DISTINCT NeutralWaybillNumber)` 去重 ### 8.2 时区处理 - 所有时间比较保持一致的UTC-5时区 - 到仓时间:`a.ReceiptTime`(已是UTC-5) - 完成时间:`CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00')` ### 8.3 考核时间逻辑复用 - 从现有SQL中复用CustomerLabelRates CTE - 从现有SQL中复用考核时间计算逻辑 - 从现有SQL中复用Daily24HCompletedOrders判断逻辑 ### 8.4 性能优化 - 添加索引:`label_replace_requests(CustomerId)` - 避免多次相同的子查询 - 使用CTE提高查询可读性 --- ## 九、输出样例 | CustomerId | CustomerCode | CustomerName | Date | CustomerLabelRate | BeforeNoonArrived | ... | |------------|--------------|--------------|------|------------------|-------------------|-----| | 1 | CUST001 | 客户A | 2026-05-16 | 95.50% | 10 | ... | | 1 | CUST001 | 客户A | 2026-05-17 | 95.50% | 8 | ... | | 2 | CUST002 | 客户B | 2026-05-16 | 78.30% | 15 | ... | --- ## 十、风险与考虑 ### 10.1 潜在风险 1. **数据一致性**:客户信息是否会变更? 2. **性能**:大量客户下的查询性能? 3. **时间范围**:查询是否需要日期范围参数? ### 10.2 扩展考虑 - 支持日期范围查询参数 - 支持按客户ID筛选 - 支持按标签率范围筛选 - 支持导出到Excel --- ## 十一、预期交付物 1. ✅ `CustomerDailyLabelStatsDto.cs` - DTO类 2. ✅ Repository新方法 - `GetCustomerDailyLabelStatsAsync()` 3. ✅ 完整SQL查询脚本 4. ✅ 编译无错误 5. ✅ 实现总结文档