Commit f7dd1d1b90d40801ab485b75588f44a21baf3f0a

Authored by 李宇
2 parents 5b6e99a5 7855bf04

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

Showing 28 changed files with 1703 additions and 65 deletions
antis-ncc-admin/src/views/lqInventory/InventoryInfoDialog.vue
@@ -191,4 +191,3 @@ export default { @@ -191,4 +191,3 @@ export default {
191 text-align: right; 191 text-align: right;
192 } 192 }
193 </style> 193 </style>
194 -  
antis-ncc-admin/src/views/personalPerformanceStatistics/index.vue
@@ -50,7 +50,7 @@ @@ -50,7 +50,7 @@
50 <el-table-column prop="EmployeeName" label="员工姓名" width="120" fixed="left"></el-table-column> 50 <el-table-column prop="EmployeeName" label="员工姓名" width="120" fixed="left"></el-table-column>
51 <el-table-column prop="StoreName" label="门店名称" width="150" fixed="left"></el-table-column> 51 <el-table-column prop="StoreName" label="门店名称" width="150" fixed="left"></el-table-column>
52 <el-table-column prop="Position" label="岗位" width="100" fixed="left"></el-table-column> 52 <el-table-column prop="Position" label="岗位" width="100" fixed="left"></el-table-column>
53 - <el-table-column prop="GoldTriangleTeam" label="金三角战队" width="120" fixed="left"></el-table-column> 53 + <el-table-column prop="GoldTriangleName" label="金三角战队" width="120" fixed="left"></el-table-column>
54 <el-table-column prop="TotalPerformance" label="总业绩" width="100" align="right"> 54 <el-table-column prop="TotalPerformance" label="总业绩" width="100" align="right">
55 <template slot-scope="scope"> 55 <template slot-scope="scope">
56 {{ formatMoney(scope.row.TotalPerformance) }} 56 {{ formatMoney(scope.row.TotalPerformance) }}
@@ -66,6 +66,22 @@ @@ -66,6 +66,22 @@
66 {{ formatMoney(scope.row.UpgradeOrderPerformance) }} 66 {{ formatMoney(scope.row.UpgradeOrderPerformance) }}
67 </template> 67 </template>
68 </el-table-column> 68 </el-table-column>
  69 + <el-table-column prop="RefundPerformance" label="退单业绩" width="100" align="right">
  70 + <template slot-scope="scope">
  71 + <span style="color: #F56C6C;">{{ formatMoney(scope.row.RefundPerformance) }}</span>
  72 + </template>
  73 + </el-table-column>
  74 + <el-table-column prop="RefundCount" label="退单次数" width="100" align="right">
  75 + <template slot-scope="scope">
  76 + <span style="color: #F56C6C;">{{ scope.row.RefundCount || 0 }}</span>
  77 + </template>
  78 + </el-table-column>
  79 + <el-table-column prop="ActualPerformance" label="实际业绩" width="100" align="right">
  80 + <template slot-scope="scope">
  81 + <span style="color: #67C23A; font-weight: bold;">{{ formatMoney(scope.row.ActualPerformance)
  82 + }}</span>
  83 + </template>
  84 + </el-table-column>
69 <el-table-column prop="FirstOrderCount" label="首开单数量" width="100" align="right"></el-table-column> 85 <el-table-column prop="FirstOrderCount" label="首开单数量" width="100" align="right"></el-table-column>
70 <el-table-column prop="UpgradeOrderCount" label="升单数量" width="100" align="right"></el-table-column> 86 <el-table-column prop="UpgradeOrderCount" label="升单数量" width="100" align="right"></el-table-column>
71 <el-table-column prop="OrderCount" label="订单总数" width="100" align="right"></el-table-column> 87 <el-table-column prop="OrderCount" label="订单总数" width="100" align="right"></el-table-column>
antis-ncc-admin/src/views/storeTotalPerformanceStatistics/index.vue
@@ -81,6 +81,12 @@ @@ -81,6 +81,12 @@
81 <span style="color: #F56C6C;">{{ scope.row.RefundCount || 0 }}</span> 81 <span style="color: #F56C6C;">{{ scope.row.RefundCount || 0 }}</span>
82 </template> 82 </template>
83 </el-table-column> 83 </el-table-column>
  84 + <el-table-column prop="ActualPerformance" label="实际业绩" width="120" align="right">
  85 + <template slot-scope="scope">
  86 + <span style="color: #67C23A; font-weight: bold;">{{ formatMoney(scope.row.ActualPerformance)
  87 + }}</span>
  88 + </template>
  89 + </el-table-column>
84 <el-table-column prop="CreateTime" label="创建时间" width="150" align="center"> 90 <el-table-column prop="CreateTime" label="创建时间" width="150" align="center">
85 <template slot-scope="scope"> 91 <template slot-scope="scope">
86 {{ formatDateTime(scope.row.CreateTime) }} 92 {{ formatDateTime(scope.row.CreateTime) }}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
  2 +{
  3 + /// <summary>
  4 + /// 健康师统计输出
  5 + /// </summary>
  6 + public class HealthCoachStatisticsOutput
  7 + {
  8 + /// <summary>
  9 + /// 健康师ID
  10 + /// </summary>
  11 + public string employeeId { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 健康师姓名
  15 + /// </summary>
  16 + public string employeeName { get; set; }
  17 +
  18 + /// <summary>
  19 + /// 门店ID
  20 + /// </summary>
  21 + public string storeId { get; set; }
  22 +
  23 + /// <summary>
  24 + /// 门店名称
  25 + /// </summary>
  26 + public string storeName { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 事业部ID
  30 + /// </summary>
  31 + public string departmentId { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 事业部名称
  35 + /// </summary>
  36 + public string departmentName { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 邀约人数 - 统计该健康师在指定时间周期内邀约的客户数量(按客户去重)
  40 + /// </summary>
  41 + public int inviteCount { get; set; }
  42 +
  43 + /// <summary>
  44 + /// 预约人数 - 统计该健康师在指定时间周期内预约的客户数量(按客户去重,无论预约状态)
  45 + /// </summary>
  46 + public int appointmentCount { get; set; }
  47 +
  48 + /// <summary>
  49 + /// 到店人数 - 统计该健康师在指定时间周期内已确认到店的客户数量(按客户去重,仅统计状态为'已确认'的预约)
  50 + /// </summary>
  51 + public int visitCount { get; set; }
  52 +
  53 + /// <summary>
  54 + /// 开单人数 - 统计该健康师在指定时间周期内开单的客户数量(按开单记录去重)
  55 + /// </summary>
  56 + public int billingCount { get; set; }
  57 +
  58 + /// <summary>
  59 + /// 开单金额 - 统计该健康师在指定时间周期内的开单业绩总金额
  60 + /// </summary>
  61 + public decimal billingAmount { get; set; }
  62 +
  63 + /// <summary>
  64 + /// 消耗金额 - 统计该健康师在指定时间周期内的消耗业绩总金额
  65 + /// </summary>
  66 + public decimal consumeAmount { get; set; }
  67 +
  68 + /// <summary>
  69 + /// 人头 - 统计该健康师在指定时间周期内服务的唯一客户数量(按客户去重)
  70 + /// </summary>
  71 + public int headCount { get; set; }
  72 +
  73 + /// <summary>
  74 + /// 人次 - 统计该健康师在指定时间周期内服务的客户人次(按客户+日期去重,同一客户不同天算多次)
  75 + /// </summary>
  76 + public int personCount { get; set; }
  77 +
  78 + /// <summary>
  79 + /// 消耗项目数 - 统计该健康师在指定时间周期内消耗的项目总次数
  80 + /// </summary>
  81 + public int projectCount { get; set; }
  82 + }
  83 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsQueryInput.cs 0 → 100644
  1 +using NCC.Common.Filter;
  2 +using System;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
  5 +{
  6 + /// <summary>
  7 + /// 健康师统计查询输入
  8 + /// </summary>
  9 + public class HealthCoachStatisticsQueryInput : PageInputBase
  10 + {
  11 + /// <summary>
  12 + /// 开始时间
  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 DepartmentId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 门店ID
  28 + /// </summary>
  29 + public string StoreId { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 健康师姓名
  33 + /// </summary>
  34 + public string EmployeeName { get; set; }
  35 + }
  36 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdxx/LqMdxxInfoOutput.cs
@@ -123,21 +123,21 @@ namespace NCC.Extend.Entitys.Dto.LqMdxx @@ -123,21 +123,21 @@ namespace NCC.Extend.Entitys.Dto.LqMdxx
123 /// <summary> 123 /// <summary>
124 /// 门店类别 124 /// 门店类别
125 /// </summary> 125 /// </summary>
126 - public int? StoreCategory { get; set; } 126 + public int? storeCategory { get; set; }
127 127
128 /// <summary> 128 /// <summary>
129 /// 门店类别名称 129 /// 门店类别名称
130 /// </summary> 130 /// </summary>
131 - public string StoreCategoryName => StoreCategory.HasValue ? EnumHelper.GetEnumDesc<StoreCategoryEnum>(StoreCategory.Value) : string.Empty; 131 + public string storeCategoryName => storeCategory.HasValue ? EnumHelper.GetEnumDesc<StoreCategoryEnum>(storeCategory.Value) : string.Empty;
132 132
133 /// <summary> 133 /// <summary>
134 /// 门店类型 134 /// 门店类型
135 /// </summary> 135 /// </summary>
136 - public int? StoreType { get; set; } 136 + public int? storeType { get; set; }
137 137
138 /// <summary> 138 /// <summary>
139 /// 门店类型名称 139 /// 门店类型名称
140 /// </summary> 140 /// </summary>
141 - public string StoreTypeName => StoreType.HasValue ? EnumHelper.GetEnumDesc<StoreTypeEnum>(StoreType.Value) : string.Empty; 141 + public string storeTypeName => storeType.HasValue ? EnumHelper.GetEnumDesc<StoreTypeEnum>(storeType.Value) : string.Empty;
142 } 142 }
143 } 143 }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqMdxx/LqMdxxListOutput.cs
@@ -152,21 +152,21 @@ namespace NCC.Extend.Entitys.Dto.LqMdxx @@ -152,21 +152,21 @@ namespace NCC.Extend.Entitys.Dto.LqMdxx
152 /// <summary> 152 /// <summary>
153 /// 门店类别 153 /// 门店类别
154 /// </summary> 154 /// </summary>
155 - public int? StoreCategory { get; set; } 155 + public int? storeCategory { get; set; }
156 156
157 /// <summary> 157 /// <summary>
158 /// 门店类别名称 158 /// 门店类别名称
159 /// </summary> 159 /// </summary>
160 - public string StoreCategoryName => StoreCategory.HasValue ? EnumHelper.GetEnumDesc<StoreCategoryEnum>(StoreCategory.Value) : string.Empty; 160 + public string storeCategoryName => storeCategory.HasValue ? EnumHelper.GetEnumDesc<StoreCategoryEnum>(storeCategory.Value) : string.Empty;
161 161
162 /// <summary> 162 /// <summary>
163 /// 门店类型 163 /// 门店类型
164 /// </summary> 164 /// </summary>
165 - public int? StoreType { get; set; } 165 + public int? storeType { get; set; }
166 166
167 /// <summary> 167 /// <summary>
168 /// 门店类型名称 168 /// 门店类型名称
169 /// </summary> 169 /// </summary>
170 - public string StoreTypeName => StoreType.HasValue ? EnumHelper.GetEnumDesc<StoreTypeEnum>(StoreType.Value) : string.Empty; 170 + public string storeTypeName => storeType.HasValue ? EnumHelper.GetEnumDesc<StoreTypeEnum>(storeType.Value) : string.Empty;
171 } 171 }
172 } 172 }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +using System.ComponentModel.DataAnnotations;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqPackageInfo
  5 +{
  6 + /// <summary>
  7 + /// 营销活动统计查询输入参数
  8 + /// </summary>
  9 + public class ActivityStatisticsInput
  10 + {
  11 + /// <summary>
  12 + /// 营销活动ID
  13 + /// </summary>
  14 + [Required(ErrorMessage = "营销活动ID不能为空")]
  15 + public string ActivityId { get; set; }
  16 +
  17 + /// <summary>
  18 + /// 开始时间(可选,默认为活动开始时间)
  19 + /// </summary>
  20 + public DateTime? StartTime { get; set; }
  21 +
  22 + /// <summary>
  23 + /// 结束时间(可选,默认为活动结束时间)
  24 + /// </summary>
  25 + public DateTime? EndTime { get; set; }
  26 +
  27 + /// <summary>
  28 + /// 门店ID列表(可选)
  29 + /// </summary>
  30 + public string[] StoreIds { get; set; }
  31 + }
  32 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsOutput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqPackageInfo
  4 +{
  5 + /// <summary>
  6 + /// 营销活动统计输出结果
  7 + /// </summary>
  8 + public class ActivityStatisticsOutput
  9 + {
  10 + /// <summary>
  11 + /// 营销活动ID
  12 + /// </summary>
  13 + public string ActivityId { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 营销活动名称
  17 + /// </summary>
  18 + public string ActivityName { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 开单数量
  22 + /// </summary>
  23 + public int BillingCount { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 开单金额
  27 + /// </summary>
  28 + public decimal BillingAmount { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 退卡数量
  32 + /// </summary>
  33 + public int RefundCount { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 退卡金额
  37 + /// </summary>
  38 + public decimal RefundAmount { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 净开单数量(开单数量 - 退卡数量)
  42 + /// </summary>
  43 + public int NetBillingCount { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 净开单金额(开单金额 - 退卡金额)
  47 + /// </summary>
  48 + public decimal NetBillingAmount { get; set; }
  49 +
  50 + /// <summary>
  51 + /// 退卡率(退卡数量 / 开单数量)
  52 + /// </summary>
  53 + public decimal RefundRate { get; set; }
  54 + }
  55 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/BusinessStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqReport
  4 +{
  5 + /// <summary>
  6 + /// 业务统计查询输入
  7 + /// </summary>
  8 + public class BusinessStatisticsInput
  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[] StoreIds { get; set; }
  24 + }
  25 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/BusinessStatisticsOutput.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqReport
  2 +{
  3 + /// <summary>
  4 + /// 业务统计输出
  5 + /// </summary>
  6 + public class BusinessStatisticsOutput
  7 + {
  8 + /// <summary>
  9 + /// 开单总金额
  10 + /// </summary>
  11 + public decimal TotalBillingAmount { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 耗卡总金额
  15 + /// </summary>
  16 + public decimal TotalConsumeAmount { get; set; }
  17 +
  18 + /// <summary>
  19 + /// 退卡总金额
  20 + /// </summary>
  21 + public decimal TotalRefundAmount { get; set; }
  22 +
  23 + /// <summary>
  24 + /// 开单人数
  25 + /// </summary>
  26 + public int BillingCount { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 耗卡人数
  30 + /// </summary>
  31 + public int ConsumeCount { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 退卡人数
  35 + /// </summary>
  36 + public int RefundCount { get; set; }
  37 + }
  38 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/CustomerTypeStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqReport
  4 +{
  5 + /// <summary>
  6 + /// 客户类型统计查询输入
  7 + /// </summary>
  8 + public class CustomerTypeStatisticsInput
  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[] StoreIds { get; set; }
  24 + }
  25 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/CustomerTypeStatisticsOutput.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqReport
  2 +{
  3 + /// <summary>
  4 + /// 客户类型统计输出
  5 + /// </summary>
  6 + public class CustomerTypeStatisticsOutput
  7 + {
  8 + /// <summary>
  9 + /// 线索客户数量
  10 + /// </summary>
  11 + public int LeadCount { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 新客数量
  15 + /// </summary>
  16 + public int NewCustomerCount { get; set; }
  17 +
  18 + /// <summary>
  19 + /// 散客数量
  20 + /// </summary>
  21 + public int CasualCustomerCount { get; set; }
  22 +
  23 + /// <summary>
  24 + /// 会员数量
  25 + /// </summary>
  26 + public int MemberCount { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 拓客总人数
  30 + /// </summary>
  31 + public int TotalInviteCount { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 消耗人数(有消耗金额的)
  35 + /// </summary>
  36 + public int ConsumeCount { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 转化率(消耗人数/拓客人数)
  40 + /// </summary>
  41 + public decimal ConversionRate { get; set; }
  42 + }
  43 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/ItemStatisticsInput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqReport
  4 +{
  5 + /// <summary>
  6 + /// 品项统计查询输入
  7 + /// </summary>
  8 + public class ItemStatisticsInput
  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[] StoreIds { get; set; }
  24 + }
  25 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/ItemStatisticsOutput.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqReport
  2 +{
  3 + /// <summary>
  4 + /// 品项统计输出
  5 + /// </summary>
  6 + public class ItemStatisticsOutput
  7 + {
  8 + /// <summary>
  9 + /// 品项ID
  10 + /// </summary>
  11 + public string ItemId { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 品项名称
  15 + /// </summary>
  16 + public string ItemName { get; set; }
  17 +
  18 + /// <summary>
  19 + /// 品项编号
  20 + /// </summary>
  21 + public string ItemNumber { get; set; }
  22 +
  23 + /// <summary>
  24 + /// 开单数量
  25 + /// </summary>
  26 + public int BillingCount { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 开单金额
  30 + /// </summary>
  31 + public decimal BillingAmount { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 消耗数量
  35 + /// </summary>
  36 + public int ConsumeCount { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 消耗金额
  40 + /// </summary>
  41 + public decimal ConsumeAmount { get; set; }
  42 +
  43 + /// <summary>
  44 + /// 退卡数量
  45 + /// </summary>
  46 + public int RefundCount { get; set; }
  47 +
  48 + /// <summary>
  49 + /// 退卡金额
  50 + /// </summary>
  51 + public decimal RefundAmount { get; set; }
  52 + }
  53 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/StorePerformanceComparisonInput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqReport
  4 +{
  5 + /// <summary>
  6 + /// 门店业绩对比统计查询输入
  7 + /// </summary>
  8 + public class StorePerformanceComparisonInput
  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[] StoreIds { get; set; }
  24 + }
  25 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqReport/StorePerformanceComparisonOutput.cs 0 → 100644
  1 +namespace NCC.Extend.Entitys.Dto.LqReport
  2 +{
  3 + /// <summary>
  4 + /// 门店业绩对比统计输出
  5 + /// </summary>
  6 + public class StorePerformanceComparisonOutput
  7 + {
  8 + /// <summary>
  9 + /// 门店ID
  10 + /// </summary>
  11 + public string StoreId { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 门店名称
  15 + /// </summary>
  16 + public string StoreName { get; set; }
  17 +
  18 + /// <summary>
  19 + /// 目标业绩
  20 + /// </summary>
  21 + public decimal TargetPerformance { get; set; }
  22 +
  23 + /// <summary>
  24 + /// 实际开单业绩
  25 + /// </summary>
  26 + public decimal ActualPerformance { get; set; }
  27 +
  28 + /// <summary>
  29 + /// 完成率(实际业绩/目标业绩)
  30 + /// </summary>
  31 + public decimal CompletionRate { get; set; }
  32 +
  33 + /// <summary>
  34 + /// 差额(实际业绩-目标业绩)
  35 + /// </summary>
  36 + public decimal Difference { get; set; }
  37 +
  38 + /// <summary>
  39 + /// 是否达标(实际业绩 >= 目标业绩)
  40 + /// </summary>
  41 + public bool IsTargetAchieved { get; set; }
  42 + }
  43 +}
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatistics/LqStoreTotalPerformanceStatisticsListOutput.cs
@@ -73,6 +73,11 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics @@ -73,6 +73,11 @@ namespace NCC.Extend.Entitys.Dto.LqStatistics
73 public int RefundCount { get; set; } 73 public int RefundCount { get; set; }
74 74
75 /// <summary> 75 /// <summary>
  76 + /// 实际业绩
  77 + /// </summary>
  78 + public decimal ActualPerformance { get; set; }
  79 +
  80 + /// <summary>
76 /// 创建时间 81 /// 创建时间
77 /// </summary> 82 /// </summary>
78 public DateTime CreateTime { get; set; } 83 public DateTime CreateTime { get; set; }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatisticsPersonalPerformance/LqStatisticsPersonalPerformanceListOutput.cs
@@ -93,6 +93,21 @@ namespace NCC.Extend.Entitys.Dto.LqStatisticsPersonalPerformance @@ -93,6 +93,21 @@ namespace NCC.Extend.Entitys.Dto.LqStatisticsPersonalPerformance
93 public decimal UpgradeOrderPerformance { get; set; } 93 public decimal UpgradeOrderPerformance { get; set; }
94 94
95 /// <summary> 95 /// <summary>
  96 + /// 退单业绩
  97 + /// </summary>
  98 + public decimal RefundPerformance { get; set; }
  99 +
  100 + /// <summary>
  101 + /// 退单次数
  102 + /// </summary>
  103 + public int RefundCount { get; set; }
  104 +
  105 + /// <summary>
  106 + /// 实际业绩
  107 + /// </summary>
  108 + public decimal ActualPerformance { get; set; }
  109 +
  110 + /// <summary>
96 /// 最后订单日期 111 /// 最后订单日期
97 /// </summary> 112 /// </summary>
98 public DateTime? LastOrderDate { get; set; } 113 public DateTime? LastOrderDate { get; set; }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqStatisticsStoreTotalPerformance/LqStatisticsStoreTotalPerformanceListOutput.cs
@@ -86,5 +86,10 @@ namespace NCC.Extend.Entitys.Dto.LqStatisticsStoreTotalPerformance @@ -86,5 +86,10 @@ namespace NCC.Extend.Entitys.Dto.LqStatisticsStoreTotalPerformance
86 /// 创建时间 86 /// 创建时间
87 /// </summary> 87 /// </summary>
88 public DateTime? createTime { get; set; } 88 public DateTime? createTime { get; set; }
  89 +
  90 + /// <summary>
  91 + /// 实际业绩
  92 + /// </summary>
  93 + public decimal actualPerformance { get; set; }
89 } 94 }
90 } 95 }
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_statistics_personal_performance/LqStatisticsPersonalPerformanceEntity.cs
@@ -114,6 +114,24 @@ namespace NCC.Extend.Entitys.lq_statistics_personal_performance @@ -114,6 +114,24 @@ namespace NCC.Extend.Entitys.lq_statistics_personal_performance
114 public decimal UpgradeOrderPerformance { get; set; } 114 public decimal UpgradeOrderPerformance { get; set; }
115 115
116 /// <summary> 116 /// <summary>
  117 + /// 退单业绩
  118 + /// </summary>
  119 + [SugarColumn(ColumnName = "F_RefundPerformance")]
  120 + public decimal RefundPerformance { get; set; }
  121 +
  122 + /// <summary>
  123 + /// 退单次数
  124 + /// </summary>
  125 + [SugarColumn(ColumnName = "F_RefundCount")]
  126 + public int RefundCount { get; set; }
  127 +
  128 + /// <summary>
  129 + /// 实际业绩
  130 + /// </summary>
  131 + [SugarColumn(ColumnName = "F_ActualPerformance")]
  132 + public decimal ActualPerformance { get; set; }
  133 +
  134 + /// <summary>
117 /// 最后订单日期 135 /// 最后订单日期
118 /// </summary> 136 /// </summary>
119 [SugarColumn(ColumnName = "F_LastOrderDate")] 137 [SugarColumn(ColumnName = "F_LastOrderDate")]
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_statistics_store_total_performance/LqStatisticsStoreTotalPerformanceEntity.cs
@@ -102,6 +102,12 @@ namespace NCC.Extend.Entitys.lq_statistics_store_total_performance @@ -102,6 +102,12 @@ namespace NCC.Extend.Entitys.lq_statistics_store_total_performance
102 public int RefundCount { get; set; } 102 public int RefundCount { get; set; }
103 103
104 /// <summary> 104 /// <summary>
  105 + /// 实际业绩
  106 + /// </summary>
  107 + [SugarColumn(ColumnName = "F_ActualPerformance")]
  108 + public decimal ActualPerformance { get; set; }
  109 +
  110 + /// <summary>
105 /// 创建时间 111 /// 创建时间
106 /// </summary> 112 /// </summary>
107 [SugarColumn(ColumnName = "F_CreateTime")] 113 [SugarColumn(ColumnName = "F_CreateTime")]
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
@@ -1179,6 +1179,25 @@ namespace NCC.Extend.LqKdKdjlb @@ -1179,6 +1179,25 @@ namespace NCC.Extend.LqKdKdjlb
1179 } 1179 }
1180 #endregion 1180 #endregion
1181 1181
  1182 + #region 获取状态枚举内容(所有的状态通用)
  1183 + /// <summary>
  1184 + /// 获取状态枚举内容
  1185 + /// </summary>
  1186 + /// <returns>状态枚举列表</returns>
  1187 + [HttpGet("status-enum")]
  1188 + public List<EnumOutput> GetStatusEnum()
  1189 + {
  1190 + return Enum.GetValues<StatusEnum>()
  1191 + .Select(e => new EnumOutput
  1192 + {
  1193 + Value = (int)e,
  1194 + Name = e.ToString(),
  1195 + Description = e.GetDescription(),
  1196 + })
  1197 + .ToList();
  1198 + }
  1199 + #endregion
  1200 +
1182 #region 修改开单记录 1201 #region 修改开单记录
1183 /// <summary> 1202 /// <summary>
1184 /// 修改开单记录 1203 /// 修改开单记录
@@ -2486,7 +2505,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -2486,7 +2505,7 @@ namespace NCC.Extend.LqKdKdjlb
2486 { 2505 {
2487 Id = YitIdHelper.NextId().ToString(), 2506 Id = YitIdHelper.NextId().ToString(),
2488 Gltkbh = refundId, 2507 Gltkbh = refundId,
2489 - Jks = jks.Jks, 2508 + Jks = jks.Jkszh,
2490 Jksxm = jks.Jksxm, 2509 Jksxm = jks.Jksxm,
2491 Jkszh = jks.Jkszh, 2510 Jkszh = jks.Jkszh,
2492 Jksyj = (totalItemDeduction / refundKdyjEntities.Count()), 2511 Jksyj = (totalItemDeduction / refundKdyjEntities.Count()),
@@ -2588,7 +2607,7 @@ namespace NCC.Extend.LqKdKdjlb @@ -2588,7 +2607,7 @@ namespace NCC.Extend.LqKdKdjlb
2588 { 2607 {
2589 Id = YitIdHelper.NextId().ToString(), 2608 Id = YitIdHelper.NextId().ToString(),
2590 Glkdbh = billingId, 2609 Glkdbh = billingId,
2591 - Jks = jks.Jks, 2610 + Jks = jks.Jkszh,
2592 Jksxm = jks.Jksxm, 2611 Jksxm = jks.Jksxm,
2593 Jkszh = jks.Jkszh, 2612 Jkszh = jks.Jkszh,
2594 Jksyj = jks.Jksyj.ToString(), 2613 Jksyj = jks.Jksyj.ToString(),
@@ -2710,6 +2729,229 @@ namespace NCC.Extend.LqKdKdjlb @@ -2710,6 +2729,229 @@ namespace NCC.Extend.LqKdKdjlb
2710 } 2729 }
2711 #endregion 2730 #endregion
2712 2731
  2732 + #region 门店整体统计表
  2733 + /// <summary>
  2734 + /// 门店整体统计表
  2735 + /// </summary>
  2736 + /// <remarks>
  2737 + /// 统计每个健康师在指定时间周期内的各项数据指标
  2738 + /// 包括:邀约人数、预约人数、到店人数、开单人数、开单金额、消耗金额、人头、人次、消耗项目数
  2739 + ///
  2740 + /// 示例请求:
  2741 + /// ```json
  2742 + /// {
  2743 + /// "startTime": "2025-10-01",
  2744 + /// "endTime": "2025-10-31",
  2745 + /// "departmentId": "部门ID",
  2746 + /// "storeId": "门店ID",
  2747 + /// "employeeName": "健康师姓名"
  2748 + /// }
  2749 + /// ```
  2750 + ///
  2751 + /// 参数说明:
  2752 + /// - startTime: 开始时间(可选,默认为当月1号)
  2753 + /// - endTime: 结束时间(可选,默认为当前时间)
  2754 + /// - departmentId: 事业部ID(可选)
  2755 + /// - storeId: 门店ID(可选)
  2756 + /// - employeeName: 健康师姓名(可选)
  2757 + ///
  2758 + /// 返回字段说明:
  2759 + /// - EmployeeId: 健康师ID
  2760 + /// - EmployeeName: 健康师姓名
  2761 + /// - StoreId: 门店ID
  2762 + /// - StoreName: 门店名称
  2763 + /// - DepartmentId: 事业部ID
  2764 + /// - DepartmentName: 事业部名称
  2765 + /// - InviteCount: 邀约人数(按客户去重)
  2766 + /// - AppointmentCount: 预约人数(按客户去重,无论预约状态)
  2767 + /// - VisitCount: 到店人数(按客户去重,仅统计状态为'已确认'的预约)
  2768 + /// - BillingCount: 开单人数(按开单记录去重)
  2769 + /// - BillingAmount: 开单金额(开单业绩总金额)
  2770 + /// - ConsumeAmount: 消耗金额(消耗业绩总金额)
  2771 + /// - HeadCount: 人头(按客户去重)
  2772 + /// - PersonCount: 人次(按客户+日期去重,同一客户不同天算多次)
  2773 + /// - ProjectCount: 消耗项目数(项目总次数)
  2774 + /// </remarks>
  2775 + /// <param name="input">查询参数</param>
  2776 + /// <returns>健康师统计数据列表</returns>
  2777 + /// <response code="200">成功返回统计数据</response>
  2778 + /// <response code="400">参数错误</response>
  2779 + /// <response code="500">服务器错误</response>
  2780 + [HttpGet("get-health-coach-statistics")]
  2781 + public async Task<dynamic> GetHealthCoachStatistics([FromQuery] HealthCoachStatisticsQueryInput input)
  2782 + {
  2783 + try
  2784 + {
  2785 + // 设置默认时间范围(如果未提供,默认为当月)
  2786 + var startTime = input.StartTime ?? new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
  2787 + var endTime = input.EndTime ?? DateTime.Now;
  2788 +
  2789 + // 构建SQL查询
  2790 + var sql = $@"
  2791 + SELECT
  2792 + u.F_Id as EmployeeId,
  2793 + u.F_REALNAME as EmployeeName,
  2794 + u.F_MDID as StoreId,
  2795 + md.dm as StoreName,
  2796 + md.syb as DepartmentId,
  2797 + dept.F_FullName as DepartmentName,
  2798 +
  2799 + -- 邀约人数
  2800 + COALESCE(invite_stats.InviteCount, 0) as InviteCount,
  2801 +
  2802 + -- 预约人数(无论状态)
  2803 + COALESCE(appointment_stats.AppointmentCount, 0) as AppointmentCount,
  2804 +
  2805 + -- 到店人数(已确认状态)
  2806 + COALESCE(visit_stats.VisitCount, 0) as VisitCount,
  2807 +
  2808 + -- 开单人数和金额
  2809 + COALESCE(billing_stats.BillingCount, 0) as BillingCount,
  2810 + COALESCE(billing_stats.BillingAmount, 0) as BillingAmount,
  2811 +
  2812 + -- 消耗相关统计
  2813 + COALESCE(consume_stats.ConsumeAmount, 0) as ConsumeAmount,
  2814 + COALESCE(consume_stats.HeadCount, 0) as HeadCount,
  2815 + COALESCE(consume_stats.PersonCount, 0) as PersonCount,
  2816 + COALESCE(consume_stats.ProjectCount, 0) as ProjectCount
  2817 +
  2818 + FROM BASE_USER u
  2819 + LEFT JOIN lq_mdxx md ON u.F_MDID = md.F_Id
  2820 + LEFT JOIN base_organize dept ON md.syb = dept.F_Id
  2821 +
  2822 + -- 邀约统计子查询
  2823 + LEFT JOIN (
  2824 + SELECT
  2825 + yyr as EmployeeId,
  2826 + COUNT(DISTINCT yykh) as InviteCount
  2827 + FROM lq_yaoyjl
  2828 + WHERE yyr IS NOT NULL
  2829 + AND F_CreateTime >= @startTime
  2830 + AND F_CreateTime <= @endTime
  2831 + GROUP BY yyr
  2832 + ) invite_stats ON u.F_Id = invite_stats.EmployeeId
  2833 +
  2834 + -- 预约统计子查询
  2835 + LEFT JOIN (
  2836 + SELECT
  2837 + yyr as EmployeeId,
  2838 + COUNT(DISTINCT gk) as AppointmentCount
  2839 + FROM lq_yyjl
  2840 + WHERE yyr IS NOT NULL
  2841 + AND F_CreateTime >= @startTime
  2842 + AND F_CreateTime <= @endTime
  2843 + GROUP BY yyr
  2844 + ) appointment_stats ON u.F_Id = appointment_stats.EmployeeId
  2845 +
  2846 + -- 到店统计子查询
  2847 + LEFT JOIN (
  2848 + SELECT
  2849 + yyr as EmployeeId,
  2850 + COUNT(DISTINCT gk) as VisitCount
  2851 + FROM lq_yyjl
  2852 + WHERE yyr IS NOT NULL
  2853 + AND F_Status = '已确认'
  2854 + AND F_CreateTime >= @startTime
  2855 + AND F_CreateTime <= @endTime
  2856 + GROUP BY yyr
  2857 + ) visit_stats ON u.F_Id = visit_stats.EmployeeId
  2858 +
  2859 + -- 开单统计子查询
  2860 + LEFT JOIN (
  2861 + SELECT
  2862 + jkszh as EmployeeId,
  2863 + COUNT(DISTINCT glkdbh) as BillingCount,
  2864 + SUM(CAST(jksyj AS DECIMAL(18,2))) as BillingAmount
  2865 + FROM lq_kd_jksyj
  2866 + WHERE jkszh IS NOT NULL
  2867 + AND F_IsEffective = 1
  2868 + AND yjsj >= @startTime
  2869 + AND yjsj <= @endTime
  2870 + GROUP BY jkszh
  2871 + ) billing_stats ON u.F_Id = billing_stats.EmployeeId
  2872 +
  2873 + -- 消耗统计子查询
  2874 + LEFT JOIN (
  2875 + SELECT
  2876 + jksyj.jkszh as EmployeeId,
  2877 + SUM(jksyj.jksyj) as ConsumeAmount,
  2878 + COUNT(DISTINCT hyhk.hy) as HeadCount,
  2879 + COUNT(DISTINCT CONCAT(jksyj.jkszh, '_', hyhk.hy, '_', DATE(hyhk.hksj))) as PersonCount,
  2880 + SUM(jksyj.F_kdpxNumber) as ProjectCount
  2881 + FROM lq_xh_jksyj jksyj
  2882 + INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
  2883 + WHERE jksyj.jkszh IS NOT NULL
  2884 + AND jksyj.F_IsEffective = 1
  2885 + AND hyhk.F_IsEffective = 1
  2886 + AND hyhk.hksj >= @startTime
  2887 + AND hyhk.hksj <= @endTime
  2888 + GROUP BY jksyj.jkszh
  2889 + ) consume_stats ON u.F_Id = consume_stats.EmployeeId
  2890 +
  2891 + WHERE u.F_GW = '健康师'
  2892 + ";
  2893 +
  2894 + // 添加条件过滤
  2895 + var conditions = new List<string>();
  2896 + var parameters = new List<SugarParameter>
  2897 + {
  2898 + new SugarParameter("@startTime", startTime),
  2899 + new SugarParameter("@endTime", endTime)
  2900 + };
  2901 +
  2902 + if (!string.IsNullOrEmpty(input.DepartmentId))
  2903 + {
  2904 + conditions.Add("md.syb = @departmentId");
  2905 + parameters.Add(new SugarParameter("@departmentId", input.DepartmentId));
  2906 + }
2713 2907
  2908 + if (!string.IsNullOrEmpty(input.StoreId))
  2909 + {
  2910 + conditions.Add("u.F_MDID = @storeId");
  2911 + parameters.Add(new SugarParameter("@storeId", input.StoreId));
  2912 + }
  2913 +
  2914 + if (!string.IsNullOrEmpty(input.EmployeeName))
  2915 + {
  2916 + conditions.Add("u.F_REALNAME LIKE @employeeName");
  2917 + parameters.Add(new SugarParameter("@employeeName", $"%{input.EmployeeName}%"));
  2918 + }
  2919 +
  2920 + if (conditions.Any())
  2921 + {
  2922 + sql += " AND " + string.Join(" AND ", conditions);
  2923 + }
  2924 +
  2925 + sql += " ORDER BY u.F_REALNAME";
  2926 +
  2927 + // 执行查询
  2928 + var allData = await _db.Ado.SqlQueryAsync<HealthCoachStatisticsOutput>(sql, parameters);
  2929 +
  2930 + // 手动分页
  2931 + var totalCount = allData.Count;
  2932 + var pagedData = allData
  2933 + .Skip((input.currentPage - 1) * input.pageSize)
  2934 + .Take(input.pageSize)
  2935 + .ToList();
  2936 +
  2937 + // 直接返回分页结果
  2938 + return new
  2939 + {
  2940 + list = pagedData,
  2941 + pagination = new
  2942 + {
  2943 + pageIndex = input.currentPage,
  2944 + pageSize = input.pageSize,
  2945 + totalCount = totalCount
  2946 + }
  2947 + };
  2948 + }
  2949 + catch (Exception ex)
  2950 + {
  2951 + _logger.LogError(ex, "获取健康师统计数据失败");
  2952 + throw NCCException.Oh($"获取健康师统计数据失败:{ex.Message}");
  2953 + }
  2954 + }
  2955 + #endregion
2714 } 2956 }
2715 } 2957 }
netcore/src/Modularity/Extend/NCC.Extend/LqMdxxService.cs
@@ -24,6 +24,7 @@ using NCC.DataEncryption; @@ -24,6 +24,7 @@ using NCC.DataEncryption;
24 using NCC.ClayObject; 24 using NCC.ClayObject;
25 using NCC.Extend.Entitys.Enum; 25 using NCC.Extend.Entitys.Enum;
26 using NCC.Code; 26 using NCC.Code;
  27 +using NCC.Extend.Entitys.Dto.Common;
27 28
28 namespace NCC.Extend.LqMdxx 29 namespace NCC.Extend.LqMdxx
29 { 30 {
@@ -120,8 +121,8 @@ namespace NCC.Extend.LqMdxx @@ -120,8 +121,8 @@ namespace NCC.Extend.LqMdxx
120 rt1 = it.Rt1, 121 rt1 = it.Rt1,
121 rt2 = it.Rt2, 122 rt2 = it.Rt2,
122 rc = it.Rc, 123 rc = it.Rc,
123 - StoreCategory = it.StoreCategory,  
124 - StoreType = it.StoreType, 124 + storeCategory = it.StoreCategory,
  125 + storeType = it.StoreType,
125 }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); 126 }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
126 return PageResult<LqMdxxListOutput>.SqlSugarPageResult(data); 127 return PageResult<LqMdxxListOutput>.SqlSugarPageResult(data);
127 } 128 }
@@ -191,8 +192,21 @@ namespace NCC.Extend.LqMdxx @@ -191,8 +192,21 @@ namespace NCC.Extend.LqMdxx
191 gsmc = it.Gsmc, 192 gsmc = it.Gsmc,
192 fr = it.Fr, 193 fr = it.Fr,
193 ywsb = it.Ywsb, 194 ywsb = it.Ywsb,
194 - StoreCategory = it.StoreCategory,  
195 - StoreType = it.StoreType, 195 + jyb = it.Jyb,
  196 + kjb = it.Kjb,
  197 + dxmb = it.Dxmb,
  198 + syb = it.Syb,
  199 + gsqssj = it.Gsqssj,
  200 + gszzsj = it.Gszzsj,
  201 + status = it.Status,
  202 + xsyj = it.Xsyj,
  203 + xhyj = it.Xhyj,
  204 + xms = it.Xms,
  205 + rt1 = it.Rt1,
  206 + rt2 = it.Rt2,
  207 + rc = it.Rc,
  208 + storeCategory = it.StoreCategory,
  209 + storeType = it.StoreType,
196 }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync(); 210 }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync();
197 return data; 211 return data;
198 } 212 }
@@ -359,11 +373,14 @@ namespace NCC.Extend.LqMdxx @@ -359,11 +373,14 @@ namespace NCC.Extend.LqMdxx
359 /// </summary> 373 /// </summary>
360 /// <returns></returns> 374 /// <returns></returns>
361 [HttpGet("Selector/StoreCategory")] 375 [HttpGet("Selector/StoreCategory")]
362 - public async Task<dynamic> GetStoreCategorySelector() 376 + public List<EnumOutput> GetStoreCategorySelector()
363 { 377 {
364 - //从Enum中获取门店类别  
365 - var storeCategoryEnum = EnumExtensions.GetEnumDescDictionary(typeof(StoreCategoryEnum));  
366 - return new { list = storeCategoryEnum }; 378 + return Enum.GetValues<StoreCategoryEnum>().Select(e => new EnumOutput
  379 + {
  380 + Value = (int)e,
  381 + Name = e.ToString(),
  382 + Description = e.GetDescription(),
  383 + }).ToList();
367 } 384 }
368 #endregion 385 #endregion
369 386
@@ -373,11 +390,18 @@ namespace NCC.Extend.LqMdxx @@ -373,11 +390,18 @@ namespace NCC.Extend.LqMdxx
373 /// </summary> 390 /// </summary>
374 /// <returns></returns> 391 /// <returns></returns>
375 [HttpGet("Selector/StoreType")] 392 [HttpGet("Selector/StoreType")]
376 - public async Task<dynamic> GetStoreTypeSelector() 393 + public List<EnumOutput> GetStoreTypeSelector()
377 { 394 {
378 //从Enum中获取门店类型 395 //从Enum中获取门店类型
379 - var storeTypeEnum = EnumExtensions.GetEnumDescDictionary(typeof(StoreTypeEnum));  
380 - return new { list = storeTypeEnum }; 396 + var storeTypeEnum = Enum.GetValues<StoreTypeEnum>()
  397 + .Select(e => new EnumOutput
  398 + {
  399 + Value = (int)e,
  400 + Name = e.ToString(),
  401 + Description = e.GetDescription(),
  402 + })
  403 + .ToList();
  404 + return storeTypeEnum;
381 } 405 }
382 #endregion 406 #endregion
383 } 407 }
netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs
@@ -551,5 +551,163 @@ namespace NCC.Extend.LqPackageInfo @@ -551,5 +551,163 @@ namespace NCC.Extend.LqPackageInfo
551 return output; 551 return output;
552 } 552 }
553 #endregion 553 #endregion
  554 +
  555 + #region 营销活动统计
  556 + /// <summary>
  557 + /// 获取营销活动统计数据
  558 + /// </summary>
  559 + /// <remarks>
  560 + /// 统计指定营销活动的开单、退卡相关数据
  561 + /// 包括:开单数量、开单金额、退卡数量、退卡金额、净开单数量、净开单金额、退卡率
  562 + ///
  563 + /// 示例请求:
  564 + /// ```json
  565 + /// {
  566 + /// "activityId": "营销活动ID",
  567 + /// "startTime": "2025-10-01",
  568 + /// "endTime": "2025-10-31",
  569 + /// "storeIds": ["门店ID1", "门店ID2"]
  570 + /// }
  571 + /// ```
  572 + ///
  573 + /// 参数说明:
  574 + /// - activityId: 营销活动ID(必填)
  575 + /// - startTime: 开始时间(可选,默认为活动开始时间)
  576 + /// - endTime: 结束时间(可选,默认为活动结束时间)
  577 + /// - storeIds: 门店ID列表(可选)
  578 + ///
  579 + /// 返回字段说明:
  580 + /// - ActivityId: 营销活动ID
  581 + /// - ActivityName: 营销活动名称
  582 + /// - BillingCount: 开单数量
  583 + /// - BillingAmount: 开单金额
  584 + /// - RefundCount: 退卡数量
  585 + /// - RefundAmount: 退卡金额
  586 + /// - NetBillingCount: 净开单数量(开单数量 - 退卡数量)
  587 + /// - NetBillingAmount: 净开单金额(开单金额 - 退卡金额)
  588 + /// - RefundRate: 退卡率(退卡数量 / 开单数量)
  589 + /// </remarks>
  590 + /// <param name="input">查询参数</param>
  591 + /// <returns>营销活动统计数据</returns>
  592 + /// <response code="200">成功返回统计数据</response>
  593 + /// <response code="400">参数错误</response>
  594 + /// <response code="500">服务器错误</response>
  595 + [HttpPost("get-activity-statistics")]
  596 + public async Task<object> GetActivityStatistics(ActivityStatisticsInput input)
  597 + {
  598 + try
  599 + {
  600 + // 1. 获取营销活动信息
  601 + var activity = await _db.Queryable<LqPackageInfoEntity>()
  602 + .Where(x => x.Id == input.ActivityId && x.IsEffective == StatusEnum.有效.GetHashCode())
  603 + .FirstAsync();
  604 +
  605 + if (activity == null)
  606 + {
  607 + throw NCCException.Oh("营销活动不存在或已失效");
  608 + }
  609 +
  610 + // 2. 设置时间范围(如果未提供,使用活动时间范围)
  611 + var startTime = input.StartTime ?? activity.StartTime;
  612 + var endTime = input.EndTime ?? activity.EndTime;
  613 +
  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. 构建门店过滤条件
  642 + string storeFilter = "";
  643 + string storeFilterRefund = "";
  644 +
  645 + if (input.StoreIds != null && input.StoreIds.Any())
  646 + {
  647 + var storeIdsStr = string.Join("','", input.StoreIds);
  648 + storeFilter = $"AND kd.djmd IN ('{storeIdsStr}')";
  649 + storeFilterRefund = $"AND hytk.md IN ('{storeIdsStr}')";
  650 + }
  651 +
  652 + // 6. 开单统计
  653 + var billingSql = $@"
  654 + 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}'
  663 + {storeFilter}";
  664 +
  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);
  668 +
  669 + // 7. 退卡统计
  670 + var refundSql = $@"
  671 + 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. 返回统计结果
  692 + return new ActivityStatisticsOutput
  693 + {
  694 + ActivityId = input.ActivityId,
  695 + ActivityName = activity.ActivityName,
  696 + BillingCount = billingCount,
  697 + BillingAmount = billingAmount,
  698 + RefundCount = refundCount,
  699 + RefundAmount = refundAmount,
  700 + NetBillingCount = netBillingCount,
  701 + NetBillingAmount = netBillingAmount,
  702 + RefundRate = refundRate
  703 + };
  704 + }
  705 + catch (Exception ex)
  706 + {
  707 + throw NCCException.Oh($"获取营销活动统计数据失败: {ex.Message}");
  708 + }
  709 + }
  710 + #endregion
  711 +
554 } 712 }
555 } 713 }
netcore/src/Modularity/Extend/NCC.Extend/LqReportService.cs
@@ -25,6 +25,8 @@ using NCC.Extend.Entitys.lq_jinsanjiao_user; @@ -25,6 +25,8 @@ using NCC.Extend.Entitys.lq_jinsanjiao_user;
25 using NCC.Extend.Entitys.lq_kd_kdjlb; 25 using NCC.Extend.Entitys.lq_kd_kdjlb;
26 using NCC.Extend.Entitys.lq_xh_hyhk; 26 using NCC.Extend.Entitys.lq_xh_hyhk;
27 using NCC.Extend.Entitys.lq_khxx; 27 using NCC.Extend.Entitys.lq_khxx;
  28 +using NCC.Extend.Entitys.Dto.LqReport;
  29 +using NCC.Extend.Entitys.Enum;
28 using SqlSugar; 30 using SqlSugar;
29 31
30 namespace NCC.Extend 32 namespace NCC.Extend
@@ -40,6 +42,12 @@ namespace NCC.Extend @@ -40,6 +42,12 @@ namespace NCC.Extend
40 private readonly IUserManager _userManager; 42 private readonly IUserManager _userManager;
41 private readonly ILogger<LqReportService> _logger; 43 private readonly ILogger<LqReportService> _logger;
42 44
  45 + /// <summary>
  46 + /// 构造函数
  47 + /// </summary>
  48 + /// <param name="db">数据库客户端</param>
  49 + /// <param name="userManager">用户管理器</param>
  50 + /// <param name="logger">日志记录器</param>
43 public LqReportService(ISqlSugarClient db, IUserManager userManager, ILogger<LqReportService> logger) 51 public LqReportService(ISqlSugarClient db, IUserManager userManager, ILogger<LqReportService> logger)
44 { 52 {
45 _db = db; 53 _db = db;
@@ -548,7 +556,7 @@ namespace NCC.Extend @@ -548,7 +556,7 @@ namespace NCC.Extend
548 556
549 #endregion 557 #endregion
550 558
551 - #region 综合仪表盘 559 + #region 获取综合仪表盘数据
552 560
553 /// <summary> 561 /// <summary>
554 /// 获取综合仪表盘数据 562 /// 获取综合仪表盘数据
@@ -682,5 +690,594 @@ namespace NCC.Extend @@ -682,5 +690,594 @@ namespace NCC.Extend
682 } 690 }
683 691
684 #endregion 692 #endregion
  693 +
  694 + #region 获取业务统计数据
  695 +
  696 + /// <summary>
  697 + /// 获取业务统计数据
  698 + /// </summary>
  699 + /// <remarks>
  700 + /// 统计指定时间范围内的开单、耗卡、退卡相关数据
  701 + /// 包括:开单总金额、耗卡总金额、退卡总金额、开单人数、耗卡人数、退卡人数
  702 + ///
  703 + /// 示例请求:
  704 + /// ```json
  705 + /// {
  706 + /// "startTime": "2025-10-01",
  707 + /// "endTime": "2025-10-31",
  708 + /// "storeIds": ["门店ID1", "门店ID2"]
  709 + /// }
  710 + /// ```
  711 + ///
  712 + /// 参数说明:
  713 + /// - startTime: 开始时间(可选,默认为当月1号)
  714 + /// - endTime: 结束时间(可选,默认为当前时间)
  715 + /// - storeIds: 门店ID列表(可选)
  716 + ///
  717 + /// 返回字段说明:
  718 + /// - TotalBillingAmount: 开单总金额
  719 + /// - TotalConsumeAmount: 耗卡总金额
  720 + /// - TotalRefundAmount: 退卡总金额
  721 + /// - BillingCount: 开单人数(按客户去重)
  722 + /// - ConsumeCount: 耗卡人数(按客户去重)
  723 + /// - RefundCount: 退卡人数(按客户去重)
  724 + /// </remarks>
  725 + /// <param name="input">查询参数</param>
  726 + /// <returns>业务统计数据</returns>
  727 + /// <response code="200">成功返回统计数据</response>
  728 + /// <response code="400">参数错误</response>
  729 + /// <response code="500">服务器错误</response>
  730 + [HttpPost("get-business-statistics")]
  731 + public async Task<object> GetBusinessStatistics(BusinessStatisticsInput input)
  732 + {
  733 + try
  734 + {
  735 + // 设置默认时间范围(如果未提供,默认为当月)
  736 + var startTime = input.StartTime ?? new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
  737 + var endTime = input.EndTime ?? DateTime.Now;
  738 +
  739 + // 第一步:获取开单统计数据
  740 + var billingSql = @"
  741 + SELECT
  742 + COUNT(DISTINCT kd.kdhy) as billing_count,
  743 + COALESCE(SUM(CAST(kd.sfyj AS DECIMAL(18,2))), 0) as billing_amount
  744 + FROM lq_kd_kdjlb kd
  745 + WHERE kd.F_IsEffective = 1
  746 + AND kd.kdrq >= @startTime
  747 + AND kd.kdrq <= @endTime";
  748 +
  749 + object billingParameters;
  750 + if (input.StoreIds != null && input.StoreIds.Any())
  751 + {
  752 + billingSql += " AND kd.F_StoreId IN @storeIds";
  753 + billingParameters = new { startTime, endTime, storeIds = input.StoreIds };
  754 + }
  755 + else
  756 + {
  757 + billingParameters = new { startTime, endTime };
  758 + }
  759 +
  760 + var billingResult = await _db.Ado.SqlQueryAsync<dynamic>(billingSql, billingParameters);
  761 + var billingCount = Convert.ToInt32(billingResult?.FirstOrDefault()?.billing_count ?? 0);
  762 + var billingAmount = Convert.ToDecimal(billingResult?.FirstOrDefault()?.billing_amount ?? 0m);
  763 +
  764 + // 第二步:获取耗卡统计数据
  765 + var consumeSql = @"
  766 + SELECT
  767 + COUNT(DISTINCT xh.hy) as consume_count,
  768 + COALESCE(SUM(CAST(xh.xfje AS DECIMAL(18,2))), 0) as consume_amount
  769 + FROM lq_xh_hyhk xh
  770 + WHERE xh.F_IsEffective = 1
  771 + AND xh.hksj >= @startTime
  772 + AND xh.hksj <= @endTime";
  773 +
  774 + object consumeParameters;
  775 + if (input.StoreIds != null && input.StoreIds.Any())
  776 + {
  777 + consumeSql += " AND xh.F_StoreId IN @storeIds";
  778 + consumeParameters = new { startTime, endTime, storeIds = input.StoreIds };
  779 + }
  780 + else
  781 + {
  782 + consumeParameters = new { startTime, endTime };
  783 + }
  784 +
  785 + var consumeResult = await _db.Ado.SqlQueryAsync<dynamic>(consumeSql, consumeParameters);
  786 + var consumeCount = Convert.ToInt32(consumeResult?.FirstOrDefault()?.consume_count ?? 0);
  787 + var consumeAmount = Convert.ToDecimal(consumeResult?.FirstOrDefault()?.consume_amount ?? 0m);
  788 +
  789 + // 第三步:获取退卡统计数据
  790 + var refundSql = @"
  791 + SELECT
  792 + COUNT(DISTINCT hytk.hy) as refund_count,
  793 + COALESCE(SUM(CAST(hytk.tkje AS DECIMAL(18,2))), 0) as refund_amount
  794 + FROM lq_hytk_hytk hytk
  795 + WHERE hytk.F_IsEffective = 1
  796 + AND hytk.tksj >= @startTime
  797 + AND hytk.tksj <= @endTime";
  798 +
  799 + object refundParameters;
  800 + if (input.StoreIds != null && input.StoreIds.Any())
  801 + {
  802 + refundSql += " AND hytk.F_StoreId IN @storeIds";
  803 + refundParameters = new { startTime, endTime, storeIds = input.StoreIds };
  804 + }
  805 + else
  806 + {
  807 + refundParameters = new { startTime, endTime };
  808 + }
  809 +
  810 + var refundResult = await _db.Ado.SqlQueryAsync<dynamic>(refundSql, refundParameters);
  811 + var refundCount = Convert.ToInt32(refundResult?.FirstOrDefault()?.refund_count ?? 0);
  812 + var refundAmount = Convert.ToDecimal(refundResult?.FirstOrDefault()?.refund_amount ?? 0m);
  813 +
  814 + var result = new BusinessStatisticsOutput
  815 + {
  816 + TotalBillingAmount = billingAmount,
  817 + TotalConsumeAmount = consumeAmount,
  818 + TotalRefundAmount = refundAmount,
  819 + BillingCount = billingCount,
  820 + ConsumeCount = consumeCount,
  821 + RefundCount = refundCount
  822 + };
  823 +
  824 + return result;
  825 + }
  826 + catch (Exception ex)
  827 + {
  828 + _logger.LogError(ex, $"获取业务统计数据失败 - 开始时间: {input?.StartTime}, 结束时间: {input?.EndTime}");
  829 + throw NCCException.Oh($"获取业务统计数据失败: {ex.Message}");
  830 + }
  831 + }
  832 +
  833 + #endregion
  834 +
  835 + #region 获取客户类型统计数据
  836 + /// <summary>
  837 + /// 获取客户类型统计数据
  838 + /// </summary>
  839 + /// <remarks>
  840 + /// 统计指定时间范围内不同类型的客户数量和转化率
  841 + /// 包括:线索、新客、散客、会员数量,以及拓客人数、消耗人数、转化率
  842 + ///
  843 + /// 示例请求:
  844 + /// ```json
  845 + /// {
  846 + /// "startTime": "2025-10-01",
  847 + /// "endTime": "2025-10-31",
  848 + /// "storeIds": ["门店ID1", "门店ID2"]
  849 + /// }
  850 + /// ```
  851 + ///
  852 + /// 参数说明:
  853 + /// - startTime: 开始时间(可选,默认为当月1号)
  854 + /// - endTime: 结束时间(可选,默认为当前时间)
  855 + /// - storeIds: 门店ID列表(可选)
  856 + ///
  857 + /// 返回字段说明:
  858 + /// - LeadCount: 线索客户数量
  859 + /// - NewCustomerCount: 新客数量
  860 + /// - CasualCustomerCount: 散客数量
  861 + /// - MemberCount: 会员数量
  862 + /// - TotalInviteCount: 拓客总人数
  863 + /// - ConsumeCount: 消耗人数(有消耗金额的)
  864 + /// - ConversionRate: 转化率(消耗人数/拓客人数)
  865 + /// </remarks>
  866 + /// <param name="input">查询参数</param>
  867 + /// <returns>客户类型统计数据</returns>
  868 + /// <response code="200">成功返回统计数据</response>
  869 + /// <response code="400">参数错误</response>
  870 + /// <response code="500">服务器错误</response>
  871 + [HttpPost("get-customer-type-statistics")]
  872 + public async Task<object> GetCustomerTypeStatistics(CustomerTypeStatisticsInput input)
  873 + {
  874 + try
  875 + {
  876 + // 设置默认时间范围(如果未提供,默认为当月)
  877 + var startTime = input.StartTime ?? new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
  878 + var endTime = input.EndTime ?? DateTime.Now;
  879 +
  880 + // 第一步:获取客户类型统计
  881 + var customerTypeSql = $@"
  882 + SELECT
  883 + SUM(CASE WHEN kh.khlx = '{MemberTypeEnum.线索.GetHashCode()}' THEN 1 ELSE 0 END) as lead_count,
  884 + SUM(CASE WHEN kh.khlx = '{MemberTypeEnum.新客.GetHashCode()}' THEN 1 ELSE 0 END) as new_customer_count,
  885 + SUM(CASE WHEN kh.khlx = '{MemberTypeEnum.散客.GetHashCode()}' THEN 1 ELSE 0 END) as casual_customer_count,
  886 + SUM(CASE WHEN kh.khlx = '{MemberTypeEnum.会员.GetHashCode()}' THEN 1 ELSE 0 END) as member_count
  887 + FROM lq_khxx kh
  888 + WHERE kh.F_CreateTime >= @startTime
  889 + AND kh.F_CreateTime <= @endTime";
  890 +
  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 + }
  901 +
  902 + var customerTypeResult = await _db.Ado.SqlQueryAsync<dynamic>(customerTypeSql, customerTypeParameters);
  903 + var leadCount = Convert.ToInt32(customerTypeResult?.FirstOrDefault()?.lead_count ?? 0);
  904 + var newCustomerCount = Convert.ToInt32(customerTypeResult?.FirstOrDefault()?.new_customer_count ?? 0);
  905 + var casualCustomerCount = Convert.ToInt32(customerTypeResult?.FirstOrDefault()?.casual_customer_count ?? 0);
  906 + var memberCount = Convert.ToInt32(customerTypeResult?.FirstOrDefault()?.member_count ?? 0);
  907 +
  908 + // 第二步:获取拓客总人数(按客户去重)
  909 + var inviteSql = @"
  910 + SELECT COUNT(DISTINCT tk.F_MemberId) as invite_count
  911 + FROM lq_tkjlb tk
  912 + WHERE tk.F_CreateTime >= @startTime
  913 + AND tk.F_CreateTime <= @endTime";
  914 +
  915 + object inviteParameters;
  916 + if (input.StoreIds != null && input.StoreIds.Any())
  917 + {
  918 + inviteSql += " AND tk.F_StoreId IN @storeIds";
  919 + inviteParameters = new { startTime, endTime, storeIds = input.StoreIds };
  920 + }
  921 + else
  922 + {
  923 + inviteParameters = new { startTime, endTime };
  924 + }
  925 +
  926 + var inviteResult = await _db.Ado.SqlQueryAsync<dynamic>(inviteSql, inviteParameters);
  927 + var totalInviteCount = Convert.ToInt32(inviteResult?.FirstOrDefault()?.invite_count ?? 0);
  928 +
  929 + // 第三步:获取消耗人数(有消耗金额的,按客户去重)
  930 + var consumeSql = @"
  931 + SELECT COUNT(DISTINCT xh.hy) as consume_count
  932 + FROM lq_xh_hyhk xh
  933 + WHERE xh.F_IsEffective = 1
  934 + AND xh.hksj >= @startTime
  935 + AND xh.hksj <= @endTime
  936 + AND xh.xfje > 0";
  937 +
  938 + object consumeParameters;
  939 + if (input.StoreIds != null && input.StoreIds.Any())
  940 + {
  941 + consumeSql += " AND xh.F_StoreId IN @storeIds";
  942 + consumeParameters = new { startTime, endTime, storeIds = input.StoreIds };
  943 + }
  944 + else
  945 + {
  946 + consumeParameters = new { startTime, endTime };
  947 + }
  948 +
  949 + var consumeResult = await _db.Ado.SqlQueryAsync<dynamic>(consumeSql, consumeParameters);
  950 + var consumeCount = Convert.ToInt32(consumeResult?.FirstOrDefault()?.consume_count ?? 0);
  951 +
  952 + // 计算转化率
  953 + var conversionRate = totalInviteCount > 0 ? Math.Round(consumeCount * 100.0m / totalInviteCount, 2) : 0;
  954 +
  955 + var result = new CustomerTypeStatisticsOutput
  956 + {
  957 + LeadCount = leadCount,
  958 + NewCustomerCount = newCustomerCount,
  959 + CasualCustomerCount = casualCustomerCount,
  960 + MemberCount = memberCount,
  961 + TotalInviteCount = totalInviteCount,
  962 + ConsumeCount = consumeCount,
  963 + ConversionRate = conversionRate
  964 + };
  965 +
  966 + return result;
  967 + }
  968 + catch (Exception ex)
  969 + {
  970 + _logger.LogError(ex, $"获取客户类型统计数据失败 - 开始时间: {input?.StartTime}, 结束时间: {input?.EndTime}");
  971 + throw NCCException.Oh($"获取客户类型统计数据失败: {ex.Message}");
  972 + }
  973 + }
  974 +
  975 + #endregion
  976 +
  977 + #region 获取门店业绩对比统计数据
  978 + /// <summary>
  979 + /// 获取门店业绩对比统计数据
  980 + /// </summary>
  981 + /// <remarks>
  982 + /// 统计指定时间范围内各门店的开单业绩与目标业绩的对比情况
  983 + /// 包括:目标业绩、实际开单业绩、完成率、差额、是否达标
  984 + ///
  985 + /// 示例请求:
  986 + /// ```json
  987 + /// {
  988 + /// "startTime": "2025-10-01",
  989 + /// "endTime": "2025-10-31",
  990 + /// "storeIds": ["门店ID1", "门店ID2"]
  991 + /// }
  992 + /// ```
  993 + ///
  994 + /// 参数说明:
  995 + /// - startTime: 开始时间(可选,默认为当月1号)
  996 + /// - endTime: 结束时间(可选,默认为当前时间)
  997 + /// - storeIds: 门店ID列表(可选)
  998 + ///
  999 + /// 返回字段说明:
  1000 + /// - StoreId: 门店ID
  1001 + /// - StoreName: 门店名称
  1002 + /// - TargetPerformance: 目标业绩(来自门店资料表xsyj字段)
  1003 + /// - ActualPerformance: 实际开单业绩(统计期间内的开单业绩总和)
  1004 + /// - CompletionRate: 完成率(实际业绩/目标业绩 × 100%)
  1005 + /// - Difference: 差额(实际业绩-目标业绩)
  1006 + /// - IsTargetAchieved: 是否达标(实际业绩 >= 目标业绩)
  1007 + /// </remarks>
  1008 + /// <param name="input">查询参数</param>
  1009 + /// <returns>门店业绩对比统计数据</returns>
  1010 + /// <response code="200">成功返回统计数据</response>
  1011 + /// <response code="400">参数错误</response>
  1012 + /// <response code="500">服务器错误</response>
  1013 + [HttpPost("get-store-performance-comparison")]
  1014 + public async Task<object> GetStorePerformanceComparison(StorePerformanceComparisonInput input)
  1015 + {
  1016 + try
  1017 + {
  1018 + // 设置默认时间范围(如果未提供,默认为当月)
  1019 + var startTime = input.StartTime ?? new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
  1020 + var endTime = input.EndTime ?? DateTime.Now;
  1021 +
  1022 + // 构建SQL查询
  1023 + var sql = @"
  1024 + SELECT
  1025 + md.F_Id as store_id,
  1026 + md.dm as store_name,
  1027 + COALESCE(CAST(md.xsyj AS DECIMAL(18,2)), 0) as target_performance,
  1028 + COALESCE(SUM(CAST(kd.sfyj AS DECIMAL(18,2))), 0) as actual_performance
  1029 + FROM lq_mdxx md
  1030 + LEFT JOIN lq_kd_kdjlb kd ON md.F_Id = kd.djmd
  1031 + AND kd.F_IsEffective = 1
  1032 + AND kd.kdrq >= @startTime
  1033 + AND kd.kdrq <= @endTime";
  1034 +
  1035 + object parameters;
  1036 + if (input.StoreIds != null && input.StoreIds.Any())
  1037 + {
  1038 + sql += " AND md.F_Id IN @storeIds";
  1039 + parameters = new { startTime, endTime, storeIds = input.StoreIds };
  1040 + }
  1041 + else
  1042 + {
  1043 + parameters = new { startTime, endTime };
  1044 + }
  1045 +
  1046 + sql += " GROUP BY md.F_Id, md.dm, md.xsyj ORDER BY actual_performance DESC";
  1047 +
  1048 + var results = await _db.Ado.SqlQueryAsync<dynamic>(sql, parameters);
  1049 +
  1050 + var storePerformanceList = new List<StorePerformanceComparisonOutput>();
  1051 +
  1052 + foreach (var item in results)
  1053 + {
  1054 + var storeId = item.store_id?.ToString();
  1055 + var storeName = item.store_name?.ToString();
  1056 + var targetPerformance = Convert.ToDecimal(item.target_performance ?? 0);
  1057 + var actualPerformance = Convert.ToDecimal(item.actual_performance ?? 0);
  1058 +
  1059 + // 计算完成率
  1060 + var completionRate = targetPerformance > 0
  1061 + ? Math.Round(actualPerformance * 100.0m / targetPerformance, 2)
  1062 + : 0;
  1063 +
  1064 + // 计算差额
  1065 + var difference = actualPerformance - targetPerformance;
  1066 +
  1067 + // 判断是否达标
  1068 + var isTargetAchieved = actualPerformance >= targetPerformance;
  1069 +
  1070 + storePerformanceList.Add(new StorePerformanceComparisonOutput
  1071 + {
  1072 + StoreId = storeId,
  1073 + StoreName = storeName,
  1074 + TargetPerformance = targetPerformance,
  1075 + ActualPerformance = actualPerformance,
  1076 + CompletionRate = completionRate,
  1077 + Difference = difference,
  1078 + IsTargetAchieved = isTargetAchieved
  1079 + });
  1080 + }
  1081 +
  1082 + return storePerformanceList;
  1083 + }
  1084 + catch (Exception ex)
  1085 + {
  1086 + _logger.LogError(ex, $"获取门店业绩对比统计数据失败 - 开始时间: {input?.StartTime}, 结束时间: {input?.EndTime}");
  1087 + throw NCCException.Oh($"获取门店业绩对比统计数据失败: {ex.Message}");
  1088 + }
  1089 + }
  1090 + #endregion
  1091 +
  1092 + #region 获取品项统计数据
  1093 +
  1094 + /// <summary>
  1095 + /// 获取品项统计数据
  1096 + /// </summary>
  1097 + /// <remarks>
  1098 + /// 统计指定时间范围内各品项的开单、消耗、退卡相关数据
  1099 + /// 包括:开单数量、开单金额、消耗数量、消耗金额、退卡数量、退卡金额
  1100 + ///
  1101 + /// 示例请求:
  1102 + /// ```json
  1103 + /// {
  1104 + /// "startTime": "2025-10-01",
  1105 + /// "endTime": "2025-10-31",
  1106 + /// "storeIds": ["门店ID1", "门店ID2"]
  1107 + /// }
  1108 + /// ```
  1109 + ///
  1110 + /// 参数说明:
  1111 + /// - startTime: 开始时间(可选,默认为当月1号)
  1112 + /// - endTime: 结束时间(可选,默认为当前时间)
  1113 + /// - storeIds: 门店ID列表(可选)
  1114 + ///
  1115 + /// 返回字段说明:
  1116 + /// - ItemId: 品项ID
  1117 + /// - ItemName: 品项名称
  1118 + /// - ItemNumber: 品项编号
  1119 + /// - BillingCount: 开单数量
  1120 + /// - BillingAmount: 开单金额
  1121 + /// - ConsumeCount: 消耗数量
  1122 + /// - ConsumeAmount: 消耗金额
  1123 + /// - RefundCount: 退卡数量
  1124 + /// - RefundAmount: 退卡金额
  1125 + /// </remarks>
  1126 + /// <param name="input">查询参数</param>
  1127 + /// <returns>品项统计数据列表(按开单金额降序排列)</returns>
  1128 + /// <response code="200">成功返回统计数据</response>
  1129 + /// <response code="400">参数错误</response>
  1130 + /// <response code="500">服务器错误</response>
  1131 + [HttpPost("get-item-statistics")]
  1132 + public async Task<object> GetItemStatistics(ItemStatisticsInput input)
  1133 + {
  1134 + try
  1135 + {
  1136 + // 设置默认时间范围(如果未提供,默认为当月)
  1137 + var startTime = input.StartTime ?? new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
  1138 + var endTime = input.EndTime ?? DateTime.Now;
  1139 +
  1140 + // 构建门店过滤条件
  1141 + string storeCondition = "";
  1142 + if (input.StoreIds != null && input.StoreIds.Any())
  1143 + {
  1144 + var storeIdsStr = string.Join("','", input.StoreIds);
  1145 + storeCondition = $@"
  1146 + AND (
  1147 + kd.djmd IN ('{storeIdsStr}') OR
  1148 + xhhyhk.F_StoreId IN ('{storeIdsStr}') OR
  1149 + hytk.md IN ('{storeIdsStr}')
  1150 + )";
  1151 + }
  1152 +
  1153 + // 使用最激进的优化:分步查询,避免复杂JOIN
  1154 + var itemStatisticsDict = new Dictionary<string, ItemStatisticsOutput>();
  1155 +
  1156 + // 1. 先获取品项基础信息
  1157 + var itemSql = @"
  1158 + SELECT F_Id, xmmc, xmbh
  1159 + FROM lq_xmzl
  1160 + WHERE F_IsEffective = 1";
  1161 +
  1162 + var itemData = await _db.Ado.SqlQueryAsync<dynamic>(itemSql);
  1163 +
  1164 + // 初始化品项字典
  1165 + foreach (var item in itemData)
  1166 + {
  1167 + var itemId = item.F_Id?.ToString();
  1168 + if (!string.IsNullOrEmpty(itemId))
  1169 + {
  1170 + itemStatisticsDict[itemId] = new ItemStatisticsOutput
  1171 + {
  1172 + ItemId = itemId,
  1173 + ItemName = item.xmmc?.ToString() ?? "未知品项",
  1174 + ItemNumber = item.xmbh?.ToString() ?? "",
  1175 + BillingCount = 0,
  1176 + BillingAmount = 0,
  1177 + ConsumeCount = 0,
  1178 + ConsumeAmount = 0,
  1179 + RefundCount = 0,
  1180 + RefundAmount = 0
  1181 + };
  1182 + }
  1183 + }
  1184 +
  1185 + // 2. 开单统计 - 直接查询,避免JOIN
  1186 + var billingSql = $@"
  1187 + SELECT
  1188 + px.px as item_id,
  1189 + SUM(px.F_ProjectNumber) as billing_count,
  1190 + SUM(CAST(px.F_ActualPrice AS DECIMAL(18,2))) as billing_amount
  1191 + FROM lq_kd_pxmx px
  1192 + WHERE px.F_IsEffective = 1
  1193 + AND px.yjsj >= '{startTime:yyyy-MM-dd HH:mm:ss}'
  1194 + AND px.yjsj <= '{endTime:yyyy-MM-dd HH:mm:ss}'
  1195 + {(input.StoreIds != null && input.StoreIds.Any() ? $"AND px.glkdbh IN (SELECT F_Id FROM lq_kd_kdjlb WHERE djmd IN ('{string.Join("','", input.StoreIds)}'))" : "")}
  1196 + GROUP BY px.px";
  1197 +
  1198 + var billingData = await _db.Ado.SqlQueryAsync<dynamic>(billingSql);
  1199 +
  1200 + // 合并开单数据
  1201 + foreach (var billing in billingData)
  1202 + {
  1203 + var itemId = billing.item_id?.ToString();
  1204 + if (!string.IsNullOrEmpty(itemId) && itemStatisticsDict.ContainsKey(itemId))
  1205 + {
  1206 + itemStatisticsDict[itemId].BillingCount = Convert.ToInt32(billing.billing_count ?? 0);
  1207 + itemStatisticsDict[itemId].BillingAmount = Convert.ToDecimal(billing.billing_amount ?? 0);
  1208 + }
  1209 + }
  1210 +
  1211 + // 3. 消耗统计 - 直接查询,避免JOIN
  1212 + var consumeSql = $@"
  1213 + SELECT
  1214 + xh.px as item_id,
  1215 + SUM(xh.F_ProjectNumber) as consume_count,
  1216 + SUM(CAST(xh.F_TotalPrice AS DECIMAL(18,2))) as consume_amount
  1217 + FROM lq_xh_pxmx xh
  1218 + WHERE xh.F_IsEffective = 1
  1219 + AND xh.F_ConsumeInfoId IN (
  1220 + SELECT F_Id FROM lq_xh_hyhk
  1221 + WHERE hksj >= '{startTime:yyyy-MM-dd HH:mm:ss}'
  1222 + AND hksj <= '{endTime:yyyy-MM-dd HH:mm:ss}'
  1223 + {(input.StoreIds != null && input.StoreIds.Any() ? $"AND F_StoreId IN ('{string.Join("','", input.StoreIds)}')" : "")}
  1224 + )
  1225 + GROUP BY xh.px";
  1226 +
  1227 + var consumeData = await _db.Ado.SqlQueryAsync<dynamic>(consumeSql);
  1228 +
  1229 + // 合并消耗数据
  1230 + foreach (var consume in consumeData)
  1231 + {
  1232 + var itemId = consume.item_id?.ToString();
  1233 + if (!string.IsNullOrEmpty(itemId) && itemStatisticsDict.ContainsKey(itemId))
  1234 + {
  1235 + itemStatisticsDict[itemId].ConsumeCount = Convert.ToInt32(consume.consume_count ?? 0);
  1236 + itemStatisticsDict[itemId].ConsumeAmount = Convert.ToDecimal(consume.consume_amount ?? 0);
  1237 + }
  1238 + }
  1239 +
  1240 + // 4. 退卡统计 - 直接查询,避免JOIN
  1241 + var refundSql = $@"
  1242 + SELECT
  1243 + hytkmx.px as item_id,
  1244 + SUM(hytkmx.F_ProjectNumber) as refund_count,
  1245 + SUM(CAST(hytkmx.tkje AS DECIMAL(18,2))) as refund_amount
  1246 + FROM lq_hytk_mx hytkmx
  1247 + WHERE hytkmx.F_IsEffective = 1
  1248 + AND hytkmx.tksj >= '{startTime:yyyy-MM-dd HH:mm:ss}'
  1249 + AND hytkmx.tksj <= '{endTime:yyyy-MM-dd HH:mm:ss}'
  1250 + {(input.StoreIds != null && input.StoreIds.Any() ? $"AND hytkmx.F_RefundInfoId IN (SELECT F_Id FROM lq_hytk_hytk WHERE md IN ('{string.Join("','", input.StoreIds)}'))" : "")}
  1251 + GROUP BY hytkmx.px";
  1252 +
  1253 + var refundData = await _db.Ado.SqlQueryAsync<dynamic>(refundSql);
  1254 +
  1255 + // 合并退卡数据
  1256 + foreach (var refund in refundData)
  1257 + {
  1258 + var itemId = refund.item_id?.ToString();
  1259 + if (!string.IsNullOrEmpty(itemId) && itemStatisticsDict.ContainsKey(itemId))
  1260 + {
  1261 + itemStatisticsDict[itemId].RefundCount = Convert.ToInt32(refund.refund_count ?? 0);
  1262 + itemStatisticsDict[itemId].RefundAmount = Convert.ToDecimal(refund.refund_amount ?? 0);
  1263 + }
  1264 + }
  1265 +
  1266 + // 5. 过滤并排序
  1267 + var itemStatisticsList = itemStatisticsDict.Values
  1268 + .Where(x => x.BillingCount > 0 || x.ConsumeCount > 0 || x.RefundCount > 0)
  1269 + .OrderByDescending(x => x.BillingAmount)
  1270 + .ToList();
  1271 +
  1272 + return itemStatisticsList;
  1273 + }
  1274 + catch (Exception ex)
  1275 + {
  1276 + _logger.LogError(ex, $"获取品项统计数据失败 - 开始时间: {input?.StartTime}, 结束时间: {input?.EndTime}");
  1277 + throw NCCException.Oh($"获取品项统计数据失败: {ex.Message}");
  1278 + }
  1279 + }
  1280 +
  1281 + #endregion
685 } 1282 }
686 } 1283 }
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
@@ -1432,6 +1432,8 @@ namespace NCC.Extend.LqStatistics @@ -1432,6 +1432,8 @@ namespace NCC.Extend.LqStatistics
1432 order_stats.FirstOrderDate, 1432 order_stats.FirstOrderDate,
1433 COALESCE(coop_stats.CooperationPerformance, 0) AS CooperationPerformance, 1433 COALESCE(coop_stats.CooperationPerformance, 0) AS CooperationPerformance,
1434 COALESCE(base_stats.BasePerformance, 0) AS BasePerformance, 1434 COALESCE(base_stats.BasePerformance, 0) AS BasePerformance,
  1435 + COALESCE(refund_stats.RefundPerformance, 0) AS RefundPerformance,
  1436 + COALESCE(refund_stats.RefundCount, 0) AS RefundCount,
1435 order_stats.TotalPerformance 1437 order_stats.TotalPerformance
1436 FROM ( 1438 FROM (
1437 -- 按开单记录统计基础数据,避免重复计算 1439 -- 按开单记录统计基础数据,避免重复计算
@@ -1541,6 +1543,23 @@ namespace NCC.Extend.LqStatistics @@ -1541,6 +1543,23 @@ namespace NCC.Extend.LqStatistics
1541 AND (xmzl.fl3 IS NULL OR xmzl.fl3 != '合作业绩') 1543 AND (xmzl.fl3 IS NULL OR xmzl.fl3 != '合作业绩')
1542 GROUP BY jksyj.jkszh 1544 GROUP BY jksyj.jkszh
1543 ) base_stats ON order_stats.EmployeeId = base_stats.EmployeeId 1545 ) base_stats ON order_stats.EmployeeId = base_stats.EmployeeId
  1546 + LEFT JOIN (
  1547 + -- 退单业绩统计
  1548 + SELECT
  1549 + hytk_jksyj.jkszh AS EmployeeId,
  1550 + SUM(CAST(hytk_jksyj.jksyj AS DECIMAL(18,2))) AS RefundPerformance,
  1551 + COUNT(*) AS RefundCount
  1552 + FROM lq_hytk_jksyj hytk_jksyj
  1553 + INNER JOIN lq_hytk_hytk hytk ON hytk_jksyj.gltkbh = hytk.F_Id
  1554 + WHERE hytk_jksyj.jksyj IS NOT NULL
  1555 + AND hytk_jksyj.jksyj != ''
  1556 + AND hytk_jksyj.jksyj != '0'
  1557 + AND hytk_jksyj.F_IsEffective = 1
  1558 + AND hytk.F_IsEffective = 1
  1559 + AND YEAR(hytk_jksyj.tksj) = @year
  1560 + AND MONTH(hytk_jksyj.tksj) = @month
  1561 + GROUP BY hytk_jksyj.jks
  1562 + ) refund_stats ON order_stats.EmployeeId = refund_stats.EmployeeId
1544 ORDER BY order_stats.TotalPerformance DESC"; 1563 ORDER BY order_stats.TotalPerformance DESC";
1545 1564
1546 // 解析统计月份 1565 // 解析统计月份
@@ -1582,9 +1601,12 @@ namespace NCC.Extend.LqStatistics @@ -1582,9 +1601,12 @@ namespace NCC.Extend.LqStatistics
1582 Position = stats.Position?.ToString() ?? "", 1601 Position = stats.Position?.ToString() ?? "",
1583 EmployeeId = stats.EmployeeId?.ToString() ?? "", 1602 EmployeeId = stats.EmployeeId?.ToString() ?? "",
1584 EmployeeName = stats.EmployeeName?.ToString() ?? "", 1603 EmployeeName = stats.EmployeeName?.ToString() ?? "",
1585 - TotalPerformance = Convert.ToDecimal(stats.TotalPerformance ?? 0), 1604 + TotalPerformance = Convert.ToDecimal(stats.TotalPerformance ?? 0) - Convert.ToDecimal(stats.RefundPerformance ?? 0),
1586 BasePerformance = Convert.ToDecimal(stats.BasePerformance ?? 0), 1605 BasePerformance = Convert.ToDecimal(stats.BasePerformance ?? 0),
1587 CooperationPerformance = Convert.ToDecimal(stats.CooperationPerformance ?? 0), 1606 CooperationPerformance = Convert.ToDecimal(stats.CooperationPerformance ?? 0),
  1607 + RefundPerformance = Convert.ToDecimal(stats.RefundPerformance ?? 0),
  1608 + RefundCount = Convert.ToInt32(stats.RefundCount ?? 0),
  1609 + ActualPerformance = Convert.ToDecimal(stats.TotalPerformance ?? 0) - Convert.ToDecimal(stats.RefundPerformance ?? 0),
1588 OrderCount = Convert.ToInt32(stats.OrderCount ?? 0), 1610 OrderCount = Convert.ToInt32(stats.OrderCount ?? 0),
1589 FirstOrderCount = Convert.ToInt32(stats.FirstOrderCount ?? 0), 1611 FirstOrderCount = Convert.ToInt32(stats.FirstOrderCount ?? 0),
1590 UpgradeOrderCount = Convert.ToInt32(stats.UpgradeOrderCount ?? 0), 1612 UpgradeOrderCount = Convert.ToInt32(stats.UpgradeOrderCount ?? 0),
@@ -1665,7 +1687,7 @@ namespace NCC.Extend.LqStatistics @@ -1665,7 +1687,7 @@ namespace NCC.Extend.LqStatistics
1665 } 1687 }
1666 1688
1667 /// <summary> 1689 /// <summary>
1668 - /// 分页查询个人开单业绩统计数据 1690 + /// 分页查询个人开单业绩统计数据(在用)
1669 /// </summary> 1691 /// </summary>
1670 /// <remarks> 1692 /// <remarks>
1671 /// 分页查询个人业绩统计数据,支持多条件筛选 1693 /// 分页查询个人业绩统计数据,支持多条件筛选
@@ -1727,7 +1749,10 @@ namespace NCC.Extend.LqStatistics @@ -1727,7 +1749,10 @@ namespace NCC.Extend.LqStatistics
1727 UpgradeOrderPerformance = it.UpgradeOrderPerformance, 1749 UpgradeOrderPerformance = it.UpgradeOrderPerformance,
1728 LastOrderDate = it.LastOrderDate, 1750 LastOrderDate = it.LastOrderDate,
1729 FirstOrderDate = it.FirstOrderDate, 1751 FirstOrderDate = it.FirstOrderDate,
1730 - CreateTime = it.CreateTime 1752 + CreateTime = it.CreateTime,
  1753 + RefundPerformance = it.RefundPerformance,
  1754 + RefundCount = it.RefundCount,
  1755 + ActualPerformance = it.ActualPerformance
1731 }).ToPagedListAsync(input.currentPage, input.pageSize); 1756 }).ToPagedListAsync(input.currentPage, input.pageSize);
1732 1757
1733 return new 1758 return new
@@ -2588,6 +2613,7 @@ namespace NCC.Extend.LqStatistics @@ -2588,6 +2613,7 @@ namespace NCC.Extend.LqStatistics
2588 UpgradeOrderPerformance = Convert.ToDecimal(data.F_UpgradeOrderPerformance ?? 0), 2613 UpgradeOrderPerformance = Convert.ToDecimal(data.F_UpgradeOrderPerformance ?? 0),
2589 RefundAmount = Convert.ToDecimal(data.F_RefundAmount ?? 0), 2614 RefundAmount = Convert.ToDecimal(data.F_RefundAmount ?? 0),
2590 RefundCount = Convert.ToInt32(data.F_RefundCount ?? 0), 2615 RefundCount = Convert.ToInt32(data.F_RefundCount ?? 0),
  2616 + ActualPerformance = Convert.ToDecimal(data.F_TotalOrderPerformance ?? 0) - Convert.ToDecimal(data.F_RefundAmount ?? 0),
2591 CreateTime = DateTime.Now 2617 CreateTime = DateTime.Now
2592 }).ToList(); 2618 }).ToList();
2593 2619
@@ -2671,6 +2697,9 @@ namespace NCC.Extend.LqStatistics @@ -2671,6 +2697,9 @@ namespace NCC.Extend.LqStatistics
2671 upgradeOrderCount = it.UpgradeOrderCount, 2697 upgradeOrderCount = it.UpgradeOrderCount,
2672 firstOrderPerformance = it.FirstOrderPerformance, 2698 firstOrderPerformance = it.FirstOrderPerformance,
2673 upgradeOrderPerformance = it.UpgradeOrderPerformance, 2699 upgradeOrderPerformance = it.UpgradeOrderPerformance,
  2700 + refundAmount = it.RefundAmount,
  2701 + refundCount = it.RefundCount,
  2702 + actualPerformance = it.ActualPerformance,
2674 createTime = it.CreateTime 2703 createTime = it.CreateTime
2675 }) 2704 })
2676 .OrderBy(sidx + " " + input.sort) 2705 .OrderBy(sidx + " " + input.sort)
@@ -3329,7 +3358,8 @@ namespace NCC.Extend.LqStatistics @@ -3329,7 +3358,8 @@ namespace NCC.Extend.LqStatistics
3329 UpgradeOrderCount = it.UpgradeOrderCount, 3358 UpgradeOrderCount = it.UpgradeOrderCount,
3330 RefundAmount = it.RefundAmount, 3359 RefundAmount = it.RefundAmount,
3331 RefundCount = it.RefundCount, 3360 RefundCount = it.RefundCount,
3332 - CreateTime = it.CreateTime.HasValue ? it.CreateTime.Value : DateTime.Now 3361 + CreateTime = it.CreateTime.HasValue ? it.CreateTime.Value : DateTime.Now,
  3362 + ActualPerformance = it.ActualPerformance
3333 }).ToList(); 3363 }).ToList();
3334 3364
3335 return new 3365 return new
netcore/src/Modularity/Extend/NCC.Extend/LqTkjlbService.cs
@@ -609,49 +609,83 @@ namespace NCC.Extend.LqTkjlb @@ -609,49 +609,83 @@ namespace NCC.Extend.LqTkjlb
609 { 609 {
610 try 610 try
611 { 611 {
612 - var sql = @" 612 + // 第一步:获取拓客总数
  613 + var tkSql = @"
  614 + SELECT COUNT(DISTINCT F_Id) as tk_count
  615 + FROM lq_tkjlb
  616 + WHERE F_EventId = @eventId";
  617 +
  618 + var tkResult = await _db.Ado.SqlQueryAsync<dynamic>(tkSql, new { eventId });
  619 + var tkCount = tkResult?.FirstOrDefault()?.tk_count ?? 0;
  620 +
  621 + // 第二步:获取邀约总数(按会员ID去重)
  622 + var yaoySql = @"
  623 + SELECT COUNT(DISTINCT tk.F_MemberId) as yaoy_count
  624 + FROM lq_tkjlb tk
  625 + LEFT JOIN lq_yaoyjl yy ON tk.F_MemberId = yy.yykh AND yy.F_StoreId = tk.F_StoreId
  626 + WHERE tk.F_EventId = @eventId AND yy.F_Id IS NOT NULL";
  627 +
  628 + var yaoyResult = await _db.Ado.SqlQueryAsync<dynamic>(yaoySql, new { eventId });
  629 + var yaoyCount = yaoyResult?.FirstOrDefault()?.yaoy_count ?? 0;
  630 +
  631 + // 第三步:获取预约总数(按会员ID去重)
  632 + var yySql = @"
  633 + SELECT COUNT(DISTINCT tk.F_MemberId) as yy_count
  634 + FROM lq_tkjlb tk
  635 + LEFT JOIN lq_yyjl yyjl ON tk.F_MemberId = yyjl.gk AND yyjl.F_Status = '已确认'
  636 + WHERE tk.F_EventId = @eventId AND yyjl.F_Id IS NOT NULL";
  637 +
  638 + var yyResult = await _db.Ado.SqlQueryAsync<dynamic>(yySql, new { eventId });
  639 + var yyCount = yyResult?.FirstOrDefault()?.yy_count ?? 0;
  640 +
  641 + // 第四步:获取耗卡总数和金额(按会员ID去重,但金额不去重)
  642 + var hkSql = @"
613 SELECT 643 SELECT
614 - SUM(tk_count) as total_tk_count,  
615 - SUM(yaoy_count) as total_yaoy_count,  
616 - SUM(yy_count) as total_yy_count,  
617 - SUM(hk_count) as total_hk_count,  
618 - SUM(kd_count) as total_kd_count,  
619 - SUM(hk_amount) as total_hk_amount,  
620 - SUM(kd_amount) as total_kd_amount,  
621 - CASE  
622 - WHEN SUM(tk_count) > 0  
623 - THEN ROUND(SUM(yy_count) * 100.0 / SUM(tk_count), 2)  
624 - ELSE 0  
625 - END as overall_yy_conversion_rate,  
626 - CASE  
627 - WHEN SUM(yy_count) > 0  
628 - THEN ROUND(SUM(hk_count) * 100.0 / SUM(yy_count), 2)  
629 - ELSE 0  
630 - END as overall_hk_conversion_rate  
631 - FROM (  
632 - SELECT  
633 - COUNT(DISTINCT tk.F_Id) as tk_count,  
634 - COUNT(DISTINCT yy.F_Id) as yaoy_count,  
635 - COUNT(DISTINCT yyjl.F_Id) as yy_count,  
636 - COUNT(DISTINCT xh.F_Id) as hk_count,  
637 - COUNT(DISTINCT kd.F_Id) as kd_count,  
638 - COALESCE(SUM(xh.xfje), 0) as hk_amount,  
639 - COALESCE(SUM(kd.sfyj), 0) as kd_amount  
640 - FROM lq_tkjlb tk  
641 - LEFT JOIN lq_yaoyjl yy ON tk.F_MemberId = yy.yykh AND yy.F_StoreId = tk.F_StoreId  
642 - LEFT JOIN lq_yyjl yyjl ON tk.F_MemberId = yyjl.gk AND yyjl.F_Status = '已确认'  
643 - LEFT JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy AND xh.F_IsEffective = 1  
644 - LEFT JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy AND kd.F_IsEffective = 1  
645 - WHERE tk.F_EventId = @eventId  
646 - ) stats"; 644 + COUNT(DISTINCT tk.F_MemberId) as hk_count,
  645 + COALESCE(SUM(xh.xfje), 0) as hk_amount
  646 + FROM lq_tkjlb tk
  647 + LEFT JOIN lq_xh_hyhk xh ON tk.F_MemberId = xh.hy AND xh.F_IsEffective = 1
  648 + WHERE tk.F_EventId = @eventId AND xh.F_Id IS NOT NULL";
647 649
648 - var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, new { eventId }); 650 + var hkResult = await _db.Ado.SqlQueryAsync<dynamic>(hkSql, new { eventId });
  651 + var hkCount = hkResult?.FirstOrDefault()?.hk_count ?? 0;
  652 + var hkAmount = hkResult?.FirstOrDefault()?.hk_amount ?? 0m;
  653 +
  654 + // 第五步:获取开单总数和金额(按会员ID去重,但金额不去重)
  655 + var kdSql = @"
  656 + SELECT
  657 + COUNT(DISTINCT tk.F_MemberId) as kd_count,
  658 + COALESCE(SUM(kd.sfyj), 0) as kd_amount
  659 + FROM lq_tkjlb tk
  660 + LEFT JOIN lq_kd_kdjlb kd ON tk.F_MemberId = kd.kdhy AND kd.F_IsEffective = 1
  661 + WHERE tk.F_EventId = @eventId AND kd.F_Id IS NOT NULL";
  662 +
  663 + var kdResult = await _db.Ado.SqlQueryAsync<dynamic>(kdSql, new { eventId });
  664 + var kdCount = kdResult?.FirstOrDefault()?.kd_count ?? 0;
  665 + var kdAmount = kdResult?.FirstOrDefault()?.kd_amount ?? 0m;
  666 +
  667 + // 计算转化率
  668 + var yyConversionRate = tkCount > 0 ? Math.Round(yyCount * 100.0 / tkCount, 2) : 0;
  669 + var hkConversionRate = yyCount > 0 ? Math.Round(hkCount * 100.0 / yyCount, 2) : 0;
  670 +
  671 + var result = new
  672 + {
  673 + total_tk_count = tkCount,
  674 + total_yaoy_count = yaoyCount,
  675 + total_yy_count = yyCount,
  676 + total_hk_count = hkCount,
  677 + total_kd_count = kdCount,
  678 + total_hk_amount = hkAmount,
  679 + total_kd_amount = kdAmount,
  680 + overall_yy_conversion_rate = yyConversionRate,
  681 + overall_hk_conversion_rate = hkConversionRate
  682 + };
649 683
650 return new 684 return new
651 { 685 {
652 success = true, 686 success = true,
653 - data = result?.FirstOrDefault(),  
654 - message = "获取总体漏斗统计数据成功" 687 + data = result,
  688 + message = "获取总体漏斗统计数据成功(高性能版本)"
655 }; 689 };
656 } 690 }
657 catch (Exception ex) 691 catch (Exception ex)