- 新增尊享包商品类型,支持 5000W 和 10000W Tokens - 增加尊享包折扣计算与折扣后价格获取方法 - PayService 新增获取商品列表接口,支持尊享包折扣展示 - PayManager 支持尊享包订单金额按折扣计算,并新增获取用户累计充值金额方法 - OpenApiService Anthropic接口增加VIP与尊享包用量校验 - AiGateWayManager 增加尊享包Token扣减逻辑 - AiAccountService 返回用户VIP状态、到期时间及尊享包Token统计信息
206 lines
7.6 KiB
C#
206 lines
7.6 KiB
C#
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.Extensions.Logging;
|
||
using Volo.Abp.Application.Services;
|
||
using Volo.Abp.Users;
|
||
using Yi.Framework.AiHub.Domain.Entities;
|
||
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||
using Yi.Framework.AiHub.Domain.Extensions;
|
||
using Yi.Framework.AiHub.Domain.Managers;
|
||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||
using Yi.Framework.AiHub.Domain.Shared.Dtos.Anthropic;
|
||
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
|
||
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi.Embeddings;
|
||
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi.Images;
|
||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||
using Yi.Framework.Rbac.Application.Contracts.IServices;
|
||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||
|
||
namespace Yi.Framework.AiHub.Application.Services;
|
||
|
||
public class OpenApiService : ApplicationService
|
||
{
|
||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||
private readonly ILogger<OpenApiService> _logger;
|
||
private readonly TokenManager _tokenManager;
|
||
private readonly AiGateWayManager _aiGateWayManager;
|
||
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||
private readonly AiBlacklistManager _aiBlacklistManager;
|
||
private readonly IAccountService _accountService;
|
||
private readonly PremiumPackageManager _premiumPackageManager;
|
||
|
||
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
||
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
||
ISqlSugarRepository<AiModelEntity> aiModelRepository, AiBlacklistManager aiBlacklistManager,
|
||
IAccountService accountService, PremiumPackageManager premiumPackageManager)
|
||
{
|
||
_httpContextAccessor = httpContextAccessor;
|
||
_logger = logger;
|
||
_tokenManager = tokenManager;
|
||
_aiGateWayManager = aiGateWayManager;
|
||
_aiModelRepository = aiModelRepository;
|
||
_aiBlacklistManager = aiBlacklistManager;
|
||
_accountService = accountService;
|
||
_premiumPackageManager = premiumPackageManager;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对话
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <param name="cancellationToken"></param>
|
||
[HttpPost("openApi/v1/chat/completions")]
|
||
public async Task ChatCompletionsAsync([FromBody] ThorChatCompletionsRequest input,
|
||
CancellationToken cancellationToken)
|
||
{
|
||
//前面都是校验,后面才是真正的调用
|
||
var httpContext = this._httpContextAccessor.HttpContext;
|
||
var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext));
|
||
await _aiBlacklistManager.VerifiyAiBlacklist(userId);
|
||
//ai网关代理httpcontext
|
||
if (input.Stream == true)
|
||
{
|
||
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||
userId, null, cancellationToken);
|
||
}
|
||
else
|
||
{
|
||
await _aiGateWayManager.CompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId,
|
||
null,
|
||
cancellationToken);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 图片生成
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <param name="cancellationToken"></param>
|
||
[HttpPost("openApi/v1/images/generations")]
|
||
public async Task ImagesGenerationsAsync([FromBody] ImageCreateRequest input, CancellationToken cancellationToken)
|
||
{
|
||
var httpContext = this._httpContextAccessor.HttpContext;
|
||
Intercept(httpContext);
|
||
var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext));
|
||
await _aiBlacklistManager.VerifiyAiBlacklist(userId);
|
||
await _aiGateWayManager.CreateImageForStatisticsAsync(httpContext, userId, null, input);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 向量生成
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <param name="cancellationToken"></param>
|
||
[HttpPost("openApi/v1/embeddings")]
|
||
public async Task EmbeddingAsync([FromBody] ThorEmbeddingInput input, CancellationToken cancellationToken)
|
||
{
|
||
var httpContext = this._httpContextAccessor.HttpContext;
|
||
Intercept(httpContext);
|
||
var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext));
|
||
await _aiBlacklistManager.VerifiyAiBlacklist(userId);
|
||
await _aiGateWayManager.EmbeddingForStatisticsAsync(httpContext, userId, null, input);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取模型列表
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet("openApi/v1/models")]
|
||
public async Task<ModelsListDto> ModelsAsync()
|
||
{
|
||
var data = await _aiModelRepository._DbQueryable
|
||
.Where(x => x.ModelType == ModelTypeEnum.Chat)
|
||
.OrderByDescending(x => x.OrderNum)
|
||
.Select(x => new ModelsDataDto
|
||
{
|
||
Id = x.ModelId,
|
||
@object = "model",
|
||
Created = DateTime.Now.ToUnixTimeSeconds(),
|
||
OwnedBy = "organization-owner",
|
||
Type = x.ModelId
|
||
}).ToListAsync();
|
||
|
||
return new ModelsListDto()
|
||
{
|
||
Data = data
|
||
};
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Anthropic对话(尊享服务专用)
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <param name="cancellationToken"></param>
|
||
[HttpPost("openApi/v1/messages")]
|
||
public async Task MessagesAsync([FromBody] AnthropicInput input,
|
||
CancellationToken cancellationToken)
|
||
{
|
||
//前面都是校验,后面才是真正的调用
|
||
var httpContext = this._httpContextAccessor.HttpContext;
|
||
var userId = await _tokenManager.GetUserIdAsync(GetTokenByHttpContext(httpContext));
|
||
await _aiBlacklistManager.VerifiyAiBlacklist(userId);
|
||
|
||
// 验证用户是否为VIP
|
||
var userInfo = await _accountService.GetAsync(null, null, userId);
|
||
if (userInfo == null)
|
||
{
|
||
throw new UserFriendlyException("用户信息不存在");
|
||
}
|
||
|
||
// 检查是否为VIP(使用RoleCodes判断)
|
||
if (!userInfo.RoleCodes.Contains(AiHubConst.VipRole) && userInfo.User.UserName != "cc")
|
||
{
|
||
throw new UserFriendlyException("该接口为尊享服务专用,需要VIP权限才能使用");
|
||
}
|
||
|
||
// 检查尊享token包用量
|
||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId);
|
||
if (availableTokens <= 0)
|
||
{
|
||
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
||
}
|
||
|
||
//ai网关代理httpcontext
|
||
if (input.Stream)
|
||
{
|
||
await _aiGateWayManager.AnthropicCompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||
userId, null, cancellationToken);
|
||
}
|
||
else
|
||
{
|
||
await _aiGateWayManager.AnthropicCompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId,
|
||
null,
|
||
cancellationToken);
|
||
}
|
||
}
|
||
|
||
|
||
#region 私有
|
||
|
||
private string? GetTokenByHttpContext(HttpContext httpContext)
|
||
{
|
||
// 获取Authorization头
|
||
string authHeader = httpContext.Request.Headers["Authorization"];
|
||
|
||
// 检查是否有Bearer token
|
||
if (authHeader != null && authHeader.StartsWith("Bearer "))
|
||
{
|
||
return authHeader.Substring("Bearer ".Length).Trim();
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private void Intercept(HttpContext httpContext)
|
||
{
|
||
if (httpContext.Request.Host.Value == "yxai.chat")
|
||
{
|
||
throw new UserFriendlyException("当前海外站点不支持大流量接口,请使用转发站点:https://ai.ccnetcore.com");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
} |