Files
LabelChange-server/.trae/documents/customer_dimension_report_plan.md
2026-06-01 16:30:29 +08:00

297 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 客户维度监控报表实现计划
## 一、需求分析
### 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
/// <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 基础框架结构
```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. ✅ 实现总结文档