From 2f9daf8020adbdcc492c92699f3dfe2cafc50886 Mon Sep 17 00:00:00 2001 From: Hyunseok Ko <56003992+lcomment@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:52:47 +0900 Subject: [PATCH 01/11] =?UTF-8?q?Refactor=20|=20CAKK-58=20|=20User=20?= =?UTF-8?q?=EB=B9=84=EC=A6=88=EB=8B=88=EC=8A=A4=EC=97=90=20Facade=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feature | CAKK-58 | User Command 관련 Facade 구현 * Refactor | CAKK-58 | 미사용 Writer 삭제 * Refactor | CAKK-58 | Service 레이어에 Facade 패턴 적용 * Test | CAKK-58 | Facade 테스트 추상 클래스 구축 및 테스트 작성 --- .../cakk/api/service/user/SignService.java | 6 +- .../cakk/api/service/user/UserService.java | 20 +--- .../com/cakk/api/common/base/ServiceTest.java | 9 +- .../user/MyPageIntegrationTest.java | 6 +- .../api/service/user/SignServiceTest.java | 20 ++-- .../api/service/user/UserServiceTest.java | 52 ++------ cakk-domain/mysql/build.gradle | 2 + .../entity/user/BusinessInformation.java | 7 +- .../cakk/domain/mysql/entity/user/User.java | 30 +++++ .../user/UserCommandFacade.java} | 19 ++- .../repository/query/UserQueryRepository.java | 33 +++++ .../mysql/repository/reader/UserReader.java | 14 +++ .../writer/BusinessInformationWriter.java | 29 ----- .../repository/writer/CakeHeartWriter.java | 29 ----- .../writer/CakeShopHeartWriter.java | 27 ----- .../repository/writer/CakeShopLikeWriter.java | 16 --- .../annotation/TestWithDisplayName.java | 41 +++++++ .../java/com/cakk/domain/base/DomainTest.java | 4 +- .../java/com/cakk/domain/base/FacadeTest.java | 94 +++++++++++++++ .../facade/user/UserCommandFacadeTest.java | 113 ++++++++++++++++++ .../facade/user/UserHeartFacadeTest.java | 8 +- .../facade/user/UserLikeFacadeTest.java | 8 +- 22 files changed, 401 insertions(+), 186 deletions(-) rename cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/{repository/writer/UserWriter.java => facade/user/UserCommandFacade.java} (59%) create mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/UserQueryRepository.java delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/BusinessInformationWriter.java delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeHeartWriter.java delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopHeartWriter.java delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopLikeWriter.java create mode 100644 cakk-domain/mysql/src/test/java/com/cakk/domain/annotation/TestWithDisplayName.java create mode 100644 cakk-domain/mysql/src/test/java/com/cakk/domain/base/FacadeTest.java create mode 100644 cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserCommandFacadeTest.java diff --git a/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java b/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java index 5581c06f..111c3dfd 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java @@ -14,8 +14,8 @@ import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; import com.cakk.domain.mysql.entity.user.User; +import com.cakk.domain.mysql.facade.user.UserCommandFacade; import com.cakk.domain.mysql.repository.reader.UserReader; -import com.cakk.domain.mysql.repository.writer.UserWriter; import com.cakk.domain.redis.repository.TokenRedisRepository; @Service @@ -26,13 +26,13 @@ public class SignService { private final JwtProvider jwtProvider; private final UserReader userReader; - private final UserWriter userWriter; + private final UserCommandFacade userCommandFacade; private final TokenRedisRepository tokenRedisRepository; @Transactional public JwtResponse signUp(final UserSignUpRequest dto) { final String providerId = oidcProviderFactory.getProviderId(dto.provider(), dto.idToken()); - final User user = userWriter.create(UserMapper.supplyUserBy(dto, providerId)); + final User user = userCommandFacade.create(UserMapper.supplyUserBy(dto, providerId)); return JwtResponse.from(jwtProvider.generateToken(user)); } diff --git a/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java b/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java index 13ff7168..96c545f7 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java @@ -11,21 +11,15 @@ import com.cakk.domain.mysql.dto.param.user.ProfileUpdateParam; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.entity.user.UserWithdrawal; +import com.cakk.domain.mysql.facade.user.UserCommandFacade; import com.cakk.domain.mysql.repository.reader.UserReader; -import com.cakk.domain.mysql.repository.writer.BusinessInformationWriter; -import com.cakk.domain.mysql.repository.writer.CakeHeartWriter; -import com.cakk.domain.mysql.repository.writer.CakeShopHeartWriter; -import com.cakk.domain.mysql.repository.writer.UserWriter; @Service @RequiredArgsConstructor public class UserService { private final UserReader userReader; - private final UserWriter userWriter; - private final CakeShopHeartWriter cakeShopHeartWriter; - private final CakeHeartWriter cakeHeartWriter; - private final BusinessInformationWriter businessInformationWriter; + private final UserCommandFacade userCommandFacade; @Transactional(readOnly = true) public ProfileInformationResponse findProfile(final User signInUser) { @@ -39,18 +33,14 @@ public void updateInformation(final User signInUser, final ProfileUpdateRequest final User user = userReader.findByUserId(signInUser.getId()); final ProfileUpdateParam param = UserMapper.supplyProfileUpdateParamBy(dto); - user.updateProfile(param); + userCommandFacade.updateProfile(user, param); } @Transactional public void withdraw(final User signInUser) { - final User user = userReader.findByUserId(signInUser.getId()); + final User user = userReader.findByIdWithAll(signInUser.getId()); final UserWithdrawal withdrawal = UserMapper.supplyUserWithdrawalBy(user); - cakeHeartWriter.deleteAllByUser(user); - cakeShopHeartWriter.deleteAllByUser(user); - businessInformationWriter.deleteAllByUser(user); - - userWriter.delete(user, withdrawal); + userCommandFacade.withdraw(user, withdrawal); } } diff --git a/cakk-api/src/test/java/com/cakk/api/common/base/ServiceTest.java b/cakk-api/src/test/java/com/cakk/api/common/base/ServiceTest.java index 7af3e991..43e3dc34 100644 --- a/cakk-api/src/test/java/com/cakk/api/common/base/ServiceTest.java +++ b/cakk-api/src/test/java/com/cakk/api/common/base/ServiceTest.java @@ -1,5 +1,7 @@ package com.cakk.api.common.base; +import java.time.LocalDate; + import org.junit.jupiter.api.extension.ExtendWith; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; @@ -18,6 +20,7 @@ import com.navercorp.fixturemonkey.jakarta.validation.plugin.JakartaValidationPlugin; import com.cakk.common.enums.Provider; +import com.cakk.common.enums.Role; import com.cakk.domain.mysql.config.JpaConfig; import com.cakk.domain.mysql.entity.user.User; @@ -55,10 +58,14 @@ protected final FixtureMonkey getBuilderMonkey() { } protected User getUser() { - return getReflectionMonkey().giveMeBuilder(User.class) + return getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", Arbitraries.of(Provider.class)) .set("providerId", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) + .set("email", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) + .set("nickname", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) + .set("birthday", LocalDate.now()) + .set("role", Arbitraries.of(Role.class)) .sample(); } diff --git a/cakk-api/src/test/java/com/cakk/api/integration/user/MyPageIntegrationTest.java b/cakk-api/src/test/java/com/cakk/api/integration/user/MyPageIntegrationTest.java index 0c1012b6..a971ab99 100644 --- a/cakk-api/src/test/java/com/cakk/api/integration/user/MyPageIntegrationTest.java +++ b/cakk-api/src/test/java/com/cakk/api/integration/user/MyPageIntegrationTest.java @@ -4,7 +4,6 @@ import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.*; import java.time.LocalDate; -import java.util.Map; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -28,11 +27,12 @@ import com.cakk.common.exception.CakkException; import com.cakk.common.response.ApiResponse; import com.cakk.domain.mysql.entity.user.User; -import com.cakk.domain.mysql.repository.reader.UserReader; @SqlGroup({ @Sql(scripts = { - "/sql/insert-test-user.sql" + "/sql/insert-test-user.sql", + "/sql/insert-cake-shop.sql", + "/sql/insert-heart.sql", }, executionPhase = BEFORE_TEST_METHOD), @Sql(scripts = "/sql/delete-all.sql", executionPhase = AFTER_TEST_METHOD) }) diff --git a/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java b/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java index fcdfaf8c..c86a0c0d 100644 --- a/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java +++ b/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java @@ -22,8 +22,8 @@ import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; import com.cakk.domain.mysql.entity.user.User; +import com.cakk.domain.mysql.facade.user.UserCommandFacade; import com.cakk.domain.mysql.repository.reader.UserReader; -import com.cakk.domain.mysql.repository.writer.UserWriter; import com.cakk.domain.redis.repository.TokenRedisRepository; @DisplayName("Sign 관련 비즈니스 로직 테스트") @@ -42,7 +42,7 @@ class SignServiceTest extends ServiceTest { private UserReader userReader; @Mock - private UserWriter userWriter; + private UserCommandFacade userCommandFacade; @Mock private TokenRedisRepository tokenRedisRepository; @@ -51,7 +51,7 @@ class SignServiceTest extends ServiceTest { void signUp1() { // given UserSignUpRequest dto = getConstructorMonkey().giveMeOne(UserSignUpRequest.class); - User user = getReflectionMonkey().giveMeBuilder(User.class) + User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", dto.provider()) .set("providerId", Arbitraries.strings().alpha().ofMinLength(10).ofMaxLength(20)) @@ -63,7 +63,7 @@ void signUp1() { .sample(); doReturn(user.getProviderId()).when(oidcProviderFactory).getProviderId(dto.provider(), dto.idToken()); - doReturn(user).when(userWriter).create(any(User.class)); + doReturn(user).when(userCommandFacade).create(any(User.class)); doReturn(jwt).when(jwtProvider).generateToken(user); // when @@ -75,7 +75,7 @@ void signUp1() { Assertions.assertNotNull(result.grantType()); verify(oidcProviderFactory, times(1)).getProviderId(dto.provider(), dto.idToken()); - verify(userWriter, times(1)).create(any(User.class)); + verify(userCommandFacade, times(1)).create(any(User.class)); verify(jwtProvider, times(1)).generateToken(user); } @@ -83,7 +83,7 @@ void signUp1() { void signUp2() { // given UserSignUpRequest dto = getConstructorMonkey().giveMeOne(UserSignUpRequest.class); - User user = getReflectionMonkey().giveMeBuilder(User.class) + User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", dto.provider()) .set("providerId", Arbitraries.strings().alpha().ofMinLength(10).ofMaxLength(20)) @@ -98,7 +98,7 @@ void signUp2() { ReturnCode.EXPIRED_JWT_TOKEN.getMessage()); verify(oidcProviderFactory, times(1)).getProviderId(dto.provider(), dto.idToken()); - verify(userWriter, times(0)).create(any(User.class)); + verify(userCommandFacade, times(0)).create(any(User.class)); verify(jwtProvider, times(0)).generateToken(user); } @@ -107,7 +107,7 @@ void signIn() { // given UserSignInRequest dto = getConstructorMonkey().giveMeOne(UserSignInRequest.class); String providerId = Arbitraries.strings().alpha().ofMinLength(10).ofMaxLength(20).sample(); - User user = getReflectionMonkey().giveMeBuilder(User.class) + User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", dto.provider()) .set("providerId", providerId) @@ -140,7 +140,7 @@ void signIn2() { // given UserSignInRequest dto = getConstructorMonkey().giveMeOne(UserSignInRequest.class); String providerId = Arbitraries.strings().alpha().ofMinLength(10).ofMaxLength(20).sample(); - User user = getReflectionMonkey().giveMeBuilder(User.class) + User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", dto.provider()) .set("providerId", providerId) @@ -164,7 +164,7 @@ void signIn2() { void recreateToken() { // given final String refreshToken = "refresh token"; - final User user = getReflectionMonkey().giveMeBuilder(User.class) + final User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("providerId", Arbitraries.strings().alpha().ofMinLength(10).ofMaxLength(20)) .set("createdAt", LocalDateTime.now()) diff --git a/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java b/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java index efbcbf1f..5eaf6d6b 100644 --- a/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java +++ b/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java @@ -17,11 +17,8 @@ import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; import com.cakk.domain.mysql.entity.user.User; +import com.cakk.domain.mysql.facade.user.UserCommandFacade; import com.cakk.domain.mysql.repository.reader.UserReader; -import com.cakk.domain.mysql.repository.writer.BusinessInformationWriter; -import com.cakk.domain.mysql.repository.writer.CakeHeartWriter; -import com.cakk.domain.mysql.repository.writer.CakeShopHeartWriter; -import com.cakk.domain.mysql.repository.writer.UserWriter; @DisplayName("유저 관련 비즈니스 로직 테스트") class UserServiceTest extends ServiceTest { @@ -33,21 +30,12 @@ class UserServiceTest extends ServiceTest { private UserReader userReader; @Mock - private UserWriter userWriter; - - @Mock - private CakeShopHeartWriter cakeShopHeartWriter; - - @Mock - private CakeHeartWriter cakeHeartWriter; - - @Mock - private BusinessInformationWriter businessInformationWriter; + private UserCommandFacade userCommandFacade; @TestWithDisplayName("유저 프로필을 조회한다.") void findProfile1() { // given - final User user = getReflectionMonkey().giveMeBuilder(User.class) + final User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", Arbitraries.of(Provider.class)) .set("providerId", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) @@ -67,7 +55,7 @@ void findProfile1() { @TestWithDisplayName("유저가 존재하지 않으면 유저 프로필 조회에 실패한다.") void findProfile2() { // given - final User user = getReflectionMonkey().giveMeBuilder(User.class) + final User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", Arbitraries.of(Provider.class)) .set("providerId", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) @@ -86,7 +74,7 @@ void findProfile2() { @TestWithDisplayName("유저 프로필을 수정한다.") void updateInformation() { // given - final User user = getReflectionMonkey().giveMeBuilder(User.class) + final User user = getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("provider", Arbitraries.of(Provider.class)) .set("providerId", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) @@ -104,44 +92,30 @@ void updateInformation() { @TestWithDisplayName("유저를 탈퇴한다.") void withdraw1() { // given - final User user = getReflectionMonkey().giveMeBuilder(User.class) - .set("id", Arbitraries.longs().greaterOrEqual(10)) - .set("provider", Arbitraries.of(Provider.class)) - .set("providerId", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) - .sample(); + final User user = getUser(); - doReturn(user).when(userReader).findByUserId(user.getId()); + doReturn(user).when(userReader).findByIdWithAll(user.getId()); // when & then Assertions.assertDoesNotThrow(() -> userService.withdraw(user)); - verify(userReader, times(1)).findByUserId(user.getId()); - verify(cakeHeartWriter, times(1)).deleteAllByUser(user); - verify(cakeShopHeartWriter, times(1)).deleteAllByUser(user); - verify(businessInformationWriter, times(1)).deleteAllByUser(user); - verify(userWriter, times(1)).delete(any(), any()); + verify(userReader, times(1)).findByIdWithAll(user.getId()); + verify(userCommandFacade, times(1)).withdraw(any(), any()); } @TestWithDisplayName("유저가 없는 경우, 탈퇴에 실패한다.") void withdraw2() { // given - final User user = getReflectionMonkey().giveMeBuilder(User.class) - .set("id", Arbitraries.longs().greaterOrEqual(10)) - .set("provider", Arbitraries.of(Provider.class)) - .set("providerId", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) - .sample(); + final User user = getUser(); - doThrow(new CakkException(ReturnCode.NOT_EXIST_USER)).when(userReader).findByUserId(user.getId()); + doThrow(new CakkException(ReturnCode.NOT_EXIST_USER)).when(userReader).findByIdWithAll(user.getId()); // when & then Assertions.assertThrows(CakkException.class, () -> userService.withdraw(user), ReturnCode.NOT_EXIST_USER.getMessage()); - verify(userReader, times(1)).findByUserId(user.getId()); - verify(cakeHeartWriter, times(0)).deleteAllByUser(user); - verify(cakeShopHeartWriter, times(0)).deleteAllByUser(user); - verify(businessInformationWriter, times(0)).deleteAllByUser(user); - verify(userWriter, times(0)).delete(any(), any()); + verify(userReader, times(1)).findByIdWithAll(user.getId()); + verify(userCommandFacade, never()).withdraw(any(), any()); } } diff --git a/cakk-domain/mysql/build.gradle b/cakk-domain/mysql/build.gradle index 289af826..0ce2d0f0 100644 --- a/cakk-domain/mysql/build.gradle +++ b/cakk-domain/mysql/build.gradle @@ -11,6 +11,8 @@ dependencies { testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.0.23") testImplementation('org.assertj:assertj-core') testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation('org.mockito:mockito-core') + testImplementation('org.mockito:mockito-junit-jupiter') testRuntimeOnly('org.junit.platform:junit-platform-launcher') // querydsl diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/BusinessInformation.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/BusinessInformation.java index 0f463261..b843c963 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/BusinessInformation.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/BusinessInformation.java @@ -56,7 +56,7 @@ public class BusinessInformation extends AuditEntity { @ColumnDefault("0") @Convert(converter = VerificationStatusConverter.class) @Column(name = "verification_status", nullable = false) - private VerificationStatus verificationStatus = VerificationStatus.UNREQUESTED; + private VerificationStatus verificationStatus; @OneToOne @MapsId @@ -85,6 +85,11 @@ public void updateBusinessOwner(final VerificationPolicy verificationPolicy, fin verificationStatus = verificationPolicy.approveToBusinessOwner(verificationStatus); } + public void unLinkBusinessOwner() { + user = null; + verificationStatus = VerificationStatus.UNREQUESTED; + } + public boolean isBusinessOwnerCandidate(VerificationPolicy verificationPolicy) { return verificationPolicy.isCandidate(Objects.requireNonNull(verificationStatus)); } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/User.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/User.java index e2ae1556..7884c6a7 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/User.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/user/User.java @@ -2,8 +2,11 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -11,10 +14,12 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import org.hibernate.annotations.ColumnDefault; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; @@ -31,7 +36,10 @@ import com.cakk.domain.mysql.dto.param.user.ProfileUpdateParam; import com.cakk.domain.mysql.entity.audit.AuditEntity; import com.cakk.domain.mysql.entity.cake.Cake; +import com.cakk.domain.mysql.entity.cake.CakeHeart; import com.cakk.domain.mysql.entity.shop.CakeShop; +import com.cakk.domain.mysql.entity.shop.CakeShopHeart; +import com.cakk.domain.mysql.entity.shop.CakeShopLike; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -83,6 +91,22 @@ public class User extends AuditEntity { @Column(name = "deleted_at") private LocalDateTime deletedAt; + @JsonIgnore + @OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST) + private Set businessInformationSet = new HashSet<>(); + + @JsonIgnore + @OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true) + private Set cakeHearts = new HashSet<>(); + + @JsonIgnore + @OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true) + private Set cakeShopHearts = new HashSet<>(); + + @JsonIgnore + @OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true) + private Set cakeShopLikes = new HashSet<>(); + @Builder public User( Provider provider, @@ -136,6 +160,12 @@ public void unHeartCakeShop(final CakeShop cakeShop) { cakeShop.unHeart(this); } + public void unHeartAndLikeAll() { + cakeHearts.clear(); + cakeShopHearts.clear(); + cakeShopLikes.clear(); + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/UserWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserCommandFacade.java similarity index 59% rename from cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/UserWriter.java rename to cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserCommandFacade.java index e32ba9b5..e1b6b465 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/UserWriter.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserCommandFacade.java @@ -1,18 +1,20 @@ -package com.cakk.domain.mysql.repository.writer; +package com.cakk.domain.mysql.facade.user; import lombok.RequiredArgsConstructor; import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; -import com.cakk.domain.mysql.annotation.Writer; +import com.cakk.domain.mysql.annotation.DomainFacade; +import com.cakk.domain.mysql.dto.param.user.ProfileUpdateParam; +import com.cakk.domain.mysql.entity.user.BusinessInformation; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.entity.user.UserWithdrawal; import com.cakk.domain.mysql.repository.jpa.UserJpaRepository; import com.cakk.domain.mysql.repository.jpa.UserWithdrawalJpaRepository; -@Writer @RequiredArgsConstructor -public class UserWriter { +@DomainFacade +public class UserCommandFacade { private final UserJpaRepository userJpaRepository; private final UserWithdrawalJpaRepository userWithdrawalJpaRepository; @@ -26,7 +28,14 @@ public User create(final User user) { return userJpaRepository.save(user); } - public void delete(final User user, final UserWithdrawal withdrawal) { + public void updateProfile(final User user, final ProfileUpdateParam param) { + user.updateProfile(param); + } + + public void withdraw(final User user, final UserWithdrawal withdrawal) { + user.unHeartAndLikeAll(); + user.getBusinessInformationSet().forEach(BusinessInformation::unLinkBusinessOwner); + userWithdrawalJpaRepository.save(withdrawal); userJpaRepository.delete(user); } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/UserQueryRepository.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/UserQueryRepository.java new file mode 100644 index 00000000..bbc1f515 --- /dev/null +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/UserQueryRepository.java @@ -0,0 +1,33 @@ +package com.cakk.domain.mysql.repository.query; + +import static com.cakk.domain.mysql.entity.user.QUser.*; + +import org.springframework.stereotype.Repository; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +import com.cakk.domain.mysql.entity.user.User; + +@Repository +@RequiredArgsConstructor +public class UserQueryRepository { + + private final JPAQueryFactory queryFactory; + + public User searchByIdWithAll(final Long userId) { + return queryFactory.selectFrom(user) + .leftJoin(user.businessInformationSet).fetchJoin() + .leftJoin(user.cakeHearts).fetchJoin() + .leftJoin(user.cakeShopHearts).fetchJoin() + .leftJoin(user.cakeShopLikes).fetchJoin() + .where(eqUserId(userId)) + .fetchOne(); + } + + private BooleanExpression eqUserId(final Long userId) { + return user.id.eq(userId); + } +} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/UserReader.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/UserReader.java index a1bd6094..8bc4dd7f 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/UserReader.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/UserReader.java @@ -1,5 +1,7 @@ package com.cakk.domain.mysql.repository.reader; +import static java.util.Objects.*; + import java.util.List; import lombok.RequiredArgsConstructor; @@ -9,12 +11,14 @@ import com.cakk.domain.mysql.annotation.Reader; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.repository.jpa.UserJpaRepository; +import com.cakk.domain.mysql.repository.query.UserQueryRepository; @Reader @RequiredArgsConstructor public class UserReader { private final UserJpaRepository userJpaRepository; + private final UserQueryRepository userQueryRepository; public User findByUserId(final Long userId) { return userJpaRepository.findById(userId).orElseThrow(() -> new CakkException(ReturnCode.NOT_EXIST_USER)); @@ -24,6 +28,16 @@ public User findByProviderId(final String providerId) { return userJpaRepository.findByProviderId(providerId).orElseThrow(() -> new CakkException(ReturnCode.NOT_EXIST_USER)); } + public User findByIdWithAll(final Long userId) { + final User user = userQueryRepository.searchByIdWithAll(userId); + + if (isNull(user)) { + throw new CakkException(ReturnCode.NOT_EXIST_USER); + } + + return user; + } + public List findAll() { return userJpaRepository.findAll(); } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/BusinessInformationWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/BusinessInformationWriter.java deleted file mode 100644 index 20e2393d..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/BusinessInformationWriter.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.cakk.domain.mysql.repository.writer; - -import static java.util.Objects.*; - -import java.util.List; - -import lombok.RequiredArgsConstructor; - -import com.cakk.domain.mysql.annotation.Writer; -import com.cakk.domain.mysql.entity.user.BusinessInformation; -import com.cakk.domain.mysql.entity.user.User; -import com.cakk.domain.mysql.repository.jpa.BusinessInformationJpaRepository; - -@Writer -@RequiredArgsConstructor -public class BusinessInformationWriter { - - private final BusinessInformationJpaRepository businessInformationJpaRepository; - - public void deleteAllByUser(final User user) { - List businessInformationList = businessInformationJpaRepository.findAllByUser(user); - - if (isNull(businessInformationList) || businessInformationList.isEmpty()) { - return; - } - - businessInformationJpaRepository.deleteAllInBatch(businessInformationList); - } -} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeHeartWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeHeartWriter.java deleted file mode 100644 index 41e0a9e9..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeHeartWriter.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.cakk.domain.mysql.repository.writer; - -import static java.util.Objects.*; - -import java.util.List; - -import lombok.RequiredArgsConstructor; - -import com.cakk.domain.mysql.annotation.Writer; -import com.cakk.domain.mysql.entity.cake.CakeHeart; -import com.cakk.domain.mysql.entity.user.User; -import com.cakk.domain.mysql.repository.jpa.CakeHeartJpaRepository; - -@Writer -@RequiredArgsConstructor -public class CakeHeartWriter { - - private final CakeHeartJpaRepository cakeHeartJpaRepository; - - public void deleteAllByUser(final User user) { - final List cakeHearts = cakeHeartJpaRepository.findAllByUser(user); - - if (isNull(cakeHearts) || cakeHearts.isEmpty()) { - return; - } - - cakeHeartJpaRepository.deleteAllInBatch(cakeHearts); - } -} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopHeartWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopHeartWriter.java deleted file mode 100644 index 7c35e04f..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopHeartWriter.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.cakk.domain.mysql.repository.writer; - -import java.util.List; - -import lombok.RequiredArgsConstructor; - -import com.cakk.domain.mysql.annotation.Writer; -import com.cakk.domain.mysql.entity.shop.CakeShopHeart; -import com.cakk.domain.mysql.entity.user.User; -import com.cakk.domain.mysql.repository.jpa.CakeShopHeartJpaRepository; - -@Writer -@RequiredArgsConstructor -public class CakeShopHeartWriter { - - private final CakeShopHeartJpaRepository cakeShopHeartJpaRepository; - - public void deleteAllByUser(final User user) { - final List cakeShopHearts = cakeShopHeartJpaRepository.findAllByUser(user); - - if (cakeShopHearts == null || cakeShopHearts.isEmpty()) { - return; - } - - cakeShopHeartJpaRepository.deleteAllInBatch(cakeShopHearts); - } -} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopLikeWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopLikeWriter.java deleted file mode 100644 index 486e1a5f..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopLikeWriter.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.cakk.domain.mysql.repository.writer; - -import lombok.RequiredArgsConstructor; - -import com.cakk.domain.mysql.annotation.Writer; -import com.cakk.domain.mysql.entity.shop.CakeShop; -import com.cakk.domain.mysql.entity.user.User; - -@Writer -@RequiredArgsConstructor -public class CakeShopLikeWriter { - - public void like(final CakeShop cakeShop, final User user) { - user.likeCakeShop(cakeShop); - } -} diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/annotation/TestWithDisplayName.java b/cakk-domain/mysql/src/test/java/com/cakk/domain/annotation/TestWithDisplayName.java new file mode 100644 index 00000000..ff42f323 --- /dev/null +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/annotation/TestWithDisplayName.java @@ -0,0 +1,41 @@ +package com.cakk.domain.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Test +@DisplayNameGeneration(TestWithDisplayName.TestDisplayNameGenerator.class) +public @interface TestWithDisplayName { + + String value() default ""; + + class TestDisplayNameGenerator extends DisplayNameGenerator.Standard { + + @Override + public String generateDisplayNameForClass(Class testClass) { + final TestWithDisplayName testWithDisplayName = testClass.getAnnotation(TestWithDisplayName.class); + + if (testWithDisplayName != null && !testWithDisplayName.value().isEmpty()) { + return testWithDisplayName.value(); + } + + return super.generateDisplayNameForClass(testClass); + } + + @Override + public String generateDisplayNameForMethod(Class testClass, Method testMethod) { + return testMethod.getName(); + } + } +} diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/base/DomainTest.java b/cakk-domain/mysql/src/test/java/com/cakk/domain/base/DomainTest.java index d02275ba..6c61d86e 100644 --- a/cakk-domain/mysql/src/test/java/com/cakk/domain/base/DomainTest.java +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/base/DomainTest.java @@ -59,7 +59,7 @@ protected static Point supplyPointBy(Double latitude, Double longitude) { } protected User getUserFixture(Role role) { - return getReflectionMonkey().giveMeBuilder(User.class) + return getConstructorMonkey().giveMeBuilder(User.class) .set("id", Arbitraries.longs().greaterOrEqual(10)) .set("email", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(50)) .set("role", role) @@ -94,7 +94,7 @@ protected BusinessInformation getBusinessInformationFixtureWithCakeShop(Verifica } protected CertificationParam getCertificationParamFixtureWithUser(User user) { - return getBuilderMonkey().giveMeBuilder(CertificationParam.class) + return getConstructorMonkey().giveMeBuilder(CertificationParam.class) .set("businessRegistrationImageUrl", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(20)) .set("idCardImageUrl", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(20)) .set("cakeShopId", Arbitraries.longs().greaterOrEqual(0)) diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/base/FacadeTest.java b/cakk-domain/mysql/src/test/java/com/cakk/domain/base/FacadeTest.java new file mode 100644 index 00000000..9d8942ac --- /dev/null +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/base/FacadeTest.java @@ -0,0 +1,94 @@ +package com.cakk.domain.base; + +import java.time.LocalDate; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.PrecisionModel; +import org.mockito.junit.jupiter.MockitoExtension; + +import net.jqwik.api.Arbitraries; + +import com.navercorp.fixturemonkey.FixtureMonkey; +import com.navercorp.fixturemonkey.api.introspector.BuilderArbitraryIntrospector; +import com.navercorp.fixturemonkey.api.introspector.ConstructorPropertiesArbitraryIntrospector; +import com.navercorp.fixturemonkey.api.introspector.FieldReflectionArbitraryIntrospector; +import com.navercorp.fixturemonkey.customizer.Values; +import com.navercorp.fixturemonkey.jakarta.validation.plugin.JakartaValidationPlugin; + +import com.cakk.common.enums.Provider; +import com.cakk.common.enums.Role; +import com.cakk.domain.mysql.entity.cake.Cake; +import com.cakk.domain.mysql.entity.shop.CakeShop; +import com.cakk.domain.mysql.entity.user.User; + +@ExtendWith(MockitoExtension.class) +public abstract class FacadeTest { + + private static final int SPATIAL_REFERENCE_IDENTIFIER_NUMBER = 4326; + + private static final GeometryFactory geometryFactory = new GeometryFactory( + new PrecisionModel(), + SPATIAL_REFERENCE_IDENTIFIER_NUMBER + ); + + protected final FixtureMonkey getConstructorMonkey() { + return FixtureMonkey.builder() + .plugin(new JakartaValidationPlugin()) + .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) + .build(); + } + + protected final FixtureMonkey getReflectionMonkey() { + return FixtureMonkey.builder() + .plugin(new JakartaValidationPlugin()) + .objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE) + .build(); + } + + protected final FixtureMonkey getBuilderMonkey() { + return FixtureMonkey.builder() + .plugin(new JakartaValidationPlugin()) + .objectIntrospector(BuilderArbitraryIntrospector.INSTANCE) + .build(); + } + + protected User getUserFixture(final Role role) { + return getConstructorMonkey().giveMeBuilder(User.class) + .set("id", Arbitraries.longs().greaterOrEqual(10)) + .set("provider", Arbitraries.of(Provider.class)) + .set("providerId", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) + .set("email", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) + .set("nickname", Arbitraries.strings().withCharRange('a', 'z').ofMinLength(1).ofMaxLength(50)) + .set("birthday", LocalDate.now()) + .set("role", role) + .sample(); + } + + protected Cake getCakeFixture() { + return getConstructorMonkey().giveMeBuilder(Cake.class) + .set("cakeImageUrl", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(50)) + .set("cakeShop", Values.just(getCakeShopFixture())) + .sample(); + } + + protected CakeShop getCakeShopFixture() { + return getConstructorMonkey().giveMeBuilder(CakeShop.class) + .set("shopName", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(30)) + .set("shopBio", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(40)) + .set("shopDescription", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(500)) + .set("likeCount", 0) + .set("heartCount", 0) + .set("location", supplyPointBy( + Arbitraries.doubles().between(-90, 90).sample(), + Arbitraries.doubles().between(-180, 180).sample()) + ) + .sample(); + } + + public static Point supplyPointBy(Double latitude, Double longitude) { + return geometryFactory.createPoint(new Coordinate(longitude, latitude)); + } +} diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserCommandFacadeTest.java b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserCommandFacadeTest.java new file mode 100644 index 00000000..69cbf4d3 --- /dev/null +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserCommandFacadeTest.java @@ -0,0 +1,113 @@ +package com.cakk.domain.facade.user; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Optional; + +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import net.jqwik.api.Arbitraries; + +import com.cakk.common.enums.Gender; +import com.cakk.common.enums.ReturnCode; +import com.cakk.common.enums.Role; +import com.cakk.common.exception.CakkException; +import com.cakk.domain.annotation.TestWithDisplayName; +import com.cakk.domain.base.FacadeTest; +import com.cakk.domain.mysql.dto.param.user.ProfileUpdateParam; +import com.cakk.domain.mysql.entity.user.User; +import com.cakk.domain.mysql.entity.user.UserWithdrawal; +import com.cakk.domain.mysql.facade.user.UserCommandFacade; +import com.cakk.domain.mysql.repository.jpa.UserJpaRepository; +import com.cakk.domain.mysql.repository.jpa.UserWithdrawalJpaRepository; + +class UserCommandFacadeTest extends FacadeTest { + + @InjectMocks + private UserCommandFacade userCommandFacade; + + @Mock + private UserJpaRepository userJpaRepository; + + @Mock + private UserWithdrawalJpaRepository userWithdrawalJpaRepository; + + @TestWithDisplayName("유저를 생성한다") + void create() { + // given + final User user = getUserFixture(Role.USER); + + // when + userCommandFacade.create(user); + + // then + verify(userJpaRepository, times(1)).findByProviderId(user.getProviderId()); + verify(userJpaRepository, times(1)).save(user); + } + + @TestWithDisplayName("유저가 이미 있으면 User 생성에 실패한다") + void create2() { + // given + final User user = getUserFixture(Role.USER); + + doReturn(Optional.of(user)).when(userJpaRepository).findByProviderId(user.getProviderId()); + + // when + assertThrows( + CakkException.class, + () -> userCommandFacade.create(user), + ReturnCode.ALREADY_EXIST_USER.getMessage() + ); + + // then + verify(userJpaRepository, times(1)).findByProviderId(user.getProviderId()); + verify(userJpaRepository, never()).save(user); + } + + @TestWithDisplayName("유저 정보를 수정한다") + void updateProfile() { + // given + final User user = getUserFixture(Role.USER); + final ProfileUpdateParam param = getConstructorMonkey().giveMeBuilder(ProfileUpdateParam.class) + .set("profileImageUrl", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(100)) + .set("nickname", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(30)) + .set("email", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(50)) + .set("gender", Arbitraries.of(Gender.class)) + .set("birthday", LocalDate.now()) + .sample(); + + // when + userCommandFacade.updateProfile(user, param); + + // then + assertEquals(param.profileImageUrl(), user.getProfileImageUrl()); + assertEquals(param.nickname(), user.getNickname()); + assertEquals(param.email(), user.getEmail()); + assertEquals(param.gender(), user.getGender()); + assertEquals(param.birthday(), user.getBirthday()); + } + + @TestWithDisplayName("유저를 탈퇴한다") + void withdraw() { + // given + final User user = getUserFixture(Role.USER); + final UserWithdrawal withdrawal = getConstructorMonkey().giveMeBuilder(UserWithdrawal.class) + .set("email", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(50)) + .set("gender", Arbitraries.of(Gender.class)) + .set("birthday", LocalDate.now()) + .set("role", Arbitraries.of(Role.class)) + .set("withdrawalDate", LocalDateTime.now()) + .sample(); + + // when + userCommandFacade.withdraw(user, withdrawal); + + // then + verify(userWithdrawalJpaRepository, times(1)).save(any()); + verify(userJpaRepository, times(1)).delete(user); + } +} diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserHeartFacadeTest.java b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserHeartFacadeTest.java index 4bd57d61..2ab46c38 100644 --- a/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserHeartFacadeTest.java +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserHeartFacadeTest.java @@ -4,17 +4,19 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; import com.cakk.common.enums.Role; -import com.cakk.domain.base.DomainTest; +import com.cakk.domain.base.FacadeTest; import com.cakk.domain.mysql.entity.cake.Cake; import com.cakk.domain.mysql.entity.shop.CakeShop; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.facade.user.UserHeartFacade; -class UserHeartFacadeTest extends DomainTest { +class UserHeartFacadeTest extends FacadeTest { - private final UserHeartFacade userHeartFacade = new UserHeartFacade(); + @InjectMocks + private UserHeartFacade userHeartFacade; @Test @DisplayName("케이크 하트를 성공한다") diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserLikeFacadeTest.java b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserLikeFacadeTest.java index 27dfc58d..08ccf240 100644 --- a/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserLikeFacadeTest.java +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserLikeFacadeTest.java @@ -7,18 +7,20 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; import com.cakk.common.enums.ReturnCode; import com.cakk.common.enums.Role; import com.cakk.common.exception.CakkException; -import com.cakk.domain.base.DomainTest; +import com.cakk.domain.base.FacadeTest; import com.cakk.domain.mysql.entity.shop.CakeShop; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.facade.user.UserLikeFacade; -class UserLikeFacadeTest extends DomainTest { +class UserLikeFacadeTest extends FacadeTest { - private final UserLikeFacade userLikeFacade = new UserLikeFacade(); + @InjectMocks + private UserLikeFacade userLikeFacade; @Test @DisplayName("케이크 샵 기대돼요 동작을 성공한다") From e90c9dced8c9fc1a0c2cfc5142a95d07fd0417be Mon Sep 17 00:00:00 2001 From: Hyunseok Ko <56003992+lcomment@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:31:17 +0900 Subject: [PATCH 02/11] =?UTF-8?q?Refactor=20|=20CAKK-59=20|=20External=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20?= =?UTF-8?q?=EB=B0=8F=20Send=20=EA=B4=80=EB=A0=A8=20=EC=B6=94=EC=83=81?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Chore | CAKK-58 | kt dsl 적용 및 kotlin 의존성 주입 * Rename .java to .kt * Refactor | CAKK-58 | send 관련 코틀린 적용 및 추상화 * Refactor | CAKK-58 | 사업자 인증 관련 리팩토링 * Refactor | CAKK-58 | 이메일 인증 관련 리팩토링 * Refactor | CAKK-58 | 함수형 인터페이스로 수정 * Refactor | CAKK-58 | 파라미터 수정 * Refactor | CAKK-58 | 템플릿 콜백 패턴 적 * Refactor | CAKK-58 | 템플릿 콜백 패턴 적용 --- build.gradle.kts | 2 +- cakk-api/build.gradle | 51 ------------- cakk-api/build.gradle.kts | 51 +++++++++++++ .../config/CertificationTemplateConfig.java | 30 +++----- .../cakk/api/config/SlackWebhookConfig.java | 22 ------ .../VerificationCodeSendTemplateConfig.java | 30 ++++++++ .../listener/CertificationEventListener.java | 2 +- .../api/listener/EmailSendEventListener.java | 20 +++-- .../java/com/cakk/api/mapper/EventMapper.java | 5 ++ .../api/template/CertificationTemplate.java | 28 ------- .../listener/EmailSendEventListenerTest.java | 11 +-- cakk-external/build.gradle | 27 ------- cakk-external/build.gradle.kts | 26 +++++++ .../com/cakk/external/config/EmailConfig.kt | 61 ++++++++++++++++ .../com/cakk/external/config/MailConfig.java | 73 ------------------- .../external/config/SlackWebhookConfig.kt | 19 +++++ .../CertificationSlackMessageExtractor.java | 2 +- .../VerificationCodeMessageExtractor.kt | 8 ++ .../VerificationCodeMimeMessageExtractor.kt | 50 +++++++++++++ .../com/cakk/external/sender/EmailSender.kt | 23 ++++++ .../com/cakk/external/sender/MessageSender.kt | 6 ++ .../external/sender/SlackMessageSender.kt | 23 ++++++ .../cakk/external/service/MailService.java | 67 ----------------- .../template/CertificationTemplate.kt | 28 +++++++ .../template/VerificationCodeSendTemplate.kt | 28 +++++++ .../cakk/external/vo/VerificationMessage.kt | 6 ++ 26 files changed, 397 insertions(+), 302 deletions(-) delete mode 100644 cakk-api/build.gradle create mode 100644 cakk-api/build.gradle.kts delete mode 100644 cakk-api/src/main/java/com/cakk/api/config/SlackWebhookConfig.java create mode 100644 cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java delete mode 100644 cakk-api/src/main/java/com/cakk/api/template/CertificationTemplate.java delete mode 100644 cakk-external/build.gradle create mode 100644 cakk-external/build.gradle.kts create mode 100644 cakk-external/src/main/java/com/cakk/external/config/EmailConfig.kt delete mode 100644 cakk-external/src/main/java/com/cakk/external/config/MailConfig.java create mode 100644 cakk-external/src/main/java/com/cakk/external/config/SlackWebhookConfig.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/sender/EmailSender.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt delete mode 100644 cakk-external/src/main/java/com/cakk/external/service/MailService.java create mode 100644 cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/vo/VerificationMessage.kt diff --git a/build.gradle.kts b/build.gradle.kts index 63ecc1b2..d15b11d2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,7 +41,7 @@ subprojects { } } - if (project.name == "cakk-admin") { + if (project.name == "cakk-admin" || project.name == "cakk-external") { apply(plugin = "org.jetbrains.kotlin.jvm") apply(plugin = "org.jetbrains.kotlin.plugin.spring") apply(plugin = "org.jetbrains.kotlin.plugin.jpa") diff --git a/cakk-api/build.gradle b/cakk-api/build.gradle deleted file mode 100644 index d66fec62..00000000 --- a/cakk-api/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -description = "api module" - -dependencies { - implementation project(':cakk-common') - implementation project(':cakk-domain:mysql') - implementation project(':cakk-domain:redis') - implementation project(':cakk-client') - implementation project(':cakk-external') - - // basic - implementation('org.springframework.boot:spring-boot-starter-web') - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation("org.springframework.boot:spring-boot-starter-aop:3.3.0") - implementation('org.springframework:spring-tx') - annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" - - // Security & OAuth - implementation('org.springframework.boot:spring-boot-starter-security') - implementation('org.springframework.boot:spring-boot-starter-oauth2-client') - implementation('com.google.api-client:google-api-client-jackson2:2.2.0') - implementation('com.google.api-client:google-api-client:2.2.0') - - // Jwt - implementation('io.jsonwebtoken:jjwt-api:0.11.5') - implementation('io.jsonwebtoken:jjwt-impl:0.11.5') - implementation('io.jsonwebtoken:jjwt-jackson:0.11.5') - - // test - testImplementation('com.tngtech.archunit:archunit-junit5:1.1.0') - testImplementation('org.springframework.boot:spring-boot-starter-test') - testImplementation('org.springframework.security:spring-security-test') - testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.0.23") - - // test container - testImplementation "org.testcontainers:junit-jupiter:1.19.7" - testImplementation "org.testcontainers:mysql:1.19.7" - - // slack 설정 - implementation 'net.gpedro.integrations.slack:slack-webhook:1.4.0' - - // Point - implementation 'org.locationtech.jts:jts-core:1.18.2' -} - -tasks.named("bootJar") { - enabled = true -} - -tasks.named("jar") { - enabled = false -} diff --git a/cakk-api/build.gradle.kts b/cakk-api/build.gradle.kts new file mode 100644 index 00000000..e7f32474 --- /dev/null +++ b/cakk-api/build.gradle.kts @@ -0,0 +1,51 @@ +description = "api module" + +dependencies { + implementation(project(":cakk-common")) + implementation(project(":cakk-domain:mysql")) + implementation(project(":cakk-domain:redis")) + implementation(project(":cakk-client")) + implementation(project(":cakk-external")) + + // basic + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("org.springframework.boot:spring-boot-starter-aop:3.3.0") + implementation("org.springframework:spring-tx") + annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + + // Security & OAuth + implementation("org.springframework.boot:spring-boot-starter-security") + implementation("org.springframework.boot:spring-boot-starter-oauth2-client") + implementation("com.google.api-client:google-api-client-jackson2:2.2.0") + implementation("com.google.api-client:google-api-client:2.2.0") + + // Jwt + implementation("io.jsonwebtoken:jjwt-api:0.11.5") + implementation("io.jsonwebtoken:jjwt-impl:0.11.5") + implementation("io.jsonwebtoken:jjwt-jackson:0.11.5") + + // test + testImplementation("com.tngtech.archunit:archunit-junit5:1.1.0") + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.springframework.security:spring-security-test") + testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.0.23") + + // test container + testImplementation("org.testcontainers:junit-jupiter:1.19.7") + testImplementation("org.testcontainers:mysql:1.19.7") + + // slack 설정 + implementation("net.gpedro.integrations.slack:slack-webhook:1.4.0") + + // Point + implementation("org.locationtech.jts:jts-core:1.18.2") +} + +tasks.bootJar { + enabled = true +} + +tasks.jar { + enabled = false +} diff --git a/cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java b/cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java index aecefa13..2c26fb8d 100644 --- a/cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java +++ b/cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java @@ -1,43 +1,33 @@ package com.cakk.api.config; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import net.gpedro.integrations.slack.SlackApi; - -import com.cakk.api.template.CertificationTemplate; -import com.cakk.external.executor.CertificationApiExecutor; -import com.cakk.external.executor.CertificationSlackApiExecutor; import com.cakk.external.extractor.CertificationMessageExtractor; import com.cakk.external.extractor.CertificationSlackMessageExtractor; +import com.cakk.external.sender.MessageSender; +import com.cakk.external.template.CertificationTemplate; @Configuration public class CertificationTemplateConfig { - private final SlackApi slackApi; - private final boolean isEnable; + private final MessageSender messageSender; public CertificationTemplateConfig( - SlackApi slackApi, - @Value("${slack.webhook.is-enable}") - boolean isEnable) { - this.slackApi = slackApi; - this.isEnable = isEnable; + @Qualifier("slackMessageSender") + MessageSender messageSender + ) { + this.messageSender = messageSender; } @Bean public CertificationTemplate certificationTemplate() { - return new CertificationTemplate(certificationApiExecutor(), certificationMessageExtractor()); - } - - @Bean - public CertificationApiExecutor certificationApiExecutor() { - return new CertificationSlackApiExecutor(slackApi, isEnable); + return new CertificationTemplate(messageSender, certificationMessageExtractor()); } @Bean - CertificationMessageExtractor certificationMessageExtractor() { + public CertificationMessageExtractor certificationMessageExtractor() { return new CertificationSlackMessageExtractor(); } } diff --git a/cakk-api/src/main/java/com/cakk/api/config/SlackWebhookConfig.java b/cakk-api/src/main/java/com/cakk/api/config/SlackWebhookConfig.java deleted file mode 100644 index 9d3df2f4..00000000 --- a/cakk-api/src/main/java/com/cakk/api/config/SlackWebhookConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.cakk.api.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import net.gpedro.integrations.slack.SlackApi; - -@Configuration -public class SlackWebhookConfig { - - private final String slackWebhookUrl; - - public SlackWebhookConfig(@Value("${slack.webhook.url}") String slackWebhookUrl) { - this.slackWebhookUrl = slackWebhookUrl; - } - - @Bean - public SlackApi slackApi() { - return new SlackApi(slackWebhookUrl); - } -} diff --git a/cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java b/cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java new file mode 100644 index 00000000..d2728629 --- /dev/null +++ b/cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java @@ -0,0 +1,30 @@ +package com.cakk.api.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.cakk.external.extractor.VerificationCodeMessageExtractor; +import com.cakk.external.sender.MessageSender; +import com.cakk.external.template.VerificationCodeSendTemplate; + +@Configuration +public class VerificationCodeSendTemplateConfig { + + private final MessageSender messageSender; + private final VerificationCodeMessageExtractor verificationCodeMessageExtractor; + + public VerificationCodeSendTemplateConfig( + @Qualifier("emailSender") + MessageSender messageSender, + VerificationCodeMessageExtractor verificationCodeMessageExtractor + ) { + this.messageSender = messageSender; + this.verificationCodeMessageExtractor = verificationCodeMessageExtractor; + } + + @Bean + public VerificationCodeSendTemplate verificationCodeSendTemplate() { + return new VerificationCodeSendTemplate(messageSender, verificationCodeMessageExtractor); + } +} diff --git a/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java b/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java index 46dedae9..2cd8d6c3 100644 --- a/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java +++ b/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java @@ -8,8 +8,8 @@ import com.cakk.api.annotation.ApplicationEventListener; import com.cakk.api.mapper.EventMapper; -import com.cakk.api.template.CertificationTemplate; import com.cakk.domain.mysql.event.shop.CertificationEvent; +import com.cakk.external.template.CertificationTemplate; import com.cakk.external.vo.CertificationMessage; @RequiredArgsConstructor diff --git a/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java b/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java index 02f3deeb..e7a7d6fa 100644 --- a/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java +++ b/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java @@ -2,24 +2,32 @@ import java.util.Objects; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; -import lombok.RequiredArgsConstructor; - import com.cakk.api.annotation.ApplicationEventListener; import com.cakk.api.dto.event.EmailWithVerificationCodeSendEvent; -import com.cakk.external.service.MailService; +import com.cakk.api.mapper.EventMapper; +import com.cakk.external.template.VerificationCodeSendTemplate; +import com.cakk.external.vo.VerificationMessage; -@RequiredArgsConstructor @ApplicationEventListener public class EmailSendEventListener { - private final MailService mailService; + + private final VerificationCodeSendTemplate verificationCodeSendTemplate; + + public EmailSendEventListener( + VerificationCodeSendTemplate verificationCodeSendTemplate + ) { + this.verificationCodeSendTemplate = verificationCodeSendTemplate; + } @Async @EventListener public void sendEmailIncludeVerificationCode(EmailWithVerificationCodeSendEvent event) { - mailService.sendEmail(Objects.requireNonNull(event.email()), Objects.requireNonNull(event.code())); + final VerificationMessage verificationMessage = EventMapper.supplyVerificationMessageBy(event); + verificationCodeSendTemplate.sendMessageForVerificationCode(verificationMessage); } } diff --git a/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java b/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java index 17eb2af9..52b8a7bf 100644 --- a/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java +++ b/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java @@ -9,6 +9,7 @@ import com.cakk.api.dto.event.IncreaseSearchCountEvent; import com.cakk.domain.mysql.event.shop.CertificationEvent; import com.cakk.external.vo.CertificationMessage; +import com.cakk.external.vo.VerificationMessage; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class EventMapper { @@ -42,4 +43,8 @@ public static CertificationMessage supplyCertificationMessageBy(final Certificat longitude ); } + + public static VerificationMessage supplyVerificationMessageBy(final EmailWithVerificationCodeSendEvent event) { + return new VerificationMessage(event.email(), event.code()); + } } diff --git a/cakk-api/src/main/java/com/cakk/api/template/CertificationTemplate.java b/cakk-api/src/main/java/com/cakk/api/template/CertificationTemplate.java deleted file mode 100644 index 9b82bbc4..00000000 --- a/cakk-api/src/main/java/com/cakk/api/template/CertificationTemplate.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.cakk.api.template; - -import lombok.RequiredArgsConstructor; - -import com.cakk.external.executor.CertificationApiExecutor; -import com.cakk.external.extractor.CertificationMessageExtractor; -import com.cakk.external.vo.CertificationMessage; - -@RequiredArgsConstructor -public class CertificationTemplate { - - private final CertificationApiExecutor certificationApiExecutor; - private final CertificationMessageExtractor certificationMessageExtractor; - - public void sendMessageForCertification(CertificationMessage certificationMessage) { - this.sendMessageForCertification(certificationMessage, certificationMessageExtractor, certificationApiExecutor); - } - - public void sendMessageForCertification( - CertificationMessage certificationMessage, - CertificationMessageExtractor certificationMessageExtractor, - CertificationApiExecutor certificationApiExecutor - ) { - T extractMessage = (T)certificationMessageExtractor.extract(certificationMessage); - certificationApiExecutor.send(extractMessage); - } -} - diff --git a/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java b/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java index 27321c39..59cca7fe 100644 --- a/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java +++ b/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java @@ -11,7 +11,8 @@ import com.cakk.api.common.annotation.TestWithDisplayName; import com.cakk.api.common.base.MockitoTest; import com.cakk.api.dto.event.EmailWithVerificationCodeSendEvent; -import com.cakk.external.service.MailService; +import com.cakk.external.sender.MessageSender; +import com.cakk.external.template.VerificationCodeSendTemplate; class EmailSendEventListenerTest extends MockitoTest { @@ -19,7 +20,7 @@ class EmailSendEventListenerTest extends MockitoTest { private EmailSendEventListener emailSendEventListener; @Mock - private MailService mailService; + private VerificationCodeSendTemplate verificationCodeSendTemplate; private EmailWithVerificationCodeSendEvent eventFixture() { return getConstructorMonkey().giveMeBuilder(EmailWithVerificationCodeSendEvent.class) @@ -33,13 +34,13 @@ void sendEmailIncludeVerificationCode() { // given EmailWithVerificationCodeSendEvent event = eventFixture(); - doNothing().when(mailService).sendEmail(event.email(), event.code()); + doNothing().when(verificationCodeSendTemplate).sendMessageForVerificationCode(any()); // when assertDoesNotThrow(() -> emailSendEventListener.sendEmailIncludeVerificationCode(event)); // then - verify(mailService, times(1)).sendEmail(event.email(), event.code()); + verify(verificationCodeSendTemplate, times(1)).sendMessageForVerificationCode(any()); } @TestWithDisplayName("이벤트에 null 데이터가 포함돼 있으면, 메일 전송 메서드가 호출 시, 에러를 반환한다.") @@ -54,6 +55,6 @@ void sendEmailIncludeVerificationCode2() { ); // then - verify(mailService, times(0)).sendEmail(event.email(), event.code()); + verify(verificationCodeSendTemplate, times(0)).sendMessageForVerificationCode(any()); } } diff --git a/cakk-external/build.gradle b/cakk-external/build.gradle deleted file mode 100644 index caf74ef3..00000000 --- a/cakk-external/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -description = "external module" - - -dependencies { - implementation project(':cakk-common') - - // Basic - implementation('org.springframework:spring-context') - - // AWS - implementation('com.amazonaws:aws-java-sdk-s3:1.12.715') - - // Mail - implementation('org.springframework.boot:spring-boot-starter-mail') - - // Slack - implementation 'net.gpedro.integrations.slack:slack-webhook:1.4.0' -} - - -bootJar { - enabled = false -} - -jar { - enabled = true -} diff --git a/cakk-external/build.gradle.kts b/cakk-external/build.gradle.kts new file mode 100644 index 00000000..422496a7 --- /dev/null +++ b/cakk-external/build.gradle.kts @@ -0,0 +1,26 @@ +description = "external module" + +dependencies { + implementation(project(":cakk-common")) + + // Basic + implementation("org.springframework:spring-context") + + // AWS + implementation("com.amazonaws:aws-java-sdk-s3:1.12.715") + + // Mail + implementation("org.springframework.boot:spring-boot-starter-mail") + + // Slack + implementation("net.gpedro.integrations.slack:slack-webhook:1.4.0") +} + + +tasks.bootJar { + enabled = false +} + +tasks.jar { + enabled = true +} diff --git a/cakk-external/src/main/java/com/cakk/external/config/EmailConfig.kt b/cakk-external/src/main/java/com/cakk/external/config/EmailConfig.kt new file mode 100644 index 00000000..f084a29f --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/config/EmailConfig.kt @@ -0,0 +1,61 @@ +package com.cakk.external.config + +import java.util.* + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.mail.javamail.JavaMailSender +import org.springframework.mail.javamail.JavaMailSenderImpl + +@Configuration +class EmailConfig( + @Value("\${spring.mail.host}") + private val host: String, + @Value("\${spring.mail.port}") + private val port: Int, + @Value("\${spring.mail.username}") + private val username: String, + @Value("\${spring.mail.password}") + private val password: String, + @Value("\${spring.mail.properties.mail.smtp.auth}") + private val auth: Boolean, + @Value("\${spring.mail.properties.mail.smtp.starttls.enable}") + private val starttlsEnable: Boolean, + @Value("\${spring.mail.properties.mail.smtp.starttls.required}") + private val starttlsRequired: Boolean, + @Value("\${spring.mail.properties.mail.smtp.connectiontimeout}") + private val connectionTimeout: Int, + @Value("\${spring.mail.properties.mail.smtp.timeout}") + private val timeout: Int, + @Value("\${spring.mail.properties.mail.smtp.writetimeout}") + private val writeTimeout: Int +) { + + @Bean + fun javaMailSender(): JavaMailSender { + val mailSender = JavaMailSenderImpl() + + mailSender.host = host + mailSender.port = port + mailSender.username = username + mailSender.password = password + mailSender.defaultEncoding = "UTF-8" + mailSender.javaMailProperties = mailProperties() + + return mailSender + } + + private fun mailProperties(): Properties { + val properties = Properties() + + properties["mail.smtp.auth"] = auth + properties["mail.smtp.starttls.enable"] = starttlsEnable + properties["mail.smtp.starttls.required"] = starttlsRequired + properties["mail.smtp.connectiontimeout"] = connectionTimeout + properties["mail.smtp.timeout"] = timeout + properties["mail.smtp.writetimeout"] = writeTimeout + + return properties + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/config/MailConfig.java b/cakk-external/src/main/java/com/cakk/external/config/MailConfig.java deleted file mode 100644 index 6552c02c..00000000 --- a/cakk-external/src/main/java/com/cakk/external/config/MailConfig.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.cakk.external.config; - -import java.util.Properties; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.JavaMailSenderImpl; - -@Configuration -public class MailConfig { - - private final String host; - private final int port; - private final String username; - private final String password; - private final boolean auth; - private final boolean starttlsEnable; - private final boolean starttlsRequired; - private final int connectionTimeout; - private final int timeout; - private final int writeTimeout; - - public MailConfig( - @Value("${spring.mail.host}") String host, - @Value("${spring.mail.port}") int port, - @Value("${spring.mail.username}") String username, - @Value("${spring.mail.password}") String password, - @Value("${spring.mail.properties.mail.smtp.auth}") boolean auth, - @Value("${spring.mail.properties.mail.smtp.starttls.enable}") boolean starttlsEnable, - @Value("${spring.mail.properties.mail.smtp.starttls.required}") boolean starttlsRequired, - @Value("${spring.mail.properties.mail.smtp.connectiontimeout}") int connectionTimeout, - @Value("${spring.mail.properties.mail.smtp.timeout}") int timeout, - @Value("${spring.mail.properties.mail.smtp.writetimeout}") int writeTimeout - ) { - this.host = host; - this.port = port; - this.username = username; - this.password = password; - this.auth = auth; - this.starttlsEnable = starttlsEnable; - this.starttlsRequired = starttlsRequired; - this.connectionTimeout = connectionTimeout; - this.timeout = timeout; - this.writeTimeout = writeTimeout; - } - - @Bean - public JavaMailSender javaMailSender() { - JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); - mailSender.setHost(host); - mailSender.setPort(port); - mailSender.setUsername(username); - mailSender.setPassword(password); - mailSender.setDefaultEncoding("UTF-8"); - mailSender.setJavaMailProperties(getMailProperties()); - - return mailSender; - } - - private Properties getMailProperties() { - Properties properties = new Properties(); - properties.put("mail.smtp.auth", auth); - properties.put("mail.smtp.starttls.enable", starttlsEnable); - properties.put("mail.smtp.starttls.required", starttlsRequired); - properties.put("mail.smtp.connectiontimeout", connectionTimeout); - properties.put("mail.smtp.timeout", timeout); - properties.put("mail.smtp.writetimeout", writeTimeout); - - return properties; - } -} diff --git a/cakk-external/src/main/java/com/cakk/external/config/SlackWebhookConfig.kt b/cakk-external/src/main/java/com/cakk/external/config/SlackWebhookConfig.kt new file mode 100644 index 00000000..eb0eafbf --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/config/SlackWebhookConfig.kt @@ -0,0 +1,19 @@ +package com.cakk.external.config + +import net.gpedro.integrations.slack.SlackApi + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class SlackWebhookConfig( + @Value("\${slack.webhook.url}") + private val slackWebhookUrl: String +) { + + @Bean + fun slackApi(): SlackApi { + return SlackApi(slackWebhookUrl) + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java b/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java index b5ee8b99..a165adbe 100644 --- a/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java +++ b/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java @@ -31,8 +31,8 @@ public SlackMessage extract(CertificationMessage certificationMessage) { SlackMessage slackMessage = new SlackMessage(); slackMessage.setAttachments(List.of(slackAttachment)); - slackMessage.setChannel("#cs_사장님인증"); slackMessage.setText("사장님 인증 요청"); + slackMessage.setChannel("#cs_사장님인증"); return slackMessage; } diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt new file mode 100644 index 00000000..ff55c0a1 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt @@ -0,0 +1,8 @@ +package com.cakk.external.extractor + +import com.cakk.external.vo.VerificationMessage + +fun interface VerificationCodeMessageExtractor { + + fun extract(verificationMessage: VerificationMessage): T +} diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt new file mode 100644 index 00000000..417238b6 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt @@ -0,0 +1,50 @@ +package com.cakk.external.extractor + +import jakarta.mail.MessagingException +import jakarta.mail.internet.MimeMessage + +import org.springframework.beans.factory.annotation.Value +import org.springframework.mail.javamail.JavaMailSender +import org.springframework.mail.javamail.MimeMessageHelper +import org.springframework.stereotype.Component + +import com.cakk.common.enums.ReturnCode +import com.cakk.common.exception.CakkException +import com.cakk.external.vo.VerificationMessage + +@Component +class VerificationCodeMimeMessageExtractor( + private val mailSender: JavaMailSender, + @Value("\${spring.mail.username}") + private val senderEmail: String +) : VerificationCodeMessageExtractor { + + override fun extract(verificationMessage: VerificationMessage): MimeMessage { + val message = mailSender.createMimeMessage() + + try { + val helper = MimeMessageHelper(message, true, "UTF-8") + + helper.setTo(verificationMessage.receiver) + helper.setFrom(senderEmail) + helper.setSubject("[케이크크] 이메일 인증") + + val body = """ +

인증코드를 확인해 주세요.

+

${verificationMessage.verificationCode}

+ 이메일 인증 절차에 따라 이메일 인증코드를 발급해드립니다.
+ 인증코드는 이메일 발송시점으로부터 3분 동안 유효합니다.
+ 만약 본인 요청에 의한 이메일 인증이 아니라면, cakk.contact@gmail.com 으로 관련 내용을 전달해 주세요. + 더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하는 케이크크가 되겠습니다. +

+ + 감사합니다. + """.trimIndent() + helper.setText(body, true) + } catch (e: MessagingException) { + throw CakkException(ReturnCode.SEND_EMAIL_ERROR) + } + + return message + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/sender/EmailSender.kt b/cakk-external/src/main/java/com/cakk/external/sender/EmailSender.kt new file mode 100644 index 00000000..5c0fdf32 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/sender/EmailSender.kt @@ -0,0 +1,23 @@ +package com.cakk.external.sender + +import jakarta.mail.internet.MimeMessage + +import org.springframework.mail.javamail.JavaMailSender +import org.springframework.stereotype.Component + +import com.cakk.common.enums.ReturnCode +import com.cakk.common.exception.CakkException + +@Component +class EmailSender( + private val mailSender: JavaMailSender +) : MessageSender { + + override fun send(message: MimeMessage) { + try { + mailSender.send(message) + } catch (e: RuntimeException) { + throw CakkException(ReturnCode.SEND_EMAIL_ERROR) + } + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt b/cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt new file mode 100644 index 00000000..fcfc0b19 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt @@ -0,0 +1,6 @@ +package com.cakk.external.sender + +fun interface MessageSender { + + fun send( message: T) +} diff --git a/cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt b/cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt new file mode 100644 index 00000000..4420ea27 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt @@ -0,0 +1,23 @@ +package com.cakk.external.sender + +import net.gpedro.integrations.slack.SlackApi +import net.gpedro.integrations.slack.SlackMessage + +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component + +@Component +class SlackMessageSender( + private val slackApi: SlackApi, + @Value("\${slack.webhook.is-enable}") + private val isEnable: Boolean +): MessageSender { + + override fun send(message: SlackMessage) { + if (!isEnable) { + return + } + + slackApi.call(message) + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/service/MailService.java b/cakk-external/src/main/java/com/cakk/external/service/MailService.java deleted file mode 100644 index 8a266e2f..00000000 --- a/cakk-external/src/main/java/com/cakk/external/service/MailService.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.cakk.external.service; - -import jakarta.mail.MessagingException; -import jakarta.mail.internet.MimeMessage; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.MimeMessageHelper; -import org.springframework.stereotype.Service; - -import com.cakk.common.enums.ReturnCode; -import com.cakk.common.exception.CakkException; - -@Service -public class MailService { - - private final JavaMailSender mailSender; - - private final String senderEmail; - - public MailService( - JavaMailSender mailSender, - @Value("${spring.mail.username}") String username - ) { - this.mailSender = mailSender; - this.senderEmail = username; - } - - public void sendEmail(final String receiverEmail, final String code) { - try { - final MimeMessage emailForm = createMailFrom(receiverEmail, code); - - mailSender.send(emailForm); - } catch (RuntimeException e) { - throw new CakkException(ReturnCode.SEND_EMAIL_ERROR); - } - } - - private MimeMessage createMailFrom(final String receiverMail, final String code) { - final MimeMessage message = mailSender.createMimeMessage(); - - try { - final MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); - - helper.setTo(receiverMail); - helper.setFrom(senderEmail); - helper.setSubject("[케이크크] 이메일 인증"); - - String body = """ -

인증코드를 확인해 주세요.

-

%s

- 이메일 인증 절차에 따라 이메일 인증코드를 발급해드립니다.
- 인증코드는 이메일 발송시점으로부터 3분 동안 유효합니다.
- 만약 본인 요청에 의한 이메일 인증이 아니라면, cakk.contact@gmail.com 으로 관련 내용을 전달해 주세요. - 더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하는 케이크크가 되겠습니다. -

- - 감사합니다. - """.formatted(code); - helper.setText(body, true); - } catch (MessagingException e) { - throw new CakkException(ReturnCode.SEND_EMAIL_ERROR); - } - - return message; - } -} diff --git a/cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt b/cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt new file mode 100644 index 00000000..0745d467 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt @@ -0,0 +1,28 @@ +package com.cakk.external.template + +import com.cakk.external.extractor.CertificationMessageExtractor +import com.cakk.external.sender.MessageSender +import com.cakk.external.vo.CertificationMessage + +class CertificationTemplate( + private val messageSender: MessageSender, + private val certificationMessageExtractor: CertificationMessageExtractor +) { + + fun sendMessageForCertification(certificationMessage: CertificationMessage) { + this.sendMessageForCertification( + certificationMessage = certificationMessage, + certificationMessageExtractor = certificationMessageExtractor, + messageSender = messageSender + ) + } + + private fun sendMessageForCertification( + certificationMessage: CertificationMessage, + certificationMessageExtractor: CertificationMessageExtractor, + messageSender: MessageSender + ) { + val extractMessage: T = certificationMessageExtractor.extract(certificationMessage) + messageSender.send(extractMessage) + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt b/cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt new file mode 100644 index 00000000..abbd5bb9 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt @@ -0,0 +1,28 @@ +package com.cakk.external.template + +import com.cakk.external.extractor.VerificationCodeMessageExtractor +import com.cakk.external.sender.MessageSender +import com.cakk.external.vo.VerificationMessage + +class VerificationCodeSendTemplate( + private val messageSender: MessageSender, + private val verificationCodeMessageExtractor: VerificationCodeMessageExtractor +) { + + fun sendMessageForVerificationCode(verificationMessage: VerificationMessage) { + this.sendMessageForVerificationCode( + verificationMessage = verificationMessage, + verificationCodeMessageExtractor = verificationCodeMessageExtractor, + messageSender = messageSender + ) + } + + private fun sendMessageForVerificationCode( + verificationMessage: VerificationMessage, + verificationCodeMessageExtractor: VerificationCodeMessageExtractor, + messageSender: MessageSender + ) { + val extractMessage: T = verificationCodeMessageExtractor.extract(verificationMessage) + messageSender.send(extractMessage) + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/vo/VerificationMessage.kt b/cakk-external/src/main/java/com/cakk/external/vo/VerificationMessage.kt new file mode 100644 index 00000000..66988a69 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/vo/VerificationMessage.kt @@ -0,0 +1,6 @@ +package com.cakk.external.vo + +data class VerificationMessage( + val receiver: String, + val verificationCode: String +) From 0346e966c8113560c3aff093dedb1376923347ec Mon Sep 17 00:00:00 2001 From: Hyunseok Ko <56003992+lcomment@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:45:58 +0900 Subject: [PATCH 03/11] =?UTF-8?q?Refactor=20|=20CAKK-59=20|=20Message=20Ex?= =?UTF-8?q?tractor,=20Sender=20=EA=B4=80=EB=A0=A8=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor | CAKK-59 | MessageExtractor 인터페이스 추상화 * Refactor | CAKK-59 | as-is 클래스 및 인터페이스 제거 * Refactor | CAKK-59 | as-is 클래스 및 인터페이스 제거 * Refactor | CAKK-59 | 메시지 extractor, template 추상화 * Refactor | CAKK-59 | 에러 메시지 리스너 구현 * Refactor | CAKK-59 | api 모듈 slack 의존성 제거 * Refactor | CAKK-59 | 메시지 관련 비즈니스 리팩토링 * Test | CAKK-59 | 메시지 전송 관련 테스트 수정 * Chore | CAKK-59 | jacoco exclude에 리스너 등록 * Refactor | CAKK-59 | email 관련 sender 네이밍 수정 * Refactor | CAKK-59 | 제네릭 범위 수정 * Test | CAKK-59 | Error 관련 리스너 테스트 작성 --- build.gradle.kts | 1 + cakk-api/build.gradle.kts | 3 - .../config/CertificationTemplateConfig.java | 33 ------- .../api/config/MessageTemplateConfig.java | 28 ++++++ .../VerificationCodeSendTemplateConfig.java | 30 ------- .../advice/GlobalControllerAdvice.java | 25 ++++-- .../cakk/api/dto/event/ErrorAlertEvent.java | 10 +++ .../listener/CertificationEventListener.java | 24 +++-- .../api/listener/EmailSendEventListener.java | 20 +++-- .../api/listener/ErrorAlertEventListener.java | 39 ++++++++ .../java/com/cakk/api/mapper/EventMapper.java | 30 +++++++ .../cakk/api/service/slack/SlackService.java | 88 ------------------- .../com/cakk/api/common/base/MockMvcTest.java | 8 +- .../GlobalControllerAdviceTest.java | 8 +- .../listener/EmailSendEventListenerTest.java | 11 ++- .../listener/ErrorAlertEventListenerTest.java | 57 ++++++++++++ .../executor/CertificationApiExecutor.java | 6 -- .../CertificationSlackApiExecutor.java | 28 ------ .../CertificationMessageExtractor.java | 8 -- .../CertificationSlackMessageExtractor.java | 40 --------- .../CertificationSlackMessageExtractor.kt | 39 ++++++++ .../ErrorAlertSlackMessageExtractor.kt | 56 ++++++++++++ .../external/extractor/MessageExtractor.kt | 6 ++ .../extractor/MimeMessageExtractor.kt | 8 ++ .../extractor/SlackMessageExtractor.kt | 8 ++ .../VerificationCodeMessageExtractor.kt | 8 -- .../VerificationCodeMimeMessageExtractor.kt | 14 +-- .../{EmailSender.kt => EmailMessageSender.kt} | 2 +- .../com/cakk/external/sender/MessageSender.kt | 2 +- .../template/CertificationTemplate.kt | 28 ------ .../cakk/external/template/MessageTemplate.kt | 16 ++++ .../template/VerificationCodeSendTemplate.kt | 28 ------ .../com/cakk/external/vo/ErrorAlertMessage.kt | 12 +++ 33 files changed, 382 insertions(+), 342 deletions(-) delete mode 100644 cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java create mode 100644 cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java delete mode 100644 cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java create mode 100644 cakk-api/src/main/java/com/cakk/api/dto/event/ErrorAlertEvent.java create mode 100644 cakk-api/src/main/java/com/cakk/api/listener/ErrorAlertEventListener.java delete mode 100644 cakk-api/src/main/java/com/cakk/api/service/slack/SlackService.java create mode 100644 cakk-api/src/test/java/com/cakk/api/listener/ErrorAlertEventListenerTest.java delete mode 100644 cakk-external/src/main/java/com/cakk/external/executor/CertificationApiExecutor.java delete mode 100644 cakk-external/src/main/java/com/cakk/external/executor/CertificationSlackApiExecutor.java delete mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/CertificationMessageExtractor.java delete mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java create mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/ErrorAlertSlackMessageExtractor.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/MessageExtractor.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/MimeMessageExtractor.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/SlackMessageExtractor.kt delete mode 100644 cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt rename cakk-external/src/main/java/com/cakk/external/sender/{EmailSender.kt => EmailMessageSender.kt} (95%) delete mode 100644 cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/template/MessageTemplate.kt delete mode 100644 cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt create mode 100644 cakk-external/src/main/java/com/cakk/external/vo/ErrorAlertMessage.kt diff --git a/build.gradle.kts b/build.gradle.kts index d15b11d2..72deccf3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -114,6 +114,7 @@ subprojects { "com.cakk.api.provider.oauth.PublicKeyProvider", "com.cakk.api.dto.**", "com.cakk.api.mapper.**", + "com.cakk.api.listener.**", "com.cakk.api.vo.**", "com.cakk.domain.**" ) diff --git a/cakk-api/build.gradle.kts b/cakk-api/build.gradle.kts index e7f32474..4e191493 100644 --- a/cakk-api/build.gradle.kts +++ b/cakk-api/build.gradle.kts @@ -35,9 +35,6 @@ dependencies { testImplementation("org.testcontainers:junit-jupiter:1.19.7") testImplementation("org.testcontainers:mysql:1.19.7") - // slack 설정 - implementation("net.gpedro.integrations.slack:slack-webhook:1.4.0") - // Point implementation("org.locationtech.jts:jts-core:1.18.2") } diff --git a/cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java b/cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java deleted file mode 100644 index 2c26fb8d..00000000 --- a/cakk-api/src/main/java/com/cakk/api/config/CertificationTemplateConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cakk.api.config; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.cakk.external.extractor.CertificationMessageExtractor; -import com.cakk.external.extractor.CertificationSlackMessageExtractor; -import com.cakk.external.sender.MessageSender; -import com.cakk.external.template.CertificationTemplate; - -@Configuration -public class CertificationTemplateConfig { - - private final MessageSender messageSender; - - public CertificationTemplateConfig( - @Qualifier("slackMessageSender") - MessageSender messageSender - ) { - this.messageSender = messageSender; - } - - @Bean - public CertificationTemplate certificationTemplate() { - return new CertificationTemplate(messageSender, certificationMessageExtractor()); - } - - @Bean - public CertificationMessageExtractor certificationMessageExtractor() { - return new CertificationSlackMessageExtractor(); - } -} diff --git a/cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java b/cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java new file mode 100644 index 00000000..168c1eca --- /dev/null +++ b/cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java @@ -0,0 +1,28 @@ +package com.cakk.api.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.cakk.external.extractor.CertificationSlackMessageExtractor; +import com.cakk.external.extractor.ErrorAlertSlackMessageExtractor; +import com.cakk.external.extractor.MessageExtractor; +import com.cakk.external.template.MessageTemplate; + +@Configuration +public class MessageTemplateConfig { + + @Bean + public MessageTemplate certificationTemplate() { + return new MessageTemplate(); + } + + @Bean + public MessageExtractor certificationMessageExtractor() { + return new CertificationSlackMessageExtractor(); + } + + @Bean + public MessageExtractor errorAlertMessageExtractor() { + return new ErrorAlertSlackMessageExtractor(); + } +} diff --git a/cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java b/cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java deleted file mode 100644 index d2728629..00000000 --- a/cakk-api/src/main/java/com/cakk/api/config/VerificationCodeSendTemplateConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.cakk.api.config; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.cakk.external.extractor.VerificationCodeMessageExtractor; -import com.cakk.external.sender.MessageSender; -import com.cakk.external.template.VerificationCodeSendTemplate; - -@Configuration -public class VerificationCodeSendTemplateConfig { - - private final MessageSender messageSender; - private final VerificationCodeMessageExtractor verificationCodeMessageExtractor; - - public VerificationCodeSendTemplateConfig( - @Qualifier("emailSender") - MessageSender messageSender, - VerificationCodeMessageExtractor verificationCodeMessageExtractor - ) { - this.messageSender = messageSender; - this.verificationCodeMessageExtractor = verificationCodeMessageExtractor; - } - - @Bean - public VerificationCodeSendTemplate verificationCodeSendTemplate() { - return new VerificationCodeSendTemplate(messageSender, verificationCodeMessageExtractor); - } -} diff --git a/cakk-api/src/main/java/com/cakk/api/controller/advice/GlobalControllerAdvice.java b/cakk-api/src/main/java/com/cakk/api/controller/advice/GlobalControllerAdvice.java index c2757e38..c31b5766 100644 --- a/cakk-api/src/main/java/com/cakk/api/controller/advice/GlobalControllerAdvice.java +++ b/cakk-api/src/main/java/com/cakk/api/controller/advice/GlobalControllerAdvice.java @@ -9,6 +9,8 @@ import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -20,27 +22,37 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import com.cakk.api.service.slack.SlackService; +import com.cakk.api.mapper.EventMapper; import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; import com.cakk.common.response.ApiResponse; @Slf4j @RestControllerAdvice -@RequiredArgsConstructor public class GlobalControllerAdvice { - private final SlackService slackService; + private final ApplicationEventPublisher applicationEventPublisher; + + private final String profile; + + public GlobalControllerAdvice( + ApplicationEventPublisher applicationEventPublisher, + @Value("${spring.profiles.active}") String profile + ) { + this.applicationEventPublisher = applicationEventPublisher; + this.profile = profile; + } @ExceptionHandler(CakkException.class) public ResponseEntity> handleCakkException(CakkException exception, HttpServletRequest request) { final ReturnCode returnCode = exception.getReturnCode(); + if (returnCode.equals(ReturnCode.INTERNAL_SERVER_ERROR) || returnCode.equals(ReturnCode.EXTERNAL_SERVER_ERROR)) { - slackService.sendSlackForError(exception, request); + applicationEventPublisher.publishEvent(EventMapper.supplyErrorAlertEventBy(exception, request, profile)); } + log.error(exception.getMessage()); return getResponseEntity(BAD_REQUEST, ApiResponse.fail(exception.getReturnCode())); } @@ -79,8 +91,9 @@ public ResponseEntity>> handleMethodArgNotValidE RuntimeException.class }) public ResponseEntity> handleServerException(Exception exception, HttpServletRequest request) { - slackService.sendSlackForError(exception, request); + applicationEventPublisher.publishEvent(EventMapper.supplyErrorAlertEventBy(exception, request, profile)); log.error(exception.getMessage()); + return getResponseEntity(INTERNAL_SERVER_ERROR, ApiResponse.error(ReturnCode.INTERNAL_SERVER_ERROR, exception.getMessage())); } diff --git a/cakk-api/src/main/java/com/cakk/api/dto/event/ErrorAlertEvent.java b/cakk-api/src/main/java/com/cakk/api/dto/event/ErrorAlertEvent.java new file mode 100644 index 00000000..6b0845ee --- /dev/null +++ b/cakk-api/src/main/java/com/cakk/api/dto/event/ErrorAlertEvent.java @@ -0,0 +1,10 @@ +package com.cakk.api.dto.event; + +import jakarta.servlet.http.HttpServletRequest; + +public record ErrorAlertEvent( + Exception exception, + HttpServletRequest request, + String profile +) { +} diff --git a/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java b/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java index 2cd8d6c3..540a2f0b 100644 --- a/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java +++ b/cakk-api/src/main/java/com/cakk/api/listener/CertificationEventListener.java @@ -1,27 +1,39 @@ package com.cakk.api.listener; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.annotation.Async; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; -import lombok.RequiredArgsConstructor; - import com.cakk.api.annotation.ApplicationEventListener; import com.cakk.api.mapper.EventMapper; import com.cakk.domain.mysql.event.shop.CertificationEvent; -import com.cakk.external.template.CertificationTemplate; +import com.cakk.external.extractor.MessageExtractor; +import com.cakk.external.sender.MessageSender; +import com.cakk.external.template.MessageTemplate; import com.cakk.external.vo.CertificationMessage; -@RequiredArgsConstructor @ApplicationEventListener public class CertificationEventListener { - private final CertificationTemplate certificationTemplate; + private final MessageTemplate messageTemplate; + private final MessageExtractor messageExtractor; + private final MessageSender messageSender; + + public CertificationEventListener( + MessageTemplate messageTemplate, + @Qualifier("certificationMessageExtractor") MessageExtractor messageExtractor, + @Qualifier("slackMessageSender") MessageSender messageSender + ) { + this.messageTemplate = messageTemplate; + this.messageExtractor = messageExtractor; + this.messageSender = messageSender; + } @Async @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void sendMessageToSlack(CertificationEvent certificationEvent) { CertificationMessage certificationMessage = EventMapper.supplyCertificationMessageBy(certificationEvent); - certificationTemplate.sendMessageForCertification(certificationMessage); + messageTemplate.sendMessage(certificationMessage, messageExtractor, messageSender); } } diff --git a/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java b/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java index e7a7d6fa..49c61a2f 100644 --- a/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java +++ b/cakk-api/src/main/java/com/cakk/api/listener/EmailSendEventListener.java @@ -1,7 +1,5 @@ package com.cakk.api.listener; -import java.util.Objects; - import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; @@ -9,25 +7,33 @@ import com.cakk.api.annotation.ApplicationEventListener; import com.cakk.api.dto.event.EmailWithVerificationCodeSendEvent; import com.cakk.api.mapper.EventMapper; -import com.cakk.external.template.VerificationCodeSendTemplate; +import com.cakk.external.extractor.MessageExtractor; +import com.cakk.external.sender.MessageSender; +import com.cakk.external.template.MessageTemplate; import com.cakk.external.vo.VerificationMessage; @ApplicationEventListener public class EmailSendEventListener { - private final VerificationCodeSendTemplate verificationCodeSendTemplate; + private final MessageTemplate messageTemplate; + private final MessageExtractor messageExtractor; + private final MessageSender messageSender; public EmailSendEventListener( - VerificationCodeSendTemplate verificationCodeSendTemplate + MessageTemplate messageTemplate, + @Qualifier("verificationCodeMimeMessageExtractor") MessageExtractor messageExtractor, + @Qualifier("emailMessageSender") MessageSender messageSender ) { - this.verificationCodeSendTemplate = verificationCodeSendTemplate; + this.messageTemplate = messageTemplate; + this.messageExtractor = messageExtractor; + this.messageSender = messageSender; } @Async @EventListener public void sendEmailIncludeVerificationCode(EmailWithVerificationCodeSendEvent event) { final VerificationMessage verificationMessage = EventMapper.supplyVerificationMessageBy(event); - verificationCodeSendTemplate.sendMessageForVerificationCode(verificationMessage); + messageTemplate.sendMessage(verificationMessage, messageExtractor, messageSender); } } diff --git a/cakk-api/src/main/java/com/cakk/api/listener/ErrorAlertEventListener.java b/cakk-api/src/main/java/com/cakk/api/listener/ErrorAlertEventListener.java new file mode 100644 index 00000000..6c0f272b --- /dev/null +++ b/cakk-api/src/main/java/com/cakk/api/listener/ErrorAlertEventListener.java @@ -0,0 +1,39 @@ +package com.cakk.api.listener; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.cakk.api.annotation.ApplicationEventListener; +import com.cakk.api.dto.event.ErrorAlertEvent; +import com.cakk.api.mapper.EventMapper; +import com.cakk.external.extractor.MessageExtractor; +import com.cakk.external.sender.MessageSender; +import com.cakk.external.template.MessageTemplate; +import com.cakk.external.vo.ErrorAlertMessage; + +@ApplicationEventListener +public class ErrorAlertEventListener { + + private final MessageTemplate messageTemplate; + private final MessageExtractor messageExtractor; + private final MessageSender messageSender; + + public ErrorAlertEventListener( + MessageTemplate messageTemplate, + @Qualifier("errorAlertMessageExtractor") MessageExtractor messageExtractor, + @Qualifier("slackMessageSender") MessageSender messageSender + ) { + this.messageTemplate = messageTemplate; + this.messageExtractor = messageExtractor; + this.messageSender = messageSender; + } + + @Async + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void sendMessageToSlack(ErrorAlertEvent errorAlertEvent) { + ErrorAlertMessage certificationMessage = EventMapper.supplyErrorAlertMessageBy(errorAlertEvent); + messageTemplate.sendMessage(certificationMessage, messageExtractor, messageSender); + } +} diff --git a/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java b/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java index 52b8a7bf..e6fd35c0 100644 --- a/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java +++ b/cakk-api/src/main/java/com/cakk/api/mapper/EventMapper.java @@ -1,14 +1,19 @@ package com.cakk.api.mapper; +import java.util.Arrays; import java.util.Objects; +import jakarta.servlet.http.HttpServletRequest; + import lombok.AccessLevel; import lombok.NoArgsConstructor; import com.cakk.api.dto.event.EmailWithVerificationCodeSendEvent; +import com.cakk.api.dto.event.ErrorAlertEvent; import com.cakk.api.dto.event.IncreaseSearchCountEvent; import com.cakk.domain.mysql.event.shop.CertificationEvent; import com.cakk.external.vo.CertificationMessage; +import com.cakk.external.vo.ErrorAlertMessage; import com.cakk.external.vo.VerificationMessage; @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -22,6 +27,14 @@ public static IncreaseSearchCountEvent supplyIncreaseSearchCountEventBy(final St return new IncreaseSearchCountEvent(keyword); } + public static ErrorAlertEvent supplyErrorAlertEventBy( + final Exception exception, + final HttpServletRequest request, + final String profile + ) { + return new ErrorAlertEvent(exception, request, profile); + } + public static CertificationMessage supplyCertificationMessageBy(final CertificationEvent certificationEvent) { Double latitude = null; Double longitude = null; @@ -47,4 +60,21 @@ public static CertificationMessage supplyCertificationMessageBy(final Certificat public static VerificationMessage supplyVerificationMessageBy(final EmailWithVerificationCodeSendEvent event) { return new VerificationMessage(event.email(), event.code()); } + + public static ErrorAlertMessage supplyErrorAlertMessageBy(final ErrorAlertEvent event) { + final String profile = event.profile(); + final String stackTrace = Arrays.toString(event.exception().getStackTrace()); + final HttpServletRequest request = event.request(); + + return new ErrorAlertMessage( + profile, + stackTrace, + request.getContextPath(), + request.getRequestURL().toString(), + request.getMethod(), + request.getParameterMap(), + request.getRemoteAddr(), + request.getHeader("User-Agent") + ); + } } diff --git a/cakk-api/src/main/java/com/cakk/api/service/slack/SlackService.java b/cakk-api/src/main/java/com/cakk/api/service/slack/SlackService.java deleted file mode 100644 index 053d02bc..00000000 --- a/cakk-api/src/main/java/com/cakk/api/service/slack/SlackService.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.cakk.api.service.slack; - -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import net.gpedro.integrations.slack.SlackApi; -import net.gpedro.integrations.slack.SlackAttachment; -import net.gpedro.integrations.slack.SlackField; -import net.gpedro.integrations.slack.SlackMessage; - -@Service -public class SlackService { - - private final SlackApi slackApi; - - private final String profile; - private final boolean isEnable; - - public SlackService( - SlackApi slackApi, - @Value("${spring.profiles.active}") - String profile, - @Value("${slack.webhook.is-enable}") - boolean isEnable - ) { - this.slackApi = slackApi; - this.profile = profile; - this.isEnable = isEnable; - } - - public void sendSlackForError(Exception exception, HttpServletRequest request) { - if (!isEnable) { - return; - } - - SlackAttachment slackAttachment = new SlackAttachment(); - slackAttachment.setFallback("Error"); - slackAttachment.setColor("danger"); - slackAttachment.setTitle("Error Detect"); - slackAttachment.setTitleLink(request.getContextPath()); - slackAttachment.setText(Arrays.toString(exception.getStackTrace())); - slackAttachment.setFields( - List.of( - new SlackField().setTitle("Request URL").setValue(request.getRequestURL().toString()), - new SlackField().setTitle("Request Method").setValue(request.getMethod()), - new SlackField().setTitle("Request Parameter").setValue(getRequestParameters(request)), - new SlackField().setTitle("Request Time").setValue(LocalDateTime.now().toString()), - new SlackField().setTitle("Request IP").setValue(request.getRemoteAddr()), - new SlackField().setTitle("Request User-Agent").setValue(request.getHeader("User-Agent")) - ) - ); - - SlackMessage slackMessage = new SlackMessage(); - - slackMessage.setAttachments(List.of(slackAttachment)); - slackMessage.setChannel("#log_server-error"); - slackMessage.setUsername("%s API Error".formatted(profile)); - slackMessage.setIcon(":alert:"); - slackMessage.setText("%s api 에러 발생".formatted(profile)); - - slackApi.call(slackMessage); - } - - private String getRequestParameters(HttpServletRequest request) { - Map parameterMap = request.getParameterMap(); - - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : parameterMap.entrySet()) { - String key = entry.getKey(); - String[] values = entry.getValue(); - - sb.append("Parameter Name: ").append(key); - - for (String value : values) { - sb.append("Parameter Value: ").append(value); - } - } - - return sb.toString(); - } -} diff --git a/cakk-api/src/test/java/com/cakk/api/common/base/MockMvcTest.java b/cakk-api/src/test/java/com/cakk/api/common/base/MockMvcTest.java index 2cafa971..02a1eec3 100644 --- a/cakk-api/src/test/java/com/cakk/api/common/base/MockMvcTest.java +++ b/cakk-api/src/test/java/com/cakk/api/common/base/MockMvcTest.java @@ -25,10 +25,10 @@ import com.cakk.api.controller.shop.ShopController; import com.cakk.api.controller.user.SignController; import com.cakk.api.filter.JwtAuthenticationFilter; +import com.cakk.api.listener.ErrorAlertEventListener; import com.cakk.api.service.like.HeartService; import com.cakk.api.service.like.LikeService; import com.cakk.api.service.shop.ShopService; -import com.cakk.api.service.slack.SlackService; import com.cakk.api.service.user.EmailVerificationService; import com.cakk.api.service.user.SignService; import com.cakk.api.service.views.ViewsService; @@ -56,9 +56,6 @@ public abstract class MockMvcTest { @MockBean protected JwtAuthenticationFilter jwtAuthenticationFilter; - @MockBean - protected SlackService slackService; - @MockBean protected S3Service s3Service; @@ -80,6 +77,9 @@ public abstract class MockMvcTest { @MockBean protected ViewsService viewsService; + @MockBean + protected ErrorAlertEventListener errorAlertEventListener; + @BeforeEach void setup(WebApplicationContext webApplicationContext) { mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) diff --git a/cakk-api/src/test/java/com/cakk/api/controller/GlobalControllerAdviceTest.java b/cakk-api/src/test/java/com/cakk/api/controller/GlobalControllerAdviceTest.java index 0aca3741..5a6a5c86 100644 --- a/cakk-api/src/test/java/com/cakk/api/controller/GlobalControllerAdviceTest.java +++ b/cakk-api/src/test/java/com/cakk/api/controller/GlobalControllerAdviceTest.java @@ -32,7 +32,7 @@ void handleCakkException2() throws Exception { final ReturnCode returnCode = ReturnCode.INTERNAL_SERVER_ERROR; doThrow(new CakkException(returnCode)).when(shopService).searchDetailById(1L); - doNothing().when(slackService).sendSlackForError(any(CakkException.class), any()); + doNothing().when(errorAlertEventListener).sendMessageToSlack(any()); // when & then mockMvc.perform(get("/shops/1")) @@ -40,7 +40,7 @@ void handleCakkException2() throws Exception { .andExpect(jsonPath("$.returnCode").value(returnCode.getCode())) .andExpect(jsonPath("$.returnMessage").value(returnCode.getMessage())); - verify(slackService, times(1)).sendSlackForError(any(CakkException.class), any()); + verify(errorAlertEventListener, times(1)).sendMessageToSlack(any()); } @TestWithDisplayName("CakkException이 발생했고 EXTERNAL_SERVER_ERROR 일 때 BAD_REQUEST를 반환한다.") @@ -49,7 +49,7 @@ void handleCakkException3() throws Exception { final ReturnCode returnCode = ReturnCode.EXTERNAL_SERVER_ERROR; doThrow(new CakkException(returnCode)).when(shopService).searchDetailById(1L); - doNothing().when(slackService).sendSlackForError(any(CakkException.class), any()); + doNothing().when(errorAlertEventListener).sendMessageToSlack(any()); // when & then mockMvc.perform(get("/shops/1")) @@ -57,7 +57,7 @@ void handleCakkException3() throws Exception { .andExpect(jsonPath("$.returnCode").value(returnCode.getCode())) .andExpect(jsonPath("$.returnMessage").value(returnCode.getMessage())); - verify(slackService, times(1)).sendSlackForError(any(CakkException.class), any()); + verify(errorAlertEventListener, times(1)).sendMessageToSlack(any()); } @TestWithDisplayName("HttpMessageNotReadableException이 발생하면 BAD_REQUEST를 반환한다.") diff --git a/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java b/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java index 59cca7fe..8b7925c2 100644 --- a/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java +++ b/cakk-api/src/test/java/com/cakk/api/listener/EmailSendEventListenerTest.java @@ -11,8 +11,7 @@ import com.cakk.api.common.annotation.TestWithDisplayName; import com.cakk.api.common.base.MockitoTest; import com.cakk.api.dto.event.EmailWithVerificationCodeSendEvent; -import com.cakk.external.sender.MessageSender; -import com.cakk.external.template.VerificationCodeSendTemplate; +import com.cakk.external.template.MessageTemplate; class EmailSendEventListenerTest extends MockitoTest { @@ -20,7 +19,7 @@ class EmailSendEventListenerTest extends MockitoTest { private EmailSendEventListener emailSendEventListener; @Mock - private VerificationCodeSendTemplate verificationCodeSendTemplate; + private MessageTemplate messageTemplate; private EmailWithVerificationCodeSendEvent eventFixture() { return getConstructorMonkey().giveMeBuilder(EmailWithVerificationCodeSendEvent.class) @@ -34,13 +33,13 @@ void sendEmailIncludeVerificationCode() { // given EmailWithVerificationCodeSendEvent event = eventFixture(); - doNothing().when(verificationCodeSendTemplate).sendMessageForVerificationCode(any()); + doNothing().when(messageTemplate).sendMessage(any(), any(), any()); // when assertDoesNotThrow(() -> emailSendEventListener.sendEmailIncludeVerificationCode(event)); // then - verify(verificationCodeSendTemplate, times(1)).sendMessageForVerificationCode(any()); + verify(messageTemplate, times(1)).sendMessage(any(), any(), any()); } @TestWithDisplayName("이벤트에 null 데이터가 포함돼 있으면, 메일 전송 메서드가 호출 시, 에러를 반환한다.") @@ -55,6 +54,6 @@ void sendEmailIncludeVerificationCode2() { ); // then - verify(verificationCodeSendTemplate, times(0)).sendMessageForVerificationCode(any()); + verify(messageTemplate, never()).sendMessage(any(), any(), any()); } } diff --git a/cakk-api/src/test/java/com/cakk/api/listener/ErrorAlertEventListenerTest.java b/cakk-api/src/test/java/com/cakk/api/listener/ErrorAlertEventListenerTest.java new file mode 100644 index 00000000..3a567fcd --- /dev/null +++ b/cakk-api/src/test/java/com/cakk/api/listener/ErrorAlertEventListenerTest.java @@ -0,0 +1,57 @@ +package com.cakk.api.listener; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; + +import org.junit.jupiter.api.BeforeEach; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; + +import com.cakk.api.common.annotation.TestWithDisplayName; +import com.cakk.api.common.base.MockitoTest; +import com.cakk.api.dto.event.ErrorAlertEvent; +import com.cakk.external.template.MessageTemplate; + +class ErrorAlertEventListenerTest extends MockitoTest { + + @InjectMocks + private ErrorAlertEventListener errorAlertEventListener; + + @Mock + private MessageTemplate messageTemplate; + + private HttpServletRequest mockRequest = Mockito.mock(HttpServletRequest.class); + + @BeforeEach + void setUp() { + Mockito.when(mockRequest.getContextPath()).thenReturn("/test"); + Mockito.when(mockRequest.getRequestURL()).thenReturn(new StringBuffer("http://localhost/test")); + Mockito.when(mockRequest.getMethod()).thenReturn("GET"); + Mockito.when(mockRequest.getParameterMap()).thenReturn(Map.of("param1", new String[]{"value1"})); + Mockito.when(mockRequest.getRemoteAddr()).thenReturn("127.0.0.1"); + Mockito.when(mockRequest.getHeader("User-Agent")).thenReturn("Mozilla/5.0"); + } + + @TestWithDisplayName("에러 알림 이벤트가 발생하면, 메시지 전송 메서드가 호출되어야 한다.") + void sendMessageToSlack() { + // given + ErrorAlertEvent errorAlertEvent = new ErrorAlertEvent( + new Exception("error"), + mockRequest, + "test" + ); + + doNothing().when(messageTemplate).sendMessage(any(), any(), any()); + + // when + assertDoesNotThrow(() -> errorAlertEventListener.sendMessageToSlack(errorAlertEvent)); + + // then + verify(messageTemplate, times(1)).sendMessage(any(), any(), any()); + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/executor/CertificationApiExecutor.java b/cakk-external/src/main/java/com/cakk/external/executor/CertificationApiExecutor.java deleted file mode 100644 index b170a6de..00000000 --- a/cakk-external/src/main/java/com/cakk/external/executor/CertificationApiExecutor.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.cakk.external.executor; - -public interface CertificationApiExecutor { - void send(T message); -} - diff --git a/cakk-external/src/main/java/com/cakk/external/executor/CertificationSlackApiExecutor.java b/cakk-external/src/main/java/com/cakk/external/executor/CertificationSlackApiExecutor.java deleted file mode 100644 index bbb424c8..00000000 --- a/cakk-external/src/main/java/com/cakk/external/executor/CertificationSlackApiExecutor.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.cakk.external.executor; - -import net.gpedro.integrations.slack.SlackApi; -import net.gpedro.integrations.slack.SlackMessage; - -public class CertificationSlackApiExecutor implements CertificationApiExecutor { - - private final SlackApi slackApi; - private final boolean isEnable; - - public CertificationSlackApiExecutor( - SlackApi slackApi, - boolean isEnable - ) { - this.slackApi = slackApi; - this.isEnable = isEnable; - } - - @Override - public void send(SlackMessage message) { - if (!isEnable) { - return; - } - - slackApi.call(message); - } -} - diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/CertificationMessageExtractor.java b/cakk-external/src/main/java/com/cakk/external/extractor/CertificationMessageExtractor.java deleted file mode 100644 index 9f88c59e..00000000 --- a/cakk-external/src/main/java/com/cakk/external/extractor/CertificationMessageExtractor.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.cakk.external.extractor; - -import com.cakk.external.vo.CertificationMessage; - -public interface CertificationMessageExtractor { - T extract(CertificationMessage certificationMessage); -} - diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java b/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java deleted file mode 100644 index a165adbe..00000000 --- a/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.cakk.external.extractor; - -import java.util.List; - -import net.gpedro.integrations.slack.SlackAttachment; -import net.gpedro.integrations.slack.SlackField; -import net.gpedro.integrations.slack.SlackMessage; - -import com.cakk.external.vo.CertificationMessage; - -public class CertificationSlackMessageExtractor implements CertificationMessageExtractor { - - @Override - public SlackMessage extract(CertificationMessage certificationMessage) { - SlackAttachment slackAttachment = new SlackAttachment(); - slackAttachment.setColor("good"); - slackAttachment.setFallback("OK"); - slackAttachment.setTitle("Request Certification"); - - slackAttachment.setFields(List.of( - new SlackField().setTitle("요청자 PK").setValue(String.valueOf(certificationMessage.userId())), - new SlackField().setTitle("요청자 이메일").setValue(certificationMessage.userEmail()), - new SlackField().setTitle("요청자 비상연락망").setValue(certificationMessage.emergencyContact()), - new SlackField().setTitle("요청자 신분증 이미지").setValue(certificationMessage.idCardImageUrl()), - new SlackField().setTitle("요청자 사업자등록증 이미지").setValue(certificationMessage.businessRegistrationImageUrl()), - new SlackField().setTitle("요청 사항").setValue(certificationMessage.message()), - new SlackField().setTitle("가게 이름").setValue(certificationMessage.shopName()), - new SlackField().setTitle("가게 위치 위도").setValue(String.valueOf(certificationMessage.latitude())), - new SlackField().setTitle("가게 위치 경도").setValue(String.valueOf(certificationMessage.longitude())) - )); - - SlackMessage slackMessage = new SlackMessage(); - slackMessage.setAttachments(List.of(slackAttachment)); - slackMessage.setText("사장님 인증 요청"); - slackMessage.setChannel("#cs_사장님인증"); - - return slackMessage; - } -} - diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.kt new file mode 100644 index 00000000..19f2a845 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/extractor/CertificationSlackMessageExtractor.kt @@ -0,0 +1,39 @@ +package com.cakk.external.extractor + +import net.gpedro.integrations.slack.SlackAttachment +import net.gpedro.integrations.slack.SlackField +import net.gpedro.integrations.slack.SlackMessage + +import com.cakk.external.vo.CertificationMessage + +class CertificationSlackMessageExtractor : SlackMessageExtractor { + + override fun extract(message: CertificationMessage): SlackMessage { + val slackAttachment = SlackAttachment() + slackAttachment.setColor("good") + slackAttachment.setFallback("OK") + slackAttachment.setTitle("Request Certification") + + slackAttachment.setFields( + listOf( + SlackField().setTitle("요청자 PK").setValue(message.userId.toString()), + SlackField().setTitle("요청자 이메일").setValue(message.userEmail), + SlackField().setTitle("요청자 비상연락망").setValue(message.emergencyContact), + SlackField().setTitle("요청자 신분증 이미지").setValue(message.idCardImageUrl), + SlackField().setTitle("요청자 사업자등록증 이미지").setValue(message.businessRegistrationImageUrl), + SlackField().setTitle("요청 사항").setValue(message.message), + SlackField().setTitle("가게 이름").setValue(message.shopName), + SlackField().setTitle("가게 위치 위도").setValue(message.latitude.toString()), + SlackField().setTitle("가게 위치 경도").setValue(message.longitude.toString()) + ) + ) + + val slackMessage = SlackMessage() + slackMessage.setAttachments(listOf(slackAttachment)) + slackMessage.setText("사장님 인증 요청") + slackMessage.setChannel("#cs_사장님인증") + + return slackMessage + } +} + diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/ErrorAlertSlackMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/ErrorAlertSlackMessageExtractor.kt new file mode 100644 index 00000000..0deca1f8 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/extractor/ErrorAlertSlackMessageExtractor.kt @@ -0,0 +1,56 @@ +package com.cakk.external.extractor + +import com.cakk.external.vo.ErrorAlertMessage +import net.gpedro.integrations.slack.SlackAttachment +import net.gpedro.integrations.slack.SlackField +import net.gpedro.integrations.slack.SlackMessage +import java.time.LocalDateTime + +class ErrorAlertSlackMessageExtractor : SlackMessageExtractor { + + override fun extract(message: ErrorAlertMessage): SlackMessage { + val slackAttachment = SlackAttachment() + slackAttachment.setFallback("Error") + slackAttachment.setColor("danger") + slackAttachment.setTitle("Error Detect") + slackAttachment.setTitleLink(message.contextPath) + slackAttachment.setText(message.stackTrace) + + slackAttachment.setFields( + listOf( + SlackField().setTitle("Request URL").setValue(message.requestURL), + SlackField().setTitle("Request Method").setValue(message.method), + SlackField().setTitle("Request Time").setValue(LocalDateTime.now().toString()), + SlackField().setTitle("Request IP").setValue(message.remoteAddr), + SlackField().setTitle("Request User-Agent").setValue(message.header), + message.parameterMap?.run { + SlackField().setTitle("Request Parameter").setValue(getRequestParameters(message.parameterMap)) + } + ) + ) + + val slackMessage = SlackMessage() + + slackMessage.setAttachments(listOf(slackAttachment)) + slackMessage.setChannel("#log_server-error") + slackMessage.setUsername("${message.serverProfile} API Error") + slackMessage.setIcon(":alert:") + slackMessage.setText("${message.serverProfile} api 에러 발생") + + return slackMessage + } + + private fun getRequestParameters(parameterMap: Map>): String { + val sb = StringBuilder() + + parameterMap.forEach { (key, values) -> + sb.append("Parameter Name: ").append(key) + + values.forEach { + sb.append("Parameter Value: ").append(it) + } + } + + return sb.toString() + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/MessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/MessageExtractor.kt new file mode 100644 index 00000000..d375c642 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/extractor/MessageExtractor.kt @@ -0,0 +1,6 @@ +package com.cakk.external.extractor + +fun interface MessageExtractor { + + fun extract(message: T): U +} diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/MimeMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/MimeMessageExtractor.kt new file mode 100644 index 00000000..a3866da0 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/extractor/MimeMessageExtractor.kt @@ -0,0 +1,8 @@ +package com.cakk.external.extractor + +import jakarta.mail.internet.MimeMessage + +interface MimeMessageExtractor : MessageExtractor { + + override fun extract(message: T): MimeMessage +} diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/SlackMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/SlackMessageExtractor.kt new file mode 100644 index 00000000..fe57731a --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/extractor/SlackMessageExtractor.kt @@ -0,0 +1,8 @@ +package com.cakk.external.extractor + +import net.gpedro.integrations.slack.SlackMessage + +fun interface SlackMessageExtractor : MessageExtractor { + + override fun extract(message: T): SlackMessage +} diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt deleted file mode 100644 index ff55c0a1..00000000 --- a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMessageExtractor.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.cakk.external.extractor - -import com.cakk.external.vo.VerificationMessage - -fun interface VerificationCodeMessageExtractor { - - fun extract(verificationMessage: VerificationMessage): T -} diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt index 417238b6..04ef2d0e 100644 --- a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt +++ b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt @@ -17,21 +17,21 @@ class VerificationCodeMimeMessageExtractor( private val mailSender: JavaMailSender, @Value("\${spring.mail.username}") private val senderEmail: String -) : VerificationCodeMessageExtractor { +) : MimeMessageExtractor { - override fun extract(verificationMessage: VerificationMessage): MimeMessage { - val message = mailSender.createMimeMessage() + override fun extract(message: VerificationMessage): MimeMessage { + val mimeMessage = mailSender.createMimeMessage() try { - val helper = MimeMessageHelper(message, true, "UTF-8") + val helper = MimeMessageHelper(mimeMessage, true, "UTF-8") - helper.setTo(verificationMessage.receiver) + helper.setTo(message.receiver) helper.setFrom(senderEmail) helper.setSubject("[케이크크] 이메일 인증") val body = """

인증코드를 확인해 주세요.

-

${verificationMessage.verificationCode}

+

${message.verificationCode}

이메일 인증 절차에 따라 이메일 인증코드를 발급해드립니다.
인증코드는 이메일 발송시점으로부터 3분 동안 유효합니다.
만약 본인 요청에 의한 이메일 인증이 아니라면, cakk.contact@gmail.com 으로 관련 내용을 전달해 주세요. @@ -45,6 +45,6 @@ class VerificationCodeMimeMessageExtractor( throw CakkException(ReturnCode.SEND_EMAIL_ERROR) } - return message + return mimeMessage } } diff --git a/cakk-external/src/main/java/com/cakk/external/sender/EmailSender.kt b/cakk-external/src/main/java/com/cakk/external/sender/EmailMessageSender.kt similarity index 95% rename from cakk-external/src/main/java/com/cakk/external/sender/EmailSender.kt rename to cakk-external/src/main/java/com/cakk/external/sender/EmailMessageSender.kt index 5c0fdf32..f6788e10 100644 --- a/cakk-external/src/main/java/com/cakk/external/sender/EmailSender.kt +++ b/cakk-external/src/main/java/com/cakk/external/sender/EmailMessageSender.kt @@ -9,7 +9,7 @@ import com.cakk.common.enums.ReturnCode import com.cakk.common.exception.CakkException @Component -class EmailSender( +class EmailMessageSender( private val mailSender: JavaMailSender ) : MessageSender { diff --git a/cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt b/cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt index fcfc0b19..9ed7ef2c 100644 --- a/cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt +++ b/cakk-external/src/main/java/com/cakk/external/sender/MessageSender.kt @@ -2,5 +2,5 @@ package com.cakk.external.sender fun interface MessageSender { - fun send( message: T) + fun send(message: T) } diff --git a/cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt b/cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt deleted file mode 100644 index 0745d467..00000000 --- a/cakk-external/src/main/java/com/cakk/external/template/CertificationTemplate.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.cakk.external.template - -import com.cakk.external.extractor.CertificationMessageExtractor -import com.cakk.external.sender.MessageSender -import com.cakk.external.vo.CertificationMessage - -class CertificationTemplate( - private val messageSender: MessageSender, - private val certificationMessageExtractor: CertificationMessageExtractor -) { - - fun sendMessageForCertification(certificationMessage: CertificationMessage) { - this.sendMessageForCertification( - certificationMessage = certificationMessage, - certificationMessageExtractor = certificationMessageExtractor, - messageSender = messageSender - ) - } - - private fun sendMessageForCertification( - certificationMessage: CertificationMessage, - certificationMessageExtractor: CertificationMessageExtractor, - messageSender: MessageSender - ) { - val extractMessage: T = certificationMessageExtractor.extract(certificationMessage) - messageSender.send(extractMessage) - } -} diff --git a/cakk-external/src/main/java/com/cakk/external/template/MessageTemplate.kt b/cakk-external/src/main/java/com/cakk/external/template/MessageTemplate.kt new file mode 100644 index 00000000..68e6c791 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/template/MessageTemplate.kt @@ -0,0 +1,16 @@ +package com.cakk.external.template + +import com.cakk.external.extractor.MessageExtractor +import com.cakk.external.sender.MessageSender + +class MessageTemplate { + + fun sendMessage( + message: T, + messageExtractor: MessageExtractor, + messageSender: MessageSender, + ) { + val extractMessage: U = messageExtractor.extract(message) + messageSender.send(extractMessage) + } +} diff --git a/cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt b/cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt deleted file mode 100644 index abbd5bb9..00000000 --- a/cakk-external/src/main/java/com/cakk/external/template/VerificationCodeSendTemplate.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.cakk.external.template - -import com.cakk.external.extractor.VerificationCodeMessageExtractor -import com.cakk.external.sender.MessageSender -import com.cakk.external.vo.VerificationMessage - -class VerificationCodeSendTemplate( - private val messageSender: MessageSender, - private val verificationCodeMessageExtractor: VerificationCodeMessageExtractor -) { - - fun sendMessageForVerificationCode(verificationMessage: VerificationMessage) { - this.sendMessageForVerificationCode( - verificationMessage = verificationMessage, - verificationCodeMessageExtractor = verificationCodeMessageExtractor, - messageSender = messageSender - ) - } - - private fun sendMessageForVerificationCode( - verificationMessage: VerificationMessage, - verificationCodeMessageExtractor: VerificationCodeMessageExtractor, - messageSender: MessageSender - ) { - val extractMessage: T = verificationCodeMessageExtractor.extract(verificationMessage) - messageSender.send(extractMessage) - } -} diff --git a/cakk-external/src/main/java/com/cakk/external/vo/ErrorAlertMessage.kt b/cakk-external/src/main/java/com/cakk/external/vo/ErrorAlertMessage.kt new file mode 100644 index 00000000..63a4b1de --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/vo/ErrorAlertMessage.kt @@ -0,0 +1,12 @@ +package com.cakk.external.vo + +data class ErrorAlertMessage( + val serverProfile: String, + val stackTrace: String?, + val contextPath: String?, + val requestURL: String?, + val method: String?, + val parameterMap: Map>?, + val remoteAddr: String?, + val header: String? +) From 1aa3922e4ed45346d26d4ec7ff3c57d64316a1fb Mon Sep 17 00:00:00 2001 From: Hyunseok Ko <56003992+lcomment@users.noreply.github.com> Date: Sat, 31 Aug 2024 12:19:06 +0900 Subject: [PATCH 04/11] =?UTF-8?q?Refactor=20|=20CAKK-61=20|=20DIP=20?= =?UTF-8?q?=EC=9C=84=EB=B0=98=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Chore | CAKK-61 | mail 및 slack 의존성 추가 * Refactor | CAKK-61 | Dip 위반 문제 해결 --- cakk-api/build.gradle.kts | 6 +++ .../api/config/MessageTemplateConfig.java | 43 +++++++++++++++++++ .../VerificationCodeMimeMessageExtractor.kt | 2 - .../external/sender/EmailMessageSender.kt | 2 - .../external/sender/SlackMessageSender.kt | 2 - 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cakk-api/build.gradle.kts b/cakk-api/build.gradle.kts index 4e191493..f1fba183 100644 --- a/cakk-api/build.gradle.kts +++ b/cakk-api/build.gradle.kts @@ -37,6 +37,12 @@ dependencies { // Point implementation("org.locationtech.jts:jts-core:1.18.2") + + // Mail + implementation("org.springframework.boot:spring-boot-starter-mail") + + // Slack + implementation("net.gpedro.integrations.slack:slack-webhook:1.4.0") } tasks.bootJar { diff --git a/cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java b/cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java index 168c1eca..4c2c69a7 100644 --- a/cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java +++ b/cakk-api/src/main/java/com/cakk/api/config/MessageTemplateConfig.java @@ -1,16 +1,44 @@ package com.cakk.api.config; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; + +import net.gpedro.integrations.slack.SlackApi; import com.cakk.external.extractor.CertificationSlackMessageExtractor; import com.cakk.external.extractor.ErrorAlertSlackMessageExtractor; import com.cakk.external.extractor.MessageExtractor; +import com.cakk.external.extractor.VerificationCodeMimeMessageExtractor; +import com.cakk.external.sender.EmailMessageSender; +import com.cakk.external.sender.MessageSender; +import com.cakk.external.sender.SlackMessageSender; import com.cakk.external.template.MessageTemplate; @Configuration public class MessageTemplateConfig { + private final JavaMailSender javaMailSender; + private final SlackApi slackApi; + + private final String senderEmail; + private final Boolean isEnable; + + public MessageTemplateConfig( + JavaMailSender javaMailSender, + SlackApi slackApi, + @Value("${spring.mail.username}") + String senderEmail, + @Value("${slack.webhook.is-enable}") + Boolean isEnable + ) { + this.javaMailSender = javaMailSender; + this.senderEmail = senderEmail; + this.isEnable = isEnable; + this.slackApi = slackApi; + } + @Bean public MessageTemplate certificationTemplate() { return new MessageTemplate(); @@ -25,4 +53,19 @@ public MessageExtractor certificationMessageExtractor() { public MessageExtractor errorAlertMessageExtractor() { return new ErrorAlertSlackMessageExtractor(); } + + @Bean + public MessageExtractor verificationCodeMimeMessageExtractor() { + return new VerificationCodeMimeMessageExtractor(javaMailSender, senderEmail); + } + + @Bean + public MessageSender emailMessageSender() { + return new EmailMessageSender(javaMailSender); + } + + @Bean + public MessageSender slackMessageSender() { + return new SlackMessageSender(slackApi, isEnable); + } } diff --git a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt index 04ef2d0e..15413760 100644 --- a/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt +++ b/cakk-external/src/main/java/com/cakk/external/extractor/VerificationCodeMimeMessageExtractor.kt @@ -6,13 +6,11 @@ import jakarta.mail.internet.MimeMessage import org.springframework.beans.factory.annotation.Value import org.springframework.mail.javamail.JavaMailSender import org.springframework.mail.javamail.MimeMessageHelper -import org.springframework.stereotype.Component import com.cakk.common.enums.ReturnCode import com.cakk.common.exception.CakkException import com.cakk.external.vo.VerificationMessage -@Component class VerificationCodeMimeMessageExtractor( private val mailSender: JavaMailSender, @Value("\${spring.mail.username}") diff --git a/cakk-external/src/main/java/com/cakk/external/sender/EmailMessageSender.kt b/cakk-external/src/main/java/com/cakk/external/sender/EmailMessageSender.kt index f6788e10..74ab9146 100644 --- a/cakk-external/src/main/java/com/cakk/external/sender/EmailMessageSender.kt +++ b/cakk-external/src/main/java/com/cakk/external/sender/EmailMessageSender.kt @@ -3,12 +3,10 @@ package com.cakk.external.sender import jakarta.mail.internet.MimeMessage import org.springframework.mail.javamail.JavaMailSender -import org.springframework.stereotype.Component import com.cakk.common.enums.ReturnCode import com.cakk.common.exception.CakkException -@Component class EmailMessageSender( private val mailSender: JavaMailSender ) : MessageSender { diff --git a/cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt b/cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt index 4420ea27..f299a392 100644 --- a/cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt +++ b/cakk-external/src/main/java/com/cakk/external/sender/SlackMessageSender.kt @@ -4,9 +4,7 @@ import net.gpedro.integrations.slack.SlackApi import net.gpedro.integrations.slack.SlackMessage import org.springframework.beans.factory.annotation.Value -import org.springframework.stereotype.Component -@Component class SlackMessageSender( private val slackApi: SlackApi, @Value("\${slack.webhook.is-enable}") From 9aefeb81c721e7eac5efe2a61b7a75140c6de05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=83=9C=EC=9A=A9?= Date: Wed, 28 Aug 2024 16:00:29 +0900 Subject: [PATCH 05/11] =?UTF-8?q?Fix=20|=20CAKK-57=20|=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EA=B0=90=EC=A7=80=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?readOnly=20=EC=98=B5=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/cakk/api/service/shop/ShopService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java b/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java index b67a1b19..aaea47ed 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java @@ -121,7 +121,7 @@ public CakeShopByMineResponse getMyBusinessId(final User user) { return ShopMapper.supplyCakeShopByMineResponseBy(result); } - @Transactional(readOnly = true) + @Transactional public void requestCertificationBusinessOwner(final CertificationParam param) { final BusinessInformation businessInformation = cakeShopReader.findBusinessInformationByCakeShopId(param.cakeShopId()); final CertificationEvent certificationEvent = verificationPolicy From 253755d4af71c3ab4961f889588db788865ef9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=83=9C=EC=9A=A9?= Date: Wed, 28 Aug 2024 17:53:08 +0900 Subject: [PATCH 06/11] =?UTF-8?q?Refactor=20|=20CAKK-57=20|=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mysql/repository/jpa/TagJpaRepository.java | 3 +++ .../domain/mysql/repository/reader/TagReader.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/jpa/TagJpaRepository.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/jpa/TagJpaRepository.java index 1e402b19..7186df2e 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/jpa/TagJpaRepository.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/jpa/TagJpaRepository.java @@ -1,5 +1,6 @@ package com.cakk.domain.mysql.repository.jpa; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -11,4 +12,6 @@ public interface TagJpaRepository extends JpaRepository { Optional findTagByTagName(String tagName); + + List findTagsByTagNameIsIn(List tagNames); } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java index 9eab6f0b..1d14bd7f 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java @@ -1,11 +1,13 @@ package com.cakk.domain.mysql.repository.reader; +import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; import com.cakk.domain.mysql.annotation.Reader; import com.cakk.domain.mysql.entity.cake.Tag; +import com.cakk.domain.mysql.mapper.TagMapper; import com.cakk.domain.mysql.repository.jpa.TagJpaRepository; @Reader @@ -17,4 +19,16 @@ public class TagReader { public Optional findByTagName(final String tagName) { return tagJpaRepository.findTagByTagName(tagName); } + + public List getTagsByTagName(final List tagNames) { + List tags = tagJpaRepository.findTagsByTagNameIsIn(tagNames); + + return tagNames.stream() + .map(tagName -> tags + .stream() + .filter(tag -> tag.getTagName().equals(tagName)) + .findAny() + .orElse(tagJpaRepository.save(TagMapper.supplyTagBy(tagName)))) + .toList(); + } } From e9bc517f05b851b3fd7f7efab26a434082d45c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=83=9C=EC=9A=A9?= Date: Wed, 28 Aug 2024 17:55:57 +0900 Subject: [PATCH 07/11] =?UTF-8?q?Fix=20|=20CAKK-57=20|=20DomainFacade=20?= =?UTF-8?q?=ED=95=A9=EC=84=B1=20=EC=95=A0=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cakk/domain/mysql/annotation/DomainFacade.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/DomainFacade.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/DomainFacade.java index 3c07ddb4..b3ecb7e4 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/DomainFacade.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/DomainFacade.java @@ -8,28 +8,26 @@ import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; -import org.springframework.stereotype.Service; /** * Indicates that an annotated class is a "Service" (e.g. a domain service object). * - *

This annotation serves as a specialization of {@link Service @Service}, + *

This annotation serves as a specialization of {@link Component @Component}, * allowing for implementation classes to be autodetected through classpath scanning. * * @author komment * @see Component - * @see Service */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -@Service +@Component public @interface DomainFacade { /** - * Alias for {@link Service#value}. + * Alias for {@link Component#value}. */ - @AliasFor(annotation = Service.class) + @AliasFor(annotation = Component.class) String value() default ""; } From f63f4c58df046c89edf39e42824963c4246902e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=83=9C=EC=9A=A9?= Date: Wed, 28 Aug 2024 20:42:54 +0900 Subject: [PATCH 08/11] =?UTF-8?q?Refactor=20|=20CAKK-57=20|=20=ED=8C=8C?= =?UTF-8?q?=EC=83=A4=EB=93=9C=20=ED=8C=A8=ED=84=B4=EC=9D=84=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cakk/api/service/cake/CakeService.java | 37 +++++++------------ .../mysql/facade/cake/CakeManagerFacade.java | 25 +++++++++++++ 2 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java diff --git a/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java b/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java index 29ae73d9..c02913b6 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java @@ -23,14 +23,15 @@ import com.cakk.domain.mysql.dto.param.cake.CakeImageResponseParam; import com.cakk.domain.mysql.dto.param.cake.CakeUpdateParam; import com.cakk.domain.mysql.entity.cake.Cake; +import com.cakk.domain.mysql.entity.cake.CakeCategory; import com.cakk.domain.mysql.entity.cake.Tag; import com.cakk.domain.mysql.entity.shop.CakeShop; import com.cakk.domain.mysql.entity.user.User; +import com.cakk.domain.mysql.facade.cake.CakeManagerFacade; import com.cakk.domain.mysql.repository.reader.CakeReader; import com.cakk.domain.mysql.repository.reader.CakeShopReader; import com.cakk.domain.mysql.repository.reader.TagReader; import com.cakk.domain.mysql.repository.writer.CakeWriter; -import com.cakk.domain.mysql.repository.writer.TagWriter; import com.cakk.domain.redis.repository.CakeViewsRedisRepository; @Transactional(readOnly = true) @@ -41,10 +42,9 @@ public class CakeService { private final CakeReader cakeReader; private final CakeWriter cakeWriter; private final TagReader tagReader; - private final TagWriter tagWriter; private final CakeShopReader cakeShopReader; private final CakeViewsRedisRepository cakeViewsRedisRepository; - + private final CakeManagerFacade cakeManagerFacade; private final ApplicationEventPublisher publisher; public CakeImageListResponse findCakeImagesByCursorAndCategory(final CakeSearchByCategoryRequest dto) { @@ -92,37 +92,28 @@ public CakeDetailResponse findCakeDetailById(Long cakeId) { @Transactional public void createCake(CakeCreateParam param) { - CakeShop cakeShop = cakeShopReader.searchByIdAndOwner(param.cakeShopId(), param.owner()); - Cake cake = param.cake(); - List tags = param.tagNames() - .stream() - .map(tagName -> tagReader.findByTagName(tagName).orElseGet(() -> tagWriter.saveTag(tagName))) - .toList(); - - cake.registerTags(tags); - cake.registerCategories(param.cakeCategories()); - cakeShop.registerCake(cake); + final CakeShop cakeShop = cakeShopReader.searchByIdAndOwner(param.cakeShopId(), param.owner()); + final Cake cake = param.cake(); + final List tags = tagReader.getTagsByTagName(param.tagNames()); + final List cakeCategories = param.cakeCategories(); + + cakeManagerFacade.createCake(cakeShop, cake, tags, cakeCategories); } @Transactional public void updateCake(CakeUpdateParam param) { final Cake cake = cakeReader.findWithCakeTagsAndCakeCategories(param.cakeId(), param.owner()); - List tags = param.tagNames() - .stream() - .map(tagName -> tagReader.findByTagName(tagName).orElseGet(() -> tagWriter.saveTag(tagName))) - .toList(); - - cake.updateCakeImageUrl(param.cakeImageUrl()); - cake.updateCakeCategories(param.cakeCategories()); - cake.updateCakeTags(tags); + final List tags = tagReader.getTagsByTagName(param.tagNames()); + final String cakeImageUrl = param.cakeImageUrl(); + final List cakeCategories = param.cakeCategories(); + + cakeManagerFacade.updateCake(cake, cakeImageUrl, tags, cakeCategories); } @Transactional public void deleteCake(User owner, Long cakeId) { final Cake cake = cakeReader.findWithCakeTagsAndCakeCategories(cakeId, owner); - cake.removeCakeCategories(); - cake.removeCakeTags(); cakeWriter.deleteCake(cake); } } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java new file mode 100644 index 00000000..31850123 --- /dev/null +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java @@ -0,0 +1,25 @@ +package com.cakk.domain.mysql.facade.cake; + +import java.util.List; + +import com.cakk.domain.mysql.annotation.DomainFacade; +import com.cakk.domain.mysql.entity.cake.Cake; +import com.cakk.domain.mysql.entity.cake.CakeCategory; +import com.cakk.domain.mysql.entity.cake.Tag; +import com.cakk.domain.mysql.entity.shop.CakeShop; + +@DomainFacade +public class CakeManagerFacade { + + public void createCake(CakeShop cakeShop, Cake cake, List tags, List cakeCategories) { + cake.registerTags(tags); + cake.registerCategories(cakeCategories); + cakeShop.registerCake(cake); + } + + public void updateCake(Cake cake, String cakeImageUrl, List tags, List cakeCategories) { + cake.updateCakeImageUrl(cakeImageUrl); + cake.updateCakeCategories(cakeCategories); + cake.updateCakeTags(tags); + } +} From 75ed6366be379f8e3129336334fdc22442d026c8 Mon Sep 17 00:00:00 2001 From: Hyunseok Ko <56003992+lcomment@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:35:22 +0900 Subject: [PATCH 09/11] =?UTF-8?q?Refactor=20|=20CAKK-62=20|=20external=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20kt=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename .java to .kt * Refactor | CAKK-62 | kotlin으로 전환 * Test | CAKK-62 | 테스트 코드 수정 --- .../api/controller/AwsS3ControllerTest.java | 6 +- .../com/cakk/external/config/S3Config.java | 46 ---------- .../java/com/cakk/external/config/S3Config.kt | 33 ++++++++ .../com/cakk/external/service/S3Service.java | 84 ------------------- .../com/cakk/external/service/S3Service.kt | 70 ++++++++++++++++ .../external/vo/CertificationMessage.java | 14 ---- .../cakk/external/vo/CertificationMessage.kt | 13 +++ .../com/cakk/external/vo/PresignedUrl.java | 8 -- .../java/com/cakk/external/vo/PresignedUrl.kt | 7 ++ 9 files changed, 126 insertions(+), 155 deletions(-) delete mode 100644 cakk-external/src/main/java/com/cakk/external/config/S3Config.java create mode 100644 cakk-external/src/main/java/com/cakk/external/config/S3Config.kt delete mode 100644 cakk-external/src/main/java/com/cakk/external/service/S3Service.java create mode 100644 cakk-external/src/main/java/com/cakk/external/service/S3Service.kt delete mode 100644 cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.java create mode 100644 cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.kt delete mode 100644 cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.java create mode 100644 cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.kt diff --git a/cakk-api/src/test/java/com/cakk/api/controller/AwsS3ControllerTest.java b/cakk-api/src/test/java/com/cakk/api/controller/AwsS3ControllerTest.java index c46d9d17..49c73957 100644 --- a/cakk-api/src/test/java/com/cakk/api/controller/AwsS3ControllerTest.java +++ b/cakk-api/src/test/java/com/cakk/api/controller/AwsS3ControllerTest.java @@ -26,8 +26,8 @@ void getImageUrl() throws Exception { // when & then mockMvc.perform(get("/aws/img")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.imagePath").value(presignedUrl.imagePath())) - .andExpect(jsonPath("$.data.imageUrl").value(presignedUrl.imageUrl())) - .andExpect(jsonPath("$.data.presignedUrl").value(presignedUrl.presignedUrl())); + .andExpect(jsonPath("$.data.imagePath").value(presignedUrl.getImagePath())) + .andExpect(jsonPath("$.data.imageUrl").value(presignedUrl.getImageUrl())) + .andExpect(jsonPath("$.data.presignedUrl").value(presignedUrl.getPresignedUrl())); } } diff --git a/cakk-external/src/main/java/com/cakk/external/config/S3Config.java b/cakk-external/src/main/java/com/cakk/external/config/S3Config.java deleted file mode 100644 index e8fc430e..00000000 --- a/cakk-external/src/main/java/com/cakk/external/config/S3Config.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.cakk.external.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; - -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; - -@Configuration -public class S3Config { - - private final String accessKey; - private final String secretKey; - private final String region; - - public S3Config( - @Value("${cloud.aws.credentials.access-key}") - String accessKey, - @Value("${cloud.aws.credentials.secret-key}") - String secretKey, - @Value("${cloud.aws.region.static}") - String region) { - this.accessKey = accessKey; - this.secretKey = secretKey; - this.region = region; - } - - @Bean - @Primary - public BasicAWSCredentials awsCredentialsProvider() { - return new BasicAWSCredentials(accessKey, secretKey); - } - - @Bean - public AmazonS3 amazonS3() { - return AmazonS3ClientBuilder.standard() - .withRegion(region) - .withCredentials(new AWSStaticCredentialsProvider(awsCredentialsProvider())) - .build(); - } -} - diff --git a/cakk-external/src/main/java/com/cakk/external/config/S3Config.kt b/cakk-external/src/main/java/com/cakk/external/config/S3Config.kt new file mode 100644 index 00000000..8999ac4e --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/config/S3Config.kt @@ -0,0 +1,33 @@ +package com.cakk.external.config + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary + +import com.amazonaws.auth.AWSStaticCredentialsProvider +import com.amazonaws.auth.BasicAWSCredentials +import com.amazonaws.services.s3.AmazonS3 +import com.amazonaws.services.s3.AmazonS3ClientBuilder + +@Configuration +class S3Config( + @Value("\${cloud.aws.credentials.access-key}") private val accessKey: String, + @Value("\${cloud.aws.credentials.secret-key}") private val secretKey: String, + @Value("\${cloud.aws.region.static}") private val region: String +) { + @Bean + @Primary + fun awsCredentialsProvider(): BasicAWSCredentials { + return BasicAWSCredentials(accessKey, secretKey) + } + + @Bean + fun amazonS3(): AmazonS3 { + return AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(AWSStaticCredentialsProvider(awsCredentialsProvider())) + .build() + } +} + diff --git a/cakk-external/src/main/java/com/cakk/external/service/S3Service.java b/cakk-external/src/main/java/com/cakk/external/service/S3Service.java deleted file mode 100644 index 758220ac..00000000 --- a/cakk-external/src/main/java/com/cakk/external/service/S3Service.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.cakk.external.service; - -import java.util.Date; -import java.util.UUID; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import com.amazonaws.AmazonServiceException; -import com.amazonaws.HttpMethod; -import com.amazonaws.SdkClientException; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; - -import com.cakk.common.enums.ReturnCode; -import com.cakk.common.exception.CakkException; -import com.cakk.external.vo.PresignedUrl; - -@Service -public class S3Service { - - private final AmazonS3 amazonS3; - private final String bucket; - private final String expiredIn; - private final String objectKey; - - public S3Service( - @Value("${cloud.aws.s3.bucket}") String bucket, - @Value("${cloud.aws.s3.expire-in}") String expiredIn, - @Value("${cloud.aws.s3.object-key}") String objectKey, - AmazonS3 amazonS3 - ) { - this.bucket = bucket; - this.expiredIn = expiredIn; - this.objectKey = objectKey; - this.amazonS3 = amazonS3; - } - - public PresignedUrl getPresignedUrlWithImagePath() { - try { - String imagePath = makeObjectKey(); - String imageUrl = getImageUrl(imagePath); - GeneratePresignedUrlRequest generatePresignedUrlRequest = createGeneratePresignedUrlRequestInstance( - imagePath); - String presignedUrl = generatePresignedUrlRequest(generatePresignedUrlRequest); - return new PresignedUrl(imagePath, imageUrl, presignedUrl); - } catch (SdkClientException e) { - throw new CakkException(ReturnCode.EXTERNAL_SERVER_ERROR); - } - } - - public void deleteObject(String imagePath) { - try { - amazonS3.deleteObject(bucket, imagePath); - } catch (AmazonServiceException e) { - throw new CakkException(ReturnCode.EXTERNAL_SERVER_ERROR); - } - } - - private GeneratePresignedUrlRequest createGeneratePresignedUrlRequestInstance(String imagePath) { - Date expiration = new Date(); - long expirationInMs = expiration.getTime(); - expirationInMs += Long.parseLong(expiredIn); - expiration.setTime(expirationInMs); - - return new GeneratePresignedUrlRequest(bucket, imagePath) - .withMethod(HttpMethod.PUT) - .withExpiration(expiration); - } - - private String generatePresignedUrlRequest(GeneratePresignedUrlRequest generatePresignedUrlRequest) - throws SdkClientException { - return amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString(); - } - - private String makeObjectKey() { - return new StringBuffer().append(objectKey).append("/").append(UUID.randomUUID()).append(".jpeg").toString(); - } - - private String getImageUrl(String imagePath) { - return amazonS3.getUrl(bucket, imagePath).toString(); - } -} - diff --git a/cakk-external/src/main/java/com/cakk/external/service/S3Service.kt b/cakk-external/src/main/java/com/cakk/external/service/S3Service.kt new file mode 100644 index 00000000..4b71753e --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/service/S3Service.kt @@ -0,0 +1,70 @@ +package com.cakk.external.service + +import java.util.* + +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service + +import com.amazonaws.AmazonServiceException +import com.amazonaws.HttpMethod +import com.amazonaws.SdkClientException +import com.amazonaws.services.s3.AmazonS3 +import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest + +import com.cakk.common.enums.ReturnCode +import com.cakk.common.exception.CakkException +import com.cakk.external.vo.PresignedUrl + +@Service +class S3Service( + private val amazonS3: AmazonS3, + @Value("\${cloud.aws.s3.bucket}") private val bucket: String, + @Value("\${cloud.aws.s3.expire-in}") private val expiredIn: String, + @Value("\${cloud.aws.s3.object-key}") private val objectKey: String +) { + + fun getPresignedUrlWithImagePath(): PresignedUrl { + try { + val imagePath = makeObjectKey() + val imageUrl = getImageUrl(imagePath) + val generatePresignedUrlRequest = createGeneratePresignedUrlRequestInstance(imagePath) + val presignedUrl = generatePresignedUrlRequest(generatePresignedUrlRequest) + + return PresignedUrl(imagePath, imageUrl, presignedUrl) + } catch (e: SdkClientException) { + throw CakkException(ReturnCode.EXTERNAL_SERVER_ERROR) + } + } + + fun deleteObject(imagePath: String) { + try { + amazonS3.deleteObject(bucket, imagePath) + } catch (e: AmazonServiceException) { + throw CakkException(ReturnCode.EXTERNAL_SERVER_ERROR) + } + } + + private fun createGeneratePresignedUrlRequestInstance(imagePath: String): GeneratePresignedUrlRequest { + val expiration = Date() + var expirationInMs = expiration.time + expirationInMs += expiredIn.toLong() + expiration.time = expirationInMs + + return GeneratePresignedUrlRequest(bucket, imagePath) + .withMethod(HttpMethod.PUT) + .withExpiration(expiration) + } + + private fun generatePresignedUrlRequest(generatePresignedUrlRequest: GeneratePresignedUrlRequest): String { + return amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString() + } + + private fun makeObjectKey(): String { + return StringBuffer().append(objectKey).append("/").append(UUID.randomUUID()).append(".jpeg").toString() + } + + private fun getImageUrl(imagePath: String): String { + return amazonS3.getUrl(bucket, imagePath).toString() + } +} + diff --git a/cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.java b/cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.java deleted file mode 100644 index 47d99371..00000000 --- a/cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.cakk.external.vo; - -public record CertificationMessage( - String businessRegistrationImageUrl, - String idCardImageUrl, - String emergencyContact, - String message, - Long userId, - String userEmail, - String shopName, - Double latitude, - Double longitude -) { -} diff --git a/cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.kt b/cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.kt new file mode 100644 index 00000000..f9ca957d --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/vo/CertificationMessage.kt @@ -0,0 +1,13 @@ +package com.cakk.external.vo + +data class CertificationMessage( + val businessRegistrationImageUrl: String, + val idCardImageUrl: String, + val emergencyContact: String, + val message: String, + val userId: Long, + val userEmail: String, + val shopName: String, + val latitude: Double, + val longitude: Double +) diff --git a/cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.java b/cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.java deleted file mode 100644 index d4d0d3ca..00000000 --- a/cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.cakk.external.vo; - -public record PresignedUrl( - String imagePath, - String imageUrl, - String presignedUrl -) { -} diff --git a/cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.kt b/cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.kt new file mode 100644 index 00000000..5f32f870 --- /dev/null +++ b/cakk-external/src/main/java/com/cakk/external/vo/PresignedUrl.kt @@ -0,0 +1,7 @@ +package com.cakk.external.vo + +data class PresignedUrl( + val imagePath: String, + val imageUrl: String, + val presignedUrl: String +) From 342f06c739a08ba3e66310d778463ed9813122c5 Mon Sep 17 00:00:00 2001 From: Hyunseok Ko <56003992+lcomment@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:06:27 +0900 Subject: [PATCH 10/11] =?UTF-8?q?Refactor=20|=20CAKK-63=20|=20Writer?= =?UTF-8?q?=EB=A5=BC=20Facade=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor | CAKK-63 | UserCommandFacade 네이밍 수정 * Refactor | CAKK-63 | Facade 패턴 적용 * Refactor | CAKK-63 | Writer 제거 --- .../com/cakk/admin/service/CakeService.kt | 77 ++++++++----------- .../com/cakk/admin/service/ShopService.kt | 11 +-- .../cakk/api/service/cake/CakeService.java | 8 +- .../cakk/api/service/shop/ShopService.java | 7 +- .../cakk/api/service/user/SignService.java | 6 +- .../cakk/api/service/user/UserService.java | 8 +- .../api/service/shop/ShopServiceTest.java | 8 +- .../api/service/user/SignServiceTest.java | 10 +-- .../api/service/user/UserServiceTest.java | 8 +- .../cakk/domain/mysql/annotation/Writer.java | 34 -------- .../domain/mysql/entity/shop/CakeShop.java | 46 +++++++---- .../mysql/facade/cake/CakeManagerFacade.java | 14 +++- .../facade/shop/CakeShopManagerFacade.java | 32 ++++++++ .../mysql/facade/tag/TagManagerFacade.java | 21 +++++ ...mandFacade.java => UserManagerFacade.java} | 2 +- .../mysql/repository/reader/TagReader.java | 5 -- .../repository/writer/CakeShopWriter.java | 40 ---------- .../mysql/repository/writer/CakeWriter.java | 18 ----- .../mysql/repository/writer/TagWriter.java | 19 ----- ...deTest.java => UserManagerFacadeTest.java} | 14 ++-- 20 files changed, 167 insertions(+), 221 deletions(-) delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/Writer.java create mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/shop/CakeShopManagerFacade.java create mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/tag/TagManagerFacade.java rename cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/{UserCommandFacade.java => UserManagerFacade.java} (97%) delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopWriter.java delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeWriter.java delete mode 100644 cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/TagWriter.java rename cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/{UserCommandFacadeTest.java => UserManagerFacadeTest.java} (91%) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/service/CakeService.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/service/CakeService.kt index 93318879..89095655 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/service/CakeService.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/service/CakeService.kt @@ -1,57 +1,44 @@ package com.cakk.admin.service +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + import com.cakk.admin.dto.param.CakeCreateByAdminParam import com.cakk.admin.dto.param.CakeUpdateByAdminParam +import com.cakk.domain.mysql.facade.cake.CakeManagerFacade +import com.cakk.domain.mysql.facade.tag.TagManagerFacade import com.cakk.domain.mysql.repository.reader.CakeReader import com.cakk.domain.mysql.repository.reader.CakeShopReader -import com.cakk.domain.mysql.repository.reader.TagReader -import com.cakk.domain.mysql.repository.writer.CakeWriter -import com.cakk.domain.mysql.repository.writer.TagWriter -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service class CakeService( - private val cakeShopReader: CakeShopReader, - private val cakeReader: CakeReader, - private val cakeWriter: CakeWriter, - private val tagReader: TagReader, - private val tagWriter: TagWriter + private val cakeShopReader: CakeShopReader, + private val cakeReader: CakeReader, + private val tagManagerFacade: TagManagerFacade, + private val cakeManagerFacade: CakeManagerFacade ) { - @Transactional - fun createCake(dto: CakeCreateByAdminParam) { - val cakeShop = cakeShopReader.findById(dto.cakeShopId) - val cake = dto.cake - val tags = dto.tagNames - .map { - tagReader.findByTagName(it).orElseGet { tagWriter.saveTag(it) } - }.toMutableList() - - cake.registerTags(tags) - cake.registerCategories(dto.cakeCategories) - cakeShop.registerCake(cake) - } - - @Transactional - fun updateCake(dto: CakeUpdateByAdminParam) { - val cake = cakeReader.findById(dto.cakeId) - val tags = dto.tagNames - .map { - tagReader.findByTagName(it).orElseGet { tagWriter.saveTag(it) } - }.toMutableList() - - cake.updateCakeImageUrl(dto.cakeImageUrl) - cake.updateCakeCategories(dto.cakeCategories) - cake.updateCakeTags(tags) - } - - @Transactional - fun deleteCake(cakeId: Long) { - val cake = cakeReader.findById(cakeId) - - cake.removeCakeCategories() - cake.removeCakeTags() - cakeWriter.deleteCake(cake) - } + @Transactional + fun createCake(dto: CakeCreateByAdminParam) { + val cakeShop = cakeShopReader.findById(dto.cakeShopId) + val cake = dto.cake + val tags = dto.tagNames.map { tagManagerFacade.saveIfNew(it) }.toMutableList() + + cakeManagerFacade.create(cakeShop, cake, tags, dto.cakeCategories) + } + + @Transactional + fun updateCake(dto: CakeUpdateByAdminParam) { + val cake = cakeReader.findById(dto.cakeId) + val tags = dto.tagNames.map { tagManagerFacade.saveIfNew(it) }.toMutableList() + + cakeManagerFacade.update(cake, dto.cakeImageUrl, tags, dto.cakeCategories) + } + + @Transactional + fun deleteCake(cakeId: Long) { + val cake = cakeReader.findById(cakeId) + + cakeManagerFacade.delete(cake) + } } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/service/ShopService.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/service/ShopService.kt index 2997f2fe..4475aa70 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/service/ShopService.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/service/ShopService.kt @@ -1,5 +1,8 @@ package com.cakk.admin.service +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + import com.cakk.admin.dto.param.CakeShopCreateByAdminParam import com.cakk.admin.dto.response.CakeShopCreateResponse import com.cakk.admin.mapper.supplyCakeShopCreateResponseBy @@ -8,20 +11,18 @@ import com.cakk.domain.mysql.dto.param.operation.UpdateShopOperationParam import com.cakk.domain.mysql.dto.param.shop.CakeShopUpdateParam import com.cakk.domain.mysql.dto.param.shop.UpdateShopAddressParam import com.cakk.domain.mysql.entity.shop.CakeShop +import com.cakk.domain.mysql.facade.shop.CakeShopManagerFacade import com.cakk.domain.mysql.repository.reader.CakeShopReader -import com.cakk.domain.mysql.repository.writer.CakeShopWriter -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service class ShopService( private val cakeShopReader: CakeShopReader, - private val cakeShopWriter: CakeShopWriter + private val cakeShopManagerFacade: CakeShopManagerFacade ) { @Transactional fun createCakeShopByCertification(dto: CakeShopCreateByAdminParam): CakeShopCreateResponse { - val result: CakeShop = cakeShopWriter.createCakeShop( + val result: CakeShop = cakeShopManagerFacade.createCakeShop( dto.cakeShop, dto.cakeShopOperations, dto.businessInformation, diff --git a/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java b/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java index c02913b6..bd2bb029 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/cake/CakeService.java @@ -31,7 +31,6 @@ import com.cakk.domain.mysql.repository.reader.CakeReader; import com.cakk.domain.mysql.repository.reader.CakeShopReader; import com.cakk.domain.mysql.repository.reader.TagReader; -import com.cakk.domain.mysql.repository.writer.CakeWriter; import com.cakk.domain.redis.repository.CakeViewsRedisRepository; @Transactional(readOnly = true) @@ -40,7 +39,6 @@ public class CakeService { private final CakeReader cakeReader; - private final CakeWriter cakeWriter; private final TagReader tagReader; private final CakeShopReader cakeShopReader; private final CakeViewsRedisRepository cakeViewsRedisRepository; @@ -97,7 +95,7 @@ public void createCake(CakeCreateParam param) { final List tags = tagReader.getTagsByTagName(param.tagNames()); final List cakeCategories = param.cakeCategories(); - cakeManagerFacade.createCake(cakeShop, cake, tags, cakeCategories); + cakeManagerFacade.create(cakeShop, cake, tags, cakeCategories); } @Transactional @@ -107,13 +105,13 @@ public void updateCake(CakeUpdateParam param) { final String cakeImageUrl = param.cakeImageUrl(); final List cakeCategories = param.cakeCategories(); - cakeManagerFacade.updateCake(cake, cakeImageUrl, tags, cakeCategories); + cakeManagerFacade.update(cake, cakeImageUrl, tags, cakeCategories); } @Transactional public void deleteCake(User owner, Long cakeId) { final Cake cake = cakeReader.findWithCakeTagsAndCakeCategories(cakeId, owner); - cakeWriter.deleteCake(cake); + cakeManagerFacade.delete(cake); } } diff --git a/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java b/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java index aaea47ed..c14ac65a 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/shop/ShopService.java @@ -51,11 +51,11 @@ import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.event.shop.CertificationEvent; import com.cakk.domain.mysql.event.views.CakeShopIncreaseViewsEvent; +import com.cakk.domain.mysql.facade.shop.CakeShopManagerFacade; import com.cakk.domain.mysql.mapper.EventMapper; import com.cakk.domain.mysql.repository.reader.BusinessInformationReader; import com.cakk.domain.mysql.repository.reader.CakeShopReader; import com.cakk.domain.mysql.repository.reader.UserReader; -import com.cakk.domain.mysql.repository.writer.CakeShopWriter; import com.cakk.domain.redis.repository.CakeShopViewsRedisRepository; @Service @@ -65,8 +65,9 @@ public class ShopService { private final UserReader userReader; private final CakeShopReader cakeShopReader; private final BusinessInformationReader businessInformationReader; - private final CakeShopWriter cakeShopWriter; + private final CakeShopManagerFacade cakeShopManagerFacade; private final CakeShopViewsRedisRepository cakeShopViewsRedisRepository; + private final VerificationPolicy verificationPolicy; private final ApplicationEventPublisher publisher; @@ -77,7 +78,7 @@ public CakeShopCreateResponse createCakeShopByCertification(final CreateShopRequ final List cakeShopOperations = ShopMapper.supplyCakeShopOperationsBy(cakeShop, request.operationDays()); final List cakeShopLinks = LinkMapper.supplyCakeShopLinksBy(cakeShop, request.links()); - final CakeShop result = cakeShopWriter.createCakeShop(cakeShop, cakeShopOperations, businessInformation, cakeShopLinks); + final CakeShop result = cakeShopManagerFacade.createCakeShop(cakeShop, cakeShopOperations, businessInformation, cakeShopLinks); return ShopMapper.supplyCakeShopCreateResponseBy(result); } diff --git a/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java b/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java index 111c3dfd..120a2146 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/user/SignService.java @@ -14,7 +14,7 @@ import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; import com.cakk.domain.mysql.entity.user.User; -import com.cakk.domain.mysql.facade.user.UserCommandFacade; +import com.cakk.domain.mysql.facade.user.UserManagerFacade; import com.cakk.domain.mysql.repository.reader.UserReader; import com.cakk.domain.redis.repository.TokenRedisRepository; @@ -26,13 +26,13 @@ public class SignService { private final JwtProvider jwtProvider; private final UserReader userReader; - private final UserCommandFacade userCommandFacade; + private final UserManagerFacade userManagerFacade; private final TokenRedisRepository tokenRedisRepository; @Transactional public JwtResponse signUp(final UserSignUpRequest dto) { final String providerId = oidcProviderFactory.getProviderId(dto.provider(), dto.idToken()); - final User user = userCommandFacade.create(UserMapper.supplyUserBy(dto, providerId)); + final User user = userManagerFacade.create(UserMapper.supplyUserBy(dto, providerId)); return JwtResponse.from(jwtProvider.generateToken(user)); } diff --git a/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java b/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java index 96c545f7..8663dee3 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/user/UserService.java @@ -11,7 +11,7 @@ import com.cakk.domain.mysql.dto.param.user.ProfileUpdateParam; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.entity.user.UserWithdrawal; -import com.cakk.domain.mysql.facade.user.UserCommandFacade; +import com.cakk.domain.mysql.facade.user.UserManagerFacade; import com.cakk.domain.mysql.repository.reader.UserReader; @Service @@ -19,7 +19,7 @@ public class UserService { private final UserReader userReader; - private final UserCommandFacade userCommandFacade; + private final UserManagerFacade userManagerFacade; @Transactional(readOnly = true) public ProfileInformationResponse findProfile(final User signInUser) { @@ -33,7 +33,7 @@ public void updateInformation(final User signInUser, final ProfileUpdateRequest final User user = userReader.findByUserId(signInUser.getId()); final ProfileUpdateParam param = UserMapper.supplyProfileUpdateParamBy(dto); - userCommandFacade.updateProfile(user, param); + userManagerFacade.updateProfile(user, param); } @Transactional @@ -41,6 +41,6 @@ public void withdraw(final User signInUser) { final User user = userReader.findByIdWithAll(signInUser.getId()); final UserWithdrawal withdrawal = UserMapper.supplyUserWithdrawalBy(user); - userCommandFacade.withdraw(user, withdrawal); + userManagerFacade.withdraw(user, withdrawal); } } diff --git a/cakk-api/src/test/java/com/cakk/api/service/shop/ShopServiceTest.java b/cakk-api/src/test/java/com/cakk/api/service/shop/ShopServiceTest.java index b496b7e4..8531d192 100644 --- a/cakk-api/src/test/java/com/cakk/api/service/shop/ShopServiceTest.java +++ b/cakk-api/src/test/java/com/cakk/api/service/shop/ShopServiceTest.java @@ -40,9 +40,9 @@ import com.cakk.domain.mysql.entity.user.BusinessInformation; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.event.shop.CertificationEvent; +import com.cakk.domain.mysql.facade.shop.CakeShopManagerFacade; import com.cakk.domain.mysql.repository.reader.CakeShopReader; import com.cakk.domain.mysql.repository.reader.UserReader; -import com.cakk.domain.mysql.repository.writer.CakeShopWriter; import com.cakk.domain.redis.repository.CakeShopViewsRedisRepository; @DisplayName("케이크 샵 조회 관련 비즈니스 로직 테스트") @@ -58,7 +58,7 @@ public class ShopServiceTest extends ServiceTest { private CakeShopReader cakeShopReader; @Mock - private CakeShopWriter cakeShopWriter; + private CakeShopManagerFacade cakeShopManagerFacade; @Mock private CakeShopViewsRedisRepository cakeShopViewsRedisRepository; @@ -203,7 +203,7 @@ void createCakeShop() { //given CreateShopRequest request = getCreateShopRequestFixture(); CakeShop cakeShop = getCakeShopFixture(); - when(cakeShopWriter.createCakeShop(any(CakeShop.class), anyList(), any(BusinessInformation.class), anyList())) + when(cakeShopManagerFacade.createCakeShop(any(CakeShop.class), anyList(), any(BusinessInformation.class), anyList())) .thenReturn(cakeShop); //when @@ -211,7 +211,7 @@ void createCakeShop() { //verify assertEquals(response.cakeShopId(), cakeShop.getId()); - verify(cakeShopWriter, times(1)) + verify(cakeShopManagerFacade, times(1)) .createCakeShop(any(CakeShop.class), anyList(), any(BusinessInformation.class), anyList()); } diff --git a/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java b/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java index c86a0c0d..d42b6bc9 100644 --- a/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java +++ b/cakk-api/src/test/java/com/cakk/api/service/user/SignServiceTest.java @@ -22,7 +22,7 @@ import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; import com.cakk.domain.mysql.entity.user.User; -import com.cakk.domain.mysql.facade.user.UserCommandFacade; +import com.cakk.domain.mysql.facade.user.UserManagerFacade; import com.cakk.domain.mysql.repository.reader.UserReader; import com.cakk.domain.redis.repository.TokenRedisRepository; @@ -42,7 +42,7 @@ class SignServiceTest extends ServiceTest { private UserReader userReader; @Mock - private UserCommandFacade userCommandFacade; + private UserManagerFacade userManagerFacade; @Mock private TokenRedisRepository tokenRedisRepository; @@ -63,7 +63,7 @@ void signUp1() { .sample(); doReturn(user.getProviderId()).when(oidcProviderFactory).getProviderId(dto.provider(), dto.idToken()); - doReturn(user).when(userCommandFacade).create(any(User.class)); + doReturn(user).when(userManagerFacade).create(any(User.class)); doReturn(jwt).when(jwtProvider).generateToken(user); // when @@ -75,7 +75,7 @@ void signUp1() { Assertions.assertNotNull(result.grantType()); verify(oidcProviderFactory, times(1)).getProviderId(dto.provider(), dto.idToken()); - verify(userCommandFacade, times(1)).create(any(User.class)); + verify(userManagerFacade, times(1)).create(any(User.class)); verify(jwtProvider, times(1)).generateToken(user); } @@ -98,7 +98,7 @@ void signUp2() { ReturnCode.EXPIRED_JWT_TOKEN.getMessage()); verify(oidcProviderFactory, times(1)).getProviderId(dto.provider(), dto.idToken()); - verify(userCommandFacade, times(0)).create(any(User.class)); + verify(userManagerFacade, times(0)).create(any(User.class)); verify(jwtProvider, times(0)).generateToken(user); } diff --git a/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java b/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java index 5eaf6d6b..6d7286e9 100644 --- a/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java +++ b/cakk-api/src/test/java/com/cakk/api/service/user/UserServiceTest.java @@ -17,7 +17,7 @@ import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; import com.cakk.domain.mysql.entity.user.User; -import com.cakk.domain.mysql.facade.user.UserCommandFacade; +import com.cakk.domain.mysql.facade.user.UserManagerFacade; import com.cakk.domain.mysql.repository.reader.UserReader; @DisplayName("유저 관련 비즈니스 로직 테스트") @@ -30,7 +30,7 @@ class UserServiceTest extends ServiceTest { private UserReader userReader; @Mock - private UserCommandFacade userCommandFacade; + private UserManagerFacade userManagerFacade; @TestWithDisplayName("유저 프로필을 조회한다.") void findProfile1() { @@ -100,7 +100,7 @@ void withdraw1() { Assertions.assertDoesNotThrow(() -> userService.withdraw(user)); verify(userReader, times(1)).findByIdWithAll(user.getId()); - verify(userCommandFacade, times(1)).withdraw(any(), any()); + verify(userManagerFacade, times(1)).withdraw(any(), any()); } @TestWithDisplayName("유저가 없는 경우, 탈퇴에 실패한다.") @@ -116,6 +116,6 @@ void withdraw2() { ReturnCode.NOT_EXIST_USER.getMessage()); verify(userReader, times(1)).findByIdWithAll(user.getId()); - verify(userCommandFacade, never()).withdraw(any(), any()); + verify(userManagerFacade, never()).withdraw(any(), any()); } } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/Writer.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/Writer.java deleted file mode 100644 index e21160d5..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/annotation/Writer.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.cakk.domain.mysql.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.core.annotation.AliasFor; -import org.springframework.stereotype.Component; - -/** - * Indicates that an annotated class is a "Writer" (e.g. a data access object). - * - *

This annotation serves as a specialization of {@link Component @Component}, - * allowing for implementation classes to be autodetected through classpath scanning. - * - * @author komment - * @see Component - * @see Repository - */ - -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Component -public @interface Writer { - - /** - * Alias for {@link Component#value}. - */ - @AliasFor(annotation = Component.class) - String value() default ""; -} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/shop/CakeShop.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/shop/CakeShop.java index 016a3b0f..e3016caa 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/shop/CakeShop.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/entity/shop/CakeShop.java @@ -76,7 +76,7 @@ public class CakeShop extends AuditEntity { @Column(name = "deleted_at") private LocalDateTime deletedAt; - @OneToOne(mappedBy = "cakeShop") + @OneToOne(mappedBy = "cakeShop", cascade = CascadeType.PERSIST) private BusinessInformation businessInformation; @OneToMany(mappedBy = "cakeShop", cascade = CascadeType.PERSIST, orphanRemoval = true) @@ -143,22 +143,6 @@ public boolean isHeartedBy(final User user) { return shopHearts.stream().anyMatch(it -> it.getUser().equals(user)); } - private void increaseLikeCount() { - this.likeCount++; - } - - private void increaseHeartCount() { - this.heartCount++; - } - - private void decreaseHeartCount() { - if (this.heartCount == 0) { - throw new CakkException(ReturnCode.INTERNAL_SERVER_ERROR); - } - - this.heartCount--; - } - public void updateBasicInformation(final CakeShopUpdateParam param) { thumbnailUrl = param.thumbnailUrl(); shopName = param.shopName(); @@ -166,6 +150,14 @@ public void updateBasicInformation(final CakeShopUpdateParam param) { shopDescription = param.shopDescription(); } + public void registerBusinessInformation(final BusinessInformation businessInformation) { + this.businessInformation = businessInformation; + } + + public void addShopLinks(final List cakeShopLinks) { + this.cakeShopLinks.addAll(cakeShopLinks); + } + public void updateShopLinks(final List cakeShopLinks) { this.cakeShopLinks.clear(); @@ -180,6 +172,10 @@ public void updateShopAddress(final UpdateShopAddressParam param) { location = param.location(); } + public void addShopOperationDays(final List cakeShopOperations) { + this.cakeShopOperations.addAll(cakeShopOperations); + } + public void updateShopOperationDays(final List cakeShopOperations) { this.cakeShopOperations.clear(); @@ -193,4 +189,20 @@ public void registerCake(final Cake cake) { cake.updateCakeShop(this); this.cakes.add(cake); } + + private void increaseLikeCount() { + this.likeCount++; + } + + private void increaseHeartCount() { + this.heartCount++; + } + + private void decreaseHeartCount() { + if (this.heartCount == 0) { + throw new CakkException(ReturnCode.INTERNAL_SERVER_ERROR); + } + + this.heartCount--; + } } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java index 31850123..a5c0258c 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/cake/CakeManagerFacade.java @@ -2,24 +2,34 @@ import java.util.List; +import lombok.RequiredArgsConstructor; + import com.cakk.domain.mysql.annotation.DomainFacade; import com.cakk.domain.mysql.entity.cake.Cake; import com.cakk.domain.mysql.entity.cake.CakeCategory; import com.cakk.domain.mysql.entity.cake.Tag; import com.cakk.domain.mysql.entity.shop.CakeShop; +import com.cakk.domain.mysql.repository.jpa.CakeJpaRepository; +@RequiredArgsConstructor @DomainFacade public class CakeManagerFacade { - public void createCake(CakeShop cakeShop, Cake cake, List tags, List cakeCategories) { + private final CakeJpaRepository cakeJpaRepository; + + public void create(CakeShop cakeShop, Cake cake, List tags, List cakeCategories) { cake.registerTags(tags); cake.registerCategories(cakeCategories); cakeShop.registerCake(cake); } - public void updateCake(Cake cake, String cakeImageUrl, List tags, List cakeCategories) { + public void update(Cake cake, String cakeImageUrl, List tags, List cakeCategories) { cake.updateCakeImageUrl(cakeImageUrl); cake.updateCakeCategories(cakeCategories); cake.updateCakeTags(tags); } + + public void delete(final Cake cake) { + cakeJpaRepository.delete(cake); + } } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/shop/CakeShopManagerFacade.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/shop/CakeShopManagerFacade.java new file mode 100644 index 00000000..5e4c21cf --- /dev/null +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/shop/CakeShopManagerFacade.java @@ -0,0 +1,32 @@ +package com.cakk.domain.mysql.facade.shop; + +import java.util.List; + +import lombok.RequiredArgsConstructor; + +import com.cakk.domain.mysql.annotation.DomainFacade; +import com.cakk.domain.mysql.entity.shop.CakeShop; +import com.cakk.domain.mysql.entity.shop.CakeShopLink; +import com.cakk.domain.mysql.entity.shop.CakeShopOperation; +import com.cakk.domain.mysql.entity.user.BusinessInformation; +import com.cakk.domain.mysql.repository.jpa.CakeShopJpaRepository; + +@RequiredArgsConstructor +@DomainFacade +public class CakeShopManagerFacade { + + private final CakeShopJpaRepository cakeShopJpaRepository; + + public CakeShop createCakeShop( + final CakeShop cakeShop, + final List cakeShopOperations, + final BusinessInformation businessInformation, + final List cakeShopLinks + ) { + cakeShop.addShopOperationDays(cakeShopOperations); + cakeShop.addShopLinks(cakeShopLinks); + cakeShop.registerBusinessInformation(businessInformation); + + return cakeShopJpaRepository.save(cakeShop); + } +} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/tag/TagManagerFacade.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/tag/TagManagerFacade.java new file mode 100644 index 00000000..767337b7 --- /dev/null +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/tag/TagManagerFacade.java @@ -0,0 +1,21 @@ +package com.cakk.domain.mysql.facade.tag; + +import lombok.RequiredArgsConstructor; + +import com.cakk.domain.mysql.annotation.DomainFacade; +import com.cakk.domain.mysql.entity.cake.Tag; +import com.cakk.domain.mysql.mapper.TagMapper; +import com.cakk.domain.mysql.repository.jpa.TagJpaRepository; + +@RequiredArgsConstructor +@DomainFacade +public class TagManagerFacade { + + private final TagJpaRepository tagJpaRepository; + + public Tag saveIfNew(final String tagName) { + return tagJpaRepository.findTagByTagName(tagName).orElseGet( + () -> tagJpaRepository.save(TagMapper.supplyTagBy(tagName)) + ); + } +} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserCommandFacade.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserManagerFacade.java similarity index 97% rename from cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserCommandFacade.java rename to cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserManagerFacade.java index e1b6b465..b196623e 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserCommandFacade.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/facade/user/UserManagerFacade.java @@ -14,7 +14,7 @@ @RequiredArgsConstructor @DomainFacade -public class UserCommandFacade { +public class UserManagerFacade { private final UserJpaRepository userJpaRepository; private final UserWithdrawalJpaRepository userWithdrawalJpaRepository; diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java index 1d14bd7f..042aebc8 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/reader/TagReader.java @@ -1,7 +1,6 @@ package com.cakk.domain.mysql.repository.reader; import java.util.List; -import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -16,10 +15,6 @@ public class TagReader { private final TagJpaRepository tagJpaRepository; - public Optional findByTagName(final String tagName) { - return tagJpaRepository.findTagByTagName(tagName); - } - public List getTagsByTagName(final List tagNames) { List tags = tagJpaRepository.findTagsByTagNameIsIn(tagNames); diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopWriter.java deleted file mode 100644 index a6b04a1b..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeShopWriter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.cakk.domain.mysql.repository.writer; - -import java.util.List; - -import lombok.RequiredArgsConstructor; - -import com.cakk.domain.mysql.annotation.Writer; -import com.cakk.domain.mysql.entity.shop.CakeShop; -import com.cakk.domain.mysql.entity.shop.CakeShopLink; -import com.cakk.domain.mysql.entity.shop.CakeShopOperation; -import com.cakk.domain.mysql.entity.user.BusinessInformation; -import com.cakk.domain.mysql.repository.jpa.BusinessInformationJpaRepository; -import com.cakk.domain.mysql.repository.jpa.CakeShopJpaRepository; -import com.cakk.domain.mysql.repository.jpa.CakeShopLinkJpaRepository; -import com.cakk.domain.mysql.repository.jpa.CakeShopOperationJpaRepository; - -@Writer -@RequiredArgsConstructor -public class CakeShopWriter { - - private final BusinessInformationJpaRepository businessInformationJpaRepository; - private final CakeShopJpaRepository cakeShopJpaRepository; - private final CakeShopOperationJpaRepository cakeShopOperationJpaRepository; - private final CakeShopLinkJpaRepository cakeShopLinkJpaRepository; - - public CakeShop createCakeShop( - final CakeShop cakeShop, - final List cakeShopOperations, - final BusinessInformation businessInformation, - final List cakeShopLinks - ) { - final CakeShop result = cakeShopJpaRepository.save(cakeShop); - - cakeShopOperationJpaRepository.saveAll(cakeShopOperations); - businessInformationJpaRepository.save(businessInformation); - cakeShopLinkJpaRepository.saveAll(cakeShopLinks); - - return result; - } -} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeWriter.java deleted file mode 100644 index d8c6dcf3..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/CakeWriter.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.cakk.domain.mysql.repository.writer; - -import lombok.RequiredArgsConstructor; - -import com.cakk.domain.mysql.annotation.Writer; -import com.cakk.domain.mysql.entity.cake.Cake; -import com.cakk.domain.mysql.repository.jpa.CakeJpaRepository; - -@Writer -@RequiredArgsConstructor -public class CakeWriter { - - private final CakeJpaRepository cakeJpaRepository; - - public void deleteCake(final Cake cake) { - cakeJpaRepository.delete(cake); - } -} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/TagWriter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/TagWriter.java deleted file mode 100644 index a83229bd..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/writer/TagWriter.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.cakk.domain.mysql.repository.writer; - -import lombok.RequiredArgsConstructor; - -import com.cakk.domain.mysql.annotation.Writer; -import com.cakk.domain.mysql.entity.cake.Tag; -import com.cakk.domain.mysql.mapper.TagMapper; -import com.cakk.domain.mysql.repository.jpa.TagJpaRepository; - -@Writer -@RequiredArgsConstructor -public class TagWriter { - - private final TagJpaRepository tagJpaRepository; - - public Tag saveTag(final String tagName) { - return tagJpaRepository.save(TagMapper.supplyTagBy(tagName)); - } -} diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserCommandFacadeTest.java b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserManagerFacadeTest.java similarity index 91% rename from cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserCommandFacadeTest.java rename to cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserManagerFacadeTest.java index 69cbf4d3..ca8ab0f2 100644 --- a/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserCommandFacadeTest.java +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/facade/user/UserManagerFacadeTest.java @@ -21,14 +21,14 @@ import com.cakk.domain.mysql.dto.param.user.ProfileUpdateParam; import com.cakk.domain.mysql.entity.user.User; import com.cakk.domain.mysql.entity.user.UserWithdrawal; -import com.cakk.domain.mysql.facade.user.UserCommandFacade; +import com.cakk.domain.mysql.facade.user.UserManagerFacade; import com.cakk.domain.mysql.repository.jpa.UserJpaRepository; import com.cakk.domain.mysql.repository.jpa.UserWithdrawalJpaRepository; -class UserCommandFacadeTest extends FacadeTest { +class UserManagerFacadeTest extends FacadeTest { @InjectMocks - private UserCommandFacade userCommandFacade; + private UserManagerFacade userManagerFacade; @Mock private UserJpaRepository userJpaRepository; @@ -42,7 +42,7 @@ void create() { final User user = getUserFixture(Role.USER); // when - userCommandFacade.create(user); + userManagerFacade.create(user); // then verify(userJpaRepository, times(1)).findByProviderId(user.getProviderId()); @@ -59,7 +59,7 @@ void create2() { // when assertThrows( CakkException.class, - () -> userCommandFacade.create(user), + () -> userManagerFacade.create(user), ReturnCode.ALREADY_EXIST_USER.getMessage() ); @@ -81,7 +81,7 @@ void updateProfile() { .sample(); // when - userCommandFacade.updateProfile(user, param); + userManagerFacade.updateProfile(user, param); // then assertEquals(param.profileImageUrl(), user.getProfileImageUrl()); @@ -104,7 +104,7 @@ void withdraw() { .sample(); // when - userCommandFacade.withdraw(user, withdrawal); + userManagerFacade.withdraw(user, withdrawal); // then verify(userWithdrawalJpaRepository, times(1)).save(any()); From 4c8ec2a8dd9bf0884e090127640dee04fa92b663 Mon Sep 17 00:00:00 2001 From: Hyunseok Ko <56003992+lcomment@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:30:32 +0900 Subject: [PATCH 11/11] =?UTF-8?q?Refactor=20|=20CAKK-65=20|=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=EB=AA=A8=EB=93=88=20kt=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename .java to .kt * Refactor | CAKK-65 | kt 전환 * Refactor | CAKK-65 | kt 전환에 따른 수정 * Refactor | CAKK-65 | lombok 제거 --- build.gradle.kts | 5 +- .../java/com/cakk/api/mapper/ShopMapper.java | 4 +- .../cakk/api/provider/oauth/OidcProvider.java | 2 +- .../api/provider/oauth/PublicKeyProvider.java | 2 +- .../user/EmailVerificationService.java | 4 +- .../cakk/common/enums/CakeDesignCategory.java | 15 ------ .../cakk/common/enums/CakeDesignCategory.kt | 15 ++++++ .../main/java/com/cakk/common/enums/Days.java | 17 ------- .../main/java/com/cakk/common/enums/Days.kt | 14 +++++ .../java/com/cakk/common/enums/Gender.java | 6 --- .../main/java/com/cakk/common/enums/Gender.kt | 8 +++ .../java/com/cakk/common/enums/LinkKind.java | 13 ----- .../java/com/cakk/common/enums/LinkKind.kt | 10 ++++ .../java/com/cakk/common/enums/Provider.java | 6 --- .../java/com/cakk/common/enums/Provider.kt | 8 +++ .../java/com/cakk/common/enums/RedisKey.java | 31 ----------- .../java/com/cakk/common/enums/RedisKey.kt | 28 ++++++++++ .../com/cakk/common/enums/ReturnCode.java | 51 ------------------- .../java/com/cakk/common/enums/ReturnCode.kt | 46 +++++++++++++++++ .../main/java/com/cakk/common/enums/Role.java | 10 ---- .../main/java/com/cakk/common/enums/Role.kt | 10 ++++ .../cakk/common/enums/VerificationStatus.java | 44 ---------------- .../cakk/common/enums/VerificationStatus.kt | 29 +++++++++++ .../cakk/common/exception/CakkException.java | 19 ------- .../cakk/common/exception/CakkException.kt | 17 +++++++ .../com/cakk/common/response/ApiResponse.java | 15 ++++-- .../com/cakk/common/utils/DecodeUtils.java | 14 ----- .../java/com/cakk/common/utils/DecodeUtils.kt | 7 +++ .../com/cakk/common/utils/RandomUtils.java | 18 ------- .../java/com/cakk/common/utils/RandomUtils.kt | 11 ++++ .../java/com/cakk/common/utils/SetUtils.java | 28 ---------- .../java/com/cakk/common/utils/SetUtils.kt | 18 +++++++ .../mysql/converter/DayOfWeekConverter.java | 2 +- .../VerificationStatusConverter.java | 5 +- 34 files changed, 246 insertions(+), 286 deletions(-) delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Days.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Days.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Gender.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Gender.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/LinkKind.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/LinkKind.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Provider.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Provider.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/RedisKey.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/RedisKey.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Role.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/Role.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.java create mode 100644 cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/exception/CakkException.java create mode 100644 cakk-common/src/main/java/com/cakk/common/exception/CakkException.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.java create mode 100644 cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.java create mode 100644 cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.kt delete mode 100644 cakk-common/src/main/java/com/cakk/common/utils/SetUtils.java create mode 100644 cakk-common/src/main/java/com/cakk/common/utils/SetUtils.kt diff --git a/build.gradle.kts b/build.gradle.kts index 72deccf3..9966d172 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,7 +41,10 @@ subprojects { } } - if (project.name == "cakk-admin" || project.name == "cakk-external") { + if (project.name == "cakk-admin" || + project.name == "cakk-external" || + project.name == "cakk-common" + ) { apply(plugin = "org.jetbrains.kotlin.jvm") apply(plugin = "org.jetbrains.kotlin.plugin.spring") apply(plugin = "org.jetbrains.kotlin.plugin.jpa") diff --git a/cakk-api/src/main/java/com/cakk/api/mapper/ShopMapper.java b/cakk-api/src/main/java/com/cakk/api/mapper/ShopMapper.java index 22c91fd3..00416350 100644 --- a/cakk-api/src/main/java/com/cakk/api/mapper/ShopMapper.java +++ b/cakk-api/src/main/java/com/cakk/api/mapper/ShopMapper.java @@ -1,5 +1,6 @@ package com.cakk.api.mapper; +import static com.cakk.common.utils.SetUtilsKt.*; import static java.util.Objects.*; import java.util.ArrayList; @@ -20,7 +21,6 @@ import com.cakk.api.dto.response.shop.CakeShopOwnerResponse; import com.cakk.api.dto.response.shop.CakeShopSearchResponse; import com.cakk.api.dto.response.shop.CakeShopSimpleResponse; -import com.cakk.common.utils.SetUtils; import com.cakk.domain.mysql.dto.param.like.HeartCakeShopResponseParam; import com.cakk.domain.mysql.dto.param.shop.CakeShopByLocationParam; import com.cakk.domain.mysql.dto.param.shop.CakeShopBySearchParam; @@ -157,7 +157,7 @@ public static CakeShopSearchResponse supplyCakeShopSearchResponseBy(final List cakeShops) { final int size = cakeShops.size(); - cakeShops.forEach(it -> SetUtils.keepOnlyNElements(it.cakeImageUrls(), 4)); + cakeShops.forEach(it -> keepOnlyNElements(it.cakeImageUrls(), 4)); return HeartCakeShopListResponse.builder() .cakeShops(cakeShops) diff --git a/cakk-api/src/main/java/com/cakk/api/provider/oauth/OidcProvider.java b/cakk-api/src/main/java/com/cakk/api/provider/oauth/OidcProvider.java index a1079357..07efcf66 100644 --- a/cakk-api/src/main/java/com/cakk/api/provider/oauth/OidcProvider.java +++ b/cakk-api/src/main/java/com/cakk/api/provider/oauth/OidcProvider.java @@ -1,7 +1,7 @@ package com.cakk.api.provider.oauth; import static com.cakk.common.enums.ReturnCode.*; -import static com.cakk.common.utils.DecodeUtils.*; +import static com.cakk.common.utils.DecodeUtilsKt.*; import java.io.IOException; import java.util.Map; diff --git a/cakk-api/src/main/java/com/cakk/api/provider/oauth/PublicKeyProvider.java b/cakk-api/src/main/java/com/cakk/api/provider/oauth/PublicKeyProvider.java index 2bfbe3fd..5db944cf 100644 --- a/cakk-api/src/main/java/com/cakk/api/provider/oauth/PublicKeyProvider.java +++ b/cakk-api/src/main/java/com/cakk/api/provider/oauth/PublicKeyProvider.java @@ -1,7 +1,7 @@ package com.cakk.api.provider.oauth; import static com.cakk.common.enums.ReturnCode.*; -import static com.cakk.common.utils.DecodeUtils.*; +import static com.cakk.common.utils.DecodeUtilsKt.*; import java.math.BigInteger; import java.security.KeyFactory; diff --git a/cakk-api/src/main/java/com/cakk/api/service/user/EmailVerificationService.java b/cakk-api/src/main/java/com/cakk/api/service/user/EmailVerificationService.java index 515cd628..3c5a302c 100644 --- a/cakk-api/src/main/java/com/cakk/api/service/user/EmailVerificationService.java +++ b/cakk-api/src/main/java/com/cakk/api/service/user/EmailVerificationService.java @@ -1,5 +1,6 @@ package com.cakk.api.service.user; +import static com.cakk.common.utils.RandomUtilsKt.*; import static java.util.Objects.*; import org.springframework.context.ApplicationEventPublisher; @@ -13,7 +14,6 @@ import com.cakk.api.mapper.EventMapper; import com.cakk.common.enums.ReturnCode; import com.cakk.common.exception.CakkException; -import com.cakk.common.utils.RandomUtils; import com.cakk.domain.redis.repository.EmailVerificationRedisRepository; @Service @@ -26,7 +26,7 @@ public class EmailVerificationService { public void sendEmailForVerification(final GenerateCodeRequest dto) { final String email = dto.email(); - final String code = RandomUtils.generateRandomStringOnlyNumber(6); + final String code = generateRandomStringOnlyNumber(6); emailVerificationRedisRepository.save(email, code); final EmailWithVerificationCodeSendEvent emailEvent = EventMapper.supplyEmailWithVerificationCodeSendEventBy(email, code); diff --git a/cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.java b/cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.java deleted file mode 100644 index 626a2ec2..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.cakk.common.enums; - -public enum CakeDesignCategory { - - THREE_DIMENSIONAL, - CHARACTER, - PHOTO, - LUNCHBOX, - FIGURE, - FLOWER, - LETTERING, - RICE_CAKE, - TIARA, - ETC -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.kt b/cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.kt new file mode 100644 index 00000000..604357de --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/CakeDesignCategory.kt @@ -0,0 +1,15 @@ +package com.cakk.common.enums + +enum class CakeDesignCategory { + + THREE_DIMENSIONAL, + CHARACTER, + PHOTO, + LUNCHBOX, + FIGURE, + FLOWER, + LETTERING, + RICE_CAKE, + TIARA, + ETC +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Days.java b/cakk-common/src/main/java/com/cakk/common/enums/Days.java deleted file mode 100644 index c318e669..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/Days.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.cakk.common.enums; - -public enum Days { - - MON(0), TUE(1), WED(2), THU(3), FRI(4), SAT(5), SUN(6); - - private Integer code; - - private Days(Integer code) { - this.code = code; - } - - public Integer getCode() { - return code; - } - -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Days.kt b/cakk-common/src/main/java/com/cakk/common/enums/Days.kt new file mode 100644 index 00000000..0162bfb2 --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/Days.kt @@ -0,0 +1,14 @@ +package com.cakk.common.enums + +enum class Days( + val code: Int +) { + + MON(0), + TUE(1), + WED(2), + THU(3), + FRI(4), + SAT(5), + SUN(6); +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Gender.java b/cakk-common/src/main/java/com/cakk/common/enums/Gender.java deleted file mode 100644 index ec59381d..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/Gender.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.cakk.common.enums; - -public enum Gender { - - MALE, FEMALE, UNKNOWN -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Gender.kt b/cakk-common/src/main/java/com/cakk/common/enums/Gender.kt new file mode 100644 index 00000000..72a11934 --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/Gender.kt @@ -0,0 +1,8 @@ +package com.cakk.common.enums + +enum class Gender { + + MALE, + FEMALE, + UNKNOWN +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/LinkKind.java b/cakk-common/src/main/java/com/cakk/common/enums/LinkKind.java deleted file mode 100644 index 59a41f41..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/LinkKind.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.cakk.common.enums; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum LinkKind { - - WEB("web"), KAKAOTALK("kakaotalk"), INSTAGRAM("instagram"); - - private final String value; -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/LinkKind.kt b/cakk-common/src/main/java/com/cakk/common/enums/LinkKind.kt new file mode 100644 index 00000000..ec4980a7 --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/LinkKind.kt @@ -0,0 +1,10 @@ +package com.cakk.common.enums + +enum class LinkKind( + val value: String +) { + + WEB("web"), + KAKAOTALK("kakaotalk"), + INSTAGRAM("instagram"); +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Provider.java b/cakk-common/src/main/java/com/cakk/common/enums/Provider.java deleted file mode 100644 index 8b930a80..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/Provider.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.cakk.common.enums; - -public enum Provider { - - APPLE, GOOGLE, KAKAO -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Provider.kt b/cakk-common/src/main/java/com/cakk/common/enums/Provider.kt new file mode 100644 index 00000000..c5468622 --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/Provider.kt @@ -0,0 +1,8 @@ +package com.cakk.common.enums + +enum class Provider { + + APPLE, + GOOGLE, + KAKAO +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/RedisKey.java b/cakk-common/src/main/java/com/cakk/common/enums/RedisKey.java deleted file mode 100644 index f5b5a423..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/RedisKey.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.cakk.common.enums; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import com.cakk.common.exception.CakkException; - -@Getter -@RequiredArgsConstructor -public enum RedisKey { - - REFRESH_TOKEN("REFRESH_TOKEN::"), - EMAIL_VERIFICATION("EMAIL::"), - SEARCH_KEYWORD("SEARCH::keyword"), - VIEWS_CAKE("VIEWS::cake"), - VIEWS_CAKE_SHOP("VIEWS::cake-shop"), - LOCK_CAKE_HEART("LOCK::cake-heart"), - LOCK_SHOP_HEART("LOCK::shop-heart"), - LOCK_SHOP_LIKE("LOCK::shop-like"); - - private final String value; - - public static RedisKey getLockByMethodName(String method) { - return switch (method) { - case "heartCake" -> LOCK_CAKE_HEART; - case "heartCakeShop" -> LOCK_SHOP_HEART; - case "likeCakeShop" -> LOCK_SHOP_LIKE; - default -> throw new CakkException(ReturnCode.INTERNAL_SERVER_ERROR); - }; - } -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/RedisKey.kt b/cakk-common/src/main/java/com/cakk/common/enums/RedisKey.kt new file mode 100644 index 00000000..3e66225d --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/RedisKey.kt @@ -0,0 +1,28 @@ +package com.cakk.common.enums + +import com.cakk.common.exception.CakkException + +enum class RedisKey( + val value: String +) { + REFRESH_TOKEN("REFRESH_TOKEN::"), + EMAIL_VERIFICATION("EMAIL::"), + SEARCH_KEYWORD("SEARCH::keyword"), + VIEWS_CAKE("VIEWS::cake"), + VIEWS_CAKE_SHOP("VIEWS::cake-shop"), + LOCK_CAKE_HEART("LOCK::cake-heart"), + LOCK_SHOP_HEART("LOCK::shop-heart"), + LOCK_SHOP_LIKE("LOCK::shop-like"); + + companion object { + @JvmStatic + fun getLockByMethodName(method: String): RedisKey { + return when (method) { + "heartCake" -> LOCK_CAKE_HEART + "heartCakeShop" -> LOCK_SHOP_HEART + "likeCakeShop" -> LOCK_SHOP_LIKE + else -> throw CakkException(ReturnCode.INTERNAL_SERVER_ERROR) + } + } + } +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.java b/cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.java deleted file mode 100644 index 7bde698c..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.cakk.common.enums; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum ReturnCode { - - SUCCESS("1000", "요청에 성공하셨습니다."), - - // 토큰 관련 (1100 ~ 1150) - NOT_EXIST_BEARER_SUFFIX("1100", "Bearer 접두사가 포함되지 않았습니다."), - WRONG_JWT_TOKEN("1101", "잘못된 jwt 토큰입니다."), - EXPIRED_JWT_TOKEN("1102", "만료된 jwt 토큰입니다."), - EMPTY_AUTH_JWT("1103", "인증 정보가 비어있는 jwt 토큰입니다."), - EMPTY_USER("1104", "비어있는 유저 정보로 jwt 토큰을 생성할 수 없습니다."), - INVALID_KEY("1105", "잘못된 key 입니다"), - EMPTY_REFRESH("1106", "리프레시 토큰이 존재하지 않습니다."), - BLACK_LIST_TOKEN("1107", "블랙 리스트에 등록된 토큰 입니다."), - EMPTY_ACCESS("1108", "액세스 토큰이 존재하지 않습니다."), - - // 공통 유저 관련 (1200 ~ 1250) - WRONG_PROVIDER("1200", "잘못된 인증 제공자 입니다."), - NOT_EXIST_USER("1201", "존재하지 않는 유저 입니다."), - ALREADY_EXIST_USER("1202", "이미 가입한 유저 입니다."), - MAX_CAKE_SHOP_LIKE("1203", "케이크샵 좋아요는 최대 50번 입니다."), - WRONG_VERIFICATION_CODE("1204", "잘못된 이메일인증 코드 입니다."), - - // 케이크 샵 에러 (1300 ~ 1350) - NOT_EXIST_CAKE_SHOP("1300", "존재하지 않는 케이크 샵 입니다"), - NOT_CAKE_SHOP_OWNER("1301", "케이크샵 주인이 아닙니다"), - CAKE_SHOP_CERTIFICATED_ISSUE("1302", "케이크 샵 인증 요청에 문제가 있습니다"), - - // 케이크 에러 (1350 ~ 1400) - NOT_EXIST_CAKE("1350", "존재하지 않는 케이크 입니다"), - NOT_EXIST_CAKE_CATEGORY("1301", "존재하지 않는 케이크 카테고리 입니다"), - - // 클라이언트 에러 - WRONG_PARAMETER("9000", "잘못된 파라미터 입니다."), - METHOD_NOT_ALLOWED("9001", "허용되지 않은 메소드 입니다."), - - // 서버 에러 (9997 ~ 9999) - SEND_EMAIL_ERROR("9996", "이메일 전송 에러 입니다."), - LOCK_RESOURCES_ERROR("9997", "락에 의해 리소스을 점유할 수 없습니다."), - INTERNAL_SERVER_ERROR("9998", "내부 서버 에러 입니다."), - EXTERNAL_SERVER_ERROR("9999", "외부 서버 에러 입니다."); - - private final String code; - private final String message; -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.kt b/cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.kt new file mode 100644 index 00000000..6fd0f370 --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/ReturnCode.kt @@ -0,0 +1,46 @@ +package com.cakk.common.enums + +enum class ReturnCode( + val code: String, + val message: String +) { + + SUCCESS("1000", "요청에 성공하셨습니다."), + + // 토큰 관련 (1100 ~ 1150) + NOT_EXIST_BEARER_SUFFIX("1100", "Bearer 접두사가 포함되지 않았습니다."), + WRONG_JWT_TOKEN("1101", "잘못된 jwt 토큰입니다."), + EXPIRED_JWT_TOKEN("1102", "만료된 jwt 토큰입니다."), + EMPTY_AUTH_JWT("1103", "인증 정보가 비어있는 jwt 토큰입니다."), + EMPTY_USER("1104", "비어있는 유저 정보로 jwt 토큰을 생성할 수 없습니다."), + INVALID_KEY("1105", "잘못된 key 입니다"), + EMPTY_REFRESH("1106", "리프레시 토큰이 존재하지 않습니다."), + BLACK_LIST_TOKEN("1107", "블랙 리스트에 등록된 토큰 입니다."), + EMPTY_ACCESS("1108", "액세스 토큰이 존재하지 않습니다."), + + // 공통 유저 관련 (1200 ~ 1250) + WRONG_PROVIDER("1200", "잘못된 인증 제공자 입니다."), + NOT_EXIST_USER("1201", "존재하지 않는 유저 입니다."), + ALREADY_EXIST_USER("1202", "이미 가입한 유저 입니다."), + MAX_CAKE_SHOP_LIKE("1203", "케이크샵 좋아요는 최대 50번 입니다."), + WRONG_VERIFICATION_CODE("1204", "잘못된 이메일인증 코드 입니다."), + + // 케이크 샵 에러 (1300 ~ 1350) + NOT_EXIST_CAKE_SHOP("1300", "존재하지 않는 케이크 샵 입니다"), + NOT_CAKE_SHOP_OWNER("1301", "케이크샵 주인이 아닙니다"), + CAKE_SHOP_CERTIFICATED_ISSUE("1302", "케이크 샵 인증 요청에 문제가 있습니다"), + + // 케이크 에러 (1350 ~ 1400) + NOT_EXIST_CAKE("1350", "존재하지 않는 케이크 입니다"), + NOT_EXIST_CAKE_CATEGORY("1301", "존재하지 않는 케이크 카테고리 입니다"), + + // 클라이언트 에러 + WRONG_PARAMETER("9000", "잘못된 파라미터 입니다."), + METHOD_NOT_ALLOWED("9001", "허용되지 않은 메소드 입니다."), + + // 서버 에러 (9997 ~ 9999) + SEND_EMAIL_ERROR("9996", "이메일 전송 에러 입니다."), + LOCK_RESOURCES_ERROR("9997", "락에 의해 리소스을 점유할 수 없습니다."), + INTERNAL_SERVER_ERROR("9998", "내부 서버 에러 입니다."), + EXTERNAL_SERVER_ERROR("9999", "외부 서버 에러 입니다."); +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Role.java b/cakk-common/src/main/java/com/cakk/common/enums/Role.java deleted file mode 100644 index e909e6dd..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/Role.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.cakk.common.enums; - -public enum Role { - - ADMIN, USER; - - public String getSecurityRole() { - return "ROLE_" + this; - } -} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/Role.kt b/cakk-common/src/main/java/com/cakk/common/enums/Role.kt new file mode 100644 index 00000000..b37c0e2a --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/Role.kt @@ -0,0 +1,10 @@ +package com.cakk.common.enums + +enum class Role { + + ADMIN, + USER; + + val securityRole: String + get() = "ROLE_$this" +} diff --git a/cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.java b/cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.java deleted file mode 100644 index fd0ce63d..00000000 --- a/cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.cakk.common.enums; - -public enum VerificationStatus { - - UNREQUESTED(0), - APPROVED(1), - REJECTED(2), - PENDING(3); - - private final Integer code; - - VerificationStatus(Integer code) { - this.code = code; - } - - public Integer getCode() { - return code; - } - - public boolean isCandidate() { - return code == 3; - } - - public boolean isNotCandidate() { - return code != 3; - } - - public boolean isApproved() { - return code == 1; - } - - public boolean isRejected() { - return code == 2; - } - - public VerificationStatus makeApproved() { - return VerificationStatus.APPROVED; - } - - public VerificationStatus makePending() { - return VerificationStatus.PENDING; - } -} - diff --git a/cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.kt b/cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.kt new file mode 100644 index 00000000..68a8947c --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/enums/VerificationStatus.kt @@ -0,0 +1,29 @@ +package com.cakk.common.enums + +enum class VerificationStatus(@JvmField val code: Int) { + UNREQUESTED(0), + APPROVED(1), + REJECTED(2), + PENDING(3); + + val isCandidate: Boolean + get() = code == 3 + + val isNotCandidate: Boolean + get() = code != 3 + + val isApproved: Boolean + get() = code == 1 + + val isRejected: Boolean + get() = code == 2 + + fun makeApproved(): VerificationStatus { + return APPROVED + } + + fun makePending(): VerificationStatus { + return PENDING + } +} + diff --git a/cakk-common/src/main/java/com/cakk/common/exception/CakkException.java b/cakk-common/src/main/java/com/cakk/common/exception/CakkException.java deleted file mode 100644 index 04a3fd14..00000000 --- a/cakk-common/src/main/java/com/cakk/common/exception/CakkException.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.cakk.common.exception; - -import lombok.Getter; - -import com.cakk.common.enums.ReturnCode; - -@Getter -public class CakkException extends RuntimeException { - - private final ReturnCode returnCode; - private final String code; - private final String message; - - public CakkException(ReturnCode returnCode) { - this.returnCode = returnCode; - this.code = returnCode.getCode(); - this.message = returnCode.getMessage(); - } -} diff --git a/cakk-common/src/main/java/com/cakk/common/exception/CakkException.kt b/cakk-common/src/main/java/com/cakk/common/exception/CakkException.kt new file mode 100644 index 00000000..bc548f35 --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/exception/CakkException.kt @@ -0,0 +1,17 @@ +package com.cakk.common.exception + +import com.cakk.common.enums.ReturnCode + +class CakkException( + private val returnCode: ReturnCode +) : RuntimeException() { + + private val code + get() = returnCode.code + override val message: String + get() = returnCode.message + + fun getReturnCode(): ReturnCode { + return returnCode + } +} diff --git a/cakk-common/src/main/java/com/cakk/common/response/ApiResponse.java b/cakk-common/src/main/java/com/cakk/common/response/ApiResponse.java index 9ddd2750..750b2cdf 100644 --- a/cakk-common/src/main/java/com/cakk/common/response/ApiResponse.java +++ b/cakk-common/src/main/java/com/cakk/common/response/ApiResponse.java @@ -1,10 +1,7 @@ package com.cakk.common.response; -import lombok.Getter; - import com.cakk.common.enums.ReturnCode; -@Getter public class ApiResponse { private String returnCode; @@ -60,4 +57,16 @@ public static ApiResponse error(ReturnCode returnCode, String errorMessa return response; } + + public String getReturnCode() { + return returnCode; + } + + public String getReturnMessage() { + return returnMessage; + } + + public T getData() { + return data; + } } diff --git a/cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.java b/cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.java deleted file mode 100644 index 8f182d9e..00000000 --- a/cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.cakk.common.utils; - -import java.util.Base64; - -public class DecodeUtils { - - private DecodeUtils() { - throw new IllegalStateException("util class"); - } - - public static byte[] decodeBase64(String string) { - return Base64.getUrlDecoder().decode(string); - } -} diff --git a/cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.kt b/cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.kt new file mode 100644 index 00000000..f2151475 --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/utils/DecodeUtils.kt @@ -0,0 +1,7 @@ +package com.cakk.common.utils + +import java.util.* + +fun decodeBase64(string: String): ByteArray { + return Base64.getUrlDecoder().decode(string) +} diff --git a/cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.java b/cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.java deleted file mode 100644 index badc056b..00000000 --- a/cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.cakk.common.utils; - -public class RandomUtils { - - private RandomUtils() { - throw new IllegalStateException("util class"); - } - - public static String generateRandomStringOnlyNumber(int length) { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < length; i++) { - sb.append((int) (Math.random() * 10)); - } - - return sb.toString(); - } -} diff --git a/cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.kt b/cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.kt new file mode 100644 index 00000000..88b6a11f --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/utils/RandomUtils.kt @@ -0,0 +1,11 @@ +package com.cakk.common.utils + +fun generateRandomStringOnlyNumber(length: Int): String { + val sb = StringBuilder() + + for (i in 0 until length) { + sb.append((Math.random() * 10).toInt()) + } + + return sb.toString() +} diff --git a/cakk-common/src/main/java/com/cakk/common/utils/SetUtils.java b/cakk-common/src/main/java/com/cakk/common/utils/SetUtils.java deleted file mode 100644 index 39ef36e0..00000000 --- a/cakk-common/src/main/java/com/cakk/common/utils/SetUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.cakk.common.utils; - -import java.util.Iterator; -import java.util.Set; - -public class SetUtils { - - private SetUtils() { - throw new IllegalStateException("util class"); - } - - public static void keepOnlyNElements(final Set set, final int max) { - if (set == null || set.size() <= max) { - return; - } - - final Iterator iterator = set.iterator(); - int count = 0; - - while (iterator.hasNext()) { - iterator.next(); - count++; - if (count > max) { - iterator.remove(); - } - } - } -} diff --git a/cakk-common/src/main/java/com/cakk/common/utils/SetUtils.kt b/cakk-common/src/main/java/com/cakk/common/utils/SetUtils.kt new file mode 100644 index 00000000..b7ed2fec --- /dev/null +++ b/cakk-common/src/main/java/com/cakk/common/utils/SetUtils.kt @@ -0,0 +1,18 @@ +package com.cakk.common.utils + +fun keepOnlyNElements(set: MutableSet?, max: Int) { + if (set == null || set.size <= max) { + return + } + + val iterator = set.iterator() + var count = 0 + + while (iterator.hasNext()) { + iterator.next() + count++ + if (count > max) { + iterator.remove() + } + } +} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/DayOfWeekConverter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/DayOfWeekConverter.java index 53a121ce..77da6a03 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/DayOfWeekConverter.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/DayOfWeekConverter.java @@ -26,7 +26,7 @@ public Days convertToEntityAttribute(final Integer code) { } return Stream.of(Days.values()) - .filter(days -> days.getCode().equals(code)) + .filter(days -> days.getCode() == code) .findFirst() .orElseThrow(() -> new CakkException(ReturnCode.INTERNAL_SERVER_ERROR)); } diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/VerificationStatusConverter.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/VerificationStatusConverter.java index e5796a8f..2b7ddfb4 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/VerificationStatusConverter.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/converter/VerificationStatusConverter.java @@ -15,7 +15,7 @@ public Integer convertToDatabaseColumn(VerificationStatus verificationStatus) { return null; } - return verificationStatus.getCode(); + return verificationStatus.code; } @Override @@ -25,9 +25,8 @@ public VerificationStatus convertToEntityAttribute(Integer code) { } return Stream.of(VerificationStatus.values()) - .filter(verificationStatus -> verificationStatus.getCode().equals(code)) + .filter(verificationStatus -> verificationStatus.code == code) .findFirst() .orElseThrow(() -> new CakkException(ReturnCode.INTERNAL_SERVER_ERROR)); } } -