|
|
@@ -448,12 +448,94 @@ public class AdoSmartOpsKpiMasterController : ControllerBase
|
|
|
await InsertLevel(l2, "ado_s9_kpi_value_l2_day");
|
|
|
await InsertLevel(l3, "ado_s9_kpi_value_l3_day");
|
|
|
|
|
|
+ // ---- 同步 LayoutItem / HomeModule(只补不改,保护已有布局配置) ----
|
|
|
+ // 背景:
|
|
|
+ // seed_layout_items_all_modules.py 的语义是"(tenant,module) 已有布局就 skip";
|
|
|
+ // 之后在 KpiMaster 里新增某个 L1/L2 指标时,LayoutItem 不会自动跟进,九宫格 / 详情会少条目。
|
|
|
+ // 这里按本次 Demo 导入涉及的指标做"缺啥补啥"的幂等同步,不覆盖任何已有行。
|
|
|
+ const long layoutFactoryId = 1;
|
|
|
+ var existingLayout = await _db.Queryable<AdoSmartOpsLayoutItem>()
|
|
|
+ .Where(x => x.TenantId == tenantId && x.FactoryId == layoutFactoryId)
|
|
|
+ .ToListAsync();
|
|
|
+ var layoutKey = new HashSet<string>(
|
|
|
+ existingLayout.Select(x => $"{x.ModuleCode}|{x.MetricCode}|{x.MetricLevel}"),
|
|
|
+ StringComparer.OrdinalIgnoreCase);
|
|
|
+
|
|
|
+ var now = DateTime.Now;
|
|
|
+ var toInsertLayout = new List<AdoSmartOpsLayoutItem>();
|
|
|
+ foreach (var p in parsed)
|
|
|
+ {
|
|
|
+ var m = p.Master;
|
|
|
+ var key = $"{m.ModuleCode}|{m.MetricCode}|{m.MetricLevel}";
|
|
|
+ if (layoutKey.Contains(key)) continue;
|
|
|
+ string? zone = null;
|
|
|
+ if (m.MetricLevel >= 2)
|
|
|
+ {
|
|
|
+ var nm = m.MetricName ?? "";
|
|
|
+ if (nm.Contains("周期") || nm.Contains("人效")) zone = "left";
|
|
|
+ else if (nm.Contains("满足率") || nm.Contains("周转")) zone = "right";
|
|
|
+ else zone = (m.SortNo % 2 == 1) ? "left" : "right";
|
|
|
+ }
|
|
|
+ toInsertLayout.Add(new AdoSmartOpsLayoutItem
|
|
|
+ {
|
|
|
+ TenantId = tenantId,
|
|
|
+ FactoryId = layoutFactoryId,
|
|
|
+ ModuleCode = m.ModuleCode,
|
|
|
+ RowId = $"{m.ModuleCode}-L{m.MetricLevel}-{m.MetricCode}",
|
|
|
+ MetricLevel = m.MetricLevel,
|
|
|
+ MetricCode = m.MetricCode,
|
|
|
+ DisplayName = null,
|
|
|
+ SortNo = m.SortNo,
|
|
|
+ ParentRowId = null,
|
|
|
+ FormulaText = null,
|
|
|
+ PanelZone = zone,
|
|
|
+ IsEnabled = 1,
|
|
|
+ UpdateTime = now,
|
|
|
+ });
|
|
|
+ layoutKey.Add(key);
|
|
|
+ }
|
|
|
+ if (toInsertLayout.Count > 0)
|
|
|
+ await _db.Insertable(toInsertLayout).ExecuteCommandAsync();
|
|
|
+
|
|
|
+ var moduleCodes = parsed.Select(p => p.Master.ModuleCode).Distinct().ToList();
|
|
|
+ var existingHm = (await _db.Queryable<AdoSmartOpsHomeModule>()
|
|
|
+ .Where(x => x.TenantId == tenantId && x.FactoryId == layoutFactoryId)
|
|
|
+ .ToListAsync())
|
|
|
+ .Select(x => x.ModuleCode)
|
|
|
+ .ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
|
+ var toInsertHm = new List<AdoSmartOpsHomeModule>();
|
|
|
+ foreach (var mc in moduleCodes)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(mc) || existingHm.Contains(mc)) continue;
|
|
|
+ toInsertHm.Add(new AdoSmartOpsHomeModule
|
|
|
+ {
|
|
|
+ TenantId = tenantId,
|
|
|
+ FactoryId = layoutFactoryId,
|
|
|
+ ModuleCode = mc,
|
|
|
+ LayoutPattern = "card_grid",
|
|
|
+ UpdateTime = now,
|
|
|
+ });
|
|
|
+ existingHm.Add(mc);
|
|
|
+ }
|
|
|
+ if (toInsertHm.Count > 0)
|
|
|
+ await _db.Insertable(toInsertHm).ExecuteCommandAsync();
|
|
|
+
|
|
|
var fromExcel = excelOverrides.Values.Count(v => v.HasTarget || v.HasActual);
|
|
|
return Ok(new
|
|
|
{
|
|
|
ok = true,
|
|
|
- message = $"导入成功:Excel 填入 {fromExcel} 条,自动补全 {parsed.Count - fromExcel} 条,共 {parsed.Count} 条指标 × {DAYS} 天",
|
|
|
- detail = new { l1 = l1.Count, l2 = l2.Count, l3 = l3.Count, days = DAYS, fromExcel, autoGen = parsed.Count - fromExcel }
|
|
|
+ message = $"导入成功:Excel 填入 {fromExcel} 条,自动补全 {parsed.Count - fromExcel} 条,共 {parsed.Count} 条指标 × {DAYS} 天;补齐布局行 {toInsertLayout.Count} 条、模块模板 {toInsertHm.Count} 条",
|
|
|
+ detail = new
|
|
|
+ {
|
|
|
+ l1 = l1.Count,
|
|
|
+ l2 = l2.Count,
|
|
|
+ l3 = l3.Count,
|
|
|
+ days = DAYS,
|
|
|
+ fromExcel,
|
|
|
+ autoGen = parsed.Count - fromExcel,
|
|
|
+ layoutInserted = toInsertLayout.Count,
|
|
|
+ homeModuleInserted = toInsertHm.Count
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
|