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
+ };
+}