using Admin.NET.Core.Service; using Admin.NET.Plugin.AiDOP.Entity.DataPlatform; using SqlSugar; namespace Admin.NET.Plugin.AiDOP.DataPlatform.Inbound; /// grant 授权 + IP 白名单 + 限流。行数上限由接收服务按 grant/entity 取 min。 public sealed class MdpInboundAuthService : ITransient { private readonly ISqlSugarClient _db; private readonly SysCacheService _cache; public MdpInboundAuthService(ISqlSugarClient db, SysCacheService cache) { _db = db; _cache = cache; } /// 返回 null = 通过;否则 (httpStatus, message)。 public async Task<(int Status, string Message)?> CheckAsync( string accessKey, long tenantId, string entityCode, string clientIp, CancellationToken ct) { var code = (entityCode ?? string.Empty).Trim().ToUpperInvariant(); var entity = await _db.Queryable() .Where(e => e.InboundEnabled == 1) .Where("UPPER(entity_code) = @code", new SugarParameter("@code", code)) .FirstAsync(ct); if (entity == null) return (403, "entity not authorized"); var grant = await _db.Queryable() .Where(g => g.AccessKey == accessKey && g.Status == 1) .Where("UPPER(entity_code) = @code", new SugarParameter("@code", code)) .FirstAsync(ct); if (grant == null) return (403, "entity not authorized"); if (grant.TenantId != tenantId) return (403, "entity not authorized"); if (!string.IsNullOrWhiteSpace(grant.IpAllowlist)) { var allowed = grant.IpAllowlist .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); var ip = (clientIp ?? string.Empty).Trim(); if (ip.Length == 0 || !allowed.Contains(ip, StringComparer.OrdinalIgnoreCase)) return (403, "ip not allowed"); } var minute = DateTime.Now.ToString("yyyyMMddHHmm"); var rateKey = $"AIDOP_INBOUND_RATE:{accessKey}:{code}:{minute}"; var current = _cache.Get(rateKey); var next = current + 1; _cache.Set(rateKey, next, TimeSpan.FromMinutes(1)); if (next > grant.RateLimitPerMin) return (429, "rate limit exceeded"); return null; } public async Task GetGrantAsync(string accessKey, string entityCode, CancellationToken ct) { var code = (entityCode ?? string.Empty).Trim().ToUpperInvariant(); return await _db.Queryable() .Where(g => g.AccessKey == accessKey && g.Status == 1) .Where("UPPER(entity_code) = @code", new SugarParameter("@code", code)) .FirstAsync(ct); } }