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}";
}
}