87 lines
3.6 KiB
Python
87 lines
3.6 KiB
Python
"""全渠道与电商 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})
|