Files
OnlineJudge/reaction/views/oj.py
2026-08-06 06:15:48 -06:00

69 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.db.models import Count, Q
from account.decorators import login_required
from problem.models import Problem
from reaction.models import Reaction, ReactionType
from reaction.serializers import SetReactionSerializer
from submission.models import JudgeStatus, Submission
from utils.api import AsyncAPIView
from utils.api.api import validate_serializer
from utils.async_helpers import async_cache_delete, async_cache_get, async_cache_set
from utils.constants import CacheKey
ACCEPTED_RESULTS = [JudgeStatus.ACCEPTED, JudgeStatus.AST_CHECK_FAILED]
class ReactionAPI(AsyncAPIView):
async def get_counts(self, problem_id):
"""返回该题七个表情的计数,带 Redis 缓存。"""
cache_key = f"{CacheKey.reaction_stats}:{problem_id}"
cached = await async_cache_get(cache_key)
if cached is not None:
return cached
counts = await Reaction.objects.filter(problem_id=problem_id).aaggregate(**{t.value: Count("id", filter=Q(type=t.value)) for t in ReactionType})
await async_cache_set(cache_key, counts, 3600)
return counts
@login_required
async def get(self, request):
problem_id = request.GET.get("problem_id")
if not problem_id:
return self.error("problem_id is required")
mine = [r.type async for r in Reaction.objects.filter(user=request.user, problem_id=problem_id)]
if not mine:
return self.success({"mine": [], "counts": None})
return self.success({"mine": mine, "counts": await self.get_counts(problem_id)})
@login_required
@validate_serializer(SetReactionSerializer)
async def post(self, request):
data = request.data
try:
problem = await Problem.objects.aget(id=data["problem_id"], visible=True)
except Problem.DoesNotExist:
return self.error("problem is not exists")
solved = await Submission.objects.filter(
user_id=request.user.id,
problem_id=problem.id,
result__in=ACCEPTED_RESULTS,
).aexists()
if not solved:
return self.error("submission is not exists or not accepted")
types = data["types"]
user = request.user
# 评价一次定终身:已经评过的题不允许再改,避免统计被反复刷
if await Reaction.objects.filter(user=user, problem=problem).aexists():
return self.error("已经评价过了,不能修改")
# ignore_conflicts 兜住并发重复提交unique_together 保证不会写重
await Reaction.objects.abulk_create(
[Reaction(user=user, problem=problem, type=t) for t in types],
ignore_conflicts=True,
)
await async_cache_delete(f"{CacheKey.reaction_stats}:{problem.id}")
return self.success({"mine": types, "counts": await self.get_counts(problem.id)})