from django.db import transaction from django.utils import timezone from rest_framework.exceptions import ValidationError from progression.models import RewardTransaction, UserAbility from .models import ContentInteraction, ContentItem, VideoProgress ABILITY_TO_DIMENSION = { ContentItem.Ability.VISION: UserAbility.Dimension.VISION, ContentItem.Ability.HUMANITIES: UserAbility.Dimension.HUMANITIES, ContentItem.Ability.DETECTION: UserAbility.Dimension.DETECTION, ContentItem.Ability.MODELING: UserAbility.Dimension.MODELING, ContentItem.Ability.CONNECTION: UserAbility.Dimension.CONNECTION, } @transaction.atomic def complete_video(user, content): if content.kind != ContentItem.Kind.VIDEO: raise ValidationError("只有视频内容可以提交观看完成") progress, _ = VideoProgress.objects.select_for_update().get_or_create( user=user, content=content, ) progress.position_seconds = max(progress.position_seconds, content.duration_seconds) progress.completed = True progress.completed_at = progress.completed_at or timezone.now() reward = None dimension = ABILITY_TO_DIMENSION.get(content.ability_dimension) if dimension and not progress.reward_granted: transaction_key = f"video-complete:{content.id}" reward_tx, created = RewardTransaction.objects.get_or_create( user=user, idempotency_key=transaction_key, defaults={ "source": "video_complete", "rewards": {"ability": dimension, "fragments": 1}, }, ) if created: ability, _ = UserAbility.objects.select_for_update().get_or_create( user=user, dimension=dimension, ) ability.fragments += 1 ability.save(update_fields=["fragments"]) progress.reward_granted = True reward = reward_tx.rewards progress.save( update_fields=[ "position_seconds", "completed", "completed_at", "reward_granted", "updated_at", ] ) ContentInteraction.objects.get_or_create( user=user, content=content, action=ContentInteraction.Action.COMPLETE, ) return progress, reward