152 lines
4.6 KiB
C#
152 lines
4.6 KiB
C#
using DongfangHydro.Dashboard.Api.Contracts;
|
|
using DongfangHydro.Dashboard.Api.Domain;
|
|
|
|
namespace DongfangHydro.Dashboard.Api.Services;
|
|
|
|
public static class DashboardMapper
|
|
{
|
|
public static ProductionNodeDto? BuildTree(IReadOnlyCollection<ProductionNode> nodes)
|
|
{
|
|
var root = nodes
|
|
.Where(node => node.ParentNodeId is null)
|
|
.OrderBy(node => node.SortOrder)
|
|
.FirstOrDefault(node => node.NodeType == "order")
|
|
?? nodes.Where(node => node.ParentNodeId is null).OrderBy(node => node.SortOrder).FirstOrDefault();
|
|
|
|
if (root is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var childrenByParent = nodes
|
|
.Where(node => node.ParentNodeId.HasValue)
|
|
.GroupBy(node => node.ParentNodeId!.Value)
|
|
.ToDictionary(
|
|
group => group.Key,
|
|
group => group.OrderBy(node => node.SortOrder).ThenBy(node => node.Code).ToList());
|
|
|
|
return MapNode(root, childrenByParent, []);
|
|
}
|
|
|
|
public static ProductionNodeDto MapNode(ProductionNode node)
|
|
{
|
|
return MapNode(node, new Dictionary<Guid, List<ProductionNode>>(), []);
|
|
}
|
|
|
|
public static TrendPointDto MapTrend(ProductionTrendPoint point)
|
|
{
|
|
return new TrendPointDto(
|
|
point.SampleTime.ToLocalTime().ToString("HH:mm:ss"),
|
|
point.Completion,
|
|
point.Risk,
|
|
point.PlannedQty,
|
|
point.ActualQty,
|
|
Math.Round(point.Achievement, 1));
|
|
}
|
|
|
|
public static RiskEventDto MapRiskEvent(RiskEvent riskEvent)
|
|
{
|
|
return new RiskEventDto(
|
|
riskEvent.Id,
|
|
riskEvent.OrderId,
|
|
riskEvent.NodeId,
|
|
riskEvent.OrderCode,
|
|
riskEvent.NodeName,
|
|
riskEvent.RiskLevel,
|
|
riskEvent.Message,
|
|
riskEvent.OccurredAt.ToLocalTime().ToString("HH:mm:ss"),
|
|
riskEvent.HandlingStatus);
|
|
}
|
|
|
|
public static OrderSummaryDto MapOrder(
|
|
ProductionOrder order,
|
|
ProductionNodeDto root,
|
|
IReadOnlyList<TrendPointDto> trend,
|
|
IReadOnlyList<RiskEventDto> events)
|
|
{
|
|
return new OrderSummaryDto(
|
|
order.Id,
|
|
order.Code,
|
|
order.ProductName,
|
|
order.BatchQty,
|
|
order.RequiredQty,
|
|
order.CompletedQty,
|
|
order.Progress,
|
|
order.Status,
|
|
order.RiskLevel,
|
|
order.DelayDays,
|
|
FormatDate(order.PlannedStart),
|
|
FormatDate(order.PlannedEnd),
|
|
order.LineName,
|
|
order.Owner,
|
|
Math.Round(order.PlanAchievement, 1),
|
|
Math.Round(order.DailyDelta, 1),
|
|
order.ThumbnailKey,
|
|
root,
|
|
trend,
|
|
events);
|
|
}
|
|
|
|
private static ProductionNodeDto MapNode(
|
|
ProductionNode node,
|
|
IReadOnlyDictionary<Guid, List<ProductionNode>> childrenByParent,
|
|
HashSet<Guid> ancestors)
|
|
{
|
|
if (!ancestors.Add(node.Id))
|
|
{
|
|
throw new InvalidOperationException($"Production node cycle detected at {node.Id}.");
|
|
}
|
|
|
|
var children = childrenByParent.TryGetValue(node.Id, out var childNodes)
|
|
? childNodes.Select(child => MapNode(child, childrenByParent, new HashSet<Guid>(ancestors))).ToList()
|
|
: [];
|
|
|
|
return new ProductionNodeDto(
|
|
node.Id,
|
|
node.OrderId,
|
|
node.ParentNodeId,
|
|
node.Code,
|
|
node.OperationCode,
|
|
node.MaterialCode,
|
|
node.Name,
|
|
node.SourceSequence,
|
|
node.Specification,
|
|
node.Material,
|
|
node.UnitWeight,
|
|
node.TotalWeight,
|
|
node.UnitWeightText,
|
|
node.TotalWeightText,
|
|
node.BomQuantity,
|
|
node.BomQuantityText,
|
|
node.Remark,
|
|
node.SourceSheet,
|
|
node.SourceRow,
|
|
node.Level,
|
|
node.NodeType,
|
|
node.SupplyType,
|
|
node.RequiredQty,
|
|
node.CompletedQty,
|
|
node.DefectQty,
|
|
node.Progress,
|
|
node.Status,
|
|
node.RiskLevel,
|
|
node.DelayDays,
|
|
node.DelayReason,
|
|
FormatDate(node.PlannedStart),
|
|
FormatDate(node.PlannedEnd),
|
|
FormatDate(node.ActualStart),
|
|
FormatDate(node.ActualEnd),
|
|
node.Owner,
|
|
node.Vendor,
|
|
node.StationName,
|
|
node.VisualKey,
|
|
Convert.ToBase64String(node.RowVersion),
|
|
children);
|
|
}
|
|
|
|
private static string FormatDate(DateTime? value)
|
|
{
|
|
return value.HasValue ? value.Value.ToString("yyyy-MM-dd") : string.Empty;
|
|
}
|
|
}
|