admin/src/Oms.Application/Shared/OmsFileUploadHelper.cs
zzh 341a318f0b feat: 客户发件地址与揽收订单,复用小包创建全链路。
新增地址簿 CRUD、pickup/create、订单 orderSource 筛选;附带包裹商品图片上传接口。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 18:58:48 +08:00

160 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Fake.Application;
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 Task<string> SaveRechargeScreenshotAsync(IFormFile? file, IWebHostEnvironment env) =>
SaveImageAsync(file, env, "recharge", "充值截图");
/// <summary>
/// 保存仓储 SKU 图片。
/// </summary>
public static Task<string> SaveWarehouseSkuImageAsync(IFormFile file, IWebHostEnvironment env) =>
SaveImageAsync(file, env, "sku", "SKU 图片");
/// <summary>
/// 保存包裹商品图片。
/// </summary>
public static Task<string> SaveWarehouseParcelImageAsync(IFormFile file, IWebHostEnvironment env) =>
SaveImageAsync(file, env, "parcel", "包裹商品图片");
private static async Task<string> SaveImageAsync(
IFormFile? file,
IWebHostEnvironment env,
string folder,
string label)
{
if (file == null || file.Length <= 0)
{
if (folder == "recharge")
{
return "0";
}
throw new BusinessException($"{label}不能为空");
}
if (file.Length > MaxImageBytes)
{
throw new BusinessException($"{label}大小不能超过 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($"{label}仅支持 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", folder, 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/{folder}/{monthFolder}/{fileName}".Replace('\\', '/');
}
private static readonly HashSet<string> ExcelExtensions =
new(StringComparer.OrdinalIgnoreCase) { ".xlsx", ".xls" };
private const long MaxOrderTemplateBytes = 10 * 1024 * 1024;
/// <summary>
/// 保存下单自定义模板样例文件。
/// </summary>
public static async Task<(string RelativePath, string FileName)> SaveOrderTemplateSampleAsync(
IFormFile file,
int customerId,
IWebHostEnvironment env)
{
if (file == null || file.Length <= 0)
{
throw new BusinessException("上传文件不能为空");
}
if (file.Length > MaxOrderTemplateBytes)
{
throw new BusinessException("模板文件大小不能超过 10MB");
}
var ext = Path.GetExtension(file.FileName);
if (string.IsNullOrWhiteSpace(ext) || !ExcelExtensions.Contains(ext))
{
throw new BusinessException("仅支持 .xlsx / .xls 文件");
}
var webRoot = env.WebRootPath;
if (string.IsNullOrWhiteSpace(webRoot))
{
webRoot = Path.Combine(env.ContentRootPath, "wwwroot");
}
var relativeDir = Path.Combine("uploads", "order-templates", customerId.ToString());
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);
}
var relativePath = $"/uploads/order-templates/{customerId}/{fileName}".Replace('\\', '/');
return (relativePath, Path.GetFileName(file.FileName));
}
public static string ResolveWebRootAbsolutePath(IWebHostEnvironment env, string relativePath)
{
var webRoot = env.WebRootPath;
if (string.IsNullOrWhiteSpace(webRoot))
{
webRoot = Path.Combine(env.ContentRootPath, "wwwroot");
}
var trimmed = relativePath.TrimStart('/').Replace('/', Path.DirectorySeparatorChar);
return Path.Combine(webRoot, trimmed);
}
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
};
}