Merge branch 'ai-agent-backend' into ai-agent
# Conflicts: # Yi.Ai.Vue3/src/pages/console/index.vue # Yi.Ai.Vue3/src/routers/modules/staticRouter.ts
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建AI应用输入
|
||||||
|
/// </summary>
|
||||||
|
public class AiAppCreateInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 应用名称
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "应用名称不能为空")]
|
||||||
|
[StringLength(100, ErrorMessage = "应用名称不能超过100个字符")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用终结点
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "应用终结点不能为空")]
|
||||||
|
[StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")]
|
||||||
|
public string Endpoint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 额外URL
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(500, ErrorMessage = "额外URL不能超过500个字符")]
|
||||||
|
public string? ExtraUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用Key
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "应用Key不能为空")]
|
||||||
|
[StringLength(500, ErrorMessage = "应用Key不能超过500个字符")]
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||||
|
public int OrderNum { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI应用DTO
|
||||||
|
/// </summary>
|
||||||
|
public class AiAppDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 应用ID
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用终结点
|
||||||
|
/// </summary>
|
||||||
|
public string Endpoint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 额外URL
|
||||||
|
/// </summary>
|
||||||
|
public string? ExtraUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用Key
|
||||||
|
/// </summary>
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序
|
||||||
|
/// </summary>
|
||||||
|
public int OrderNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreationTime { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Yi.Framework.Ddd.Application.Contracts;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取AI应用列表输入
|
||||||
|
/// </summary>
|
||||||
|
public class AiAppGetListInput : PagedAllResultRequestDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索关键词(搜索应用名称)
|
||||||
|
/// </summary>
|
||||||
|
public string? SearchKey { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新AI应用输入
|
||||||
|
/// </summary>
|
||||||
|
public class AiAppUpdateInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 应用ID
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "应用ID不能为空")]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用名称
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "应用名称不能为空")]
|
||||||
|
[StringLength(100, ErrorMessage = "应用名称不能超过100个字符")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用终结点
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "应用终结点不能为空")]
|
||||||
|
[StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")]
|
||||||
|
public string Endpoint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 额外URL
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(500, ErrorMessage = "额外URL不能超过500个字符")]
|
||||||
|
public string? ExtraUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用Key
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "应用Key不能为空")]
|
||||||
|
[StringLength(500, ErrorMessage = "应用Key不能超过500个字符")]
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||||
|
public int OrderNum { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建AI模型输入
|
||||||
|
/// </summary>
|
||||||
|
public class AiModelCreateInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 处理名
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "处理名不能为空")]
|
||||||
|
[StringLength(100, ErrorMessage = "处理名不能超过100个字符")]
|
||||||
|
public string HandlerName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型ID
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型ID不能为空")]
|
||||||
|
[StringLength(200, ErrorMessage = "模型ID不能超过200个字符")]
|
||||||
|
public string ModelId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型名称
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型名称不能为空")]
|
||||||
|
[StringLength(200, ErrorMessage = "模型名称不能超过200个字符")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型描述
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||||
|
public int OrderNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI应用ID
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "AI应用ID不能为空")]
|
||||||
|
public Guid AiAppId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 额外信息
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")]
|
||||||
|
public string? ExtraInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型类型
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型类型不能为空")]
|
||||||
|
public ModelTypeEnum ModelType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型API类型
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型API类型不能为空")]
|
||||||
|
public ModelApiTypeEnum ModelApiType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型倍率
|
||||||
|
/// </summary>
|
||||||
|
[Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")]
|
||||||
|
public decimal Multiplier { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型显示倍率
|
||||||
|
/// </summary>
|
||||||
|
[Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")]
|
||||||
|
public decimal MultiplierShow { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 供应商分组名称
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")]
|
||||||
|
public string? ProviderName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型图标URL
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")]
|
||||||
|
public string? IconUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为尊享模型
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPremium { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI模型DTO
|
||||||
|
/// </summary>
|
||||||
|
public class AiModelDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 模型ID
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理名
|
||||||
|
/// </summary>
|
||||||
|
public string HandlerName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型ID
|
||||||
|
/// </summary>
|
||||||
|
public string ModelId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型描述
|
||||||
|
/// </summary>
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序
|
||||||
|
/// </summary>
|
||||||
|
public int OrderNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI应用ID
|
||||||
|
/// </summary>
|
||||||
|
public Guid AiAppId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 额外信息
|
||||||
|
/// </summary>
|
||||||
|
public string? ExtraInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型类型
|
||||||
|
/// </summary>
|
||||||
|
public ModelTypeEnum ModelType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型API类型
|
||||||
|
/// </summary>
|
||||||
|
public ModelApiTypeEnum ModelApiType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型倍率
|
||||||
|
/// </summary>
|
||||||
|
public decimal Multiplier { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型显示倍率
|
||||||
|
/// </summary>
|
||||||
|
public decimal MultiplierShow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 供应商分组名称
|
||||||
|
/// </summary>
|
||||||
|
public string? ProviderName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型图标URL
|
||||||
|
/// </summary>
|
||||||
|
public string? IconUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为尊享模型
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPremium { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Yi.Framework.Ddd.Application.Contracts;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取AI模型列表输入
|
||||||
|
/// </summary>
|
||||||
|
public class AiModelGetListInput : PagedAllResultRequestDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索关键词(搜索模型名称、模型ID)
|
||||||
|
/// </summary>
|
||||||
|
public string? SearchKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI应用ID筛选
|
||||||
|
/// </summary>
|
||||||
|
public Guid? AiAppId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否只显示尊享模型
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsPremiumOnly { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新AI模型输入
|
||||||
|
/// </summary>
|
||||||
|
public class AiModelUpdateInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 模型ID
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型ID不能为空")]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理名
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "处理名不能为空")]
|
||||||
|
[StringLength(100, ErrorMessage = "处理名不能超过100个字符")]
|
||||||
|
public string HandlerName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型ID
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型ID不能为空")]
|
||||||
|
[StringLength(200, ErrorMessage = "模型ID不能超过200个字符")]
|
||||||
|
public string ModelId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型名称
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型名称不能为空")]
|
||||||
|
[StringLength(200, ErrorMessage = "模型名称不能超过200个字符")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型描述
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||||
|
public int OrderNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI应用ID
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "AI应用ID不能为空")]
|
||||||
|
public Guid AiAppId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 额外信息
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")]
|
||||||
|
public string? ExtraInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型类型
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型类型不能为空")]
|
||||||
|
public ModelTypeEnum ModelType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型API类型
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "模型API类型不能为空")]
|
||||||
|
public ModelApiTypeEnum ModelApiType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型倍率
|
||||||
|
/// </summary>
|
||||||
|
[Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")]
|
||||||
|
public decimal Multiplier { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型显示倍率
|
||||||
|
/// </summary>
|
||||||
|
[Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")]
|
||||||
|
public decimal MultiplierShow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 供应商分组名称
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")]
|
||||||
|
public string? ProviderName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型图标URL
|
||||||
|
/// </summary>
|
||||||
|
[StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")]
|
||||||
|
public string? IconUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为尊享模型
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPremium { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渠道商管理服务接口
|
||||||
|
/// </summary>
|
||||||
|
public interface IChannelService
|
||||||
|
{
|
||||||
|
#region AI应用管理
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取AI应用列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">查询参数</param>
|
||||||
|
/// <returns>分页应用列表</returns>
|
||||||
|
Task<PagedResultDto<AiAppDto>> GetAppListAsync(AiAppGetListInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID获取AI应用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">应用ID</param>
|
||||||
|
/// <returns>应用详情</returns>
|
||||||
|
Task<AiAppDto> GetAppByIdAsync(Guid id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建AI应用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">创建输入</param>
|
||||||
|
/// <returns>创建的应用</returns>
|
||||||
|
Task<AiAppDto> CreateAppAsync(AiAppCreateInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新AI应用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">更新输入</param>
|
||||||
|
/// <returns>更新后的应用</returns>
|
||||||
|
Task<AiAppDto> UpdateAppAsync(AiAppUpdateInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除AI应用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">应用ID</param>
|
||||||
|
Task DeleteAppAsync(Guid id);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region AI模型管理
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取AI模型列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">查询参数</param>
|
||||||
|
/// <returns>分页模型列表</returns>
|
||||||
|
Task<PagedResultDto<AiModelDto>> GetModelListAsync(AiModelGetListInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID获取AI模型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">模型ID</param>
|
||||||
|
/// <returns>模型详情</returns>
|
||||||
|
Task<AiModelDto> GetModelByIdAsync(Guid id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建AI模型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">创建输入</param>
|
||||||
|
/// <returns>创建的模型</returns>
|
||||||
|
Task<AiModelDto> CreateModelAsync(AiModelCreateInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新AI模型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">更新输入</param>
|
||||||
|
/// <returns>更新后的模型</returns>
|
||||||
|
Task<AiModelDto> UpdateModelAsync(AiModelUpdateInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除AI模型(软删除)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">模型ID</param>
|
||||||
|
Task DeleteModelAsync(Guid id);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ using Volo.Abp.Users;
|
|||||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.DailyTask;
|
using Yi.Framework.AiHub.Application.Contracts.Dtos.DailyTask;
|
||||||
using Yi.Framework.AiHub.Domain.Entities;
|
using Yi.Framework.AiHub.Domain.Entities;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
using Yi.Framework.AiHub.Domain.Extensions;
|
using Yi.Framework.AiHub.Domain.Extensions;
|
||||||
using Yi.Framework.AiHub.Domain.Managers;
|
using Yi.Framework.AiHub.Domain.Managers;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
@@ -25,6 +26,7 @@ public class DailyTaskService : ApplicationService
|
|||||||
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||||||
private readonly ILogger<DailyTaskService> _logger;
|
private readonly ILogger<DailyTaskService> _logger;
|
||||||
private IDistributedLockProvider DistributedLock => LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>();
|
private IDistributedLockProvider DistributedLock => LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>();
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||||||
// 任务配置
|
// 任务配置
|
||||||
private readonly Dictionary<int, (long RequiredTokens, long RewardTokens, string Name, string Description)>
|
private readonly Dictionary<int, (long RequiredTokens, long RewardTokens, string Name, string Description)>
|
||||||
_taskConfigs = new()
|
_taskConfigs = new()
|
||||||
@@ -37,12 +39,13 @@ public class DailyTaskService : ApplicationService
|
|||||||
ISqlSugarRepository<DailyTaskRewardRecordAggregateRoot> dailyTaskRepository,
|
ISqlSugarRepository<DailyTaskRewardRecordAggregateRoot> dailyTaskRepository,
|
||||||
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
||||||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
||||||
ILogger<DailyTaskService> logger)
|
ILogger<DailyTaskService> logger, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
||||||
{
|
{
|
||||||
_dailyTaskRepository = dailyTaskRepository;
|
_dailyTaskRepository = dailyTaskRepository;
|
||||||
_messageRepository = messageRepository;
|
_messageRepository = messageRepository;
|
||||||
_premiumPackageRepository = premiumPackageRepository;
|
_premiumPackageRepository = premiumPackageRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_aiModelRepository = aiModelRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -179,10 +182,16 @@ public class DailyTaskService : ApplicationService
|
|||||||
var tomorrow = today.AddDays(1);
|
var tomorrow = today.AddDays(1);
|
||||||
|
|
||||||
// 查询今日所有使用尊享包模型的消息(role=system 表示消耗)
|
// 查询今日所有使用尊享包模型的消息(role=system 表示消耗)
|
||||||
|
// 先获取所有尊享模型的ModelId列表
|
||||||
|
var premiumModelIds = await _aiModelRepository._DbQueryable
|
||||||
|
.Where(x => x.IsPremium)
|
||||||
|
.Select(x => x.ModelId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
var totalTokens = await _messageRepository._DbQueryable
|
var totalTokens = await _messageRepository._DbQueryable
|
||||||
.Where(x => x.UserId == userId)
|
.Where(x => x.UserId == userId)
|
||||||
.Where(x => x.Role == "system") // system角色表示实际消耗
|
.Where(x => x.Role == "system") // system角色表示实际消耗
|
||||||
.Where(x => PremiumPackageConst.ModeIds.Contains(x.ModelId)) // 尊享包模型
|
.Where(x => premiumModelIds.Contains(x.ModelId)) // 尊享包模型
|
||||||
.Where(x => x.CreationTime >= today && x.CreationTime < tomorrow)
|
.Where(x => x.CreationTime >= today && x.CreationTime < tomorrow)
|
||||||
.SumAsync(x => x.TokenUsage.TotalTokenCount);
|
.SumAsync(x => x.TokenUsage.TotalTokenCount);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
using Mapster;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SqlSugar;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
using Volo.Abp.Application.Services;
|
||||||
|
using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||||
|
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Application.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渠道商管理服务实现
|
||||||
|
/// </summary>
|
||||||
|
[Authorize]
|
||||||
|
public class ChannelService : ApplicationService, IChannelService
|
||||||
|
{
|
||||||
|
private readonly ISqlSugarRepository<AiAppAggregateRoot, Guid> _appRepository;
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity, Guid> _modelRepository;
|
||||||
|
|
||||||
|
public ChannelService(
|
||||||
|
ISqlSugarRepository<AiAppAggregateRoot, Guid> appRepository,
|
||||||
|
ISqlSugarRepository<AiModelEntity, Guid> modelRepository)
|
||||||
|
{
|
||||||
|
_appRepository = appRepository;
|
||||||
|
_modelRepository = modelRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region AI应用管理
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取AI应用列表
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("channel/app")]
|
||||||
|
public async Task<PagedResultDto<AiAppDto>> GetAppListAsync(AiAppGetListInput input)
|
||||||
|
{
|
||||||
|
RefAsync<int> total = 0;
|
||||||
|
|
||||||
|
var entities = await _appRepository._DbQueryable
|
||||||
|
.WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x => x.Name.Contains(input.SearchKey))
|
||||||
|
.OrderByDescending(x => x.OrderNum)
|
||||||
|
.OrderByDescending(x => x.CreationTime)
|
||||||
|
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
|
||||||
|
|
||||||
|
var output = entities.Adapt<List<AiAppDto>>();
|
||||||
|
return new PagedResultDto<AiAppDto>(total, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID获取AI应用
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("channel/app/{id}")]
|
||||||
|
public async Task<AiAppDto> GetAppByIdAsync([FromRoute]Guid id)
|
||||||
|
{
|
||||||
|
var entity = await _appRepository.GetByIdAsync(id);
|
||||||
|
return entity.Adapt<AiAppDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建AI应用
|
||||||
|
/// </summary>
|
||||||
|
public async Task<AiAppDto> CreateAppAsync(AiAppCreateInput input)
|
||||||
|
{
|
||||||
|
var entity = new AiAppAggregateRoot
|
||||||
|
{
|
||||||
|
Name = input.Name,
|
||||||
|
Endpoint = input.Endpoint,
|
||||||
|
ExtraUrl = input.ExtraUrl,
|
||||||
|
ApiKey = input.ApiKey,
|
||||||
|
OrderNum = input.OrderNum
|
||||||
|
};
|
||||||
|
|
||||||
|
await _appRepository.InsertAsync(entity);
|
||||||
|
return entity.Adapt<AiAppDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新AI应用
|
||||||
|
/// </summary>
|
||||||
|
public async Task<AiAppDto> UpdateAppAsync(AiAppUpdateInput input)
|
||||||
|
{
|
||||||
|
var entity = await _appRepository.GetByIdAsync(input.Id);
|
||||||
|
|
||||||
|
entity.Name = input.Name;
|
||||||
|
entity.Endpoint = input.Endpoint;
|
||||||
|
entity.ExtraUrl = input.ExtraUrl;
|
||||||
|
entity.ApiKey = input.ApiKey;
|
||||||
|
entity.OrderNum = input.OrderNum;
|
||||||
|
|
||||||
|
await _appRepository.UpdateAsync(entity);
|
||||||
|
return entity.Adapt<AiAppDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除AI应用
|
||||||
|
/// </summary>
|
||||||
|
[HttpDelete("channel/app/{id}")]
|
||||||
|
public async Task DeleteAppAsync([FromRoute]Guid id)
|
||||||
|
{
|
||||||
|
// 检查是否有关联的模型
|
||||||
|
var hasModels = await _modelRepository._DbQueryable
|
||||||
|
.Where(x => x.AiAppId == id && !x.IsDeleted)
|
||||||
|
.AnyAsync();
|
||||||
|
|
||||||
|
if (hasModels)
|
||||||
|
{
|
||||||
|
throw new Volo.Abp.UserFriendlyException("该应用下存在模型,无法删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _appRepository.DeleteAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region AI模型管理
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取AI模型列表
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("channel/model")]
|
||||||
|
public async Task<PagedResultDto<AiModelDto>> GetModelListAsync(AiModelGetListInput input)
|
||||||
|
{
|
||||||
|
RefAsync<int> total = 0;
|
||||||
|
|
||||||
|
var query = _modelRepository._DbQueryable
|
||||||
|
.Where(x => !x.IsDeleted)
|
||||||
|
.WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x =>
|
||||||
|
x.Name.Contains(input.SearchKey) || x.ModelId.Contains(input.SearchKey))
|
||||||
|
.WhereIF(input.AiAppId.HasValue, x => x.AiAppId == input.AiAppId.Value)
|
||||||
|
.WhereIF(input.IsPremiumOnly == true, x => x.IsPremium);
|
||||||
|
|
||||||
|
var entities = await query
|
||||||
|
.OrderBy(x => x.OrderNum)
|
||||||
|
.OrderByDescending(x => x.Id)
|
||||||
|
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
|
||||||
|
|
||||||
|
var output = entities.Adapt<List<AiModelDto>>();
|
||||||
|
return new PagedResultDto<AiModelDto>(total, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID获取AI模型
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("channel/model/{id}")]
|
||||||
|
public async Task<AiModelDto> GetModelByIdAsync([FromRoute]Guid id)
|
||||||
|
{
|
||||||
|
var entity = await _modelRepository.GetByIdAsync(id);
|
||||||
|
return entity.Adapt<AiModelDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建AI模型
|
||||||
|
/// </summary>
|
||||||
|
public async Task<AiModelDto> CreateModelAsync(AiModelCreateInput input)
|
||||||
|
{
|
||||||
|
// 验证应用是否存在
|
||||||
|
var appExists = await _appRepository._DbQueryable
|
||||||
|
.Where(x => x.Id == input.AiAppId)
|
||||||
|
.AnyAsync();
|
||||||
|
|
||||||
|
if (!appExists)
|
||||||
|
{
|
||||||
|
throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
var entity = new AiModelEntity
|
||||||
|
{
|
||||||
|
HandlerName = input.HandlerName,
|
||||||
|
ModelId = input.ModelId,
|
||||||
|
Name = input.Name,
|
||||||
|
Description = input.Description,
|
||||||
|
OrderNum = input.OrderNum,
|
||||||
|
AiAppId = input.AiAppId,
|
||||||
|
ExtraInfo = input.ExtraInfo,
|
||||||
|
ModelType = input.ModelType,
|
||||||
|
ModelApiType = input.ModelApiType,
|
||||||
|
Multiplier = input.Multiplier,
|
||||||
|
MultiplierShow = input.MultiplierShow,
|
||||||
|
ProviderName = input.ProviderName,
|
||||||
|
IconUrl = input.IconUrl,
|
||||||
|
IsPremium = input.IsPremium,
|
||||||
|
IsDeleted = false
|
||||||
|
};
|
||||||
|
|
||||||
|
await _modelRepository.InsertAsync(entity);
|
||||||
|
return entity.Adapt<AiModelDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新AI模型
|
||||||
|
/// </summary>
|
||||||
|
public async Task<AiModelDto> UpdateModelAsync(AiModelUpdateInput input)
|
||||||
|
{
|
||||||
|
var entity = await _modelRepository.GetByIdAsync(input.Id);
|
||||||
|
|
||||||
|
// 验证应用是否存在
|
||||||
|
if (entity.AiAppId != input.AiAppId)
|
||||||
|
{
|
||||||
|
var appExists = await _appRepository._DbQueryable
|
||||||
|
.Where(x => x.Id == input.AiAppId)
|
||||||
|
.AnyAsync();
|
||||||
|
|
||||||
|
if (!appExists)
|
||||||
|
{
|
||||||
|
throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.HandlerName = input.HandlerName;
|
||||||
|
entity.ModelId = input.ModelId;
|
||||||
|
entity.Name = input.Name;
|
||||||
|
entity.Description = input.Description;
|
||||||
|
entity.OrderNum = input.OrderNum;
|
||||||
|
entity.AiAppId = input.AiAppId;
|
||||||
|
entity.ExtraInfo = input.ExtraInfo;
|
||||||
|
entity.ModelType = input.ModelType;
|
||||||
|
entity.ModelApiType = input.ModelApiType;
|
||||||
|
entity.Multiplier = input.Multiplier;
|
||||||
|
entity.MultiplierShow = input.MultiplierShow;
|
||||||
|
entity.ProviderName = input.ProviderName;
|
||||||
|
entity.IconUrl = input.IconUrl;
|
||||||
|
entity.IsPremium = input.IsPremium;
|
||||||
|
|
||||||
|
await _modelRepository.UpdateAsync(entity);
|
||||||
|
return entity.Adapt<AiModelDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除AI模型(软删除)
|
||||||
|
/// </summary>
|
||||||
|
[HttpDelete("channel/model/{id}")]
|
||||||
|
public async Task DeleteModelAsync(Guid id)
|
||||||
|
{
|
||||||
|
await _modelRepository.DeleteByIdAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -40,35 +40,37 @@ namespace Yi.Framework.AiHub.Application.Services;
|
|||||||
public class AiChatService : ApplicationService
|
public class AiChatService : ApplicationService
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
|
||||||
private readonly AiBlacklistManager _aiBlacklistManager;
|
private readonly AiBlacklistManager _aiBlacklistManager;
|
||||||
private readonly ILogger<AiChatService> _logger;
|
private readonly ILogger<AiChatService> _logger;
|
||||||
private readonly AiGateWayManager _aiGateWayManager;
|
private readonly AiGateWayManager _aiGateWayManager;
|
||||||
|
private readonly ModelManager _modelManager;
|
||||||
private readonly PremiumPackageManager _premiumPackageManager;
|
private readonly PremiumPackageManager _premiumPackageManager;
|
||||||
private readonly ChatManager _chatManager;
|
private readonly ChatManager _chatManager;
|
||||||
private readonly TokenManager _tokenManager;
|
private readonly TokenManager _tokenManager;
|
||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly ISqlSugarRepository<AgentStoreAggregateRoot> _agentStoreRepository;
|
private readonly ISqlSugarRepository<AgentStoreAggregateRoot> _agentStoreRepository;
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||||||
|
|
||||||
public AiChatService(IHttpContextAccessor httpContextAccessor,
|
public AiChatService(IHttpContextAccessor httpContextAccessor,
|
||||||
AiBlacklistManager aiBlacklistManager,
|
AiBlacklistManager aiBlacklistManager,
|
||||||
ISqlSugarRepository<AiModelEntity> aiModelRepository,
|
|
||||||
ILogger<AiChatService> logger,
|
ILogger<AiChatService> logger,
|
||||||
AiGateWayManager aiGateWayManager,
|
AiGateWayManager aiGateWayManager,
|
||||||
|
ModelManager modelManager,
|
||||||
PremiumPackageManager premiumPackageManager,
|
PremiumPackageManager premiumPackageManager,
|
||||||
ChatManager chatManager, TokenManager tokenManager, IAccountService accountService,
|
ChatManager chatManager, TokenManager tokenManager, IAccountService accountService,
|
||||||
ISqlSugarRepository<AgentStoreAggregateRoot> agentStoreRepository)
|
ISqlSugarRepository<AgentStoreAggregateRoot> agentStoreRepository, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_aiBlacklistManager = aiBlacklistManager;
|
_aiBlacklistManager = aiBlacklistManager;
|
||||||
_aiModelRepository = aiModelRepository;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_aiGateWayManager = aiGateWayManager;
|
_aiGateWayManager = aiGateWayManager;
|
||||||
|
_modelManager = modelManager;
|
||||||
_premiumPackageManager = premiumPackageManager;
|
_premiumPackageManager = premiumPackageManager;
|
||||||
_chatManager = chatManager;
|
_chatManager = chatManager;
|
||||||
_tokenManager = tokenManager;
|
_tokenManager = tokenManager;
|
||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
_agentStoreRepository = agentStoreRepository;
|
_agentStoreRepository = agentStoreRepository;
|
||||||
|
_aiModelRepository = aiModelRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ public class AiChatService : ApplicationService
|
|||||||
ModelName = x.Name,
|
ModelName = x.Name,
|
||||||
ModelDescribe = x.Description,
|
ModelDescribe = x.Description,
|
||||||
Remark = x.Description,
|
Remark = x.Description,
|
||||||
IsPremiumPackage = PremiumPackageConst.ModeIds.Contains(x.ModelId)
|
IsPremiumPackage = x.IsPremium
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -137,19 +139,24 @@ public class AiChatService : ApplicationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
//如果是尊享包服务,需要校验是是否尊享包足够
|
//如果是尊享包服务,需要校验是是否尊享包足够
|
||||||
if (CurrentUser.IsAuthenticated && PremiumPackageConst.ModeIds.Contains(input.Model))
|
if (CurrentUser.IsAuthenticated)
|
||||||
{
|
{
|
||||||
// 检查尊享token包用量
|
var isPremium = await _modelManager.IsPremiumModelAsync(input.Model);
|
||||||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId());
|
|
||||||
if (availableTokens <= 0)
|
if (isPremium)
|
||||||
{
|
{
|
||||||
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
// 检查尊享token包用量
|
||||||
|
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId());
|
||||||
|
if (availableTokens <= 0)
|
||||||
|
{
|
||||||
|
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ai网关代理httpcontext
|
//ai网关代理httpcontext
|
||||||
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||||||
CurrentUser.Id, sessionId, null, cancellationToken);
|
CurrentUser.Id, sessionId, null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -185,7 +192,7 @@ public class AiChatService : ApplicationService
|
|||||||
|
|
||||||
//ai网关代理httpcontext
|
//ai网关代理httpcontext
|
||||||
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||||||
CurrentUser.Id, null, null, cancellationToken);
|
CurrentUser.Id, null, null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -212,7 +219,9 @@ public class AiChatService : ApplicationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
//如果是尊享包服务,需要校验是是否尊享包足够
|
//如果是尊享包服务,需要校验是是否尊享包足够
|
||||||
if (PremiumPackageConst.ModeIds.Contains(input.ModelId))
|
var isPremium = await _modelManager.IsPremiumModelAsync(input.ModelId);
|
||||||
|
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
// 检查尊享token包用量
|
// 检查尊享token包用量
|
||||||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(tokenValidation.UserId);
|
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(tokenValidation.UserId);
|
||||||
@@ -230,7 +239,7 @@ public class AiChatService : ApplicationService
|
|||||||
input.ModelId,
|
input.ModelId,
|
||||||
tokenValidation.UserId,
|
tokenValidation.UserId,
|
||||||
input.Tools,
|
input.Tools,
|
||||||
cancellationToken);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class AiImageService : ApplicationService
|
|||||||
private readonly IBackgroundJobManager _backgroundJobManager;
|
private readonly IBackgroundJobManager _backgroundJobManager;
|
||||||
private readonly AiBlacklistManager _aiBlacklistManager;
|
private readonly AiBlacklistManager _aiBlacklistManager;
|
||||||
private readonly PremiumPackageManager _premiumPackageManager;
|
private readonly PremiumPackageManager _premiumPackageManager;
|
||||||
|
private readonly ModelManager _modelManager;
|
||||||
private readonly IGuidGenerator _guidGenerator;
|
private readonly IGuidGenerator _guidGenerator;
|
||||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||||
private readonly TokenManager _tokenManager;
|
private readonly TokenManager _tokenManager;
|
||||||
@@ -37,6 +38,7 @@ public class AiImageService : ApplicationService
|
|||||||
IBackgroundJobManager backgroundJobManager,
|
IBackgroundJobManager backgroundJobManager,
|
||||||
AiBlacklistManager aiBlacklistManager,
|
AiBlacklistManager aiBlacklistManager,
|
||||||
PremiumPackageManager premiumPackageManager,
|
PremiumPackageManager premiumPackageManager,
|
||||||
|
ModelManager modelManager,
|
||||||
IGuidGenerator guidGenerator,
|
IGuidGenerator guidGenerator,
|
||||||
IWebHostEnvironment webHostEnvironment, TokenManager tokenManager)
|
IWebHostEnvironment webHostEnvironment, TokenManager tokenManager)
|
||||||
{
|
{
|
||||||
@@ -44,6 +46,7 @@ public class AiImageService : ApplicationService
|
|||||||
_backgroundJobManager = backgroundJobManager;
|
_backgroundJobManager = backgroundJobManager;
|
||||||
_aiBlacklistManager = aiBlacklistManager;
|
_aiBlacklistManager = aiBlacklistManager;
|
||||||
_premiumPackageManager = premiumPackageManager;
|
_premiumPackageManager = premiumPackageManager;
|
||||||
|
_modelManager = modelManager;
|
||||||
_guidGenerator = guidGenerator;
|
_guidGenerator = guidGenerator;
|
||||||
_webHostEnvironment = webHostEnvironment;
|
_webHostEnvironment = webHostEnvironment;
|
||||||
_tokenManager = tokenManager;
|
_tokenManager = tokenManager;
|
||||||
@@ -76,8 +79,9 @@ public class AiImageService : ApplicationService
|
|||||||
throw new UserFriendlyException("图片生成功能需要VIP用户才能使用,请购买VIP后重新登录重试");
|
throw new UserFriendlyException("图片生成功能需要VIP用户才能使用,请购买VIP后重新登录重试");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尊享包校验
|
// 尊享包校验 - 使用ModelManager统一判断
|
||||||
if (PremiumPackageConst.ModeIds.Contains(input.ModelId))
|
var isPremium = await _modelManager.IsPremiumModelAsync(input.ModelId);
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId);
|
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId);
|
||||||
if (availableTokens <= 0)
|
if (availableTokens <= 0)
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using Mapster;
|
using Mapster;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Model;
|
using Yi.Framework.AiHub.Application.Contracts.Dtos.Model;
|
||||||
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.Model;
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
|
using Yi.Framework.AiHub.Domain.Managers;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Extensions;
|
using Yi.Framework.AiHub.Domain.Shared.Extensions;
|
||||||
@@ -18,10 +20,12 @@ namespace Yi.Framework.AiHub.Application.Services.Chat;
|
|||||||
public class ModelService : ApplicationService, IModelService
|
public class ModelService : ApplicationService, IModelService
|
||||||
{
|
{
|
||||||
private readonly ISqlSugarRepository<AiModelEntity, Guid> _modelRepository;
|
private readonly ISqlSugarRepository<AiModelEntity, Guid> _modelRepository;
|
||||||
|
private readonly ModelManager _modelManager;
|
||||||
|
|
||||||
public ModelService(ISqlSugarRepository<AiModelEntity, Guid> modelRepository)
|
public ModelService(ISqlSugarRepository<AiModelEntity, Guid> modelRepository, ModelManager modelManager)
|
||||||
{
|
{
|
||||||
_modelRepository = modelRepository;
|
_modelRepository = modelRepository;
|
||||||
|
_modelManager = modelManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,8 +45,7 @@ public class ModelService : ApplicationService, IModelService
|
|||||||
input.ModelTypes.Contains(x.ModelType))
|
input.ModelTypes.Contains(x.ModelType))
|
||||||
.WhereIF(input.ModelApiTypes is not null, x =>
|
.WhereIF(input.ModelApiTypes is not null, x =>
|
||||||
input.ModelApiTypes.Contains(x.ModelApiType))
|
input.ModelApiTypes.Contains(x.ModelApiType))
|
||||||
.WhereIF(input.IsPremiumOnly == true, x =>
|
.WhereIF(input.IsPremiumOnly == true, x => x.IsPremium)
|
||||||
PremiumPackageConst.ModeIds.Contains(x.ModelId))
|
|
||||||
.GroupBy(x => x.ModelId)
|
.GroupBy(x => x.ModelId)
|
||||||
.Select(x => x.ModelId)
|
.Select(x => x.ModelId)
|
||||||
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total));
|
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total));
|
||||||
@@ -61,7 +64,7 @@ public class ModelService : ApplicationService, IModelService
|
|||||||
MultiplierShow = x.First().MultiplierShow,
|
MultiplierShow = x.First().MultiplierShow,
|
||||||
ProviderName = x.First().ProviderName,
|
ProviderName = x.First().ProviderName,
|
||||||
IconUrl = x.First().IconUrl,
|
IconUrl = x.First().IconUrl,
|
||||||
IsPremium = PremiumPackageConst.ModeIds.Contains(x.First().ModelId),
|
IsPremium = x.First().IsPremium,
|
||||||
OrderNum = x.First().OrderNum
|
OrderNum = x.First().OrderNum
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
@@ -77,11 +80,11 @@ public class ModelService : ApplicationService, IModelService
|
|||||||
.Where(x => !x.IsDeleted)
|
.Where(x => !x.IsDeleted)
|
||||||
.Where(x => !string.IsNullOrEmpty(x.ProviderName))
|
.Where(x => !string.IsNullOrEmpty(x.ProviderName))
|
||||||
.GroupBy(x => x.ProviderName)
|
.GroupBy(x => x.ProviderName)
|
||||||
.OrderBy(x => x.ProviderName)
|
.OrderBy(x => x.OrderNum)
|
||||||
.Select(x => x.ProviderName)
|
.Select(x => x.ProviderName)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return providers;
|
return providers!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -115,4 +118,13 @@ public class ModelService : ApplicationService, IModelService
|
|||||||
|
|
||||||
return Task.FromResult(options);
|
return Task.FromResult(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除尊享模型ID缓存
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("model/clear-premium-cache")]
|
||||||
|
public async Task ClearPremiumModelCacheAsync()
|
||||||
|
{
|
||||||
|
await _modelManager.ClearPremiumModelIdsCacheAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,10 @@ using Volo.Abp.Application.Services;
|
|||||||
using Volo.Abp.Users;
|
using Volo.Abp.Users;
|
||||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Token;
|
using Yi.Framework.AiHub.Application.Contracts.Dtos.Token;
|
||||||
using Yi.Framework.AiHub.Domain.Entities;
|
using Yi.Framework.AiHub.Domain.Entities;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
||||||
using Yi.Framework.AiHub.Domain.Extensions;
|
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.Consts;
|
||||||
using Yi.Framework.Ddd.Application.Contracts;
|
using Yi.Framework.Ddd.Application.Contracts;
|
||||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
@@ -23,13 +25,15 @@ public class TokenService : ApplicationService
|
|||||||
{
|
{
|
||||||
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
||||||
private readonly ISqlSugarRepository<UsageStatisticsAggregateRoot> _usageStatisticsRepository;
|
private readonly ISqlSugarRepository<UsageStatisticsAggregateRoot> _usageStatisticsRepository;
|
||||||
|
private readonly ModelManager _modelManager;
|
||||||
public TokenService(
|
public TokenService(
|
||||||
ISqlSugarRepository<TokenAggregateRoot> tokenRepository,
|
ISqlSugarRepository<TokenAggregateRoot> tokenRepository,
|
||||||
ISqlSugarRepository<UsageStatisticsAggregateRoot> usageStatisticsRepository)
|
ISqlSugarRepository<UsageStatisticsAggregateRoot> usageStatisticsRepository,
|
||||||
|
ModelManager modelManager)
|
||||||
{
|
{
|
||||||
_tokenRepository = tokenRepository;
|
_tokenRepository = tokenRepository;
|
||||||
_usageStatisticsRepository = usageStatisticsRepository;
|
_usageStatisticsRepository = usageStatisticsRepository;
|
||||||
|
_modelManager = modelManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -51,8 +55,8 @@ public class TokenService : ApplicationService
|
|||||||
return new PagedResultDto<TokenGetListOutputDto>();
|
return new PagedResultDto<TokenGetListOutputDto>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取尊享包模型ID列表
|
// 通过ModelManager获取尊享包模型ID列表
|
||||||
var premiumModelIds = PremiumPackageConst.ModeIds;
|
var premiumModelIds = await _modelManager.GetPremiumModelIdsAsync();
|
||||||
|
|
||||||
// 批量查询所有Token的尊享包已使用额度
|
// 批量查询所有Token的尊享包已使用额度
|
||||||
var tokenIds = tokens.Select(t => t.Id).ToList();
|
var tokenIds = tokens.Select(t => t.Id).ToList();
|
||||||
|
|||||||
@@ -27,25 +27,27 @@ public class OpenApiService : ApplicationService
|
|||||||
private readonly ILogger<OpenApiService> _logger;
|
private readonly ILogger<OpenApiService> _logger;
|
||||||
private readonly TokenManager _tokenManager;
|
private readonly TokenManager _tokenManager;
|
||||||
private readonly AiGateWayManager _aiGateWayManager;
|
private readonly AiGateWayManager _aiGateWayManager;
|
||||||
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
private readonly ModelManager _modelManager;
|
||||||
private readonly AiBlacklistManager _aiBlacklistManager;
|
private readonly AiBlacklistManager _aiBlacklistManager;
|
||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly PremiumPackageManager _premiumPackageManager;
|
private readonly PremiumPackageManager _premiumPackageManager;
|
||||||
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreRepository;
|
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreRepository;
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||||||
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
public OpenApiService(IHttpContextAccessor httpContextAccessor, ILogger<OpenApiService> logger,
|
||||||
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
TokenManager tokenManager, AiGateWayManager aiGateWayManager,
|
||||||
ISqlSugarRepository<AiModelEntity> aiModelRepository, AiBlacklistManager aiBlacklistManager,
|
ModelManager modelManager, AiBlacklistManager aiBlacklistManager,
|
||||||
IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreRepository)
|
IAccountService accountService, PremiumPackageManager premiumPackageManager, ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreRepository, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_tokenManager = tokenManager;
|
_tokenManager = tokenManager;
|
||||||
_aiGateWayManager = aiGateWayManager;
|
_aiGateWayManager = aiGateWayManager;
|
||||||
_aiModelRepository = aiModelRepository;
|
_modelManager = modelManager;
|
||||||
_aiBlacklistManager = aiBlacklistManager;
|
_aiBlacklistManager = aiBlacklistManager;
|
||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
_premiumPackageManager = premiumPackageManager;
|
_premiumPackageManager = premiumPackageManager;
|
||||||
_imageStoreRepository = imageStoreRepository;
|
_imageStoreRepository = imageStoreRepository;
|
||||||
|
_aiModelRepository = aiModelRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -65,7 +67,9 @@ public class OpenApiService : ApplicationService
|
|||||||
await _aiBlacklistManager.VerifiyAiBlacklist(userId);
|
await _aiBlacklistManager.VerifiyAiBlacklist(userId);
|
||||||
|
|
||||||
//如果是尊享包服务,需要校验是是否尊享包足够
|
//如果是尊享包服务,需要校验是是否尊享包足够
|
||||||
if (PremiumPackageConst.ModeIds.Contains(input.Model))
|
var isPremium = await _modelManager.IsPremiumModelAsync(input.Model);
|
||||||
|
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
// 检查尊享token包用量
|
// 检查尊享token包用量
|
||||||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId);
|
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId);
|
||||||
@@ -79,13 +83,13 @@ public class OpenApiService : ApplicationService
|
|||||||
if (input.Stream == true)
|
if (input.Stream == true)
|
||||||
{
|
{
|
||||||
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||||||
userId, null, tokenId, cancellationToken);
|
userId, null, tokenId,CancellationToken.None);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _aiGateWayManager.CompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId,
|
await _aiGateWayManager.CompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input, userId,
|
||||||
null, tokenId,
|
null, tokenId,
|
||||||
cancellationToken);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,14 +197,14 @@ public class OpenApiService : ApplicationService
|
|||||||
{
|
{
|
||||||
await _aiGateWayManager.AnthropicCompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext,
|
await _aiGateWayManager.AnthropicCompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext,
|
||||||
input,
|
input,
|
||||||
userId, null, tokenId, cancellationToken);
|
userId, null, tokenId, CancellationToken.None);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _aiGateWayManager.AnthropicCompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
await _aiGateWayManager.AnthropicCompleteChatForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||||||
userId,
|
userId,
|
||||||
null, tokenId,
|
null, tokenId,
|
||||||
cancellationToken);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,14 +249,14 @@ public class OpenApiService : ApplicationService
|
|||||||
{
|
{
|
||||||
await _aiGateWayManager.OpenAiResponsesStreamForStatisticsAsync(_httpContextAccessor.HttpContext,
|
await _aiGateWayManager.OpenAiResponsesStreamForStatisticsAsync(_httpContextAccessor.HttpContext,
|
||||||
input,
|
input,
|
||||||
userId, null, tokenId, cancellationToken);
|
userId, null, tokenId, CancellationToken.None);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _aiGateWayManager.OpenAiResponsesAsyncForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
await _aiGateWayManager.OpenAiResponsesAsyncForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||||||
userId,
|
userId,
|
||||||
null, tokenId,
|
null, tokenId,
|
||||||
cancellationToken);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +308,7 @@ public class OpenApiService : ApplicationService
|
|||||||
modelId, input,
|
modelId, input,
|
||||||
userId,
|
userId,
|
||||||
null, tokenId,
|
null, tokenId,
|
||||||
cancellationToken);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -312,7 +316,7 @@ public class OpenApiService : ApplicationService
|
|||||||
modelId, input,
|
modelId, input,
|
||||||
userId,
|
userId,
|
||||||
null, tokenId,
|
null, tokenId,
|
||||||
cancellationToken);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ using Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
|||||||
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||||
using Yi.Framework.AiHub.Domain.Entities;
|
using Yi.Framework.AiHub.Domain.Entities;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
||||||
using Yi.Framework.AiHub.Domain.Extensions;
|
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.Consts;
|
||||||
using Yi.Framework.Ddd.Application.Contracts;
|
using Yi.Framework.Ddd.Application.Contracts;
|
||||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
@@ -27,17 +29,19 @@ public class UsageStatisticsService : ApplicationService, IUsageStatisticsServic
|
|||||||
private readonly ISqlSugarRepository<UsageStatisticsAggregateRoot> _usageStatisticsRepository;
|
private readonly ISqlSugarRepository<UsageStatisticsAggregateRoot> _usageStatisticsRepository;
|
||||||
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||||||
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
||||||
|
private readonly ModelManager _modelManager;
|
||||||
public UsageStatisticsService(
|
public UsageStatisticsService(
|
||||||
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
||||||
ISqlSugarRepository<UsageStatisticsAggregateRoot> usageStatisticsRepository,
|
ISqlSugarRepository<UsageStatisticsAggregateRoot> usageStatisticsRepository,
|
||||||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
||||||
ISqlSugarRepository<TokenAggregateRoot> tokenRepository)
|
ISqlSugarRepository<TokenAggregateRoot> tokenRepository,
|
||||||
|
ModelManager modelManager)
|
||||||
{
|
{
|
||||||
_messageRepository = messageRepository;
|
_messageRepository = messageRepository;
|
||||||
_usageStatisticsRepository = usageStatisticsRepository;
|
_usageStatisticsRepository = usageStatisticsRepository;
|
||||||
_premiumPackageRepository = premiumPackageRepository;
|
_premiumPackageRepository = premiumPackageRepository;
|
||||||
_tokenRepository = tokenRepository;
|
_tokenRepository = tokenRepository;
|
||||||
|
_modelManager = modelManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -181,7 +185,9 @@ public class UsageStatisticsService : ApplicationService, IUsageStatisticsServic
|
|||||||
public async Task<List<TokenPremiumUsageDto>> GetPremiumTokenUsageByTokenAsync()
|
public async Task<List<TokenPremiumUsageDto>> GetPremiumTokenUsageByTokenAsync()
|
||||||
{
|
{
|
||||||
var userId = CurrentUser.GetId();
|
var userId = CurrentUser.GetId();
|
||||||
var premiumModelIds = PremiumPackageConst.ModeIds;
|
|
||||||
|
// 通过ModelManager获取所有尊享模型的ModelId列表
|
||||||
|
var premiumModelIds = await _modelManager.GetPremiumModelIdsAsync();
|
||||||
|
|
||||||
// 从UsageStatistics表获取尊享模型的token消耗统计(按TokenId聚合)
|
// 从UsageStatistics表获取尊享模型的token消耗统计(按TokenId聚合)
|
||||||
var tokenUsages = await _usageStatisticsRepository._DbQueryable
|
var tokenUsages = await _usageStatisticsRepository._DbQueryable
|
||||||
|
|||||||
@@ -107,35 +107,6 @@ public class AzureDatabricksChatCompletionsService(ILogger<AzureDatabricksChatCo
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// var content = result?.Choices?.FirstOrDefault()?.Delta;
|
|
||||||
//
|
|
||||||
// if (first && content?.Content == OpenAIConstant.ThinkStart)
|
|
||||||
// {
|
|
||||||
// isThink = true;
|
|
||||||
// continue;
|
|
||||||
// // 需要将content的内容转换到其他字段
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (isThink && content?.Content?.Contains(OpenAIConstant.ThinkEnd) == true)
|
|
||||||
// {
|
|
||||||
// isThink = false;
|
|
||||||
// // 需要将content的内容转换到其他字段
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (isThink && result?.Choices != null)
|
|
||||||
// {
|
|
||||||
// // 需要将content的内容转换到其他字段
|
|
||||||
// foreach (var choice in result.Choices)
|
|
||||||
// {
|
|
||||||
// choice.Delta.ReasoningContent = choice.Delta.Content;
|
|
||||||
// choice.Delta.Content = string.Empty;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// first = false;
|
|
||||||
|
|
||||||
yield return result;
|
yield return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
|
|||||||
|
|
||||||
namespace Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorCustomOpenAI.Chats;
|
namespace Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorCustomOpenAI.Chats;
|
||||||
|
|
||||||
public sealed class OpenAiChatCompletionsService(ILogger<OpenAiChatCompletionsService> logger,IHttpClientFactory httpClientFactory)
|
public sealed class OpenAiChatCompletionsService(
|
||||||
|
ILogger<OpenAiChatCompletionsService> logger,
|
||||||
|
IHttpClientFactory httpClientFactory)
|
||||||
: IChatCompletionService
|
: IChatCompletionService
|
||||||
{
|
{
|
||||||
public async IAsyncEnumerable<ThorChatCompletionsResponse> CompleteChatStreamAsync(AiModelDescribe options,
|
public async IAsyncEnumerable<ThorChatCompletionsResponse> CompleteChatStreamAsync(AiModelDescribe options,
|
||||||
@@ -19,8 +21,18 @@ public sealed class OpenAiChatCompletionsService(ILogger<OpenAiChatCompletionsSe
|
|||||||
using var openai =
|
using var openai =
|
||||||
Activity.Current?.Source.StartActivity("OpenAI 对话流式补全");
|
Activity.Current?.Source.StartActivity("OpenAI 对话流式补全");
|
||||||
|
|
||||||
|
var endpoint = options?.Endpoint.TrimEnd('/');
|
||||||
|
|
||||||
|
//兼容 v1结尾
|
||||||
|
if (endpoint != null && endpoint.EndsWith("/v1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
endpoint = endpoint.Substring(0, endpoint.Length - "/v1".Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestUri = endpoint + "/v1/chat/completions";
|
||||||
|
|
||||||
var response = await httpClientFactory.CreateClient().HttpRequestRaw(
|
var response = await httpClientFactory.CreateClient().HttpRequestRaw(
|
||||||
options?.Endpoint.TrimEnd('/') + "/chat/completions",
|
requestUri,
|
||||||
chatCompletionCreate, options.ApiKey);
|
chatCompletionCreate, options.ApiKey);
|
||||||
|
|
||||||
openai?.SetTag("Model", chatCompletionCreate.Model);
|
openai?.SetTag("Model", chatCompletionCreate.Model);
|
||||||
@@ -130,8 +142,16 @@ public sealed class OpenAiChatCompletionsService(ILogger<OpenAiChatCompletionsSe
|
|||||||
using var openai =
|
using var openai =
|
||||||
Activity.Current?.Source.StartActivity("OpenAI 对话补全");
|
Activity.Current?.Source.StartActivity("OpenAI 对话补全");
|
||||||
|
|
||||||
|
var endpoint = options?.Endpoint.TrimEnd('/');
|
||||||
|
|
||||||
|
//兼容 v1结尾
|
||||||
|
if (endpoint != null && endpoint.EndsWith("/v1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
endpoint = endpoint.Substring(0, endpoint.Length - "/v1".Length);
|
||||||
|
}
|
||||||
|
var requestUri = endpoint + "/v1/chat/completions";
|
||||||
var response = await httpClientFactory.CreateClient().PostJsonAsync(
|
var response = await httpClientFactory.CreateClient().PostJsonAsync(
|
||||||
options?.Endpoint.TrimEnd('/') + "/chat/completions",
|
requestUri,
|
||||||
chatCompletionCreate, options.ApiKey).ConfigureAwait(false);
|
chatCompletionCreate, options.ApiKey).ConfigureAwait(false);
|
||||||
|
|
||||||
openai?.SetTag("Model", chatCompletionCreate.Model);
|
openai?.SetTag("Model", chatCompletionCreate.Model);
|
||||||
@@ -152,7 +172,8 @@ public sealed class OpenAiChatCompletionsService(ILogger<OpenAiChatCompletionsSe
|
|||||||
if (response.StatusCode >= HttpStatusCode.BadRequest)
|
if (response.StatusCode >= HttpStatusCode.BadRequest)
|
||||||
{
|
{
|
||||||
var error = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
var error = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
||||||
logger.LogError("OpenAI对话异常 请求地址:{Address}, StatusCode: {StatusCode} Response: {Response}", options.Endpoint,
|
logger.LogError("OpenAI对话异常 请求地址:{Address}, StatusCode: {StatusCode} Response: {Response}",
|
||||||
|
options.Endpoint,
|
||||||
response.StatusCode, error);
|
response.StatusCode, error);
|
||||||
|
|
||||||
throw new BusinessException("OpenAI对话异常", response.StatusCode.ToString());
|
throw new BusinessException("OpenAI对话异常", response.StatusCode.ToString());
|
||||||
|
|||||||
@@ -22,7 +22,16 @@ public class OpenAiResponseService(ILogger<OpenAiResponseService> logger,IHttpCl
|
|||||||
|
|
||||||
var client = httpClientFactory.CreateClient();
|
var client = httpClientFactory.CreateClient();
|
||||||
|
|
||||||
var response = await client.HttpRequestRaw(options.Endpoint.TrimEnd('/') + "/responses", input, options.ApiKey);
|
var endpoint = options?.Endpoint.TrimEnd('/');
|
||||||
|
|
||||||
|
//兼容 v1结尾
|
||||||
|
if (endpoint != null && endpoint.EndsWith("/v1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
endpoint = endpoint.Substring(0, endpoint.Length - "/v1".Length);
|
||||||
|
}
|
||||||
|
var requestUri = endpoint + "/v1/responses";
|
||||||
|
|
||||||
|
var response = await client.HttpRequestRaw(requestUri, input, options.ApiKey);
|
||||||
|
|
||||||
openai?.SetTag("Model", input.Model);
|
openai?.SetTag("Model", input.Model);
|
||||||
openai?.SetTag("Response", response.StatusCode.ToString());
|
openai?.SetTag("Response", response.StatusCode.ToString());
|
||||||
@@ -86,8 +95,17 @@ public class OpenAiResponseService(ILogger<OpenAiResponseService> logger,IHttpCl
|
|||||||
using var openai =
|
using var openai =
|
||||||
Activity.Current?.Source.StartActivity("OpenAI 响应");
|
Activity.Current?.Source.StartActivity("OpenAI 响应");
|
||||||
|
|
||||||
|
var endpoint = options?.Endpoint.TrimEnd('/');
|
||||||
|
|
||||||
|
//兼容 v1结尾
|
||||||
|
if (endpoint != null && endpoint.EndsWith("/v1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
endpoint = endpoint.Substring(0, endpoint.Length - "/v1".Length);
|
||||||
|
}
|
||||||
|
var requestUri = endpoint + "/v1/responses";
|
||||||
|
|
||||||
var response = await httpClientFactory.CreateClient().PostJsonAsync(
|
var response = await httpClientFactory.CreateClient().PostJsonAsync(
|
||||||
options?.Endpoint.TrimEnd('/') + "/responses",
|
requestUri,
|
||||||
chatCompletionCreate, options.ApiKey).ConfigureAwait(false);
|
chatCompletionCreate, options.ApiKey).ConfigureAwait(false);
|
||||||
|
|
||||||
openai?.SetTag("Model", chatCompletionCreate.Model);
|
openai?.SetTag("Model", chatCompletionCreate.Model);
|
||||||
|
|||||||
@@ -23,9 +23,17 @@ public sealed class DeepSeekChatCompletionsService(ILogger<DeepSeekChatCompletio
|
|||||||
|
|
||||||
using var openai =
|
using var openai =
|
||||||
Activity.Current?.Source.StartActivity("OpenAI 对话流式补全");
|
Activity.Current?.Source.StartActivity("OpenAI 对话流式补全");
|
||||||
|
|
||||||
|
var endpoint = options?.Endpoint.TrimEnd('/');
|
||||||
|
//兼容 v1结尾
|
||||||
|
if (endpoint != null && endpoint.EndsWith("/v1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
endpoint = endpoint.Substring(0, endpoint.Length - "/v1".Length);
|
||||||
|
}
|
||||||
|
var requestUri = endpoint + "/v1/chat/completions";
|
||||||
|
|
||||||
var response = await httpClientFactory.CreateClient().HttpRequestRaw(
|
var response = await httpClientFactory.CreateClient().HttpRequestRaw(
|
||||||
options?.Endpoint.TrimEnd('/') + "/chat/completions",
|
requestUri,
|
||||||
chatCompletionCreate, options.ApiKey);
|
chatCompletionCreate, options.ApiKey);
|
||||||
|
|
||||||
openai?.SetTag("Model", chatCompletionCreate.Model);
|
openai?.SetTag("Model", chatCompletionCreate.Model);
|
||||||
@@ -92,40 +100,6 @@ public sealed class DeepSeekChatCompletionsService(ILogger<DeepSeekChatCompletio
|
|||||||
|
|
||||||
var result = JsonSerializer.Deserialize<ThorChatCompletionsResponse>(line,
|
var result = JsonSerializer.Deserialize<ThorChatCompletionsResponse>(line,
|
||||||
ThorJsonSerializer.DefaultOptions);
|
ThorJsonSerializer.DefaultOptions);
|
||||||
|
|
||||||
// var content = result?.Choices?.FirstOrDefault()?.Delta;
|
|
||||||
//
|
|
||||||
// // if (first && string.IsNullOrWhiteSpace(content?.Content) && string.IsNullOrEmpty(content?.ReasoningContent))
|
|
||||||
// // {
|
|
||||||
// // continue;
|
|
||||||
// // }
|
|
||||||
//
|
|
||||||
// if (first && content.Content == OpenAIConstant.ThinkStart)
|
|
||||||
// {
|
|
||||||
// isThink = true;
|
|
||||||
// //continue;
|
|
||||||
// // 需要将content的内容转换到其他字段
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (isThink && content.Content.Contains(OpenAIConstant.ThinkEnd))
|
|
||||||
// {
|
|
||||||
// isThink = false;
|
|
||||||
// // 需要将content的内容转换到其他字段
|
|
||||||
// //continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (isThink)
|
|
||||||
// {
|
|
||||||
// // 需要将content的内容转换到其他字段
|
|
||||||
// foreach (var choice in result.Choices)
|
|
||||||
// {
|
|
||||||
// //choice.Delta.ReasoningContent = choice.Delta.Content;
|
|
||||||
// //choice.Delta.Content = string.Empty;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// first = false;
|
|
||||||
|
|
||||||
yield return result;
|
yield return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,8 +116,16 @@ public sealed class DeepSeekChatCompletionsService(ILogger<DeepSeekChatCompletio
|
|||||||
options.Endpoint = "https://api.deepseek.com/v1";
|
options.Endpoint = "https://api.deepseek.com/v1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var endpoint = options?.Endpoint.TrimEnd('/');
|
||||||
|
//兼容 v1结尾
|
||||||
|
if (endpoint != null && endpoint.EndsWith("/v1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
endpoint = endpoint.Substring(0, endpoint.Length - "/v1".Length);
|
||||||
|
}
|
||||||
|
var requestUri = endpoint + "/v1/chat/completions";
|
||||||
|
|
||||||
var response = await httpClientFactory.CreateClient().PostJsonAsync(
|
var response = await httpClientFactory.CreateClient().PostJsonAsync(
|
||||||
options?.Endpoint.TrimEnd('/') + "/chat/completions",
|
requestUri,
|
||||||
chatCompletionCreate, options.ApiKey).ConfigureAwait(false);
|
chatCompletionCreate, options.ApiKey).ConfigureAwait(false);
|
||||||
|
|
||||||
openai?.SetTag("Model", chatCompletionCreate.Model);
|
openai?.SetTag("Model", chatCompletionCreate.Model);
|
||||||
|
|||||||
@@ -80,4 +80,9 @@ public class AiModelEntity : Entity<Guid>, IOrderNum, ISoftDelete
|
|||||||
/// 模型图标URL
|
/// 模型图标URL
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? IconUrl { get; set; }
|
public string? IconUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为尊享模型
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPremium { get; set; }
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,12 @@ public class AiGateWayManager : DomainService
|
|||||||
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage, tokenId);
|
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage, tokenId);
|
||||||
|
|
||||||
// 扣减尊享token包用量
|
// 扣减尊享token包用量
|
||||||
if (PremiumPackageConst.ModeIds.Contains(request.Model))
|
var isPremium = await _aiModelRepository._DbQueryable
|
||||||
|
.Where(x => x.ModelId == request.Model)
|
||||||
|
.Select(x => x.IsPremium)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
var totalTokens = data.Usage?.TotalTokens ?? 0;
|
var totalTokens = data.Usage?.TotalTokens ?? 0;
|
||||||
if (totalTokens > 0)
|
if (totalTokens > 0)
|
||||||
@@ -300,12 +305,20 @@ public class AiGateWayManager : DomainService
|
|||||||
await _usageStatisticsManager.SetUsageAsync(userId, request.Model, tokenUsage, tokenId);
|
await _usageStatisticsManager.SetUsageAsync(userId, request.Model, tokenUsage, tokenId);
|
||||||
|
|
||||||
// 扣减尊享token包用量
|
// 扣减尊享token包用量
|
||||||
if (userId is not null && PremiumPackageConst.ModeIds.Contains(request.Model))
|
if (userId is not null)
|
||||||
{
|
{
|
||||||
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
var isPremium = await _aiModelRepository._DbQueryable
|
||||||
if (totalTokens > 0)
|
.Where(x => x.ModelId == request.Model)
|
||||||
|
.Select(x => x.IsPremium)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
|
var totalTokens = tokenUsage.TotalTokens ?? 0;
|
||||||
|
if (totalTokens > 0)
|
||||||
|
{
|
||||||
|
await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,12 +376,20 @@ public class AiGateWayManager : DomainService
|
|||||||
await _usageStatisticsManager.SetUsageAsync(userId, model, response.Usage, tokenId);
|
await _usageStatisticsManager.SetUsageAsync(userId, model, response.Usage, tokenId);
|
||||||
|
|
||||||
// 扣减尊享token包用量
|
// 扣减尊享token包用量
|
||||||
if (userId is not null && PremiumPackageConst.ModeIds.Contains(request.Model))
|
if (userId is not null)
|
||||||
{
|
{
|
||||||
var totalTokens = response.Usage.TotalTokens ?? 0;
|
var isPremium = await _aiModelRepository._DbQueryable
|
||||||
if (totalTokens > 0)
|
.Where(x => x.ModelId == request.Model)
|
||||||
|
.Select(x => x.IsPremium)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
|
var totalTokens = response.Usage.TotalTokens ?? 0;
|
||||||
|
if (totalTokens > 0)
|
||||||
|
{
|
||||||
|
await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using Volo.Abp.Domain.Services;
|
|||||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||||
using Yi.Framework.AiHub.Domain.AiGateWay;
|
using Yi.Framework.AiHub.Domain.AiGateWay;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Attributes;
|
using Yi.Framework.AiHub.Domain.Shared.Attributes;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
@@ -34,12 +35,13 @@ public class ChatManager : DomainService
|
|||||||
private readonly UsageStatisticsManager _usageStatisticsManager;
|
private readonly UsageStatisticsManager _usageStatisticsManager;
|
||||||
private readonly PremiumPackageManager _premiumPackageManager;
|
private readonly PremiumPackageManager _premiumPackageManager;
|
||||||
private readonly AiGateWayManager _aiGateWayManager;
|
private readonly AiGateWayManager _aiGateWayManager;
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity, Guid> _aiModelRepository;
|
||||||
|
|
||||||
public ChatManager(ILoggerFactory loggerFactory,
|
public ChatManager(ILoggerFactory loggerFactory,
|
||||||
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
||||||
ISqlSugarRepository<AgentStoreAggregateRoot> agentStoreRepository, AiMessageManager aiMessageManager,
|
ISqlSugarRepository<AgentStoreAggregateRoot> agentStoreRepository, AiMessageManager aiMessageManager,
|
||||||
UsageStatisticsManager usageStatisticsManager, PremiumPackageManager premiumPackageManager,
|
UsageStatisticsManager usageStatisticsManager, PremiumPackageManager premiumPackageManager,
|
||||||
AiGateWayManager aiGateWayManager)
|
AiGateWayManager aiGateWayManager, ISqlSugarRepository<AiModelEntity, Guid> aiModelRepository)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_messageRepository = messageRepository;
|
_messageRepository = messageRepository;
|
||||||
@@ -48,6 +50,7 @@ public class ChatManager : DomainService
|
|||||||
_usageStatisticsManager = usageStatisticsManager;
|
_usageStatisticsManager = usageStatisticsManager;
|
||||||
_premiumPackageManager = premiumPackageManager;
|
_premiumPackageManager = premiumPackageManager;
|
||||||
_aiGateWayManager = aiGateWayManager;
|
_aiGateWayManager = aiGateWayManager;
|
||||||
|
_aiModelRepository = aiModelRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -207,7 +210,12 @@ public class ChatManager : DomainService
|
|||||||
await _usageStatisticsManager.SetUsageAsync(userId, modelId, usage, tokenId);
|
await _usageStatisticsManager.SetUsageAsync(userId, modelId, usage, tokenId);
|
||||||
|
|
||||||
//扣减尊享token包用量
|
//扣减尊享token包用量
|
||||||
if (PremiumPackageConst.ModeIds.Contains(modelId))
|
var isPremium = await _aiModelRepository._DbQueryable
|
||||||
|
.Where(x => x.ModelId == modelId)
|
||||||
|
.Select(x => x.IsPremium)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
var totalTokens = usage?.TotalTokens ?? 0;
|
var totalTokens = usage?.TotalTokens ?? 0;
|
||||||
if (totalTokens > 0)
|
if (totalTokens > 0)
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Volo.Abp.Caching;
|
||||||
|
using Volo.Abp.Domain.Services;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
|
|
||||||
|
namespace Yi.Framework.AiHub.Domain.Managers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模型管理器
|
||||||
|
/// </summary>
|
||||||
|
public class ModelManager : DomainService
|
||||||
|
{
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||||||
|
private readonly IDistributedCache<List<string>, string> _distributedCache;
|
||||||
|
private readonly ILogger<ModelManager> _logger;
|
||||||
|
private const string PREMIUM_MODEL_IDS_CACHE_KEY = "PremiumModelIds";
|
||||||
|
|
||||||
|
public ModelManager(
|
||||||
|
ISqlSugarRepository<AiModelEntity> aiModelRepository,
|
||||||
|
IDistributedCache<List<string>, string> distributedCache,
|
||||||
|
ILogger<ModelManager> logger)
|
||||||
|
{
|
||||||
|
_aiModelRepository = aiModelRepository;
|
||||||
|
_distributedCache = distributedCache;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有尊享模型ID列表(使用分布式缓存,10分钟过期)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>尊享模型ID列表</returns>
|
||||||
|
public async Task<List<string>> GetPremiumModelIdsAsync()
|
||||||
|
{
|
||||||
|
var output = await _distributedCache.GetOrAddAsync(
|
||||||
|
PREMIUM_MODEL_IDS_CACHE_KEY,
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
// 从数据库查询
|
||||||
|
var premiumModelIds = await _aiModelRepository._DbQueryable
|
||||||
|
.Where(x => x.IsPremium)
|
||||||
|
.Select(x => x.ModelId)
|
||||||
|
.ToListAsync();
|
||||||
|
return premiumModelIds;
|
||||||
|
},
|
||||||
|
() => new Microsoft.Extensions.Caching.Distributed.DistributedCacheEntryOptions
|
||||||
|
{
|
||||||
|
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return output ?? new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断指定模型是否为尊享模型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modelId">模型ID</param>
|
||||||
|
/// <returns>是否为尊享模型</returns>
|
||||||
|
public async Task<bool> IsPremiumModelAsync(string modelId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(modelId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var premiumModelIds = await GetPremiumModelIdsAsync();
|
||||||
|
return premiumModelIds.Contains(modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除尊享模型ID缓存
|
||||||
|
/// </summary>
|
||||||
|
public async Task ClearPremiumModelIdsCacheAsync()
|
||||||
|
{
|
||||||
|
await _distributedCache.RemoveAsync(PREMIUM_MODEL_IDS_CACHE_KEY);
|
||||||
|
_logger.LogInformation("已清除尊享模型ID分布式缓存");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
using Yi.Framework.AiHub.Domain.Entities;
|
using Yi.Framework.AiHub.Domain.Entities;
|
||||||
|
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||||
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
using Yi.Framework.AiHub.Domain.Entities.OpenApi;
|
||||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
@@ -32,13 +33,16 @@ public class TokenManager : DomainService
|
|||||||
{
|
{
|
||||||
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
private readonly ISqlSugarRepository<TokenAggregateRoot> _tokenRepository;
|
||||||
private readonly ISqlSugarRepository<UsageStatisticsAggregateRoot> _usageStatisticsRepository;
|
private readonly ISqlSugarRepository<UsageStatisticsAggregateRoot> _usageStatisticsRepository;
|
||||||
|
private readonly ISqlSugarRepository<AiModelEntity, Guid> _aiModelRepository;
|
||||||
|
|
||||||
public TokenManager(
|
public TokenManager(
|
||||||
ISqlSugarRepository<TokenAggregateRoot> tokenRepository,
|
ISqlSugarRepository<TokenAggregateRoot> tokenRepository,
|
||||||
ISqlSugarRepository<UsageStatisticsAggregateRoot> usageStatisticsRepository)
|
ISqlSugarRepository<UsageStatisticsAggregateRoot> usageStatisticsRepository,
|
||||||
|
ISqlSugarRepository<AiModelEntity, Guid> aiModelRepository)
|
||||||
{
|
{
|
||||||
_tokenRepository = tokenRepository;
|
_tokenRepository = tokenRepository;
|
||||||
_usageStatisticsRepository = usageStatisticsRepository;
|
_usageStatisticsRepository = usageStatisticsRepository;
|
||||||
|
_aiModelRepository = aiModelRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -92,14 +96,20 @@ public class TokenManager : DomainService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果是尊享模型且Token设置了额度限制,检查是否超限
|
// 如果是尊享模型且Token设置了额度限制,检查是否超限
|
||||||
if (!string.IsNullOrEmpty(modelId) &&
|
if (!string.IsNullOrEmpty(modelId) && entity.PremiumQuotaLimit.HasValue)
|
||||||
PremiumPackageConst.ModeIds.Contains(modelId) &&
|
|
||||||
entity.PremiumQuotaLimit.HasValue)
|
|
||||||
{
|
{
|
||||||
var usedQuota = await GetTokenPremiumUsedQuotaAsync(entity.UserId, entity.Id);
|
var isPremium = await _aiModelRepository._DbQueryable
|
||||||
if (usedQuota >= entity.PremiumQuotaLimit.Value)
|
.Where(x => x.ModelId == modelId)
|
||||||
|
.Select(x => x.IsPremium)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (isPremium)
|
||||||
{
|
{
|
||||||
throw new UserFriendlyException($"当前Token的尊享包额度已用完(已使用:{usedQuota},限制:{entity.PremiumQuotaLimit.Value}),请调整额度限制或使用其他Token", "403");
|
var usedQuota = await GetTokenPremiumUsedQuotaAsync(entity.UserId, entity.Id);
|
||||||
|
if (usedQuota >= entity.PremiumQuotaLimit.Value)
|
||||||
|
{
|
||||||
|
throw new UserFriendlyException($"当前Token的尊享包额度已用完(已使用:{usedQuota},限制:{entity.PremiumQuotaLimit.Value}),请调整额度限制或使用其他Token", "403");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +126,11 @@ public class TokenManager : DomainService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<long> GetTokenPremiumUsedQuotaAsync(Guid userId, Guid tokenId)
|
private async Task<long> GetTokenPremiumUsedQuotaAsync(Guid userId, Guid tokenId)
|
||||||
{
|
{
|
||||||
var premiumModelIds = PremiumPackageConst.ModeIds;
|
// 先获取所有尊享模型的ModelId列表
|
||||||
|
var premiumModelIds = await _aiModelRepository._DbQueryable
|
||||||
|
.Where(x => x.IsPremium)
|
||||||
|
.Select(x => x.ModelId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
var usedQuota = await _usageStatisticsRepository._DbQueryable
|
var usedQuota = await _usageStatisticsRepository._DbQueryable
|
||||||
.Where(x => x.UserId == userId && x.TokenId == tokenId && premiumModelIds.Contains(x.ModelId))
|
.Where(x => x.UserId == userId && x.TokenId == tokenId && premiumModelIds.Contains(x.ModelId))
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ namespace Yi.Abp.Web
|
|||||||
var app = context.GetApplicationBuilder();
|
var app = context.GetApplicationBuilder();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
//app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<AgentStoreAggregateRoot>();
|
//app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<AiModelEntity>();
|
||||||
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<ActivationCodeRecordAggregateRoot>();
|
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<ActivationCodeRecordAggregateRoot>();
|
||||||
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<UsageStatisticsAggregateRoot>();
|
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<UsageStatisticsAggregateRoot>();
|
||||||
|
|
||||||
|
|||||||
100
Yi.Ai.Vue3/src/api/channel/index.ts
Normal file
100
Yi.Ai.Vue3/src/api/channel/index.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { del, get, post, put } from '@/utils/request';
|
||||||
|
import type {
|
||||||
|
AiAppDto,
|
||||||
|
AiAppCreateInput,
|
||||||
|
AiAppUpdateInput,
|
||||||
|
AiAppGetListInput,
|
||||||
|
AiModelDto,
|
||||||
|
AiModelCreateInput,
|
||||||
|
AiModelUpdateInput,
|
||||||
|
AiModelGetListInput,
|
||||||
|
PagedResultDto,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
// ==================== AI应用管理 ====================
|
||||||
|
|
||||||
|
// 获取AI应用列表
|
||||||
|
export function getAppList(params?: AiAppGetListInput) {
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
if (params?.searchKey) {
|
||||||
|
queryParams.append('SearchKey', params.searchKey);
|
||||||
|
}
|
||||||
|
if (params?.skipCount !== undefined) {
|
||||||
|
queryParams.append('SkipCount', params.skipCount.toString());
|
||||||
|
}
|
||||||
|
if (params?.maxResultCount !== undefined) {
|
||||||
|
queryParams.append('MaxResultCount', params.maxResultCount.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryString = queryParams.toString();
|
||||||
|
const url = queryString ? `/channel/app?${queryString}` : '/channel/app';
|
||||||
|
|
||||||
|
return get<PagedResultDto<AiAppDto>>(url).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据ID获取AI应用
|
||||||
|
export function getAppById(id: string) {
|
||||||
|
return get<AiAppDto>(`/channel/app/${id}`).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AI应用
|
||||||
|
export function createApp(data: AiAppCreateInput) {
|
||||||
|
return post<AiAppDto>('/channel/app', data).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新AI应用
|
||||||
|
export function updateApp(data: AiAppUpdateInput) {
|
||||||
|
return put<AiAppDto>('/channel/app', data).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除AI应用
|
||||||
|
export function deleteApp(id: string) {
|
||||||
|
return del(`/channel/app/${id}`).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== AI模型管理 ====================
|
||||||
|
|
||||||
|
// 获取AI模型列表
|
||||||
|
export function getModelList(params?: AiModelGetListInput) {
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
if (params?.searchKey) {
|
||||||
|
queryParams.append('SearchKey', params.searchKey);
|
||||||
|
}
|
||||||
|
if (params?.aiAppId) {
|
||||||
|
queryParams.append('AiAppId', params.aiAppId);
|
||||||
|
}
|
||||||
|
if (params?.isPremiumOnly !== undefined) {
|
||||||
|
queryParams.append('IsPremiumOnly', params.isPremiumOnly.toString());
|
||||||
|
}
|
||||||
|
if (params?.skipCount !== undefined) {
|
||||||
|
queryParams.append('SkipCount', params.skipCount.toString());
|
||||||
|
}
|
||||||
|
if (params?.maxResultCount !== undefined) {
|
||||||
|
queryParams.append('MaxResultCount', params.maxResultCount.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryString = queryParams.toString();
|
||||||
|
const url = queryString ? `/channel/model?${queryString}` : '/channel/model';
|
||||||
|
|
||||||
|
return get<PagedResultDto<AiModelDto>>(url).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据ID获取AI模型
|
||||||
|
export function getModelById(id: string) {
|
||||||
|
return get<AiModelDto>(`/channel/model/${id}`).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AI模型
|
||||||
|
export function createModel(data: AiModelCreateInput) {
|
||||||
|
return post<AiModelDto>('/channel/model', data).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新AI模型
|
||||||
|
export function updateModel(data: AiModelUpdateInput) {
|
||||||
|
return put<AiModelDto>('/channel/model', data).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除AI模型
|
||||||
|
export function deleteModel(id: string) {
|
||||||
|
return del(`/channel/model/${id}`).json();
|
||||||
|
}
|
||||||
121
Yi.Ai.Vue3/src/api/channel/types.ts
Normal file
121
Yi.Ai.Vue3/src/api/channel/types.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// 模型类型枚举
|
||||||
|
export enum ModelTypeEnum {
|
||||||
|
Chat = 0,
|
||||||
|
Image = 1,
|
||||||
|
Embedding = 2,
|
||||||
|
PremiumChat = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模型API类型枚举
|
||||||
|
export enum ModelApiTypeEnum {
|
||||||
|
OpenAi = 0,
|
||||||
|
Claude = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI应用DTO
|
||||||
|
export interface AiAppDto {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
endpoint: string;
|
||||||
|
extraUrl?: string;
|
||||||
|
apiKey: string;
|
||||||
|
orderNum: number;
|
||||||
|
creationTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AI应用输入
|
||||||
|
export interface AiAppCreateInput {
|
||||||
|
name: string;
|
||||||
|
endpoint: string;
|
||||||
|
extraUrl?: string;
|
||||||
|
apiKey: string;
|
||||||
|
orderNum: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新AI应用输入
|
||||||
|
export interface AiAppUpdateInput {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
endpoint: string;
|
||||||
|
extraUrl?: string;
|
||||||
|
apiKey: string;
|
||||||
|
orderNum: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取AI应用列表输入
|
||||||
|
export interface AiAppGetListInput {
|
||||||
|
searchKey?: string;
|
||||||
|
skipCount?: number;
|
||||||
|
maxResultCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI模型DTO
|
||||||
|
export interface AiModelDto {
|
||||||
|
id: string;
|
||||||
|
handlerName: string;
|
||||||
|
modelId: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
orderNum: number;
|
||||||
|
aiAppId: string;
|
||||||
|
extraInfo?: string;
|
||||||
|
modelType: ModelTypeEnum;
|
||||||
|
modelApiType: ModelApiTypeEnum;
|
||||||
|
multiplier: number;
|
||||||
|
multiplierShow: number;
|
||||||
|
providerName?: string;
|
||||||
|
iconUrl?: string;
|
||||||
|
isPremium: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建AI模型输入
|
||||||
|
export interface AiModelCreateInput {
|
||||||
|
handlerName: string;
|
||||||
|
modelId: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
orderNum: number;
|
||||||
|
aiAppId: string;
|
||||||
|
extraInfo?: string;
|
||||||
|
modelType: ModelTypeEnum;
|
||||||
|
modelApiType: ModelApiTypeEnum;
|
||||||
|
multiplier: number;
|
||||||
|
multiplierShow: number;
|
||||||
|
providerName?: string;
|
||||||
|
iconUrl?: string;
|
||||||
|
isPremium: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新AI模型输入
|
||||||
|
export interface AiModelUpdateInput {
|
||||||
|
id: string;
|
||||||
|
handlerName: string;
|
||||||
|
modelId: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
orderNum: number;
|
||||||
|
aiAppId: string;
|
||||||
|
extraInfo?: string;
|
||||||
|
modelType: ModelTypeEnum;
|
||||||
|
modelApiType: ModelApiTypeEnum;
|
||||||
|
multiplier: number;
|
||||||
|
multiplierShow: number;
|
||||||
|
providerName?: string;
|
||||||
|
iconUrl?: string;
|
||||||
|
isPremium: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取AI模型列表输入
|
||||||
|
export interface AiModelGetListInput {
|
||||||
|
searchKey?: string;
|
||||||
|
aiAppId?: string;
|
||||||
|
isPremiumOnly?: boolean;
|
||||||
|
skipCount?: number;
|
||||||
|
maxResultCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页结果
|
||||||
|
export interface PagedResultDto<T> {
|
||||||
|
items: T[];
|
||||||
|
totalCount: number;
|
||||||
|
}
|
||||||
550
Yi.Ai.Vue3/src/pages/console/channel/index.vue
Normal file
550
Yi.Ai.Vue3/src/pages/console/channel/index.vue
Normal file
@@ -0,0 +1,550 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import { Delete, Edit, Plus, Refresh, View } from '@element-plus/icons-vue';
|
||||||
|
import type { AiAppDto, AiModelDto } from '@/api/channel/types';
|
||||||
|
import {
|
||||||
|
getAppList,
|
||||||
|
createApp,
|
||||||
|
updateApp,
|
||||||
|
deleteApp,
|
||||||
|
getModelList,
|
||||||
|
createModel,
|
||||||
|
updateModel,
|
||||||
|
deleteModel,
|
||||||
|
} from '@/api/channel';
|
||||||
|
|
||||||
|
// ==================== 应用管理 ====================
|
||||||
|
const appList = ref<AiAppDto[]>([]);
|
||||||
|
const appLoading = ref(false);
|
||||||
|
const selectedAppId = ref<string>('');
|
||||||
|
|
||||||
|
// 应用对话框
|
||||||
|
const appDialogVisible = ref(false);
|
||||||
|
const appDialogTitle = ref('');
|
||||||
|
const appForm = ref<Partial<AiAppDto>>({});
|
||||||
|
const appDetailDialogVisible = ref(false);
|
||||||
|
const appDetailData = ref<AiAppDto | null>(null);
|
||||||
|
|
||||||
|
// 获取应用列表
|
||||||
|
async function fetchAppList() {
|
||||||
|
appLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getAppList({
|
||||||
|
skipCount: 0,
|
||||||
|
maxResultCount: 100,
|
||||||
|
});
|
||||||
|
appList.value = res.data.items;
|
||||||
|
|
||||||
|
// 默认选中第一个应用
|
||||||
|
if (appList.value.length > 0 && !selectedAppId.value) {
|
||||||
|
selectedAppId.value = appList.value[0].id;
|
||||||
|
fetchModelList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
ElMessage.error(error.message || '获取应用列表失败');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
appLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择应用
|
||||||
|
function handleSelectApp(appId: string) {
|
||||||
|
selectedAppId.value = appId;
|
||||||
|
fetchModelList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看应用详情
|
||||||
|
function handleViewAppDetail(app: AiAppDto) {
|
||||||
|
appDetailData.value = app;
|
||||||
|
appDetailDialogVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开应用对话框
|
||||||
|
function openAppDialog(type: 'create' | 'edit', row?: AiAppDto) {
|
||||||
|
appDialogTitle.value = type === 'create' ? '创建应用' : '编辑应用';
|
||||||
|
if (type === 'create') {
|
||||||
|
appForm.value = {
|
||||||
|
name: '',
|
||||||
|
endpoint: '',
|
||||||
|
extraUrl: '',
|
||||||
|
apiKey: '',
|
||||||
|
orderNum: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appForm.value = { ...row };
|
||||||
|
}
|
||||||
|
appDialogVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存应用
|
||||||
|
async function saveApp() {
|
||||||
|
try {
|
||||||
|
if (appForm.value.id) {
|
||||||
|
await updateApp(appForm.value as any);
|
||||||
|
ElMessage.success('更新成功');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await createApp(appForm.value as any);
|
||||||
|
ElMessage.success('创建成功');
|
||||||
|
}
|
||||||
|
appDialogVisible.value = false;
|
||||||
|
fetchAppList();
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
ElMessage.error(error.message || '保存失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除应用
|
||||||
|
async function handleDeleteApp(row: AiAppDto) {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm('确定要删除该应用吗?删除后该应用下的所有模型将无法使用。', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
await deleteApp(row.id);
|
||||||
|
ElMessage.success('删除成功');
|
||||||
|
|
||||||
|
// 如果删除的是当前选中的应用,清空选中状态
|
||||||
|
if (selectedAppId.value === row.id) {
|
||||||
|
selectedAppId.value = '';
|
||||||
|
modelList.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAppList();
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
ElMessage.error(error.message || '删除失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 模型管理 ====================
|
||||||
|
const modelList = ref<AiModelDto[]>([]);
|
||||||
|
const modelLoading = ref(false);
|
||||||
|
const modelSearchKey = ref('');
|
||||||
|
const modelDialogVisible = ref(false);
|
||||||
|
const modelDialogTitle = ref('');
|
||||||
|
const modelForm = ref<Partial<AiModelDto>>({});
|
||||||
|
|
||||||
|
// 获取模型列表
|
||||||
|
async function fetchModelList() {
|
||||||
|
if (!selectedAppId.value) {
|
||||||
|
modelList.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modelLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getModelList({
|
||||||
|
aiAppId: selectedAppId.value,
|
||||||
|
searchKey: modelSearchKey.value,
|
||||||
|
skipCount: 0,
|
||||||
|
maxResultCount: 100,
|
||||||
|
});
|
||||||
|
modelList.value = res.data.items;
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
ElMessage.error(error.message || '获取模型列表失败');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
modelLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开模型对话框
|
||||||
|
function openModelDialog(type: 'create' | 'edit', row?: AiModelDto) {
|
||||||
|
if (!selectedAppId.value) {
|
||||||
|
ElMessage.warning('请先选择一个应用');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modelDialogTitle.value = type === 'create' ? '创建模型' : '编辑模型';
|
||||||
|
if (type === 'create') {
|
||||||
|
modelForm.value = {
|
||||||
|
handlerName: '',
|
||||||
|
modelId: '',
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
orderNum: 0,
|
||||||
|
aiAppId: selectedAppId.value,
|
||||||
|
extraInfo: '',
|
||||||
|
modelType: 0,
|
||||||
|
modelApiType: 0,
|
||||||
|
multiplier: 1,
|
||||||
|
multiplierShow: 1,
|
||||||
|
providerName: '',
|
||||||
|
iconUrl: '',
|
||||||
|
isPremium: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
modelForm.value = { ...row };
|
||||||
|
}
|
||||||
|
modelDialogVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存模型
|
||||||
|
async function saveModel() {
|
||||||
|
try {
|
||||||
|
if (modelForm.value.id) {
|
||||||
|
await updateModel(modelForm.value as any);
|
||||||
|
ElMessage.success('更新成功');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await createModel(modelForm.value as any);
|
||||||
|
ElMessage.success('创建成功');
|
||||||
|
}
|
||||||
|
modelDialogVisible.value = false;
|
||||||
|
fetchModelList();
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
ElMessage.error(error.message || '保存失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除模型
|
||||||
|
async function handleDeleteModel(row: AiModelDto) {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm('确定要删除该模型吗?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
await deleteModel(row.id);
|
||||||
|
ElMessage.success('删除成功');
|
||||||
|
fetchModelList();
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
ElMessage.error(error.message || '删除失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
fetchAppList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="channel-management">
|
||||||
|
<div class="channel-container">
|
||||||
|
<!-- 左侧应用列表 -->
|
||||||
|
<div class="app-list-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<h3>应用列表</h3>
|
||||||
|
<el-button type="primary" size="small" :icon="Plus" @click="openAppDialog('create')">
|
||||||
|
新建
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-scrollbar class="app-list-scrollbar">
|
||||||
|
<div v-loading="appLoading" class="app-list">
|
||||||
|
<div
|
||||||
|
v-for="app in appList"
|
||||||
|
:key="app.id"
|
||||||
|
class="app-item"
|
||||||
|
:class="{ active: selectedAppId === app.id }"
|
||||||
|
@click="handleSelectApp(app.id)"
|
||||||
|
>
|
||||||
|
<div class="app-item-content">
|
||||||
|
<div class="app-name">{{ app.name }}</div>
|
||||||
|
<div class="app-actions">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
:icon="View"
|
||||||
|
@click.stop="handleViewAppDetail(app)"
|
||||||
|
>
|
||||||
|
详情
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
:icon="Edit"
|
||||||
|
@click.stop="openAppDialog('edit', app)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
:icon="Delete"
|
||||||
|
@click.stop="handleDeleteApp(app)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-empty v-if="!appLoading && appList.length === 0" description="暂无应用" />
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧模型列表 -->
|
||||||
|
<div class="model-list-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<h3>模型列表</h3>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-input
|
||||||
|
v-model="modelSearchKey"
|
||||||
|
placeholder="搜索模型"
|
||||||
|
style="width: 200px; margin-right: 10px"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="fetchModelList"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" size="small" :icon="Plus" @click="openModelDialog('create')">
|
||||||
|
新建
|
||||||
|
</el-button>
|
||||||
|
<el-button size="small" :icon="Refresh" @click="fetchModelList">
|
||||||
|
刷新
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!selectedAppId" class="empty-tip">
|
||||||
|
<el-empty description="请先选择左侧的应用" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
v-else
|
||||||
|
v-loading="modelLoading"
|
||||||
|
:data="modelList"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
height="calc(100vh - 220px)"
|
||||||
|
>
|
||||||
|
<el-table-column prop="name" label="模型名称" min-width="150" />
|
||||||
|
<el-table-column prop="modelId" label="模型ID" min-width="200" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="handlerName" label="处理名" min-width="120" />
|
||||||
|
<el-table-column prop="providerName" label="供应商" width="100" />
|
||||||
|
<el-table-column label="是否尊享" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.isPremium ? 'warning' : 'info'">
|
||||||
|
{{ row.isPremium ? '尊享' : '普通' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="multiplier" label="模型倍率" width="100" />
|
||||||
|
<el-table-column prop="multiplierShow" label="显示倍率" width="100" />
|
||||||
|
<el-table-column prop="orderNum" label="排序" width="80" />
|
||||||
|
<el-table-column label="操作" width="150" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" :icon="Edit" @click="openModelDialog('edit', row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="danger" :icon="Delete" @click="handleDeleteModel(row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 应用详情对话框 -->
|
||||||
|
<el-dialog v-model="appDetailDialogVisible" title="应用详情" width="600px">
|
||||||
|
<el-descriptions v-if="appDetailData" :column="1" border>
|
||||||
|
<el-descriptions-item label="应用名称">{{ appDetailData.name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="终结点">{{ appDetailData.endpoint }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="额外URL">{{ appDetailData.extraUrl || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="API Key">
|
||||||
|
<el-input :model-value="appDetailData.apiKey" type="textarea" readonly />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="排序">{{ appDetailData.orderNum }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">{{ appDetailData.creationTime }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 应用编辑对话框 -->
|
||||||
|
<el-dialog v-model="appDialogVisible" :title="appDialogTitle" width="600px">
|
||||||
|
<el-form :model="appForm" label-width="120px">
|
||||||
|
<el-form-item label="应用名称" required>
|
||||||
|
<el-input v-model="appForm.name" placeholder="请输入应用名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="终结点" required>
|
||||||
|
<el-input v-model="appForm.endpoint" placeholder="请输入应用终结点URL" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="额外URL">
|
||||||
|
<el-input v-model="appForm.extraUrl" placeholder="请输入额外URL(可选)" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API Key" required>
|
||||||
|
<el-input v-model="appForm.apiKey" type="textarea" placeholder="请输入API Key" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序">
|
||||||
|
<el-input-number v-model="appForm.orderNum" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="appDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="saveApp">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 模型编辑对话框 -->
|
||||||
|
<el-dialog v-model="modelDialogVisible" :title="modelDialogTitle" width="700px">
|
||||||
|
<el-form :model="modelForm" label-width="120px">
|
||||||
|
<el-form-item label="模型名称" required>
|
||||||
|
<el-input v-model="modelForm.name" placeholder="请输入模型名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="模型ID" required>
|
||||||
|
<el-input v-model="modelForm.modelId" placeholder="请输入模型ID" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="处理名" required>
|
||||||
|
<el-input v-model="modelForm.handlerName" placeholder="请输入处理名" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="供应商名称">
|
||||||
|
<el-input v-model="modelForm.providerName" placeholder="如:OpenAI、Anthropic等" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="模型描述">
|
||||||
|
<el-input v-model="modelForm.description" type="textarea" placeholder="请输入模型描述" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="是否尊享模型">
|
||||||
|
<el-switch v-model="modelForm.isPremium" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="模型倍率">
|
||||||
|
<el-input-number v-model="modelForm.multiplier" :min="0.01" :step="0.1" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="显示倍率">
|
||||||
|
<el-input-number v-model="modelForm.multiplierShow" :min="0.01" :step="0.1" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="模型类型" required>
|
||||||
|
<el-select v-model="modelForm.modelType" placeholder="请选择模型类型">
|
||||||
|
<el-option label="聊天" :value="0" />
|
||||||
|
<el-option label="图片" :value="1" />
|
||||||
|
<el-option label="嵌入" :value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API类型" required>
|
||||||
|
<el-select v-model="modelForm.modelApiType" placeholder="请选择API类型">
|
||||||
|
<el-option label="OpenAI" :value="0" />
|
||||||
|
<el-option label="Claude" :value="1" />
|
||||||
|
<el-option label="Response" :value="2" />
|
||||||
|
<el-option label="GenerateContent" :value="3" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="图标URL">
|
||||||
|
<el-input v-model="modelForm.iconUrl" placeholder="请输入模型图标URL" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序">
|
||||||
|
<el-input-number v-model="modelForm.orderNum" :min="0" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="modelDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="saveModel">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.channel-management {
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.channel-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-list-panel {
|
||||||
|
width: 350px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-list-panel {
|
||||||
|
flex: 1;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-list-scrollbar {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-list {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-item {
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
background: #f0f9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #409eff;
|
||||||
|
background: #ecf5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-item-content {
|
||||||
|
.app-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -19,6 +19,7 @@ const navItems = [
|
|||||||
{ name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' },
|
{ name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' },
|
||||||
{ name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' },
|
{ name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' },
|
||||||
{ name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' },
|
{ name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' },
|
||||||
|
{ name: 'channel', label: '渠道商管理', icon: 'Setting', path: '/console/channel' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 当前激活的菜单
|
// 当前激活的菜单
|
||||||
|
|||||||
@@ -207,6 +207,14 @@ export const layoutRouter: RouteRecordRaw[] = [
|
|||||||
title: '激活码兑换',
|
title: '激活码兑换',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'channel',
|
||||||
|
name: 'consoleChannel',
|
||||||
|
component: () => import('@/pages/console/channel/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '渠道商管理',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
@@ -7,7 +7,6 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_WEB_BASE_API: string;
|
readonly VITE_WEB_BASE_API: string;
|
||||||
readonly VITE_API_URL: string;
|
readonly VITE_API_URL: string;
|
||||||
readonly VITE_FILE_UPLOAD_API: string;
|
readonly VITE_FILE_UPLOAD_API: string;
|
||||||
readonly VITE_BUILD_COMPRESS: string;
|
|
||||||
readonly VITE_SSO_SEVER_URL: string;
|
readonly VITE_SSO_SEVER_URL: string;
|
||||||
readonly VITE_APP_VERSION: string;
|
readonly VITE_APP_VERSION: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user