ReservationService.java
package com.deveagles.be15_deveagles_be.features.schedules.command.application.service;
import com.deveagles.be15_deveagles_be.common.events.ReservationCreatedEvent;
import com.deveagles.be15_deveagles_be.common.exception.BusinessException;
import com.deveagles.be15_deveagles_be.common.exception.ErrorCode;
import com.deveagles.be15_deveagles_be.features.customers.query.dto.response.CustomerIdResponse;
import com.deveagles.be15_deveagles_be.features.customers.query.service.CustomerQueryService;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.CreateReservationFullRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.CreateReservationRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.UpdateReservationRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.application.dto.request.UpdateReservationStatusRequest;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.aggregate.Reservation;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.aggregate.ReservationDetail;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.aggregate.ReservationStatusName;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.repository.ReservationDetailRepository;
import com.deveagles.be15_deveagles_be.features.schedules.command.domain.repository.ReservationRepository;
import jakarta.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ReservationService {
private final ReservationRepository reservationRepository;
private final ReservationDetailRepository reservationDetailRepository;
private final CustomerQueryService customerQueryService;
private final ApplicationEventPublisher eventPublisher;
@Transactional
public Long createReservation(CreateReservationRequest request) {
// 1. 고객 ID 조회
Optional<CustomerIdResponse> optionalCustomer =
customerQueryService.findCustomerIdByPhoneNumber(request.customerPhone(), request.shopId());
Long customerId = null;
String staffMemo;
if (optionalCustomer.isPresent()) {
customerId = optionalCustomer.get().id();
staffMemo = null;
} else {
staffMemo = "임시 고객: " + request.customerName() + " / " + request.customerPhone();
}
// 2. 예약 엔티티 생성
Reservation reservation =
Reservation.builder()
.staffId(request.staffId())
.shopId(request.shopId())
.customerId(customerId)
.reservationStatusName(ReservationStatusName.PENDING)
.staffMemo(staffMemo)
.reservationMemo(request.reservationMemo())
.reservationStartAt(request.reservationStartAt())
.reservationEndAt(request.reservationEndAt())
.build();
reservationRepository.save(reservation);
// 3. 시술 항목 저장
for (Long secondaryItemId : request.secondaryItemIds()) {
ReservationDetail detail =
ReservationDetail.builder()
.reservationId(reservation.getReservationId())
.secondaryItemId(secondaryItemId)
.build();
reservationDetailRepository.save(detail);
}
ReservationCreatedEvent event =
new ReservationCreatedEvent(reservation.getShopId(), request.customerName());
eventPublisher.publishEvent(event);
return reservation.getReservationId();
}
@Transactional
public Long createFullReservation(Long shopId, CreateReservationFullRequest request) {
Long customerId = request.customerId(); // null이면 미등록 고객
Reservation reservation =
Reservation.builder()
.staffId(request.staffId())
.shopId(shopId)
.customerId(customerId)
.reservationStatusName(ReservationStatusName.PENDING)
.staffMemo(request.staffMemo())
.reservationMemo(request.reservationMemo())
.reservationStartAt(request.reservationStartAt())
.reservationEndAt(request.reservationEndAt())
.build();
reservationRepository.save(reservation);
for (Long secondaryItemId : request.secondaryItemIds()) {
ReservationDetail detail =
ReservationDetail.builder()
.reservationId(reservation.getReservationId())
.secondaryItemId(secondaryItemId)
.build();
reservationDetailRepository.save(detail);
}
return reservation.getReservationId();
}
@Transactional
public void updateReservation(Long shopId, Long reservationId, UpdateReservationRequest request) {
Reservation reservation =
reservationRepository
.findById(reservationId)
.orElseThrow(() -> new BusinessException(ErrorCode.RESERVATION_NOT_FOUND));
if (!reservation.getShopId().equals(shopId)) {
throw new BusinessException(ErrorCode.RESERVATION_NOT_FOUND);
}
reservation.update(
request.staffId(),
ReservationStatusName.valueOf(request.reservationStatusName()),
request.staffMemo(),
request.reservationMemo(),
request.reservationStartAt(),
request.reservationEndAt());
reservationDetailRepository.deleteByReservationId(reservationId);
List<ReservationDetail> newDetails =
request.secondaryItemIds().stream()
.map(
itemId ->
ReservationDetail.builder()
.reservationId(reservationId)
.secondaryItemId(itemId)
.build())
.toList();
reservationDetailRepository.saveAll(newDetails);
}
@Transactional
public void deleteReservation(Long shopId, Long reservationId) {
Reservation reservation =
reservationRepository
.findById(reservationId)
.orElseThrow(() -> new BusinessException(ErrorCode.RESERVATION_NOT_FOUND));
if (!reservation.getShopId().equals(shopId)) {
throw new BusinessException(ErrorCode.RESERVATION_NOT_FOUND);
}
reservation.setDeletedAt(LocalDateTime.now());
}
@Transactional
public void changeReservationStatuses(
Long shopId, List<UpdateReservationStatusRequest> requestList) {
for (UpdateReservationStatusRequest request : requestList) {
Reservation reservation =
reservationRepository
.findById(request.reservationId())
.orElseThrow(() -> new BusinessException(ErrorCode.RESERVATION_NOT_FOUND));
if (!reservation.getShopId().equals(shopId)) {
throw new BusinessException(ErrorCode.RESERVATION_NOT_FOUND);
}
if (reservation.getReservationStatusName() == ReservationStatusName.PAID) {
throw new BusinessException(ErrorCode.MODIFY_NOT_ALLOWED_FOR_PAID_RESERVATION);
}
reservation.changeStatus(request.reservationStatusName());
}
}
}