using RIZO.Model; using RIZO.Model.MES.product; using RIZO.Model.MES.product.Dto; using RIZO.Repository; using RIZO.Service.MES.product.IService; using Infrastructure; using Infrastructure.Attribute; using Infrastructure.Extensions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NPOI.HPSF; using NPOI.HSSF.UserModel; using NPOI.SS.Formula.Functions; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; using System.Drawing.Printing; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using static System.Runtime.InteropServices.JavaScript.JSType; namespace RIZO.Service.MES.product { [AppService(ServiceType = typeof(IProweekplanManageService), ServiceLifetime = LifeTime.Transient)] public class ProweekplanManageService : BaseService, IProweekplanManageService { private static bool _hasDeletedOldData = false; // 静态变量,标记是否已执行过删除旧数据 /// /// 查询周计划 /// public PagedInfo SearchWeekplan(WeekplanQueryDto weekplanQuery) { if (weekplanQuery == null) { return null; } return Context.Queryable() .WhereIF(weekplanQuery.year > 0, p => p.PlanYear == weekplanQuery.year) .WhereIF(weekplanQuery.week > 0, p => p.PlanWeek == weekplanQuery.week) .WhereIF(!string.IsNullOrEmpty(weekplanQuery.partnumber), p => p.Specification.Contains(weekplanQuery.partnumber)) .Select(p => new ProWeeklyPlanChildDateDto() { Id = p.Id, PlanCode = p.PlanCode, PlanYear = p.PlanYear, PlanWeek = p.PlanWeek, PlanStartDate = p.PlanStartDate, PlanEndDate = p.PlanEndDate, OrderCode = p.OrderCode, ProductCode = p.ProductCode, ProductName = p.ProductName, Specification = p.Specification, Color = p.Color, PlanQty = p.PlanQty, CompletedQty = p.CompletedQty, RemainingQty = p.PlanQty - p.CompletedQty, // 若数据库中没有,可在此计算 ScrapQty = p.ScrapQty, WorkshopCode = p.WorkshopCode, WorkshopName = p.WorkshopName, LineCode = p.LineCode, LineName = p.LineName, GroupCode = p.GroupCode, GroupName = p.GroupName, ShiftType = p.ShiftType, PlanStatus = p.PlanStatus, Planner = p.Planner, Priority = p.Priority, Sort = p.Sort, MaterialReady = p.MaterialReady, Remark = p.Remark, CreatedBy = p.CreatedBy, CreatedTime = p.CreatedTime, // 假设 p 中有此字段,否则用 DateTime.Now UpdatedBy = p.UpdatedBy, UpdatedTime = p.UpdatedTime, proWeeklyDatechildList = SqlFunc.Subqueryable() .Where(d => d.PlanCode == p.PlanCode) .ToList() }).ToPage_NO_Convert(weekplanQuery); } /// /// Excel导入 /// public int ImportExcel(IFormFile formFile, string username) { _hasDeletedOldData = false; int insertCount = 0; using (var stream = formFile.OpenReadStream()) { try { List insertProWeekPlanList = new List(); List insertProWeeklyDateList = new List(); IWorkbook workbook = new XSSFWorkbook(stream); ISheet sheet = workbook.GetSheet("Sheet1"); // 处理第2行 获取标题(含周数信息) IRow secondRow = sheet.GetRow(1); string title = secondRow?.GetCell(0)?.ToString() ?? ""; int week = extractWeek(title); // 你自己实现的提取周数的方法 int year = DateTime.Now.Year; // 从第6行开始遍历数据行 for (int row = 6; row <= sheet.LastRowNum; row++) { IRow currentRow = sheet.GetRow(row); if (currentRow == null) continue; int index = row - 5; ProWeeklyPlan proWeeklyPlan = new ProWeeklyPlan { Id = SnowFlakeSingle.Instance.NextId(), PlanCode = "WP" + SnowFlakeSingle.Instance.NextId().ToString("D19").PadLeft(19, '0'), PlanYear = DateTime.Now.Year, PlanWeek = week, CreatedTime = DateTime.Now, ProductCode = currentRow.GetCell(2)?.ToString(), ProductName = currentRow.GetCell(3)?.ToString(), Specification = currentRow.GetCell(4)?.ToString(), Planner = currentRow.GetCell(6)?.ToString(), CreatedBy = username, PlanQty = (int)(currentRow.GetCell(7)?.NumericCellValue ?? 0), CompletedQty = (int)(currentRow.GetCell(8)?.NumericCellValue ?? 0), }; // 如果产品相关字段全部为空,则跳过该行 if (string.IsNullOrEmpty(proWeeklyPlan.ProductCode) && string.IsNullOrEmpty(proWeeklyPlan.ProductName) && string.IsNullOrEmpty(proWeeklyPlan.Specification)) { continue; } // 可选字段 proWeeklyPlan.PlanStatus = !string.IsNullOrEmpty(currentRow.GetCell(5)?.ToString()) ? currentRow.GetCell(5).ToString() : null; // 初始化默认日期 proWeeklyPlan.PlanStartDate = DateTime.MinValue; proWeeklyPlan.PlanEndDate = DateTime.MinValue; // 处理星期一到星期日(第9列开始,每4列一组:类型、计划数、是否变更、实际数) for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) { int colIndex = 9 + dayOfWeek * 4; ICell dateCell = sheet.GetRow(2)?.GetCell(colIndex); ICell dayNameCell = sheet.GetRow(3)?.GetCell(colIndex); ICell productTypeCell = currentRow.GetCell(colIndex); ICell planQtyCell = currentRow.GetCell(colIndex + 1); ICell isChangeCell = currentRow.GetCell(colIndex + 2); ICell actualQtyCell = currentRow.GetCell(colIndex + 3); DateTime? weekDate = dateCell?.DateCellValue; string dayOfWeekName = dayNameCell?.ToString() ?? ""; string productType = productTypeCell?.ToString() ?? ""; int planNum = (int)(planQtyCell?.NumericCellValue ?? 0); string isChange = isChangeCell?.ToString() ?? ""; int actualQt = (int)(actualQtyCell?.NumericCellValue ?? 0); var proWeeklyDate = new ProWeeklyDate { Id = SnowFlakeSingle.Instance.NextId(), FkWeeklyId = proWeeklyPlan.Id, PlanCode = proWeeklyPlan.PlanCode, WeekDate = weekDate, DayOfWeek = dayOfWeekName, ProductType = productType, PlanNum = planNum, IsChange = isChange, ActualQt = actualQt, CreatedTime = DateTime.Now }; insertProWeeklyDateList.Add(proWeeklyDate); // 设置周计划的开始和结束日期 if (dayOfWeek == 0 && weekDate.HasValue) { proWeeklyPlan.PlanStartDate = weekDate.Value; proWeeklyPlan.PlanYear = weekDate.Value.Year; year = weekDate.Value.Year; } if (dayOfWeek == 6 && weekDate.HasValue) { proWeeklyPlan.PlanEndDate = weekDate.Value; } } insertProWeekPlanList.Add(proWeeklyPlan); // ====================== // 🚩分批提交:每500条计划及其关联日期插入一次 // ====================== if (insertProWeekPlanList.Count >= 500) { InsertBatchData(insertProWeekPlanList, insertProWeeklyDateList, year, week, username); insertCount = insertCount + insertProWeekPlanList.Count + insertProWeeklyDateList.Count; // 清空缓存列表 insertProWeekPlanList.Clear(); insertProWeeklyDateList.Clear(); } } // 插入剩余不足500条的数据 if (insertProWeekPlanList.Count > 0) { insertCount = insertCount + insertProWeekPlanList.Count + insertProWeeklyDateList.Count; InsertBatchData(insertProWeekPlanList, insertProWeeklyDateList, year, week, username); } return insertCount; } catch (Exception ex) { // 建议记录日志,而不是直接抛出 // Log.Error(ex, "导入Excel失败"); throw; // 不要 throw ex,直接 throw 保留堆栈 } } } /// /// 批量插入数据(分批次事务处理) /// private void InsertBatchData( List proWeeklyPlans, List proWeeklyDates, int year, int week, string username) { UseTran(() => { // 只在第一次调用该方法时执行删除旧数据的逻辑 if (!_hasDeletedOldData) { // 1. 删除旧数据:根据年份+周数+计划编码删除 ProWeeklyDate,然后删除 ProWeeklyPlan Context.Deleteable() .Where(d => SqlFunc.Subqueryable() .Where(p => p.PlanYear == year && p.PlanWeek == week && p.Id == d.FkWeeklyId) .Any() ) .ExecuteCommand(); Context.Deleteable() .Where(p => p.PlanYear == year && p.PlanWeek == week) .ExecuteCommand(); // 标记为已删除,后续不再执行 _hasDeletedOldData = true; } // 2. 【每次调用都执行】插入新的周计划和日计划 Context.Insertable(proWeeklyPlans).ExecuteCommand(); Context.Insertable(proWeeklyDates).ExecuteCommand(); }); } const int BATCH_SIZE = 500; // TODO ERP导入 /// /// ERP导入 /// /// /// public int ImportERP() { throw new NotImplementedException(); } /// /// 预览日计划 /// /// /// /// public PagedInfo PreviewDatePlan(DatePlanRequest datePlanRequest) { if (datePlanRequest.ChooseDate == null) { return null; } var query = Context.Queryable() .LeftJoin((p, d) => p.PlanCode == d.PlanCode) .Where((p, d) => d.WeekDate.ToString().Contains(datePlanRequest.ChooseDate)) .Where((p, d) => d.PlanNum > 0) .Select((p, d) => new { p.ProductCode, p.ProductName, p.Specification, p.GroupCode, p.LineCode, p.Priority, p.PlanStatus, p.PlanStartDate, p.PlanEndDate, p.Remark, d.PlanNum, d.WeekDate, d.CreateBy, d.CreatedTime, d.UpdateBy, d.UpdatedTime, p.Id }) //.ToPageList(datePlanRequest.PageNum, datePlanRequest.PageSize); .ToPage(datePlanRequest); // 3. 在内存中处理工单号生成(关键:避免SQL转换) var resultList = new List(); for (int i = 0; i < query.Result.Count; i++) { var item = query.Result[i]; int index = (datePlanRequest.PageNum - 1) * datePlanRequest.PageSize + i + 1; // 生成工单号(纯C#操作,不涉及SQL) string workorder = $"H{datePlanRequest.ChooseDate.Replace("_", "")}_{item.GroupCode}_{item.LineCode}_{index:000}"; resultList.Add(new ProWorkorder { Id = XueHua, Workorder = workorder, ProductionCode = item.ProductCode, ProductionName = item.ProductName, Specification = item.Specification, CustomCode = null, Unit = "件", DeliveryNum = item.PlanNum, IsCarton = 0, PackageCapacity = null, LineCode = item.LineCode, GroupCode = item.GroupCode, WorkorderDate = item.WeekDate, Priority = item.Priority, Status = Convert.ToInt32(item.PlanStatus), Remark = item.Remark, Beat = 0, StartTime = item.PlanStartDate, EndTime = item.PlanEndDate, CreatedBy = item.CreateBy, CreatedTime = item.CreatedTime, UpdatedBy = item.UpdateBy, UpdatedTime = item.UpdatedTime, }); } // 4. 构建并返回分页结果 return new PagedInfo { Result = resultList, TotalNum = query.TotalNum, PageIndex = datePlanRequest.PageNum, PageSize = datePlanRequest.PageSize }; } /// /// 更新周计划 /// /// /// /// public int UpdateWeekPlan(ProWeeklyPlanAndDateDto updateWeekPlanDto) { throw new NotImplementedException(); } /// TODO 是否关联(库存、效率、箱标签) /// /// 是否关联(库存、效率、箱标签) /// /// /// /// /// /// public int IsAssociateOthers(int inventory, int efficiency, int boxLabel) { throw new NotImplementedException(); } /// /// 处理一行Excel数据 /// /// 当前行 /// 索引 /// 用户名 /// 周数 /// 星期信息 /// 计划和日期列表 private (ProWeeklyPlan, List) ProcessRow(IRow row, int index, string username, int week, List<(DateTime Date, string DayName, int DateCellIndex)> weekDays) { try { ProWeeklyPlan proWeeklyPlan = new ProWeeklyPlan(); proWeeklyPlan.Id = SnowFlakeSingle.Instance.NextId(); proWeeklyPlan.PlanCode = "WP" + DateTime.Now.ToString("yyMM") + index.ToString("000"); proWeeklyPlan.PlanYear = weekDays.Any() ? weekDays.First().Date.Year : DateTime.Now.Year; proWeeklyPlan.PlanWeek = week; proWeeklyPlan.CreatedTime = DateTime.Now; proWeeklyPlan.CreatedBy = username; //班组 proWeeklyPlan.GroupName = row.GetCell(0)?.ToString(); //车型 string part_no = row.GetCell(1)?.ToString(); //产品编码 proWeeklyPlan.ProductCode = row.GetCell(2)?.ToString(); //产品名称 proWeeklyPlan.ProductName = row.GetCell(3)?.ToString(); //产品零件号 proWeeklyPlan.Specification = row.GetCell(4)?.ToString(); //生产状态 string planStatus = row.GetCell(5)?.ToString(); if (!string.IsNullOrEmpty(planStatus)) { proWeeklyPlan.PlanStatus = planStatus; } //车间计划员 proWeeklyPlan.Planner = row.GetCell(6)?.ToString(); //本周计划装配数量 if (row.GetCell(7) != null) { double planQty = row.GetCell(7).NumericCellValue; proWeeklyPlan.PlanQty = (int)planQty; } //本周实际装配数 if (row.GetCell(8) != null) { double actualQty = row.GetCell(8).NumericCellValue; proWeeklyPlan.CompletedQty = (int)actualQty; } // 设置计划开始和结束日期 if (weekDays.Any()) { proWeeklyPlan.PlanStartDate = weekDays.First().Date; proWeeklyPlan.PlanEndDate = weekDays.Last().Date; } // 创建每日计划 List weeklyDates = new List(); foreach (var (date, dayName, colIndex) in weekDays) { // 确保列索引有效 if (colIndex + 3 <= row.LastCellNum) // 检查是否有足够的列 { // 产品类型 string productType = row.GetCell(colIndex)?.ToString(); // 计划数量 int planNum = 0; if (row.GetCell(colIndex + 1) != null) { planNum = (int)row.GetCell(colIndex + 1).NumericCellValue; } // 是否变更 string isChange = row.GetCell(colIndex + 2)?.ToString(); // 实际数量 int actualQt = 0; if (row.GetCell(colIndex + 3) != null) { actualQt = (int)row.GetCell(colIndex + 3).NumericCellValue; } ProWeeklyDate weeklyDate = new ProWeeklyDate { Id = SnowFlakeSingle.Instance.NextId(), FkWeeklyId = proWeeklyPlan.Id, PlanCode = proWeeklyPlan.PlanCode, WeekDate = date, DayOfWeek = dayName, ProductType = productType, PlanNum = planNum, IsChange = isChange, ActualQt = actualQt, CreatedTime = DateTime.Now, CreateBy = username }; weeklyDates.Add(weeklyDate); } } return (proWeeklyPlan, weeklyDates); } catch (Exception ex) { // 记录错误但继续处理其他行 Console.WriteLine($"处理行数据时出错: {ex.Message}"); return (null, null); } } /// /// 批量处理数据并执行数据库操作 /// /// 周计划列表 /// 日期计划列表 /// 年份 /// 周数 private void ProcessBatchData(List weeklyPlans, List weeklyDates, int year, int week) { if (!weeklyPlans.Any()) return; UseTran2(() => { // 如果是第一批数据,删除本周的旧数据 if (weeklyPlans.Count <= BATCH_SIZE * 2) // 简单判断是否为第一批 { Context.Deleteable().Where(p => p.PlanYear == year && p.PlanWeek == week).ExecuteCommand(); // 不需要单独删除ProWeeklyDate,因为它们通过外键关联会被级联删除 } // 批量插入周计划 if (weeklyPlans.Any()) { Context.Insertable(weeklyPlans).ExecuteCommand(); } // 批量插入日期计划 if (weeklyDates.Any()) { // 分批插入,每批最多2000条记录 const int MAX_BATCH_INSERT = 2000; for (int i = 0; i < weeklyDates.Count; i += MAX_BATCH_INSERT) { var batch = weeklyDates.Skip(i).Take(MAX_BATCH_INSERT).ToList(); if (batch.Any()) { Context.Insertable(batch).ExecuteCommand(); } } } }); } /// /// 更新实际组装进度 /// /// /// /// public int UpdateActualAssemblyProgress01(int year, int week) { int result = 0; // 1. 获取该周的开始和结束日期 var weekInfo = Context.Queryable() .Where(it => it.PlanYear == year && it.PlanWeek == week) .Select(it => new { it.PlanStartDate, it.PlanEndDate }) .First(); if (weekInfo == null) return result; DateTime weekStartDate = weekInfo.PlanStartDate; DateTime weekEndDate = weekInfo.PlanEndDate; // 2. 查询该周所有的生产计划(ProWeeklyPlan) var proWeeklyPlans = Context.Queryable() .Where(it => it.PlanYear == year && it.PlanWeek == week) .ToList(); if (proWeeklyPlans == null || !proWeeklyPlans.Any()) return result; // 3. 提取所有唯一的 (ProductionCode, Specification) 组合,用于后续统计 var planGroups = proWeeklyPlans .Select(p => new { p.ProductCode, p.Specification, p.Id, p.PlanQty }) .ToList(); if (!planGroups.Any()) return result; // 4. 【关键优化点】一次性查询所有相关的工单数据,并按 ProductCode + Specification 分组统计总合格数 var workOrderStats = Context.Queryable() .LeftJoin((wo, rw) => wo.Workorder == rw.FkWorkorder) .Where((wo, rw) => wo.WorkorderDate >= weekStartDate && wo.WorkorderDate <= weekEndDate && planGroups.Select(pg => pg.ProductCode).Contains(wo.ProductionCode) && planGroups.Select(pg => pg.Specification).Contains(wo.Specification) ) .GroupBy((wo, rw) => new { wo.ProductionCode, wo.Specification }) .Select((wo, rw) => new { ProductionCode = wo.ProductionCode, Specification = wo.Specification, TotalDeliveryNum = SqlFunc.AggregateSum(rw.QualifiedNumber) ?? 0 // 注意:rw 可能是 Reportwork,需有 QualifiedNumber 字段 }) .ToList(); // 5. 遍历原始计划,从统计结果中找到对应的 TotalDeliveryNum,进行更新 foreach (var plan in proWeeklyPlans) { var key = new { plan.ProductCode, plan.Specification }; var stat = workOrderStats.FirstOrDefault(s => s.ProductionCode == key.ProductCode && s.Specification == key.Specification); int completedQty = stat?.TotalDeliveryNum ?? 0; int remainingQty = plan.PlanQty - completedQty; // 只有当数据有变化时才更新(可选,减少不必要的更新) if (plan.CompletedQty != completedQty || plan.RemainingQty != remainingQty) { plan.CompletedQty = completedQty; plan.RemainingQty = remainingQty; plan.UpdatedBy = "system"; plan.UpdatedTime = DateTime.Now; result += Context.Updateable(plan).ExecuteCommand(); } } return result; } public int UpdateActualAssemblyProgress03(int year, int week) { int result = 0; //获取周的日期开始到结束 var WeekStartAndEnd = Context.Queryable().Where(it => it.PlanYear == year && it.PlanWeek == week) .Select(it => new { it.PlanStartDate, it.PlanEndDate }).First(); if (WeekStartAndEnd == null) { return result; } DateTime WeekStartDate = WeekStartAndEnd.PlanStartDate; DateTime WeekEndDate = WeekStartAndEnd.PlanEndDate; // List proWeeklyPlans = Context.Queryable().Where(it => it.PlanYear == year && it.PlanWeek == week).ToList(); if (proWeeklyPlans.Count > 0) { foreach (var plan in proWeeklyPlans) { //计算实际装配数 var workorderData = Context .Queryable() .LeftJoin((w, r) => w.Id == r.Id) .Where((w, r) => w.WorkorderDate >= WeekStartDate && w.WorkorderDate <= WeekEndDate && w.ProductionCode == plan.ProductCode && w.Specification == plan.Specification) .GroupBy((w, r) => new { w.ProductionCode, w.Specification }) .Select((w, r) => new { w.ProductionCode, w.Specification, TotalDeliveryNum = SqlFunc.AggregateSum(r.QualifiedNumber) }).First(); if (workorderData != null) { plan.CompletedQty = workorderData.TotalDeliveryNum ?? 0; plan.RemainingQty = plan.PlanQty - plan.CompletedQty; plan.UpdatedBy = "system"; plan.UpdatedTime = DateTime.Now; } // 更新周一到周日的实际装配数 // 获取当前计划关联的所有日期明细 var weeklyDates = Context.Queryable() .Where(d => d.FkWeeklyId == plan.Id) .ToList(); // 遍历一周中的每一天 for (DateTime currentDate = WeekStartDate; currentDate <= WeekEndDate; currentDate = currentDate.AddDays(1)) { // 查找对应日期的明细记录 var weeklyDate = weeklyDates.FirstOrDefault(d => d.WeekDate.HasValue && d.WeekDate.Value.Date == currentDate.Date); if (weeklyDate != null) { // 查询当天该产品的实际合格数量 var dailyActualQty = Context .Queryable() .LeftJoin((w, r) => w.Id == r.Id) .Where((w, r) => w.WorkorderDate.HasValue && w.WorkorderDate.Value.Date == currentDate.Date && w.ProductionCode == plan.ProductCode && w.Specification == plan.Specification ) .GroupBy((w, r) => new { w.ProductionCode, w.Specification }) .Select((w, r) => SqlFunc.AggregateSum(r.QualifiedNumber) ?? 0) .First(); // 如果实际数量有变化,则更新 if (weeklyDate.ActualQt != dailyActualQty) { weeklyDate.ActualQt = dailyActualQty; weeklyDate.UpdateBy = "system"; weeklyDate.UpdatedTime = DateTime.Now; result += Context.Updateable(weeklyDate).ExecuteCommand(); } } } } } return result; } public int UpdateActualAssemblyProgress(int year, int week) { int result = 0; // 1. 获取该周的开始和结束日期 var weekInfo = Context.Queryable() .Where(it => it.PlanYear == year && it.PlanWeek == week) .Select(it => new { it.PlanStartDate, it.PlanEndDate }) .First(); if (weekInfo == null) return result; DateTime weekStartDate = weekInfo.PlanStartDate; DateTime weekEndDate = weekInfo.PlanEndDate; // 2. 查询该周所有的生产计划(ProWeeklyPlan) var proWeeklyPlans = Context.Queryable() .Where(it => it.PlanYear == year && it.PlanWeek == week) .ToList(); if (proWeeklyPlans == null || !proWeeklyPlans.Any()) return result; // 3. 提取所有唯一的 (ProductionCode, Specification, Id) 组合 var planGroups = proWeeklyPlans .Select(p => new { p.ProductCode, p.Specification, p.Id, p.PlanQty }) .ToList(); if (!planGroups.Any()) return result; // 4. 一次性查询所有相关的工单数据,按 ProductCode + Specification 分组统计总合格数 var workOrderStats = Context.Queryable() .LeftJoin((wo, rw) => wo.Workorder == rw.FkWorkorder) .Where((wo, rw) => wo.WorkorderDate >= weekStartDate && wo.WorkorderDate <= weekEndDate && planGroups.Select(pg => pg.ProductCode).Contains(wo.ProductionCode) && planGroups.Select(pg => pg.Specification).Contains(wo.Specification) ) .GroupBy((wo, rw) => new { wo.ProductionCode, wo.Specification }) .Select((wo, rw) => new { ProductionCode = wo.ProductionCode, Specification = wo.Specification, TotalDeliveryNum = SqlFunc.AggregateSum(rw.QualifiedNumber) ?? 0 }) .ToList(); // 5. 一次性查询所有相关的工单数据,按 ProductCode + Specification + WorkorderDate 分组统计每日合格数 var dailyWorkOrderStats = Context.Queryable() .LeftJoin((wo, rw) => wo.Workorder == rw.FkWorkorder) .Where((wo, rw) => wo.WorkorderDate >= weekStartDate && wo.WorkorderDate <= weekEndDate && planGroups.Select(pg => pg.ProductCode).Contains(wo.ProductionCode) && planGroups.Select(pg => pg.Specification).Contains(wo.Specification) ) .GroupBy((wo, rw) => new { wo.ProductionCode, wo.Specification, Date =wo.WorkorderDate }) .Select((wo, rw) => new { ProductionCode = wo.ProductionCode, Specification = wo.Specification, Date = wo.WorkorderDate, DailyDeliveryNum = SqlFunc.AggregateSum(rw.QualifiedNumber) ?? 0 }) .ToList(); // 6. 一次性获取所有相关的ProWeeklyDate记录 var weeklyDateIds = proWeeklyPlans.Select(p => p.Id).ToList(); var allWeeklyDates = Context.Queryable() .Where(d => weeklyDateIds.Contains((long)d.FkWeeklyId)) .ToList(); // 7. 准备批量更新的数据 var weeklyPlansToUpdate = new List(); var weeklyDatesToUpdate = new List(); // 8. 遍历原始计划,更新数据 foreach (var plan in proWeeklyPlans) { // 更新计划的总装配数 var key = new { plan.ProductCode, plan.Specification }; var stat = workOrderStats.FirstOrDefault(s => s.ProductionCode == key.ProductCode && s.Specification == key.Specification); int completedQty = stat?.TotalDeliveryNum ?? 0; int remainingQty = plan.PlanQty - completedQty; // 只有当数据有变化时才更新 if (plan.CompletedQty != completedQty || plan.RemainingQty != remainingQty) { plan.CompletedQty = completedQty; plan.RemainingQty = remainingQty; plan.UpdatedBy = "system"; plan.UpdatedTime = DateTime.Now; weeklyPlansToUpdate.Add(plan); } // 更新每日装配数 var planWeeklyDates = allWeeklyDates.Where(d => d.FkWeeklyId == plan.Id).ToList(); foreach (var weeklyDate in planWeeklyDates) { if (!weeklyDate.WeekDate.HasValue) continue; // 从预计算的统计数据中查找当天该产品的实际数量 var dailyKey = new { plan.ProductCode, plan.Specification, Date = weeklyDate.WeekDate.Value }; var dailyStat = dailyWorkOrderStats.FirstOrDefault( s => s.ProductionCode == dailyKey.ProductCode && s.Specification == dailyKey.Specification && s.Date == dailyKey.Date ); int dailyActualQty = dailyStat?.DailyDeliveryNum ?? 0; // 只有当数据有变化时才更新 if (weeklyDate.ActualQt != dailyActualQty) { weeklyDate.ActualQt = dailyActualQty; weeklyDate.UpdateBy = "system"; weeklyDate.UpdatedTime = DateTime.Now; weeklyDatesToUpdate.Add(weeklyDate); } } } // 9. 批量更新数据 if (weeklyPlansToUpdate.Any()) { result += Context.Updateable(weeklyPlansToUpdate).ExecuteCommand(); } if (weeklyDatesToUpdate.Any()) { // 分批批量更新,每批最多500条记录 const int BATCH_SIZE = 500; for (int i = 0; i < weeklyDatesToUpdate.Count; i += BATCH_SIZE) { var batch = weeklyDatesToUpdate.Skip(i).Take(BATCH_SIZE).ToList(); result += Context.Updateable(batch).ExecuteCommand(); } } return result; } /// /// 提取周 /// /// /// private int extractWeek(string title) { global::System.Text.RegularExpressions.Match match = Regex.Match(title, @"[((](\d+)[))]"); if (match.Success) { string numberStr = match.Groups[1].Value; // 获取第一个捕获组中的内容,即数字部分 if (int.TryParse(numberStr, out int weekNumber)) { Console.WriteLine("提取到的周数是: " + weekNumber); // 输出: 35 return weekNumber; } else { Console.WriteLine("括号内不是有效的数字。"); return -1; } } else { Console.WriteLine("未找到括号内的数字。"); return -1; } } /// /// 导出日计划 /// /// /// /// (byte[] fileBytes, string fileName) IProweekplanManageService.ExportWeekDatePlan(int year, int week, string dayofweek) { // 1. 查询数据:周日计划及关联日期数据 List ProWeeklyPlanAndDateList = Context.Queryable() .LeftJoin((p, d) => p.PlanCode == d.PlanCode) .WhereIF(year > 0, (p, d) => p.PlanYear == year) .WhereIF(week > 0, (p, d) => p.PlanWeek == week) .WhereIF(!string.IsNullOrEmpty(dayofweek), (p, d) => d.DayOfWeek == dayofweek) .Select((p, d) => new ProWeeklyPlanAndDateDto { // ProWeeklyPlan 字段(p) ProWeeklyPlanId = p.Id, PlanCode = p.PlanCode, PlanYear = p.PlanYear, PlanWeek = p.PlanWeek, PlanStartDate = p.PlanStartDate, PlanEndDate = p.PlanEndDate, OrderCode = p.OrderCode, ProductCode = p.ProductCode, ProductName = p.ProductName, Specification = p.Specification, Color = p.Color, PlanQty = p.PlanQty, CompletedQty = p.CompletedQty, RemainingQty = p.PlanQty - p.CompletedQty, ScrapQty = p.ScrapQty, WorkshopCode = p.WorkshopCode, WorkshopName = p.WorkshopName, LineCode = p.LineCode, LineName = p.LineName, GroupCode = p.GroupCode, GroupName = p.GroupName, ShiftType = p.ShiftType, PlanStatus = p.PlanStatus, Planner = p.Planner, Priority = p.Priority, Sort = p.Sort, MaterialReady = p.MaterialReady, Remark = p.Remark, ProWeeklyPlanCreatedBy = p.CreatedBy, ProWeeklyPlanCreatedTime = p.CreatedTime, ProWeeklyPlanUpdatedBy = p.UpdatedBy, ProWeeklyPlanUpdatedTime = p.UpdatedTime, // ProWeeklyDate 字段(d) ProWeeklyDateId = d.Id, FkWeeklyId = d.FkWeeklyId, ProWeeklyPlanPlanCode = d.PlanCode, WeekDate = d.WeekDate, DayOfWeek = d.DayOfWeek, ProductType = d.ProductType, PlanNum = d.PlanNum, IsChange = d.IsChange, ActualQt = d.ActualQt, CreateBy = d.CreateBy, CreatedTime = d.CreatedTime, UpdateBy = d.UpdateBy, UpdatedTime = d.UpdatedTime }) .ToList(); // 2. 转为工单列表(可选,如果你后续要用,目前你只是导出 Excel) List proWorkorderList = new List(); if (ProWeeklyPlanAndDateList != null && ProWeeklyPlanAndDateList.Count > 0) { foreach (var item in ProWeeklyPlanAndDateList) { ProWorkorder proWorkorder = new ProWorkorder { Id = SnowFlakeSingle.Instance.NextId().ToString(), Workorder = string.Empty, ProductionCode = item.ProductCode, ProductionName = item.ProductName, Specification = item.Specification, DeliveryNum = item.PlanNum }; proWorkorderList.Add(proWorkorder); } } // 3. 读取 Excel 模板 IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment)); string fileName = "workorder"; // 模板文件名(不含扩展名) string sFileName = $"{fileName}.xlsx"; string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName); if (!global::System.IO.File.Exists(fullPath)) { throw new CustomException("Excel 模板文件未找到,请确保 ImportTemplate/workorder.xlsx 存在"); } // 4. 加载模板 workbook using (FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read)) { IWorkbook workbook = new XSSFWorkbook(fileStream); ISheet sheet = workbook.GetSheet("Sheet1"); if (sheet == null) { throw new CustomException("未找到 Sheet1,请检查 Excel 模板"); } // 5. 填充数据(从第4行开始,即索引 3) int dataRowIndex = 3; // 可选:在第一行写入当前时间(比如 A2 单元格) if (sheet.GetRow(1) != null) { sheet.GetRow(1).CreateCell(0).SetCellValue(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } // 6. 遍历工单数据,填充每一行 foreach (var item in proWorkorderList) { IRow dataRow = sheet.GetRow(dataRowIndex) ?? sheet.CreateRow(dataRowIndex); dataRow.CreateCell(0).SetCellValue(item.ProductionCode); // A列 dataRow.CreateCell(1).SetCellValue(item.ProductionName); // B列 dataRow.CreateCell(2).SetCellValue(item.Specification); // C列 dataRow.CreateCell(4).SetCellValue((double)item.DeliveryNum); // D列 dataRowIndex++; } // 7. 写入到 MemoryStream(关键:不要用 using 包裹,否则流会被释放!) var memoryStream = new MemoryStream(); workbook.Write(memoryStream); // 8. 返回 byte[] 和文件名 return (memoryStream.ToArray(), "导出数据.xlsx"); } } /// /// 导出周计划 /// /// /// (byte[] fileBytes, string fileName) IProweekplanManageService.ExportWeekOrderPlan(int year, int week) { // 1. 先查询所有符合条件的头表数据 var weeklyPlans = Context.Queryable() .WhereIF(year > 0, p => p.PlanYear == year) .WhereIF(week > 0, p => p.PlanWeek == week) .ToList(); // 2. 批量查询所有关联的子表数据 var planCodes = weeklyPlans.Select(p => p.PlanCode).ToList(); var allDateDetails = Context.Queryable() .Where(d => planCodes.Contains(d.PlanCode)) .OrderBy(d => d.WeekDate) .ToList(); List ProWeeklyPlanAndDateList = weeklyPlans .Select(plan => new ProWeeklyPlanAndDateDto { // 填充头表字段 ProWeeklyPlanId = plan.Id, PlanCode = plan.PlanCode, PlanYear = plan.PlanYear, PlanWeek = plan.PlanWeek, PlanStartDate = plan.PlanStartDate, PlanEndDate = plan.PlanEndDate, OrderCode = plan.OrderCode, ProductCode = plan.ProductCode, ProductName = plan.ProductName, Specification = plan.Specification, Color = plan.Color, PlanQty = plan.PlanQty, CompletedQty = plan.CompletedQty, RemainingQty = plan.PlanQty - plan.CompletedQty, ScrapQty = plan.ScrapQty, WorkshopCode = plan.WorkshopCode, WorkshopName = plan.WorkshopName, LineCode = plan.LineCode, LineName = plan.LineName, GroupCode = plan.GroupCode, GroupName = plan.GroupName, ShiftType = plan.ShiftType, PlanStatus = plan.PlanStatus, Planner = plan.Planner, Priority = plan.Priority, Sort = plan.Sort, MaterialReady = plan.MaterialReady, Remark = plan.Remark, ProWeeklyPlanCreatedBy = plan.CreatedBy, ProWeeklyPlanCreatedTime = plan.CreatedTime, ProWeeklyPlanUpdatedBy = plan.UpdatedBy, ProWeeklyPlanUpdatedTime = plan.UpdatedTime, // 内存中关联子表数据 proWeeklyDatechildList = allDateDetails .Where(d => d.PlanCode == plan.PlanCode) .Select(d => new ProWeeklyDate { ProductType = d.ProductType, PlanNum = d.PlanNum, IsChange = d.IsChange, ActualQt = d.ActualQt, WeekDate = d.WeekDate, DayOfWeek = d.DayOfWeek }) .ToList() }) .ToList(); // 3. 读取 Excel 模板 IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment)); string fileName = "weekplan"; // 模板文件名(不含扩展名) string sFileName = $"{fileName}.xlsx"; string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName); if (!global::System.IO.File.Exists(fullPath)) { throw new CustomException("Excel 模板文件未找到,请确保 ImportTemplate/workorder.xlsx 存在"); } // 4. 加载模板 workbook using (FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read)) { IWorkbook workbook = new XSSFWorkbook(fileStream); ISheet sheet = workbook.GetSheet("Sheet1"); if (sheet == null) { throw new CustomException("未找到 Sheet1,请检查 Excel 模板"); } // 5. 填充数据(从第7行开始,即索引 6) int dataRowIndex = 6; // 可选:在第一行写入当前时间(比如 A2 单元格) if (sheet.GetRow(1) != null) { // 创建字体对象 var font = sheet.Workbook.CreateFont(); font.FontHeightInPoints = 16; // 创建单元格样式并应用字体和对齐方式 var style = sheet.Workbook.CreateCellStyle(); style.SetFont(font); style.Alignment = HorizontalAlignment.Center; style.VerticalAlignment = VerticalAlignment.Center; // 创建单元格并应用样式 var cell = sheet.GetRow(1).CreateCell(0); cell.SetCellValue($"第({week})周生产计划"); cell.CellStyle = style; } // 6. 遍历工单数据,填充每一行 foreach (var item in ProWeeklyPlanAndDateList) { IRow dataRow = sheet.GetRow(dataRowIndex) ?? sheet.CreateRow(dataRowIndex); dataRow.CreateCell(0).SetCellValue(item.GroupName); // A列 dataRow.CreateCell(1).SetCellValue(Regex.Match(item.ProductName, @"^[^\u4e00-\u9fff]*").Value.Trim()); // B列 dataRow.CreateCell(2).SetCellValue(item.ProductCode); // C列 dataRow.CreateCell(3).SetCellValue(item.ProductName); // D列 dataRow.CreateCell(4).SetCellValue(item.Specification); dataRow.CreateCell(5).SetCellValue(item.PlanStatus); dataRow.CreateCell(6).SetCellValue(item.Planner); dataRow.CreateCell(7).SetCellValue(item.proWeeklyDatechildList.Sum(d => d.PlanNum ?? 0)); dataRow.CreateCell(8).SetCellValue(item.proWeeklyDatechildList.Sum(d => d.ActualQt ?? 0)); // 从 I 列后开始填充(索引9) int startCol = 9; // 循环7天数据 foreach (var day in item.proWeeklyDatechildList) { // 依次填充4个字段 dataRow.CreateCell(startCol++).SetCellValue(day.ProductType); dataRow.CreateCell(startCol++).SetCellValue((double)day.PlanNum); dataRow.CreateCell(startCol++).SetCellValue(day.IsChange); dataRow.CreateCell(startCol++).SetCellValue((double)day.ActualQt); } dataRowIndex++; } // 7. 写入到 MemoryStream(关键:不要用 using 包裹,否则流会被释放!) var memoryStream = new MemoryStream(); workbook.Write(memoryStream); // 8. 返回 byte[] 和文件名 return (memoryStream.ToArray(), "导出数据.xlsx"); } } /// /// 导出日计划 /// /// (byte[] fileBytes, string fileName) IProweekplanManageService.ExportDatePlan(string chooseDate) { var queryList = Context.Queryable() .LeftJoin((p, d) => p.PlanCode == d.PlanCode) .Where((p, d) => d.WeekDate.ToString().Contains(chooseDate)) .Where((p, d) => d.PlanNum > 0) .Select((p, d) => new { p.ProductCode, p.ProductName, p.Specification, p.GroupCode, p.LineCode, p.Priority, p.PlanStatus, p.PlanStartDate, p.PlanEndDate, p.Remark, d.PlanNum, d.WeekDate, d.CreateBy, d.CreatedTime, d.UpdateBy, d.UpdatedTime, p.Id }).ToList(); List proWorkorderList = new List(); if (queryList != null && queryList.Count > 0) { foreach (var item in queryList) { ProWorkorder proWorkorder = new ProWorkorder { Id = XueHua, Workorder = string.Empty, ProductionCode = item.ProductCode, ProductionName = item.ProductName, Specification = item.Specification, DeliveryNum = item.PlanNum }; proWorkorderList.Add(proWorkorder); } } // 读取 Excel 模板 IWebHostEnvironment webHostEnvironment = (IWebHostEnvironment)App.ServiceProvider.GetService(typeof(IWebHostEnvironment)); string fileName = "workorder"; // 模板文件名(不含扩展名) string sFileName = $"{fileName}.xlsx"; string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName); if (!global::System.IO.File.Exists(fullPath)) { throw new CustomException("Excel 模板文件未找到,请确保 ImportTemplate/workorder.xlsx 存在"); } // 4. 加载模板 workbook using (FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read)) { IWorkbook workbook = new XSSFWorkbook(fileStream); ISheet sheet = workbook.GetSheet("Sheet1"); if (sheet == null) { throw new CustomException("未找到 Sheet1,请检查 Excel 模板"); } // 填充数据(从第4行开始,即索引 3) int dataRowIndex = 3; // 可选:在第一行写入当前时间(比如 A2 单元格) if (sheet.GetRow(1) != null) { sheet.GetRow(1).CreateCell(0).SetCellValue(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } // 遍历工单数据,填充每一行 foreach (var item in proWorkorderList) { IRow dataRow = sheet.GetRow(dataRowIndex) ?? sheet.CreateRow(dataRowIndex); dataRow.CreateCell(0).SetCellValue(item.ProductionCode); // A列 dataRow.CreateCell(1).SetCellValue(item.ProductionName); // B列 dataRow.CreateCell(2).SetCellValue(item.Specification); // C列 dataRow.CreateCell(4).SetCellValue((double)item.DeliveryNum); // D列 dataRowIndex++; } // 写入到 MemoryStream(关键:不要用 using 包裹,否则流会被释放!) var memoryStream = new MemoryStream(); workbook.Write(memoryStream); // 返回 byte[] 和文件名 return (memoryStream.ToArray(), "导出数据.xlsx"); } } } }