-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #89 from woogieon8on/feature/reservation
Feat: 동방 예약 기능 구현
- Loading branch information
Showing
13 changed files
with
355 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
src/main/java/kahlua/KahluaProject/controller/ReservationController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package kahlua.KahluaProject.controller; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import kahlua.KahluaProject.apipayload.ApiResponse; | ||
import kahlua.KahluaProject.dto.reservation.request.ReservationProceedRequest; | ||
import kahlua.KahluaProject.dto.reservation.request.ReservationRequest; | ||
import kahlua.KahluaProject.dto.reservation.response.ReservationListResponse; | ||
import kahlua.KahluaProject.dto.reservation.response.ReservationResponse; | ||
import kahlua.KahluaProject.security.AuthDetails; | ||
import kahlua.KahluaProject.service.ReservationService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.format.annotation.DateTimeFormat; | ||
import org.springframework.messaging.handler.annotation.*; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.time.LocalDate; | ||
import java.util.Map; | ||
|
||
@Tag(name = "동방 예약", description = "동방 예약 기능 관련 API") | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class ReservationController { | ||
|
||
private final ReservationService reservationService; | ||
|
||
// 예약 시간 선택한 사람에게 우선권 부여 | ||
@MessageMapping("/reserve.proceed/{date}") | ||
@SendTo("/topic/public/{date}") | ||
public ReservationResponse proceed(@DestinationVariable String reservationDate, | ||
@Header("simpSessionAttributes") Map<String, Object> simpSessionAttributes, | ||
@Payload ReservationProceedRequest reservationProceedRequest) { | ||
|
||
return reservationService.proceed(reservationProceedRequest, reservationDate, simpSessionAttributes); | ||
} | ||
|
||
// 예약 확정 후 예약내역 DB에 저장 | ||
@MessageMapping("/reserve.complete/{date}") | ||
@SendTo("/topic/public/{date}") | ||
public ReservationResponse complete(@DestinationVariable String reservationDate, | ||
@Header("simpSessionAttributes") Map<String, Object> simpSessionAttributes, | ||
@Payload ReservationRequest reservationRequest) { | ||
|
||
return reservationService.save(reservationRequest, reservationDate, simpSessionAttributes); | ||
} | ||
|
||
@GetMapping("/v1/reservation") | ||
@Operation(summary = "날짜별 예약내역 목록 조회", description = "지정한 날짜에 해당하는 예약내역 목록을 사용 시작 시간 오름차순으로 조회합니다." + | ||
"<br> 쿼리 파라미터 날짜 형식은 yyyy-MM-dd 입니다") | ||
public ApiResponse<ReservationListResponse> getReservationListByDate(@RequestParam(name = "date") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { | ||
|
||
return ApiResponse.onSuccess(reservationService.getByDate(date)); | ||
} | ||
|
||
@GetMapping("/v1/reservation/check") | ||
@Operation(summary = "사용자 예약내역 조회", description = "사용자의 전체 예약내역을 예약 날짜 오름차순으로 조회합니다." + | ||
"<br> 같은 날짜에 대해서는 시작 시간 오름차순으로 정렬합니다.") | ||
public ApiResponse<ReservationListResponse> getReservationList(@AuthenticationPrincipal AuthDetails authDetails) { | ||
|
||
return ApiResponse.onSuccess(reservationService.getByUser(authDetails.user())); | ||
} | ||
|
||
@DeleteMapping("/v1/reservation/check/{reservationId}") | ||
@Operation(summary = "예약내역 삭제", description = "지정한 예약내역을 삭제합니다.") | ||
public ApiResponse<?> delete(@PathVariable Long reservationId) { | ||
|
||
reservationService.delete(reservationId); | ||
return ApiResponse.onSuccess("예약내역 삭제 성공"); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/main/java/kahlua/KahluaProject/converter/ReservationConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package kahlua.KahluaProject.converter; | ||
|
||
import kahlua.KahluaProject.domain.reservation.Reservation; | ||
import kahlua.KahluaProject.domain.reservation.ReservationStatus; | ||
import kahlua.KahluaProject.domain.user.User; | ||
import kahlua.KahluaProject.dto.reservation.request.ReservationRequest; | ||
import kahlua.KahluaProject.dto.reservation.response.ReservationResponse; | ||
|
||
import java.time.LocalDate; | ||
|
||
public class ReservationConverter { | ||
|
||
|
||
public static Reservation toReservation(ReservationRequest reservationRequest, User user, LocalDate reservationDate, ReservationStatus status) { | ||
|
||
return Reservation.builder() | ||
.user(user) | ||
.type(reservationRequest.type()) | ||
.clubRoomUsername(reservationRequest.clubroomUsername()) | ||
.reservationDate(reservationDate) | ||
.startTime(reservationRequest.startTime()) | ||
.endTime(reservationRequest.endTime()) | ||
.status(status) | ||
.build(); | ||
} | ||
public static ReservationResponse toReservationResponse(Reservation reservation, String email) { | ||
|
||
return ReservationResponse.builder() | ||
.reservationId(reservation.getId()) | ||
.email(email) | ||
.type(reservation.getType()) | ||
.clubroomUsername(reservation.getClubRoomUsername()) | ||
.reservationDate(reservation.getReservationDate()) | ||
.startTime(reservation.getStartTime()) | ||
.endTime(reservation.getEndTime()) | ||
.status(reservation.getStatus()) | ||
.build(); | ||
|
||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,5 @@ | |
|
||
public enum ReservationStatus { | ||
|
||
PROCEEDING, RESERVED, CANCELLED | ||
PROCEEDING, RESERVED | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/kahlua/KahluaProject/dto/reservation/request/ReservationProceedRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package kahlua.KahluaProject.dto.reservation.request; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
|
||
import java.time.LocalTime; | ||
|
||
public record ReservationProceedRequest( | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone = "Asia/Seoul") | ||
LocalTime startTime, | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone = "Asia/Seoul") | ||
LocalTime endTime | ||
) { | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/kahlua/KahluaProject/dto/reservation/request/ReservationRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package kahlua.KahluaProject.dto.reservation.request; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import kahlua.KahluaProject.domain.reservation.ReservationType; | ||
|
||
import java.time.LocalTime; | ||
|
||
public record ReservationRequest( | ||
ReservationType type, | ||
String clubroomUsername, | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone = "Asia/Seoul") | ||
LocalTime startTime, | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone = "Asia/Seoul") | ||
LocalTime endTime | ||
) { | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/kahlua/KahluaProject/dto/reservation/response/ReservationListResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package kahlua.KahluaProject.dto.reservation.response; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
|
||
import java.util.List; | ||
|
||
public record ReservationListResponse( | ||
@Schema(description = "예약내역 목록") | ||
List<ReservationResponse> reservationResponseList | ||
) { | ||
} |
36 changes: 36 additions & 0 deletions
36
src/main/java/kahlua/KahluaProject/dto/reservation/response/ReservationResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package kahlua.KahluaProject.dto.reservation.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import kahlua.KahluaProject.domain.reservation.ReservationStatus; | ||
import kahlua.KahluaProject.domain.reservation.ReservationType; | ||
import lombok.Builder; | ||
|
||
import java.time.LocalDate; | ||
import java.time.LocalTime; | ||
|
||
@Builder | ||
public record ReservationResponse( | ||
@Schema(description = "예약 id", example = "1") | ||
Long reservationId, | ||
@Schema(description = "예약자 이메일", example = "[email protected]") | ||
String email, | ||
@Schema(description = "예약 유형", example = "TEAM") | ||
ReservationType type, | ||
@Schema(description = "예약자명", example = "깔루아팀") | ||
String clubroomUsername, | ||
|
||
@Schema(description = "예약 날짜", example = "2024-01-01") | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul") | ||
LocalDate reservationDate, | ||
@Schema(description = "사용 시작 시간", example = "10:00:00") | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm", timezone = "Asia/Seoul") | ||
LocalTime startTime, | ||
@Schema(description = "사용 종료 시간", example = "11:00:00") | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone = "Asia/Seoul") | ||
LocalTime endTime, | ||
|
||
@Schema(description = "예약 상태", example = "RESERVED") | ||
ReservationStatus status | ||
) { | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/kahlua/KahluaProject/repository/reservation/ReservationCustomRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package kahlua.KahluaProject.repository.reservation; | ||
|
||
import kahlua.KahluaProject.domain.reservation.Reservation; | ||
import kahlua.KahluaProject.domain.user.User; | ||
|
||
import java.time.LocalDate; | ||
import java.util.List; | ||
|
||
public interface ReservationCustomRepository { | ||
|
||
List<Reservation> findByDate(LocalDate date); | ||
List<Reservation> findByUser(User user); | ||
} |
39 changes: 39 additions & 0 deletions
39
...ain/java/kahlua/KahluaProject/repository/reservation/ReservationCustomRepositoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package kahlua.KahluaProject.repository.reservation; | ||
|
||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import kahlua.KahluaProject.domain.reservation.Reservation; | ||
import kahlua.KahluaProject.domain.user.User; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.time.LocalDate; | ||
import java.util.List; | ||
|
||
import static kahlua.KahluaProject.domain.reservation.QReservation.reservation; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
public class ReservationCustomRepositoryImpl implements ReservationCustomRepository{ | ||
|
||
private final JPAQueryFactory jpaQueryFactory; | ||
|
||
@Override | ||
public List<Reservation> findByDate(LocalDate date) { | ||
|
||
return jpaQueryFactory | ||
.selectFrom(reservation) | ||
.where(reservation.reservationDate.eq(date)) | ||
.orderBy(reservation.startTime.asc()) | ||
.fetch(); | ||
} | ||
|
||
@Override | ||
public List<Reservation> findByUser(User user) { | ||
|
||
return jpaQueryFactory | ||
.selectFrom(reservation) | ||
.where(reservation.user.eq(user)) | ||
.orderBy(reservation.reservationDate.asc(), reservation.startTime.asc()) | ||
.fetch(); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/main/java/kahlua/KahluaProject/repository/reservation/ReservationRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package kahlua.KahluaProject.repository.reservation; | ||
|
||
import kahlua.KahluaProject.domain.reservation.Reservation; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface ReservationRepository extends JpaRepository<Reservation, Long>, ReservationCustomRepository { | ||
} |
98 changes: 98 additions & 0 deletions
98
src/main/java/kahlua/KahluaProject/service/ReservationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package kahlua.KahluaProject.service; | ||
|
||
import jakarta.transaction.Transactional; | ||
import kahlua.KahluaProject.apipayload.code.status.ErrorStatus; | ||
import kahlua.KahluaProject.domain.reservation.Reservation; | ||
import kahlua.KahluaProject.domain.reservation.ReservationStatus; | ||
import kahlua.KahluaProject.domain.user.User; | ||
import kahlua.KahluaProject.dto.reservation.request.ReservationProceedRequest; | ||
import kahlua.KahluaProject.dto.reservation.request.ReservationRequest; | ||
import kahlua.KahluaProject.dto.reservation.response.ReservationListResponse; | ||
import kahlua.KahluaProject.dto.reservation.response.ReservationResponse; | ||
import kahlua.KahluaProject.exception.GeneralException; | ||
import kahlua.KahluaProject.repository.reservation.ReservationRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.time.LocalDate; | ||
import java.time.format.DateTimeFormatter; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
import static kahlua.KahluaProject.converter.ReservationConverter.*; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ReservationService { | ||
|
||
private final ReservationRepository reservationRepository; | ||
private final UserService userService; | ||
|
||
public ReservationResponse proceed(ReservationProceedRequest reservationProceedRequest, String reservationDate, Map<String, Object> header) { | ||
|
||
String email = getValueFromHeader(header, "email"); | ||
|
||
return ReservationResponse.builder() | ||
.email(email) | ||
.reservationDate(toLocalDate(reservationDate)) | ||
.startTime(reservationProceedRequest.startTime()) | ||
.endTime(reservationProceedRequest.endTime()) | ||
.status(ReservationStatus.PROCEEDING) | ||
.build(); | ||
} | ||
|
||
@Transactional | ||
public ReservationResponse save(ReservationRequest reservationRequest, String reservationDate, Map<String, Object> header) { | ||
|
||
String email = getValueFromHeader(header, "email"); | ||
User user = userService.getUserByEmail(email); | ||
|
||
Reservation reservation = toReservation(reservationRequest, user, toLocalDate(reservationDate), ReservationStatus.RESERVED); | ||
Reservation savedReservation = reservationRepository.save(reservation); | ||
|
||
return toReservationResponse(savedReservation, email); | ||
} | ||
|
||
@Transactional | ||
public ReservationListResponse getByDate(LocalDate date) { | ||
|
||
List<Reservation> reservationList = reservationRepository.findByDate(date); | ||
|
||
List<ReservationResponse> reservationResponseList = reservationList.stream() | ||
.map(reservation -> toReservationResponse(reservation, reservation.getUser().getEmail())) | ||
.collect(Collectors.toList()); | ||
|
||
return new ReservationListResponse(reservationResponseList); | ||
} | ||
|
||
public ReservationListResponse getByUser(User user) { | ||
|
||
List<Reservation> reservationList = reservationRepository.findByUser(user); | ||
|
||
List<ReservationResponse> reservationResponseList = reservationList.stream() | ||
.map(reservation -> toReservationResponse(reservation, user.getEmail())) | ||
.collect(Collectors.toList()); | ||
|
||
return new ReservationListResponse(reservationResponseList); | ||
} | ||
|
||
@Transactional | ||
public void delete(Long reservationId) { | ||
Reservation reservation = reservationRepository.findById(reservationId) | ||
.orElseThrow(() -> new GeneralException(ErrorStatus.RESERVATION_NOT_FOUND)); | ||
|
||
reservationRepository.delete(reservation); | ||
} | ||
|
||
// String to LocalDateTime | ||
private LocalDate toLocalDate(String date) { | ||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); | ||
return LocalDate.parse(date, formatter); | ||
} | ||
|
||
private String getValueFromHeader(Map<String, Object> header, String key) { | ||
return (String)header.get(key); | ||
} | ||
|
||
} |