Commit 11b81187f08d8f74392574e06d24dd7e77a61d59
1 parent
6b697cd3
feat: 优化业绩统计逻辑,按部门ID去重累加完成业绩;添加事业部名称排序功能;更新门店品项统计返回结构,支持层级展示
Showing
5 changed files
with
257 additions
and
95 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqPackageInfo/ActivityStatisticsByStoreItemOutput.cs
| ... | ... | @@ -18,15 +18,15 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo |
| 18 | 18 | public string ActivityName { get; set; } |
| 19 | 19 | |
| 20 | 20 | /// <summary> |
| 21 | - /// 门店品项统计列表 | |
| 21 | + /// 门店统计列表(层级结构:每个门店下包含品项列表) | |
| 22 | 22 | /// </summary> |
| 23 | - public List<StoreItemStatisticsItem> StoreItemList { get; set; } | |
| 23 | + public List<StoreStatisticsWithItems> StoreList { get; set; } | |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | /// <summary> |
| 27 | - /// 门店品项统计项 | |
| 27 | + /// 门店统计(包含品项列表) | |
| 28 | 28 | /// </summary> |
| 29 | - public class StoreItemStatisticsItem | |
| 29 | + public class StoreStatisticsWithItems | |
| 30 | 30 | { |
| 31 | 31 | /// <summary> |
| 32 | 32 | /// 门店ID |
| ... | ... | @@ -39,6 +39,17 @@ namespace NCC.Extend.Entitys.Dto.LqPackageInfo |
| 39 | 39 | public string StoreName { get; set; } |
| 40 | 40 | |
| 41 | 41 | /// <summary> |
| 42 | + /// 该门店下的品项统计列表 | |
| 43 | + /// </summary> | |
| 44 | + public List<StoreItemStatisticsItem> ItemList { get; set; } | |
| 45 | + } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 门店品项统计项 | |
| 49 | + /// </summary> | |
| 50 | + public class StoreItemStatisticsItem | |
| 51 | + { | |
| 52 | + /// <summary> | |
| 42 | 53 | /// 品项ID |
| 43 | 54 | /// </summary> |
| 44 | 55 | public string ItemId { get; set; } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
| ... | ... | @@ -13,6 +13,7 @@ using System; |
| 13 | 13 | using System.Collections.Generic; |
| 14 | 14 | using System.Linq; |
| 15 | 15 | using System.Text; |
| 16 | +using System.Text.RegularExpressions; | |
| 16 | 17 | using System.Threading.Tasks; |
| 17 | 18 | using Yitter.IdGenerator; |
| 18 | 19 | using NCC.Common.Helper; |
| ... | ... | @@ -529,48 +530,39 @@ namespace NCC.Extend |
| 529 | 530 | return new List<TianwangGroupPerformanceCompletionOutput>(); |
| 530 | 531 | } |
| 531 | 532 | |
| 532 | - // 第二步:统计各天王团的完成业绩(分类型统计) | |
| 533 | - var completedPerformanceDataList = new List<dynamic>(); | |
| 533 | + // 第二步:统计各天王团的完成业绩(直接按部门ID统计,避免重复计算) | |
| 534 | + var deptIdsStrForQuery = string.Join("','", departmentDict.Keys); | |
| 534 | 535 | |
| 535 | - foreach (var deptType in tianwangDepartments) | |
| 536 | - { | |
| 537 | - var deptIdsStrForQuery = string.Join("','", departmentDict.Keys); | |
| 538 | - | |
| 539 | - var completedPerformanceSql = $@" | |
| 540 | - SELECT | |
| 541 | - o.F_Id as DepartmentId, | |
| 542 | - COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance | |
| 543 | - FROM lq_kd_kdjlb billing | |
| 544 | - INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id | |
| 545 | - INNER JOIN base_organize o ON store.{deptType.Key} = o.F_Id | |
| 546 | - WHERE billing.F_IsEffective = 1 | |
| 547 | - AND o.F_Category = 'department' | |
| 548 | - AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) | |
| 549 | - AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}' | |
| 550 | - AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}' | |
| 551 | - AND o.F_Id IN ('{deptIdsStrForQuery}') | |
| 552 | - GROUP BY o.F_Id"; | |
| 536 | + // 使用 OR 条件连接三个部门类型字段,确保每个开单记录只被统计一次到对应的部门 | |
| 537 | + var completedPerformanceSql = $@" | |
| 538 | + SELECT | |
| 539 | + o.F_Id as DepartmentId, | |
| 540 | + COALESCE(SUM(billing.sfyj), 0) as CompletedPerformance | |
| 541 | + FROM lq_kd_kdjlb billing | |
| 542 | + INNER JOIN lq_mdxx store ON billing.djmd = store.F_Id | |
| 543 | + INNER JOIN base_organize o ON ( | |
| 544 | + (store.jyb = o.F_Id AND o.F_FullName IN ('教育一部', '教育二部')) | |
| 545 | + OR (store.kjb = o.F_Id AND o.F_FullName IN ('科技一部', '科技二部')) | |
| 546 | + OR (store.dxmb = o.F_Id AND o.F_FullName IN ('大项目一部', '大项目二部')) | |
| 547 | + ) | |
| 548 | + WHERE billing.F_IsEffective = 1 | |
| 549 | + AND o.F_Category = 'department' | |
| 550 | + AND (o.F_DeleteMark IS NULL OR o.F_DeleteMark != 1) | |
| 551 | + AND DATE(billing.kdrq) >= '{startDate:yyyy-MM-dd}' | |
| 552 | + AND DATE(billing.kdrq) <= '{endDate:yyyy-MM-dd}' | |
| 553 | + AND o.F_Id IN ('{deptIdsStrForQuery}') | |
| 554 | + GROUP BY o.F_Id"; | |
| 553 | 555 | |
| 554 | - var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql); | |
| 555 | - if (completedPerformanceData != null) | |
| 556 | - { | |
| 557 | - completedPerformanceDataList.AddRange(completedPerformanceData); | |
| 558 | - } | |
| 559 | - } | |
| 556 | + var completedPerformanceData = await _db.Ado.SqlQueryAsync<dynamic>(completedPerformanceSql); | |
| 560 | 557 | |
| 561 | - // 第三步:合并数据并计算完成率(按部门ID去重累加) | |
| 558 | + // 第三步:将完成业绩数据填充到部门字典中 | |
| 562 | 559 | var departmentPerformanceDict = new Dictionary<string, decimal>(); |
| 563 | - foreach (var item in completedPerformanceDataList) | |
| 560 | + if (completedPerformanceData != null) | |
| 564 | 561 | { |
| 565 | - var departmentId = item.DepartmentId.ToString(); | |
| 566 | - var completedPerformance = Convert.ToDecimal(item.CompletedPerformance); | |
| 567 | - | |
| 568 | - if (departmentPerformanceDict.ContainsKey(departmentId)) | |
| 569 | - { | |
| 570 | - departmentPerformanceDict[departmentId] += completedPerformance; | |
| 571 | - } | |
| 572 | - else | |
| 562 | + foreach (var item in completedPerformanceData) | |
| 573 | 563 | { |
| 564 | + var departmentId = item.DepartmentId.ToString(); | |
| 565 | + var completedPerformance = Convert.ToDecimal(item.CompletedPerformance); | |
| 574 | 566 | departmentPerformanceDict[departmentId] = completedPerformance; |
| 575 | 567 | } |
| 576 | 568 | } |
| ... | ... | @@ -1069,7 +1061,9 @@ namespace NCC.Extend |
| 1069 | 1061 | }) |
| 1070 | 1062 | .ToList(), |
| 1071 | 1063 | }) |
| 1072 | - .OrderBy(x => x.BusinessUnitName) // 按事业部名称排序 | |
| 1064 | + .ToList() | |
| 1065 | + .OrderBy(x => ExtractBusinessUnitNumber(x.BusinessUnitName)) // 按事业部数字排序(事业一部、事业二部...) | |
| 1066 | + .ThenBy(x => x.BusinessUnitName) // 如果数字相同,按名称排序 | |
| 1073 | 1067 | .ToList(); |
| 1074 | 1068 | |
| 1075 | 1069 | return result; |
| ... | ... | @@ -1152,19 +1146,12 @@ namespace NCC.Extend |
| 1152 | 1146 | |
| 1153 | 1147 | // 日期(格式:11月8日) |
| 1154 | 1148 | textBuilder.AppendLine($"{targetDate.Month}月{targetDate.Day}日"); |
| 1155 | - textBuilder.AppendLine(); | |
| 1156 | - | |
| 1157 | 1149 | // 今日总业绩和总单量 |
| 1158 | 1150 | textBuilder.AppendLine($"今日总业绩:{totalPerformance:F0}"); |
| 1159 | - textBuilder.AppendLine(); | |
| 1160 | 1151 | textBuilder.AppendLine($"今日总单量:{totalOrderCount}"); |
| 1161 | - textBuilder.AppendLine(); | |
| 1162 | - | |
| 1163 | 1152 | // 标语 |
| 1164 | 1153 | textBuilder.AppendLine("绿纤人用结果捍卫尊严 "); |
| 1165 | - textBuilder.AppendLine(); | |
| 1166 | 1154 | textBuilder.AppendLine("业绩捷报 "); |
| 1167 | - textBuilder.AppendLine(); | |
| 1168 | 1155 | |
| 1169 | 1156 | // 每个事业部的数据 |
| 1170 | 1157 | foreach (var unit in statistics) |
| ... | ... | @@ -1175,10 +1162,7 @@ namespace NCC.Extend |
| 1175 | 1162 | |
| 1176 | 1163 | // 事业部业绩和单量 |
| 1177 | 1164 | textBuilder.AppendLine($"{unit.BusinessUnitName}总业绩:{unit.TotalPerformance:F0}"); |
| 1178 | - textBuilder.AppendLine(); | |
| 1179 | 1165 | textBuilder.AppendLine($"{unit.BusinessUnitName}总单量:{unit.TotalOrderCount}"); |
| 1180 | - textBuilder.AppendLine(); | |
| 1181 | - | |
| 1182 | 1166 | // 按门店分组订单 |
| 1183 | 1167 | var ordersByStore = unit.Orders |
| 1184 | 1168 | .GroupBy(x => x.StoreName) |
| ... | ... | @@ -1207,7 +1191,6 @@ namespace NCC.Extend |
| 1207 | 1191 | { |
| 1208 | 1192 | textBuilder.AppendLine($"第{orderIndex}单:{order.StoreName}{teacherNames}{order.Amount:F0}"); |
| 1209 | 1193 | } |
| 1210 | - textBuilder.AppendLine(); | |
| 1211 | 1194 | orderIndex++; |
| 1212 | 1195 | } |
| 1213 | 1196 | } |
| ... | ... | @@ -1220,6 +1203,84 @@ namespace NCC.Extend |
| 1220 | 1203 | throw NCCException.Oh($"获取事业部开单统计文本失败: {ex.Message}", ex); |
| 1221 | 1204 | } |
| 1222 | 1205 | } |
| 1206 | + | |
| 1207 | + /// <summary> | |
| 1208 | + /// 从事业部名称中提取数字(用于排序) | |
| 1209 | + /// 例如:"事业一部" -> 1, "事业二部" -> 2, "事业三部" -> 3 | |
| 1210 | + /// </summary> | |
| 1211 | + /// <param name="businessUnitName">事业部名称</param> | |
| 1212 | + /// <returns>提取的数字,如果提取失败返回999(排在最后)</returns> | |
| 1213 | + private int ExtractBusinessUnitNumber(string businessUnitName) | |
| 1214 | + { | |
| 1215 | + if (string.IsNullOrEmpty(businessUnitName)) | |
| 1216 | + { | |
| 1217 | + return 999; | |
| 1218 | + } | |
| 1219 | + | |
| 1220 | + // 使用正则表达式提取数字 | |
| 1221 | + var match = Regex.Match(businessUnitName, @"[一二三四五六七八九十]+|[\d]+"); | |
| 1222 | + if (match.Success) | |
| 1223 | + { | |
| 1224 | + var numberStr = match.Value; | |
| 1225 | + | |
| 1226 | + // 如果是中文数字,转换为阿拉伯数字 | |
| 1227 | + if (Regex.IsMatch(numberStr, @"[一二三四五六七八九十]+")) | |
| 1228 | + { | |
| 1229 | + var chineseNumbers = new Dictionary<string, int> | |
| 1230 | + { | |
| 1231 | + { "一", 1 }, { "二", 2 }, { "三", 3 }, { "四", 4 }, { "五", 5 }, | |
| 1232 | + { "六", 6 }, { "七", 7 }, { "八", 8 }, { "九", 9 }, { "十", 10 } | |
| 1233 | + }; | |
| 1234 | + | |
| 1235 | + // 处理"十"的情况(如"十一"、"十二"等) | |
| 1236 | + if (numberStr == "十") | |
| 1237 | + { | |
| 1238 | + return 10; | |
| 1239 | + } | |
| 1240 | + else if (numberStr.StartsWith("十")) | |
| 1241 | + { | |
| 1242 | + // 十一、十二...十九 | |
| 1243 | + var remainder = numberStr.Substring(1); | |
| 1244 | + if (chineseNumbers.ContainsKey(remainder)) | |
| 1245 | + { | |
| 1246 | + return 10 + chineseNumbers[remainder]; | |
| 1247 | + } | |
| 1248 | + } | |
| 1249 | + else if (numberStr.EndsWith("十")) | |
| 1250 | + { | |
| 1251 | + // 二十、三十...九十 | |
| 1252 | + var prefix = numberStr.Substring(0, numberStr.Length - 1); | |
| 1253 | + if (chineseNumbers.ContainsKey(prefix)) | |
| 1254 | + { | |
| 1255 | + return chineseNumbers[prefix] * 10; | |
| 1256 | + } | |
| 1257 | + } | |
| 1258 | + else if (numberStr.Length == 2 && numberStr.Contains("十")) | |
| 1259 | + { | |
| 1260 | + // 二十一、二十二...九十九 | |
| 1261 | + var parts = numberStr.Split('十'); | |
| 1262 | + if (parts.Length == 2) | |
| 1263 | + { | |
| 1264 | + var tens = parts[0] == "" ? 1 : (chineseNumbers.ContainsKey(parts[0]) ? chineseNumbers[parts[0]] : 0); | |
| 1265 | + var ones = parts[1] == "" ? 0 : (chineseNumbers.ContainsKey(parts[1]) ? chineseNumbers[parts[1]] : 0); | |
| 1266 | + return tens * 10 + ones; | |
| 1267 | + } | |
| 1268 | + } | |
| 1269 | + else if (chineseNumbers.ContainsKey(numberStr)) | |
| 1270 | + { | |
| 1271 | + return chineseNumbers[numberStr]; | |
| 1272 | + } | |
| 1273 | + } | |
| 1274 | + else if (int.TryParse(numberStr, out var number)) | |
| 1275 | + { | |
| 1276 | + // 如果是阿拉伯数字,直接返回 | |
| 1277 | + return number; | |
| 1278 | + } | |
| 1279 | + } | |
| 1280 | + | |
| 1281 | + // 如果提取失败,返回999(排在最后) | |
| 1282 | + return 999; | |
| 1283 | + } | |
| 1223 | 1284 | #endregion |
| 1224 | 1285 | |
| 1225 | 1286 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqPackageInfoService.cs
| ... | ... | @@ -901,6 +901,7 @@ namespace NCC.Extend.LqPackageInfo |
| 901 | 901 | } |
| 902 | 902 | |
| 903 | 903 | // 5. 按品项分组统计(先获取基础统计数据) |
| 904 | + // 注意:SqlSugar 的 AggregateSum 可能不支持三元运算符,需要先查询数据再计算 | |
| 904 | 905 | var itemStatistics = await baseQuery |
| 905 | 906 | .GroupBy((px, kd) => new { ItemId = px.Px, ItemName = px.Pxmc }) |
| 906 | 907 | .Select((px, kd) => new |
| ... | ... | @@ -908,14 +909,25 @@ namespace NCC.Extend.LqPackageInfo |
| 908 | 909 | ItemId = px.Px ?? "", |
| 909 | 910 | ItemName = px.Pxmc ?? "", |
| 910 | 911 | SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber), |
| 911 | - SalesAmount = SqlFunc.AggregateSum(px.ActualPrice > 0 ? px.ActualPrice : px.TotalPrice), | |
| 912 | + SalesAmountActual = SqlFunc.AggregateSum(px.ActualPrice), | |
| 913 | + SalesAmountTotal = SqlFunc.AggregateSum(px.TotalPrice), | |
| 912 | 914 | SalesCount = SqlFunc.AggregateCount(px.Id) |
| 913 | 915 | }) |
| 914 | 916 | .ToListAsync(); |
| 915 | 917 | |
| 918 | + // 计算实际销售金额(ActualPrice > 0 时使用 ActualPrice,否则使用 TotalPrice) | |
| 919 | + var itemStatisticsWithAmount = itemStatistics.Select(stat => new | |
| 920 | + { | |
| 921 | + stat.ItemId, | |
| 922 | + stat.ItemName, | |
| 923 | + stat.SalesQuantity, | |
| 924 | + SalesAmount = stat.SalesAmountActual > 0 ? stat.SalesAmountActual : stat.SalesAmountTotal, | |
| 925 | + stat.SalesCount | |
| 926 | + }).ToList(); | |
| 927 | + | |
| 916 | 928 | // 6. 单独统计每个品项的开单数量(去重开单ID) |
| 917 | 929 | var itemList = new List<ItemStatisticsItem>(); |
| 918 | - foreach (var stat in itemStatistics) | |
| 930 | + foreach (var stat in itemStatisticsWithAmount) | |
| 919 | 931 | { |
| 920 | 932 | var billingCountQuery = _db.Queryable<LqKdPxmxEntity, LqKdKdjlbEntity>( |
| 921 | 933 | (px, kd) => px.Glkdbh == kd.Id) |
| ... | ... | @@ -981,6 +993,20 @@ namespace NCC.Extend.LqPackageInfo |
| 981 | 993 | /// "storeIds": ["门店ID1", "门店ID2"] |
| 982 | 994 | /// } |
| 983 | 995 | /// ``` |
| 996 | + /// | |
| 997 | + /// 返回格式(层级结构): | |
| 998 | + /// - ActivityId: 营销活动ID | |
| 999 | + /// - ActivityName: 营销活动名称 | |
| 1000 | + /// - StoreList: 门店统计列表 | |
| 1001 | + /// - StoreId: 门店ID | |
| 1002 | + /// - StoreName: 门店名称 | |
| 1003 | + /// - ItemList: 该门店下的品项统计列表 | |
| 1004 | + /// - ItemId: 品项ID | |
| 1005 | + /// - ItemName: 品项名称 | |
| 1006 | + /// - SalesQuantity: 销售数量(项目次数总和) | |
| 1007 | + /// - SalesAmount: 销售金额 | |
| 1008 | + /// - BillingCount: 开单数量(包含该品项的开单数) | |
| 1009 | + /// - SalesCount: 销售次数(品项明细记录数) | |
| 984 | 1010 | /// </remarks> |
| 985 | 1011 | /// <param name="input">查询参数</param> |
| 986 | 1012 | /// <returns>按门店品项统计数据</returns> |
| ... | ... | @@ -1021,6 +1047,7 @@ namespace NCC.Extend.LqPackageInfo |
| 1021 | 1047 | } |
| 1022 | 1048 | |
| 1023 | 1049 | // 5. 按门店和品项分组统计(先获取基础统计数据) |
| 1050 | + // 注意:SqlSugar 的 AggregateSum 可能不支持三元运算符,需要先查询数据再计算 | |
| 1024 | 1051 | var storeItemStatistics = await baseQuery |
| 1025 | 1052 | .GroupBy((px, kd, md) => new |
| 1026 | 1053 | { |
| ... | ... | @@ -1036,14 +1063,28 @@ namespace NCC.Extend.LqPackageInfo |
| 1036 | 1063 | ItemId = px.Px ?? "", |
| 1037 | 1064 | ItemName = px.Pxmc ?? "", |
| 1038 | 1065 | SalesQuantity = SqlFunc.AggregateSum(px.ProjectNumber), |
| 1039 | - SalesAmount = SqlFunc.AggregateSum(px.ActualPrice > 0 ? px.ActualPrice : px.TotalPrice), | |
| 1066 | + SalesAmountActual = SqlFunc.AggregateSum(px.ActualPrice), | |
| 1067 | + SalesAmountTotal = SqlFunc.AggregateSum(px.TotalPrice), | |
| 1040 | 1068 | SalesCount = SqlFunc.AggregateCount(px.Id) |
| 1041 | 1069 | }) |
| 1042 | 1070 | .ToListAsync(); |
| 1043 | 1071 | |
| 1044 | - // 6. 单独统计每个门店品项的开单数量(去重开单ID) | |
| 1045 | - var storeItemList = new List<StoreItemStatisticsItem>(); | |
| 1046 | - foreach (var stat in storeItemStatistics) | |
| 1072 | + // 计算实际销售金额(ActualPrice > 0 时使用 ActualPrice,否则使用 TotalPrice) | |
| 1073 | + var storeItemStatisticsWithAmount = storeItemStatistics.Select(stat => new | |
| 1074 | + { | |
| 1075 | + stat.StoreId, | |
| 1076 | + stat.StoreName, | |
| 1077 | + stat.ItemId, | |
| 1078 | + stat.ItemName, | |
| 1079 | + stat.SalesQuantity, | |
| 1080 | + SalesAmount = stat.SalesAmountActual > 0 ? stat.SalesAmountActual : stat.SalesAmountTotal, | |
| 1081 | + stat.SalesCount | |
| 1082 | + }).ToList(); | |
| 1083 | + | |
| 1084 | + // 6. 单独统计每个门店品项的开单数量(去重开单ID),并组装成层级结构 | |
| 1085 | + var storeItemDict = new Dictionary<string, List<StoreItemStatisticsItem>>(); | |
| 1086 | + | |
| 1087 | + foreach (var stat in storeItemStatisticsWithAmount) | |
| 1047 | 1088 | { |
| 1048 | 1089 | var billingCountQuery = _db.Queryable<LqKdPxmxEntity, LqKdKdjlbEntity>( |
| 1049 | 1090 | (px, kd) => px.Glkdbh == kd.Id) |
| ... | ... | @@ -1064,28 +1105,53 @@ namespace NCC.Extend.LqPackageInfo |
| 1064 | 1105 | .Select((px, kd) => px.Glkdbh) |
| 1065 | 1106 | .CountAsync(); |
| 1066 | 1107 | |
| 1067 | - storeItemList.Add(new StoreItemStatisticsItem | |
| 1108 | + var itemStat = new StoreItemStatisticsItem | |
| 1068 | 1109 | { |
| 1069 | - StoreId = stat.StoreId ?? "", | |
| 1070 | - StoreName = stat.StoreName ?? "", | |
| 1071 | 1110 | ItemId = stat.ItemId ?? "", |
| 1072 | 1111 | ItemName = stat.ItemName ?? "", |
| 1073 | 1112 | SalesQuantity = stat.SalesQuantity, |
| 1074 | 1113 | SalesAmount = stat.SalesAmount, |
| 1075 | 1114 | BillingCount = billingCount, |
| 1076 | 1115 | SalesCount = stat.SalesCount |
| 1077 | - }); | |
| 1116 | + }; | |
| 1117 | + | |
| 1118 | + // 按门店分组 | |
| 1119 | + var storeKey = stat.StoreId ?? ""; | |
| 1120 | + if (!storeItemDict.ContainsKey(storeKey)) | |
| 1121 | + { | |
| 1122 | + storeItemDict[storeKey] = new List<StoreItemStatisticsItem>(); | |
| 1123 | + } | |
| 1124 | + storeItemDict[storeKey].Add(itemStat); | |
| 1078 | 1125 | } |
| 1079 | 1126 | |
| 1080 | - // 7. 排序 | |
| 1081 | - storeItemList = storeItemList.OrderBy(x => x.StoreName).ThenBy(x => x.ItemName).ToList(); | |
| 1127 | + // 7. 组装层级结构:门店 -> 品项列表 | |
| 1128 | + var storeList = new List<StoreStatisticsWithItems>(); | |
| 1129 | + foreach (var kvp in storeItemDict) | |
| 1130 | + { | |
| 1131 | + var firstItem = storeItemStatistics.FirstOrDefault(s => s.StoreId == kvp.Key); | |
| 1132 | + if (firstItem != null) | |
| 1133 | + { | |
| 1134 | + // 对品项列表排序 | |
| 1135 | + var sortedItemList = kvp.Value.OrderBy(x => x.ItemName).ToList(); | |
| 1082 | 1136 | |
| 1083 | - // 7. 返回统计结果 | |
| 1137 | + storeList.Add(new StoreStatisticsWithItems | |
| 1138 | + { | |
| 1139 | + StoreId = firstItem.StoreId ?? "", | |
| 1140 | + StoreName = firstItem.StoreName ?? "", | |
| 1141 | + ItemList = sortedItemList | |
| 1142 | + }); | |
| 1143 | + } | |
| 1144 | + } | |
| 1145 | + | |
| 1146 | + // 8. 按门店名称排序 | |
| 1147 | + storeList = storeList.OrderBy(x => x.StoreName).ToList(); | |
| 1148 | + | |
| 1149 | + // 9. 返回统计结果 | |
| 1084 | 1150 | return new ActivityStatisticsByStoreItemOutput |
| 1085 | 1151 | { |
| 1086 | 1152 | ActivityId = input.ActivityId, |
| 1087 | 1153 | ActivityName = activity.ActivityName, |
| 1088 | - StoreItemList = storeItemList | |
| 1154 | + StoreList = storeList | |
| 1089 | 1155 | }; |
| 1090 | 1156 | } |
| 1091 | 1157 | catch (Exception ex) | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
| ... | ... | @@ -1073,45 +1073,62 @@ namespace NCC.Extend.LqStatistics |
| 1073 | 1073 | var startDate = new DateTime(now.Year, now.Month, 1); |
| 1074 | 1074 | var endDate = startDate.AddMonths(1).AddDays(-1); |
| 1075 | 1075 | |
| 1076 | - // 构建查询参数 | |
| 1077 | - var parameters = new Dictionary<string, object> { { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") }, { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") } }; | |
| 1076 | + // 获取当前月份(YYYYMM格式) | |
| 1077 | + var currentMonth = now.ToString("yyyyMM"); | |
| 1078 | 1078 | |
| 1079 | - // 查询本月全门店统计数据 | |
| 1080 | - var sql = | |
| 1081 | - @" | |
| 1079 | + // 1. 获取本月的门店目标业绩总和(从门店目标表获取 F_StoreTarget 字段,按月份过滤) | |
| 1080 | + var targetPerformanceSql = @" | |
| 1082 | 1081 | SELECT |
| 1083 | - SUM(target_performance) AS TotalTargetPerformance, | |
| 1084 | - SUM(actual_performance) AS TotalActualPerformance, | |
| 1085 | - CASE | |
| 1086 | - WHEN SUM(target_performance) > 0 | |
| 1087 | - THEN (SUM(actual_performance) / SUM(target_performance)) * 100 | |
| 1088 | - ELSE 0 | |
| 1089 | - END AS TotalCompletionRate | |
| 1090 | - FROM v_store_performance_simple"; | |
| 1082 | + COALESCE(SUM(F_StoreTarget), 0) AS TotalTargetPerformance | |
| 1083 | + FROM lq_md_target | |
| 1084 | + WHERE F_Month = @month"; | |
| 1091 | 1085 | |
| 1092 | - var result = await _db.Ado.SqlQueryAsync<dynamic>(sql, parameters); | |
| 1093 | - var stats = result.FirstOrDefault(); | |
| 1086 | + var targetParameters = new Dictionary<string, object> | |
| 1087 | + { | |
| 1088 | + { "@month", currentMonth } | |
| 1089 | + }; | |
| 1094 | 1090 | |
| 1095 | - if (stats != null) | |
| 1091 | + var targetResult = await _db.Ado.SqlQueryAsync<dynamic>(targetPerformanceSql, targetParameters); | |
| 1092 | + var targetPerformance = 0m; | |
| 1093 | + if (targetResult != null && targetResult.Any()) | |
| 1096 | 1094 | { |
| 1097 | - var targetPerformance = Convert.ToDecimal(stats.TotalTargetPerformance ?? 0); | |
| 1098 | - var actualPerformance = Convert.ToDecimal(stats.TotalActualPerformance ?? 0); | |
| 1099 | - var completionRate = Convert.ToDecimal(stats.TotalCompletionRate ?? 0); | |
| 1095 | + targetPerformance = Convert.ToDecimal(targetResult.FirstOrDefault()?.TotalTargetPerformance ?? 0); | |
| 1096 | + } | |
| 1100 | 1097 | |
| 1101 | - return new | |
| 1102 | - { | |
| 1103 | - TargetPerformance = targetPerformance, | |
| 1104 | - ActualPerformance = actualPerformance, | |
| 1105 | - CompletionRate = completionRate, | |
| 1106 | - Month = now.ToString("yyyy年MM月"), | |
| 1107 | - }; | |
| 1098 | + // 2. 从开单记录表获取本月实付金额的总和(sfyj 字段) | |
| 1099 | + var actualPerformanceSql = @" | |
| 1100 | + SELECT | |
| 1101 | + COALESCE(SUM(sfyj), 0) AS TotalActualPerformance | |
| 1102 | + FROM lq_kd_kdjlb | |
| 1103 | + WHERE F_IsEffective = 1 | |
| 1104 | + AND kdrq >= @startDate | |
| 1105 | + AND kdrq <= @endDate"; | |
| 1106 | + | |
| 1107 | + var parameters = new Dictionary<string, object> | |
| 1108 | + { | |
| 1109 | + { "@startDate", startDate.ToString("yyyy-MM-dd 00:00:00") }, | |
| 1110 | + { "@endDate", endDate.ToString("yyyy-MM-dd 23:59:59") } | |
| 1111 | + }; | |
| 1112 | + | |
| 1113 | + var actualResult = await _db.Ado.SqlQueryAsync<dynamic>(actualPerformanceSql, parameters); | |
| 1114 | + var actualPerformance = 0m; | |
| 1115 | + if (actualResult != null && actualResult.Any()) | |
| 1116 | + { | |
| 1117 | + actualPerformance = Convert.ToDecimal(actualResult.FirstOrDefault()?.TotalActualPerformance ?? 0); | |
| 1118 | + } | |
| 1119 | + | |
| 1120 | + // 3. 计算完成率 | |
| 1121 | + var completionRate = 0m; | |
| 1122 | + if (targetPerformance > 0) | |
| 1123 | + { | |
| 1124 | + completionRate = (actualPerformance / targetPerformance) * 100m; | |
| 1108 | 1125 | } |
| 1109 | 1126 | |
| 1110 | 1127 | return new |
| 1111 | 1128 | { |
| 1112 | - TargetPerformance = 0m, | |
| 1113 | - ActualPerformance = 0m, | |
| 1114 | - CompletionRate = 0m, | |
| 1129 | + TargetPerformance = targetPerformance, | |
| 1130 | + ActualPerformance = actualPerformance, | |
| 1131 | + CompletionRate = decimal.Round(completionRate, 2), | |
| 1115 | 1132 | Month = now.ToString("yyyy年MM月"), |
| 1116 | 1133 | }; |
| 1117 | 1134 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
| ... | ... | @@ -964,6 +964,13 @@ namespace NCC.Extend.LqXhHyhk |
| 964 | 964 | { |
| 965 | 965 | // 开启事务 |
| 966 | 966 | _db.BeginTran(); |
| 967 | + // 查询会员信息 | |
| 968 | + var memberInfo = await _db.Queryable<LqKhxxEntity>().Where(w => w.Id == entity.Hy).FirstAsync(); | |
| 969 | + //如果会员类型是线索,那么就更新为新客 | |
| 970 | + if (memberInfo.Khlx == MemberTypeEnum.线索.GetHashCode().ToString()) | |
| 971 | + { | |
| 972 | + memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); | |
| 973 | + } | |
| 967 | 974 | // 新增会员耗卡记录 |
| 968 | 975 | var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync(); |
| 969 | 976 | // 收集所有需要插入的实体,然后批量插入 | ... | ... |