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}场次,请提前前往检录区做好准备。单位报名多个场次时 order 可为“1、3、4、5”。 /// current 参数格式:{项目名称}第{x}场次。 /// public async Task SendCheckinNotice(string phone, string eventName, string currentSession, string sessionOrders) { if (string.IsNullOrWhiteSpace(_options.CheckinNoticeTemplateCode)) { _logger.LogInformation("检录通知模板未配置,跳过短信发送:{Phone}", phone); return false; } var param = JsonConvert.SerializeObject(new { @event = eventName, current = currentSession, order = sessionOrders }); 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)未配置"); } }