generated from pricelees/issue-pr-template
refactor: ReservationService 코틀린 전환
This commit is contained in:
parent
186884e6ce
commit
036947153d
@ -1,227 +1,244 @@
|
|||||||
package roomescape.reservation.business;
|
package roomescape.reservation.business
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import org.springframework.data.jpa.domain.Specification
|
||||||
import java.time.LocalDateTime;
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
import java.util.List;
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.transaction.annotation.Transactional
|
||||||
import org.springframework.http.HttpStatus;
|
import roomescape.common.exception.ErrorType
|
||||||
import org.springframework.stereotype.Service;
|
import roomescape.common.exception.RoomescapeException
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import roomescape.member.business.MemberService
|
||||||
|
import roomescape.reservation.infrastructure.persistence.*
|
||||||
import roomescape.common.exception.ErrorType;
|
import roomescape.reservation.web.*
|
||||||
import roomescape.common.exception.RoomescapeException;
|
import roomescape.theme.business.ThemeService
|
||||||
import roomescape.member.business.MemberService;
|
import java.time.LocalDate
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity;
|
import java.time.LocalDateTime
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity;
|
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository;
|
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationSearchSpecification;
|
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus;
|
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity;
|
|
||||||
import roomescape.reservation.web.AdminReservationRequest;
|
|
||||||
import roomescape.reservation.web.MyReservationsResponse;
|
|
||||||
import roomescape.reservation.web.ReservationRequest;
|
|
||||||
import roomescape.reservation.web.ReservationResponse;
|
|
||||||
import roomescape.reservation.web.ReservationsResponse;
|
|
||||||
import roomescape.reservation.web.WaitingRequest;
|
|
||||||
import roomescape.theme.business.ThemeService;
|
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional
|
||||||
public class ReservationService {
|
class ReservationService(
|
||||||
|
private val reservationRepository: ReservationRepository,
|
||||||
|
private val reservationTimeService: ReservationTimeService,
|
||||||
|
private val memberService: MemberService,
|
||||||
|
private val themeService: ThemeService,
|
||||||
|
) {
|
||||||
|
|
||||||
private final ReservationRepository reservationRepository;
|
@Transactional(readOnly = true)
|
||||||
private final ReservationTimeService reservationTimeService;
|
fun findAllReservations(): ReservationsResponse {
|
||||||
private final MemberService memberService;
|
val spec: Specification<ReservationEntity> = ReservationSearchSpecification()
|
||||||
private final ThemeService themeService;
|
.confirmed()
|
||||||
|
.build()
|
||||||
|
|
||||||
public ReservationService(
|
|
||||||
ReservationRepository reservationRepository,
|
return ReservationsResponse(findAllReservationByStatus(spec))
|
||||||
ReservationTimeService reservationTimeService,
|
|
||||||
MemberService memberService,
|
|
||||||
ThemeService themeService
|
|
||||||
) {
|
|
||||||
this.reservationRepository = reservationRepository;
|
|
||||||
this.reservationTimeService = reservationTimeService;
|
|
||||||
this.memberService = memberService;
|
|
||||||
this.themeService = themeService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ReservationsResponse findAllReservations() {
|
fun findAllWaiting(): ReservationsResponse {
|
||||||
Specification<ReservationEntity> spec = new ReservationSearchSpecification().confirmed().build();
|
val spec: Specification<ReservationEntity> = ReservationSearchSpecification()
|
||||||
List<ReservationResponse> response = findAllReservationByStatus(spec);
|
.waiting()
|
||||||
|
.build()
|
||||||
|
|
||||||
return new ReservationsResponse(response);
|
return ReservationsResponse(findAllReservationByStatus(spec))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
private fun findAllReservationByStatus(spec: Specification<ReservationEntity>): List<ReservationResponse> {
|
||||||
public ReservationsResponse findAllWaiting() {
|
return reservationRepository.findAll(spec).map { it.toResponse() }
|
||||||
Specification<ReservationEntity> spec = new ReservationSearchSpecification().waiting().build();
|
|
||||||
List<ReservationResponse> response = findAllReservationByStatus(spec);
|
|
||||||
|
|
||||||
return new ReservationsResponse(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ReservationResponse> findAllReservationByStatus(Specification<ReservationEntity> spec) {
|
fun removeReservationById(reservationId: Long, memberId: Long) {
|
||||||
return reservationRepository.findAll(spec)
|
validateIsMemberAdmin(memberId)
|
||||||
.stream()
|
reservationRepository.deleteById(reservationId)
|
||||||
.map(ReservationResponse::from)
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeReservationById(Long reservationId, Long memberId) {
|
fun addReservation(request: ReservationRequest, memberId: Long): ReservationEntity {
|
||||||
validateIsMemberAdmin(memberId);
|
validateIsReservationExist(request.themeId, request.timeId, request.date)
|
||||||
reservationRepository.deleteById(reservationId);
|
return getReservationForSave(
|
||||||
|
request.timeId,
|
||||||
|
request.themeId,
|
||||||
|
request.date,
|
||||||
|
memberId,
|
||||||
|
ReservationStatus.CONFIRMED
|
||||||
|
).also {
|
||||||
|
reservationRepository.save(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReservationEntity addReservation(ReservationRequest request, Long memberId) {
|
fun addReservationByAdmin(request: AdminReservationRequest): ReservationResponse {
|
||||||
validateIsReservationExist(request.themeId, request.timeId, request.date);
|
validateIsReservationExist(request.themeId, request.timeId, request.date)
|
||||||
ReservationEntity reservation = getReservationForSave(request.timeId, request.themeId, request.date, memberId,
|
|
||||||
ReservationStatus.CONFIRMED);
|
return addReservationWithoutPayment(
|
||||||
return reservationRepository.save(reservation);
|
request.themeId,
|
||||||
|
request.timeId,
|
||||||
|
request.date,
|
||||||
|
request.memberId,
|
||||||
|
ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReservationResponse addReservationByAdmin(AdminReservationRequest request) {
|
fun addWaiting(request: WaitingRequest, memberId: Long): ReservationResponse {
|
||||||
validateIsReservationExist(request.themeId, request.timeId, request.date);
|
validateMemberAlreadyReserve(request.themeId, request.timeId, request.date, memberId)
|
||||||
return addReservationWithoutPayment(request.themeId, request.timeId, request.date,
|
return addReservationWithoutPayment(
|
||||||
request.memberId, ReservationStatus.CONFIRMED_PAYMENT_REQUIRED);
|
request.themeId,
|
||||||
|
request.timeId,
|
||||||
|
request.date,
|
||||||
|
memberId,
|
||||||
|
ReservationStatus.WAITING
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReservationResponse addWaiting(WaitingRequest request, Long memberId) {
|
private fun addReservationWithoutPayment(
|
||||||
validateMemberAlreadyReserve(request.themeId, request.timeId, request.date, memberId);
|
themeId: Long,
|
||||||
return addReservationWithoutPayment(request.themeId, request.timeId, request.date, memberId,
|
timeId: Long,
|
||||||
ReservationStatus.WAITING);
|
date: LocalDate,
|
||||||
}
|
memberId: Long,
|
||||||
|
status: ReservationStatus
|
||||||
|
): ReservationResponse = getReservationForSave(timeId, themeId, date, memberId, status)
|
||||||
|
.also {
|
||||||
|
reservationRepository.save(it)
|
||||||
|
}.toResponse()
|
||||||
|
|
||||||
private ReservationResponse addReservationWithoutPayment(Long themeId, Long timeId, LocalDate date, Long memberId,
|
|
||||||
ReservationStatus status) {
|
|
||||||
ReservationEntity reservation = getReservationForSave(timeId, themeId, date, memberId, status);
|
|
||||||
ReservationEntity saved = reservationRepository.save(reservation);
|
|
||||||
return ReservationResponse.from(saved);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateMemberAlreadyReserve(Long themeId, Long timeId, LocalDate date, Long memberId) {
|
private fun validateMemberAlreadyReserve(themeId: Long?, timeId: Long?, date: LocalDate?, memberId: Long?) {
|
||||||
Specification<ReservationEntity> spec = new ReservationSearchSpecification()
|
val spec: Specification<ReservationEntity> = ReservationSearchSpecification()
|
||||||
.sameMemberId(memberId)
|
.sameMemberId(memberId)
|
||||||
.sameThemeId(themeId)
|
.sameThemeId(themeId)
|
||||||
.sameTimeId(timeId)
|
.sameTimeId(timeId)
|
||||||
.sameDate(date)
|
.sameDate(date)
|
||||||
.build();
|
.build()
|
||||||
|
|
||||||
if (reservationRepository.exists(spec)) {
|
if (reservationRepository.exists(spec)) {
|
||||||
throw new RoomescapeException(ErrorType.HAS_RESERVATION_OR_WAITING, HttpStatus.BAD_REQUEST);
|
throw RoomescapeException(ErrorType.HAS_RESERVATION_OR_WAITING, HttpStatus.BAD_REQUEST)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateIsReservationExist(Long themeId, Long timeId, LocalDate date) {
|
private fun validateIsReservationExist(themeId: Long, timeId: Long, date: LocalDate) {
|
||||||
Specification<ReservationEntity> spec = new ReservationSearchSpecification()
|
val spec: Specification<ReservationEntity> = ReservationSearchSpecification()
|
||||||
.confirmed()
|
.confirmed()
|
||||||
.sameThemeId(themeId)
|
.sameThemeId(themeId)
|
||||||
.sameTimeId(timeId)
|
.sameTimeId(timeId)
|
||||||
.sameDate(date)
|
.sameDate(date)
|
||||||
.build();
|
.build()
|
||||||
|
|
||||||
if (reservationRepository.exists(spec)) {
|
if (reservationRepository.exists(spec)) {
|
||||||
throw new RoomescapeException(ErrorType.RESERVATION_DUPLICATED, HttpStatus.CONFLICT);
|
throw RoomescapeException(ErrorType.RESERVATION_DUPLICATED, HttpStatus.CONFLICT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateDateAndTime(
|
private fun validateDateAndTime(
|
||||||
LocalDate requestDate,
|
requestDate: LocalDate,
|
||||||
ReservationTimeEntity requestReservationTime
|
requestReservationTime: ReservationTimeEntity
|
||||||
) {
|
) {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
val now = LocalDateTime.now()
|
||||||
LocalDateTime request = LocalDateTime.of(requestDate, requestReservationTime.getStartAt());
|
val request = LocalDateTime.of(requestDate, requestReservationTime.startAt)
|
||||||
|
|
||||||
if (request.isBefore(now)) {
|
if (request.isBefore(now)) {
|
||||||
throw new RoomescapeException(ErrorType.RESERVATION_PERIOD_IN_PAST,
|
throw RoomescapeException(
|
||||||
String.format("[now: %s %s | request: %s %s]",
|
ErrorType.RESERVATION_PERIOD_IN_PAST,
|
||||||
now.toLocalDate(), now.toLocalTime(), requestDate, requestReservationTime.getStartAt()),
|
"[now: $now | request: $request]",
|
||||||
HttpStatus.BAD_REQUEST
|
HttpStatus.BAD_REQUEST
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReservationEntity getReservationForSave(Long timeId, Long themeId, LocalDate date, Long memberId,
|
private fun getReservationForSave(
|
||||||
ReservationStatus status) {
|
timeId: Long,
|
||||||
ReservationTimeEntity time = reservationTimeService.findTimeById(timeId);
|
themeId: Long,
|
||||||
ThemeEntity theme = themeService.findThemeById(themeId);
|
date: LocalDate,
|
||||||
MemberEntity member = memberService.findById(memberId);
|
memberId: Long,
|
||||||
|
status: ReservationStatus
|
||||||
|
): ReservationEntity {
|
||||||
|
val time = reservationTimeService.findTimeById(timeId)
|
||||||
|
val theme = themeService.findThemeById(themeId)
|
||||||
|
val member = memberService.findById(memberId)
|
||||||
|
|
||||||
validateDateAndTime(date, time);
|
validateDateAndTime(date, time)
|
||||||
return new ReservationEntity(null, date, time, theme, member, status);
|
|
||||||
|
return ReservationEntity(
|
||||||
|
date = date,
|
||||||
|
reservationTime = time,
|
||||||
|
theme = theme,
|
||||||
|
member = member,
|
||||||
|
reservationStatus = status
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ReservationsResponse findFilteredReservations(Long themeId, Long memberId, LocalDate dateFrom,
|
fun findFilteredReservations(
|
||||||
LocalDate dateTo) {
|
themeId: Long?,
|
||||||
validateDateForSearch(dateFrom, dateTo);
|
memberId: Long?,
|
||||||
Specification<ReservationEntity> spec = new ReservationSearchSpecification()
|
dateFrom: LocalDate?,
|
||||||
|
dateTo: LocalDate?
|
||||||
|
): ReservationsResponse {
|
||||||
|
validateDateForSearch(dateFrom, dateTo)
|
||||||
|
val spec: Specification<ReservationEntity> = ReservationSearchSpecification()
|
||||||
.confirmed()
|
.confirmed()
|
||||||
.sameThemeId(themeId)
|
.sameThemeId(themeId)
|
||||||
.sameMemberId(memberId)
|
.sameMemberId(memberId)
|
||||||
.dateStartFrom(dateFrom)
|
.dateStartFrom(dateFrom)
|
||||||
.dateEndAt(dateTo)
|
.dateEndAt(dateTo)
|
||||||
.build();
|
.build()
|
||||||
|
|
||||||
List<ReservationResponse> response = reservationRepository.findAll(spec)
|
return ReservationsResponse(findAllReservationByStatus(spec))
|
||||||
.stream()
|
|
||||||
.map(ReservationResponse::from)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return new ReservationsResponse(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateDateForSearch(LocalDate startFrom, LocalDate endAt) {
|
private fun validateDateForSearch(startFrom: LocalDate?, endAt: LocalDate?) {
|
||||||
if (startFrom == null || endAt == null) {
|
if (startFrom == null || endAt == null) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (startFrom.isAfter(endAt)) {
|
if (startFrom.isAfter(endAt)) {
|
||||||
throw new RoomescapeException(ErrorType.INVALID_DATE_RANGE,
|
throw RoomescapeException(
|
||||||
String.format("[startFrom: %s, endAt: %s", startFrom, endAt), HttpStatus.BAD_REQUEST);
|
ErrorType.INVALID_DATE_RANGE,
|
||||||
|
"[startFrom: $startFrom, endAt: $endAt", HttpStatus.BAD_REQUEST
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public MyReservationsResponse findMemberReservations(Long memberId) {
|
fun findMemberReservations(memberId: Long): MyReservationsResponse {
|
||||||
return new MyReservationsResponse(reservationRepository.findMyReservations(memberId));
|
return MyReservationsResponse(reservationRepository.findMyReservations(memberId))
|
||||||
}
|
}
|
||||||
|
|
||||||
public void approveWaiting(Long reservationId, Long memberId) {
|
fun approveWaiting(reservationId: Long, memberId: Long) {
|
||||||
validateIsMemberAdmin(memberId);
|
validateIsMemberAdmin(memberId)
|
||||||
if (reservationRepository.isExistConfirmedReservation(reservationId)) {
|
if (reservationRepository. isExistConfirmedReservation(reservationId)) {
|
||||||
throw new RoomescapeException(ErrorType.RESERVATION_DUPLICATED, HttpStatus.CONFLICT);
|
throw RoomescapeException(ErrorType.RESERVATION_DUPLICATED, HttpStatus.CONFLICT)
|
||||||
}
|
}
|
||||||
reservationRepository.updateStatusByReservationId(reservationId, ReservationStatus.CONFIRMED_PAYMENT_REQUIRED);
|
reservationRepository.updateStatusByReservationId(reservationId, ReservationStatus.CONFIRMED_PAYMENT_REQUIRED)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelWaiting(Long reservationId, Long memberId) {
|
fun cancelWaiting(reservationId: Long, memberId: Long) {
|
||||||
ReservationEntity waiting = reservationRepository.findById(reservationId)
|
reservationRepository.findByIdOrNull(reservationId)?.takeIf {
|
||||||
.filter(ReservationEntity::isWaiting)
|
it.isWaiting() && it.isSameMember(memberId)
|
||||||
.filter(r -> r.isSameMember(memberId))
|
}?.let {
|
||||||
.orElseThrow(() -> throwReservationNotFound(reservationId));
|
reservationRepository.delete(it)
|
||||||
reservationRepository.delete(waiting);
|
} ?: throw throwReservationNotFound(reservationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void denyWaiting(Long reservationId, Long memberId) {
|
fun denyWaiting(reservationId: Long, memberId: Long) {
|
||||||
validateIsMemberAdmin(memberId);
|
validateIsMemberAdmin(memberId)
|
||||||
ReservationEntity waiting = reservationRepository.findById(reservationId)
|
reservationRepository.findByIdOrNull(reservationId)?.takeIf {
|
||||||
.filter(ReservationEntity::isWaiting)
|
it.isWaiting()
|
||||||
.orElseThrow(() -> throwReservationNotFound(reservationId));
|
}?.let {
|
||||||
reservationRepository.delete(waiting);
|
reservationRepository.delete(it)
|
||||||
|
} ?: throw throwReservationNotFound(reservationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateIsMemberAdmin(Long memberId) {
|
private fun validateIsMemberAdmin(memberId: Long) {
|
||||||
MemberEntity member = memberService.findById(memberId);
|
memberService.findById(memberId).takeIf {
|
||||||
if (member.isAdmin()) {
|
it.isAdmin()
|
||||||
return;
|
} ?: throw RoomescapeException(
|
||||||
}
|
ErrorType.PERMISSION_DOES_NOT_EXIST,
|
||||||
throw new RoomescapeException(ErrorType.PERMISSION_DOES_NOT_EXIST, HttpStatus.FORBIDDEN);
|
"[memberId: $memberId]",
|
||||||
|
HttpStatus.FORBIDDEN
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private RoomescapeException throwReservationNotFound(Long reservationId) {
|
private fun throwReservationNotFound(reservationId: Long?): RoomescapeException {
|
||||||
return new RoomescapeException(ErrorType.RESERVATION_NOT_FOUND,
|
return RoomescapeException(
|
||||||
String.format("[reservationId: %d]", reservationId), HttpStatus.NOT_FOUND);
|
ErrorType.RESERVATION_NOT_FOUND,
|
||||||
|
"[reservationId: $reservationId]",
|
||||||
|
HttpStatus.NOT_FOUND
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,11 @@ import com.fasterxml.jackson.annotation.JsonProperty
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
import roomescape.member.web.MemberResponse
|
import roomescape.member.web.MemberResponse
|
||||||
import roomescape.member.web.MemberResponse.Companion.fromEntity
|
import roomescape.member.web.MemberResponse.Companion.fromEntity
|
||||||
|
import roomescape.member.web.toResponse
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||||
import roomescape.theme.web.ThemeResponse
|
import roomescape.theme.web.ThemeResponse
|
||||||
|
import roomescape.theme.web.toResponse
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
|
|
||||||
@ -85,10 +87,18 @@ data class ReservationResponse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ReservationEntity.toResponse(): ReservationResponse = ReservationResponse(
|
||||||
|
id = this.id!!,
|
||||||
|
date = this.date,
|
||||||
|
member = this.member.toResponse(),
|
||||||
|
time = this.reservationTime.toResponse(),
|
||||||
|
theme = this.theme.toResponse(),
|
||||||
|
status = this.reservationStatus
|
||||||
|
)
|
||||||
|
|
||||||
@Schema(name = "예약 목록 조회 응답", description = "모든 예약 정보 조회 응답시 사용됩니다.")
|
@Schema(name = "예약 목록 조회 응답", description = "모든 예약 정보 조회 응답시 사용됩니다.")
|
||||||
@JvmRecord
|
@JvmRecord
|
||||||
data class ReservationsResponse(
|
data class ReservationsResponse(
|
||||||
@field:Schema(description = "모든 예약 및 대기 목록")
|
@field:Schema(description = "모든 예약 및 대기 목록")
|
||||||
val reservations: List<ReservationResponse>
|
val reservations: List<ReservationResponse>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user