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 Tea;
using YD_Event.Core.Entities;
using YD_Event.Core.Options;
namespace YD_Event.Application.Sms;
///
/// 阿里云短信服务实现
///
public class AliyunSmsService : ITransient
{
private readonly SmsOptions _options;
private readonly ILogger _logger;
private readonly ISqlSugarRepository _smsLogRepository;
private AlibabaCloud.SDK.Dysmsapi20170525.Client _client;
public AliyunSmsService(IOptions options,
ILogger logger,
ISqlSugarRepository smsLogRepository)
{
_options = options.Value;
_logger = logger;
_smsLogRepository = smsLogRepository;
InitClient();
}
///
/// 初始化阿里云短信客户端
///
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 AlibabaCloud.SDK.Dysmsapi20170525.Client(config);
}
catch (Exception ex)
{
_logger.LogError(ex, "阿里云短信客户端初始化失败");
}
}
///
/// 发送验证码短信
///
public async Task SendVerificationCode(string phone, string code)
{
// 校验配置
ValidateConfig();
if (string.IsNullOrWhiteSpace(_options.VerificationCodeTemplateCode))
throw Oops.Bah("短信验证码模板未配置");
var param = JsonConvert.SerializeObject(new { code = code });
await SendSms(phone, _options.VerificationCodeTemplateCode, param);
}
///
/// 发送检录通知短信;未配置检录模板时返回 false 由调用方降级处理。
/// 模板示例:您报名的${event}正在检录,当前检录至${current},您所在第${order}场次,请提前前往检录区做好准备。
/// current 参数格式:{项目名称}第{x}场次。
///
public async Task SendCheckinNotice(string phone, string eventName, string currentSession, int sessionOrder)
{
if (string.IsNullOrWhiteSpace(_options.CheckinNoticeTemplateCode))
{
_logger.LogInformation("检录通知模板未配置,跳过短信发送:{Phone}", phone);
return false;
}
var param = JsonConvert.SerializeObject(new
{
@event = eventName,
current = currentSession,
order = sessionOrder.ToString()
});
await SendSms(phone, _options.CheckinNoticeTemplateCode, param);
return true;
}
///
/// 发送短信核心方法
///
public async Task SendSms(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 runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions
{
Autoretry = true,
MaxAttempts = 3 // 异常重试最多3次
};
// 4. 准备日志实体
var log = new SysSmsLog
{
Phone = phone,
TemplateCode = templateCode,
TemplateParam = templateParam,
CreatedTime = DateTime.Now
};
try
{
// 5. 发送请求
var response = await _client.SendSmsWithOptionsAsync(request, runtime);
// 6. 处理响应
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, "阿里云短信发送异常: {Message}, Recommend: {Recommend}", error.Message, error.Data != null && error.Data.Contains("Recommend") ? error.Data["Recommend"] : "");
throw Oops.Bah("短信服务异常,请稍后重试");
}
catch (Exception ex)
{
// 其他异常(网络等)
log.Success = false;
log.Exception = ex.Message;
_logger.LogError(ex, "短信发送未知异常");
throw Oops.Bah("短信发送失败,请检查网络或联系管理员");
}
finally
{
// 7. 日志持久化
if (_smsLogRepository != null)
{
await _smsLogRepository.InsertAsync(log);
}
}
}
///
/// 校验配置是否有效
///
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)未配置");
}
}