From af397d25c858cde3705b230a81c403c083f30ce4 Mon Sep 17 00:00:00 2001 From: zpc Date: Thu, 24 Apr 2025 00:57:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChouBox.Code/AppExtend/ChouBoxCodeBase.cs | 57 +++++++ ChouBox.Code/Other/SMSBLL.cs | 151 ++++++++++++++++-- ChouBox.Model/Entities/Goods.cs | 5 + ChouBox.Model/Entities/GoodsOffshelfLog.cs | 10 ++ .../Entities/UserVerificationCodes.cs | 70 ++++++++ ChouBox.Model/Entities/YoudaContext.cs | 91 ++++++++++- ChouBox.Model/efcore-gen.md | 2 +- .../Controllers/AccountController.cs | 8 +- ChouBox.WebApi/Filters/ResultFormatFilter.cs | 111 ++++++++++++- ChouBox.WebApi/Program.cs | 6 +- ChouBox.WebApi/appsettings.Development.json | 7 + .../Base/CustomException.cs | 60 +++++++ .../HuanMeng.DotNetCore/Base/ResponseCode.cs | 7 +- .../MiddlewareExtend/ExceptionMiddleware.cs | 11 +- 14 files changed, 562 insertions(+), 34 deletions(-) create mode 100644 ChouBox.Model/Entities/UserVerificationCodes.cs create mode 100644 Utile/HuanMeng.DotNetCore/Base/CustomException.cs diff --git a/ChouBox.Code/AppExtend/ChouBoxCodeBase.cs b/ChouBox.Code/AppExtend/ChouBoxCodeBase.cs index 72cfabb..c368c27 100644 --- a/ChouBox.Code/AppExtend/ChouBoxCodeBase.cs +++ b/ChouBox.Code/AppExtend/ChouBoxCodeBase.cs @@ -4,6 +4,7 @@ using ChouBox.Code.TencentCloudExtend.Model; using ChouBox.Model.Entities; using HuanMeng.DotNetCore.Base; +using HuanMeng.DotNetCore.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; @@ -89,10 +90,66 @@ public class ChouBoxCodeBase if (_httpContextAccessor == null) { _httpContextAccessor = _serviceProvider.GetRequiredService(); + } return _httpContextAccessor; } } + + /// + /// 获取客户端真实IP地址,考虑Nginx等代理情况 + /// + /// 客户端真实IP地址 + protected string GetRealIpAddress() + { + var context = HttpContextAccessor.HttpContext; + if (context == null) + return "unknown"; + + // 尝试从X-Forwarded-For获取真实IP + string ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + + // 如果X-Forwarded-For为空,尝试X-Real-IP + if (string.IsNullOrEmpty(ip)) + { + ip = context.Request.Headers["X-Real-IP"].FirstOrDefault(); + } + + // 尝试其他可能包含真实IP的头 + if (string.IsNullOrEmpty(ip)) + { + ip = context.Request.Headers["Proxy-Client-IP"].FirstOrDefault(); + } + + if (string.IsNullOrEmpty(ip)) + { + ip = context.Request.Headers["WL-Proxy-Client-IP"].FirstOrDefault(); + } + + if (string.IsNullOrEmpty(ip)) + { + ip = context.Request.Headers["HTTP_CLIENT_IP"].FirstOrDefault(); + } + + if (string.IsNullOrEmpty(ip)) + { + ip = context.Request.Headers["HTTP_X_FORWARDED_FOR"].FirstOrDefault(); + } + + // 如果还是获取不到,则使用RemoteIpAddress + if (string.IsNullOrEmpty(ip)) + { + ip = context.Connection.RemoteIpAddress?.ToString(); + } + + // 如果X-Forwarded-For包含多个IP,取第一个非内网IP + if (!string.IsNullOrEmpty(ip) && ip.Contains(",")) + { + ip = ip.Split(',')[0].Trim(); + } + + return ip ?? "unknown"; + } #endregion #region 日志 diff --git a/ChouBox.Code/Other/SMSBLL.cs b/ChouBox.Code/Other/SMSBLL.cs index ea70f7d..267fee9 100644 --- a/ChouBox.Code/Other/SMSBLL.cs +++ b/ChouBox.Code/Other/SMSBLL.cs @@ -2,6 +2,11 @@ using ChouBox.Code.AppExtend; using ChouBox.Code.TencentCloudExtend; +using ChouBox.Model.Entities; + +using HuanMeng.DotNetCore.Base; + +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -16,17 +21,23 @@ namespace ChouBox.Code.Other; /// public class SMSBLL : ChouBoxCodeBase { + private const string RETRY_COUNT_SUFFIX = ":RetryCount"; + private const string LOCK_SUFFIX = ":Lock"; + public SMSBLL(IServiceProvider serviceProvider) : base(serviceProvider) { } + /// /// 发送验证码 /// /// - /// - /// + /// 验证码类型:1-注册,2-登录,3-找回密码,4-修改手机,5-修改邮箱,6-其他 + /// 用户ID,可为空表示未登录用户 + /// 返回下一次可请求的等待秒数,-1表示当天不能再请求 + /// /// - public async Task SendPhoneAsync(string phone) + public async Task SendPhoneAsync(string phone, sbyte codeType = 2, ulong? userId = null) { if (string.IsNullOrEmpty(phone)) { @@ -37,23 +48,131 @@ public class SMSBLL : ChouBoxCodeBase { throw new ArgumentNullException("暂未开放发送验证码!"); } + + // 检查当天发送次数限制 + var dailyCountKey = $"VerificationCodeDailyCount:{phone}:{DateTime.Now:yyyyMMdd}"; + var dailyCount = RedisCache.StringGet(dailyCountKey) ?? 0; + + + + // 获取重试次数 + var retryCountKey = $"VerificationCode:{phone}{RETRY_COUNT_SUFFIX}"; + var retryCount = RedisCache.StringGet(retryCountKey) ?? 0; + var resendLockKey = $"VerificationCode:{phone}{LOCK_SUFFIX}"; + var resendLock = RedisCache.StringGet(resendLockKey); + + // 计算本次等待时间 + int waitSeconds = GetWaitSeconds(retryCount); + + if (resendLock != null && !string.IsNullOrEmpty(resendLock)) + { + // 获取锁的剩余过期时间(秒) + var remainingSeconds = RedisCache.KeyTimeToLive(resendLockKey)?.TotalSeconds ?? 0; + remainingSeconds = Math.Ceiling(remainingSeconds); // 向上取整 + + string message; + if (remainingSeconds <= 60) + { + message = $"验证码发送过于频繁,请{remainingSeconds}秒后再试。"; + } + else + { + int remainingMinutes = (int)Math.Floor(remainingSeconds / 60); + message = $"验证码发送过于频繁,请{remainingMinutes}分钟后再试。"; + } + + throw new CustomException(message); + } + + // 设置每天最大发送次数为5次 + const int maxDailyCount = 5; + if (dailyCount >= maxDailyCount) + { + // 记录达到每日上限日志 + Logger.LogWarning($"手机号{phone}当天验证码发送次数已达上限({maxDailyCount}次)"); + throw new CustomException($"当天验证码发送次数已达上限,请明天再试"); + } + Random random = new Random(); var verificationCode = random.Next(1000, 9999); var redisKey = $"VerificationCode:{phone}"; - var redisVerificationCode = RedisCache.StringGet(redisKey); - if (redisVerificationCode != null && !string.IsNullOrEmpty(redisVerificationCode)) - { - throw new Exception("验证码已发送!"); - } - TencentSMSSendVerificationCode tencentSMSSendVerificationCode = new TencentSMSSendVerificationCode(smsConfig, phone); - var result = await tencentSMSSendVerificationCode.SendVerificationCode(verificationCode.ToString(), 5); - if (!result) - { - throw new Exception("验证码发送失败"); - } - await RedisCache.StringSetAsync(redisKey, verificationCode.ToString(), TimeSpan.FromMinutes(5)); + // 获取客户端真实IP地址 + string ipAddress = GetRealIpAddress(); - return "验证码已发送"; + try + { + TencentSMSSendVerificationCode tencentSMSSendVerificationCode = new TencentSMSSendVerificationCode(smsConfig, phone); + var result = await tencentSMSSendVerificationCode.SendVerificationCode(verificationCode.ToString(), 5); + if (!result) + { + // 记录发送失败日志 + Logger.LogError($"验证码发送失败,手机号:{phone},IP:{ipAddress}"); + throw new CustomException("验证码发送失败"); + } + + // 发送成功,存入Redis,5分钟有效期 + await RedisCache.StringSetAsync(redisKey, verificationCode.ToString(), TimeSpan.FromMinutes(5)); + + // 更新重试次数和锁定时间 + retryCount++; + var nextWaitSeconds = GetWaitSeconds(retryCount); + + // 设置不能重发的锁 + await RedisCache.StringSetAsync(resendLockKey, "1", TimeSpan.FromSeconds(waitSeconds)); + + // 更新重试次数,5分钟后过期 + await RedisCache.StringSetAsync(retryCountKey, retryCount.ToString(), TimeSpan.FromMinutes(5)); + + // 更新当天发送次数计数并设置过期时间为当天剩余时间 + dailyCount++; + var endOfDay = DateTime.Today.AddDays(1).AddSeconds(-1); // 当天23:59:59 + var expireTime = endOfDay - DateTime.Now; + await RedisCache.StringSetAsync(dailyCountKey, dailyCount.ToString(), expireTime); + + // 保存到数据库 + var verificationRecord = new UserVerificationCodes + { + UserId = userId, + Account = phone, + Code = verificationCode.ToString(), + CodeType = codeType, + Channel = "sms", + IpAddress = ipAddress, + Status = 0, // 未使用 + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + ExpiredAt = DateTime.Now.AddMinutes(5) + }; + + await Dao.Context.UserVerificationCodes.AddAsync(verificationRecord); + await Dao.Context.SaveChangesAsync(); + + return nextWaitSeconds; + } + catch (Exception ex) + { + // 记录异常日志 + Logger.LogError($"发送验证码异常:{ex.Message},手机号:{phone},IP:{ipAddress}"); + throw; + } + } + + /// + /// 根据重试次数获取等待时间 + /// + /// 重试次数 + /// 等待秒数,-1表示不允许再次请求 + private int GetWaitSeconds(int retryCount) + { + return retryCount switch + { + 0 => 30, // 第1次请求后等待30秒 + 1 => 60, // 第2次请求后等待60秒 + 2 => 60, // 第3次请求后等待60秒 + 3 => 300, // 第4次请求后等待300秒(5分钟) + 4 => 300, // 第5次请求后等待300秒(5分钟) + _ => -1 // 超过5次则不允许再次请求 + }; } } diff --git a/ChouBox.Model/Entities/Goods.cs b/ChouBox.Model/Entities/Goods.cs index 64c5fae..5406f67 100644 --- a/ChouBox.Model/Entities/Goods.cs +++ b/ChouBox.Model/Entities/Goods.cs @@ -296,4 +296,9 @@ public partial class Goods /// 每日限购次数 /// public int DailyXiangou { get; set; } + + /// + /// 下架金额,当盒子利润值小于它时,下架(如100 盒子利润出现负数后,超过-100就下架) + /// + public int XiajiaJine { get; set; } } diff --git a/ChouBox.Model/Entities/GoodsOffshelfLog.cs b/ChouBox.Model/Entities/GoodsOffshelfLog.cs index 1f60f65..40df2fe 100644 --- a/ChouBox.Model/Entities/GoodsOffshelfLog.cs +++ b/ChouBox.Model/Entities/GoodsOffshelfLog.cs @@ -39,4 +39,14 @@ public partial class GoodsOffshelfLog /// 下架时间 /// public int CreateTime { get; set; } + + /// + /// 备注 + /// + public string? Remarks { get; set; } + + /// + /// 是否已读 + /// + public int IsRead { get; set; } } diff --git a/ChouBox.Model/Entities/UserVerificationCodes.cs b/ChouBox.Model/Entities/UserVerificationCodes.cs new file mode 100644 index 0000000..ff130ba --- /dev/null +++ b/ChouBox.Model/Entities/UserVerificationCodes.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; + +namespace ChouBox.Model.Entities; + +/// +/// 用户验证码表 +/// +public partial class UserVerificationCodes +{ + /// + /// 主键ID + /// + public ulong Id { get; set; } + + /// + /// 用户ID,可为空表示未登录用户 + /// + public ulong? UserId { get; set; } + + /// + /// 账号(手机号/邮箱) + /// + public string Account { get; set; } = null!; + + /// + /// 验证码 + /// + public string Code { get; set; } = null!; + + /// + /// 验证码类型:1-注册,2-登录,3-找回密码,4-修改手机,5-修改邮箱,6-其他 + /// + public sbyte CodeType { get; set; } + + /// + /// 发送渠道:sms-短信,email-邮件 + /// + public string Channel { get; set; } = null!; + + /// + /// 请求IP地址 + /// + public string? IpAddress { get; set; } + + /// + /// 状态:0-未使用,1-已使用,2-已失效 + /// + public sbyte Status { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdatedAt { get; set; } + + /// + /// 过期时间 + /// + public DateTime ExpiredAt { get; set; } + + /// + /// 使用时间 + /// + public DateTime? UsedAt { get; set; } +} diff --git a/ChouBox.Model/Entities/YoudaContext.cs b/ChouBox.Model/Entities/YoudaContext.cs index 74c257e..968885d 100644 --- a/ChouBox.Model/Entities/YoudaContext.cs +++ b/ChouBox.Model/Entities/YoudaContext.cs @@ -5,7 +5,6 @@ using Pomelo.EntityFrameworkCore.MySql.Scaffolding.Internal; namespace ChouBox.Model.Entities; -/// public partial class YoudaContext : DbContext { public YoudaContext() @@ -407,6 +406,11 @@ public partial class YoudaContext : DbContext /// public virtual DbSet UserTaskList { get; set; } + /// + /// 用户验证码表 + /// + public virtual DbSet UserVerificationCodes { get; set; } + /// /// 会员vip /// @@ -964,6 +968,8 @@ public partial class YoudaContext : DbContext entity.HasIndex(e => e.Id, "id").IsUnique(); + entity.HasIndex(e => new { e.UserId, e.Addtime, e.Status }, "idx_user_addtime_status"); + entity.Property(e => e.Id) .HasColumnType("int(10) unsigned") .HasColumnName("id"); @@ -1469,6 +1475,10 @@ public partial class YoudaContext : DbContext .HasComment("下架生效抽数") .HasColumnType("int(11)") .HasColumnName("xiajia_auto_coushu"); + entity.Property(e => e.XiajiaJine) + .HasComment("下架金额,当盒子利润值小于它时,下架(如100 盒子利润出现负数后,超过-100就下架)") + .HasColumnType("int(11)") + .HasColumnName("xiajia_jine"); entity.Property(e => e.XiajiaLirun) .HasComment("下架利润率") .HasColumnType("int(11)") @@ -1807,6 +1817,10 @@ public partial class YoudaContext : DbContext .HasPrecision(10, 2) .HasComment("出货总价值") .HasColumnName("goods_total"); + entity.Property(e => e.IsRead) + .HasComment("是否已读") + .HasColumnType("int(11)") + .HasColumnName("is_read"); entity.Property(e => e.OrderTotal) .HasPrecision(10, 2) .HasComment("订单总价值") @@ -1815,6 +1829,10 @@ public partial class YoudaContext : DbContext .HasPrecision(10, 2) .HasComment("当前利润率") .HasColumnName("profit_rate"); + entity.Property(e => e.Remarks) + .HasMaxLength(1000) + .HasComment("备注") + .HasColumnName("remarks"); entity.Property(e => e.XiajiaLirun) .HasPrecision(10, 2) .HasComment("配置的下架利润阈值") @@ -2733,8 +2751,12 @@ public partial class YoudaContext : DbContext entity.HasIndex(e => e.Id, "id").IsUnique(); + entity.HasIndex(e => new { e.GoodsId, e.ShangId, e.ParentGoodsListId }, "idx_count_fast"); + entity.HasIndex(e => new { e.GoodsId, e.UserId, e.Addtime }, "idx_gid_uid_time"); + entity.HasIndex(e => new { e.ShangId, e.ParentGoodsListId, e.GoodsId, e.OrderType, e.Id }, "idx_opt_query"); + entity.HasIndex(e => new { e.UserId, e.Addtime }, "idx_user_addtime"); entity.HasIndex(e => e.InsuranceIs, "insurance_is"); @@ -3922,7 +3944,7 @@ public partial class YoudaContext : DbContext .HasColumnType("int(11) unsigned") .HasColumnName("mb_number"); entity.Property(e => e.Mobile) - .HasMaxLength(11) + .HasMaxLength(15) .HasComment("手机号") .HasColumnName("mobile"); entity.Property(e => e.Money) @@ -4739,6 +4761,71 @@ public partial class YoudaContext : DbContext .HasColumnName("z_number"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity.ToTable("user_verification_codes", tb => tb.HasComment("用户验证码表")); + + entity.HasIndex(e => new { e.Account, e.CodeType }, "idx_account_code_type"); + + entity.HasIndex(e => e.Code, "idx_code"); + + entity.HasIndex(e => e.ExpiredAt, "idx_expired_at"); + + entity.Property(e => e.Id) + .HasComment("主键ID") + .HasColumnType("bigint(20) unsigned") + .HasColumnName("id"); + entity.Property(e => e.Account) + .HasMaxLength(100) + .HasComment("账号(手机号/邮箱)") + .HasColumnName("account"); + entity.Property(e => e.Channel) + .HasMaxLength(20) + .HasComment("发送渠道:sms-短信,email-邮件") + .HasColumnName("channel"); + entity.Property(e => e.Code) + .HasMaxLength(20) + .HasComment("验证码") + .HasColumnName("code"); + entity.Property(e => e.CodeType) + .HasComment("验证码类型:1-注册,2-登录,3-找回密码,4-修改手机,5-修改邮箱,6-其他") + .HasColumnType("tinyint(4)") + .HasColumnName("code_type"); + entity.Property(e => e.CreatedAt) + .HasDefaultValueSql("CURRENT_TIMESTAMP") + .HasComment("创建时间") + .HasColumnType("datetime") + .HasColumnName("created_at"); + entity.Property(e => e.ExpiredAt) + .HasComment("过期时间") + .HasColumnType("datetime") + .HasColumnName("expired_at"); + entity.Property(e => e.IpAddress) + .HasMaxLength(50) + .HasComment("请求IP地址") + .HasColumnName("ip_address"); + entity.Property(e => e.Status) + .HasComment("状态:0-未使用,1-已使用,2-已失效") + .HasColumnType("tinyint(4)") + .HasColumnName("status"); + entity.Property(e => e.UpdatedAt) + .ValueGeneratedOnAddOrUpdate() + .HasDefaultValueSql("CURRENT_TIMESTAMP") + .HasComment("更新时间") + .HasColumnType("datetime") + .HasColumnName("updated_at"); + entity.Property(e => e.UsedAt) + .HasComment("使用时间") + .HasColumnType("datetime") + .HasColumnName("used_at"); + entity.Property(e => e.UserId) + .HasComment("用户ID,可为空表示未登录用户") + .HasColumnType("bigint(20) unsigned") + .HasColumnName("user_id"); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => e.Id).HasName("PRIMARY"); diff --git a/ChouBox.Model/efcore-gen.md b/ChouBox.Model/efcore-gen.md index 571b694..63deebd 100644 --- a/ChouBox.Model/efcore-gen.md +++ b/ChouBox.Model/efcore-gen.md @@ -8,5 +8,5 @@ dotnet ef dbcontext scaffold "Server=192.168.1.56;Database=youda;User=youda;Pass dotnet ef dbcontext scaffold "Server=192.168.1.56;Database=youda;User=youda;Password=youda;" Pomelo.EntityFrameworkCore.MySql -o Entities/ --use-database-names --no-pluralize --force --context-dir Context --project ChouBox.Model -dotnet ef dbcontext scaffold "Server=192.168.1.56;Database=youda;User=youda;Password=youda;" Pomelo.EntityFrameworkCore.MySql -o Entities/ --no-pluralize --force +dotnet ef dbcontext scaffold "server=192.168.195.13;port=3306;database=youda;user=youda;password=youda;charset=utf8mb4" Pomelo.EntityFrameworkCore.MySql -o Entities/ --no-pluralize --force ``` \ No newline at end of file diff --git a/ChouBox.WebApi/Controllers/AccountController.cs b/ChouBox.WebApi/Controllers/AccountController.cs index 3e7b7a8..70c7ac8 100644 --- a/ChouBox.WebApi/Controllers/AccountController.cs +++ b/ChouBox.WebApi/Controllers/AccountController.cs @@ -3,6 +3,7 @@ using HuanMeng.DotNetCore.AttributeExtend; using HuanMeng.DotNetCore.Base; using HuanMeng.DotNetCore.Extensions; + using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -14,7 +15,7 @@ namespace ChouBox.WebApi.Controllers; /// [Route("api/v2/[controller]")] [ApiController] -public class AccountController(IServiceProvider serviceProvider) : ControllerBase +public class AccountController(IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor) : ControllerBase { @@ -25,9 +26,10 @@ public class AccountController(IServiceProvider serviceProvider) : ControllerBas /// [HttpPost] [Route("sendSms")] - public async Task SendPhoneAsync([FromForm]string phone) + [Message("验证码发送成功!")] + public async Task SendPhoneAsync([FromForm] string phone) { SMSBLL sms = new SMSBLL(serviceProvider); - return await sms.SendPhoneAsync(phone); + return await sms.SendPhoneAsync(phone); ; } } diff --git a/ChouBox.WebApi/Filters/ResultFormatFilter.cs b/ChouBox.WebApi/Filters/ResultFormatFilter.cs index 958a014..4023690 100644 --- a/ChouBox.WebApi/Filters/ResultFormatFilter.cs +++ b/ChouBox.WebApi/Filters/ResultFormatFilter.cs @@ -56,15 +56,112 @@ namespace ChouBox.WebApi.Filters context.Result = new JsonResult(response); } // 处理其他类型的结果(如ActionResult) - else if (context.Result is ObjectResult objResult && objResult.Value is string stringValue) + else if (context.Result is ObjectResult objResult) { - var response = new BaseResponse + // 处理基础类型 + if (objResult.Value is string stringValue) { - Status = 1, - Msg = customMessage, - Data = stringValue - }; - context.Result = new JsonResult(response); + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = stringValue + }; + context.Result = new JsonResult(response); + } + else if (objResult.Value is int intValue) + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = intValue + }; + context.Result = new JsonResult(response); + } + else if (objResult.Value is bool boolValue) + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = boolValue + }; + context.Result = new JsonResult(response); + } + else if (objResult.Value is long longValue) + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = longValue + }; + context.Result = new JsonResult(response); + } + else if (objResult.Value is double doubleValue) + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = doubleValue + }; + context.Result = new JsonResult(response); + } + else if (objResult.Value is decimal decimalValue) + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = decimalValue + }; + context.Result = new JsonResult(response); + } + else if (objResult.Value is DateTime dateTimeValue) + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = dateTimeValue + }; + context.Result = new JsonResult(response); + } + else if (objResult.Value is Guid guidValue) + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = guidValue + }; + context.Result = new JsonResult(response); + } + // 如果不是基础类型,则直接使用object类型的BaseResponse + else if (objResult.Value != null) + { + // 处理其他非基础类型 + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = objResult.Value + }; + context.Result = new JsonResult(response); + } + // 处理null值 + else + { + var response = new BaseResponse + { + Status = 1, + Msg = customMessage, + Data = null + }; + context.Result = new JsonResult(response); + } } } diff --git a/ChouBox.WebApi/Program.cs b/ChouBox.WebApi/Program.cs index 27bc983..157fa31 100644 --- a/ChouBox.WebApi/Program.cs +++ b/ChouBox.WebApi/Program.cs @@ -86,8 +86,8 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddScoped(); #region 添加跨域 -var _myAllowSpecificOrigins = "_myAllowSpecificOrigins"; -builder.Services.AddCustomCors(_myAllowSpecificOrigins); +//var _myAllowSpecificOrigins = "_myAllowSpecificOrigins"; +//builder.Services.AddCustomCors(_myAllowSpecificOrigins); #endregion var app = builder.Build(); @@ -102,7 +102,7 @@ app.UseHttpsRedirection(); app.UseAuthorization(); //使用跨域 -app.UseCors(_myAllowSpecificOrigins); +//app.UseCors(_myAllowSpecificOrigins); //执行扩展中间件 app.UseMiddlewareAll(); diff --git a/ChouBox.WebApi/appsettings.Development.json b/ChouBox.WebApi/appsettings.Development.json index 02a4b21..4087c21 100644 --- a/ChouBox.WebApi/appsettings.Development.json +++ b/ChouBox.WebApi/appsettings.Development.json @@ -8,5 +8,12 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, //服务器配置 + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://*:81" + } + } } } diff --git a/Utile/HuanMeng.DotNetCore/Base/CustomException.cs b/Utile/HuanMeng.DotNetCore/Base/CustomException.cs new file mode 100644 index 0000000..cafe1be --- /dev/null +++ b/Utile/HuanMeng.DotNetCore/Base/CustomException.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.DotNetCore.Base +{ + /// + /// 自定义异常类,用于特定业务场景 + /// 该异常被捕获时不会记录日志,适用于普通业务逻辑错误 + /// + public class CustomException : Exception + { + /// + /// 错误参数名 + /// + public string ParamName { get; } + + /// + /// 创建自定义异常 + /// + /// 错误消息 + public CustomException(string message) : base(message) + { + ParamName = message; + } + + /// + /// 创建自定义异常 + /// + /// 错误消息 + /// 参数名 + public CustomException(string message, string paramName) : base(message) + { + ParamName = paramName; + } + + /// + /// 创建自定义异常 + /// + /// 错误消息 + /// 内部异常 + public CustomException(string message, Exception innerException) : base(message, innerException) + { + ParamName = message; + } + + /// + /// 创建自定义异常 + /// + /// 错误消息 + /// 参数名 + /// 内部异常 + public CustomException(string message, string paramName, Exception innerException) : base(message, innerException) + { + ParamName = paramName; + } + } +} \ No newline at end of file diff --git a/Utile/HuanMeng.DotNetCore/Base/ResponseCode.cs b/Utile/HuanMeng.DotNetCore/Base/ResponseCode.cs index 10a79d0..8b2a43a 100644 --- a/Utile/HuanMeng.DotNetCore/Base/ResponseCode.cs +++ b/Utile/HuanMeng.DotNetCore/Base/ResponseCode.cs @@ -12,7 +12,12 @@ public enum ResponseCode /// /// 成功 /// - Success = 0, + Success = 1, + + /// + /// 通用 + /// + Common = 0, /// /// 正在处理中 diff --git a/Utile/HuanMeng.DotNetCore/MiddlewareExtend/ExceptionMiddleware.cs b/Utile/HuanMeng.DotNetCore/MiddlewareExtend/ExceptionMiddleware.cs index 3ce1d62..6974f9f 100644 --- a/Utile/HuanMeng.DotNetCore/MiddlewareExtend/ExceptionMiddleware.cs +++ b/Utile/HuanMeng.DotNetCore/MiddlewareExtend/ExceptionMiddleware.cs @@ -36,7 +36,7 @@ namespace HuanMeng.DotNetCore.MiddlewareExtend context.Response.StatusCode = StatusCodes.Status200OK; - BaseResponse baseResponse = new BaseResponse(ResponseCode.ParamError, ex.ParamName ?? "参数错误", null) + BaseResponse baseResponse = new BaseResponse(ResponseCode.Common, ex.ParamName ?? "参数错误", null) { }; @@ -44,6 +44,15 @@ namespace HuanMeng.DotNetCore.MiddlewareExtend // 将异常信息写入 HTTP 响应 await context.Response.WriteAsync(baseResponse.ToString()); } + catch (CustomException ex) + { + // 对自定义异常的特殊处理,不记录日志 + context.Response.StatusCode = StatusCodes.Status200OK; + BaseResponse baseResponse = new BaseResponse(ResponseCode.Common, ex.ParamName ?? "", null); + context.Response.ContentType = "application/json; charset=utf-8"; + // 将异常信息写入 HTTP 响应 + await context.Response.WriteAsync(baseResponse.ToString()); + } catch (Exception ex) {