新增提示词翻译 所有的提示词 中文翻译为英文!!!

This commit is contained in:
lq1405 2025-07-25 12:47:29 +08:00
parent ee299c8538
commit 1f71879e0f
20 changed files with 926 additions and 32 deletions

2
.gitignore vendored
View File

@ -361,3 +361,5 @@ MigrationBackup/
# Fody - auto-generated XML schema # Fody - auto-generated XML schema
FodyWeavers.xsd FodyWeavers.xsd
/src/Configuration/config/transfer.json
/src/Configuration/config/transfer.json

View File

@ -55,5 +55,50 @@ namespace lai_transfer.Common.Extensions
return fullPath; return fullPath;
} }
/// <summary>
/// 从HttpContext的RequestServices中获取服务
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <param name="httpContext">Http上下文</param>
/// <returns>服务实例如果未找到则返回null</returns>
public static T? GetService<T>(this HttpContext httpContext) where T : class
{
return httpContext.RequestServices.GetService<T>();
}
/// <summary>
/// 从HttpContext的RequestServices中获取必需的服务
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <param name="httpContext">Http上下文</param>
/// <returns>服务实例</returns>
/// <exception cref="InvalidOperationException">如果服务未注册则抛出异常</exception>
public static T GetRequiredService<T>(this HttpContext httpContext) where T : notnull
{
return httpContext.RequestServices.GetRequiredService<T>();
}
/// <summary>
/// 从HttpContext的RequestServices中获取服务非泛型版本
/// </summary>
/// <param name="httpContext">Http上下文</param>
/// <param name="serviceType">服务类型</param>
/// <returns>服务实例如果未找到则返回null</returns>
public static object? GetService(this HttpContext httpContext, Type serviceType)
{
return httpContext.RequestServices.GetService(serviceType);
}
/// <summary>
/// 从HttpContext的RequestServices中获取必需的服务非泛型版本
/// </summary>
/// <param name="httpContext">Http上下文</param>
/// <param name="serviceType">服务类型</param>
/// <returns>服务实例</returns>
/// <exception cref="InvalidOperationException">如果服务未注册则抛出异常</exception>
public static object GetRequiredService(this HttpContext httpContext, Type serviceType)
{
return httpContext.RequestServices.GetRequiredService(serviceType);
}
} }
} }

View File

@ -1,6 +1,4 @@
using lai_transfer.Common.Filters; using lai_transfer.Common.Filters;
using lai_transfer.Configuration;
using Microsoft.AspNetCore.Mvc.Filters;
namespace lai_transfer.Common.Extensions namespace lai_transfer.Common.Extensions
{ {

View File

@ -62,8 +62,6 @@
return TypedResults.Unauthorized(); return TypedResults.Unauthorized();
} }
} }
} }
} }
} }

View File

@ -6,14 +6,6 @@ namespace lai_transfer.Common.Helper
{ {
private static readonly ILogger _logger = LogHelper.GetLogger<ConfigHelper>(); private static readonly ILogger _logger = LogHelper.GetLogger<ConfigHelper>();
// 存储Origin配置
public static class Origin
{
// 将private set改为internal set允许同一程序集中的代码设置属性值
public static string BaseUrl { get; internal set; }
public static string Token { get; internal set; }
}
// 初始化配置 // 初始化配置
public static void Initialize() public static void Initialize()
{ {
@ -24,15 +16,105 @@ namespace lai_transfer.Common.Helper
// 读取配置文件 // 读取配置文件
var reader = new JsonConfigReader("Configuration/config/transfer.json"); var reader = new JsonConfigReader("Configuration/config/transfer.json");
// 加载Origin配置 // 初始化Origin配置
Origin.BaseUrl = reader.GetString("Origin.BaseUrl") ?? string.Empty; Origin.Init(reader);
Origin.Token = reader.GetString("Origin.Token") ?? string.Empty;
Translate.Init(reader);
_logger.LogInformation("配置加载完成"); _logger.LogInformation("配置加载完成");
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "加载配置文件失败"); _logger.LogError(ex, "加载配置文件失败");
// 报错,结束程序运行
throw new InvalidOperationException("无法加载应用程序配置,请检查配置文件是否存在或格式是否正确。", ex);
}
}
public static class Translate
{
// 将private set改为internal set允许同一程序集中的代码设置属性值
public static bool Enable { get; internal set; } = false;
// 安全模式 会优先百度 百度 报错 再使用OpenAI
public static string Model { get; internal set; } = "BAIDU";
public static string BaiduAppId { get; internal set; } = string.Empty;
public static string BaiduAppSecret { get; internal set; } = string.Empty;
public static string OpenaiGptApiUrl { get; internal set; } = string.Empty;
public static string OpenaiGptApiKey { get; internal set; } = string.Empty;
public static TimeSpan TimeOut { get; internal set; } = TimeSpan.FromSeconds(10);
public static string OpenaiGptModel { get; internal set; } = "gpt-4o-mini";
public static int OpenaiGptMaxTokens { get; internal set; } = 2048;
public static double OpenaiGptTemperature { get; internal set; } = 0;
/// <summary>
/// 初始化翻译配置
/// </summary>
public static void Init(JsonConfigReader reader)
{
try
{
_logger.LogInformation("正在加载翻译配置...");
// 加载翻译配置
Enable = reader.GetBool("Translate.Enable") ?? false;
Model = reader.GetString("Translate.Model") ?? "BAIDU";
BaiduAppId = reader.GetString("Translate.BaiduAppId") ?? string.Empty;
BaiduAppSecret = reader.GetString("Translate.BaiduAppSecret") ?? string.Empty;
OpenaiGptApiUrl = reader.GetString("Translate.OpenaiGptApiUrl") ?? "https://laitooo.net";
OpenaiGptApiKey = reader.GetString("Translate.OpenaiGptApiKey") ?? string.Empty;
OpenaiGptModel = reader.GetString("Translate.OpenaiGptModel") ?? "Doubao-lite-32k";
OpenaiGptMaxTokens = reader.GetInt("Translate.OpenaiGptMaxTokens") ?? 2048;
OpenaiGptTemperature = reader.GetDouble("Translate.OpenaiGptTemperature") ?? 0;
TimeOut = TimeSpan.FromSeconds(reader.GetInt("Translate.TimeOut") ?? 20);
_logger.LogInformation("翻译配置加载完成");
}
catch (Exception ex)
{
_logger.LogError(ex, "加载翻译配置失败");
// 报错,结束程序运行
throw new InvalidOperationException("无法加载翻译配置,请检查配置文件是否存在或格式是否正确。", ex);
}
}
}
// 存储Origin配置
public static class Origin
{
// 将private set改为internal set允许同一程序集中的代码设置属性值
public static string BaseUrl { get; internal set; } = string.Empty;
public static string Token { get; internal set; } = string.Empty;
/// <summary>
/// 初始化Origin配置
/// </summary>
public static void Init(JsonConfigReader reader)
{
try
{
_logger.LogInformation("正在加载Origin配置...");
// 加载Origin配置
BaseUrl = reader.GetString("Origin.BaseUrl") ?? string.Empty;
Token = reader.GetString("Origin.Token") ?? string.Empty;
_logger.LogInformation("Origin配置加载完成");
}
catch (Exception ex)
{
_logger.LogError(ex, "加载Origin配置失败");
// 报错,结束程序运行
throw new InvalidOperationException("无法加载Origin配置请检查配置文件是否存在或格式是否正确。", ex);
}
} }
} }
} }

View File

@ -34,5 +34,244 @@ namespace lai_transfer.Common.Helper
return Encoding.UTF8.GetString(stream.ToArray()); return Encoding.UTF8.GetString(stream.ToArray());
} }
/// <summary>
/// 获取指定属性的值
/// </summary>
/// <param name="element">JSON元素</param>
/// <param name="propertyName">属性名称</param>
/// <returns>属性值的JsonElement如果属性不存在则返回null</returns>
public static JsonElement? GetJsonProperty(JsonElement element, string propertyName)
{
// 检查元素是否为对象
if (element.ValueKind != JsonValueKind.Object)
return null;
// 尝试获取属性
if (element.TryGetProperty(propertyName, out JsonElement property))
{
return property;
}
return null;
}
/// <summary>
/// 获取指定属性的字符串值
/// </summary>
/// <param name="element">JSON元素</param>
/// <param name="propertyName">属性名称</param>
/// <returns>属性的字符串值如果属性不存在或无法转换则返回null</returns>
public static string? GetJsonPropertyString(JsonElement element, string propertyName)
{
var property = GetJsonProperty(element, propertyName);
return property?.GetString();
}
/// <summary>
/// 获取指定属性的整数值
/// </summary>
/// <param name="element">JSON元素</param>
/// <param name="propertyName">属性名称</param>
/// <returns>属性的整数值如果属性不存在或无法转换则返回null</returns>
public static int? GetJsonPropertyInt(JsonElement element, string propertyName)
{
var property = GetJsonProperty(element, propertyName);
if (property.HasValue && property.Value.TryGetInt32(out int value))
{
return value;
}
return null;
}
/// <summary>
/// 获取指定属性的布尔值
/// </summary>
/// <param name="element">JSON元素</param>
/// <param name="propertyName">属性名称</param>
/// <returns>属性的布尔值如果属性不存在或无法转换则返回null</returns>
public static bool? GetJsonPropertyBool(JsonElement element, string propertyName)
{
var property = GetJsonProperty(element, propertyName);
if (property.HasValue)
{
try
{
return property.Value.GetBoolean();
}
catch
{
return null;
}
}
return null;
}
/// <summary>
/// 设置或更新JSON对象中指定属性的值
/// </summary>
/// <param name="element">JSON元素</param>
/// <param name="propertyName">属性名称</param>
/// <param name="value">要设置的值</param>
/// <returns>更新后的JsonElement</returns>
public static JsonElement SetJsonProperty(JsonElement element, string propertyName, object? value)
{
// 检查元素是否为对象
if (element.ValueKind != JsonValueKind.Object)
return element;
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
writer.WriteStartObject();
bool propertyFound = false;
// 遍历所有现有属性
foreach (var property in element.EnumerateObject())
{
if (property.Name == propertyName)
{
// 写入新值
WritePropertyValue(writer, propertyName, value);
propertyFound = true;
}
else
{
// 保留现有属性
property.WriteTo(writer);
}
}
// 如果属性不存在,添加新属性
if (!propertyFound)
{
WritePropertyValue(writer, propertyName, value);
}
writer.WriteEndObject();
writer.Flush();
// 解析生成的JSON并返回JsonElement
var jsonString = Encoding.UTF8.GetString(stream.ToArray());
using var document = JsonDocument.Parse(jsonString);
return document.RootElement.Clone();
}
/// <summary>
/// 设置或更新JSON对象中指定属性的值返回JSON字符串
/// </summary>
/// <param name="element">JSON元素</param>
/// <param name="propertyName">属性名称</param>
/// <param name="value">要设置的值</param>
/// <returns>更新后的JSON字符串</returns>
public static string SetJsonPropertyAsString(JsonElement element, string propertyName, object? value)
{
var updatedElement = SetJsonProperty(element, propertyName, value);
return updatedElement.GetRawText();
}
/// <summary>
/// 设置多个属性的值
/// </summary>
/// <param name="element">JSON元素</param>
/// <param name="properties">属性名称和值的字典</param>
/// <returns>更新后的JSON字符串</returns>
public static string SetJsonProperties(JsonElement element, Dictionary<string, object?> properties)
{
// 检查元素是否为对象
if (element.ValueKind != JsonValueKind.Object)
return element.GetRawText();
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
writer.WriteStartObject();
var processedProperties = new HashSet<string>();
// 遍历所有现有属性
foreach (var property in element.EnumerateObject())
{
if (properties.ContainsKey(property.Name))
{
// 写入新值
WritePropertyValue(writer, property.Name, properties[property.Name]);
processedProperties.Add(property.Name);
}
else
{
// 保留现有属性
property.WriteTo(writer);
}
}
// 添加不存在的新属性
foreach (var kvp in properties)
{
if (!processedProperties.Contains(kvp.Key))
{
WritePropertyValue(writer, kvp.Key, kvp.Value);
}
}
writer.WriteEndObject();
writer.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
}
/// <summary>
/// 写入属性值到JSON writer
/// </summary>
/// <param name="writer">JSON writer</param>
/// <param name="propertyName">属性名称</param>
/// <param name="value">属性值</param>
private static void WritePropertyValue(Utf8JsonWriter writer, string propertyName, object? value)
{
writer.WritePropertyName(propertyName);
switch (value)
{
case null:
writer.WriteNullValue();
break;
case string stringValue:
writer.WriteStringValue(stringValue);
break;
case int intValue:
writer.WriteNumberValue(intValue);
break;
case long longValue:
writer.WriteNumberValue(longValue);
break;
case float floatValue:
writer.WriteNumberValue(floatValue);
break;
case double doubleValue:
writer.WriteNumberValue(doubleValue);
break;
case decimal decimalValue:
writer.WriteNumberValue(decimalValue);
break;
case bool boolValue:
writer.WriteBooleanValue(boolValue);
break;
case DateTime dateTimeValue:
writer.WriteStringValue(dateTimeValue.ToString("O")); // ISO 8601 format
break;
case JsonElement jsonElement:
jsonElement.WriteTo(writer);
break;
default:
{
// 对于复杂对象序列化为JSON字符串
var jsonString = JsonSerializer.Serialize(value);
using var doc = JsonDocument.Parse(jsonString);
doc.RootElement.WriteTo(writer);
break;
}
}
}
} }
} }

View File

@ -0,0 +1,83 @@
namespace lai_transfer.Common.Helper
{
/// <summary>
/// 全局服务定位器用于在没有HttpContext的地方获取依赖注入的服务
/// </summary>
public static class ServiceLocator
{
private static IServiceProvider? _serviceProvider;
/// <summary>
/// 初始化服务提供者在Program.cs中调用
/// </summary>
/// <param name="serviceProvider">服务提供者</param>
public static void Initialize(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
/// <summary>
/// 获取服务
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <returns>服务实例如果未找到则返回null</returns>
public static T? GetService<T>() where T : class
{
EnsureInitialized();
return _serviceProvider!.GetService<T>();
}
/// <summary>
/// 获取必需的服务
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <returns>服务实例</returns>
/// <exception cref="InvalidOperationException">如果服务未注册则抛出异常</exception>
public static T GetRequiredService<T>() where T : notnull
{
EnsureInitialized();
return _serviceProvider!.GetRequiredService<T>();
}
/// <summary>
/// 获取服务(非泛型版本)
/// </summary>
/// <param name="serviceType">服务类型</param>
/// <returns>服务实例如果未找到则返回null</returns>
public static object? GetService(Type serviceType)
{
EnsureInitialized();
return _serviceProvider!.GetService(serviceType);
}
/// <summary>
/// 获取必需的服务(非泛型版本)
/// </summary>
/// <param name="serviceType">服务类型</param>
/// <returns>服务实例</returns>
/// <exception cref="InvalidOperationException">如果服务未注册则抛出异常</exception>
public static object GetRequiredService(Type serviceType)
{
EnsureInitialized();
return _serviceProvider!.GetRequiredService(serviceType);
}
/// <summary>
/// 创建服务范围
/// </summary>
/// <returns>服务范围</returns>
public static IServiceScope CreateScope()
{
EnsureInitialized();
return _serviceProvider!.CreateScope();
}
private static void EnsureInitialized()
{
if (_serviceProvider == null)
{
throw new InvalidOperationException("ServiceLocator 未初始化。请在 Program.cs 中调用 ServiceLocator.Initialize(app.Services)");
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"Origin": {
"BaseUrl": "https://mjapi.bzu.cn",
"Token": "23830faf80e8e69"
},
"Translate": {
"Enable": true,
"Model": "BAIDU",
"BaiduAppId": "2024032500",
"BaiduAppSecret": "hcCG8H",
"OpenaiGptApiUrl": "https://laitool.net/v1/chat/completions",
"OpenaiGptApiKey": "sk-r5",
"OpenaiGptModel": "Doubao-pro-32k",
"OpenaiGptMaxTokens": 2048,
"OpenaiGptTemperature": 0,
"TimeOut": 20
}
}

View File

@ -1,6 +0,0 @@
{
"Origin": {
"BaseUrl": "https://mjapi.bzu.cn",
"Token": "23830faf80e8e69988b3fcd9aa08e9ad123"
}
}

View File

@ -1,12 +1,17 @@
using FluentValidation; using FluentValidation;
using lai_transfer.Configuration; using lai_transfer.Configuration;
using lai_transfer.Model.Entity; using lai_transfer.Model.Entity;
using lai_transfer.Services;
using lai_transfer.EndpointServices.MJTransferEndpoint;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Serilog; using Serilog;
using System.Text; using System.Text;
using Midjourney.Infrastructure.Services;
using lai_transfer.Common.Helper;
namespace lai_transfer namespace lai_transfer
{ {
@ -40,11 +45,31 @@ namespace lai_transfer
// 添加JWT认证配置 // 添加JWT认证配置
builder.Services.AddJWTAuthentication(); builder.Services.AddJWTAuthentication();
builder.Services.AddService();
// 注册校验 // 注册校验
builder.Services.AddValidatorsFromAssembly(typeof(ConfigureServices).Assembly); builder.Services.AddValidatorsFromAssembly(typeof(ConfigureServices).Assembly);
} }
/// <summary>
/// 注册一些服务
/// </summary>
/// <param name="services"></param>
private static void AddService(this IServiceCollection services)
{
if (ConfigHelper.Translate.Model == "GPT")
{
// 翻译服务注入
services.AddSingleton<ITranslateService, GPTTranslateService>();
}
else
{
services.AddSingleton<ITranslateService, BaiduTranslateService>();
}
}
/// <summary> /// <summary>
/// JWT 配置 /// JWT 配置
/// </summary> /// </summary>

View File

@ -220,7 +220,7 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
if (isPartner && !string.IsNullOrEmpty(partnerTaskId)) if (isPartner && !string.IsNullOrEmpty(partnerTaskId))
{ {
_logger.LogInformation($"处理合作伙伴任务: {partnerTaskId}"); _logger.LogInformation($"处理合作伙伴任务: {partnerTaskId}, TaskId : {data?.id ?? string.Empty}");
// 1111111111 // 1111111111
@ -279,7 +279,7 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
if (isOfficial && !string.IsNullOrEmpty(officialTaskId)) if (isOfficial && !string.IsNullOrEmpty(officialTaskId))
{ {
_logger.LogInformation($"处理官方任务: {officialTaskId}"); _logger.LogInformation($"处理官方任务: {officialTaskId}, TaskId : {data?.id ?? string.Empty}");
// 直接遍历,让异常处理兜底 // 直接遍历,让异常处理兜底
var imageUrls = new List<object>(); var imageUrls = new List<object>();
@ -336,7 +336,7 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
if (isYouChuan && !string.IsNullOrEmpty(youChuanTaskId)) if (isYouChuan && !string.IsNullOrEmpty(youChuanTaskId))
{ {
_logger.LogInformation($"处理悠船任务: {youChuanTaskId}"); _logger.LogInformation($"处理悠船任务: {youChuanTaskId}, TaskId : {data?.id ?? string.Empty}");
// 直接遍历,让异常处理兜底 // 直接遍历,让异常处理兜底
var imageUrls = new List<object>(); var imageUrls = new List<object>();

View File

@ -1,23 +1,26 @@
using lai_transfer.Common.Extensions; using lai_transfer.Common.Extensions;
using lai_transfer.Common.Helper; using lai_transfer.Common.Helper;
using lai_transfer.Common.Results; using lai_transfer.Common.Results;
using lai_transfer.Configuration;
using lai_transfer.Endpoints; using lai_transfer.Endpoints;
using lai_transfer.Services;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
namespace lai_transfer.EndpointServices.MJTransferEndpoint namespace lai_transfer.EndpointServices.MJTransferEndpoint
{ {
public class MJPostImagineService : IEndpoint public class MJPostImagineService : IEndpoint
{ {
private static readonly ILogger _logger = LogHelper.GetLogger<MJPostImagineService>();
public static void Map(IEndpointRouteBuilder app) => app public static void Map(IEndpointRouteBuilder app) => app
.MapPost("/submit/imagine", Handle) .MapPost("/submit/imagine", Handle)
.WithSummary("Midjourney 提交 Imagine 任务") .WithSummary("Midjourney 提交 Imagine 任务")
.WithMJAuthorizationHeader(); .WithMJAuthorizationHeader();
private static async Task<IResult> Handle(JsonElement model, HttpContext httpContext, ILogger<MJPostImagineService> logger) private static async Task<IResult> Handle(JsonElement model, HttpContext httpContext)
{ {
(string content, string contentType, int statusCode) = await SendOriginalImagine(model, httpContext, logger); (string content, string contentType, int statusCode) = await SendOriginalImagine(model, httpContext);
return Results.Text(content, contentType, statusCode: statusCode); return Results.Text(content, contentType, statusCode: statusCode);
} }
@ -27,7 +30,7 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
/// <param name="model"></param> /// <param name="model"></param>
/// <param name="httpContext"></param> /// <param name="httpContext"></param>
/// <returns></returns> /// <returns></returns>
private static async Task<(string content, string contentType, int statusCode)> SendOriginalImagine(JsonElement model, HttpContext httpContext, ILogger<MJPostImagineService> logger) private static async Task<(string content, string contentType, int statusCode)> SendOriginalImagine(JsonElement model, HttpContext httpContext)
{ {
try try
{ {
@ -39,9 +42,18 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {authorizationResult.Token}"); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {authorizationResult.Token}");
client.Timeout = Timeout.InfiniteTimeSpan; client.Timeout = Timeout.InfiniteTimeSpan;
// 删除回调参数 notifyHook // 强制翻译提示词
string prompt = JSONHelper.GetJsonPropertyString(model, "prompt") ?? string.Empty;
// 使用HttpContext扩展方法获取翻译服务
var translateService = ServiceLocator.GetRequiredService<ITranslateService>();
prompt = await TranslatePrompt(prompt, translateService);
model = JSONHelper.SetJsonProperty(model, "prompt", prompt);
// 删除回调参数 notifyHook
string body = JSONHelper.RemoveJsonProperties(model, "notifyHook"); string body = JSONHelper.RemoveJsonProperties(model, "notifyHook");
//return (body, "application/json", 200);
// 发送请求 // 发送请求
var response = await client.PostAsync(url, new StringContent(body, Encoding.UTF8, "application/json")); var response = await client.PostAsync(url, new StringContent(body, Encoding.UTF8, "application/json"));
string content = await response.Content.ReadAsStringAsync(); string content = await response.Content.ReadAsStringAsync();
@ -49,10 +61,81 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogError(ex, $"请求 {httpContext.GetFullRequestPath()} 失败"); _logger.LogError(ex, $"请求 {httpContext.GetFullRequestPath()} 失败");
// 处理异常,返回错误信息 // 处理异常,返回错误信息
return (ex.Message, "application/json", StatusCodes.Status500InternalServerError); return (ex.Message, "application/json", StatusCodes.Status500InternalServerError);
} }
} }
/// <summary>
/// 翻译提示词
/// </summary>
/// <param name="prompt">原始提示词</param>
/// <param name="translateService">翻译服务</param>
/// <returns>翻译后的提示词</returns>
public static async Task<string> TranslatePrompt(string prompt, ITranslateService translateService)
{
if (string.IsNullOrWhiteSpace(prompt))
return prompt;
try
{
// 检查是否启用翻译功能
if (!ConfigHelper.Translate.Enable)
return prompt;
// 如果不包含中文,直接返回
if (string.IsNullOrWhiteSpace(prompt) || !translateService.ContainsChinese(prompt))
return prompt;
string paramStr = "";
var paramMatcher = Regex.Match(prompt, "\\x20+--[a-z]+.*$", RegexOptions.IgnoreCase);
if (paramMatcher.Success)
{
paramStr = paramMatcher.Value;
}
string promptWithoutParam = prompt.Substring(0, prompt.Length - paramStr.Length);
List<string> imageUrls = new List<string>();
var imageMatcher = Regex.Matches(promptWithoutParam, "https?://[a-z0-9-_:@&?=+,.!/~*'%$]+\\x20+", RegexOptions.IgnoreCase);
foreach (Match match in imageMatcher)
{
imageUrls.Add(match.Value);
}
string text = promptWithoutParam;
foreach (string imageUrl in imageUrls)
{
text = text.Replace(imageUrl, "");
}
text = text.Trim();
if (!string.IsNullOrWhiteSpace(text))
{
text = await translateService.TranslateToEnglish(text);
}
if (!string.IsNullOrWhiteSpace(paramStr))
{
// 当有 --no 参数时, 翻译 --no 参数, 并替换原参数
// --sref https://mjcdn.googlec.cc/1.jpg --no aa, bb, cc
var paramNomatcher = Regex.Match(paramStr, "--no\\s+(.*?)(?=--|$)");
if (paramNomatcher.Success)
{
string paramNoStr = paramNomatcher.Groups[1].Value.Trim();
string paramNoStrEn = await translateService.TranslateToEnglish(paramNoStr);
// 提取 --no 之前的参数
paramStr = paramStr.Substring(0, paramNomatcher.Index);
// 替换 --no 参数
paramStr = paramStr + paramNomatcher.Result("--no " + paramNoStrEn + " ");
}
}
return string.Concat(imageUrls) + text.Trim() + paramStr;
}
catch (Exception ex)
{
LogHelper.LogError(ex, "翻译提示词失败,使用原始提示词");
return prompt;
}
}
} }
} }

View File

@ -2,6 +2,7 @@
using lai_transfer.Common.Helper; using lai_transfer.Common.Helper;
using lai_transfer.Common.Results; using lai_transfer.Common.Results;
using lai_transfer.Endpoints; using lai_transfer.Endpoints;
using lai_transfer.Services;
using System.Text.Json; using System.Text.Json;
namespace lai_transfer.EndpointServices.MJTransferEndpoint namespace lai_transfer.EndpointServices.MJTransferEndpoint
@ -30,6 +31,16 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
using HttpClient client = new HttpClient(); using HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {transferAuthorizationResult.Token}"); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {transferAuthorizationResult.Token}");
client.Timeout = Timeout.InfiniteTimeSpan; client.Timeout = Timeout.InfiniteTimeSpan;
// 强制翻译提示词
string prompt = JSONHelper.GetJsonPropertyString(model, "prompt") ?? string.Empty;
// 使用HttpContext扩展方法获取翻译服务
var translateService = ServiceLocator.GetRequiredService<ITranslateService>();
prompt = await MJPostImagineService.TranslatePrompt(prompt, translateService);
model = JSONHelper.SetJsonProperty(model, "prompt", prompt);
// 删除回调参数 notifyHook // 删除回调参数 notifyHook
string body = JSONHelper.RemoveJsonProperties(model, "notifyHook"); string body = JSONHelper.RemoveJsonProperties(model, "notifyHook");
// 发送请求 // 发送请求

View File

@ -9,7 +9,6 @@ namespace lai_transfer.EndpointServices.MJTransferEndpoint
{ {
public class MJPostUploadDiscordImages : IEndpoint public class MJPostUploadDiscordImages : IEndpoint
{ {
private static readonly ILogger _logger = LogHelper.GetLogger<MJPostUploadDiscordImages>(); private static readonly ILogger _logger = LogHelper.GetLogger<MJPostUploadDiscordImages>();
public static void Map(IEndpointRouteBuilder app) => app public static void Map(IEndpointRouteBuilder app) => app
.MapPost("/submit/upload-discord-images", Handle) .MapPost("/submit/upload-discord-images", Handle)

View File

@ -0,0 +1,88 @@
using lai_transfer.Common.Extensions;
using lai_transfer.Common.Helper;
using lai_transfer.Services;
using System.Threading.Tasks;
namespace lai_transfer.Examples
{
/// <summary>
/// 展示如何使用服务获取扩展方法的示例
/// </summary>
public static class ServiceUsageExamples
{
/// <summary>
/// 在有HttpContext的场景下使用服务
/// </summary>
/// <param name="httpContext">Http上下文</param>
public static async Task ExampleWithHttpContext(HttpContext httpContext)
{
// 方式1使用HttpContext扩展方法获取服务
var translateService = httpContext.GetRequiredService<ITranslateService>();
var loggerFactory = httpContext.GetService<ILoggerFactory>();
var logger = loggerFactory?.CreateLogger("ServiceUsageExamples");
// 使用服务
string result = await translateService.TranslateToEnglish("你好世界");
logger?.LogInformation($"翻译结果: {result}");
}
/// <summary>
/// 在没有HttpContext的场景下使用服务如静态方法、后台任务等
/// </summary>
public static async Task ExampleWithoutHttpContext()
{
// 方式2使用全局服务定位器获取服务
var translateService = ServiceLocator.GetRequiredService<ITranslateService>();
var loggerFactory = ServiceLocator.GetService<ILoggerFactory>();
var logger = loggerFactory?.CreateLogger("ServiceUsageExamples");
// 使用服务
string result = await translateService.TranslateToEnglish("你好世界");
logger?.LogInformation($"翻译结果: {result}");
}
/// <summary>
/// 在需要服务作用域的场景下使用
/// </summary>
public static async Task ExampleWithScope()
{
// 创建新的作用域
using var scope = ServiceLocator.CreateScope();
var scopedServices = scope.ServiceProvider;
// 从作用域中获取服务
var translateService = scopedServices.GetRequiredService<ITranslateService>();
// 使用服务
string result = await translateService.TranslateToEnglish("你好世界");
// 作用域结束时会自动释放资源
}
/// <summary>
/// 在端点处理器中的使用示例
/// </summary>
public static async Task<IResult> EndpointExample(HttpContext httpContext)
{
try
{
// 直接从HttpContext获取服务无需在方法参数中声明
var translateService = httpContext.GetRequiredService<ITranslateService>();
var loggerFactory = httpContext.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("ServiceUsageExamples");
// 业务逻辑
string prompt = "测试文本";
string translatedPrompt = await translateService.TranslateToEnglish(prompt);
logger.LogInformation($"翻译完成: {prompt} -> {translatedPrompt}");
return Results.Ok(new { original = prompt, translated = translatedPrompt });
}
catch (Exception ex)
{
return Results.Problem($"处理失败: {ex.Message}");
}
}
}
}

View File

@ -16,6 +16,9 @@ app.Configure();
LogHelper.Initialize(app.Services.GetRequiredService<ILoggerFactory>()); LogHelper.Initialize(app.Services.GetRequiredService<ILoggerFactory>());
ConfigHelper.Initialize(); ConfigHelper.Initialize();
// 初始化服务定位器
ServiceLocator.Initialize(app.Services);
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {

View File

@ -0,0 +1,104 @@

using lai_transfer.Common.Helper;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace lai_transfer.Services
{
/// <summary>
/// 百度翻译服务
/// </summary>
public class BaiduTranslateService : ITranslateService
{
private const string TRANSLATE_API = "https://fanyi-api.baidu.com/api/trans/vip/translate";
private static readonly ILogger _logger = LogHelper.GetLogger<BaiduTranslateService>();
public BaiduTranslateService()
{
}
public async Task<string> TranslateToEnglish(string prompt)
{
var appid = ConfigHelper.Translate.BaiduAppId;
var appSecret = ConfigHelper.Translate.BaiduAppSecret;
if (string.IsNullOrWhiteSpace(appid) || string.IsNullOrWhiteSpace(appSecret))
{
return prompt;
}
if (!ContainsChinese(prompt))
{
return prompt;
}
string salt = new Random().Next(10000, 99999).ToString();
string sign = ComputeMd5Hash(appid + prompt + salt + appSecret);
var body = new Dictionary<string, string>
{
{ "from", "zh" },
{ "to", "en" },
{ "appid", appid },
{ "salt", salt },
{ "q", prompt },
{ "sign", sign }
};
try
{
using (var client = new HttpClient())
{
var content = new FormUrlEncodedContent(body);
var response = await client.PostAsync(TRANSLATE_API, content);
if (!response.IsSuccessStatusCode || string.IsNullOrWhiteSpace(response.Content.ReadAsStringAsync().Result))
{
throw new InvalidOperationException($"{response.StatusCode} - {response.Content.ReadAsStringAsync().Result}");
}
var result = JsonDocument.Parse(response.Content.ReadAsStringAsync().Result);
if (result.RootElement.TryGetProperty("error_code", out var errorCode))
{
throw new InvalidOperationException($"{errorCode.GetString()} - {result.RootElement.GetProperty("error_msg").GetString()}");
}
var transResult = result.RootElement.GetProperty("trans_result").EnumerateArray();
var translatedStrings = new List<string>();
foreach (var item in transResult)
{
translatedStrings.Add(item.GetProperty("dst").GetString());
}
return string.Join("\n", translatedStrings);
}
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to call Baidu Translate");
}
return prompt;
}
private static string ComputeMd5Hash(string input)
{
using (var md5 = MD5.Create())
{
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
public bool ContainsChinese(string prompt)
{
// 匹配基本汉字区、扩展A区和部分扩展B区
string chinesePattern = @"[\u4e00-\u9fa5\u3400-\u4DBF]";
return Regex.IsMatch(prompt, chinesePattern);
}
}
}

View File

@ -0,0 +1,105 @@
using lai_transfer.Common.Helper;
using lai_transfer.Services;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace Midjourney.Infrastructure.Services
{
/// <summary>
/// OpenAI GPT翻译服务
/// </summary>
public class GPTTranslateService : ITranslateService
{
private const string TRANSLATE_API = "https://api.openai.com/v1/chat/completions";
private static readonly ILogger _logger = LogHelper.GetLogger<GPTTranslateService>();
private readonly string _apiUrl;
private readonly string _apiKey;
private readonly TimeSpan _timeout;
private readonly string _model;
private readonly int _maxTokens;
private readonly double _temperature;
private readonly HttpClient _httpClient;
public GPTTranslateService()
{
_apiUrl = ConfigHelper.Translate.OpenaiGptApiUrl;
_apiKey = ConfigHelper.Translate.OpenaiGptApiKey;
_timeout = ConfigHelper.Translate.TimeOut;
_model = ConfigHelper.Translate.OpenaiGptModel;
_maxTokens = ConfigHelper.Translate.OpenaiGptMaxTokens;
_temperature = ConfigHelper.Translate.OpenaiGptTemperature;
_httpClient = new HttpClient() { Timeout = _timeout };
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
}
public async Task<string> TranslateToEnglish(string prompt)
{
if (string.IsNullOrWhiteSpace(_apiKey) || string.IsNullOrWhiteSpace(_apiUrl))
{
return prompt;
}
if (!ContainsChinese(prompt))
{
return prompt;
}
var requestBody = new
{
model = _model,
stream = false,
messages = new[]
{
new { role = "system", content = "把中文翻译成英文" },
new { role = "user", content = prompt }
},
max_tokens = _maxTokens,
temperature = _temperature
};
try
{
var content = new StringContent(JsonSerializer.Serialize(requestBody), Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(_apiUrl, content);
if (!response.IsSuccessStatusCode || string.IsNullOrWhiteSpace(response.Content.ReadAsStringAsync().Result))
{
throw new InvalidOperationException($"{response.StatusCode} - {response.Content.ReadAsStringAsync().Result}");
}
var result = JsonDocument.Parse(response.Content.ReadAsStringAsync().Result);
var choices = result.RootElement.GetProperty("choices").EnumerateArray();
var translatedText = choices.First().GetProperty("message").GetProperty("content").GetString();
return translatedText?.Trim() ?? prompt;
}
catch (HttpRequestException e)
{
_logger.LogWarning(e, "HTTP request failed");
}
catch (JsonException e)
{
_logger.LogWarning(e, "Failed to parse JSON response");
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to call OpenAI Translate");
}
return prompt;
}
public bool ContainsChinese(string prompt)
{
// 匹配基本汉字区、扩展A区和部分扩展B区
string chinesePattern = @"[\u4e00-\u9fa5\u3400-\u4DBF]";
return Regex.IsMatch(prompt, chinesePattern);
}
}
}

View File

@ -0,0 +1,9 @@
namespace lai_transfer.Services
{
public interface ITranslateService
{
Task<string> TranslateToEnglish(string prompt);
bool ContainsChinese(string prompt);
}
}

View File

@ -95,6 +95,13 @@ namespace lai_transfer.Tool.Extensions
return section?.GetValue<int>(); return section?.GetValue<int>();
} }
// 添加一个 getdouble 方法
public double? GetDouble(string path, bool forceReload = false)
{
var section = GetSection(path, forceReload);
return section?.GetValue<double>();
}
/// <summary> /// <summary>
/// 获取指定路径的布尔值 /// 获取指定路径的布尔值
/// </summary> /// </summary>