Skip to content
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

feat: 알림톡 기능 추가 #288

Closed
wants to merge 3 commits into from
Closed
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ out/

/src/main/resources/application-dev.yml
/src/main/resources/application-local.yml
/src/main/resources/application-prod.yml
/src/main/resources/application-prod.yml
shallwe-master.pem
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.shallwe.domain.auth.dto.request;

import com.shallwe.domain.auth.dto.MessageMapping;
import com.shallwe.global.infrastructure.sms.dto.MessageMapping;
import lombok.Builder;
import lombok.Data;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
import java.util.List;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ExperienceGiftServiceImpl implements ExperienceGiftService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.shallwe.domain.common.Status;
import com.shallwe.domain.experiencegift.domain.ExperienceGift;
import com.shallwe.domain.shopowner.domain.ShopOwner;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

Expand All @@ -13,7 +14,12 @@
public interface ExperienceGiftRepository extends JpaRepository<ExperienceGift, Long>, ExperienceGiftQuerydslRepository{

List<ExperienceGift> findByTitleContainsAndStatus(String title,Status status);

List<ExperienceGift> findByShopOwnerIdAndStatus(Long id, Status status);

Optional<ExperienceGift> findByIdAndShopOwner(Long experienceGiftId, ShopOwner shopOwner);

@EntityGraph(attributePaths = {"shopOwner"})
Optional<ExperienceGift> findExperienceGiftById(Long id);

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.shallwe.domain.reservation.application;

import com.shallwe.domain.reservation.dto.response.DeleteReservationRes;
import com.shallwe.domain.reservation.dto.request.OwnerReservationCreate;
import com.shallwe.domain.reservation.dto.response.ReservationResponse;
import com.shallwe.domain.reservation.dto.request.UserReservationCreate;
Expand All @@ -12,8 +11,8 @@
public interface ReservationManipulationService {

List<ReservationResponse> addOwnerReservation(OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal);
ReservationResponse addUserReservation(UserReservationCreate reservationRequest, UserPrincipal userPrincipal);
ReservationResponse addUserReservation(UserReservationCreate reservationRequest, UserPrincipal userPrincipal) throws Exception;
ReservationResponse updateReservation(UpdateReservationReq updateReq, UserPrincipal userPrincipal);
DeleteReservationRes deleteReservation(Long id);
void cancelReservation(UserPrincipal userPrincipal, Long reservationId) throws Exception;

}
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
package com.shallwe.domain.reservation.application;

import static com.shallwe.domain.reservation.domain.ReservationStatus.*;

import static com.shallwe.domain.reservation.domain.ReservationStatus.WAITING;

import com.shallwe.domain.common.Status;
import com.shallwe.domain.experiencegift.domain.ExperienceGift;
import com.shallwe.domain.experiencegift.domain.repository.ExperienceGiftRepository;
import com.shallwe.domain.experiencegift.exception.ExperienceGiftNotFoundException;
import com.shallwe.domain.reservation.domain.Reservation;
import com.shallwe.domain.reservation.domain.ReservationStatus;
import com.shallwe.domain.reservation.domain.repository.ReservationRepository;
import com.shallwe.domain.reservation.dto.response.DeleteReservationRes;
import com.shallwe.domain.reservation.dto.request.OwnerReservationCreate;
import com.shallwe.domain.reservation.dto.response.ReservationResponse;
import com.shallwe.domain.reservation.dto.request.UserReservationCreate;
import com.shallwe.domain.reservation.dto.request.UpdateReservationReq;
import com.shallwe.domain.reservation.exception.InvalidReservationException;
import com.shallwe.domain.reservation.exception.InvalidReservationUpdateException;
import com.shallwe.domain.reservation.exception.InvalidSenderException;
import com.shallwe.domain.reservation.exception.InvalidReceiverException;
import com.shallwe.domain.shopowner.domain.ShopOwner;
import com.shallwe.domain.shopowner.domain.repository.ShopOwnerRepository;
import com.shallwe.domain.shopowner.exception.InvalidShopOwnerException;
import com.shallwe.domain.reservation.exception.*;

import com.shallwe.domain.user.domain.User;
import com.shallwe.domain.user.domain.repository.UserRepository;
import com.shallwe.domain.user.exception.InvalidUserException;
import com.shallwe.global.config.security.token.UserPrincipal;

import java.util.List;
import java.util.stream.Collectors;

import com.shallwe.global.infrastructure.sms.NaverSmsClient;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -36,69 +38,114 @@
@Transactional(readOnly = true)
public class ReservationManipulationServiceImpl implements ReservationManipulationService {

private final ReservationRepository reservationRepository;
private final ExperienceGiftRepository experienceGiftRepository;
private final ShopOwnerRepository shopOwnerRepository;
private final UserRepository userRepository;

@Transactional
public List<ReservationResponse> addOwnerReservation(OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal) {
ExperienceGift experienceGift = experienceGiftRepository.findById(ownerReservationCreate.getExperienceGiftId())
.orElseThrow(ExperienceGiftNotFoundException::new);

ShopOwner owner = shopOwnerRepository.findById(userPrincipal.getId())
.orElseThrow(InvalidShopOwnerException::new);

List<Reservation> reservations = OwnerReservationCreate.toEntityForOwner(ownerReservationCreate, experienceGift, owner);
return reservations.stream()
.map(reservationRepository::save)
.map(ReservationResponse::toDtoOwner)
.collect(Collectors.toList());
private final ReservationRepository reservationRepository;
private final ExperienceGiftRepository experienceGiftRepository;
private final UserRepository userRepository;
private final NaverSmsClient naverSmsClient;

@Transactional
public List<ReservationResponse> addOwnerReservation(
OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal) {
ExperienceGift experienceGift = experienceGiftRepository.findById(
ownerReservationCreate.getExperienceGiftId())
.orElseThrow(ExperienceGiftNotFoundException::new);

List<Reservation> reservations = OwnerReservationCreate.toEntityForOwner(ownerReservationCreate,
experienceGift);
return reservations.stream()
.map(reservationRepository::save)
.map(ReservationResponse::toDtoOwner)
.collect(Collectors.toList());
}

@Transactional
public ReservationResponse addUserReservation(UserReservationCreate reservationRequest,
UserPrincipal userPrincipal) throws Exception {
User sender = userRepository.findById(userPrincipal.getId())
.orElseThrow(InvalidSenderException::new);

User receiver = userRepository.findByPhoneNumberAndStatus(reservationRequest.getPhoneNumber(),
Status.ACTIVE)
.orElseThrow(InvalidReceiverException::new);

ExperienceGift experienceGift = experienceGiftRepository.findExperienceGiftById(
reservationRequest.getExperienceGiftId())
.orElseThrow(ExperienceGiftNotFoundException::new);

Reservation reservation = reservationRepository.findByDateAndTimeAndExperienceGiftWithPessimisticLock(
reservationRequest.getDate(), reservationRequest.getTime(), experienceGift)
.orElseThrow(InvalidReservationException::new);

if (reservation.getReservationStatus().equals(WAITING)) {
reservation.updateStatus(BOOKED);
reservation.updateUserReservationRequest(reservationRequest, sender, receiver);
naverSmsClient.sendApply(receiver, experienceGift, reservation);
experienceGift.addReservationCount();
} else {
throw new InvalidReservationException();
}

return ReservationResponse.toDtoUser(reservation);
}

@Transactional
public ReservationResponse updateReservation(UpdateReservationReq updateReq,
UserPrincipal userPrincipal) {

//현재 예약 검색
Reservation currentReservation = reservationRepository.findById(updateReq.getReservationId())
.orElseThrow(InvalidReservationException::new);

//변경 가능한 예약 검색
Reservation newReservation = reservationRepository.findByDateAndTimeAndExperienceGift(
updateReq.getDate(), updateReq.getTime(),
currentReservation.getExperienceGift()
).orElseThrow(InvalidReservationUpdateException::new);

//DTO 생성
UserReservationCreate reservationCreateDto = UserReservationCreate.builder()
.persons(currentReservation.getPersons())
.experienceGiftId(currentReservation.getExperienceGift().getId())
.invitationComment(currentReservation.getInvitationComment())
.phoneNumber(currentReservation.getPhoneNumber())
.imageUrl(currentReservation.getInvitationImg())
.build();

//기존 예약 정보를 현재 예약 정보로 이동
newReservation.updateUserReservationRequest(reservationCreateDto,
currentReservation.getSender(), currentReservation.getReceiver());
newReservation.updateStatus(BOOKED);
Reservation updated = reservationRepository.save(newReservation);

//기존 예약 정보 상태 초기화
currentReservation.clearReservation();
currentReservation.updateStatus(WAITING);
Reservation curr = reservationRepository.save(currentReservation);

return ReservationResponse.toDtoUser(updated);
}

@Override
@Transactional
public ReservationResponse addUserReservation(UserReservationCreate reservationRequest, UserPrincipal userPrincipal) {
User sender = userRepository.findById(userPrincipal.getId())
.orElseThrow(InvalidSenderException::new);

User receiver = userRepository.findByPhoneNumberAndStatus(reservationRequest.getPhoneNumber(), Status.ACTIVE)
.orElseThrow(InvalidReceiverException::new);

ExperienceGift experienceGift = experienceGiftRepository.findById(reservationRequest.getExperienceGiftId())
.orElseThrow(ExperienceGiftNotFoundException::new);

Reservation reservation = reservationRepository.findByDateAndTimeAndExperienceGift(
reservationRequest.getDate(), reservationRequest.getTime(), experienceGift)
public void cancelReservation(final UserPrincipal userPrincipal, final Long reservationId) throws Exception {
Reservation reservation = reservationRepository.findReservationById(reservationId)
.orElseThrow(InvalidReservationException::new);

if (reservation.getReservationStatus().equals(WAITING)) {
reservation.updateStatus(ReservationStatus.BOOKED);
reservation.updateUserReservationRequest(reservationRequest, sender, receiver);
experienceGift.addReservationCount();
} else {
throw new InvalidReservationException();
User user = userRepository.findById(userPrincipal.getId())
.orElseThrow(InvalidUserException::new);

if (!reservation.getSender().getId().equals(user.getId()))
throw new UserReservationMismatchException();

if(reservation.getReservationStatus().equals(BOOKED)) { // BOOKED 상태일 때 WAITING으로 변경
reservation.updateStatus(WAITING);
naverSmsClient.sendCancel(reservation);
reservation.clearReservation();
} else if(reservation.getReservationStatus().equals(CONFIRMED)) { // CONFIRMED 상태일 때 CANCELLED로 변경
reservation.updateStatus(CANCELLED);
naverSmsClient.sendCancel(reservation);
} else { // 예약 상태가 BOOKED나 CONFIRMED가 아닐 경우
throw new NotAvailableReservationStatusException();
}
return ReservationResponse.toDtoUser(reservation);
}

@Transactional
public ReservationResponse updateReservation(UpdateReservationReq updateReq, UserPrincipal userPrincipal) {
Reservation updateReservation = reservationRepository.findById(
updateReq.getReservationId()).map(
reservation -> {
reservation.updateReservation(updateReq);
return reservationRepository.save(reservation);
}
).orElseThrow(InvalidReservationException::new);
return ReservationResponse.toDtoUser(updateReservation);
}

@Transactional
public DeleteReservationRes deleteReservation(Long id) {
Reservation reservation = reservationRepository.findById(id)
.orElseThrow(InvalidReservationException::new);
reservation.updateStatus(Status.DELETE);
return DeleteReservationRes.toDTO();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ public class Reservation extends BaseEntity {
@Schema(description = "선물 ID")
private ExperienceGift experienceGift;

@ManyToOne
@JoinColumn(name = "owner_id", nullable = false)
@Schema(description = "사장ID")
private ShopOwner owner;


@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sender_id")
@Schema(description = "보내는이 ID")
Expand Down Expand Up @@ -82,9 +76,8 @@ public class Reservation extends BaseEntity {
private List<MemoryPhoto> memoryPhotos = new ArrayList<>();

@Builder
public Reservation(ExperienceGift experienceGift, ShopOwner owner, LocalDate date, LocalTime time, ReservationStatus reservationStatus) {
public Reservation(ExperienceGift experienceGift, LocalDate date, LocalTime time, ReservationStatus reservationStatus) {
this.experienceGift = experienceGift;
this.owner = owner;
this.date = date;
this.time = time;
this.reservationStatus = reservationStatus;
Expand All @@ -107,4 +100,15 @@ public void updateUserReservationRequest(UserReservationCreate reservationReques
public void updateStatus(ReservationStatus status) {
this.reservationStatus = status;
}


public void clearReservation() {
this.sender = null;
this.receiver = null;
this.phoneNumber = null;
this.invitationComment = null;
this.persons = null;
this.invitationImg = null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import com.shallwe.domain.reservation.domain.ReservationStatus;
import com.shallwe.domain.shopowner.domain.ShopOwner;
import com.shallwe.domain.user.domain.User;
import jakarta.persistence.LockModeType;
import java.time.LocalDate;
import java.time.LocalTime;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
Expand All @@ -35,4 +37,13 @@ public interface ReservationRepository extends JpaRepository<Reservation, Long>,

Optional<List<Reservation>> findAllByExperienceGiftAndDate(ExperienceGift experienceGift, LocalDate date);


@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select r from Reservation r where r.date = :date and r.time = :time and r.experienceGift=:experienceGift")
Optional<Reservation> findByDateAndTimeAndExperienceGiftWithPessimisticLock(LocalDate date, LocalTime time, ExperienceGift experienceGift);

@EntityGraph(attributePaths = {"experienceGift", "experienceGift.shopOwner", "sender", "receiver"})
Optional<Reservation> findReservationById(Long reservationId);


}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class OwnerReservationCreate {
private Map<LocalDate, List<LocalTime>> dateTimeMap;

public static List<Reservation> toEntityForOwner(OwnerReservationCreate ownerReservationCreate,
ExperienceGift experienceGift, ShopOwner owner) {
ExperienceGift experienceGift) {
List<Reservation> reservations = new ArrayList<>();

for (Map.Entry<LocalDate, List<LocalTime>> entry : ownerReservationCreate.getDateTimeMap()
Expand All @@ -42,7 +42,6 @@ public static List<Reservation> toEntityForOwner(OwnerReservationCreate ownerRes
.experienceGift(experienceGift)
.date(date)
.time(time)
.owner(owner)
.reservationStatus(WAITING)
.build();
reservations.add(toEntity);
Expand Down
Loading
Loading