"""全渠道与电商对接模型: 1. ChannelAccount 电商平台授权店铺配置(抖店、拼多多、1688 等) 2. ChannelOrder 外部拉取订单及本地销售订单映射 3. WebhookEvent 外部回调与事件分发日志 """ from django.db import models from apps.core.base_models import TenantScopedModel class ChannelAccount(TenantScopedModel): """渠道授权店铺配置。""" PLATFORM_CHOICES = [ ("douyin", "抖音电商 / 抖店"), ("pinduoduo", "拼多多"), ("1688", "1688 阿里分销"), ("kuaishou", "快手小店"), ("custom", "自定义开放接口"), ] platform = models.CharField(max_length=32, choices=PLATFORM_CHOICES, default="douyin") shop_id = models.CharField(max_length=64, help_text="外部平台店铺ID") shop_name = models.CharField(max_length=128, help_text="店铺名称") app_key = models.CharField(max_length=128, blank=True, default="") app_secret = models.CharField(max_length=255, blank=True, default="") access_token = models.TextField(blank=True, default="") refresh_token = models.TextField(blank=True, default="") token_expires_at = models.DateTimeField(null=True, blank=True) is_active = models.BooleanField(default=True) # 业务映射默认值 default_warehouse = models.ForeignKey( "inventory.Warehouse", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text="该店铺订单默认出库仓库", ) default_customer = models.ForeignKey( "partner.Customer", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text="该店铺归属的客户(挂账往来)", ) class Meta: db_table = "channel_account" unique_together = [("tenant", "platform", "shop_id")] verbose_name_plural = "电商店铺配置" ordering = ["platform", "shop_name"] def __str__(self): return f"[{self.get_platform_display()}] {self.shop_name} ({self.shop_id})" class ChannelOrder(TenantScopedModel): """外部渠道拉取的订单。""" SYNC_STATUS_CHOICES = [ ("pending", "待转单"), ("converted", "已转销售单"), ("failed", "转单失败"), ("ignored", "已忽略"), ] channel_account = models.ForeignKey( ChannelAccount, on_delete=models.CASCADE, related_name="orders", ) external_order_id = models.CharField(max_length=128, help_text="外部平台订单号") order_status = models.CharField(max_length=64, default="", help_text="外部平台订单状态(如 PAID, SHIPPED)") order_amount = models.DecimalField(max_digits=18, decimal_places=4, default=0) buyer_name = models.CharField(max_length=64, blank=True, default="") buyer_phone = models.CharField(max_length=32, blank=True, default="") receiver_address = models.CharField(max_length=255, blank=True, default="") sync_status = models.CharField(max_length=16, choices=SYNC_STATUS_CHOICES, default="pending") sync_error = models.TextField(blank=True, default="") # 关联生成的本地销售单 sales_order = models.ForeignKey( "sales.SalesOrder", null=True, blank=True, on_delete=models.SET_NULL, related_name="channel_orders", help_text="关联的内部销售订单", ) raw_payload = models.JSONField(default=dict, blank=True, help_text="外部原始订单 JSON") class Meta: db_table = "channel_order" unique_together = [("tenant", "channel_account", "external_order_id")] verbose_name_plural = "渠道订单" indexes = [ models.Index(fields=["tenant", "sync_status"]), models.Index(fields=["tenant", "external_order_id"]), ] ordering = ["-created_at"] def __str__(self): return f"{self.channel_account.shop_name} 订单: {self.external_order_id} [{self.sync_status}]" class WebhookEvent(TenantScopedModel): """Webhook 回调与事件分发日志。""" DIRECTION_CHOICES = [ ("inbound", "外部推送回调"), ("outbound", "本系统向外推送"), ] STATUS_CHOICES = [ ("received", "已接收"), ("processed", "处理成功"), ("failed", "处理失败"), ] direction = models.CharField(max_length=16, choices=DIRECTION_CHOICES, default="inbound") event_type = models.CharField(max_length=64, help_text="事件类型,如 order.paid, stock.changed") source = models.CharField(max_length=32, default="douyin", help_text="来源系统或平台") payload = models.JSONField(default=dict, blank=True) status = models.CharField(max_length=16, choices=STATUS_CHOICES, default="received") error_message = models.TextField(blank=True, default="") class Meta: db_table = "channel_webhook_event" verbose_name_plural = "Webhook事件日志" ordering = ["-created_at"]