diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys.Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys.Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs new file mode 100644 index 0000000..c50954a --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys.Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs @@ -0,0 +1,51 @@ +using System; + +namespace NCC.Extend.Entitys.Dto.LqTechTeacherSalary +{ + /// + /// 科技部老师统计数据输出 + /// + public class TechTeacherStatisticsOutput + { + /// + /// 员工ID + /// + public string EmployeeId { get; set; } + + /// + /// 员工姓名 + /// + public string EmployeeName { get; set; } + + /// + /// 开单业绩 + /// + public decimal OrderAchievement { get; set; } + + /// + /// 消耗业绩 + /// + public decimal ConsumeAchievement { get; set; } + + /// + /// 退卡业绩 + /// + public decimal RefundAchievement { get; set; } + + /// + /// 人头(按月份+客户去重统计) + /// + public int PersonCount { get; set; } + + /// + /// 人次(按日期+客户去重统计) + /// + public decimal PersonTimes { get; set; } + + /// + /// 手工费 + /// + public decimal LaborCost { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsageApplication/LqInventoryUsageApplicationListOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsageApplication/LqInventoryUsageApplicationListOutput.cs index 930dc84..a3fc6ec 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsageApplication/LqInventoryUsageApplicationListOutput.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqInventoryUsageApplication/LqInventoryUsageApplicationListOutput.cs @@ -89,6 +89,11 @@ namespace NCC.Extend.Entitys.Dto.LqInventoryUsageApplication public List currentApprovers { get; set; } /// + /// 当前审批人ID列表(用于快速获取审批人ID) + /// + public List approverIds { get; set; } + + /// /// 使用记录统计信息(批次总数量、总金额等) /// public BatchUsageInfo batchUsageInfo { get; set; } diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsInput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsInput.cs new file mode 100644 index 0000000..2ee97b8 --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsInput.cs @@ -0,0 +1,18 @@ +namespace NCC.Extend.Entitys.Dto.LqTechTeacherSalary +{ + /// + /// 科技部老师统计数据查询参数 + /// + public class TechTeacherStatisticsInput + { + /// + /// 年份 + /// + public int Year { get; set; } + + /// + /// 月份(1-12) + /// + public int Month { get; set; } + } +} diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs new file mode 100644 index 0000000..aea0d3b --- /dev/null +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqTechTeacherSalary/TechTeacherStatisticsOutput.cs @@ -0,0 +1,51 @@ +using System; + +namespace NCC.Extend.Entitys.Dto.LqTechTeacherSalary +{ + /// + /// 科技部老师统计数据输出 + /// + public class TechTeacherStatisticsOutput + { + /// + /// 员工ID + /// + public string EmployeeId { get; set; } + + /// + /// 员工姓名 + /// + public string EmployeeName { get; set; } + + /// + /// 开单业绩 + /// + public decimal OrderAchievement { get; set; } + + /// + /// 消耗业绩 + /// + public decimal ConsumeAchievement { get; set; } + + /// + /// 退卡业绩 + /// + public decimal RefundAchievement { get; set; } + + /// + /// 人头(按月份+客户去重统计) + /// + public int PersonCount { get; set; } + + /// + /// 人次(按日期+客户去重统计) + /// + public decimal PersonTimes { get; set; } + + /// + /// 手工费(仅统计耗卡中的手工费) + /// + public decimal LaborCost { get; set; } + } +} + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs index 68fa78a..07cc06b 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_application_node/LqInventoryUsageApplicationNodeEntity.cs @@ -64,3 +64,4 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_application_node + diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs index b9933d3..e4472cc 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_inventory_usage_approval_record/LqInventoryUsageApprovalRecordEntity.cs @@ -82,3 +82,4 @@ namespace NCC.Extend.Entitys.lq_inventory_usage_approval_record + diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs index 8c53d98..8d53f7c 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqInventoryUsageService.cs @@ -428,7 +428,7 @@ namespace NCC.Extend CreateTime = DateTime.Now, IsEffective = StatusEnum.有效.GetHashCode() }; - + // 设置申请总金额(该批次所有商品的总价) // 使用反射设置,避免服务未重启时找不到属性的问题 try @@ -450,6 +450,27 @@ namespace NCC.Extend { throw NCCException.Oh("创建申请记录失败"); } + + // 创建审批记录(当前节点的审批人) + var approvalRecord = new LqInventoryUsageApprovalRecordEntity + { + Id = YitIdHelper.NextId().ToString(), + ApplicationId = applicationEntity.Id, + NodeId = applicationEntity.CurrentNodeId, + NodeOrder = 1, + ApproverId = approverUser.Id, + ApproverName = approverUser.RealName ?? "", + ApprovalResult = "待审批", + ApprovalOpinion = "", + ApprovalTime = DateTime.Now, + IsCurrentNode = 1 + }; + + var approvalInsertCount = await _db.Insertable(approvalRecord).ExecuteCommandAsync(); + if (approvalInsertCount <= 0) + { + throw NCCException.Oh("创建审批记录失败"); + } } _db.Ado.CommitTran(); @@ -779,7 +800,7 @@ namespace NCC.Extend ReceiveUser = application.ReceiveUser, Remarks = application.Remarks }; - + // 设置申请总金额(该批次所有商品的总价) // 使用反射设置,避免服务未重启时找不到属性的问题 try @@ -1595,8 +1616,8 @@ namespace NCC.Extend // 关键字搜索(申请编号、申请人姓名、门店名称) if (!string.IsNullOrWhiteSpace(input.keyword)) { - query = query.Where((app, store) => - app.Id.Contains(input.keyword) || + query = query.Where((app, store) => + app.Id.Contains(input.keyword) || app.ApplicationUserName.Contains(input.keyword) || store.Dm.Contains(input.keyword)); } @@ -1651,17 +1672,20 @@ namespace NCC.Extend // 获取所有申请ID,用于查询审批人和使用记录统计 var applicationIds = pageList.list.Select(x => x.id).ToList(); + // 初始化审批人相关数据 + var approvalRecords = new List(); + var approverDict = new Dictionary(); + if (applicationIds.Any()) { // 查询当前审批人信息(通过审批记录表,查找当前节点的审批人) - var approvalRecords = await _db.Queryable() + approvalRecords = await _db.Queryable() .Where(x => applicationIds.Contains(x.ApplicationId) && x.IsCurrentNode == 1) .Where(x => x.ApprovalResult == "待审批" || x.ApprovalResult == "") .ToListAsync(); // 获取审批人ID列表 - var approverIds = approvalRecords.Select(x => x.ApproverId).Distinct().ToList(); - var approverDict = new Dictionary(); + var approverIds = approvalRecords.Select(x => x.ApproverId).Distinct().Where(x => !string.IsNullOrEmpty(x)).ToList(); if (approverIds.Any()) { var approvers = await _db.Queryable() @@ -1670,9 +1694,13 @@ namespace NCC.Extend .ToListAsync(); approverDict = approvers.ToDictionary(k => k.Id, v => v.RealName ?? ""); } + } - // 查询使用记录统计(按批次ID分组) - var batchIds = pageList.list.Select(x => x.usageBatchId).Distinct().ToList(); + // 查询使用记录统计(按批次ID分组) + var batchIds = pageList.list.Select(x => x.usageBatchId).Distinct().ToList(); + var usageDict = new Dictionary(); + if (batchIds.Any()) + { var usageRecords = await _db.Queryable() .Where(x => batchIds.Contains(x.UsageBatchId)) .Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()) @@ -1686,64 +1714,70 @@ namespace NCC.Extend }) .ToListAsync(); - var usageDict = usageRecords.ToDictionary(k => k.UsageBatchId, v => v); + usageDict = usageRecords.ToDictionary(k => k.UsageBatchId, v => (dynamic)v); + } - // 查询领取人信息 - var receiveUserIds = pageList.list.Where(x => !string.IsNullOrWhiteSpace(x.receiveUser)) - .Select(x => x.receiveUser) + // 查询领取人信息 + var receiveUserIds = pageList.list.Where(x => !string.IsNullOrWhiteSpace(x.receiveUser)) + .Select(x => x.receiveUser) + .Distinct() + .ToList(); + var receiveUserDict = new Dictionary(); + if (receiveUserIds.Any()) + { + var receiveUsers = await _db.Queryable() + .Where(x => receiveUserIds.Contains(x.Id)) + .Select(x => new { x.Id, x.RealName }) + .ToListAsync(); + receiveUserDict = receiveUsers.ToDictionary(k => k.Id, v => v.RealName ?? ""); + } + + // 填充审批人和使用记录统计信息 + foreach (var item in pageList.list) + { + // 当前审批人信息 + var currentApprovers = approvalRecords + .Where(x => x.ApplicationId == item.id) + .Select(x => new CurrentApproverInfo + { + approverId = x.ApproverId, + approverName = approverDict.ContainsKey(x.ApproverId) ? approverDict[x.ApproverId] : x.ApproverName, + approvalResult = x.ApprovalResult + }) + .ToList(); + item.currentApprovers = currentApprovers ?? new List(); + // 审批人ID列表(从审批人信息中提取) + item.approverIds = currentApprovers + .Where(x => !string.IsNullOrEmpty(x.approverId)) + .Select(x => x.approverId) .Distinct() .ToList(); - var receiveUserDict = new Dictionary(); - if (receiveUserIds.Any()) - { - var receiveUsers = await _db.Queryable() - .Where(x => receiveUserIds.Contains(x.Id)) - .Select(x => new { x.Id, x.RealName }) - .ToListAsync(); - receiveUserDict = receiveUsers.ToDictionary(k => k.Id, v => v.RealName ?? ""); - } - // 填充审批人和使用记录统计信息 - foreach (var item in pageList.list) + // 使用记录统计信息 + if (usageDict.ContainsKey(item.usageBatchId)) { - // 当前审批人信息 - var currentApprovers = approvalRecords - .Where(x => x.ApplicationId == item.id) - .Select(x => new CurrentApproverInfo - { - approverId = x.ApproverId, - approverName = approverDict.ContainsKey(x.ApproverId) ? approverDict[x.ApproverId] : x.ApproverName, - approvalResult = x.ApprovalResult - }) - .ToList(); - item.currentApprovers = currentApprovers; - - // 使用记录统计信息 - if (usageDict.ContainsKey(item.usageBatchId)) + var usage = usageDict[item.usageBatchId]; + item.batchUsageInfo = new BatchUsageInfo { - var usage = usageDict[item.usageBatchId]; - item.batchUsageInfo = new BatchUsageInfo - { - totalCount = usage.TotalCount, - totalQuantity = usage.TotalQuantity, - totalAmount = usage.TotalAmount - }; - } - else + totalCount = usage.TotalCount, + totalQuantity = usage.TotalQuantity, + totalAmount = usage.TotalAmount + }; + } + else + { + item.batchUsageInfo = new BatchUsageInfo { - item.batchUsageInfo = new BatchUsageInfo - { - totalCount = 0, - totalQuantity = 0, - totalAmount = 0 - }; - } + totalCount = 0, + totalQuantity = 0, + totalAmount = 0 + }; + } - // 领取人姓名 - if (!string.IsNullOrWhiteSpace(item.receiveUser) && receiveUserDict.ContainsKey(item.receiveUser)) - { - item.receiveUserName = receiveUserDict[item.receiveUser]; - } + // 领取人姓名 + if (!string.IsNullOrWhiteSpace(item.receiveUser) && receiveUserDict.ContainsKey(item.receiveUser)) + { + item.receiveUserName = receiveUserDict[item.receiveUser]; } } @@ -1803,7 +1837,7 @@ namespace NCC.Extend // 由于审批记录在审批时才创建,这里需要通过审批记录表查找当前用户已审批的记录 // 然后排除这些申请,找出状态为"审批中"但当前用户还未审批的申请 // 注意:这里需要根据实际业务逻辑调整,如果审批人信息存储在节点审批人表中,应该通过那个表查询 - + // 先查询当前用户已经审批过的申请ID(排除这些) var approvedApplicationIds = await _db.Queryable() .Where(x => x.ApproverId == currentUserId) @@ -1811,13 +1845,13 @@ namespace NCC.Extend .Select(x => x.ApplicationId) .Distinct() .ToListAsync(); - + // 查询所有状态为"审批中"的申请ID var allPendingApplicationIds = await _db.Queryable() .Where(x => x.ApprovalStatus == "审批中" && x.IsEffective == StatusEnum.有效.GetHashCode()) .Select(x => x.Id) .ToListAsync(); - + // 找出当前用户需要审批的申请(状态为"审批中"且当前用户还未审批的) // 注意:这里简化处理,实际应该通过审批人配置表来确定审批人 // 如果审批人信息没有单独存储,可能需要通过其他方式确定 @@ -1859,8 +1893,8 @@ namespace NCC.Extend // 关键字搜索 if (!string.IsNullOrWhiteSpace(input.keyword)) { - query = query.Where((app, store) => - app.Id.Contains(input.keyword) || + query = query.Where((app, store) => + app.Id.Contains(input.keyword) || app.ApplicationUserName.Contains(input.keyword) || store.Dm.Contains(input.keyword)); } @@ -1965,5 +1999,106 @@ namespace NCC.Extend #endregion + #region 测试接口:为现有申请记录添加审批记录 + /// + /// 为现有申请记录添加审批记录(测试用) + /// + /// + /// 为"审批中"状态的申请记录添加当前节点的审批记录,用于测试审批人信息返回 + /// + /// 申请ID(可选,不传则处理所有"审批中"状态的申请) + /// 审批人ID(可选,不传则使用admin) + /// 处理结果 + [HttpPost("Test/AddApprovalRecord")] + public async Task AddApprovalRecordForTest(string applicationId = null, string approverId = null) + { + try + { + _db.Ado.BeginTran(); + + // 获取审批人信息(默认使用admin) + var defaultApproverId = approverId ?? "admin"; + var approverUser = await _db.Queryable() + .Where(x => x.Id == defaultApproverId) + .Select(x => new { x.Id, x.RealName }) + .FirstAsync(); + + if (approverUser == null) + { + throw NCCException.Oh("审批人不存在"); + } + + // 查询需要处理的申请记录 + var query = _db.Queryable() + .Where(x => x.ApprovalStatus == "审批中"); + + if (!string.IsNullOrWhiteSpace(applicationId)) + { + query = query.Where(x => x.Id == applicationId); + } + + var applications = await query.ToListAsync(); + + if (!applications.Any()) + { + return new { success = true, message = "没有需要处理的申请记录", count = 0 }; + } + + var successCount = 0; + var skipCount = 0; + + foreach (var application in applications) + { + // 检查是否已存在当前节点的审批记录 + var existingRecord = await _db.Queryable() + .Where(x => x.ApplicationId == application.Id && x.IsCurrentNode == 1) + .Where(x => x.ApprovalResult == "待审批" || x.ApprovalResult == "") + .FirstAsync(); + + if (existingRecord != null) + { + skipCount++; + continue; + } + + // 创建审批记录 + var approvalRecord = new LqInventoryUsageApprovalRecordEntity + { + Id = YitIdHelper.NextId().ToString(), + ApplicationId = application.Id, + NodeId = application.CurrentNodeId ?? application.Id, // 使用申请ID作为节点ID(如果CurrentNodeId为空) + NodeOrder = 1, + ApproverId = approverUser.Id, + ApproverName = approverUser.RealName ?? "", + ApprovalResult = "待审批", + ApprovalOpinion = "", + ApprovalTime = DateTime.Now, + IsCurrentNode = 1 + }; + + await _db.Insertable(approvalRecord).ExecuteCommandAsync(); + successCount++; + } + + _db.Ado.CommitTran(); + + return new + { + success = true, + message = $"处理完成:成功添加 {successCount} 条审批记录,跳过 {skipCount} 条(已存在)", + total = applications.Count, + successCount = successCount, + skipCount = skipCount + }; + } + catch (Exception ex) + { + _db.Ado.RollbackTran(); + _logger.LogError(ex, "添加审批记录失败"); + throw NCCException.Oh($"添加失败:{ex.Message}"); + } + } + #endregion + } } diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs index d8e49dc..a68a498 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs @@ -619,6 +619,7 @@ namespace NCC.Extend salary.DailyAverageConsumption, salary.DailyAverageProjectCount, daysInMonth, + salary.WorkingDays, isNewStore); // 4.2 提成计算 @@ -732,7 +733,7 @@ namespace NCC.Extend /// /// 计算底薪 /// - private decimal CalculateBaseSalary(decimal dailyAvgConsumption, decimal dailyAvgProjectCount, int daysInMonth, bool isNewStore) + private decimal CalculateBaseSalary(decimal dailyAvgConsumption, decimal dailyAvgProjectCount, int daysInMonth, decimal workingDays, bool isNewStore) { // 规则调整:按日均计算 // 一星:月消耗 10000 / 当月天数,项目数 96 / 当月天数 @@ -788,7 +789,13 @@ namespace NCC.Extend baseSalary = 2000; } - return baseSalary; + // 最终计算:(底薪 / 当月天数) * 在店天数 + if (daysInMonth > 0) + { + baseSalary = (baseSalary / daysInMonth) * workingDays; + } + + return Math.Round(baseSalary, 2); } /// diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqTechTeacherSalaryService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqTechTeacherSalaryService.cs index 84c73d4..655a5f0 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqTechTeacherSalaryService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqTechTeacherSalaryService.cs @@ -13,6 +13,7 @@ using NCC.Extend.Entitys.lq_attendance_summary; using NCC.Extend.Entitys.lq_tech_teacher_salary_statistics; using NCC.Extend.Entitys.lq_xh_hyhk; using NCC.Extend.Entitys.lq_xh_kjbsyj; +using NCC.Extend.Entitys.lq_person_times_record; using NCC.System.Entitys.Permission; using SqlSugar; using System; @@ -509,6 +510,199 @@ namespace NCC.Extend return (1m, consumeAchievement * 0.01m); } } + + /// + /// 统计科技部老师某个月的数据 + /// + /// + /// 实时统计科技部老师某个月的数据,包括: + /// - 开单业绩:从 lq_kd_kjbsyj 表统计 + /// - 消耗业绩:从 lq_xh_kjbsyj 表统计(关联 lq_xh_hyhk 获取时间) + /// - 退卡业绩:从 lq_hytk_kjbsyj 表统计 + /// - 手工费:从耗卡表(lq_xh_kjbsyj)的 F_LaborCost 字段统计 + /// - 人头:从 lq_person_times_record 表统计(按月份+客户去重) + /// - 人次:从 lq_person_times_record 表统计(按日期+客户去重) + /// + /// 示例请求: + /// ```json + /// { + /// "year": 2025, + /// "month": 11 + /// } + /// ``` + /// + /// 查询参数(年份、月份) + /// 科技部老师统计数据列表 + [HttpGet("statistics")] + public async Task> GetTechTeacherStatistics([FromQuery] TechTeacherStatisticsInput input) + { + // 1. 参数验证 + if (input.Year < 2020 || input.Year > 2100) + { + throw new Exception("年份范围不正确"); + } + if (input.Month < 1 || input.Month > 12) + { + throw new Exception("月份范围不正确(1-12)"); + } + + // 2. 计算时间范围 + var startDate = new DateTime(input.Year, input.Month, 1); + var endDate = startDate.AddMonths(1).AddDays(-1); + var monthStr = $"{input.Year}{input.Month:D2}"; + + // 3. 获取所有科技部老师(岗位为"科技老师") + var techTeacherList = await _db.Queryable() + .Where(x => x.Gw == "科技老师") + .Select(x => new + { + EmployeeId = x.Id, + EmployeeName = x.RealName, + EmployeeAccount = x.Account + }) + .ToListAsync(); + + if (techTeacherList == null || !techTeacherList.Any()) + { + return new List(); + } + + var teacherIds = techTeacherList.Select(x => x.EmployeeId).ToList(); + var teacherAccounts = techTeacherList.Where(x => !string.IsNullOrEmpty(x.EmployeeAccount)).Select(x => x.EmployeeAccount).ToList(); + + // 4. 使用聚合查询统计开单业绩和手工费(优化性能) + // 注意:kjblsyj字段是varchar类型,需要转换 + var orderStatsList = await _db.Queryable() + .Where(x => x.Yjsj >= startDate && x.Yjsj <= endDate.AddDays(1) && x.IsEffective == 1) + .Where(x => teacherIds.Contains(x.Kjbls) || teacherAccounts.Contains(x.Kjblszh)) + .ToListAsync(); + + var orderStats = orderStatsList + .Where(x => !string.IsNullOrEmpty(x.Kjbls)) + .GroupBy(x => x.Kjbls) + .Select(g => new + { + TeacherId = g.Key, + OrderAchievement = g.Sum(x => decimal.TryParse(x.Kjblsyj, out var val) ? val : 0m) + }) + .ToList(); + + // 5. 使用聚合查询统计消耗业绩和手工费(关联耗卡主表获取时间) + var consumeStatsList = await _db.Queryable( + (kjbsyj, hyhk) => kjbsyj.Glkdbh == hyhk.Id && hyhk.IsEffective == 1) + .Where((kjbsyj, hyhk) => kjbsyj.IsEffective == 1 + && hyhk.Hksj >= startDate && hyhk.Hksj <= endDate.AddDays(1)) + .Where((kjbsyj, hyhk) => teacherIds.Contains(kjbsyj.Kjbls) || teacherAccounts.Contains(kjbsyj.Kjblszh)) + .Select((kjbsyj, hyhk) => new + { + TeacherId = kjbsyj.Kjbls, + ConsumeAchievement = kjbsyj.Kjblsyj, + LaborCost = kjbsyj.LaborCost + }) + .ToListAsync(); + + var consumeStats = consumeStatsList + .Where(x => !string.IsNullOrEmpty(x.TeacherId)) + .GroupBy(x => x.TeacherId) + .Select(g => new + { + TeacherId = g.Key, + ConsumeAchievement = g.Sum(x => x.ConsumeAchievement ?? 0m), + LaborCost = g.Sum(x => x.LaborCost ?? 0m) + }) + .ToList(); + + // 6. 使用聚合查询统计退卡业绩和手工费 + var refundStatsList = await _db.Queryable() + .Where(x => x.Tksj >= startDate && x.Tksj <= endDate.AddDays(1) && x.IsEffective == 1) + .Where(x => teacherIds.Contains(x.Kjbls) || teacherAccounts.Contains(x.Kjblszh)) + .ToListAsync(); + + var refundStats = refundStatsList + .Where(x => !string.IsNullOrEmpty(x.Kjbls)) + .GroupBy(x => x.Kjbls) + .Select(g => new + { + TeacherId = g.Key, + RefundAchievement = g.Sum(x => x.Kjblsyj ?? 0m) + }) + .ToList(); + + // 7. 统计人头(按月份+客户去重) + var personCountRecords = await _db.Queryable() + .Where(x => x.PersonType == "科技老师" + && x.WorkMonth == monthStr + && x.IsEffective == 1 + && teacherIds.Contains(x.PersonId)) + .ToListAsync(); + + var personCountStats = personCountRecords + .GroupBy(x => new { x.PersonId, x.MemberId }) + .Select(g => new { TeacherId = g.Key.PersonId }) + .GroupBy(x => x.TeacherId) + .Select(g => new + { + TeacherId = g.Key, + PersonCount = g.Count() + }) + .ToList(); + + // 8. 统计人次(按日期+客户去重,汇总数量) + var personTimesRecords = await _db.Queryable() + .Where(x => x.PersonType == "科技老师" + && x.WorkMonth == monthStr + && x.IsEffective == 1 + && teacherIds.Contains(x.PersonId)) + .ToListAsync(); + + // 先按日期+客户去重,取最大数量,然后按老师汇总 + var personTimesStats = personTimesRecords + .GroupBy(x => new { x.PersonId, x.WorkDate, x.MemberId }) + .Select(g => new + { + TeacherId = g.Key.PersonId, + Quantity = g.Max(x => x.Quantity ?? 0m) // 按日期+客户去重,取最大数量 + }) + .GroupBy(x => x.TeacherId) + .Select(g => new + { + TeacherId = g.Key, + PersonTimes = g.Sum(x => x.Quantity) // 汇总所有去重后的数量 + }) + .ToList(); + + // 9. 构建结果字典(优化查找性能) + var orderDict = orderStats.ToDictionary(x => x.TeacherId, x => x); + var consumeDict = consumeStats.ToDictionary(x => x.TeacherId, x => x); + var refundDict = refundStats.ToDictionary(x => x.TeacherId, x => x); + var personCountDict = personCountStats.ToDictionary(x => x.TeacherId, x => x); + var personTimesDict = personTimesStats.ToDictionary(x => x.TeacherId, x => x); + + // 10. 组装结果 + var result = new List(); + foreach (var teacher in techTeacherList) + { + var orderStat = orderDict.ContainsKey(teacher.EmployeeId) ? orderDict[teacher.EmployeeId] : null; + var consumeStat = consumeDict.ContainsKey(teacher.EmployeeId) ? consumeDict[teacher.EmployeeId] : null; + var refundStat = refundDict.ContainsKey(teacher.EmployeeId) ? refundDict[teacher.EmployeeId] : null; + var personCountStat = personCountDict.ContainsKey(teacher.EmployeeId) ? personCountDict[teacher.EmployeeId] : null; + var personTimesStat = personTimesDict.ContainsKey(teacher.EmployeeId) ? personTimesDict[teacher.EmployeeId] : null; + + result.Add(new TechTeacherStatisticsOutput + { + EmployeeId = teacher.EmployeeId, + EmployeeName = teacher.EmployeeName, + OrderAchievement = orderStat?.OrderAchievement ?? 0m, + ConsumeAchievement = consumeStat?.ConsumeAchievement ?? 0m, + RefundAchievement = refundStat?.RefundAchievement ?? 0m, + PersonCount = personCountStat?.PersonCount ?? 0, + PersonTimes = personTimesStat?.PersonTimes ?? 0m, + LaborCost = consumeStat?.LaborCost ?? 0m // 手工费只统计耗卡中的手工费 + }); + } + + return result; + } } } diff --git a/sql/创建库存使用申请审批流程表.sql b/sql/创建库存使用申请审批流程表.sql index 595ea58..4d22796 100644 --- a/sql/创建库存使用申请审批流程表.sql +++ b/sql/创建库存使用申请审批流程表.sql @@ -108,3 +108,4 @@ WHERE u.`F_UnitPrice` = 0 OR u.`F_UnitPrice` IS NULL; +