新增地址簿 CRUD、pickup/create、订单 orderSource 筛选;附带包裹商品图片上传接口。 Co-authored-by: Cursor <cursoragent@cursor.com>
160 lines
5.3 KiB
C#
160 lines
5.3 KiB
C#
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
|
||
};
|
||
}
|