Files
QuestionLibraryMQConsumer/QuestionLibraryMQConsumer/Handlers/BookCreatedHandler.cs
glz dcfdf18e85 refactor: 重构书籍实体与消息模型,新增OSS文件复制功能
1. 更新Book实体的Source字段为SourceId并修改数据库列映射
2. 修正BookCreatedMessage的中文注释与字段类型
3. 新增阿里云OSS配置类、OssService实现与接口
4. 新增OtherSystem实体用于存储外部系统配置
5. 在BookCreatedHandler中添加OSS文件跨桶复制逻辑
6. 新增Aliyun.OSS.SDK.NetCore依赖包
7. 配置OSS服务依赖注入
2026-05-20 15:53:32 +08:00

171 lines
6.8 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 QuestionLibraryMQConsumer.Data;
using QuestionLibraryMQConsumer.Entities;
using QuestionLibraryMQConsumer.Models;
using QuestionLibraryMQConsumer.Services;
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace QuestionLibraryMQConsumer.Handlers
{
public class BookCreatedHandler : IMessageHandler
{
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
private readonly ILogger<BookCreatedHandler> _logger;
private readonly QuestionLibraryDb _db;
private readonly IOssService _ossService;
public string RoutingKey => "questionLibrary.book.created";
public Type MessageType => typeof(BookCreatedMessage);
public bool RequiresManualAck => false;
public BookCreatedHandler(
ILogger<BookCreatedHandler> logger,
QuestionLibraryDb db,
IOssService ossService)
{
_logger = logger;
_db = db;
_ossService = ossService;
}
public async Task HandleJsonAsync(string messageJson, CancellationToken cancellationToken)
{
_logger.LogInformation("[Handler入口] HandleJsonAsync被调用,messageJson长度:{msg} messageJson长度: {Length}", messageJson, messageJson?.Length ?? 0);
var message = JsonSerializer.Deserialize<BookCreatedMessage>(messageJson, _jsonOptions);
if (message == null)
{
_logger.LogError("[Handler错误] 反序列化失败, messageJson: {Json}", messageJson);
throw new JsonException("Failed to deserialize BookCreatedMessage");
}
_logger.LogInformation("[Handler反序列化] 成功, MessageId: {MessageId}, BookId: {BookId}", message.MessageId, message.BookId);
await HandleAsync(message, cancellationToken);
}
public async Task HandleAsync(BookCreatedMessage message, CancellationToken cancellationToken)
{
_logger.LogInformation("处理书籍创建消息: {MessageId}, 书籍ID: {BookId}, 书籍名称: {BookName}",
message.MessageId, message.BookId, message.BookName);
var exists = await _db.Db.Queryable<Book>()
.AnyAsync(b => b.Id == message.BookId);
if (exists)
{
_logger.LogWarning("书籍 {BookId} 已存在,跳过创建", message.BookId);
return;
}
// 查询外部系统配置获取源bucket信息
var otherSystem = await _db.Db.Queryable<OtherSystem>()
.FirstAsync(s => s.Id == message.SourceId);
string pdfUrl = message.PdfUrl ?? string.Empty;
string coverUrl = message.CoverUrl ?? string.Empty;
string backCoverUrl = message.BackCoverUrl ?? string.Empty;
// 如果找到外部系统配置执行OSS跨bucket复制
if (otherSystem != null && !string.IsNullOrEmpty(otherSystem.BucketName) && !string.IsNullOrEmpty(otherSystem.Domain))
{
_logger.LogInformation("找到外部系统配置 SourceId: {SourceId}, Bucket: {Bucket}, Domain: {Domain}",
message.SourceId, otherSystem.BucketName, otherSystem.Domain);
// 并行复制所有URL
var copyTasks = new List<Task>();
if (!string.IsNullOrEmpty(pdfUrl))
{
copyTasks.Add(CopyUrlAsync("PdfUrl", pdfUrl, otherSystem, newUrl => pdfUrl = newUrl, cancellationToken));
}
if (!string.IsNullOrEmpty(coverUrl))
{
copyTasks.Add(CopyUrlAsync("CoverUrl", coverUrl, otherSystem, newUrl => coverUrl = newUrl, cancellationToken));
}
if (!string.IsNullOrEmpty(backCoverUrl))
{
copyTasks.Add(CopyUrlAsync("BackCoverUrl", backCoverUrl, otherSystem, newUrl => backCoverUrl = newUrl, cancellationToken));
}
if (copyTasks.Count > 0)
{
await Task.WhenAll(copyTasks);
}
}
else
{
_logger.LogWarning("未找到外部系统配置 SourceId: {SourceId}使用原始URL", message.SourceId);
}
var book = new Book
{
Id = message.BookId,
Name = message.BookName,
Title = message.Subtitle,
Width = message.PaperWidth,
Height = message.PaperHeight,
Cover = coverUrl,
PdfUrl = pdfUrl,
BackCover = backCoverUrl,
CreatedTime = DateTime.UtcNow,
UpdatedTime = DateTime.UtcNow,
SourceId = message.SourceId,
Status = (int)BookStatusEnum.MissCatalog
};
await _db.Db.Insertable(book).ExecuteCommandAsync();
_logger.LogInformation("书籍 {BookId} 创建成功", message.BookId);
}
private async Task CopyUrlAsync(string fieldName, string originalPath, OtherSystem otherSystem, Action<string> setNewPath, CancellationToken cancellationToken)
{
try
{
var newPath = await _ossService.CopyToOwnBucketAsync(originalPath, otherSystem.BucketName, cancellationToken);
setNewPath(newPath);
_logger.LogInformation("{FieldName} OSS复制成功: {OriginalPath} -> {NewPath}", fieldName, originalPath, newPath);
}
catch (Exception ex)
{
_logger.LogError(ex, "{FieldName} OSS复制失败使用原始路径: {OriginalPath}", fieldName, originalPath);
// 保持原始路径不变
}
}
public async Task HandleWithManualAckAsync(string messageJson, IChannel channel, ulong deliveryTag, CancellationToken cancellationToken)
{
var message = JsonSerializer.Deserialize<BookCreatedMessage>(messageJson, _jsonOptions);
if (message == null)
throw new JsonException("Failed to deserialize BookCreatedMessage");
try
{
await HandleAsync(message, cancellationToken);
await channel.BasicAckAsync(deliveryTag, false, CancellationToken.None);
_logger.LogInformation("消息 {MessageId} 处理成功并已确认", message.MessageId);
}
catch (Exception ex)
{
_logger.LogError(ex, "消息 {MessageId} 处理失败,拒绝消息", message.MessageId);
await channel.BasicNackAsync(deliveryTag, false, false, CancellationToken.None);
}
}
}
}