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";
}
}