单位认证完善
This commit is contained in:
parent
3268e24f1d
commit
8a94c46eed
@ -1,4 +1,4 @@
|
||||
using YD_Event.Application.Logging.Dtos;
|
||||
using YD_Event.Application.Logging.Dtos;
|
||||
|
||||
namespace YD_Event.Application.Logging;
|
||||
/// <summary>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using YD_Event.Application.Logging.Dtos;
|
||||
using YD_Event.Application.Logging.Dtos;
|
||||
|
||||
namespace YD_Event.Application.Logging;
|
||||
/// <summary>
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace YD_Event.Application.Merchant.Dtos;
|
||||
|
||||
public class SubmitCertificationInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 单位名称
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "单位名称不能为空")]
|
||||
public string MerchantName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 统一社会信用代码
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "统一社会信用代码不能为空")]
|
||||
public string CreditCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 营业执照图片URL
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "请上传营业执照")]
|
||||
public string BusinessLicenseUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 地址
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "地址不能为空")]
|
||||
public string Address { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系方式
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "联系方式不能为空")]
|
||||
[RegularExpression(@"^1[3456789]\d{9}$", ErrorMessage = "手机号格式不正确")]
|
||||
public string ContactPhone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 验证码
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "验证码不能为空")]
|
||||
public string VerificationCode { get; set; }
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using YD_Event.Application.Auth;
|
||||
using YD_Event.Application.Merchant.Dtos;
|
||||
using YD_Event.Application.Sms;
|
||||
using YD_Event.Core.Entities;
|
||||
using YD_Event.Core.Enum;
|
||||
|
||||
@ -14,16 +16,111 @@ public class MerchantService : BaseService<YD_Event.Core.Entities.Merchant>, ITr
|
||||
private readonly ISqlSugarRepository<UserCoupon> _userCouponRepository;
|
||||
private readonly AuthManager _authManager;
|
||||
private readonly IIdGenerator _idGenerator;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly AliyunSmsService _smsService;
|
||||
|
||||
public MerchantService(ISqlSugarRepository<YD_Event.Core.Entities.Merchant> repository,
|
||||
ISqlSugarRepository<UserCoupon> userCouponRepository,
|
||||
AuthManager authManager,
|
||||
IIdGenerator idGenerator) : base(repository)
|
||||
IIdGenerator idGenerator,
|
||||
IMemoryCache memoryCache,
|
||||
AliyunSmsService smsService) : base(repository)
|
||||
{
|
||||
_repository = repository;
|
||||
_userCouponRepository = userCouponRepository;
|
||||
_authManager = authManager;
|
||||
_idGenerator = idGenerator;
|
||||
_memoryCache = memoryCache;
|
||||
_smsService = smsService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送验证码
|
||||
/// </summary>
|
||||
[DisplayName("发送验证码")]
|
||||
[HttpPost("sendVerificationCode")]
|
||||
[AllowAnonymous]
|
||||
public async Task SendVerificationCode([FromQuery] string phone)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(phone) || !System.Text.RegularExpressions.Regex.IsMatch(phone, @"^1[3456789]\d{9}$"))
|
||||
throw Oops.Bah("手机号格式不正确");
|
||||
|
||||
// 生成6位验证码
|
||||
var code = new Random().Next(100000, 999999).ToString();
|
||||
|
||||
// 存入缓存,有效期5分钟
|
||||
_memoryCache.Set($"verification_code_{phone}", code, TimeSpan.FromMinutes(5));
|
||||
|
||||
// 发送短信
|
||||
await _smsService.SendVerificationCodeAsync(phone, code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交单位认证
|
||||
/// </summary>
|
||||
[DisplayName("提交单位认证")]
|
||||
[HttpPost("submitCertification")]
|
||||
public async Task SubmitCertification(SubmitCertificationInput input)
|
||||
{
|
||||
// 验证验证码
|
||||
if (!_memoryCache.TryGetValue($"verification_code_{input.ContactPhone}", out string? cachedCode) || cachedCode != input.VerificationCode)
|
||||
{
|
||||
throw Oops.Bah("验证码错误或已过期");
|
||||
}
|
||||
|
||||
// 获取当前用户关联的商户信息
|
||||
var userId = _authManager.UserId;
|
||||
var merchant = await _repository.GetFirstAsync(m => m.CreatedUserId == userId);
|
||||
|
||||
if (merchant == null)
|
||||
{
|
||||
// 新增商户
|
||||
merchant = new YD_Event.Core.Entities.Merchant
|
||||
{
|
||||
Id = _idGenerator.NextId(),
|
||||
MerchantName = input.MerchantName,
|
||||
CreditCode = input.CreditCode,
|
||||
BusinessLicenseUrl = input.BusinessLicenseUrl,
|
||||
Address = input.Address,
|
||||
ContactPhone = input.ContactPhone,
|
||||
BankName = "", // 默认为空,后续完善
|
||||
AccountNumber = "", // 默认为空,后续完善
|
||||
AuditStatus = AuditStatus.PendingAudit, // 待审核
|
||||
Status = AvailabilityStatus.Enable,
|
||||
CreatedUserId = userId,
|
||||
CreatedTime = DateTime.Now
|
||||
};
|
||||
await _repository.InsertAsync(merchant);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 更新商户信息
|
||||
merchant.MerchantName = input.MerchantName;
|
||||
merchant.CreditCode = input.CreditCode;
|
||||
merchant.BusinessLicenseUrl = input.BusinessLicenseUrl;
|
||||
merchant.Address = input.Address;
|
||||
merchant.ContactPhone = input.ContactPhone;
|
||||
merchant.AuditStatus = AuditStatus.PendingAudit; // 重新提交后变为待审核
|
||||
merchant.UpdatedUserId = userId;
|
||||
merchant.UpdatedTime = DateTime.Now;
|
||||
|
||||
await _repository.UpdateAsync(merchant);
|
||||
}
|
||||
|
||||
// 清除验证码
|
||||
_memoryCache.Remove($"verification_code_{input.ContactPhone}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取我的认证信息
|
||||
/// </summary>
|
||||
[DisplayName("获取我的认证信息")]
|
||||
[HttpGet("myCertification")]
|
||||
public async Task<YD_Event.Core.Entities.Merchant> GetMyCertification()
|
||||
{
|
||||
var userId = _authManager.UserId;
|
||||
var merchant = await _repository.GetFirstAsync(m => m.CreatedUserId == userId);
|
||||
return merchant;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
166
YD_Event.Application/Sms/AliyunSmsService.cs
Normal file
166
YD_Event.Application/Sms/AliyunSmsService.cs
Normal file
@ -0,0 +1,166 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 阿里云短信服务实现
|
||||
/// </summary>
|
||||
public class AliyunSmsService : ITransient
|
||||
{
|
||||
private readonly SmsOptions _options;
|
||||
private readonly ILogger<AliyunSmsService> _logger;
|
||||
private readonly ISqlSugarRepository<SysSmsLog> _smsLogRepository;
|
||||
private AlibabaCloud.SDK.Dysmsapi20170525.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 AlibabaCloud.SDK.Dysmsapi20170525.Client(config);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "阿里云短信客户端初始化失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送验证码短信
|
||||
/// </summary>
|
||||
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>
|
||||
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)未配置");
|
||||
}
|
||||
}
|
||||
@ -2508,6 +2508,36 @@
|
||||
商户审核输入
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.Merchant.Dtos.SubmitCertificationInput.MerchantName">
|
||||
<summary>
|
||||
单位名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.Merchant.Dtos.SubmitCertificationInput.CreditCode">
|
||||
<summary>
|
||||
统一社会信用代码
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.Merchant.Dtos.SubmitCertificationInput.BusinessLicenseUrl">
|
||||
<summary>
|
||||
营业执照图片URL
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.Merchant.Dtos.SubmitCertificationInput.Address">
|
||||
<summary>
|
||||
地址
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.Merchant.Dtos.SubmitCertificationInput.ContactPhone">
|
||||
<summary>
|
||||
联系方式
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.Merchant.Dtos.SubmitCertificationInput.VerificationCode">
|
||||
<summary>
|
||||
验证码
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.Merchant.Dtos.UpdateMerchantInput.Id">
|
||||
<summary>
|
||||
主键Id
|
||||
@ -2518,6 +2548,21 @@
|
||||
商户管理
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Merchant.MerchantService.SendVerificationCode(System.String)">
|
||||
<summary>
|
||||
发送验证码
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Merchant.MerchantService.SubmitCertification(YD_Event.Application.Merchant.Dtos.SubmitCertificationInput)">
|
||||
<summary>
|
||||
提交单位认证
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Merchant.MerchantService.GetMyCertification">
|
||||
<summary>
|
||||
获取我的认证信息
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Merchant.MerchantService.Page(YD_Event.Application.Merchant.Dtos.MerchantQueryInput)">
|
||||
<summary>
|
||||
商户分页查询
|
||||
@ -3581,6 +3626,31 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:YD_Event.Application.Sms.AliyunSmsService">
|
||||
<summary>
|
||||
阿里云短信服务实现
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.InitClient">
|
||||
<summary>
|
||||
初始化阿里云短信客户端
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.SendVerificationCodeAsync(System.String,System.String)">
|
||||
<summary>
|
||||
发送验证码短信
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.SendSmsAsync(System.String,System.String,System.String)">
|
||||
<summary>
|
||||
发送短信核心方法
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.ValidateConfig">
|
||||
<summary>
|
||||
校验配置是否有效
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Application.User.Dtos.AddSysUserInput.Name">
|
||||
<summary>
|
||||
姓名
|
||||
|
||||
@ -139,5 +139,11 @@
|
||||
// }
|
||||
//]
|
||||
}
|
||||
},
|
||||
// 阿里云短信配置
|
||||
"ALiYunSMS": {
|
||||
"Endpoint": "dysmsapi.aliyuncs.com",
|
||||
"Sign": "oVpeH1PpDUnWTcBrUSSYkQNS4Fex8Y",
|
||||
"Template": "SMS_147140268"
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
using SqlSugar;
|
||||
using SqlSugar;
|
||||
using YD_Event.Core.Enum;
|
||||
|
||||
namespace YD_Event.Core.Entities;
|
||||
|
||||
63
YD_Event.Core/Entities/SysSmsLog.cs
Normal file
63
YD_Event.Core/Entities/SysSmsLog.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using SqlSugar;
|
||||
using YD_Event.Core.Enum;
|
||||
|
||||
namespace YD_Event.Core.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 短信发送日志
|
||||
/// </summary>
|
||||
[SugarTable("SysSmsLog")]
|
||||
public class SysSmsLog : Entity<long>, ICreatedTime
|
||||
{
|
||||
/// <summary>
|
||||
/// 接收手机号
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 20)]
|
||||
public string Phone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 短信模板Code
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 50)]
|
||||
public string TemplateCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板参数
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 512, IsNullable = true)]
|
||||
public string TemplateParam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发送状态(成功/失败)
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 响应代码
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 50, IsNullable = true)]
|
||||
public string ResponseCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 响应消息
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 512, IsNullable = true)]
|
||||
public string ResponseMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求ID
|
||||
/// </summary>
|
||||
[SugarColumn(Length = 128, IsNullable = true)]
|
||||
public string RequestId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 异常信息
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "text", IsNullable = true)]
|
||||
public string Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发送时间
|
||||
/// </summary>
|
||||
public DateTime CreatedTime { get; set; }
|
||||
}
|
||||
@ -14,6 +14,7 @@ public static class ServiceCollectionExtension
|
||||
service.AddConfigurableOptions<DbConnectionOptions>();
|
||||
service.AddConfigurableOptions<OssConnectionOptions>();
|
||||
service.AddConfigurableOptions<SnowIdOptions>();
|
||||
service.AddConfigurableOptions<SmsOptions>();
|
||||
return service;
|
||||
}
|
||||
}
|
||||
38
YD_Event.Core/Options/SmsOptions.cs
Normal file
38
YD_Event.Core/Options/SmsOptions.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Furion.ConfigurableOptions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace YD_Event.Core.Options;
|
||||
|
||||
/// <summary>
|
||||
/// 短信配置选项
|
||||
/// </summary>
|
||||
[OptionsSettings("ALiYunSMS")]
|
||||
public class SmsOptions : IConfigurableOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// AccessKeyId
|
||||
/// </summary>
|
||||
public string AccessKeyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AccessKeySecret
|
||||
/// </summary>
|
||||
public string AccessKeySecret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 短信签名
|
||||
/// </summary>
|
||||
[ConfigurationKeyName("Sign")]
|
||||
public string SignName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 验证码模板Code
|
||||
/// </summary>
|
||||
[ConfigurationKeyName("Template")]
|
||||
public string VerificationCodeTemplateCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 短信API Endpoint
|
||||
/// </summary>
|
||||
public string Endpoint { get; set; } = "dysmsapi.aliyuncs.com";
|
||||
}
|
||||
174
YD_Event.Core/Sms/AliyunSmsService.cs
Normal file
174
YD_Event.Core/Sms/AliyunSmsService.cs
Normal file
@ -0,0 +1,174 @@
|
||||
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)未配置");
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@
|
||||
<None Remove="YD_Event.Core.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="4.3.0" />
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="EasyCaching.CSRedis" Version="1.9.2" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
||||
|
||||
@ -3366,6 +3366,56 @@
|
||||
创建时间
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:YD_Event.Core.Entities.SysSmsLog">
|
||||
<summary>
|
||||
短信发送日志
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.Phone">
|
||||
<summary>
|
||||
接收手机号
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.TemplateCode">
|
||||
<summary>
|
||||
短信模板Code
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.TemplateParam">
|
||||
<summary>
|
||||
模板参数
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.Success">
|
||||
<summary>
|
||||
发送状态(成功/失败)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.ResponseCode">
|
||||
<summary>
|
||||
响应代码
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.ResponseMessage">
|
||||
<summary>
|
||||
响应消息
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.RequestId">
|
||||
<summary>
|
||||
请求ID
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.Exception">
|
||||
<summary>
|
||||
异常信息
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Entities.SysSmsLog.CreatedTime">
|
||||
<summary>
|
||||
发送时间
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:YD_Event.Core.Entities.SysUser">
|
||||
<summary>
|
||||
系统用户表
|
||||
@ -4156,6 +4206,36 @@
|
||||
是否启用对象存储(不启用将存储至站点目录)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:YD_Event.Core.Options.SmsOptions">
|
||||
<summary>
|
||||
短信配置选项
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Options.SmsOptions.AccessKeyId">
|
||||
<summary>
|
||||
AccessKeyId
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Options.SmsOptions.AccessKeySecret">
|
||||
<summary>
|
||||
AccessKeySecret
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Options.SmsOptions.SignName">
|
||||
<summary>
|
||||
短信签名
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Options.SmsOptions.VerificationCodeTemplateCode">
|
||||
<summary>
|
||||
验证码模板Code
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:YD_Event.Core.Options.SmsOptions.Endpoint">
|
||||
<summary>
|
||||
短信API Endpoint
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:YD_Event.Core.Options.SnowIdOptions">
|
||||
<summary>
|
||||
雪花id配置
|
||||
@ -4196,6 +4276,38 @@
|
||||
子选项
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:YD_Event.Application.Sms.AliyunSmsService">
|
||||
<summary>
|
||||
阿里云短信服务实现
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.InitClient">
|
||||
<summary>
|
||||
初始化阿里云短信客户端
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.SendVerificationCodeAsync(System.String,System.String)">
|
||||
<summary>
|
||||
发送验证码短信
|
||||
</summary>
|
||||
<param name="phone">手机号</param>
|
||||
<param name="code">验证码</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.SendSmsAsync(System.String,System.String,System.String)">
|
||||
<summary>
|
||||
发送自定义短信
|
||||
</summary>
|
||||
<param name="phone">手机号</param>
|
||||
<param name="templateCode">模板编码</param>
|
||||
<param name="templateParam">模板参数JSON字符串</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:YD_Event.Application.Sms.AliyunSmsService.ValidateConfig">
|
||||
<summary>
|
||||
校验短信配置
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Easy.Core.EnumExtension.EnumDic">
|
||||
<summary>
|
||||
缓存
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user