shgx_tz_mom/ZR.Service/mes/qc/backend/QcBackEndService.cs
2025-05-20 14:02:12 +08:00

1274 lines
52 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 System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Transactions;
using Aliyun.OSS;
using AutoMapper;
using Infrastructure.Attribute;
using Infrastructure.Extensions;
using JinianNet.JNTemplate;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.Extensions.Logging;
using MQTTnet.Protocol;
using SqlSugar;
using ZR.Common.MqttHelper;
using ZR.Model;
using ZR.Model.Business;
using ZR.Model.Dto;
using ZR.Model.MES.wms;
using ZR.Repository;
using ZR.Service.Business.IBusinessService;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace ZR.Service.Business
{
/// <summary>
/// 质量BackEnd工单业务模块Service业务层处理
/// </summary>
[AppService(ServiceType = typeof(IQcBackEndService), ServiceLifetime = LifeTime.Transient)]
public class QcBackEndService : BaseService<QcBackEndServiceWorkorder>, IQcBackEndService
{
private readonly MqttService _mqttService; // 注入MqttService
private readonly ILogger<QcBackEndService> _logger;
public QcBackEndService(MqttService mqttService, ILogger<QcBackEndService> logger)
{
_mqttService = mqttService;
_logger = logger;
}
public QcBackEndLabelAnalysisDto AnalyzeLabelToDto(string label, int type)
{
QcBackEndLabelAnalysisDto labelAnalysisDto =
new()
{
IsOk = true,
Msg = "解析成功!",
LabelCode = label,
};
// 判断内外箱标签
// 解析零件号
labelAnalysisDto.Partnumber = DoAnalyzePartnumber(label);
// 解析数量
labelAnalysisDto.Number = DoAnalyzeQuantity(label);
if (string.IsNullOrEmpty(labelAnalysisDto.Partnumber))
{
labelAnalysisDto.IsOk = false;
labelAnalysisDto.Msg = "标签,零件号解析异常!";
}
// TYPE === 1 时,同时根据物料清单获取详细信息
if (type == 1)
{
WmMaterial material = Context
.Queryable<WmMaterial>()
.Where(it => it.Partnumber == labelAnalysisDto.Partnumber)
.Where(it => it.Type == 1)
.Where(it => it.Status == 1)
.First();
if (material == null)
{
labelAnalysisDto.IsOk = false;
labelAnalysisDto.Msg = "物料清单内无此零件号!请检查物料清单:" + labelAnalysisDto.Partnumber;
}
else
{
labelAnalysisDto.Color = material.Color;
labelAnalysisDto.Specification = material.Specification;
labelAnalysisDto.Description = !string.IsNullOrEmpty(material.Description)
? material.Description
: material.ProductName;
}
}
return labelAnalysisDto;
}
// 标签的零件号解析
public string DoAnalyzePartnumber(string label)
{
// 通用规则下的标签零件号抓取
var predicate = Expressionable
.Create<QcBackEndBaseLabelAnalysis>()
.And(it => it.Code == "PartNumber")
.And(it => it.Status == "1");
List<QcBackEndBaseLabelAnalysis> analysisList = Context
.Queryable<QcBackEndBaseLabelAnalysis>()
.Where(predicate.ToExpression())
.ToList();
foreach (QcBackEndBaseLabelAnalysis analysis in analysisList)
{
if (string.IsNullOrEmpty(analysis.Expression))
{
continue;
}
// 零件号正则表达式
Regex pattern = new(@analysis.Expression);
Match match = pattern.Match(label);
if (match.Success && match.Groups.Count > 1)
{
return match.Groups[1].Value;
}
}
// 非通用规则下的标签零件号抓取
// 解析T58门把手产品标签
try
{
// 直接从标签中截取前 15 个字符作为零件号
if (!string.IsNullOrEmpty(label) && label.Length >= 40 && label.EndsWith('$'))
{
return label.Substring(0, 17);
}
}
catch (Exception)
{
return "";
}
return "";
}
// 标签的数量解析
public int DoAnalyzeQuantity(string label)
{
int result = 0;
// 标签零件号抓取
var predicate = Expressionable
.Create<QcBackEndBaseLabelAnalysis>()
.And(it => it.Code == "Quantity")
.And(it => it.Status == "1");
List<QcBackEndBaseLabelAnalysis> analysisList = Context
.Queryable<QcBackEndBaseLabelAnalysis>()
.Where(predicate.ToExpression())
.ToList();
foreach (QcBackEndBaseLabelAnalysis analysis in analysisList)
{
if (string.IsNullOrEmpty(analysis.Expression))
{
continue;
}
// 零件号正则表达式
Regex pattern = new(@analysis.Expression);
Match match = pattern.Match(label);
if (match.Success && match.Groups.Count > 1)
{
if (Int32.TryParse(match.Groups[1].Value, out result))
{
return result;
}
else
{
return -1;
}
}
}
return -1;
}
// 标签的批次号解析
public string DoAnalyzeBatchCode(string label)
{
// 标签批次号正则抓取
var predicate = Expressionable
.Create<QcBackEndBaseLabelAnalysis>()
.And(it => it.Code == "BatchCode")
.And(it => it.Status == "1");
List<QcBackEndBaseLabelAnalysis> analysisList = Context
.Queryable<QcBackEndBaseLabelAnalysis>()
.Where(predicate.ToExpression())
.ToList();
foreach (QcBackEndBaseLabelAnalysis analysis in analysisList)
{
if (string.IsNullOrEmpty(analysis.Expression))
{
continue;
}
// 零件号正则表达式
Regex pattern = new(@analysis.Expression);
Match match = pattern.Match(label);
if (match.Success && match.Groups.Count > 1)
{
return match.Groups[1].Value;
}
}
return "";
}
public List<QcBackEndAlterationDefectDto> GetDefectInitOptions()
{
List<QcBackEndAlterationDefectDto> defectList = new();
var predicate = Expressionable
.Create<QcBackEndBaseDefect>()
.And(it => it.Status == "1");
/*List<string> groupList = Context
.Queryable<QcBackEndBaseDefect>()
.Where(predicate.ToExpression())
.GroupBy(it => it.Group)
.Select(it => it.Group)
.ToList();*/
List<string> groupList = new() { "油漆", "设备", "毛坯", "程序", "班组操作" };
foreach (string group in groupList)
{
QcBackEndAlterationDefectDto defectDto = new();
defectDto.GroupName = group;
List<QcBackEndChildrenDefectDto> children = Context
.Queryable<QcBackEndBaseDefect>()
.Where(it => it.Group == group)
.Where(predicate.ToExpression())
.Select(it => new QcBackEndChildrenDefectDto
{
Name = it.Name,
Code = it.Code,
Type = it.Type,
Num = 0
})
.ToList();
defectDto.Children = children;
defectList.Add(defectDto);
}
return defectList;
}
public List<QcBackEndAlterationDefectDto> GetDefectTableOptions()
{
List<QcBackEndAlterationDefectDto> defectList = new();
var predicate = Expressionable
.Create<QcBackEndBaseDefect>()
.And(it => it.Type == "打磨")
.And(it => it.Status == "1");
/* List<string> groupList = Context
.Queryable<QcBackEndBaseDefect>()
.Where(predicate.ToExpression())
.GroupBy(it => it.Group)
.Select(it => it.Group)
.ToList();*/
List<string> groupList = new() { "油漆", "设备", "毛坯", "程序", "班组操作" };
foreach (string group in groupList)
{
QcBackEndAlterationDefectDto defectDto = new();
defectDto.GroupName = group;
List<QcBackEndChildrenDefectDto> children = Context
.Queryable<QcBackEndBaseDefect>()
.Where(it => it.Group == group)
.Where(predicate.ToExpression())
.Select(it => new QcBackEndChildrenDefectDto
{
Name = it.Name,
Code = SqlFunc.IIF(
SqlFunc.Length(it.Code) >= 2,
SqlFunc.MergeString(
SqlFunc.Substring(it.Code, 0, 1), // 获取第一个字符注意SQL 中索引通常从1开始
SqlFunc.Substring(it.Code, SqlFunc.Length(it.Code) - 1, 1) // 获取最后一个字符
),
it.Code
),
Type = it.Type,
Num = 0
})
.ToList();
defectDto.Children = children;
defectList.Add(defectDto);
}
return defectList;
}
public List<QcBackEndBaseGroupDto> GetGroupOptions()
{
var predicate = Expressionable.Create<QcBackEndBaseGroup>().And(it => it.Status == "1");
var response = Context
.Queryable<QcBackEndBaseGroup>()
.Where(predicate.ToExpression())
.Select(it => new QcBackEndBaseGroupDto())
.ToList();
return response;
}
public List<QcBackEndBaseSiteDto> GetStieOptions()
{
var predicate = Expressionable.Create<QcBackEndBaseSite>().And(it => it.Status == "1");
var response = Context
.Queryable<QcBackEndBaseSite>()
.Where(predicate.ToExpression())
.Select(it => new QcBackEndBaseSiteDto())
.ToList();
return response;
}
public QcBackEndServiceWorkorder StartBackEndWorkOrder(QcBackEndWorkorderDetailDto data)
{
// 检查是否是已扫过的外箱标签
QcBackEndServiceWorkorder oldWorkOrder = Context
.Queryable<QcBackEndServiceWorkorder>()
.Where(it => it.Label == data.Label)
.First();
// 有旧记录则返回旧记录
if (oldWorkOrder != null)
{
return oldWorkOrder;
}
// 没有旧记录则开启新的记录
try
{
// 检查箱标签是否当内标签扫过
/*bool isInnerLabelScan = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.Label == data.Label)
.Where(it => it.LabelType == 2)
.Any();
if (isInnerLabelScan)
{
throw new Exception("标签异常,该标签已经当内标签扫过!");
}*/
Context.Ado.BeginTran();
DateTime nowTime = DateTime.Now;
// 创建新工单号
QcBackEndWorkorderDetailDto workorderInfo = GetNewWorkOrderCreate(data);
// 赋值
data.WorkOrder = workorderInfo.WorkOrder;
data.SerialNumber = workorderInfo.SerialNumber;
data.StartTime = nowTime;
QcBackEndServiceWorkorder newModel = GetNewWorkOrderInfo(data);
QcBackEndServiceWorkorder result = Context
.Insertable(newModel)
.ExecuteReturnEntity();
if (result == null)
{
Context.Ado.RollbackTran();
throw new Exception("插入新工单异常");
}
// 工单开始记录
QcBackEndRecordLabelScan newScanLabelRecord =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = result.WorkOrder,
PartNumber = result.PartNumber,
Team = result.Team,
SiteNo = result.SiteNo,
ComNo = result.ComNo,
Label = result.Label,
LabelType = 1,
LabelSort = 0,
ScanTime = $"{nowTime:yyyy-MM-dd HH:mm:ss}",
Type = "1",
Status = "1",
Remark = "新工单创建扫描箱标签",
CreatedBy = "后台系统",
CreatedTime = nowTime,
};
int res = Context.Insertable(newScanLabelRecord).ExecuteCommand();
if (res == 0)
{
Context.Ado.RollbackTran();
throw new Exception("插入标签记录异常");
}
QcBackEndLogWorkorder qcBackEndLog =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
Name = "工单开始",
Content = $"工单:{result.WorkOrder}开始,开始时间{nowTime:yyyy-MM-dd HH:mm:ss}",
Type = "100",
Status = "1",
Remark = "触摸屏操作记录",
CreatedBy = "系统",
CreatedTime = nowTime
};
Context.Insertable(qcBackEndLog).ExecuteCommand();
Context.Ado.CommitTran();
return result;
}
catch (Exception ex)
{
Context.Ado.RollbackTran();
throw new Exception(ex.Message);
}
}
// 创建新工单号
public QcBackEndWorkorderDetailDto GetNewWorkOrderCreate(QcBackEndWorkorderDetailDto data)
{
QcBackEndWorkorderDetailDto result = new();
string newWorkOrder = "";
DateTime today = DateTime.Today;
// 检查是否是已扫过的外箱标签
QcBackEndServiceWorkorder lastWorkOrder = Context
.Queryable<QcBackEndServiceWorkorder>()
.Where(it => it.CreatedTime.Value.Date == today)
.OrderByDescending(it => it.SerialNumber)
.First();
if (lastWorkOrder != null)
{
// 递增序列号
int sequenceNumber = lastWorkOrder.SerialNumber + 1;
if (data.IsOnetime == 1)
{
newWorkOrder = $"W{today:yyyyMMdd}{sequenceNumber:D3}";
}
else if (data.IsPolish == 1)
{
newWorkOrder = $"P{today:yyyyMMdd}{sequenceNumber:D3}";
}
else
{
newWorkOrder = $"{today:yyyyMMdd}{sequenceNumber:D3}";
}
result.SerialNumber = sequenceNumber;
}
else
{
// 如果今天还没有创建过工单,则从 "001" 开始
if (data.IsOnetime == 1)
{
newWorkOrder = $"W{today:yyyyMMdd}001";
}
else if (data.IsPolish == 1)
{
newWorkOrder = $"P{today:yyyyMMdd}001";
}
else
{
newWorkOrder = $"{today:yyyyMMdd}001";
}
result.SerialNumber = 1;
}
result.WorkOrder = newWorkOrder;
return result;
}
public static QcBackEndServiceWorkorder GetNewWorkOrderInfo(
QcBackEndWorkorderDetailDto data
)
{
// 新工单
QcBackEndServiceWorkorder model =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = data.WorkOrder,
SerialNumber = data.SerialNumber,
PartNumber = data.PartNumber,
Specification = data.Specification,
Color = data.Color,
Description = data.Description,
Team = data.Team,
SiteNo = data.SiteNo,
ComNo = data.ComNo,
IsOnetime = data.IsOnetime,
IsPolish = data.IsPolish,
IsBack = data.IsBack,
IsOut = data.IsOut,
StartTime = data.StartTime,
EndTime = null,
Label = data.Label,
RequireNumber = 0,
QualifiedNumber = 0,
PolishNumber = 0,
DamoNumber = 0,
BaofeiNumber = 0,
Type = "1",
Status = "1",
Remark = "系统新增工单",
CreatedBy = data.CreatedBy,
CreatedTime = data.CreatedTime,
UpdatedBy = data.UpdatedBy,
UpdatedTime = data.UpdatedTime
};
return model;
}
public QcBackEndServiceWorkorder ChangeWorkOrderDefect(QcBackEndWorkorderDefectDto data)
{
try
{
if (string.IsNullOrEmpty(data.DefectCode))
{
throw new Exception("缺陷项传入为空!");
}
Context.Ado.BeginTran();
DateTime nowTime = DateTime.Now;
// 获取缺陷信息
QcBackEndBaseDefect defect = Context
.Queryable<QcBackEndBaseDefect>()
.Where(it => it.Code == data.DefectCode && it.Status == "1")
.First();
if (defect == null)
{
throw new Exception("缺陷项不在缺陷清单中!");
}
// 工单信息修改
QcBackEndServiceWorkorder qcBackEndWorkorder = Context
.Queryable<QcBackEndServiceWorkorder>()
.Where(it => it.WorkOrder == data.WorkOrder)
.First();
if (qcBackEndWorkorder == null)
{
throw new Exception("工单不存在!");
}
// 获取当前工作单缺陷记录
QcBackEndRecordWorkorderDefect workOrderDefect = Context
.Queryable<QcBackEndRecordWorkorderDefect>()
.Where(it => it.WorkOrder == data.WorkOrder && it.DefectCode == data.DefectCode)
.First();
if (data.Type == "1")
{
// Type === 1 短按新增
if (workOrderDefect != null)
{
// 数据库中有记录DefectNum + 1
workOrderDefect.DefectNum += 1;
workOrderDefect.UpdatedTime = data.CreatedTime;
workOrderDefect.UpdatedBy = data.CreatedBy;
Context.Updateable(workOrderDefect).ExecuteCommand();
}
else
{
// 数据库中无记录,新增记录并设置 DefectNum = 1
workOrderDefect = new QcBackEndRecordWorkorderDefect
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = data.WorkOrder,
PartNumber = qcBackEndWorkorder.PartNumber,
Team = qcBackEndWorkorder.Team,
SiteNo = qcBackEndWorkorder.SiteNo,
ComNo = qcBackEndWorkorder.ComNo,
DefectName = defect.Name,
DefectCode = data.DefectCode,
DefectType = defect.Type,
ClickTime = $"{nowTime:yyyy-MM-dd HH:mm:ss}",
Type = "1",
Status = "1",
Remark = "",
CreatedBy = data.CreatedBy,
CreatedTime = data.CreatedTime,
DefectNum = 1
};
Context.Insertable(workOrderDefect).ExecuteCommand();
}
}
else if (data.Type == "2")
{
// Type == 2 长按修改
if (workOrderDefect != null)
{
// 数据库中有记录,直接赋值
workOrderDefect.UpdatedTime = data.CreatedTime;
workOrderDefect.UpdatedBy = data.CreatedBy;
workOrderDefect.DefectNum = data.DefectNum; // 假设 data.DefectNum 存在
Context.Updateable(workOrderDefect).ExecuteCommand();
}
else
{
// 数据库中无记录,新增记录
workOrderDefect = new QcBackEndRecordWorkorderDefect
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = data.WorkOrder,
PartNumber = qcBackEndWorkorder.PartNumber,
Team = qcBackEndWorkorder.Team,
SiteNo = qcBackEndWorkorder.SiteNo,
ComNo = qcBackEndWorkorder.ComNo,
DefectName = defect.Name,
DefectCode = data.DefectCode,
DefectType = defect.Type,
ClickTime = $"{nowTime:yyyy-MM-dd HH:mm:ss}",
Type = "1",
Status = "1",
Remark = "",
CreatedBy = data.CreatedBy,
CreatedTime = data.CreatedTime,
DefectNum = data.DefectNum // 假设 data.DefectNum 存在
};
Context.Insertable(workOrderDefect).ExecuteCommand();
}
}
UpdateWorkOrderDetail(data.WorkOrder);
// 提交事务
Context.Ado.CommitTran();
return qcBackEndWorkorder;
}
catch (Exception ex)
{
// 回滚事务
Context.Ado.RollbackTran();
throw new Exception("操作失败!", ex);
}
}
public List<QcBackEndRecordWorkorderDefect> GetWorkOrderDefectList(string workorder)
{
return Context
.Queryable<QcBackEndRecordWorkorderDefect>()
.Where(it => it.WorkOrder == workorder)
.ToList();
}
public QcBackEndServiceWorkorder UpdateWorkOrderDetail(string workorder)
{
QcBackEndServiceWorkorder qcBackEndWorkorder = Context
.Queryable<QcBackEndServiceWorkorder>()
.Where(it => it.WorkOrder == workorder)
// .Where(it=>it.Status == "1")
.First();
// 更新工单中的统计数据
qcBackEndWorkorder.QualifiedNumber = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == workorder)
.Where(it => it.LabelType == 2)
.Count();
qcBackEndWorkorder.PolishNumber =
Context
.Queryable<QcBackEndRecordWorkorderDefect>()
.Where(it => it.WorkOrder == workorder && it.DefectType == "抛光")
.Sum(it => it.DefectNum) ?? 0;
qcBackEndWorkorder.DamoNumber =
Context
.Queryable<QcBackEndRecordWorkorderDefect>()
.Where(it => it.WorkOrder == workorder && it.DefectType == "打磨")
.Sum(it => it.DefectNum) ?? 0;
qcBackEndWorkorder.BaofeiNumber =
Context
.Queryable<QcBackEndRecordWorkorderDefect>()
.Where(it => it.WorkOrder == workorder && it.DefectType == "报废")
.Sum(it => it.DefectNum) ?? 0;
qcBackEndWorkorder.RequireNumber =
qcBackEndWorkorder.PolishNumber
+ qcBackEndWorkorder.DamoNumber
+ qcBackEndWorkorder.BaofeiNumber
+ (qcBackEndWorkorder.QualifiedNumber ?? 0);
// 更新工单统计信息到数据库
Context
.Updateable(qcBackEndWorkorder)
.UpdateColumns(it => new
{
it.RequireNumber,
it.QualifiedNumber,
it.PolishNumber,
it.DamoNumber,
it.BaofeiNumber
})
.ExecuteCommand();
return qcBackEndWorkorder;
}
// 后道扫内标签与外标签
public string ScanInnerLabel(QcBackEndLabelScanDto data)
{
try
{
Context.Ado.BeginTran();
DateTime nowTime = DateTime.Now;
// 标签防错 (内标签零件号)
string partNumber = DoAnalyzePartnumber(data.Label);
// 内标签包含外标签
if (!partNumber.Contains(data.PartNumber))
{
Context.Ado.RollbackTran();
return "内标签零件号与工单零件号不一致!";
}
bool hasAny = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.Label == data.Label)
.Where(it => it.LabelType == 2)
.Any();
if (hasAny)
{
Context.Ado.RollbackTran();
return "此内标签已扫过,禁止重复扫码!";
}
// 内标签工单确认
QcBackEndServiceWorkorder workorder = Context
.Queryable<QcBackEndServiceWorkorder>()
.Where(it => it.WorkOrder == data.WorkOrder)
.First();
if (workorder == null)
{
Context.Ado.RollbackTran();
return $"工单异常:工单不存在{data.WorkOrder}";
}
// 打印配置确认
QcBackendBaseOutpackage packageLabelConfig = Context
.Queryable<QcBackendBaseOutpackage>()
.Where(it => workorder.Description.Contains(it.CheckStr))
.First();
if (packageLabelConfig == null)
{
Context.Ado.RollbackTran();
return $"该产品内标签,未检测到对应打印参数:{data.PartNumber}";
}
// 上一个内标签流水号检查
int oldInnerLabelSort = 0;
QcBackEndRecordLabelScan labelScan = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == data.WorkOrder)
.Where(it => it.LabelType == 2)
.OrderByDescending(it => it.LabelSort)
.First();
if (labelScan != null)
{
oldInnerLabelSort = labelScan.LabelSort ?? 0;
}
// 上一个箱标签流水号
int oldPackageLabelSort = 0;
QcBackEndRecordLabelScan packageLabelScan = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == data.WorkOrder)
.Where(it => it.LabelType == 1)
.OrderByDescending(it => it.LabelSort)
.First();
if(packageLabelScan != null)
{
oldPackageLabelSort = packageLabelScan.LabelSort ?? 0;
}
// 内标签总数
int innerLabelCount = Context.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == data.WorkOrder)
.Where(it => it.LabelType == 2)
.Count();
// 箱标签总数
int packageLabelCount = Context.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == data.WorkOrder)
.Where(it => it.LabelType == 1)
.Count();
packageLabelCount -= 1;
if(packageLabelCount < 0)
{
packageLabelCount = 0;
}
// 新内标签流水号
int newInnerLabelSort = oldInnerLabelSort + 1;
// 新外标签流水号
int newPackageLabelSort = oldPackageLabelSort + 1;
// 满箱数
int maxPackage = packageLabelConfig.PackageNum ?? 0;
// 新标签录入
QcBackEndRecordLabelScan newLabelScran =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = data.WorkOrder,
PartNumber = data.PartNumber,
Team = data.Team,
SiteNo = data.SiteNo,
ComNo = data.ComNo,
Label = data.Label,
LabelType = 2,
LabelSort = newInnerLabelSort,
ScanTime = $"{nowTime:yyyy-MM-dd HH:mm:ss}",
Type = "1",
Status = "1",
Remark = "",
CreatedBy = data.CreatedBy,
CreatedTime = data.CreatedTime,
};
//20250516 TODO 判断下一次是否要扫满箱标签
// XXX 满箱判断锁,预留
bool IsCheckFullPackage = true;
// 判断是否是扫的后道箱标签
bool isPackageLabel = false;
string checkPackageLabelStr = "LabelType=1";
if (data.Label.Contains(checkPackageLabelStr))
{
isPackageLabel = true;
}
if (isPackageLabel)
{
bool hasAnyPackageLabel = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.Label == data.Label)
.Where(it => it.LabelType == 1)
.Any();
if (hasAnyPackageLabel)
{
Context.Ado.RollbackTran();
return "此箱标签已扫过,禁止重复扫码!";
}
}
// 是否需要扫后道箱标签
bool needScanPackageLabel = packageLabelCount * maxPackage < innerLabelCount;
// 扫描的是箱标签时 判断是否需要扫箱标签(是箱标签,需要扫,上一组内标签满了)
if (isPackageLabel && IsCheckFullPackage )
{
// 需要扫并且内标签也满了,扫完录入
if (oldInnerLabelSort >= maxPackage && oldInnerLabelSort % maxPackage == 0 && needScanPackageLabel)
{
newLabelScran.LabelType = 1;
newLabelScran.LabelSort = newPackageLabelSort;
newLabelScran.Remark = "扫描箱标签";
int res1 = Context.Insertable(newLabelScran).ExecuteCommand();
if (res1 == 0)
{
Context.Ado.RollbackTran();
return "箱标签录入系统失败!";
}
Context.Ado.CommitTran();
return "箱标签扫入成功!";
}
else
{
Context.Ado.RollbackTran();
return "未满箱,或已扫过箱标签,不需要再次扫箱标签!";
}
}
// 扫描非箱标签,判断此时是否要扫外箱标签
if (IsCheckFullPackage && newInnerLabelSort >= maxPackage && newInnerLabelSort % maxPackage == 1 && needScanPackageLabel)
{
Context.Ado.RollbackTran();
return $"已满箱,请扫描箱标签!当前满箱判断数:{maxPackage}";
}
// 已扫过则继续扫内箱标签
//TODO 其他情况触发箱标签打印判定
newLabelScran.LabelType = 2;
newLabelScran.LabelSort = newInnerLabelSort;
newLabelScran.Remark = "";
int res2 = Context.Insertable(newLabelScran).ExecuteCommand();
if (res2 == 0)
{
Context.Ado.RollbackTran();
return "产标签录入系统失败!";
}
CheckAndPrintPackageLabel(newLabelScran, packageLabelConfig);
Context.Ado.CommitTran();
return "ok";
}
catch (Exception e)
{
Context.Ado.RollbackTran();
return $"异常:{e.Message}";
}
}
/// <summary>
/// 判断是否需要自动出满箱标签
/// </summary>
/// <param name="workorder"></param>
public void CheckAndPrintPackageLabel(QcBackEndRecordLabelScan newLabelScran,QcBackendBaseOutpackage packageLabelConfig)
{
DateTime nowTime = DateTime.Now;
// 判断是否需要自动出满箱标签
int checkSort = newLabelScran.LabelSort ?? 0;
int maxPackage = packageLabelConfig.PackageNum ?? 0;
if (checkSort >= maxPackage && checkSort % maxPackage == 0)
{
// 需要打外箱标签
SendPrintPackageLabelAsync(newLabelScran, packageLabelConfig.FileUrl, maxPackage)
.Wait();
/*int packageSort = 0;
QcBackEndRecordLabelScan packagelabelScan = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == newLabelScran.WorkOrder)
.Where(it => it.LabelType == 1)
.OrderByDescending(it => it.LabelSort)
.First();
if (packagelabelScan != null)
{
packageSort = packagelabelScan.LabelSort ?? 0;
}
QcBackEndRecordLabelScan newPackagePrintLabel =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = newLabelScran.WorkOrder,
PartNumber = newLabelScran.PartNumber,
Team = newLabelScran.Team,
SiteNo = newLabelScran.SiteNo,
ComNo = newLabelScran.ComNo,
Label =
$"Code=BN{newLabelScran.WorkOrder}_{newLabelScran.Team}{packageSort + 1}^ItemNumber={newLabelScran.PartNumber}^Order={newLabelScran.WorkOrder}^Qty={maxPackage}^Type=packageLabel",
LabelType = 1,
LabelSort = packageSort + 1,
ScanTime = $"{nowTime:yyyy-MM-dd HH:mm:ss}",
Type = "1",
Status = "1",
Remark = "自动出满箱标签",
CreatedBy = newLabelScran.CreatedBy,
CreatedTime = newLabelScran.CreatedTime,
};
int res = Context.Insertable(newPackagePrintLabel).ExecuteCommand();*/
}
}
/// <summary>
/// 发送打印后道外箱标签的mqtt信息
/// </summary>
public async Task SendPrintPackageLabelAsync(
QcBackEndRecordLabelScan newLabelScran,
string path,
int maxPackage
)
{
try
{
// 构造主题和消息内容 发送给1站点打印 XXX 预留多站点打印的情况)
string topic = $"shgg_mes/backEnd/print/1站点";
QcBackEndPrintMqttEventDto mqttEventDto = CreateNewQcBackEndPrintMqttEventDto(
newLabelScran,
path,
maxPackage
);
var payload = JsonSerializer.Serialize(mqttEventDto);
// 调用MqttService的发布方法支持异步调用
await _mqttService.PublishAsync(
topic,
payload,
MqttQualityOfServiceLevel.AtLeastOnce,
// 可选:设置消息保留
retain: false
);
_logger.LogInformation($"发送后道外箱标签打印成功:{topic}");
}
catch (Exception ex)
{
_logger.LogError(ex, $"发送后道外箱标签打印失败:{ex.Message}");
throw; // 或根据业务需求处理异常
}
}
/// <summary>
/// 生成打印后道外箱标签的mqtt信息
/// </summary>
/// <param name="newLabelScran"></param>
/// <param name="path"></param>
/// <returns></returns>
public QcBackEndPrintMqttEventDto CreateNewQcBackEndPrintMqttEventDto(
QcBackEndRecordLabelScan newLabelScran,
string path,
int maxPackage
)
{
// 提取箱流水号
int packageSort = 0;
QcBackEndRecordLabelScan packagelabelScan = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == newLabelScran.WorkOrder)
.Where(it => it.LabelType == 1)
.OrderByDescending(it => it.LabelSort)
.First();
if (packagelabelScan != null)
{
packageSort = packagelabelScan.LabelSort + 1 ?? 0;
}
// 提取产品描述
WmMaterial material = Context
.Queryable<WmMaterial>()
.Where(it => it.Partnumber == newLabelScran.PartNumber)
.Where(it => it.Type == 1)
.Where(it => it.Status == 1)
.First();
// 解析产品批次号,如果没有,则生成最新批次号
string batchCode = DoAnalyzeBatchCode(newLabelScran.Label);
if (string.IsNullOrEmpty(batchCode))
{
batchCode = DateTime.Now.ToString("yyMMdd") + "000";
}
// 生成工单号
string workOrder = $"{batchCode}_{packageSort}";
string newLabelCode =
$"Code=PGW{workOrder}^ItemNumber={newLabelScran.PartNumber}^Order=W{workOrder}^Qty={maxPackage}^LabelType=1^LabelBy=HD^Team={newLabelScran.Team}";
string newPackageCode = $"BOX:PGW{workOrder}{newLabelScran.Team}1";
QcBackEndPrintMqttEventDto mqttEventDto =
new()
{
Path = path,
SiteNo = "1站点",
Name = "后道外箱标签打印",
PartNumber = newLabelScran.PartNumber,
Description = material.Description ?? "",
Color = material.Color ?? "",
Specification = material.Specification ?? "",
WorkOrder = workOrder,
PackageCode = newPackageCode,
Team = newLabelScran.Team,
Sort = packageSort,
ProductionTime = "20" + batchCode.Substring(0,6),
BatchCode = batchCode,
PackageNum = maxPackage,
LabelCode = newLabelCode,
LabelType = 1,
CreatedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
};
return mqttEventDto;
}
public string EndBackEndWorkOrderAndCreateStatistics(string workorder)
{
try
{
Context.Ado.BeginTran();
DateTime nowTime = DateTime.Now;
// 工单信息修改
QcBackEndServiceWorkorder qcBackEndWorkorder = Context
.Queryable<QcBackEndServiceWorkorder>()
.Where(it => it.WorkOrder == workorder)
.First();
if (qcBackEndWorkorder == null)
{
throw new Exception("工单不存在!");
}
qcBackEndWorkorder.EndTime = nowTime;
qcBackEndWorkorder.Type = "2";
if (!qcBackEndWorkorder.Remark.Contains("已生成过报表"))
{
qcBackEndWorkorder.Remark += "已生成过报表";
}
Context.Updateable(qcBackEndWorkorder).ExecuteCommand();
// 生成报表记录
List<QcBackEndServiceStatistics> addList = new();
string groupCode = SnowFlakeSingle.Instance.NextId().ToString();
addList.Add(CreateNewStatistics(qcBackEndWorkorder, groupCode, 1));
addList.Add(CreateNewStatistics(qcBackEndWorkorder, groupCode, 2));
addList.Add(CreateNewStatistics(qcBackEndWorkorder, groupCode, 3));
Context
.Deleteable<QcBackEndServiceStatistics>()
.Where(it => it.WorkOrder == workorder)
.ExecuteCommand();
Context.Insertable(addList).ExecuteCommand();
QcBackEndLogWorkorder qcBackEndLog =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
Name = "工单结束",
Content = $"工单:{workorder}结束,结束时间{nowTime:yyyy-MM-dd HH:mm:ss}",
Type = "200",
Status = "1",
Remark = "触摸屏操作记录",
CreatedBy = "系统",
CreatedTime = nowTime
};
Context.Insertable(qcBackEndLog).ExecuteCommand();
// 提交事务
Context.Ado.CommitTran();
return "ok";
}
catch (Exception ex)
{
// 回滚事务
Context.Ado.RollbackTran();
return ex.Message;
}
}
public QcBackEndServiceStatistics CreateNewStatistics(
QcBackEndServiceWorkorder data,
string groupCode,
int groupSort
)
{
List<QcBackEndRecordWorkorderDefect> defectList = Context
.Queryable<QcBackEndRecordWorkorderDefect>()
.Where(it => it.WorkOrder == data.WorkOrder)
.WhereIF(groupSort == 1, it => it.DefectType == "抛光")
.WhereIF(groupSort == 2, it => it.DefectType == "打磨")
.WhereIF(groupSort == 3, it => it.DefectType == "报废")
.ToList();
string JsonString = JsonSerializer.Serialize(defectList);
// 计算合格率
string qualifiedRate = CalculateQualifiedRate(data);
DateTime nowTime = DateTime.Now;
QcBackEndServiceStatistics workorderStatistics =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = data.WorkOrder,
PartNumber = data.PartNumber,
Specification = data.Specification,
Color = data.Color,
Description = data.Description,
Team = data.Team,
SiteNo = data.SiteNo,
ComNo = data.ComNo,
IsOnetime = data.IsOnetime,
IsPolish = data.IsPolish,
IsBack = data.IsBack,
IsOut = data.IsOut,
StartTime = data.StartTime,
EndTime = data.EndTime,
Label = data.Label,
RequireNumber = data.RequireNumber,
QualifiedNumber = data.QualifiedNumber,
QualifiedRate = qualifiedRate,
PolishNumber = data.PolishNumber,
DamoNumber = data.DamoNumber,
BaofeiNumber = data.BaofeiNumber,
GroupCode = groupCode,
GroupSort = groupSort,
GroupDefectJson = JsonString,
Type = "1",
Status = "1",
Remark = "结束工单系统新增质量报表",
CreatedBy = "后端",
CreatedTime = nowTime,
};
return workorderStatistics;
}
public static string CalculateQualifiedRate(QcBackEndServiceWorkorder data)
{
if (data == null || data.RequireNumber <= 0)
{
return "0%";
}
double qualifiedRate =
(double)data.QualifiedNumber.Value / data.RequireNumber.Value * 100;
return $"{qualifiedRate:F1}%";
}
public QcBackEndServiceWorkorder GenerateVirtualLabel(
QcBackEndWorkorderDetailDto workorderDetail
)
{
try
{
Context.Ado.BeginTran();
// 检查当前工单已扫码合格数
int qualifiedNumber = workorderDetail.QualifiedNumber ?? -1;
if (qualifiedNumber < 0)
{
throw new ArgumentException(
"传入合格数异常!",
nameof(workorderDetail.QualifiedNumber)
);
}
int labelCount = GetLabelCountForWorkOrder(workorderDetail.WorkOrder);
if (labelCount < qualifiedNumber)
{
GenerateVirtualLabels(workorderDetail, qualifiedNumber - labelCount);
}
else if (labelCount > qualifiedNumber)
{
DeleteExcessLabels(workorderDetail.WorkOrder, labelCount - qualifiedNumber);
}
Context.Ado.CommitTran();
return UpdateWorkOrderDetail(workorderDetail.WorkOrder);
}
catch (Exception e)
{
Context.Ado.RollbackTran();
throw new Exception($"生成虚拟标签时出错: {e.Message}", e);
}
}
private int GetLabelCountForWorkOrder(string workOrder)
{
return Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == workOrder && it.LabelType == 2)
.Count();
}
private void GenerateVirtualLabels(
QcBackEndWorkorderDetailDto workOrderDetail,
int countToGenerate
)
{
List<QcBackEndRecordLabelScan> virtualLabels = new List<QcBackEndRecordLabelScan>();
int nextLabelNumber = GetNextLabelNumber(workOrderDetail.WorkOrder);
for (int i = 0; i < countToGenerate; i++)
{
string uniqueLabel = GenerateUniqueSequentialLabel(
workOrderDetail.WorkOrder,
nextLabelNumber++
);
virtualLabels.Add(
new QcBackEndRecordLabelScan
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
WorkOrder = workOrderDetail.WorkOrder,
PartNumber = workOrderDetail.PartNumber,
Team = workOrderDetail.Team,
SiteNo = workOrderDetail.SiteNo,
ComNo = workOrderDetail.ComNo,
ScanTime = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
Type = "2",
Status = "1",
Remark = "虚拟标签",
CreatedTime = DateTime.UtcNow,
CreatedBy = "系统",
LabelType = 2,
LabelSort = nextLabelNumber,
Label = uniqueLabel
}
);
}
Context.Insertable(virtualLabels).ExecuteCommand();
}
private void DeleteExcessLabels(string workOrder, int countToDelete)
{
var labelsToDelete = Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == workOrder && it.LabelType == 2)
.OrderByDescending(it => it.LabelSort)
.Take(countToDelete)
.ToList();
Context.Deleteable(labelsToDelete).ExecuteCommand();
}
private int GetNextLabelNumber(string workOrder)
{
return Context
.Queryable<QcBackEndRecordLabelScan>()
.Where(it => it.WorkOrder == workOrder && it.LabelType == 2)
.Max(it => it.LabelSort ?? 0);
}
private string GenerateUniqueSequentialLabel(string workOrder, int number)
{
const string prefix = "VIRT";
string baseLabel = $"{prefix}-{GenerateUniqueId()}-{number:D5}";
string uniqueLabel = baseLabel;
while (IsLabelExists(workOrder, uniqueLabel))
{
uniqueLabel = $"{baseLabel}-{GenerateUniqueId()}";
}
return uniqueLabel;
}
private bool IsLabelExists(string workOrder, string label)
{
return Context
.Queryable<QcBackEndRecordLabelScan>()
.Any(it => it.WorkOrder == workOrder && it.LabelType == 2 && it.Label == label);
}
private string GenerateUniqueId()
{
return Guid.NewGuid().ToString("N").Substring(0, 10); // Generate a 10-character unique ID
}
}
}