refactor: 优化项目配置、实体类和OSS服务

1. 调整RabbitMQ队列配置为新的业务队列
2. 修正Book实体类的列名映射为驼峰命名
3. 重构OSS服务的复制方法,支持自定义目标路径
4. 完善BookCreatedHandler,动态获取路由键并统一文件存储路径
This commit is contained in:
glz
2026-05-21 18:04:00 +08:00
parent 1a4087c27e
commit a8955953c4
6 changed files with 49 additions and 45 deletions

View File

@ -13,6 +13,11 @@ namespace QuestionLibraryMQConsumer.Entities
[SugarTable("book")] [SugarTable("book")]
public partial class Book : SqlSugarBaseEntity public partial class Book : SqlSugarBaseEntity
{ {
public Book()
{
}
/// <summary> /// <summary>
/// Desc:主键id /// Desc:主键id
/// Default: /// Default:
@ -34,7 +39,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:False /// Nullable:False
/// </summary> /// </summary>
[SugarColumn(ColumnName = "totalpage")] [SugarColumn(ColumnName = "totalPage")]
public int TotalPage { get; set; } public int TotalPage { get; set; }
/// <summary> /// <summary>
@ -42,7 +47,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:False /// Nullable:False
/// </summary> /// </summary>
[SugarColumn(ColumnName = "verifypage")] [SugarColumn(ColumnName = "verifyPage")]
public int VerifyPage { get; set; } public int VerifyPage { get; set; }
/// <summary> /// <summary>
@ -58,7 +63,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "pdfurl")] [SugarColumn(ColumnName = "pdfUrl")]
public string PdfUrl { get; set; } public string PdfUrl { get; set; }
/// <summary> /// <summary>
@ -106,7 +111,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "backcover")] [SugarColumn(ColumnName = "backCover")]
public string BackCover { get; set; } public string BackCover { get; set; }
/// <summary> /// <summary>
@ -114,7 +119,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "pdfpreviewurl")] [SugarColumn(ColumnName = "pdfPreviewUrl")]
public string PdfPreviewUrl { get; set; } public string PdfPreviewUrl { get; set; }
/// <summary> /// <summary>
@ -130,7 +135,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "downloadbookpagepdfname")] [SugarColumn(ColumnName = "downloadBookPagePdfName")]
public string Downloadbookpagepdfname { get; set; } public string Downloadbookpagepdfname { get; set; }
/// <summary> /// <summary>
@ -138,7 +143,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default:b'0' /// Default:b'0'
/// Nullable:False /// Nullable:False
/// </summary> /// </summary>
[SugarColumn(ColumnName = "is_deleted")] [SugarColumn(ColumnName = "isDeleted")]
public bool IsDeleted { get; set; } public bool IsDeleted { get; set; }
/// <summary> /// <summary>
@ -146,7 +151,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "created_by")] [SugarColumn(ColumnName = "createdBy")]
public long? CreatedBy { get; set; } public long? CreatedBy { get; set; }
/// <summary> /// <summary>
@ -154,7 +159,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:False /// Nullable:False
/// </summary> /// </summary>
[SugarColumn(ColumnName = "created_time")] [SugarColumn(ColumnName = "createdTime")]
public DateTime CreatedTime { get; set; } public DateTime CreatedTime { get; set; }
/// <summary> /// <summary>
@ -162,7 +167,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "updated_by")] [SugarColumn(ColumnName = "updatedBy")]
public long? UpdatedBy { get; set; } public long? UpdatedBy { get; set; }
/// <summary> /// <summary>
@ -170,7 +175,7 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "updated_time")] [SugarColumn(ColumnName = "updatedTime")]
public DateTime? UpdatedTime { get; set; } public DateTime? UpdatedTime { get; set; }
/// <summary> /// <summary>
@ -194,7 +199,8 @@ namespace QuestionLibraryMQConsumer.Entities
/// Default: /// Default:
/// Nullable:True /// Nullable:True
/// </summary> /// </summary>
[SugarColumn(ColumnName = "source_id")] [SugarColumn(ColumnName = "sourceId")]
public long SourceId { get; set; } public long SourceId { get; set; }
} }
} }

View File

@ -11,7 +11,7 @@ namespace QuestionLibraryMQConsumer.Entities
///其他系统 ///其他系统
///</summary> ///</summary>
[SugarTable("other_system")] [SugarTable("other_system")]
public partial class OtherSystem : SqlSugarBaseEntity public partial class OtherSystem
{ {
/// <summary> /// <summary>
/// Desc:主键id /// Desc:主键id

View File

@ -1,4 +1,6 @@
using QuestionLibraryMQConsumer.Data; using Microsoft.Extensions.Options;
using QuestionLibraryMQConsumer.Configuration;
using QuestionLibraryMQConsumer.Data;
using QuestionLibraryMQConsumer.Entities; using QuestionLibraryMQConsumer.Entities;
using QuestionLibraryMQConsumer.Models; using QuestionLibraryMQConsumer.Models;
using QuestionLibraryMQConsumer.Services; using QuestionLibraryMQConsumer.Services;
@ -6,6 +8,7 @@ using RabbitMQ.Client;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -22,19 +25,26 @@ namespace QuestionLibraryMQConsumer.Handlers
private readonly ILogger<BookCreatedHandler> _logger; private readonly ILogger<BookCreatedHandler> _logger;
private readonly QuestionLibraryDb _db; private readonly QuestionLibraryDb _db;
private readonly IOssService _ossService; private readonly IOssService _ossService;
private readonly string _routingKey;
public string RoutingKey => "questionLibrary.book.created"; public string RoutingKey => _routingKey;
public Type MessageType => typeof(BookCreatedMessage); public Type MessageType => typeof(BookCreatedMessage);
public bool RequiresManualAck => false; public bool RequiresManualAck => false;
public BookCreatedHandler( public BookCreatedHandler(
ILogger<BookCreatedHandler> logger, ILogger<BookCreatedHandler> logger,
QuestionLibraryDb db, QuestionLibraryDb db,
IOssService ossService) IOssService ossService,
IOptions<RabbitMqOptions> rabbitMqOptions)
{ {
_logger = logger; _logger = logger;
_db = db; _db = db;
_ossService = ossService; _ossService = ossService;
var handlerTypeName = nameof(BookCreatedHandler);
_routingKey = rabbitMqOptions.Value.Queues
.FirstOrDefault(q => q.HandlerType == handlerTypeName)
?.RoutingKey ?? string.Empty;
} }
public async Task HandleJsonAsync(string messageJson, CancellationToken cancellationToken) public async Task HandleJsonAsync(string messageJson, CancellationToken cancellationToken)
@ -86,17 +96,20 @@ namespace QuestionLibraryMQConsumer.Handlers
if (!string.IsNullOrEmpty(pdfUrl)) if (!string.IsNullOrEmpty(pdfUrl))
{ {
copyTasks.Add(CopyUrlAsync("PdfUrl", pdfUrl, otherSystem, newUrl => pdfUrl = newUrl, cancellationToken)); var pdfExt = Path.GetExtension(pdfUrl);
copyTasks.Add(CopyUrlAsync("PdfUrl", pdfUrl, $"book/{message.BookId}/book{pdfExt}", otherSystem, newUrl => pdfUrl = newUrl, cancellationToken));
} }
if (!string.IsNullOrEmpty(coverUrl)) if (!string.IsNullOrEmpty(coverUrl))
{ {
copyTasks.Add(CopyUrlAsync("CoverUrl", coverUrl, otherSystem, newUrl => coverUrl = newUrl, cancellationToken)); var coverExt = Path.GetExtension(coverUrl);
copyTasks.Add(CopyUrlAsync("CoverUrl", coverUrl, $"book/{message.BookId}/cover{coverExt}", otherSystem, newUrl => coverUrl = newUrl, cancellationToken));
} }
if (!string.IsNullOrEmpty(backCoverUrl)) if (!string.IsNullOrEmpty(backCoverUrl))
{ {
copyTasks.Add(CopyUrlAsync("BackCoverUrl", backCoverUrl, otherSystem, newUrl => backCoverUrl = newUrl, cancellationToken)); var backCoverExt = Path.GetExtension(backCoverUrl);
copyTasks.Add(CopyUrlAsync("BackCoverUrl", backCoverUrl, $"book/{message.BookId}/backCover{backCoverExt}", otherSystem, newUrl => backCoverUrl = newUrl, cancellationToken));
} }
if (copyTasks.Count > 0) if (copyTasks.Count > 0)
@ -130,11 +143,11 @@ namespace QuestionLibraryMQConsumer.Handlers
_logger.LogInformation("书籍 {BookId} 创建成功", message.BookId); _logger.LogInformation("书籍 {BookId} 创建成功", message.BookId);
} }
private async Task CopyUrlAsync(string fieldName, string originalPath, OtherSystem otherSystem, Action<string> setNewPath, CancellationToken cancellationToken) private async Task CopyUrlAsync(string fieldName, string originalPath, string targetPath, OtherSystem otherSystem, Action<string> setNewPath, CancellationToken cancellationToken)
{ {
try try
{ {
var newPath = await _ossService.CopyToOwnBucketAsync(originalPath, otherSystem.BucketName, cancellationToken); var newPath = await _ossService.CopyToOwnBucketAsync(originalPath, otherSystem.BucketName, targetPath, cancellationToken);
setNewPath(newPath); setNewPath(newPath);
_logger.LogInformation("{FieldName} OSS复制成功: {OriginalPath} -> {NewPath}", fieldName, originalPath, newPath); _logger.LogInformation("{FieldName} OSS复制成功: {OriginalPath} -> {NewPath}", fieldName, originalPath, newPath);
} }

View File

@ -9,5 +9,5 @@ public interface IOssService
/// <param name="sourceBucket">源bucket名称</param> /// <param name="sourceBucket">源bucket名称</param>
/// <param name="cancellationToken">取消令牌</param> /// <param name="cancellationToken">取消令牌</param>
/// <returns>复制后的新URL</returns> /// <returns>复制后的新URL</returns>
Task<string> CopyToOwnBucketAsync(string sourcePath, string sourceBucket, CancellationToken cancellationToken = default); Task<string> CopyToOwnBucketAsync(string sourcePath, string sourceBucket, string targetPath, CancellationToken cancellationToken = default);
} }

View File

@ -18,7 +18,7 @@ public class OssService : IOssService
_ossOptions = ossOptions.Value; _ossOptions = ossOptions.Value;
} }
public async Task<string> CopyToOwnBucketAsync(string sourcePath, string sourceBucket, CancellationToken cancellationToken = default) public async Task<string> CopyToOwnBucketAsync(string sourcePath, string sourceBucket, string targetPath, CancellationToken cancellationToken = default)
{ {
if (string.IsNullOrEmpty(sourcePath)) if (string.IsNullOrEmpty(sourcePath))
{ {
@ -34,13 +34,13 @@ public class OssService : IOssService
var client = new OssClient(_ossOptions.Endpoint, _ossOptions.AccessKeyID, _ossOptions.AccessKeySecret); var client = new OssClient(_ossOptions.Endpoint, _ossOptions.AccessKeyID, _ossOptions.AccessKeySecret);
// 执行跨bucket复制 // 执行跨bucket复制
var request = new CopyObjectRequest(sourceBucket, objectKey, _ossOptions.BucketName, objectKey); var request = new CopyObjectRequest(sourceBucket, objectKey, _ossOptions.BucketName, targetPath);
var result = client.CopyObject(request); var result = client.CopyObject(request);
_logger.LogInformation("OSS跨bucket复制成功: {SourceBucket}/{ObjectKey} -> {TargetBucket}/{TargetKey}, ETag: {ETag}", _logger.LogInformation("OSS跨bucket复制成功: {SourceBucket}/{ObjectKey} -> {TargetBucket}/{TargetKey}, ETag: {ETag}",
sourceBucket, objectKey, _ossOptions.BucketName, objectKey, result.ETag); sourceBucket, objectKey, _ossOptions.BucketName, targetPath, result.ETag);
return objectKey; return targetPath;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -10,25 +10,10 @@
"PrefetchCount": 10, "PrefetchCount": 10,
"Queues": [ "Queues": [
{ {
"QueueName": "questionLibrary.book.created.queue", "QueueName": "mq.book.push",
"ExchangeName": "icr.direct", "ExchangeName": "ex.book.direct",
"RoutingKey": "questionLibrary.book.created", "RoutingKey": "rk.book.push",
"Durable": true, "HandlerType": "BookCreatedHandler",
"Exclusive": false,
"AutoDelete": false
},
{
"QueueName": "questionLibrary.book.created.queue",
"ExchangeName": "question.library.exchange.dev",
"RoutingKey": "questionLibrary.book.created",
"Durable": true,
"Exclusive": false,
"AutoDelete": false
},
{
"QueueName": "questionLibrary.book.created.queue",
"ExchangeName": "question.library.exchange",
"RoutingKey": "questionLibrary.book.created",
"Durable": true, "Durable": true,
"Exclusive": false, "Exclusive": false,
"AutoDelete": false "AutoDelete": false