-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor/travel #224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor/travel #224
Changes from all commits
0af8a93
0b9dd66
01f5027
022813a
3d4d2cc
6c5d195
4f56440
0d9ca7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| package org.triple.backend.travel.application; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.checkerframework.checker.nullness.qual.MonotonicNonNull; | ||
| import org.springframework.dao.DataIntegrityViolationException; | ||
| import org.springframework.dao.OptimisticLockingFailureException; | ||
| import org.springframework.stereotype.Service; | ||
| import org.triple.backend.global.error.BusinessException; | ||
| import org.triple.backend.group.domain.group.Group; | ||
| import org.triple.backend.travel.entity.TravelItinerary; | ||
| import org.triple.backend.travel.entity.UserRole; | ||
| import org.triple.backend.travel.entity.UserTravelItinerary; | ||
| import org.triple.backend.travel.exception.TravelItineraryErrorCode; | ||
| import org.triple.backend.travel.exception.UserTravelItineraryErrorCode; | ||
| import org.triple.backend.travel.repository.TravelItineraryJpaRepository; | ||
| import org.triple.backend.travel.repository.TravelReviewImageJpaRepository; | ||
| import org.triple.backend.travel.repository.TravelReviewJpaRepository; | ||
| import org.triple.backend.travel.repository.UserTravelItineraryJpaRepository; | ||
| import org.triple.backend.user.domain.User; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class TravelCommand { | ||
| private final TravelReader travelReader; | ||
| private final TravelItineraryJpaRepository travelItineraryJpaRepository; | ||
| private final TravelReviewJpaRepository travelReviewJpaRepository; | ||
| private final TravelReviewImageJpaRepository travelReviewImageJpaRepository; | ||
| private final UserTravelItineraryJpaRepository userTravelItineraryJpaRepository; | ||
|
|
||
| public TravelItinerary saveTravelItinerary( | ||
| final User leader, | ||
| final Group group, | ||
| final String title, | ||
| final LocalDateTime startAt, | ||
| final LocalDateTime endAt, | ||
| final String description, | ||
| final List<User> members | ||
| ) { | ||
| TravelItinerary travelItinerary = TravelItinerary.of(title, startAt, endAt, group, description); | ||
| TravelItinerary savedTravelItinerary = travelItineraryJpaRepository.save(travelItinerary); | ||
|
|
||
| userTravelItineraryJpaRepository.save(UserTravelItinerary.of(leader, savedTravelItinerary, UserRole.LEADER)); | ||
|
|
||
| List<UserTravelItinerary> memberTravelItineraries = new ArrayList<>(); | ||
| for (User member : members) { | ||
| memberTravelItineraries.add(UserTravelItinerary.of(member, savedTravelItinerary, UserRole.MEMBER)); | ||
| } | ||
| userTravelItineraryJpaRepository.saveAll(memberTravelItineraries); | ||
|
|
||
| savedTravelItinerary.increaseMemberCount(memberTravelItineraries.size()); | ||
| return savedTravelItinerary; | ||
| } | ||
|
|
||
| public void addUserTravelItinerary( | ||
| final User user, | ||
| final TravelItinerary travelItinerary, | ||
| final UserRole role | ||
| ) { | ||
| try { | ||
| travelItinerary.increaseMemberCount(1); | ||
| userTravelItineraryJpaRepository.save(UserTravelItinerary.of(user, travelItinerary, role)); | ||
| travelItineraryJpaRepository.flush(); | ||
| } catch (DataIntegrityViolationException e) { | ||
| throw new BusinessException(UserTravelItineraryErrorCode.ALREADY_JOINED_TRAVEL); | ||
| } catch (OptimisticLockingFailureException e) { | ||
| throw new BusinessException(UserTravelItineraryErrorCode.CONCURRENT_TRAVEL_ITINERARY); | ||
| } | ||
| } | ||
|
|
||
| public void updateTravel(final Long travelItineraryId, final String title, final LocalDateTime startAt, | ||
| final LocalDateTime endAt, final String description) { | ||
| TravelItinerary travel = travelReader.findTravel(travelItineraryId); | ||
|
|
||
| try { | ||
| travel.changeTitle(title); | ||
| travel.changeStartAt(startAt); | ||
| travel.changeEndAt(endAt); | ||
| travel.changeDescription(description); | ||
| travelItineraryJpaRepository.flush(); | ||
| } catch (OptimisticLockingFailureException e) { | ||
| throw new BusinessException(TravelItineraryErrorCode.CONCURRENT_TRAVEL_ITINERARY_UPDATE); | ||
| } | ||
| } | ||
|
|
||
| public void deleteTravel(final TravelItinerary travelItinerary) { | ||
| try { | ||
| userTravelItineraryJpaRepository.bulkDeleteByTravelId(travelItinerary.getId()); | ||
| travelItineraryJpaRepository.delete(travelItinerary); | ||
| travelItineraryJpaRepository.flush(); | ||
| } catch (OptimisticLockingFailureException e) { | ||
| throw new BusinessException(TravelItineraryErrorCode.CONCURRENT_TRAVEL_ITINERARY_DELETE); | ||
| } | ||
| } | ||
|
|
||
| public void leaveTravel(final Long travelId, final Long userId) { | ||
| UserTravelItinerary userTravelItinerary = userTravelItineraryJpaRepository | ||
| .findByUserIdAndTravelItineraryIdAndUserRole(userId, travelId, UserRole.MEMBER) | ||
| .orElseThrow(() -> new BusinessException(UserTravelItineraryErrorCode.USER_TRAVEL_ITINERARY_NOT_FOUND)); | ||
|
|
||
| TravelItinerary travelItinerary = userTravelItinerary.getTravelItinerary(); | ||
|
|
||
| try { | ||
| travelItinerary.decreaseMemberCount(); | ||
| userTravelItineraryJpaRepository.delete(userTravelItinerary); | ||
| userTravelItineraryJpaRepository.flush(); | ||
| } catch (OptimisticLockingFailureException e) { | ||
| throw new BusinessException(UserTravelItineraryErrorCode.CONCURRENT_TRAVEL_ITINERARY_LEAVE); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package org.triple.backend.travel.application; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.triple.backend.global.error.BusinessException; | ||
| import org.triple.backend.travel.entity.TravelItinerary; | ||
| import org.triple.backend.travel.entity.UserRole; | ||
| import org.triple.backend.travel.entity.UserTravelItinerary; | ||
| import org.triple.backend.travel.exception.TravelItineraryErrorCode; | ||
| import org.triple.backend.travel.exception.UserTravelItineraryErrorCode; | ||
| import org.triple.backend.travel.presentation.dto.res.TravelItineraryInfoResponseDto; | ||
| import org.triple.backend.travel.presentation.dto.res.TravelItineraryCursorResponseDto.TravelSummaryDto; | ||
| import org.triple.backend.travel.repository.TravelItineraryJpaRepository; | ||
| import org.triple.backend.travel.repository.TravelReviewImageJpaRepository; | ||
| import org.triple.backend.travel.repository.TravelReviewJpaRepository; | ||
| import org.triple.backend.travel.repository.UserTravelItineraryJpaRepository; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class TravelReader { | ||
| private final TravelItineraryJpaRepository travelItineraryJpaRepository; | ||
| private final TravelReviewJpaRepository travelReviewJpaRepository; | ||
| private final TravelReviewImageJpaRepository travelReviewImageJpaRepository; | ||
| private final UserTravelItineraryJpaRepository userTravelItineraryJpaRepository; | ||
|
|
||
| public long countGroupTravels(final Long groupId) { | ||
| return travelItineraryJpaRepository.countByGroupId(groupId); | ||
| } | ||
|
|
||
| public List<TravelSummaryDto> readGroupTravels(final Long groupId, final int size, final Long cursor) { | ||
| return travelItineraryJpaRepository.findGroupTravels(groupId, size, cursor); | ||
| } | ||
|
|
||
| public TravelItineraryInfoResponseDto getTravelInfo(final TravelItinerary travelItinerary) { | ||
| List<UserTravelItinerary> members = userTravelItineraryJpaRepository.findAllByTravelItineraryId(travelItinerary.getId()); | ||
| return TravelItineraryInfoResponseDto.of(travelItinerary, members); | ||
| } | ||
|
|
||
| public TravelItinerary findTravel(final Long travelId) { | ||
| return travelItineraryJpaRepository.findById(travelId) | ||
| .orElseThrow(() -> new BusinessException(TravelItineraryErrorCode.TRAVEL_NOT_FOUND)); | ||
| } | ||
|
|
||
| public UserTravelItinerary findUserTravelByStatus( | ||
| final Long userId, | ||
| final Long travelId, | ||
| final UserRole userRole | ||
| ) { | ||
| if (userRole == null) { | ||
| return userTravelItineraryJpaRepository.findByUserIdAndTravelItineraryId(userId, travelId) | ||
| .orElseThrow(() -> new BusinessException(UserTravelItineraryErrorCode.USER_TRAVEL_ITINERARY_NOT_FOUND)); | ||
| } | ||
| return userTravelItineraryJpaRepository.findByUserIdAndTravelItineraryIdAndUserRole(userId, travelId, userRole) | ||
| .orElseThrow(() -> new BusinessException(TravelItineraryErrorCode.USER_TRAVEL_NOT_FOUND)); | ||
|
Comment on lines
+53
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent error codes are used for the same logical failure (user not found in travel). Line 54 throws |
||
| } | ||
|
|
||
| public TravelItinerary findTravelForUpdate(Long travelId) { | ||
| return travelItineraryJpaRepository.findByIdForUpdate(travelId) | ||
| .orElseThrow(() -> new BusinessException(TravelItineraryErrorCode.TRAVEL_NOT_FOUND)); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method performs a redundant database fetch. The caller (
TravelUseCase.updateTravel) has already performed authorization checks which likely involved fetching the same entity or its related mappings. Consider passing theTravelItineraryentity directly to this method to avoid unnecessary database round-trips.