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

7.8 KiB
Raw Blame History

24H换单率超过100% - 最终根本原因与修复计划

日期2026-05-16
核心问题24H换单率远超100%4745%等),根本原因是分母计算错误


业务逻辑最终澄清(用户确认)

包裹的属性关系

考核时间
   ↑
   决定者:作业时的标签率(交接单维度)
   ↓
标签率(两种)
   1. 作业时标签率(固定)
      = 最早扫描时间之前的有标签包裹 / 该交接单所有包裹
      用途决定考核时间、决定是否纳入24H换单率
   
   2. 统计的标签率(最终)
      = 当前有标签的包裹 / 该交接单所有包裹
      用途:业务报表展示

标签率与考核时间的关系

对于交接单中的每个包裹:
- 作业时标签率≥80% → 有考核时间 → 需要在规定时间内完成
- 作业时标签率<80% → 无考核时间 → 完成即达标

关键:考核时间是交接单维度确定的,然后应用到该交接单的所有包裹

分母的精确定义(用户最终确认)

高标签率应该换单数 = 当日到货 且 作业时标签率≥80% 的交接单中的有标签包裹数

具体计算方式:
1. 找出当日到货的所有交接单
2. 对每个交接单:
   a. 计算作业时标签率 = 最早扫描之前有标签的数 / 总数
   b. 如果作业时标签率≥80%
      → 计算这个交接单有多少个有标签的包裹
      → 这些有标签包裹加入应该换单数
   c. 如果作业时标签率<80%
      → 跳过这个交接单(不加入应该换单数)
3. 求和得到总的高标签率应该换单数

当前代码的根本问题

问题1InterchangeUnitLabelRatesAtFirstScan的计算

当前代码第761-791行

SELECT 
    l.BillOfLadingNumber,
    l.MasterPackageNumber,
    COUNT(DISTINCT l.Id) AS total_requests,
    COUNT(DISTINCT CASE 
        WHEN l.Label IS NOT NULL AND l.Label != '' 
        AND l.LabelRetrievedAt < (...)
        THEN l.Id 
    END) AS labeled_at_first_scan,
    ...
FROM label_replace_requests l
GROUP BY l.BillOfLadingNumber, l.MasterPackageNumber

问题

  • COUNT(DISTINCT l.Id) 统计的是"所有包裹"的数量
  • 但根据用户规则,应该统计的是"有标签的包裹"的数量

正确做法

  • 分子应该是:最早扫描时间之前有标签的包裹数
  • 分母应该是:该交接单所有有标签的包裹数(不是所有包裹)

问题2DailyHighLabelRateShould的计算

当前代码第1100-1117行

DailyHighLabelRateShould AS (
    SELECT 
        DATE(CONVERT_TZ(ar.到货时间, '+00:00', '-05:00')) AS 日期,
        COUNT(DISTINCT CONCAT(ar.BillOfLadingNumber, '|', ar.MasterPackageNumber)) 
            AS 高标签率应该换单数
    FROM ArrivalRequests ar
    INNER JOIN InterchangeUnitLabelRatesAtFirstScan...
    WHERE label_rate_at_first_scan >= 80

问题

  • 这里统计的是交接单级别的计数
  • 但应该统计的是"有标签的包裹"数量
  • 如果一个交接单有10个包裹其中8个有标签标签率80%
  • 应该换单数应该是8不是1

导致的后果

  • 分母被严重低估每个交接单只算1而不是该交接单的有标签包裹数
  • 分子/分母比率就会巨大

修复方案

第1步修正InterchangeUnitLabelRatesAtFirstScan

改进思路

  • 保留交接单维度的标签率计算(用于决定考核时间)
  • 但同时记录"有标签包裹数"用于分母计算
InterchangeUnitLabelRatesAtFirstScan AS (
    SELECT 
        l.BillOfLadingNumber,
        l.MasterPackageNumber,
        -- 统计有标签的包裹数(用于后续分母计算)
        COUNT(DISTINCT CASE 
            WHEN l.Label IS NOT NULL AND l.Label != '' 
            THEN l.Id 
        END) AS total_labeled_at_any_time,
        -- 作业时有标签的包裹数
        COUNT(DISTINCT CASE 
            WHEN l.Label IS NOT NULL AND l.Label != '' 
            AND l.LabelRetrievedAt < (
                SELECT MIN(lsh.CreatedAt)
                FROM label_scan_history lsh
                WHERE lsh.NeutralWaybillNumber = l.NeutralWaybillNumber
            )
            THEN l.Id 
        END) AS labeled_at_first_scan,
        -- 作业时标签率 = 作业时有标签数 / 所有有标签数
        ROUND(
            COUNT(DISTINCT CASE 
                WHEN l.Label IS NOT NULL AND l.Label != '' 
                AND l.LabelRetrievedAt < (
                    SELECT MIN(lsh.CreatedAt)
                    FROM label_scan_history lsh
                    WHERE lsh.NeutralWaybillNumber = l.NeutralWaybillNumber
                )
                THEN l.Id 
            END) * 100.0 / 
            COUNT(DISTINCT CASE 
                WHEN l.Label IS NOT NULL AND l.Label != '' 
                THEN l.Id 
            END),
            2
        ) AS label_rate_at_first_scan
    FROM label_replace_requests l
    GROUP BY l.BillOfLadingNumber, l.MasterPackageNumber
)

第2步修正DailyHighLabelRateShould

改进思路

  • 统计的是"有标签的包裹数",而不是交接单数
DailyHighLabelRateShould AS (
    SELECT 
        DATE(CONVERT_TZ(ar.到货时间, '+00:00', '-05:00')) AS 日期,
        -- 改为统计:有标签的包裹数
        SUM(CASE 
            WHEN iulr_first.label_rate_at_first_scan >= 80 
            THEN iulr_first.total_labeled_at_any_time
            ELSE 0
        END) AS 高标签率应该换单数
    FROM ArrivalRequests ar
    INNER JOIN InterchangeUnitLabelRatesAtFirstScan iulr_first
        ON ar.BillOfLadingNumber = iulr_first.BillOfLadingNumber
        AND ar.MasterPackageNumber = iulr_first.MasterPackageNumber
    WHERE ar.到货日期 IS NOT NULL
    GROUP BY DATE(CONVERT_TZ(ar.到货时间, '+00:00', '-05:00'))
)

第3步确保分子与分母维度一致

分子的逻辑DailyBeforeNoonPassed + DailyAfternoonPassed

  • 已经是按"订单"NeutralWaybillNumber计数
  • 这是正确的

但需要确保

  • 只统计有标签的订单
  • 只统计作业时标签率≥80%的订单
  • 只统计在考核时间内完成的订单

实施步骤

步骤1修改InterchangeUnitLabelRatesAtFirstScan CTE

  • 位置第761-791行
  • 任务添加total_labeled_at_any_time字段调整label_rate_at_first_scan计算逻辑
  • 验证:确保新增字段计算正确

步骤2修改DailyHighLabelRateShould CTE

  • 位置第1100-1117行
  • 任务改为SUM统计有标签包裹数而不是COUNT交接单
  • 验证:结果应该更大(因为统计包裹而不是交接单)

步骤3编译验证

  • 运行:dotnet build src/DAL/DAL.csproj
  • 预期:编译成功

步骤4测试和验证

测试内容不要求≤100%,因为这是可能的正常现象):

测试1验证分母计算逻辑

SELECT 
    日期,
    高标签率应该换单数,
    (SELECT COUNT(*) FROM ...) as 应该的包裹数
FROM DailyHighLabelRateShould
WHERE 日期 = '2026-05-15'

测试2验证分子分母是否合理对应

SELECT 
    日期,
    (SELECT SUM(...) FROM DailyBeforeNoonPassed WHERE 日期='2026-05-15') as 分子_16点前,
    (SELECT SUM(...) FROM DailyAfternoonPassed WHERE 日期='2026-05-15') as 分子_16点后,
    (SELECT 高标签率应该换单数 FROM DailyHighLabelRateShould WHERE 日期='2026-05-15') as 分母

测试3验证数据是否能手工解释

  • 选择某一天的数据
  • 查看具体的订单数和包裹数
  • 确认分子分母的计算逻辑是否符合业务规则
  • 不要求分子≤分母或24H换单率≤100%,因为这是可能的正常现象

预期结果

修复后:

  • 分母正确反映"有标签包裹数"而不是"交接单数"
  • 数据逻辑一致、可解释
  • 24H换单率数据合理虽然可能>100%但不会是4745%这样极端的值)
  • 分子分母的对应关系清晰