Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,27 @@ public class HomeConverter {

private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("MM-dd");

public static HomeResponse.OngoingVoteRoom toOngoingVoteRoom(VoteRoom voteRoom) {

List<String> profileImageUrls = voteRoom.getTripPlan().getRoom().getRoomMembers().stream()
.map(member -> member.getUser().getProfileImage())
.filter(Objects::nonNull)
.collect(Collectors.toList());
public static HomeResponse.OngoingVoteRoom toOngoingVoteRoom(TripPlan tripPlan) {
List<String> profileImageUrls = tripPlan.getRoom().getRoomMembers().stream()
.map(member -> member.getUser().getProfileImage())
.filter(Objects::nonNull)
.collect(Collectors.toList());

return new HomeResponse.OngoingVoteRoom(
voteRoom.getTripPlan().getId(),
voteRoom.getTripPlan().getRoom().getId(),
voteRoom.getTripPlan().getRoom().getMaster().getId(),
voteRoom.getTripPlan().getRoom().getName(),
voteRoom.getTripPlan().getLocation(),
voteRoom.getTripPlan().getRoom().getRoomMembers().size(),
voteRoom.getTripPlan().getVoteLimitTime(),
voteRoom.getTripPlan().getRoom().getRoomMembers().stream().filter(m -> m.getUser().getVote() != null).count(),
tripPlan.getId(),
tripPlan.getRoom().getId(),
tripPlan.getRoom().getMaster().getId(),
tripPlan.getRoom().getName(),
tripPlan.getLocation(),
tripPlan.getRoom().getRoomMembers().size(),
tripPlan.getVoteLimitTime(),
tripPlan.getRoom().getRoomMembers().stream().filter(m -> m.getUser().getVote() != null).count(),
profileImageUrls,
voteRoom.getCreatedAt(),
voteRoom.getTripPlan().getTripPlanType(),
voteRoom.getTripPlan().getLatitude(),
voteRoom.getTripPlan().getLongitude()
);
tripPlan.getCreatedAt(),
tripPlan.getTripPlanType(),
tripPlan.getLatitude(),
tripPlan.getLongitude()
);
}

public static HomeResponse.CompletedVoteRoom toCompletedVoteRoom(TripPlan tripPlan, Long aiCourseId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
public interface HomeRepository extends JpaRepository<TripPlan, Long> {
List<TripPlan> findByStatus(Status status);

@Query("SELECT vr FROM VoteRoom vr JOIN vr.tripPlan tp WHERE tp.status = 'ONGOING'")
List<VoteRoom> findAllOngoingVoteRooms();
@Query("SELECT tp FROM TripPlan tp WHERE tp.status = 'ONGOING'")
List<TripPlan> findAllOngoingTripPlans();

// 완료된 투표방과 연관된 종료 날짜가 현재 또는 미래인 여행 계획 조회
@Query("SELECT tp FROM TripPlan tp JOIN tp.voteRoom vr WHERE vr.tripPlan.status = 'COMPLETED' AND tp.endDate >= CURRENT_DATE")
List<TripPlan> findCompletedVoteRoomsWithEndDateAfterNow();
@Query("SELECT tp FROM TripPlan tp WHERE tp.status = 'ONGOING' OR (tp.status = 'COMPLETED' AND tp.tripPlanType = 'COURSE')")
List<TripPlan> findAllOngoingAndCompletedCourseTripPlans();
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
import com.umc.yeogi_gal_lae.api.notification.service.NotificationService;
import com.umc.yeogi_gal_lae.api.room.repository.RoomMemberRepository;
import com.umc.yeogi_gal_lae.api.tripPlan.domain.TripPlan;
import com.umc.yeogi_gal_lae.api.tripPlan.repository.TripPlanRepository;
import com.umc.yeogi_gal_lae.api.tripPlan.types.Status;
import com.umc.yeogi_gal_lae.api.tripPlan.types.TripPlanType;
import com.umc.yeogi_gal_lae.api.user.domain.User;
import com.umc.yeogi_gal_lae.api.user.repository.UserRepository;
import com.umc.yeogi_gal_lae.api.vote.domain.VoteRoom;
import com.umc.yeogi_gal_lae.api.vote.dto.request.VoteRoomRequest;
import com.umc.yeogi_gal_lae.api.vote.repository.VoteRoomRepository;
import com.umc.yeogi_gal_lae.api.vote.service.ValidVoteResultService;
import com.umc.yeogi_gal_lae.global.error.BusinessException;
import com.umc.yeogi_gal_lae.global.error.ErrorCode;
import com.umc.yeogi_gal_lae.global.success.SuccessCode;
Expand All @@ -21,15 +26,19 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class HomeService {

private final HomeRepository homeRepository;
private final TripPlanRepository tripPlanRepository;
private final RoomMemberRepository roomMemberRepository;
private final UserRepository userRepository;
private final VoteRoomRepository voteRoomRepository;
private final ValidVoteResultService validVoteResultService;
private final NotificationService notificationService;
private final AICourseRepository aiCourseRepository;

Expand All @@ -42,9 +51,12 @@ public Response<HomeResponse.OngoingVoteRoomList> getOngoingVoteRooms(String use
.map(roomMember -> roomMember.getRoom().getId())
.collect(Collectors.toList());

List<HomeResponse.OngoingVoteRoom> rooms = homeRepository.findAllOngoingVoteRooms().stream()
.filter(voteRoom -> userRoomIds.contains(voteRoom.getTripPlan().getRoom().getId()))
.filter(voteRoom -> !isVoteTimeExpired(voteRoom)) // ⬅ **제한 시간이 초과된 투표방 제거**
List<HomeResponse.OngoingVoteRoom> rooms = homeRepository.findAllOngoingAndCompletedCourseTripPlans().stream()
.filter(tripPlan -> userRoomIds.contains(tripPlan.getRoom().getId()))
.filter(tripPlan ->
(tripPlan.getTripPlanType() == TripPlanType.COURSE) ||
(tripPlan.getTripPlanType() == TripPlanType.SCHEDULE && !isVoteTimeExpired(tripPlan.getVoteRoom()))
)
.map(HomeConverter::toOngoingVoteRoom)
.collect(Collectors.toList());

Expand All @@ -55,24 +67,54 @@ public Response<HomeResponse.OngoingVoteRoomList> getOngoingVoteRooms(String use
// 완료된 투표방과 연관된 종료 날짜가 현재 또는 미래인 여행 계획 조회
public Response<HomeResponse.CompletedVoteRoomList> getFutureVoteBasedTrips(String userEmail) {
User user = userRepository.findByEmail(userEmail)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

// 사용자가 속한 방 ID 목록 조회
List<Long> userRoomIds = roomMemberRepository.findAllByUserId(user.getId())
.stream()
.map(roomMember -> roomMember.getRoom().getId())
.collect(Collectors.toList());

// 완료된 투표방과 연관된 여행 계획 중 종료 날짜가 현재 또는 미래인 여행 필터링
List<HomeResponse.CompletedVoteRoom> rooms = homeRepository.findCompletedVoteRoomsWithEndDateAfterNow().stream()
.filter(tripPlan -> userRoomIds.contains(tripPlan.getRoom().getId()))
.map(tripPlan -> {
Long aiCourseId = aiCourseRepository.findLatestByTripPlanId(tripPlan.getId())
.map(aiCourse -> aiCourse.getId())
.orElse(null);
return HomeConverter.toCompletedVoteRoom(tripPlan, aiCourseId);
})
.collect(Collectors.toList());
.stream()
.map(roomMember -> roomMember.getRoom().getId())
.collect(Collectors.toList());

List<TripPlan> allOngoingTripPlans = homeRepository.findAllOngoingTripPlans();

allOngoingTripPlans.forEach(tripPlan -> {
try {
if (tripPlan.getTripPlanType() == TripPlanType.COURSE && isCourseTimeExpired(tripPlan)) {
tripPlan.setStatus(Status.COMPLETED);
tripPlanRepository.save(tripPlan);
} else if (tripPlan.getTripPlanType() == TripPlanType.SCHEDULE) {
VoteRoom voteRoom = tripPlan.getVoteRoom();
if (voteRoom != null) {
VoteRoomRequest voteRoomRequest = VoteRoomRequest.builder()
.tripId(tripPlan.getId())
.roomId(tripPlan.getRoom().getId())
.voteRoomId(voteRoom.getId())
.build();

boolean isVoteFailed = validVoteResultService.validResult(voteRoomRequest);

if (isVoteFailed) {
voteRoomRepository.delete(voteRoom);
}
}
}
} catch(BusinessException e){
// VOTE_NOT_COMPLETED_YET 예외 발생 시, 해당 여행을 건너뛰고 나머지 여행들은 계속 조회
if (!e.getErrorCode().equals(ErrorCode.VOTE_NOT_COMPLETED_YET)) {
throw e;
}
}
});

List<HomeResponse.CompletedVoteRoom> rooms = homeRepository.findByStatus(Status.COMPLETED).stream()
.filter(tripPlan -> userRoomIds.contains(tripPlan.getRoom().getId()))
.filter(tripPlan -> !tripPlan.getEndDate().isBefore(LocalDate.now()))
.map(tripPlan -> {
Long aiCourseId = aiCourseRepository.findLatestByTripPlanId(tripPlan.getId())
.map(aiCourse -> aiCourse.getId())
.orElse(null);
return HomeConverter.toCompletedVoteRoom(tripPlan, aiCourseId);
})
.collect(Collectors.toList());

return Response.of(SuccessCode.COMPLETED_VOTE_ROOMS_FETCH_OK, new HomeResponse.CompletedVoteRoomList(rooms.size(), rooms));
}
Expand Down Expand Up @@ -108,6 +150,16 @@ private boolean isVoteTimeExpired(VoteRoom voteRoom) {
return LocalDateTime.now().isAfter(voteEndTime);
}

private boolean isCourseTimeExpired(TripPlan tripPlan) {
if (tripPlan.getTripPlanType() != TripPlanType.COURSE || tripPlan.getVoteLimitTime() == null) {
return false;
}

return LocalDateTime.now().isAfter(
tripPlan.getCreatedAt().plusSeconds(tripPlan.getVoteLimitTime().getSeconds())
);
}

/**
* 특정 사용자의 읽지 않은 알림 여부 반환 (이메일 기반)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static TripPlanResponse toResponse(TripPlan tripPlan) {
.id(tripPlan.getId())
.roomId(tripPlan.getRoom().getId())
.masterId(tripPlan.getRoom().getMaster().getId())
.voteRoomId(tripPlan.getVoteRoom().getId())
.voteRoomId(tripPlan.getVoteRoom() != null ? tripPlan.getVoteRoom().getId() : null)
.location(tripPlan.getLocation())
.startDate(tripPlan.getStartDate() != null ? tripPlan.getStartDate().toString() : null)
.endDate(tripPlan.getEndDate() != null ? tripPlan.getEndDate().toString() : null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,16 @@ private void linkTripPlanToRoomMembers(TripPlan tripPlan, Room room) {
}

/**
* 🚀 여행 계획이 생성되면 자동으로 투표방을 생성하는 메서드
* 여행 계획이 생성되면 자동으로 투표방을 생성하는 메서드
*/
private void createVoteRoomForTrip(TripPlan tripPlan) {

// 코스 계획(COURSE)일 경우 투표방을 만들지 않고 ONGOING으로
if (tripPlan.getTripPlanType() == TripPlanType.COURSE) {
tripPlan.setStatus(Status.ONGOING);
return;
}

// 기존에 존재하는 투표방이 있는지 확인 (중복 생성 방지)
if (voteRoomRepository.findByTripPlanId(tripPlan.getId()).isPresent()) {
throw new BusinessException(ErrorCode.VOTE_ROOM_ALREADY_EXISTS);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.umc.yeogi_gal_lae.api.vote.controller;

import com.umc.yeogi_gal_lae.api.tripPlan.domain.TripPlan;
import com.umc.yeogi_gal_lae.api.tripPlan.repository.TripPlanRepository;
import com.umc.yeogi_gal_lae.api.tripPlan.types.TripPlanType;
import com.umc.yeogi_gal_lae.api.vote.AuthenticatedUserUtils;
import com.umc.yeogi_gal_lae.api.vote.dto.request.VoteRequest;
import com.umc.yeogi_gal_lae.api.vote.dto.VoteResponse;
import com.umc.yeogi_gal_lae.api.vote.dto.request.VoteRoomRequest;
import com.umc.yeogi_gal_lae.api.vote.service.ValidVoteResultService;
import com.umc.yeogi_gal_lae.api.vote.service.VoteService;
import com.umc.yeogi_gal_lae.global.common.response.Response;
import com.umc.yeogi_gal_lae.global.error.BusinessException;
import com.umc.yeogi_gal_lae.global.error.ErrorCode;
import com.umc.yeogi_gal_lae.global.success.SuccessCode;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -31,6 +35,7 @@ public class VoteController {
@Autowired
private final VoteService voteService;
private final ValidVoteResultService validVoteResultService;
private final TripPlanRepository tripPlanRepository;

@Operation(summary = "투표하고자 하는 여행 계획의 정보 조회 API", description = "현재 투표의 여행 계획 정보에 해당합니다.")
@GetMapping("/vote/trip-info")
Expand All @@ -50,6 +55,13 @@ public Response<VoteResponse.VoteInfoDTO> getTripPlanInfoForVote(@RequestParam @
public Response<Void> createVote(@RequestBody @Valid VoteRequest.createVoteReq voteRequest) {

String userEmail = AuthenticatedUserUtils.getAuthenticatedUserEmail();
TripPlan tripPlan = tripPlanRepository.findById(voteRequest.getTripId())
.orElseThrow(() -> new BusinessException(ErrorCode.TRIP_PLAN_NOT_FOUND));

// 코스 계획(COURSE)일 경우 투표 불가능
if (tripPlan.getTripPlanType() == TripPlanType.COURSE) {
throw new BusinessException(ErrorCode.VOTE_NOT_ALLOWED_FOR_COURSE);
}

voteRequest.setUserEmail(userEmail);
voteService.createVote(voteRequest, userEmail);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public enum ErrorCode implements BaseStatus {
VOTE_NOT_COMPLETED_YET(HttpStatus.BAD_REQUEST, "VOTE_400", "아직 투표가 종료되지 않았습니다."),
VOTE_ROOM_NOT_FOUND(HttpStatus.NOT_FOUND, "VOTE_401", "요청 하신 투표 방을 찾을 수 없습니다."),
VOTE_RESULT_FAILED(HttpStatus.BAD_REQUEST, "VOTE_403", "여행 확정에 실패하셨습니다. 이 방은 사라집니다."),
VOTE_NOT_ALLOWED_FOR_COURSE(HttpStatus.BAD_REQUEST, "VOTE_404", "코스는 투표가 허용되지 않습니다."),

// Room Member Error
ROOM_MEMBER_NOT_EXIST(HttpStatus.BAD_REQUEST, "ROOM_MEMBER_404", "방에 멤버가 존재하지 않습니다."),
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ spring:

jwt:
secret: ${JWT_SECRET}
access-token-validity: 1800000 # 30분
access-token-validity: 86400000 # 30분
refresh-token-validity: 1209600000 # 2주
access:
header: Authorization
Expand Down