全员战报明细到店数统计问题修复.md 5.02 KB

全员战报明细到店数统计问题修复

📋 问题描述

在全员战报明细统计中,发现有的数据的到店数比拓客数还多,不符合业务逻辑。

业务规则

  • 一个人多次到店的话在这里就算一次
  • 到店数应该小于等于拓客数

🔍 问题分析

问题位置

文件: netcore/src/Modularity/Extend/NCC.Extend/LqTkDashboardService.cs
方法: GetEmployeeParticipationStatistics
行数: 第575-588行(修复前)

问题原因

修复前的SQL逻辑

LEFT JOIN (
    -- 到店人数统计
    SELECT 
        tk.F_ExpansionUserId as EmployeeId,
        COUNT(DISTINCT tk.F_MemberId) as VisitCount
    FROM lq_tkjlb tk
    INNER JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy AND xh.F_IsEffective = 1
    WHERE {eventFilter}
        AND tk.F_ExpansionTime >= '{startTimeStr}'
        AND tk.F_ExpansionTime <= '{endTimeStr}'
        AND xh.hksj >= '{startTimeStr}'
        AND xh.hksj <= '{endTimeStr}'
    GROUP BY tk.F_ExpansionUserId
) visit ON emp.EmployeeId = visit.EmployeeId

问题点

  1. 缺少时间关联约束:没有限制到店时间必须在拓客时间之后,可能统计到拓客之前的到店记录
  2. JOIN导致重复计数:当同一个会员被同一个员工多次拓客,且该会员多次到店时,INNER JOIN 会产生多行记录
  3. 虽然使用了 DISTINCT,但 JOIN 的逻辑可能导致计数不准确

问题场景示例

假设:

  • 员工A在1月1日拓客了会员B
  • 员工A在1月5日又拓客了会员B(重复拓客)
  • 会员B在1月3日到店1次
  • 会员B在1月6日到店1次

修复前的统计结果

  • 拓客数:1(COUNT(DISTINCT tk.F_MemberId))
  • 到店数:2(因为 JOIN 产生了2条记录:1月1日拓客+1月3日到店,1月5日拓客+1月6日到店)

问题:到店数(2)> 拓客数(1),不符合逻辑

✅ 修复方案

修复后的SQL逻辑(最终版本)

LEFT JOIN (
    -- 到店人数统计(一个人多次到店只算一次)
    -- 先找出每个员工拓客的所有会员(去重),然后统计这些会员中有到店记录的会员数
    SELECT 
        emp_members.EmployeeId,
        COUNT(DISTINCT CASE WHEN xh.hy IS NOT NULL THEN emp_members.MemberId END) as VisitCount
    FROM (
        -- 先获取每个员工拓客的所有会员(去重),并记录首次拓客时间
        SELECT DISTINCT
            tk.F_ExpansionUserId as EmployeeId,
            tk.F_MemberId as MemberId,
            MIN(tk.F_ExpansionTime) as FirstExpansionTime
        FROM lq_tkjlb tk
        WHERE {eventFilter}
            AND tk.F_ExpansionTime >= '{startTimeStr}'
            AND tk.F_ExpansionTime <= '{endTimeStr}'
            AND tk.F_MemberId IS NOT NULL
        GROUP BY tk.F_ExpansionUserId, tk.F_MemberId
    ) emp_members
    LEFT JOIN lq_xh_hyhk xh ON emp_members.MemberId = xh.hy 
        AND xh.F_IsEffective = 1
        AND xh.hksj >= emp_members.FirstExpansionTime
        AND xh.hksj >= '{startTimeStr}'
        AND xh.hksj <= '{endTimeStr}'
    GROUP BY emp_members.EmployeeId
) visit ON emp.EmployeeId = visit.EmployeeId

修复要点

  1. 先获取拓客会员列表(去重)

    • 使用子查询先找出每个员工拓客的所有会员(去重)
    • 记录每个会员的首次拓客时间(MIN(tk.F_ExpansionTime)
    • 确保同一个会员只出现一次,即使被多次拓客
  2. 统计有到店记录的会员数

    • 在去重后的会员列表中,LEFT JOIN 耗卡表
    • 使用 COUNT(DISTINCT CASE WHEN xh.hy IS NOT NULL THEN emp_members.MemberId END) 统计有到店记录的会员数
    • 确保到店时间在首次拓客时间之后(xh.hksj >= emp_members.FirstExpansionTime
  3. 双重去重保障

    • 第一层:在子查询中使用 GROUP BY tk.F_ExpansionUserId, tk.F_MemberId 确保每个员工-会员组合只出现一次
    • 第二层:使用 COUNT(DISTINCT ...) 确保最终统计时每个会员只算一次

修复后的统计结果(使用上面的示例)

  • 拓客数:1(COUNT(DISTINCT tk.F_MemberId))
  • 到店数:1(会员B有到店记录,只算一次)

结果:到店数(1)≤ 拓客数(1),符合逻辑 ✅

📝 修改文件

  • 文件: netcore/src/Modularity/Extend/NCC.Extend/LqTkDashboardService.cs
  • 方法: GetEmployeeParticipationStatistics
  • 修改位置: 第575-590行

✅ 验证建议

  1. 验证到店数 ≤ 拓客数:检查所有员工的统计数据,确保到店数不超过拓客数
  2. 验证去重逻辑:选择一个多次到店的会员,验证其只被统计一次
  3. 验证时间约束:选择一个拓客前有到店记录的会员,验证不会被统计到

🔗 相关接口

接口地址: POST /api/Extend/LqTkDashboard/GetEmployeeParticipationStatistics

接口说明: 获取员工参与统计(全员战报明细)

返回字段:

  • ExpansionCount: 拓客数
  • VisitCount: 到店数(修复后)
  • BillingCount: 开单数
  • BillingAmount: 开单金额