2025-11-27 19:01:16 +08:00
|
|
|
|
using SqlSugar;
|
|
|
|
|
|
using Volo.Abp.Domain.Services;
|
|
|
|
|
|
using Yi.Framework.AiHub.Domain.Entities;
|
2026-01-01 00:44:02 +08:00
|
|
|
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
2025-07-03 22:31:39 +08:00
|
|
|
|
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
2025-11-27 19:01:16 +08:00
|
|
|
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
2025-07-03 22:31:39 +08:00
|
|
|
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Yi.Framework.AiHub.Domain.Managers;
|
|
|
|
|
|
|
2025-11-27 19:01:16 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Token验证结果
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class TokenValidationResult
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 用户Id
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Guid UserId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Token Id
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Guid TokenId { get; set; }
|
2026-01-02 19:26:09 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// token
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string Token { get; set; }
|
2026-02-12 17:36:31 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Token名称
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string TokenName { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否启用请求日志记录
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsEnableLog { get; set; }
|
2025-11-27 19:01:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 22:31:39 +08:00
|
|
|
|
public class TokenManager : DomainService
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
2025-11-27 19:01:16 +08:00
|
|
|
|
private readonly ISqlSugarRepository<UsageStatisticsAggregateRoot> _usageStatisticsRepository;
|
2026-01-01 00:44:02 +08:00
|
|
|
|
private readonly ISqlSugarRepository<AiModelEntity, Guid> _aiModelRepository;
|
2025-07-03 22:31:39 +08:00
|
|
|
|
|
2025-11-27 19:01:16 +08:00
|
|
|
|
public TokenManager(
|
|
|
|
|
|
ISqlSugarRepository<TokenAggregateRoot> tokenRepository,
|
2026-01-01 00:44:02 +08:00
|
|
|
|
ISqlSugarRepository<UsageStatisticsAggregateRoot> usageStatisticsRepository,
|
|
|
|
|
|
ISqlSugarRepository<AiModelEntity, Guid> aiModelRepository)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
_tokenRepository = tokenRepository;
|
2025-11-27 19:01:16 +08:00
|
|
|
|
_usageStatisticsRepository = usageStatisticsRepository;
|
2026-01-01 00:44:02 +08:00
|
|
|
|
_aiModelRepository = aiModelRepository;
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-27 19:01:16 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 验证Token并返回用户Id和TokenId
|
|
|
|
|
|
/// </summary>
|
2026-01-02 19:26:09 +08:00
|
|
|
|
/// <param name="tokenOrId">Token密钥或者TokenId</param>
|
2025-11-27 19:01:16 +08:00
|
|
|
|
/// <param name="modelId">模型Id(用于判断是否是尊享模型需要检查额度)</param>
|
|
|
|
|
|
/// <returns>Token验证结果</returns>
|
2026-01-02 19:26:09 +08:00
|
|
|
|
public async Task<TokenValidationResult> ValidateTokenAsync(object tokenOrId, string? modelId = null)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2026-01-02 19:26:09 +08:00
|
|
|
|
|
|
|
|
|
|
if (tokenOrId is null)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2025-11-27 19:01:16 +08:00
|
|
|
|
throw new UserFriendlyException("当前请求未包含token", "401");
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
2026-01-02 19:26:09 +08:00
|
|
|
|
|
|
|
|
|
|
TokenAggregateRoot entity;
|
|
|
|
|
|
if (tokenOrId is Guid tokenId)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2026-01-02 19:26:09 +08:00
|
|
|
|
entity = await _tokenRepository._DbQueryable
|
|
|
|
|
|
.Where(x => x.Id == tokenId)
|
|
|
|
|
|
.FirstAsync();
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
2026-01-02 19:26:09 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var tokenStr = tokenOrId.ToString();
|
|
|
|
|
|
if (!tokenStr.StartsWith("yi-"))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new UserFriendlyException("当前请求token非法", "401");
|
|
|
|
|
|
}
|
|
|
|
|
|
entity = await _tokenRepository._DbQueryable
|
|
|
|
|
|
.Where(x => x.Token == tokenStr)
|
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-27 19:01:16 +08:00
|
|
|
|
if (entity is null)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2025-11-27 19:01:16 +08:00
|
|
|
|
throw new UserFriendlyException("当前请求token无效", "401");
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
2025-11-27 19:01:16 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查Token是否被禁用
|
|
|
|
|
|
if (entity.IsDisabled)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2025-11-27 19:01:16 +08:00
|
|
|
|
throw new UserFriendlyException("当前Token已被禁用,请启用后再使用", "403");
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-27 19:01:16 +08:00
|
|
|
|
// 检查Token是否过期
|
|
|
|
|
|
if (entity.ExpireTime.HasValue && entity.ExpireTime.Value < DateTime.Now)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2025-11-27 19:01:16 +08:00
|
|
|
|
throw new UserFriendlyException("当前Token已过期,请更新过期时间或创建新的Token", "403");
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-27 19:01:16 +08:00
|
|
|
|
// 如果是尊享模型且Token设置了额度限制,检查是否超限
|
2026-01-01 00:44:02 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(modelId) && entity.PremiumQuotaLimit.HasValue)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2026-01-01 00:44:02 +08:00
|
|
|
|
var isPremium = await _aiModelRepository._DbQueryable
|
|
|
|
|
|
.Where(x => x.ModelId == modelId)
|
|
|
|
|
|
.Select(x => x.IsPremium)
|
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (isPremium)
|
2025-07-03 22:31:39 +08:00
|
|
|
|
{
|
2026-01-01 00:44:02 +08:00
|
|
|
|
var usedQuota = await GetTokenPremiumUsedQuotaAsync(entity.UserId, entity.Id);
|
|
|
|
|
|
if (usedQuota >= entity.PremiumQuotaLimit.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new UserFriendlyException($"当前Token的尊享包额度已用完(已使用:{usedQuota},限制:{entity.PremiumQuotaLimit.Value}),请调整额度限制或使用其他Token", "403");
|
|
|
|
|
|
}
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-27 19:01:16 +08:00
|
|
|
|
|
|
|
|
|
|
return new TokenValidationResult
|
|
|
|
|
|
{
|
|
|
|
|
|
UserId = entity.UserId,
|
2026-01-02 19:26:09 +08:00
|
|
|
|
TokenId = entity.Id,
|
2026-02-12 17:36:31 +08:00
|
|
|
|
Token = entity.Token,
|
|
|
|
|
|
TokenName = entity.Name,
|
|
|
|
|
|
IsEnableLog = entity.IsEnableLog
|
2025-11-27 19:01:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取Token的尊享包已使用额度
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task<long> GetTokenPremiumUsedQuotaAsync(Guid userId, Guid tokenId)
|
|
|
|
|
|
{
|
2026-01-01 00:44:02 +08:00
|
|
|
|
// 先获取所有尊享模型的ModelId列表
|
|
|
|
|
|
var premiumModelIds = await _aiModelRepository._DbQueryable
|
|
|
|
|
|
.Where(x => x.IsPremium)
|
|
|
|
|
|
.Select(x => x.ModelId)
|
|
|
|
|
|
.ToListAsync();
|
2025-11-27 19:01:16 +08:00
|
|
|
|
|
|
|
|
|
|
var usedQuota = await _usageStatisticsRepository._DbQueryable
|
|
|
|
|
|
.Where(x => x.UserId == userId && x.TokenId == tokenId && premiumModelIds.Contains(x.ModelId))
|
|
|
|
|
|
.SumAsync(x => x.TotalTokenCount);
|
|
|
|
|
|
|
|
|
|
|
|
return usedQuota;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取用户的Token(兼容旧接口,返回第一个可用的Token)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[Obsolete("请使用 ValidateTokenAsync 方法")]
|
|
|
|
|
|
public async Task<string?> GetAsync(Guid userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var entity = await _tokenRepository._DbQueryable
|
|
|
|
|
|
.Where(x => x.UserId == userId && !x.IsDisabled)
|
|
|
|
|
|
.OrderBy(x => x.CreationTime)
|
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
|
|
return entity?.Token;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取用户Id(兼容旧接口)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[Obsolete("请使用 ValidateTokenAsync 方法")]
|
|
|
|
|
|
public async Task<Guid> GetUserIdAsync(string? token)
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = await ValidateTokenAsync(token);
|
|
|
|
|
|
return result.UserId;
|
2025-07-03 22:31:39 +08:00
|
|
|
|
}
|
2025-11-27 19:01:16 +08:00
|
|
|
|
}
|