|
|
@@ -89,6 +89,9 @@ public class AdoS0MfgLinePostsController : ControllerBase
|
|
|
.Where(x => x.LineSkillMasterId == id)
|
|
|
.OrderBy(x => x.Id)
|
|
|
.ToListAsync();
|
|
|
+ // 补显人员技能的业务编码/名称:一次批量查询 + 字典映射,杜绝 N+1;
|
|
|
+ // 显式限定当前租户,不跨租户取名。
|
|
|
+ await FillPersonSkillDisplayAsync(skills, tenantId);
|
|
|
return Ok(new { header, linePostSkills = skills });
|
|
|
}
|
|
|
|
|
|
@@ -96,7 +99,7 @@ public class AdoS0MfgLinePostsController : ControllerBase
|
|
|
public async Task<IActionResult> CreateAsync([FromBody] AdoS0LineSkillMasterUpsertDto dto)
|
|
|
{
|
|
|
if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
|
|
|
- var fkErr = await ValidatePersonSkillsAsync(dto, tenantId);
|
|
|
+ var (fkErr, skillMap) = await ResolvePersonSkillCodesAsync(dto, tenantId);
|
|
|
if (fkErr != null) return BadRequest(new { message = fkErr });
|
|
|
|
|
|
var db = _postRep.Context;
|
|
|
@@ -124,7 +127,7 @@ public class AdoS0MfgLinePostsController : ControllerBase
|
|
|
|
|
|
foreach (var s in dto.Skills)
|
|
|
{
|
|
|
- var row = MapSkill(post.Id, s, tenantId);
|
|
|
+ var row = MapSkill(post, s, tenantId, skillMap);
|
|
|
await _skillRep.AsInsertable(row).ExecuteCommandAsync();
|
|
|
}
|
|
|
|
|
|
@@ -145,7 +148,7 @@ public class AdoS0MfgLinePostsController : ControllerBase
|
|
|
var header = await _postRep.ByIdScopedAsync(id, tenantId);
|
|
|
if (header == null) return NotFound();
|
|
|
|
|
|
- var fkErr = await ValidatePersonSkillsAsync(dto, tenantId);
|
|
|
+ var (fkErr, skillMap) = await ResolvePersonSkillCodesAsync(dto, tenantId);
|
|
|
if (fkErr != null) return BadRequest(new { message = fkErr });
|
|
|
|
|
|
var db = _postRep.Context;
|
|
|
@@ -164,10 +167,10 @@ public class AdoS0MfgLinePostsController : ControllerBase
|
|
|
header.UpdateTime = dto.UpdateTime ?? DateTime.Now;
|
|
|
await _postRep.AsUpdateable(header).ExecuteCommandAsync();
|
|
|
|
|
|
- await _skillRep.AsDeleteable().Where(x => x.LineSkillMasterId == id).ExecuteCommandAsync();
|
|
|
+ await _skillRep.AsDeleteable().Where(x => x.LineSkillMasterId == id && x.TenantId == tenantId).ExecuteCommandAsync();
|
|
|
foreach (var s in dto.Skills)
|
|
|
{
|
|
|
- var row = MapSkill(id, s, tenantId);
|
|
|
+ var row = MapSkill(header, s, tenantId, skillMap);
|
|
|
await _skillRep.AsInsertable(row).ExecuteCommandAsync();
|
|
|
}
|
|
|
|
|
|
@@ -192,7 +195,7 @@ public class AdoS0MfgLinePostsController : ControllerBase
|
|
|
await db.Ado.BeginTranAsync();
|
|
|
try
|
|
|
{
|
|
|
- await _skillRep.AsDeleteable().Where(x => x.LineSkillMasterId == id).ExecuteCommandAsync();
|
|
|
+ await _skillRep.AsDeleteable().Where(x => x.LineSkillMasterId == id && x.TenantId == tenantId).ExecuteCommandAsync();
|
|
|
await _postRep.DeleteAsync(header);
|
|
|
await db.Ado.CommitTranAsync();
|
|
|
return Ok(new { message = "删除成功" });
|
|
|
@@ -204,25 +207,89 @@ public class AdoS0MfgLinePostsController : ControllerBase
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private async Task<string?> ValidatePersonSkillsAsync(AdoS0LineSkillMasterUpsertDto dto, long tenantId)
|
|
|
+ /// <summary>
|
|
|
+ /// 把提交的人员技能【业务编码】解析为当前租户下的 PersonSkill.Id。
|
|
|
+ ///
|
|
|
+ /// 作用域依据:uk_PersonSkill_code = UNIQUE (tenant_id, CompanyRefId, FactoryRefId, Code)。
|
|
|
+ /// PersonSkill 表**没有 Domain 列**,故不按 Domain 收窄;产线岗位主表也不带
|
|
|
+ /// CompanyRefId / FactoryRefId,无法据此再收窄,因此按 (租户, Code) 解析并对
|
|
|
+ /// 多义(同租户下跨公司/工厂重码)显式报错,而不是随便取一条。
|
|
|
+ ///
|
|
|
+ /// 返回 error 时不写库;成功时返回 code -> id 映射。
|
|
|
+ /// </summary>
|
|
|
+ private async Task<(string? Error, Dictionary<string, long> Map)> ResolvePersonSkillCodesAsync(
|
|
|
+ AdoS0LineSkillMasterUpsertDto dto, long tenantId)
|
|
|
{
|
|
|
+ var map = new Dictionary<string, long>(StringComparer.Ordinal);
|
|
|
if (dto.Skills == null || dto.Skills.Count == 0)
|
|
|
- return null;
|
|
|
+ return (null, map);
|
|
|
|
|
|
- var ids = dto.Skills.Select(s => s.PersonSkillId).Distinct().ToList();
|
|
|
- var cnt = await _personSkillRep.ScopedTo(tenantId)
|
|
|
+ var codes = dto.Skills
|
|
|
+ .Select(s => (s.PersonSkillCode ?? string.Empty).Trim())
|
|
|
+ .Where(c => c.Length > 0)
|
|
|
+ .Distinct(StringComparer.Ordinal)
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ if (codes.Count == 0)
|
|
|
+ return ("人员技能不能为空", map);
|
|
|
+
|
|
|
+ var rows = await _personSkillRep.ScopedTo(tenantId)
|
|
|
+ .Where(p => codes.Contains(p.Code))
|
|
|
+ .Select(p => new { p.Id, p.Code })
|
|
|
+ .ToListAsync();
|
|
|
+
|
|
|
+ foreach (var code in codes)
|
|
|
+ {
|
|
|
+ var hits = rows.Where(r => string.Equals(r.Code, code, StringComparison.Ordinal)).ToList();
|
|
|
+ if (hits.Count == 0)
|
|
|
+ return ($"人员技能 {code} 不存在或不属于当前作用域", map);
|
|
|
+ if (hits.Count > 1)
|
|
|
+ return ($"人员技能编码 {code} 在当前租户下存在 {hits.Count} 条同码主数据,无法唯一确定,请先处理主数据重码", map);
|
|
|
+ map[code] = hits[0].Id;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (null, map);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>批量补显 PersonSkill 的 Code / Name(单次查询 + 字典映射,无 N+1)。</summary>
|
|
|
+ private async Task FillPersonSkillDisplayAsync(List<AdoS0LineSkillDetail> rows, long tenantId)
|
|
|
+ {
|
|
|
+ if (rows.Count == 0) return;
|
|
|
+ var ids = rows.Select(r => r.PersonSkillId).Distinct().ToList();
|
|
|
+ var skills = await _personSkillRep.ScopedTo(tenantId)
|
|
|
.Where(p => ids.Contains(p.Id))
|
|
|
- .CountAsync();
|
|
|
- return cnt == ids.Count ? null : "存在无效的人员技能主数据引用";
|
|
|
+ .Select(p => new { p.Id, p.Code, p.Name })
|
|
|
+ .ToListAsync();
|
|
|
+ var map = skills.GroupBy(x => x.Id).ToDictionary(g => g.Key, g => g.First());
|
|
|
+ foreach (var r in rows)
|
|
|
+ {
|
|
|
+ if (map.TryGetValue(r.PersonSkillId, out var p))
|
|
|
+ {
|
|
|
+ r.PersonSkillCode = p.Code;
|
|
|
+ r.PersonSkillName = p.Name;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private static AdoS0LineSkillDetail MapSkill(long lineSkillMasterId, AdoS0LineSkillDetailUpsertDto s, long tenantId)
|
|
|
+ /// <summary>
|
|
|
+ /// 明细行映射。除新体系字段外,同时回填旧系统遗留业务键
|
|
|
+ /// (Domain / Site / ProdLine / JOBNo 取自主表,SkillNo 取自提交的技能业务编码),
|
|
|
+ /// 避免 Update 的「删除+重插」把这些列写成 NULL。
|
|
|
+ /// </summary>
|
|
|
+ private static AdoS0LineSkillDetail MapSkill(AdoS0LineSkillMaster master, AdoS0LineSkillDetailUpsertDto s,
|
|
|
+ long tenantId, IReadOnlyDictionary<string, long> skillMap)
|
|
|
{
|
|
|
+ var code = (s.PersonSkillCode ?? string.Empty).Trim();
|
|
|
return new AdoS0LineSkillDetail
|
|
|
{
|
|
|
TenantId = tenantId,
|
|
|
- LineSkillMasterId = lineSkillMasterId,
|
|
|
- PersonSkillId = s.PersonSkillId,
|
|
|
+ LineSkillMasterId = master.Id,
|
|
|
+ PersonSkillId = skillMap[code],
|
|
|
+ Domain = master.Domain,
|
|
|
+ Site = master.Site,
|
|
|
+ ProdLine = master.ProdLine,
|
|
|
+ JOBNo = master.JOBNo,
|
|
|
+ SkillNo = code,
|
|
|
RequiredLevel = s.RequiredLevel,
|
|
|
Remark = s.Remark,
|
|
|
EffectiveDate = s.EffectiveDate,
|