# 全员战报明细到店数统计问题修复 ## 📋 问题描述 在全员战报明细统计中,发现有的数据的到店数比拓客数还多,不符合业务逻辑。 **业务规则**: - 一个人多次到店的话在这里就算一次 - 到店数应该小于等于拓客数 ## 🔍 问题分析 ### 问题位置 **文件**: `netcore/src/Modularity/Extend/NCC.Extend/LqTkDashboardService.cs` **方法**: `GetEmployeeParticipationStatistics` **行数**: 第575-588行(修复前) ### 问题原因 **修复前的SQL逻辑**: ```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逻辑(最终版本) ```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`: 开单金额