5 changed files with 267 additions and 0 deletions
@ -0,0 +1,42 @@ |
|||||||
|
using ATS.NonCustodial.Domain.Shared.AggRootEntities; |
||||||
|
using ATS.NonCustodial.Domain.Shared.Constants; |
||||||
|
using ATS.NonCustodial.Domain.Shared.Enums; |
||||||
|
using ATS.NonCustodial.Shared.Common.Enums; |
||||||
|
using ATS.NonCustodial.Shared.Common.Enums.IM; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
using System.ComponentModel.DataAnnotations; |
||||||
|
using System.ComponentModel.DataAnnotations.Schema; |
||||||
|
using System.Numerics; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Domain.Entities.Admins |
||||||
|
{ |
||||||
|
[Table("app_sms")] |
||||||
|
public class AppSMS: EntityFull |
||||||
|
{ |
||||||
|
public AppSMS() |
||||||
|
{ } |
||||||
|
|
||||||
|
public AppSMS(long id) |
||||||
|
{ |
||||||
|
Id = id; |
||||||
|
} |
||||||
|
|
||||||
|
public string phone { get; set; } = string.Empty; |
||||||
|
|
||||||
|
public string content { get; set; } = string.Empty; |
||||||
|
|
||||||
|
public string? code { get; set; } |
||||||
|
|
||||||
|
public DateTime sendTime { get; set; } |
||||||
|
|
||||||
|
public DateTime expiresTime { get; set; } |
||||||
|
|
||||||
|
public bool isUsed { get; set; } = false; |
||||||
|
|
||||||
|
public DateTime? useTime { get; set; } |
||||||
|
|
||||||
|
public string ipAddress { get; set; } = string.Empty; |
||||||
|
|
||||||
|
public string type { get; set; } = "default"; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,170 @@ |
|||||||
|
using ATS.NonCustodial.Application.Base; |
||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth; |
||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth.Input; |
||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth.Output; |
||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.SMS; |
||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.User; |
||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Logs.LoginLog.Input; |
||||||
|
using ATS.NonCustodial.Domain.Entities.Admins; |
||||||
|
using ATS.NonCustodial.Domain.Entities.Business; |
||||||
|
using ATS.NonCustodial.Domain.Entities.Business.EarlyWarning; |
||||||
|
using ATS.NonCustodial.Domain.Shared.Enums; |
||||||
|
using ATS.NonCustodial.Domain.Shared.OrmRepositories.Basic.EfCore; |
||||||
|
using ATS.NonCustodial.DynamicApi; |
||||||
|
using ATS.NonCustodial.DynamicApi.Attributes; |
||||||
|
using ATS.NonCustodial.Shared.Captcha.Dto; |
||||||
|
using ATS.NonCustodial.Shared.Common.Attributes; |
||||||
|
using ATS.NonCustodial.Shared.Common.Auth; |
||||||
|
using ATS.NonCustodial.Shared.Common.Constants; |
||||||
|
using ATS.NonCustodial.Shared.Common.Dtos; |
||||||
|
using ATS.NonCustodial.Shared.Common.Enums; |
||||||
|
using ATS.NonCustodial.Shared.Common.UnifiedResults; |
||||||
|
using ATS.NonCustodial.Shared.Configurations.Options; |
||||||
|
using ATS.NonCustodial.Shared.Extensions; |
||||||
|
using ATS.NonCustodial.Shared.Helpers; |
||||||
|
using ATS.NonCustodial.Shared.Helpers.Core.Hash; |
||||||
|
using ATS.NonCustodial.Shared.Helpers.Http; |
||||||
|
using ATS.NonCustodial.Shared.Tools.Captcha; |
||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Microsoft.AspNetCore.Cors; |
||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Newtonsoft.Json; |
||||||
|
using NPOI.SS.Formula.Functions; |
||||||
|
using NPOI.Util; |
||||||
|
using StackExchange.Profiling; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Security.Claims; |
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Application.Impl.Admins |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 短信服务 |
||||||
|
/// </summary> |
||||||
|
[DynamicApi(Area = "admin")] |
||||||
|
public class SMSService : AdminAppServiceBase, ISMSService, IDynamicApi |
||||||
|
{ |
||||||
|
private readonly IEfRepository<AppSMS?, long> _appSMSRepository; |
||||||
|
|
||||||
|
public SMSService( |
||||||
|
IEfRepository<AppSMS?, long> appSMSRepository) |
||||||
|
{ |
||||||
|
_appSMSRepository = appSMSRepository; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 发送验证码 |
||||||
|
/// </summary> |
||||||
|
/// <param name="phone"></param> |
||||||
|
/// <param name="ipAddress"></param> |
||||||
|
/// <param name="type"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpGet] |
||||||
|
[AllowAnonymous] |
||||||
|
public async Task<IResultOutput> SendCheckCodeSMS(string phone, string ipAddress = "", string type = "CheckCode") |
||||||
|
{ |
||||||
|
// 检查是否可以发送(一分钟内只能发送一次) |
||||||
|
if (!await CanSendCodeAsync(phone)) |
||||||
|
{ |
||||||
|
return ResultOutput.NotOk("请求过于频繁,请稍后再试"); |
||||||
|
} |
||||||
|
|
||||||
|
// 生成随机验证码(6位数字) |
||||||
|
var random = new Random(); |
||||||
|
var code = random.Next(100000, 999999).ToString(); |
||||||
|
|
||||||
|
// 创建验证码记录 |
||||||
|
var addSMS = new AppSMS |
||||||
|
{ |
||||||
|
phone = phone, |
||||||
|
code = code, |
||||||
|
sendTime = DateTime.UtcNow, |
||||||
|
expiresTime = DateTime.UtcNow.AddMinutes(5), // 5分钟有效期 |
||||||
|
ipAddress = ipAddress, |
||||||
|
type = type |
||||||
|
}; |
||||||
|
|
||||||
|
// 发送短信 |
||||||
|
var sendResult = SendSMS(phone, code); |
||||||
|
if (!sendResult) |
||||||
|
{ |
||||||
|
return ResultOutput.NotOk("短信发送失败,请稍后重试"); |
||||||
|
} |
||||||
|
|
||||||
|
var sms = await _appSMSRepository.InsertAsync(addSMS); |
||||||
|
|
||||||
|
return ResultOutput.Ok(true); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 校验验证码 |
||||||
|
/// </summary> |
||||||
|
/// <param name="phoneNumber"></param> |
||||||
|
/// <param name="code"></param> |
||||||
|
/// <param name="type"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task<bool> CheckCodeAsync(string phoneNumber, string code, string type = "default") |
||||||
|
{ |
||||||
|
var now = DateTime.UtcNow; |
||||||
|
|
||||||
|
// 查找有效的验证码 |
||||||
|
var validCode = await _appSMSRepository.AsQueryable() |
||||||
|
.Where(v => v.phone == phoneNumber |
||||||
|
&& v.code == code |
||||||
|
&& v.type == type |
||||||
|
&& !v.isUsed |
||||||
|
&& v.expiresTime > now) |
||||||
|
.OrderByDescending(v => v.sendTime) |
||||||
|
.FirstOrDefaultAsync(); |
||||||
|
|
||||||
|
if (validCode == null) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// 标记为已使用 |
||||||
|
validCode.isUsed = true; |
||||||
|
validCode.useTime = now; |
||||||
|
await _appSMSRepository.UpdateAsync(validCode); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 发送前校验 |
||||||
|
/// </summary> |
||||||
|
/// <param name="phone"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public async Task<bool> CanSendCodeAsync(string phone) |
||||||
|
{ |
||||||
|
var oneMinuteAgo = DateTime.UtcNow.AddMinutes(-1); |
||||||
|
|
||||||
|
// 检查一分钟内是否有发送记录 |
||||||
|
var recentCode = await _appSMSRepository.AsQueryable() |
||||||
|
.Where(v => v.phone == phone |
||||||
|
&& v.sendTime > oneMinuteAgo) |
||||||
|
.OrderByDescending(v => v.sendTime) |
||||||
|
.FirstOrDefaultAsync(); |
||||||
|
|
||||||
|
return recentCode == null; |
||||||
|
} |
||||||
|
|
||||||
|
#region Private |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 发送短信 |
||||||
|
/// </summary> |
||||||
|
/// <param name="phone"></param> |
||||||
|
/// <param name="msg"></param> |
||||||
|
/// <returns></returns> |
||||||
|
private bool SendSMS(string phone, string msg) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth.Input; |
||||||
|
using ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth.Output; |
||||||
|
using ATS.NonCustodial.Domain.Shared.Enums; |
||||||
|
using ATS.NonCustodial.Shared.Captcha.Dto; |
||||||
|
using ATS.NonCustodial.Shared.Common.Dtos; |
||||||
|
using ATS.NonCustodial.Shared.Common.UnifiedResults; |
||||||
|
|
||||||
|
namespace ATS.NonCustodial.Application.Contracts.Interfaces.Admins.SMS |
||||||
|
{ |
||||||
|
public interface ISMSService |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 发送验证码 |
||||||
|
/// </summary> |
||||||
|
/// <param name="phone"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task<IResultOutput> SendCheckCodeSMS(string phone, string ipAddress = "", string type = "CheckCode"); |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 校验验证码 |
||||||
|
/// </summary> |
||||||
|
/// <param name="phoneNumber"></param> |
||||||
|
/// <param name="code"></param> |
||||||
|
/// <param name="type"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task<bool> CheckCodeAsync(string phoneNumber, string code, string type = "default"); |
||||||
|
|
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 发送前校验 |
||||||
|
/// </summary> |
||||||
|
/// <param name="phone"></param> |
||||||
|
/// <returns></returns> |
||||||
|
Task<bool> CanSendCodeAsync(string phone); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue