175 lines
5.5 KiB
C#
175 lines
5.5 KiB
C#
|
|
using AlibabaCloud.OpenApiClient.Models;
|
|||
|
|
using AlibabaCloud.SDK.Dysmsapi20170525;
|
|||
|
|
using AlibabaCloud.SDK.Dysmsapi20170525.Models;
|
|||
|
|
using Furion.DependencyInjection;
|
|||
|
|
using Furion.FriendlyException;
|
|||
|
|
using Microsoft.Extensions.Logging;
|
|||
|
|
using Microsoft.Extensions.Options;
|
|||
|
|
using Newtonsoft.Json;
|
|||
|
|
using SqlSugar;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using Tea;
|
|||
|
|
using YD_Event.Core.Entities;
|
|||
|
|
using YD_Event.Core.Options;
|
|||
|
|
|
|||
|
|
namespace YD_Event.Application.Sms;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 阿里云短信服务实现
|
|||
|
|
/// </summary>
|
|||
|
|
public class AliyunSmsService : ITransient
|
|||
|
|
{
|
|||
|
|
private readonly SmsOptions _options;
|
|||
|
|
private readonly ILogger<AliyunSmsService> _logger;
|
|||
|
|
private readonly ISqlSugarRepository<SysSmsLog> _smsLogRepository;
|
|||
|
|
private Client _client;
|
|||
|
|
|
|||
|
|
public AliyunSmsService(IOptions<SmsOptions> options,
|
|||
|
|
ILogger<AliyunSmsService> logger,
|
|||
|
|
ISqlSugarRepository<SysSmsLog> smsLogRepository)
|
|||
|
|
{
|
|||
|
|
_options = options.Value;
|
|||
|
|
_logger = logger;
|
|||
|
|
_smsLogRepository = smsLogRepository;
|
|||
|
|
InitClient();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化阿里云短信客户端
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitClient()
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(_options.AccessKeyId) || string.IsNullOrWhiteSpace(_options.AccessKeySecret))
|
|||
|
|
{
|
|||
|
|
_logger.LogWarning("阿里云短信配置缺失:AccessKeyId或AccessKeySecret为空");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var config = new Config
|
|||
|
|
{
|
|||
|
|
AccessKeyId = _options.AccessKeyId,
|
|||
|
|
AccessKeySecret = _options.AccessKeySecret,
|
|||
|
|
Endpoint = _options.Endpoint
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
_client = new Client(config);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError(ex, "阿里云短信客户端初始化失败");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 发送验证码短信
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="phone">手机号</param>
|
|||
|
|
/// <param name="code">验证码</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public async Task SendVerificationCodeAsync(string phone, string code)
|
|||
|
|
{
|
|||
|
|
// 校验配置
|
|||
|
|
ValidateConfig();
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(_options.VerificationCodeTemplateCode))
|
|||
|
|
throw Oops.Bah("短信验证码模板未配置");
|
|||
|
|
|
|||
|
|
var param = JsonConvert.SerializeObject(new { code = code });
|
|||
|
|
await SendSmsAsync(phone, _options.VerificationCodeTemplateCode, param);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 发送自定义短信
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="phone">手机号</param>
|
|||
|
|
/// <param name="templateCode">模板编码</param>
|
|||
|
|
/// <param name="templateParam">模板参数JSON字符串</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public async Task SendSmsAsync(string phone, string templateCode, string templateParam)
|
|||
|
|
{
|
|||
|
|
// 1. 参数校验
|
|||
|
|
if (string.IsNullOrWhiteSpace(phone)) throw Oops.Bah("手机号不能为空");
|
|||
|
|
if (string.IsNullOrWhiteSpace(templateCode)) throw Oops.Bah("短信模板不能为空");
|
|||
|
|
|
|||
|
|
ValidateConfig();
|
|||
|
|
|
|||
|
|
// 2. 构造请求
|
|||
|
|
var request = new SendSmsRequest
|
|||
|
|
{
|
|||
|
|
PhoneNumbers = phone,
|
|||
|
|
SignName = _options.SignName,
|
|||
|
|
TemplateCode = templateCode,
|
|||
|
|
TemplateParam = templateParam
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 3. 准备日志实体
|
|||
|
|
var log = new SysSmsLog
|
|||
|
|
{
|
|||
|
|
Phone = phone,
|
|||
|
|
TemplateCode = templateCode,
|
|||
|
|
TemplateParam = templateParam,
|
|||
|
|
CreatedTime = DateTime.Now
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 4. 发送请求
|
|||
|
|
var response = await _client.SendSmsAsync(request);
|
|||
|
|
|
|||
|
|
// 5. 处理响应
|
|||
|
|
log.ResponseCode = response.Body.Code;
|
|||
|
|
log.ResponseMessage = response.Body.Message;
|
|||
|
|
log.RequestId = response.Body.RequestId;
|
|||
|
|
log.Success = response.Body.Code == "OK";
|
|||
|
|
|
|||
|
|
if (!log.Success)
|
|||
|
|
{
|
|||
|
|
_logger.LogWarning($"短信发送失败:{phone}, Code: {response.Body.Code}, Message: {response.Body.Message}");
|
|||
|
|
throw Oops.Bah($"短信发送失败:{response.Body.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (TeaException error)
|
|||
|
|
{
|
|||
|
|
// 阿里云SDK异常
|
|||
|
|
log.Success = false;
|
|||
|
|
log.Exception = $"Code: {error.Code}, Message: {error.Message}, Data: {JsonConvert.SerializeObject(error.Data)}";
|
|||
|
|
_logger.LogError(error, "阿里云短信发送异常");
|
|||
|
|
throw Oops.Bah("短信服务异常,请稍后重试");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
// 其他异常(网络等)
|
|||
|
|
log.Success = false;
|
|||
|
|
log.Exception = ex.Message;
|
|||
|
|
_logger.LogError(ex, "短信发送未知异常");
|
|||
|
|
throw Oops.Bah("短信发送失败,请检查网络或联系管理员");
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
// 6. 日志持久化
|
|||
|
|
await _smsLogRepository.InsertAsync(log);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 校验短信配置
|
|||
|
|
/// </summary>
|
|||
|
|
public void ValidateConfig()
|
|||
|
|
{
|
|||
|
|
if (_client == null)
|
|||
|
|
{
|
|||
|
|
InitClient();
|
|||
|
|
if (_client == null)
|
|||
|
|
throw Oops.Bah("短信服务未初始化,请检查AccessKeyId和AccessKeySecret配置");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(_options.SignName))
|
|||
|
|
throw Oops.Bah("短信签名(SignName)未配置");
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(_options.VerificationCodeTemplateCode))
|
|||
|
|
throw Oops.Bah("验证码模板(VerificationCodeTemplateCode)未配置");
|
|||
|
|
}
|
|||
|
|
}
|