using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using DOAN.Model; using DOAN.Model.BZFM; using DOAN.Model.MES.base_; using DOAN.Model.MES.product; using DOAN.Service.MES.product.IService; using Infrastructure; using Infrastructure.Attribute; using Microsoft.AspNetCore.Http; using NLog; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using SqlSugar; namespace DOAN.Service.MES.product { /// /// 工单导入服务 /// [AppService( ServiceType = typeof(IProWorkorderImportService), ServiceLifetime = LifeTime.Transient )] public class ProWorkorderImportService : BaseService, IProWorkorderImportService { /// /// 日志记录器 /// private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); /// /// 生成唯一的工单编号 /// /// 工单对象 /// 日期值 /// 当前编号索引 /// 昵称代码 /// 最大尝试次数 /// 生成的工单编号和更新后的索引 private Tuple GenerateUniqueWorkorderNo(ProWorkorder workorder, DateTime dateValue, int currentIndex, string nickCode, int maxAttempts = 100) { string workorderNo; bool isUnique; int attempts = 0; do { attempts++; if (attempts > maxAttempts) { throw new Exception( $"生成工单编号失败,超过最大尝试次数({maxAttempts}),productionCode: {workorder.productionCode}" ); } workorderNo = dateValue.ToString("yyyyMMdd") + "_" + workorder.GroupCode + workorder.RouteCode + "_" + nickCode + "_" + currentIndex.ToString("000"); // 检查编号是否已存在 isUnique = !Context .Queryable() .Where(it => it.Workorder == workorderNo) .Any(); if (!isUnique) { currentIndex++; } } while (!isUnique); return new Tuple(workorderNo, currentIndex); } /// /// 获取指定productionCode当天的最大工单编号索引 /// /// 产品代码 /// 日期值 /// 最大编号索引 private int GetMaxWorkorderIndex(string productionCode, DateTime dateValue) { var workorderNumbers = Context .Queryable() .Where(it => it.WorkorderDate == dateValue.Date) .Where(it => it.productionCode == productionCode) .Select(it => it.Workorder) .ToList() .Where(w => w.StartsWith(dateValue.ToString("yyyyMMdd"))) .Select(w => { var parts = w.Split('_'); if (parts.Length >= 4 && int.TryParse(parts[3], out int index)) return index; return 0; }) .ToList(); // 如果没有找到记录,返回0 return workorderNumbers.Count > 0 ? workorderNumbers.Max() : 0; } /// /// 从Excel文件读取工单数据 /// /// Excel文件 /// 用户名 /// 返回的日期值 /// 工单列表 private List ReadWorkordersFromExcel(IFormFile formFile, string username, out DateTime dateValue) { List workorderList = new List(); dateValue = DateTime.Now; using (var stream = formFile.OpenReadStream()) { IWorkbook workbook = new XSSFWorkbook(stream); ISheet sheet = workbook.GetSheetAt(0); // 处理第2行 获取日期 IRow secondRow = sheet.GetRow(1); ICell cell = secondRow.GetCell(0); // 安全获取日期值 if (cell != null && cell.DateCellValue.HasValue) { dateValue = cell.DateCellValue.Value; } // 遍历每一行 for (int row = 4; row <= sheet.LastRowNum; row++) { IRow currentRow = sheet.GetRow(row); if (currentRow != null) // 确保行不为空 { ProWorkorder workorder = new ProWorkorder(); //00 主体品名 ICell currentCell_01 = currentRow.GetCell(0); workorder.productionName = currentCell_01?.ToString(); if ( currentCell_01 == null || string.IsNullOrEmpty(workorder.productionName) ) { throw new Exception($"{row + 1}行【主体品名】不可为空"); } //01 成品型号 ICell currentCell_02 = currentRow.GetCell(1); workorder.productionCode = currentCell_02?.ToString(); if ( currentCell_02 == null || string.IsNullOrEmpty(workorder.productionCode) ) { throw new Exception($"{row + 1}行【主体型号】不可为空"); } //02 单位 ICell currentCell_04 = currentRow.GetCell(2); workorder.Unit = currentCell_04?.ToString() ?? string.Empty; //3 计划数量 ICell currentCell_07 = currentRow.GetCell(3); if (currentCell_07 != null) { if (currentCell_07.CellType == CellType.Numeric) { workorder.PlanNum = (int)currentCell_07.NumericCellValue; } else if (currentCell_07.CellType == CellType.String && int.TryParse(currentCell_07.StringCellValue, out int planNum)) { workorder.PlanNum = planNum; } else { workorder.PlanNum = 0; } } else { workorder.PlanNum = 0; } if (workorder.PlanNum < 0) { workorder.PlanNum = 0; } //4 原材料名称 ICell currentCell_11 = currentRow.GetCell(4); workorder.MaterialName = currentCell_11?.ToString(); if ( currentCell_11 == null || string.IsNullOrEmpty(workorder.MaterialName) ) { throw new Exception($"{row + 1}行【材料型号】不可为空"); } //5 原材料编号 ICell currentCell_12 = currentRow.GetCell(5); workorder.MaterialCode = currentCell_12?.ToString(); if ( currentCell_12 == null || string.IsNullOrEmpty(workorder.MaterialCode) ) { throw new Exception($"{row + 1}行【材料编码】不可为空"); } //6 原材料材质 ICell currentCell_13 = currentRow.GetCell(6); workorder.MaterialtextureCode = currentCell_13?.ToString() ?? string.Empty; //7 炉号 ICell currentCell_14 = currentRow.GetCell(7); workorder.StoveCode = currentCell_14?.ToString() ?? string.Empty; //8 图号 ICell currentCell_15 = currentRow.GetCell(8); workorder.DrawingCode = currentCell_15?.ToString() ?? string.Empty; //9 版本 ICell currentCell_16 = currentRow.GetCell(9); workorder.Version = currentCell_16?.ToString() ?? string.Empty; //10 指导日期 ICell cell17 = currentRow.GetCell(10); // 安全获取日期值 if (cell17 != null && cell17.DateCellValue.HasValue) { workorder.InstructionDate = cell17.DateCellValue.Value; } else { workorder.InstructionDate = dateValue; } // 11 车间code ICell currentCell_18 = currentRow.GetCell(11); if (currentCell_18 == null) { workorder.WorkshopCode = string.Empty; } else { if (currentCell_18.CellType == CellType.Numeric) { workorder.WorkshopCode = currentCell_18.NumericCellValue.ToString(); } else if (currentCell_18.CellType == CellType.String) { workorder.WorkshopCode = currentCell_18.StringCellValue; } else { workorder.WorkshopCode = string.Empty; } } //12 组号code ICell currentCell_19 = currentRow.GetCell(12); if (currentCell_19 == null) { workorder.GroupCode = string.Empty; } else { if (currentCell_19.CellType == CellType.Numeric) { workorder.GroupCode = currentCell_19.NumericCellValue.ToString(); } else if (currentCell_19.CellType == CellType.String) { workorder.GroupCode = currentCell_19.StringCellValue; } else { workorder.GroupCode = string.Empty; } } //13 班号code ICell currentCell_20 = currentRow.GetCell(13); if (currentCell_20 == null) { workorder.RouteCode = string.Empty; } else { if (currentCell_20.CellType == CellType.Numeric) { workorder.RouteCode = currentCell_20.NumericCellValue.ToString(); } else if (currentCell_20.CellType == CellType.String) { workorder.RouteCode = currentCell_20.StringCellValue; } else { workorder.RouteCode = string.Empty; } } //14 优先级 ICell currentCell_21 = currentRow.GetCell(14); if (currentCell_21 == null) { workorder.Priority = 1; } else { string priorityStr = string.Empty; if (currentCell_21.CellType == CellType.String) { priorityStr = currentCell_21.StringCellValue; } else if (currentCell_21.CellType == CellType.Numeric) { priorityStr = currentCell_21.NumericCellValue.ToString(); } if (priorityStr == "优先") { workorder.Priority = 3; } else if (priorityStr == "插单") { workorder.Priority = 2; } else if (priorityStr == "正常" || string.IsNullOrEmpty(priorityStr)) { workorder.Priority = 1; } else { workorder.Priority = 1; } } //15 节拍 ICell currentCell_22 = currentRow.GetCell(15); if (currentCell_22 != null) { if (currentCell_22.CellType == CellType.Numeric) { workorder.Beat = (int)currentCell_22.NumericCellValue; } else if (currentCell_22.CellType == CellType.String && int.TryParse(currentCell_22.StringCellValue, out int beat)) { workorder.Beat = beat; } else { workorder.Beat = 0; } } else { workorder.Beat = 0; } //16 进料单号(外购) ICell currentCell_010 = currentRow.GetCell(16); workorder.FeedOrder = currentCell_010?.StringCellValue ?? string.Empty; //17 客户单号(出货) ICell currentCell_011 = currentRow.GetCell(17); workorder.CustomerOrder = currentCell_011?.StringCellValue ?? string.Empty; //18 备注 ICell currentCell_012 = currentRow.GetCell(18); workorder.Remark01 = currentCell_012?.StringCellValue ?? string.Empty; workorder.Id = SnowFlakeSingle.Instance.NextId().ToString(); workorder.CreatedBy = username; workorder.CreatedTime = DateTime.Now; workorder.WorkorderDate = dateValue; workorder.DefectNum = 0; workorder.ShipmentNum = 0; workorder.Status = 1; workorderList.Add(workorder); } } } return workorderList; } /// /// 导入工单 必须整删除 整改 /// /// /// /// public int ImportData(IFormFile formFile, string username) { int result = 0; List workorderList; DateTime dateValue; // XXX 改为从物料清单获取信息 List mmMaterials = Context .Queryable() .Where(it => it.Status == "启用") .ToList(); try { Logger.Info($"开始导入工单数据,用户名: {username}"); workorderList = ReadWorkordersFromExcel(formFile, username, out dateValue); Logger.Info($"读取到 {workorderList.Count} 条工单数据"); // 按productionCode分组并顺序编号 // 先按productionCode分组,确保同一产品的工单连续排序 var productionCodeGroups = workorderList .GroupBy(w => w.productionCode) .ToList(); // 获取所有工单的最大sort值,用于后续排序 var maxSortNullable = Context .Queryable() .Where(it => it.WorkorderDate == dateValue.Date) //.Select(it => it.Sort) .Max(it => it.Sort); // 如果没有找到记录,设置默认值0 int maxSort = maxSortNullable.HasValue ? maxSortNullable.Value : 0; // 从maxSort + 10开始,确保sort值按10、20、30...递增 int currentSort = (maxSort / 10) * 10 + 10; foreach (var group in productionCodeGroups) { Logger.Info($"处理产品代码: {group.Key},共 {group.Count()} 条工单"); // 获取当前productionCode当天已有的最大编号 int maxIndex = GetMaxWorkorderIndex(group.Key, dateValue); // 从最大编号+1开始顺序编号 int currentIndex = maxIndex + 1; Logger.Info($"产品代码: {group.Key},当前最大编号索引: {maxIndex},开始编号: {currentIndex}"); foreach (var workorder in group) { string nickCode = mmMaterials .Where(it => it.MaterialCode == workorder.productionCode) .Select(it => it.Type) .FirstOrDefault(); // 生成唯一的工单编号 var generateResult = GenerateUniqueWorkorderNo(workorder, dateValue, currentIndex, nickCode); workorder.Workorder = generateResult.Item1; // 使用连续的sort值,不受编号冲突影响 workorder.Sort = currentSort; Logger.Info($"生成工单编号: {workorder.Workorder},产品: {workorder.productionName},sort: {currentSort}"); currentIndex = generateResult.Item2 + 1; // 增加sort值,确保下一个工单的sort值为当前值+10 currentSort += 10; } } UseTran2(() => { Logger.Info($"删除日期 {dateValue.ToShortDateString()} 的现有工单数据"); Context .Deleteable() .Where(it => it.WorkorderDate == dateValue) .ExecuteCommand(); Logger.Info($"插入新工单数据,共 {workorderList.Count} 条"); result = Context.Insertable(workorderList).ExecuteCommand(); }); Logger.Info($"工单导入完成,成功导入 {result} 条数据"); return result; } catch (Exception ex) { Logger.Error(ex, $"导入工单时出错: {ex.Message}"); throw new Exception($"导入工单时出错,错误: {ex.Message}"); } } /// /// 分批导入,追加导入 /// /// /// /// public int ImportDataAppend(IFormFile formFile, string username) { int result = 0; List workorderList; DateTime dateValue; // XXX 改为从物料清单获取信息 List mmMaterials = Context .Queryable() .Where(it => it.Status == "启用") .ToList(); try { Logger.Info($"开始追加导入工单数据,用户名: {username}"); workorderList = ReadWorkordersFromExcel(formFile, username, out dateValue); Logger.Info($"读取到 {workorderList.Count} 条工单数据"); // 按productionCode分组并顺序编号 var productionCodeGroups = workorderList.GroupBy(w => w.productionCode).ToList(); // 获取所有工单的最大sort值,用于后续排序 var maxSortNullable = Context .Queryable() .Where(it => it.WorkorderDate == dateValue.Date) //.Select(it => it.Sort) .Max(it => it.Sort); // 如果没有找到记录,设置默认值0 int maxSort = maxSortNullable.HasValue ? maxSortNullable.Value : 0; // 从maxSort + 10开始,确保sort值按10、20、30...递增 int currentSort = (maxSort / 10) * 10 + 10; foreach (var group in productionCodeGroups) { Logger.Info($"处理产品代码: {group.Key},共 {group.Count()} 条工单"); // 获取当前productionCode当天已有的最大编号 int maxIndex = GetMaxWorkorderIndex(group.Key, dateValue); // 从最大编号+1开始顺序编号 int currentIndex = maxIndex + 1; Logger.Info($"产品代码: {group.Key},当前最大编号索引: {maxIndex},开始编号: {currentIndex}"); foreach (var workorder in group) { string nickCode = mmMaterials .Where(it => it.MaterialCode == workorder.productionCode) .Select(it => it.Type) .FirstOrDefault(); // 生成唯一的工单编号 var generateResult = GenerateUniqueWorkorderNo(workorder, dateValue, currentIndex, nickCode); workorder.Workorder = generateResult.Item1; // 使用连续的sort值,不受编号冲突影响 workorder.Sort = currentSort; Logger.Info($"生成工单编号: {workorder.Workorder},产品: {workorder.productionName},sort: {currentSort}"); currentIndex = generateResult.Item2 + 1; // 增加sort值,确保下一个工单的sort值为当前值+10 currentSort += 10; } } UseTran2(() => { // 追加导入,不删除现有数据 Logger.Info($"追加插入工单数据,共 {workorderList.Count} 条"); result = Context.Insertable(workorderList).ExecuteCommand(); }); Logger.Info($"工单追加导入完成,成功导入 {result} 条数据"); return result; } catch (Exception ex) { Logger.Error(ex, $"追加导入工单时出错: {ex.Message}"); throw new Exception($"追加导入工单时出错,错误: {ex.Message}"); } } } }