From f18dd7a470759d923349ad8735958fae12c3e10c Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Thu, 25 Jun 2026 12:14:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=85=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/Recharge/SubmitRechargeFormInput.cs | 31 ++++++++ .../Services/Recharge/RechargeService.cs | 25 ++++--- .../Shared/OmsFileUploadHelper.cs | 73 +++++++++++++++++++ 3 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 src/Oms.Application/Dtos/Recharge/SubmitRechargeFormInput.cs create mode 100644 src/Oms.Application/Shared/OmsFileUploadHelper.cs diff --git a/src/Oms.Application/Dtos/Recharge/SubmitRechargeFormInput.cs b/src/Oms.Application/Dtos/Recharge/SubmitRechargeFormInput.cs new file mode 100644 index 0000000..d5fc9dc --- /dev/null +++ b/src/Oms.Application/Dtos/Recharge/SubmitRechargeFormInput.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; +using Oms.Domain.RechargeAggregate.Enums; + +namespace Oms.Application.Dtos.Recharge; + +/// +/// 充值提交入参(multipart/form-data,截图字段名为 rechargeScreenshot)。 +/// +public class SubmitRechargeFormInput +{ + /// + /// 客户主键(t_customer.Id)。 + /// + public int? CustomerId { get; set; } + + [Range(typeof(decimal), "0.01", "999999999999")] + public decimal RechargeAmount { get; set; } + + [Required] + public PayType PayType { get; set; } + + [StringLength(512)] + public string? Remark { get; set; } + + /// + /// 充值截图文件。 + /// + [Required] + public IFormFile RechargeScreenshot { get; set; } = null!; +} diff --git a/src/Oms.Application/Services/Recharge/RechargeService.cs b/src/Oms.Application/Services/Recharge/RechargeService.cs index e1c44fd..e7382a9 100644 --- a/src/Oms.Application/Services/Recharge/RechargeService.cs +++ b/src/Oms.Application/Services/Recharge/RechargeService.cs @@ -1,5 +1,7 @@ using System.Linq.Expressions; using Fake.Application; +using Fake.Domain.Exceptions; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using MiniExcelLibs; using Oms.Application.Dtos.Recharge; @@ -17,20 +19,16 @@ public class RechargeService : ApplicationService { private IRechargeRecordRepository RechargeRecordRepository => LazyServiceProvider.GetRequiredService(); private ICustomerRepository CustomerRepository => LazyServiceProvider.GetRequiredService(); + private IWebHostEnvironment WebHostEnvironment => LazyServiceProvider.GetRequiredService(); /// /// 提交充值申请。 /// - /// 入参:充值提交参数。 + /// 入参:multipart 表单,截图字段 rechargeScreenshot。 /// 充值记录结果。 - /// - /// 核心业务逻辑: - /// 1. 校验入参与关联对象、唯一性等前置条件。 - /// 2. 组装目标实体及相关明细数据。 - /// 3. 写入审计字段并持久化结果。 - /// [HttpPost("/recharge/recharge/submit")] - public async Task SubmitAsync(SubmitRechargeInput input) + [Consumes("multipart/form-data")] + public async Task SubmitAsync([FromForm] SubmitRechargeFormInput input) { var customerId = input.CustomerId ?? CurrentUser.GetId(); if (customerId <= 0) @@ -44,6 +42,15 @@ public class RechargeService : ApplicationService throw new BusinessException("客户不存在"); } + if (input.RechargeAmount < 0.01M) + { + throw new BusinessException("充值金额不能小于 0.01"); + } + + var screenshotPath = await OmsFileUploadHelper.SaveRechargeScreenshotAsync( + input.RechargeScreenshot, + WebHostEnvironment); + var entity = new RechargeRecord { CustomerId = customer.Id, @@ -51,7 +58,7 @@ public class RechargeService : ApplicationService RechargeTime = DateTime.Now, PayType = input.PayType, RechargeAmount = input.RechargeAmount, - RechargeScreenshot = input.RechargeScreenshot, + RechargeScreenshot = screenshotPath, RechargeStatus = RechargeStatus.Pending, Remark = input.Remark }; diff --git a/src/Oms.Application/Shared/OmsFileUploadHelper.cs b/src/Oms.Application/Shared/OmsFileUploadHelper.cs new file mode 100644 index 0000000..eb28e95 --- /dev/null +++ b/src/Oms.Application/Shared/OmsFileUploadHelper.cs @@ -0,0 +1,73 @@ +using Fake.Domain.Exceptions; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; + +namespace Oms.Application.Shared; + +/// +/// OMS 上传文件落盘(wwwroot/uploads)。 +/// +public static class OmsFileUploadHelper +{ + private static readonly HashSet ImageExtensions = + new(StringComparer.OrdinalIgnoreCase) { ".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp" }; + + private const long MaxImageBytes = 5 * 1024 * 1024; + + /// + /// 保存充值截图,返回以 /uploads 开头的相对访问路径。 + /// + public static async Task SaveRechargeScreenshotAsync(IFormFile file, IWebHostEnvironment env) + { + if (file == null || file.Length <= 0) + { + throw new BusinessException("请上传充值截图"); + } + + if (file.Length > MaxImageBytes) + { + throw new BusinessException("充值截图大小不能超过 5MB"); + } + + var ext = Path.GetExtension(file.FileName); + if (string.IsNullOrWhiteSpace(ext) || !ImageExtensions.Contains(ext)) + { + ext = GuessExtensionFromContentType(file.ContentType) ?? ".jpg"; + } + + if (!ImageExtensions.Contains(ext)) + { + throw new BusinessException("充值截图仅支持 jpg、png、webp、gif、bmp 格式"); + } + + var webRoot = env.WebRootPath; + if (string.IsNullOrWhiteSpace(webRoot)) + { + webRoot = Path.Combine(env.ContentRootPath, "wwwroot"); + } + + var monthFolder = DateTime.Now.ToString("yyyyMM"); + var relativeDir = Path.Combine("uploads", "recharge", monthFolder); + var absoluteDir = Path.Combine(webRoot, relativeDir); + Directory.CreateDirectory(absoluteDir); + + var fileName = $"{Guid.NewGuid():N}{ext.ToLowerInvariant()}"; + var absolutePath = Path.Combine(absoluteDir, fileName); + await using (var stream = File.Create(absolutePath)) + { + await file.CopyToAsync(stream); + } + + return $"/uploads/recharge/{monthFolder}/{fileName}".Replace('\\', '/'); + } + + private static string? GuessExtensionFromContentType(string? contentType) => contentType?.ToLowerInvariant() switch + { + "image/jpeg" or "image/jpg" => ".jpg", + "image/png" => ".png", + "image/webp" => ".webp", + "image/gif" => ".gif", + "image/bmp" => ".bmp", + _ => null + }; +}