Feat: [#88] 24시간 내 조회수 중복 방지

This commit is contained in:
sm4640
2025-08-21 00:31:04 +09:00
parent b5476d9cb0
commit 3152346ec7
2 changed files with 34 additions and 3 deletions

View File

@@ -7,6 +7,11 @@ from portfolios.models import Portfolio
from projects.serializers import ProjectNocodetoolSerializer from projects.serializers import ProjectNocodetoolSerializer
from portfolios.serializers import PortfolioNocodetoolSerializer from portfolios.serializers import PortfolioNocodetoolSerializer
from django.utils import timezone
from django.db.models import F
from django_redis import get_redis_connection
NOCODETOOL_MODEL_MAP = { NOCODETOOL_MODEL_MAP = {
'project': Project, 'project': Project,
'portfolio': Portfolio, 'portfolio': Portfolio,
@@ -27,3 +32,26 @@ class NocodetoolObjectMapService:
def mapping_model_serializer(related_type: str): def mapping_model_serializer(related_type: str):
return NOCODETOOL_SERIALIZER_MAP.get(related_type, None) return NOCODETOOL_SERIALIZER_MAP.get(related_type, None)
class NocodetoolHitService:
def hit_once(obj, request, ttl=60*60*24):
user_key = request.user.id if request.user.is_authenticated else request.session.session_key
if not user_key:
request.session.save()
user_key = request.session.session_key()
today = timezone.localdate().isoformat()
key = f"viewed:obj:{obj.id}:{today}"
redis_conn = get_redis_connection("default")
added = redis_conn.sadd(key, user_key)
if added:
if redis_conn.ttl(key) == -1:
redis_conn.expire(key, ttl)
obj.__class__.objects.filter(id=obj.id).update(view_count=F("view_count") + 1)
return True
return False

View File

@@ -7,8 +7,8 @@ from django.db import transaction
from .models import Code, Page, Element from .models import Code, Page, Element
from .serializers import CodeSerializer from .serializers import CodeSerializer
from .permissions import IsOwnerOrMemberInCreateAndUpdateAndDelete from .permissions import IsOwnerOrMemberInCreateAndUpdateAndDelete, IsNotPublished
from .services import NocodetoolObjectMapService from .services import NocodetoolObjectMapService, NocodetoolHitService
from users.models import User from users.models import User
from portfolios.models import Portfolio from portfolios.models import Portfolio
@@ -18,13 +18,16 @@ from bson import ObjectId
class NoCodeToolAPIView(APIView): class NoCodeToolAPIView(APIView):
permission_classes = [IsAuthenticated, IsOwnerOrMemberInCreateAndUpdateAndDelete] permission_classes = [IsAuthenticated, IsNotPublished, IsOwnerOrMemberInCreateAndUpdateAndDelete]
@transaction.atomic
def get(self, request): def get(self, request):
related_type = request.query_params.get("type") related_type = request.query_params.get("type")
related_id = request.query_params.get("id") related_id = request.query_params.get("id")
code_id = None code_id = None
if obj := NocodetoolObjectMapService.mapping_model_instance(related_type, related_id): if obj := NocodetoolObjectMapService.mapping_model_instance(related_type, related_id):
if obj.is_published:
NocodetoolHitService.hit_once(obj, request)
code_id = ObjectId(obj.code_id) code_id = ObjectId(obj.code_id)
if not code_id: if not code_id: