Files
2026-09-04 13:05:54 +03:30

130 lines
5.6 KiB
Python

from django.db.models import Count
from rest_framework import status
from rest_framework.exceptions import NotFound, ValidationError
from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from apps.banker.models import ContactGroup
from apps.billing.permissions import HasActiveFeature
from apps.messaging.models import BulkMessage, MessageRecipient
from apps.billing.forms import normalize_iran_phone
from .serializers import (
BulkMessageSerializer,
ContactMessageHistorySerializer,
SendBulkMessageSerializer,
)
from .services import BulkMessageService
class SendBulkMessageView(APIView):
"""POST: می‌سازد و بلافاصله dispatch می‌کند (پیامک/کانال‌های Business-API-دار
خودکار، بقیه needs_manual برای صف deep-link سمت اپ)."""
permission_classes = [IsAuthenticated, HasActiveFeature]
feature_key = "sms_bank"
throttle_scope = "messaging_send"
def post(self, request):
serializer = SendBulkMessageSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
if data["group_id"] and not ContactGroup.objects.filter(user=request.user, id=data["group_id"]).exists():
raise ValidationError({"group_id": "گروه یافت نشد."})
bulk = BulkMessageService.create_and_dispatch(
user=request.user,
channels=data["channels"],
group_id=data["group_id"],
contact_ids=data["contact_ids"],
template_id=data["template_id"],
body_override=data["body_override"],
)
return Response(BulkMessageSerializer(bulk).data, status=status.HTTP_201_CREATED)
class BulkMessageDetailView(RetrieveAPIView):
serializer_class = BulkMessageSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return BulkMessage.objects.filter(user=self.request.user)
class MessagingStatsView(APIView):
"""آمار ارسال: چند پیام موفق به گیت‌وی/API فرستاده شده، چند تا ناموفق
بوده، چند تا نیاز به تایید دستی داشته/دارد — بر اساس MessageRecipient
(نه رسید تحویل واقعی گوشی، چون آن نیاز به webhook از سرویس‌دهنده‌ی
پیامک دارد که در این نسخه وصل نشده؛ «ارسال‌شده» یعنی سرور با موفقیت آن
را به API/گیت‌وی تحویل داده)."""
permission_classes = [IsAuthenticated, HasActiveFeature]
feature_key = "sms_bank"
def get(self, request):
rows = (
MessageRecipient.objects.filter(bulk_message__user=request.user)
.values("status")
.annotate(count=Count("id"))
)
counts = {row["status"]: row["count"] for row in rows}
total = sum(counts.values())
return Response(
{
"total": total,
"sent": counts.get(MessageRecipient.Status.SENT, 0),
"failed": counts.get(MessageRecipient.Status.FAILED, 0),
"needs_manual": counts.get(MessageRecipient.Status.NEEDS_MANUAL, 0),
"manual_confirmed": counts.get(MessageRecipient.Status.MANUAL_CONFIRMED, 0),
}
)
class ConfirmManualSendView(APIView):
"""اپ بعد از این‌که کاربر دستی در واتساپ/تلگرام/بله/ایتا زد «ارسال»، این را صدا می‌زند."""
permission_classes = [IsAuthenticated, HasActiveFeature]
feature_key = "sms_bank"
def post(self, request, bulk_id, recipient_id):
try:
recipient = BulkMessageService.confirm_manual(request.user, bulk_id, recipient_id)
except MessageRecipient.DoesNotExist:
raise NotFound("گیرنده یافت نشد.")
return Response({"success": True, "status": recipient.status})
class ContactMessageHistoryView(APIView):
"""تاریخچه‌ی پیام‌های فرستاده‌شده به یک شماره.
چرا بر اساس *شماره* و نه شناسه‌ی مخاطب: صفحه‌ی «مشتریان» و «مخاطبین»
دو موجودیتِ جدا هستند و کاربر انتظار دارد روی پروفایلِ مشتری هم ببیند
چه پیامی برایش رفته. شماره تنها چیزی است که این دو را به هم وصل
می‌کند، پس با شماره‌ی نرمال‌شده جستجو می‌کنیم.
"""
permission_classes = [IsAuthenticated]
def get(self, request):
raw = request.query_params.get("phone", "") or ""
phone = normalize_iran_phone(raw)
if not phone:
raise ValidationError({"phone": "شماره لازم است."})
# ارقامِ ذخیره‌شده ممکن است با ۰ یا +۹۸ باشند؛ روی چند شکل تطبیق
# می‌دهیم تا تاریخچه به‌خاطر تفاوتِ قالبِ شماره گم نشود.
variants = {phone, phone.lstrip("0"), "+98" + phone.lstrip("0"), "98" + phone.lstrip("0")}
rows = (
MessageRecipient.objects.filter(
bulk_message__user=request.user,
contact__phone__in=variants,
)
.select_related("bulk_message")
.order_by("-bulk_message__created_at", "-id")[:100]
)
return Response(ContactMessageHistorySerializer(rows, many=True).data)