feat: add products/cases page models and forms lead-capture app

- apps/products: ProductIndexPage/ProductPage
- apps/cases: CaseStudyIndexPage/CaseStudyPage
- apps/forms: FormDefinition/FormDefinitionField/Lead + submit API
- core/blocks: CaseStudyBlock, FormBlock (with API representation)
- register new apps, custom leads API route, migrations
This commit is contained in:
2026-08-06 16:04:21 +08:00
parent 7f51a31150
commit 12f7caa158
25 changed files with 618 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
"""
线索提交接口。挂载在 /api/v1/custom/leads/(详见 apps/forms/urls.py 与
documents/设计方案分析与完善版.md §2.7 自定义业务接口设计)。
公开表单提交接口,限流走 DRF ScopedRateThrottle 的 "forms" scope5/min,防刷)。
"""
import json
from django.conf import settings
from django.core.mail import send_mail
from rest_framework.response import Response
from rest_framework.throttling import ScopedRateThrottle
from rest_framework.views import APIView
from .models import FormDefinition, Lead
from .serializers import LeadSubmitSerializer
class LeadSubmitView(APIView):
throttle_classes = [ScopedRateThrottle]
throttle_scope = "forms"
def post(self, request, *args, **kwargs):
serializer = LeadSubmitSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
form_id = serializer.validated_data["form_id"]
try:
form_def = FormDefinition.objects.get(pk=form_id)
except FormDefinition.DoesNotExist:
return Response(
{"error": {"code": "NOT_FOUND", "message": "表单不存在"}}, status=404
)
lead = Lead.objects.create(
form=form_def,
data=serializer.validated_data["data"],
source_url=serializer.validated_data.get("source_url", ""),
)
if form_def.notification_email:
send_mail(
subject=f"【新线索】{form_def.name}",
message=json.dumps(lead.data, ensure_ascii=False, indent=2),
from_email=getattr(settings, "DEFAULT_FROM_EMAIL", None),
recipient_list=[form_def.notification_email],
fail_silently=True,
)
return Response(
{"success": True, "message": form_def.success_message}, status=201
)