"""全渠道与电商 ViewSets: 1. ChannelAccountViewSet: 店铺授权管理、action sync_orders 触发拉单 2. ChannelOrderViewSet: 渠道订单列表、action convert 手动转单 3. WebhookViewSet: 接收外部 Webhook 推送 """ from rest_framework import status from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.exceptions import ValidationError from asgiref.sync import sync_to_async from adrf.viewsets import ViewSet from apps.core.viewset import BaseTenantViewSet from .models import ChannelAccount, ChannelOrder, WebhookEvent from .serializers import ChannelAccountSerializer, ChannelOrderSerializer, WebhookEventSerializer from . import services as channel_services class ChannelAccountViewSet(BaseTenantViewSet): model = ChannelAccount serializer_class = ChannelAccountSerializer search_fields = ["shop_id", "shop_name", "platform"] @action(detail=True, methods=["post"], url_path="sync-orders") async def sync_orders(self, request, pk=None): """主动从该电商渠道拉取最新订单并自动转内部销售单。""" tenant = await self.get_tenant() try: account = await sync_to_async(self.model.objects.get)(tenant=tenant, pk=pk) except self.model.DoesNotExist: return Response({"detail": "店铺配置不存在"}, status=status.HTTP_404_NOT_FOUND) auto_convert = request.data.get("auto_convert", True) result = await sync_to_async(channel_services.sync_channel_orders)(account, auto_convert=auto_convert) return Response({"ok": True, "result": result}) class ChannelOrderViewSet(BaseTenantViewSet): model = ChannelOrder serializer_class = ChannelOrderSerializer search_fields = ["external_order_id", "buyer_name", "buyer_phone"] @action(detail=True, methods=["post"], url_path="convert") async def convert(self, request, pk=None): """将指定的外部渠道订单转换为内部销售订单。""" tenant = await self.get_tenant() try: corder = await sync_to_async(self.model.objects.get)(tenant=tenant, pk=pk) except self.model.DoesNotExist: return Response({"detail": "订单不存在"}, status=status.HTTP_404_NOT_FOUND) try: so = await sync_to_async(channel_services.convert_channel_order_to_sales_order)(corder) return Response({ "ok": True, "sales_order_id": so.id, "sales_order_no": so.bill_no, }) except Exception as e: return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) class WebhookViewSet(ViewSet): """接收外部平台的 Webhook 实时通知(如抖店支付推送)。""" async def get_tenant(self): from apps.core.viewset import resolve_tenant from django.conf import settings code = self.request.META.get("HTTP_X_TENANT_ID", settings.TENANT_DEFAULT) return await sync_to_async(resolve_tenant)(code) @action(detail=False, methods=["post"], url_path="inbound") async def inbound(self, request): tenant = await self.get_tenant() if not tenant: raise ValidationError({"tenant": "无法识别租户"}) platform = request.data.get("platform", "douyin") event_type = request.data.get("event_type", "order.paid") payload = request.data.get("payload", {}) event = await sync_to_async(channel_services.handle_inbound_webhook)( tenant, platform=platform, event_type=event_type, payload=payload ) return Response({"ok": True, "event_id": event.id, "status": event.status})