diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..97f6331
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ $(NoWarn);CS0105;CS0108;CS0168;CS0169;CS0618;CS1570;CS1572;CS1573;CS1591;CS8600;CS8601;CS8602;CS8603;CS8604;CS8618;CS8625;CS8629;CS8634;CS8714;CS9113
+
+
diff --git a/QYZH.InteractiveMagazine.IService/IUserJournalService.cs b/QYZH.InteractiveMagazine.IService/IUserJournalService.cs
index 38102c6..0528b2f 100644
--- a/QYZH.InteractiveMagazine.IService/IUserJournalService.cs
+++ b/QYZH.InteractiveMagazine.IService/IUserJournalService.cs
@@ -35,9 +35,17 @@ public interface IUserJournalService : IBaseService
/// 生成期刊二维码记录
///
/// 生成输入
- /// 操作人Id
+ /// 操作人名称
/// 二维码记录
- Task CreateQrCodeAsync(CreateUserJournalQrCodeInput input, long operatorId);
+ Task CreateQrCodeAsync(CreateUserJournalQrCodeInput input, string operatorName);
+
+ ///
+ /// 批量提交期刊二维码生成任务
+ ///
+ /// 生成输入
+ /// 操作人名称
+ /// 提交结果
+ Task CreateQrCodesAsync(CreateUserJournalQrCodeInput input, string operatorName);
///
/// 分页查询期刊二维码记录
@@ -53,11 +61,18 @@ public interface IUserJournalService : IBaseService
/// 二维码记录
Task GetQrCodeDetailAsync(long id);
+ ///
+ /// 根据期刊Id获取未绑定用户的二维码列表
+ ///
+ /// 期刊Id
+ /// 未绑定二维码列表
+ Task> GetUnboundQrCodesByJournalIdAsync(long journalId);
+
///
/// 删除未绑定的期刊二维码记录
///
/// 删除输入
- /// 操作人Id
+ /// 操作人名称
/// 是否成功
- Task DeleteQrCodeAsync(DeleteUserJournalQrCodeInput input, long operatorId);
+ Task DeleteQrCodeAsync(DeleteUserJournalQrCodeInput input, string operatorName);
}
diff --git a/QYZH.InteractiveMagazine.Models/Dto/Journal/BindJournalDto.cs b/QYZH.InteractiveMagazine.Models/Dto/Journal/BindJournalDto.cs
index 909874d..0907687 100644
--- a/QYZH.InteractiveMagazine.Models/Dto/Journal/BindJournalDto.cs
+++ b/QYZH.InteractiveMagazine.Models/Dto/Journal/BindJournalDto.cs
@@ -117,11 +117,67 @@ public class CreateUserJournalQrCodeInput
///
public long JournalId { get; set; }
+ ///
+ /// 生成数量
+ ///
+ public int Count { get; set; } = 1;
+}
+
+///
+/// 生成期刊二维码提交结果
+///
+public class CreateUserJournalQrCodeOutput
+{
+ ///
+ /// 期刊Id
+ ///
+ public long JournalId { get; set; }
+
+ ///
+ /// 请求生成数量
+ ///
+ public int RequestedCount { get; set; }
+
+ ///
+ /// 已提交生成数量
+ ///
+ public int AcceptedCount { get; set; }
+
+ ///
+ /// 二维码记录Id列表
+ ///
+ public List RecordIds { get; set; } = [];
+
+ ///
+ /// 是否后台生成
+ ///
+ public bool IsAsync { get; set; }
+
+ ///
+ /// 提示信息
+ ///
+ public string Message { get; set; } = string.Empty;
}
///
/// 期刊二维码查询输入DTO
///
+public class GenerateUserJournalQrCodeMessage
+{
+ ///
+ /// 二维码记录Id列表
+ ///
+ public List RecordIds { get; set; } = [];
+
+ ///
+ /// 操作人名称
+ ///
+ public string OperatorName { get; set; } = string.Empty;
+}
+
+///
+/// 鏈熷垔浜岀淮鐮佹煡璇㈣緭鍏TO
+///
public class UserJournalQrCodeQueryInput : PageQueryModel
{
///
diff --git a/QYZH.InteractiveMagazine.Models/Enum/UserJournalStatusEnum.cs b/QYZH.InteractiveMagazine.Models/Enum/UserJournalStatusEnum.cs
index a7c77ea..56035a0 100644
--- a/QYZH.InteractiveMagazine.Models/Enum/UserJournalStatusEnum.cs
+++ b/QYZH.InteractiveMagazine.Models/Enum/UserJournalStatusEnum.cs
@@ -17,5 +17,17 @@ public enum UserJournalStatusEnum
/// 正常
///
[Description("正常")]
- Active = 1
+ Active = 1,
+
+ ///
+ /// 生成中
+ ///
+ [Description("生成中")]
+ Generating = 2,
+
+ ///
+ /// 生成失败
+ ///
+ [Description("生成失败")]
+ Failed = 3
}
diff --git a/QYZH.InteractiveMagazine.Repository/Core/SqlSugarExtension.cs b/QYZH.InteractiveMagazine.Repository/Core/SqlSugarExtension.cs
index 47defe5..7e94779 100644
--- a/QYZH.InteractiveMagazine.Repository/Core/SqlSugarExtension.cs
+++ b/QYZH.InteractiveMagazine.Repository/Core/SqlSugarExtension.cs
@@ -1,7 +1,10 @@
using QYZH.InteractiveMagazine.Models.Entity;
+using Microsoft.AspNetCore.Http;
+using QYZH.InteractiveMagazine.Infrastructure.Context;
using SqlSugar;
using System.Linq.Expressions;
using System.Reflection;
+using System.Security.Claims;
using Yitter.IdGenerator;
@@ -117,14 +120,18 @@ namespace QYZH.InteractiveMagazine.Repository.Core
db.Aop.DataExecuting = (oldValue, entityInfo) =>
{
var entityValue = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue)?.ToString();
- var currnetUserName = "";
+ var currentUserName = GetCurrentUserName();
/*** inset生效 ***/
if (entityInfo.OperationType == DataFilterType.InsertByObject)
{
if (entityInfo.PropertyName == "CreatedAt" && (string.IsNullOrWhiteSpace(entityValue) || entityValue == DateTime.MinValue.ToString()))
entityInfo.SetValue(DateTime.Now);//修改CreateTime字段
- else if (entityInfo.PropertyName == "CreatedBy" && (string.IsNullOrWhiteSpace(entityValue)))
- entityInfo.SetValue(currnetUserName);//修改创建人字段
+ else if (entityInfo.PropertyName == "CreatedBy" && ShouldSetOperatorName(entityValue, currentUserName))
+ entityInfo.SetValue(currentUserName);//修改创建人字段
+ else if (entityInfo.PropertyName == "UpdatedAt" && (string.IsNullOrWhiteSpace(entityValue) || entityValue == DateTime.MinValue.ToString()))
+ entityInfo.SetValue(DateTime.Now);//修改UpdatedTime字段
+ else if (entityInfo.PropertyName == "UpdatedBy" && ShouldSetOperatorName(entityValue, currentUserName))
+ entityInfo.SetValue(currentUserName);//修改更新人字段
else if (entityInfo.PropertyName == "IsDeleted" && string.IsNullOrWhiteSpace(entityValue))
entityInfo.SetValue("0");//修改CreateTime字段
}
@@ -134,8 +141,8 @@ namespace QYZH.InteractiveMagazine.Repository.Core
{
if (entityInfo.PropertyName == "UpdatedAt" && (string.IsNullOrWhiteSpace(entityValue) || entityValue == DateTime.MinValue.ToString()))
entityInfo.SetValue(DateTime.Now);//修改UpdatedTime字段
- else if (entityInfo.PropertyName == "UpdatedBy" && (string.IsNullOrWhiteSpace(entityValue) || entityValue == "0"))
- entityInfo.SetValue(currnetUserName);//修改更新人字段
+ else if (entityInfo.PropertyName == "UpdatedBy" && ShouldSetOperatorName(entityValue, currentUserName))
+ entityInfo.SetValue(currentUserName);//修改更新人字段
}
};
return db;
@@ -143,6 +150,39 @@ namespace QYZH.InteractiveMagazine.Repository.Core
}
+ private static string GetCurrentUserName()
+ {
+ try
+ {
+ var httpContextAccessor = ServiceContext.ServiceProvider?.GetService(typeof(IHttpContextAccessor)) as IHttpContextAccessor;
+ var user = httpContextAccessor?.HttpContext?.User;
+ var userName = user?.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value
+ ?? user?.Identity?.Name;
+
+ return string.IsNullOrWhiteSpace(userName) ? "System" : userName;
+ }
+ catch
+ {
+ return "System";
+ }
+ }
+
+ private static bool ShouldSetOperatorName(string? entityValue, string currentUserName)
+ {
+ if (string.IsNullOrWhiteSpace(entityValue) || entityValue == "0")
+ {
+ return true;
+ }
+
+ if (currentUserName != "System" && long.TryParse(entityValue, out _))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
///
/// 把一个字符串转成驼峰规则的字符串
///
diff --git a/QYZH.InteractiveMagazine.Service/UserJournalService.cs b/QYZH.InteractiveMagazine.Service/UserJournalService.cs
index d3d7847..e5af33c 100644
--- a/QYZH.InteractiveMagazine.Service/UserJournalService.cs
+++ b/QYZH.InteractiveMagazine.Service/UserJournalService.cs
@@ -29,6 +29,9 @@ public class UserJournalService(
private const string JournalExchange = "ex.journal";
private const string BindJournalQueue = "mq.journal.bindUser";
private const string BindJournalRoutingKey = "rk.journal.bindUser";
+ private const string QrCodeGenerateQueue = "mq.journal.qrcode.generate";
+ private const string QrCodeGenerateRoutingKey = "rk.journal.qrcode.generate";
+ private const int MaxBatchQrCodeCount = 500;
///
/// 用户绑定期刊(扫码绑定)
@@ -137,7 +140,7 @@ public class UserJournalService(
///
/// 生成期刊二维码记录
///
- public async Task CreateQrCodeAsync(CreateUserJournalQrCodeInput input, long operatorId)
+ public async Task CreateQrCodeAsync(CreateUserJournalQrCodeInput input, string operatorName)
{
if (input.JournalId <= 0)
{
@@ -162,9 +165,9 @@ public class UserJournalService(
Type = 0,
Status = (int)UserJournalStatusEnum.Active,
IsDeleted = false,
- CreatedBy = operatorId.ToString(),
+ CreatedBy = operatorName,
CreatedAt = DateTime.Now,
- UpdatedBy = operatorId.ToString(),
+ UpdatedBy = operatorName,
UpdatedAt = DateTime.Now
};
@@ -191,6 +194,88 @@ public class UserJournalService(
///
/// 分页查询期刊二维码记录
///
+ public async Task CreateQrCodesAsync(CreateUserJournalQrCodeInput input, string operatorName)
+ {
+ if (input.JournalId <= 0)
+ {
+ throw new BusinessException("期刊Id不能为空", ResultCode.BAD_REQUEST);
+ }
+
+ if (input.Count <= 0 || input.Count > MaxBatchQrCodeCount)
+ {
+ throw new BusinessException($"生成数量必须在1-{MaxBatchQrCodeCount}之间", ResultCode.BAD_REQUEST);
+ }
+
+ var journal = await journalRepository.GetByIdAsync(input.JournalId);
+ if (journal == null || journal.IsDeleted)
+ {
+ throw new BusinessException("期刊不存在", ResultCode.NOT_FOUND);
+ }
+
+ if (journal.Status != (int)JournalStatusEnum.Published)
+ {
+ throw new BusinessException("该期刊暂未发布,无法生成二维码", ResultCode.UNPROCESSABLE_ENTITY);
+ }
+
+ var now = DateTime.Now;
+ var records = Enumerable.Range(0, input.Count)
+ .Select(_ => new UserJournal
+ {
+ UserId = null,
+ JournalId = input.JournalId,
+ Type = 0,
+ Status = (int)UserJournalStatusEnum.Generating,
+ IsDeleted = false,
+ CreatedBy = operatorName,
+ CreatedAt = now,
+ UpdatedBy = operatorName,
+ UpdatedAt = now
+ })
+ .ToList();
+
+ var insertCount = await userJournalRepository.Context.Insertable(records).ExecuteCommandAsync();
+ if (insertCount <= 0)
+ {
+ throw new BusinessException("提交二维码生成任务失败,请稍后重试", ResultCode.GLOBAL_ERROR);
+ }
+
+ var recordIds = records.Select(r => r.Id).ToList();
+ var messageSent = await rabbitMqService.SendAsync(new RabbitMQSendParam
+ {
+ Exchange = JournalExchange,
+ Queue = QrCodeGenerateQueue,
+ RoutingKey = QrCodeGenerateRoutingKey,
+ Data = new GenerateUserJournalQrCodeMessage
+ {
+ RecordIds = recordIds,
+ OperatorName = operatorName
+ }
+ });
+
+ if (!messageSent)
+ {
+ await userJournalRepository.Updateable()
+ .SetColumns(uj => uj.Status == (int)UserJournalStatusEnum.Failed)
+ .SetColumns(uj => uj.UpdatedBy == operatorName)
+ .SetColumns(uj => uj.UpdatedAt == DateTime.Now)
+ .Where(uj => recordIds.Contains(uj.Id) && !uj.IsDeleted && uj.Status == (int)UserJournalStatusEnum.Generating)
+ .ExecuteCommandAsync();
+
+ logger.LogError("发送期刊二维码生成消息失败,RecordIds: {RecordIds}", string.Join(",", recordIds));
+ throw new BusinessException("二维码生成任务提交失败,请稍后重试", ResultCode.GLOBAL_ERROR);
+ }
+
+ return new CreateUserJournalQrCodeOutput
+ {
+ JournalId = input.JournalId,
+ RequestedCount = input.Count,
+ AcceptedCount = insertCount,
+ RecordIds = recordIds,
+ IsAsync = true,
+ Message = "二维码生成任务已提交,请稍后查询未绑定二维码列表"
+ };
+ }
+
public async Task> GetQrCodePageListAsync(UserJournalQrCodeQueryInput input)
{
if (input.PageIndex <= 0)
@@ -237,7 +322,7 @@ public class UserJournalService(
///
/// 删除未绑定的期刊二维码记录
///
- public async Task DeleteQrCodeAsync(DeleteUserJournalQrCodeInput input, long operatorId)
+ public async Task DeleteQrCodeAsync(DeleteUserJournalQrCodeInput input, string operatorName)
{
if (input.Ids == null || input.Ids.Count == 0)
{
@@ -262,7 +347,7 @@ public class UserJournalService(
var updateCount = await userJournalRepository.Updateable()
.SetColumns(uj => uj.IsDeleted == true)
.SetColumns(uj => uj.Status == (int)UserJournalStatusEnum.Inactive)
- .SetColumns(uj => uj.UpdatedBy == operatorId.ToString())
+ .SetColumns(uj => uj.UpdatedBy == operatorName)
.SetColumns(uj => uj.UpdatedAt == DateTime.Now)
.Where(uj => ids.Contains(uj.Id) && !uj.IsDeleted && (uj.UserId == null || uj.UserId == 0))
.ExecuteCommandAsync();
@@ -270,6 +355,24 @@ public class UserJournalService(
return updateCount == ids.Count;
}
+ public async Task> GetUnboundQrCodesByJournalIdAsync(long journalId)
+ {
+ if (journalId <= 0)
+ {
+ throw new BusinessException("期刊Id不能为空", ResultCode.BAD_REQUEST);
+ }
+
+ var records = await userJournalRepository.Queryable()
+ .Where(uj => uj.JournalId == journalId && !uj.IsDeleted)
+ .Where(uj => uj.UserId == null || uj.UserId == 0)
+ .Where(uj => uj.Status == (int)UserJournalStatusEnum.Active)
+ .Where(uj => !string.IsNullOrEmpty(uj.QrCodeUrl))
+ .OrderByDescending(uj => uj.CreatedAt)
+ .ToListAsync();
+
+ return await BuildQrCodeOutputsAsync(records);
+ }
+
private async Task> BuildQrCodeOutputsAsync(List records)
{
if (records.Count == 0)
diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/UserJournalQrCodeController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/UserJournalQrCodeController.cs
index 36591f3..9115b76 100644
--- a/QYZH.InteractiveMagazine.WebApi/Controllers/UserJournalQrCodeController.cs
+++ b/QYZH.InteractiveMagazine.WebApi/Controllers/UserJournalQrCodeController.cs
@@ -19,10 +19,22 @@ public class UserJournalQrCodeController(IUserJournalService userJournalService)
/// 生成输入
/// 二维码记录
[HttpPost("add")]
- public async Task> AddAsync([FromBody] CreateUserJournalQrCodeInput input)
+ public async Task> AddAsync([FromBody] CreateUserJournalQrCodeInput input)
{
- var result = await userJournalService.CreateQrCodeAsync(input, GetCurrentUserId() ?? 0);
- return BaseResponse.Success(result);
+ var result = await userJournalService.CreateQrCodesAsync(input, GetCurrentUserName() ?? "System");
+ return BaseResponse.Success(result);
+ }
+
+ ///
+ /// 根据期刊Id获取未绑定用户的二维码地址
+ ///
+ /// 期刊Id
+ /// 未绑定二维码列表
+ [HttpGet("unbound/{journalId:long}")]
+ public async Task>> GetUnboundByJournalIdAsync(long journalId)
+ {
+ var result = await userJournalService.GetUnboundQrCodesByJournalIdAsync(journalId);
+ return BaseResponse>.Success(result);
}
///
@@ -57,7 +69,7 @@ public class UserJournalQrCodeController(IUserJournalService userJournalService)
[HttpPost("delete")]
public async Task> DeleteAsync([FromBody] DeleteUserJournalQrCodeInput input)
{
- var result = await userJournalService.DeleteQrCodeAsync(input, GetCurrentUserId() ?? 0);
+ var result = await userJournalService.DeleteQrCodeAsync(input, GetCurrentUserName() ?? "System");
return BaseResponse.Success(result);
}
}
diff --git a/QYZH.InteractiveMagazine.WorkService/Consumers/UserJournalQrCodeGenerateConsumer.cs b/QYZH.InteractiveMagazine.WorkService/Consumers/UserJournalQrCodeGenerateConsumer.cs
new file mode 100644
index 0000000..02d20eb
--- /dev/null
+++ b/QYZH.InteractiveMagazine.WorkService/Consumers/UserJournalQrCodeGenerateConsumer.cs
@@ -0,0 +1,129 @@
+using QYZH.InteractiveMagazine.Common.Helpers;
+using QYZH.InteractiveMagazine.Infrastructure.OSS;
+using QYZH.InteractiveMagazine.Models.Common;
+using QYZH.InteractiveMagazine.Models.Dto;
+using QYZH.InteractiveMagazine.Models.Entity;
+using QYZH.InteractiveMagazine.Models.Enum;
+using SqlSugar;
+using System.Text;
+using System.Text.Json;
+
+namespace QYZH.InteractiveMagazine.WorkService.Consumers;
+
+///
+/// 期刊二维码生成消费者
+///
+public class UserJournalQrCodeGenerateConsumer(
+ ILogger logger,
+ IServiceScopeFactory scopeFactory,
+ OssService ossService) : IQueueConsumer
+{
+ public string Exchange => "ex.journal";
+
+ public string QueueName => "mq.journal.qrcode.generate";
+
+ public string RoutingKey => "rk.journal.qrcode.generate";
+
+ public async Task HandleAsync(byte[] message, CancellationToken cancellationToken = default)
+ {
+ var content = Encoding.UTF8.GetString(message);
+ logger.LogInformation("收到期刊二维码生成消息: {Message}", content);
+
+ var data = JsonSerializer.Deserialize(content, new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true
+ }) ?? throw new InvalidOperationException("期刊二维码生成消息为空");
+
+ if (data.RecordIds.Count == 0)
+ {
+ logger.LogWarning("期刊二维码生成消息缺少记录Id");
+ return;
+ }
+
+ using var scope = scopeFactory.CreateScope();
+ var client = scope.ServiceProvider.GetRequiredService();
+ var operatorName = string.IsNullOrWhiteSpace(data.OperatorName) ? "System" : data.OperatorName;
+
+ foreach (var recordId in data.RecordIds.Distinct())
+ {
+ await GenerateQrCodeImageAsync(client, recordId, operatorName, cancellationToken);
+ }
+ }
+
+ public async Task OnErrorAsync(byte[] message, Exception exception)
+ {
+ logger.LogError(exception, "处理期刊二维码生成消息失败: {Message}", Encoding.UTF8.GetString(message));
+
+ try
+ {
+ var data = JsonSerializer.Deserialize(Encoding.UTF8.GetString(message), new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true
+ });
+
+ if (data?.RecordIds.Count > 0)
+ {
+ using var scope = scopeFactory.CreateScope();
+ var client = scope.ServiceProvider.GetRequiredService();
+ var operatorName = string.IsNullOrWhiteSpace(data.OperatorName) ? "System" : data.OperatorName;
+ await client.Updateable()
+ .SetColumns(uj => uj.Status == (int)UserJournalStatusEnum.Failed)
+ .SetColumns(uj => uj.UpdatedBy == operatorName)
+ .SetColumns(uj => uj.UpdatedAt == DateTime.Now)
+ .Where(uj => data.RecordIds.Contains(uj.Id) && !uj.IsDeleted && uj.Status == (int)UserJournalStatusEnum.Generating)
+ .ExecuteCommandAsync();
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "标记期刊二维码生成失败状态异常");
+ }
+ }
+
+ private async Task GenerateQrCodeImageAsync(ISqlSugarClient client, long recordId, string operatorName, CancellationToken cancellationToken)
+ {
+ try
+ {
+ var record = await client.Queryable()
+ .Where(uj => uj.Id == recordId && !uj.IsDeleted)
+ .FirstAsync(cancellationToken);
+
+ if (record == null || record.Status != (int)UserJournalStatusEnum.Generating)
+ {
+ return;
+ }
+
+ var qrCodeContent = BuildQrCodeContent(record.JournalId, record.Id);
+ var qrCodeKey = $"journal/qrcode/{record.JournalId}/{record.Id}.png";
+ using var qrCodeStream = new MemoryStream(QrCodeHelper.GeneratePng(qrCodeContent));
+ var uploadedKey = ossService.PutObject(qrCodeKey, qrCodeStream);
+ if (string.IsNullOrWhiteSpace(uploadedKey))
+ {
+ throw new BusinessException("二维码图片上传失败", ResultCode.GLOBAL_ERROR);
+ }
+
+ await client.Updateable()
+ .SetColumns(uj => uj.QrCodeUrl == uploadedKey)
+ .SetColumns(uj => uj.Status == (int)UserJournalStatusEnum.Active)
+ .SetColumns(uj => uj.UpdatedBy == operatorName)
+ .SetColumns(uj => uj.UpdatedAt == DateTime.Now)
+ .Where(uj => uj.Id == recordId && !uj.IsDeleted && uj.Status == (int)UserJournalStatusEnum.Generating)
+ .ExecuteCommandAsync(cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "生成期刊二维码失败,RecordId: {RecordId}", recordId);
+ await client.Updateable()
+ .SetColumns(uj => uj.Status == (int)UserJournalStatusEnum.Failed)
+ .SetColumns(uj => uj.UpdatedBy == operatorName)
+ .SetColumns(uj => uj.UpdatedAt == DateTime.Now)
+ .Where(uj => uj.Id == recordId && !uj.IsDeleted && uj.Status == (int)UserJournalStatusEnum.Generating)
+ .ExecuteCommandAsync(cancellationToken);
+ }
+ }
+
+ private static string BuildQrCodeContent(long journalId, long id)
+ {
+ return JsonSerializer.Serialize(new { JournalId = journalId, Id = id });
+ }
+}
diff --git a/QYZH.InteractiveMagazine.WorkService/Program.cs b/QYZH.InteractiveMagazine.WorkService/Program.cs
index 16ccff2..3df9ab6 100644
--- a/QYZH.InteractiveMagazine.WorkService/Program.cs
+++ b/QYZH.InteractiveMagazine.WorkService/Program.cs
@@ -77,6 +77,7 @@ builder.Services.AddRabbitMQ(builder.Configuration);
// 注册队列消费者(新增消费者只需实现 IQueueConsumer 并在此注册)
builder.Services.AddScoped();
+builder.Services.AddScoped();
// 注册消费者后台服务
builder.Services.AddHostedService();