Files
TakeoutSaaS.TenantApi/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs

1934 lines
91 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using MassTransit;
using TakeoutSaaS.Domain.Analytics.Entities;
using TakeoutSaaS.Domain.Coupons.Entities;
using TakeoutSaaS.Domain.CustomerService.Entities;
using TakeoutSaaS.Domain.Deliveries.Entities;
using TakeoutSaaS.Domain.Distribution.Entities;
using TakeoutSaaS.Domain.Common.Enums;
using TakeoutSaaS.Domain.Engagement.Entities;
using TakeoutSaaS.Domain.GroupBuying.Entities;
using TakeoutSaaS.Domain.Inventory.Entities;
using TakeoutSaaS.Domain.Membership.Entities;
using TakeoutSaaS.Domain.Merchants.Entities;
using TakeoutSaaS.Domain.Navigation.Entities;
using TakeoutSaaS.Domain.Ordering.Entities;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Payments.Entities;
using TakeoutSaaS.Domain.Products.Entities;
using TakeoutSaaS.Domain.Queues.Entities;
using TakeoutSaaS.Domain.Reservations.Entities;
using TakeoutSaaS.Domain.Stores.Entities;
using TakeoutSaaS.Domain.Stores.Enums;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Infrastructure.Common.Persistence;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Security;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
using TakeoutSaaS.Infrastructure.App.Persistence.Configurations;
namespace TakeoutSaaS.Infrastructure.App.Persistence;
/// <summary>
/// 业务主库 DbContext。
/// </summary>
public sealed class TakeoutAppDbContext(
DbContextOptions<TakeoutAppDbContext> options,
ITenantProvider tenantProvider,
ICurrentUserAccessor? currentUserAccessor = null,
IIdGenerator? idGenerator = null)
: TenantAwareDbContext(options, tenantProvider, currentUserAccessor, idGenerator)
{
/// <summary>
/// 租户聚合根。
/// </summary>
public DbSet<Tenant> Tenants => Set<Tenant>();
/// <summary>
/// 租户套餐。
/// </summary>
public DbSet<TenantPackage> TenantPackages => Set<TenantPackage>();
/// <summary>
/// 租户订阅。
/// </summary>
public DbSet<TenantSubscription> TenantSubscriptions => Set<TenantSubscription>();
/// <summary>
/// 租户订阅历史。
/// </summary>
public DbSet<TenantSubscriptionHistory> TenantSubscriptionHistories => Set<TenantSubscriptionHistory>();
/// <summary>
/// 租户配额使用记录。
/// </summary>
public DbSet<TenantQuotaUsage> TenantQuotaUsages => Set<TenantQuotaUsage>();
/// <summary>
/// 租户配额使用历史记录。
/// </summary>
public DbSet<TenantQuotaUsageHistory> TenantQuotaUsageHistories => Set<TenantQuotaUsageHistory>();
/// <summary>
/// 租户账单。
/// </summary>
public DbSet<TenantBillingStatement> TenantBillingStatements => Set<TenantBillingStatement>();
/// <summary>
/// 租户支付记录。
/// </summary>
public DbSet<TenantPayment> TenantPayments => Set<TenantPayment>();
/// <summary>
/// 租户通知。
/// </summary>
public DbSet<TenantNotification> TenantNotifications => Set<TenantNotification>();
/// <summary>
/// 租户公告。
/// </summary>
public DbSet<TenantAnnouncement> TenantAnnouncements => Set<TenantAnnouncement>();
/// <summary>
/// 租户公告已读记录。
/// </summary>
public DbSet<TenantAnnouncementRead> TenantAnnouncementReads => Set<TenantAnnouncementRead>();
/// <summary>
/// 租户认证资料。
/// </summary>
public DbSet<TenantVerificationProfile> TenantVerificationProfiles => Set<TenantVerificationProfile>();
/// <summary>
/// 租户账单/配额可见角色规则。
/// </summary>
public DbSet<TenantVisibilityRoleRule> TenantVisibilityRoleRules => Set<TenantVisibilityRoleRule>();
/// <summary>
/// 配额包定义。
/// </summary>
public DbSet<QuotaPackage> QuotaPackages => Set<QuotaPackage>();
/// <summary>
/// 租户配额包购买记录。
/// </summary>
public DbSet<TenantQuotaPackagePurchase> TenantQuotaPackagePurchases => Set<TenantQuotaPackagePurchase>();
/// <summary>
/// 商户实体。
/// </summary>
public DbSet<Merchant> Merchants => Set<Merchant>();
/// <summary>
/// 商户资质文件。
/// </summary>
public DbSet<MerchantDocument> MerchantDocuments => Set<MerchantDocument>();
/// <summary>
/// 商户合同。
/// </summary>
public DbSet<MerchantContract> MerchantContracts => Set<MerchantContract>();
/// <summary>
/// 商户员工。
/// </summary>
public DbSet<MerchantStaff> MerchantStaff => Set<MerchantStaff>();
/// <summary>
/// 商户分类。
/// </summary>
public DbSet<MerchantCategory> MerchantCategories => Set<MerchantCategory>();
/// <summary>
/// 门店实体。
/// </summary>
public DbSet<Store> Stores => Set<Store>();
/// <summary>
/// 门店费用配置。
/// </summary>
public DbSet<StoreFee> StoreFees => Set<StoreFee>();
/// <summary>
/// 门店资质证照。
/// </summary>
public DbSet<StoreQualification> StoreQualifications => Set<StoreQualification>();
/// <summary>
/// 门店审核记录。
/// </summary>
public DbSet<StoreAuditRecord> StoreAuditRecords => Set<StoreAuditRecord>();
/// <summary>
/// 门店营业时间。
/// </summary>
public DbSet<StoreBusinessHour> StoreBusinessHours => Set<StoreBusinessHour>();
/// <summary>
/// 门店节假日。
/// </summary>
public DbSet<StoreHoliday> StoreHolidays => Set<StoreHoliday>();
/// <summary>
/// 门店配送区域。
/// </summary>
public DbSet<StoreDeliveryZone> StoreDeliveryZones => Set<StoreDeliveryZone>();
/// <summary>
/// 门店配送设置。
/// </summary>
public DbSet<StoreDeliverySetting> StoreDeliverySettings => Set<StoreDeliverySetting>();
/// <summary>
/// 门店堂食设置。
/// </summary>
public DbSet<StoreDineInSetting> StoreDineInSettings => Set<StoreDineInSetting>();
/// <summary>
/// 门店桌台区域。
/// </summary>
public DbSet<StoreTableArea> StoreTableAreas => Set<StoreTableArea>();
/// <summary>
/// 门店桌台。
/// </summary>
public DbSet<StoreTable> StoreTables => Set<StoreTable>();
/// <summary>
/// 门店员工班次。
/// </summary>
public DbSet<StoreEmployeeShift> StoreEmployeeShifts => Set<StoreEmployeeShift>();
/// <summary>
/// 自提配置。
/// </summary>
public DbSet<StorePickupSetting> StorePickupSettings => Set<StorePickupSetting>();
/// <summary>
/// 自提时间段。
/// </summary>
public DbSet<StorePickupSlot> StorePickupSlots => Set<StorePickupSlot>();
/// <summary>
/// 门店班次模板。
/// </summary>
public DbSet<StoreStaffTemplate> StoreStaffTemplates => Set<StoreStaffTemplate>();
/// <summary>
/// 门店每周排班。
/// </summary>
public DbSet<StoreStaffWeeklySchedule> StoreStaffWeeklySchedules => Set<StoreStaffWeeklySchedule>();
/// <summary>
/// 商品分类。
/// </summary>
public DbSet<ProductCategory> ProductCategories => Set<ProductCategory>();
/// <summary>
/// 商品。
/// </summary>
public DbSet<Product> Products => Set<Product>();
/// <summary>
/// 商品属性组。
/// </summary>
public DbSet<ProductAttributeGroup> ProductAttributeGroups => Set<ProductAttributeGroup>();
/// <summary>
/// 商品属性项。
/// </summary>
public DbSet<ProductAttributeOption> ProductAttributeOptions => Set<ProductAttributeOption>();
/// <summary>
/// 门店规格做法模板。
/// </summary>
public DbSet<ProductSpecTemplate> ProductSpecTemplates => Set<ProductSpecTemplate>();
/// <summary>
/// 门店规格做法模板选项。
/// </summary>
public DbSet<ProductSpecTemplateOption> ProductSpecTemplateOptions => Set<ProductSpecTemplateOption>();
/// <summary>
/// 门店规格做法模板关联商品。
/// </summary>
public DbSet<ProductSpecTemplateProduct> ProductSpecTemplateProducts => Set<ProductSpecTemplateProduct>();
/// <summary>
/// 商品标签。
/// </summary>
public DbSet<ProductLabel> ProductLabels => Set<ProductLabel>();
/// <summary>
/// 标签与商品关联。
/// </summary>
public DbSet<ProductLabelProduct> ProductLabelProducts => Set<ProductLabelProduct>();
/// <summary>
/// 商品时段规则。
/// </summary>
public DbSet<ProductSchedule> ProductSchedules => Set<ProductSchedule>();
/// <summary>
/// 时段规则与商品关联。
/// </summary>
public DbSet<ProductScheduleProduct> ProductScheduleProducts => Set<ProductScheduleProduct>();
/// <summary>
/// SKU 实体。
/// </summary>
public DbSet<ProductSku> ProductSkus => Set<ProductSku>();
/// <summary>
/// SKU 异步保存任务。
/// </summary>
public DbSet<ProductSkuSaveJob> ProductSkuSaveJobs => Set<ProductSkuSaveJob>();
/// <summary>
/// 套餐分组。
/// </summary>
public DbSet<ProductComboGroup> ProductComboGroups => Set<ProductComboGroup>();
/// <summary>
/// 套餐分组商品。
/// </summary>
public DbSet<ProductComboGroupItem> ProductComboGroupItems => Set<ProductComboGroupItem>();
/// <summary>
/// 加料分组。
/// </summary>
public DbSet<ProductAddonGroup> ProductAddonGroups => Set<ProductAddonGroup>();
/// <summary>
/// 加料选项。
/// </summary>
public DbSet<ProductAddonOption> ProductAddonOptions => Set<ProductAddonOption>();
/// <summary>
/// 定价规则。
/// </summary>
public DbSet<ProductPricingRule> ProductPricingRules => Set<ProductPricingRule>();
/// <summary>
/// 商品媒体资源。
/// </summary>
public DbSet<ProductMediaAsset> ProductMediaAssets => Set<ProductMediaAsset>();
/// <summary>
/// 库存项目。
/// </summary>
public DbSet<InventoryItem> InventoryItems => Set<InventoryItem>();
/// <summary>
/// 库存调整记录。
/// </summary>
public DbSet<InventoryAdjustment> InventoryAdjustments => Set<InventoryAdjustment>();
/// <summary>
/// 库存批次。
/// </summary>
public DbSet<InventoryBatch> InventoryBatches => Set<InventoryBatch>();
/// <summary>
/// 库存锁定记录。
/// </summary>
public DbSet<InventoryLockRecord> InventoryLockRecords => Set<InventoryLockRecord>();
/// <summary>
/// 购物车。
/// </summary>
public DbSet<ShoppingCart> ShoppingCarts => Set<ShoppingCart>();
/// <summary>
/// 购物车明细。
/// </summary>
public DbSet<CartItem> CartItems => Set<CartItem>();
/// <summary>
/// 购物车加料。
/// </summary>
public DbSet<CartItemAddon> CartItemAddons => Set<CartItemAddon>();
/// <summary>
/// 结账会话。
/// </summary>
public DbSet<CheckoutSession> CheckoutSessions => Set<CheckoutSession>();
/// <summary>
/// 订单聚合。
/// </summary>
public DbSet<Order> Orders => Set<Order>();
/// <summary>
/// 订单明细。
/// </summary>
public DbSet<OrderItem> OrderItems => Set<OrderItem>();
/// <summary>
/// 订单状态流转。
/// </summary>
public DbSet<OrderStatusHistory> OrderStatusHistories => Set<OrderStatusHistory>();
/// <summary>
/// 退款申请。
/// </summary>
public DbSet<RefundRequest> RefundRequests => Set<RefundRequest>();
/// <summary>
/// 支付记录。
/// </summary>
public DbSet<PaymentRecord> PaymentRecords => Set<PaymentRecord>();
/// <summary>
/// 支付退款记录。
/// </summary>
public DbSet<PaymentRefundRecord> PaymentRefundRecords => Set<PaymentRefundRecord>();
/// <summary>
/// 预订记录。
/// </summary>
public DbSet<Reservation> Reservations => Set<Reservation>();
/// <summary>
/// 排号记录。
/// </summary>
public DbSet<QueueTicket> QueueTickets => Set<QueueTicket>();
/// <summary>
/// 配送订单。
/// </summary>
public DbSet<DeliveryOrder> DeliveryOrders => Set<DeliveryOrder>();
/// <summary>
/// 配送事件。
/// </summary>
public DbSet<DeliveryEvent> DeliveryEvents => Set<DeliveryEvent>();
/// <summary>
/// 团购订单。
/// </summary>
public DbSet<GroupOrder> GroupOrders => Set<GroupOrder>();
/// <summary>
/// 团购参与者。
/// </summary>
public DbSet<GroupParticipant> GroupParticipants => Set<GroupParticipant>();
/// <summary>
/// 优惠券模板。
/// </summary>
public DbSet<CouponTemplate> CouponTemplates => Set<CouponTemplate>();
/// <summary>
/// 优惠券实例。
/// </summary>
public DbSet<Coupon> Coupons => Set<Coupon>();
/// <summary>
/// 营销活动。
/// </summary>
public DbSet<PromotionCampaign> PromotionCampaigns => Set<PromotionCampaign>();
/// <summary>
/// 新客有礼配置。
/// </summary>
public DbSet<NewCustomerGiftSetting> NewCustomerGiftSettings => Set<NewCustomerGiftSetting>();
/// <summary>
/// 新客有礼券规则。
/// </summary>
public DbSet<NewCustomerCouponRule> NewCustomerCouponRules => Set<NewCustomerCouponRule>();
/// <summary>
/// 新客邀请记录。
/// </summary>
public DbSet<NewCustomerInviteRecord> NewCustomerInviteRecords => Set<NewCustomerInviteRecord>();
/// <summary>
/// 新客成长记录。
/// </summary>
public DbSet<NewCustomerGrowthRecord> NewCustomerGrowthRecords => Set<NewCustomerGrowthRecord>();
/// <summary>
/// 会员档案。
/// </summary>
public DbSet<MemberProfile> MemberProfiles => Set<MemberProfile>();
/// <summary>
/// 会员等级。
/// </summary>
public DbSet<MemberTier> MemberTiers => Set<MemberTier>();
/// <summary>
/// 积分流水。
/// </summary>
public DbSet<MemberPointLedger> MemberPointLedgers => Set<MemberPointLedger>();
/// <summary>
/// 会话记录。
/// </summary>
public DbSet<ChatSession> ChatSessions => Set<ChatSession>();
/// <summary>
/// 会话消息。
/// </summary>
public DbSet<ChatMessage> ChatMessages => Set<ChatMessage>();
/// <summary>
/// 工单记录。
/// </summary>
public DbSet<SupportTicket> SupportTickets => Set<SupportTicket>();
/// <summary>
/// 工单评论。
/// </summary>
public DbSet<TicketComment> TicketComments => Set<TicketComment>();
/// <summary>
/// 分销合作伙伴。
/// </summary>
public DbSet<AffiliatePartner> AffiliatePartners => Set<AffiliatePartner>();
/// <summary>
/// 分销订单。
/// </summary>
public DbSet<AffiliateOrder> AffiliateOrders => Set<AffiliateOrder>();
/// <summary>
/// 分销结算。
/// </summary>
public DbSet<AffiliatePayout> AffiliatePayouts => Set<AffiliatePayout>();
/// <summary>
/// 打卡活动。
/// </summary>
public DbSet<CheckInCampaign> CheckInCampaigns => Set<CheckInCampaign>();
/// <summary>
/// 打卡记录。
/// </summary>
public DbSet<CheckInRecord> CheckInRecords => Set<CheckInRecord>();
/// <summary>
/// 社区帖子。
/// </summary>
public DbSet<CommunityPost> CommunityPosts => Set<CommunityPost>();
/// <summary>
/// 社区评论。
/// </summary>
public DbSet<CommunityComment> CommunityComments => Set<CommunityComment>();
/// <summary>
/// 社区互动。
/// </summary>
public DbSet<CommunityReaction> CommunityReactions => Set<CommunityReaction>();
/// <summary>
/// 地图位置。
/// </summary>
public DbSet<MapLocation> MapLocations => Set<MapLocation>();
/// <summary>
/// 导航请求。
/// </summary>
public DbSet<NavigationRequest> NavigationRequests => Set<NavigationRequest>();
/// <summary>
/// 指标定义。
/// </summary>
public DbSet<MetricDefinition> MetricDefinitions => Set<MetricDefinition>();
/// <summary>
/// 指标快照。
/// </summary>
public DbSet<MetricSnapshot> MetricSnapshots => Set<MetricSnapshot>();
/// <summary>
/// 告警规则。
/// </summary>
public DbSet<MetricAlertRule> MetricAlertRules => Set<MetricAlertRule>();
/// <summary>
/// 配置实体映射关系。
/// </summary>
/// <param name="modelBuilder">模型构建器。</param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 1. 调用基类配置
base.OnModelCreating(modelBuilder);
// 2. 配置全部实体映射
ConfigureTenant(modelBuilder.Entity<Tenant>());
ConfigureMerchant(modelBuilder.Entity<Merchant>());
ConfigureStore(modelBuilder.Entity<Store>());
ConfigureTenantPackage(modelBuilder.Entity<TenantPackage>());
ConfigureTenantSubscription(modelBuilder.Entity<TenantSubscription>());
ConfigureTenantSubscriptionHistory(modelBuilder.Entity<TenantSubscriptionHistory>());
ConfigureTenantQuotaUsage(modelBuilder.Entity<TenantQuotaUsage>());
ConfigureTenantQuotaUsageHistory(modelBuilder.Entity<TenantQuotaUsageHistory>());
ConfigureTenantBilling(modelBuilder.Entity<TenantBillingStatement>());
ConfigureTenantPayment(modelBuilder.Entity<TenantPayment>());
ConfigureTenantNotification(modelBuilder.Entity<TenantNotification>());
ConfigureTenantAnnouncement(modelBuilder.Entity<TenantAnnouncement>());
ConfigureTenantAnnouncementRead(modelBuilder.Entity<TenantAnnouncementRead>());
ConfigureTenantVerificationProfile(modelBuilder.Entity<TenantVerificationProfile>());
ConfigureTenantVisibilityRoleRule(modelBuilder.Entity<TenantVisibilityRoleRule>());
ConfigureQuotaPackage(modelBuilder.Entity<QuotaPackage>());
ConfigureTenantQuotaPackagePurchase(modelBuilder.Entity<TenantQuotaPackagePurchase>());
ConfigureMerchantDocument(modelBuilder.Entity<MerchantDocument>());
ConfigureMerchantContract(modelBuilder.Entity<MerchantContract>());
ConfigureMerchantStaff(modelBuilder.Entity<MerchantStaff>());
ConfigureMerchantCategory(modelBuilder.Entity<MerchantCategory>());
ConfigureStoreFee(modelBuilder.Entity<StoreFee>());
ConfigureStoreQualification(modelBuilder.Entity<StoreQualification>());
ConfigureStoreAuditRecord(modelBuilder.Entity<StoreAuditRecord>());
ConfigureStoreBusinessHour(modelBuilder.Entity<StoreBusinessHour>());
ConfigureStoreHoliday(modelBuilder.Entity<StoreHoliday>());
ConfigureStoreDeliveryZone(modelBuilder.Entity<StoreDeliveryZone>());
ConfigureStoreDeliverySetting(modelBuilder.Entity<StoreDeliverySetting>());
ConfigureStoreDineInSetting(modelBuilder.Entity<StoreDineInSetting>());
ConfigureStoreTableArea(modelBuilder.Entity<StoreTableArea>());
ConfigureStoreTable(modelBuilder.Entity<StoreTable>());
ConfigureStoreEmployeeShift(modelBuilder.Entity<StoreEmployeeShift>());
ConfigureStorePickupSetting(modelBuilder.Entity<StorePickupSetting>());
ConfigureStorePickupSlot(modelBuilder.Entity<StorePickupSlot>());
ConfigureStoreStaffTemplate(modelBuilder.Entity<StoreStaffTemplate>());
ConfigureStoreStaffWeeklySchedule(modelBuilder.Entity<StoreStaffWeeklySchedule>());
ConfigureProductCategory(modelBuilder.Entity<ProductCategory>());
ConfigureProduct(modelBuilder.Entity<Product>());
ConfigureProductAttributeGroup(modelBuilder.Entity<ProductAttributeGroup>());
ConfigureProductAttributeOption(modelBuilder.Entity<ProductAttributeOption>());
ConfigureProductSpecTemplate(modelBuilder.Entity<ProductSpecTemplate>());
ConfigureProductSpecTemplateOption(modelBuilder.Entity<ProductSpecTemplateOption>());
ConfigureProductSpecTemplateProduct(modelBuilder.Entity<ProductSpecTemplateProduct>());
ConfigureProductLabel(modelBuilder.Entity<ProductLabel>());
ConfigureProductLabelProduct(modelBuilder.Entity<ProductLabelProduct>());
ConfigureProductSchedule(modelBuilder.Entity<ProductSchedule>());
ConfigureProductScheduleProduct(modelBuilder.Entity<ProductScheduleProduct>());
ConfigureProductSku(modelBuilder.Entity<ProductSku>());
ConfigureProductSkuSaveJob(modelBuilder.Entity<ProductSkuSaveJob>());
ConfigureProductComboGroup(modelBuilder.Entity<ProductComboGroup>());
ConfigureProductComboGroupItem(modelBuilder.Entity<ProductComboGroupItem>());
ConfigureProductAddonGroup(modelBuilder.Entity<ProductAddonGroup>());
ConfigureProductAddonOption(modelBuilder.Entity<ProductAddonOption>());
ConfigureProductPricingRule(modelBuilder.Entity<ProductPricingRule>());
ConfigureProductMediaAsset(modelBuilder.Entity<ProductMediaAsset>());
ConfigureInventoryItem(modelBuilder.Entity<InventoryItem>());
ConfigureInventoryAdjustment(modelBuilder.Entity<InventoryAdjustment>());
ConfigureInventoryBatch(modelBuilder.Entity<InventoryBatch>());
ConfigureInventoryLockRecord(modelBuilder.Entity<InventoryLockRecord>());
ConfigureShoppingCart(modelBuilder.Entity<ShoppingCart>());
ConfigureCartItem(modelBuilder.Entity<CartItem>());
ConfigureCartItemAddon(modelBuilder.Entity<CartItemAddon>());
ConfigureCheckoutSession(modelBuilder.Entity<CheckoutSession>());
ConfigureOrder(modelBuilder.Entity<Order>());
ConfigureOrderItem(modelBuilder.Entity<OrderItem>());
ConfigureOrderStatusHistory(modelBuilder.Entity<OrderStatusHistory>());
ConfigureRefundRequest(modelBuilder.Entity<RefundRequest>());
ConfigurePaymentRecord(modelBuilder.Entity<PaymentRecord>());
ConfigurePaymentRefundRecord(modelBuilder.Entity<PaymentRefundRecord>());
ConfigureReservation(modelBuilder.Entity<Reservation>());
ConfigureQueueTicket(modelBuilder.Entity<QueueTicket>());
ConfigureDelivery(modelBuilder.Entity<DeliveryOrder>());
ConfigureDeliveryEvent(modelBuilder.Entity<DeliveryEvent>());
ConfigureGroupOrder(modelBuilder.Entity<GroupOrder>());
ConfigureGroupParticipant(modelBuilder.Entity<GroupParticipant>());
ConfigureCouponTemplate(modelBuilder.Entity<CouponTemplate>());
ConfigureCoupon(modelBuilder.Entity<Coupon>());
ConfigurePromotionCampaign(modelBuilder.Entity<PromotionCampaign>());
ConfigureNewCustomerGiftSetting(modelBuilder.Entity<NewCustomerGiftSetting>());
ConfigureNewCustomerCouponRule(modelBuilder.Entity<NewCustomerCouponRule>());
ConfigureNewCustomerInviteRecord(modelBuilder.Entity<NewCustomerInviteRecord>());
ConfigureNewCustomerGrowthRecord(modelBuilder.Entity<NewCustomerGrowthRecord>());
ConfigureMemberProfile(modelBuilder.Entity<MemberProfile>());
ConfigureMemberTier(modelBuilder.Entity<MemberTier>());
ConfigureMemberPointLedger(modelBuilder.Entity<MemberPointLedger>());
ConfigureChatSession(modelBuilder.Entity<ChatSession>());
ConfigureChatMessage(modelBuilder.Entity<ChatMessage>());
ConfigureSupportTicket(modelBuilder.Entity<SupportTicket>());
ConfigureTicketComment(modelBuilder.Entity<TicketComment>());
ConfigureAffiliatePartner(modelBuilder.Entity<AffiliatePartner>());
ConfigureAffiliateOrder(modelBuilder.Entity<AffiliateOrder>());
ConfigureAffiliatePayout(modelBuilder.Entity<AffiliatePayout>());
ConfigureCheckInCampaign(modelBuilder.Entity<CheckInCampaign>());
ConfigureCheckInRecord(modelBuilder.Entity<CheckInRecord>());
ConfigureCommunityPost(modelBuilder.Entity<CommunityPost>());
ConfigureCommunityComment(modelBuilder.Entity<CommunityComment>());
ConfigureCommunityReaction(modelBuilder.Entity<CommunityReaction>());
ConfigureMapLocation(modelBuilder.Entity<MapLocation>());
ConfigureNavigationRequest(modelBuilder.Entity<NavigationRequest>());
ConfigureMetricDefinition(modelBuilder.Entity<MetricDefinition>());
ConfigureMetricSnapshot(modelBuilder.Entity<MetricSnapshot>());
ConfigureMetricAlertRule(modelBuilder.Entity<MetricAlertRule>());
modelBuilder.AddInboxStateEntity();
modelBuilder.AddOutboxMessageEntity();
modelBuilder.AddOutboxStateEntity();
// 3. 应用多租户全局查询过滤器
ApplyTenantQueryFilters(modelBuilder);
}
private static void ConfigureTenant(EntityTypeBuilder<Tenant> builder)
{
builder.ToTable("tenants");
builder.HasKey(x => x.Id);
builder.Property(x => x.Code).HasMaxLength(64).IsRequired();
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.ShortName).HasMaxLength(64);
builder.Property(x => x.ContactName).HasMaxLength(64);
builder.Property(x => x.ContactPhone).HasMaxLength(32);
builder.Property(x => x.ContactEmail).HasMaxLength(128);
builder.Property(x => x.Industry).HasMaxLength(64);
builder.Property(x => x.LogoUrl).HasColumnType("text");
builder.Property(x => x.GeoStatus).HasConversion<int>().HasDefaultValue(GeoLocationStatus.Pending);
builder.Property(x => x.GeoFailReason).HasMaxLength(500);
builder.Property(x => x.GeoRetryCount).HasDefaultValue(0);
builder.Property(x => x.Remarks).HasMaxLength(512);
builder.Property(x => x.OperatingMode).HasConversion<int>();
builder.HasIndex(x => x.Code).IsUnique();
builder.HasIndex(x => x.ContactPhone).IsUnique();
builder.HasIndex(x => new { x.GeoStatus, x.GeoNextRetryAt });
builder.HasIndex(x => new { x.Longitude, x.Latitude })
.HasFilter("\"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL");
}
private static void ConfigureTenantVerificationProfile(EntityTypeBuilder<TenantVerificationProfile> builder)
{
builder.ToTable("tenant_verification_profiles");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantId).IsRequired();
builder.Property(x => x.BusinessLicenseNumber).HasMaxLength(64);
builder.Property(x => x.BusinessLicenseUrl).HasMaxLength(512);
builder.Property(x => x.LegalPersonName).HasMaxLength(64);
builder.Property(x => x.LegalPersonIdNumber).HasMaxLength(32);
builder.Property(x => x.LegalPersonIdFrontUrl).HasMaxLength(512);
builder.Property(x => x.LegalPersonIdBackUrl).HasMaxLength(512);
builder.Property(x => x.BankAccountName).HasMaxLength(128);
builder.Property(x => x.BankAccountNumber).HasMaxLength(64);
builder.Property(x => x.BankName).HasMaxLength(128);
builder.Property(x => x.ReviewRemarks).HasMaxLength(512);
builder.Property(x => x.ReviewedByName).HasMaxLength(64);
builder.HasIndex(x => x.TenantId).IsUnique();
}
// 租户订阅历史映射
private static void ConfigureTenantSubscriptionHistory(EntityTypeBuilder<TenantSubscriptionHistory> builder)
{
builder.ToTable("tenant_subscription_histories");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantId).IsRequired();
builder.Property(x => x.TenantSubscriptionId).IsRequired();
builder.Property(x => x.Notes).HasMaxLength(512);
builder.Property(x => x.Currency).HasMaxLength(8);
builder.HasIndex(x => new { x.TenantId, x.TenantSubscriptionId });
}
private static void ConfigureMerchant(EntityTypeBuilder<Merchant> builder)
{
builder.ToTable("merchants");
builder.HasKey(x => x.Id);
builder.Property(x => x.BrandName).HasMaxLength(128).IsRequired();
builder.Property(x => x.BrandAlias).HasMaxLength(64);
builder.Property(x => x.LegalPerson).HasMaxLength(64);
builder.Property(x => x.BusinessLicenseNumber).HasMaxLength(64);
builder.Property(x => x.ContactPhone).HasMaxLength(32).IsRequired();
builder.Property(x => x.ContactEmail).HasMaxLength(128);
builder.Property(x => x.Province).HasMaxLength(64);
builder.Property(x => x.City).HasMaxLength(64);
builder.Property(x => x.District).HasMaxLength(64);
builder.Property(x => x.Address).HasMaxLength(256);
builder.Property(x => x.ReviewRemarks).HasMaxLength(512);
builder.Property(x => x.OperatingMode).HasConversion<int>();
builder.Property(x => x.GeoStatus).HasConversion<int>().HasDefaultValue(GeoLocationStatus.Pending);
builder.Property(x => x.GeoFailReason).HasMaxLength(500);
builder.Property(x => x.GeoRetryCount).HasDefaultValue(0);
builder.Property(x => x.IsFrozen).HasDefaultValue(false);
builder.Property(x => x.FrozenReason).HasMaxLength(500);
builder.Property(x => x.ClaimedByName).HasMaxLength(100);
builder.Ignore(x => x.RowVersion);
builder.Property<uint>("xmin")
.HasColumnName("xmin")
.HasColumnType("xid")
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken();
builder.HasIndex(x => x.TenantId);
builder.HasIndex(x => new { x.TenantId, x.Status });
builder.HasIndex(x => x.ClaimedBy);
builder.HasIndex(x => new { x.TenantId, x.GeoStatus, x.GeoNextRetryAt });
builder.HasIndex(x => new { x.Longitude, x.Latitude })
.HasFilter("\"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL");
}
private static void ConfigureStore(EntityTypeBuilder<Store> builder)
{
builder.ToTable("stores");
builder.HasKey(x => x.Id);
builder.Property(x => x.Code).HasMaxLength(32).IsRequired();
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.Phone).HasMaxLength(32);
builder.Property(x => x.ManagerName).HasMaxLength(64);
builder.Property(x => x.BusinessLicenseNumber).HasMaxLength(50);
builder.Property(x => x.LegalRepresentative).HasMaxLength(100);
builder.Property(x => x.RegisteredAddress).HasMaxLength(500);
builder.Property(x => x.BusinessLicenseImageUrl).HasMaxLength(500);
builder.Property(x => x.SignboardImageUrl).HasMaxLength(500);
builder.Property(x => x.OwnershipType).HasConversion<int>();
builder.Property(x => x.AuditStatus).HasConversion<int>();
builder.Property(x => x.BusinessStatus).HasConversion<int>();
builder.Property(x => x.ClosureReason).HasConversion<int?>();
builder.Property(x => x.ClosureReasonText).HasMaxLength(500);
builder.Property(x => x.RejectionReason).HasMaxLength(500);
builder.Property(x => x.ForceCloseReason).HasMaxLength(500);
builder.Property(x => x.Province).HasMaxLength(64);
builder.Property(x => x.City).HasMaxLength(64);
builder.Property(x => x.District).HasMaxLength(64);
builder.Property(x => x.Address).HasMaxLength(256);
builder.Property(x => x.BusinessHours).HasMaxLength(256);
builder.Property(x => x.Announcement).HasMaxLength(512);
builder.Property(x => x.DeliveryRadiusKm).HasPrecision(6, 2);
builder.Property(x => x.GeoStatus).HasConversion<int>().HasDefaultValue(GeoLocationStatus.Pending);
builder.Property(x => x.GeoFailReason).HasMaxLength(500);
builder.Property(x => x.GeoRetryCount).HasDefaultValue(0);
builder.HasIndex(x => new { x.TenantId, x.MerchantId });
builder.HasIndex(x => new { x.TenantId, x.Code }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.AuditStatus });
builder.HasIndex(x => new { x.TenantId, x.BusinessStatus });
builder.HasIndex(x => new { x.TenantId, x.OwnershipType });
builder.HasIndex(x => new { x.TenantId, x.MerchantId, x.GeoStatus, x.GeoNextRetryAt });
builder.HasIndex(x => new { x.Longitude, x.Latitude })
.HasFilter("\"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL");
builder.HasIndex(x => new { x.MerchantId, x.BusinessLicenseNumber })
.IsUnique()
.HasFilter("\"BusinessLicenseNumber\" IS NOT NULL AND \"Status\" <> 3");
}
private static void ConfigureStoreFee(EntityTypeBuilder<StoreFee> builder)
{
builder.ToTable("store_fees");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.MinimumOrderAmount).HasPrecision(10, 2);
builder.Property(x => x.BaseDeliveryFee).HasPrecision(10, 2);
builder.Property(x => x.PackagingFeeMode).HasConversion<int>();
builder.Property(x => x.OrderPackagingFeeMode).HasConversion<int>();
builder.Property(x => x.FixedPackagingFee).HasPrecision(10, 2);
builder.Property(x => x.PackagingFeeTiersJson).HasColumnType("text");
builder.Property(x => x.FreeDeliveryThreshold).HasPrecision(10, 2);
builder.Property(x => x.CutleryFeeEnabled).HasDefaultValue(false);
builder.Property(x => x.CutleryFeeAmount).HasPrecision(10, 2);
builder.Property(x => x.RushFeeEnabled).HasDefaultValue(false);
builder.Property(x => x.RushFeeAmount).HasPrecision(10, 2);
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
builder.HasIndex(x => x.TenantId);
}
private static void ConfigureStoreQualification(EntityTypeBuilder<StoreQualification> builder)
{
builder.ToTable("store_qualifications");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.QualificationType).HasConversion<int>();
builder.Property(x => x.FileUrl).HasMaxLength(500).IsRequired();
builder.Property(x => x.DocumentNumber).HasMaxLength(100);
builder.Property(x => x.IssuedAt).HasColumnType("date");
builder.Property(x => x.ExpiresAt).HasColumnType("date");
builder.Property(x => x.SortOrder).HasDefaultValue(100);
builder.HasIndex(x => new { x.TenantId, x.StoreId });
builder.HasIndex(x => x.ExpiresAt)
.HasFilter("\"ExpiresAt\" IS NOT NULL");
builder.Ignore(x => x.IsExpired);
builder.Ignore(x => x.IsExpiringSoon);
}
private static void ConfigureStoreAuditRecord(EntityTypeBuilder<StoreAuditRecord> builder)
{
builder.ToTable("store_audit_records");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Action).HasConversion<int>();
builder.Property(x => x.PreviousStatus).HasConversion<int?>();
builder.Property(x => x.NewStatus).HasConversion<int>();
builder.Property(x => x.OperatorName).HasMaxLength(100).IsRequired();
builder.Property(x => x.RejectionReason).HasMaxLength(500);
builder.Property(x => x.Remarks).HasMaxLength(1000);
builder.HasIndex(x => new { x.TenantId, x.StoreId });
builder.HasIndex(x => x.CreatedAt);
}
private static void ConfigureProductCategory(EntityTypeBuilder<ProductCategory> builder)
{
builder.ToTable("product_categories");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.Description).HasMaxLength(256);
builder.Property(x => x.Icon).HasMaxLength(512);
builder.Property(x => x.ChannelsJson).HasColumnType("text").HasDefaultValue("[\"wm\"]");
builder.HasIndex(x => new { x.TenantId, x.StoreId });
}
private static void ConfigureProduct(EntityTypeBuilder<Product> builder)
{
builder.ToTable("products");
builder.HasKey(x => x.Id);
builder.Property(x => x.SpuCode).HasMaxLength(32).IsRequired();
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.Subtitle).HasMaxLength(256);
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<int>().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<int?>();
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");
builder.Property(x => x.SortWeight).HasDefaultValue(0);
builder.Property(x => x.PackingFee).HasPrecision(18, 2);
builder.HasIndex(x => new { x.TenantId, x.StoreId });
builder.HasIndex(x => new { x.TenantId, x.SpuCode }).IsUnique();
}
private static void ConfigureOrder(EntityTypeBuilder<Order> builder)
{
builder.ToTable("orders");
builder.HasKey(x => x.Id);
builder.Property(x => x.OrderNo).HasMaxLength(32).IsRequired();
builder.Property(x => x.CustomerName).HasMaxLength(64);
builder.Property(x => x.CustomerPhone).HasMaxLength(32);
builder.Property(x => x.TableNo).HasMaxLength(32);
builder.Property(x => x.QueueNumber).HasMaxLength(32);
builder.Property(x => x.CancelReason).HasMaxLength(256);
builder.Property(x => x.Remark).HasMaxLength(512);
builder.Property(x => x.ItemsAmount).HasPrecision(18, 2);
builder.Property(x => x.DiscountAmount).HasPrecision(18, 2);
builder.Property(x => x.PayableAmount).HasPrecision(18, 2);
builder.Property(x => x.PaidAmount).HasPrecision(18, 2);
builder.HasIndex(x => new { x.TenantId, x.OrderNo }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Status });
}
private static void ConfigureOrderItem(EntityTypeBuilder<OrderItem> builder)
{
builder.ToTable("order_items");
builder.HasKey(x => x.Id);
builder.Property(x => x.ProductName).HasMaxLength(128).IsRequired();
builder.Property(x => x.SkuName).HasMaxLength(128);
builder.Property(x => x.Unit).HasMaxLength(16);
builder.Property(x => x.UnitPrice).HasPrecision(18, 2);
builder.Property(x => x.DiscountAmount).HasPrecision(18, 2);
builder.Property(x => x.SubTotal).HasPrecision(18, 2);
builder.Property(x => x.AttributesJson).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.OrderId });
builder.HasOne<Order>()
.WithMany()
.HasForeignKey(x => x.OrderId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigurePaymentRecord(EntityTypeBuilder<PaymentRecord> builder)
{
builder.ToTable("payment_records");
builder.HasKey(x => x.Id);
builder.Property(x => x.Amount).HasPrecision(18, 2);
builder.Property(x => x.TradeNo).HasMaxLength(64);
builder.Property(x => x.ChannelTransactionId).HasMaxLength(64);
builder.Property(x => x.Remark).HasMaxLength(256);
builder.Property(x => x.Payload).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.OrderId });
}
private static void ConfigureReservation(EntityTypeBuilder<Reservation> builder)
{
builder.ToTable("reservations");
builder.HasKey(x => x.Id);
builder.Property(x => x.ReservationNo).HasMaxLength(32).IsRequired();
builder.Property(x => x.CustomerName).HasMaxLength(64).IsRequired();
builder.Property(x => x.CustomerPhone).HasMaxLength(32).IsRequired();
builder.Property(x => x.TablePreference).HasMaxLength(64);
builder.Property(x => x.Remark).HasMaxLength(512);
builder.Property(x => x.CheckInCode).HasMaxLength(32);
builder.HasIndex(x => new { x.TenantId, x.StoreId });
builder.HasIndex(x => new { x.TenantId, x.ReservationNo }).IsUnique();
}
private static void ConfigureQueueTicket(EntityTypeBuilder<QueueTicket> builder)
{
builder.ToTable("queue_tickets");
builder.HasKey(x => x.Id);
builder.Property(x => x.TicketNumber).HasMaxLength(32).IsRequired();
builder.Property(x => x.Remark).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.StoreId });
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TicketNumber }).IsUnique();
}
private static void ConfigureDelivery(EntityTypeBuilder<DeliveryOrder> builder)
{
builder.ToTable("delivery_orders");
builder.HasKey(x => x.Id);
builder.Property(x => x.ProviderOrderId).HasMaxLength(64);
builder.Property(x => x.DeliveryFee).HasPrecision(18, 2);
builder.Property(x => x.CourierName).HasMaxLength(64);
builder.Property(x => x.CourierPhone).HasMaxLength(32);
builder.Property(x => x.FailureReason).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.OrderId }).IsUnique();
}
private static void ConfigureTenantPackage(EntityTypeBuilder<TenantPackage> builder)
{
builder.ToTable("tenant_packages");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.Description).HasMaxLength(512);
builder.Property(x => x.FeaturePoliciesJson).HasColumnType("text");
builder.Property(x => x.PublishStatus)
.HasConversion<int>()
.HasDefaultValue(TenantPackagePublishStatus.Draft)
.HasSentinel((TenantPackagePublishStatus)(-1))
.HasComment("发布状态0=草稿1=已发布。");
// 1. 解决 EF Core 默认值哨兵问题:当我们希望插入 false/0 时,若数据库配置了 default 且 EF 认为该值是“未设置”,会导致 insert 省略列,最终落库为默认值。
// 2. 发布状态使用 -1 作为哨兵,避免 Draft=0 被误判为“未设置”而触发数据库默认值(发布/草稿切换必须可控)。
// 3. 将布尔开关哨兵值设置为数据库默认值true 作为哨兵false 才会被显式写入,从而保证“可见性/可售开关”在新增时可正确落库。
builder.Property(x => x.IsPublicVisible)
.HasDefaultValue(true)
.HasSentinel(true)
.HasComment("是否对外可见(展示页/套餐列表可见性)。");
builder.Property(x => x.IsAllowNewTenantPurchase)
.HasDefaultValue(true)
.HasSentinel(true)
.HasComment("是否允许新租户购买/选择(仅影响新购)。");
// 4. 展示配置:推荐标识与标签(用于套餐展示页/对比页)
builder.Property(x => x.IsRecommended)
.HasDefaultValue(false)
.HasComment("是否推荐展示(运营推荐标识)。");
builder.Property(x => x.Tags)
.HasColumnType("text[]")
.HasComment("套餐标签(用于展示与对比页)。");
builder.Property(x => x.SortOrder).HasDefaultValue(0).HasComment("展示排序,数值越小越靠前。");
builder.HasIndex(x => new { x.IsActive, x.SortOrder });
builder.HasIndex(x => new { x.PublishStatus, x.IsActive, x.IsPublicVisible, x.IsAllowNewTenantPurchase, x.SortOrder });
}
private static void ConfigureTenantSubscription(EntityTypeBuilder<TenantSubscription> builder)
{
builder.ToTable("tenant_subscriptions");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantPackageId).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.TenantPackageId });
}
private static void ConfigureTenantQuotaUsage(EntityTypeBuilder<TenantQuotaUsage> builder)
{
builder.ToTable("tenant_quota_usages");
builder.HasKey(x => x.Id);
builder.Property(x => x.QuotaType).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.QuotaType }).IsUnique();
}
private static void ConfigureTenantQuotaUsageHistory(EntityTypeBuilder<TenantQuotaUsageHistory> builder)
{
builder.ToTable("tenant_quota_usage_histories");
builder.HasKey(x => x.Id);
builder.Property(x => x.QuotaType).HasConversion<int>();
builder.Property(x => x.ChangeType).HasConversion<int>();
builder.Property(x => x.ChangeReason).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.QuotaType, x.RecordedAt });
builder.HasIndex(x => new { x.TenantId, x.RecordedAt });
}
private static void ConfigureTenantBilling(EntityTypeBuilder<TenantBillingStatement> builder)
{
new TenantBillingStatementConfiguration().Configure(builder);
}
private static void ConfigureTenantPayment(EntityTypeBuilder<TenantPayment> builder)
{
new TenantPaymentConfiguration().Configure(builder);
}
private static void ConfigureTenantNotification(EntityTypeBuilder<TenantNotification> builder)
{
builder.ToTable("tenant_notifications");
builder.HasKey(x => x.Id);
builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
builder.Property(x => x.Message).HasMaxLength(1024).IsRequired();
builder.Property(x => x.Channel).HasConversion<int>();
builder.Property(x => x.Severity).HasConversion<int>();
builder.Property(x => x.MetadataJson).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.Channel, x.SentAt });
}
private static void ConfigureTenantVisibilityRoleRule(EntityTypeBuilder<TenantVisibilityRoleRule> builder)
{
builder.ToTable("tenant_visibility_role_rules");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantId).IsRequired();
builder.Property(x => x.QuotaVisibleRoleCodes).HasColumnType("text[]");
builder.Property(x => x.BillingVisibleRoleCodes).HasColumnType("text[]");
builder.Property(x => x.UpdatedBy).IsRequired();
builder.Property(x => x.UpdatedAt).IsRequired();
builder.HasIndex(x => x.TenantId).IsUnique();
}
private static void ConfigureTenantAnnouncement(EntityTypeBuilder<TenantAnnouncement> builder)
{
builder.ToTable("tenant_announcements");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantId).IsRequired();
builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
builder.Property(x => x.Content).HasColumnType("text").IsRequired();
builder.Property(x => x.AnnouncementType)
.HasConversion<int>();
builder.Property(x => x.PublisherScope)
.HasConversion<int>();
builder.Property(x => x.PublisherUserId);
builder.Property(x => x.Status)
.HasConversion<int>();
builder.Property(x => x.PublishedAt);
builder.Property(x => x.RevokedAt);
builder.Property(x => x.ScheduledPublishAt);
builder.Property(x => x.TargetType).HasMaxLength(64).IsRequired();
builder.Property(x => x.TargetParameters).HasColumnType("text");
builder.Property(x => x.Priority).IsRequired();
builder.Property<bool>("IsActive").IsRequired();
builder.Property(x => x.RowVersion)
.IsRowVersion()
.IsConcurrencyToken()
.HasColumnType("bytea");
ConfigureAuditableEntity(builder);
ConfigureSoftDeleteEntity(builder);
builder.HasIndex("TenantId", "AnnouncementType", "IsActive");
builder.HasIndex(x => new { x.TenantId, x.EffectiveFrom, x.EffectiveTo });
builder.HasIndex(x => new { x.TenantId, x.Status, x.EffectiveFrom });
builder.HasIndex(x => new { x.Status, x.EffectiveFrom })
.HasFilter("\"TenantId\" = 0");
}
private static void ConfigureTenantAnnouncementRead(EntityTypeBuilder<TenantAnnouncementRead> builder)
{
builder.ToTable("tenant_announcement_reads");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantId).IsRequired();
builder.Property(x => x.AnnouncementId).IsRequired();
builder.Property(x => x.UserId);
builder.Property(x => x.ReadAt).IsRequired();
ConfigureAuditableEntity(builder);
ConfigureSoftDeleteEntity(builder);
builder.HasIndex(x => new { x.TenantId, x.AnnouncementId, x.UserId }).IsUnique();
}
private static void ConfigureMerchantDocument(EntityTypeBuilder<MerchantDocument> builder)
{
builder.ToTable("merchant_documents");
builder.HasKey(x => x.Id);
builder.Property(x => x.MerchantId).IsRequired();
builder.Property(x => x.DocumentType).HasConversion<int>();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.FileUrl).HasMaxLength(512).IsRequired();
builder.Property(x => x.DocumentNumber).HasMaxLength(64);
builder.HasIndex(x => new { x.TenantId, x.MerchantId, x.DocumentType });
}
private static void ConfigureMerchantContract(EntityTypeBuilder<MerchantContract> builder)
{
builder.ToTable("merchant_contracts");
builder.HasKey(x => x.Id);
builder.Property(x => x.MerchantId).IsRequired();
builder.Property(x => x.ContractNumber).HasMaxLength(64).IsRequired();
builder.Property(x => x.FileUrl).HasMaxLength(512).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.TerminationReason).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.MerchantId, x.ContractNumber }).IsUnique();
}
private static void ConfigureMerchantStaff(EntityTypeBuilder<MerchantStaff> builder)
{
builder.ToTable("merchant_staff");
builder.HasKey(x => x.Id);
builder.Property(x => x.MerchantId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.Phone).HasMaxLength(32).IsRequired();
builder.Property(x => x.Email).HasMaxLength(128);
builder.Property(x => x.RoleType).HasConversion<int>();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.PermissionsJson).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.MerchantId, x.Phone });
}
private static void ConfigureMerchantCategory(EntityTypeBuilder<MerchantCategory> builder)
{
builder.ToTable("merchant_categories");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.DisplayOrder).HasDefaultValue(0);
builder.Property(x => x.IsActive).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.Name }).IsUnique();
}
private static void ConfigureStoreBusinessHour(EntityTypeBuilder<StoreBusinessHour> builder)
{
builder.ToTable("store_business_hours");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.HourType).HasConversion<int>();
builder.Property(x => x.Notes).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.DayOfWeek });
}
private static void ConfigureStoreHoliday(EntityTypeBuilder<StoreHoliday> builder)
{
builder.ToTable("store_holidays");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Date).IsRequired();
builder.Property(x => x.EndDate);
builder.Property(x => x.IsAllDay).HasDefaultValue(true);
builder.Property(x => x.StartTime);
builder.Property(x => x.EndTime);
builder.Property(x => x.OverrideType).HasDefaultValue(OverrideType.Closed);
builder.Property(x => x.IsClosed).HasDefaultValue(true);
builder.Property(x => x.Reason).HasMaxLength(200);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Date });
}
private static void ConfigureStoreDeliveryZone(EntityTypeBuilder<StoreDeliveryZone> builder)
{
builder.ToTable("store_delivery_zones");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.ZoneName).HasMaxLength(64).IsRequired();
builder.Property(x => x.PolygonGeoJson).HasColumnType("text").IsRequired();
builder.Property(x => x.MinimumOrderAmount).HasPrecision(18, 2);
builder.Property(x => x.DeliveryFee).HasPrecision(18, 2);
builder.Property(x => x.Color).HasMaxLength(32);
builder.Property(x => x.Priority).HasDefaultValue(100);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ZoneName });
}
private static void ConfigureStoreDeliverySetting(EntityTypeBuilder<StoreDeliverySetting> builder)
{
builder.ToTable("store_delivery_settings");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Mode).HasConversion<int>();
builder.Property(x => x.FreeDeliveryThreshold).HasPrecision(10, 2);
builder.Property(x => x.MaxDeliveryDistance).HasPrecision(10, 2);
builder.Property(x => x.RadiusTiersJson).HasColumnType("text");
builder.Property(x => x.RadiusCenterLatitude).HasPrecision(10, 7);
builder.Property(x => x.RadiusCenterLongitude).HasPrecision(10, 7);
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
}
private static void ConfigureStoreDineInSetting(EntityTypeBuilder<StoreDineInSetting> builder)
{
builder.ToTable("store_dinein_settings");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Enabled).HasDefaultValue(true);
builder.Property(x => x.DefaultDiningMinutes).HasDefaultValue(90);
builder.Property(x => x.OvertimeReminderMinutes).HasDefaultValue(10);
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
}
private static void ConfigureStoreTableArea(EntityTypeBuilder<StoreTableArea> builder)
{
builder.ToTable("store_table_areas");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.Description).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name }).IsUnique();
}
private static void ConfigureStoreTable(EntityTypeBuilder<StoreTable> builder)
{
builder.ToTable("store_tables");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.TableCode).HasMaxLength(32).IsRequired();
builder.Property(x => x.Tags).HasMaxLength(128);
builder.Property(x => x.QrCodeUrl).HasMaxLength(512);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TableCode }).IsUnique();
}
private static void ConfigureStoreEmployeeShift(EntityTypeBuilder<StoreEmployeeShift> builder)
{
builder.ToTable("store_employee_shifts");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.StaffId).IsRequired();
builder.Property(x => x.RoleType).HasConversion<int>();
builder.Property(x => x.Notes).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ShiftDate, x.StaffId }).IsUnique();
}
private static void ConfigureStorePickupSetting(EntityTypeBuilder<StorePickupSetting> builder)
{
builder.ToTable("store_pickup_settings");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.DefaultCutoffMinutes).HasDefaultValue(30);
builder.Property(x => x.Mode).HasConversion<int>();
builder.Property(x => x.FineRuleJson).HasColumnType("text");
builder.Property(x => x.RowVersion)
.IsRequired()
.IsConcurrencyToken()
.ValueGeneratedNever();
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
}
private static void ConfigureStorePickupSlot(EntityTypeBuilder<StorePickupSlot> builder)
{
builder.ToTable("store_pickup_slots");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.Weekdays).HasMaxLength(32).IsRequired();
builder.Property(x => x.CutoffMinutes).HasDefaultValue(30);
builder.Property(x => x.RowVersion)
.IsRequired()
.IsConcurrencyToken()
.ValueGeneratedNever();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name });
}
private static void ConfigureStoreStaffTemplate(EntityTypeBuilder<StoreStaffTemplate> builder)
{
builder.ToTable("store_staff_templates");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.MorningStartTime).IsRequired();
builder.Property(x => x.MorningEndTime).IsRequired();
builder.Property(x => x.EveningStartTime).IsRequired();
builder.Property(x => x.EveningEndTime).IsRequired();
builder.Property(x => x.FullStartTime).IsRequired();
builder.Property(x => x.FullEndTime).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
}
private static void ConfigureStoreStaffWeeklySchedule(EntityTypeBuilder<StoreStaffWeeklySchedule> builder)
{
builder.ToTable("store_staff_weekly_schedules");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.StaffId).IsRequired();
builder.Property(x => x.DayOfWeek).IsRequired();
builder.Property(x => x.ShiftType).HasConversion<int>();
builder.Property(x => x.StartTime);
builder.Property(x => x.EndTime);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.StaffId, x.DayOfWeek }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.DayOfWeek });
}
private static void ConfigureProductAttributeGroup(EntityTypeBuilder<ProductAttributeGroup> builder)
{
builder.ToTable("product_attribute_groups");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.SelectionType).HasConversion<int>();
builder.Property(x => x.StoreId);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name });
}
private static void ConfigureProductAttributeOption(EntityTypeBuilder<ProductAttributeOption> builder)
{
builder.ToTable("product_attribute_options");
builder.HasKey(x => x.Id);
builder.Property(x => x.AttributeGroupId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.ExtraPrice).HasPrecision(18, 2);
builder.HasIndex(x => new { x.TenantId, x.AttributeGroupId, x.Name }).IsUnique();
}
private static void ConfigureProductSpecTemplate(EntityTypeBuilder<ProductSpecTemplate> builder)
{
builder.ToTable("product_spec_templates");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.Description).HasMaxLength(256).IsRequired();
builder.Property(x => x.TemplateType).HasConversion<int>();
builder.Property(x => x.SelectionType).HasConversion<int>();
builder.Property(x => x.MinSelect).IsRequired();
builder.Property(x => x.MaxSelect).IsRequired();
builder.Property(x => x.SortOrder).IsRequired();
builder.Property(x => x.IsEnabled).IsRequired();
builder.Property(x => x.IsRequired).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TemplateType, x.Name }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TemplateType, x.IsEnabled });
}
private static void ConfigureProductSpecTemplateOption(EntityTypeBuilder<ProductSpecTemplateOption> builder)
{
builder.ToTable("product_spec_template_options");
builder.HasKey(x => x.Id);
builder.Property(x => x.TemplateId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.ExtraPrice).HasPrecision(18, 2);
builder.Property(x => x.Stock).IsRequired();
builder.Property(x => x.IsEnabled).IsRequired();
builder.Property(x => x.SortOrder).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.TemplateId, x.Name }).IsUnique();
}
private static void ConfigureProductSpecTemplateProduct(EntityTypeBuilder<ProductSpecTemplateProduct> builder)
{
builder.ToTable("product_spec_template_products");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.TemplateId).IsRequired();
builder.Property(x => x.ProductId).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TemplateId, x.ProductId }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ProductId });
}
private static void ConfigureProductLabel(EntityTypeBuilder<ProductLabel> builder)
{
builder.ToTable("product_labels");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.Color).HasMaxLength(32).IsRequired();
builder.Property(x => x.SortOrder).IsRequired();
builder.Property(x => x.IsEnabled).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.IsEnabled, x.SortOrder });
}
private static void ConfigureProductLabelProduct(EntityTypeBuilder<ProductLabelProduct> builder)
{
builder.ToTable("product_label_products");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.LabelId).IsRequired();
builder.Property(x => x.ProductId).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.LabelId, x.ProductId }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ProductId });
}
private static void ConfigureProductSchedule(EntityTypeBuilder<ProductSchedule> builder)
{
builder.ToTable("product_schedules");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.StartTime).IsRequired();
builder.Property(x => x.EndTime).IsRequired();
builder.Property(x => x.WeekDaysMask).IsRequired();
builder.Property(x => x.IsEnabled).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.IsEnabled });
}
private static void ConfigureProductScheduleProduct(EntityTypeBuilder<ProductScheduleProduct> builder)
{
builder.ToTable("product_schedule_products");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.ScheduleId).IsRequired();
builder.Property(x => x.ProductId).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ScheduleId, x.ProductId }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ProductId });
}
private static void ConfigureProductSku(EntityTypeBuilder<ProductSku> builder)
{
builder.ToTable("product_skus");
builder.HasKey(x => x.Id);
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.SkuCode).HasMaxLength(32).IsRequired();
builder.Property(x => x.Barcode).HasMaxLength(64);
builder.Property(x => x.Price).HasPrecision(18, 2);
builder.Property(x => x.OriginalPrice).HasPrecision(18, 2);
builder.Property(x => x.Weight).HasPrecision(10, 3);
builder.Property(x => x.AttributesJson).HasColumnType("text");
builder.Property(x => x.IsEnabled).HasDefaultValue(true);
builder.HasIndex(x => new { x.TenantId, x.ProductId });
builder.HasIndex(x => new { x.TenantId, x.SkuCode }).IsUnique();
}
private static void ConfigureProductSkuSaveJob(EntityTypeBuilder<ProductSkuSaveJob> builder)
{
builder.ToTable("product_sku_save_jobs");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.Mode).HasMaxLength(16).IsRequired();
builder.Property(x => x.PayloadJson).HasColumnType("text").IsRequired();
builder.Property(x => x.ProgressTotal).IsRequired();
builder.Property(x => x.ProgressProcessed).IsRequired();
builder.Property(x => x.FailedCount).IsRequired();
builder.Property(x => x.ErrorMessage).HasMaxLength(2000);
builder.Property(x => x.HangfireJobId).HasMaxLength(64);
builder.HasIndex(x => new { x.TenantId, x.ProductId, x.CreatedAt });
builder.HasIndex(x => new { x.TenantId, x.Status, x.CreatedAt });
}
private static void ConfigureProductComboGroup(EntityTypeBuilder<ProductComboGroup> builder)
{
builder.ToTable("product_combo_groups");
builder.HasKey(x => x.Id);
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.MinSelect).IsRequired();
builder.Property(x => x.MaxSelect).IsRequired();
builder.Property(x => x.SortOrder).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.ProductId, x.SortOrder });
builder.HasIndex(x => new { x.TenantId, x.ProductId, x.Name });
}
private static void ConfigureProductComboGroupItem(EntityTypeBuilder<ProductComboGroupItem> builder)
{
builder.ToTable("product_combo_group_items");
builder.HasKey(x => x.Id);
builder.Property(x => x.ComboGroupId).IsRequired();
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.Quantity).IsRequired();
builder.Property(x => x.SortOrder).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.ComboGroupId, x.SortOrder });
builder.HasIndex(x => new { x.TenantId, x.ComboGroupId, x.ProductId }).IsUnique();
}
private static void ConfigureProductAddonGroup(EntityTypeBuilder<ProductAddonGroup> builder)
{
builder.ToTable("product_addon_groups");
builder.HasKey(x => x.Id);
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.SelectionType).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.ProductId, x.Name });
}
private static void ConfigureProductAddonOption(EntityTypeBuilder<ProductAddonOption> builder)
{
builder.ToTable("product_addon_options");
builder.HasKey(x => x.Id);
builder.Property(x => x.AddonGroupId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.ExtraPrice).HasPrecision(18, 2);
}
private static void ConfigureProductPricingRule(EntityTypeBuilder<ProductPricingRule> builder)
{
builder.ToTable("product_pricing_rules");
builder.HasKey(x => x.Id);
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.RuleType).HasConversion<int>();
builder.Property(x => x.ConditionsJson).HasColumnType("text").IsRequired();
builder.Property(x => x.Price).HasPrecision(18, 2);
builder.Property(x => x.WeekdaysJson).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.ProductId, x.RuleType });
}
private static void ConfigureProductMediaAsset(EntityTypeBuilder<ProductMediaAsset> builder)
{
builder.ToTable("product_media_assets");
builder.HasKey(x => x.Id);
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.MediaType).HasConversion<int>();
builder.Property(x => x.Url).HasMaxLength(512).IsRequired();
builder.Property(x => x.Caption).HasMaxLength(256);
}
private static void ConfigureInventoryItem(EntityTypeBuilder<InventoryItem> builder)
{
builder.ToTable("inventory_items");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.ProductSkuId).IsRequired();
builder.Property(x => x.BatchNumber).HasMaxLength(64);
builder.Property(x => x.Location).HasMaxLength(64);
builder.Property(x => x.RowVersion)
.IsConcurrencyToken();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ProductSkuId, x.BatchNumber });
}
private static void ConfigureInventoryAdjustment(EntityTypeBuilder<InventoryAdjustment> builder)
{
builder.ToTable("inventory_adjustments");
builder.HasKey(x => x.Id);
builder.Property(x => x.InventoryItemId).IsRequired();
builder.Property(x => x.AdjustmentType).HasConversion<int>();
builder.Property(x => x.Reason).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.InventoryItemId, x.OccurredAt });
}
private static void ConfigureInventoryBatch(EntityTypeBuilder<InventoryBatch> builder)
{
builder.ToTable("inventory_batches");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.ProductSkuId).IsRequired();
builder.Property(x => x.BatchNumber).HasMaxLength(64).IsRequired();
builder.Property(x => x.RowVersion)
.IsConcurrencyToken();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ProductSkuId, x.BatchNumber }).IsUnique();
}
private static void ConfigureInventoryLockRecord(EntityTypeBuilder<InventoryLockRecord> builder)
{
builder.ToTable("inventory_lock_records");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.ProductSkuId).IsRequired();
builder.Property(x => x.Quantity).IsRequired();
builder.Property(x => x.IdempotencyKey).HasMaxLength(128).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.RowVersion)
.IsConcurrencyToken();
builder.HasIndex(x => new { x.TenantId, x.IdempotencyKey }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ProductSkuId, x.Status });
}
private static void ConfigureShoppingCart(EntityTypeBuilder<ShoppingCart> builder)
{
builder.ToTable("shopping_carts");
builder.HasKey(x => x.Id);
builder.Property(x => x.UserId).IsRequired();
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.TableContext).HasMaxLength(64);
builder.Property(x => x.DeliveryPreference).HasMaxLength(32);
builder.HasIndex(x => new { x.TenantId, x.UserId, x.StoreId }).IsUnique();
}
private static void ConfigureCartItem(EntityTypeBuilder<CartItem> builder)
{
builder.ToTable("cart_items");
builder.HasKey(x => x.Id);
builder.Property(x => x.ShoppingCartId).IsRequired();
builder.Property(x => x.ProductId).IsRequired();
builder.Property(x => x.ProductName).HasMaxLength(128).IsRequired();
builder.Property(x => x.UnitPrice).HasPrecision(18, 2);
builder.Property(x => x.Remark).HasMaxLength(256);
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.AttributesJson).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.ShoppingCartId });
}
private static void ConfigureCartItemAddon(EntityTypeBuilder<CartItemAddon> builder)
{
builder.ToTable("cart_item_addons");
builder.HasKey(x => x.Id);
builder.Property(x => x.CartItemId).IsRequired();
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.ExtraPrice).HasPrecision(18, 2);
}
private static void ConfigureCheckoutSession(EntityTypeBuilder<CheckoutSession> builder)
{
builder.ToTable("checkout_sessions");
builder.HasKey(x => x.Id);
builder.Property(x => x.UserId).IsRequired();
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.SessionToken).HasMaxLength(64).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.ValidationResultJson).HasColumnType("text").IsRequired();
builder.HasIndex(x => new { x.TenantId, x.SessionToken }).IsUnique();
}
private static void ConfigureOrderStatusHistory(EntityTypeBuilder<OrderStatusHistory> builder)
{
builder.ToTable("order_status_histories");
builder.HasKey(x => x.Id);
builder.Property(x => x.OrderId).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.Notes).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.OrderId, x.OccurredAt });
}
private static void ConfigureRefundRequest(EntityTypeBuilder<RefundRequest> builder)
{
builder.ToTable("refund_requests");
builder.HasKey(x => x.Id);
builder.Property(x => x.OrderId).IsRequired();
builder.Property(x => x.RefundNo).HasMaxLength(32).IsRequired();
builder.Property(x => x.Amount).HasPrecision(18, 2);
builder.Property(x => x.Reason).HasMaxLength(256).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.ReviewNotes).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.RefundNo }).IsUnique();
}
private static void ConfigurePaymentRefundRecord(EntityTypeBuilder<PaymentRefundRecord> builder)
{
builder.ToTable("payment_refund_records");
builder.HasKey(x => x.Id);
builder.Property(x => x.PaymentRecordId).IsRequired();
builder.Property(x => x.OrderId).IsRequired();
builder.Property(x => x.Amount).HasPrecision(18, 2);
builder.Property(x => x.ChannelRefundId).HasMaxLength(64);
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.Payload).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.PaymentRecordId });
}
private static void ConfigureDeliveryEvent(EntityTypeBuilder<DeliveryEvent> builder)
{
builder.ToTable("delivery_events");
builder.HasKey(x => x.Id);
builder.Property(x => x.DeliveryOrderId).IsRequired();
builder.Property(x => x.EventType).HasConversion<int>();
builder.Property(x => x.Message).HasMaxLength(256);
builder.Property(x => x.Payload).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.DeliveryOrderId, x.EventType });
}
private static void ConfigureGroupOrder(EntityTypeBuilder<GroupOrder> builder)
{
builder.ToTable("group_orders");
builder.HasKey(x => x.Id);
builder.Property(x => x.GroupOrderNo).HasMaxLength(32).IsRequired();
builder.Property(x => x.GroupPrice).HasPrecision(18, 2);
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.GroupOrderNo }).IsUnique();
}
private static void ConfigureGroupParticipant(EntityTypeBuilder<GroupParticipant> builder)
{
builder.ToTable("group_participants");
builder.HasKey(x => x.Id);
builder.Property(x => x.GroupOrderId).IsRequired();
builder.Property(x => x.OrderId).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.GroupOrderId, x.UserId }).IsUnique();
}
private static void ConfigureCouponTemplate(EntityTypeBuilder<CouponTemplate> builder)
{
builder.ToTable("coupon_templates");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.CouponType).HasConversion<int>();
builder.Property(x => x.Description).HasMaxLength(512);
builder.Property(x => x.TotalQuantity);
builder.Property(x => x.PerUserLimit);
builder.Property(x => x.StoreScopeJson).HasColumnType("text");
builder.Property(x => x.ProductScopeJson).HasColumnType("text");
builder.Property(x => x.ChannelsJson).HasColumnType("text");
builder.Property(x => x.Status).HasConversion<int>();
}
private static void ConfigureCoupon(EntityTypeBuilder<Coupon> builder)
{
builder.ToTable("coupons");
builder.HasKey(x => x.Id);
builder.Property(x => x.CouponTemplateId).IsRequired();
builder.Property(x => x.Code).HasMaxLength(32).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.Code }).IsUnique();
}
private static void ConfigurePromotionCampaign(EntityTypeBuilder<PromotionCampaign> builder)
{
builder.ToTable("promotion_campaigns");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.PromotionType).HasConversion<int>();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.RulesJson).HasColumnType("text").IsRequired();
builder.Property(x => x.AudienceDescription).HasMaxLength(512);
builder.Property(x => x.BannerUrl).HasMaxLength(512);
}
private static void ConfigureNewCustomerGiftSetting(EntityTypeBuilder<NewCustomerGiftSetting> builder)
{
builder.ToTable("new_customer_gift_settings");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.GiftType).HasConversion<int>();
builder.Property(x => x.DirectReduceAmount).HasPrecision(18, 2);
builder.Property(x => x.DirectMinimumSpend).HasPrecision(18, 2);
builder.Property(x => x.ShareChannelsJson).HasColumnType("text").IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
}
private static void ConfigureNewCustomerCouponRule(EntityTypeBuilder<NewCustomerCouponRule> builder)
{
builder.ToTable("new_customer_coupon_rules");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Scene).HasConversion<int>();
builder.Property(x => x.CouponType).HasConversion<int>();
builder.Property(x => x.Value).HasPrecision(18, 2);
builder.Property(x => x.MinimumSpend).HasPrecision(18, 2);
builder.Property(x => x.ValidDays).IsRequired();
builder.Property(x => x.SortOrder).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Scene, x.SortOrder });
}
private static void ConfigureNewCustomerInviteRecord(EntityTypeBuilder<NewCustomerInviteRecord> builder)
{
builder.ToTable("new_customer_invite_records");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.InviterName).HasMaxLength(64).IsRequired();
builder.Property(x => x.InviteeName).HasMaxLength(64).IsRequired();
builder.Property(x => x.InviteTime).IsRequired();
builder.Property(x => x.OrderStatus).HasConversion<int>();
builder.Property(x => x.RewardStatus).HasConversion<int>();
builder.Property(x => x.SourceChannel).HasMaxLength(32);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.InviteTime });
}
private static void ConfigureNewCustomerGrowthRecord(EntityTypeBuilder<NewCustomerGrowthRecord> builder)
{
builder.ToTable("new_customer_growth_records");
builder.HasKey(x => x.Id);
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.CustomerKey).HasMaxLength(64).IsRequired();
builder.Property(x => x.CustomerName).HasMaxLength(64);
builder.Property(x => x.RegisteredAt).IsRequired();
builder.Property(x => x.SourceChannel).HasMaxLength(32);
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.CustomerKey }).IsUnique();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.RegisteredAt });
}
private static void ConfigureMemberProfile(EntityTypeBuilder<MemberProfile> builder)
{
builder.ToTable("member_profiles");
builder.HasKey(x => x.Id);
builder.Property(x => x.Mobile).HasMaxLength(32).IsRequired();
builder.Property(x => x.Nickname).HasMaxLength(64);
builder.Property(x => x.AvatarUrl).HasMaxLength(256);
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.Mobile }).IsUnique();
}
private static void ConfigureMemberTier(EntityTypeBuilder<MemberTier> builder)
{
builder.ToTable("member_tiers");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
builder.Property(x => x.BenefitsJson).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.Name }).IsUnique();
}
private static void ConfigureMemberPointLedger(EntityTypeBuilder<MemberPointLedger> builder)
{
builder.ToTable("member_point_ledgers");
builder.HasKey(x => x.Id);
builder.Property(x => x.MemberId).IsRequired();
builder.Property(x => x.Reason).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.MemberId, x.OccurredAt });
}
private static void ConfigureChatSession(EntityTypeBuilder<ChatSession> builder)
{
builder.ToTable("chat_sessions");
builder.HasKey(x => x.Id);
builder.Property(x => x.SessionCode).HasMaxLength(64).IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.SessionCode }).IsUnique();
}
private static void ConfigureChatMessage(EntityTypeBuilder<ChatMessage> builder)
{
builder.ToTable("chat_messages");
builder.HasKey(x => x.Id);
builder.Property(x => x.ChatSessionId).IsRequired();
builder.Property(x => x.SenderType).HasConversion<int>();
builder.Property(x => x.ContentType).HasMaxLength(64).IsRequired();
builder.Property(x => x.Content).HasMaxLength(1024).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.ChatSessionId, x.CreatedAt });
}
private static void ConfigureSupportTicket(EntityTypeBuilder<SupportTicket> builder)
{
builder.ToTable("support_tickets");
builder.HasKey(x => x.Id);
builder.Property(x => x.TicketNo).HasMaxLength(32).IsRequired();
builder.Property(x => x.Subject).HasMaxLength(128).IsRequired();
builder.Property(x => x.Description).HasColumnType("text").IsRequired();
builder.Property(x => x.Priority).HasConversion<int>();
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.TicketNo }).IsUnique();
}
private static void ConfigureTicketComment(EntityTypeBuilder<TicketComment> builder)
{
builder.ToTable("ticket_comments");
builder.HasKey(x => x.Id);
builder.Property(x => x.SupportTicketId).IsRequired();
builder.Property(x => x.Content).HasMaxLength(1024).IsRequired();
builder.Property(x => x.AttachmentsJson).HasColumnType("text");
builder.HasIndex(x => new { x.TenantId, x.SupportTicketId });
}
private static void ConfigureAffiliatePartner(EntityTypeBuilder<AffiliatePartner> builder)
{
builder.ToTable("affiliate_partners");
builder.HasKey(x => x.Id);
builder.Property(x => x.DisplayName).HasMaxLength(64).IsRequired();
builder.Property(x => x.Phone).HasMaxLength(32);
builder.Property(x => x.ChannelType).HasConversion<int>();
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.Remarks).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.DisplayName });
}
private static void ConfigureAffiliateOrder(EntityTypeBuilder<AffiliateOrder> builder)
{
builder.ToTable("affiliate_orders");
builder.HasKey(x => x.Id);
builder.Property(x => x.AffiliatePartnerId).IsRequired();
builder.Property(x => x.OrderId).IsRequired();
builder.Property(x => x.OrderAmount).HasPrecision(18, 2);
builder.Property(x => x.EstimatedCommission).HasPrecision(18, 2);
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.AffiliatePartnerId, x.OrderId }).IsUnique();
}
private static void ConfigureAffiliatePayout(EntityTypeBuilder<AffiliatePayout> builder)
{
builder.ToTable("affiliate_payouts");
builder.HasKey(x => x.Id);
builder.Property(x => x.AffiliatePartnerId).IsRequired();
builder.Property(x => x.Period).HasMaxLength(32).IsRequired();
builder.Property(x => x.Amount).HasPrecision(18, 2);
builder.Property(x => x.Status).HasConversion<int>();
builder.Property(x => x.Remarks).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.AffiliatePartnerId, x.Period }).IsUnique();
}
private static void ConfigureCheckInCampaign(EntityTypeBuilder<CheckInCampaign> builder)
{
builder.ToTable("checkin_campaigns");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.Description).HasMaxLength(512);
builder.Property(x => x.RewardsJson).HasColumnType("text").IsRequired();
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.Name });
}
private static void ConfigureCheckInRecord(EntityTypeBuilder<CheckInRecord> builder)
{
builder.ToTable("checkin_records");
builder.HasKey(x => x.Id);
builder.Property(x => x.CheckInCampaignId).IsRequired();
builder.Property(x => x.UserId).IsRequired();
builder.Property(x => x.RewardJson).HasColumnType("text").IsRequired();
builder.HasIndex(x => new { x.TenantId, x.CheckInCampaignId, x.UserId, x.CheckInDate }).IsUnique();
}
private static void ConfigureCommunityPost(EntityTypeBuilder<CommunityPost> builder)
{
builder.ToTable("community_posts");
builder.HasKey(x => x.Id);
builder.Property(x => x.AuthorUserId).IsRequired();
builder.Property(x => x.Title).HasMaxLength(128);
builder.Property(x => x.Content).HasColumnType("text").IsRequired();
builder.Property(x => x.MediaJson).HasColumnType("text");
builder.Property(x => x.Status).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.AuthorUserId, x.CreatedAt });
}
private static void ConfigureCommunityComment(EntityTypeBuilder<CommunityComment> builder)
{
builder.ToTable("community_comments");
builder.HasKey(x => x.Id);
builder.Property(x => x.PostId).IsRequired();
builder.Property(x => x.Content).HasMaxLength(512).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.PostId, x.CreatedAt });
}
private static void ConfigureCommunityReaction(EntityTypeBuilder<CommunityReaction> builder)
{
builder.ToTable("community_reactions");
builder.HasKey(x => x.Id);
builder.Property(x => x.PostId).IsRequired();
builder.Property(x => x.ReactionType).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.PostId, x.UserId }).IsUnique();
}
private static void ConfigureMapLocation(EntityTypeBuilder<MapLocation> builder)
{
builder.ToTable("map_locations");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.Address).HasMaxLength(256).IsRequired();
builder.Property(x => x.Landmark).HasMaxLength(128);
builder.HasIndex(x => new { x.TenantId, x.StoreId });
}
private static void ConfigureNavigationRequest(EntityTypeBuilder<NavigationRequest> builder)
{
builder.ToTable("navigation_requests");
builder.HasKey(x => x.Id);
builder.Property(x => x.UserId).IsRequired();
builder.Property(x => x.StoreId).IsRequired();
builder.Property(x => x.Channel).HasConversion<int>();
builder.Property(x => x.TargetApp).HasConversion<int>();
builder.HasIndex(x => new { x.TenantId, x.UserId, x.StoreId, x.RequestedAt });
}
private static void ConfigureMetricDefinition(EntityTypeBuilder<MetricDefinition> builder)
{
builder.ToTable("metric_definitions");
builder.HasKey(x => x.Id);
builder.Property(x => x.Code).HasMaxLength(64).IsRequired();
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.Description).HasMaxLength(512);
builder.Property(x => x.DimensionsJson).HasColumnType("text");
builder.Property(x => x.DefaultAggregation).HasMaxLength(32).IsRequired();
builder.HasIndex(x => new { x.TenantId, x.Code }).IsUnique();
}
private static void ConfigureMetricSnapshot(EntityTypeBuilder<MetricSnapshot> builder)
{
builder.ToTable("metric_snapshots");
builder.HasKey(x => x.Id);
builder.Property(x => x.MetricDefinitionId).IsRequired();
builder.Property(x => x.DimensionKey).HasMaxLength(256).IsRequired();
builder.Property(x => x.Value).HasPrecision(18, 4);
builder.HasIndex(x => new { x.TenantId, x.MetricDefinitionId, x.DimensionKey, x.WindowStart, x.WindowEnd }).IsUnique();
}
private static void ConfigureMetricAlertRule(EntityTypeBuilder<MetricAlertRule> builder)
{
builder.ToTable("metric_alert_rules");
builder.HasKey(x => x.Id);
builder.Property(x => x.MetricDefinitionId).IsRequired();
builder.Property(x => x.ConditionJson).HasColumnType("text").IsRequired();
builder.Property(x => x.Severity).HasConversion<int>();
builder.Property(x => x.NotificationChannels).HasMaxLength(256);
builder.HasIndex(x => new { x.TenantId, x.MetricDefinitionId, x.Severity });
}
private static void ConfigureQuotaPackage(EntityTypeBuilder<QuotaPackage> builder)
{
builder.ToTable("quota_packages");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
builder.Property(x => x.QuotaType).HasConversion<int>().IsRequired();
builder.Property(x => x.QuotaValue).HasPrecision(18, 2).IsRequired();
builder.Property(x => x.Price).HasPrecision(18, 2).IsRequired();
builder.Property(x => x.IsActive).IsRequired();
builder.Property(x => x.SortOrder).HasDefaultValue(0);
builder.Property(x => x.Description).HasMaxLength(512);
builder.HasIndex(x => new { x.QuotaType, x.IsActive, x.SortOrder });
}
private static void ConfigureTenantQuotaPackagePurchase(EntityTypeBuilder<TenantQuotaPackagePurchase> builder)
{
builder.ToTable("tenant_quota_package_purchases");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantId).IsRequired();
builder.Property(x => x.QuotaPackageId).IsRequired();
builder.Property(x => x.QuotaValue).HasPrecision(18, 2).IsRequired();
builder.Property(x => x.Price).HasPrecision(18, 2).IsRequired();
builder.Property(x => x.PurchasedAt).IsRequired();
builder.Property(x => x.Notes).HasMaxLength(512);
builder.HasIndex(x => new { x.TenantId, x.QuotaPackageId, x.PurchasedAt });
}
}