修复充值

This commit is contained in:
zzh 2026-06-25 12:14:59 +08:00
parent 1c5a38ad73
commit f18dd7a470
3 changed files with 120 additions and 9 deletions

View File

@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http;
using Oms.Domain.RechargeAggregate.Enums;
namespace Oms.Application.Dtos.Recharge;
/// <summary>
/// 充值提交入参(multipart/form-data,截图字段名为 rechargeScreenshot)。
/// </summary>
public class SubmitRechargeFormInput
{
/// <summary>
/// 客户主键(t_customer.Id)。
/// </summary>
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; }
/// <summary>
/// 充值截图文件。
/// </summary>
[Required]
public IFormFile RechargeScreenshot { get; set; } = null!;
}

View File

@ -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<IRechargeRecordRepository>();
private ICustomerRepository CustomerRepository => LazyServiceProvider.GetRequiredService<ICustomerRepository>();
private IWebHostEnvironment WebHostEnvironment => LazyServiceProvider.GetRequiredService<IWebHostEnvironment>();
/// <summary>
/// 提交充值申请。
/// </summary>
/// <param name="input">入参:充值提交参数。</param>
/// <param name="input">入参:multipart 表单,截图字段 rechargeScreenshot。</param>
/// <returns>充值记录结果。</returns>
/// <remarks>
/// 核心业务逻辑:
/// 1. 校验入参与关联对象、唯一性等前置条件。
/// 2. 组装目标实体及相关明细数据。
/// 3. 写入审计字段并持久化结果。
/// </remarks>
[HttpPost("/recharge/recharge/submit")]
public async Task<RechargeRecordOutput> SubmitAsync(SubmitRechargeInput input)
[Consumes("multipart/form-data")]
public async Task<RechargeRecordOutput> SubmitAsync([FromForm] SubmitRechargeFormInput input)
{
var customerId = input.CustomerId ?? CurrentUser.GetId<int>();
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
};

View File

@ -0,0 +1,73 @@
using Fake.Domain.Exceptions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
namespace Oms.Application.Shared;
/// <summary>
/// OMS 上传文件落盘(wwwroot/uploads)。
/// </summary>
public static class OmsFileUploadHelper
{
private static readonly HashSet<string> ImageExtensions =
new(StringComparer.OrdinalIgnoreCase) { ".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp" };
private const long MaxImageBytes = 5 * 1024 * 1024;
/// <summary>
/// 保存充值截图,返回以 /uploads 开头的相对访问路径。
/// </summary>
public static async Task<string> 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
};
}