using System.Text.Json; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.BackgroundJobs; using Volo.Abp.Guids; using Volo.Abp.Users; using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat; using Yi.Framework.AiHub.Application.Jobs; using Yi.Framework.AiHub.Domain.Entities.Chat; 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.Enums; using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.AiHub.Application.Services.Chat; /// /// AI图片生成服务 /// [Authorize] public class AiImageService : ApplicationService { private readonly ISqlSugarRepository _imageTaskRepository; private readonly IBackgroundJobManager _backgroundJobManager; private readonly AiBlacklistManager _aiBlacklistManager; private readonly PremiumPackageManager _premiumPackageManager; private readonly ModelManager _modelManager; private readonly IGuidGenerator _guidGenerator; private readonly IWebHostEnvironment _webHostEnvironment; private readonly TokenManager _tokenManager; public AiImageService( ISqlSugarRepository imageTaskRepository, IBackgroundJobManager backgroundJobManager, AiBlacklistManager aiBlacklistManager, PremiumPackageManager premiumPackageManager, ModelManager modelManager, IGuidGenerator guidGenerator, IWebHostEnvironment webHostEnvironment, TokenManager tokenManager) { _imageTaskRepository = imageTaskRepository; _backgroundJobManager = backgroundJobManager; _aiBlacklistManager = aiBlacklistManager; _premiumPackageManager = premiumPackageManager; _modelManager = modelManager; _guidGenerator = guidGenerator; _webHostEnvironment = webHostEnvironment; _tokenManager = tokenManager; } /// /// 生成图片(异步任务) /// /// 图片生成输入参数 /// 任务ID [HttpPost("ai-image/generate")] [Authorize] public async Task GenerateAsync([FromBody] ImageGenerationInput input) { var userId = CurrentUser.GetId(); // 黑名单校验 await _aiBlacklistManager.VerifiyAiBlacklist(userId); //校验token if (input.TokenId is not null) { await _tokenManager.ValidateTokenAsync(input.TokenId, input.ModelId); } // VIP校验 if (!CurrentUser.IsAiVip()) { throw new UserFriendlyException("图片生成功能需要VIP用户才能使用,请购买VIP后重新登录重试"); } // 尊享包校验 - 使用ModelManager统一判断 var isPremium = await _modelManager.IsPremiumModelAsync(input.ModelId); if (isPremium) { var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId); if (availableTokens <= 0) { throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); } } // 创建任务实体 var task = new ImageStoreTaskAggregateRoot { Prompt = input.Prompt, ReferenceImagesPrefixBase64 = input.ReferenceImagesPrefixBase64 ?? new List(), ReferenceImagesUrl = new List(), TaskStatus = TaskStatusEnum.Processing, UserId = userId, TokenId = input.TokenId }; await _imageTaskRepository.InsertAsync(task); // 入队后台任务 await _backgroundJobManager.EnqueueAsync(new ImageGenerationJobArgs { TaskId = task.Id, }); return task.Id; } /// /// 查询任务状态 /// /// 任务ID /// 任务详情 [HttpGet("ai-image/task/{taskId}")] public async Task GetTaskAsync([FromRoute] Guid taskId) { var userId = CurrentUser.GetId(); var task = await _imageTaskRepository.GetFirstAsync(x => x.Id == taskId && x.UserId == userId); if (task == null) { throw new UserFriendlyException("任务不存在或无权访问"); } return new ImageTaskOutput { Id = task.Id, Prompt = task.Prompt, // ReferenceImagesBase64 = task.ReferenceImagesBase64, // ReferenceImagesUrl = task.ReferenceImagesUrl, // StoreBase64 = task.StoreBase64, StoreUrl = task.StoreUrl, TaskStatus = task.TaskStatus, CreationTime = task.CreationTime }; } /// /// 上传Base64图片转换为URL /// /// Base64图片数据(包含前缀如 data:image/png;base64,) /// 图片访问URL [HttpPost("ai-image/upload-base64")] public async Task UploadBase64ToUrlAsync([FromBody] string base64Data) { if (string.IsNullOrWhiteSpace(base64Data)) { throw new UserFriendlyException("Base64数据不能为空"); } // 解析Base64数据 string mimeType = "image/png"; string base64Content = base64Data; if (base64Data.Contains(",")) { var parts = base64Data.Split(','); if (parts.Length == 2) { // 提取MIME类型 var header = parts[0]; if (header.Contains(":") && header.Contains(";")) { mimeType = header.Split(':')[1].Split(';')[0]; } base64Content = parts[1]; } } // 获取文件扩展名 var extension = mimeType switch { "image/png" => ".png", "image/jpeg" => ".jpg", "image/jpg" => ".jpg", "image/gif" => ".gif", "image/webp" => ".webp", _ => ".png" }; // 解码Base64 byte[] imageBytes; try { imageBytes = Convert.FromBase64String(base64Content); } catch (FormatException) { throw new UserFriendlyException("Base64格式无效"); } // 创建存储目录 var uploadPath = Path.Combine(_webHostEnvironment.ContentRootPath, "wwwroot", "ai-images"); if (!Directory.Exists(uploadPath)) { Directory.CreateDirectory(uploadPath); } // 生成文件名并保存 var fileId = _guidGenerator.Create(); var fileName = $"{fileId}{extension}"; var filePath = Path.Combine(uploadPath, fileName); await File.WriteAllBytesAsync(filePath, imageBytes); // 返回访问URL return $"/ai-images/{fileName}"; } /// /// 分页查询任务列表 /// /// 分页查询参数 /// 任务列表 [HttpGet("ai-image/tasks")] public async Task> GetTaskPageAsync([FromQuery] ImageTaskPageInput input) { var userId = CurrentUser.GetId(); var query = _imageTaskRepository._DbQueryable .Where(x => x.UserId == userId) .WhereIF(input.TaskStatus.HasValue, x => x.TaskStatus == input.TaskStatus!.Value) .OrderByDescending(x => x.CreationTime); var total = await query.CountAsync(); var items = await query .Skip((input.PageIndex - 1) * input.PageSize) .Take(input.PageSize) .Select(x => new ImageTaskOutput { Id = x.Id, Prompt = x.Prompt, // ReferenceImagesBase64 = x.ReferenceImagesBase64, // ReferenceImagesUrl = x.ReferenceImagesUrl, // StoreBase64 = x.StoreBase64, StoreUrl = x.StoreUrl, TaskStatus = x.TaskStatus, CreationTime = x.CreationTime }) .ToListAsync(); return new PagedResult(total, items); } } /// /// 分页结果 /// /// 数据类型 public class PagedResult { /// /// 总数 /// public long Total { get; set; } /// /// 数据列表 /// public List Items { get; set; } public PagedResult(long total, List items) { Total = total; Items = items; } }