From c334ced67d5476666b5041b4e1aa18c1b500f95a Mon Sep 17 00:00:00 2001 From: xb205 <62425964+devxb@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:30:40 +0900 Subject: [PATCH] release: 0.2.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 리뷰 등록시 회원 매너 온도 반영 기능 추가 및 리뷰 등록 리팩토링 (#158) * refactor: 회원 리뷰 등록 API 리팩토링 (#157) * feat: 회원 리뷰 등록시, 온도 반영 및 리뷰 피드백 반영 (#157) * feat: User 엔티티 메소드 추가 (#157) * feat: Review enum 필드 추가 (#157) * test: 리뷰 등록시 온도 업데이트에 대한 단위 테스트 (#157) * test: 리뷰 등록 기능 통합 테스트 (#157) * refactor: 회원 리뷰 등록 메소드 수정 (#157) * test: 불필요한 테스트 제거 및 CI 오류 수정 (#157) * test: MeetingRepository 테스트에서 시간과 id비교 비활서화 * refactor: 회원 탈퇴 URI 변경 (#162) * refactor: 회원 탈퇴 URI 수정 (#161) * test: 회원 탈퇴 URI 수정에 대한 테스트 수정 (#161) * fix: CI 에러 수정 (#161) * fix: 필드 값 비교를 위해 deprecated 된 메소드을 대체 (#161) * fix: 필드 값 비교를 위해 deprecated 된 메소드을 대체 (#161) * feat: fcm token 업데이트 api 추가 (#166) * fix: user_alert ddl의 pk에 auto_increment를 추가한다 * feat: token 업데이트 api를 추가한다 --------- Co-authored-by: ChoiDongKuen --- .../alert/controller/AlertController.java | 19 ++++++++ .../alert/domain/AlertRepository.java | 10 ++++ .../teumteum/alert/domain/AlertService.java | 18 +++++++- .../net/teumteum/alert/domain/UserAlert.java | 4 ++ .../request/UpdateAlertTokenRequest.java | 10 ++++ .../db/migration/V10__create_alert.sql | 2 +- .../alert/domain/AlertRepositoryTest.java | 46 +++++++++++++++++++ src/test/resources/schema.sql | 2 +- 8 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 src/main/java/net/teumteum/alert/domain/request/UpdateAlertTokenRequest.java create mode 100644 src/test/java/net/teumteum/alert/domain/AlertRepositoryTest.java diff --git a/src/main/java/net/teumteum/alert/controller/AlertController.java b/src/main/java/net/teumteum/alert/controller/AlertController.java index 349e4df..bf998ea 100644 --- a/src/main/java/net/teumteum/alert/controller/AlertController.java +++ b/src/main/java/net/teumteum/alert/controller/AlertController.java @@ -1,11 +1,16 @@ package net.teumteum.alert.controller; +import io.sentry.Sentry; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import net.teumteum.alert.domain.AlertService; import net.teumteum.alert.domain.request.RegisterAlertRequest; +import net.teumteum.alert.domain.request.UpdateAlertTokenRequest; +import net.teumteum.core.error.ErrorResponse; import net.teumteum.core.security.service.SecurityService; import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseStatus; @@ -24,4 +29,18 @@ public void registerAlert(@Valid @RequestBody RegisterAlertRequest registerAlert var loginUserId = securityService.getCurrentUserId(); alertService.registerAlert(loginUserId, registerAlertRequest); } + + @PatchMapping("/alerts") + @ResponseStatus(HttpStatus.OK) + public void updateAlert(@Valid @RequestBody UpdateAlertTokenRequest updateAlertTokenRequest) { + var loginUserId = securityService.getCurrentUserId(); + alertService.updateAlertToken(loginUserId, updateAlertTokenRequest); + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleIllegalArgumentException(IllegalArgumentException illegalArgumentException) { + Sentry.captureException(illegalArgumentException); + return ErrorResponse.of(illegalArgumentException); + } } diff --git a/src/main/java/net/teumteum/alert/domain/AlertRepository.java b/src/main/java/net/teumteum/alert/domain/AlertRepository.java index 87b2e6d..33c34cf 100644 --- a/src/main/java/net/teumteum/alert/domain/AlertRepository.java +++ b/src/main/java/net/teumteum/alert/domain/AlertRepository.java @@ -1,7 +1,10 @@ package net.teumteum.alert.domain; +import jakarta.persistence.LockModeType; import java.util.List; +import java.util.Optional; 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; @@ -9,4 +12,11 @@ public interface AlertRepository extends JpaRepository { @Query("select u from user_alert as u where u.userId in :userIds") List findAllByUserId(@Param("userIds") Iterable userIds); + + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("select u from user_alert as u where u.userId = :userId") + Optional findByUserIdWithLock(@Param("userId") Long userId); + + Optional findByUserId(@Param("userId") Long userId); + } diff --git a/src/main/java/net/teumteum/alert/domain/AlertService.java b/src/main/java/net/teumteum/alert/domain/AlertService.java index 8d876cf..65d0b8e 100644 --- a/src/main/java/net/teumteum/alert/domain/AlertService.java +++ b/src/main/java/net/teumteum/alert/domain/AlertService.java @@ -4,6 +4,7 @@ import java.util.Set; import lombok.RequiredArgsConstructor; import net.teumteum.alert.domain.request.RegisterAlertRequest; +import net.teumteum.alert.domain.request.UpdateAlertTokenRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,8 +17,21 @@ public class AlertService { @Transactional public void registerAlert(Long userId, RegisterAlertRequest registerAlertRequest) { - var alert = new UserAlert(null, userId, registerAlertRequest.token()); - alertRepository.save(alert); + alertRepository.findByUserId(userId) + .ifPresentOrElse(userAlert -> { + throw new IllegalArgumentException("이미 토큰이 생성된 user입니다. \"" + userId +"\""); + }, () -> { + var alert = new UserAlert(null, userId, registerAlertRequest.token()); + alertRepository.save(alert); + }); + } + + @Transactional + public void updateAlertToken(Long userId, UpdateAlertTokenRequest updateAlertTokenRequest) { + var userAlert = alertRepository.findByUserIdWithLock(userId) + .orElseThrow(() -> new IllegalArgumentException("userId에 해당하는 토큰을 찾을 수 없습니다.")); + + userAlert.updateToken(updateAlertTokenRequest.token()); } public List findAllByUserId(Set userIds) { diff --git a/src/main/java/net/teumteum/alert/domain/UserAlert.java b/src/main/java/net/teumteum/alert/domain/UserAlert.java index c5a537c..ec88c65 100644 --- a/src/main/java/net/teumteum/alert/domain/UserAlert.java +++ b/src/main/java/net/teumteum/alert/domain/UserAlert.java @@ -31,4 +31,8 @@ public class UserAlert { public String getToken() { return token; } + + public void updateToken(String token) { + this.token = token; + } } diff --git a/src/main/java/net/teumteum/alert/domain/request/UpdateAlertTokenRequest.java b/src/main/java/net/teumteum/alert/domain/request/UpdateAlertTokenRequest.java new file mode 100644 index 0000000..921feca --- /dev/null +++ b/src/main/java/net/teumteum/alert/domain/request/UpdateAlertTokenRequest.java @@ -0,0 +1,10 @@ +package net.teumteum.alert.domain.request; + +import jakarta.validation.constraints.NotNull; + +public record UpdateAlertTokenRequest( + @NotNull + String token +) { + +} diff --git a/src/main/resources/db/migration/V10__create_alert.sql b/src/main/resources/db/migration/V10__create_alert.sql index 40d54db..ec03a7f 100644 --- a/src/main/resources/db/migration/V10__create_alert.sql +++ b/src/main/resources/db/migration/V10__create_alert.sql @@ -1,5 +1,5 @@ create table if not exists user_alert( - id bigint primary key, + id bigint primary key auto_increment, user_id bigint unique not null, token text not null ); diff --git a/src/test/java/net/teumteum/alert/domain/AlertRepositoryTest.java b/src/test/java/net/teumteum/alert/domain/AlertRepositoryTest.java new file mode 100644 index 0000000..baff4e6 --- /dev/null +++ b/src/test/java/net/teumteum/alert/domain/AlertRepositoryTest.java @@ -0,0 +1,46 @@ +package net.teumteum.alert.domain; + +import jakarta.persistence.EntityManager; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@DataJpaTest +@ExtendWith(SpringExtension.class) +@DisplayName("AlertRepository 클래스의") +class AlertRepositoryTest { + + @Autowired + private AlertRepository alertRepository; + + @Autowired + private EntityManager entityManager; + + @Nested + @DisplayName("findByUserIdWithLock 메소드는") + class findByUserIdWithLock_method { + + @Test + @DisplayName("userId로 userAlert를 조회한다.") + void find_userAlert_by_userId() { + // given + var userId = 1L; + var userAlert = new UserAlert(1L, userId, "token"); + + alertRepository.saveAndFlush(userAlert); + entityManager.clear(); + + // when + var result = alertRepository.findByUserIdWithLock(userId); + + // then + Assertions.assertThat(result).isPresent(); + } + } + +} diff --git a/src/test/resources/schema.sql b/src/test/resources/schema.sql index 28efff4..dbf1fcb 100644 --- a/src/test/resources/schema.sql +++ b/src/test/resources/schema.sql @@ -79,7 +79,7 @@ create table if not exists withdraw_reasons ); create table if not exists user_alert( - id bigint primary key, + id bigint primary key auto_increment, user_id bigint unique not null, token text not null );