Commit bdb9fb61ea76a549bbab1f387910db7ae40efe0b

Authored by 李宇
2 parents 72ae1b1c 25ab25d3

Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP

Showing 27 changed files with 2000 additions and 107 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 事业部业绩完成情况输入参数
  8 + /// </summary>
  9 + public class BusinessUnitPerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 事业部ID列表(可选,为空则查询所有事业部)
  23 + /// </summary>
  24 + public List<string> BusinessUnitIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 事业部业绩完成情况输出结果
  8 + /// </summary>
  9 + public class BusinessUnitPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 事业部ID
  13 + /// </summary>
  14 + public string BusinessUnitId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 事业部名称
  18 + /// </summary>
  19 + public string BusinessUnitName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 目标业绩(管理门店的生命线总和)
  23 + /// </summary>
  24 + public decimal TargetPerformance { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 完成业绩(管理门店的开单业绩总和)
  28 + /// </summary>
  29 + public decimal CompletedPerformance { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 完成率(百分比)
  33 + /// </summary>
  34 + public decimal CompletionRate { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 门店数量
  38 + /// </summary>
  39 + public int StoreCount { get; set; }
  40 + }
  41 +}
  42 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/ManagerPerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 经理业绩完成情况输入参数
  8 + /// </summary>
  9 + public class ManagerPerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 经理用户ID(可选)
  23 + /// </summary>
  24 + public string ManagerId { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/ManagerPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 经理业绩完成情况输出结果
  8 + /// </summary>
  9 + public class ManagerPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 经理用户ID
  13 + /// </summary>
  14 + public string ManagerId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 经理姓名
  18 + /// </summary>
  19 + public string ManagerName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 门店ID
  23 + /// </summary>
  24 + public string StoreId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 门店名称
  28 + /// </summary>
  29 + public string StoreName { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 目标业绩一阶段(smx1)
  33 + /// </summary>
  34 + public decimal Target1 { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 目标业绩二阶段(smx2)
  38 + /// </summary>
  39 + public decimal? Target2 { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 目标业绩三阶段(smx3)
  43 + /// </summary>
  44 + public decimal? Target3 { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 完成业绩
  48 + /// </summary>
  49 + public decimal CompletedPerformance { get; set; }
  50 +
  51 + /// <summary>
  52 + /// 完成率一阶段(%)
  53 + /// </summary>
  54 + public decimal CompletionRate1 { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 完成率二阶段(%)
  58 + /// </summary>
  59 + public decimal? CompletionRate2 { get; set; }
  60 +
  61 + /// <summary>
  62 + /// 完成率三阶段(%)
  63 + /// </summary>
  64 + public decimal? CompletionRate3 { get; set; }
  65 + }
  66 +}
  67 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/ManagerSummaryPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 经理汇总业绩完成情况输出结果
  8 + /// </summary>
  9 + public class ManagerSummaryPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 经理用户ID
  13 + /// </summary>
  14 + public string ManagerId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 经理姓名
  18 + /// </summary>
  19 + public string ManagerName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 总目标业绩一阶段
  23 + /// </summary>
  24 + public decimal TotalTarget1 { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 总目标业绩二阶段
  28 + /// </summary>
  29 + public decimal TotalTarget2 { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 总目标业绩三阶段
  33 + /// </summary>
  34 + public decimal TotalTarget3 { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 总完成业绩
  38 + /// </summary>
  39 + public decimal TotalCompletedPerformance { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 总完成率一阶段(%)
  43 + /// </summary>
  44 + public decimal TotalCompletionRate1 { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 总完成率二阶段(%)
  48 + /// </summary>
  49 + public decimal TotalCompletionRate2 { get; set; }
  50 +
  51 + /// <summary>
  52 + /// 总完成率三阶段(%)
  53 + /// </summary>
  54 + public decimal TotalCompletionRate3 { get; set; }
  55 +
  56 + /// <summary>
  57 + /// 管理门店数量
  58 + /// </summary>
  59 + public int StoreCount { get; set; }
  60 + }
  61 +}
  62 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StoreDailyStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店每日统计输入参数
  8 + /// </summary>
  9 + public class StoreDailyStatisticsInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 门店ID列表(可选,为空则查询所有门店)
  23 + /// </summary>
  24 + public List<string> StoreIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StoreDailyStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店每日统计输出结果
  8 + /// </summary>
  9 + public class StoreDailyStatisticsOutput
  10 + {
  11 + /// <summary>
  12 + /// 门店ID
  13 + /// </summary>
  14 + public string StoreId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 门店名称
  18 + /// </summary>
  19 + public string StoreName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 在职员工数
  23 + /// </summary>
  24 + public int EmployeeCount { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 人头数(去重后的消费会员数)
  28 + /// </summary>
  29 + public int HeadCount { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 人次(日度去重客户数)
  33 + /// </summary>
  34 + public int PersonCount { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 项目数(消耗的项目总数)
  38 + /// </summary>
  39 + public int ProjectCount { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 消耗业绩(总金额)
  43 + /// </summary>
  44 + public decimal ConsumePerformance { get; set; }
  45 + }
  46 +}
  47 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StorePerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店业绩完成情况输入参数
  8 + /// </summary>
  9 + public class StorePerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 门店ID列表(可选,为空则查询所有门店)
  23 + /// </summary>
  24 + public List<string> StoreIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/StorePerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 门店业绩完成情况输出结果
  8 + /// </summary>
  9 + public class StorePerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 门店ID
  13 + /// </summary>
  14 + public string StoreId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 门店名称
  18 + /// </summary>
  19 + public string StoreName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 目标业绩(生命线)
  23 + /// </summary>
  24 + public decimal TargetPerformance { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 完成业绩(开单业绩)
  28 + /// </summary>
  29 + public decimal CompletedPerformance { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 完成率(百分比)
  33 + /// </summary>
  34 + public decimal CompletionRate { get; set; }
  35 + }
  36 +}
  37 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TechTeacherDailyStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 科技部老师统计输入参数
  8 + /// </summary>
  9 + public class TechTeacherDailyStatisticsInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 科技部ID(可选,不传则查询所有科技部)
  23 + /// </summary>
  24 + public string TechDepartmentId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 老师ID列表(可选)
  28 + /// </summary>
  29 + public List<string> TeacherIds { get; set; }
  30 + }
  31 +}
  32 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TechTeacherDailyStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 科技部老师统计输出结果
  8 + /// </summary>
  9 + public class TechTeacherDailyStatisticsOutput
  10 + {
  11 + /// <summary>
  12 + /// 科技部ID
  13 + /// </summary>
  14 + public string TechDepartmentId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 科技部名称
  18 + /// </summary>
  19 + public string TechDepartmentName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 老师ID
  23 + /// </summary>
  24 + public string TeacherId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 老师姓名
  28 + /// </summary>
  29 + public string TeacherName { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 见客数(不同客户数量)
  33 + /// </summary>
  34 + public int CustomerCount { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 消耗项目数
  38 + /// </summary>
  39 + public int ConsumeProjectCount { get; set; }
  40 +
  41 + /// <summary>
  42 + /// 消耗业绩
  43 + /// </summary>
  44 + public decimal ConsumeAchievement { get; set; }
  45 +
  46 + /// <summary>
  47 + /// 开单业绩
  48 + /// </summary>
  49 + public decimal OrderAchievement { get; set; }
  50 + }
  51 +}
  52 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionInput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 天王团业绩完成情况输入参数
  8 + /// </summary>
  9 + public class TianwangGroupPerformanceCompletionInput
  10 + {
  11 + /// <summary>
  12 + /// 开始时间(可选,不传则默认为当月1号)
  13 + /// </summary>
  14 + public DateTime? StartTime { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 结束时间(可选,不传则默认为今天)
  18 + /// </summary>
  19 + public DateTime? EndTime { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 天王团部门ID列表(可选,为空则查询所有天王团)
  23 + /// </summary>
  24 + public List<string> DepartmentIds { get; set; }
  25 + }
  26 +}
  27 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/TianwangGroupPerformanceCompletionOutput.cs 0 → 100644
  1 +using System;
  2 +using System.Collections.Generic;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqDailyReport
  5 +{
  6 + /// <summary>
  7 + /// 天王团业绩完成情况输出结果
  8 + /// </summary>
  9 + public class TianwangGroupPerformanceCompletionOutput
  10 + {
  11 + /// <summary>
  12 + /// 部门ID
  13 + /// </summary>
  14 + public string DepartmentId { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 部门名称
  18 + /// </summary>
  19 + public string DepartmentName { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 目标业绩(管理门店的生命线总和)
  23 + /// </summary>
  24 + public decimal TargetPerformance { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 完成业绩(管理门店的开单业绩总和)
  28 + /// </summary>
  29 + public decimal CompletedPerformance { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 完成率(百分比)
  33 + /// </summary>
  34 + public decimal CompletionRate { get; set; }
  35 +
  36 + /// <summary>
  37 + /// 门店数量
  38 + /// </summary>
  39 + public int StoreCount { get; set; }
  40 + }
  41 +}
  42 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsInput.cs
... ... @@ -28,5 +28,15 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo
28 28 /// 门店ID列表(可选)
29 29 /// </summary>
30 30 public string[] StoreIds { get; set; }
  31 +
  32 + /// <summary>
  33 + /// 页码(默认1)
  34 + /// </summary>
  35 + public int PageIndex { get; set; } = 1;
  36 +
  37 + /// <summary>
  38 + /// 每页数量(默认20)
  39 + /// </summary>
  40 + public int PageSize { get; set; } = 20;
31 41 }
32 42 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsOutput.cs
1 1 using System;
  2 +using System.Collections.Generic;
2 3  
3 4 namespace NCC.Extend.Entitys.Dto.LqPackageInfo
4 5 {
... ... @@ -28,28 +29,59 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo
28 29 public decimal BillingAmount { get; set; }
29 30  
30 31 /// <summary>
31   - /// 退卡数量
  32 + /// 欠款金额(所有开单的欠款总和)
32 33 /// </summary>
33   - public int RefundCount { get; set; }
  34 + public decimal DebtAmount { get; set; }
34 35  
35 36 /// <summary>
36   - /// 退卡金额
  37 + /// 开单列表
37 38 /// </summary>
38   - public decimal RefundAmount { get; set; }
  39 + public List<ActivityBillingItem> BillingList { get; set; }
  40 + }
  41 +
  42 + /// <summary>
  43 + /// 活动开单项
  44 + /// </summary>
  45 + public class ActivityBillingItem
  46 + {
  47 + /// <summary>
  48 + /// 开单ID
  49 + /// </summary>
  50 + public string BillingId { get; set; }
  51 +
  52 + /// <summary>
  53 + /// 开单日期
  54 + /// </summary>
  55 + public DateTime? BillingDate { get; set; }
39 56  
40 57 /// <summary>
41   - /// 净开单数量(开单数量 - 退卡数量)
  58 + /// 客户名称
42 59 /// </summary>
43   - public int NetBillingCount { get; set; }
  60 + public string CustomerName { get; set; }
44 61  
45 62 /// <summary>
46   - /// 净开单金额(开单金额 - 退卡金额)
  63 + /// 客户电话
  64 + /// </summary>
  65 + public string CustomerPhone { get; set; }
  66 +
  67 + /// <summary>
  68 + /// 门店ID
  69 + /// </summary>
  70 + public string StoreId { get; set; }
  71 +
  72 + /// <summary>
  73 + /// 门店名称
  74 + /// </summary>
  75 + public string StoreName { get; set; }
  76 +
  77 + /// <summary>
  78 + /// 开单金额
47 79 /// </summary>
48   - public decimal NetBillingAmount { get; set; }
  80 + public decimal Amount { get; set; }
49 81  
50 82 /// <summary>
51   - /// 退卡率(退卡数量 / 开单数量)
  83 + /// 欠款金额
52 84 /// </summary>
53   - public decimal RefundRate { get; set; }
  85 + public decimal DebtAmount { get; set; }
54 86 }
55 87 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/BusinessStatisticsOutput.cs
... ... @@ -34,5 +34,30 @@ namespace NCC.Extend.Entitys.Dto.LqReport
34 34 /// 退卡人数
35 35 /// </summary>
36 36 public int RefundCount { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 消耗目标业绩(所有门店xhyj字段的总和)
  40 + /// </summary>
  41 + public decimal TargetConsumeAmount { get; set; }
  42 +
  43 + /// <summary>
  44 + /// 消耗完成率(耗卡总金额 / 消耗目标业绩 * 100)
  45 + /// </summary>
  46 + public decimal ConsumeCompletionRate { get; set; }
  47 +
  48 + /// <summary>
  49 + /// 开单目标业绩(所有门店xsyj字段的总和)
  50 + /// </summary>
  51 + public decimal TargetBillingAmount { get; set; }
  52 +
  53 + /// <summary>
  54 + /// 开单完成业绩(开单总金额 - 退卡总金额)
  55 + /// </summary>
  56 + public decimal CompletedBillingAmount { get; set; }
  57 +
  58 + /// <summary>
  59 + /// 开单完成率(完成业绩 / 开单目标业绩 * 100)
  60 + /// </summary>
  61 + public decimal BillingCompletionRate { get; set; }
37 62 }
38 63 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/EmployeePerformanceStatisticsOutput.cs
... ... @@ -69,5 +69,15 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
69 69 /// 人次(日度去重客户数)
70 70 /// </summary>
71 71 public int PersonCount { get; set; }
  72 +
  73 + /// <summary>
  74 + /// 开单项目数(项目次数总和)
  75 + /// </summary>
  76 + public int BillingProjectCount { get; set; }
  77 +
  78 + /// <summary>
  79 + /// 消耗项目数(项目次数总和)
  80 + /// </summary>
  81 + public int ConsumeProjectCount { get; set; }
72 82 }
73 83 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTkjlb/TkStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqTkjlb
  4 +{
  5 + /// <summary>
  6 + /// 拓客统计数据输入
  7 + /// </summary>
  8 + public class TkStatisticsInput
  9 + {
  10 + /// <summary>
  11 + /// 开始时间(可选)
  12 + /// </summary>
  13 + public DateTime? StartTime { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 结束时间(可选)
  17 + /// </summary>
  18 + public DateTime? EndTime { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 活动ID(可选)
  22 + /// </summary>
  23 + public string EventId { get; set; }
  24 + }
  25 +}
  26 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTkjlb/TkStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqTkjlb
  4 +{
  5 + /// <summary>
  6 + /// 拓客统计数据输出
  7 + /// </summary>
  8 + public class TkStatisticsOutput
  9 + {
  10 + /// <summary>
  11 + /// 拓客人数(去重后的人数)
  12 + /// </summary>
  13 + public int TkCount { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 邀约人头(已邀约人数)
  17 + /// </summary>
  18 + public int YaoyCount { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 预约人头(已预约人数)
  22 + /// </summary>
  23 + public int YyCount { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 到店人头(已到店人数)
  27 + /// </summary>
  28 + public int DdCount { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 开单人头(已开单人数)
  32 + /// </summary>
  33 + public int KdCount { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 开单金额
  37 + /// </summary>
  38 + public decimal KdAmount { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 消费人头(已消费人数)
  42 + /// </summary>
  43 + public int XfCount { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 消费金额
  47 + /// </summary>
  48 + public decimal XfAmount { get; set; }
  49 + }
  50 +}
  51 +
... ...
netcore/src/Modularity/Extend/NCC.Extend.Interfaces/ILqTkjlbService.cs
1 1 using System.Threading.Tasks;
  2 +using NCC.Extend.Entitys.Dto.LqTkjlb;
2 3  
3 4 namespace NCC.Extend.Interfaces.LqTkjlb
4 5 {
... ... @@ -15,5 +16,12 @@ namespace NCC.Extend.Interfaces.LqTkjlb
15 16 /// </summary>
16 17 /// <returns></returns>
17 18 Task<dynamic> GetTeamDetail();
  19 +
  20 + /// <summary>
  21 + /// 获取拓客统计数据
  22 + /// </summary>
  23 + /// <param name="input">查询参数</param>
  24 + /// <returns>拓客统计数据</returns>
  25 + Task<TkStatisticsOutput> GetTkStatistics(TkStatisticsInput input);
18 26 }
19 27 }
20 28 \ No newline at end of file
... ...
netcore/src/Modularity/Extend/NCC.Extend.Interfaces/LqDailyReport/ILqDailyReportService.cs 0 → 100644
  1 +using System.Collections.Generic;
  2 +using System.Threading.Tasks;
  3 +using NCC.Extend.Entitys.Dto.LqDailyReport;
  4 +
  5 +namespace NCC.Extend.Interfaces.LqDailyReport
  6 +{
  7 + /// <summary>
  8 + /// 日报服务接口
  9 + /// </summary>
  10 + public interface ILqDailyReportService
  11 + {
  12 + /// <summary>
  13 + /// 获取门店每日运营统计数据
  14 + /// </summary>
  15 + /// <param name="input">查询参数</param>
  16 + /// <returns>门店统计数据列表</returns>
  17 + Task<List<StoreDailyStatisticsOutput>> GetStoreDailyStatistics(StoreDailyStatisticsInput input);
  18 +
  19 + /// <summary>
  20 + /// 获取门店业绩完成情况
  21 + /// </summary>
  22 + /// <param name="input">查询参数</param>
  23 + /// <returns>门店业绩完成情况列表</returns>
  24 + Task<List<StorePerformanceCompletionOutput>> GetStorePerformanceCompletion(StorePerformanceCompletionInput input);
  25 +
  26 + /// <summary>
  27 + /// 获取事业部业绩完成情况
  28 + /// </summary>
  29 + /// <param name="input">查询参数</param>
  30 + /// <returns>事业部业绩完成情况列表</returns>
  31 + Task<List<BusinessUnitPerformanceCompletionOutput>> GetBusinessUnitPerformanceCompletion(BusinessUnitPerformanceCompletionInput input);
  32 +
  33 + /// <summary>
  34 + /// 获取天王团业绩完成情况
  35 + /// </summary>
  36 + /// <param name="input">查询参数</param>
  37 + /// <returns>天王团业绩完成情况列表</returns>
  38 + Task<List<TianwangGroupPerformanceCompletionOutput>> GetTianwangGroupPerformanceCompletion(TianwangGroupPerformanceCompletionInput input);
  39 +
  40 + /// <summary>
  41 + /// 获取经理业绩完成情况
  42 + /// </summary>
  43 + /// <param name="input">查询参数</param>
  44 + /// <returns>经理业绩完成情况列表</returns>
  45 + Task<List<ManagerPerformanceCompletionOutput>> GetManagerPerformanceCompletion(ManagerPerformanceCompletionInput input);
  46 +
  47 + /// <summary>
  48 + /// 获取经理汇总业绩完成情况
  49 + /// </summary>
  50 + /// <param name="input">查询参数</param>
  51 + /// <returns>经理汇总业绩完成情况列表</returns>
  52 + Task<List<ManagerSummaryPerformanceCompletionOutput>> GetManagerSummaryPerformanceCompletion(ManagerPerformanceCompletionInput input);
  53 +
  54 + /// <summary>
  55 + /// 获取科技部老师日报统计
  56 + /// </summary>
  57 + /// <param name="input">查询参数</param>
  58 + /// <returns>科技部老师统计列表</returns>
  59 + Task<List<TechTeacherDailyStatisticsOutput>> GetTechTeacherDailyStatistics(TechTeacherDailyStatisticsInput input);
  60 + }
  61 +}
  62 +
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs 0 → 100644
  1 +using NCC.Common.Core.Manager;
  2 +using NCC.Common.Enum;
  3 +using NCC.Common.Extension;
  4 +using NCC.Common.Filter;
  5 +using NCC.Dependency;
  6 +using NCC.DynamicApiController;
  7 +using NCC.FriendlyException;
  8 +using NCC.Extend.Interfaces.LqDailyReport;
  9 +using Mapster;
  10 +using Microsoft.AspNetCore.Mvc;
  11 +using SqlSugar;
  12 +using System;
  13 +using System.Collections.Generic;
  14 +using System.Linq;
  15 +using System.Threading.Tasks;
  16 +using Yitter.IdGenerator;
  17 +using NCC.Common.Helper;
  18 +using NCC.JsonSerialization;
  19 +using NCC.Common.Model.NPOI;
  20 +using NCC.Common.Configuration;
  21 +using NCC.DataEncryption;
  22 +using NCC.ClayObject;
  23 +using NCC.Extend.Entitys.Dto.LqDailyReport;
  24 +using NCC.Extend.Entitys.lq_kd_kdjlb;
  25 +
  26 +namespace NCC.Extend
  27 +{
  28 + /// <summary>
  29 + /// 日报服务
  30 + /// </summary>
  31 + [ApiDescriptionSettings(Tag = "绿纤日报服务", Name = "LqDailyReport", Order = 200)]
  32 + [Route("api/Extend/[controller]")]
  33 + public class LqDailyReportService : ILqDailyReportService, IDynamicApiController, ITransient
  34 + {
  35 + private readonly ISqlSugarRepository<LqKdKdjlbEntity> _repository;
  36 + private readonly SqlSugarScope _db;
  37 + private readonly IUserManager _userManager;
  38 +
  39 + /// <summary>
  40 + /// 初始化一个<see cref="LqDailyReportService"/>类型的新实例
  41 + /// </summary>
  42 + public LqDailyReportService(
  43 + ISqlSugarRepository<LqKdKdjlbEntity> repository,
  44 + IUserManager userManager)
  45 + {
  46 + _repository = repository;
  47 + _db = _repository.Context;
  48 + _userManager = userManager;
  49 + }
  50 +
  51 + /// <summary>
  52 + /// 获取时间范围,如果未传入则默认为本月
  53 + /// </summary>
  54 + private (DateTime startDate, DateTime endDate) GetTimeRange(DateTime? startTime, DateTime? endTime)
  55 + {
  56 + var now = DateTime.Now;
  57 + var start = startTime ?? new DateTime(now.Year, now.Month, 1);
  58 + var end = endTime ?? now;
  59 +
  60 + // 确保开始时间不晚于结束时间
  61 + if (start > end)
  62 + {
  63 + var temp = start;
  64 + start = end;
  65 + end = temp;
  66 + }
  67 +
  68 + return (start, end);
  69 + }
  70 +
  71 + #region 获取门店每日运营统计数据
  72 + /// <summary>
  73 + /// 获取门店每日运营统计数据
  74 + /// </summary>
  75 + /// <remarks>
  76 + /// 根据指定时间范围统计各门店的运营数据,包括在职员工数、人头数、人次、项目数、消耗业绩等信息
  77 + ///
  78 + /// 示例请求:
  79 + /// ```json
  80 + /// {
  81 + /// "startTime": "2025-10-01",
  82 + /// "endTime": "2025-10-27",
  83 + /// "storeIds": ["门店ID1", "门店ID2"]
  84 + /// }
  85 + /// ```
  86 + ///
  87 + /// 参数说明:
  88 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  89 + /// - endTime: 结束时间,可选,不传则默认为今天
  90 + /// - storeIds: 门店ID列表,可选,不传则查询所有门店
  91 + ///
  92 + /// 返回说明:
  93 + /// - StoreId: 门店ID
  94 + /// - StoreName: 门店名称
  95 + /// - EmployeeCount: 在职员工数(F_EnabledMark=1且有岗位)
  96 + /// - HeadCount: 人头数(去重后的消费会员数)
  97 + /// - PersonCount: 人次(日度去重客户数,同一天同一客户只算一次)
  98 + /// - ProjectCount: 项目数(消耗的项目总数)
  99 + /// - ConsumePerformance: 消耗业绩(总金额)
  100 + /// </remarks>
  101 + /// <param name="input">查询参数</param>
  102 + /// <returns>门店统计数据列表</returns>
  103 + /// <response code="200">成功返回门店统计列表</response>
  104 + /// <response code="400">日期格式错误或参数无效</response>
  105 + /// <response code="500">服务器内部错误</response>
  106 + [HttpPost("get-store-daily-statistics")]
  107 + public async Task<List<StoreDailyStatisticsOutput>> GetStoreDailyStatistics(StoreDailyStatisticsInput input)
  108 + {
  109 + // 获取时间范围
  110 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  111 +
  112 + // 构建门店过滤条件
  113 + var storeFilter = "";
  114 + if (input.StoreIds != null && input.StoreIds.Any())
  115 + {
  116 + var storeIdsStr = string.Join("','", input.StoreIds);
  117 + storeFilter = $"AND consume.Md IN ('{storeIdsStr}')";
  118 + }
  119 +
  120 + // SQL查询:获取门店每日运营数据
  121 + var sql = $@"
  122 + SELECT
  123 + consume.Md as StoreId,
  124 + MAX(store.dm) as StoreName,
  125 + -- 在职员工数
  126 + COALESCE((
  127 + SELECT COUNT(DISTINCT u.F_Id)
  128 + FROM BASE_USER u
  129 + WHERE u.F_MDID = consume.Md
  130 + AND u.F_EnabledMark = 1
  131 + AND (u.F_DeleteMark IS NULL OR u.F_DeleteMark = 0)
  132 + AND u.F_GW IS NOT NULL
  133 + ), 0) as EmployeeCount,
  134 + -- 人头数(去重后的消费会员数)
  135 + COALESCE(COUNT(DISTINCT consume.Hy), 0) as HeadCount,
  136 + -- 人次(日度去重客户数)
  137 + COALESCE(COUNT(DISTINCT CONCAT(consume.Hy, '-', DATE(consume.Hksj))), 0) as PersonCount,
  138 + -- 项目数(消耗的项目总数)
  139 + COALESCE(SUM(project.F_ProjectNumber), 0) as ProjectCount,
  140 + -- 消耗业绩(总金额)
  141 + COALESCE(SUM(project.F_TotalPrice), 0) as ConsumePerformance
  142 + FROM lq_xh_hyhk consume
  143 + LEFT JOIN lq_mdxx store ON consume.Md = store.F_Id
  144 + LEFT JOIN lq_xh_pxmx project ON consume.F_Id = project.F_ConsumeInfoId AND project.F_IsEffective = 1
  145 + WHERE consume.F_IsEffective = 1
  146 + AND DATE(consume.Hksj) >= '{startDate:yyyy-MM-dd}'
  147 + AND DATE(consume.Hksj) <= '{endDate:yyyy-MM-dd}'
  148 + {storeFilter}
  149 + GROUP BY consume.Md
  150 + ORDER BY ConsumePerformance DESC";
  151 +
  152 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  153 +
  154 + var outputList = result.Select(item => new StoreDailyStatisticsOutput
  155 + {
  156 + StoreId = item.StoreId,
  157 + StoreName = item.StoreName,
  158 + EmployeeCount = Convert.ToInt32(item.EmployeeCount),
  159 + HeadCount = Convert.ToInt32(item.HeadCount),
  160 + PersonCount = Convert.ToInt32(item.PersonCount),
  161 + ProjectCount = Convert.ToInt32(item.ProjectCount),
  162 + ConsumePerformance = Convert.ToDecimal(item.ConsumePerformance)
  163 + }).ToList();
  164 + return outputList;
  165 + }
  166 + #endregion
  167 +
  168 + #region 获取门店业绩完成情况
  169 + /// <summary>
  170 + /// 获取门店业绩完成情况
  171 + /// </summary>
  172 + /// <remarks>
  173 + /// 根据指定日期统计各门店的业绩完成情况,包括目标业绩、完成业绩、完成率
  174 + ///
  175 + /// 示例请求:
  176 + /// ```json
  177 + /// {
  178 + /// "startTime": "2025-10-01",
  179 + /// "endTime": "2025-10-22",
  180 + /// "storeIds": ["门店ID1", "门店ID2"]
  181 + /// }
  182 + /// ```
  183 + ///
  184 + /// 参数说明:
  185 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  186 + /// - endTime: 结束时间,可选,不传则默认为今天
  187 + /// - storeIds: 门店ID列表,可选,不传则查询所有门店
  188 + ///
  189 + /// 返回说明:
  190 + /// - StoreId: 门店ID
  191 + /// - StoreName: 门店名称
  192 + /// - TargetPerformance: 目标业绩(生命线,来自门店信息表)
  193 + /// - CompletedPerformance: 完成业绩(当月1号至统计日期的开单业绩总和)
  194 + /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
  195 + /// </remarks>
  196 + /// <param name="input">查询参数</param>
  197 + /// <returns>门店业绩完成情况列表</returns>
  198 + /// <response code="200">成功返回门店业绩完成情况列表</response>
  199 + /// <response code="400">日期格式错误或参数无效</response>
  200 + /// <response code="500">服务器内部错误</response>
  201 + [HttpPost("get-store-performance-completion")]
  202 + public async Task<List<StorePerformanceCompletionOutput>> GetStorePerformanceCompletion(StorePerformanceCompletionInput input)
  203 + {
  204 + // 获取时间范围
  205 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  206 +
  207 + // 构建门店过滤条件
  208 + var storeFilter = "";
  209 + if (input.StoreIds != null && input.StoreIds.Any())
  210 + {
  211 + var storeIdsStr = string.Join("','", input.StoreIds);
  212 + storeFilter = $"AND store.F_Id IN ('{storeIdsStr}')";
  213 + }
  214 +
  215 + // SQL查询:获取门店业绩完成情况
  216 + var sql = $@"
  217 + SELECT
  218 + store.F_Id as StoreId,
  219 + store.dm as StoreName,
  220 + -- 目标业绩(生命线)
  221 + COALESCE(store.xsyj, 0) as TargetPerformance,
  222 + -- 完成业绩(开单业绩总和)
  223 + COALESCE((
  224 + SELECT SUM(billing.sfyj)
  225 + FROM lq_kd_kdjlb billing
  226 + WHERE billing.djmd = store.F_Id
  227 + AND billing.F_IsEffective = 1
  228 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  229 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  230 + ), 0) as CompletedPerformance
  231 + FROM lq_mdxx store
  232 + WHERE 1=1 {storeFilter}
  233 + ORDER BY CompletedPerformance DESC";
  234 +
  235 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  236 +
  237 + var outputList = result.Select(item =>
  238 + {
  239 + var targetPerformance = Convert.ToDecimal(item.TargetPerformance);
  240 + var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
  241 + var completionRate = targetPerformance > 0 ? (completedPerformance / targetPerformance * 100m) : 0m;
  242 +
  243 + return new StorePerformanceCompletionOutput
  244 + {
  245 + StoreId = item.StoreId,
  246 + StoreName = item.StoreName,
  247 + TargetPerformance = targetPerformance,
  248 + CompletedPerformance = completedPerformance,
  249 + CompletionRate = decimal.Round(completionRate, 2)
  250 + };
  251 + }).ToList();
  252 +
  253 + return outputList;
  254 + }
  255 + #endregion
  256 +
  257 + #region 获取事业部业绩完成情况
  258 + /// <summary>
  259 + /// 获取事业部业绩完成情况
  260 + /// </summary>
  261 + /// <remarks>
  262 + /// 根据指定日期统计各事业部的业绩完成情况,包括目标业绩、完成业绩、完成率
  263 + ///
  264 + /// 示例请求:
  265 + /// ```json
  266 + /// {
  267 + /// "startTime": "2025-10-01",
  268 + /// "endTime": "2025-10-22",
  269 + /// "businessUnitIds": ["事业部ID1", "事业部ID2"]
  270 + /// }
  271 + /// ```
  272 + ///
  273 + /// 参数说明:
  274 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  275 + /// - endTime: 结束时间,可选,不传则默认为今天
  276 + /// - businessUnitIds: 事业部ID列表,可选,不传则查询所有事业部
  277 + ///
  278 + /// 返回说明:
  279 + /// - BusinessUnitId: 事业部ID
  280 + /// - BusinessUnitName: 事业部名称
  281 + /// - TargetPerformance: 目标业绩(管理门店的生命线总和)
  282 + /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
  283 + /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
  284 + /// - StoreCount: 门店数量
  285 + /// </remarks>
  286 + /// <param name="input">查询参数</param>
  287 + /// <returns>事业部业绩完成情况列表</returns>
  288 + /// <response code="200">成功返回事业部业绩完成情况列表</response>
  289 + /// <response code="400">日期格式错误或参数无效</response>
  290 + /// <response code="500">服务器内部错误</response>
  291 + [HttpPost("get-business-unit-performance-completion")]
  292 + public async Task<List<BusinessUnitPerformanceCompletionOutput>> GetBusinessUnitPerformanceCompletion(BusinessUnitPerformanceCompletionInput input)
  293 + {
  294 + // 获取时间范围
  295 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  296 +
  297 + // 构建事业部过滤条件
  298 + var businessUnitFilter = "";
  299 + if (input.BusinessUnitIds != null && input.BusinessUnitIds.Any())
  300 + {
  301 + var filterUnitIdsStr = string.Join("','", input.BusinessUnitIds);
  302 + businessUnitFilter = $"AND o.F_Id IN ('{filterUnitIdsStr}')";
  303 + }
  304 +
  305 + // 分步查询,提高效率
  306 + // 第一步:获取事业部列表及目标业绩(只统计事业一部到事业六部)
  307 + var businessUnitSql = $@"
  308 + SELECT
  309 + o.F_Id as BusinessUnitId,
  310 + o.F_FullName as BusinessUnitName,
  311 + COALESCE(SUM(store.xsyj), 0) as TargetPerformance,
  312 + COUNT(DISTINCT store.F_Id) as StoreCount
  313 + FROM base_organize o
  314 + LEFT JOIN lq_mdxx store ON store.syb = o.F_Id
  315 + WHERE o.F_Category = 'department'
  316 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  317 + AND o.F_FullName IN ('事业一部', '事业二部', '事业三部', '事业四部', '事业五部', '事业六部')
  318 + {businessUnitFilter}
  319 + GROUP BY o.F_Id, o.F_FullName";
  320 +
  321 + var businessUnits = await _db.Ado.SqlQueryAsync<dynamic>(businessUnitSql);
  322 +
  323 + if (businessUnits == null || !businessUnits.Any())
  324 + {
  325 + return new List<BusinessUnitPerformanceCompletionOutput>();
  326 + }
  327 +
  328 + var businessUnitDict = new Dictionary<string, BusinessUnitPerformanceCompletionOutput>();
  329 +
  330 + foreach (var unit in businessUnits)
  331 + {
  332 + businessUnitDict[unit.BusinessUnitId.ToString()] = new BusinessUnitPerformanceCompletionOutput
  333 + {
  334 + BusinessUnitId = unit.BusinessUnitId.ToString(),
  335 + BusinessUnitName = unit.BusinessUnitName.ToString(),
  336 + TargetPerformance = Convert.ToDecimal(unit.TargetPerformance),
  337 + CompletedPerformance = 0,
  338 + CompletionRate = 0,
  339 + StoreCount = Convert.ToInt32(unit.StoreCount)
  340 + };
  341 + }
  342 +
  343 + // 第二步:统计各事业部的完成业绩
  344 + var businessUnitIds = businessUnitDict.Keys.ToList();
  345 + var unitIdsStr = string.Join("','", businessUnitIds);
  346 +
  347 + var completedPerformanceSql = $@"
  348 + SELECT
  349 + store.syb as BusinessUnitId,
  350 + COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance
  351 + FROM lq_kd_kdjlb billing
  352 + INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id
  353 + INNER JOIN base_organize o ON store.syb = o.F_Id
  354 + WHERE billing.F_IsEffective = 1
  355 + AND o.F_Category = 'department'
  356 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  357 + AND o.F_FullName IN ('事业一部', '事业二部', '事业三部', '事业四部', '事业五部', '事业六部')
  358 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  359 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  360 + AND store.syb IN ('{unitIdsStr}')
  361 + GROUP BY store.syb";
  362 +
  363 + var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql);
  364 +
  365 + // 第三步:合并数据并计算完成率
  366 + foreach (var item in completedPerformanceData)
  367 + {
  368 + var businessUnitId = item.BusinessUnitId.ToString();
  369 + if (businessUnitDict.ContainsKey(businessUnitId))
  370 + {
  371 + var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
  372 + var unit = businessUnitDict[businessUnitId];
  373 + unit.CompletedPerformance = completedPerformance;
  374 +
  375 + var completionRate = unit.TargetPerformance > 0
  376 + ? (completedPerformance / unit.TargetPerformance * 100m)
  377 + : 0m;
  378 + unit.CompletionRate = decimal.Round(completionRate, 2);
  379 + }
  380 + }
  381 +
  382 + var outputList = businessUnitDict.Values
  383 + .OrderByDescending(x => x.CompletedPerformance)
  384 + .ToList();
  385 +
  386 + return outputList;
  387 + }
  388 + #endregion
  389 +
  390 + #region 获取天王团业绩完成情况
  391 + /// <summary>
  392 + /// 获取天王团业绩完成情况
  393 + /// </summary>
  394 + /// <remarks>
  395 + /// 根据指定日期统计各天王团的业绩完成情况,包括目标业绩、完成业绩、完成率
  396 + ///
  397 + /// 示例请求:
  398 + /// ```json
  399 + /// {
  400 + /// "startTime": "2025-10-01",
  401 + /// "endTime": "2025-10-22",
  402 + /// "departmentIds": ["部门ID1", "部门ID2"]
  403 + /// }
  404 + /// ```
  405 + ///
  406 + /// 参数说明:
  407 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  408 + /// - endTime: 结束时间,可选,不传则默认为今天
  409 + /// - departmentIds: 天王团部门ID列表,可选,不传则查询所有天王团
  410 + ///
  411 + /// 返回说明:
  412 + /// - DepartmentId: 部门ID
  413 + /// - DepartmentName: 部门名称(教育一部、教育二部、科技一部、科技二部、大项目一部、大项目二部)
  414 + /// - TargetPerformance: 目标业绩(管理门店的生命线总和)
  415 + /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
  416 + /// - CompletionRate: 完成率(百分比,CompletedPerformance / TargetPerformance * 100)
  417 + /// - StoreCount: 门店数量
  418 + /// </remarks>
  419 + /// <param name="input">查询参数</param>
  420 + /// <returns>天王团业绩完成情况列表</returns>
  421 + /// <response code="200">成功返回天王团业绩完成情况列表</response>
  422 + /// <response code="400">日期格式错误或参数无效</response>
  423 + /// <response code="500">服务器内部错误</response>
  424 + [HttpPost("get-tianwang-group-performance-completion")]
  425 + public async Task<List<TianwangGroupPerformanceCompletionOutput>> GetTianwangGroupPerformanceCompletion(TianwangGroupPerformanceCompletionInput input)
  426 + {
  427 + // 获取时间范围
  428 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  429 +
  430 + // 构建部门过滤条件
  431 + var departmentFilter = "";
  432 + if (input.DepartmentIds != null && input.DepartmentIds.Any())
  433 + {
  434 + var filterDeptIdsStr = string.Join("','", input.DepartmentIds);
  435 + departmentFilter = $"AND o.F_Id IN ('{filterDeptIdsStr}')";
  436 + }
  437 +
  438 + // 天王团部门信息(字段名和部门名称的映射)
  439 + var tianwangDepartments = new Dictionary<string, (string fieldName, string[] departmentNames)>
  440 + {
  441 + { "jyb", ("jyb", new[] { "教育一部", "教育二部" }) },
  442 + { "kjb", ("kjb", new[] { "科技一部", "科技二部" }) },
  443 + { "dxmb", ("dxmb", new[] { "大项目一部", "大项目二部" }) }
  444 + };
  445 +
  446 + // 分步查询,提高效率
  447 + var departmentDict = new Dictionary<string, TianwangGroupPerformanceCompletionOutput>();
  448 + var allDeptNames = new List<string>();
  449 +
  450 + // 循环查询每个天王团类型
  451 + foreach (var deptType in tianwangDepartments)
  452 + {
  453 + var deptNamesStr = string.Join("','", deptType.Value.departmentNames);
  454 + allDeptNames.AddRange(deptType.Value.departmentNames);
  455 +
  456 + // 第一步:获取天王团列表及目标业绩
  457 + var departmentSql = $@"
  458 + SELECT
  459 + o.F_Id as DepartmentId,
  460 + o.F_FullName as DepartmentName,
  461 + COALESCE(SUM(store.xsyj), 0) as TargetPerformance,
  462 + COUNT(DISTINCT store.F_Id) as StoreCount
  463 + FROM base_organize o
  464 + LEFT JOIN lq_mdxx store ON store.{deptType.Key} = o.F_Id
  465 + WHERE o.F_Category = 'department'
  466 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  467 + AND o.F_FullName IN ('{deptNamesStr}')
  468 + {departmentFilter}
  469 + GROUP BY o.F_Id, o.F_FullName";
  470 +
  471 + var departments = await _db.Ado.SqlQueryAsync<dynamic>(departmentSql);
  472 +
  473 + foreach (var dept in departments ?? Enumerable.Empty<dynamic>())
  474 + {
  475 + if (!departmentDict.ContainsKey(dept.DepartmentId.ToString()))
  476 + {
  477 + departmentDict[dept.DepartmentId.ToString()] = new TianwangGroupPerformanceCompletionOutput
  478 + {
  479 + DepartmentId = dept.DepartmentId.ToString(),
  480 + DepartmentName = dept.DepartmentName.ToString(),
  481 + TargetPerformance = Convert.ToDecimal(dept.TargetPerformance),
  482 + CompletedPerformance = 0,
  483 + CompletionRate = 0,
  484 + StoreCount = Convert.ToInt32(dept.StoreCount)
  485 + };
  486 + }
  487 + }
  488 + }
  489 +
  490 + if (!departmentDict.Any())
  491 + {
  492 + return new List<TianwangGroupPerformanceCompletionOutput>();
  493 + }
  494 +
  495 + // 第二步:统计各天王团的完成业绩(分类型统计)
  496 + var completedPerformanceDataList = new List<dynamic>();
  497 +
  498 + foreach (var deptType in tianwangDepartments)
  499 + {
  500 + var deptIdsStrForQuery = string.Join("','", departmentDict.Keys);
  501 +
  502 + var completedPerformanceSql = $@"
  503 + SELECT
  504 + o.F_Id as DepartmentId,
  505 + COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance
  506 + FROM lq_kd_kdjlb billing
  507 + INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id
  508 + INNER JOIN base_organize o ON store.{deptType.Key} = o.F_Id
  509 + WHERE billing.F_IsEffective = 1
  510 + AND o.F_Category = 'department'
  511 + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1)
  512 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  513 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  514 + AND o.F_Id IN ('{deptIdsStrForQuery}')
  515 + GROUP BY o.F_Id";
  516 +
  517 + var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql);
  518 + if (completedPerformanceData != null)
  519 + {
  520 + completedPerformanceDataList.AddRange(completedPerformanceData);
  521 + }
  522 + }
  523 +
  524 + // 第三步:合并数据并计算完成率(按部门ID去重累加)
  525 + var departmentPerformanceDict = new Dictionary<string, decimal>();
  526 + foreach (var item in completedPerformanceDataList)
  527 + {
  528 + var departmentId = item.DepartmentId.ToString();
  529 + var completedPerformance = Convert.ToDecimal(item.CompletedPerformance);
  530 +
  531 + if (departmentPerformanceDict.ContainsKey(departmentId))
  532 + {
  533 + departmentPerformanceDict[departmentId] += completedPerformance;
  534 + }
  535 + else
  536 + {
  537 + departmentPerformanceDict[departmentId] = completedPerformance;
  538 + }
  539 + }
  540 +
  541 + // 计算完成率
  542 + foreach (var kvp in departmentPerformanceDict)
  543 + {
  544 + if (departmentDict.ContainsKey(kvp.Key))
  545 + {
  546 + var dept = departmentDict[kvp.Key];
  547 + dept.CompletedPerformance = kvp.Value;
  548 +
  549 + var completionRate = dept.TargetPerformance > 0
  550 + ? (kvp.Value / dept.TargetPerformance * 100m)
  551 + : 0m;
  552 + dept.CompletionRate = decimal.Round(completionRate, 2);
  553 + }
  554 + }
  555 +
  556 + var outputList = departmentDict.Values
  557 + .OrderByDescending(x => x.CompletedPerformance)
  558 + .ToList();
  559 +
  560 + return outputList;
  561 + }
  562 + #endregion
  563 +
  564 + #region 获取经理业绩完成情况
  565 + /// <summary>
  566 + /// 获取经理业绩完成情况
  567 + /// </summary>
  568 + /// <remarks>
  569 + /// 根据指定日期统计各经理在各门店的业绩完成情况,包括目标业绩(三个阶段)、完成业绩、完成率
  570 + ///
  571 + /// 示例请求:
  572 + /// ```json
  573 + /// {
  574 + /// "startTime": "2025-10-01",
  575 + /// "endTime": "2025-10-22",
  576 + /// "managerId": "经理ID"
  577 + /// }
  578 + /// ```
  579 + ///
  580 + /// 参数说明:
  581 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  582 + /// - endTime: 结束时间,可选,不传则默认为今天
  583 + /// - managerId: 经理用户ID,可选,不传则查询所有经理
  584 + ///
  585 + /// 返回说明:
  586 + /// - ManagerId: 经理用户ID
  587 + /// - ManagerName: 经理姓名
  588 + /// - StoreId/StoreName: 门店信息
  589 + /// - Target1/2/3: 目标业绩一/二/三阶段(smx1/smx2/smx3)
  590 + /// - CompletedPerformance: 完成业绩(指定时间范围内的开单业绩总和)
  591 + /// - CompletionRate1/2/3: 完成率各阶段(百分比)
  592 + /// </remarks>
  593 + /// <param name="input">查询参数</param>
  594 + /// <returns>经理业绩完成情况列表</returns>
  595 + /// <response code="200">成功返回经理业绩完成情况列表</response>
  596 + /// <response code="400">日期格式错误或参数无效</response>
  597 + /// <response code="500">服务器内部错误</response>
  598 + [HttpPost("get-manager-performance-completion")]
  599 + public async Task<List<ManagerPerformanceCompletionOutput>> GetManagerPerformanceCompletion(ManagerPerformanceCompletionInput input)
  600 + {
  601 + // 获取时间范围
  602 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  603 +
  604 + // 构建经理过滤条件
  605 + var managerFilter = "";
  606 + if (!string.IsNullOrWhiteSpace(input.ManagerId))
  607 + {
  608 + managerFilter = $"AND target.zjl_userid = '{input.ManagerId}'";
  609 + }
  610 +
  611 + // SQL查询:获取经理在各门店的目标业绩和完成业绩
  612 + var sql = $@"
  613 + SELECT
  614 + target.zjl_userid as ManagerId,
  615 + u.F_RealName as ManagerName,
  616 + target.md_id as StoreId,
  617 + store.dm as StoreName,
  618 + target.smx1 as Target1,
  619 + target.smx2 as Target2,
  620 + target.smx3 as Target3,
  621 + -- 完成业绩
  622 + COALESCE((
  623 + SELECT SUM(billing.sfyj)
  624 + FROM lq_kd_kdjlb billing
  625 + WHERE billing.djmd = target.md_id
  626 + AND billing.F_IsEffective = 1
  627 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  628 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  629 + ), 0) as CompletedPerformance
  630 + FROM lq_zjl_mdsmxsz target
  631 + INNER JOIN BASE_USER u ON target.zjl_userid = u.F_Id
  632 + INNER JOIN lq_mdxx store ON target.md_id = store.F_Id
  633 + WHERE target.deletemark = 0
  634 + {managerFilter}
  635 + ORDER BY target.zjl_userid, store.dm";
  636 +
  637 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  638 +
  639 + var outputList = result.Select(item =>
  640 + {
  641 + var target1 = Convert.ToDecimal(item.Target1);
  642 + var target2 = item.Target2 != null ? Convert.ToDecimal(item.Target2) : 0;
  643 + var target3 = item.Target3 != null ? Convert.ToDecimal(item.Target3) : 0;
  644 + var completed = Convert.ToDecimal(item.CompletedPerformance);
  645 +
  646 + var rate1 = target1 > 0 ? (completed / target1 * 100m) : 0m;
  647 + var rate2 = target2 > 0 ? (completed / target2 * 100m) : 0m;
  648 + var rate3 = target3 > 0 ? (completed / target3 * 100m) : 0m;
  649 +
  650 + return new ManagerPerformanceCompletionOutput
  651 + {
  652 + ManagerId = item.ManagerId.ToString(),
  653 + ManagerName = item.ManagerName.ToString(),
  654 + StoreId = item.StoreId.ToString(),
  655 + StoreName = item.StoreName.ToString(),
  656 + Target1 = target1,
  657 + Target2 = target2 > 0 ? target2 : (decimal?)null,
  658 + Target3 = target3 > 0 ? target3 : (decimal?)null,
  659 + CompletedPerformance = completed,
  660 + CompletionRate1 = decimal.Round(rate1, 2),
  661 + CompletionRate2 = target2 > 0 ? decimal.Round(rate2, 2) : (decimal?)null,
  662 + CompletionRate3 = target3 > 0 ? decimal.Round(rate3, 2) : (decimal?)null
  663 + };
  664 + }).ToList();
  665 +
  666 + return outputList;
  667 + }
  668 +
  669 + /// <summary>
  670 + /// 获取经理汇总业绩完成情况
  671 + /// </summary>
  672 + /// <remarks>
  673 + /// 根据指定日期统计各经理的总业绩完成情况,汇总所有管理门店的数据
  674 + ///
  675 + /// 示例请求:
  676 + /// ```json
  677 + /// {
  678 + /// "startTime": "2025-10-01",
  679 + /// "endTime": "2025-10-22",
  680 + /// "managerId": "经理ID"
  681 + /// }
  682 + /// ```
  683 + ///
  684 + /// 参数说明:
  685 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  686 + /// - endTime: 结束时间,可选,不传则默认为今天
  687 + /// - managerId: 经理用户ID,可选,不传则查询所有经理
  688 + ///
  689 + /// 返回说明:
  690 + /// - ManagerId: 经理用户ID
  691 + /// - ManagerName: 经理姓名
  692 + /// - TotalTarget1/2/3: 总目标业绩一/二/三阶段
  693 + /// - TotalCompletedPerformance: 总完成业绩(指定时间范围内的开单业绩总和)
  694 + /// - TotalCompletionRate1/2/3: 总完成率各阶段
  695 + /// - StoreCount: 管理门店数量
  696 + /// </remarks>
  697 + /// <param name="input">查询参数</param>
  698 + /// <returns>经理汇总业绩完成情况列表</returns>
  699 + /// <response code="200">成功返回经理汇总业绩完成情况列表</response>
  700 + /// <response code="400">日期格式错误或参数无效</response>
  701 + /// <response code="500">服务器内部错误</response>
  702 + [HttpPost("get-manager-summary-performance-completion")]
  703 + public async Task<List<ManagerSummaryPerformanceCompletionOutput>> GetManagerSummaryPerformanceCompletion(ManagerPerformanceCompletionInput input)
  704 + {
  705 + // 获取时间范围
  706 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  707 +
  708 + // 构建经理过滤条件
  709 + var managerFilter = "";
  710 + if (!string.IsNullOrWhiteSpace(input.ManagerId))
  711 + {
  712 + managerFilter = $"AND target.zjl_userid = '{input.ManagerId}'";
  713 + }
  714 +
  715 + // SQL查询:获取经理汇总业绩
  716 + var sql = $@"
  717 + SELECT
  718 + target.zjl_userid as ManagerId,
  719 + u.F_RealName as ManagerName,
  720 + SUM(target.smx1) as TotalTarget1,
  721 + COALESCE(SUM(target.smx2), 0) as TotalTarget2,
  722 + COALESCE(SUM(target.smx3), 0) as TotalTarget3,
  723 + COUNT(DISTINCT target.md_id) as StoreCount,
  724 + -- 总完成业绩
  725 + SUM(COALESCE((
  726 + SELECT SUM(billing.sfyj)
  727 + FROM lq_kd_kdjlb billing
  728 + WHERE billing.djmd = target.md_id
  729 + AND billing.F_IsEffective = 1
  730 + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}'
  731 + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}'
  732 + ), 0)) as TotalCompletedPerformance
  733 + FROM lq_zjl_mdsmxsz target
  734 + INNER JOIN BASE_USER u ON target.zjl_userid = u.F_Id
  735 + INNER JOIN lq_mdxx store ON target.md_id = store.F_Id
  736 + WHERE target.deletemark = 0
  737 + {managerFilter}
  738 + GROUP BY target.zjl_userid, u.F_RealName
  739 + ORDER BY TotalCompletedPerformance DESC";
  740 +
  741 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  742 +
  743 + var outputList = result.Select(item =>
  744 + {
  745 + var totalTarget1 = Convert.ToDecimal(item.TotalTarget1);
  746 + var totalTarget2 = Convert.ToDecimal(item.TotalTarget2);
  747 + var totalTarget3 = Convert.ToDecimal(item.TotalTarget3);
  748 + var totalCompleted = Convert.ToDecimal(item.TotalCompletedPerformance);
  749 +
  750 + var rate1 = totalTarget1 > 0 ? (totalCompleted / totalTarget1 * 100m) : 0m;
  751 + var rate2 = totalTarget2 > 0 ? (totalCompleted / totalTarget2 * 100m) : 0m;
  752 + var rate3 = totalTarget3 > 0 ? (totalCompleted / totalTarget3 * 100m) : 0m;
  753 +
  754 + return new ManagerSummaryPerformanceCompletionOutput
  755 + {
  756 + ManagerId = item.ManagerId.ToString(),
  757 + ManagerName = item.ManagerName.ToString(),
  758 + TotalTarget1 = totalTarget1,
  759 + TotalTarget2 = totalTarget2,
  760 + TotalTarget3 = totalTarget3,
  761 + TotalCompletedPerformance = totalCompleted,
  762 + TotalCompletionRate1 = decimal.Round(rate1, 2),
  763 + TotalCompletionRate2 = decimal.Round(rate2, 2),
  764 + TotalCompletionRate3 = decimal.Round(rate3, 2),
  765 + StoreCount = Convert.ToInt32(item.StoreCount)
  766 + };
  767 + }).ToList();
  768 +
  769 + return outputList;
  770 + }
  771 + #endregion
  772 +
  773 + #region 获取科技部老师统计
  774 + /// <summary>
  775 + /// 获取科技部老师统计
  776 + /// </summary>
  777 + /// <remarks>
  778 + /// 根据指定日期统计科技部老师的业绩情况,包括见客数、消耗项目数、消耗业绩、开单业绩
  779 + ///
  780 + /// 示例请求:
  781 + /// ```json
  782 + /// {
  783 + /// "startTime": "2025-10-01",
  784 + /// "endTime": "2025-10-22",
  785 + /// "techDepartmentId": "科技部ID",
  786 + /// "teacherIds": ["老师ID1", "老师ID2"]
  787 + /// }
  788 + /// ```
  789 + ///
  790 + /// 参数说明:
  791 + /// - startTime: 开始时间,可选,不传则默认为当月1号
  792 + /// - endTime: 结束时间,可选,不传则默认为今天
  793 + /// - techDepartmentId: 科技部ID,可选,不传则查询所有科技部
  794 + /// - teacherIds: 老师ID列表,可选
  795 + ///
  796 + /// 返回说明:
  797 + /// - TechDepartmentId: 科技部ID
  798 + /// - TechDepartmentName: 科技部名称
  799 + /// - TeacherId: 老师ID
  800 + /// - TeacherName: 老师姓名
  801 + /// - CustomerCount: 见客数(不同客户数量)
  802 + /// - ConsumeProjectCount: 消耗项目数
  803 + /// - ConsumeAchievement: 消耗业绩
  804 + /// - OrderAchievement: 开单业绩
  805 + /// </remarks>
  806 + /// <param name="input">查询参数</param>
  807 + /// <returns>科技部老师统计列表</returns>
  808 + /// <response code="200">成功返回科技部老师统计列表</response>
  809 + /// <response code="400">日期格式错误或参数无效</response>
  810 + /// <response code="500">服务器内部错误</response>
  811 + [HttpPost("get-tech-teacher-daily-statistics")]
  812 + public async Task<List<TechTeacherDailyStatisticsOutput>> GetTechTeacherDailyStatistics(TechTeacherDailyStatisticsInput input)
  813 + {
  814 + // 获取时间范围
  815 + var (startDate, endDate) = GetTimeRange(input.StartTime, input.EndTime);
  816 +
  817 + // 构建过滤条件
  818 + var techFilter = "";
  819 + if (!string.IsNullOrWhiteSpace(input.TechDepartmentId))
  820 + {
  821 + techFilter = $"AND techDept.F_Id = '{input.TechDepartmentId}'";
  822 + }
  823 +
  824 + var teacherFilter = "";
  825 + if (input.TeacherIds != null && input.TeacherIds.Any())
  826 + {
  827 + var teacherIdsStr = string.Join("','", input.TeacherIds);
  828 + teacherFilter = $"AND consume.kjbls IN ('{teacherIdsStr}')";
  829 + }
  830 +
  831 + var teacherFilterForOrder = "";
  832 + if (input.TeacherIds != null && input.TeacherIds.Any())
  833 + {
  834 + var teacherIdsStr = string.Join("','", input.TeacherIds);
  835 + teacherFilterForOrder = $"AND ord.kjbls IN ('{teacherIdsStr}')";
  836 + }
  837 +
  838 + // SQL查询:统计科技部老师的消耗业绩、见客数、项目数
  839 + var consumeSql = $@"
  840 + SELECT
  841 + techDept.F_Id as TechDepartmentId,
  842 + techDept.F_FullName as TechDepartmentName,
  843 + consume.kjbls as TeacherId,
  844 + consume.kjblsxm as TeacherName,
  845 + COUNT(DISTINCT hyhk.hy) as CustomerCount,
  846 + SUM(consume.F_hdpxNumber) as ConsumeProjectCount,
  847 + SUM(consume.kjblsyj) as ConsumeAchievement
  848 + FROM lq_xh_kjbsyj consume
  849 + INNER JOIN lq_xh_hyhk hyhk ON consume.glkdbh = hyhk.F_Id
  850 + INNER JOIN lq_mdxx store ON hyhk.md = store.F_Id
  851 + LEFT JOIN base_organize techDept ON store.kjb = techDept.F_Id
  852 + WHERE consume.F_IsEffective = 1
  853 + AND hyhk.F_IsEffective = 1
  854 + AND DATE(hyhk.hksj) >= '{startDate:yyyy-MM-dd}'
  855 + AND DATE(hyhk.hksj) <= '{endDate:yyyy-MM-dd}'
  856 + {techFilter}
  857 + {teacherFilter}
  858 + GROUP BY techDept.F_Id, techDept.F_FullName, consume.kjbls, consume.kjblsxm";
  859 +
  860 + var consumeResult = await _db.Ado.SqlQueryAsync<dynamic>(consumeSql);
  861 +
  862 + // 查询开单业绩
  863 + var orderSql = $@"
  864 + SELECT
  865 + techDept.F_Id as TechDepartmentId,
  866 + ord.kjbls as TeacherId,
  867 + SUM(ord.kjblsyj) as OrderAchievement
  868 + FROM lq_kd_kjbsyj ord
  869 + INNER JOIN lq_kd_kdjlb kdjlb ON ord.glkdbh = kdjlb.F_Id
  870 + INNER JOIN lq_mdxx store ON kdjlb.djmd = store.F_Id
  871 + LEFT JOIN base_organize techDept ON store.kjb = techDept.F_Id
  872 + WHERE ord.F_IsEffective = 1
  873 + AND kdjlb.F_IsEffective = 1
  874 + AND DATE(kdjlb.kdrq) >= '{startDate:yyyy-MM-dd}'
  875 + AND DATE(kdjlb.kdrq) <= '{endDate:yyyy-MM-dd}'
  876 + {techFilter}
  877 + {teacherFilterForOrder}
  878 + GROUP BY techDept.F_Id, ord.kjbls";
  879 +
  880 + var orderResult = await _db.Ado.SqlQueryAsync<dynamic>(orderSql);
  881 +
  882 + // 合并数据
  883 + var teacherDict = new Dictionary<string, TechTeacherDailyStatisticsOutput>();
  884 +
  885 + foreach (var item in consumeResult ?? Enumerable.Empty<dynamic>())
  886 + {
  887 + var key = $"{item.TechDepartmentId}_{item.TeacherId}";
  888 + teacherDict[key] = new TechTeacherDailyStatisticsOutput
  889 + {
  890 + TechDepartmentId = item.TechDepartmentId?.ToString(),
  891 + TechDepartmentName = item.TechDepartmentName?.ToString(),
  892 + TeacherId = item.TeacherId?.ToString(),
  893 + TeacherName = item.TeacherName?.ToString(),
  894 + CustomerCount = Convert.ToInt32(item.CustomerCount),
  895 + ConsumeProjectCount = Convert.ToInt32(item.ConsumeProjectCount),
  896 + ConsumeAchievement = Convert.ToDecimal(item.ConsumeAchievement),
  897 + OrderAchievement = 0
  898 + };
  899 + }
  900 +
  901 + // 合并开单业绩
  902 + foreach (var item in orderResult ?? Enumerable.Empty<dynamic>())
  903 + {
  904 + var key = $"{item.TechDepartmentId}_{item.TeacherId}";
  905 + if (teacherDict.ContainsKey(key))
  906 + {
  907 + teacherDict[key].OrderAchievement = Convert.ToDecimal(item.OrderAchievement);
  908 + }
  909 + }
  910 +
  911 + var outputList = teacherDict.Values
  912 + .OrderBy(x => x.TechDepartmentName)
  913 + .ThenBy(x => x.TeacherName)
  914 + .ToList();
  915 +
  916 + return outputList;
  917 + }
  918 + #endregion
  919 + }
  920 +}
  921 +
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqEventService.cs
... ... @@ -800,6 +800,8 @@ namespace NCC.Extend.LqEvent
800 800 }
801 801 #endregion
802 802  
  803 +
  804 +
803 805 #region 根据活动ID获取人员统计数据
804 806  
805 807 /// <summary>
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs
... ... @@ -557,8 +557,8 @@ namespace NCC.Extend.LqPackageInfo
557 557 /// 获取营销活动统计数据
558 558 /// </summary>
559 559 /// <remarks>
560   - /// 统计指定营销活动的开单、退卡相关数据
561   - /// 包括:开单数量、开单金额、退卡数量、退卡金额、净开单数量、净开单金额、退卡率
  560 + /// 统计指定营销活动的开单数据,支持分页
  561 + /// 包括:总记录数、开单总金额、欠款总金额、开单列表
562 562 ///
563 563 /// 示例请求:
564 564 /// ```json
... ... @@ -566,7 +566,9 @@ namespace NCC.Extend.LqPackageInfo
566 566 /// "activityId": "营销活动ID",
567 567 /// "startTime": "2025-10-01",
568 568 /// "endTime": "2025-10-31",
569   - /// "storeIds": ["门店ID1", "门店ID2"]
  569 + /// "storeIds": ["门店ID1", "门店ID2"],
  570 + /// "pageIndex": 1,
  571 + /// "pageSize": 20
570 572 /// }
571 573 /// ```
572 574 ///
... ... @@ -575,17 +577,16 @@ namespace NCC.Extend.LqPackageInfo
575 577 /// - startTime: 开始时间(可选,默认为活动开始时间)
576 578 /// - endTime: 结束时间(可选,默认为活动结束时间)
577 579 /// - storeIds: 门店ID列表(可选)
  580 + /// - pageIndex: 页码(默认1)
  581 + /// - pageSize: 每页数量(默认20)
578 582 ///
579 583 /// 返回字段说明:
580 584 /// - ActivityId: 营销活动ID
581 585 /// - ActivityName: 营销活动名称
582 586 /// - BillingCount: 开单数量
583   - /// - BillingAmount: 开单金额
584   - /// - RefundCount: 退卡数量
585   - /// - RefundAmount: 退卡金额
586   - /// - NetBillingCount: 净开单数量(开单数量 - 退卡数量)
587   - /// - NetBillingAmount: 净开单金额(开单金额 - 退卡金额)
588   - /// - RefundRate: 退卡率(退卡数量 / 开单数量)
  587 + /// - BillingAmount: 开单总金额
  588 + /// - DebtAmount: 欠款总金额
  589 + /// - BillingList: 开单列表(包含开单ID、日期、客户信息、门店信息、金额、欠款等)
589 590 /// </remarks>
590 591 /// <param name="input">查询参数</param>
591 592 /// <returns>营销活动统计数据</returns>
... ... @@ -611,95 +612,78 @@ namespace NCC.Extend.LqPackageInfo
611 612 var startTime = input.StartTime ?? activity.StartTime;
612 613 var endTime = input.EndTime ?? activity.EndTime;
613 614  
614   - // 3. 获取营销活动关联的品项ID列表
615   - var itemIds = await _db.Queryable<LqPackageItemDetailEntity>()
616   - .Where(x => x.ActivityId == input.ActivityId && x.IsEffective == StatusEnum.有效.GetHashCode())
617   - .Select(x => x.ItemId)
618   - .ToListAsync();
619   -
620   - if (!itemIds.Any())
621   - {
622   - return new ActivityStatisticsOutput
623   - {
624   - ActivityId = input.ActivityId,
625   - ActivityName = activity.ActivityName,
626   - BillingCount = 0,
627   - BillingAmount = 0,
628   - RefundCount = 0,
629   - RefundAmount = 0,
630   - NetBillingCount = 0,
631   - NetBillingAmount = 0,
632   - RefundRate = 0
633   - };
634   - }
635   -
636   - // 4. 构建品项ID过滤条件
637   - var itemIdsStr = string.Join("','", itemIds);
638   - var itemIdsFilter = $"AND px.px IN ('{itemIdsStr}')";
639   - var itemIdsFilterRefund = $"AND hytkmx.px IN ('{itemIdsStr}')";
640   -
641   - // 5. 构建门店过滤条件
  615 + // 3. 构建门店过滤条件
642 616 string storeFilter = "";
643   - string storeFilterRefund = "";
644 617  
645 618 if (input.StoreIds != null && input.StoreIds.Any())
646 619 {
647 620 var storeIdsStr = string.Join("','", input.StoreIds);
648 621 storeFilter = $"AND kd.djmd IN ('{storeIdsStr}')";
649   - storeFilterRefund = $"AND hytk.md IN ('{storeIdsStr}')";
650 622 }
651 623  
652   - // 6. 开单统计
653   - var billingSql = $@"
  624 + // 4. 获取总记录数和总金额
  625 + var totalSql = $@"
654 626 SELECT
655   - COUNT(DISTINCT kd.F_Id) as billing_count,
656   - SUM(CAST(px.F_ActualPrice AS DECIMAL(18,2))) as billing_amount
657   - FROM lq_kd_pxmx px
658   - LEFT JOIN lq_kd_kdjlb kd ON px.glkdbh = kd.F_Id
659   - WHERE px.F_IsEffective = 1
660   - {itemIdsFilter}
661   - AND px.yjsj >= '{startTime:yyyy-MM-dd HH:mm:ss}'
662   - AND px.yjsj <= '{endTime:yyyy-MM-dd HH:mm:ss}'
  627 + COUNT(*) as total,
  628 + COALESCE(SUM(CAST(kd.sfyj AS DECIMAL(18,2))), 0) as billing_amount,
  629 + COALESCE(SUM(CAST(kd.qk AS DECIMAL(18,2))), 0) as debt_amount
  630 + FROM lq_kd_kdjlb kd
  631 + WHERE kd.F_IsEffective = 1
  632 + AND kd.F_ActivityId = '{input.ActivityId}'
  633 + AND kd.kdrq >= '{startTime:yyyy-MM-dd HH:mm:ss}'
  634 + AND kd.kdrq <= '{endTime:yyyy-MM-dd HH:mm:ss}'
663 635 {storeFilter}";
664 636  
665   - var billingData = await _db.Ado.SqlQueryAsync<dynamic>(billingSql);
666   - var billingCount = Convert.ToInt32(billingData.FirstOrDefault()?.billing_count ?? 0);
667   - var billingAmount = Convert.ToDecimal(billingData.FirstOrDefault()?.billing_amount ?? 0);
  637 + var totalData = await _db.Ado.SqlQueryAsync<dynamic>(totalSql);
  638 + var billingCount = Convert.ToInt32(totalData.FirstOrDefault()?.total ?? 0);
  639 + var billingAmount = Convert.ToDecimal(totalData.FirstOrDefault()?.billing_amount ?? 0);
  640 + var debtAmount = Convert.ToDecimal(totalData.FirstOrDefault()?.debt_amount ?? 0);
668 641  
669   - // 7. 退卡统计
670   - var refundSql = $@"
  642 + // 5. 分页查询开单列表
  643 + var offset = (input.PageIndex - 1) * input.PageSize;
  644 + var billingListSql = $@"
671 645 SELECT
672   - COUNT(DISTINCT hytk.F_Id) as refund_count,
673   - SUM(CAST(hytkmx.tkje AS DECIMAL(18,2))) as refund_amount
674   - FROM lq_hytk_mx hytkmx
675   - LEFT JOIN lq_hytk_hytk hytk ON hytkmx.F_RefundInfoId = hytk.F_Id
676   - WHERE hytkmx.F_IsEffective = 1
677   - {itemIdsFilterRefund}
678   - AND hytkmx.tksj >= '{startTime:yyyy-MM-dd HH:mm:ss}'
679   - AND hytkmx.tksj <= '{endTime:yyyy-MM-dd HH:mm:ss}'
680   - {storeFilterRefund}";
681   -
682   - var refundData = await _db.Ado.SqlQueryAsync<dynamic>(refundSql);
683   - var refundCount = Convert.ToInt32(refundData.FirstOrDefault()?.refund_count ?? 0);
684   - var refundAmount = Convert.ToDecimal(refundData.FirstOrDefault()?.refund_amount ?? 0);
685   -
686   - // 8. 计算净值和退卡率
687   - var netBillingCount = billingCount - refundCount;
688   - var netBillingAmount = billingAmount - refundAmount;
689   - var refundRate = billingCount > 0 ? Math.Round((decimal)refundCount / billingCount * 100, 2) : 0;
690   -
691   - // 9. 返回统计结果
  646 + kd.F_Id as BillingId,
  647 + kd.kdrq as BillingDate,
  648 + COALESCE(kh.khmc, '') as CustomerName,
  649 + COALESCE(kh.sjh, '') as CustomerPhone,
  650 + kd.djmd as StoreId,
  651 + COALESCE(md.dm, '') as StoreName,
  652 + CAST(kd.sfyj AS DECIMAL(18,2)) as Amount,
  653 + CAST(kd.qk AS DECIMAL(18,2)) as DebtAmount
  654 + FROM lq_kd_kdjlb kd
  655 + LEFT JOIN lq_khxx kh ON kd.kdhy = kh.F_Id
  656 + LEFT JOIN lq_mdxx md ON kd.djmd = md.F_Id
  657 + WHERE kd.F_IsEffective = 1
  658 + AND kd.F_ActivityId = '{input.ActivityId}'
  659 + AND kd.kdrq >= '{startTime:yyyy-MM-dd HH:mm:ss}'
  660 + AND kd.kdrq <= '{endTime:yyyy-MM-dd HH:mm:ss}'
  661 + {storeFilter}
  662 + ORDER BY kd.kdrq DESC
  663 + LIMIT {input.PageSize} OFFSET {offset}";
  664 +
  665 + var billingListData = await _db.Ado.SqlQueryAsync<dynamic>(billingListSql);
  666 + var billingList = billingListData?.Select(item => new ActivityBillingItem
  667 + {
  668 + BillingId = item.BillingId?.ToString(),
  669 + BillingDate = item.BillingDate != null ? (DateTime?)item.BillingDate : null,
  670 + CustomerName = item.CustomerName?.ToString(),
  671 + CustomerPhone = item.CustomerPhone?.ToString(),
  672 + StoreId = item.StoreId?.ToString(),
  673 + StoreName = item.StoreName?.ToString(),
  674 + Amount = Convert.ToDecimal(item.Amount ?? 0),
  675 + DebtAmount = Convert.ToDecimal(item.DebtAmount ?? 0)
  676 + }).ToList() ?? new List<ActivityBillingItem>();
  677 +
  678 + // 6. 返回统计结果
692 679 return new ActivityStatisticsOutput
693 680 {
694 681 ActivityId = input.ActivityId,
695 682 ActivityName = activity.ActivityName,
696 683 BillingCount = billingCount,
697 684 BillingAmount = billingAmount,
698   - RefundCount = refundCount,
699   - RefundAmount = refundAmount,
700   - NetBillingCount = netBillingCount,
701   - NetBillingAmount = netBillingAmount,
702   - RefundRate = refundRate
  685 + DebtAmount = debtAmount,
  686 + BillingList = billingList
703 687 };
704 688 }
705 689 catch (Exception ex)
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
... ... @@ -715,12 +715,17 @@ namespace NCC.Extend
715 715 /// - storeIds: 门店ID列表(可选)
716 716 ///
717 717 /// 返回字段说明:
718   - /// - TotalBillingAmount: 开单总金额
  718 + /// - TotalBillingAmount: 开单总金额(未扣除退卡)
719 719 /// - TotalConsumeAmount: 耗卡总金额
720 720 /// - TotalRefundAmount: 退卡总金额
721 721 /// - BillingCount: 开单人数(按客户去重)
722 722 /// - ConsumeCount: 耗卡人数(按客户去重)
723 723 /// - RefundCount: 退卡人数(按客户去重)
  724 + /// - TargetConsumeAmount: 消耗目标业绩(所有门店xhyj字段的总和)
  725 + /// - ConsumeCompletionRate: 消耗完成率(百分比)
  726 + /// - TargetBillingAmount: 开单目标业绩(所有门店xsyj字段的总和)
  727 + /// - CompletedBillingAmount: 开单完成业绩(开单总金额 - 退卡总金额)
  728 + /// - BillingCompletionRate: 开单完成率(百分比)
724 729 /// </remarks>
725 730 /// <param name="input">查询参数</param>
726 731 /// <returns>业务统计数据</returns>
... ... @@ -749,7 +754,7 @@ namespace NCC.Extend
749 754 object billingParameters;
750 755 if (input.StoreIds != null && input.StoreIds.Any())
751 756 {
752   - billingSql += " AND kd.F_StoreId IN @storeIds";
  757 + billingSql += " AND kd.djmd IN @storeIds";
753 758 billingParameters = new { startTime, endTime, storeIds = input.StoreIds };
754 759 }
755 760 else
... ... @@ -774,7 +779,7 @@ namespace NCC.Extend
774 779 object consumeParameters;
775 780 if (input.StoreIds != null && input.StoreIds.Any())
776 781 {
777   - consumeSql += " AND xh.F_StoreId IN @storeIds";
  782 + consumeSql += " AND xh.md IN @storeIds";
778 783 consumeParameters = new { startTime, endTime, storeIds = input.StoreIds };
779 784 }
780 785 else
... ... @@ -799,7 +804,7 @@ namespace NCC.Extend
799 804 object refundParameters;
800 805 if (input.StoreIds != null && input.StoreIds.Any())
801 806 {
802   - refundSql += " AND hytk.F_StoreId IN @storeIds";
  807 + refundSql += " AND hytk.md IN @storeIds";
803 808 refundParameters = new { startTime, endTime, storeIds = input.StoreIds };
804 809 }
805 810 else
... ... @@ -811,6 +816,50 @@ namespace NCC.Extend
811 816 var refundCount = Convert.ToInt32(refundResult?.FirstOrDefault()?.refund_count ?? 0);
812 817 var refundAmount = Convert.ToDecimal(refundResult?.FirstOrDefault()?.refund_amount ?? 0m);
813 818  
  819 + // 第四步:获取消耗目标业绩(所有门店xhyj字段的总和)
  820 + var targetConsumeSql = "SELECT COALESCE(SUM(CAST(md.xhyj AS DECIMAL(18,2))), 0) as target_consume_amount FROM lq_mdxx md WHERE 1=1";
  821 + object targetConsumeParameters = null;
  822 +
  823 + if (input.StoreIds != null && input.StoreIds.Any())
  824 + {
  825 + targetConsumeSql += " AND md.F_Id IN @storeIds";
  826 + targetConsumeParameters = new { storeIds = input.StoreIds };
  827 + }
  828 +
  829 + var targetConsumeResult = await _db.Ado.SqlQueryAsync<dynamic>(targetConsumeSql, targetConsumeParameters);
  830 + var targetConsumeAmount = Convert.ToDecimal(targetConsumeResult?.FirstOrDefault()?.target_consume_amount ?? 0m);
  831 +
  832 + // 第五步:获取开单目标业绩(所有门店xsyj字段的总和)
  833 + var targetBillingSql = "SELECT COALESCE(SUM(CAST(md.xsyj AS DECIMAL(18,2))), 0) as target_billing_amount FROM lq_mdxx md WHERE 1=1";
  834 + object targetBillingParameters = null;
  835 +
  836 + if (input.StoreIds != null && input.StoreIds.Any())
  837 + {
  838 + targetBillingSql += " AND md.F_Id IN @storeIds";
  839 + targetBillingParameters = new { storeIds = input.StoreIds };
  840 + }
  841 +
  842 + var targetBillingResult = await _db.Ado.SqlQueryAsync<dynamic>(targetBillingSql, targetBillingParameters);
  843 + var targetBillingAmount = Convert.ToDecimal(targetBillingResult?.FirstOrDefault()?.target_billing_amount ?? 0m);
  844 +
  845 + // 计算开单完成业绩(开单总金额 - 退卡总金额)
  846 + var completedBillingAmount = billingAmount - refundAmount;
  847 +
  848 + // 计算完成率
  849 + var consumeCompletionRate = 0m;
  850 + if (targetConsumeAmount > 0)
  851 + {
  852 + consumeCompletionRate = (consumeAmount / targetConsumeAmount) * 100m;
  853 + consumeCompletionRate = decimal.Round(consumeCompletionRate, 2);
  854 + }
  855 +
  856 + var billingCompletionRate = 0m;
  857 + if (targetBillingAmount > 0)
  858 + {
  859 + billingCompletionRate = (completedBillingAmount / targetBillingAmount) * 100m;
  860 + billingCompletionRate = decimal.Round(billingCompletionRate, 2);
  861 + }
  862 +
814 863 var result = new BusinessStatisticsOutput
815 864 {
816 865 TotalBillingAmount = billingAmount,
... ... @@ -818,7 +867,12 @@ namespace NCC.Extend
818 867 TotalRefundAmount = refundAmount,
819 868 BillingCount = billingCount,
820 869 ConsumeCount = consumeCount,
821   - RefundCount = refundCount
  870 + RefundCount = refundCount,
  871 + TargetConsumeAmount = targetConsumeAmount,
  872 + ConsumeCompletionRate = consumeCompletionRate,
  873 + TargetBillingAmount = targetBillingAmount,
  874 + CompletedBillingAmount = completedBillingAmount,
  875 + BillingCompletionRate = billingCompletionRate
822 876 };
823 877  
824 878 return result;
... ... @@ -878,6 +932,7 @@ namespace NCC.Extend
878 932 var endTime = input.EndTime ?? DateTime.Now;
879 933  
880 934 // 第一步:获取客户类型统计
  935 + // 注意:lq_khxx表没有门店字段,无法按门店过滤
881 936 var customerTypeSql = $@"
882 937 SELECT
883 938 SUM(CASE WHEN kh.khlx = '{MemberTypeEnum.线索.GetHashCode()}' THEN 1 ELSE 0 END) as lead_count,
... ... @@ -888,16 +943,7 @@ namespace NCC.Extend
888 943 WHERE kh.F_CreateTime >= @startTime
889 944 AND kh.F_CreateTime <= @endTime";
890 945  
891   - object customerTypeParameters;
892   - if (input.StoreIds != null && input.StoreIds.Any())
893   - {
894   - customerTypeSql += " AND kh.F_StoreId IN @storeIds";
895   - customerTypeParameters = new { startTime, endTime, storeIds = input.StoreIds };
896   - }
897   - else
898   - {
899   - customerTypeParameters = new { startTime, endTime };
900   - }
  946 + object customerTypeParameters = new { startTime, endTime };
901 947  
902 948 var customerTypeResult = await _db.Ado.SqlQueryAsync<dynamic>(customerTypeSql, customerTypeParameters);
903 949 var leadCount = Convert.ToInt32(customerTypeResult?.FirstOrDefault()?.lead_count ?? 0);
... ... @@ -938,7 +984,7 @@ namespace NCC.Extend
938 984 object consumeParameters;
939 985 if (input.StoreIds != null && input.StoreIds.Any())
940 986 {
941   - consumeSql += " AND xh.F_StoreId IN @storeIds";
  987 + consumeSql += " AND xh.md IN @storeIds";
942 988 consumeParameters = new { startTime, endTime, storeIds = input.StoreIds };
943 989 }
944 990 else
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
... ... @@ -3389,7 +3389,7 @@ namespace NCC.Extend.LqStatistics
3389 3389 /// </summary>
3390 3390 /// <remarks>
3391 3391 /// 根据员工ID和月份统计员工的完整业绩数据
3392   - /// 包括:拓客人数、邀约人数、预约人数、开单、消耗、退卡、人头、人次
  3392 + /// 包括:拓客人数、邀约人数、预约人数、开单、消耗、退卡、人头、人次、项目数
3393 3393 ///
3394 3394 /// 示例请求:
3395 3395 /// ```json
... ... @@ -3411,8 +3411,10 @@ namespace NCC.Extend.LqStatistics
3411 3411 /// - AppointmentCount: 预约人数
3412 3412 /// - BillingCount: 开单数量
3413 3413 /// - BillingAmount: 开单金额
  3414 + /// - BillingProjectCount: 开单项目数(项目次数总和)
3414 3415 /// - ConsumeCount: 消耗数量
3415 3416 /// - ConsumeAmount: 消耗金额
  3417 + /// - ConsumeProjectCount: 消耗项目数(项目次数总和)
3416 3418 /// - RefundCount: 退卡数量
3417 3419 /// - RefundAmount: 退卡金额
3418 3420 /// - HeadCount: 人头(月度去重客户数)
... ... @@ -3461,6 +3463,12 @@ namespace NCC.Extend.LqStatistics
3461 3463 // 8. 人次统计
3462 3464 var personCount = await GetPersonCount(input.UserId, statisticsMonth);
3463 3465  
  3466 + // 9. 开单项目数统计
  3467 + var billingProjectCount = await GetBillingProjectCount(input.UserId, statisticsMonth);
  3468 +
  3469 + // 10. 消耗项目数统计
  3470 + var consumeProjectCount = await GetConsumeProjectCount(input.UserId, statisticsMonth);
  3471 +
3464 3472 return new EmployeePerformanceStatisticsOutput
3465 3473 {
3466 3474 UserId = input.UserId,
... ... @@ -3475,7 +3483,9 @@ namespace NCC.Extend.LqStatistics
3475 3483 RefundCount = refundStats.Count,
3476 3484 RefundAmount = refundStats.Amount,
3477 3485 HeadCount = headCount,
3478   - PersonCount = personCount
  3486 + PersonCount = personCount,
  3487 + BillingProjectCount = billingProjectCount,
  3488 + ConsumeProjectCount = consumeProjectCount
3479 3489 };
3480 3490 }
3481 3491 catch (Exception ex)
... ... @@ -3625,6 +3635,42 @@ namespace NCC.Extend.LqStatistics
3625 3635 return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0);
3626 3636 }
3627 3637  
  3638 + /// <summary>
  3639 + /// 统计开单项目数(项目次数总和)
  3640 + /// </summary>
  3641 + private async Task<int> GetBillingProjectCount(string userId, string month)
  3642 + {
  3643 + var sql = $@"
  3644 + SELECT COALESCE(SUM(pxmx.F_ProjectNumber), 0) as Count
  3645 + FROM lq_kd_jksyj jksyj
  3646 + INNER JOIN lq_kd_pxmx pxmx ON jksyj.F_kdpxid = pxmx.F_Id
  3647 + WHERE jksyj.jkszh = '{userId}'
  3648 + AND jksyj.F_IsEffective = 1
  3649 + AND DATE_FORMAT(jksyj.yjsj, '%Y%m') = '{month}'";
  3650 +
  3651 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  3652 + return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0);
  3653 + }
  3654 +
  3655 + /// <summary>
  3656 + /// 统计消耗项目数(项目次数总和)
  3657 + /// </summary>
  3658 + private async Task<int> GetConsumeProjectCount(string userId, string month)
  3659 + {
  3660 + var sql = $@"
  3661 + SELECT COALESCE(SUM(pxmx.F_ProjectNumber), 0) as Count
  3662 + FROM lq_xh_jksyj jksyj
  3663 + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  3664 + INNER JOIN lq_xh_pxmx pxmx ON pxmx.F_ConsumeInfoId = hyhk.F_Id
  3665 + WHERE jksyj.jkszh = '{userId}'
  3666 + AND jksyj.F_IsEffective = 1
  3667 + AND hyhk.F_IsEffective = 1
  3668 + AND DATE_FORMAT(hyhk.hksj, '%Y%m') = '{month}'";
  3669 +
  3670 + var result = await _db.Ado.SqlQueryAsync<dynamic>(sql);
  3671 + return Convert.ToInt32(result.FirstOrDefault()?.Count ?? 0);
  3672 + }
  3673 +
3628 3674 #endregion
3629 3675  
3630 3676 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
... ... @@ -960,5 +960,159 @@ namespace NCC.Extend.LqTkjlb
960 960 }
961 961 #endregion
962 962  
  963 + #region 拓客统计数据
  964 + /// <summary>
  965 + /// 获取拓客统计数据
  966 + /// </summary>
  967 + /// <remarks>
  968 + /// 统计拓客相关的各项数据,包括拓客人数、邀约人头、预约人头、到店人头、开单人头、开单金额、消费人头、消费金额
  969 + ///
  970 + /// 示例请求:
  971 + /// ```json
  972 + /// {
  973 + /// "startTime": "2025-10-01",
  974 + /// "endTime": "2025-10-31",
  975 + /// "eventId": "活动ID"
  976 + /// }
  977 + /// ```
  978 + ///
  979 + /// 参数说明:
  980 + /// - startTime: 开始时间(可选)
  981 + /// - endTime: 结束时间(可选)
  982 + /// - eventId: 活动ID(可选)
  983 + ///
  984 + /// 返回字段说明:
  985 + /// - TkCount: 拓客人数
  986 + /// - YaoyCount: 邀约人头
  987 + /// - YyCount: 预约人头
  988 + /// - DdCount: 到店人头
  989 + /// - KdCount: 开单人头
  990 + /// - KdAmount: 开单金额
  991 + /// - XfCount: 消费人头
  992 + /// - XfAmount: 消费金额
  993 + /// </remarks>
  994 + /// <param name="input">查询参数</param>
  995 + /// <returns>拓客统计数据</returns>
  996 + /// <response code="200">成功返回统计数据</response>
  997 + /// <response code="400">参数错误</response>
  998 + /// <response code="500">服务器内部错误</response>
  999 + [HttpPost("get-tk-statistics")]
  1000 + public async Task<TkStatisticsOutput> GetTkStatistics(TkStatisticsInput input)
  1001 + {
  1002 + try
  1003 + {
  1004 + // 构建基本过滤条件
  1005 + string timeFilter = "";
  1006 + if (input.StartTime.HasValue && input.EndTime.HasValue)
  1007 + {
  1008 + timeFilter = $@"
  1009 + AND tk.F_CreateTime >= '{input.StartTime:yyyy-MM-dd HH:mm:ss}'
  1010 + AND tk.F_CreateTime <= '{input.EndTime:yyyy-MM-dd HH:mm:ss}'";
  1011 + }
  1012 + else if (input.StartTime.HasValue)
  1013 + {
  1014 + timeFilter = $"AND tk.F_CreateTime >= '{input.StartTime:yyyy-MM-dd HH:mm:ss}'";
  1015 + }
  1016 + else if (input.EndTime.HasValue)
  1017 + {
  1018 + timeFilter = $"AND tk.F_CreateTime <= '{input.EndTime:yyyy-MM-dd HH:mm:ss}'";
  1019 + }
  1020 +
  1021 + string eventFilter = "";
  1022 + if (!string.IsNullOrWhiteSpace(input.EventId))
  1023 + {
  1024 + eventFilter = $"AND tk.F_EventId = '{input.EventId}'";
  1025 + }
  1026 +
  1027 + // 第一步:获取拓客人数(去重会员ID)
  1028 + var tkSql = $@"
  1029 + SELECT COUNT(DISTINCT tk.F_MemberId) as tk_count
  1030 + FROM lq_tkjlb tk
  1031 + WHERE 1=1 {timeFilter} {eventFilter}";
  1032 +
  1033 + var tkResult = await _db.Ado.SqlQueryAsync<dynamic>(tkSql);
  1034 + var tkCount = Convert.ToInt32(tkResult?.FirstOrDefault()?.tk_count ?? 0);
  1035 +
  1036 + // 第二步:获取邀约人头(去重会员ID)
  1037 + var yaoySql = $@"
  1038 + SELECT COUNT(DISTINCT tk.F_MemberId) as yaoy_count
  1039 + FROM lq_tkjlb tk
  1040 + INNER JOIN lq_yaoyjl yy ON tk.F_MemberId = yy.yykh
  1041 + AND yy.F_StoreId = tk.F_StoreId
  1042 + WHERE 1=1 {timeFilter} {eventFilter}";
  1043 +
  1044 + var yaoyResult = await _db.Ado.SqlQueryAsync<dynamic>(yaoySql);
  1045 + var yaoyCount = Convert.ToInt32(yaoyResult?.FirstOrDefault()?.yaoy_count ?? 0);
  1046 +
  1047 + // 第三步:获取预约人头(去重会员ID)
  1048 + var yySql = $@"
  1049 + SELECT COUNT(DISTINCT tk.F_MemberId) as yy_count
  1050 + FROM lq_tkjlb tk
  1051 + INNER JOIN lq_yyjl yyjl ON tk.F_MemberId = yyjl.gk
  1052 + AND yyjl.F_Status = '已确认'
  1053 + WHERE 1=1 {timeFilter} {eventFilter}";
  1054 +
  1055 + var yyResult = await _db.Ado.SqlQueryAsync<dynamic>(yySql);
  1056 + var yyCount = Convert.ToInt32(yyResult?.FirstOrDefault()?.yy_count ?? 0);
  1057 +
  1058 + // 第四步:获取到店人头(预约且状态为已确认,假设预约表有到店时间字段)
  1059 + var ddSql = $@"
  1060 + SELECT COUNT(DISTINCT tk.F_MemberId) as dd_count
  1061 + FROM lq_tkjlb tk
  1062 + INNER JOIN lq_yyjl yyjl ON tk.F_MemberId = yyjl.gk
  1063 + AND yyjl.F_Status = '已确认'
  1064 + WHERE yyjl.yysj <= NOW()
  1065 + AND 1=1 {timeFilter} {eventFilter}";
  1066 +
  1067 + var ddResult = await _db.Ado.SqlQueryAsync<dynamic>(ddSql);
  1068 + var ddCount = Convert.ToInt32(ddResult?.FirstOrDefault()?.dd_count ?? 0);
  1069 +
  1070 + // 第五步:获取开单人头和金额(去重会员ID,金额累加)
  1071 + var kdSql = $@"
  1072 + SELECT
  1073 + COUNT(DISTINCT tk.F_MemberId) as kd_count,
  1074 + COALESCE(SUM(kd.sfyj), 0) as kd_amount
  1075 + FROM lq_tkjlb tk
  1076 + INNER JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy
  1077 + AND kd.F_IsEffective = 1
  1078 + WHERE 1=1 {timeFilter} {eventFilter}";
  1079 +
  1080 + var kdResult = await _db.Ado.SqlQueryAsync<dynamic>(kdSql);
  1081 + var kdCount = Convert.ToInt32(kdResult?.FirstOrDefault()?.kd_count ?? 0);
  1082 + var kdAmount = Convert.ToDecimal(kdResult?.FirstOrDefault()?.kd_amount ?? 0);
  1083 +
  1084 + // 第六步:获取消费人头和金额(去重会员ID,金额累加)
  1085 + var xfSql = $@"
  1086 + SELECT
  1087 + COUNT(DISTINCT tk.F_MemberId) as xf_count,
  1088 + COALESCE(SUM(xh.xfje), 0) as xf_amount
  1089 + FROM lq_tkjlb tk
  1090 + INNER JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy
  1091 + AND xh.F_IsEffective = 1
  1092 + WHERE 1=1 {timeFilter} {eventFilter}";
  1093 +
  1094 + var xfResult = await _db.Ado.SqlQueryAsync<dynamic>(xfSql);
  1095 + var xfCount = Convert.ToInt32(xfResult?.FirstOrDefault()?.xf_count ?? 0);
  1096 + var xfAmount = Convert.ToDecimal(xfResult?.FirstOrDefault()?.xf_amount ?? 0);
  1097 +
  1098 + return new TkStatisticsOutput
  1099 + {
  1100 + TkCount = tkCount,
  1101 + YaoyCount = yaoyCount,
  1102 + YyCount = yyCount,
  1103 + DdCount = ddCount,
  1104 + KdCount = kdCount,
  1105 + KdAmount = kdAmount,
  1106 + XfCount = xfCount,
  1107 + XfAmount = xfAmount
  1108 + };
  1109 + }
  1110 + catch (Exception ex)
  1111 + {
  1112 + throw NCCException.Oh($"获取拓客统计数据失败: {ex.Message}");
  1113 + }
  1114 + }
  1115 + #endregion
  1116 +
963 1117 }
964 1118 }
... ...