|
|
@@ -22,30 +22,15 @@ namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Manufacturing;
|
|
|
[NonUnify]
|
|
|
public class AdoS0ProcessFlowCardsController : ControllerBase
|
|
|
{
|
|
|
- /// <summary>租户内优先匹配的公司编码(仅作优先条件,取不到即按机构类型/根节点在本租户内兜底)。</summary>
|
|
|
- private const string PreferredCompanyCode = "S0-C001";
|
|
|
-
|
|
|
- /// <summary>租户内优先匹配的工厂编码(同上,绝不跨租户查找)。</summary>
|
|
|
- private const string PreferredFactoryCode = "S0-F001";
|
|
|
-
|
|
|
- /// <summary>机构类型-公司(SysOrg.Type 数据字典值)。</summary>
|
|
|
- private const string CompanyOrgType = "201";
|
|
|
-
|
|
|
- /// <summary>机构类型-工厂(SysOrg.Type 数据字典值)。</summary>
|
|
|
- private const string FactoryOrgType = "501";
|
|
|
-
|
|
|
private readonly SqlSugarRepository<AdoS0ProcessFlowCard> _rep;
|
|
|
private readonly SqlSugarRepository<AdoS0MfgRoutingOpDetail> _routingRep;
|
|
|
- private readonly SqlSugarRepository<SysOrg> _orgRep;
|
|
|
|
|
|
public AdoS0ProcessFlowCardsController(
|
|
|
SqlSugarRepository<AdoS0ProcessFlowCard> rep,
|
|
|
- SqlSugarRepository<AdoS0MfgRoutingOpDetail> routingRep,
|
|
|
- SqlSugarRepository<SysOrg> orgRep)
|
|
|
+ SqlSugarRepository<AdoS0MfgRoutingOpDetail> routingRep)
|
|
|
{
|
|
|
_rep = rep;
|
|
|
_routingRep = routingRep;
|
|
|
- _orgRep = orgRep;
|
|
|
}
|
|
|
|
|
|
[HttpGet]
|
|
|
@@ -92,9 +77,8 @@ public class AdoS0ProcessFlowCardsController : ControllerBase
|
|
|
public async Task<IActionResult> CreateAsync([FromBody] AdoS0ProcessFlowCardUpsertDto dto)
|
|
|
{
|
|
|
if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
|
|
|
- var (companyId, factoryId) = await ResolveDefaultOrgAsync(tenantId);
|
|
|
- if (companyId <= 0 || factoryId <= 0)
|
|
|
- return AdoS0ApiErrors.InvalidRequest("当前租户未配置机构,无法确定流转卡的公司/工厂归属,请先在机构管理中维护");
|
|
|
+ var (companyId, factoryId) = await AdoS0OrgScope.ResolveAsync(_rep.Context, tenantId);
|
|
|
+ if (companyId <= 0 || factoryId <= 0) return AdoS0OrgScope.NotConfiguredError("流转卡");
|
|
|
|
|
|
var flowCardNo = dto.FlowCardNo?.Trim() ?? string.Empty;
|
|
|
if (await _rep.IsAnyAsync(x => x.TenantId == tenantId && x.FactoryRefId == factoryId && x.FlowCardNo == flowCardNo))
|
|
|
@@ -186,79 +170,4 @@ public class AdoS0ProcessFlowCardsController : ControllerBase
|
|
|
return Ok(new { message = "删除成功" });
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 工艺路线联动选项:按所选物料编码过滤该物料关联的 RouteCode(去重)。
|
|
|
- /// 用稳定物料编码 ItemNum 关联(RoutingOpDetail.MaterialCode),不用物料名称。
|
|
|
- /// 返回空 → 前端允许手工填写兜底;返回非空 → 前端强制下拉。
|
|
|
- /// </summary>
|
|
|
- [HttpGet("routing-options")]
|
|
|
- public async Task<IActionResult> GetRoutingOptionsAsync([FromQuery] string? itemNum)
|
|
|
- {
|
|
|
- if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
|
|
|
- var key = itemNum?.Trim();
|
|
|
- if (string.IsNullOrWhiteSpace(key))
|
|
|
- return Ok(Array.Empty<object>());
|
|
|
-
|
|
|
- var list = await _routingRep.AsQueryable()
|
|
|
- .Where(x => x.MaterialCode == key)
|
|
|
- .GroupBy(x => new { x.RouteCode, x.RouteName })
|
|
|
- .Select(x => new { routeCode = x.RouteCode, routeName = x.RouteName })
|
|
|
- .ToListAsync();
|
|
|
-
|
|
|
- var options = list
|
|
|
- .Where(x => !string.IsNullOrWhiteSpace(x.routeCode))
|
|
|
- .OrderBy(x => x.routeCode)
|
|
|
- .ToList();
|
|
|
- return Ok(options);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 解析当前租户的归属组织(公司 / 工厂)。
|
|
|
- ///
|
|
|
- /// 租户边界:只在 <paramref name="tenantId"/> 内解析,唯一查询显式带 <c>TenantId == tenantId</c>;
|
|
|
- /// **禁止 ClearFilter** —— SysOrg 的 Code 没有唯一约束(index_SysOrg_C 是普通索引、非 UNIQUE),
|
|
|
- /// 且 S0-C001 / S0-F001 只是 S0OrgSeedData 灌给 AIDOP-Demo 租户的示范种子、其余租户并无此组织;
|
|
|
- /// 清掉租户过滤器按 Code 取组织,会把他租户的 SysOrg.Id 写进本租户单据(跨租户组织引用)。
|
|
|
- ///
|
|
|
- /// Code 定位:S0-C001 / S0-F001 仅作"租户内优先匹配条件",取不到即在本租户内按机构类型 / 根节点兜底;
|
|
|
- /// 不跨租户查找、不复制种子、不新增组织。
|
|
|
- ///
|
|
|
- /// 优先级——公司:
|
|
|
- /// C1 本租户 Code=S0-C001 → C3 本租户根节点(Pid=0)且 Type=201 → C2 本租户任意 Type=201 → C4 本租户根节点(Pid=0)。
|
|
|
- /// 优先级——工厂:
|
|
|
- /// F1 本租户 Code=S0-F001 且父级为已解析公司 → F2 本租户 Type=501 且父级为已解析公司
|
|
|
- /// → F3 本租户任意 Type=501 → F4 回落到公司自身(兼容未建工厂节点的租户,不新增组织)。
|
|
|
- ///
|
|
|
- /// 确定性:一次取本租户机构并按 (OrderNo, Id) 稳定排序,各级用 FirstOrDefault 在有序集合上取首个,
|
|
|
- /// 避免多候选时结果随执行计划漂移(原实现的无序 FirstAsync 是不确定的)。
|
|
|
- ///
|
|
|
- /// 无解:返回 (0, 0) 交由调用方转 400 业务错误,**不抛异常** ——
|
|
|
- /// AdoS0ExceptionFilter 会把 /api/s0/* 上的未处理异常统一映射为 500 S01999
|
|
|
- /// (见 AdoS0ApiErrors.TryMapUnhandledException),抛异常会把"租户未配置机构"这类业务态误报成系统故障。
|
|
|
- /// </summary>
|
|
|
- /// <param name="tenantId">AdoS0TenantScope 解析出的可信租户(JWT claim),不接受 payload 传入。</param>
|
|
|
- private async Task<(long CompanyId, long FactoryId)> ResolveDefaultOrgAsync(long tenantId)
|
|
|
- {
|
|
|
- // 单次取数 + 内存分级,避免逐级往返;排序在 SQL 侧一次确定,后续各级取首个即稳定。
|
|
|
- var orgs = await _orgRep.AsQueryable()
|
|
|
- .Where(x => x.TenantId == tenantId)
|
|
|
- .OrderBy(x => new { x.OrderNo, x.Id })
|
|
|
- .Select(x => new { x.Id, x.Code, x.Type, x.Pid })
|
|
|
- .ToListAsync();
|
|
|
- if (orgs.Count == 0) return (0, 0);
|
|
|
-
|
|
|
- var company =
|
|
|
- orgs.FirstOrDefault(o => o.Code == PreferredCompanyCode) // C1
|
|
|
- ?? orgs.FirstOrDefault(o => o.Pid == 0 && o.Type == CompanyOrgType) // C3
|
|
|
- ?? orgs.FirstOrDefault(o => o.Type == CompanyOrgType) // C2
|
|
|
- ?? orgs.FirstOrDefault(o => o.Pid == 0); // C4
|
|
|
- if (company == null) return (0, 0);
|
|
|
-
|
|
|
- var factory =
|
|
|
- orgs.FirstOrDefault(o => o.Code == PreferredFactoryCode && o.Pid == company.Id) // F1
|
|
|
- ?? orgs.FirstOrDefault(o => o.Type == FactoryOrgType && o.Pid == company.Id) // F2
|
|
|
- ?? orgs.FirstOrDefault(o => o.Type == FactoryOrgType); // F3
|
|
|
-
|
|
|
- return (company.Id, factory?.Id ?? company.Id); // F4
|
|
|
- }
|
|
|
}
|