1294 lines
57 KiB
C#
1294 lines
57 KiB
C#
|
||
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<ProWorkorder>, IProweekplanManageService
|
||
{
|
||
private static bool _hasDeletedOldData = false; // 静态变量,标记是否已执行过删除旧数据
|
||
|
||
/// <summary>
|
||
/// 查询周计划
|
||
/// </summary>
|
||
public PagedInfo<ProWeeklyPlanChildDateDto> SearchWeekplan(WeekplanQueryDto weekplanQuery)
|
||
{
|
||
if (weekplanQuery == null)
|
||
{
|
||
return null;
|
||
}
|
||
return Context.Queryable<ProWeeklyPlan>()
|
||
.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<ProWeeklyDate>()
|
||
.Where(d => d.PlanCode == p.PlanCode)
|
||
.ToList()
|
||
}).ToPage_NO_Convert(weekplanQuery);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Excel导入
|
||
/// </summary>
|
||
public int ImportExcel(IFormFile formFile, string username)
|
||
{
|
||
_hasDeletedOldData = false;
|
||
int insertCount = 0;
|
||
using (var stream = formFile.OpenReadStream())
|
||
{
|
||
try
|
||
{
|
||
List<ProWeeklyPlan> insertProWeekPlanList = new List<ProWeeklyPlan>();
|
||
List<ProWeeklyDate> insertProWeeklyDateList = new List<ProWeeklyDate>();
|
||
|
||
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 保留堆栈
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量插入数据(分批次事务处理)
|
||
/// </summary>
|
||
private void InsertBatchData(
|
||
List<ProWeeklyPlan> proWeeklyPlans,
|
||
List<ProWeeklyDate> proWeeklyDates,
|
||
int year,
|
||
int week,
|
||
string username)
|
||
{
|
||
UseTran(() =>
|
||
{
|
||
// 只在第一次调用该方法时执行删除旧数据的逻辑
|
||
if (!_hasDeletedOldData)
|
||
{
|
||
// 1. 删除旧数据:根据年份+周数+计划编码删除 ProWeeklyDate,然后删除 ProWeeklyPlan
|
||
|
||
|
||
Context.Deleteable<ProWeeklyDate>()
|
||
.Where(d => SqlFunc.Subqueryable<ProWeeklyPlan>()
|
||
.Where(p => p.PlanYear == year && p.PlanWeek == week && p.Id == d.FkWeeklyId)
|
||
.Any()
|
||
)
|
||
.ExecuteCommand();
|
||
|
||
Context.Deleteable<ProWeeklyPlan>()
|
||
.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导入
|
||
/// <summary>
|
||
/// ERP导入
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public int ImportERP()
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 预览日计划
|
||
/// </summary>
|
||
/// <param name="datePlanRequest"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public PagedInfo<ProWorkorder> PreviewDatePlan(DatePlanRequest datePlanRequest)
|
||
{
|
||
if (datePlanRequest.ChooseDate == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var query = Context.Queryable<ProWeeklyPlan>()
|
||
.LeftJoin<ProWeeklyDate>((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<ProWorkorder>();
|
||
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<ProWorkorder>
|
||
{
|
||
Result = resultList,
|
||
TotalNum = query.TotalNum,
|
||
PageIndex = datePlanRequest.PageNum,
|
||
PageSize = datePlanRequest.PageSize
|
||
};
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 更新周计划
|
||
/// </summary>
|
||
/// <param name="updateWeekPlanDto"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public int UpdateWeekPlan(ProWeeklyPlanAndDateDto updateWeekPlanDto)
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
|
||
/// TODO 是否关联(库存、效率、箱标签)
|
||
/// <summary>
|
||
/// 是否关联(库存、效率、箱标签)
|
||
/// </summary>
|
||
/// <param name="inventory"></param>
|
||
/// <param name="efficiency"></param>
|
||
/// <param name="boxLabel"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public int IsAssociateOthers(int inventory, int efficiency, int boxLabel)
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 处理一行Excel数据
|
||
/// </summary>
|
||
/// <param name="row">当前行</param>
|
||
/// <param name="index">索引</param>
|
||
/// <param name="username">用户名</param>
|
||
/// <param name="week">周数</param>
|
||
/// <param name="weekDays">星期信息</param>
|
||
/// <returns>计划和日期列表</returns>
|
||
private (ProWeeklyPlan, List<ProWeeklyDate>) 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<ProWeeklyDate> weeklyDates = new List<ProWeeklyDate>();
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量处理数据并执行数据库操作
|
||
/// </summary>
|
||
/// <param name="weeklyPlans">周计划列表</param>
|
||
/// <param name="weeklyDates">日期计划列表</param>
|
||
/// <param name="year">年份</param>
|
||
/// <param name="week">周数</param>
|
||
private void ProcessBatchData(List<ProWeeklyPlan> weeklyPlans, List<ProWeeklyDate> weeklyDates, int year, int week)
|
||
{
|
||
if (!weeklyPlans.Any()) return;
|
||
|
||
UseTran2(() =>
|
||
{
|
||
// 如果是第一批数据,删除本周的旧数据
|
||
if (weeklyPlans.Count <= BATCH_SIZE * 2) // 简单判断是否为第一批
|
||
{
|
||
Context.Deleteable<ProWeeklyPlan>().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();
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新实际组装进度
|
||
/// </summary>
|
||
/// <param name="year"></param>
|
||
/// <param name="week"></param>
|
||
/// <returns></returns>
|
||
public int UpdateActualAssemblyProgress01(int year, int week)
|
||
{
|
||
int result = 0;
|
||
|
||
// 1. 获取该周的开始和结束日期
|
||
var weekInfo = Context.Queryable<ProWeeklyPlan>()
|
||
.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<ProWeeklyPlan>()
|
||
.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<ProWorkorder>()
|
||
.LeftJoin<ProReportwork>((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<ProWeeklyPlan>().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<ProWeeklyPlan> proWeeklyPlans = Context.Queryable<ProWeeklyPlan>().Where(it => it.PlanYear == year && it.PlanWeek == week).ToList();
|
||
|
||
if (proWeeklyPlans.Count > 0)
|
||
{
|
||
foreach (var plan in proWeeklyPlans)
|
||
{
|
||
//计算实际装配数
|
||
var workorderData = Context
|
||
.Queryable<ProWorkorder>()
|
||
.LeftJoin<ProReportwork>((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<ProWeeklyDate>()
|
||
.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<ProWorkorder>()
|
||
.LeftJoin<ProReportwork>((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<ProWeeklyPlan>()
|
||
.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<ProWeeklyPlan>()
|
||
.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<ProWorkorder>()
|
||
.LeftJoin<ProReportwork>((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<ProWorkorder>()
|
||
.LeftJoin<ProReportwork>((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<ProWeeklyDate>()
|
||
.Where(d => weeklyDateIds.Contains((long)d.FkWeeklyId))
|
||
.ToList();
|
||
|
||
// 7. 准备批量更新的数据
|
||
var weeklyPlansToUpdate = new List<ProWeeklyPlan>();
|
||
var weeklyDatesToUpdate = new List<ProWeeklyDate>();
|
||
|
||
// 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提取周
|
||
/// </summary>
|
||
/// <param name="title"></param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出日计划
|
||
/// </summary>
|
||
/// <param name="year"></param>
|
||
/// <param name="week"></param>
|
||
/// <param name="dayofweek"></param>
|
||
(byte[] fileBytes, string fileName) IProweekplanManageService.ExportWeekDatePlan(int year, int week, string dayofweek)
|
||
{
|
||
// 1. 查询数据:周日计划及关联日期数据
|
||
List<ProWeeklyPlanAndDateDto> ProWeeklyPlanAndDateList = Context.Queryable<ProWeeklyPlan>()
|
||
.LeftJoin<ProWeeklyDate>((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<ProWorkorder> proWorkorderList = new List<ProWorkorder>();
|
||
|
||
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");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出周计划
|
||
/// </summary>
|
||
/// <param name="year"></param>
|
||
/// <param name="week"></param>
|
||
(byte[] fileBytes, string fileName) IProweekplanManageService.ExportWeekOrderPlan(int year, int week)
|
||
{
|
||
// 1. 先查询所有符合条件的头表数据
|
||
var weeklyPlans = Context.Queryable<ProWeeklyPlan>()
|
||
.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<ProWeeklyDate>()
|
||
.Where(d => planCodes.Contains(d.PlanCode))
|
||
.OrderBy(d => d.WeekDate)
|
||
.ToList();
|
||
|
||
List<ProWeeklyPlanAndDateDto> 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");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出日计划
|
||
/// </summary>
|
||
/// <param name="chooseDate"></param>
|
||
(byte[] fileBytes, string fileName) IProweekplanManageService.ExportDatePlan(string chooseDate)
|
||
{
|
||
var queryList = Context.Queryable<ProWeeklyPlan>()
|
||
.LeftJoin<ProWeeklyDate>((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<ProWorkorder> proWorkorderList = new List<ProWorkorder>();
|
||
|
||
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");
|
||
}
|
||
}
|
||
}
|
||
}
|