feat: ADRF async views (phase1) + native async serializers (phase2) + async cache infra

This commit is contained in:
async-upgrade
2026-09-06 14:26:17 +08:00
parent 9a6577f71e
commit 8f488fcaaa
55 changed files with 2224 additions and 1513 deletions
+73 -61
View File
@@ -1,6 +1,7 @@
from rest_framework import status
from rest_framework.views import APIView
from adrf.views import APIView
from rest_framework.permissions import IsAuthenticated
from asgiref.sync import sync_to_async
from django.db import transaction
from django.db.models import F
from django.utils import timezone
@@ -121,7 +122,7 @@ class TaskListAPIView(APIView):
operation_description='获取所有活跃任务列表及当前用户的进度和等级信息',
responses={200: success_response, 401: unauthorized_response, 500: error_response},
)
def get(self, request):
async def get(self, request):
try:
user = request.user
now = timezone.localtime(timezone.now())
@@ -131,7 +132,7 @@ class TaskListAPIView(APIView):
tasks = TaskDefinition.objects.filter(is_active=True)
task_list = []
for task in tasks:
async for task in tasks:
if task.task_type == 'daily':
period_key = daily_period
elif task.task_type == 'weekly':
@@ -139,9 +140,9 @@ class TaskListAPIView(APIView):
else:
period_key = 'permanent'
progress = UserTaskProgress.objects.filter(
progress = await UserTaskProgress.objects.filter(
user=user, task=task, period_key=period_key
).first()
).afirst()
task_list.append({
'id': task.id,
@@ -160,15 +161,19 @@ class TaskListAPIView(APIView):
'is_claimed': progress.is_claimed if progress else False,
})
user_level = UserLevel.objects.filter(user=user).first()
next_threshold = LevelThreshold.objects.filter(
user_level = await UserLevel.objects.filter(user=user).afirst()
next_threshold = await LevelThreshold.objects.filter(
level__gt=user_level.level if user_level else 1
).order_by('level').first() if user_level else None
).order_by('level').afirst() if user_level else None
cur_threshold = await LevelThreshold.objects.filter(
level=user_level.level
).afirst() if user_level else None
level_data = {
'level': user_level.level if user_level else 1,
'xp': user_level.xp if user_level else 0,
'title': (LevelThreshold.objects.filter(level=user_level.level).first().title if user_level and LevelThreshold.objects.filter(level=user_level.level).exists() else '新手'),
'title': cur_threshold.title if cur_threshold else '新手',
'next_level_xp': next_threshold.xp_required if next_threshold else None,
'next_level_title': next_threshold.title if next_threshold else None,
}
@@ -208,7 +213,7 @@ class TaskTrackAPIView(APIView):
),
responses={200: success_response, 400: error_response, 401: unauthorized_response, 500: error_response},
)
def post(self, request):
async def post(self, request):
action_type = request.data.get('action_type')
count = int(request.data.get('count', 1))
@@ -228,7 +233,8 @@ class TaskTrackAPIView(APIView):
)
try:
updated_tasks = track_user_action(request.user, action_type, count)
# track_user_action 为同步 helper(user.py 也以 sync_to_async 调用),线程池兜底
updated_tasks = await sync_to_async(track_user_action)(request.user, action_type, count)
return create_standardized_response(
data={'updated_tasks': updated_tasks},
code=ResponseCode.SUCCESS,
@@ -256,10 +262,10 @@ class TaskClaimAPIView(APIView):
],
responses={200: success_response, 400: error_response, 401: unauthorized_response, 500: error_response},
)
def post(self, request, task_id):
async def post(self, request, task_id):
try:
user = request.user
task = TaskDefinition.objects.filter(id=task_id, is_active=True).first()
task = await TaskDefinition.objects.filter(id=task_id, is_active=True).afirst()
if not task:
return create_standardized_error_response(
@@ -276,9 +282,9 @@ class TaskClaimAPIView(APIView):
else:
period_key = 'permanent'
progress = UserTaskProgress.objects.filter(
progress = await UserTaskProgress.objects.filter(
user=user, task=task, period_key=period_key
).first()
).afirst()
if not progress or not progress.is_completed:
return create_standardized_error_response(
@@ -294,53 +300,59 @@ class TaskClaimAPIView(APIView):
status_code=status.HTTP_400_BAD_REQUEST
)
with transaction.atomic():
locked_user = FUser.objects.select_for_update().get(pk=user.pk)
user_level, _ = UserLevel.objects.select_for_update().get_or_create(
user=locked_user,
defaults={'xp': 0, 'level': 1}
)
if task.reward_points > 0:
locked_user.points = F('points') + task.reward_points
locked_user.save(update_fields=['points'])
locked_user.refresh_from_db()
PointTransaction.objects.create(
# select_for_update + 事务 + refresh_from_db 整体在同步函数内执行,线程池兜底
def _claim_reward():
with transaction.atomic():
locked_user = FUser.objects.select_for_update().get(pk=user.pk)
user_level, _ = UserLevel.objects.select_for_update().get_or_create(
user=locked_user,
transaction_type='earn',
currency_type='points',
amount=task.reward_points,
balance_after=locked_user.points,
description=f'完成任务: {task.name}',
defaults={'xp': 0, 'level': 1}
)
if task.reward_coins > 0:
locked_user.coins = F('coins') + task.reward_coins
locked_user.save(update_fields=['coins'])
locked_user.refresh_from_db()
PointTransaction.objects.create(
user=locked_user,
transaction_type='earn',
currency_type='coins',
amount=task.reward_coins,
balance_after=locked_user.coins,
description=f'完成任务: {task.name}',
)
if task.reward_points > 0:
locked_user.points = F('points') + task.reward_points
locked_user.save(update_fields=['points'])
locked_user.refresh_from_db()
PointTransaction.objects.create(
user=locked_user,
transaction_type='earn',
currency_type='points',
amount=task.reward_points,
balance_after=locked_user.points,
description=f'完成任务: {task.name}',
)
if task.reward_coins > 0:
locked_user.coins = F('coins') + task.reward_coins
locked_user.save(update_fields=['coins'])
locked_user.refresh_from_db()
PointTransaction.objects.create(
user=locked_user,
transaction_type='earn',
currency_type='coins',
amount=task.reward_coins,
balance_after=locked_user.coins,
description=f'完成任务: {task.name}',
)
if task.reward_xp > 0:
user_level.xp = F('xp') + task.reward_xp
user_level.save(update_fields=['xp', 'updated_at'])
user_level.refresh_from_db()
check_level_up(user_level)
progress.is_claimed = True
progress.claimed_at = now
progress.save(update_fields=['is_claimed', 'claimed_at'])
if task.reward_xp > 0:
user_level.xp = F('xp') + task.reward_xp
user_level.save(update_fields=['xp', 'updated_at'])
user_level.refresh_from_db()
check_level_up(user_level)
next_threshold = LevelThreshold.objects.filter(
level__gt=user_level.level
).order_by('level').first()
progress.is_claimed = True
progress.claimed_at = now
progress.save(update_fields=['is_claimed', 'claimed_at'])
return locked_user, user_level, next_threshold
user_level.refresh_from_db()
next_threshold = LevelThreshold.objects.filter(
level__gt=user_level.level
).order_by('level').first()
locked_user, user_level, next_threshold = await sync_to_async(_claim_reward)()
return create_standardized_response(
data={
@@ -375,19 +387,19 @@ class UserLevelAPIView(APIView):
operation_description='获取当前用户的等级、经验值、等级称号和升级进度',
responses={200: success_response, 401: unauthorized_response, 500: error_response},
)
def get(self, request):
async def get(self, request):
try:
user = request.user
user_level, _ = UserLevel.objects.get_or_create(
user_level, _ = await UserLevel.objects.aget_or_create(
user=user, defaults={'xp': 0, 'level': 1}
)
current_threshold = LevelThreshold.objects.filter(
current_threshold = await LevelThreshold.objects.filter(
level=user_level.level
).first()
next_threshold = LevelThreshold.objects.filter(
).afirst()
next_threshold = await LevelThreshold.objects.filter(
level__gt=user_level.level
).order_by('level').first()
).order_by('level').afirst()
current_xp = current_threshold.xp_required if current_threshold else 0
next_xp = next_threshold.xp_required if next_threshold else user_level.xp