From 136ff1f656c8ebdbce17e3c629b128f7c9ab3c50 Mon Sep 17 00:00:00 2001
From: wangping <995664179@qq.com>
Date: Wed, 17 Sep 2025 16:08:54 +0800
Subject: [PATCH] =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E7=9B=B8=E5=85=B3=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3=E4=B8=8E=E6=A8=A1=E5=9E=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Entities/Admins/AppSMS.cs | 42 +++++
.../Impl/Admins/AuthService.cs | 12 ++
.../Impl/Admins/SMSService.cs | 170 ++++++++++++++++++
.../Interfaces/Admins/Auth/IAuthService.cs | 7 +
.../Interfaces/Admins/SMS/ISMSService.cs | 36 ++++
5 files changed, 267 insertions(+)
create mode 100644 src/1.datas/ATS.NonCustodial.Domain/Entities/Admins/AppSMS.cs
create mode 100644 src/2.services/ATS.NonCustodial.Application/Impl/Admins/SMSService.cs
create mode 100644 src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/SMS/ISMSService.cs
diff --git a/src/1.datas/ATS.NonCustodial.Domain/Entities/Admins/AppSMS.cs b/src/1.datas/ATS.NonCustodial.Domain/Entities/Admins/AppSMS.cs
new file mode 100644
index 0000000..f2a49d4
--- /dev/null
+++ b/src/1.datas/ATS.NonCustodial.Domain/Entities/Admins/AppSMS.cs
@@ -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";
+ }
+}
diff --git a/src/2.services/ATS.NonCustodial.Application/Impl/Admins/AuthService.cs b/src/2.services/ATS.NonCustodial.Application/Impl/Admins/AuthService.cs
index 7f1df81..ce7f938 100644
--- a/src/2.services/ATS.NonCustodial.Application/Impl/Admins/AuthService.cs
+++ b/src/2.services/ATS.NonCustodial.Application/Impl/Admins/AuthService.cs
@@ -35,6 +35,7 @@ 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
{
@@ -387,6 +388,17 @@ namespace ATS.NonCustodial.Application.Impl.Admins
return result;
}
+ ///
+ /// 根据身份证号获取电话号码
+ ///
+ ///
+ ///
+ public async Task GetPhoneByIDCard(string idCard)
+ {
+ var user = await _appUserRepository.FindAsync(a => a.ChatPersonType == ChatPersonTypeEnum.SupervisedPerson && a.IdCard == idCard);
+ return ResultOutput.Ok(user?.Phone);
+ }
+
#region Private
///
diff --git a/src/2.services/ATS.NonCustodial.Application/Impl/Admins/SMSService.cs b/src/2.services/ATS.NonCustodial.Application/Impl/Admins/SMSService.cs
new file mode 100644
index 0000000..c868967
--- /dev/null
+++ b/src/2.services/ATS.NonCustodial.Application/Impl/Admins/SMSService.cs
@@ -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
+{
+ ///
+ /// 短信服务
+ ///
+ [DynamicApi(Area = "admin")]
+ public class SMSService : AdminAppServiceBase, ISMSService, IDynamicApi
+ {
+ private readonly IEfRepository _appSMSRepository;
+
+ public SMSService(
+ IEfRepository appSMSRepository)
+ {
+ _appSMSRepository = appSMSRepository;
+ }
+
+ ///
+ /// 发送验证码
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [AllowAnonymous]
+ public async Task 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);
+ }
+
+ ///
+ /// 校验验证码
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task 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;
+ }
+
+ ///
+ /// 发送前校验
+ ///
+ ///
+ ///
+ public async Task 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
+
+ ///
+ /// 发送短信
+ ///
+ ///
+ ///
+ ///
+ private bool SendSMS(string phone, string msg)
+ {
+ return true;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/Auth/IAuthService.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/Auth/IAuthService.cs
index 74a736e..9c56d63 100644
--- a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/Auth/IAuthService.cs
+++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/Auth/IAuthService.cs
@@ -79,5 +79,12 @@ namespace ATS.NonCustodial.Application.Contracts.Interfaces.Admins.Auth
/// 用户信息
///
Task GetToken(AuthLoginOutput? user);
+
+ ///
+ /// 根据身份证号获取电话号码
+ ///
+ ///
+ ///
+ Task GetPhoneByIDCard(string idCard);
}
}
\ No newline at end of file
diff --git a/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/SMS/ISMSService.cs b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/SMS/ISMSService.cs
new file mode 100644
index 0000000..f9d3fcd
--- /dev/null
+++ b/src/3.contracts/ATS.NonCustodial.Application.Contracts/Interfaces/Admins/SMS/ISMSService.cs
@@ -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
+ {
+ ///
+ /// 发送验证码
+ ///
+ ///
+ ///
+ Task SendCheckCodeSMS(string phone, string ipAddress = "", string type = "CheckCode");
+
+ ///
+ /// 校验验证码
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task CheckCodeAsync(string phoneNumber, string code, string type = "default");
+
+
+ ///
+ /// 发送前校验
+ ///
+ ///
+ ///
+ Task CanSendCodeAsync(string phone);
+ }
+}