瀏覽代碼

feat(s8): t2a seed order flow orders and stages

YY968XX 1 月之前
父節點
當前提交
7e42ba5a27

+ 134 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/S8OrderFlowOrderSeedData.cs

@@ -0,0 +1,134 @@
+using Admin.NET.Plugin.AiDOP.Entity.S8.OrderFlow;
+
+namespace Admin.NET.Plugin.AiDOP.SeedData;
+
+/// <summary>
+/// ORDER-FLOW-S8-INTEGRATED-DOMAIN-RESET-1 t2a:S8 订单执行链路主档种子(20 单)。
+/// SO-2026-001 = PPT 第 1 页真值;其余 19 单沿用旧 demo seed 的业务维度与派生规则,
+/// 但 ORDER_FLOW 编码统一从 lower 升为 UPPER node_code。
+/// 新表 ado_s8_order_flow_order 不保留 exception_count 预存字段,
+/// 异常数后续由 ado_s8_exception 实时聚合。
+/// </summary>
+[IncreSeed]
+public class S8OrderFlowOrderSeedData : ISqlSugarEntitySeedData<AdoS8OrderFlowOrder>
+{
+    public IEnumerable<AdoS8OrderFlowOrder> HasData() => S8OrderFlowDataset.BuildOrders();
+}
+
+internal static class S8OrderFlowDataset
+{
+    /// <summary>沿用旧 demo seed 的 release_at 基准。</summary>
+    internal static readonly DateTime ReleaseBase = new(2026, 4, 15, 9, 0, 0);
+    internal static readonly DateTime CreatedAt = DateTime.Parse("2026-05-12 00:00:00");
+
+    /// <summary>OrderId 基址(与旧 demo OrderIdBase 不同号段,避免冲突)。</summary>
+    internal const long OrderIdBase = 1329909100000L;
+    internal const long StageIdBase = 1329909110000L;
+
+    /// <summary>lower → UPPER 映射(旧 demo seed 编码 → ORDER_FLOW dimension node_code)。</summary>
+    internal static string ToUpperFlow(string lowerKey) => lowerKey switch
+    {
+        "order_review"            => "ORDER_REVIEW_PLAN_CALC",
+        "product_design"          => "PRODUCT_DESIGN",
+        "material_procurement"    => "MATERIAL_PURCHASE",
+        "body_production"         => "BODY_PRODUCTION",
+        "final_assembly_shipping" => "FINAL_ASSEMBLY_DELIVERY",
+        _ => throw new ArgumentException($"未知 lower flow key: {lowerKey}")
+    };
+
+    /// <summary>UPPER → 展示中文名。</summary>
+    internal static string UpperFlowName(string upperCode) => upperCode switch
+    {
+        "ORDER_REVIEW_PLAN_CALC"  => "评审/排产/测算",
+        "PRODUCT_DESIGN"          => "产品设计",
+        "MATERIAL_PURCHASE"       => "材料采购",
+        "BODY_PRODUCTION"         => "本体生产",
+        "FINAL_ASSEMBLY_DELIVERY" => "总装发货",
+        _ => upperCode
+    };
+
+    /// <summary>UPPER 顺序:5 阶段在 lifecycle / stage seed 中按此 sort_no 1~5。</summary>
+    internal static readonly (string Upper, string Name, decimal Kpi)[] FlowDef =
+    {
+        ("ORDER_REVIEW_PLAN_CALC",  "评审/排产/测算", 5m),
+        ("PRODUCT_DESIGN",          "产品设计",       3m),
+        ("MATERIAL_PURCHASE",       "材料采购",       14m),
+        ("BODY_PRODUCTION",         "本体生产",       6m),
+        ("FINAL_ASSEMBLY_DELIVERY", "总装发货",       3m),
+    };
+
+    internal record OrderSpec(
+        int Idx, string OrderCode, string ProductName,
+        string CustomerName, string CustomerCode, string CustomerType,
+        string ProductLine, string Region, string Priority,
+        string WorkflowStatus, string CurrentNodeLower, string FocusNodeLower,
+        int? ResponseMinutes, int? ProcessingMinutes, int? TotalLossMinutes);
+
+    /// <summary>20 单 specs。与旧 S8DemoOrderFlowSeedData.Specs 业务维度一一对齐(除 ORDER_FLOW 编码升级 + 去掉 ExceptionCount)。</summary>
+    internal static readonly OrderSpec[] Specs =
+    {
+        new(1,  "SO-2026-001", "高压接线盒",   "电气设备厂A", "C001", "KA",    "A产品线", "华北", "P2", "completed",   "final_assembly_shipping", "final_assembly_shipping", 2880, 1440, 4320),
+        new(2,  "SO-2026-002", "转向机总成",   "电气设备厂B", "C002", "KA",    "B产品线", "华东", "P3", "completed",   "final_assembly_shipping", "material_procurement",     110,  850,  960),
+        new(3,  "SO-2026-003", "电池箱横梁",   "电气设备厂C", "C003", "KA",    "C产品线", "华南", "P2", "completed",   "final_assembly_shipping", "body_production",           80,  720,  800),
+        new(4,  "SO-2026-004", "高压接线盒",   "电气设备厂D", "C004", "SMB",   "A产品线", "华北", "P3", "completed",   "final_assembly_shipping", "product_design",            95,  680,  775),
+        new(5,  "SO-2026-005", "电池箱横梁",   "电气设备厂E", "C005", "MICRO", "C产品线", "华南", "P2", "completed",   "final_assembly_shipping", "product_design",            70,  610,  680),
+        new(6,  "SO-2026-006", "驱动总成支架", "电气设备厂A", "C001", "KA",    "A产品线", "华北", "P2", "completed",   "final_assembly_shipping", "material_procurement",     130, 1240, 2350),
+        new(7,  "SO-2026-007", "减速器壳体",   "电气设备厂B", "C002", "KA",    "B产品线", "华东", "P2", "completed",   "final_assembly_shipping", "body_production",          145, 1080, 2210),
+        new(8,  "SO-2026-008", "项目定制骨架", "电气设备厂C", "C003", "KA",    "C产品线", "华南", "P1", "completed",   "final_assembly_shipping", "body_production",          165, 1320, 2640),
+        new(9,  "SO-2026-009", "控制器外壳",   "电气设备厂D", "C004", "SMB",   "A产品线", "华北", "P2", "completed",   "final_assembly_shipping", "order_review",              60,  540,  600),
+        new(10, "SO-2026-010", "试制急单件",   "电气设备厂E", "C005", "MICRO", "C产品线", "华南", "P2", "completed",   "final_assembly_shipping", "product_design",            88,  730,  818),
+        new(11, "SO-2026-011", "冷却模块框架", "电气设备厂A", "C001", "KA",    "B产品线", "华东", "P2", "completed",   "final_assembly_shipping", "final_assembly_shipping",   50,  410,  460),
+        new(12, "SO-2026-012", "动力舱隔板",   "电气设备厂B", "C002", "KA",    "B产品线", "华东", "P2", "completed",   "final_assembly_shipping", "material_procurement",     100,  920, 1020),
+        new(13, "SO-2026-013", "模组支架",     "电气设备厂C", "C003", "KA",    "C产品线", "华南", "P2", "completed",   "final_assembly_shipping", "body_production",           92,  860,  952),
+        new(14, "SO-2026-014", "一体化支撑件", "电气设备厂D", "C004", "SMB",   "A产品线", "华北", "P3", "completed",   "final_assembly_shipping", "material_procurement",      75,  700,  775),
+        new(15, "SO-2026-015", "项目定制骨架", "电气设备厂E", "C005", "MICRO", "C产品线", "华南", "P2", "completed",   "final_assembly_shipping", "order_review",              65,  580,  645),
+        new(16, "SO-2026-016", "控制器外壳",   "电气设备厂A", "C001", "KA",    "A产品线", "华北", "P2", "in_progress", "order_review",            "order_review",              90, null, null),
+        new(17, "SO-2026-017", "转向机总成",   "电气设备厂B", "C002", "KA",    "B产品线", "华东", "P2", "in_progress", "product_design",          "product_design",           105, null, null),
+        new(18, "SO-2026-018", "项目定制骨架", "电气设备厂C", "C003", "KA",    "C产品线", "华南", "P2", "in_progress", "material_procurement",    "material_procurement",     120, null, null),
+        new(19, "SO-2026-019", "驱动总成支架", "电气设备厂D", "C004", "SMB",   "A产品线", "华北", "P1", "in_progress", "body_production",         "body_production",          140, null, null),
+        new(20, "SO-2026-020", "试制急单件",   "电气设备厂E", "C005", "MICRO", "C产品线", "华南", "P1", "in_progress", "final_assembly_shipping", "final_assembly_shipping",   55, null, null),
+    };
+
+    public static IEnumerable<AdoS8OrderFlowOrder> BuildOrders()
+    {
+        var targetCycle = FlowDef.Sum(s => s.Kpi);
+        foreach (var spec in Specs)
+        {
+            var lifecycle = S8OrderFlowStageDataset.BuildLifecycleValues(spec);
+            decimal? actualCycleDays = null;
+            if (spec.WorkflowStatus == "completed" && lifecycle[^1].actualEnd.HasValue)
+            {
+                var days = decimal.Round((decimal)(lifecycle[^1].actualEnd!.Value - ReleaseBase).TotalDays, 1);
+                actualCycleDays = days;
+            }
+
+            yield return new AdoS8OrderFlowOrder
+            {
+                Id = OrderIdBase + spec.Idx,
+                TenantId = 1,
+                FactoryId = 1,
+                OrderCode = spec.OrderCode,
+                ProductName = spec.ProductName,
+                ProductLine = spec.ProductLine,
+                CustomerCode = spec.CustomerCode,
+                CustomerName = spec.CustomerName,
+                CustomerType = spec.CustomerType,
+                Region = spec.Region,
+                Priority = spec.Priority,
+                WorkflowStatus = spec.WorkflowStatus,
+                CurrentOrderFlowCode = ToUpperFlow(spec.CurrentNodeLower),
+                ReleaseAt = ReleaseBase,
+                TargetCycleDays = targetCycle,
+                ActualCycleDays = actualCycleDays,
+                ResponseMinutes = spec.ResponseMinutes,
+                ProcessingMinutes = spec.ProcessingMinutes,
+                TotalLossMinutes = spec.TotalLossMinutes,
+                ScenarioCode = spec.OrderCode == "SO-2026-001" ? "PPT" : "DEMO",
+                DataSource = "SEED",
+                CreatedAt = CreatedAt,
+                UpdatedAt = null,
+                IsDeleted = false,
+            };
+        }
+    }
+}

+ 180 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/S8OrderFlowStageSeedData.cs

@@ -0,0 +1,180 @@
+using Admin.NET.Plugin.AiDOP.Entity.S8.OrderFlow;
+
+namespace Admin.NET.Plugin.AiDOP.SeedData;
+
+/// <summary>
+/// ORDER-FLOW-S8-INTEGRATED-DOMAIN-RESET-1 t2a:S8 订单执行链路五阶段执行明细种子。
+/// 20 单 × 5 阶段 = 100 行;order_flow_code 全部 UPPER;
+/// SO-2026-001 lifecycle 严格按 PPT 第 1 页;其余 19 单按 FocusNodeLower 模板派生
+/// (与旧 S8DemoOrderFlowSeedData.BuildLifecycleValues 同算法)。
+/// </summary>
+[IncreSeed]
+public class S8OrderFlowStageSeedData : ISqlSugarEntitySeedData<AdoS8OrderFlowStage>
+{
+    public IEnumerable<AdoS8OrderFlowStage> HasData() => S8OrderFlowStageDataset.BuildStages();
+}
+
+internal static class S8OrderFlowStageDataset
+{
+    /// <summary>BuildLifecycleValues 输出的 5 阶段行(lower key 索引,保留以匹配 spec.CurrentNodeLower / FocusNodeLower)。</summary>
+    internal record StageRow(
+        string lowerKey, string upperCode, string upperName, decimal kpi, decimal actualDays,
+        DateTime expected, DateTime? actualStart, DateTime? actualEnd,
+        string status, decimal? nodeVar, decimal? cumVar);
+
+    public static IEnumerable<AdoS8OrderFlowStage> BuildStages()
+    {
+        long stageSeq = 0;
+        foreach (var spec in S8OrderFlowDataset.Specs)
+        {
+            var orderId = S8OrderFlowDataset.OrderIdBase + spec.Idx;
+            var orderCode = spec.OrderCode;
+            var scenario = orderCode == "SO-2026-001" ? "PPT" : "DEMO";
+            var lifecycle = BuildLifecycleValues(spec);
+
+            for (var i = 0; i < lifecycle.Length; i++)
+            {
+                var s = lifecycle[i];
+                yield return new AdoS8OrderFlowStage
+                {
+                    Id = S8OrderFlowDataset.StageIdBase + (++stageSeq),
+                    OrderId = orderId,
+                    OrderCode = orderCode,
+                    OrderFlowCode = s.upperCode,
+                    OrderFlowName = s.upperName,
+                    OwnerDept = null,
+                    SortNo = i + 1,
+                    PlannedDays = s.kpi,
+                    ActualDays = s.status == "pending" ? null : s.actualDays,
+                    TargetAt = s.expected,
+                    ActualStartAt = s.actualStart,
+                    ActualEndAt = s.actualEnd,
+                    Status = s.status,
+                    NodeVarianceDays = s.nodeVar,
+                    CumulativeVarianceDays = s.cumVar,
+                    ScenarioCode = scenario,
+                    DataSource = "SEED",
+                    TenantId = 1,
+                    FactoryId = 1,
+                    CreatedAt = S8OrderFlowDataset.CreatedAt,
+                    UpdatedAt = null,
+                    IsDeleted = false,
+                };
+            }
+        }
+    }
+
+    /// <summary>
+    /// SO-2026-001 走 PPT 第 1 页真值(与旧 demo BuildSo001Lifecycle 等价);
+    /// 其余 19 单按 FocusNodeLower 模板派生(与旧 demo 算法一致),最终 lower → UPPER 一次性映射。
+    /// </summary>
+    internal static StageRow[] BuildLifecycleValues(S8OrderFlowDataset.OrderSpec spec)
+    {
+        if (spec.OrderCode == "SO-2026-001") return BuildSo001Lifecycle();
+
+        var release = S8OrderFlowDataset.ReleaseBase;
+        var (focusNodeVar, focusActual) = spec.FocusNodeLower switch
+        {
+            "order_review"            => (+2m, 7m),
+            "product_design"          => (+2m, 5m),
+            "material_procurement"    => (+2m, 16m),
+            "body_production"         => (+2m, 8m),
+            "final_assembly_shipping" => (+1m, 4m),
+            _ => (0m, 0m),
+        };
+        var focusStatus = spec.FocusNodeLower switch
+        {
+            "order_review"            => "red",
+            "product_design"          => "red",
+            "material_procurement"    => "yellow",
+            "body_production"         => "yellow",
+            "final_assembly_shipping" => "yellow",
+            _ => "green",
+        };
+
+        // lower → UPPER 顺序对照(与 FlowDef 同序)。
+        var flowOrder = new[]
+        {
+            ("order_review",            "ORDER_REVIEW_PLAN_CALC",  "评审/排产/测算", 5m),
+            ("product_design",          "PRODUCT_DESIGN",          "产品设计",       3m),
+            ("material_procurement",    "MATERIAL_PURCHASE",       "材料采购",       14m),
+            ("body_production",         "BODY_PRODUCTION",         "本体生产",       6m),
+            ("final_assembly_shipping", "FINAL_ASSEMBLY_DELIVERY", "总装发货",       3m),
+        };
+
+        var rows = new StageRow[5];
+        var cumVar = 0m;
+        var cursor = release;
+        int currentNodeIdx = Array.FindIndex(flowOrder, s => s.Item1 == spec.CurrentNodeLower);
+
+        for (var i = 0; i < 5; i++)
+        {
+            var (lower, upper, name, kpi) = flowOrder[i];
+            decimal actualDays = kpi;
+            decimal nodeVar = 0;
+            string status = "green";
+            if (lower == spec.FocusNodeLower)
+            {
+                actualDays = focusActual;
+                nodeVar = focusNodeVar;
+                status = focusStatus;
+            }
+            cumVar += nodeVar;
+
+            DateTime? actualStart;
+            DateTime? actualEnd;
+
+            if (spec.WorkflowStatus == "completed" || i < currentNodeIdx)
+            {
+                actualStart = cursor;
+                actualEnd = cursor.AddDays((double)actualDays);
+                cursor = actualEnd.Value;
+            }
+            else if (i == currentNodeIdx)
+            {
+                actualStart = cursor;
+                actualEnd = null;
+                actualDays = 0;
+                status = "pending";
+                nodeVar = 0;
+                cumVar = (i == 0) ? 0m : rows[i - 1].cumVar ?? 0m;
+            }
+            else
+            {
+                actualStart = null;
+                actualEnd = null;
+                actualDays = 0;
+                status = "pending";
+                nodeVar = 0;
+            }
+
+            DateTime expected = release.AddDays((double)flowOrder.Take(i + 1).Sum(s => s.Item4));
+
+            rows[i] = new StageRow(
+                lower, upper, name, kpi,
+                actualDays,
+                expected,
+                actualStart,
+                actualEnd,
+                status,
+                status == "pending" ? null : nodeVar,
+                status == "pending" ? null : cumVar);
+        }
+        return rows;
+    }
+
+    /// <summary>SO-2026-001 PPT 第 1 页 hard-coded 真值(与旧 demo BuildSo001Lifecycle 完全一致,lower → UPPER 映射)。</summary>
+    private static StageRow[] BuildSo001Lifecycle()
+    {
+        DateTime D(int m, int d, int h, int mi) => new(2026, m, d, h, mi, 0);
+
+        return new[]
+        {
+            new StageRow("order_review",            "ORDER_REVIEW_PLAN_CALC",  "评审/排产/测算", 5m,  5m,  new DateTime(2026, 4, 20), D(4, 15, 9, 0),  D(4, 20, 15, 0), "green",  0m,  0m),
+            new StageRow("product_design",          "PRODUCT_DESIGN",          "产品设计",       3m,  5m,  new DateTime(2026, 4, 23), D(4, 20, 15, 0), D(4, 25, 10, 0), "red",   +2m, +2m),
+            new StageRow("material_procurement",    "MATERIAL_PURCHASE",       "材料采购",       14m, 13m, new DateTime(2026, 5, 6),  D(4, 25, 10, 0), D(5, 7,  14, 0), "yellow",-1m, +1m),
+            new StageRow("body_production",         "BODY_PRODUCTION",         "本体生产",       6m,  5m,  new DateTime(2026, 5, 12), D(5, 7,  14, 0), D(5, 12, 18, 0), "green", -1m,  0m),
+            new StageRow("final_assembly_shipping", "FINAL_ASSEMBLY_DELIVERY", "总装发货",       3m,  3m,  new DateTime(2026, 5, 15), D(5, 12, 18, 0), D(5, 15, 10, 0), "green",  0m,  0m),
+        };
+    }
+}