feat: 添加订单事件管道与状态流转命令
Some checks failed
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Failing after 12s

- 新增 OrderStatusChangedEvent、OrderUrgeEvent 事件类型
- 扩展 EventRoutingKeys(orders.status-changed、orders.urged)
- 丰富 OrderCreatedEvent(StoreId、Channel、DeliveryType 等字段)
- CreateOrderCommandHandler 注入 IEventPublisher 并发布事件
- 新增接单/拒单/出餐完成/确认送达 Command + Handler(4对)
- 新增 MassTransit Consumer(OrderCreated/StatusChanged/Urge → SignalR)
- Program.cs 注册 3 个 Consumer
This commit is contained in:
2026-02-27 13:08:41 +08:00
parent 7c06ac3e29
commit bb2ff6167e
16 changed files with 770 additions and 2 deletions

View File

@@ -0,0 +1,35 @@
using MassTransit;
using Microsoft.AspNetCore.SignalR;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.TenantApi.Hubs;
namespace TakeoutSaaS.TenantApi.Consumers;
/// <summary>
/// 订单创建事件消费者 — 推送新订单到看板。
/// </summary>
public sealed class OrderCreatedConsumer(IHubContext<OrderBoardHub> hubContext)
: IConsumer<OrderCreatedEvent>
{
/// <inheritdoc />
public async Task Consume(ConsumeContext<OrderCreatedEvent> context)
{
var e = context.Message;
var group = $"store:{e.TenantId}:{e.StoreId}";
// 1. 推送新订单到对应门店 Group
await hubContext.Clients.Group(group).SendAsync("NewOrder", new
{
e.OrderId,
e.OrderNo,
e.Amount,
e.StoreId,
e.Channel,
e.DeliveryType,
e.CustomerName,
e.ItemsSummary,
e.TableNo,
e.CreatedAt
}, context.CancellationToken);
}
}

View File

@@ -0,0 +1,35 @@
using MassTransit;
using Microsoft.AspNetCore.SignalR;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.TenantApi.Hubs;
namespace TakeoutSaaS.TenantApi.Consumers;
/// <summary>
/// 订单状态变更事件消费者 — 推送状态更新到看板。
/// </summary>
public sealed class OrderStatusChangedConsumer(IHubContext<OrderBoardHub> hubContext)
: IConsumer<OrderStatusChangedEvent>
{
/// <inheritdoc />
public async Task Consume(ConsumeContext<OrderStatusChangedEvent> context)
{
var e = context.Message;
var group = $"store:{e.TenantId}:{e.StoreId}";
// 1. 推送状态变更到对应门店 Group
await hubContext.Clients.Group(group).SendAsync("OrderStatusChanged", new
{
e.OrderId,
e.OrderNo,
e.OldStatus,
e.NewStatus,
e.Channel,
e.DeliveryType,
e.CustomerName,
e.ItemsSummary,
e.PaidAmount,
e.OccurredAt
}, context.CancellationToken);
}
}

View File

@@ -0,0 +1,29 @@
using MassTransit;
using Microsoft.AspNetCore.SignalR;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.TenantApi.Hubs;
namespace TakeoutSaaS.TenantApi.Consumers;
/// <summary>
/// 订单催单事件消费者 — 推送催单通知到看板。
/// </summary>
public sealed class OrderUrgeConsumer(IHubContext<OrderBoardHub> hubContext)
: IConsumer<OrderUrgeEvent>
{
/// <inheritdoc />
public async Task Consume(ConsumeContext<OrderUrgeEvent> context)
{
var e = context.Message;
var group = $"store:{e.TenantId}:{e.StoreId}";
// 1. 推送催单通知到对应门店 Group
await hubContext.Clients.Group(group).SendAsync("OrderUrged", new
{
e.OrderId,
e.OrderNo,
e.UrgeCount,
e.OccurredAt
}, context.CancellationToken);
}
}

View File

@@ -0,0 +1,25 @@
using MediatR;
using TakeoutSaaS.Application.App.Orders.Dto;
namespace TakeoutSaaS.Application.App.Orders.Commands;
/// <summary>
/// 接单命令。
/// </summary>
public sealed record AcceptOrderCommand : IRequest<OrderBoardCardDto>
{
/// <summary>
/// 订单标识。
/// </summary>
public required long OrderId { get; init; }
/// <summary>
/// 租户标识。
/// </summary>
public required long TenantId { get; init; }
/// <summary>
/// 操作人标识。
/// </summary>
public long? OperatorId { get; init; }
}

View File

@@ -0,0 +1,25 @@
using MediatR;
using TakeoutSaaS.Application.App.Orders.Dto;
namespace TakeoutSaaS.Application.App.Orders.Commands;
/// <summary>
/// 出餐完成命令。
/// </summary>
public sealed record CompletePreparationCommand : IRequest<OrderBoardCardDto>
{
/// <summary>
/// 订单标识。
/// </summary>
public required long OrderId { get; init; }
/// <summary>
/// 租户标识。
/// </summary>
public required long TenantId { get; init; }
/// <summary>
/// 操作人标识。
/// </summary>
public long? OperatorId { get; init; }
}

View File

@@ -0,0 +1,25 @@
using MediatR;
using TakeoutSaaS.Application.App.Orders.Dto;
namespace TakeoutSaaS.Application.App.Orders.Commands;
/// <summary>
/// 确认送达/取餐命令。
/// </summary>
public sealed record ConfirmDeliveryCommand : IRequest<OrderBoardCardDto>
{
/// <summary>
/// 订单标识。
/// </summary>
public required long OrderId { get; init; }
/// <summary>
/// 租户标识。
/// </summary>
public required long TenantId { get; init; }
/// <summary>
/// 操作人标识。
/// </summary>
public long? OperatorId { get; init; }
}

View File

@@ -0,0 +1,30 @@
using MediatR;
using TakeoutSaaS.Application.App.Orders.Dto;
namespace TakeoutSaaS.Application.App.Orders.Commands;
/// <summary>
/// 拒单命令。
/// </summary>
public sealed record RejectOrderCommand : IRequest<OrderBoardCardDto>
{
/// <summary>
/// 订单标识。
/// </summary>
public required long OrderId { get; init; }
/// <summary>
/// 租户标识。
/// </summary>
public required long TenantId { get; init; }
/// <summary>
/// 拒单原因。
/// </summary>
public required string Reason { get; init; }
/// <summary>
/// 操作人标识。
/// </summary>
public long? OperatorId { get; init; }
}

View File

@@ -0,0 +1,98 @@
using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Orders.Commands;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.Messaging;
using TakeoutSaaS.Application.Messaging.Abstractions;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Enums;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Ids;
namespace TakeoutSaaS.Application.App.Orders.Handlers;
/// <summary>
/// 接单命令处理器。
/// </summary>
public sealed class AcceptOrderCommandHandler(
IOrderRepository orderRepository,
IEventPublisher eventPublisher,
IIdGenerator idGenerator,
ILogger<AcceptOrderCommandHandler> logger)
: IRequestHandler<AcceptOrderCommand, OrderBoardCardDto>
{
/// <inheritdoc />
public async Task<OrderBoardCardDto> Handle(AcceptOrderCommand request, CancellationToken cancellationToken)
{
// 1. 查找订单
var order = await orderRepository.FindByIdAsync(request.OrderId, request.TenantId, cancellationToken)
?? throw new BusinessException(ErrorCodes.NotFound, "订单不存在");
// 2. 校验状态
if (order.Status != OrderStatus.AwaitingPreparation)
{
throw new BusinessException(ErrorCodes.BadRequest, $"当前状态 {order.Status} 不允许接单");
}
// 3. 更新状态
var oldStatus = order.Status;
order.Status = OrderStatus.InProgress;
// 4. 写入状态流转记录
var history = new OrderStatusHistory
{
Id = idGenerator.NextId(),
OrderId = order.Id,
TenantId = order.TenantId,
Status = OrderStatus.InProgress,
OperatorId = request.OperatorId,
Notes = "商户接单",
OccurredAt = DateTime.UtcNow
};
await orderRepository.UpdateOrderAsync(order, cancellationToken);
await orderRepository.AddStatusHistoryAsync(history, cancellationToken);
await orderRepository.SaveChangesAsync(cancellationToken);
// 5. 发布状态变更事件
await eventPublisher.PublishAsync(EventRoutingKeys.OrderStatusChanged, new OrderStatusChangedEvent
{
OrderId = order.Id,
OrderNo = order.OrderNo,
TenantId = order.TenantId,
StoreId = order.StoreId,
OldStatus = (int)oldStatus,
NewStatus = (int)order.Status,
Channel = (int)order.Channel,
DeliveryType = (int)order.DeliveryType,
CustomerName = order.CustomerName,
PaidAmount = order.PaidAmount,
OccurredAt = history.OccurredAt
}, cancellationToken);
// 6. 记录日志
logger.LogInformation("接单 {OrderNo} ({OrderId})", order.OrderNo, order.Id);
// 7. 返回看板卡片 DTO
return MapToCardDto(order);
}
private static OrderBoardCardDto MapToCardDto(Order order) => new()
{
Id = order.Id,
OrderNo = order.OrderNo,
StoreId = order.StoreId,
Channel = order.Channel,
DeliveryType = order.DeliveryType,
Status = order.Status,
CustomerName = order.CustomerName,
CustomerPhone = order.CustomerPhone,
TableNo = order.TableNo,
QueueNumber = order.QueueNumber,
PaidAmount = order.PaidAmount,
CreatedAt = order.CreatedAt
};
}

View File

@@ -0,0 +1,98 @@
using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Orders.Commands;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.Messaging;
using TakeoutSaaS.Application.Messaging.Abstractions;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Enums;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Ids;
namespace TakeoutSaaS.Application.App.Orders.Handlers;
/// <summary>
/// 出餐完成命令处理器。
/// </summary>
public sealed class CompletePreparationCommandHandler(
IOrderRepository orderRepository,
IEventPublisher eventPublisher,
IIdGenerator idGenerator,
ILogger<CompletePreparationCommandHandler> logger)
: IRequestHandler<CompletePreparationCommand, OrderBoardCardDto>
{
/// <inheritdoc />
public async Task<OrderBoardCardDto> Handle(CompletePreparationCommand request, CancellationToken cancellationToken)
{
// 1. 查找订单
var order = await orderRepository.FindByIdAsync(request.OrderId, request.TenantId, cancellationToken)
?? throw new BusinessException(ErrorCodes.NotFound, "订单不存在");
// 2. 校验状态
if (order.Status != OrderStatus.InProgress)
{
throw new BusinessException(ErrorCodes.BadRequest, $"当前状态 {order.Status} 不允许标记出餐完成");
}
// 3. 更新状态
var oldStatus = order.Status;
order.Status = OrderStatus.Ready;
// 4. 写入状态流转记录
var history = new OrderStatusHistory
{
Id = idGenerator.NextId(),
OrderId = order.Id,
TenantId = order.TenantId,
Status = OrderStatus.Ready,
OperatorId = request.OperatorId,
Notes = "出餐完成",
OccurredAt = DateTime.UtcNow
};
await orderRepository.UpdateOrderAsync(order, cancellationToken);
await orderRepository.AddStatusHistoryAsync(history, cancellationToken);
await orderRepository.SaveChangesAsync(cancellationToken);
// 5. 发布状态变更事件
await eventPublisher.PublishAsync(EventRoutingKeys.OrderStatusChanged, new OrderStatusChangedEvent
{
OrderId = order.Id,
OrderNo = order.OrderNo,
TenantId = order.TenantId,
StoreId = order.StoreId,
OldStatus = (int)oldStatus,
NewStatus = (int)order.Status,
Channel = (int)order.Channel,
DeliveryType = (int)order.DeliveryType,
CustomerName = order.CustomerName,
PaidAmount = order.PaidAmount,
OccurredAt = history.OccurredAt
}, cancellationToken);
// 6. 记录日志
logger.LogInformation("出餐完成 {OrderNo} ({OrderId})", order.OrderNo, order.Id);
// 7. 返回看板卡片 DTO
return MapToCardDto(order);
}
private static OrderBoardCardDto MapToCardDto(Order order) => new()
{
Id = order.Id,
OrderNo = order.OrderNo,
StoreId = order.StoreId,
Channel = order.Channel,
DeliveryType = order.DeliveryType,
Status = order.Status,
CustomerName = order.CustomerName,
CustomerPhone = order.CustomerPhone,
TableNo = order.TableNo,
QueueNumber = order.QueueNumber,
PaidAmount = order.PaidAmount,
CreatedAt = order.CreatedAt
};
}

View File

@@ -0,0 +1,99 @@
using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Orders.Commands;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.Messaging;
using TakeoutSaaS.Application.Messaging.Abstractions;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Enums;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Ids;
namespace TakeoutSaaS.Application.App.Orders.Handlers;
/// <summary>
/// 确认送达/取餐命令处理器。
/// </summary>
public sealed class ConfirmDeliveryCommandHandler(
IOrderRepository orderRepository,
IEventPublisher eventPublisher,
IIdGenerator idGenerator,
ILogger<ConfirmDeliveryCommandHandler> logger)
: IRequestHandler<ConfirmDeliveryCommand, OrderBoardCardDto>
{
/// <inheritdoc />
public async Task<OrderBoardCardDto> Handle(ConfirmDeliveryCommand request, CancellationToken cancellationToken)
{
// 1. 查找订单
var order = await orderRepository.FindByIdAsync(request.OrderId, request.TenantId, cancellationToken)
?? throw new BusinessException(ErrorCodes.NotFound, "订单不存在");
// 2. 校验状态
if (order.Status != OrderStatus.Ready)
{
throw new BusinessException(ErrorCodes.BadRequest, $"当前状态 {order.Status} 不允许确认送达");
}
// 3. 更新状态
var oldStatus = order.Status;
order.Status = OrderStatus.Completed;
order.FinishedAt = DateTime.UtcNow;
// 4. 写入状态流转记录
var history = new OrderStatusHistory
{
Id = idGenerator.NextId(),
OrderId = order.Id,
TenantId = order.TenantId,
Status = OrderStatus.Completed,
OperatorId = request.OperatorId,
Notes = "确认送达/取餐",
OccurredAt = DateTime.UtcNow
};
await orderRepository.UpdateOrderAsync(order, cancellationToken);
await orderRepository.AddStatusHistoryAsync(history, cancellationToken);
await orderRepository.SaveChangesAsync(cancellationToken);
// 5. 发布状态变更事件
await eventPublisher.PublishAsync(EventRoutingKeys.OrderStatusChanged, new OrderStatusChangedEvent
{
OrderId = order.Id,
OrderNo = order.OrderNo,
TenantId = order.TenantId,
StoreId = order.StoreId,
OldStatus = (int)oldStatus,
NewStatus = (int)order.Status,
Channel = (int)order.Channel,
DeliveryType = (int)order.DeliveryType,
CustomerName = order.CustomerName,
PaidAmount = order.PaidAmount,
OccurredAt = history.OccurredAt
}, cancellationToken);
// 6. 记录日志
logger.LogInformation("确认送达 {OrderNo} ({OrderId})", order.OrderNo, order.Id);
// 7. 返回看板卡片 DTO
return MapToCardDto(order);
}
private static OrderBoardCardDto MapToCardDto(Order order) => new()
{
Id = order.Id,
OrderNo = order.OrderNo,
StoreId = order.StoreId,
Channel = order.Channel,
DeliveryType = order.DeliveryType,
Status = order.Status,
CustomerName = order.CustomerName,
CustomerPhone = order.CustomerPhone,
TableNo = order.TableNo,
QueueNumber = order.QueueNumber,
PaidAmount = order.PaidAmount,
CreatedAt = order.CreatedAt
};
}

View File

@@ -2,6 +2,9 @@ using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Orders.Commands; using TakeoutSaaS.Application.App.Orders.Commands;
using TakeoutSaaS.Application.App.Orders.Dto; using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.Messaging;
using TakeoutSaaS.Application.Messaging.Abstractions;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.Domain.Orders.Entities; using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Repositories; using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Ids; using TakeoutSaaS.Shared.Abstractions.Ids;
@@ -14,6 +17,7 @@ namespace TakeoutSaaS.Application.App.Orders.Handlers;
public sealed class CreateOrderCommandHandler( public sealed class CreateOrderCommandHandler(
IOrderRepository orderRepository, IOrderRepository orderRepository,
IIdGenerator idGenerator, IIdGenerator idGenerator,
IEventPublisher eventPublisher,
ILogger<CreateOrderCommandHandler> logger) ILogger<CreateOrderCommandHandler> logger)
: IRequestHandler<CreateOrderCommand, OrderDto> : IRequestHandler<CreateOrderCommand, OrderDto>
{ {
@@ -81,10 +85,31 @@ public sealed class CreateOrderCommandHandler(
await orderRepository.SaveChangesAsync(cancellationToken); await orderRepository.SaveChangesAsync(cancellationToken);
// 5. 记录日志 // 5. 发布订单创建事件
var itemsSummary = items.Count > 0
? string.Join("、", items.Take(3).Select(x => x.ProductName))
+ (items.Count > 3 ? $" 等{items.Count}件" : string.Empty)
: string.Empty;
await eventPublisher.PublishAsync(EventRoutingKeys.OrderCreated, new OrderCreatedEvent
{
OrderId = order.Id,
OrderNo = order.OrderNo,
Amount = order.PaidAmount,
TenantId = order.TenantId,
StoreId = order.StoreId,
Channel = (int)order.Channel,
DeliveryType = (int)order.DeliveryType,
CustomerName = order.CustomerName,
ItemsSummary = itemsSummary,
TableNo = order.TableNo,
CreatedAt = order.CreatedAt
}, cancellationToken);
// 6. 记录日志
logger.LogInformation("创建订单 {OrderNo} ({OrderId})", order.OrderNo, order.Id); logger.LogInformation("创建订单 {OrderNo} ({OrderId})", order.OrderNo, order.Id);
// 6. 返回 DTO // 7. 返回 DTO
return MapToDto(order, items, [], []); return MapToDto(order, items, [], []);
} }

View File

@@ -0,0 +1,100 @@
using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Orders.Commands;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.Messaging;
using TakeoutSaaS.Application.Messaging.Abstractions;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Enums;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Ids;
namespace TakeoutSaaS.Application.App.Orders.Handlers;
/// <summary>
/// 拒单命令处理器。
/// </summary>
public sealed class RejectOrderCommandHandler(
IOrderRepository orderRepository,
IEventPublisher eventPublisher,
IIdGenerator idGenerator,
ILogger<RejectOrderCommandHandler> logger)
: IRequestHandler<RejectOrderCommand, OrderBoardCardDto>
{
/// <inheritdoc />
public async Task<OrderBoardCardDto> Handle(RejectOrderCommand request, CancellationToken cancellationToken)
{
// 1. 查找订单
var order = await orderRepository.FindByIdAsync(request.OrderId, request.TenantId, cancellationToken)
?? throw new BusinessException(ErrorCodes.NotFound, "订单不存在");
// 2. 校验状态
if (order.Status != OrderStatus.AwaitingPreparation)
{
throw new BusinessException(ErrorCodes.BadRequest, $"当前状态 {order.Status} 不允许拒单");
}
// 3. 更新状态
var oldStatus = order.Status;
order.Status = OrderStatus.Cancelled;
order.CancelledAt = DateTime.UtcNow;
order.CancelReason = request.Reason;
// 4. 写入状态流转记录
var history = new OrderStatusHistory
{
Id = idGenerator.NextId(),
OrderId = order.Id,
TenantId = order.TenantId,
Status = OrderStatus.Cancelled,
OperatorId = request.OperatorId,
Notes = $"商户拒单:{request.Reason}",
OccurredAt = DateTime.UtcNow
};
await orderRepository.UpdateOrderAsync(order, cancellationToken);
await orderRepository.AddStatusHistoryAsync(history, cancellationToken);
await orderRepository.SaveChangesAsync(cancellationToken);
// 5. 发布状态变更事件
await eventPublisher.PublishAsync(EventRoutingKeys.OrderStatusChanged, new OrderStatusChangedEvent
{
OrderId = order.Id,
OrderNo = order.OrderNo,
TenantId = order.TenantId,
StoreId = order.StoreId,
OldStatus = (int)oldStatus,
NewStatus = (int)order.Status,
Channel = (int)order.Channel,
DeliveryType = (int)order.DeliveryType,
CustomerName = order.CustomerName,
PaidAmount = order.PaidAmount,
OccurredAt = history.OccurredAt
}, cancellationToken);
// 6. 记录日志
logger.LogInformation("拒单 {OrderNo} ({OrderId}),原因:{Reason}", order.OrderNo, order.Id, request.Reason);
// 7. 返回看板卡片 DTO
return MapToCardDto(order);
}
private static OrderBoardCardDto MapToCardDto(Order order) => new()
{
Id = order.Id,
OrderNo = order.OrderNo,
StoreId = order.StoreId,
Channel = order.Channel,
DeliveryType = order.DeliveryType,
Status = order.Status,
CustomerName = order.CustomerName,
CustomerPhone = order.CustomerPhone,
TableNo = order.TableNo,
QueueNumber = order.QueueNumber,
PaidAmount = order.PaidAmount,
CreatedAt = order.CreatedAt
};
}

View File

@@ -10,6 +10,16 @@ public static class EventRoutingKeys
/// </summary> /// </summary>
public const string OrderCreated = "orders.created"; public const string OrderCreated = "orders.created";
/// <summary>
/// 订单状态变更事件路由键。
/// </summary>
public const string OrderStatusChanged = "orders.status-changed";
/// <summary>
/// 订单催单事件路由键。
/// </summary>
public const string OrderUrged = "orders.urged";
/// <summary> /// <summary>
/// 支付成功事件路由键。 /// 支付成功事件路由键。
/// </summary> /// </summary>

View File

@@ -25,6 +25,36 @@ public sealed class OrderCreatedEvent
/// </summary> /// </summary>
public long TenantId { get; init; } public long TenantId { get; init; }
/// <summary>
/// 门店标识。
/// </summary>
public long StoreId { get; init; }
/// <summary>
/// 下单渠道。
/// </summary>
public int Channel { get; init; }
/// <summary>
/// 履约类型。
/// </summary>
public int DeliveryType { get; init; }
/// <summary>
/// 顾客姓名。
/// </summary>
public string? CustomerName { get; init; }
/// <summary>
/// 商品摘要。
/// </summary>
public string? ItemsSummary { get; init; }
/// <summary>
/// 桌号。
/// </summary>
public string? TableNo { get; init; }
/// <summary> /// <summary>
/// 创建时间UTC /// 创建时间UTC
/// </summary> /// </summary>

View File

@@ -0,0 +1,67 @@
namespace TakeoutSaaS.Application.Messaging.Events;
/// <summary>
/// 订单状态变更事件。
/// </summary>
public sealed class OrderStatusChangedEvent
{
/// <summary>
/// 订单标识。
/// </summary>
public long OrderId { get; init; }
/// <summary>
/// 订单编号。
/// </summary>
public string OrderNo { get; init; } = string.Empty;
/// <summary>
/// 所属租户。
/// </summary>
public long TenantId { get; init; }
/// <summary>
/// 门店标识。
/// </summary>
public long StoreId { get; init; }
/// <summary>
/// 变更前状态。
/// </summary>
public int OldStatus { get; init; }
/// <summary>
/// 变更后状态。
/// </summary>
public int NewStatus { get; init; }
/// <summary>
/// 下单渠道。
/// </summary>
public int Channel { get; init; }
/// <summary>
/// 履约类型。
/// </summary>
public int DeliveryType { get; init; }
/// <summary>
/// 顾客姓名。
/// </summary>
public string? CustomerName { get; init; }
/// <summary>
/// 商品摘要。
/// </summary>
public string? ItemsSummary { get; init; }
/// <summary>
/// 实付金额。
/// </summary>
public decimal PaidAmount { get; init; }
/// <summary>
/// 发生时间UTC
/// </summary>
public DateTime OccurredAt { get; init; }
}

View File

@@ -0,0 +1,37 @@
namespace TakeoutSaaS.Application.Messaging.Events;
/// <summary>
/// 订单催单事件。
/// </summary>
public sealed class OrderUrgeEvent
{
/// <summary>
/// 订单标识。
/// </summary>
public long OrderId { get; init; }
/// <summary>
/// 订单编号。
/// </summary>
public string OrderNo { get; init; } = string.Empty;
/// <summary>
/// 所属租户。
/// </summary>
public long TenantId { get; init; }
/// <summary>
/// 门店标识。
/// </summary>
public long StoreId { get; init; }
/// <summary>
/// 催单次数。
/// </summary>
public int UrgeCount { get; init; }
/// <summary>
/// 发生时间UTC
/// </summary>
public DateTime OccurredAt { get; init; }
}