From f7c2ae4bac5785c60da8b0928758f9c8a215d04a Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Sat, 21 Feb 2026 17:23:48 +0800 Subject: [PATCH] feat(product): add product list/detail/save/soldout/batch api support --- .../Contracts/Product/ProductContracts.cs | 447 + .../Controllers/ProductController.cs | 758 ++ .../Products/Commands/CreateProductCommand.cs | 50 + .../Commands/SoldoutProductCommand.cs | 46 + .../Products/Commands/UpdateProductCommand.cs | 50 + .../App/Products/Dto/ProductDto.cs | 50 + .../Handlers/CreateProductCommandHandler.cs | 20 + .../SearchProductPickerQueryHandler.cs | 10 +- .../Handlers/SearchProductsQueryHandler.cs | 49 +- .../Handlers/SoldoutProductCommandHandler.cs | 46 + .../Handlers/UpdateProductCommandHandler.cs | 20 + .../App/Products/ProductMapping.cs | 10 + .../Products/Queries/SearchProductsQuery.cs | 15 + .../CreateProductCommandValidator.cs | 4 + .../SearchProductsQueryValidator.cs | 1 + .../SoldoutProductCommandValidator.cs | 24 + .../UpdateProductCommandValidator.cs | 4 + .../Products/Entities/Product.cs | 50 + .../Products/Enums/ProductKind.cs | 17 + .../Products/Enums/ProductSoldoutMode.cs | 22 + .../App/Persistence/TakeoutAppDbContext.cs | 7 + ...AddProductListAndSoldoutFields.Designer.cs | 8422 +++++++++++++++++ ...21120000_AddProductListAndSoldoutFields.cs | 135 + .../TakeoutAppDbContextModelSnapshot.cs | 51 + 24 files changed, 10302 insertions(+), 6 deletions(-) create mode 100644 src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductContracts.cs create mode 100644 src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Commands/SoldoutProductCommand.cs create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Handlers/SoldoutProductCommandHandler.cs create mode 100644 src/Application/TakeoutSaaS.Application/App/Products/Validators/SoldoutProductCommandValidator.cs create mode 100644 src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductKind.cs create mode 100644 src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductSoldoutMode.cs create mode 100644 src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.Designer.cs create mode 100644 src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.cs diff --git a/src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductContracts.cs b/src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductContracts.cs new file mode 100644 index 0000000..8243d2c --- /dev/null +++ b/src/Api/TakeoutSaaS.TenantApi/Contracts/Product/ProductContracts.cs @@ -0,0 +1,447 @@ +namespace TakeoutSaaS.TenantApi.Contracts.Product; + +/// +/// 商品列表查询请求。 +/// +public sealed class ProductListRequest +{ + /// + /// 门店 ID。 + /// + public string StoreId { get; set; } = string.Empty; + + /// + /// 分类 ID。 + /// + public string? CategoryId { get; set; } + + /// + /// 关键字。 + /// + public string? Keyword { get; set; } + + /// + /// 状态(on_sale/off_shelf/sold_out)。 + /// + public string? Status { get; set; } + + /// + /// 类型(single/combo)。 + /// + public string? Kind { get; set; } + + /// + /// 页码。 + /// + public int Page { get; set; } = 1; + + /// + /// 每页条数。 + /// + public int PageSize { get; set; } = 20; +} + +/// +/// 商品详情查询请求。 +/// +public sealed class ProductDetailRequest +{ + /// + /// 门店 ID。 + /// + public string StoreId { get; set; } = string.Empty; + + /// + /// 商品 ID。 + /// + public string ProductId { get; set; } = string.Empty; +} + +/// +/// 保存商品请求。 +/// +public sealed class SaveProductRequest +{ + /// + /// 门店 ID。 + /// + public string StoreId { get; set; } = string.Empty; + + /// + /// 商品 ID(编辑时传)。 + /// + public string? Id { get; set; } + + /// + /// 分类 ID。 + /// + public string CategoryId { get; set; } = string.Empty; + + /// + /// 商品类型(single/combo)。 + /// + public string Kind { get; set; } = "single"; + + /// + /// 商品名称。 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 副标题。 + /// + public string Subtitle { get; set; } = string.Empty; + + /// + /// 描述。 + /// + public string Description { get; set; } = string.Empty; + + /// + /// 售价。 + /// + public decimal Price { get; set; } + + /// + /// 原价。 + /// + public decimal? OriginalPrice { get; set; } + + /// + /// 库存。 + /// + public int Stock { get; set; } + + /// + /// 标签。 + /// + public List Tags { get; set; } = []; + + /// + /// 状态(on_sale/off_shelf/sold_out)。 + /// + public string Status { get; set; } = "off_shelf"; + + /// + /// 上架方式(draft/now/scheduled)。 + /// + public string ShelfMode { get; set; } = "draft"; + + /// + /// SPU 编码(可选)。 + /// + public string? SpuCode { get; set; } + + /// + /// 定时上架时间。 + /// + public string? TimedOnShelfAt { get; set; } + + /// + /// 商品图片地址列表。 + /// + public List ImageUrls { get; set; } = []; +} + +/// +/// 删除商品请求。 +/// +public sealed class DeleteProductRequest +{ + /// + /// 门店 ID。 + /// + public string StoreId { get; set; } = string.Empty; + + /// + /// 商品 ID。 + /// + public string ProductId { get; set; } = string.Empty; +} + +/// +/// 商品状态变更请求。 +/// +public sealed class ChangeProductStatusRequest +{ + /// + /// 门店 ID。 + /// + public string StoreId { get; set; } = string.Empty; + + /// + /// 商品 ID。 + /// + public string ProductId { get; set; } = string.Empty; + + /// + /// 状态(on_sale/off_shelf/sold_out)。 + /// + public string Status { get; set; } = "off_shelf"; +} + +/// +/// 商品沽清请求。 +/// +public sealed class SoldoutProductRequest +{ + /// + /// 门店 ID。 + /// + public string StoreId { get; set; } = string.Empty; + + /// + /// 商品 ID。 + /// + public string ProductId { get; set; } = string.Empty; + + /// + /// 沽清模式(today/timed/permanent)。 + /// + public string Mode { get; set; } = "today"; + + /// + /// 剩余可售。 + /// + public int RemainStock { get; set; } + + /// + /// 沽清原因。 + /// + public string Reason { get; set; } = string.Empty; + + /// + /// 恢复时间。 + /// + public string? RecoverAt { get; set; } + + /// + /// 同步平台。 + /// + public bool SyncToPlatform { get; set; } = true; + + /// + /// 通知店长。 + /// + public bool NotifyManager { get; set; } +} + +/// +/// 批量操作请求。 +/// +public sealed class BatchProductActionRequest +{ + /// + /// 门店 ID。 + /// + public string StoreId { get; set; } = string.Empty; + + /// + /// 动作(batch_on/batch_off/batch_delete/batch_soldout)。 + /// + public string Action { get; set; } = string.Empty; + + /// + /// 商品 ID 列表。 + /// + public List ProductIds { get; set; } = []; + + /// + /// 剩余可售(沽清时)。 + /// + public int? RemainStock { get; set; } + + /// + /// 原因(沽清时)。 + /// + public string? Reason { get; set; } + + /// + /// 恢复时间(沽清时)。 + /// + public string? RecoverAt { get; set; } + + /// + /// 同步平台(沽清时)。 + /// + public bool? SyncToPlatform { get; set; } + + /// + /// 通知店长(沽清时)。 + /// + public bool? NotifyManager { get; set; } +} + +/// +/// 商品列表响应。 +/// +public sealed class ProductListResultResponse +{ + /// + /// 列表项。 + /// + public List Items { get; set; } = []; + + /// + /// 总数。 + /// + public int Total { get; set; } + + /// + /// 页码。 + /// + public int Page { get; set; } + + /// + /// 每页条数。 + /// + public int PageSize { get; set; } +} + +/// +/// 商品列表项响应。 +/// +public class ProductListItemResponse +{ + /// + /// 商品 ID。 + /// + public string Id { get; set; } = string.Empty; + + /// + /// 分类 ID。 + /// + public string CategoryId { get; set; } = string.Empty; + + /// + /// 分类名称。 + /// + public string CategoryName { get; set; } = string.Empty; + + /// + /// 图片地址。 + /// + public string ImageUrl { get; set; } = string.Empty; + + /// + /// 商品类型(single/combo)。 + /// + public string Kind { get; set; } = "single"; + + /// + /// 商品名称。 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 原价。 + /// + public decimal? OriginalPrice { get; set; } + + /// + /// 售价。 + /// + public decimal Price { get; set; } + + /// + /// 月销量。 + /// + public int SalesMonthly { get; set; } + + /// + /// 沽清模式。 + /// + public string? SoldoutMode { get; set; } + + /// + /// SPU 编码。 + /// + public string SpuCode { get; set; } = string.Empty; + + /// + /// 状态(on_sale/off_shelf/sold_out)。 + /// + public string Status { get; set; } = "off_shelf"; + + /// + /// 库存。 + /// + public int Stock { get; set; } + + /// + /// 副标题。 + /// + public string Subtitle { get; set; } = string.Empty; + + /// + /// 标签。 + /// + public List Tags { get; set; } = []; +} + +/// +/// 商品详情响应。 +/// +public sealed class ProductDetailResponse : ProductListItemResponse +{ + /// + /// 商品图片列表。 + /// + public List ImageUrls { get; set; } = []; + + /// + /// 商品描述。 + /// + public string Description { get; set; } = string.Empty; + + /// + /// 是否通知店长。 + /// + public bool NotifyManager { get; set; } + + /// + /// 恢复时间。 + /// + public string? RecoverAt { get; set; } + + /// + /// 剩余可售。 + /// + public int RemainStock { get; set; } + + /// + /// 沽清原因。 + /// + public string SoldoutReason { get; set; } = string.Empty; + + /// + /// 是否同步平台。 + /// + public bool SyncToPlatform { get; set; } +} + +/// +/// 批量操作响应。 +/// +public sealed class BatchProductActionResultResponse +{ + /// + /// 动作。 + /// + public string Action { get; set; } = string.Empty; + + /// + /// 总条数。 + /// + public int TotalCount { get; set; } + + /// + /// 成功条数。 + /// + public int SuccessCount { get; set; } + + /// + /// 失败条数。 + /// + public int FailedCount { get; set; } +} diff --git a/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs b/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs new file mode 100644 index 0000000..4ff31ae --- /dev/null +++ b/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs @@ -0,0 +1,758 @@ +using System.Globalization; +using System.Text.Json; +using MediatR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using TakeoutSaaS.Application.App.Products.Commands; +using TakeoutSaaS.Application.App.Products.Dto; +using TakeoutSaaS.Application.App.Products.Queries; +using TakeoutSaaS.Application.App.Stores.Services; +using TakeoutSaaS.Domain.Products.Enums; +using TakeoutSaaS.Infrastructure.App.Persistence; +using TakeoutSaaS.Shared.Abstractions.Constants; +using TakeoutSaaS.Shared.Abstractions.Exceptions; +using TakeoutSaaS.Shared.Abstractions.Results; +using TakeoutSaaS.Shared.Web.Api; +using TakeoutSaaS.TenantApi.Contracts.Product; + +namespace TakeoutSaaS.TenantApi.Controllers; + +/// +/// 租户端商品主接口。 +/// +[ApiVersion("1.0")] +[Authorize] +[Route("api/tenant/v{version:apiVersion}/product")] +public sealed class ProductController( + IMediator mediator, + TakeoutAppDbContext dbContext, + StoreContextService storeContextService) : BaseApiController +{ + /// + /// 商品列表。 + /// + [HttpGet("list")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task> List( + [FromQuery] ProductListRequest request, + CancellationToken cancellationToken) + { + var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId)); + await EnsureStoreAccessibleAsync(storeId, cancellationToken); + + var (status, isSoldOut) = ParseUiStatusFilter(request.Status); + var kind = ParseKindOrNull(request.Kind); + + var result = await mediator.Send(new SearchProductsQuery + { + StoreId = storeId, + CategoryId = StoreApiHelpers.ParseSnowflakeOrNull(request.CategoryId), + Keyword = request.Keyword, + Status = status, + IsSoldOut = isSoldOut, + Kind = kind, + Page = Math.Max(1, request.Page), + PageSize = Math.Clamp(request.PageSize, 1, 200) + }, cancellationToken); + + var categoryNameLookup = await BuildCategoryNameLookupAsync(storeId, cancellationToken); + return ApiResponse.Ok(new ProductListResultResponse + { + Items = result.Items.Select(item => MapListItem(item, categoryNameLookup)).ToList(), + Total = result.TotalCount, + Page = result.Page, + PageSize = result.PageSize + }); + } + + /// + /// 商品详情。 + /// + [HttpGet("detail")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task> Detail( + [FromQuery] ProductDetailRequest request, + CancellationToken cancellationToken) + { + var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId)); + await EnsureStoreAccessibleAsync(storeId, cancellationToken); + + var productId = StoreApiHelpers.ParseRequiredSnowflake(request.ProductId, nameof(request.ProductId)); + var product = await mediator.Send(new GetProductByIdQuery + { + ProductId = productId + }, cancellationToken); + + if (product is null || product.StoreId != storeId) + { + return ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); + } + + var categoryNameLookup = await BuildCategoryNameLookupAsync(storeId, cancellationToken); + return ApiResponse.Ok(MapDetailItem(product, categoryNameLookup)); + } + + /// + /// 新增或编辑商品。 + /// + [HttpPost("save")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task> Save( + [FromBody] SaveProductRequest request, + CancellationToken cancellationToken) + { + var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId)); + await EnsureStoreAccessibleAsync(storeId, cancellationToken); + + var categoryId = StoreApiHelpers.ParseRequiredSnowflake(request.CategoryId, nameof(request.CategoryId)); + var kind = ParseKind(request.Kind); + var productId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id); + var timedOnShelfAt = ParseTimedOnShelfAt(request.ShelfMode, request.TimedOnShelfAt); + var normalizedStatus = ResolveStatusByShelfMode(request.ShelfMode, request.Status); + var imageUrls = NormalizeImageUrls(request.ImageUrls); + + ProductDto? existing = null; + if (productId.HasValue) + { + existing = await mediator.Send(new GetProductByIdQuery + { + ProductId = productId.Value + }, cancellationToken); + + if (existing is null || existing.StoreId != storeId) + { + return ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); + } + } + + var tagsJson = SerializeTagsJson(request.Tags); + var soldoutMode = ResolveSoldoutModeForSave(existing, normalizedStatus, request.Status); + var recoverAt = soldoutMode == ProductSoldoutMode.Timed ? existing?.RecoverAt : null; + int? remainStock = soldoutMode.HasValue ? Math.Max(0, existing?.RemainStock ?? request.Stock) : null; + var soldoutReason = soldoutMode.HasValue ? existing?.SoldoutReason : null; + + ProductDto? saved; + if (existing is null) + { + saved = await mediator.Send(new CreateProductCommand + { + StoreId = storeId, + CategoryId = categoryId, + SpuCode = string.IsNullOrWhiteSpace(request.SpuCode) ? GenerateSpuCode() : request.SpuCode.Trim(), + Name = request.Name.Trim(), + Subtitle = request.Subtitle?.Trim(), + Description = request.Description?.Trim(), + Price = request.Price, + OriginalPrice = request.OriginalPrice.HasValue && request.OriginalPrice.Value > 0 ? request.OriginalPrice : null, + StockQuantity = Math.Max(0, request.Stock), + Status = normalizedStatus, + Kind = kind, + TagsJson = tagsJson, + TimedOnShelfAt = timedOnShelfAt, + SoldoutMode = soldoutMode, + RecoverAt = recoverAt, + RemainStock = remainStock, + SoldoutReason = soldoutReason, + SyncToPlatform = existing?.SyncToPlatform ?? true, + NotifyManager = existing?.NotifyManager ?? false, + CoverImage = imageUrls.FirstOrDefault(), + GalleryImages = string.Join(',', imageUrls) + }, cancellationToken); + } + else + { + saved = await mediator.Send(new UpdateProductCommand + { + ProductId = existing.Id, + StoreId = storeId, + CategoryId = categoryId, + SpuCode = string.IsNullOrWhiteSpace(request.SpuCode) ? existing.SpuCode : request.SpuCode.Trim(), + Name = request.Name.Trim(), + Subtitle = request.Subtitle?.Trim(), + Unit = existing.Unit, + Price = request.Price, + OriginalPrice = request.OriginalPrice.HasValue && request.OriginalPrice.Value > 0 ? request.OriginalPrice : null, + StockQuantity = Math.Max(0, request.Stock), + MaxQuantityPerOrder = existing.MaxQuantityPerOrder, + Status = normalizedStatus, + Kind = kind, + SalesMonthly = existing.SalesMonthly, + TagsJson = tagsJson, + SoldoutMode = soldoutMode, + RecoverAt = recoverAt, + RemainStock = remainStock, + SoldoutReason = soldoutReason, + SyncToPlatform = existing.SyncToPlatform, + NotifyManager = existing.NotifyManager, + TimedOnShelfAt = timedOnShelfAt, + CoverImage = imageUrls.FirstOrDefault() ?? existing.CoverImage, + GalleryImages = imageUrls.Count > 0 ? string.Join(',', imageUrls) : existing.GalleryImages, + Description = request.Description?.Trim(), + EnableDineIn = existing.EnableDineIn, + EnablePickup = existing.EnablePickup, + EnableDelivery = existing.EnableDelivery, + IsFeatured = existing.IsFeatured + }, cancellationToken); + } + + if (saved is null) + { + return ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); + } + + var categoryNameLookup = await BuildCategoryNameLookupAsync(storeId, cancellationToken); + return ApiResponse.Ok(MapDetailItem(saved, categoryNameLookup)); + } + + /// + /// 删除商品。 + /// + [HttpPost("delete")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task> Delete( + [FromBody] DeleteProductRequest request, + CancellationToken cancellationToken) + { + var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId)); + await EnsureStoreAccessibleAsync(storeId, cancellationToken); + + var productId = StoreApiHelpers.ParseRequiredSnowflake(request.ProductId, nameof(request.ProductId)); + var existing = await mediator.Send(new GetProductByIdQuery + { + ProductId = productId + }, cancellationToken); + if (existing is null || existing.StoreId != storeId) + { + return ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); + } + + await mediator.Send(new DeleteProductCommand + { + ProductId = productId + }, cancellationToken); + return ApiResponse.Ok(null); + } + + /// + /// 变更商品状态。 + /// + [HttpPost("status/change")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task> ChangeStatus( + [FromBody] ChangeProductStatusRequest request, + CancellationToken cancellationToken) + { + var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId)); + await EnsureStoreAccessibleAsync(storeId, cancellationToken); + + var productId = StoreApiHelpers.ParseRequiredSnowflake(request.ProductId, nameof(request.ProductId)); + var existing = await mediator.Send(new GetProductByIdQuery + { + ProductId = productId + }, cancellationToken); + if (existing is null || existing.StoreId != storeId) + { + return ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); + } + + var status = ParseUiStatus(request.Status); + ProductSoldoutMode? soldoutMode = status == ProductStatus.OffShelf && string.Equals(request.Status, "sold_out", StringComparison.OrdinalIgnoreCase) + ? existing.SoldoutMode ?? ProductSoldoutMode.Today + : null; + + var updated = await UpdateProductStatusAsync( + existing, + status, + soldoutMode, + soldoutMode.HasValue ? Math.Max(0, existing.RemainStock ?? existing.StockQuantity ?? 0) : null, + soldoutMode.HasValue ? existing.SoldoutReason : null, + soldoutMode == ProductSoldoutMode.Timed ? existing.RecoverAt : null, + cancellationToken); + + var categoryNameLookup = await BuildCategoryNameLookupAsync(storeId, cancellationToken); + return ApiResponse.Ok(MapDetailItem(updated, categoryNameLookup)); + } + + /// + /// 沽清商品。 + /// + [HttpPost("soldout")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task> Soldout( + [FromBody] SoldoutProductRequest request, + CancellationToken cancellationToken) + { + var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId)); + await EnsureStoreAccessibleAsync(storeId, cancellationToken); + + var productId = StoreApiHelpers.ParseRequiredSnowflake(request.ProductId, nameof(request.ProductId)); + var existing = await mediator.Send(new GetProductByIdQuery + { + ProductId = productId + }, cancellationToken); + if (existing is null || existing.StoreId != storeId) + { + return ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); + } + + var soldoutMode = ParseSoldoutMode(request.Mode); + var recoverAt = soldoutMode == ProductSoldoutMode.Timed ? ParseDateTimeOrNull(request.RecoverAt, nameof(request.RecoverAt)) : null; + + var result = await mediator.Send(new SoldoutProductCommand + { + ProductId = productId, + Mode = soldoutMode, + RemainStock = Math.Max(0, request.RemainStock), + Reason = request.Reason, + RecoverAt = recoverAt, + SyncToPlatform = request.SyncToPlatform, + NotifyManager = request.NotifyManager + }, cancellationToken); + + if (result is null) + { + return ApiResponse.Error(ErrorCodes.NotFound, "商品不存在"); + } + + var categoryNameLookup = await BuildCategoryNameLookupAsync(storeId, cancellationToken); + return ApiResponse.Ok(MapDetailItem(result, categoryNameLookup)); + } + + /// + /// 批量操作商品。 + /// + [HttpPost("batch")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task> Batch( + [FromBody] BatchProductActionRequest request, + CancellationToken cancellationToken) + { + var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId)); + await EnsureStoreAccessibleAsync(storeId, cancellationToken); + + var productIds = StoreApiHelpers.ParseSnowflakeList(request.ProductIds); + if (productIds.Count == 0) + { + throw new BusinessException(ErrorCodes.BadRequest, "productIds 不能为空"); + } + + var action = (request.Action ?? string.Empty).Trim().ToLowerInvariant(); + var totalCount = productIds.Count; + var successCount = 0; + + foreach (var productId in productIds) + { + try + { + var existing = await mediator.Send(new GetProductByIdQuery + { + ProductId = productId + }, cancellationToken); + if (existing is null || existing.StoreId != storeId) + { + continue; + } + + switch (action) + { + case "batch_delete": + { + await mediator.Send(new DeleteProductCommand + { + ProductId = productId + }, cancellationToken); + successCount++; + break; + } + case "batch_on": + { + await UpdateProductStatusAsync(existing, ProductStatus.OnSale, null, null, null, null, cancellationToken); + successCount++; + break; + } + case "batch_off": + { + await UpdateProductStatusAsync(existing, ProductStatus.OffShelf, null, null, null, null, cancellationToken); + successCount++; + break; + } + case "batch_soldout": + { + var mode = ParseSoldoutMode("today"); + var recoverAt = ParseDateTimeOrNull(request.RecoverAt, nameof(request.RecoverAt)); + await mediator.Send(new SoldoutProductCommand + { + ProductId = productId, + Mode = mode, + RemainStock = Math.Max(0, request.RemainStock ?? 0), + Reason = request.Reason, + RecoverAt = recoverAt, + SyncToPlatform = request.SyncToPlatform ?? true, + NotifyManager = request.NotifyManager ?? false + }, cancellationToken); + successCount++; + break; + } + default: + throw new BusinessException(ErrorCodes.BadRequest, "不支持的批量操作"); + } + } + catch + { + // 批量接口按项容错,统一统计失败数。 + } + } + + return ApiResponse.Ok(new BatchProductActionResultResponse + { + Action = action, + TotalCount = totalCount, + SuccessCount = successCount, + FailedCount = Math.Max(0, totalCount - successCount) + }); + } + + private async Task UpdateProductStatusAsync( + ProductDto existing, + ProductStatus status, + ProductSoldoutMode? soldoutMode, + int? remainStock, + string? soldoutReason, + DateTime? recoverAt, + CancellationToken cancellationToken) + { + var stock = existing.StockQuantity ?? 0; + if (remainStock.HasValue) + { + stock = Math.Max(0, remainStock.Value); + } + + var updated = await mediator.Send(new UpdateProductCommand + { + ProductId = existing.Id, + StoreId = existing.StoreId, + CategoryId = existing.CategoryId, + SpuCode = existing.SpuCode, + Name = existing.Name, + Subtitle = existing.Subtitle, + Unit = existing.Unit, + Price = existing.Price, + OriginalPrice = existing.OriginalPrice, + StockQuantity = stock, + MaxQuantityPerOrder = existing.MaxQuantityPerOrder, + Status = status, + Kind = existing.Kind, + SalesMonthly = existing.SalesMonthly, + TagsJson = existing.TagsJson, + SoldoutMode = soldoutMode, + RecoverAt = soldoutMode == ProductSoldoutMode.Timed ? recoverAt : null, + RemainStock = soldoutMode.HasValue ? stock : null, + SoldoutReason = soldoutMode.HasValue ? soldoutReason : null, + SyncToPlatform = soldoutMode.HasValue ? existing.SyncToPlatform : true, + NotifyManager = soldoutMode.HasValue ? existing.NotifyManager : false, + TimedOnShelfAt = existing.TimedOnShelfAt, + CoverImage = existing.CoverImage, + GalleryImages = existing.GalleryImages, + Description = existing.Description, + EnableDineIn = existing.EnableDineIn, + EnablePickup = existing.EnablePickup, + EnableDelivery = existing.EnableDelivery, + IsFeatured = existing.IsFeatured + }, cancellationToken); + + if (updated is null) + { + throw new BusinessException(ErrorCodes.NotFound, "商品不存在"); + } + + return updated; + } + + private async Task EnsureStoreAccessibleAsync(long storeId, CancellationToken cancellationToken) + { + var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService); + await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, storeId, cancellationToken); + } + + private async Task> BuildCategoryNameLookupAsync(long storeId, CancellationToken cancellationToken) + { + var categories = await mediator.Send(new GetProductCategoryListQuery + { + StoreId = storeId + }, cancellationToken); + return categories.ToDictionary(item => item.Id, item => item.Name); + } + + private static ProductListItemResponse MapListItem(ProductDto source, IReadOnlyDictionary categoryNameLookup) + { + var stock = source.StockQuantity ?? 0; + var tags = ParseTagsJson(source.TagsJson); + return new ProductListItemResponse + { + Id = source.Id.ToString(), + CategoryId = source.CategoryId.ToString(), + CategoryName = categoryNameLookup.GetValueOrDefault(source.CategoryId, string.Empty), + ImageUrl = source.CoverImage ?? string.Empty, + Kind = ToKindText(source.Kind), + Name = source.Name, + OriginalPrice = source.OriginalPrice, + Price = source.Price, + SalesMonthly = Math.Max(0, source.SalesMonthly), + SoldoutMode = ToSoldoutModeText(source.SoldoutMode), + SpuCode = source.SpuCode, + Status = ToUiStatus(source.Status, source.SoldoutMode), + Stock = stock, + Subtitle = source.Subtitle ?? string.Empty, + Tags = tags + }; + } + + private static ProductDetailResponse MapDetailItem(ProductDto source, IReadOnlyDictionary categoryNameLookup) + { + var listItem = MapListItem(source, categoryNameLookup); + return new ProductDetailResponse + { + Id = listItem.Id, + CategoryId = listItem.CategoryId, + CategoryName = listItem.CategoryName, + ImageUrl = listItem.ImageUrl, + Kind = listItem.Kind, + Name = listItem.Name, + OriginalPrice = listItem.OriginalPrice, + Price = listItem.Price, + SalesMonthly = listItem.SalesMonthly, + SoldoutMode = listItem.SoldoutMode, + SpuCode = listItem.SpuCode, + Status = listItem.Status, + Stock = listItem.Stock, + Subtitle = listItem.Subtitle, + Tags = listItem.Tags, + ImageUrls = BuildImageUrls(source), + Description = source.Description ?? string.Empty, + NotifyManager = source.NotifyManager, + RecoverAt = source.RecoverAt?.ToString("yyyy-MM-dd HH:mm:ss"), + RemainStock = Math.Max(0, source.RemainStock ?? source.StockQuantity ?? 0), + SoldoutReason = source.SoldoutReason ?? string.Empty, + SyncToPlatform = source.SyncToPlatform + }; + } + + private static ProductKind ParseKind(string? kind) + { + return (kind ?? string.Empty).Trim().ToLowerInvariant() switch + { + "combo" => ProductKind.Combo, + "" or "single" => ProductKind.Single, + _ => throw new BusinessException(ErrorCodes.BadRequest, "kind 非法") + }; + } + + private static ProductKind? ParseKindOrNull(string? kind) + { + if (string.IsNullOrWhiteSpace(kind)) + { + return null; + } + + return ParseKind(kind); + } + + private static (ProductStatus? Status, bool? IsSoldOut) ParseUiStatusFilter(string? status) + { + if (string.IsNullOrWhiteSpace(status)) + { + return (null, null); + } + + return status.Trim().ToLowerInvariant() switch + { + "on_sale" => (ProductStatus.OnSale, false), + "off_shelf" => (ProductStatus.OffShelf, false), + "sold_out" => (null, true), + _ => throw new BusinessException(ErrorCodes.BadRequest, "status 非法") + }; + } + + private static ProductStatus ParseUiStatus(string? status) + { + return (status ?? string.Empty).Trim().ToLowerInvariant() switch + { + "on_sale" => ProductStatus.OnSale, + "off_shelf" => ProductStatus.OffShelf, + "sold_out" => ProductStatus.OffShelf, + _ => throw new BusinessException(ErrorCodes.BadRequest, "status 非法") + }; + } + + private static ProductStatus ResolveStatusByShelfMode(string? shelfMode, string? status) + { + return (shelfMode ?? string.Empty).Trim().ToLowerInvariant() switch + { + "now" => ProductStatus.OnSale, + "draft" => ProductStatus.OffShelf, + "scheduled" => ProductStatus.OffShelf, + _ => ParseUiStatus(status) + }; + } + + private static ProductSoldoutMode ParseSoldoutMode(string? mode) + { + return (mode ?? string.Empty).Trim().ToLowerInvariant() switch + { + "today" or "" => ProductSoldoutMode.Today, + "timed" => ProductSoldoutMode.Timed, + "permanent" => ProductSoldoutMode.Permanent, + _ => throw new BusinessException(ErrorCodes.BadRequest, "mode 非法") + }; + } + + private static ProductSoldoutMode? ResolveSoldoutModeForSave(ProductDto? existing, ProductStatus status, string? requestStatus) + { + if (status == ProductStatus.OnSale) + { + return null; + } + + if (string.Equals(requestStatus, "sold_out", StringComparison.OrdinalIgnoreCase)) + { + return existing?.SoldoutMode ?? ProductSoldoutMode.Today; + } + + return null; + } + + private static DateTime? ParseTimedOnShelfAt(string? shelfMode, string? timedOnShelfAt) + { + if (!string.Equals(shelfMode, "scheduled", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + var parsed = ParseDateTimeOrNull(timedOnShelfAt, nameof(timedOnShelfAt)); + if (!parsed.HasValue) + { + throw new BusinessException(ErrorCodes.BadRequest, "定时上架时间不能为空"); + } + + return parsed; + } + + private static DateTime? ParseDateTimeOrNull(string? value, string fieldName) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + if (!DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var parsed)) + { + throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 格式非法"); + } + + return parsed.ToUniversalTime(); + } + + private static List ParseTagsJson(string? tagsJson) + { + if (string.IsNullOrWhiteSpace(tagsJson)) + { + return []; + } + + try + { + var tags = JsonSerializer.Deserialize>(tagsJson, StoreApiHelpers.JsonOptions) ?? []; + return tags + .Select(item => item?.Trim() ?? string.Empty) + .Where(item => !string.IsNullOrWhiteSpace(item)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Take(8) + .ToList(); + } + catch + { + return []; + } + } + + private static string SerializeTagsJson(IEnumerable? tags) + { + var normalized = (tags ?? []) + .Select(item => item?.Trim() ?? string.Empty) + .Where(item => !string.IsNullOrWhiteSpace(item)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Take(8) + .ToList(); + return JsonSerializer.Serialize(normalized); + } + + private static List BuildImageUrls(ProductDto product) + { + var urls = new List(); + if (!string.IsNullOrWhiteSpace(product.CoverImage)) + { + urls.Add(product.CoverImage.Trim()); + } + + if (!string.IsNullOrWhiteSpace(product.GalleryImages)) + { + urls.AddRange(product.GalleryImages + .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Where(url => !string.IsNullOrWhiteSpace(url))); + } + + return urls + .Select(url => url.Trim()) + .Where(url => !string.IsNullOrWhiteSpace(url)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Take(5) + .ToList(); + } + + private static List NormalizeImageUrls(IEnumerable? imageUrls) + { + return (imageUrls ?? []) + .Select(url => url?.Trim() ?? string.Empty) + .Where(url => !string.IsNullOrWhiteSpace(url)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Take(5) + .ToList(); + } + + private static string ToKindText(ProductKind kind) + { + return kind == ProductKind.Combo ? "combo" : "single"; + } + + private static string? ToSoldoutModeText(ProductSoldoutMode? mode) + { + return mode switch + { + ProductSoldoutMode.Today => "today", + ProductSoldoutMode.Timed => "timed", + ProductSoldoutMode.Permanent => "permanent", + _ => null + }; + } + + private static string ToUiStatus(ProductStatus status, ProductSoldoutMode? soldoutMode) + { + if (soldoutMode.HasValue) + { + return "sold_out"; + } + + return status switch + { + ProductStatus.OnSale => "on_sale", + _ => "off_shelf" + }; + } + + private static string GenerateSpuCode() + { + var now = DateTime.UtcNow; + var seed = (int)(now.Ticks % 10_000); + return $"SPU{now:yyyyMMddHHmmss}{seed:D4}"; + } +} diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Commands/CreateProductCommand.cs b/src/Application/TakeoutSaaS.Application/App/Products/Commands/CreateProductCommand.cs index 50d08c0..ea3737d 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Commands/CreateProductCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Commands/CreateProductCommand.cs @@ -64,6 +64,56 @@ public sealed class CreateProductCommand : IRequest /// public ProductStatus Status { get; set; } = ProductStatus.Draft; + /// + /// 商品类型。 + /// + public ProductKind Kind { get; set; } = ProductKind.Single; + + /// + /// 月销量。 + /// + public int SalesMonthly { get; set; } + + /// + /// 标签 JSON。 + /// + public string? TagsJson { get; set; } + + /// + /// 沽清模式。 + /// + public ProductSoldoutMode? SoldoutMode { get; set; } + + /// + /// 沽清恢复时间。 + /// + public DateTime? RecoverAt { get; set; } + + /// + /// 沽清后剩余可售库存。 + /// + public int? RemainStock { get; set; } + + /// + /// 沽清原因。 + /// + public string? SoldoutReason { get; set; } + + /// + /// 是否同步通知平台。 + /// + public bool SyncToPlatform { get; set; } = true; + + /// + /// 是否通知店长。 + /// + public bool NotifyManager { get; set; } + + /// + /// 定时上架时间。 + /// + public DateTime? TimedOnShelfAt { get; set; } + /// /// 主图。 /// diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Commands/SoldoutProductCommand.cs b/src/Application/TakeoutSaaS.Application/App/Products/Commands/SoldoutProductCommand.cs new file mode 100644 index 0000000..e74f247 --- /dev/null +++ b/src/Application/TakeoutSaaS.Application/App/Products/Commands/SoldoutProductCommand.cs @@ -0,0 +1,46 @@ +using MediatR; +using TakeoutSaaS.Application.App.Products.Dto; +using TakeoutSaaS.Domain.Products.Enums; + +namespace TakeoutSaaS.Application.App.Products.Commands; + +/// +/// 商品沽清命令。 +/// +public sealed class SoldoutProductCommand : IRequest +{ + /// + /// 商品 ID。 + /// + public long ProductId { get; init; } + + /// + /// 沽清模式。 + /// + public ProductSoldoutMode Mode { get; init; } = ProductSoldoutMode.Today; + + /// + /// 剩余可售库存。 + /// + public int RemainStock { get; init; } + + /// + /// 沽清原因。 + /// + public string? Reason { get; init; } + + /// + /// 恢复时间(定时沽清)。 + /// + public DateTime? RecoverAt { get; init; } + + /// + /// 是否同步平台。 + /// + public bool SyncToPlatform { get; init; } = true; + + /// + /// 是否通知店长。 + /// + public bool NotifyManager { get; init; } +} diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Commands/UpdateProductCommand.cs b/src/Application/TakeoutSaaS.Application/App/Products/Commands/UpdateProductCommand.cs index 09cd056..f24eea2 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Commands/UpdateProductCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Commands/UpdateProductCommand.cs @@ -69,6 +69,56 @@ public sealed record UpdateProductCommand : IRequest /// public ProductStatus Status { get; init; } = ProductStatus.Draft; + /// + /// 商品类型。 + /// + public ProductKind Kind { get; init; } = ProductKind.Single; + + /// + /// 月销量。 + /// + public int SalesMonthly { get; init; } + + /// + /// 标签 JSON。 + /// + public string? TagsJson { get; init; } + + /// + /// 沽清模式。 + /// + public ProductSoldoutMode? SoldoutMode { get; init; } + + /// + /// 沽清恢复时间。 + /// + public DateTime? RecoverAt { get; init; } + + /// + /// 沽清后剩余可售库存。 + /// + public int? RemainStock { get; init; } + + /// + /// 沽清原因。 + /// + public string? SoldoutReason { get; init; } + + /// + /// 是否同步通知平台。 + /// + public bool SyncToPlatform { get; init; } = true; + + /// + /// 是否通知店长。 + /// + public bool NotifyManager { get; init; } + + /// + /// 定时上架时间。 + /// + public DateTime? TimedOnShelfAt { get; init; } + /// /// 主图。 /// diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Dto/ProductDto.cs b/src/Application/TakeoutSaaS.Application/App/Products/Dto/ProductDto.cs index 27adb83..ac2aef6 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Dto/ProductDto.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Dto/ProductDto.cs @@ -78,6 +78,56 @@ public sealed class ProductDto /// public ProductStatus Status { get; init; } + /// + /// 商品类型。 + /// + public ProductKind Kind { get; init; } + + /// + /// 月销量。 + /// + public int SalesMonthly { get; init; } + + /// + /// 标签 JSON。 + /// + public string? TagsJson { get; init; } + + /// + /// 沽清模式。 + /// + public ProductSoldoutMode? SoldoutMode { get; init; } + + /// + /// 沽清恢复时间。 + /// + public DateTime? RecoverAt { get; init; } + + /// + /// 剩余可售库存。 + /// + public int? RemainStock { get; init; } + + /// + /// 沽清原因。 + /// + public string? SoldoutReason { get; init; } + + /// + /// 是否同步通知平台。 + /// + public bool SyncToPlatform { get; init; } + + /// + /// 是否通知店长。 + /// + public bool NotifyManager { get; init; } + + /// + /// 定时上架时间。 + /// + public DateTime? TimedOnShelfAt { get; init; } + /// /// 主图。 /// diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs index 2bf1e33..aef223c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs @@ -33,6 +33,16 @@ public sealed class CreateProductCommandHandler(IProductRepository productReposi StockQuantity = request.StockQuantity, MaxQuantityPerOrder = request.MaxQuantityPerOrder, Status = request.Status, + Kind = request.Kind, + SalesMonthly = request.SalesMonthly, + TagsJson = request.TagsJson?.Trim(), + SoldoutMode = request.SoldoutMode, + RecoverAt = request.RecoverAt, + RemainStock = request.RemainStock, + SoldoutReason = request.SoldoutReason?.Trim(), + SyncToPlatform = request.SyncToPlatform, + NotifyManager = request.NotifyManager, + TimedOnShelfAt = request.TimedOnShelfAt, CoverImage = request.CoverImage?.Trim(), GalleryImages = request.GalleryImages?.Trim(), Description = request.Description?.Trim(), @@ -66,6 +76,16 @@ public sealed class CreateProductCommandHandler(IProductRepository productReposi StockQuantity = product.StockQuantity, MaxQuantityPerOrder = product.MaxQuantityPerOrder, Status = product.Status, + Kind = product.Kind, + SalesMonthly = product.SalesMonthly, + TagsJson = product.TagsJson, + SoldoutMode = product.SoldoutMode, + RecoverAt = product.RecoverAt, + RemainStock = product.RemainStock, + SoldoutReason = product.SoldoutReason, + SyncToPlatform = product.SyncToPlatform, + NotifyManager = product.NotifyManager, + TimedOnShelfAt = product.TimedOnShelfAt, CoverImage = product.CoverImage, GalleryImages = product.GalleryImages, Description = product.Description, diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductPickerQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductPickerQueryHandler.cs index 47efc27..1a0cecf 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductPickerQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductPickerQueryHandler.cs @@ -39,17 +39,21 @@ public sealed class SearchProductPickerQueryHandler( Name = product.Name, Price = product.Price, SpuCode = product.SpuCode, - Status = ToPickerStatus(product.Status) + Status = ToPickerStatus(product.Status, product.SoldoutMode.HasValue) }) .ToList(); } - private static string ToPickerStatus(ProductStatus status) + private static string ToPickerStatus(ProductStatus status, bool isSoldout) { + if (isSoldout) + { + return "sold_out"; + } + return status switch { ProductStatus.OnSale => "on_sale", - ProductStatus.Archived => "sold_out", _ => "off_shelf" }; } diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductsQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductsQueryHandler.cs index 660a35b..c4c2773 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductsQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SearchProductsQueryHandler.cs @@ -22,16 +22,49 @@ public sealed class SearchProductsQueryHandler( public async Task> Handle(SearchProductsQuery request, CancellationToken cancellationToken) { var tenantId = _tenantProvider.GetCurrentTenantId(); - var products = await _productRepository.SearchAsync(tenantId, request.StoreId, request.CategoryId, request.Status, cancellationToken); - var sorted = ApplySorting(products, request.SortBy, request.SortDescending); + // 沽清查询需要包含 OffShelf 状态后再做二次过滤。 + var repositoryStatus = request.IsSoldOut == true ? null : request.Status; + var products = await _productRepository.SearchAsync( + tenantId, + request.StoreId, + request.CategoryId, + repositoryStatus, + cancellationToken); + + IEnumerable filtered = products; + + if (!string.IsNullOrWhiteSpace(request.Keyword)) + { + var keyword = request.Keyword.Trim().ToLowerInvariant(); + filtered = filtered.Where(item => + item.Name.ToLower().Contains(keyword) || + item.SpuCode.ToLower().Contains(keyword)); + } + + if (request.Kind.HasValue) + { + filtered = filtered.Where(item => item.Kind == request.Kind.Value); + } + + if (request.IsSoldOut == true) + { + filtered = filtered.Where(item => item.SoldoutMode.HasValue); + } + else if (request.Status == Domain.Products.Enums.ProductStatus.OffShelf) + { + filtered = filtered.Where(item => !item.SoldoutMode.HasValue); + } + + var filteredList = filtered.ToList(); + var sorted = ApplySorting(filteredList, request.SortBy, request.SortDescending); var paged = sorted .Skip((request.Page - 1) * request.PageSize) .Take(request.PageSize) .ToList(); var items = paged.Select(MapToDto).ToList(); - return new PagedResult(items, request.Page, request.PageSize, products.Count); + return new PagedResult(items, request.Page, request.PageSize, filteredList.Count); } private static IOrderedEnumerable ApplySorting( @@ -63,6 +96,16 @@ public sealed class SearchProductsQueryHandler( StockQuantity = product.StockQuantity, MaxQuantityPerOrder = product.MaxQuantityPerOrder, Status = product.Status, + Kind = product.Kind, + SalesMonthly = product.SalesMonthly, + TagsJson = product.TagsJson, + SoldoutMode = product.SoldoutMode, + RecoverAt = product.RecoverAt, + RemainStock = product.RemainStock, + SoldoutReason = product.SoldoutReason, + SyncToPlatform = product.SyncToPlatform, + NotifyManager = product.NotifyManager, + TimedOnShelfAt = product.TimedOnShelfAt, CoverImage = product.CoverImage, GalleryImages = product.GalleryImages, Description = product.Description, diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SoldoutProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SoldoutProductCommandHandler.cs new file mode 100644 index 0000000..7f40c2a --- /dev/null +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/SoldoutProductCommandHandler.cs @@ -0,0 +1,46 @@ +using MediatR; +using Microsoft.Extensions.Logging; +using TakeoutSaaS.Application.App.Products.Commands; +using TakeoutSaaS.Application.App.Products.Dto; +using TakeoutSaaS.Application.App.Products; +using TakeoutSaaS.Domain.Products.Enums; +using TakeoutSaaS.Domain.Products.Repositories; +using TakeoutSaaS.Shared.Abstractions.Tenancy; + +namespace TakeoutSaaS.Application.App.Products.Handlers; + +/// +/// 商品沽清命令处理器。 +/// +public sealed class SoldoutProductCommandHandler( + IProductRepository productRepository, + ITenantProvider tenantProvider, + ILogger logger) + : IRequestHandler +{ + /// + public async Task Handle(SoldoutProductCommand request, CancellationToken cancellationToken) + { + var tenantId = tenantProvider.GetCurrentTenantId(); + var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + if (product is null) + { + return null; + } + + product.Status = ProductStatus.OffShelf; + product.SoldoutMode = request.Mode; + product.RemainStock = Math.Max(0, request.RemainStock); + product.StockQuantity = Math.Max(0, request.RemainStock); + product.SoldoutReason = string.IsNullOrWhiteSpace(request.Reason) ? null : request.Reason.Trim(); + product.RecoverAt = request.Mode == ProductSoldoutMode.Timed ? request.RecoverAt : null; + product.SyncToPlatform = request.SyncToPlatform; + product.NotifyManager = request.NotifyManager; + + await productRepository.UpdateProductAsync(product, cancellationToken); + await productRepository.SaveChangesAsync(cancellationToken); + logger.LogInformation("商品沽清 {ProductId},模式 {Mode}", product.Id, request.Mode); + + return ProductMapping.ToDto(product); + } +} diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs index e616788..d7df44e 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs @@ -44,6 +44,16 @@ public sealed class UpdateProductCommandHandler( existing.StockQuantity = request.StockQuantity; existing.MaxQuantityPerOrder = request.MaxQuantityPerOrder; existing.Status = request.Status; + existing.Kind = request.Kind; + existing.SalesMonthly = request.SalesMonthly; + existing.TagsJson = request.TagsJson?.Trim(); + existing.SoldoutMode = request.SoldoutMode; + existing.RecoverAt = request.RecoverAt; + existing.RemainStock = request.RemainStock; + existing.SoldoutReason = request.SoldoutReason?.Trim(); + existing.SyncToPlatform = request.SyncToPlatform; + existing.NotifyManager = request.NotifyManager; + existing.TimedOnShelfAt = request.TimedOnShelfAt; existing.CoverImage = request.CoverImage?.Trim(); existing.GalleryImages = request.GalleryImages?.Trim(); existing.Description = request.Description?.Trim(); @@ -76,6 +86,16 @@ public sealed class UpdateProductCommandHandler( StockQuantity = product.StockQuantity, MaxQuantityPerOrder = product.MaxQuantityPerOrder, Status = product.Status, + Kind = product.Kind, + SalesMonthly = product.SalesMonthly, + TagsJson = product.TagsJson, + SoldoutMode = product.SoldoutMode, + RecoverAt = product.RecoverAt, + RemainStock = product.RemainStock, + SoldoutReason = product.SoldoutReason, + SyncToPlatform = product.SyncToPlatform, + NotifyManager = product.NotifyManager, + TimedOnShelfAt = product.TimedOnShelfAt, CoverImage = product.CoverImage, GalleryImages = product.GalleryImages, Description = product.Description, diff --git a/src/Application/TakeoutSaaS.Application/App/Products/ProductMapping.cs b/src/Application/TakeoutSaaS.Application/App/Products/ProductMapping.cs index 77cf89f..764a40b 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/ProductMapping.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/ProductMapping.cs @@ -28,6 +28,16 @@ public static class ProductMapping StockQuantity = product.StockQuantity, MaxQuantityPerOrder = product.MaxQuantityPerOrder, Status = product.Status, + Kind = product.Kind, + SalesMonthly = product.SalesMonthly, + TagsJson = product.TagsJson, + SoldoutMode = product.SoldoutMode, + RecoverAt = product.RecoverAt, + RemainStock = product.RemainStock, + SoldoutReason = product.SoldoutReason, + SyncToPlatform = product.SyncToPlatform, + NotifyManager = product.NotifyManager, + TimedOnShelfAt = product.TimedOnShelfAt, CoverImage = product.CoverImage, GalleryImages = product.GalleryImages, Description = product.Description, diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Queries/SearchProductsQuery.cs b/src/Application/TakeoutSaaS.Application/App/Products/Queries/SearchProductsQuery.cs index b1d4b31..f456039 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Queries/SearchProductsQuery.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Queries/SearchProductsQuery.cs @@ -25,6 +25,21 @@ public sealed class SearchProductsQuery : IRequest> /// public ProductStatus? Status { get; init; } + /// + /// 商品类型过滤。 + /// + public ProductKind? Kind { get; init; } + + /// + /// 关键字(名称/编码)。 + /// + public string? Keyword { get; init; } + + /// + /// 是否仅沽清商品。 + /// + public bool? IsSoldOut { get; init; } + /// /// 页码。 /// diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Validators/CreateProductCommandValidator.cs b/src/Application/TakeoutSaaS.Application/App/Products/Validators/CreateProductCommandValidator.cs index 91bd6ac..9ebcd93 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Validators/CreateProductCommandValidator.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Validators/CreateProductCommandValidator.cs @@ -23,6 +23,10 @@ public sealed class CreateProductCommandValidator : AbstractValidator x.OriginalPrice).GreaterThanOrEqualTo(0).When(x => x.OriginalPrice.HasValue); RuleFor(x => x.StockQuantity).GreaterThanOrEqualTo(0).When(x => x.StockQuantity.HasValue); RuleFor(x => x.MaxQuantityPerOrder).GreaterThan(0).When(x => x.MaxQuantityPerOrder.HasValue); + RuleFor(x => x.SalesMonthly).GreaterThanOrEqualTo(0); + RuleFor(x => x.TagsJson).MaximumLength(2000); + RuleFor(x => x.RemainStock).GreaterThanOrEqualTo(0).When(x => x.RemainStock.HasValue); + RuleFor(x => x.SoldoutReason).MaximumLength(256); RuleFor(x => x.CoverImage).MaximumLength(256); RuleFor(x => x.GalleryImages).MaximumLength(1024); } diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Validators/SearchProductsQueryValidator.cs b/src/Application/TakeoutSaaS.Application/App/Products/Validators/SearchProductsQueryValidator.cs index 23e3962..0e8cd8a 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Validators/SearchProductsQueryValidator.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Validators/SearchProductsQueryValidator.cs @@ -16,5 +16,6 @@ public sealed class SearchProductsQueryValidator : AbstractValidator x.Page).GreaterThan(0); RuleFor(x => x.PageSize).InclusiveBetween(1, 200); RuleFor(x => x.SortBy).MaximumLength(64); + RuleFor(x => x.Keyword).MaximumLength(64); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Validators/SoldoutProductCommandValidator.cs b/src/Application/TakeoutSaaS.Application/App/Products/Validators/SoldoutProductCommandValidator.cs new file mode 100644 index 0000000..4ea9d12 --- /dev/null +++ b/src/Application/TakeoutSaaS.Application/App/Products/Validators/SoldoutProductCommandValidator.cs @@ -0,0 +1,24 @@ +using FluentValidation; +using TakeoutSaaS.Application.App.Products.Commands; +using TakeoutSaaS.Domain.Products.Enums; + +namespace TakeoutSaaS.Application.App.Products.Validators; + +/// +/// 商品沽清命令验证器。 +/// +public sealed class SoldoutProductCommandValidator : AbstractValidator +{ + /// + /// 初始化验证规则。 + /// + public SoldoutProductCommandValidator() + { + RuleFor(x => x.ProductId).GreaterThan(0); + RuleFor(x => x.RemainStock).GreaterThanOrEqualTo(0); + RuleFor(x => x.Reason).MaximumLength(256); + RuleFor(x => x.RecoverAt) + .NotNull() + .When(x => x.Mode == ProductSoldoutMode.Timed); + } +} diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Validators/UpdateProductCommandValidator.cs b/src/Application/TakeoutSaaS.Application/App/Products/Validators/UpdateProductCommandValidator.cs index 20200f2..52c2e03 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Validators/UpdateProductCommandValidator.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Validators/UpdateProductCommandValidator.cs @@ -24,6 +24,10 @@ public sealed class UpdateProductCommandValidator : AbstractValidator x.OriginalPrice).GreaterThanOrEqualTo(0).When(x => x.OriginalPrice.HasValue); RuleFor(x => x.StockQuantity).GreaterThanOrEqualTo(0).When(x => x.StockQuantity.HasValue); RuleFor(x => x.MaxQuantityPerOrder).GreaterThan(0).When(x => x.MaxQuantityPerOrder.HasValue); + RuleFor(x => x.SalesMonthly).GreaterThanOrEqualTo(0); + RuleFor(x => x.TagsJson).MaximumLength(2000); + RuleFor(x => x.RemainStock).GreaterThanOrEqualTo(0).When(x => x.RemainStock.HasValue); + RuleFor(x => x.SoldoutReason).MaximumLength(256); RuleFor(x => x.CoverImage).MaximumLength(256); RuleFor(x => x.GalleryImages).MaximumLength(1024); } diff --git a/src/Domain/TakeoutSaaS.Domain/Products/Entities/Product.cs b/src/Domain/TakeoutSaaS.Domain/Products/Entities/Product.cs index 953a322..e44bb07 100644 --- a/src/Domain/TakeoutSaaS.Domain/Products/Entities/Product.cs +++ b/src/Domain/TakeoutSaaS.Domain/Products/Entities/Product.cs @@ -63,6 +63,56 @@ public sealed class Product : MultiTenantEntityBase /// public ProductStatus Status { get; set; } = ProductStatus.Draft; + /// + /// 商品类型。 + /// + public ProductKind Kind { get; set; } = ProductKind.Single; + + /// + /// 月销量。 + /// + public int SalesMonthly { get; set; } + + /// + /// 标签 JSON(字符串数组)。 + /// + public string? TagsJson { get; set; } + + /// + /// 沽清模式。 + /// + public ProductSoldoutMode? SoldoutMode { get; set; } + + /// + /// 沽清恢复时间。 + /// + public DateTime? RecoverAt { get; set; } + + /// + /// 沽清后剩余可售。 + /// + public int? RemainStock { get; set; } + + /// + /// 沽清原因。 + /// + public string? SoldoutReason { get; set; } + + /// + /// 是否同步通知外卖平台。 + /// + public bool SyncToPlatform { get; set; } = true; + + /// + /// 是否通知店长。 + /// + public bool NotifyManager { get; set; } + + /// + /// 定时上架时间。 + /// + public DateTime? TimedOnShelfAt { get; set; } + /// /// 主图。 /// diff --git a/src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductKind.cs b/src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductKind.cs new file mode 100644 index 0000000..4f0840c --- /dev/null +++ b/src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductKind.cs @@ -0,0 +1,17 @@ +namespace TakeoutSaaS.Domain.Products.Enums; + +/// +/// 商品类型。 +/// +public enum ProductKind +{ + /// + /// 单品。 + /// + Single = 0, + + /// + /// 套餐。 + /// + Combo = 1 +} diff --git a/src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductSoldoutMode.cs b/src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductSoldoutMode.cs new file mode 100644 index 0000000..cc9c7f8 --- /dev/null +++ b/src/Domain/TakeoutSaaS.Domain/Products/Enums/ProductSoldoutMode.cs @@ -0,0 +1,22 @@ +namespace TakeoutSaaS.Domain.Products.Enums; + +/// +/// 商品沽清模式。 +/// +public enum ProductSoldoutMode +{ + /// + /// 今日沽清。 + /// + Today = 0, + + /// + /// 定时沽清。 + /// + Timed = 1, + + /// + /// 永久沽清。 + /// + Permanent = 2 +} diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs index 68dbf53..0cd1a45 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs @@ -737,6 +737,13 @@ public sealed class TakeoutAppDbContext( builder.Property(x => x.Unit).HasMaxLength(16); builder.Property(x => x.Price).HasPrecision(18, 2); builder.Property(x => x.OriginalPrice).HasPrecision(18, 2); + builder.Property(x => x.Kind).HasConversion().HasDefaultValue(Domain.Products.Enums.ProductKind.Single); + builder.Property(x => x.SalesMonthly).HasDefaultValue(0); + builder.Property(x => x.TagsJson).HasColumnType("text").HasDefaultValue("[]"); + builder.Property(x => x.SoldoutMode).HasConversion(); + builder.Property(x => x.SoldoutReason).HasMaxLength(256); + builder.Property(x => x.SyncToPlatform).HasDefaultValue(true); + builder.Property(x => x.NotifyManager).HasDefaultValue(false); builder.Property(x => x.CoverImage).HasMaxLength(256); builder.Property(x => x.GalleryImages).HasMaxLength(1024); builder.Property(x => x.Description).HasColumnType("text"); diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.Designer.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.Designer.cs new file mode 100644 index 0000000..74e7aca --- /dev/null +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.Designer.cs @@ -0,0 +1,8422 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using TakeoutSaaS.Infrastructure.App.Persistence; + +#nullable disable + +namespace TakeoutSaaS.Infrastructure.Migrations +{ + [DbContext(typeof(TakeoutAppDbContext))] + [Migration("20260221120000_AddProductListAndSoldoutFields")] + partial class AddProductListAndSoldoutFields + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("TakeoutSaaS.Domain.Analytics.Entities.MetricAlertRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConditionJson") + .IsRequired() + .HasColumnType("text") + .HasComment("触发条件 JSON。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Enabled") + .HasColumnType("boolean") + .HasComment("是否启用。"); + + b.Property("MetricDefinitionId") + .HasColumnType("bigint") + .HasComment("关联指标。"); + + b.Property("NotificationChannels") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("通知渠道。"); + + b.Property("Severity") + .HasColumnType("integer") + .HasComment("告警级别。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "MetricDefinitionId", "Severity"); + + b.ToTable("metric_alert_rules", null, t => + { + t.HasComment("指标告警规则。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Analytics.Entities.MetricDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("指标编码。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DefaultAggregation") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("默认聚合方式。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("说明。"); + + b.Property("DimensionsJson") + .HasColumnType("text") + .HasComment("维度描述 JSON。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("指标名称。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Code") + .IsUnique(); + + b.ToTable("metric_definitions", null, t => + { + t.HasComment("指标定义,描述可观测的数据点。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Analytics.Entities.MetricSnapshot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DimensionKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("维度键(JSON)。"); + + b.Property("MetricDefinitionId") + .HasColumnType("bigint") + .HasComment("指标定义 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("Value") + .HasPrecision(18, 4) + .HasColumnType("numeric(18,4)") + .HasComment("数值。"); + + b.Property("WindowEnd") + .HasColumnType("timestamp with time zone") + .HasComment("统计时间窗口结束。"); + + b.Property("WindowStart") + .HasColumnType("timestamp with time zone") + .HasComment("统计时间窗口开始。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "MetricDefinitionId", "DimensionKey", "WindowStart", "WindowEnd") + .IsUnique(); + + b.ToTable("metric_snapshots", null, t => + { + t.HasComment("指标快照,用于大盘展示。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Coupons.Entities.Coupon", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("券码或序列号。"); + + b.Property("CouponTemplateId") + .HasColumnType("bigint") + .HasComment("模板标识。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone") + .HasComment("到期时间。"); + + b.Property("IssuedAt") + .HasColumnType("timestamp with time zone") + .HasComment("发放时间。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("订单 ID(已使用时记录)。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UsedAt") + .HasColumnType("timestamp with time zone") + .HasComment("使用时间。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("归属用户。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Code") + .IsUnique(); + + b.ToTable("coupons", null, t => + { + t.HasComment("用户领取的券。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Coupons.Entities.CouponTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowStack") + .HasColumnType("boolean") + .HasComment("是否允许叠加其他优惠。"); + + b.Property("ChannelsJson") + .HasColumnType("text") + .HasComment("发放渠道(JSON)。"); + + b.Property("ClaimedQuantity") + .HasColumnType("integer") + .HasComment("已领取数量。"); + + b.Property("CouponType") + .HasColumnType("integer") + .HasComment("券类型。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注。"); + + b.Property("DiscountCap") + .HasColumnType("numeric") + .HasComment("折扣上限(针对折扣券)。"); + + b.Property("MinimumSpend") + .HasColumnType("numeric") + .HasComment("最低消费门槛。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("模板名称。"); + + b.Property("ProductScopeJson") + .HasColumnType("text") + .HasComment("适用品类或商品范围(JSON)。"); + + b.Property("RelativeValidDays") + .HasColumnType("integer") + .HasComment("有效天数(相对发放时间)。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("StoreScopeJson") + .HasColumnType("text") + .HasComment("适用门店 ID 集合(JSON)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TotalQuantity") + .HasColumnType("integer") + .HasComment("总发放数量上限。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("ValidFrom") + .HasColumnType("timestamp with time zone") + .HasComment("可用开始时间。"); + + b.Property("ValidTo") + .HasColumnType("timestamp with time zone") + .HasComment("可用结束时间。"); + + b.Property("Value") + .HasColumnType("numeric") + .HasComment("面值或折扣额度。"); + + b.HasKey("Id"); + + b.ToTable("coupon_templates", null, t => + { + t.HasComment("优惠券模板。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Coupons.Entities.PromotionCampaign", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AudienceDescription") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("目标人群描述。"); + + b.Property("BannerUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("营销素材(如 banner)。"); + + b.Property("Budget") + .HasColumnType("numeric") + .HasComment("预算金额。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndAt") + .HasColumnType("timestamp with time zone") + .HasComment("结束时间。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("活动名称。"); + + b.Property("PromotionType") + .HasColumnType("integer") + .HasComment("活动类型。"); + + b.Property("RulesJson") + .IsRequired() + .HasColumnType("text") + .HasComment("活动规则 JSON。"); + + b.Property("StartAt") + .HasColumnType("timestamp with time zone") + .HasComment("开始时间。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("活动状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.ToTable("promotion_campaigns", null, t => + { + t.HasComment("营销活动配置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.CustomerService.Entities.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChatSessionId") + .HasColumnType("bigint") + .HasComment("会话标识。"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasComment("消息内容。"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("消息类型(文字/图片/语音等)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("IsRead") + .HasColumnType("boolean") + .HasComment("是否已读。"); + + b.Property("ReadAt") + .HasColumnType("timestamp with time zone") + .HasComment("读取时间。"); + + b.Property("SenderType") + .HasColumnType("integer") + .HasComment("发送方类型。"); + + b.Property("SenderUserId") + .HasColumnType("bigint") + .HasComment("发送方用户 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ChatSessionId", "CreatedAt"); + + b.ToTable("chat_messages", null, t => + { + t.HasComment("会话消息。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.CustomerService.Entities.ChatSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AgentUserId") + .HasColumnType("bigint") + .HasComment("当前客服员工 ID。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("CustomerUserId") + .HasColumnType("bigint") + .HasComment("顾客用户 ID。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone") + .HasComment("结束时间。"); + + b.Property("IsBotActive") + .HasColumnType("boolean") + .HasComment("是否机器人接待中。"); + + b.Property("SessionCode") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("会话编号。"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone") + .HasComment("开始时间。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("会话状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店(可空为系统会话)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "SessionCode") + .IsUnique(); + + b.ToTable("chat_sessions", null, t => + { + t.HasComment("客服会话。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.CustomerService.Entities.SupportTicket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssignedAgentId") + .HasColumnType("bigint") + .HasComment("指派的客服。"); + + b.Property("ClosedAt") + .HasColumnType("timestamp with time zone") + .HasComment("关闭时间。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("CustomerUserId") + .HasColumnType("bigint") + .HasComment("客户用户 ID。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasComment("工单详情。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("关联订单(如有)。"); + + b.Property("Priority") + .HasColumnType("integer") + .HasComment("优先级。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("工单主题。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TicketNo") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("工单编号。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "TicketNo") + .IsUnique(); + + b.ToTable("support_tickets", null, t => + { + t.HasComment("客服工单。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.CustomerService.Entities.TicketComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AttachmentsJson") + .HasColumnType("text") + .HasComment("附件 JSON。"); + + b.Property("AuthorUserId") + .HasColumnType("bigint") + .HasComment("评论人 ID。"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasComment("评论内容。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("IsInternal") + .HasColumnType("boolean") + .HasComment("是否内部备注。"); + + b.Property("SupportTicketId") + .HasColumnType("bigint") + .HasComment("工单标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "SupportTicketId"); + + b.ToTable("ticket_comments", null, t => + { + t.HasComment("工单评论/流转记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Deliveries.Entities.DeliveryEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DeliveryOrderId") + .HasColumnType("bigint") + .HasComment("配送单标识。"); + + b.Property("EventType") + .HasColumnType("integer") + .HasComment("事件类型。"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("事件描述。"); + + b.Property("OccurredAt") + .HasColumnType("timestamp with time zone") + .HasComment("发生时间。"); + + b.Property("Payload") + .HasColumnType("text") + .HasComment("原始数据 JSON。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "DeliveryOrderId", "EventType"); + + b.ToTable("delivery_events", null, t => + { + t.HasComment("配送状态事件流水。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Deliveries.Entities.DeliveryOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CourierName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("骑手姓名。"); + + b.Property("CourierPhone") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("骑手电话。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DeliveredAt") + .HasColumnType("timestamp with time zone") + .HasComment("完成时间。"); + + b.Property("DeliveryFee") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("配送费。"); + + b.Property("DispatchedAt") + .HasColumnType("timestamp with time zone") + .HasComment("下发时间。"); + + b.Property("FailureReason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("异常原因。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("获取或设置关联订单 ID。"); + + b.Property("PickedUpAt") + .HasColumnType("timestamp with time zone") + .HasComment("取餐时间。"); + + b.Property("Provider") + .HasColumnType("integer") + .HasComment("配送服务商。"); + + b.Property("ProviderOrderId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("第三方配送单号。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "OrderId") + .IsUnique(); + + b.ToTable("delivery_orders", null, t => + { + t.HasComment("配送单。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Distribution.Entities.AffiliateOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AffiliatePartnerId") + .HasColumnType("bigint") + .HasComment("推广人标识。"); + + b.Property("BuyerUserId") + .HasColumnType("bigint") + .HasComment("用户 ID。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EstimatedCommission") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("预计佣金。"); + + b.Property("OrderAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("订单金额。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("关联订单。"); + + b.Property("SettledAt") + .HasColumnType("timestamp with time zone") + .HasComment("结算完成时间。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("当前状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "AffiliatePartnerId", "OrderId") + .IsUnique(); + + b.ToTable("affiliate_orders", null, t => + { + t.HasComment("分销订单记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Distribution.Entities.AffiliatePartner", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChannelType") + .HasColumnType("integer") + .HasComment("渠道类型。"); + + b.Property("CommissionRate") + .HasColumnType("numeric") + .HasComment("分成比例(0-1)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("昵称或渠道名称。"); + + b.Property("Phone") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("联系电话。"); + + b.Property("Remarks") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("审核备注。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("当前状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户 ID(如绑定登录账号)。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "DisplayName"); + + b.ToTable("affiliate_partners", null, t => + { + t.HasComment("分销/推广合作伙伴。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Distribution.Entities.AffiliatePayout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AffiliatePartnerId") + .HasColumnType("bigint") + .HasComment("合作伙伴标识。"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("结算金额。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("PaidAt") + .HasColumnType("timestamp with time zone") + .HasComment("打款时间。"); + + b.Property("Period") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("结算周期描述。"); + + b.Property("Remarks") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("备注。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "AffiliatePartnerId", "Period") + .IsUnique(); + + b.ToTable("affiliate_payouts", null, t => + { + t.HasComment("佣金结算记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Engagement.Entities.CheckInCampaign", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowMakeupCount") + .HasColumnType("integer") + .HasComment("支持补签次数。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("活动描述。"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone") + .HasComment("结束日期。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("活动名称。"); + + b.Property("RewardsJson") + .IsRequired() + .HasColumnType("text") + .HasComment("连签奖励 JSON。"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasComment("开始日期。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("checkin_campaigns", null, t => + { + t.HasComment("签到活动配置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Engagement.Entities.CheckInRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CheckInCampaignId") + .HasColumnType("bigint") + .HasComment("活动标识。"); + + b.Property("CheckInDate") + .HasColumnType("timestamp with time zone") + .HasComment("签到日期(本地)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("IsMakeup") + .HasColumnType("boolean") + .HasComment("是否补签。"); + + b.Property("RewardJson") + .IsRequired() + .HasColumnType("text") + .HasComment("获得奖励 JSON。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户标识。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "CheckInCampaignId", "UserId", "CheckInDate") + .IsUnique(); + + b.ToTable("checkin_records", null, t => + { + t.HasComment("用户签到记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Engagement.Entities.CommunityComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorUserId") + .HasColumnType("bigint") + .HasComment("评论人。"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("评论内容。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasComment("状态。"); + + b.Property("ParentId") + .HasColumnType("bigint") + .HasComment("父级评论 ID。"); + + b.Property("PostId") + .HasColumnType("bigint") + .HasComment("动态标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "PostId", "CreatedAt"); + + b.ToTable("community_comments", null, t => + { + t.HasComment("社区评论。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Engagement.Entities.CommunityPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorUserId") + .HasColumnType("bigint") + .HasComment("作者用户 ID。"); + + b.Property("CommentCount") + .HasColumnType("integer") + .HasComment("评论数。"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasComment("内容。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("LikeCount") + .HasColumnType("integer") + .HasComment("点赞数。"); + + b.Property("MediaJson") + .HasColumnType("text") + .HasComment("媒体资源 JSON。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("Title") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("标题。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "AuthorUserId", "CreatedAt"); + + b.ToTable("community_posts", null, t => + { + t.HasComment("社区动态。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Engagement.Entities.CommunityReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("PostId") + .HasColumnType("bigint") + .HasComment("动态 ID。"); + + b.Property("ReactedAt") + .HasColumnType("timestamp with time zone") + .HasComment("时间戳。"); + + b.Property("ReactionType") + .HasColumnType("integer") + .HasComment("反应类型。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户 ID。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "PostId", "UserId") + .IsUnique(); + + b.ToTable("community_reactions", null, t => + { + t.HasComment("社区互动反馈。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.GroupBuying.Entities.GroupOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelledAt") + .HasColumnType("timestamp with time zone") + .HasComment("取消时间。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("CurrentCount") + .HasColumnType("integer") + .HasComment("当前已参与人数。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndAt") + .HasColumnType("timestamp with time zone") + .HasComment("结束时间。"); + + b.Property("GroupOrderNo") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("拼单编号。"); + + b.Property("GroupPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("拼团价格。"); + + b.Property("LeaderUserId") + .HasColumnType("bigint") + .HasComment("团长用户 ID。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("关联商品或套餐。"); + + b.Property("StartAt") + .HasColumnType("timestamp with time zone") + .HasComment("开始时间。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("拼团状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("SucceededAt") + .HasColumnType("timestamp with time zone") + .HasComment("成团时间。"); + + b.Property("TargetCount") + .HasColumnType("integer") + .HasComment("成团需要的人数。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupOrderNo") + .IsUnique(); + + b.ToTable("group_orders", null, t => + { + t.HasComment("拼单活动。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.GroupBuying.Entities.GroupParticipant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("GroupOrderId") + .HasColumnType("bigint") + .HasComment("拼单活动标识。"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone") + .HasComment("参与时间。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("对应订单标识。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("参与状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户标识。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "GroupOrderId", "UserId") + .IsUnique(); + + b.ToTable("group_participants", null, t => + { + t.HasComment("拼单参与者。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Inventory.Entities.InventoryAdjustment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdjustmentType") + .HasColumnType("integer") + .HasComment("调整类型。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("InventoryItemId") + .HasColumnType("bigint") + .HasComment("对应的库存记录标识。"); + + b.Property("OccurredAt") + .HasColumnType("timestamp with time zone") + .HasComment("发生时间。"); + + b.Property("OperatorId") + .HasColumnType("bigint") + .HasComment("操作人标识。"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasComment("调整数量,正数增加,负数减少。"); + + b.Property("Reason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("原因说明。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "InventoryItemId", "OccurredAt"); + + b.ToTable("inventory_adjustments", null, t => + { + t.HasComment("库存调整记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Inventory.Entities.InventoryBatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BatchNumber") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("批次编号。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExpireDate") + .HasColumnType("timestamp with time zone") + .HasComment("过期日期。"); + + b.Property("ProductSkuId") + .HasColumnType("bigint") + .HasComment("SKU 标识。"); + + b.Property("ProductionDate") + .HasColumnType("timestamp with time zone") + .HasComment("生产日期。"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasComment("入库数量。"); + + b.Property("RemainingQuantity") + .HasColumnType("integer") + .HasComment("剩余数量。"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasComment("并发控制字段。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "ProductSkuId", "BatchNumber") + .IsUnique(); + + b.ToTable("inventory_batches", null, t => + { + t.HasComment("SKU 批次信息。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Inventory.Entities.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BatchConsumeStrategy") + .HasColumnType("integer") + .HasComment("批次扣减策略。"); + + b.Property("BatchNumber") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("批次编号,可为空表示混批。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExpireDate") + .HasColumnType("timestamp with time zone") + .HasComment("过期日期。"); + + b.Property("IsPresale") + .HasColumnType("boolean") + .HasComment("是否预售商品。"); + + b.Property("IsSoldOut") + .HasColumnType("boolean") + .HasComment("是否标记售罄。"); + + b.Property("Location") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("储位或仓位信息。"); + + b.Property("MaxQuantityPerOrder") + .HasColumnType("integer") + .HasComment("单品限购(覆盖商品级 MaxQuantityPerOrder)。"); + + b.Property("PresaleCapacity") + .HasColumnType("integer") + .HasComment("预售名额(上限)。"); + + b.Property("PresaleEndTime") + .HasColumnType("timestamp with time zone") + .HasComment("预售结束时间(UTC)。"); + + b.Property("PresaleLocked") + .HasColumnType("integer") + .HasComment("当前预售已锁定数量。"); + + b.Property("PresaleStartTime") + .HasColumnType("timestamp with time zone") + .HasComment("预售开始时间(UTC)。"); + + b.Property("ProductSkuId") + .HasColumnType("bigint") + .HasComment("SKU 标识。"); + + b.Property("QuantityOnHand") + .HasColumnType("integer") + .HasComment("可用库存。"); + + b.Property("QuantityReserved") + .HasColumnType("integer") + .HasComment("已锁定库存(订单占用)。"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasComment("并发控制字段。"); + + b.Property("SafetyStock") + .HasColumnType("integer") + .HasComment("安全库存阈值。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "ProductSkuId", "BatchNumber"); + + b.ToTable("inventory_items", null, t => + { + t.HasComment("SKU 在门店的库存信息。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Inventory.Entities.InventoryLockRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone") + .HasComment("过期时间(UTC)。"); + + b.Property("IdempotencyKey") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("幂等键。"); + + b.Property("IsPresale") + .HasColumnType("boolean") + .HasComment("是否预售锁定。"); + + b.Property("ProductSkuId") + .HasColumnType("bigint") + .HasComment("SKU ID。"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasComment("锁定数量。"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasComment("并发控制字段。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("锁定状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "IdempotencyKey") + .IsUnique(); + + b.HasIndex("TenantId", "StoreId", "ProductSkuId", "Status"); + + b.ToTable("inventory_lock_records", null, t => + { + t.HasComment("库存锁定记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Membership.Entities.MemberPointLedger", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BalanceAfterChange") + .HasColumnType("integer") + .HasComment("变动后余额。"); + + b.Property("ChangeAmount") + .HasColumnType("integer") + .HasComment("变动数量,可为负值。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone") + .HasComment("过期时间(如适用)。"); + + b.Property("MemberId") + .HasColumnType("bigint") + .HasComment("会员标识。"); + + b.Property("OccurredAt") + .HasColumnType("timestamp with time zone") + .HasComment("发生时间。"); + + b.Property("Reason") + .HasColumnType("integer") + .HasComment("变动原因。"); + + b.Property("SourceId") + .HasColumnType("bigint") + .HasComment("来源 ID(订单、活动等)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "MemberId", "OccurredAt"); + + b.ToTable("member_point_ledgers", null, t => + { + t.HasComment("积分变动流水。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Membership.Entities.MemberProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AvatarUrl") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("头像。"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone") + .HasComment("生日。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("GrowthValue") + .HasColumnType("integer") + .HasComment("成长值/经验值。"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone") + .HasComment("注册时间。"); + + b.Property("MemberTierId") + .HasColumnType("bigint") + .HasComment("当前会员等级 ID。"); + + b.Property("Mobile") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("手机号。"); + + b.Property("Nickname") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("昵称。"); + + b.Property("PointsBalance") + .HasColumnType("integer") + .HasComment("会员积分余额。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("会员状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户标识。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Mobile") + .IsUnique(); + + b.ToTable("member_profiles", null, t => + { + t.HasComment("会员档案。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Membership.Entities.MemberTier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BenefitsJson") + .IsRequired() + .HasColumnType("text") + .HasComment("等级权益(JSON)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("等级名称。"); + + b.Property("RequiredGrowth") + .HasColumnType("integer") + .HasComment("所需成长值。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name") + .IsUnique(); + + b.ToTable("member_tiers", null, t => + { + t.HasComment("会员等级定义。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Merchants.Entities.Merchant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("详细地址。"); + + b.Property("ApprovedAt") + .HasColumnType("timestamp with time zone") + .HasComment("审核通过时间。"); + + b.Property("ApprovedBy") + .HasColumnType("bigint") + .HasComment("审核通过人。"); + + b.Property("BrandAlias") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("品牌简称或别名。"); + + b.Property("BrandName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("品牌名称(对外展示)。"); + + b.Property("BusinessLicenseImageUrl") + .HasColumnType("text") + .HasComment("营业执照扫描件地址。"); + + b.Property("BusinessLicenseNumber") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("营业执照号。"); + + b.Property("Category") + .HasColumnType("text") + .HasComment("品牌所属品类,如火锅、咖啡等。"); + + b.Property("City") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("所在城市。"); + + b.Property("ClaimExpiresAt") + .HasColumnType("timestamp with time zone") + .HasComment("领取过期时间。"); + + b.Property("ClaimedAt") + .HasColumnType("timestamp with time zone") + .HasComment("领取时间。"); + + b.Property("ClaimedBy") + .HasColumnType("bigint") + .HasComment("当前领取人。"); + + b.Property("ClaimedByName") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("当前领取人姓名。"); + + b.Property("ContactEmail") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("联系邮箱。"); + + b.Property("ContactPhone") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("联系电话。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("District") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("所在区县。"); + + b.Property("FrozenAt") + .HasColumnType("timestamp with time zone") + .HasComment("冻结时间。"); + + b.Property("FrozenReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("冻结原因。"); + + b.Property("GeoFailReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("地理定位失败原因。"); + + b.Property("GeoNextRetryAt") + .HasColumnType("timestamp with time zone") + .HasComment("下次地理定位重试时间(UTC)。"); + + b.Property("GeoRetryCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("地理定位重试次数。"); + + b.Property("GeoStatus") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("地理定位状态。"); + + b.Property("GeoUpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("地理定位最近成功时间(UTC)。"); + + b.Property("IsFrozen") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否冻结业务。"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone") + .HasComment("入驻时间。"); + + b.Property("LastReviewedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次审核时间。"); + + b.Property("LastReviewedBy") + .HasColumnType("bigint") + .HasComment("最近一次审核人。"); + + b.Property("Latitude") + .HasColumnType("double precision") + .HasComment("纬度信息。"); + + b.Property("LegalPerson") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("法人或负责人姓名。"); + + b.Property("LogoUrl") + .HasColumnType("text") + .HasComment("品牌 Logo。"); + + b.Property("Longitude") + .HasColumnType("double precision") + .HasComment("经度信息。"); + + b.Property("OperatingMode") + .HasColumnType("integer") + .HasComment("经营模式(同一主体/不同主体)。"); + + b.Property("Province") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("所在省份。"); + + b.Property("ReviewRemarks") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("审核备注或驳回原因。"); + + b.Property("ServicePhone") + .HasColumnType("text") + .HasComment("客服电话。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("入驻状态。"); + + b.Property("SupportEmail") + .HasColumnType("text") + .HasComment("客服邮箱。"); + + b.Property("TaxNumber") + .HasColumnType("text") + .HasComment("税号/统一社会信用代码。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("xmin") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("ClaimedBy"); + + b.HasIndex("TenantId"); + + b.HasIndex("Longitude", "Latitude") + .HasFilter("\"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL"); + + b.HasIndex("TenantId", "Status"); + + b.HasIndex("TenantId", "GeoStatus", "GeoNextRetryAt"); + + b.ToTable("merchants", null, t => + { + t.HasComment("商户主体信息,承载入驻和资质审核结果。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Merchants.Entities.MerchantCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DisplayOrder") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("显示顺序,越小越靠前。"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasComment("是否可用。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("类目名称。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name") + .IsUnique(); + + b.ToTable("merchant_categories", null, t => + { + t.HasComment("商户可选类目。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Merchants.Entities.MerchantContract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContractNumber") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("合同编号。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone") + .HasComment("合同结束时间。"); + + b.Property("FileUrl") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("合同文件存储地址。"); + + b.Property("MerchantId") + .HasColumnType("bigint") + .HasComment("所属商户标识。"); + + b.Property("SignedAt") + .HasColumnType("timestamp with time zone") + .HasComment("签署时间。"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasComment("合同开始时间。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("合同状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TerminatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("终止时间。"); + + b.Property("TerminationReason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("终止原因。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "MerchantId", "ContractNumber") + .IsUnique(); + + b.ToTable("merchant_contracts", null, t => + { + t.HasComment("商户合同记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Merchants.Entities.MerchantDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DocumentNumber") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("证照编号。"); + + b.Property("DocumentType") + .HasColumnType("integer") + .HasComment("证照类型。"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone") + .HasComment("到期日期。"); + + b.Property("FileUrl") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("证照文件链接。"); + + b.Property("IssuedAt") + .HasColumnType("timestamp with time zone") + .HasComment("签发日期。"); + + b.Property("MerchantId") + .HasColumnType("bigint") + .HasComment("所属商户标识。"); + + b.Property("Remarks") + .HasColumnType("text") + .HasComment("审核备注或驳回原因。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("审核状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "MerchantId", "DocumentType"); + + b.ToTable("merchant_documents", null, t => + { + t.HasComment("商户提交的资质或证照材料。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Merchants.Entities.MerchantStaff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("邮箱地址。"); + + b.Property("IdentityUserId") + .HasColumnType("bigint") + .HasComment("登录账号 ID(指向统一身份体系)。"); + + b.Property("MerchantId") + .HasColumnType("bigint") + .HasComment("所属商户标识。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("员工姓名。"); + + b.Property("PermissionsJson") + .HasColumnType("text") + .HasComment("自定义权限(JSON)。"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("手机号。"); + + b.Property("RoleType") + .HasColumnType("integer") + .HasComment("员工角色类型。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("员工状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("可选的关联门店 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "MerchantId", "Phone"); + + b.ToTable("merchant_staff", null, t => + { + t.HasComment("商户员工账号,支持门店维度分配。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Navigation.Entities.MapLocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("地址。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Landmark") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("打车/导航落点描述。"); + + b.Property("Latitude") + .HasColumnType("double precision") + .HasComment("纬度。"); + + b.Property("Longitude") + .HasColumnType("double precision") + .HasComment("经度。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("名称。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("关联门店 ID,可空表示独立 POI。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId"); + + b.ToTable("map_locations", null, t => + { + t.HasComment("地图 POI 信息,用于门店定位和推荐。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Navigation.Entities.NavigationRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Channel") + .HasColumnType("integer") + .HasComment("来源通道(小程序、H5 等)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("RequestedAt") + .HasColumnType("timestamp with time zone") + .HasComment("请求时间。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店 ID。"); + + b.Property("TargetApp") + .HasColumnType("integer") + .HasComment("跳转的地图应用。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户 ID。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "StoreId", "RequestedAt"); + + b.ToTable("navigation_requests", null, t => + { + t.HasComment("用户发起的导航请求日志。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Ordering.Entities.CartItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AttributesJson") + .HasColumnType("text") + .HasComment("扩展 JSON(规格、加料选项等)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("商品或 SKU 标识。"); + + b.Property("ProductName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("商品名称快照。"); + + b.Property("ProductSkuId") + .HasColumnType("bigint") + .HasComment("SKU 标识。"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasComment("数量。"); + + b.Property("Remark") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("自定义备注(口味要求)。"); + + b.Property("ShoppingCartId") + .HasColumnType("bigint") + .HasComment("所属购物车标识。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UnitPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("单价快照。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ShoppingCartId"); + + b.ToTable("cart_items", null, t => + { + t.HasComment("购物车条目。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Ordering.Entities.CartItemAddon", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CartItemId") + .HasColumnType("bigint") + .HasComment("所属购物车条目。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExtraPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("附加价格。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("选项名称。"); + + b.Property("OptionId") + .HasColumnType("bigint") + .HasComment("选项 ID(可对应 ProductAddonOption)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.ToTable("cart_item_addons", null, t => + { + t.HasComment("购物车条目的加料/附加项。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Ordering.Entities.CheckoutSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone") + .HasComment("过期时间(UTC)。"); + + b.Property("SessionToken") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("会话 Token。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("会话状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户标识。"); + + b.Property("ValidationResultJson") + .IsRequired() + .HasColumnType("text") + .HasComment("校验结果明细 JSON。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "SessionToken") + .IsUnique(); + + b.ToTable("checkout_sessions", null, t => + { + t.HasComment("结账会话,记录校验上下文。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Ordering.Entities.ShoppingCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DeliveryPreference") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("履约方式(堂食/自提/配送)缓存。"); + + b.Property("LastModifiedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次修改时间(UTC)。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("购物车状态,包含正常/锁定。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TableContext") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("桌码或场景标识(扫码点餐)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("用户标识。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId", "StoreId") + .IsUnique(); + + b.ToTable("shopping_carts", null, t => + { + t.HasComment("用户购物车,按租户/门店隔离。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Orders.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelReason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("取消原因。"); + + b.Property("CancelledAt") + .HasColumnType("timestamp with time zone") + .HasComment("取消时间。"); + + b.Property("Channel") + .HasColumnType("integer") + .HasComment("下单渠道。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("CustomerName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("顾客姓名。"); + + b.Property("CustomerPhone") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("顾客手机号。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DeliveryType") + .HasColumnType("integer") + .HasComment("履约类型。"); + + b.Property("DiscountAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("优惠金额。"); + + b.Property("FinishedAt") + .HasColumnType("timestamp with time zone") + .HasComment("完成时间。"); + + b.Property("ItemsAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("商品总额。"); + + b.Property("OrderNo") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("订单号。"); + + b.Property("PaidAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("实付金额。"); + + b.Property("PaidAt") + .HasColumnType("timestamp with time zone") + .HasComment("支付时间。"); + + b.Property("PayableAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("应付金额。"); + + b.Property("PaymentStatus") + .HasColumnType("integer") + .HasComment("支付状态。"); + + b.Property("QueueNumber") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("排队号(如有)。"); + + b.Property("Remark") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注。"); + + b.Property("ReservationId") + .HasColumnType("bigint") + .HasComment("预约 ID。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("当前状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店。"); + + b.Property("TableNo") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("就餐桌号。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "OrderNo") + .IsUnique(); + + b.HasIndex("TenantId", "StoreId", "Status"); + + b.ToTable("orders", null, t => + { + t.HasComment("交易订单。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Orders.Entities.OrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AttributesJson") + .HasColumnType("text") + .HasComment("自定义属性 JSON。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DiscountAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("折扣金额。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("订单 ID。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("商品 ID。"); + + b.Property("ProductName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("商品名称。"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasComment("数量。"); + + b.Property("SkuName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("SKU/规格描述。"); + + b.Property("SubTotal") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("小计。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("Unit") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasComment("单位。"); + + b.Property("UnitPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("单价。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("TenantId", "OrderId"); + + b.ToTable("order_items", null, t => + { + t.HasComment("订单明细。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Orders.Entities.OrderStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Notes") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("备注信息。"); + + b.Property("OccurredAt") + .HasColumnType("timestamp with time zone") + .HasComment("发生时间。"); + + b.Property("OperatorId") + .HasColumnType("bigint") + .HasComment("操作人标识(可为空表示系统)。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("订单标识。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("变更后的状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "OrderId", "OccurredAt"); + + b.ToTable("order_status_histories", null, t => + { + t.HasComment("订单状态流转记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Orders.Entities.RefundRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("申请金额。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("关联订单标识。"); + + b.Property("ProcessedAt") + .HasColumnType("timestamp with time zone") + .HasComment("审核完成时间。"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("申请原因。"); + + b.Property("RefundNo") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("退款单号。"); + + b.Property("RequestedAt") + .HasColumnType("timestamp with time zone") + .HasComment("用户提交时间。"); + + b.Property("ReviewNotes") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("审核备注。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("退款状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "RefundNo") + .IsUnique(); + + b.ToTable("refund_requests", null, t => + { + t.HasComment("售后/退款申请。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Payments.Entities.PaymentRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("支付金额。"); + + b.Property("ChannelTransactionId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("第三方渠道单号。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Method") + .HasColumnType("integer") + .HasComment("支付方式。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("关联订单。"); + + b.Property("PaidAt") + .HasColumnType("timestamp with time zone") + .HasComment("支付完成时间。"); + + b.Property("Payload") + .HasColumnType("text") + .HasComment("原始回调内容。"); + + b.Property("Remark") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("错误/备注。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("支付状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TradeNo") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("系统交易号。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "OrderId"); + + b.ToTable("payment_records", null, t => + { + t.HasComment("支付流水。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Payments.Entities.PaymentRefundRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("退款金额。"); + + b.Property("ChannelRefundId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("渠道退款流水号。"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("完成时间。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("OrderId") + .HasColumnType("bigint") + .HasComment("关联订单标识。"); + + b.Property("Payload") + .HasColumnType("text") + .HasComment("渠道返回的原始数据 JSON。"); + + b.Property("PaymentRecordId") + .HasColumnType("bigint") + .HasComment("原支付记录标识。"); + + b.Property("RequestedAt") + .HasColumnType("timestamp with time zone") + .HasComment("退款请求时间。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("退款状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "PaymentRecordId"); + + b.ToTable("payment_refund_records", null, t => + { + t.HasComment("支付渠道退款流水。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("bigint") + .HasComment("所属分类。"); + + b.Property("CoverImage") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("主图。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasColumnType("text") + .HasComment("商品描述。"); + + b.Property("EnableDelivery") + .HasColumnType("boolean") + .HasComment("支持配送。"); + + b.Property("EnableDineIn") + .HasColumnType("boolean") + .HasComment("支持堂食。"); + + b.Property("EnablePickup") + .HasColumnType("boolean") + .HasComment("支持自提。"); + + b.Property("GalleryImages") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasComment("Gallery 图片逗号分隔。"); + + b.Property("IsFeatured") + .HasColumnType("boolean") + .HasComment("是否热门推荐。"); + + b.Property("Kind") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("商品类型。"); + + b.Property("MaxQuantityPerOrder") + .HasColumnType("integer") + .HasComment("最大每单限购。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("商品名称。"); + + b.Property("NotifyManager") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否通知店长。"); + + b.Property("OriginalPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("原价。"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("现价。"); + + b.Property("RecoverAt") + .HasColumnType("timestamp with time zone") + .HasComment("沽清恢复时间。"); + + b.Property("RemainStock") + .HasColumnType("integer") + .HasComment("沽清后剩余可售。"); + + b.Property("SalesMonthly") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("月销量。"); + + b.Property("SoldoutMode") + .HasColumnType("integer") + .HasComment("沽清模式。"); + + b.Property("SoldoutReason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("沽清原因。"); + + b.Property("SpuCode") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("商品编码。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("商品状态。"); + + b.Property("StockQuantity") + .HasColumnType("integer") + .HasComment("库存数量(可选)。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("Subtitle") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("副标题/卖点。"); + + b.Property("SyncToPlatform") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否同步通知外卖平台。"); + + b.Property("TagsJson") + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("[]") + .HasComment("标签 JSON(字符串数组)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TimedOnShelfAt") + .HasColumnType("timestamp with time zone") + .HasComment("定时上架时间。"); + + b.Property("Unit") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasComment("售卖单位(份/杯等)。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "SpuCode") + .IsUnique(); + + b.HasIndex("TenantId", "StoreId"); + + b.ToTable("products", null, t => + { + t.HasComment("商品(SPU)信息。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductAddonGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("IsRequired") + .HasColumnType("boolean") + .HasComment("是否必选。"); + + b.Property("MaxSelect") + .HasColumnType("integer") + .HasComment("最大选择数量。"); + + b.Property("MinSelect") + .HasColumnType("integer") + .HasComment("最小选择数量。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("分组名称。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("所属商品。"); + + b.Property("SelectionType") + .HasColumnType("integer") + .HasComment("选择类型。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ProductId", "Name"); + + b.ToTable("product_addon_groups", null, t => + { + t.HasComment("加料/做法分组。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductAddonOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddonGroupId") + .HasColumnType("bigint") + .HasComment("所属加料分组。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExtraPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("附加价格。"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasComment("是否默认选项。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("选项名称。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.ToTable("product_addon_options", null, t => + { + t.HasComment("加料选项。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductAttributeGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("IsRequired") + .HasColumnType("boolean") + .HasComment("是否必选。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("分组名称,例如“辣度”“份量”。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("所属商品标识。"); + + b.Property("SelectionType") + .HasColumnType("integer") + .HasComment("选择类型(单选/多选)。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("显示排序。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("关联门店,可为空表示所有门店共享。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "Name"); + + b.ToTable("product_attribute_groups", null, t => + { + t.HasComment("商品规格/属性分组。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductAttributeOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AttributeGroupId") + .HasColumnType("bigint") + .HasComment("所属规格组。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExtraPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("附加价格。"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasComment("是否默认选中。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("选项名称。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "AttributeGroupId", "Name") + .IsUnique(); + + b.ToTable("product_attribute_options", null, t => + { + t.HasComment("商品规格选项。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChannelsJson") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("[\"wm\"]") + .HasComment("分类可见渠道 JSON。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("分类描述。"); + + b.Property("Icon") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("分类图标。"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("分类名称。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId"); + + b.ToTable("product_categories", null, t => + { + t.HasComment("商品分类。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductLabel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Color") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("标签颜色(HEX)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("标签名称。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "Name") + .IsUnique(); + + b.HasIndex("TenantId", "StoreId", "IsEnabled", "SortOrder"); + + b.ToTable("product_labels", null, t => + { + t.HasComment("商品标签模板(门店维度)。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductLabelProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("LabelId") + .HasColumnType("bigint") + .HasComment("标签 ID。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("商品 ID。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "ProductId"); + + b.HasIndex("TenantId", "StoreId", "LabelId", "ProductId") + .IsUnique(); + + b.ToTable("product_label_products", null, t => + { + t.HasComment("标签与商品关联关系。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductMediaAsset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Caption") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("描述或标题。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("MediaType") + .HasColumnType("integer") + .HasComment("媒体类型。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("商品标识。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("媒资链接。"); + + b.HasKey("Id"); + + b.ToTable("product_media_assets", null, t => + { + t.HasComment("商品媒资素材。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductPricingRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConditionsJson") + .IsRequired() + .HasColumnType("text") + .HasComment("条件描述(JSON),如会员等级、渠道等。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasComment("生效结束时间。"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("特殊价格。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("所属商品。"); + + b.Property("RuleType") + .HasColumnType("integer") + .HasComment("策略类型。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasComment("生效开始时间。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("WeekdaysJson") + .HasColumnType("text") + .HasComment("生效星期(JSON 数组)。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ProductId", "RuleType"); + + b.ToTable("product_pricing_rules", null, t => + { + t.HasComment("商品价格策略,支持会员价/时段价等。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndTime") + .HasColumnType("interval") + .HasComment("结束时间。"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("规则名称。"); + + b.Property("StartTime") + .HasColumnType("interval") + .HasComment("开始时间。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("WeekDaysMask") + .HasColumnType("integer") + .HasComment("星期位掩码(周一到周日)。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "IsEnabled"); + + b.HasIndex("TenantId", "StoreId", "Name") + .IsUnique(); + + b.ToTable("product_schedules", null, t => + { + t.HasComment("商品时段规则(门店维度)。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductScheduleProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("商品 ID。"); + + b.Property("ScheduleId") + .HasColumnType("bigint") + .HasComment("时段规则 ID。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "ProductId"); + + b.HasIndex("TenantId", "StoreId", "ScheduleId", "ProductId") + .IsUnique(); + + b.ToTable("product_schedule_products", null, t => + { + t.HasComment("时段规则与商品关联关系。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductSku", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AttributesJson") + .IsRequired() + .HasColumnType("text") + .HasComment("规格属性 JSON(记录选项 ID)。"); + + b.Property("Barcode") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("条形码。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("OriginalPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("原价。"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("售价。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("所属商品标识。"); + + b.Property("SkuCode") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("SKU 编码。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("StockQuantity") + .HasColumnType("integer") + .HasComment("可售库存。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("Weight") + .HasPrecision(10, 3) + .HasColumnType("numeric(10,3)") + .HasComment("重量(千克)。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "SkuCode") + .IsUnique(); + + b.ToTable("product_skus", null, t => + { + t.HasComment("商品 SKU,记录具体规格组合价格。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductSpecTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("模板描述。"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用。"); + + b.Property("IsRequired") + .HasColumnType("boolean") + .HasComment("是否必选。"); + + b.Property("MaxSelect") + .HasColumnType("integer") + .HasComment("最大可选数。"); + + b.Property("MinSelect") + .HasColumnType("integer") + .HasComment("最小可选数。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("模板名称。"); + + b.Property("SelectionType") + .HasColumnType("integer") + .HasComment("选择方式。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("TemplateType") + .HasColumnType("integer") + .HasComment("模板类型。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "TemplateType", "IsEnabled"); + + b.HasIndex("TenantId", "StoreId", "TemplateType", "Name") + .IsUnique(); + + b.ToTable("product_spec_templates", null, t => + { + t.HasComment("门店规格做法模板。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductSpecTemplateOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExtraPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("附加价格。"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("选项名称。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("Stock") + .HasColumnType("integer") + .HasComment("库存数量。"); + + b.Property("TemplateId") + .HasColumnType("bigint") + .HasComment("模板 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "TemplateId", "Name") + .IsUnique(); + + b.ToTable("product_spec_template_options", null, t => + { + t.HasComment("规格做法模板选项。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Products.Entities.ProductSpecTemplateProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ProductId") + .HasColumnType("bigint") + .HasComment("商品 ID。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("所属门店。"); + + b.Property("TemplateId") + .HasColumnType("bigint") + .HasComment("模板 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "ProductId"); + + b.HasIndex("TenantId", "StoreId", "TemplateId", "ProductId") + .IsUnique(); + + b.ToTable("product_spec_template_products", null, t => + { + t.HasComment("规格做法模板与商品关联。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Queues.Entities.QueueTicket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CalledAt") + .HasColumnType("timestamp with time zone") + .HasComment("叫号时间。"); + + b.Property("CancelledAt") + .HasColumnType("timestamp with time zone") + .HasComment("取消时间。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EstimatedWaitMinutes") + .HasColumnType("integer") + .HasComment("预计等待分钟。"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasComment("过号时间。"); + + b.Property("PartySize") + .HasColumnType("integer") + .HasComment("就餐人数。"); + + b.Property("Remark") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("备注。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("获取或设置所属门店 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TicketNumber") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("排队编号。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId"); + + b.HasIndex("TenantId", "StoreId", "TicketNumber") + .IsUnique(); + + b.ToTable("queue_tickets", null, t => + { + t.HasComment("排队叫号。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Reservations.Entities.Reservation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelledAt") + .HasColumnType("timestamp with time zone") + .HasComment("取消时间。"); + + b.Property("CheckInCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("核销码/到店码。"); + + b.Property("CheckedInAt") + .HasColumnType("timestamp with time zone") + .HasComment("实际签到时间。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("CustomerName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("客户姓名。"); + + b.Property("CustomerPhone") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("联系电话。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("PeopleCount") + .HasColumnType("integer") + .HasComment("用餐人数。"); + + b.Property("Remark") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注。"); + + b.Property("ReservationNo") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("预约号。"); + + b.Property("ReservationTime") + .HasColumnType("timestamp with time zone") + .HasComment("预约时间(UTC)。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店。"); + + b.Property("TablePreference") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("桌型/标签。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ReservationNo") + .IsUnique(); + + b.HasIndex("TenantId", "StoreId"); + + b.ToTable("reservations", null, t => + { + t.HasComment("预约/预订记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.Store", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActivatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("审核通过时间。"); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("详细地址。"); + + b.Property("Announcement") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("门店公告。"); + + b.Property("AuditStatus") + .HasColumnType("integer") + .HasComment("审核状态。"); + + b.Property("BusinessHours") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("门店营业时段描述(备用字符串)。"); + + b.Property("BusinessLicenseImageUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("门店营业执照图片地址(主体不一致模式使用)。"); + + b.Property("BusinessLicenseNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("门店营业执照号(主体不一致模式使用)。"); + + b.Property("BusinessStatus") + .HasColumnType("integer") + .HasComment("经营状态。"); + + b.Property("CategoryId") + .HasColumnType("bigint") + .HasComment("行业类目 ID。"); + + b.Property("City") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("所在城市。"); + + b.Property("ClosureReason") + .HasColumnType("integer") + .HasComment("歇业原因。"); + + b.Property("ClosureReasonText") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("歇业原因补充说明。"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("门店编码,便于扫码及外部对接。"); + + b.Property("Country") + .HasColumnType("text") + .HasComment("所在国家或地区。"); + + b.Property("CoverImageUrl") + .HasColumnType("text") + .HasComment("门店海报。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DeliveryRadiusKm") + .HasPrecision(6, 2) + .HasColumnType("numeric(6,2)") + .HasComment("默认配送半径(公里)。"); + + b.Property("Description") + .HasColumnType("text") + .HasComment("门店描述或公告。"); + + b.Property("District") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("区县信息。"); + + b.Property("ForceCloseReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("强制关闭原因。"); + + b.Property("ForceClosedAt") + .HasColumnType("timestamp with time zone") + .HasComment("强制关闭时间。"); + + b.Property("GeoFailReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("地理定位失败原因。"); + + b.Property("GeoNextRetryAt") + .HasColumnType("timestamp with time zone") + .HasComment("下次地理定位重试时间(UTC)。"); + + b.Property("GeoRetryCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("地理定位重试次数。"); + + b.Property("GeoStatus") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("地理定位状态。"); + + b.Property("GeoUpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("地理定位最近成功时间(UTC)。"); + + b.Property("Latitude") + .HasColumnType("double precision") + .HasComment("纬度。"); + + b.Property("LegalRepresentative") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("门店法人(主体不一致模式使用)。"); + + b.Property("Longitude") + .HasColumnType("double precision") + .HasComment("高德/腾讯地图经度。"); + + b.Property("ManagerName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("门店负责人姓名。"); + + b.Property("MerchantId") + .HasColumnType("bigint") + .HasComment("所属商户标识。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("门店名称。"); + + b.Property("OwnershipType") + .HasColumnType("integer") + .HasComment("主体类型。"); + + b.Property("Phone") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("联系电话。"); + + b.Property("Province") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("所在省份。"); + + b.Property("RegisteredAddress") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("门店注册地址(主体不一致模式使用)。"); + + b.Property("RejectionReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("审核驳回原因。"); + + b.Property("SignboardImageUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("门头招牌图 URL。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("门店当前运营状态。"); + + b.Property("SubmittedAt") + .HasColumnType("timestamp with time zone") + .HasComment("提交审核时间。"); + + b.Property("SupportsDelivery") + .HasColumnType("boolean") + .HasComment("是否支持配送。"); + + b.Property("SupportsDineIn") + .HasColumnType("boolean") + .HasComment("是否支持堂食。"); + + b.Property("SupportsPickup") + .HasColumnType("boolean") + .HasComment("是否支持自提。"); + + b.Property("SupportsQueueing") + .HasColumnType("boolean") + .HasComment("支持排队叫号。"); + + b.Property("SupportsReservation") + .HasColumnType("boolean") + .HasComment("支持预约。"); + + b.Property("Tags") + .HasColumnType("text") + .HasComment("门店标签(逗号分隔)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("Longitude", "Latitude") + .HasFilter("\"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL"); + + b.HasIndex("MerchantId", "BusinessLicenseNumber") + .IsUnique() + .HasFilter("\"BusinessLicenseNumber\" IS NOT NULL AND \"Status\" <> 3"); + + b.HasIndex("TenantId", "AuditStatus"); + + b.HasIndex("TenantId", "BusinessStatus"); + + b.HasIndex("TenantId", "Code") + .IsUnique(); + + b.HasIndex("TenantId", "MerchantId"); + + b.HasIndex("TenantId", "OwnershipType"); + + b.HasIndex("TenantId", "MerchantId", "GeoStatus", "GeoNextRetryAt"); + + b.ToTable("stores", null, t => + { + t.HasComment("门店信息,承载营业配置与能力。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreAuditRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("integer") + .HasComment("操作类型。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("NewStatus") + .HasColumnType("integer") + .HasComment("操作后状态。"); + + b.Property("OperatorId") + .HasColumnType("bigint") + .HasComment("操作人 ID。"); + + b.Property("OperatorName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("操作人名称。"); + + b.Property("PreviousStatus") + .HasColumnType("integer") + .HasComment("操作前状态。"); + + b.Property("RejectionReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("驳回理由文本。"); + + b.Property("RejectionReasonId") + .HasColumnType("bigint") + .HasComment("驳回理由 ID。"); + + b.Property("Remarks") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasComment("备注。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("TenantId", "StoreId"); + + b.ToTable("store_audit_records", null, t => + { + t.HasComment("门店审核记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreBusinessHour", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CapacityLimit") + .HasColumnType("integer") + .HasComment("最大接待容量或单量限制。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DayOfWeek") + .HasColumnType("integer") + .HasComment("星期几,0 表示周日。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndTime") + .HasColumnType("interval") + .HasComment("结束时间(本地时间)。"); + + b.Property("HourType") + .HasColumnType("integer") + .HasComment("时段类型(正常营业、休息、预约等)。"); + + b.Property("Notes") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("备注。"); + + b.Property("StartTime") + .HasColumnType("interval") + .HasComment("开始时间(本地时间)。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "DayOfWeek"); + + b.ToTable("store_business_hours", null, t => + { + t.HasComment("门店营业时段配置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreDeliverySetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EtaAdjustmentMinutes") + .HasColumnType("integer") + .HasComment("配送时效加成(分钟)。"); + + b.Property("FreeDeliveryThreshold") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("免配送费门槛。"); + + b.Property("HourlyCapacityLimit") + .HasColumnType("integer") + .HasComment("每小时配送上限。"); + + b.Property("MaxDeliveryDistance") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("最大配送距离(公里)。"); + + b.Property("Mode") + .HasColumnType("integer") + .HasComment("配送模式。"); + + b.Property("RadiusCenterLatitude") + .HasPrecision(10, 7) + .HasColumnType("numeric(10,7)") + .HasComment("半径配送中心点纬度。"); + + b.Property("RadiusCenterLongitude") + .HasPrecision(10, 7) + .HasColumnType("numeric(10,7)") + .HasComment("半径配送中心点经度。"); + + b.Property("RadiusTiersJson") + .HasColumnType("text") + .HasComment("半径梯度配置 JSON。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId") + .IsUnique(); + + b.ToTable("store_delivery_settings", null, t => + { + t.HasComment("门店配送设置聚合。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreDeliveryZone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Color") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("区域颜色。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DeliveryFee") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("配送费。"); + + b.Property("EstimatedMinutes") + .HasColumnType("integer") + .HasComment("预计送达分钟。"); + + b.Property("MinimumOrderAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("起送价。"); + + b.Property("PolygonGeoJson") + .IsRequired() + .HasColumnType("text") + .HasComment("GeoJSON 表示的多边形范围。"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(100) + .HasComment("优先级(数值越小越优先)。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("ZoneName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("区域名称。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "ZoneName"); + + b.ToTable("store_delivery_zones", null, t => + { + t.HasComment("门店配送范围配置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreDineInSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DefaultDiningMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(90) + .HasComment("默认用餐时长(分钟)。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否启用堂食。"); + + b.Property("OvertimeReminderMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(10) + .HasComment("超时提醒阈值(分钟)。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId") + .IsUnique(); + + b.ToTable("store_dinein_settings", null, t => + { + t.HasComment("门店堂食基础设置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreEmployeeShift", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndTime") + .HasColumnType("interval") + .HasComment("结束时间。"); + + b.Property("Notes") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("备注。"); + + b.Property("RoleType") + .HasColumnType("integer") + .HasComment("排班角色。"); + + b.Property("ShiftDate") + .HasColumnType("timestamp with time zone") + .HasComment("班次日期。"); + + b.Property("StaffId") + .HasColumnType("bigint") + .HasComment("员工标识。"); + + b.Property("StartTime") + .HasColumnType("interval") + .HasComment("开始时间。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "ShiftDate", "StaffId") + .IsUnique(); + + b.ToTable("store_employee_shifts", null, t => + { + t.HasComment("门店员工排班记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreFee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BaseDeliveryFee") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("基础配送费(元)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("CutleryFeeAmount") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("餐具费金额。"); + + b.Property("CutleryFeeEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否启用餐具费。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("FixedPackagingFee") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("固定打包费(总计模式有效)。"); + + b.Property("FreeDeliveryThreshold") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("免配送费门槛。"); + + b.Property("MinimumOrderAmount") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("起送费(元)。"); + + b.Property("OrderPackagingFeeMode") + .HasColumnType("integer") + .HasComment("订单打包费规则(按订单收费时生效)。"); + + b.Property("PackagingFeeMode") + .HasColumnType("integer") + .HasComment("打包费模式。"); + + b.Property("PackagingFeeTiersJson") + .HasColumnType("text") + .HasComment("阶梯打包费配置(JSON)。"); + + b.Property("RushFeeAmount") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasComment("加急费金额。"); + + b.Property("RushFeeEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否启用加急费。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("TenantId", "StoreId") + .IsUnique(); + + b.ToTable("store_fees", null, t => + { + t.HasComment("门店费用配置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreHoliday", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasComment("开始日期(原 Date 字段)。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone") + .HasComment("结束日期(可选,用于日期范围,如春节 1.28~2.4)。"); + + b.Property("EndTime") + .HasColumnType("interval") + .HasComment("结束时间(IsAllDay=false 时使用)。"); + + b.Property("IsAllDay") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否全天生效。true=全天;false=仅 StartTime~EndTime 时段。"); + + b.Property("IsClosed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否闭店(兼容旧数据,新逻辑请用 OverrideType)。"); + + b.Property("OverrideType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("覆盖类型(闭店/临时营业/调整时间)。"); + + b.Property("Reason") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasComment("说明内容。"); + + b.Property("StartTime") + .HasColumnType("interval") + .HasComment("开始时间(IsAllDay=false 时使用)。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "Date"); + + b.ToTable("store_holidays", null, t => + { + t.HasComment("门店临时时段配置(节假日/歇业/调整营业时间)。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StorePickupSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowDaysAhead") + .HasColumnType("integer") + .HasComment("可预约天数(含当天)。"); + + b.Property("AllowToday") + .HasColumnType("boolean") + .HasComment("是否允许当天自提。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DefaultCutoffMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(30) + .HasComment("默认截单分钟(开始前多少分钟截止)。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("FineRuleJson") + .HasColumnType("text") + .HasComment("精细规则 JSON。"); + + b.Property("MaxQuantityPerOrder") + .HasColumnType("integer") + .HasComment("单笔自提最大份数。"); + + b.Property("Mode") + .HasColumnType("integer") + .HasComment("自提配置模式。"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .IsRequired() + .HasColumnType("bytea") + .HasComment("并发控制字段。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId") + .IsUnique(); + + b.ToTable("store_pickup_settings", null, t => + { + t.HasComment("门店自提配置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StorePickupSlot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer") + .HasComment("容量(份数)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("CutoffMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(30) + .HasComment("截单分钟(开始前多少分钟截止)。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndTime") + .HasColumnType("interval") + .HasComment("当天结束时间(UTC)。"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("档期名称。"); + + b.Property("ReservedCount") + .HasColumnType("integer") + .HasComment("已占用数量。"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .IsRequired() + .HasColumnType("bytea") + .HasComment("并发控制字段。"); + + b.Property("StartTime") + .HasColumnType("interval") + .HasComment("当天开始时间(UTC)。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("Weekdays") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("适用星期(逗号分隔 1-7)。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "Name"); + + b.ToTable("store_pickup_slots", null, t => + { + t.HasComment("门店自提档期。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreQualification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DocumentNumber") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("证照编号。"); + + b.Property("ExpiresAt") + .HasColumnType("date") + .HasComment("到期日期。"); + + b.Property("FileUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("证照文件 URL。"); + + b.Property("IssuedAt") + .HasColumnType("date") + .HasComment("签发日期。"); + + b.Property("QualificationType") + .HasColumnType("integer") + .HasComment("资质类型。"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(100) + .HasComment("排序值。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("ExpiresAt") + .HasFilter("\"ExpiresAt\" IS NOT NULL"); + + b.HasIndex("TenantId", "StoreId"); + + b.ToTable("store_qualifications", null, t => + { + t.HasComment("门店资质证照。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreStaffTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EveningEndTime") + .HasColumnType("interval") + .HasComment("晚班结束时间。"); + + b.Property("EveningStartTime") + .HasColumnType("interval") + .HasComment("晚班开始时间。"); + + b.Property("FullEndTime") + .HasColumnType("interval") + .HasComment("全天班结束时间。"); + + b.Property("FullStartTime") + .HasColumnType("interval") + .HasComment("全天班开始时间。"); + + b.Property("MorningEndTime") + .HasColumnType("interval") + .HasComment("早班结束时间。"); + + b.Property("MorningStartTime") + .HasColumnType("interval") + .HasComment("早班开始时间。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId") + .IsUnique(); + + b.ToTable("store_staff_templates", null, t => + { + t.HasComment("门店员工班次模板。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreStaffWeeklySchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DayOfWeek") + .HasColumnType("integer") + .HasComment("星期(0=周一,6=周日)。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EndTime") + .HasColumnType("interval") + .HasComment("结束时间(休息时为空)。"); + + b.Property("ShiftType") + .HasColumnType("integer") + .HasComment("班次类型。"); + + b.Property("StaffId") + .HasColumnType("bigint") + .HasComment("员工 ID。"); + + b.Property("StartTime") + .HasColumnType("interval") + .HasComment("开始时间(休息时为空)。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店 ID。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "DayOfWeek"); + + b.HasIndex("TenantId", "StoreId", "StaffId", "DayOfWeek") + .IsUnique(); + + b.ToTable("store_staff_weekly_schedules", null, t => + { + t.HasComment("门店员工每周排班。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreTable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AreaId") + .HasColumnType("bigint") + .HasComment("所在区域 ID。"); + + b.Property("Capacity") + .HasColumnType("integer") + .HasComment("可容纳人数。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("QrCodeUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("桌码二维码地址。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("当前桌台状态。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TableCode") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("桌码。"); + + b.Property("Tags") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("桌台标签(堂食、快餐等)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "TableCode") + .IsUnique(); + + b.ToTable("store_tables", null, t => + { + t.HasComment("桌台信息与二维码绑定。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Stores.Entities.StoreTableArea", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("区域描述。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("区域名称。"); + + b.Property("SortOrder") + .HasColumnType("integer") + .HasComment("排序值。"); + + b.Property("StoreId") + .HasColumnType("bigint") + .HasComment("门店标识。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "StoreId", "Name") + .IsUnique(); + + b.ToTable("store_table_areas", null, t => + { + t.HasComment("门店桌台区域配置。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.QuotaPackage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("描述。"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasComment("是否上架。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("配额包名称。"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("价格。"); + + b.Property("QuotaType") + .HasColumnType("integer") + .HasComment("配额类型。"); + + b.Property("QuotaValue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("配额数值。"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("排序。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("QuotaType", "IsActive", "SortOrder"); + + b.ToTable("quota_packages", null, t => + { + t.HasComment("配额包定义(系统提供的可购买配额包)。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("text") + .HasComment("详细地址信息。"); + + b.Property("City") + .HasColumnType("text") + .HasComment("所在城市。"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("租户短编码,作为跨系统引用的唯一标识。"); + + b.Property("ContactEmail") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("主联系人邮箱。"); + + b.Property("ContactName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("主联系人姓名。"); + + b.Property("ContactPhone") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("主联系人电话。"); + + b.Property("Country") + .HasColumnType("text") + .HasComment("所在国家/地区。"); + + b.Property("CoverImageUrl") + .HasColumnType("text") + .HasComment("品牌海报或封面图。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EffectiveFrom") + .HasColumnType("timestamp with time zone") + .HasComment("服务生效时间(UTC)。"); + + b.Property("EffectiveTo") + .HasColumnType("timestamp with time zone") + .HasComment("服务到期时间(UTC)。"); + + b.Property("GeoFailReason") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("地理定位失败原因。"); + + b.Property("GeoNextRetryAt") + .HasColumnType("timestamp with time zone") + .HasComment("下次地理定位重试时间(UTC)。"); + + b.Property("GeoRetryCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("地理定位重试次数。"); + + b.Property("GeoStatus") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("地理定位状态。"); + + b.Property("GeoUpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("地理定位最近成功时间(UTC)。"); + + b.Property("Industry") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("所属行业,如餐饮、零售等。"); + + b.Property("Latitude") + .HasColumnType("double precision") + .HasComment("纬度信息。"); + + b.Property("LegalEntityName") + .HasColumnType("text") + .HasComment("法人或公司主体名称。"); + + b.Property("LogoUrl") + .HasColumnType("text") + .HasComment("LOGO 图片地址。"); + + b.Property("Longitude") + .HasColumnType("double precision") + .HasComment("经度信息。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("租户全称或品牌名称。"); + + b.Property("OperatingMode") + .HasColumnType("integer") + .HasComment("经营模式(同一主体/不同主体)。"); + + b.Property("PrimaryOwnerUserId") + .HasColumnType("bigint") + .HasComment("系统内对应的租户所有者账号 ID。"); + + b.Property("Province") + .HasColumnType("text") + .HasComment("所在省份或州。"); + + b.Property("Remarks") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注信息,用于运营记录特殊说明。"); + + b.Property("ShortName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("对外展示的简称。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("租户当前状态,涵盖审核、启用、停用等场景。"); + + b.Property("SuspendedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次暂停服务时间。"); + + b.Property("SuspensionReason") + .HasColumnType("text") + .HasComment("暂停或终止的原因说明。"); + + b.Property("Tags") + .HasColumnType("text") + .HasComment("业务标签集合(逗号分隔)。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("Website") + .HasColumnType("text") + .HasComment("官网或主要宣传链接。"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("ContactPhone") + .IsUnique(); + + b.HasIndex("GeoStatus", "GeoNextRetryAt"); + + b.HasIndex("Longitude", "Latitude") + .HasFilter("\"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL"); + + b.ToTable("tenants", null, t => + { + t.HasComment("租户信息,描述租户的生命周期与基础资料。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantAnnouncement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AnnouncementType") + .HasColumnType("integer") + .HasComment("公告类型。"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text") + .HasComment("公告正文(可为 Markdown/HTML,前端自行渲染)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EffectiveFrom") + .HasColumnType("timestamp with time zone") + .HasComment("生效时间(UTC)。"); + + b.Property("EffectiveTo") + .HasColumnType("timestamp with time zone") + .HasComment("失效时间(UTC),为空表示长期有效。"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasComment("是否启用(已弃用,迁移期保留)。"); + + b.Property("Priority") + .HasColumnType("integer") + .HasComment("展示优先级,数值越大越靠前。"); + + b.Property("PublishedAt") + .HasColumnType("timestamp with time zone") + .HasComment("实际发布时间(UTC)。"); + + b.Property("PublisherScope") + .HasColumnType("integer") + .HasComment("发布者范围。"); + + b.Property("PublisherUserId") + .HasColumnType("bigint") + .HasComment("发布者用户 ID(系统或租户后台账号)。"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone") + .HasComment("撤销时间(UTC)。"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea") + .HasComment("并发控制字段。"); + + b.Property("ScheduledPublishAt") + .HasColumnType("timestamp with time zone") + .HasComment("预定发布时间(UTC)。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("公告状态。"); + + b.Property("TargetParameters") + .HasColumnType("text") + .HasComment("目标受众参数(JSON)。"); + + b.Property("TargetType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("目标受众类型。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("公告标题。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("Status", "EffectiveFrom") + .HasFilter("\"TenantId\" = 0"); + + b.HasIndex("TenantId", "AnnouncementType", "IsActive"); + + b.HasIndex("TenantId", "EffectiveFrom", "EffectiveTo"); + + b.HasIndex("TenantId", "Status", "EffectiveFrom"); + + b.ToTable("tenant_announcements", null, t => + { + t.HasComment("租户公告。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantAnnouncementRead", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AnnouncementId") + .HasColumnType("bigint") + .HasComment("公告 ID。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ReadAt") + .HasColumnType("timestamp with time zone") + .HasComment("已读时间。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UserId") + .HasColumnType("bigint") + .HasComment("已读用户 ID(后台账号),为空表示租户级已读。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "AnnouncementId", "UserId") + .IsUnique(); + + b.ToTable("tenant_announcement_reads", null, t => + { + t.HasComment("租户公告已读记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantBillingStatement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AmountDue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("应付金额(原始金额)。"); + + b.Property("AmountPaid") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("实付金额。"); + + b.Property("BillingType") + .HasColumnType("integer") + .HasComment("账单类型(订阅账单/配额包账单/手动账单/续费账单)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("Currency") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(8) + .HasColumnType("character varying(8)") + .HasDefaultValue("CNY") + .HasComment("货币类型(默认 CNY)。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("DiscountAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("折扣金额。"); + + b.Property("DueDate") + .HasColumnType("timestamp with time zone") + .HasComment("到期日。"); + + b.Property("LineItemsJson") + .HasColumnType("text") + .HasComment("账单明细 JSON,记录各项费用。"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注信息(如:人工备注、取消原因等)。"); + + b.Property("OverdueNotifiedAt") + .HasColumnType("timestamp with time zone") + .HasComment("逾期通知时间。"); + + b.Property("PeriodEnd") + .HasColumnType("timestamp with time zone") + .HasComment("账单周期结束时间。"); + + b.Property("PeriodStart") + .HasColumnType("timestamp with time zone") + .HasComment("账单周期开始时间。"); + + b.Property("ReminderSentAt") + .HasColumnType("timestamp with time zone") + .HasComment("提醒发送时间(续费提醒、逾期提醒等)。"); + + b.Property("StatementNo") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("账单编号,供对账查询。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("当前付款状态。"); + + b.Property("SubscriptionId") + .HasColumnType("bigint") + .HasComment("关联的订阅 ID(仅当 BillingType 为 Subscription 或 Renewal 时有值)。"); + + b.Property("TaxAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("税费金额。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("idx_billing_created_at"); + + b.HasIndex("Status", "DueDate") + .HasDatabaseName("idx_billing_status_duedate") + .HasFilter("\"Status\" IN (0, 2)"); + + b.HasIndex("TenantId", "StatementNo") + .IsUnique(); + + b.HasIndex("TenantId", "Status", "DueDate") + .HasDatabaseName("idx_billing_tenant_status_duedate"); + + b.ToTable("tenant_billing_statements", null, t => + { + t.HasComment("租户账单,用于呈现周期性收费。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantNotification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Channel") + .HasColumnType("integer") + .HasComment("发布通道(站内、邮件、短信等)。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasComment("通知正文。"); + + b.Property("MetadataJson") + .HasColumnType("text") + .HasComment("附加元数据 JSON。"); + + b.Property("ReadAt") + .HasColumnType("timestamp with time zone") + .HasComment("租户是否已阅读。"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone") + .HasComment("推送时间。"); + + b.Property("Severity") + .HasColumnType("integer") + .HasComment("通知重要级别。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("通知标题。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Channel", "SentAt"); + + b.ToTable("tenant_notifications", null, t => + { + t.HasComment("面向租户的站内通知或消息推送。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantPackage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("套餐描述,包含适用场景、权益等。"); + + b.Property("FeaturePoliciesJson") + .HasColumnType("text") + .HasComment("权益明细 JSON,记录自定义特性开关。"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasComment("是否仍启用(系统控制)。"); + + b.Property("IsAllowNewTenantPurchase") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否允许新租户购买/选择(仅影响新购)。"); + + b.Property("IsPublicVisible") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否对外可见(展示页/套餐列表可见性)。"); + + b.Property("IsRecommended") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否推荐展示(运营推荐标识)。"); + + b.Property("MaxAccountCount") + .HasColumnType("integer") + .HasComment("允许创建的最大账号数。"); + + b.Property("MaxDeliveryOrders") + .HasColumnType("integer") + .HasComment("每月可调用的配送单数量上限。"); + + b.Property("MaxSmsCredits") + .HasColumnType("integer") + .HasComment("每月短信额度上限。"); + + b.Property("MaxStorageGb") + .HasColumnType("integer") + .HasComment("存储容量上限(GB)。"); + + b.Property("MaxStoreCount") + .HasColumnType("integer") + .HasComment("允许的最大门店数。"); + + b.Property("MonthlyPrice") + .HasColumnType("numeric") + .HasComment("月付价格,单位:人民币元。"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("套餐名称,展示给租户的简称。"); + + b.Property("PackageType") + .HasColumnType("integer") + .HasComment("套餐分类(试用、标准、旗舰等)。"); + + b.Property("PublishStatus") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("发布状态:0=草稿,1=已发布。"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("展示排序,数值越小越靠前。"); + + b.PrimitiveCollection("Tags") + .IsRequired() + .HasColumnType("text[]") + .HasComment("套餐标签(用于展示与对比页)。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("YearlyPrice") + .HasColumnType("numeric") + .HasComment("年付价格,单位:人民币元。"); + + b.HasKey("Id"); + + b.HasIndex("IsActive", "SortOrder"); + + b.HasIndex("PublishStatus", "IsActive", "IsPublicVisible", "IsAllowNewTenantPurchase", "SortOrder"); + + b.ToTable("tenant_packages", null, t => + { + t.HasComment("系统提供的租户套餐定义。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantPayment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("支付金额。"); + + b.Property("BillingStatementId") + .HasColumnType("bigint") + .HasComment("关联的账单 ID。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("Method") + .HasColumnType("integer") + .HasComment("支付方式。"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注信息。"); + + b.Property("PaidAt") + .HasColumnType("timestamp with time zone") + .HasComment("支付时间。"); + + b.Property("ProofUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("支付凭证 URL。"); + + b.Property("RefundReason") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("退款原因。"); + + b.Property("RefundedAt") + .HasColumnType("timestamp with time zone") + .HasComment("退款时间。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("支付状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TransactionNo") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("交易号。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("VerifiedAt") + .HasColumnType("timestamp with time zone") + .HasComment("审核时间。"); + + b.Property("VerifiedBy") + .HasColumnType("bigint") + .HasComment("审核人 ID(管理员)。"); + + b.HasKey("Id"); + + b.HasIndex("TransactionNo") + .HasDatabaseName("idx_payment_transaction_no") + .HasFilter("\"TransactionNo\" IS NOT NULL"); + + b.HasIndex("BillingStatementId", "PaidAt") + .HasDatabaseName("idx_payment_billing_paidat"); + + b.HasIndex("TenantId", "BillingStatementId"); + + b.ToTable("tenant_payments", null, t => + { + t.HasComment("租户支付记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantQuotaPackagePurchase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone") + .HasComment("过期时间(可选)。"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注。"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("购买价格。"); + + b.Property("PurchasedAt") + .HasColumnType("timestamp with time zone") + .HasComment("购买时间。"); + + b.Property("QuotaPackageId") + .HasColumnType("bigint") + .HasComment("配额包 ID。"); + + b.Property("QuotaValue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)") + .HasComment("购买时的配额值。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "QuotaPackageId", "PurchasedAt"); + + b.ToTable("tenant_quota_package_purchases", null, t => + { + t.HasComment("租户配额包购买记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantQuotaUsage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("LastResetAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次重置时间。"); + + b.Property("LimitValue") + .HasColumnType("numeric") + .HasComment("当前配额上限。"); + + b.Property("QuotaType") + .HasColumnType("integer") + .HasComment("配额类型,例如门店数、短信条数等。"); + + b.Property("ResetCycle") + .HasColumnType("text") + .HasComment("配额刷新周期描述(如月、年)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UsedValue") + .HasColumnType("numeric") + .HasComment("已消耗的数量。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "QuotaType") + .IsUnique(); + + b.ToTable("tenant_quota_usages", null, t => + { + t.HasComment("租户配额使用情况快照。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantQuotaUsageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChangeAmount") + .HasColumnType("numeric") + .HasComment("变更量(可选)。"); + + b.Property("ChangeReason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("变更原因(可选)。"); + + b.Property("ChangeType") + .HasColumnType("integer") + .HasComment("变更类型。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("LimitValue") + .HasColumnType("numeric") + .HasComment("限额值(记录时刻的快照)。"); + + b.Property("QuotaType") + .HasColumnType("integer") + .HasComment("配额类型。"); + + b.Property("RecordedAt") + .HasColumnType("timestamp with time zone") + .HasComment("记录时间(UTC)。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.Property("UsedValue") + .HasColumnType("numeric") + .HasComment("已使用值(记录时刻的快照)。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "RecordedAt"); + + b.HasIndex("TenantId", "QuotaType", "RecordedAt"); + + b.ToTable("tenant_quota_usage_histories", null, t => + { + t.HasComment("租户配额使用历史记录(用于追踪配额上下限与使用量的时间序列变化)。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoRenew") + .HasColumnType("boolean") + .HasComment("是否开启自动续费。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EffectiveFrom") + .HasColumnType("timestamp with time zone") + .HasComment("订阅生效时间(UTC)。"); + + b.Property("EffectiveTo") + .HasColumnType("timestamp with time zone") + .HasComment("订阅到期时间(UTC)。"); + + b.Property("NextBillingDate") + .HasColumnType("timestamp with time zone") + .HasComment("下一个计费时间,配合自动续费使用。"); + + b.Property("Notes") + .HasColumnType("text") + .HasComment("运营备注信息。"); + + b.Property("ScheduledPackageId") + .HasColumnType("bigint") + .HasComment("若已排期升降配,对应的新套餐 ID。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("订阅当前状态。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("TenantPackageId") + .HasColumnType("bigint") + .HasComment("当前订阅关联的套餐标识。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "TenantPackageId"); + + b.ToTable("tenant_subscriptions", null, t => + { + t.HasComment("租户套餐订阅记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantSubscriptionHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("numeric") + .HasComment("相关费用。"); + + b.Property("ChangeType") + .HasColumnType("integer") + .HasComment("变更类型。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("Currency") + .HasMaxLength(8) + .HasColumnType("character varying(8)") + .HasComment("币种。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("EffectiveFrom") + .HasColumnType("timestamp with time zone") + .HasComment("生效时间。"); + + b.Property("EffectiveTo") + .HasColumnType("timestamp with time zone") + .HasComment("到期时间。"); + + b.Property("FromPackageId") + .HasColumnType("bigint") + .HasComment("原套餐 ID。"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("备注。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("租户标识。"); + + b.Property("TenantSubscriptionId") + .HasColumnType("bigint") + .HasComment("对应的订阅 ID。"); + + b.Property("ToPackageId") + .HasColumnType("bigint") + .HasComment("新套餐 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "TenantSubscriptionId"); + + b.ToTable("tenant_subscription_histories", null, t => + { + t.HasComment("租户套餐订阅变更记录。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantVerificationProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdditionalDataJson") + .HasColumnType("text") + .HasComment("附加资料(JSON)。"); + + b.Property("BankAccountName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("开户名。"); + + b.Property("BankAccountNumber") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("银行账号。"); + + b.Property("BankName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComment("银行名称。"); + + b.Property("BusinessLicenseNumber") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("营业执照编号。"); + + b.Property("BusinessLicenseUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("营业执照文件地址。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.Property("LegalPersonIdBackUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("法人身份证反面。"); + + b.Property("LegalPersonIdFrontUrl") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("法人身份证正面。"); + + b.Property("LegalPersonIdNumber") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasComment("法人身份证号。"); + + b.Property("LegalPersonName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("法人姓名。"); + + b.Property("ReviewRemarks") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasComment("审核备注。"); + + b.Property("ReviewedAt") + .HasColumnType("timestamp with time zone") + .HasComment("审核时间。"); + + b.Property("ReviewedBy") + .HasColumnType("bigint") + .HasComment("审核人 ID。"); + + b.Property("ReviewedByName") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasComment("审核人姓名。"); + + b.Property("Status") + .HasColumnType("integer") + .HasComment("实名状态。"); + + b.Property("SubmittedAt") + .HasColumnType("timestamp with time zone") + .HasComment("提交时间。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("对应的租户标识。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId") + .IsUnique(); + + b.ToTable("tenant_verification_profiles", null, t => + { + t.HasComment("租户实名认证资料。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantVisibilityRoleRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasComment("实体唯一标识。"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.PrimitiveCollection("BillingVisibleRoleCodes") + .IsRequired() + .HasColumnType("text[]") + .HasComment("账单可见角色编码集合。"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间(UTC)。"); + + b.Property("CreatedBy") + .HasColumnType("bigint") + .HasComment("创建人用户标识,匿名或系统操作时为 null。"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasComment("软删除时间(UTC),未删除时为 null。"); + + b.Property("DeletedBy") + .HasColumnType("bigint") + .HasComment("删除人用户标识(软删除),未删除时为 null。"); + + b.PrimitiveCollection("QuotaVisibleRoleCodes") + .IsRequired() + .HasColumnType("text[]") + .HasComment("配额可见角色编码集合。"); + + b.Property("TenantId") + .HasColumnType("bigint") + .HasComment("所属租户 ID。"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("最近一次更新时间(UTC),从未更新时为 null。"); + + b.Property("UpdatedBy") + .HasColumnType("bigint") + .HasComment("最后更新人用户标识,匿名或系统操作时为 null。"); + + b.HasKey("Id"); + + b.HasIndex("TenantId") + .IsUnique(); + + b.ToTable("tenant_visibility_role_rules", null, t => + { + t.HasComment("租户账单/配额可见角色规则。"); + }); + }); + + modelBuilder.Entity("TakeoutSaaS.Domain.Orders.Entities.OrderItem", b => + { + b.HasOne("TakeoutSaaS.Domain.Orders.Entities.Order", null) + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.cs new file mode 100644 index 0000000..94a4b18 --- /dev/null +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/20260221120000_AddProductListAndSoldoutFields.cs @@ -0,0 +1,135 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TakeoutSaaS.Infrastructure.Migrations +{ + /// + public partial class AddProductListAndSoldoutFields : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Kind", + table: "products", + type: "integer", + nullable: false, + defaultValue: 0, + comment: "商品类型。"); + + migrationBuilder.AddColumn( + name: "NotifyManager", + table: "products", + type: "boolean", + nullable: false, + defaultValue: false, + comment: "是否通知店长。"); + + migrationBuilder.AddColumn( + name: "RecoverAt", + table: "products", + type: "timestamp with time zone", + nullable: true, + comment: "沽清恢复时间。"); + + migrationBuilder.AddColumn( + name: "RemainStock", + table: "products", + type: "integer", + nullable: true, + comment: "沽清后剩余可售。"); + + migrationBuilder.AddColumn( + name: "SalesMonthly", + table: "products", + type: "integer", + nullable: false, + defaultValue: 0, + comment: "月销量。"); + + migrationBuilder.AddColumn( + name: "SoldoutMode", + table: "products", + type: "integer", + nullable: true, + comment: "沽清模式。"); + + migrationBuilder.AddColumn( + name: "SoldoutReason", + table: "products", + type: "character varying(256)", + maxLength: 256, + nullable: true, + comment: "沽清原因。"); + + migrationBuilder.AddColumn( + name: "SyncToPlatform", + table: "products", + type: "boolean", + nullable: false, + defaultValue: true, + comment: "是否同步通知外卖平台。"); + + migrationBuilder.AddColumn( + name: "TagsJson", + table: "products", + type: "text", + nullable: true, + defaultValue: "[]", + comment: "标签 JSON(字符串数组)。"); + + migrationBuilder.AddColumn( + name: "TimedOnShelfAt", + table: "products", + type: "timestamp with time zone", + nullable: true, + comment: "定时上架时间。"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Kind", + table: "products"); + + migrationBuilder.DropColumn( + name: "NotifyManager", + table: "products"); + + migrationBuilder.DropColumn( + name: "RecoverAt", + table: "products"); + + migrationBuilder.DropColumn( + name: "RemainStock", + table: "products"); + + migrationBuilder.DropColumn( + name: "SalesMonthly", + table: "products"); + + migrationBuilder.DropColumn( + name: "SoldoutMode", + table: "products"); + + migrationBuilder.DropColumn( + name: "SoldoutReason", + table: "products"); + + migrationBuilder.DropColumn( + name: "SyncToPlatform", + table: "products"); + + migrationBuilder.DropColumn( + name: "TagsJson", + table: "products"); + + migrationBuilder.DropColumn( + name: "TimedOnShelfAt", + table: "products"); + } + } +} diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/TakeoutAppDbContextModelSnapshot.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/TakeoutAppDbContextModelSnapshot.cs index d2201bc..8ded544 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/TakeoutAppDbContextModelSnapshot.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Migrations/TakeoutAppDbContextModelSnapshot.cs @@ -3980,6 +3980,12 @@ namespace TakeoutSaaS.Infrastructure.Migrations .HasColumnType("boolean") .HasComment("是否热门推荐。"); + b.Property("Kind") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("商品类型。"); + b.Property("MaxQuantityPerOrder") .HasColumnType("integer") .HasComment("最大每单限购。"); @@ -3990,6 +3996,12 @@ namespace TakeoutSaaS.Infrastructure.Migrations .HasColumnType("character varying(128)") .HasComment("商品名称。"); + b.Property("NotifyManager") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否通知店长。"); + b.Property("OriginalPrice") .HasPrecision(18, 2) .HasColumnType("numeric(18,2)") @@ -4000,6 +4012,29 @@ namespace TakeoutSaaS.Infrastructure.Migrations .HasColumnType("numeric(18,2)") .HasComment("现价。"); + b.Property("RecoverAt") + .HasColumnType("timestamp with time zone") + .HasComment("沽清恢复时间。"); + + b.Property("RemainStock") + .HasColumnType("integer") + .HasComment("沽清后剩余可售。"); + + b.Property("SalesMonthly") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("月销量。"); + + b.Property("SoldoutMode") + .HasColumnType("integer") + .HasComment("沽清模式。"); + + b.Property("SoldoutReason") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("沽清原因。"); + b.Property("SpuCode") .IsRequired() .HasMaxLength(32) @@ -4023,10 +4058,26 @@ namespace TakeoutSaaS.Infrastructure.Migrations .HasColumnType("character varying(256)") .HasComment("副标题/卖点。"); + b.Property("SyncToPlatform") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否同步通知外卖平台。"); + + b.Property("TagsJson") + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("[]") + .HasComment("标签 JSON(字符串数组)。"); + b.Property("TenantId") .HasColumnType("bigint") .HasComment("所属租户 ID。"); + b.Property("TimedOnShelfAt") + .HasColumnType("timestamp with time zone") + .HasComment("定时上架时间。"); + b.Property("Unit") .HasMaxLength(16) .HasColumnType("character varying(16)")