ActivityPolicy.java

package qwerty.chaekit.service.group;

import jakarta.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import qwerty.chaekit.domain.group.activity.Activity;
import qwerty.chaekit.domain.group.activity.activitymember.ActivityMemberRepository;
import qwerty.chaekit.domain.group.activity.repository.ActivityRepository;
import qwerty.chaekit.domain.group.groupmember.GroupMemberRepository;
import qwerty.chaekit.domain.member.user.UserProfile;
import qwerty.chaekit.global.enums.ErrorCode;
import qwerty.chaekit.global.exception.BadRequestException;
import qwerty.chaekit.global.exception.ForbiddenException;
import qwerty.chaekit.service.ebook.EbookPolicy;

import java.time.LocalDate;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ActivityPolicy {
    private final ActivityRepository activityRepository;
    private final GroupMemberRepository groupMemberRepository;
    private final ActivityMemberRepository activityMemberRepository;
    private final EbookPolicy ebookPolicy;

    public void assertJoinable(UserProfile user, Activity activity) {
        groupMemberRepository.findByUserAndReadingGroupAndAcceptedTrue(user, activity.getGroup())
                .orElseThrow(() -> new BadRequestException(ErrorCode.GROUP_MEMBER_ONLY));

        if (activityMemberRepository.existsByUserAndActivity(user, activity)) {
            throw new BadRequestException(ErrorCode.ACTIVITY_ALREADY_JOINED);
        }

        if (activity.isEnded()) {
            throw new BadRequestException(ErrorCode.ACTIVITY_ALREADY_ENDED);
        }

        ebookPolicy.assertEBookPurchased(user, activity.getBook());
    }

    public void assertActivityPeriodValid(long groupId, @Nullable Long activityId, LocalDate startTime, LocalDate endTime) {
        if (startTime.isAfter(endTime)) {
            throw new BadRequestException(ErrorCode.ACTIVITY_TIME_INVALID);
        }

        activityRepository.findByGroup_Id(groupId).stream()
                .filter(a -> !a.getId().equals(activityId))
                .forEach(a -> {
                    boolean isBefore = endTime.isBefore(a.getStartTime());
                    boolean isAfter = startTime.isAfter(a.getEndTime());
                    if (!(isBefore || isAfter)) {
                        throw new BadRequestException(ErrorCode.ACTIVITY_TIME_CONFLICT);
                    }
                });
    }
    
    public void assertJoined(UserProfile user, Activity activity) {
        if (user.getId() == null || !activityMemberRepository.existsByUserAndActivity(user, activity)) {
            throw new ForbiddenException(ErrorCode.ACTIVITY_MEMBER_ONLY);
        }
    }

    public void assertJoined(Long userId, Long activityId) {
        UserProfile user = UserProfile.builder().id(userId).build();
        Activity activity = Activity.builder().id(activityId).build();
        assertJoined(user, activity);
    }
    
}