1. 新增并完善ResultCode枚举,补充标准HTTP状态码对应的业务状态码 2. 重构BusinessException,新增基于ResultCode的构造函数和ThrowIf扩展方法 3. 替换所有硬编码的HTTP状态码为统一的ResultCode枚举 4. 优化全局异常中间件,根据业务状态码映射对应HTTP状态码并规范化JSON响应 5. 修复OssImageHelper和AutoDotCodeConsumer中的OSS文件处理逻辑 6. 新增用户答题快照实体类 7. 清理废弃的宠物模块迁移脚本
112 lines
4.1 KiB
C#
112 lines
4.1 KiB
C#
using Microsoft.Extensions.Logging;
|
||
using QYZH.InteractiveMagazine.Common.Extensions;
|
||
using QYZH.InteractiveMagazine.Models.Common;
|
||
using QYZH.InteractiveMagazine.Models.Dto;
|
||
|
||
namespace QYZH.InteractiveMagazine.Infrastructure.OSS;
|
||
|
||
/// <summary>
|
||
/// OSS 图片搬运辅助类
|
||
/// 负责将 temp 临时目录下的文件搬运到正式目录
|
||
/// </summary>
|
||
public class OssImageHelper
|
||
{
|
||
private readonly OssService _ossService;
|
||
private readonly ILogger _logger;
|
||
|
||
public OssImageHelper(OssService ossService, ILogger logger)
|
||
{
|
||
_ossService = ossService;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断图片是否在 temp 临时目录
|
||
/// </summary>
|
||
public static bool IsTempImage(string? imageUrl)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(imageUrl)) return false;
|
||
return imageUrl.Contains("/temp/", StringComparison.OrdinalIgnoreCase);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将 temp 目录下的图片搬运到正式目录,返回正式路径
|
||
/// 如果图片不在 temp 目录,原样返回
|
||
/// </summary>
|
||
/// <param name="imageUrl">原始图片 URL(可能是完整 URL 或相对路径)</param>
|
||
/// <param name="formalFolder">正式目录前缀,如 "product/makeupcard/123" 或 "pet/skin/456"</param>
|
||
/// <returns>正式路径(相对路径),搬运失败时抛出异常</returns>
|
||
public async Task<string> MoveToFormalAsync(string imageUrl, string formalFolder)
|
||
{
|
||
if (!IsTempImage(imageUrl))
|
||
return imageUrl;
|
||
|
||
// 提取 OSS 相对路径(去掉域名前缀)
|
||
var sourcePath = imageUrl.RemoveDomain();
|
||
if (string.IsNullOrWhiteSpace(sourcePath))
|
||
{
|
||
_logger.LogWarning("图片路径解析失败,ImageUrl: {ImageUrl}", imageUrl);
|
||
return imageUrl;
|
||
}
|
||
|
||
// URL 解码(处理中文文件名)
|
||
sourcePath = Uri.UnescapeDataString(sourcePath);
|
||
|
||
// 提取文件名
|
||
var fileName = Path.GetFileName(sourcePath);
|
||
if (string.IsNullOrWhiteSpace(fileName))
|
||
{
|
||
_logger.LogWarning("无法从路径中提取文件名,Path: {Path}", sourcePath);
|
||
return imageUrl;
|
||
}
|
||
|
||
// 生成正式路径
|
||
var targetPath = $"{formalFolder}/{fileName}";
|
||
|
||
_logger.LogInformation("开始搬运图片,Source: {Source} -> Target: {Target}", sourcePath, targetPath);
|
||
|
||
try
|
||
{
|
||
// 拷贝文件到正式目录
|
||
var copyResult = _ossService.CopyObject(sourcePath, targetPath);
|
||
if (!copyResult)
|
||
{
|
||
_logger.LogError("OSS 图片拷贝失败,Source: {Source}, Target: {Target}", sourcePath, targetPath);
|
||
throw new Exception("OSS 图片拷贝失败");
|
||
}
|
||
|
||
// 删除临时文件(失败不影响主流程)
|
||
var deleteResult = _ossService.DeleteObject(sourcePath);
|
||
if (!deleteResult)
|
||
{
|
||
_logger.LogWarning("OSS 临时文件删除失败(不影响主流程),Source: {Source}", sourcePath);
|
||
}
|
||
|
||
_logger.LogInformation("图片搬运成功,Target: {Target}", targetPath);
|
||
return targetPath;
|
||
}
|
||
catch (Exception ex) when (ex is not BusinessException)
|
||
{
|
||
_logger.LogError(ex, "OSS 图片搬运异常,Source: {Source}, Target: {Target}", sourcePath, targetPath);
|
||
throw new BusinessException("图片搬运失败,请重试", ResultCode.GLOBAL_ERROR);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量搬运 temp 图片到正式目录
|
||
/// </summary>
|
||
/// <param name="imageUrls">原始图片 URL 列表</param>
|
||
/// <param name="formalFolder">正式目录前缀</param>
|
||
/// <returns>正式路径列表(顺序与输入一致)</returns>
|
||
public async Task<List<string>> MoveBatchToFormalAsync(IEnumerable<string> imageUrls, string formalFolder)
|
||
{
|
||
var results = new List<string>();
|
||
foreach (var url in imageUrls)
|
||
{
|
||
var formal = await MoveToFormalAsync(url, formalFolder);
|
||
results.Add(formal);
|
||
}
|
||
return results;
|
||
}
|
||
}
|