上传源代码版本
This commit is contained in:
296
.trae/documents/customer_dimension_report_plan.md
Normal file
296
.trae/documents/customer_dimension_report_plan.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# 客户维度监控报表实现计划
|
||||
|
||||
## 一、需求分析
|
||||
|
||||
### 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. ✅ 实现总结文档
|
||||
|
||||
Reference in New Issue
Block a user