feat: complete realtime challenge matchmaking

This commit is contained in:
2026-08-09 03:07:38 +08:00
parent b6fc2431ca
commit 821311f4ad
11 changed files with 767 additions and 31 deletions
+31 -3
View File
@@ -6,8 +6,12 @@ from rest_framework.views import APIView
from .game_services import game_payload, request_sudoku_hint, start_game, submit_game
from .models import Contest, ContestAttempt, MathGameAttempt, RealtimeMatch
from .services import (
cancel_waiting_match,
create_challenge,
find_match,
join_challenge,
match_payload,
refresh_match_state,
start_attempt,
submit_attempt,
)
@@ -44,13 +48,16 @@ class AttemptStartView(APIView):
class AttemptSubmitView(APIView):
def post(self, request, attempt_id):
get_object_or_404(ContestAttempt, id=attempt_id, user=request.user)
attempt = get_object_or_404(ContestAttempt, id=attempt_id, user=request.user)
payload = submit_attempt(
user=request.user,
attempt_id=attempt_id,
raw_answers=request.data.get("answers", []),
submission_key=request.headers.get("Idempotency-Key"),
)
if attempt.match_id:
match = refresh_match_state(attempt.match_id)
return Response(match_payload(match, request.user))
return Response(payload)
@@ -63,12 +70,33 @@ class MatchmakingView(APIView):
class MatchStateView(APIView):
def get(self, request, match_id):
match = get_object_or_404(
existing = get_object_or_404(
RealtimeMatch.objects.select_related("player_one", "player_two"),
id=match_id,
)
if request.user.id not in (match.player_one_id, match.player_two_id):
if request.user.id not in (existing.player_one_id, existing.player_two_id):
return Response(status=status.HTTP_403_FORBIDDEN)
match = refresh_match_state(match_id)
return Response(match_payload(match, request.user))
class ChallengeCreateView(APIView):
def post(self, request, slug):
contest = get_object_or_404(Contest, slug=slug)
match = create_challenge(request.user, contest)
return Response(match_payload(match, request.user), status=status.HTTP_201_CREATED)
class ChallengeJoinView(APIView):
def post(self, request):
match = join_challenge(request.user, request.data.get("challenge_code"))
return Response(match_payload(match, request.user))
class MatchCancelView(APIView):
def post(self, request, match_id):
get_object_or_404(RealtimeMatch, id=match_id)
match = cancel_waiting_match(request.user, match_id)
return Response(match_payload(match, request.user))