feat: 新增消息Outbox机制、雪花ID配置优化及多项功能完善

1.  新增数据库唯一约束和Message_Outbox表脚本
2.  新增雪花ID、Hangfire存储、MQ重试等配置实体
3.  重构各项目雪花ID生成逻辑,改为从配置读取WorkerId
4.  优化积分服务分页查询、用户背包更新逻辑
5.  新增JWT令牌Redis过期刷新逻辑
6.  完善RabbitMQ死信队列消息头信息
7.  新增可靠MQ消息发布服务和Outbox派发后台服务
8.  替换原有RabbitMQ直接发送为Outbox可靠发布
9.  优化签到服务逻辑,新增重复签到校验和补签卡扣减逻辑
10. 修复自动铺码消费逻辑,新增点阵页预占和释放机制
This commit is contained in:
glz
2026-07-10 10:44:00 +08:00
parent 685a8aeaec
commit cbdee5068a
33 changed files with 693 additions and 152 deletions

View File

@ -0,0 +1,38 @@
namespace QYZH.InteractiveMagazine.Models.Dto.RabbitMQ;
/// <summary>
/// MQ消息发布入参。
/// </summary>
/// <typeparam name="T">消息数据类型。</typeparam>
public class MessagePublishInput<T>
{
/// <summary>
/// 交换机。
/// </summary>
public string Exchange { get; set; } = string.Empty;
/// <summary>
/// 队列。
/// </summary>
public string Queue { get; set; } = string.Empty;
/// <summary>
/// 路由键。
/// </summary>
public string RoutingKey { get; set; } = string.Empty;
/// <summary>
/// 消息数据。
/// </summary>
public T Data { get; set; } = default!;
/// <summary>
/// 业务类型。
/// </summary>
public string BusinessType { get; set; } = string.Empty;
/// <summary>
/// 业务ID。
/// </summary>
public long BusinessId { get; set; }
}

View File

@ -0,0 +1,22 @@
namespace QYZH.InteractiveMagazine.Models.Dto.RabbitMQ;
/// <summary>
/// MQ消息发布结果。
/// </summary>
public class MessagePublishResult
{
/// <summary>
/// 是否成功。
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Outbox消息ID。
/// </summary>
public List<long> OutboxIds { get; set; } = [];
/// <summary>
/// 结果消息。
/// </summary>
public string Message { get; set; } = string.Empty;
}

View File

@ -0,0 +1,60 @@
using SqlSugar;
namespace QYZH.InteractiveMagazine.Models.Entity;
/// <summary>
/// 消息Outbox。
/// </summary>
[SugarTable("Message_Outbox")]
public partial class MessageOutbox : SqlSugarBaseEntity
{
/// <summary>
/// 交换机。
/// </summary>
public string Exchange { get; set; } = string.Empty;
/// <summary>
/// 队列。
/// </summary>
public string Queue { get; set; } = string.Empty;
/// <summary>
/// 路由键。
/// </summary>
public string RoutingKey { get; set; } = string.Empty;
/// <summary>
/// 消息内容JSON。
/// </summary>
public string Payload { get; set; } = string.Empty;
/// <summary>
/// 重试次数。
/// </summary>
public int RetryCount { get; set; }
/// <summary>
/// 下次重试时间。
/// </summary>
public DateTime? NextRetryAt { get; set; }
/// <summary>
/// 发送成功时间。
/// </summary>
public DateTime? SentAt { get; set; }
/// <summary>
/// 最后错误。
/// </summary>
public string? LastError { get; set; }
/// <summary>
/// 业务类型。
/// </summary>
public string BusinessType { get; set; } = string.Empty;
/// <summary>
/// 业务ID。
/// </summary>
public long BusinessId { get; set; }
}

View File

@ -0,0 +1,33 @@
using System.ComponentModel;
namespace QYZH.InteractiveMagazine.Models.Enum;
/// <summary>
/// 消息Outbox状态。
/// </summary>
public enum MessageOutboxStatusEnum
{
/// <summary>
/// 待发送。
/// </summary>
[Description("待发送")]
Pending = 0,
/// <summary>
/// 已发送。
/// </summary>
[Description("已发送")]
Sent = 1,
/// <summary>
/// 发送失败待重试。
/// </summary>
[Description("发送失败待重试")]
Failed = 2,
/// <summary>
/// 已放弃。
/// </summary>
[Description("已放弃")]
Abandoned = 3
}

View File

@ -0,0 +1,12 @@
namespace QYZH.InteractiveMagazine.Models.Settings;
/// <summary>
/// Hangfire存储配置。
/// </summary>
public class HangfireStorageSettings
{
/// <summary>
/// 存储类型,当前默认 Memory可配置为 Redis。
/// </summary>
public string StorageType { get; set; } = "Memory";
}

View File

@ -0,0 +1,17 @@
namespace QYZH.InteractiveMagazine.Models.Settings;
/// <summary>
/// RabbitMQ重试配置。
/// </summary>
public class RabbitMQRetrySettings
{
/// <summary>
/// 最大重试次数。
/// </summary>
public int MaxRetryCount { get; set; } = 3;
/// <summary>
/// 重试延迟毫秒数。
/// </summary>
public int RetryDelayMilliseconds { get; set; } = 30000;
}

View File

@ -0,0 +1,12 @@
namespace QYZH.InteractiveMagazine.Models.Settings;
/// <summary>
/// 雪花ID配置。
/// </summary>
public class SnowflakeSettings
{
/// <summary>
/// WorkerId生产部署时每个写库进程必须唯一。
/// </summary>
public ushort WorkerId { get; set; }
}