完善机组车台功能,并导入机组车台 / 车间班次数据
一、机组车台功能完善 - 车间下拉改为只取「前缫」职责类型的部门(新增接口 /api/jizuchetai/qianxian-deps) - 补齐校验:车间、机组号、开始车台号必填;结束车台号不得小于开始车台号;同车间同机组不可重复 - 前端页面同步调整:车间改为下拉选择、增加必填提示、表格加 row-key 二、数据导入(新增 ProdSeedData,幂等导入器) - biz_prod_qs_jzct:机组车台 35 条(其中 11 条已删除)→ 有效 24 条 - biz_prod_qs_cjbc:车间班次 7 条(其中 1 条已删除)→ 有效 6 条 - F9 的部门 Id 通过部门名称映射到本系统部门;已删除的数据不导入; 已存在且一致的数据不重复写入,缺失的自动补回
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Interfaces.SnowflakeID;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
using F9WebAiTest.Domain.Entities.Prod;
|
||||
using F9WebAiTest.Domain.Enums.SYS.Employees;
|
||||
using SysDept = F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.Data;
|
||||
|
||||
/// <summary>
|
||||
/// 生产模块基础数据种子(来源于 F9 系统导出):
|
||||
/// 1) biz_prod_qs_jzct —— 机组车台
|
||||
/// 2) biz_prod_qs_cjbc —— 车间班次
|
||||
/// 幂等:按「车间 + 机组」/「车间 + 班次」匹配,存在则校正,不存在则新增;已删除的数据不导入。
|
||||
/// </summary>
|
||||
public static class ProdSeedData
|
||||
{
|
||||
private const string JzctResource = "F9WebAiTest.Infrastructure.Data.SeedData.biz_prod_qs_jzct.json";
|
||||
private const string CjbcResource = "F9WebAiTest.Infrastructure.Data.SeedData.biz_prod_qs_cjbc.json";
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOpts = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString
|
||||
};
|
||||
|
||||
public static void Seed(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
SeedJizuChetai(fsql);
|
||||
SeedChejianBanci(fsql);
|
||||
}
|
||||
|
||||
// ==================== 机组车台 ====================
|
||||
private static void SeedJizuChetai(IFreeSql fsql)
|
||||
{
|
||||
try
|
||||
{
|
||||
var items = Load<JzctItem>(JzctResource);
|
||||
if (items.Count == 0) return;
|
||||
|
||||
var deptMap = BuildDeptMap(fsql);
|
||||
var existing = fsql.Select<JizuChetai>().ToList()
|
||||
.GroupBy(x => (x.DepartmentId, x.Jizu))
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
|
||||
int added = 0, updated = 0, skipped = 0;
|
||||
var unmappedDeps = new HashSet<long>();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item.DeletedAt)) { skipped++; continue; }
|
||||
|
||||
if (!deptMap.TryGetValue(item.DepId, out var deptId))
|
||||
{
|
||||
unmappedDeps.Add(item.DepId);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = (deptId, item.Jizu);
|
||||
if (existing.TryGetValue(key, out var e))
|
||||
{
|
||||
if (e.ChetaiStart != item.ChetaiStart || e.ChetaiEnd != item.ChetaiEnd)
|
||||
{
|
||||
e.ChetaiStart = item.ChetaiStart;
|
||||
e.ChetaiEnd = item.ChetaiEnd;
|
||||
fsql.Update<JizuChetai>().SetSource(e).ExecuteAffrows();
|
||||
updated++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fsql.Insert(new JizuChetai
|
||||
{
|
||||
DepartmentId = deptId,
|
||||
Jizu = item.Jizu,
|
||||
ChetaiStart = item.ChetaiStart,
|
||||
ChetaiEnd = item.ChetaiEnd
|
||||
}).ExecuteAffrows();
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"[ProdSeed] 机组车台:来源 {items.Count} 条,新增 {added} 条,更新 {updated} 条,跳过(已删除/无法映射){skipped} 条");
|
||||
if (unmappedDeps.Count > 0)
|
||||
Console.WriteLine($"[ProdSeed] 提示:以下 F9 部门 Id 未匹配到本系统部门,已跳过 -> {string.Join(", ", unmappedDeps)}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[ProdSeed] 机组车台导入跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 车间班次 ====================
|
||||
private static void SeedChejianBanci(IFreeSql fsql)
|
||||
{
|
||||
try
|
||||
{
|
||||
var items = Load<CjbcItem>(CjbcResource);
|
||||
if (items.Count == 0) return;
|
||||
|
||||
var deptMap = BuildDeptMap(fsql);
|
||||
var existing = fsql.Select<ChejianBanci>().ToList()
|
||||
.GroupBy(x => (x.DeptId, x.Banci))
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
|
||||
int added = 0, updated = 0, skipped = 0;
|
||||
var unmappedDeps = new HashSet<long>();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item.DeletedAt)) { skipped++; continue; }
|
||||
|
||||
if (!deptMap.TryGetValue(item.DepId, out var deptId))
|
||||
{
|
||||
unmappedDeps.Add(item.DepId);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var banci = (Prod_Banci_Enum)item.Banci;
|
||||
var key = (deptId, banci);
|
||||
if (existing.TryGetValue(key, out var e))
|
||||
{
|
||||
if (Math.Abs(e.KaoqinDayhours - item.KaoqinDayhours) > 0.001)
|
||||
{
|
||||
e.KaoqinDayhours = item.KaoqinDayhours;
|
||||
fsql.Update<ChejianBanci>().SetSource(e).ExecuteAffrows();
|
||||
updated++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fsql.Insert(new ChejianBanci
|
||||
{
|
||||
DeptId = deptId,
|
||||
Banci = banci,
|
||||
KaoqinDayhours = item.KaoqinDayhours
|
||||
}).ExecuteAffrows();
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"[ProdSeed] 车间班次:来源 {items.Count} 条,新增 {added} 条,更新 {updated} 条,跳过(已删除/无法映射){skipped} 条");
|
||||
if (unmappedDeps.Count > 0)
|
||||
Console.WriteLine($"[ProdSeed] 提示:以下 F9 部门 Id 未匹配到本系统部门,已跳过 -> {string.Join(", ", unmappedDeps)}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[ProdSeed] 车间班次导入跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 公共 ====================
|
||||
|
||||
/// <summary>建立 F9 部门 Id → 本系统部门 Id 的映射(以部门名称为桥梁)</summary>
|
||||
private static Dictionary<long, long> BuildDeptMap(IFreeSql fsql)
|
||||
{
|
||||
var f9NameMap = BusinessSeedData.DeptData.ToDictionary(x => x.Id, x => x.Name);
|
||||
var localNameMap = fsql.Select<SysDept.Department>().ToList()
|
||||
.GroupBy(d => d.Name)
|
||||
.ToDictionary(g => g.Key, g => g.First().Id);
|
||||
|
||||
var map = new Dictionary<long, long>();
|
||||
foreach (var (f9Id, name) in f9NameMap)
|
||||
{
|
||||
if (localNameMap.TryGetValue(name, out var localId)) map[f9Id] = localId;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static List<T> Load<T>(string resourceName)
|
||||
{
|
||||
var asm = Assembly.GetExecutingAssembly();
|
||||
using var stream = asm.GetManifestResourceStream(resourceName);
|
||||
if (stream == null) return new List<T>();
|
||||
return JsonSerializer.Deserialize<List<T>>(stream, JsonOpts) ?? new List<T>();
|
||||
}
|
||||
|
||||
// ---- 数据项 ----
|
||||
private class JzctItem
|
||||
{
|
||||
[JsonPropertyName("id")] public long Id { get; set; }
|
||||
[JsonPropertyName("deleted_at")] public string? DeletedAt { get; set; }
|
||||
[JsonPropertyName("dep_id")] public long DepId { get; set; }
|
||||
[JsonPropertyName("jizu")] public int Jizu { get; set; }
|
||||
[JsonPropertyName("chetai_start")] public int ChetaiStart { get; set; }
|
||||
[JsonPropertyName("chetai_end")] public int ChetaiEnd { get; set; }
|
||||
}
|
||||
|
||||
private class CjbcItem
|
||||
{
|
||||
[JsonPropertyName("id")] public long Id { get; set; }
|
||||
[JsonPropertyName("deleted_at")] public string? DeletedAt { get; set; }
|
||||
[JsonPropertyName("dep_id")] public long DepId { get; set; }
|
||||
[JsonPropertyName("banci")] public int Banci { get; set; }
|
||||
[JsonPropertyName("kaoqin_dayhours")] public double KaoqinDayhours { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user