feat: add mini ordering catalog and order APIs
All checks were successful
Build and Deploy MiniApi / build-and-deploy (push) Successful in 23s

This commit is contained in:
2026-03-10 10:03:32 +08:00
parent 2a0e2e6d62
commit 227266d183
33 changed files with 2483 additions and 19 deletions

View File

@@ -0,0 +1,12 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 分类摘要。
/// </summary>
public sealed class MiniCategoryDto
{
public string Id { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public int Sort { get; init; }
public int ProductCount { get; init; }
}

View File

@@ -0,0 +1,12 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 结算校验请求。
/// </summary>
public sealed class MiniCheckoutValidationRequest
{
public string StoreId { get; init; } = string.Empty;
public string Scene { get; init; } = string.Empty;
public string Channel { get; init; } = string.Empty;
public IReadOnlyList<MiniOrderLineInput> Items { get; init; } = [];
}

View File

@@ -0,0 +1,10 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 结算校验结果。
/// </summary>
public sealed class MiniCheckoutValidationResponse
{
public bool Valid { get; init; }
public string Message { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,14 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 创建订单请求。
/// </summary>
public sealed class MiniCreateOrderRequest
{
public string StoreId { get; init; } = string.Empty;
public string Scene { get; init; } = string.Empty;
public string Channel { get; init; } = string.Empty;
public string? Remark { get; init; }
public string? TableNo { get; init; }
public IReadOnlyList<MiniOrderLineInput> Items { get; init; } = [];
}

View File

@@ -0,0 +1,15 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 创建订单结果。
/// </summary>
public sealed class MiniCreateOrderResponse
{
public string OrderId { get; init; } = string.Empty;
public string OrderNo { get; init; } = string.Empty;
public string StatusText { get; init; } = string.Empty;
public string PaymentStatusText { get; init; } = string.Empty;
public decimal PayableAmount { get; init; }
public string PayableAmountText { get; init; } = "0.00";
public bool MockPayAvailable { get; init; }
}

View File

@@ -0,0 +1,30 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 菜单分组。
/// </summary>
public sealed class MiniMenuSectionDto
{
public string CategoryId { get; init; } = string.Empty;
public string CategoryName { get; init; } = string.Empty;
public IReadOnlyList<MiniMenuProductDto> Products { get; init; } = [];
/// <summary>
/// 菜单商品卡片。
/// </summary>
public sealed class MiniMenuProductDto
{
public string Id { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public string Description { get; init; } = string.Empty;
public string CoverImageUrl { get; init; } = string.Empty;
public decimal Price { get; init; }
public string PriceText { get; init; } = "0.00";
public decimal? OriginalPrice { get; init; }
public string? OriginalPriceText { get; init; }
public string SalesText { get; init; } = string.Empty;
public IReadOnlyList<string> TagTexts { get; init; } = [];
public bool SoldOut { get; init; }
public bool HasOptions { get; init; }
}
}

View File

@@ -0,0 +1,14 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 模拟支付结果。
/// </summary>
public sealed class MiniMockPayResponse
{
public string OrderId { get; init; } = string.Empty;
public string StatusText { get; init; } = string.Empty;
public string PaymentStatusText { get; init; } = string.Empty;
public decimal PaidAmount { get; init; }
public string PaidAmountText { get; init; } = "0.00";
public string SuccessTip { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,59 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 订单详情。
/// </summary>
public sealed class MiniOrderDetailDto
{
public string Id { get; init; } = string.Empty;
public string OrderNo { get; init; } = string.Empty;
public string StoreId { get; init; } = string.Empty;
public string StoreName { get; init; } = string.Empty;
public string StatusText { get; init; } = string.Empty;
public string PaymentStatusText { get; init; } = string.Empty;
public string Scene { get; init; } = string.Empty;
public string CustomerName { get; init; } = string.Empty;
public string CustomerPhone { get; init; } = string.Empty;
public string TableNo { get; init; } = string.Empty;
public string Remark { get; init; } = string.Empty;
public decimal ItemsAmount { get; init; }
public decimal PackagingFee { get; init; }
public decimal DeliveryFee { get; init; }
public decimal DiscountAmount { get; init; }
public decimal PayableAmount { get; init; }
public decimal PaidAmount { get; init; }
public string ItemSummary { get; init; } = string.Empty;
public string CreatedAt { get; init; } = string.Empty;
public string? PaidAt { get; init; }
public string ActionText { get; init; } = string.Empty;
public IReadOnlyList<MiniOrderItemDto> Items { get; init; } = [];
public IReadOnlyList<MiniOrderTimelineDto> Timeline { get; init; } = [];
/// <summary>
/// 订单商品明细。
/// </summary>
public sealed class MiniOrderItemDto
{
public string Id { get; init; } = string.Empty;
public string ProductId { get; init; } = string.Empty;
public string ProductName { get; init; } = string.Empty;
public string SkuName { get; init; } = string.Empty;
public string Unit { get; init; } = string.Empty;
public int Quantity { get; init; }
public decimal UnitPrice { get; init; }
public string UnitPriceText { get; init; } = "0.00";
public decimal SubTotal { get; init; }
public string SubTotalText { get; init; } = "0.00";
}
/// <summary>
/// 订单时间线。
/// </summary>
public sealed class MiniOrderTimelineDto
{
public int Status { get; init; }
public string StatusText { get; init; } = string.Empty;
public string Notes { get; init; } = string.Empty;
public string OccurredAt { get; init; } = string.Empty;
}
}

View File

@@ -0,0 +1,12 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 结算行项目输入。
/// </summary>
public sealed class MiniOrderLineInput
{
public string ProductId { get; init; } = string.Empty;
public string? SkuId { get; init; }
public int Quantity { get; init; }
public IReadOnlyList<string> AddonItemIds { get; init; } = [];
}

View File

@@ -0,0 +1,19 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 订单摘要。
/// </summary>
public sealed class MiniOrderSummaryDto
{
public string Id { get; init; } = string.Empty;
public string OrderNo { get; init; } = string.Empty;
public string StoreName { get; init; } = string.Empty;
public string StatusText { get; init; } = string.Empty;
public string PaymentStatusText { get; init; } = string.Empty;
public string Scene { get; init; } = string.Empty;
public string ItemSummary { get; init; } = string.Empty;
public decimal TotalAmount { get; init; }
public string TotalAmountText { get; init; } = "0.00";
public string CreatedAt { get; init; } = string.Empty;
public string ActionText { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,12 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 金额试算请求。
/// </summary>
public sealed class MiniPriceEstimateRequest
{
public string StoreId { get; init; } = string.Empty;
public string Scene { get; init; } = string.Empty;
public string Channel { get; init; } = string.Empty;
public IReadOnlyList<MiniOrderLineInput> Items { get; init; } = [];
}

View File

@@ -0,0 +1,21 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 金额试算结果。
/// </summary>
public sealed class MiniPriceEstimateResponse
{
public string StoreId { get; init; } = string.Empty;
public string Scene { get; init; } = string.Empty;
public int TotalCount { get; init; }
public decimal OriginalAmount { get; init; }
public string OriginalAmountText { get; init; } = "0.00";
public decimal PackagingFee { get; init; }
public string PackagingFeeText { get; init; } = "0.00";
public decimal DeliveryFee { get; init; }
public string DeliveryFeeText { get; init; } = "0.00";
public decimal DiscountAmount { get; init; }
public string DiscountAmountText { get; init; } = "0.00";
public decimal PayableAmount { get; init; }
public string PayableAmountText { get; init; } = "0.00";
}

View File

@@ -0,0 +1,69 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 商品详情。
/// </summary>
public sealed class MiniProductDetailDto
{
public string Id { get; init; } = string.Empty;
public string StoreId { get; init; } = string.Empty;
public string CategoryId { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public string Subtitle { get; init; } = string.Empty;
public string Description { get; init; } = string.Empty;
public string CoverImageUrl { get; init; } = string.Empty;
public IReadOnlyList<string> GalleryImages { get; init; } = [];
public string Unit { get; init; } = string.Empty;
public decimal BasePrice { get; init; }
public string BasePriceText { get; init; } = "0.00";
public decimal? OriginalPrice { get; init; }
public string? OriginalPriceText { get; init; }
public bool SoldOut { get; init; }
public int MonthlySales { get; init; }
public IReadOnlyList<string> TagTexts { get; init; } = [];
public string? DefaultSkuId { get; init; }
public IReadOnlyList<MiniProductSkuDto> Skus { get; init; } = [];
public IReadOnlyList<MiniProductOptionGroupDto> OptionGroups { get; init; } = [];
/// <summary>
/// 商品 SKU。
/// </summary>
public sealed class MiniProductSkuDto
{
public string Id { get; init; } = string.Empty;
public decimal Price { get; init; }
public string PriceText { get; init; } = "0.00";
public decimal? OriginalPrice { get; init; }
public string? OriginalPriceText { get; init; }
public int? StockQuantity { get; init; }
public bool SoldOut { get; init; }
public IReadOnlyList<string> SelectedOptionIds { get; init; } = [];
}
/// <summary>
/// 商品选项分组。
/// </summary>
public sealed class MiniProductOptionGroupDto
{
public string Id { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public string GroupType { get; init; } = string.Empty;
public string SelectionType { get; init; } = string.Empty;
public bool Required { get; init; }
public int MinSelect { get; init; }
public int MaxSelect { get; init; }
public IReadOnlyList<MiniProductOptionDto> Options { get; init; } = [];
}
/// <summary>
/// 商品选项。
/// </summary>
public sealed class MiniProductOptionDto
{
public string Id { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public decimal ExtraPrice { get; init; }
public string ExtraPriceText { get; init; } = "0.00";
public bool SoldOut { get; init; }
}
}

View File

@@ -0,0 +1,14 @@
namespace TakeoutSaaS.Application.App.Mini.Contracts;
/// <summary>
/// 门店摘要。
/// </summary>
public sealed class MiniStoreSummaryDto
{
public string Id { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public string Address { get; init; } = string.Empty;
public string BusinessHours { get; init; } = string.Empty;
public IReadOnlyList<string> Supports { get; init; } = [];
public IReadOnlyList<string> TagTexts { get; init; } = [];
}

View File

@@ -0,0 +1,50 @@
using TakeoutSaaS.Application.App.Mini.Contracts;
namespace TakeoutSaaS.Application.App.Mini;
/// <summary>
/// 小程序真实业务服务抽象。
/// </summary>
public interface IMiniAppService
{
/// <summary>
/// 查询可选门店。
/// </summary>
Task<IReadOnlyList<MiniStoreSummaryDto>> GetStoresAsync(long tenantId, CancellationToken cancellationToken = default);
/// <summary>
/// 查询门店分类。
/// </summary>
Task<IReadOnlyList<MiniCategoryDto>> GetCategoriesAsync(long tenantId, string storeId, string scene, string channel, CancellationToken cancellationToken = default);
/// <summary>
/// 查询门店菜单。
/// </summary>
Task<IReadOnlyList<MiniMenuSectionDto>> GetMenuAsync(long tenantId, string storeId, string scene, string channel, CancellationToken cancellationToken = default);
/// <summary>
/// 查询商品详情。
/// </summary>
Task<MiniProductDetailDto?> GetProductDetailAsync(long tenantId, string productId, string scene, string channel, CancellationToken cancellationToken = default);
/// <summary>
/// 试算订单金额。
/// </summary>
Task<MiniPriceEstimateResponse> EstimatePriceAsync(long tenantId, MiniPriceEstimateRequest request, CancellationToken cancellationToken = default);
/// <summary>
/// 校验结算请求。
/// </summary>
Task<MiniCheckoutValidationResponse> ValidateCheckoutAsync(long tenantId, MiniCheckoutValidationRequest request, CancellationToken cancellationToken = default);
/// <summary>
/// 查询顾客订单列表。
/// </summary>
Task<IReadOnlyList<MiniOrderSummaryDto>> GetOrdersAsync(long tenantId, string customerPhone, CancellationToken cancellationToken = default);
/// <summary>
/// 查询顾客订单详情。
/// </summary>
Task<MiniOrderDetailDto?> GetOrderDetailAsync(long tenantId, string orderId, string customerPhone, CancellationToken cancellationToken = default);
/// <summary>
/// 创建订单。
/// </summary>
Task<MiniCreateOrderResponse> CreateOrderAsync(long tenantId, string customerName, string customerPhone, MiniCreateOrderRequest request, CancellationToken cancellationToken = default);
/// <summary>
/// 模拟支付。
/// </summary>
Task<MiniMockPayResponse> MockPayAsync(long tenantId, string orderId, string customerPhone, CancellationToken cancellationToken = default);
}