8.1 KiB
8.1 KiB
客户维度监控报表实现计划
一、需求分析
1.1 新报表概述
基于现有日级报表 GetDailyLabelStatsChineseAsync() 的逻辑,创建一个客户维度的监控报表
1.2 报表核心维度
- 第一维度:客户(CustomerId / CustomerCode / CustomerName)
- 第二维度:日期(可选:按日期聚合)
1.3 新增数据字段
- 客户标签率:该客户的有标签订单数 / 总订单数
- 分段统计:各客户在16点前后的到仓和考核通过情况
- 核心指标:每个客户的换单成功率、完成率等
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
字段清单:
// 基础信息
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 新增方法签名
/// <summary>
/// 获取客户维度的每日标签换单统计数据
/// </summary>
public async Task<List<CustomerDailyLabelStatsDto>> 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 基础框架结构
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 客户标签率计算
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 按客户分组的到仓统计
-- 需要关联 ArrivalRequests 以获取 CustomerId
-- 按 CustomerId + DATE(到货时间) 分组
-- 统计16点前后到仓数量(去重)
6.3 按客户分组的考核通过统计
-- 复用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 潜在风险
- 数据一致性:客户信息是否会变更?
- 性能:大量客户下的查询性能?
- 时间范围:查询是否需要日期范围参数?
10.2 扩展考虑
- 支持日期范围查询参数
- 支持按客户ID筛选
- 支持按标签率范围筛选
- 支持导出到Excel
十一、预期交付物
- ✅
CustomerDailyLabelStatsDto.cs- DTO类 - ✅ Repository新方法 -
GetCustomerDailyLabelStatsAsync() - ✅ 完整SQL查询脚本
- ✅ 编译无错误
- ✅ 实现总结文档