using Admin.NET.Plugin.AiDOP.Entity.S8.OrderFlow; namespace Admin.NET.Plugin.AiDOP.SeedData; /// /// S8-ORDER-CHAIN-BODY-PRODUCTION-ORDER-LEVEL-SEED-FIX-1:本体生产「工序明细」/「损失因素」/「操作员表现」三表 seed。 /// /// ────────────────────────────────────────────────────────────────────── /// S8-...-FIX-1S(阶段 2 失败修正):本项目实际 SEED 数据落地路径是 UpdateScripts SQL /// 的 DELETE+INSERT(与 procurement 1.0.148.sql 模式一致),不是 SqlSugar IncreSeed /// 自动写入。阶段 2 实测启动后三表创建成功但 IncreSeed 0 行写入。 /// 处理:移除 IncreSeed attribute,保留本算法 SOT 作为 SQL 生成依据与未来 IMPORT/AGG /// 接入的算法 owner;本批次 221 行 SEED 数据由 WIP-S8-BODY-PRODUCTION-ORDER-LEVEL-SEED.sql /// 的 INSERT 语句落地。 /// ────────────────────────────────────────────────────────────────────── /// /// baseline 行(order_id=NULL)来自 MANUFACTURING_DETAIL_FIXTURE 逐字段对齐: /// - process:(P10/P20/P30/P40/TOTAL) × (pi, actual, plan_qty, rate, status) /// - loss factor:(MATERIAL/EQUIPMENT/QUALITY/EFFICIENCY/SUBTOTAL) × (count, ratio_pct, loss_hours) /// - operator:(张/李/王) × (avg_hours, status) /// /// 订单级行(order_id≠NULL,scenario=ORDER_LEVEL): /// 对每个 BODY_PRODUCTION stage.status != "pending" 的订单(16 单),按 A=stage.actualDays 与 baseline 比例派生: /// ratio = A / 6.8 /// process 行: /// baseline_actuals = [2.4, 0.8, 3.0, 0.6] /// actual[0..2] = round(baseline_actuals[i] * A / 6.8, 3) /// actual[3] = A - sum(actual[0..2]) // 守恒尾差修正 /// plan_qty[i] = round(baseline_plan[i] * ratio) /// achievement_rate[i] = baseline_rate[i] (per-process 固定属性) /// achievement_status[i] = ≥0.95 green / ≥0.80 yellow / 否则 red (由 rate 派生) /// cycle_status[i] = ≤pi green / ≤pi*1.2 yellow / 否则 red (由 actual vs pi 派生) /// TOTAL 行:pi=6, actual=A, plan_qty=NULL, /// rate=0.9000 / status="yellow" 锚定 fixture (S8-...-FIX-1R), /// 不再做 plan_qty 加权计算,与 baseline TOTAL 一致; /// cycle_status 仍由 actual vs 6 派生。SEED 过渡口径,IMPORT/AGG 接入后替换。 /// loss factor 行: /// count[i] = round(baseline_count[i] * ratio) /// ratio_pct[i] = baseline_ratio_pct[i] // 结构保留 /// loss_hours[i] = round(baseline_loss_hours[i] * ratio, 2) /// SUBTOTAL: count=round(30*ratio), ratio_pct=100, loss_hours=round(7.5*ratio,2) // fixture-pinned 比例缩放 /// operator 行: /// avg_hours[i] = round(baseline_hours[i] * ratio, 2) /// status[i] = baseline_status[i] // 每操作员固定 status /// /// 真实数据源(IMPORT / AGG)接入后,service 优先采用 IMPORT/AGG,同 (order_code, process/factor/operator) SEED 行仅兜底。 /// // S8-...-FIX-1S:[IncreSeed] 已移除(项目实际不通过 SqlSugar IncreSeed 自动写入)。 // 数据落地见 UpdateScripts/WIP-S8-BODY-PRODUCTION-ORDER-LEVEL-SEED.sql 的 INSERT。 public class S8OrderFlowManufacturingProcessSeedData : ISqlSugarEntitySeedData { public IEnumerable HasData() => S8OrderFlowManufacturingDataset.BuildProcessBaselineRows() .Concat(S8OrderFlowManufacturingDataset.BuildProcessOrderLevelRows()); } // S8-...-FIX-1S:[IncreSeed] 已移除(项目实际不通过 SqlSugar IncreSeed 自动写入)。 // 数据落地见 UpdateScripts/WIP-S8-BODY-PRODUCTION-ORDER-LEVEL-SEED.sql 的 INSERT。 public class S8OrderFlowManufacturingLossFactorSeedData : ISqlSugarEntitySeedData { public IEnumerable HasData() => S8OrderFlowManufacturingDataset.BuildLossFactorBaselineRows() .Concat(S8OrderFlowManufacturingDataset.BuildLossFactorOrderLevelRows()); } // S8-...-FIX-1S:[IncreSeed] 已移除(项目实际不通过 SqlSugar IncreSeed 自动写入)。 // 数据落地见 UpdateScripts/WIP-S8-BODY-PRODUCTION-ORDER-LEVEL-SEED.sql 的 INSERT。 public class S8OrderFlowManufacturingOperatorSeedData : ISqlSugarEntitySeedData { public IEnumerable HasData() => S8OrderFlowManufacturingDataset.BuildOperatorBaselineRows() .Concat(S8OrderFlowManufacturingDataset.BuildOperatorOrderLevelRows()); } internal static class S8OrderFlowManufacturingDataset { // ─────────────── ID base ranges(与 procurement-pivot 同号段策略) ─────────────── internal const long ProcessBaselineIdBase = 1329909160000L; internal const long ProcessOrderLevelIdBase = 1329909170000L; internal const long LossFactorBaselineIdBase = 1329909180000L; internal const long LossFactorOrderLevelIdBase = 1329909190000L; internal const long OperatorBaselineIdBase = 1329909200000L; internal const long OperatorOrderLevelIdBase = 1329909210000L; internal const string ScenarioBaseline = "BASELINE_PPT"; internal const string ScenarioOrderLevel = "ORDER_LEVEL"; // BODY_PRODUCTION 在 5 阶段流水中固定为索引 3(order_review→product_design→material_procurement→body_production→final_assembly_shipping)。 private const int BodyProductionStageIndex = 3; // ─────────────── baseline 真值(与 MANUFACTURING_DETAIL_FIXTURE 逐字段对齐) ─────────────── private sealed record ProcessBaseline( int SortNo, string Code, string Name, decimal Pi, decimal Actual, int? PlanQty, decimal? Rate, string AchStatus); /// fixture 工序基线:(sort, code, name, pi, actual, plan, rate, ach_status)。 private static readonly ProcessBaseline[] ProcessBaselines = { new(1, "P10", "10工序", 1.0m, 2.4m, 45, 0.5500m, "red"), new(2, "P20", "20工序", 1.0m, 0.8m, 3, 0.9700m, "yellow"), new(3, "P30", "30工序", 2.5m, 3.0m, 15, 0.8500m, "green"), new(4, "P40", "40工序", 1.5m, 0.6m, 2, 0.9800m, "red"), // TOTAL:pi=6, actual=6.8, plan_qty=NULL, rate=0.90 (fixture-pinned), achievement_status=yellow(来自 fixture 表格颜色)。 new(5, "TOTAL", "合计", 6.0m, 6.8m, null, 0.9000m, "yellow"), }; private sealed record LossFactorBaseline( int SortNo, string Code, string Name, int? Count, decimal? RatioPct, decimal? LossHours); /// fixture 损失因素基线:(sort, code, name, count, ratio_pct, loss_hours)。 /// SUBTOTAL loss_hours=7.50 来自 fixture,不等于 4 因素之和 26.15;保留 fixture 锚定。 private static readonly LossFactorBaseline[] LossFactorBaselines = { new(1, "MATERIAL", "材料影响", 9, 30.00m, 9.00m), new(2, "EQUIPMENT", "设备影响", 6, 20.00m, 4.50m), new(3, "QUALITY", "质量影响", 3, 10.00m, 3.40m), new(4, "EFFICIENCY", "作业效率损失", 12, 40.00m, 9.25m), new(5, "SUBTOTAL", "损失小计", 30, 100.00m, 7.50m), }; private sealed record OperatorBaseline(int SortNo, string Code, string Name, decimal Hours, string Status); /// fixture 操作员基线:(sort, code, name, avg_hours, status)。 private static readonly OperatorBaseline[] OperatorBaselines = { new(1, "OP_ZHANG", "张师傅", 20.00m, "red"), new(2, "OP_LI", "李师傅", 10.00m, "yellow"), new(3, "OP_WANG", "王师傅", 8.00m, "green"), }; /// baseline TOTAL.actual_days(6.8)作为订单级 ratio = A/6.8 的归一化分母。 private const decimal BaselineTotalActualDays = 6.8m; private const decimal BaselineTotalPiDays = 6.0m; // ──────────────────────────────────────────────────────────── // Process 行 // ──────────────────────────────────────────────────────────── public static IEnumerable BuildProcessBaselineRows() { long seq = 0; foreach (var b in ProcessBaselines) { yield return new AdoS8OrderFlowManufacturingProcess { Id = ProcessBaselineIdBase + (++seq), OrderId = null, OrderCode = null, ProcessCode = b.Code, ProcessName = b.Name, PiDays = b.Pi, ActualDays = b.Actual, CycleStatus = ClassifyCycleStatus(b.Actual, b.Pi), PlanQty = b.PlanQty, AchievementRate = b.Rate, AchievementStatus = b.AchStatus, SortNo = b.SortNo, ScenarioCode = ScenarioBaseline, DataSource = "SEED", TenantId = 1, FactoryId = 1, CreatedAt = S8OrderFlowDataset.CreatedAt, UpdatedAt = null, IsDeleted = false, }; } } public static IEnumerable BuildProcessOrderLevelRows() { long seq = 0; foreach (var spec in S8OrderFlowDataset.Specs) { var lifecycle = S8OrderFlowStageDataset.BuildLifecycleValues(spec); var stage = lifecycle[BodyProductionStageIndex]; if (stage.status == "pending") continue; var A = stage.actualDays; var ratio = A / BaselineTotalActualDays; var orderId = S8OrderFlowDataset.OrderIdBase + spec.Idx; // 4 个工序 actual:前 3 个直接缩放,第 4 个尾差修正以满足 sum == A。 var actuals = new decimal[4]; decimal sumFirstThree = 0m; for (var i = 0; i < 3; i++) { actuals[i] = decimal.Round(ProcessBaselines[i].Actual * ratio, 3); sumFirstThree += actuals[i]; } actuals[3] = decimal.Round(A - sumFirstThree, 3); // 4 工序 plan_qty(TOTAL 行不计入;取 baseline.PlanQty 非 null 即可,TOTAL 是第 5 行) var planQtys = new int[4]; for (var i = 0; i < 4; i++) { var basePlan = ProcessBaselines[i].PlanQty ?? 0; planQtys[i] = (int)decimal.Round(basePlan * ratio, MidpointRounding.AwayFromZero); } // 4 工序行 for (var i = 0; i < 4; i++) { var baseRow = ProcessBaselines[i]; var rate = baseRow.Rate!.Value; yield return new AdoS8OrderFlowManufacturingProcess { Id = ProcessOrderLevelIdBase + (++seq), OrderId = orderId, OrderCode = spec.OrderCode, ProcessCode = baseRow.Code, ProcessName = baseRow.Name, PiDays = baseRow.Pi, ActualDays = actuals[i], CycleStatus = ClassifyCycleStatus(actuals[i], baseRow.Pi), PlanQty = planQtys[i], AchievementRate = rate, AchievementStatus = ClassifyAchievementStatus(rate), SortNo = baseRow.SortNo, ScenarioCode = ScenarioOrderLevel, DataSource = "SEED", TenantId = 1, FactoryId = 1, CreatedAt = S8OrderFlowDataset.CreatedAt, UpdatedAt = null, IsDeleted = false, }; } // TOTAL 行:plan_qty=NULL。 // S8-...-FIX-1R:achievement_rate / achievement_status 锚定 fixture(ProcessBaselines[4], // 即 0.9000 / yellow),不再用 4 工序 plan_qty 加权计算,与 baseline TOTAL 保持一致。 // SEED 过渡口径;真实 IMPORT/AGG 接入后由真实生产达成数据替换。 var fixtureTotalRate = ProcessBaselines[4].Rate!.Value; // 0.9000m var fixtureTotalStatus = ProcessBaselines[4].AchStatus; // "yellow" yield return new AdoS8OrderFlowManufacturingProcess { Id = ProcessOrderLevelIdBase + (++seq), OrderId = orderId, OrderCode = spec.OrderCode, ProcessCode = ProcessBaselines[4].Code, ProcessName = ProcessBaselines[4].Name, PiDays = BaselineTotalPiDays, ActualDays = decimal.Round(A, 3), CycleStatus = ClassifyCycleStatus(A, BaselineTotalPiDays), PlanQty = null, AchievementRate = fixtureTotalRate, AchievementStatus = fixtureTotalStatus, SortNo = ProcessBaselines[4].SortNo, ScenarioCode = ScenarioOrderLevel, DataSource = "SEED", TenantId = 1, FactoryId = 1, CreatedAt = S8OrderFlowDataset.CreatedAt, UpdatedAt = null, IsDeleted = false, }; } } // ──────────────────────────────────────────────────────────── // Loss factor 行 // ──────────────────────────────────────────────────────────── public static IEnumerable BuildLossFactorBaselineRows() { long seq = 0; foreach (var b in LossFactorBaselines) { yield return new AdoS8OrderFlowManufacturingLossFactor { Id = LossFactorBaselineIdBase + (++seq), OrderId = null, OrderCode = null, FactorCode = b.Code, FactorName = b.Name, CountValue = b.Count, RatioPct = b.RatioPct, LossHours = b.LossHours, SortNo = b.SortNo, ScenarioCode = ScenarioBaseline, DataSource = "SEED", TenantId = 1, FactoryId = 1, CreatedAt = S8OrderFlowDataset.CreatedAt, UpdatedAt = null, IsDeleted = false, }; } } public static IEnumerable BuildLossFactorOrderLevelRows() { long seq = 0; foreach (var spec in S8OrderFlowDataset.Specs) { var lifecycle = S8OrderFlowStageDataset.BuildLifecycleValues(spec); var stage = lifecycle[BodyProductionStageIndex]; if (stage.status == "pending") continue; var A = stage.actualDays; var ratio = A / BaselineTotalActualDays; var orderId = S8OrderFlowDataset.OrderIdBase + spec.Idx; foreach (var b in LossFactorBaselines) { int? count = b.Count.HasValue ? (int)decimal.Round(b.Count.Value * ratio, MidpointRounding.AwayFromZero) : null; decimal? hours = b.LossHours.HasValue ? decimal.Round(b.LossHours.Value * ratio, 2) : null; yield return new AdoS8OrderFlowManufacturingLossFactor { Id = LossFactorOrderLevelIdBase + (++seq), OrderId = orderId, OrderCode = spec.OrderCode, FactorCode = b.Code, FactorName = b.Name, CountValue = count, RatioPct = b.RatioPct, // 结构保留:baseline ratio_pct 原值复制 LossHours = hours, SortNo = b.SortNo, ScenarioCode = ScenarioOrderLevel, DataSource = "SEED", TenantId = 1, FactoryId = 1, CreatedAt = S8OrderFlowDataset.CreatedAt, UpdatedAt = null, IsDeleted = false, }; } } } // ──────────────────────────────────────────────────────────── // Operator 行 // ──────────────────────────────────────────────────────────── public static IEnumerable BuildOperatorBaselineRows() { long seq = 0; foreach (var b in OperatorBaselines) { yield return new AdoS8OrderFlowManufacturingOperator { Id = OperatorBaselineIdBase + (++seq), OrderId = null, OrderCode = null, OperatorCode = b.Code, OperatorName = b.Name, AvgHours = b.Hours, Status = b.Status, SortNo = b.SortNo, ScenarioCode = ScenarioBaseline, DataSource = "SEED", TenantId = 1, FactoryId = 1, CreatedAt = S8OrderFlowDataset.CreatedAt, UpdatedAt = null, IsDeleted = false, }; } } public static IEnumerable BuildOperatorOrderLevelRows() { long seq = 0; foreach (var spec in S8OrderFlowDataset.Specs) { var lifecycle = S8OrderFlowStageDataset.BuildLifecycleValues(spec); var stage = lifecycle[BodyProductionStageIndex]; if (stage.status == "pending") continue; var A = stage.actualDays; var ratio = A / BaselineTotalActualDays; var orderId = S8OrderFlowDataset.OrderIdBase + spec.Idx; foreach (var b in OperatorBaselines) { var hours = decimal.Round(b.Hours * ratio, 2); yield return new AdoS8OrderFlowManufacturingOperator { Id = OperatorOrderLevelIdBase + (++seq), OrderId = orderId, OrderCode = spec.OrderCode, OperatorCode = b.Code, OperatorName = b.Name, AvgHours = hours, Status = b.Status, // 结构保留:每操作员固定 status SortNo = b.SortNo, ScenarioCode = ScenarioOrderLevel, DataSource = "SEED", TenantId = 1, FactoryId = 1, CreatedAt = S8OrderFlowDataset.CreatedAt, UpdatedAt = null, IsDeleted = false, }; } } } // ──────────────────────────────────────────────────────────── // status classifiers(seed-time 一次性,运行期不再重判) // ──────────────────────────────────────────────────────────── /// cycle_status:actual ≤ pi → green;≤ pi*1.2 → yellow;否则 red。 private static string ClassifyCycleStatus(decimal actual, decimal pi) { if (actual <= pi) return "green"; if (actual <= pi * 1.2m) return "yellow"; return "red"; } /// achievement_status:rate ≥ 0.95 green / ≥ 0.80 yellow / 否则 red。 private static string ClassifyAchievementStatus(decimal rate) { if (rate >= 0.95m) return "green"; if (rate >= 0.80m) return "yellow"; return "red"; } }