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 : 예약 알림 기능 추가 #93

Merged
merged 14 commits into from
Jan 11, 2024
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
@@ -1,20 +1,22 @@
package com.prgrms.catchtable.common.notification;

import java.util.function.Function;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public enum NotificationContent {
RESERVATION_COMPLETED("예약이 완료되었습니다"),
RESERVATION_ONE_HOUR_LEFT("예약 시간 1시간 전입니다."),
RESERVATION_TIME_TO_ENTER("예약시간이 되었습니다"),
WAITING_REGISTER_COMPLETED("웨이팅 등록이 완료되었습니다"),
WAITING_RANK_THIRD("웨이팅 순서가 3번째가 되었습니다"),
WAITING_TIME_TO_ENTER("웨이팅이 끝났습니다. 입장 부탁드립니다."),
WAITING_CANCELLED_AUTOMATICALLY("웨이팅이 자동으로 취소되었습니다.");
RESERVATION_COMPLETED(time -> time.concat(" 시간의 예약이 완료 되었습니다.")),
RESERVATION_CANCELLED(time -> time.concat(" 시간의 예약이 취소 되었습니다")),
RESERVATION_ONE_HOUR_LEFT(time -> time.concat(" 시간 예약까지 한시간 남았습니다")),
RESERVATION_TIME_OUT(time -> "예약 시간이 되었습니다. 입장해주세요.");
Comment on lines +10 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


private final String message;

private final Function<String, String> expression;

public String getMessage(String time) {
return expression.apply(time);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드명을 getMessage()이런 식으로 변경해도 괜찮을 것 같다는 생각이 듭니다..!


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import static org.springframework.transaction.event.TransactionPhase.AFTER_COMMIT;

import com.prgrms.catchtable.member.domain.Member;
import com.prgrms.catchtable.notification.dto.request.SendMessageToMemberRequest;
import com.prgrms.catchtable.notification.dto.request.SendMessageToOwnerRequest;
import com.prgrms.catchtable.notification.service.NotificationService;
import com.prgrms.catchtable.owner.domain.Owner;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
Expand All @@ -19,12 +21,18 @@ public class NotificationEvent {
@Async
@TransactionalEventListener(phase = AFTER_COMMIT) // 호출한쪽의 트랜잭션이 커밋 된 후 이벤트 발생
public void sendMessage(SendMessageToMemberRequest request) {
notificationService.sendMessageAndSave(request.member(), request.content());
Member member = request.member();
if (member.isNotification_activated()) {
notificationService.sendMessageAndSave(member, request.content());
}
}

@Async
@TransactionalEventListener(phase = AFTER_COMMIT) // 호출한쪽의 트랜잭션이 커밋 된 후 이벤트 발생
public void sendMessage(SendMessageToOwnerRequest request) {
notificationService.sendMessageAndSave(request.owner(), request.content());
Owner owner = request.owner();
if (owner.isNotification_activated()) {
notificationService.sendMessageAndSave(request.owner(), request.content());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.prgrms.catchtable.notification.dto.request;

import com.prgrms.catchtable.common.notification.NotificationContent;
import com.prgrms.catchtable.member.domain.Member;
import lombok.Builder;

@Builder
public record SendMessageToMemberRequest(Member member,
NotificationContent content) {
String content) {

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.prgrms.catchtable.notification.dto.request;

import com.prgrms.catchtable.common.notification.NotificationContent;
import com.prgrms.catchtable.owner.domain.Owner;
import lombok.Builder;

@Builder
public record SendMessageToOwnerRequest(Owner owner,
NotificationContent content) {
String content) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import static org.springframework.http.HttpMethod.POST;

import com.prgrms.catchtable.common.exception.custom.BadRequestCustomException;
import com.prgrms.catchtable.common.notification.NotificationContent;
import com.prgrms.catchtable.member.domain.Member;
import com.prgrms.catchtable.member.repository.MemberRepository;
import com.prgrms.catchtable.notification.domain.NotificationMember;
Expand Down Expand Up @@ -38,11 +37,11 @@ public class NotificationService {
private final OwnerRepository ownerRepository; // 추후 삭제 예정
private JSONObject jsonObject;

public void sendMessageAndSave(Member member, NotificationContent content) {
public void sendMessageAndSave(Member member, String content) {
String url = "https://slack.com/api/chat.postMessage"; // slack 메세지를 보내도록 요청하는 Slack API

String email = member.getEmail();
String message = content.getMessage();
String message = content;
String slackId = getSlackIdByEmail(email); // 이메일을 통해 사용자의 슬랙 고유 ID 추출

requestToSendMessage(slackId, message); // 알림 요청 보내는 함수 호출
Expand All @@ -56,11 +55,11 @@ public void sendMessageAndSave(Member member, NotificationContent content) {

}

public void sendMessageAndSave(Owner owner, NotificationContent content) {
public void sendMessageAndSave(Owner owner, String content) {
String url = "https://slack.com/api/chat.postMessage"; // slack 메세지를 보내도록 요청하는 Slack API

String email = owner.getEmail();
String message = content.getMessage();
String message = content;
String slackId = getSlackIdByEmail(email);

requestToSendMessage(slackId, message);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/prgrms/catchtable/owner/domain/Owner.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public class Owner extends BaseEntity implements UserDetails {
@Column(name = "date_birth")
private LocalDate dateBirth;

@Column(name = "notification_activated")
private boolean notification_activated;

@OneToOne(fetch = LAZY)
@JoinColumn(name = "shop_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Shop shop;
Expand All @@ -75,6 +78,7 @@ public Owner(String name, String email, String password, String phoneNumber, Gen
this.gender = gender;
this.dateBirth = dateBirth;
this.role = Role.OWNER;
this.notification_activated = true;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.prgrms.catchtable.owner.repository;

import com.prgrms.catchtable.owner.domain.Owner;
import com.prgrms.catchtable.shop.domain.Shop;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

Expand All @@ -10,4 +11,6 @@ public interface OwnerRepository extends JpaRepository<Owner, Long> {

Optional<Owner> findOwnerByEmail(String email);

Optional<Owner> findOwnerByShop(Shop shop);

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ public ResponseEntity<ModifyReservationResponse> modifyReservation(

@DeleteMapping("/{reservationId}")
public ResponseEntity<CancelReservationResponse> cancelReservation(
@LogIn Member member,
@PathVariable("reservationId") Long reservationId) {
return ResponseEntity.ok(memberReservationService.cancelReservation(reservationId));
return ResponseEntity.ok(memberReservationService.cancelReservation(member, reservationId));
}

@GetMapping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import static com.prgrms.catchtable.common.exception.ErrorCode.ALREADY_OCCUPIED_RESERVATION_TIME;
import static com.prgrms.catchtable.common.exception.ErrorCode.ALREADY_PREOCCUPIED_RESERVATION_TIME;
import static com.prgrms.catchtable.common.exception.ErrorCode.NOT_EXIST_OWNER;
import static com.prgrms.catchtable.common.exception.ErrorCode.NOT_EXIST_RESERVATION;
import static com.prgrms.catchtable.common.exception.ErrorCode.NOT_EXIST_TIME;
import static com.prgrms.catchtable.common.notification.NotificationContent.RESERVATION_CANCELLED;
import static com.prgrms.catchtable.common.notification.NotificationContent.RESERVATION_COMPLETED;
import static com.prgrms.catchtable.reservation.domain.ReservationStatus.CANCELLED;
import static com.prgrms.catchtable.reservation.domain.ReservationStatus.COMPLETED;
import static com.prgrms.catchtable.reservation.dto.mapper.ReservationMapper.toCancelReservationResponse;
Expand All @@ -13,7 +16,12 @@

import com.prgrms.catchtable.common.exception.custom.BadRequestCustomException;
import com.prgrms.catchtable.common.exception.custom.NotFoundCustomException;
import com.prgrms.catchtable.common.notification.NotificationContent;
import com.prgrms.catchtable.member.domain.Member;
import com.prgrms.catchtable.notification.dto.request.SendMessageToMemberRequest;
import com.prgrms.catchtable.notification.dto.request.SendMessageToOwnerRequest;
import com.prgrms.catchtable.owner.domain.Owner;
import com.prgrms.catchtable.owner.repository.OwnerRepository;
import com.prgrms.catchtable.reservation.domain.Reservation;
import com.prgrms.catchtable.reservation.domain.ReservationTime;
import com.prgrms.catchtable.reservation.dto.mapper.ReservationMapper;
Expand All @@ -29,6 +37,7 @@
import com.prgrms.catchtable.shop.domain.Shop;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -40,6 +49,8 @@ public class MemberReservationService {
private final ReservationRepository reservationRepository;
private final ReservationAsync reservationAsync;
private final ReservationLockRepository reservationLockRepository;
private final OwnerRepository ownerRepository;
private final ApplicationEventPublisher publisher;

@Transactional
public CreateReservationResponse preOccupyReservation(Member member,
Expand All @@ -52,7 +63,8 @@ public CreateReservationResponse preOccupyReservation(Member member,
Thread.currentThread().interrupt();
}
}
ReservationTime reservationTime = reservationTimeRepository.findById(reservationTimeId)
ReservationTime reservationTime = reservationTimeRepository.findByIdWithShop(
reservationTimeId)
.orElseThrow(() -> {
reservationLockRepository.unlock(reservationTimeId);
return new NotFoundCustomException(NOT_EXIST_TIME);
Expand Down Expand Up @@ -91,6 +103,10 @@ public CreateReservationResponse registerReservation(Member member,
.member(member)
.build();
Reservation savedReservation = reservationRepository.save(reservation);

sendMessageToMemberAndOwner(member, reservationTime,
RESERVATION_COMPLETED); //점주와 회원에게 알림 발송

return toCreateReservationResponse(savedReservation);
}

Expand Down Expand Up @@ -129,7 +145,7 @@ public ModifyReservationResponse modifyReservation(Long reservavtionId,
}

@Transactional
public CancelReservationResponse cancelReservation(Long reservationId) {
public CancelReservationResponse cancelReservation(Member member, Long reservationId) {
Reservation reservation = reservationRepository.findByIdWithReservationTimeAndShop(
reservationId)
.orElseThrow(() -> new NotFoundCustomException(NOT_EXIST_RESERVATION));
Expand All @@ -140,9 +156,30 @@ public CancelReservationResponse cancelReservation(Long reservationId) {

reservationTime.setOccupiedFalse();

sendMessageToMemberAndOwner(member, reservationTime, RESERVATION_CANCELLED);

return toCancelReservationResponse(reservation);
}

private void sendMessageToMemberAndOwner(Member member, ReservationTime reservationTime,
NotificationContent content) {

Owner owner = ownerRepository.findOwnerByShop(reservationTime.getShop())
.orElseThrow(() -> new NotFoundCustomException(NOT_EXIST_OWNER));

SendMessageToMemberRequest sendMessageToMember = new SendMessageToMemberRequest(
member,
content.getMessage(reservationTime.getTime().toString())
); // 회원에게 보낼 해당 시간대의 예약 완료 알림 생성

SendMessageToOwnerRequest sendMessageToOwner = new SendMessageToOwnerRequest(owner,
content.getMessage(
reservationTime.getTime().toString())); // 해당 시간의 예약 취소 메세지 dto 생성

publisher.publishEvent(sendMessageToMember);
publisher.publishEvent(sendMessageToOwner); // 취소한 예약의 매장 점주에게 예약 취소 알림 발송
}

private void validateIsPreOccupied(ReservationTime reservationTime) {
if (reservationTime.isPreOccupied()) {
reservationLockRepository.unlock(reservationTime.getId());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.prgrms.catchtable.owner.repository;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;

import com.prgrms.catchtable.owner.domain.Owner;
import com.prgrms.catchtable.owner.fixture.OwnerFixture;
import com.prgrms.catchtable.shop.domain.Shop;
import com.prgrms.catchtable.shop.fixture.ShopFixture;
import com.prgrms.catchtable.shop.repository.ShopRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
class OwnerRepositoryTest {

@Autowired
private OwnerRepository ownerRepository;
@Autowired
private ShopRepository shopRepository;

@Test
@DisplayName("매장을 통해 점주를 찾을 수 있다")
void findByShop() {
Shop shop = ShopFixture.shop();
Shop savedShop = shopRepository.save(shop);

Owner owner = OwnerFixture.getOwner("injun", "injun2480");
owner.insertShop(savedShop);
Owner savedOwner = ownerRepository.save(owner);

Owner findOwner = ownerRepository.findOwnerByShop(savedShop).orElseThrow();

assertThat(findOwner).isEqualTo(savedOwner);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import com.prgrms.catchtable.member.MemberFixture;
import com.prgrms.catchtable.member.domain.Member;
import com.prgrms.catchtable.member.repository.MemberRepository;
import com.prgrms.catchtable.owner.domain.Owner;
import com.prgrms.catchtable.owner.fixture.OwnerFixture;
import com.prgrms.catchtable.owner.repository.OwnerRepository;
import com.prgrms.catchtable.reservation.domain.Reservation;
import com.prgrms.catchtable.reservation.domain.ReservationTime;
import com.prgrms.catchtable.reservation.dto.request.CreateReservationRequest;
Expand All @@ -28,6 +31,7 @@
import com.prgrms.catchtable.shop.domain.Shop;
import com.prgrms.catchtable.shop.repository.ShopRepository;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -47,13 +51,18 @@ class MemberReservationControllerTest extends BaseIntegrationTest {
private ReservationRepository reservationRepository;
@Autowired
private MemberRepository memberRepository;
@Autowired
private OwnerRepository ownerRepository;
private Member member = MemberFixture.member("dlswns661035@gmail.com");

@BeforeEach
void setUp() {
Shop shop = ShopData.getShop();
Shop savedShop = shopRepository.save(shop);

Owner owner = OwnerFixture.getOwner("injun", "injun2480");
owner.insertShop(savedShop);
ownerRepository.save(owner);

Member savedMember = memberRepository.save(member);

Expand All @@ -66,6 +75,11 @@ void setUp() {
httpHeaders.add("RefreshToken", token.getRefreshToken());
}

@AfterEach
void tearDown() {
shopRepository.deleteAll();
}

@Test
@DisplayName("예약 선점 api 호출에 성공한다.")
void preOccupyReservation() throws Exception {
Expand Down Expand Up @@ -193,6 +207,7 @@ void cancelReservation() throws Exception {
Reservation savedReservation = reservationRepository.save(reservation);

mockMvc.perform(delete("/reservations/{reservationId}", savedReservation.getId())
.headers(httpHeaders)
.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(CANCELLED.toString()));
Expand Down
Loading