feat: 完善材料管理与焊接业务功能

This commit is contained in:
2026-08-06 15:33:11 +08:00
parent e418b60fdb
commit 1de898812e
49 changed files with 2566 additions and 555 deletions
+214 -33
View File
@@ -259,28 +259,60 @@ namespace BLL
/// <param name="pipelineIds"></param>
/// <param name="warehouseCode"></param>
/// <returns></returns>
public static List<Tw_PipeMatMatchOutput> GetPipeMatMatch(string projectId, List<string> pipelineIds, string warehouseCode, Dictionary<string, List<string>> priorityWeldJoints = null)
public static List<Tw_PipeMatMatchOutput> GetPipeMatMatch(string projectId, List<string> pipelineIds, string warehouseCode, Dictionary<string, List<string>> priorityWeldJoints = null, string pipeArea = null)
{
using (Model.SGGLDB db = new Model.SGGLDB(Funs.ConnString))
{
var results = new List<Tw_PipeMatMatchOutput>();
if (pipelineIds == null || !pipelineIds.Any())
{
return results;
}
pipelineIds = pipelineIds.Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
if (!pipelineIds.Any())
{
return results;
}
// 获取所需材料列表
var pipeLineMats = (from x in db.HJGL_PipeLineMat
join z in db.HJGL_Pipeline on x.PipelineId equals z.PipelineId
join m in db.WBS_UnitWork on z.UnitWorkId equals m.UnitWorkId
join w in db.HJGL_WeldJoint on x.WeldJointId equals w.WeldJointId into weldJoin
from w in weldJoin.DefaultIfEmpty()
// 材料匹配只处理所需量大于0的非散件材料,避免零用量明细占用库存或影响焊口生成任务单判断
where z.ProjectId == projectId && pipelineIds.Contains(z.PipelineId) && x.PrefabricatedComponents != "" && (x.Number ?? 0) > 0
select new
{
PipeLineMat = x,
PipelineCode = z.PipelineCode,
UnitWorkId = z.UnitWorkId,
UnitWorkName = m.UnitWorkName,
WeldJointCode = w == null ? null : w.WeldJointCode
}).ToList();
// 先保留当前区域内已焊和未焊数据,用于识别“已经开工但尚未完成”的续作组件。
var pipeLineMatQuery = from x in db.HJGL_PipeLineMat
join z in db.HJGL_Pipeline on x.PipelineId equals z.PipelineId
join m in db.WBS_UnitWork on z.UnitWorkId equals m.UnitWorkId
join w in db.HJGL_WeldJoint on x.WeldJointId equals w.WeldJointId into weldJoin
from w in weldJoin.DefaultIfEmpty()
// 先保留组件全部材料,避免数量为0的历史已焊口导致续作组件识别遗漏
where z.ProjectId == projectId && pipelineIds.Contains(z.PipelineId)
&& x.PrefabricatedComponents != null && x.PrefabricatedComponents != ""
select new
{
PipeLineMat = x,
PipelineCode = z.PipelineCode,
UnitWorkId = z.UnitWorkId,
UnitWorkName = m.UnitWorkName,
WeldJoint = w
};
string jointAttribute = GetJointAttributeByPipeArea(pipeArea);
bool filterByPipeArea = !string.IsNullOrEmpty(jointAttribute);
if (filterByPipeArea)
{
// 页面用途以材料表 PipeArea 为准,工厂/现场只能匹配相应类型的焊口。
pipeLineMatQuery = pipeLineMatQuery.Where(x => x.PipeLineMat.PipeArea == pipeArea
&& x.WeldJoint != null
&& x.WeldJoint.JointAttribute == jointAttribute);
}
var allPipeLineMats = pipeLineMatQuery.ToList();
var continuationComponentKeys = new HashSet<string>(allPipeLineMats
.Where(x => x.WeldJoint != null && !string.IsNullOrEmpty(x.WeldJoint.WeldingDailyId))
.Select(x => GetComponentKey(x.PipeLineMat.PipelineId, x.PipeLineMat.PrefabricatedComponents)));
// 页面匹配只展示并消耗尚未焊接的焊口材料;不传区域的旧调用保留原查询行为。
var pipeLineMats = filterByPipeArea
? allPipeLineMats.Where(x => x.WeldJoint != null
&& x.WeldJoint.WeldingDailyId==null
&& (x.PipeLineMat.Number ?? 0) > 0).ToList()
: allPipeLineMats.Where(x => (x.PipeLineMat.Number ?? 0) > 0).ToList();
var materialCodes = pipeLineMats
.Where(x => !string.IsNullOrEmpty(x.PipeLineMat.MaterialCode))
@@ -311,17 +343,16 @@ namespace BLL
{
lib = libByMaterialCode[x.PipeLineMat.MaterialCode];
}
// 未反写时只能按导入材料编码 Code 找候选材料库,用于展示材料名称、规格等基础信息
// 未反写时只能按导入材料编码 Code 找候选材料库。
else if (!string.IsNullOrEmpty(x.PipeLineMat.MaterialCode2) && libByCode.ContainsKey(x.PipeLineMat.MaterialCode2))
{
lib = libByCode[x.PipeLineMat.MaterialCode2];
}
// MaterialCode 对外展示导入材料编码,避免把临时匹配出的材料主编码误当成已确认结果。
string code = !string.IsNullOrEmpty(x.PipeLineMat.MaterialCode2) ? x.PipeLineMat.MaterialCode2 : lib?.Code;
return new Tw_PipeMatMatchOutput
{
Id = Guid.NewGuid().ToString(),
{
Id = Guid.NewGuid().ToString(),
PipeLineMatId = x.PipeLineMat.PipeLineMatId,
PipelineId = x.PipeLineMat.PipelineId,
PipelineCode = x.PipelineCode,
@@ -329,7 +360,7 @@ namespace BLL
UnitWorkName = x.UnitWorkName,
PrefabricatedComponents = x.PipeLineMat.PrefabricatedComponents,
WeldJointId = x.PipeLineMat.WeldJointId,
WeldJointCode = x.WeldJointCode,
WeldJointCode = x.WeldJoint == null ? null : x.WeldJoint.WeldJointCode,
MaterialCode = code,
MatchMaterialCode = x.PipeLineMat.MaterialCode,
Code = code,
@@ -342,29 +373,179 @@ namespace BLL
NeedNum = x.PipeLineMat.Number,
};
}).ToList();
var newRequiredMaterials = new List<Tw_PipeMatMatchOutput>();
var orderedPipelineMaterials = new Dictionary<string, List<Tw_PipeMatMatchOutput>>();
foreach (string id in pipelineIds)
{
orderedPipelineMaterials[id] = requiredMaterials
.Where(x => x.PipelineId == id)
.OrderBy(x => x.PrefabricatedComponents)
.ThenBy(x => x.WeldJointCode)
.ThenBy(x => x.MaterialCode)
.ThenBy(x => x.PipeLineMatId)
.ToList();
}
var newRequiredMaterials = new List<Tw_PipeMatMatchOutput>();
var addedMaterialIds = new HashSet<string>();
// 先处理全部管线的手动优先焊口,保证人工选择始终高于任何自动优先规则。
foreach (string id in pipelineIds)
{
var pipelineMaterials = requiredMaterials.Where(x => x.PipelineId == id).ToList();
if (priorityWeldJoints != null && priorityWeldJoints.ContainsKey(id) && priorityWeldJoints[id] != null && priorityWeldJoints[id].Any())
{
// 手动优先焊口只改变本次匹配消耗顺序,不写入长期规则。
var weldJointIds = priorityWeldJoints[id];
newRequiredMaterials.AddRange(pipelineMaterials
var manualPriorityMaterials = orderedPipelineMaterials[id]
.Where(x => weldJointIds.Contains(x.WeldJointId))
.OrderBy(x => weldJointIds.IndexOf(x.WeldJointId)));
newRequiredMaterials.AddRange(pipelineMaterials
.Where(x => !weldJointIds.Contains(x.WeldJointId)));
}
else
{
newRequiredMaterials.AddRange(pipelineMaterials);
.OrderBy(x => weldJointIds.IndexOf(x.WeldJointId))
.ThenBy(x => x.PrefabricatedComponents)
.ThenBy(x => x.WeldJointCode)
.ThenBy(x => x.MaterialCode)
.ToList();
newRequiredMaterials.AddRange(manualPriorityMaterials);
foreach (var item in manualPriorityMaterials)
{
addedMaterialIds.Add(item.PipeLineMatId);
}
}
}
// 再处理全部续作组件,避免前一条管线的未开工组件抢占后一条管线续作组件的库存。
foreach (string id in pipelineIds)
{
var continuationMaterials = orderedPipelineMaterials[id]
.Where(x => !addedMaterialIds.Contains(x.PipeLineMatId)
&& continuationComponentKeys.Contains(GetComponentKey(x.PipelineId, x.PrefabricatedComponents)))
.ToList();
newRequiredMaterials.AddRange(continuationMaterials);
foreach (var item in continuationMaterials)
{
addedMaterialIds.Add(item.PipeLineMatId);
}
}
// 最后处理所有尚未开工组件,保持管线及组件内的稳定排序。
foreach (string id in pipelineIds)
{
newRequiredMaterials.AddRange(orderedPipelineMaterials[id].Where(x => !addedMaterialIds.Contains(x.PipeLineMatId)));
}
results = GetMatMatchOutput(newRequiredMaterials, warehouseCode, projectId, true);
// 组件匹配率以当前区域剩余未焊口的材料需求为口径,并回填到每条明细供表格合并展示。
foreach (var componentGroup in results.GroupBy(x => new { x.PipelineId, x.PrefabricatedComponents }))
{
decimal needNum = componentGroup.Sum(x => x.NeedNum) ?? 0;
decimal matchNum = componentGroup.Sum(x => x.MatchNum) ?? 0;
decimal componentMatchRate = needNum > 0 ? matchNum / needNum : 0;
string componentMatchRateString = Math.Round(componentMatchRate * 100, 2).ToString() + "%";
foreach (var item in componentGroup)
{
item.ComponentMatchRate = componentMatchRate;
item.ComponentMatchRateString = componentMatchRateString;
}
}
return results;
}
}
/// <summary>
/// 获取单位工程下当前区域内同时存在已焊口和未焊口的组件汇总。
/// </summary>
public static List<Tw_PartialWeldedComponentOutput> GetPartialWeldedComponents(string projectId, string unitWorkId, string pipeArea, string warehouseCode)
{
string jointAttribute = GetJointAttributeByPipeArea(pipeArea);
if (string.IsNullOrEmpty(projectId) || string.IsNullOrEmpty(unitWorkId) || string.IsNullOrEmpty(jointAttribute))
{
return new List<Tw_PartialWeldedComponentOutput>();
}
using (Model.SGGLDB db = new Model.SGGLDB(Funs.ConnString))
{
var componentWeldRows = (from x in db.HJGL_PipeLineMat
join z in db.HJGL_Pipeline on x.PipelineId equals z.PipelineId
join m in db.WBS_UnitWork on z.UnitWorkId equals m.UnitWorkId
join w in db.HJGL_WeldJoint on x.WeldJointId equals w.WeldJointId
where z.ProjectId == projectId && z.UnitWorkId == unitWorkId
&& x.PipeArea == pipeArea
&& x.PrefabricatedComponents != null && x.PrefabricatedComponents != ""
&& w.JointAttribute == jointAttribute
select new
{
z.PipelineId,
z.PipelineCode,
z.UnitWorkId,
m.UnitWorkName,
x.PrefabricatedComponents,
w.WeldJointId,
w.WeldingDailyId
}).ToList();
var partialComponents = componentWeldRows
.GroupBy(x => new { x.PipelineId, x.PipelineCode, x.UnitWorkId, x.UnitWorkName, x.PrefabricatedComponents })
.Select(group =>
{
var weldJoints = group.GroupBy(x => x.WeldJointId).Select(x => x.First()).ToList();
int weldedCount = weldJoints.Count(x => !string.IsNullOrEmpty(x.WeldingDailyId));
int totalCount = weldJoints.Count;
return new Tw_PartialWeldedComponentOutput
{
UnitWorkId = group.Key.UnitWorkId,
UnitWorkName = group.Key.UnitWorkName,
PipelineId = group.Key.PipelineId,
PipelineCode = group.Key.PipelineCode,
PrefabricatedComponents = group.Key.PrefabricatedComponents,
PipeArea = pipeArea,
PipeAreaText = pipeArea == PipelineService.PipeArea_SHOP ? "工厂预制" : "现场安装",
TotalWeldJointCount = totalCount,
WeldedWeldJointCount = weldedCount,
UnweldedWeldJointCount = totalCount - weldedCount
};
})
.Where(x => x.WeldedWeldJointCount > 0 && x.UnweldedWeldJointCount > 0)
.OrderBy(x => x.PipelineCode)
.ThenBy(x => x.PrefabricatedComponents)
.ToList();
if (!partialComponents.Any())
{
return partialComponents;
}
// 导出匹配率复用页面匹配规则,保证区域、未焊口及续作组件优先级完全一致。
var pipelineIds = partialComponents.Select(x => x.PipelineId).Distinct().ToList();
var matchOutputs = GetPipeMatMatch(projectId, pipelineIds, warehouseCode, null, pipeArea);
var componentRates = matchOutputs
.GroupBy(x => GetComponentKey(x.PipelineId, x.PrefabricatedComponents))
.ToDictionary(x => x.Key, x => x.First().ComponentMatchRate ?? 0);
foreach (var component in partialComponents)
{
decimal componentRate;
componentRates.TryGetValue(GetComponentKey(component.PipelineId, component.PrefabricatedComponents), out componentRate);
component.ComponentMatchRate = componentRate;
component.ComponentMatchRateString = Math.Round(componentRate * 100, 2).ToString() + "%";
}
return partialComponents;
}
}
private static string GetJointAttributeByPipeArea(string pipeArea)
{
if (pipeArea == PipelineService.PipeArea_SHOP)
{
return "预制口";
}
if (pipeArea == PipelineService.PipeArea_FIELD)
{
return "安装口";
}
return null;
}
private static string GetComponentKey(string pipelineId, string componentCode)
{
return (pipelineId ?? string.Empty) + "\u001f" + (componentCode ?? string.Empty);
}
/// <summary>
/// 根据管线材料匹配结果,获取管线匹配率
/// </summary>