using Admin.NET.Plugin.AiDOP.Infrastructure; using Admin.NET.Plugin.AiDOP.Supply; namespace Admin.NET.Plugin.AiDOP.WorkOrder; /// /// 物料需求生成(MRP 入口,对接 S4 采购执行闭环)。 /// /// 【租户安全边界】需认证访问(已移除类级 [AllowAnonymous]);租户一律经 /// AidopTenantScope.ResolveOrThrow 从认证后 JWT 解析,无有效租户即拒绝, /// 不读前端 tenantId/domain 数字、无默认回退(原实现:类级匿名 + ResolveTenantId(domain) /// 信任前端直传的 domain 数字自选租户,否则回退宽松解析)。 /// [ApiDescriptionSettings(Order = 261, Description = "物料需求生成")] [Route("api/WorkOrder")] [NonUnify] public class MaterialRequirementGenerationService : IDynamicApiController, ITransient { private readonly UserManager _userManager; private readonly ProcurementPipelineService _pipeline; private readonly AidopActionRunLogWriter _runLog; public MaterialRequirementGenerationService( UserManager userManager, ProcurementPipelineService pipeline, AidopActionRunLogWriter runLog) { _userManager = userManager; _pipeline = pipeline; _runLog = runLog; } /// 同步/生成物料需求并执行采购闭环。 [DisplayName("生成物料需求")] [HttpPost("material-requirement/generate")] public async Task Generate([FromQuery] string domain) { var tenantId = ResolveTenantId(domain); var account = _userManager.Account ?? "system"; var logId = await _runLog.StartAsync("S4_MRP_GENERATE", tenantId, "tenant", tenantId, domain ?? tenantId.ToString()); try { var result = await _pipeline.ExecuteCoreAsync(tenantId, account, createFromShortage: true); await _runLog.SuccessAsync(logId, result.Message, result); return result; } catch (Exception ex) { await _runLog.FailedAsync(logId, ex.Message); throw Oops.Oh(ex.Message); } } private long ResolveTenantId(string? domain) { // 租户只信 JWT:不再从前端直传的 domain 数字解析(那是让调用方自选租户), // domain 仅作为公司域业务参数用于日志归档。 return AidopTenantScope.ResolveOrThrow(_userManager); } }