feat: 优化AI图片存储与访问流程
- 统一图片存储服务地址常量,返回完整可访问URL - 图片上传接口支持匿名访问,并按日期创建存储目录 - ImageStoreTask 移除无用生成图片 Base64 字段,调整大字段存储配置 - 创建图片任务时补充 ModelId 信息 - 优先使用 Authorization 头部,避免覆盖已有认证信息 - 前端补充 Element Plus Descriptions 组件类型声明
This commit is contained in:
@@ -98,7 +98,8 @@ public class AiImageService : ApplicationService
|
|||||||
ReferenceImagesUrl = new List<string>(),
|
ReferenceImagesUrl = new List<string>(),
|
||||||
TaskStatus = TaskStatusEnum.Processing,
|
TaskStatus = TaskStatusEnum.Processing,
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
TokenId = input.TokenId
|
TokenId = input.TokenId,
|
||||||
|
ModelId = input.ModelId
|
||||||
};
|
};
|
||||||
|
|
||||||
await _imageTaskRepository.InsertAsync(task);
|
await _imageTaskRepository.InsertAsync(task);
|
||||||
@@ -146,6 +147,7 @@ public class AiImageService : ApplicationService
|
|||||||
/// <param name="base64Data">Base64图片数据(包含前缀如 data:image/png;base64,)</param>
|
/// <param name="base64Data">Base64图片数据(包含前缀如 data:image/png;base64,)</param>
|
||||||
/// <returns>图片访问URL</returns>
|
/// <returns>图片访问URL</returns>
|
||||||
[HttpPost("ai-image/upload-base64")]
|
[HttpPost("ai-image/upload-base64")]
|
||||||
|
[AllowAnonymous]
|
||||||
public async Task<string> UploadBase64ToUrlAsync([FromBody] string base64Data)
|
public async Task<string> UploadBase64ToUrlAsync([FromBody] string base64Data)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(base64Data))
|
if (string.IsNullOrWhiteSpace(base64Data))
|
||||||
@@ -194,22 +196,31 @@ public class AiImageService : ApplicationService
|
|||||||
throw new UserFriendlyException("Base64格式无效");
|
throw new UserFriendlyException("Base64格式无效");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建存储目录
|
// ==============================
|
||||||
var uploadPath = Path.Combine(_webHostEnvironment.ContentRootPath, "wwwroot", "ai-images");
|
// ✅ 按日期创建目录(yyyyMMdd)
|
||||||
|
// ==============================
|
||||||
|
var dateFolder = DateTime.Now.ToString("yyyyMMdd");
|
||||||
|
var uploadPath = Path.Combine(
|
||||||
|
_webHostEnvironment.ContentRootPath,
|
||||||
|
"wwwroot",
|
||||||
|
"ai-images",
|
||||||
|
dateFolder
|
||||||
|
);
|
||||||
|
|
||||||
if (!Directory.Exists(uploadPath))
|
if (!Directory.Exists(uploadPath))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(uploadPath);
|
Directory.CreateDirectory(uploadPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成文件名并保存
|
// 保存文件
|
||||||
var fileId = _guidGenerator.Create();
|
var fileId = _guidGenerator.Create();
|
||||||
var fileName = $"{fileId}{extension}";
|
var fileName = $"{fileId}{extension}";
|
||||||
var filePath = Path.Combine(uploadPath, fileName);
|
var filePath = Path.Combine(uploadPath, fileName);
|
||||||
|
|
||||||
await File.WriteAllBytesAsync(filePath, imageBytes);
|
await File.WriteAllBytesAsync(filePath, imageBytes);
|
||||||
|
|
||||||
// 返回访问URL
|
// 返回包含日期目录的访问URL
|
||||||
return $"/ai-images/{fileName}";
|
return $"/wwwroot/ai-images/{dateFolder}/{fileName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class ImageStoreTaskAggregateRoot : FullAuditedAggregateRoot<Guid>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参考图PrefixBase64(带前缀,如 data:image/png;base64,xxx)
|
/// 参考图PrefixBase64(带前缀,如 data:image/png;base64,xxx)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(IsJson = true)]
|
[SugarColumn(IsJson = true,ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
||||||
public List<string> ReferenceImagesPrefixBase64 { get; set; }
|
public List<string> ReferenceImagesPrefixBase64 { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -26,11 +26,6 @@ public class ImageStoreTaskAggregateRoot : FullAuditedAggregateRoot<Guid>
|
|||||||
public List<string> ReferenceImagesUrl { get; set; }
|
public List<string> ReferenceImagesUrl { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 生成图片PrefixBase64(带前缀,如 data:image/png;base64,xxx)
|
|
||||||
/// </summary>
|
|
||||||
public string? StorePrefixBase64 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 图片绝对路径
|
/// 图片绝对路径
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -976,7 +976,7 @@ public class AiGateWayManager : DomainService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string ImageStoreHost = "http://localhost:19001/api/app";
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gemini 生成(Image)-非流式-缓存处理
|
/// Gemini 生成(Image)-非流式-缓存处理
|
||||||
/// 返回图片绝对路径
|
/// 返回图片绝对路径
|
||||||
@@ -1010,12 +1010,11 @@ public class AiGateWayManager : DomainService
|
|||||||
//远程调用上传接口,将base64转换为URL
|
//远程调用上传接口,将base64转换为URL
|
||||||
var httpClient = LazyServiceProvider.LazyGetRequiredService<IHttpClientFactory>().CreateClient();
|
var httpClient = LazyServiceProvider.LazyGetRequiredService<IHttpClientFactory>().CreateClient();
|
||||||
// var uploadUrl = $"https://ccnetcore.com/prod-api/ai-hub/ai-image/upload-base64";
|
// var uploadUrl = $"https://ccnetcore.com/prod-api/ai-hub/ai-image/upload-base64";
|
||||||
var uploadUrl = $"http://localhost:19001/api/app/ai-image/upload-base64";
|
var uploadUrl = $"{ImageStoreHost}/ai-image/upload-base64";
|
||||||
var content = new StringContent(JsonSerializer.Serialize(imageBase64), Encoding.UTF8, "application/json");
|
var content = new StringContent(JsonSerializer.Serialize(imageBase64), Encoding.UTF8, "application/json");
|
||||||
var uploadResponse = await httpClient.PostAsync(uploadUrl, content, cancellationToken);
|
var uploadResponse = await httpClient.PostAsync(uploadUrl, content, cancellationToken);
|
||||||
uploadResponse.EnsureSuccessStatusCode();
|
uploadResponse.EnsureSuccessStatusCode();
|
||||||
var storeUrl = await uploadResponse.Content.ReadAsStringAsync(cancellationToken);
|
var storeUrl = await uploadResponse.Content.ReadAsStringAsync(cancellationToken);
|
||||||
storeUrl = storeUrl.Trim('"'); // 移除JSON字符串的引号
|
|
||||||
|
|
||||||
var tokenUsage = new ThorUsageResponse
|
var tokenUsage = new ThorUsageResponse
|
||||||
{
|
{
|
||||||
@@ -1042,8 +1041,7 @@ public class AiGateWayManager : DomainService
|
|||||||
}
|
}
|
||||||
|
|
||||||
//设置存储base64和url
|
//设置存储base64和url
|
||||||
imageStoreTask.StorePrefixBase64 = imageBase64;
|
imageStoreTask.SetSuccess($"{ImageStoreHost}{storeUrl}");
|
||||||
imageStoreTask.SetSuccess(storeUrl);
|
|
||||||
await _imageStoreTaskRepository.UpdateAsync(imageStoreTask);
|
await _imageStoreTaskRepository.UpdateAsync(imageStoreTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ namespace Yi.Abp.Web
|
|||||||
//本地开发环境,可以禁用作业执行
|
//本地开发环境,可以禁用作业执行
|
||||||
if (host.IsDevelopment())
|
if (host.IsDevelopment())
|
||||||
{
|
{
|
||||||
Configure<AbpBackgroundWorkerOptions>(options => { options.IsEnabled = false; });
|
//Configure<AbpBackgroundWorkerOptions>(options => { options.IsEnabled = false; });
|
||||||
}
|
}
|
||||||
|
|
||||||
//请求日志
|
//请求日志
|
||||||
@@ -298,7 +298,8 @@ namespace Yi.Abp.Web
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (messageContext.Request.Cookies.TryGetValue("Token", out var cookiesToken))
|
if (!messageContext.Request.Headers.ContainsKey("Authorization") &&
|
||||||
|
messageContext.Request.Cookies.TryGetValue("Token", out var cookiesToken))
|
||||||
{
|
{
|
||||||
messageContext.Token = cookiesToken;
|
messageContext.Token = cookiesToken;
|
||||||
}
|
}
|
||||||
@@ -358,8 +359,8 @@ namespace Yi.Abp.Web
|
|||||||
var app = context.GetApplicationBuilder();
|
var app = context.GetApplicationBuilder();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
//app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<AiModelEntity>();
|
// app.ApplicationServices.GetRequiredService<ISqlSugarDbContext>().SqlSugarClient.CodeFirst.InitTables<ImageStoreTaskAggregateRoot>();
|
||||||
// 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>();
|
||||||
|
|
||||||
//跨域
|
//跨域
|
||||||
|
|||||||
2
Yi.Ai.Vue3/types/components.d.ts
vendored
2
Yi.Ai.Vue3/types/components.d.ts
vendored
@@ -25,6 +25,8 @@ declare module 'vue' {
|
|||||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
|
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||||
|
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
|
|||||||
Reference in New Issue
Block a user