From dfb27a2401837228f3b510a92d49d73b850c2cd4 Mon Sep 17 00:00:00 2001 From: Sanghoon Date: Tue, 14 May 2024 10:43:12 +0900 Subject: [PATCH] API - v1.0.1 --- Api/build.gradle | 2 + .../controller/ArchivingController.java | 12 +- .../service/DeleteArchivingUseCase.java | 2 - .../service/GetArchivingContentsUseCase.java | 21 +- .../service/UpdateArchivingPinUseCase.java | 2 - .../service/UpdateArchivingScrapUseCase.java | 2 - .../service/UpdateArchivingUseCase.java | 4 +- .../api/auth/controller/AuthController.java | 6 +- .../api/auth/service/WithdrawUserUseCase.java | 8 +- .../api/block/service/DeleteBlockUseCase.java | 2 - .../api/chore/controller/ChoreController.java | 2 +- .../server/api/common/aop/TestTimerAop.java | 35 ---- .../common/filter/MultiReadInputStream.java | 2 +- .../api/common/util/StringParamUtil.java | 14 -- .../server/api/common/util/StringUtil.java | 30 +++ .../api/config/GlobalExceptionHandler.java | 2 +- .../content/service/DeleteContentUseCase.java | 2 - .../content/service/UpdateContentUseCase.java | 4 +- .../service/ClearDeletedObjectUseCase.java | 4 +- .../service/ClearOldDeletedObjectUseCase.java | 2 +- .../service/RestoreDeletedObjectUseCase.java | 2 - .../service/GetRelativeSearchListUseCase.java | 6 +- .../service/RenewalSearchDataUseCase.java | 182 +++++++++++++++--- .../service/SearchArchivingUseCase.java | 4 +- .../api/tag/service/DeleteTagUseCase.java | 2 - .../api/tag/service/UpdateTagUseCase.java | 2 - .../user/service/UpdateUserInfoUseCase.java | 6 +- Core/build.gradle | 1 - .../server/core/consts/AllchiveConst.java | 13 +- .../server/core/error/GlobalErrorCode.java | 2 + .../TxTemplateExecutionFailException.java | 14 ++ Domain/build.gradle | 1 - .../distributedLock/DistributedLockAop.java | 2 +- .../async/CustomAsyncExceptionHandler.java | 4 +- .../config/async}/EnableAsyncConfig.java | 3 +- .../archiving/adaptor/ArchivingAdaptor.java | 5 + .../repository/ArchivingCustomRepository.java | 2 + .../ArchivingCustomRepositoryImpl.java | 18 ++ .../content/adaptor/ContentAdaptor.java | 7 +- .../adaptor/ContentTagGroupAdaptor.java | 4 + .../domains/content/adaptor/TagAdaptor.java | 6 + .../repository/ContentCustomRepository.java | 4 +- .../ContentCustomRepositoryImpl.java | 23 ++- .../ContentTagGroupCustomRepository.java | 2 + .../ContentTagGroupCustomRepositoryImpl.java | 15 ++ .../repository/TagCustomRepository.java | 4 + .../repository/TagCustomRepositoryImpl.java | 14 ++ .../quitReason/adaptor/QuitReasonAdaptor.java | 17 ++ .../domains/quitReason/domain/QuitReason.java | 35 ++++ .../quitReason/domain/enums/Reason.java | 29 +++ .../repository/QuitReasonRepository.java | 7 + .../service/QuitReasonDomainService.java | 17 ++ .../src/main/resources/application-domain.yml | 18 +- Infrastructure/build.gradle | 13 +- .../redis/lock/LockManagerImpl.java | 30 ++- .../s3/event}/S3ImageDeleteEvent.java | 2 +- .../s3/service/S3DeleteObjectService.java | 2 +- .../slack/SlackMessageGenerater.java | 4 +- .../slack/SlackSendMessageHandler.java | 4 +- .../slack/event}/SlackAsyncErrorEvent.java | 2 +- .../slack/event}/SlackErrorEvent.java | 2 +- README.md | 2 +- build.gradle | 4 +- config/nginx/default.conf | 4 - config/nginx/default.prod.conf | 4 - docker-compose.local.yml | 8 +- 66 files changed, 526 insertions(+), 184 deletions(-) delete mode 100644 Api/src/main/java/allchive/server/api/common/aop/TestTimerAop.java delete mode 100644 Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java create mode 100644 Api/src/main/java/allchive/server/api/common/util/StringUtil.java create mode 100644 Core/src/main/java/allchive/server/core/error/exception/TxTemplateExecutionFailException.java rename {Core/src/main/java/allchive/server/core => Domain/src/main/java/allchive/server/domain/config}/async/CustomAsyncExceptionHandler.java (87%) rename {Core/src/main/java/allchive/server/core/config => Domain/src/main/java/allchive/server/domain/config/async}/EnableAsyncConfig.java (95%) create mode 100644 Domain/src/main/java/allchive/server/domain/domains/quitReason/adaptor/QuitReasonAdaptor.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/QuitReason.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/enums/Reason.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/quitReason/repository/QuitReasonRepository.java create mode 100644 Domain/src/main/java/allchive/server/domain/domains/quitReason/service/QuitReasonDomainService.java rename {Core/src/main/java/allchive/server/core/event/events/s3 => Infrastructure/src/main/java/allchive/server/infrastructure/s3/event}/S3ImageDeleteEvent.java (88%) rename {Core/src/main/java/allchive/server/core/event/events/slack => Infrastructure/src/main/java/allchive/server/infrastructure/slack/event}/SlackAsyncErrorEvent.java (92%) rename {Core/src/main/java/allchive/server/core/event/events/slack => Infrastructure/src/main/java/allchive/server/infrastructure/slack/event}/SlackErrorEvent.java (92%) diff --git a/Api/build.gradle b/Api/build.gradle index a2abfcf6..9763a42f 100644 --- a/Api/build.gradle +++ b/Api/build.gradle @@ -11,6 +11,8 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-aop' // swagger implementation 'org.springdoc:springdoc-openapi-ui:1.6.12' diff --git a/Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java b/Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java index f70e31a8..188c311a 100644 --- a/Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java +++ b/Api/src/main/java/allchive/server/api/archiving/controller/ArchivingController.java @@ -11,8 +11,10 @@ import allchive.server.api.common.slice.SliceResponse; import allchive.server.api.config.security.SecurityUtil; import allchive.server.domain.domains.archiving.domain.enums.Category; +import allchive.server.domain.domains.content.domain.enums.ContentType; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springdoc.api.annotations.ParameterObject; @@ -67,7 +69,9 @@ public void deleteArchiving(@PathVariable("archivingId") Long archivingId) { @Operation( summary = "주제별 아카이빙 리스트를 가져옵니다.", - description = "sort parameter는 입력하지 말아주세요! sorting : 스크랩 여부 -> 스크랩 수 -> 생성일자") + description = + "sort parameter는 입력하지 말아주세요! sorting : 스크랩 여부 -> 스크랩 수 -> 생성일자" + + "\nsort에 popular쓰면 최신순 안쓰면 인기순 입니다!") @GetMapping() public SliceResponse getArchiving( @RequestParam("category") Category category, @@ -111,8 +115,10 @@ public ArchivingsResponse getPopularArchiving() { @GetMapping(value = "/{archivingId}/contents") public ArchivingContentsResponse getArchivingContents( @PathVariable("archivingId") Long archivingId, - @ParameterObject @PageableDefault(size = 10) Pageable pageable) { - return getArchivingContentsUseCase.execute(archivingId, pageable); + @ParameterObject @PageableDefault(size = 10) Pageable pageable, + @RequestParam(value = "type", required = false) ContentType contentType, + @RequestParam(value = "tagIds", required = false) List tagIds) { + return getArchivingContentsUseCase.execute(archivingId, pageable, contentType, tagIds); } @Operation(summary = "아카이빙을 스크랩합니다.", description = "스크랩 취소면 cancel에 true 값 보내주세요") diff --git a/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java index 2e5c631f..bace7d2c 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/DeleteArchivingUseCase.java @@ -11,7 +11,6 @@ import allchive.server.domain.domains.recycle.domain.Recycle; import allchive.server.domain.domains.recycle.service.RecycleDomainService; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -21,7 +20,6 @@ public class DeleteArchivingUseCase { private final RecycleMapper recycleMapper; private final RecycleDomainService recycleDomainService; - @Transactional @DistributedLock( lockType = DistributedLockType.ARCHIVING, identifier = {"archivingId"}) diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java index e340d442..220e39f3 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingContentsUseCase.java @@ -14,15 +14,18 @@ import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.enums.ContentType; import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; import java.util.List; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.transaction.annotation.Transactional; +@Slf4j @UseCase @RequiredArgsConstructor public class GetArchivingContentsUseCase { @@ -35,13 +38,14 @@ public class GetArchivingContentsUseCase { private final ScrapAdaptor scrapAdaptor; @Transactional(readOnly = true) - public ArchivingContentsResponse execute(Long archivingId, Pageable pageable) { + public ArchivingContentsResponse execute( + Long archivingId, Pageable pageable, ContentType contentType, List tagIds) { Long userId = SecurityUtil.getCurrentUserId(); validateExecution(archivingId, userId); Archiving archiving = archivingAdaptor.findById(archivingId); User owner = userAdaptor.findById(archiving.getUserId()); Slice contentResponseSlice = - getContentResponseSlice(archivingId, pageable); + getContentResponseSlice(archivingId, pageable, contentType, tagIds); return ArchivingContentsResponse.of( SliceResponse.of(contentResponseSlice), archiving, @@ -55,9 +59,18 @@ private void validateExecution(Long archivingId, Long userId) { archivingValidator.validateNotDeleteExceptUser(archivingId, userId); } - private Slice getContentResponseSlice(Long archivingId, Pageable pageable) { + private Slice getContentResponseSlice( + Long archivingId, Pageable pageable, ContentType contentType, List tagIds) { + List contentIds = null; + if (tagIds != null) { + contentIds = + contentTagGroupAdaptor.queryContentTagGroupByTagIdInWithContent(tagIds).stream() + .map(contentTagGroup -> contentTagGroup.getContent().getId()) + .toList(); + } Slice contentList = - contentAdaptor.querySliceContentByArchivingId(archivingId, pageable); + contentAdaptor.querySliceContentByArchivingIdAndContentTypeAndIdIn( + archivingId, pageable, contentType, contentIds); List contentTagGroupList = contentTagGroupAdaptor.queryContentTagGroupByContentIn(contentList.getContent()); return contentList.map( diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java index 9ee2df45..fc5f5a0b 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingPinUseCase.java @@ -7,7 +7,6 @@ import allchive.server.domain.domains.archiving.service.ArchivingDomainService; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -15,7 +14,6 @@ public class UpdateArchivingPinUseCase { private final ArchivingValidator archivingValidator; private final ArchivingDomainService archivingDomainService; - @Transactional @DistributedLock( lockType = DistributedLockType.ARCHIVING_PIN, identifier = {"archivingId", "userId"}) diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java index 59a9422e..57aa50c9 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingScrapUseCase.java @@ -14,7 +14,6 @@ import allchive.server.domain.domains.user.service.ScrapDomainService; import allchive.server.domain.domains.user.validator.ScrapValidator; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -25,7 +24,6 @@ public class UpdateArchivingScrapUseCase { private final ArchivingDomainService archivingDomainService; private final ScrapValidator scrapValidator; - @Transactional @DistributedLock( lockType = DistributedLockType.ARCHIVING_SCRAP, identifier = {"archivingId", "userId"}) diff --git a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java index 54f908c3..b1872823 100644 --- a/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/archiving/service/UpdateArchivingUseCase.java @@ -6,16 +6,15 @@ import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; import allchive.server.core.event.Event; -import allchive.server.core.event.events.s3.S3ImageDeleteEvent; import allchive.server.domain.common.aop.distributedLock.DistributedLock; import allchive.server.domain.common.enums.DistributedLockType; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.service.ArchivingDomainService; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.infrastructure.s3.event.S3ImageDeleteEvent; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -24,7 +23,6 @@ public class UpdateArchivingUseCase { private final ArchivingAdaptor archivingAdaptor; private final ArchivingValidator archivingValidator; - @Transactional @DistributedLock( lockType = DistributedLockType.ARCHIVING, identifier = {"archivingId"}) diff --git a/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java b/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java index 3874224d..5fe14ae9 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/AuthController.java @@ -6,6 +6,7 @@ import allchive.server.api.auth.service.TokenRefreshUseCase; import allchive.server.api.auth.service.WithdrawUserUseCase; import allchive.server.api.config.security.SecurityUtil; +import allchive.server.domain.domains.quitReason.domain.enums.Reason; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -26,9 +27,10 @@ public class AuthController { @DeleteMapping("/withdrawal") public void withDrawUser( @RequestParam(required = false, name = "appleCode", value = "") String appleCode, - @RequestHeader(value = "referer", required = false) String referer) { + @RequestHeader(value = "referer", required = false) String referer, + @RequestParam("quitReason") Reason reason) { Long userId = SecurityUtil.getCurrentUserId(); - withdrawUserUseCase.execute(appleCode, referer, userId); + withdrawUserUseCase.execute(appleCode, referer, userId, reason); } @Operation(summary = "회원탈퇴를 합니다. (개발용)", deprecated = true) diff --git a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java index ae6b4780..481137eb 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/WithdrawUserUseCase.java @@ -16,6 +16,9 @@ import allchive.server.domain.domains.content.service.ContentDomainService; import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; import allchive.server.domain.domains.content.service.TagDomainService; +import allchive.server.domain.domains.quitReason.domain.QuitReason; +import allchive.server.domain.domains.quitReason.domain.enums.Reason; +import allchive.server.domain.domains.quitReason.service.QuitReasonDomainService; import allchive.server.domain.domains.recycle.service.RecycleDomainService; import allchive.server.domain.domains.report.service.ReportDomainService; import allchive.server.domain.domains.search.service.LatestSearchDomainService; @@ -47,13 +50,14 @@ public class WithdrawUserUseCase { private final ReportDomainService reportDomainService; private final UserDomainService userDomainService; private final ArchivingDomainService archivingDomainService; + private final QuitReasonDomainService quitReasonDomainService; - @Transactional @DistributedLock( lockType = DistributedLockType.USER, identifier = {"userId"}) - public void execute(String appleAccessToken, String referer, Long userId) { + public void execute(String appleAccessToken, String referer, Long userId, Reason reason) { User user = userAdaptor.findById(userId); + quitReasonDomainService.save(QuitReason.of(userId, reason)); // oauth쪽 탈퇴 withdrawOauth(user.getOauthInfo().getProvider(), appleAccessToken, user, referer); // 우리쪽 탈퇴 diff --git a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java index 65a1579c..c69a4ce3 100644 --- a/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/DeleteBlockUseCase.java @@ -12,7 +12,6 @@ import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.validator.UserValidator; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -22,7 +21,6 @@ public class DeleteBlockUseCase { private final BlockDomainService blockDomainService; private final UserAdaptor userAdaptor; - @Transactional @DistributedLock( lockType = DistributedLockType.BLOCK, identifier = {"fromUserId", "toUserId"}) diff --git a/Api/src/main/java/allchive/server/api/chore/controller/ChoreController.java b/Api/src/main/java/allchive/server/api/chore/controller/ChoreController.java index 4608f0c7..c2b4a0c6 100644 --- a/Api/src/main/java/allchive/server/api/chore/controller/ChoreController.java +++ b/Api/src/main/java/allchive/server/api/chore/controller/ChoreController.java @@ -26,5 +26,5 @@ public void errorExample( @Operation(hidden = true) @GetMapping(value = "health") - public void errorExample() {} + public void healthCheck() {} } diff --git a/Api/src/main/java/allchive/server/api/common/aop/TestTimerAop.java b/Api/src/main/java/allchive/server/api/common/aop/TestTimerAop.java deleted file mode 100644 index 44a0667a..00000000 --- a/Api/src/main/java/allchive/server/api/common/aop/TestTimerAop.java +++ /dev/null @@ -1,35 +0,0 @@ -package allchive.server.api.common.aop; - - -import allchive.server.core.helper.SpringEnvironmentHelper; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.springframework.stereotype.Component; -import org.springframework.util.StopWatch; - -@Aspect -@Component -@Slf4j -@RequiredArgsConstructor -public class TestTimerAop { - private final SpringEnvironmentHelper springEnvironmentHelper; - - @Around("@annotation(org.springframework.transaction.annotation.Transactional)") - public Object handleEvent(ProceedingJoinPoint joinPoint) throws Throwable { - if (!springEnvironmentHelper.isProdAndDevProfile()) { - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - - Object result = joinPoint.proceed(); - - stopWatch.stop(); - log.info(String.valueOf(stopWatch.getLastTaskTimeMillis())); - return result; - } else { - return joinPoint.proceed(); - } - } -} diff --git a/Api/src/main/java/allchive/server/api/common/filter/MultiReadInputStream.java b/Api/src/main/java/allchive/server/api/common/filter/MultiReadInputStream.java index f75f1c7a..1f111244 100644 --- a/Api/src/main/java/allchive/server/api/common/filter/MultiReadInputStream.java +++ b/Api/src/main/java/allchive/server/api/common/filter/MultiReadInputStream.java @@ -1,12 +1,12 @@ package allchive.server.api.common.filter; -import com.amazonaws.util.IOUtils; import java.io.*; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.tomcat.util.http.fileupload.IOUtils; public class MultiReadInputStream extends HttpServletRequestWrapper { private ByteArrayOutputStream cachedBytes; diff --git a/Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java b/Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java deleted file mode 100644 index f4203056..00000000 --- a/Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.api.common.util; - - -import allchive.server.core.error.exception.EmptyParamValueException; -import org.springframework.stereotype.Component; - -@Component -public class StringParamUtil { - public static void checkEmptyString(String param) { - if (param.equals("")) { - throw EmptyParamValueException.EXCEPTION; - } - } -} diff --git a/Api/src/main/java/allchive/server/api/common/util/StringUtil.java b/Api/src/main/java/allchive/server/api/common/util/StringUtil.java new file mode 100644 index 00000000..d44056d2 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/common/util/StringUtil.java @@ -0,0 +1,30 @@ +package allchive.server.api.common.util; + + +import allchive.server.core.error.exception.EmptyParamValueException; +import org.springframework.stereotype.Component; + +import static allchive.server.core.consts.AllchiveConst.HANGUL_BASE; +import static allchive.server.core.consts.AllchiveConst.KOREAN_ALPHA; + +@Component +public class StringUtil { + public static void checkEmptyString(String param) { + if (param.equals("")) { + throw EmptyParamValueException.EXCEPTION; + } + } + + public static char extractKoreanInitial(char ch) { + int index = (ch - HANGUL_BASE) / (21 * 28); // 21개의 중성, 28개의 종성 + return KOREAN_ALPHA[index]; + } + + public static boolean isEnglish(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); + } + + public static boolean isKorean(char ch) { + return (ch >= '\uAC00' && ch <= '\uD7A3'); + } +} diff --git a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java index 9d4d500c..3d7f4aa2 100644 --- a/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java +++ b/Api/src/main/java/allchive/server/api/config/GlobalExceptionHandler.java @@ -8,7 +8,7 @@ import allchive.server.core.error.ErrorResponse; import allchive.server.core.error.GlobalErrorCode; import allchive.server.core.event.Event; -import allchive.server.core.event.events.slack.SlackErrorEvent; +import allchive.server.infrastructure.slack.event.SlackErrorEvent; import java.util.List; import java.util.Map; import java.util.stream.Collectors; diff --git a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java index 0675ccf5..203bb64d 100644 --- a/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/DeleteContentUseCase.java @@ -16,7 +16,6 @@ import allchive.server.domain.domains.recycle.domain.enums.RecycleType; import allchive.server.domain.domains.recycle.service.RecycleDomainService; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -28,7 +27,6 @@ public class DeleteContentUseCase { private final ContentAdaptor contentAdaptor; private final ArchivingAsyncDomainService archivingAsyncDomainService; - @Transactional @DistributedLock( lockType = DistributedLockType.CONTENT, identifier = {"contentId"}) diff --git a/Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java index 1637ddf5..2406e2fb 100644 --- a/Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/UpdateContentUseCase.java @@ -9,7 +9,6 @@ import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; import allchive.server.core.event.Event; -import allchive.server.core.event.events.s3.S3ImageDeleteEvent; import allchive.server.domain.common.aop.distributedLock.DistributedLock; import allchive.server.domain.common.enums.DistributedLockType; import allchive.server.domain.domains.archiving.service.ArchivingAsyncDomainService; @@ -25,9 +24,9 @@ import allchive.server.domain.domains.content.service.TagAsyncDomainService; import allchive.server.domain.domains.content.validator.ContentValidator; import allchive.server.domain.domains.content.validator.TagValidator; +import allchive.server.infrastructure.s3.event.S3ImageDeleteEvent; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -43,7 +42,6 @@ public class UpdateContentUseCase { private final ContentTagGroupAdaptor contentTagGroupAdaptor; private final ArchivingAsyncDomainService archivingAsyncDomainService; - @Transactional @DistributedLock( lockType = DistributedLockType.CONTENT, identifier = {"contentId"}) diff --git a/Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java index 96baf050..d0eab74a 100644 --- a/Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java +++ b/Api/src/main/java/allchive/server/api/recycle/service/ClearDeletedObjectUseCase.java @@ -4,7 +4,6 @@ import allchive.server.api.recycle.model.dto.request.ClearDeletedObjectRequest; import allchive.server.core.annotation.UseCase; import allchive.server.core.event.Event; -import allchive.server.core.event.events.s3.S3ImageDeleteEvent; import allchive.server.domain.common.aop.distributedLock.DistributedLock; import allchive.server.domain.common.enums.DistributedLockType; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; @@ -21,12 +20,12 @@ import allchive.server.domain.domains.recycle.validator.RecycleValidator; import allchive.server.domain.domains.report.service.ReportDomainService; import allchive.server.domain.domains.user.service.ScrapDomainService; +import allchive.server.infrastructure.s3.event.S3ImageDeleteEvent; import allchive.server.infrastructure.s3.service.S3DeleteObjectService; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -44,7 +43,6 @@ public class ClearDeletedObjectUseCase { private final S3DeleteObjectService s3DeleteObjectService; private final ArchivingAdaptor archivingAdaptor; - @Transactional @DistributedLock( lockType = DistributedLockType.RECYCLE, identifier = {"userId"}) diff --git a/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java index e8eca4d2..8101e117 100644 --- a/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java +++ b/Api/src/main/java/allchive/server/api/recycle/service/ClearOldDeletedObjectUseCase.java @@ -3,7 +3,6 @@ import allchive.server.core.annotation.UseCase; import allchive.server.core.event.Event; -import allchive.server.core.event.events.s3.S3ImageDeleteEvent; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.service.ArchivingDomainService; @@ -18,6 +17,7 @@ import allchive.server.domain.domains.recycle.service.RecycleDomainService; import allchive.server.domain.domains.report.service.ReportDomainService; import allchive.server.domain.domains.user.service.ScrapDomainService; +import allchive.server.infrastructure.s3.event.S3ImageDeleteEvent; import allchive.server.infrastructure.s3.service.S3DeleteObjectService; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java index bbe1f22b..6af8af3e 100644 --- a/Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java +++ b/Api/src/main/java/allchive/server/api/recycle/service/RestoreDeletedObjectUseCase.java @@ -15,7 +15,6 @@ import allchive.server.domain.domains.recycle.validator.RecycleValidator; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -28,7 +27,6 @@ public class RestoreDeletedObjectUseCase { private final RecycleDomainService recycleDomainService; private final ContentAdaptor contentAdaptor; - @Transactional @DistributedLock( lockType = DistributedLockType.RECYCLE, identifier = {"userId"}) diff --git a/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java b/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java index f148d8b5..5ab717e9 100644 --- a/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/GetRelativeSearchListUseCase.java @@ -1,9 +1,9 @@ package allchive.server.api.search.service; +import static allchive.server.core.consts.AllchiveConst.ASTERISK; import static allchive.server.core.consts.AllchiveConst.SEARCH_KEY; -import static jodd.util.StringPool.ASTERISK; -import allchive.server.api.common.util.StringParamUtil; +import allchive.server.api.common.util.StringUtil; import allchive.server.api.search.model.dto.response.SearchListResponse; import allchive.server.core.annotation.UseCase; import java.util.ArrayList; @@ -33,7 +33,7 @@ public SearchListResponse execute(String word) { } private void validateExecution(String word) { - StringParamUtil.checkEmptyString(word); + StringUtil.checkEmptyString(word); } private List getAutoCompleteList(Set rangeList, String keyword) { diff --git a/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java b/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java index 64dc256e..9a327d39 100644 --- a/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java @@ -1,17 +1,21 @@ package allchive.server.api.search.service; -import static allchive.server.core.consts.AllchiveConst.ASTERISK; -import static allchive.server.core.consts.AllchiveConst.SEARCH_KEY; +import static allchive.server.core.consts.AllchiveConst.*; +import allchive.server.api.common.util.StringUtil; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; -import java.util.HashSet; -import java.util.Set; + +import java.util.*; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.transaction.annotation.Transactional; @@ -38,40 +42,156 @@ public void executeForce() { } private void renewalData() { - redisTemplate.delete(SEARCH_KEY); + long startTime = System.currentTimeMillis(); + delete(); renewalArchiving(); renewalTag(); + long stopTime = System.currentTimeMillis(); + log.info("{}", stopTime - startTime); } - private void renewalTag() { - Set tags = new HashSet<>(tagAdaptor.findAll()); - tags.forEach( - tag -> { - redisTemplate.opsForZSet().add(SEARCH_KEY, tag.getName().trim() + ASTERISK, 0); - for (int index = 0; index <= tag.getName().length(); index++) { - redisTemplate - .opsForZSet() - .add(SEARCH_KEY, tag.getName().trim().substring(0, index), 0); + private void delete() { + redisTemplate.executePipelined((RedisCallback) + redisConnection -> { + for (char c = 'A'; c <= 'Z'; c++) { + String key = SEARCH_KEY + c; + redisConnection.del(key.getBytes()); + } + + for (int index = 0; index < 19; index++) { + String key = SEARCH_KEY + KOREAN_ALPHA[index]; + redisConnection.del(key.getBytes()); + } + + for (int index = 0; index < 9; index++) { + String key = SEARCH_KEY + index; + redisConnection.del(key.getBytes()); } + + redisConnection.del((SEARCH_KEY + '?').getBytes()); + return null; }); } + private void renewalTag() { + int pageNum = 0; + while (true) { + PageRequest pageRequest = PageRequest.of(pageNum, BULK_SIZE); + Slice slicedTags = tagAdaptor.querySliceTag(pageRequest); + Set tags = new HashSet<>(slicedTags.getContent()); + redisTemplate.executePipelined( + (RedisCallback) + redisConnection -> { + tags.forEach( + tag -> { + String key; + if (StringUtil.isKorean( + tag.getName().trim().charAt(0))) { + key = + SEARCH_KEY + + StringUtil.extractKoreanInitial( + tag.getName() + .trim() + .charAt(0)); + } else { + key = + SEARCH_KEY + + tag.getName() + .trim() + .toUpperCase() + .charAt(0); + } + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + (tag.getName().trim() + ASTERISK) + .getBytes()); + for (int index = 0; + index <= tag.getName().length(); + index++) { + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + tag.getName() + .trim() + .substring(0, index) + .getBytes()); + } + }); + return null; + }); + + if (!slicedTags.hasNext()) { + break; + } + pageNum++; + } + } + private void renewalArchiving() { - Set archivings = - new HashSet<>(archivingAdaptor.findAllByPublicStatus(Boolean.TRUE)); - archivings.forEach( - archiving -> { - redisTemplate - .opsForZSet() - .add(SEARCH_KEY, archiving.getTitle().trim() + ASTERISK, 0); - for (int index = 0; index <= archiving.getTitle().length(); index++) { - redisTemplate - .opsForZSet() - .add( - SEARCH_KEY, - archiving.getTitle().trim().substring(0, index), - 0); - } - }); + int pageNum = 0; + while (true) { + PageRequest pageRequest = PageRequest.of(pageNum, BULK_SIZE); + Slice slicedArchivings = + archivingAdaptor.querySliceArchivingByPublicStatus(pageRequest, true); + Set archivings = new HashSet<>(slicedArchivings.getContent()); + redisTemplate.executePipelined( + (RedisCallback) + redisConnection -> { + archivings.forEach( + archiving -> { + String key; + if (StringUtil.isKorean( + archiving.getTitle().trim().charAt(0))) { + key = + SEARCH_KEY + + StringUtil.extractKoreanInitial( + archiving + .getTitle() + .trim() + .charAt(0)); + } else { + key = + SEARCH_KEY + + archiving + .getTitle() + .trim() + .toUpperCase() + .charAt(0); + } + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + (archiving.getTitle().trim() + ASTERISK) + .getBytes()); + for (int index = 0; + index <= archiving.getTitle().length(); + index++) { + redisConnection + .zSetCommands() + .zAdd( + key.getBytes(), + 0, + archiving + .getTitle() + .trim() + .substring(0, index) + .getBytes()); + } + }); + return null; + }); + + if (!slicedArchivings.hasNext()) { + break; + } + pageNum++; + } } -} +} \ No newline at end of file diff --git a/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java b/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java index 168ca036..c78320e9 100644 --- a/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/SearchArchivingUseCase.java @@ -3,7 +3,7 @@ import allchive.server.api.archiving.model.dto.response.ArchivingResponse; import allchive.server.api.common.page.PageResponse; -import allchive.server.api.common.util.StringParamUtil; +import allchive.server.api.common.util.StringUtil; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.search.model.dto.response.SearchResponse; import allchive.server.api.search.model.enums.ArchivingType; @@ -65,7 +65,7 @@ public SearchResponse execute(Pageable pageable, ArchivingType type, String word } private void validateExecution(String word) { - StringParamUtil.checkEmptyString(word); + StringUtil.checkEmptyString(word); } private Set getTagArchivingIds(Long userId, String word) { diff --git a/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java b/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java index 78500ed7..7e63f850 100644 --- a/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java +++ b/Api/src/main/java/allchive/server/api/tag/service/DeleteTagUseCase.java @@ -11,7 +11,6 @@ import allchive.server.domain.domains.content.service.TagDomainService; import allchive.server.domain.domains.content.validator.TagValidator; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -21,7 +20,6 @@ public class DeleteTagUseCase { private final ContentTagGroupDomainService contentTagGroupDomainService; private final TagDomainService tagDomainService; - @Transactional @DistributedLock( lockType = DistributedLockType.TAG, identifier = {"tagId"}) diff --git a/Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java b/Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java index 853ca5bc..a2aacda6 100644 --- a/Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java +++ b/Api/src/main/java/allchive/server/api/tag/service/UpdateTagUseCase.java @@ -9,7 +9,6 @@ import allchive.server.domain.domains.content.service.TagDomainService; import allchive.server.domain.domains.content.validator.TagValidator; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -17,7 +16,6 @@ public class UpdateTagUseCase { private final TagValidator tagValidator; private final TagDomainService tagDomainService; - @Transactional @DistributedLock( lockType = DistributedLockType.TAG, identifier = {"tagId"}) diff --git a/Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java b/Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java index 2a536f0a..ef32c64d 100644 --- a/Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/UpdateUserInfoUseCase.java @@ -5,19 +5,20 @@ import allchive.server.api.user.model.dto.request.UpdateUserInfoRequest; import allchive.server.core.annotation.UseCase; import allchive.server.core.event.Event; -import allchive.server.core.event.events.s3.S3ImageDeleteEvent; import allchive.server.domain.common.aop.distributedLock.DistributedLock; import allchive.server.domain.common.enums.DistributedLockType; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.service.UserDomainService; import allchive.server.domain.domains.user.validator.UserValidator; +import allchive.server.infrastructure.s3.event.S3ImageDeleteEvent; import allchive.server.infrastructure.s3.service.S3DeleteObjectService; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; +import lombok.extern.slf4j.Slf4j; @UseCase +@Slf4j @RequiredArgsConstructor public class UpdateUserInfoUseCase { private final UserDomainService userDomainService; @@ -25,7 +26,6 @@ public class UpdateUserInfoUseCase { private final UserAdaptor userAdaptor; private final S3DeleteObjectService s3DeleteObjectService; - @Transactional @DistributedLock( lockType = DistributedLockType.USER, identifier = {"userId"}) diff --git a/Core/build.gradle b/Core/build.gradle index d633c34e..f67e5636 100644 --- a/Core/build.gradle +++ b/Core/build.gradle @@ -5,7 +5,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' - api 'org.springframework.boot:spring-boot-starter-web' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' diff --git a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java index ee91f75a..55ce8ab3 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -1,5 +1,7 @@ package allchive.server.core.consts; +import java.util.List; + public class AllchiveConst { public static final String AUTH_HEADER = "Authorization"; public static final String BEARER = "Bearer "; @@ -35,11 +37,18 @@ public class AllchiveConst { "https://all-chive-bucket.s3.ap-northeast-2.amazonaws.com/"; public static final String[] SwaggerPatterns = { - "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", + "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v3/api-docs", }; - public static final String SEARCH_KEY = "ARCHIVING_TITLE"; + public static final String SEARCH_KEY = "SEARCH_KEY_"; public static final String ASTERISK = "*"; + public static final int BULK_SIZE = 100; + public static final char[] KOREAN_ALPHA = { + 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', + 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' + }; + // 한글 유니코드에서 '가'의 코드 포인트 + public static final int HANGUL_BASE = 0xAC00; public static final int PLUS_ONE = 1; public static final int MINUS_ONE = -1; diff --git a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java index 69364ba0..268e3295 100644 --- a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java +++ b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java @@ -22,6 +22,8 @@ public enum GlobalErrorCode implements BaseErrorCode { INVALID_LOCK_IDENTIFIER(INTERNAL_SERVER, "GLOBAL_500_4", "잘못된 lock identifier 입니다."), ALREADY_REDISSON_UNLOCK(INTERNAL_SERVER, "GLOBAL_500_5", "Redisson Lock Already UnLock"), INTERRUPTED_REDISSON(INTERNAL_SERVER, "GLOBAL_500_6", "Redisson interruption"), + FAIL_TO_TRANSACTION_TEMPLATE_EXECUTE_ERROR( + BAD_REQUEST, "GLOBAL_500_7", "fail to tx-templates execute error"), /** 토큰 에러 * */ INVALID_TOKEN(UNAUTHORIZED, "AUTH_401_2", "올바르지 않은 토큰입니다."), diff --git a/Core/src/main/java/allchive/server/core/error/exception/TxTemplateExecutionFailException.java b/Core/src/main/java/allchive/server/core/error/exception/TxTemplateExecutionFailException.java new file mode 100644 index 00000000..838cdb4e --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/TxTemplateExecutionFailException.java @@ -0,0 +1,14 @@ +package allchive.server.core.error.exception; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.core.error.GlobalErrorCode; + +public class TxTemplateExecutionFailException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new TxTemplateExecutionFailException(); + + private TxTemplateExecutionFailException() { + super(GlobalErrorCode.FAIL_TO_TRANSACTION_TEMPLATE_EXECUTE_ERROR); + } +} diff --git a/Domain/build.gradle b/Domain/build.gradle index a23fa539..81b129c8 100644 --- a/Domain/build.gradle +++ b/Domain/build.gradle @@ -6,7 +6,6 @@ dependencies { implementation 'mysql:mysql-connector-java:8.0.33' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' - runtimeOnly 'com.h2database:h2' implementation project(':Core') implementation project(':Infrastructure') diff --git a/Domain/src/main/java/allchive/server/domain/common/aop/distributedLock/DistributedLockAop.java b/Domain/src/main/java/allchive/server/domain/common/aop/distributedLock/DistributedLockAop.java index 318294bd..a4f94918 100644 --- a/Domain/src/main/java/allchive/server/domain/common/aop/distributedLock/DistributedLockAop.java +++ b/Domain/src/main/java/allchive/server/domain/common/aop/distributedLock/DistributedLockAop.java @@ -19,7 +19,7 @@ @Aspect @Component -@Order(Ordered.HIGHEST_PRECEDENCE + 1) +@Order(Ordered.HIGHEST_PRECEDENCE) @RequiredArgsConstructor public class DistributedLockAop { private final LockManager lockManager; diff --git a/Core/src/main/java/allchive/server/core/async/CustomAsyncExceptionHandler.java b/Domain/src/main/java/allchive/server/domain/config/async/CustomAsyncExceptionHandler.java similarity index 87% rename from Core/src/main/java/allchive/server/core/async/CustomAsyncExceptionHandler.java rename to Domain/src/main/java/allchive/server/domain/config/async/CustomAsyncExceptionHandler.java index c737426c..267c432b 100644 --- a/Core/src/main/java/allchive/server/core/async/CustomAsyncExceptionHandler.java +++ b/Domain/src/main/java/allchive/server/domain/config/async/CustomAsyncExceptionHandler.java @@ -1,8 +1,8 @@ -package allchive.server.core.async; +package allchive.server.domain.config.async; import allchive.server.core.event.Event; -import allchive.server.core.event.events.slack.SlackAsyncErrorEvent; +import allchive.server.infrastructure.slack.event.SlackAsyncErrorEvent; import java.lang.reflect.Method; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/Core/src/main/java/allchive/server/core/config/EnableAsyncConfig.java b/Domain/src/main/java/allchive/server/domain/config/async/EnableAsyncConfig.java similarity index 95% rename from Core/src/main/java/allchive/server/core/config/EnableAsyncConfig.java rename to Domain/src/main/java/allchive/server/domain/config/async/EnableAsyncConfig.java index 4d6f773b..d0f80b21 100644 --- a/Core/src/main/java/allchive/server/core/config/EnableAsyncConfig.java +++ b/Domain/src/main/java/allchive/server/domain/config/async/EnableAsyncConfig.java @@ -1,8 +1,7 @@ -package allchive.server.core.config; +package allchive.server.domain.config.async; import static allchive.server.core.consts.AllchiveConst.*; -import allchive.server.core.async.CustomAsyncExceptionHandler; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import lombok.RequiredArgsConstructor; diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java index 53705302..cf417d78 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/adaptor/ArchivingAdaptor.java @@ -104,4 +104,9 @@ public List queryArchivingOrderByScrapCntLimit5ExceptBlockList( List blockList) { return archivingRepository.queryArchivingOrderByScrapCntLimit5ExceptBlockList(blockList); } + + public Slice querySliceArchivingByPublicStatus( + Pageable pageable, Boolean publicStatus) { + return archivingRepository.querySliceArchivingByPublicStatus(pageable, publicStatus); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java index 68e33120..40712e01 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepository.java @@ -34,4 +34,6 @@ Page querySliceArchivingByKeywordExceptBlockOrderByTagArchvingIds( Long userId); List queryArchivingOrderByScrapCntLimit5ExceptBlockList(List blockList); + + Slice querySliceArchivingByPublicStatus(Pageable pageable, Boolean publicStatus); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java index 58a1bb47..1e8c6beb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/repository/ArchivingCustomRepositoryImpl.java @@ -160,6 +160,20 @@ public List queryArchivingOrderByScrapCntLimit5ExceptBlockList( .fetch(); } + @Override + public Slice querySliceArchivingByPublicStatus( + Pageable pageable, Boolean publicStatus) { + List archivings = + queryFactory + .select(archiving) + .from(archiving) + .where(publicStatusEq(publicStatus), deleteStatusFalse()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + PLUS_ONE) + .fetch(); + return SliceUtil.toSlice(archivings, pageable); + } + private BooleanExpression userIdNotIn(List blockList) { return archiving.userId.notIn(blockList); } @@ -168,6 +182,10 @@ private BooleanExpression publicStatusTrue() { return archiving.publicStatus.eq(Boolean.TRUE); } + private BooleanExpression publicStatusEq(Boolean publicStatus) { + return archiving.publicStatus.eq(publicStatus); + } + private BooleanExpression categoryEq(Category category) { if (category.equals(Category.ALL)) { return null; diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java index b14cfdd7..0fd8b399 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentAdaptor.java @@ -3,6 +3,7 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; import allchive.server.domain.domains.content.exception.exceptions.ContentNotFoundException; import allchive.server.domain.domains.content.repository.ContentRepository; import java.util.List; @@ -15,8 +16,10 @@ public class ContentAdaptor { private final ContentRepository contentRepository; - public Slice querySliceContentByArchivingId(Long archivingId, Pageable pageable) { - return contentRepository.querySliceContentByArchivingId(archivingId, pageable); + public Slice querySliceContentByArchivingIdAndContentTypeAndIdIn( + Long archivingId, Pageable pageable, ContentType contentType, List contentIds) { + return contentRepository.querySliceContentByArchivingIdAndContentTypeAndIdIn( + archivingId, pageable, contentType, contentIds); } public void save(Content content) { diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java index cab208d8..732b7c1f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/ContentTagGroupAdaptor.java @@ -49,4 +49,8 @@ public List queryContentTagGroupByTagInWithContent(List ta public void deleteAll(List contentTagGroups) { contentTagGroupRepository.deleteAll(contentTagGroups); } + + public List queryContentTagGroupByTagIdInWithContent(List tagIds) { + return contentTagGroupRepository.queryContentTagGroupByTagIdInWithContent(tagIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java index 0b65c5be..d8270235 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/adaptor/TagAdaptor.java @@ -7,6 +7,8 @@ import allchive.server.domain.domains.content.repository.TagRepository; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; @Adaptor @RequiredArgsConstructor @@ -52,4 +54,8 @@ public List queryTagByUserIdContainName(Long userId, String word) { public List findAll() { return tagRepository.findAll(); } + + public Slice querySliceTag(Pageable pageable) { + return tagRepository.querySliceTag(pageable); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java index 34e2fc7a..b5ad9502 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepository.java @@ -2,12 +2,14 @@ import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; public interface ContentCustomRepository { - Slice querySliceContentByArchivingId(Long archivingId, Pageable pageable); + Slice querySliceContentByArchivingIdAndContentTypeAndIdIn( + Long archivingId, Pageable pageable, ContentType contentType, List contentIds); List queryContentInArchivingIds(List archivingIds); diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java index 90edcb37..b12e4abb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentCustomRepositoryImpl.java @@ -4,6 +4,7 @@ import allchive.server.domain.common.util.SliceUtil; import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -18,11 +19,16 @@ public class ContentCustomRepositoryImpl implements ContentCustomRepository { private final JPAQueryFactory queryFactory; @Override - public Slice querySliceContentByArchivingId(Long archivingId, Pageable pageable) { + public Slice querySliceContentByArchivingIdAndContentTypeAndIdIn( + Long archivingId, Pageable pageable, ContentType contentType, List contentIds) { List archivings = queryFactory .selectFrom(content) - .where(archivingIdEq(archivingId), deleteStatusFalse()) + .where( + archivingIdEq(archivingId), + deleteStatusFalse(), + contentTypeEq(contentType), + idIn(contentIds)) .orderBy(createdAtDesc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize() + 1) @@ -50,6 +56,19 @@ private BooleanExpression deleteStatusFalse() { return content.deleteStatus.eq(Boolean.FALSE); } + private BooleanExpression contentTypeEq(ContentType contentType) { + if (contentType == null) { + return null; + } else { + return content.contentType.eq(contentType); + } + } + + private BooleanExpression idIn(List contentIds) { + if (contentIds == null) return null; + return content.id.in(contentIds); + } + private BooleanExpression archivingIdEq(Long archivingId) { return content.archivingId.eq(archivingId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java index 05105f26..2d975b1a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepository.java @@ -12,4 +12,6 @@ public interface ContentTagGroupCustomRepository { List queryContentTagGroupByContentWithTag(Content content); List queryContentTagGroupByTagInWithContent(List tags); + + List queryContentTagGroupByTagIdInWithContent(List tagIds); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java index 055f32ce..96169616 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/ContentTagGroupCustomRepositoryImpl.java @@ -51,6 +51,16 @@ public List queryContentTagGroupByTagInWithContent(List ta .fetch(); } + @Override + public List queryContentTagGroupByTagIdInWithContent(List tagIds) { + return queryFactory + .selectFrom(contentTagGroup) + .join(contentTagGroup.content, content) + .fetchJoin() + .where(tagIdIn(tagIds)) + .fetch(); + } + private BooleanExpression contentIdIn(List contentList) { return contentTagGroup.content.in(contentList); } @@ -63,6 +73,11 @@ private BooleanExpression tagIn(List tagList) { return contentTagGroup.tag.in(tagList); } + private BooleanExpression tagIdIn(List tagIds) { + if (tagIds == null) return null; + return contentTagGroup.tag.id.in(tagIds); + } + private OrderSpecifier createdAtDesc() { return contentTagGroup.createdAt.desc(); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java index 8975dcee..f46b53c3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepository.java @@ -3,6 +3,8 @@ import allchive.server.domain.domains.content.domain.Tag; import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; public interface TagCustomRepository { List queryTagByUserIdOrderByUsedAt(Long userId); @@ -10,4 +12,6 @@ public interface TagCustomRepository { List queryTagByTagIdIn(List tagIds); List queryTagByUserIdContainName(Long userId, String name); + + Slice querySliceTag(Pageable pageable); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java index a8f5b9ad..37aff9d7 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/repository/TagCustomRepositoryImpl.java @@ -2,6 +2,7 @@ import static allchive.server.domain.domains.content.domain.QTag.tag; +import allchive.server.domain.common.util.SliceUtil; import allchive.server.domain.domains.content.domain.Tag; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; @@ -9,6 +10,8 @@ import java.time.LocalDateTime; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; @RequiredArgsConstructor public class TagCustomRepositoryImpl implements TagCustomRepository { @@ -33,6 +36,17 @@ public List queryTagByUserIdContainName(Long userId, String name) { return queryFactory.selectFrom(tag).where(userIdEq(userId), titleContain(name)).fetch(); } + @Override + public Slice querySliceTag(Pageable pageable) { + List tags = + queryFactory + .selectFrom(tag) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) + .fetch(); + return SliceUtil.toSlice(tags, pageable); + } + private BooleanExpression tagUserIdEq(Long userId) { return tag.userId.eq(userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/quitReason/adaptor/QuitReasonAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/quitReason/adaptor/QuitReasonAdaptor.java new file mode 100644 index 00000000..7efe3027 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/quitReason/adaptor/QuitReasonAdaptor.java @@ -0,0 +1,17 @@ +package allchive.server.domain.domains.quitReason.adaptor; + + +import allchive.server.core.annotation.Adaptor; +import allchive.server.domain.domains.quitReason.domain.QuitReason; +import allchive.server.domain.domains.quitReason.repository.QuitReasonRepository; +import lombok.RequiredArgsConstructor; + +@Adaptor +@RequiredArgsConstructor +public class QuitReasonAdaptor { + private final QuitReasonRepository quitReasonRepository; + + public void save(QuitReason quitReason) { + quitReasonRepository.save(quitReason); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/QuitReason.java b/Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/QuitReason.java new file mode 100644 index 00000000..5a88caa4 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/QuitReason.java @@ -0,0 +1,35 @@ +package allchive.server.domain.domains.quitReason.domain; + + +import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.quitReason.domain.enums.Reason; +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Table(name = "tbl_quit_reason") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class QuitReason extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull private Long userId; + + private Reason reason; + + @Builder + private QuitReason(Long userId, Reason reason) { + this.userId = userId; + this.reason = reason; + } + + public static QuitReason of(Long userId, Reason reason) { + return QuitReason.builder().userId(userId).reason(reason).build(); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/enums/Reason.java b/Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/enums/Reason.java new file mode 100644 index 00000000..86281c33 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/quitReason/domain/enums/Reason.java @@ -0,0 +1,29 @@ +package allchive.server.domain.domains.quitReason.domain.enums; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.stream.Stream; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Reason { + NOT_USED("NOT_USED"), // 자주 사용하지 않아서 + UNCONFORTABLE("UNCONFORTABLE"), // 이용이 불편하고 장애가 많아서 + USE_OTHER_SERVICE("USE_OTHER_SERVICE"), // 다른 대체 서비스를 이용해서 + EXPENSIVE("EXPENSIVE"), // 서비스 이용료가 비싸서 + NEW_ACCOUNT("NEW_ACCOUNT"), // 새 계정을 만들고싶어서 + ETC("ETC"); + + @JsonValue private String value; + + @JsonCreator + public static Reason parsing(String inputValue) { + return Stream.of(Reason.values()) + .filter(type -> type.getValue().equals(inputValue)) + .findFirst() + .orElse(null); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/quitReason/repository/QuitReasonRepository.java b/Domain/src/main/java/allchive/server/domain/domains/quitReason/repository/QuitReasonRepository.java new file mode 100644 index 00000000..962ab784 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/quitReason/repository/QuitReasonRepository.java @@ -0,0 +1,7 @@ +package allchive.server.domain.domains.quitReason.repository; + + +import allchive.server.domain.domains.quitReason.domain.QuitReason; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface QuitReasonRepository extends JpaRepository {} diff --git a/Domain/src/main/java/allchive/server/domain/domains/quitReason/service/QuitReasonDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/quitReason/service/QuitReasonDomainService.java new file mode 100644 index 00000000..f93c5937 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/quitReason/service/QuitReasonDomainService.java @@ -0,0 +1,17 @@ +package allchive.server.domain.domains.quitReason.service; + + +import allchive.server.core.annotation.DomainService; +import allchive.server.domain.domains.quitReason.adaptor.QuitReasonAdaptor; +import allchive.server.domain.domains.quitReason.domain.QuitReason; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class QuitReasonDomainService { + private final QuitReasonAdaptor quitReasonAdaptor; + + public void save(QuitReason quitReason) { + quitReasonAdaptor.save(quitReason); + } +} diff --git a/Domain/src/main/resources/application-domain.yml b/Domain/src/main/resources/application-domain.yml index 4f1b7ffe..0727ce71 100644 --- a/Domain/src/main/resources/application-domain.yml +++ b/Domain/src/main/resources/application-domain.yml @@ -7,10 +7,10 @@ spring: activate: on-profile: local datasource: - url: jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&tinyInt1isBit=false + url: jdbc:mysql://localhost:3307/test?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&tinyInt1isBit=false driver-class-name: com.mysql.cj.jdbc.Driver username: root - password: test + password: root hikari: maxLifetime: 580000 maximum-pool-size: 20 @@ -21,7 +21,7 @@ spring: format_sql: true default_batch_fetch_size: 100 hibernate: - ddl-auto: update + ddl-auto: none defer-datasource-initialization: true open-in-view: false database-platform: org.hibernate.dialect.MySQL5InnoDBDialect @@ -29,8 +29,8 @@ spring: logging: level: - com.zaxxer.hikari.HikariConfig: DEBUG - com.zaxxer.hikari: TRACE +# com.zaxxer.hikari.HikariConfig: DEBUG +# com.zaxxer.hikari: TRACE org.springframework.orm.jpa: DEBUG org.springframework.transaction: DEBUG # org.hibernate.SQL: debug @@ -56,7 +56,7 @@ spring: format_sql: ${FORMAT_SQL:true} default_batch_fetch_size: ${JPA_BATCH_FETCH_SIZE:100} hibernate: - ddl-auto: update + ddl-auto: none defer-datasource-initialization: true open-in-view: false database-platform: org.hibernate.dialect.MySQL5InnoDBDialect @@ -64,8 +64,8 @@ spring: logging: level: - com.zaxxer.hikari.HikariConfig: DEBUG - com.zaxxer.hikari: TRACE +# com.zaxxer.hikari.HikariConfig: DEBUG +# com.zaxxer.hikari: TRACE org.springframework.orm.jpa: DEBUG org.springframework.transaction: DEBUG org.hibernate.SQL: debug @@ -91,7 +91,7 @@ spring: format_sql: ${FORMAT_SQL:true} default_batch_fetch_size: ${JPA_BATCH_FETCH_SIZE:100} hibernate: - ddl-auto: update + ddl-auto: none defer-datasource-initialization: true open-in-view: false database-platform: org.hibernate.dialect.MySQL5InnoDBDialect diff --git a/Infrastructure/build.gradle b/Infrastructure/build.gradle index dc4f5d97..29ec7597 100644 --- a/Infrastructure/build.gradle +++ b/Infrastructure/build.gradle @@ -3,12 +3,13 @@ jar { enabled = true } dependencies { api 'org.springframework.boot:spring-boot-starter-data-redis' - api 'org.redisson:redisson:3.19.0' - api 'io.github.openfeign:feign-httpclient:12.1' - api 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.4' - api 'com.amazonaws:aws-java-sdk-s3control:1.12.372' - api 'com.nimbusds:nimbus-jose-jwt:3.10' - api("com.slack.api:slack-api-client:1.28.0") + implementation 'org.redisson:redisson:3.19.0' + implementation 'io.github.openfeign:feign-httpclient:12.1' + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.4' + implementation 'com.amazonaws:aws-java-sdk-s3control:1.12.372' + implementation 'com.nimbusds:nimbus-jose-jwt:3.10' + implementation "com.slack.api:slack-api-client:1.28.0" + implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/redis/lock/LockManagerImpl.java b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/lock/LockManagerImpl.java index cc047107..272ede39 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/redis/lock/LockManagerImpl.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/redis/lock/LockManagerImpl.java @@ -2,9 +2,12 @@ import static allchive.server.core.consts.AllchiveConst.LOCK_LEASE_TIME; import static allchive.server.core.consts.AllchiveConst.LOCK_WAIT_TIME; +import static org.springframework.transaction.TransactionDefinition.PROPAGATION_NEVER; +import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED; import allchive.server.core.error.exception.AlreadyRedissonUnlockException; import allchive.server.core.error.exception.InterruptRedissonException; +import allchive.server.core.error.exception.TxTemplateExecutionFailException; import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -12,33 +15,50 @@ import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; @Component @RequiredArgsConstructor @Slf4j public class LockManagerImpl implements LockManager { private final RedissonClient redissonClient; + private final TransactionTemplate txTemplate; - @Transactional(propagation = Propagation.NEVER) public Object lock(ProceedingJoinPoint joinPoint, String key) throws Throwable { - log.info("lock key : {}", key); RLock rLock = redissonClient.getLock(key); + log.info("lock : {}", key); try { boolean available = rLock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS); if (!available) { return false; } - return joinPoint.proceed(); + + TransactionCallback txCallback = + status -> { + try { + return joinPoint.proceed(); + } catch (Throwable e) { + throw TxTemplateExecutionFailException.EXCEPTION; + } + }; + return executeTransaction( + status -> executeTransaction(txCallback, PROPAGATION_REQUIRED), + PROPAGATION_NEVER); } catch (InterruptedException e) { throw InterruptRedissonException.EXCEPTION; } finally { try { + log.info("unlock : {}", key); rLock.unlock(); } catch (IllegalMonitorStateException e) { throw AlreadyRedissonUnlockException.EXCEPTION; } } } + + private Object executeTransaction(TransactionCallback block, int propagationBehavior) { + txTemplate.setPropagationBehavior(propagationBehavior); + return txTemplate.execute(block); + } } diff --git a/Core/src/main/java/allchive/server/core/event/events/s3/S3ImageDeleteEvent.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/event/S3ImageDeleteEvent.java similarity index 88% rename from Core/src/main/java/allchive/server/core/event/events/s3/S3ImageDeleteEvent.java rename to Infrastructure/src/main/java/allchive/server/infrastructure/s3/event/S3ImageDeleteEvent.java index c7916522..42737e6d 100644 --- a/Core/src/main/java/allchive/server/core/event/events/s3/S3ImageDeleteEvent.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/event/S3ImageDeleteEvent.java @@ -1,4 +1,4 @@ -package allchive.server.core.event.events.s3; +package allchive.server.infrastructure.s3.event; import java.util.List; diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3DeleteObjectService.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3DeleteObjectService.java index 2ef0854f..c8effb82 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3DeleteObjectService.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3DeleteObjectService.java @@ -2,7 +2,7 @@ import allchive.server.core.error.exception.S3ObjectNotFoundException; -import allchive.server.core.event.events.s3.S3ImageDeleteEvent; +import allchive.server.infrastructure.s3.event.S3ImageDeleteEvent; import com.amazonaws.services.s3.AmazonS3; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackMessageGenerater.java b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackMessageGenerater.java index 8007019f..3706561f 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackMessageGenerater.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackMessageGenerater.java @@ -4,8 +4,8 @@ import static com.slack.api.model.block.Blocks.section; import static com.slack.api.model.block.composition.BlockCompositions.plainText; -import allchive.server.core.event.events.slack.SlackAsyncErrorEvent; -import allchive.server.core.event.events.slack.SlackErrorEvent; +import allchive.server.infrastructure.slack.event.SlackAsyncErrorEvent; +import allchive.server.infrastructure.slack.event.SlackErrorEvent; import com.slack.api.model.block.Blocks; import com.slack.api.model.block.DividerBlock; import com.slack.api.model.block.HeaderBlock; diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackSendMessageHandler.java b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackSendMessageHandler.java index 6adb2d84..97a64024 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackSendMessageHandler.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/SlackSendMessageHandler.java @@ -1,8 +1,8 @@ package allchive.server.infrastructure.slack; -import allchive.server.core.event.events.slack.SlackAsyncErrorEvent; -import allchive.server.core.event.events.slack.SlackErrorEvent; +import allchive.server.infrastructure.slack.event.SlackAsyncErrorEvent; +import allchive.server.infrastructure.slack.event.SlackErrorEvent; import com.slack.api.webhook.Payload; import java.io.IOException; import lombok.RequiredArgsConstructor; diff --git a/Core/src/main/java/allchive/server/core/event/events/slack/SlackAsyncErrorEvent.java b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/event/SlackAsyncErrorEvent.java similarity index 92% rename from Core/src/main/java/allchive/server/core/event/events/slack/SlackAsyncErrorEvent.java rename to Infrastructure/src/main/java/allchive/server/infrastructure/slack/event/SlackAsyncErrorEvent.java index f5654070..61ad4943 100644 --- a/Core/src/main/java/allchive/server/core/event/events/slack/SlackAsyncErrorEvent.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/event/SlackAsyncErrorEvent.java @@ -1,4 +1,4 @@ -package allchive.server.core.event.events.slack; +package allchive.server.infrastructure.slack.event; import lombok.Builder; diff --git a/Core/src/main/java/allchive/server/core/event/events/slack/SlackErrorEvent.java b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/event/SlackErrorEvent.java similarity index 92% rename from Core/src/main/java/allchive/server/core/event/events/slack/SlackErrorEvent.java rename to Infrastructure/src/main/java/allchive/server/infrastructure/slack/event/SlackErrorEvent.java index 77f8bacb..c72a654c 100644 --- a/Core/src/main/java/allchive/server/core/event/events/slack/SlackErrorEvent.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/slack/event/SlackErrorEvent.java @@ -1,4 +1,4 @@ -package allchive.server.core.event.events.slack; +package allchive.server.infrastructure.slack.event; import lombok.Builder; diff --git a/README.md b/README.md index 7b1ba995..2546047b 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# AllChive_Server \ No newline at end of file +# AllChive Server \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3bcba069..0ea8003d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -plugins { +:plugins { id 'java' id 'org.springframework.boot' version '2.7.13' id 'com.diffplug.spotless' version '6.11.0' @@ -10,7 +10,7 @@ repositories { subprojects { group = 'allchive.server' - version = '0.0.1-SNAPSHOT' + version = '1.0.1' apply plugin: 'java' apply plugin: 'java-library' diff --git a/config/nginx/default.conf b/config/nginx/default.conf index e3b213a3..8f203e0c 100644 --- a/config/nginx/default.conf +++ b/config/nginx/default.conf @@ -4,10 +4,6 @@ server { autoindex_localtime on; - if ($http_x_forwarded_proto != 'https') { - return 301 https://$host$request_uri; - } - location /api { proxy_pass http://server:8080; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/config/nginx/default.prod.conf b/config/nginx/default.prod.conf index dc7de01b..aee59867 100644 --- a/config/nginx/default.prod.conf +++ b/config/nginx/default.prod.conf @@ -4,10 +4,6 @@ server { autoindex_localtime on; - if ($http_x_forwarded_proto != 'https') { - return 301 https://$host$request_uri; - } - location /api { proxy_pass http://server:8080; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 5e6e699d..fff61f67 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -15,8 +15,10 @@ services: volumes: - ./mysqldata:/var/lib/mysql environment: - - MYSQL_ROOT_PASSWORD=test + - MYSQL_USER=user + - MYSQL_PASSWORD=user + - MYSQL_ROOT_PASSWORD=root - MYSQL_HOST=localhost - - MYSQL_PORT=3306 + - MYSQL_PORT=3307 ports: - - "3306:3306" \ No newline at end of file + - "3307:3306" \ No newline at end of file