Files
tiku-backend.net/Tiku.Infrastructure/Auth/AliyunSmsProvider.cs

170 lines
5.8 KiB
C#

using System.Text.Json;
using AlibabaCloud.OpenApiClient.Models;
using AlibabaCloud.SDK.Dysmsapi20170525.Models;
using Tiku.Application.Auth;
using Tiku.Application.Tenancy;
using Tiku.Domain.Tenancy;
using DysmsClient = AlibabaCloud.SDK.Dysmsapi20170525.Client;
namespace Tiku.Infrastructure.Auth;
internal sealed class AliyunSmsProvider(
ITenantExternalProviderConfigService providerConfigService) : ISmsProvider
{
private const string ProviderCode = "aliyun_sms";
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
public async Task<SmsProviderSendResult> SendAsync(
SmsProviderSendRequest request,
CancellationToken cancellationToken = default)
{
TenantExternalProviderAccount account;
try
{
account = await providerConfigService.GetActiveProviderAsync(
request.TenantId,
TenantExternalProviderCapability.Sms,
null,
cancellationToken);
}
catch (TenantExternalProviderException)
{
return new SmsProviderSendResult("noop", "accepted");
}
if (string.Equals(account.Provider, "noop", StringComparison.OrdinalIgnoreCase) ||
string.Equals(account.Provider, "local_dev", StringComparison.OrdinalIgnoreCase))
{
return new SmsProviderSendResult(account.Provider, "accepted");
}
if (!string.Equals(account.Provider, ProviderCode, StringComparison.OrdinalIgnoreCase))
{
throw new SmsProviderException(
$"SMS provider '{account.Provider}' is not supported.",
"sms_provider_unsupported");
}
var signName = Required(account.ConfigPublic, "signName", "smsSignName");
var templateCode = ResolveTemplateCode(account.ConfigPublic, request.Purpose);
var accessKeyId = Required(account.SecretPayload, "accessKeyId");
var accessKeySecret = Required(account.SecretPayload, "accessKeySecret");
var endpoint = Optional(account.ConfigPublic, "endpoint") ?? "dysmsapi.aliyuncs.com";
var regionId = Optional(account.ConfigPublic, "regionId", "region") ?? "cn-hangzhou";
var client = new DysmsClient(new Config
{
AccessKeyId = accessKeyId,
AccessKeySecret = accessKeySecret,
Endpoint = endpoint,
RegionId = regionId
});
var sendRequest = new SendSmsRequest
{
PhoneNumbers = request.Phone,
SignName = signName,
TemplateCode = templateCode,
TemplateParam = BuildTemplateParam(account.ConfigPublic, request),
OutId = request.TenantId.ToString("N")
};
try
{
var response = await client.SendSmsAsync(sendRequest).WaitAsync(cancellationToken);
var body = response.Body;
if (body is null)
{
throw new SmsProviderException(
"Aliyun SMS returned an empty response.",
"aliyun_sms_empty_response");
}
if (!string.Equals(body.Code, "OK", StringComparison.OrdinalIgnoreCase))
{
throw new SmsProviderException(
$"Aliyun SMS send failed: {body.Code}.",
"aliyun_sms_send_rejected");
}
return new SmsProviderSendResult(
ProviderCode,
body.Code ?? "OK",
body.BizId ?? body.RequestId);
}
catch (SmsProviderException)
{
throw;
}
catch (Exception exception) when (exception is not OperationCanceledException)
{
throw new SmsProviderException(
"Aliyun SMS send failed.",
"aliyun_sms_send_failed",
exception);
}
}
private static string ResolveTemplateCode(JsonElement config, SmsPurpose purpose)
{
if (config.ValueKind == JsonValueKind.Object &&
config.TryGetProperty("templateCodes", out var templateCodes) &&
templateCodes.ValueKind == JsonValueKind.Object)
{
var purposeKey = purpose.ToString().ToLowerInvariant();
if (templateCodes.TryGetProperty(purposeKey, out var purposeTemplate) &&
purposeTemplate.ValueKind == JsonValueKind.String &&
!string.IsNullOrWhiteSpace(purposeTemplate.GetString()))
{
return purposeTemplate.GetString()!;
}
}
return Required(config, "templateCode", "smsTemplateCode");
}
private static string BuildTemplateParam(JsonElement config, SmsProviderSendRequest request)
{
var codeField = Optional(config, "codeField") ?? "code";
return JsonSerializer.Serialize(
new Dictionary<string, string>
{
[codeField] = request.Code
},
JsonOptions);
}
private static string Required(JsonElement element, params string[] keys)
{
var value = Optional(element, keys);
if (!string.IsNullOrWhiteSpace(value))
{
return value;
}
throw new SmsProviderException(
"Aliyun SMS provider configuration is incomplete.",
"aliyun_sms_config_incomplete");
}
private static string? Optional(JsonElement element, params string[] keys)
{
if (element.ValueKind != JsonValueKind.Object)
{
return null;
}
foreach (var key in keys)
{
if (element.TryGetProperty(key, out var property) &&
property.ValueKind == JsonValueKind.String &&
!string.IsNullOrWhiteSpace(property.GetString()))
{
return property.GetString();
}
}
return null;
}
}