99 lines
3.9 KiB
C#
99 lines
3.9 KiB
C#
using MAX.Framework.Commons;
|
|
using MAX.Framework.Dtos;
|
|
using MAX.Framework.Validation;
|
|
using MAX.Plugins;
|
|
using Microsoft.AspNetCore.WebUtilities;
|
|
using Store.Modules.CloudProduct.Dtos;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace Store.Modules.CloudProduct.Services;
|
|
|
|
/// <summary>调用外部网站的图片识别额度接口,单例复用 HTTP 连接。</summary>
|
|
public sealed class ImageAnalysisQuotaService : IDisposable
|
|
{
|
|
private readonly HttpClient client;
|
|
private readonly PluginContext<CloudProductPlugin> context;
|
|
private readonly string ApiKey;
|
|
/// <summary>从站点根地址创建客户端,定期更新连接以跟随 DNS 变化。</summary>
|
|
public ImageAnalysisQuotaService(PluginContext<CloudProductPlugin> context)
|
|
{
|
|
this.context = context;
|
|
var url = context.RequireConfiguration("ImageAnalysisQuota:Url");
|
|
ApiKey = context.RequireConfiguration("ImageAnalysisQuota:APIKey");
|
|
if (!Uri.TryCreate(url, UriKind.Absolute, out var endpoint) ||
|
|
(endpoint.Scheme != Uri.UriSchemeHttp && endpoint.Scheme != Uri.UriSchemeHttps))
|
|
throw new InvalidOperationException("ImageAnalysisQuota:Url 必须是 HTTP 或 HTTPS 站点地址。");
|
|
if (string.IsNullOrEmpty(ApiKey))
|
|
throw new InvalidOperationException("ApiKey 不能为空");
|
|
|
|
client = new HttpClient(new SocketsHttpHandler
|
|
{
|
|
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
|
|
AllowAutoRedirect = false
|
|
})
|
|
{ BaseAddress = endpoint };
|
|
}
|
|
|
|
/// <summary>随服务释放客户端及连接池。</summary>
|
|
public void Dispose() => client.Dispose();
|
|
|
|
/// <summary>
|
|
/// 使用指定用户的识别额度,返回外部接口原始响应;HTTP 失败直接抛出异常。
|
|
/// 额度扣减不自动重试,避免请求已执行但响应丢失时重复扣减。
|
|
/// </summary>
|
|
public async Task<bool> UseImageAnalysisQuotaAsync(UseImageAnalysisQuotaRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
RequestValidator.Validate(request);
|
|
// 外部协议要求 token 和 username 放在查询字符串中,统一编码特殊字符。
|
|
var requestUrl = QueryHelpers.AddQueryString("/wxxd_user/UseImageAnalysisQuota", new Dictionary<string, string?>
|
|
{
|
|
["token"] = ApiKey,
|
|
["username"] = request.UserName
|
|
});
|
|
using var response = await client.PostAsync(requestUrl, null, cancellationToken);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
// shortcut: 尚未提供响应协议,明确业务成功字段后再添加响应 DTO 和业务失败判断。
|
|
string html = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
var result = JsonSerializer.Deserialize<ApiResult>(html);
|
|
if (result != null && result.Success)
|
|
{
|
|
return true;
|
|
}
|
|
throw new AppException(result.Msg);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 使用指定用户的识别额度,返回外部接口原始响应;HTTP 失败直接抛出异常。
|
|
/// 额度扣减不自动重试,避免请求已执行但响应丢失时重复扣减。
|
|
/// </summary>
|
|
public async Task<int> GetUserImageAnalysisQuota(UseImageAnalysisQuotaRequest request,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
RequestValidator.Validate(request);
|
|
// 外部协议要求 token 和 username 放在查询字符串中,统一编码特殊字符。
|
|
var requestUrl = QueryHelpers.AddQueryString("/wxxd_user/GetUserImageAnalysisQuota", new Dictionary<string, string?>
|
|
{
|
|
["token"] = ApiKey,
|
|
["username"] = request.UserName
|
|
});
|
|
using var response = await client.PostAsync(requestUrl, null, cancellationToken);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
// shortcut: 尚未提供响应协议,明确业务成功字段后再添加响应 DTO 和业务失败判断。
|
|
string html = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
var options = new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true
|
|
};
|
|
var result = JsonSerializer.Deserialize<ApiResult<int>>(html, options);
|
|
if (result != null && result.Success)
|
|
{
|
|
return result.Data;
|
|
}
|
|
throw new AppException(result.Msg);
|
|
}
|
|
}
|