Skip to content

Commit

Permalink
feat [#111] 타임테이블에 페스티벌 추가 API 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
ch1hyun committed Jan 18, 2025
1 parent 4a09cb8 commit 662d655
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.sopt.confeti.api.user.dto.request.AddTimetableFestivalRequest;
import org.sopt.confeti.api.user.dto.response.UserTimetableDetailResponse;
import org.sopt.confeti.api.user.facade.UserTimetableFacade;
import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalDTO;
import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO;
import org.sopt.confeti.global.common.BaseResponse;
import org.sopt.confeti.global.message.SuccessMessage;
Expand All @@ -14,6 +16,8 @@
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -32,6 +36,15 @@ public ResponseEntity<BaseResponse<?>> getTimetablesListAndDate(@RequestHeader("
return ApiResponseUtil.success(SuccessMessage.SUCCESS, UserTimetableDetailResponse.of(userTimetableDTO, s3FileHandler));
}

@PostMapping
public ResponseEntity<BaseResponse<?>> addTimetableFestival(
@RequestHeader("Authorization") long userId,
@RequestBody AddTimetableFestivalRequest addTimetableFestivalRequest
) {
userTimetableFacade.addTimetableFestivals(userId, AddTimetableFestivalDTO.from(addTimetableFestivalRequest));
return ApiResponseUtil.success(SuccessMessage.SUCCESS);
}

@DeleteMapping("/{festivalId}")
public ResponseEntity<BaseResponse<?>> removeTimetableFestival(
@RequestHeader("Authorization") long userId,
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package org.sopt.confeti.api.user.facade;

import java.util.function.Predicate;
import lombok.RequiredArgsConstructor;
import org.sopt.confeti.annotation.Facade;
import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalArtiestDTO;
import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalDTO;
import org.sopt.confeti.api.user.facade.dto.response.UserTimetableDTO;
import org.sopt.confeti.domain.festival.Festival;
import org.sopt.confeti.domain.festival.application.FestivalService;
import org.sopt.confeti.domain.timetablefestival.TimetableFestival;
import org.sopt.confeti.domain.timetablefestival.application.TimetableFestivalService;
import org.sopt.confeti.domain.user.User;
import org.sopt.confeti.domain.user.application.UserService;
import org.sopt.confeti.global.exception.ConflictException;
import org.sopt.confeti.global.exception.NotFoundException;
import org.sopt.confeti.global.exception.UnauthorizedException;
import org.sopt.confeti.global.message.ErrorMessage;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -17,31 +24,85 @@
@RequiredArgsConstructor
public class UserTimetableFacade {

private static final int TIMETABLE_FESTIVAL_COUNT_MAXIMUM = 3;

private final UserService userService;
private final TimetableFestivalService timetableFestivalService;
private final FestivalService festivalService;

@Transactional(readOnly = true)
public UserTimetableDTO getTimetablesListAndDate(long userId) {
if (!userService.existsById(userId)) {
throw new NotFoundException(ErrorMessage.NOT_FOUND);
}
validateExistUser(userId);

List<TimetableFestival> festivalList = timetableFestivalService.getFetivalList(userId);
return UserTimetableDTO.from(festivalList);
}

@Transactional
public void removeTimetableFestival(final long userId, final long festivalId) {
validateExistUser(userId);
validateExistFestival(festivalId);
validateExistTimetableFestival(userId, festivalId);

timetableFestivalService.removeTimetableFestival(userId, festivalId);
}

@Transactional
public void addTimetableFestivals(final long userId, final AddTimetableFestivalDTO from) {
User user = userService.findById(userId);
List<Festival> currentFestivals = timetableFestivalService.findByUserId(userId).stream()
.map(TimetableFestival::getFestival)
.toList();
List<Festival> addFestivals = festivalService.findByIdIn(
from.festivals().stream()
.distinct()
.map(AddTimetableFestivalArtiestDTO::festivalId)
.toList()
);

validateDuplicateTimetableFestival(currentFestivals, addFestivals);
validateCountTimetableFestival(currentFestivals.size(), addFestivals.size());

timetableFestivalService.addTimetableFestivals(user, addFestivals);
}

@Transactional
protected void validateDuplicateTimetableFestival(final List<Festival> currentFestivals, final List<Festival> addFestivals) {
if (
!userService.existsById(userId) ||
!festivalService.existsById(festivalId) ||
!timetableFestivalService.existsByUserIdAndFestivalId(userId, festivalId)
currentFestivals.stream()
.anyMatch(currentFestival -> addFestivals.stream()
.anyMatch(Predicate.isEqual(currentFestival)))
) {
throw new ConflictException(ErrorMessage.CONFLICT);
}
}

@Transactional(readOnly = true)
protected void validateExistUser(final long userId) {
if (!userService.existsById(userId)) {
throw new UnauthorizedException(ErrorMessage.UNAUTHORIZED);
}
}

@Transactional(readOnly = true)
protected void validateExistFestival(final long festivalId) {
if (!festivalService.existsById(festivalId)) {
throw new NotFoundException(ErrorMessage.NOT_FOUND);
}
}

@Transactional(readOnly = true)
protected void validateCountTimetableFestival(final long currentCount, final int addCount) {
if (currentCount + addCount > TIMETABLE_FESTIVAL_COUNT_MAXIMUM) {
throw new ConflictException(ErrorMessage.CONFLICT);
}
}

timetableFestivalService.removeTimetableFestival(userId, festivalId);
@Transactional(readOnly = true)
protected void validateExistTimetableFestival(final long userId, final long festivalId) {
if (!timetableFestivalService.existsByUserIdAndFestivalId(userId, festivalId)) {
throw new NotFoundException(ErrorMessage.NOT_FOUND);
}
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.sopt.confeti.domain.festival.application;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.sopt.confeti.api.performance.facade.dto.request.CreateFestivalDTO;
import org.sopt.confeti.domain.festival.Festival;
Expand All @@ -8,6 +9,7 @@
import org.sopt.confeti.global.message.ErrorMessage;
import org.sopt.confeti.global.util.artistsearcher.ArtistResolver;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
Expand All @@ -16,12 +18,14 @@ public class FestivalService {
private final FestivalRepository festivalRepository;
private final ArtistResolver artistResolver;

@Transactional(readOnly = true)
public Festival findById(Long festivalId) {
Festival festival = festivalRepository.findById(festivalId)
.orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND));
return festival;
}

@Transactional(readOnly = true)
public Festival getFestivalDetailByFestivalId(final long festivalId) {
Festival festival = festivalRepository.findById(festivalId)
.orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND));
Expand All @@ -30,13 +34,20 @@ public Festival getFestivalDetailByFestivalId(final long festivalId) {
return festival;
}

@Transactional
public void create(final CreateFestivalDTO createFestivalDTO) {
festivalRepository.save(
Festival.create(createFestivalDTO)
);
}

@Transactional(readOnly = true)
public boolean existsById(final long festivalId) {
return festivalRepository.existsById(festivalId);
}

@Transactional
public List<Festival> findByIdIn(final List<Long> festivalIds) {
return festivalRepository.findByIdIn(festivalIds);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.sopt.confeti.domain.festival.infra.repository;

import java.util.List;
import org.sopt.confeti.domain.festival.Festival;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FestivalRepository extends JpaRepository<Festival, Long> {
List<Festival> findByIdIn(final List<Long> festivalIds);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.sopt.confeti.domain.timetablefestival.application;

import lombok.AllArgsConstructor;
import org.sopt.confeti.api.user.facade.dto.request.AddTimetableFestivalDTO;
import org.sopt.confeti.domain.festival.Festival;
import org.sopt.confeti.domain.timetablefestival.TimetableFestival;
import org.sopt.confeti.domain.timetablefestival.infra.repository.TimetableFestivalRepository;
import org.sopt.confeti.domain.user.User;
import org.springframework.stereotype.Service;

import java.util.List;
Expand All @@ -27,4 +30,23 @@ public boolean existsByUserIdAndFestivalId(final long userId, final long festiva
public void removeTimetableFestival(final long userId, final long festivalId) {
timetableFestivalRepository.deleteByUserIdAndFestivalId(userId, festivalId);
}

@Transactional(readOnly = true)
public int countByUserId(final long userId) {
return timetableFestivalRepository.countByUserId(userId);
}

@Transactional
public void addTimetableFestivals(final User user, final List<Festival> festivals) {
timetableFestivalRepository.saveAll(
festivals.stream()
.map(festival -> TimetableFestival.create(user, festival))
.toList()
);
}

@Transactional(readOnly = true)
public List<TimetableFestival> findByUserId(final long userId) {
return timetableFestivalRepository.findByUserId(userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface TimetableFestivalRepository extends JpaRepository<TimetableFest
boolean existsByUserIdAndFestivalId(final long userId, final long festivalId);

void deleteByUserIdAndFestivalId(final long userId, final long festivalId);

int countByUserId(final long userId);
}

0 comments on commit 662d655

Please sign in to comment.