PortalActivityService.java

package cl.prgm.portaltrip.application.service;

import java.time.OffsetDateTime;
import java.util.UUID;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cl.prgm.portaltrip.domain.exception.ResourceNotFoundException;
import cl.prgm.portaltrip.infrastructure.persistence.PortalActivityEntity;
import cl.prgm.portaltrip.infrastructure.persistence.UserEntity;
import cl.prgm.portaltrip.infrastructure.persistence.repository.PortalActivityJpaRepository;
import cl.prgm.portaltrip.infrastructure.persistence.repository.UserJpaRepository;
import cl.prgm.portaltrip.infrastructure.web.dto.PortalActivityRequestDto;
import cl.prgm.portaltrip.infrastructure.web.dto.PortalActivityResponseDto;

@Service
public class PortalActivityService {
	private final UserJpaRepository users;
	private final PortalActivityJpaRepository activities;
	private final PortalStipendService stipends;

	public PortalActivityService(UserJpaRepository users, PortalActivityJpaRepository activities,
			PortalStipendService stipends) {
		this.users = users;
		this.activities = activities;
		this.stipends = stipends;
	}

	@Transactional
	public PortalActivityResponseDto start(UUID userId) {
		UserEntity user = lock(userId);
		OffsetDateTime now = OffsetDateTime.now();
		PortalActivityEntity activity = activities.findById(userId).orElseGet(
				() -> activities.save(PortalActivityEntity.start(userId, now)));
		if (activity.completed() || activity.expired(now)) activity.restart(now);
		return response(activity, user);
	}

	@Transactional
	public PortalActivityResponseDto report(UUID userId, PortalActivityRequestDto sample) {
		UserEntity user = lock(userId);
		PortalActivityEntity activity = activities.findById(userId)
				.orElseThrow(() -> new ResourceNotFoundException("Portal activity", userId));
		activity.sample(sample.cycleId(), sample.sequence(), sample.activeMs(), sample.distance(), OffsetDateTime.now());
		if (!activity.completed() && activity.progress() >= 1) {
			activity.complete(stipends.claim(userId, activity.movement(), activity.activeDurationMs()).payout());
		}
		return response(activity, user);
	}

	private UserEntity lock(UUID userId) {
		return users.findByIdForUpdate(userId).orElseThrow(() -> new ResourceNotFoundException("User", userId));
	}

	private PortalActivityResponseDto response(PortalActivityEntity activity, UserEntity user) {
		return new PortalActivityResponseDto(activity.cycleId(), activity.nextSequence(), activity.progress(),
				activity.payout(), user.toDomain().balance());
	}
}