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,不得使用外租户工厂。 /// /// /// 只判 是否为空。 /// 不得再用 orgId == tenantId 判「未配置」:Admin.NET 种子/SQL 脚本建的租户 /// 其根组织 Id 与租户 Id 同号是官方约定(见 SysTenantSeedData「Id=OrgId=DefaultTenantId」 /// 与 SysOrgSeedData 根组织),只有界面 AddTenant 建的租户才另发雪花号。 /// 旧判据会把系统默认 / Ai-DOP 演示公司等种子租户全部误判为「未配置有效工厂」而拒绝服务。 /// public static long ResolveFactory(long tenantId, long orgId, long? candidateFactoryId) { // 文案面向使用者:说清「是什么状态、该找谁、去哪里配」,不暴露表名/列名。 if (orgId <= 0) 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}"; } }