Valeo_Line_MES_backend/RIZO.Service/MES/product/ProweekplanManageService.cs
2026-01-12 14:57:56 +08:00

1294 lines
57 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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");
}
}
}
}