baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""消息通知与预警 ViewSets:
|
||||
1. NotificationViewSet: 查询、标记已读、全部已读
|
||||
2. AlertRuleViewSet: 预警规则维护、触发全量检测
|
||||
"""
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from asgiref.sync import sync_to_async
|
||||
|
||||
from apps.core.viewset import BaseTenantViewSet
|
||||
from .models import Notification, AlertRule
|
||||
from .serializers import NotificationSerializer, AlertRuleSerializer
|
||||
from . import services as notify_services
|
||||
|
||||
|
||||
class NotificationViewSet(BaseTenantViewSet):
|
||||
model = Notification
|
||||
serializer_class = NotificationSerializer
|
||||
search_fields = ["title", "content"]
|
||||
|
||||
async def get_queryset(self):
|
||||
qs = await super().get_queryset()
|
||||
# 仅查全员广播或当前用户的通知
|
||||
user = self.request.user
|
||||
if user.is_authenticated:
|
||||
qs = qs.filter(Q(recipient__isnull=True) | Q(recipient=user))
|
||||
else:
|
||||
# Keep the queryset safe even if this view is reused without the
|
||||
# default IsAuthenticated permission.
|
||||
qs = qs.none()
|
||||
return qs
|
||||
|
||||
@action(detail=True, methods=["post"], url_path="mark-read")
|
||||
async def mark_read(self, request, pk=None):
|
||||
"""标为已读。"""
|
||||
# Use the same tenant and recipient scope as list/retrieve. This
|
||||
# makes another user's notification indistinguishable from a missing
|
||||
# notification instead of allowing its state to be changed.
|
||||
queryset = await self.get_queryset()
|
||||
try:
|
||||
notification = await sync_to_async(queryset.get)(pk=pk)
|
||||
except self.model.DoesNotExist:
|
||||
return Response({"detail": "通知不存在"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
notification.is_read = True
|
||||
notification.read_at = timezone.now()
|
||||
await sync_to_async(notification.save)(update_fields=["is_read", "read_at"])
|
||||
return Response({"ok": True, "id": notification.id, "is_read": True})
|
||||
|
||||
@action(detail=False, methods=["post"], url_path="mark-all-read")
|
||||
async def mark_all_read(self, request):
|
||||
"""全部标为已读。"""
|
||||
tenant = await self.get_tenant()
|
||||
if not tenant:
|
||||
raise ValidationError({"tenant": "无法识别租户"})
|
||||
|
||||
user = request.user
|
||||
if not user.is_authenticated:
|
||||
return Response({"ok": True, "updated_count": 0})
|
||||
updated = await sync_to_async(
|
||||
self.model.objects.filter(tenant=tenant, is_read=False)
|
||||
.filter(Q(recipient__isnull=True) | Q(recipient=user))
|
||||
.update
|
||||
)(is_read=True, read_at=timezone.now())
|
||||
return Response({"ok": True, "updated_count": updated})
|
||||
|
||||
|
||||
class AlertRuleViewSet(BaseTenantViewSet):
|
||||
model = AlertRule
|
||||
serializer_class = AlertRuleSerializer
|
||||
search_fields = ["code", "name"]
|
||||
|
||||
@action(detail=False, methods=["post"], url_path="init-default")
|
||||
async def init_default(self, request):
|
||||
"""初始化默认预警规则。"""
|
||||
tenant = await self.get_tenant()
|
||||
if not tenant:
|
||||
raise ValidationError({"tenant": "无法识别租户"})
|
||||
n = await sync_to_async(notify_services.init_default_alert_rules)(tenant)
|
||||
return Response({"ok": True, "created_count": n})
|
||||
|
||||
@action(detail=False, methods=["post"], url_path="run-checks")
|
||||
async def run_checks(self, request):
|
||||
"""主动触发全量业务智能预警扫描(库存低限、应收逾期)。"""
|
||||
tenant = await self.get_tenant()
|
||||
if not tenant:
|
||||
raise ValidationError({"tenant": "无法识别租户"})
|
||||
result = await sync_to_async(notify_services.run_all_alert_checks)(tenant)
|
||||
return Response({"ok": True, "result": result})
|
||||
Reference in New Issue
Block a user