Commit a30ca3d5eaa888da35dd992a47269cfabaa1361c
1 parent
a78c0ae8
feat: 添加事业部开单统计功能;修复科技部老师统计方法,不使用视图直接从表查询;优化错误处理返回全0结果
Showing
4 changed files
with
386 additions
and
84 deletions
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitBillingStatisticsInput.cs
0 → 100644
| 1 | +using System; | |
| 2 | + | |
| 3 | +namespace NCC.Extend.Entitys.Dto.LqDailyReport | |
| 4 | +{ | |
| 5 | + /// <summary> | |
| 6 | + /// 事业部开单统计查询输入 | |
| 7 | + /// </summary> | |
| 8 | + public class BusinessUnitBillingStatisticsInput | |
| 9 | + { | |
| 10 | + /// <summary> | |
| 11 | + /// 统计日期(格式:yyyy-MM-dd) | |
| 12 | + /// </summary> | |
| 13 | + public string Date { get; set; } | |
| 14 | + } | |
| 15 | +} | |
| 16 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqDailyReport/BusinessUnitBillingStatisticsOutput.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | + | |
| 4 | +namespace NCC.Extend.Entitys.Dto.LqDailyReport | |
| 5 | +{ | |
| 6 | + /// <summary> | |
| 7 | + /// 事业部开单统计输出 | |
| 8 | + /// </summary> | |
| 9 | + public class BusinessUnitBillingStatisticsOutput | |
| 10 | + { | |
| 11 | + /// <summary> | |
| 12 | + /// 事业部ID | |
| 13 | + /// </summary> | |
| 14 | + public string BusinessUnitId { get; set; } | |
| 15 | + | |
| 16 | + /// <summary> | |
| 17 | + /// 事业部名称 | |
| 18 | + /// </summary> | |
| 19 | + public string BusinessUnitName { get; set; } | |
| 20 | + | |
| 21 | + /// <summary> | |
| 22 | + /// 总业绩 | |
| 23 | + /// </summary> | |
| 24 | + public decimal TotalPerformance { get; set; } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 总单量 | |
| 28 | + /// </summary> | |
| 29 | + public int TotalOrderCount { get; set; } | |
| 30 | + | |
| 31 | + /// <summary> | |
| 32 | + /// 开单列表 | |
| 33 | + /// </summary> | |
| 34 | + public List<BillingOrderInfo> Orders { get; set; } | |
| 35 | + } | |
| 36 | + | |
| 37 | + /// <summary> | |
| 38 | + /// 开单信息 | |
| 39 | + /// </summary> | |
| 40 | + public class BillingOrderInfo | |
| 41 | + { | |
| 42 | + /// <summary> | |
| 43 | + /// 开单ID | |
| 44 | + /// </summary> | |
| 45 | + public string OrderId { get; set; } | |
| 46 | + | |
| 47 | + /// <summary> | |
| 48 | + /// 门店名称 | |
| 49 | + /// </summary> | |
| 50 | + public string StoreName { get; set; } | |
| 51 | + | |
| 52 | + /// <summary> | |
| 53 | + /// 健康师姓名(多个用逗号分隔) | |
| 54 | + /// </summary> | |
| 55 | + public string HealthTeacherNames { get; set; } | |
| 56 | + | |
| 57 | + /// <summary> | |
| 58 | + /// 金额 | |
| 59 | + /// </summary> | |
| 60 | + public decimal Amount { get; set; } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 开单时间 | |
| 64 | + /// </summary> | |
| 65 | + public DateTime? OrderTime { get; set; } | |
| 66 | + } | |
| 67 | +} | |
| 68 | + | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqDailyReportService.cs
| ... | ... | @@ -12,6 +12,7 @@ using SqlSugar; |
| 12 | 12 | using System; |
| 13 | 13 | using System.Collections.Generic; |
| 14 | 14 | using System.Linq; |
| 15 | +using System.Text; | |
| 15 | 16 | using System.Threading.Tasks; |
| 16 | 17 | using Yitter.IdGenerator; |
| 17 | 18 | using NCC.Common.Helper; |
| ... | ... | @@ -22,6 +23,10 @@ using NCC.DataEncryption; |
| 22 | 23 | using NCC.ClayObject; |
| 23 | 24 | using NCC.Extend.Entitys.Dto.LqDailyReport; |
| 24 | 25 | using NCC.Extend.Entitys.lq_kd_kdjlb; |
| 26 | +using NCC.Extend.Entitys.lq_kd_jksyj; | |
| 27 | +using NCC.Extend.Entitys.lq_mdxx; | |
| 28 | +using NCC.System.Entitys.Permission; | |
| 29 | +using NCC.Extend.Entitys.Enum; | |
| 25 | 30 | using Microsoft.AspNetCore.Authorization; |
| 26 | 31 | |
| 27 | 32 | namespace NCC.Extend |
| ... | ... | @@ -957,7 +962,198 @@ namespace NCC.Extend |
| 957 | 962 | |
| 958 | 963 | return outputList; |
| 959 | 964 | } |
| 965 | + | |
| 966 | + /// <summary> | |
| 967 | + /// 获取指定日期的事业部开单统计数据 | |
| 968 | + /// </summary> | |
| 969 | + /// <remarks> | |
| 970 | + /// 统计指定日期的事业部开单数据,包括: | |
| 971 | + /// - 只统计有效的开单(F_IsEffective = 1) | |
| 972 | + /// - 只统计有金额的开单(sfyj > 0) | |
| 973 | + /// - 只统计有效的健康师业绩(F_IsEffective = 1) | |
| 974 | + /// - 如果有多个健康师就合并显示 | |
| 975 | + /// - 按照事业部名称排序 | |
| 976 | + /// - 每个事业部内的开单按照时间先后顺序进行排序 | |
| 977 | + /// | |
| 978 | + /// 示例请求: | |
| 979 | + /// ```json | |
| 980 | + /// { | |
| 981 | + /// "date": "2025-11-10" | |
| 982 | + /// } | |
| 983 | + /// ``` | |
| 984 | + /// </remarks> | |
| 985 | + /// <param name="input">查询参数</param> | |
| 986 | + /// <returns>事业部开单统计数据列表</returns> | |
| 987 | + /// <response code="200">成功返回统计数据</response> | |
| 988 | + /// <response code="400">日期格式错误或参数无效</response> | |
| 989 | + /// <response code="500">服务器内部错误</response> | |
| 990 | + [HttpPost("get-business-unit-billing-statistics")] | |
| 991 | + [AllowAnonymous] | |
| 992 | + public async Task<List<BusinessUnitBillingStatisticsOutput>> GetBusinessUnitBillingStatistics(BusinessUnitBillingStatisticsInput input) | |
| 993 | + { | |
| 994 | + try | |
| 995 | + { | |
| 996 | + // 1. 验证日期参数 | |
| 997 | + if (string.IsNullOrEmpty(input.Date)) | |
| 998 | + { | |
| 999 | + throw NCCException.Oh("日期不能为空"); | |
| 1000 | + } | |
| 1001 | + | |
| 1002 | + if (!DateTime.TryParse(input.Date, out var targetDate)) | |
| 1003 | + { | |
| 1004 | + throw NCCException.Oh("日期格式错误,请使用 yyyy-MM-dd 格式"); | |
| 1005 | + } | |
| 1006 | + | |
| 1007 | + // 2. 查询指定日期的有效开单记录(有金额的) | |
| 1008 | + var billingQuery = _db.Queryable<LqKdKdjlbEntity, LqMdxxEntity, OrganizeEntity>( | |
| 1009 | + (billing, store, org) => billing.Djmd == store.Id && store.Syb == org.Id) | |
| 1010 | + .Where((billing, store, org) => billing.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1011 | + .Where((billing, store, org) => billing.Sfyj > 0) // 只统计有金额的开单 | |
| 1012 | + .Where((billing, store, org) => billing.Kdrq.HasValue && billing.Kdrq.Value.Date == targetDate.Date) | |
| 1013 | + .Where((billing, store, org) => org.Category == "department") | |
| 1014 | + .Where((billing, store, org) => org.FullName.Contains("事业")) | |
| 1015 | + .Select((billing, store, org) => new | |
| 1016 | + { | |
| 1017 | + OrderId = billing.Id, | |
| 1018 | + StoreName = store.Dm, | |
| 1019 | + BusinessUnitId = org.Id, | |
| 1020 | + BusinessUnitName = org.FullName, | |
| 1021 | + Amount = billing.Sfyj, | |
| 1022 | + OrderTime = billing.Kdrq, | |
| 1023 | + }) | |
| 1024 | + .ToListAsync(); | |
| 1025 | + | |
| 1026 | + var billingRecords = await billingQuery; | |
| 1027 | + | |
| 1028 | + if (!billingRecords.Any()) | |
| 1029 | + { | |
| 1030 | + return new List<BusinessUnitBillingStatisticsOutput>(); | |
| 1031 | + } | |
| 1032 | + | |
| 1033 | + // 3. 批量查询健康师信息 | |
| 1034 | + var orderIds = billingRecords.Select(x => x.OrderId).Distinct().ToList(); | |
| 1035 | + var healthTeachers = await _db.Queryable<LqKdJksyjEntity>() | |
| 1036 | + .Where(x => orderIds.Contains(x.Glkdbh) && x.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 1037 | + .Select(x => new | |
| 1038 | + { | |
| 1039 | + OrderId = x.Glkdbh, | |
| 1040 | + TeacherName = x.Jksxm, | |
| 1041 | + }) | |
| 1042 | + .ToListAsync(); | |
| 1043 | + | |
| 1044 | + // 4. 按开单ID分组健康师 | |
| 1045 | + var healthTeacherDict = healthTeachers | |
| 1046 | + .GroupBy(x => x.OrderId) | |
| 1047 | + .ToDictionary( | |
| 1048 | + g => g.Key, | |
| 1049 | + g => string.Join("、", g.Select(x => x.TeacherName).Distinct()) | |
| 1050 | + ); | |
| 1051 | + | |
| 1052 | + // 5. 按事业部分组统计 | |
| 1053 | + var result = billingRecords | |
| 1054 | + .GroupBy(x => new { x.BusinessUnitId, x.BusinessUnitName }) | |
| 1055 | + .Select(g => new BusinessUnitBillingStatisticsOutput | |
| 1056 | + { | |
| 1057 | + BusinessUnitId = g.Key.BusinessUnitId, | |
| 1058 | + BusinessUnitName = g.Key.BusinessUnitName, | |
| 1059 | + TotalPerformance = g.Sum(x => x.Amount), | |
| 1060 | + TotalOrderCount = g.Count(), | |
| 1061 | + Orders = g.OrderBy(x => x.OrderTime ?? DateTime.MinValue) | |
| 1062 | + .Select(x => new BillingOrderInfo | |
| 1063 | + { | |
| 1064 | + OrderId = x.OrderId, | |
| 1065 | + StoreName = x.StoreName, | |
| 1066 | + HealthTeacherNames = healthTeacherDict.GetValueOrDefault(x.OrderId, ""), | |
| 1067 | + Amount = x.Amount, | |
| 1068 | + OrderTime = x.OrderTime, | |
| 1069 | + }) | |
| 1070 | + .ToList(), | |
| 1071 | + }) | |
| 1072 | + .OrderBy(x => x.BusinessUnitName) // 按事业部名称排序 | |
| 1073 | + .ToList(); | |
| 1074 | + | |
| 1075 | + return result; | |
| 1076 | + } | |
| 1077 | + catch (Exception ex) | |
| 1078 | + { | |
| 1079 | + throw NCCException.Oh($"获取事业部开单统计数据失败: {ex.Message}", ex); | |
| 1080 | + } | |
| 1081 | + } | |
| 1082 | + | |
| 1083 | + /// <summary> | |
| 1084 | + /// 将事业部开单统计数据格式化为中文文本 | |
| 1085 | + /// </summary> | |
| 1086 | + /// <remarks> | |
| 1087 | + /// 将结构化的统计数据格式化为中文文本格式,用于显示或发送消息。 | |
| 1088 | + /// | |
| 1089 | + /// 示例请求: | |
| 1090 | + /// ```json | |
| 1091 | + /// { | |
| 1092 | + /// "date": "2025-11-10" | |
| 1093 | + /// } | |
| 1094 | + /// ``` | |
| 1095 | + /// | |
| 1096 | + /// 返回格式: | |
| 1097 | + /// ``` | |
| 1098 | + /// 事业一部业绩:xxxx | |
| 1099 | + /// 事业一部总单量:xxx | |
| 1100 | + /// 第一单:xxx店、xxx健康师,金额xxx | |
| 1101 | + /// 第二单:xxx店、xxx健康师,金额xxx | |
| 1102 | + /// | |
| 1103 | + /// 事业二部业绩:xxxx | |
| 1104 | + /// 事业二部总单量:xxx | |
| 1105 | + /// 第一单:xxx店、xxx健康师,金额xxx | |
| 1106 | + /// 第二单:xxx店、xxx健康师,金额xxx | |
| 1107 | + /// ``` | |
| 1108 | + /// </remarks> | |
| 1109 | + /// <param name="input">查询参数</param> | |
| 1110 | + /// <returns>格式化的中文文本</returns> | |
| 1111 | + /// <response code="200">成功返回格式化文本</response> | |
| 1112 | + /// <response code="400">日期格式错误或参数无效</response> | |
| 1113 | + /// <response code="500">服务器内部错误</response> | |
| 1114 | + [HttpPost("get-business-unit-billing-statistics-text")] | |
| 1115 | + [AllowAnonymous] | |
| 1116 | + public async Task<string> GetBusinessUnitBillingStatisticsText(BusinessUnitBillingStatisticsInput input) | |
| 1117 | + { | |
| 1118 | + try | |
| 1119 | + { | |
| 1120 | + // 1. 获取统计数据 | |
| 1121 | + var statistics = await GetBusinessUnitBillingStatistics(input); | |
| 1122 | + | |
| 1123 | + if (!statistics.Any()) | |
| 1124 | + { | |
| 1125 | + return $"日期 {input.Date} 暂无开单数据"; | |
| 1126 | + } | |
| 1127 | + | |
| 1128 | + // 2. 格式化为中文文本 | |
| 1129 | + var textBuilder = new StringBuilder(); | |
| 1130 | + | |
| 1131 | + foreach (var unit in statistics) | |
| 1132 | + { | |
| 1133 | + textBuilder.AppendLine($"{unit.BusinessUnitName}业绩:{unit.TotalPerformance:F2}"); | |
| 1134 | + textBuilder.AppendLine($"{unit.BusinessUnitName}总单量:{unit.TotalOrderCount}"); | |
| 1135 | + | |
| 1136 | + int orderIndex = 1; | |
| 1137 | + foreach (var order in unit.Orders) | |
| 1138 | + { | |
| 1139 | + var teacherNames = string.IsNullOrEmpty(order.HealthTeacherNames) ? "无健康师" : order.HealthTeacherNames; | |
| 1140 | + textBuilder.AppendLine($"第{orderIndex}单:{order.StoreName}、{teacherNames},金额{order.Amount:F2}"); | |
| 1141 | + orderIndex++; | |
| 1142 | + } | |
| 1143 | + | |
| 1144 | + textBuilder.AppendLine(); // 空行分隔 | |
| 1145 | + } | |
| 1146 | + | |
| 1147 | + return textBuilder.ToString().TrimEnd(); | |
| 1148 | + } | |
| 1149 | + catch (Exception ex) | |
| 1150 | + { | |
| 1151 | + throw NCCException.Oh($"获取事业部开单统计文本失败: {ex.Message}", ex); | |
| 1152 | + } | |
| 1153 | + } | |
| 960 | 1154 | #endregion |
| 1155 | + | |
| 1156 | + | |
| 961 | 1157 | } |
| 962 | 1158 | } |
| 963 | 1159 | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
| ... | ... | @@ -28,8 +28,11 @@ using NCC.Extend.Entitys.lq_kd_kjbsyj; |
| 28 | 28 | using NCC.Extend.Entitys.lq_mdxx; |
| 29 | 29 | using NCC.Extend.Entitys.lq_md_xdbhsj; |
| 30 | 30 | using NCC.Extend.Entitys.lq_xh_kjbsyj; |
| 31 | +using NCC.Extend.Entitys.lq_xh_hyhk; | |
| 32 | +using NCC.Extend.Entitys.lq_xh_pxmx; | |
| 31 | 33 | using NCC.Extend.Entitys.lq_ycsd_jsj; |
| 32 | 34 | using NCC.Extend.Entitys.lq_yjmxb; |
| 35 | +using NCC.Extend.Entitys.Enum; | |
| 33 | 36 | using NCC.Extend.Entitys.lq_statistics_gold_triangle; |
| 34 | 37 | using NCC.Extend.Entitys.lq_statistics_personal_performance; |
| 35 | 38 | using NCC.Extend.Entitys.lq_statistics_store_consume_performance; |
| ... | ... | @@ -870,125 +873,144 @@ namespace NCC.Extend.LqStatistics |
| 870 | 873 | public async Task<List<TechTeacherSimpleStatisticsOutput>> GetTechTeacherStatistics(TechTeacherStatisticsInput input) |
| 871 | 874 | { |
| 872 | 875 | try |
| 873 | - { // 1. 从用户表获取所有科技部老师 | |
| 874 | - var allTeachers = await _db.Queryable<UserEntity>() | |
| 875 | - .Where(x => x.Gw == "科技老师") | |
| 876 | + { | |
| 877 | + // 1. 验证必须传入科技老师ID | |
| 878 | + if (string.IsNullOrEmpty(input.TeacherId)) | |
| 879 | + { | |
| 880 | + return new List<TechTeacherSimpleStatisticsOutput> | |
| 881 | + { | |
| 882 | + new TechTeacherSimpleStatisticsOutput | |
| 883 | + { | |
| 884 | + DepartmentName = "科技部", | |
| 885 | + TeacherName = "", | |
| 886 | + OrderAchievement = 0m, | |
| 887 | + OrderItemCount = 0, | |
| 888 | + ConsumeAchievement = 0m, | |
| 889 | + ConsumeItemCount = 0, | |
| 890 | + ConsumeProjectCount = 0, | |
| 891 | + ConsumeLaborCost = 0m, | |
| 892 | + } | |
| 893 | + }; | |
| 894 | + } | |
| 895 | + | |
| 896 | + // 2. 获取科技老师信息 | |
| 897 | + var teacher = await _db.Queryable<UserEntity>() | |
| 898 | + .Where(x => x.Gw == "科技老师" && x.Id == input.TeacherId) | |
| 876 | 899 | .Select(x => new |
| 877 | 900 | { |
| 878 | 901 | TeacherId = x.Id, |
| 879 | 902 | TeacherName = x.RealName, |
| 880 | 903 | TeacherAccount = x.Account, |
| 881 | 904 | }) |
| 882 | - .ToListAsync(); | |
| 883 | - | |
| 884 | - // 2. 获取业绩流水数据 | |
| 885 | - var flowQuery = _db.Queryable<VTechTeacherFlowEntity>(); | |
| 905 | + .FirstAsync(); | |
| 886 | 906 | |
| 887 | - // 老师过滤(支持通过ID或账号匹配) | |
| 888 | - if (!string.IsNullOrEmpty(input.TeacherId)) | |
| 907 | + if (teacher == null) | |
| 889 | 908 | { |
| 890 | - // 先通过用户ID查询账号,然后同时匹配ID和账号 | |
| 891 | - var teacherAccount = allTeachers.FirstOrDefault(t => t.TeacherId == input.TeacherId)?.TeacherAccount; | |
| 892 | - if (!string.IsNullOrEmpty(teacherAccount)) | |
| 909 | + return new List<TechTeacherSimpleStatisticsOutput> | |
| 893 | 910 | { |
| 894 | - // 同时匹配ID和账号(因为视图中的teacher_id可能是ID或账号) | |
| 895 | - flowQuery = flowQuery.Where(x => x.TeacherId == input.TeacherId || x.TeacherAccount == teacherAccount); | |
| 896 | - } | |
| 897 | - else | |
| 898 | - { | |
| 899 | - // 如果找不到账号,只匹配ID | |
| 900 | - flowQuery = flowQuery.Where(x => x.TeacherId == input.TeacherId); | |
| 901 | - } | |
| 911 | + new TechTeacherSimpleStatisticsOutput | |
| 912 | + { | |
| 913 | + DepartmentName = "科技部", | |
| 914 | + TeacherName = "", | |
| 915 | + OrderAchievement = 0m, | |
| 916 | + OrderItemCount = 0, | |
| 917 | + ConsumeAchievement = 0m, | |
| 918 | + ConsumeItemCount = 0, | |
| 919 | + ConsumeProjectCount = 0, | |
| 920 | + ConsumeLaborCost = 0m, | |
| 921 | + } | |
| 922 | + }; | |
| 902 | 923 | } |
| 903 | 924 | |
| 904 | - if (!string.IsNullOrEmpty(input.TeacherName)) | |
| 905 | - { | |
| 906 | - flowQuery = flowQuery.Where(x => x.TeacherName.Contains(input.TeacherName)); | |
| 907 | - } | |
| 925 | + // 3. 查询开单业绩(从 lq_kd_kjbsyj 关联 lq_kd_kdjlb 和 lq_kd_pxmx) | |
| 926 | + var orderQuery = _db.Queryable<LqKdKjbsyjEntity, LqKdKdjlbEntity, LqKdPxmxEntity>( | |
| 927 | + (kjbsyj, kdjlb, pxmx) => kjbsyj.Glkdbh == kdjlb.Id && kjbsyj.Kdpxid == pxmx.Id) | |
| 928 | + .Where((kjbsyj, kdjlb, pxmx) => kjbsyj.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 929 | + .Where((kjbsyj, kdjlb, pxmx) => kdjlb.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 930 | + .Where((kjbsyj, kdjlb, pxmx) => kjbsyj.Kjblszh == teacher.TeacherAccount); | |
| 908 | 931 | |
| 909 | 932 | // 日期过滤 |
| 910 | 933 | if (input.StartDate.HasValue) |
| 911 | 934 | { |
| 912 | - flowQuery = flowQuery.Where(x => x.BusinessDate >= input.StartDate.Value); | |
| 935 | + orderQuery = orderQuery.Where((kjbsyj, kdjlb, pxmx) => kjbsyj.Yjsj >= input.StartDate.Value); | |
| 913 | 936 | } |
| 914 | 937 | |
| 915 | 938 | if (input.EndDate.HasValue) |
| 916 | 939 | { |
| 917 | - flowQuery = flowQuery.Where(x => x.BusinessDate <= input.EndDate.Value); | |
| 940 | + orderQuery = orderQuery.Where((kjbsyj, kdjlb, pxmx) => kjbsyj.Yjsj <= input.EndDate.Value); | |
| 918 | 941 | } |
| 919 | 942 | |
| 920 | - var flowRecords = await flowQuery.ToListAsync(); | |
| 921 | - | |
| 922 | - // 3. 按老师账号分组统计业绩数据(包括耗卡手工费) | |
| 923 | - // 注意:视图中的teacher_id是kjbls,teacher_account是kjblszh,使用账号来匹配更准确 | |
| 924 | - var teacherStatsDict = flowRecords | |
| 925 | - .GroupBy(x => new | |
| 943 | + var orderStats = await orderQuery | |
| 944 | + .Select((kjbsyj, kdjlb, pxmx) => new | |
| 926 | 945 | { |
| 927 | - TeacherAccount = x.TeacherAccount ?? string.Empty, | |
| 928 | - TeacherId = x.TeacherId ?? string.Empty, | |
| 929 | - TeacherName = x.TeacherName ?? string.Empty, | |
| 946 | + OrderAchievement = SqlFunc.ToDecimal(kjbsyj.Kjblsyj), | |
| 947 | + OrderItemCount = SqlFunc.ToInt32(pxmx.ProjectNumber), | |
| 930 | 948 | }) |
| 931 | - .ToDictionary( | |
| 932 | - g => g.Key.TeacherAccount ?? string.Empty, | |
| 933 | - g => new | |
| 934 | - { | |
| 935 | - ConsumeProjectCount = (int)(g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ProjectCount)), | |
| 936 | - ConsumeAchievement = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.Achievement), | |
| 937 | - OrderAchievement = g.Where(x => x.BusinessType == "开卡").Sum(x => x.Achievement), | |
| 938 | - OrderItemCount = g.Where(x => x.BusinessType == "开卡").Sum(x => x.ItemCount), | |
| 939 | - ConsumeItemCount = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.ItemCount), | |
| 940 | - ConsumeLaborCost = g.Where(x => x.BusinessType == "耗卡").Sum(x => x.LaborCost), | |
| 941 | - } | |
| 942 | - ); | |
| 949 | + .ToListAsync(); | |
| 943 | 950 | |
| 944 | - // 4. 构建结果,包含所有老师 | |
| 945 | - var result = new List<TechTeacherSimpleStatisticsOutput>(); | |
| 951 | + // 4. 查询耗卡业绩(从 lq_xh_kjbsyj 关联 lq_xh_hyhk 和 lq_xh_pxmx) | |
| 952 | + // 注意:lq_xh_kjbsyj.kjbls 字段存储的是健康师id,不是科技部老师id | |
| 953 | + // 科技部老师信息在 kjblszh(账号)和 kjblsxm(姓名)字段 | |
| 954 | + var consumeQuery = _db.Queryable<LqXhKjbsyjEntity, LqXhHyhkEntity, LqXhPxmxEntity>( | |
| 955 | + (kjbsyj, hyhk, pxmx) => kjbsyj.Glkdbh == hyhk.Id && kjbsyj.Hkpxid == pxmx.Id) | |
| 956 | + .Where((kjbsyj, hyhk, pxmx) => kjbsyj.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 957 | + .Where((kjbsyj, hyhk, pxmx) => hyhk.IsEffective == StatusEnum.有效.GetHashCode()) | |
| 958 | + .Where((kjbsyj, hyhk, pxmx) => kjbsyj.Kjblszh == teacher.TeacherAccount); | |
| 946 | 959 | |
| 947 | - foreach (var teacher in allTeachers) | |
| 960 | + // 日期过滤 | |
| 961 | + if (input.StartDate.HasValue) | |
| 948 | 962 | { |
| 949 | - // 应用过滤条件 | |
| 950 | - if (!string.IsNullOrEmpty(input.TeacherId) && teacher.TeacherId != input.TeacherId) | |
| 951 | - continue; | |
| 952 | - | |
| 953 | - if (!string.IsNullOrEmpty(input.TeacherName) && !teacher.TeacherName.Contains(input.TeacherName)) | |
| 954 | - continue; | |
| 963 | + consumeQuery = consumeQuery.Where((kjbsyj, hyhk, pxmx) => kjbsyj.Yjsj >= input.StartDate.Value); | |
| 964 | + } | |
| 955 | 965 | |
| 956 | - // 使用账号来匹配业绩数据(因为视图中的teacher_account是kjblszh) | |
| 957 | - var teacherAccount = teacher.TeacherAccount ?? string.Empty; | |
| 958 | - var stats = teacherStatsDict.GetValueOrDefault( | |
| 959 | - teacherAccount, | |
| 960 | - new | |
| 961 | - { | |
| 962 | - ConsumeProjectCount = 0, | |
| 963 | - ConsumeAchievement = 0m, | |
| 964 | - OrderAchievement = 0m, | |
| 965 | - OrderItemCount = 0, | |
| 966 | - ConsumeItemCount = 0, | |
| 967 | - ConsumeLaborCost = 0m, | |
| 968 | - } | |
| 969 | - ); | |
| 966 | + if (input.EndDate.HasValue) | |
| 967 | + { | |
| 968 | + consumeQuery = consumeQuery.Where((kjbsyj, hyhk, pxmx) => kjbsyj.Yjsj <= input.EndDate.Value); | |
| 969 | + } | |
| 970 | 970 | |
| 971 | - var teacherStats = new TechTeacherSimpleStatisticsOutput | |
| 971 | + var consumeStats = await consumeQuery | |
| 972 | + .Select((kjbsyj, hyhk, pxmx) => new | |
| 972 | 973 | { |
| 973 | - DepartmentName = "科技部", // 固定为科技部 | |
| 974 | - TeacherName = teacher.TeacherName, | |
| 975 | - ConsumeProjectCount = stats.ConsumeProjectCount, | |
| 976 | - ConsumeAchievement = stats.ConsumeAchievement, | |
| 977 | - OrderAchievement = stats.OrderAchievement, | |
| 978 | - OrderItemCount = stats.OrderItemCount, | |
| 979 | - ConsumeItemCount = stats.ConsumeItemCount, | |
| 980 | - ConsumeLaborCost = stats.ConsumeLaborCost, | |
| 981 | - }; | |
| 974 | + ConsumeAchievement = kjbsyj.Kjblsyj, | |
| 975 | + ConsumeItemCount = SqlFunc.ToInt32(pxmx.ProjectNumber), | |
| 976 | + ConsumeProjectCount = SqlFunc.ToInt32(kjbsyj.HdpxNumber), | |
| 977 | + ConsumeLaborCost = kjbsyj.LaborCost, | |
| 978 | + }) | |
| 979 | + .ToListAsync(); | |
| 982 | 980 | |
| 983 | - result.Add(teacherStats); | |
| 984 | - } | |
| 981 | + // 5. 统计并返回结果 | |
| 982 | + var result = new TechTeacherSimpleStatisticsOutput | |
| 983 | + { | |
| 984 | + DepartmentName = "科技部", | |
| 985 | + TeacherName = teacher.TeacherName, | |
| 986 | + OrderAchievement = orderStats.Sum(x => x.OrderAchievement != null && decimal.TryParse(x.OrderAchievement.ToString(), out var val) ? val : 0m), | |
| 987 | + OrderItemCount = orderStats.Sum(x => x.OrderItemCount), | |
| 988 | + ConsumeAchievement = consumeStats.Sum(x => x.ConsumeAchievement ?? 0m), | |
| 989 | + ConsumeItemCount = consumeStats.Sum(x => x.ConsumeItemCount), | |
| 990 | + ConsumeProjectCount = consumeStats.Sum(x => x.ConsumeProjectCount), | |
| 991 | + ConsumeLaborCost = consumeStats.Sum(x => x.ConsumeLaborCost ?? 0m), | |
| 992 | + }; | |
| 985 | 993 | |
| 986 | - return result; | |
| 994 | + return new List<TechTeacherSimpleStatisticsOutput> { result }; | |
| 987 | 995 | } |
| 988 | 996 | catch (Exception ex) |
| 989 | 997 | { |
| 990 | 998 | _logger.LogError(ex, "获取科技部老师业绩统计时发生错误"); |
| 991 | - throw NCCException.Oh("获取科技部老师业绩统计失败", ex); | |
| 999 | + // 发生错误时返回全0的结果,而不是抛出异常 | |
| 1000 | + return new List<TechTeacherSimpleStatisticsOutput> | |
| 1001 | + { | |
| 1002 | + new TechTeacherSimpleStatisticsOutput | |
| 1003 | + { | |
| 1004 | + DepartmentName = "科技部", | |
| 1005 | + TeacherName = "", | |
| 1006 | + OrderAchievement = 0m, | |
| 1007 | + OrderItemCount = 0, | |
| 1008 | + ConsumeAchievement = 0m, | |
| 1009 | + ConsumeItemCount = 0, | |
| 1010 | + ConsumeProjectCount = 0, | |
| 1011 | + ConsumeLaborCost = 0m, | |
| 1012 | + } | |
| 1013 | + }; | |
| 992 | 1014 | } |
| 993 | 1015 | } |
| 994 | 1016 | ... | ... |