using Admin.NET.Core; namespace Admin.NET.Plugin.AiDOP.Infrastructure; public sealed record SmartOpsTrustedScope(long TenantId, long FactoryId, long UserId); /// /// 智慧诊断与改善闭环的可信作用域:只信登录上下文,工厂由租户关系解析。 /// public sealed class SmartOpsTrustedScopeResolver : ITransient { public const long PlatformTenantId = SqlSugarConst.DefaultTenantId; private readonly ISqlSugarClient _db; private readonly UserManager _userManager; public SmartOpsTrustedScopeResolver(ISqlSugarClient db, UserManager userManager) { _db = db; _userManager = userManager; } public async Task ResolveAsync(long? candidateFactoryId = null) { var tenantId = AidopTenantScope.ResolveOrThrow(_userManager); SmartOpsTrustedScopeRules.EnsureBusinessTenant(tenantId); var orgId = await _db.Queryable() .ClearFilter() .Where(x => x.Id == tenantId) .Select(x => x.OrgId) .FirstAsync(); var factoryId = SmartOpsTrustedScopeRules.ResolveFactory(tenantId, orgId, candidateFactoryId); var userId = _userManager.UserId; if (userId <= 0) throw Oops.Oh("无法确定当前用户,请重新登录"); return new SmartOpsTrustedScope(tenantId, factoryId, userId); } } public static class SmartOpsTrustedScopeRules { public const long PlatformTenantId = SqlSugarConst.DefaultTenantId; public static void EnsureBusinessTenant(long tenantId) { if (tenantId <= 0 || tenantId == 1 || tenantId == PlatformTenantId) throw new InvalidOperationException("平台租户不得作为业务事实作用域"); } /// /// 客户端工厂只是筛选建议。未授权或不存在时回落到租户 OrgId,不得使用外租户工厂。 /// public static long ResolveFactory(long tenantId, long orgId, long? candidateFactoryId) { if (orgId <= 0 || orgId == tenantId) throw new InvalidOperationException("当前租户未配置有效工厂"); if (candidateFactoryId is > 0 && candidateFactoryId.Value != tenantId && candidateFactoryId.Value == orgId) return candidateFactoryId.Value; return orgId; } public static bool IsAuthorizedFactory(long tenantId, long orgId, long factoryId) => factoryId > 0 && factoryId != tenantId && factoryId == orgId; } public static class SmartOpsImprovementPlanNo { public static string BuildPrefix(DateTime now) => $"SOP-{now:yyyyMMdd}-"; public static string Next(string prefix, IEnumerable existing) { var max = 0; foreach (var planNo in existing) { if (string.IsNullOrWhiteSpace(planNo) || !planNo.StartsWith(prefix, StringComparison.Ordinal)) continue; if (int.TryParse(planNo.AsSpan(prefix.Length), out var n)) max = Math.Max(max, n); } return $"{prefix}{max + 1:000}"; } }