diff --git a/.gitignore b/.gitignore index 9d21c5b9..303c6f31 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ out/ .env.* -Domain/src/main/generated/**/*.java \ No newline at end of file +Domain/src/main/generated/**/*.java +Core/src/main/resources/static/apple/**.p8 \ No newline at end of file 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 ef85f68d..dfdbc71e 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 @@ -6,6 +6,7 @@ import allchive.server.api.archiving.model.dto.response.ArchivingContentsResponse; import allchive.server.api.archiving.model.dto.response.ArchivingResponse; import allchive.server.api.archiving.model.dto.response.ArchivingTitleResponse; +import allchive.server.api.archiving.model.dto.response.ArchivingsResponse; import allchive.server.api.archiving.service.*; import allchive.server.api.common.slice.SliceResponse; import allchive.server.domain.domains.archiving.domain.enums.Category; @@ -26,6 +27,7 @@ public class ArchivingController { private final CreateArchivingUseCase createArchivingUseCase; private final UpdateArchivingUseCase updateArchivingUseCase; + private final GetArchivingInfoUseCase getArchivingInfoUseCase; private final DeleteArchivingUseCase deleteArchivingUseCase; private final GetArchivingUseCase getArchivingUseCase; private final GetArchivedArchivingUseCase getArchivedArchivingUseCase; @@ -34,6 +36,7 @@ public class ArchivingController { private final GetArchivingContentsUseCase getArchivingContentsUseCase; private final UpdateArchivingScrapUseCase updateArchivingScrapUseCase; private final UpdateArchivingPinUseCase updateArchivingPinUseCase; + private final GetPopularArchivingUseCase getPopularArchivingUseCase; @Operation(summary = "아카이빙을 생성합니다.") @PostMapping() @@ -41,6 +44,12 @@ public void createArchiving(@RequestBody CreateArchivingRequest createArchivingR createArchivingUseCase.execute(createArchivingRequest); } + @Operation(summary = "아카이빙 정보 수정시 보여줄 정보를 가져옵니다.") + @GetMapping(value = "/{archivingId}") + public ArchivingResponse getArchiving(@PathVariable("archivingId") Long archivingId) { + return getArchivingInfoUseCase.execute(archivingId); + } + @Operation(summary = "아카이빙을 수정합니다.") @PatchMapping(value = "/{archivingId}") public void updateArchiving( @@ -91,6 +100,12 @@ public ArchivingTitleResponse getScrapArchiving() { return getArchivingTitleUseCase.execute(); } + @Operation(summary = "인기있는 아카이빙 5개를 가져옵니다") + @GetMapping(value = "/popular") + public ArchivingsResponse getPopularArchiving() { + return getPopularArchivingUseCase.execute(); + } + @Operation(summary = "아카이빙별 컨텐츠 리스트를 가져옵니다.") @GetMapping(value = "/{archivingId}/contents") public ArchivingContentsResponse getArchivingContents( diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java index d4e14344..6e17c9bf 100644 --- a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingContentsResponse.java @@ -3,8 +3,12 @@ import allchive.server.api.common.slice.SliceResponse; import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.core.annotation.DateFormat; import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.archiving.domain.enums.Category; +import allchive.server.domain.domains.user.domain.User; import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; @@ -15,39 +19,78 @@ public class ArchivingContentsResponse { @Schema(description = "아카이빙 제목") private String archivingTitle; + @Schema(description = "아카이빙 카테고리") + private Category category; + @Schema(description = "아카이빙 고유번호") private Long archivingId; + @DateFormat + @Schema(description = "아카이빙 생성일") + private LocalDateTime createdAt; + @Schema(description = "아카이빙의 총 컨텐츠 개수") private Long totalContentsCount; + @Schema(description = "아카이빙 소유자 고유번호") + private Long ownerId; + + @Schema(description = "아카이빙 소유자 닉네임") + private String ownerNickname; + + @Schema(description = "아카이빙 소유자 프로픨 이미지") + private String ownerProfileImgUrl; + @Schema(description = "유저 소유 여부") private Boolean isMine; + @Schema(description = "아카이빙 스크랩 여부") + private Boolean isScrap; + @Builder private ArchivingContentsResponse( SliceResponse contents, String archivingTitle, + Category category, Long archivingId, + LocalDateTime createdAt, Long totalContentsCount, - Boolean isMine) { + Long ownerId, + String ownerNickname, + String ownerProfileImgUrl, + Boolean isMine, + Boolean isScrap) { this.contents = contents; this.archivingTitle = archivingTitle; + this.category = category; this.archivingId = archivingId; + this.createdAt = createdAt; this.totalContentsCount = totalContentsCount; + this.ownerId = ownerId; + this.ownerNickname = ownerNickname; + this.ownerProfileImgUrl = ownerProfileImgUrl; this.isMine = isMine; + this.isScrap = isScrap; } public static ArchivingContentsResponse of( SliceResponse contentResponseSlice, Archiving archiving, - Boolean isMine) { + User user, + Boolean isMine, + Boolean isScrap) { return ArchivingContentsResponse.builder() .archivingId(archiving.getId()) + .createdAt(archiving.getCreatedAt()) .archivingTitle(archiving.getTitle()) - .totalContentsCount(archiving.getScrapCnt() + archiving.getImgCnt()) + .category(archiving.getCategory()) + .totalContentsCount(archiving.getLinkCnt() + archiving.getImgCnt()) .contents(contentResponseSlice) + .ownerId(user.getId()) + .ownerNickname(user.getNickname()) + .ownerProfileImgUrl(user.getProfileImgUrl()) .isMine(isMine) + .isScrap(isScrap) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java index c63ac0d1..359d6cf8 100644 --- a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingResponse.java @@ -44,6 +44,9 @@ public class ArchivingResponse { @Schema(description = "아카이빙 스크랩/고정 여부, true == 스크랩/고정됨") private boolean markStatus; + @Schema(description = "아카이빙 공개 여부") + private boolean publicStatus; + @Builder private ArchivingResponse( Long archivingId, @@ -54,7 +57,8 @@ private ArchivingResponse( Long imgCnt, Long linkCnt, Long scrapCnt, - boolean markStatus) { + boolean markStatus, + boolean publicStatus) { this.archivingId = archivingId; this.title = title; this.imageUrl = imageUrl; @@ -64,6 +68,7 @@ private ArchivingResponse( this.linkCnt = linkCnt; this.scrapCnt = scrapCnt; this.markStatus = markStatus; + this.publicStatus = publicStatus; } public static ArchivingResponse of(Archiving archiving, boolean markStatus) { @@ -77,6 +82,7 @@ public static ArchivingResponse of(Archiving archiving, boolean markStatus) { .linkCnt(archiving.getLinkCnt()) .scrapCnt(archiving.getScrapCnt()) .markStatus(markStatus) + .publicStatus(archiving.getPublicStatus()) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingTitleResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingTitleResponse.java index 038f4893..8f065bfc 100644 --- a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingTitleResponse.java +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingTitleResponse.java @@ -2,92 +2,95 @@ import allchive.server.api.archiving.model.vo.TitleContentCntVo; +import allchive.server.api.common.json.naming.UpperCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; import java.util.ArrayList; import java.util.List; import lombok.Builder; import lombok.Getter; @Getter +@JsonNaming(UpperCaseStrategy.class) public class ArchivingTitleResponse { - private List food; - private List life; - private List homeLiving; - private List shopping; - private List sport; - private List selfImprovement; - private List tech; - private List design; - private List trend; + private List FOOD; + private List LIFE; + private List HOME_LIVING; + private List SHOPPING; + private List SPORT; + private List SELF_IMPROVEMENT; + private List TECH; + private List DESIGN; + private List TREND; @Builder private ArchivingTitleResponse( - List food, - List life, - List homeLiving, - List shopping, - List sport, - List selfImprovement, - List tech, - List design, - List trend) { - this.food = food; - this.life = life; - this.homeLiving = homeLiving; - this.shopping = shopping; - this.sport = sport; - this.selfImprovement = selfImprovement; - this.tech = tech; - this.design = design; - this.trend = trend; + List FOOD, + List LIFE, + List HOME_LIVING, + List SHOPPING, + List SPORT, + List SELF_IMPROVEMENT, + List TECH, + List DESIGN, + List TREND) { + this.FOOD = FOOD; + this.LIFE = LIFE; + this.HOME_LIVING = HOME_LIVING; + this.SHOPPING = SHOPPING; + this.SPORT = SPORT; + this.SELF_IMPROVEMENT = SELF_IMPROVEMENT; + this.TECH = TECH; + this.DESIGN = DESIGN; + this.TREND = TREND; } public static ArchivingTitleResponse init() { return ArchivingTitleResponse.builder() - .food(new ArrayList<>()) - .life(new ArrayList<>()) - .homeLiving(new ArrayList<>()) - .shopping(new ArrayList<>()) - .sport(new ArrayList<>()) - .selfImprovement(new ArrayList<>()) - .tech(new ArrayList<>()) - .design(new ArrayList<>()) - .trend(new ArrayList<>()) + .FOOD(new ArrayList<>()) + .LIFE(new ArrayList<>()) + .HOME_LIVING(new ArrayList<>()) + .SHOPPING(new ArrayList<>()) + .SPORT(new ArrayList<>()) + .SELF_IMPROVEMENT(new ArrayList<>()) + .TECH(new ArrayList<>()) + .DESIGN(new ArrayList<>()) + .TREND(new ArrayList<>()) .build(); } public void addFood(TitleContentCntVo vo) { - this.food.add(vo); + this.FOOD.add(vo); } public void addLife(TitleContentCntVo vo) { - this.life.add(vo); + this.LIFE.add(vo); } public void addHomeLiving(TitleContentCntVo vo) { - this.homeLiving.add(vo); + this.HOME_LIVING.add(vo); } public void addShopping(TitleContentCntVo vo) { - this.shopping.add(vo); + this.SHOPPING.add(vo); } public void addSport(TitleContentCntVo vo) { - this.sport.add(vo); + this.SPORT.add(vo); } public void addSelfImprovement(TitleContentCntVo vo) { - this.selfImprovement.add(vo); + this.SELF_IMPROVEMENT.add(vo); } public void addTech(TitleContentCntVo vo) { - this.tech.add(vo); + this.TECH.add(vo); } public void addDesign(TitleContentCntVo vo) { - this.design.add(vo); + this.DESIGN.add(vo); } public void addTrend(TitleContentCntVo vo) { - this.trend.add(vo); + this.TREND.add(vo); } } diff --git a/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingsResponse.java b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingsResponse.java new file mode 100644 index 00000000..a86b4cf6 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/model/dto/response/ArchivingsResponse.java @@ -0,0 +1,20 @@ +package allchive.server.api.archiving.model.dto.response; + + +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ArchivingsResponse { + List archivings; + + @Builder + private ArchivingsResponse(List archivings) { + this.archivings = archivings; + } + + public static ArchivingsResponse of(List archivings) { + return ArchivingsResponse.builder().archivings(archivings).build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/archiving/model/vo/TitleContentCntVo.java b/Api/src/main/java/allchive/server/api/archiving/model/vo/TitleContentCntVo.java index 60085516..4154baab 100644 --- a/Api/src/main/java/allchive/server/api/archiving/model/vo/TitleContentCntVo.java +++ b/Api/src/main/java/allchive/server/api/archiving/model/vo/TitleContentCntVo.java @@ -8,6 +8,9 @@ @Getter public class TitleContentCntVo { + @Schema(description = "아카이빙 고유 아이디") + private Long archivingId; + @Schema(defaultValue = "아카이빙 제목", description = "아카이빙 제목") private String title; @@ -15,14 +18,16 @@ public class TitleContentCntVo { private Long contentCnt; @Builder - private TitleContentCntVo(String title, Long contentCnt) { + private TitleContentCntVo(Long archivingId, String title, Long contentCnt) { + this.archivingId = archivingId; this.title = title; this.contentCnt = contentCnt; } public static TitleContentCntVo from(Archiving archiving) { return TitleContentCntVo.builder() - .contentCnt(archiving.getImgCnt() + archiving.getScrapCnt()) + .archivingId(archiving.getId()) + .contentCnt(archiving.getImgCnt() + archiving.getLinkCnt()) .title(archiving.getTitle()) .build(); } 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 d54ddd61..3f81f4fa 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 @@ -30,5 +30,6 @@ public void execute(Long archivingId) { private void validateExecution(Long archivingId, Long userId) { archivingValidator.verifyUser(userId, archivingId); + archivingValidator.validateNotDeleted(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 cc628be7..e340d442 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,6 +14,9 @@ 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.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 org.springframework.data.domain.Pageable; @@ -28,23 +31,28 @@ public class GetArchivingContentsUseCase { private final ContentAdaptor contentAdaptor; private final ContentTagGroupAdaptor contentTagGroupAdaptor; private final ContentMapper contentMapper; + private final UserAdaptor userAdaptor; + private final ScrapAdaptor scrapAdaptor; @Transactional(readOnly = true) public ArchivingContentsResponse execute(Long archivingId, Pageable pageable) { Long userId = SecurityUtil.getCurrentUserId(); validateExecution(archivingId, userId); Archiving archiving = archivingAdaptor.findById(archivingId); + User owner = userAdaptor.findById(archiving.getUserId()); Slice contentResponseSlice = getContentResponseSlice(archivingId, pageable); return ArchivingContentsResponse.of( SliceResponse.of(contentResponseSlice), archiving, - calculateIsMine(archiving, userId)); + owner, + calculateIsMine(archiving, userId), + calculateIsScrap(archiving, userId)); } private void validateExecution(Long archivingId, Long userId) { - archivingValidator.validatePublicStatus(archivingId, userId); - archivingValidator.validateDeleteStatus(archivingId, userId); + archivingValidator.validatePublic(archivingId, userId); + archivingValidator.validateNotDeleteExceptUser(archivingId, userId); } private Slice getContentResponseSlice(Long archivingId, Pageable pageable) { @@ -62,4 +70,12 @@ private Boolean calculateIsMine(Archiving archiving, Long userId) { } return Boolean.FALSE; } + + private Boolean calculateIsScrap(Archiving archiving, Long userId) { + User user = userAdaptor.findById(userId); + if (scrapAdaptor.existsByUserAndArchivingId(user, archiving.getId())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } } diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingInfoUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingInfoUseCase.java new file mode 100644 index 00000000..4da59f70 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetArchivingInfoUseCase.java @@ -0,0 +1,28 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +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.archiving.validator.ArchivingValidator; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetArchivingInfoUseCase { + private final ArchivingValidator archivingValidator; + private final ArchivingAdaptor archivingAdaptor; + + @Transactional(readOnly = true) + public ArchivingResponse execute(Long archivingId) { + validateExecution(archivingId); + Archiving archiving = archivingAdaptor.findById(archivingId); + return ArchivingResponse.of(archiving, Boolean.FALSE); + } + + private void validateExecution(Long archivingId) { + archivingValidator.validateExistById(archivingId); + } +} diff --git a/Api/src/main/java/allchive/server/api/archiving/service/GetPopularArchivingUseCase.java b/Api/src/main/java/allchive/server/api/archiving/service/GetPopularArchivingUseCase.java new file mode 100644 index 00000000..5eab76ec --- /dev/null +++ b/Api/src/main/java/allchive/server/api/archiving/service/GetPopularArchivingUseCase.java @@ -0,0 +1,45 @@ +package allchive.server.api.archiving.service; + + +import allchive.server.api.archiving.model.dto.response.ArchivingResponse; +import allchive.server.api.archiving.model.dto.response.ArchivingsResponse; +import allchive.server.api.config.security.SecurityUtil; +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.user.adaptor.ScrapAdaptor; +import allchive.server.domain.domains.user.domain.Scrap; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class GetPopularArchivingUseCase { + private final ArchivingAdaptor archivingAdaptor; + private final ScrapAdaptor scrapAdaptor; + + public ArchivingsResponse execute() { + Long userId = SecurityUtil.getCurrentUserId(); + List scraps = scrapAdaptor.findAllByUserId(userId); + List archivingResponses = + archivingAdaptor.queryArchivingOrderByScrapCntLimit5().stream() + .filter(archiving -> archiving.getScrapCnt() > 0) + .map( + archiving -> + ArchivingResponse.of( + archiving, calculateIsScrap(scraps, archiving))) + .collect(Collectors.toList()); + return ArchivingsResponse.of(archivingResponses); + } + + private Boolean calculateIsScrap(List scraps, Archiving archiving) { + if (scraps.stream() + .filter(scrap -> scrap.getArchivingId().equals(archiving.getId())) + .count() + == 1L) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } +} 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 5108bc34..4534daf7 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 @@ -18,16 +18,11 @@ public class UpdateArchivingPinUseCase { public void execute(Long archivingId, Boolean cancel) { Long userId = SecurityUtil.getCurrentUserId(); validateExecution(archivingId, userId, cancel); - if (cancel) { - archivingDomainService.updatePin(archivingId, userId, false); - } else { - archivingDomainService.updatePin(archivingId, userId, true); - } + archivingDomainService.updatePin(archivingId, userId, !cancel); } private void validateExecution(Long archivingId, Long userId, Boolean cancel) { - archivingValidator.validateExistById(archivingId); - archivingValidator.validateDeleteStatus(archivingId, userId); + archivingValidator.validateNotDeleteExceptUser(archivingId, userId); if (cancel) { archivingValidator.validateNotPinStatus(archivingId, userId); } else { 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 9ff7468b..6a29b529 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 @@ -1,5 +1,7 @@ package allchive.server.api.archiving.service; +import static allchive.server.core.consts.AllchiveConst.MINUS_ONE; +import static allchive.server.core.consts.AllchiveConst.PLUS_ONE; import allchive.server.api.config.security.SecurityUtil; import allchive.server.core.annotation.UseCase; @@ -29,19 +31,20 @@ public void execute(Long archivingId, Boolean cancel) { User user = userAdaptor.findById(userId); if (cancel) { scrapDomainService.deleteScrapByUserAndArchivingId(user, archivingId); - archivingDomainService.updateScrapCount(archivingId, -1); + archivingDomainService.updateScrapCount(archivingId, MINUS_ONE); } else { Scrap scrap = Scrap.of(user, archivingId); scrapDomainService.save(scrap); - archivingDomainService.updateScrapCount(archivingId, 1); + archivingDomainService.updateScrapCount(archivingId, PLUS_ONE); } } private void validateExecution(Long archivingId, Long userId, Boolean cancel) { - archivingValidator.validateExistById(archivingId); - archivingValidator.validateDeleteStatus(archivingId, userId); - if (!cancel) { + archivingValidator.validateNotDeleteExceptUser(archivingId, userId); + if (cancel) { scrapValidator.validateExistScrap(userId, archivingId); + } else { + scrapValidator.validateNotExistScrap(userId, 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 6cb01deb..dd3491e9 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 @@ -5,7 +5,6 @@ import allchive.server.api.auth.service.LogOutUserUseCase; import allchive.server.api.auth.service.TokenRefreshUseCase; import allchive.server.api.auth.service.WithdrawUserUseCase; -import allchive.server.domain.domains.user.domain.enums.OauthProvider; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -23,12 +22,18 @@ public class AuthController { private final TokenRefreshUseCase tokenRefreshUseCase; @Operation(summary = "회원탈퇴를 합니다.") - @DeleteMapping("/withdrawal/{provider}") + @DeleteMapping("/withdrawal") public void withDrawUser( - @PathVariable OauthProvider provider, - @RequestParam(required = false, name = "appleAccessToken", value = "") - String appleAccessToken) { - withdrawUserUseCase.execute(provider, appleAccessToken); + @RequestParam(required = false, name = "appleCode", value = "") String appleCode, + @RequestHeader(value = "referer", required = false) String referer) { + withdrawUserUseCase.execute(appleCode, referer); + } + + @Operation(summary = "회원탈퇴를 합니다. (개발용)", deprecated = true) + @DeleteMapping("/withdrawal/dev") + public void withDrawUserDev( + @RequestParam(required = false, name = "appleCode", value = "") String appleCode) { + withdrawUserUseCase.executeDev(appleCode); } @Operation(summary = "로그아웃을 합니다.") diff --git a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java index dfc246a5..d3272f59 100644 --- a/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java +++ b/Api/src/main/java/allchive/server/api/auth/controller/OauthController.java @@ -82,7 +82,18 @@ public OauthSignInResponse oauthUserIdTokenLogin( public OauthRegisterResponse oauthUserRegister( @PathVariable("provider") OauthProvider provider, @RequestParam("idToken") String idToken, + @RequestParam(value = "oauthAccessToken", required = false) String accessToken, @RequestBody RegisterRequest registerRequest) { - return oauthRegisterUseCase.execute(provider, idToken, registerRequest); + return oauthRegisterUseCase.execute(provider, idToken, accessToken, registerRequest); + } + + @Operation(summary = "회원가입 (개발용)", deprecated = true) + @PostMapping("/register/{provider}/dev") + public OauthRegisterResponse oauthUserRegisterDev( + @PathVariable("provider") OauthProvider provider, + @RequestParam("idToken") String idToken, + @RequestParam(value = "oauthAccessToken", required = false) String accessToken, + @RequestBody RegisterRequest registerRequest) { + return oauthRegisterUseCase.executeDev(provider, idToken, accessToken, registerRequest); } } diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java b/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java deleted file mode 100644 index a6be5cc0..00000000 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/KakaoUserInfoDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package allchive.server.api.auth.model.dto; - - -import allchive.server.domain.domains.user.domain.enums.OauthInfo; -import allchive.server.domain.domains.user.domain.enums.OauthProvider; -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class KakaoUserInfoDto { - - // oauth인증한 사용자 고유 아이디 - private final String oauthId; - - private final String email; - private final String phoneNumber; - private final String profileImage; - private final String name; - private final OauthProvider oauthProvider; - - public OauthInfo toOauthInfo() { - return OauthInfo.of(oauthProvider, oauthId); - } -} -; diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/OauthUserInfoDto.java b/Api/src/main/java/allchive/server/api/auth/model/dto/OauthUserInfoDto.java new file mode 100644 index 00000000..e324ba55 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/OauthUserInfoDto.java @@ -0,0 +1,30 @@ +package allchive.server.api.auth.model.dto; + + +import allchive.server.domain.domains.user.domain.enums.OauthProvider; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoInformationResponse; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class OauthUserInfoDto { + private String email; + private String name; + private OauthProvider oauthProvider; + + @Builder + private OauthUserInfoDto(String email, String name, OauthProvider oauthProvider) { + this.email = email; + this.name = name; + this.oauthProvider = oauthProvider; + } + + public static OauthUserInfoDto fromKakao(KakaoInformationResponse response) { + return OauthUserInfoDto.builder() + .email(response.getEmail()) + .name(response.getNickName()) + .oauthProvider(OauthProvider.KAKAO) + .build(); + } +} +; diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java index fe3d10f2..4dd83eae 100644 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java +++ b/Api/src/main/java/allchive/server/api/auth/model/dto/request/RegisterRequest.java @@ -26,4 +26,13 @@ public class RegisterRequest { @ArraySchema(schema = @Schema(description = "관심 주제", defaultValue = "FOOD")) private List<@ValidEnum(target = Category.class) Category> categories; + + @Schema(defaultValue = "false", description = "마케팅 정보 수신 동의 여부") + private boolean marketingAgreement; + + @Schema(defaultValue = "이름", description = "이름, 애플 회원가입용") + private String name; + + @Schema(defaultValue = "이메일", description = "이메일, 애플 회원가입용") + private String email; } diff --git a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthUserInfoResponse.java b/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthUserInfoResponse.java deleted file mode 100644 index e0c97af3..00000000 --- a/Api/src/main/java/allchive/server/api/auth/model/dto/response/OauthUserInfoResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -package allchive.server.api.auth.model.dto.response; - - -import allchive.server.api.auth.model.dto.KakaoUserInfoDto; -import lombok.Builder; -import lombok.Getter; - -@Getter -public class OauthUserInfoResponse { - private final String email; - private final String phoneNumber; - private final String profileImage; - private final String name; - - @Builder - public OauthUserInfoResponse( - String email, String phoneNumber, String profileImage, String name) { - this.email = email; - this.phoneNumber = phoneNumber; - this.profileImage = profileImage; - this.name = name; - } - - public static OauthUserInfoResponse from(KakaoUserInfoDto kakaoUserInfoDto) { - return OauthUserInfoResponse.builder() - .email(kakaoUserInfoDto.getEmail()) - .phoneNumber(kakaoUserInfoDto.getPhoneNumber()) - .profileImage(kakaoUserInfoDto.getProfileImage()) - .name(kakaoUserInfoDto.getName()) - .build(); - } -} diff --git a/Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java index ec842de9..5163c761 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/LogOutUserUseCase.java @@ -5,12 +5,14 @@ import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.user.adaptor.RefreshTokenAdaptor; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor public class LogOutUserUseCase { private final RefreshTokenAdaptor refreshTokenAdaptor; + @Transactional public void execute() { Long userId = SecurityUtil.getCurrentUserId(); refreshTokenAdaptor.deleteTokenByUserId(userId); diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java index 2886b644..a15974dd 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthLoginUseCase.java @@ -6,36 +6,56 @@ import allchive.server.api.auth.service.helper.OauthHelper; import allchive.server.api.auth.service.helper.TokenGenerateHelper; import allchive.server.core.annotation.UseCase; +import allchive.server.core.helper.SpringEnvironmentHelper; import allchive.server.domain.domains.user.domain.User; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.OauthProvider; import allchive.server.domain.domains.user.service.UserDomainService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.transaction.annotation.Transactional; @UseCase +@Slf4j @RequiredArgsConstructor public class OauthLoginUseCase { private final OauthHelper oauthHelper; private final UserDomainService userDomainService; private final TokenGenerateHelper tokenGenerateHelper; + private final SpringEnvironmentHelper springEnvironmentHelper; + @Transactional public OauthSignInResponse loginWithCode(OauthProvider provider, String code, String referer) { final OauthTokenResponse oauthTokenResponse = oauthHelper.getCredential(provider, code, referer); return processLoginWithIdToken(provider, oauthTokenResponse.getIdToken()); } + @Transactional public OauthSignInResponse loginWithIdToken(OauthProvider provider, String idToken) { return processLoginWithIdToken(provider, idToken); } + @Transactional public OauthSignInResponse devLogin(OauthProvider provider, String code) { final OauthTokenResponse oauthTokenResponse = oauthHelper.getCredentialDev(provider, code); - return processLoginWithIdToken(provider, oauthTokenResponse.getIdToken()); + if (!springEnvironmentHelper.isProdProfile()) { + log.info("{}", oauthTokenResponse.getAccessToken()); + } + return processLoginWithIdTokenDev(provider, oauthTokenResponse.getIdToken()); } private OauthSignInResponse processLoginWithIdToken(OauthProvider provider, String idToken) { final OauthInfo oauthInfo = oauthHelper.getOauthInfo(provider, idToken); + return checkUserCanLogin(oauthInfo, idToken); + } + + private OauthSignInResponse processLoginWithIdTokenDev(OauthProvider provider, String idToken) { + final OauthInfo oauthInfo = oauthHelper.getOauthInfoDev(provider, idToken); + return checkUserCanLogin(oauthInfo, idToken); + } + + private OauthSignInResponse checkUserCanLogin(OauthInfo oauthInfo, String idToken) { if (userDomainService.checkUserCanLogin(oauthInfo)) { User user = userDomainService.loginUser(oauthInfo); return tokenGenerateHelper.execute(user); diff --git a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java index 377dc16d..8bb08449 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/OauthRegisterUseCase.java @@ -1,6 +1,7 @@ package allchive.server.api.auth.service; +import allchive.server.api.auth.model.dto.OauthUserInfoDto; import allchive.server.api.auth.model.dto.request.RegisterRequest; import allchive.server.api.auth.model.dto.response.OauthRegisterResponse; import allchive.server.api.auth.service.helper.OauthHelper; @@ -12,6 +13,7 @@ import allchive.server.domain.domains.user.domain.enums.OauthProvider; import allchive.server.domain.domains.user.service.UserDomainService; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -20,15 +22,63 @@ public class OauthRegisterUseCase { private final UserDomainService userDomainService; private final TokenGenerateHelper tokenGenerateHelper; + @Transactional public OauthRegisterResponse execute( - OauthProvider provider, String idToken, RegisterRequest registerRequest) { + OauthProvider provider, + String idToken, + String oauthAccessToken, + RegisterRequest request) { final OauthInfo oauthInfo = oauthHelper.getOauthInfo(provider, idToken); - final User user = - userDomainService.registerUser( - registerRequest.getNickname(), - UrlUtil.convertUrlToKey(registerRequest.getProfileImgUrl()), - registerRequest.getCategories(), - oauthInfo); + final OauthUserInfoDto oauthUserInfoDto = getUserInfo(provider, oauthAccessToken); + final User user = registerUser(provider, oauthInfo, oauthUserInfoDto, request); return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); } + + @Transactional + public OauthRegisterResponse executeDev( + OauthProvider provider, + String idToken, + String oauthAccessToken, + RegisterRequest request) { + final OauthInfo oauthInfo = oauthHelper.getOauthInfoDev(provider, idToken); + final OauthUserInfoDto oauthUserInfoDto = getUserInfo(provider, oauthAccessToken); + final User user = registerUser(provider, oauthInfo, oauthUserInfoDto, request); + return OauthRegisterResponse.from(tokenGenerateHelper.execute(user)); + } + + private User registerUser( + OauthProvider provider, + OauthInfo oauthInfo, + OauthUserInfoDto oauthUserInfoDto, + RegisterRequest request) { + switch (provider) { + case APPLE: + return userDomainService.registerUser( + request.getName(), + request.getEmail(), + request.getNickname(), + UrlUtil.convertUrlToKey(request.getProfileImgUrl()), + request.getCategories(), + request.isMarketingAgreement(), + oauthInfo); + default: + return userDomainService.registerUser( + oauthUserInfoDto.getName(), + oauthUserInfoDto.getEmail(), + request.getNickname(), + UrlUtil.convertUrlToKey(request.getProfileImgUrl()), + request.getCategories(), + request.isMarketingAgreement(), + oauthInfo); + } + } + + private OauthUserInfoDto getUserInfo(OauthProvider provider, String oauthAccessToken) { + switch (provider) { + case APPLE: + return null; + default: + return oauthHelper.getUserInfo(provider, oauthAccessToken); + } + } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java index 740c8a5c..0bc1cd7a 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java +++ b/Api/src/main/java/allchive/server/api/auth/service/TokenRefreshUseCase.java @@ -10,6 +10,7 @@ import allchive.server.domain.domains.user.domain.RefreshTokenEntity; import allchive.server.domain.domains.user.domain.User; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -19,6 +20,7 @@ public class TokenRefreshUseCase { private final UserAdaptor userAdaptor; private final TokenGenerateHelper tokenGenerateHelper; + @Transactional public OauthRegisterResponse execute(String refreshToken) { RefreshTokenEntity oldToken = refreshTokenAdaptor.findTokenByRefreshToken(refreshToken); Long userId = jwtTokenProvider.parseRefreshToken(oldToken.getRefreshToken()); 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 9e10da37..28aaf10f 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 @@ -7,6 +7,7 @@ import allchive.server.core.error.exception.InvalidOauthProviderException; 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.block.service.BlockDomainService; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; @@ -24,6 +25,7 @@ import allchive.server.domain.domains.user.service.UserDomainService; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -42,20 +44,41 @@ public class WithdrawUserUseCase { private final RecycleDomainService recycleDomainService; private final ReportDomainService reportDomainService; private final UserDomainService userDomainService; + private final ArchivingDomainService archivingDomainService; - public void execute(OauthProvider provider, String appleAccessToken) { + @Transactional + public void execute(String appleAccessToken, String referer) { Long userId = SecurityUtil.getCurrentUserId(); User user = userAdaptor.findById(userId); // oauth쪽 탈퇴 - withdrawOauth(provider, appleAccessToken, user); + withdrawOauth(user.getOauthInfo().getProvider(), appleAccessToken, user, referer); // 우리쪽 탈퇴 withdrawService(userId, user); } - private void withdrawOauth(OauthProvider provider, String appleAccessToken, User user) { + @Transactional + public void executeDev(String appleAccessToken) { + Long userId = SecurityUtil.getCurrentUserId(); + User user = userAdaptor.findById(userId); + // oauth쪽 탈퇴 + withdrawOauthDev(user.getOauthInfo().getProvider(), appleAccessToken, user); + // 우리쪽 탈퇴 + withdrawService(userId, user); + } + + private void withdrawOauth( + OauthProvider provider, String appleAccessToken, User user, String referer) { + switch (provider) { + case KAKAO -> oauthHelper.withdraw(provider, user.getOauthInfo().getOid(), null, null); + case APPLE -> oauthHelper.withdraw(provider, null, appleAccessToken, referer); + default -> throw InvalidOauthProviderException.EXCEPTION; + } + } + + private void withdrawOauthDev(OauthProvider provider, String appleAccessToken, User user) { switch (provider) { - case KAKAO -> oauthHelper.withdraw(provider, user.getOauthInfo().getOid(), null); - case APPLE -> oauthHelper.withdraw(provider, null, appleAccessToken); + case KAKAO -> oauthHelper.withdrawDev(provider, user.getOauthInfo().getOid(), null); + case APPLE -> oauthHelper.withdrawDev(provider, null, appleAccessToken); default -> throw InvalidOauthProviderException.EXCEPTION; } } @@ -73,6 +96,7 @@ private void withdrawService(Long userId, User user) { contentDomainService.deleteAllByArchivingIdIn(archivingId); recycleDomainService.deleteAllByUserId(userId); reportDomainService.deleteAllByReportedUserId(userId); + archivingDomainService.deleteAllById(archivingId); userDomainService.deleteUserById(userId); } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOauthHelper.java index 5bdb06e0..4db26f04 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOauthHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/AppleOauthHelper.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.Helper; import allchive.server.core.dto.OIDCDecodePayload; +import allchive.server.core.error.exception.NoAppleCodeException; import allchive.server.core.properties.AppleOAuthProperties; import allchive.server.domain.domains.user.domain.enums.OauthInfo; import allchive.server.domain.domains.user.domain.enums.OauthProvider; @@ -35,7 +36,7 @@ public String getAppleOauthLinkDev() { return appleOAuthProperties.getBaseUrl() + String.format( APPLE_OAUTH_QUERY_STRING, - appleOAuthProperties.getClientId(), + appleOAuthProperties.getWebClientId(), appleOAuthProperties.getRedirectUrl()); } @@ -50,10 +51,10 @@ public AppleTokenResponse getAppleOAuthToken(String code, String referer) { public AppleTokenResponse getAppleOAuthTokenDev(String code) { return appleOAuthClient.appleAuth( - appleOAuthProperties.getClientId(), + appleOAuthProperties.getWebClientId(), appleOAuthProperties.getRedirectUrl(), code, - this.getClientSecret()); + this.getClientSecretDev()); } /** idtoken 분석 * */ @@ -65,6 +66,14 @@ public OauthInfo getAppleOAuthInfoByIdToken(String idToken) { .build(); } + public OauthInfo getAppleOAuthInfoByIdTokenDev(String idToken) { + OIDCDecodePayload oidcDecodePayload = this.getOIDCDecodePayloadDev(idToken); + return OauthInfo.builder() + .provider(OauthProvider.APPLE) + .oid(oidcDecodePayload.getSub()) + .build(); + } + /** oidc decode * */ public OIDCDecodePayload getOIDCDecodePayload(String token) { OIDCPublicKeysResponse oidcPublicKeysResponse = appleOIDCClient.getAppleOIDCOpenKeys(); @@ -75,10 +84,36 @@ public OIDCDecodePayload getOIDCDecodePayload(String token) { oidcPublicKeysResponse); } + public OIDCDecodePayload getOIDCDecodePayloadDev(String token) { + OIDCPublicKeysResponse oidcPublicKeysResponse = appleOIDCClient.getAppleOIDCOpenKeys(); + return oAuthOIDCHelper.getPayloadFromIdToken( + token, + appleOAuthProperties.getBaseUrl(), + appleOAuthProperties.getWebClientId(), + oidcPublicKeysResponse); + } + /** apple측 회원 탈퇴 * */ - public void withdrawAppleOauthUser(String appleOAuthAccessToken) { + public void withdrawAppleOauthUser(String code, String referer) { + if (code == null) { + throw NoAppleCodeException.EXCEPTION; + } + AppleTokenResponse appleTokenResponse = getAppleOAuthToken(code, referer); + appleOAuthClient.revoke( + appleOAuthProperties.getClientId(), + appleTokenResponse.getAccessToken(), + this.getClientSecret()); + } + + public void withdrawAppleOauthUserDev(String code) { + if (code == null) { + throw NoAppleCodeException.EXCEPTION; + } + AppleTokenResponse appleTokenResponse = getAppleOAuthTokenDev(code); appleOAuthClient.revoke( - appleOAuthProperties.getClientId(), appleOAuthAccessToken, this.getClientSecret()); + appleOAuthProperties.getWebClientId(), + appleTokenResponse.getAccessToken(), + this.getClientSecret()); } /** client secret 가져오기 * */ @@ -87,7 +122,16 @@ private String getClientSecret() { appleOAuthProperties.getTeamId(), appleOAuthProperties.getClientId(), appleOAuthProperties.getKeyId(), - appleOAuthProperties.getKeyPath(), + appleOAuthProperties.getAuthKey(), + appleOAuthProperties.getBaseUrl()); + } + + private String getClientSecretDev() { + return AppleLoginUtil.createClientSecret( + appleOAuthProperties.getTeamId(), + appleOAuthProperties.getWebClientId(), + appleOAuthProperties.getKeyId(), + appleOAuthProperties.getAuthKey(), appleOAuthProperties.getBaseUrl()); } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java index 14fe20ec..37a3e8c9 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/KakaoOauthHelper.java @@ -1,7 +1,9 @@ package allchive.server.api.auth.service.helper; +import static allchive.server.core.consts.AllchiveConst.BEARER; import static allchive.server.core.consts.AllchiveConst.KAKAO_OAUTH_QUERY_STRING; +import allchive.server.api.auth.model.dto.OauthUserInfoDto; import allchive.server.core.annotation.Helper; import allchive.server.core.dto.OIDCDecodePayload; import allchive.server.core.properties.KakaoOAuthProperties; @@ -9,6 +11,7 @@ import allchive.server.domain.domains.user.domain.enums.OauthProvider; import allchive.server.infrastructure.oauth.kakao.client.KakaoInfoClient; import allchive.server.infrastructure.oauth.kakao.client.KakaoOauthClient; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoInformationResponse; import allchive.server.infrastructure.oauth.kakao.dto.KakaoTokenResponse; import allchive.server.infrastructure.oauth.kakao.dto.KakaoUnlinkTarget; import allchive.server.infrastructure.oauth.kakao.dto.OIDCPublicKeysResponse; @@ -26,7 +29,6 @@ public class KakaoOauthHelper { /** link * */ public String getKaKaoOauthLink(String referer) { - // TODO : 프론트 콜백 URL 알아내면 바꾸기 return kakaoOauthProperties.getBaseUrl() + String.format( KAKAO_OAUTH_QUERY_STRING, @@ -38,13 +40,12 @@ public String getKaKaoOauthLinkDev() { return kakaoOauthProperties.getBaseUrl() + String.format( KAKAO_OAUTH_QUERY_STRING, - kakaoOauthProperties.getClientId(), + kakaoOauthProperties.getWebClientId(), kakaoOauthProperties.getRedirectUrl()); } /** token * */ public KakaoTokenResponse getKakaoOauthToken(String code, String referer) { - // TODO : 프론트 콜백 URL 알아내면 바꾸기 return kakaoOauthClient.kakaoAuth( kakaoOauthProperties.getClientId(), referer + "kakao/callback", @@ -54,7 +55,7 @@ public KakaoTokenResponse getKakaoOauthToken(String code, String referer) { public KakaoTokenResponse getKakaoOauthTokenDev(String code) { return kakaoOauthClient.kakaoAuth( - kakaoOauthProperties.getClientId(), + kakaoOauthProperties.getWebClientId(), kakaoOauthProperties.getRedirectUrl(), code, kakaoOauthProperties.getClientSecret()); @@ -66,6 +67,11 @@ public OauthInfo getKakaoOauthInfoByIdToken(String idToken) { return OauthInfo.of(OauthProvider.KAKAO, oidcDecodePayload.getSub()); } + public OauthInfo getKakaoOauthInfoByIdTokenDev(String idToken) { + OIDCDecodePayload oidcDecodePayload = getOIDCDecodePayloadDev(idToken); + return OauthInfo.of(OauthProvider.KAKAO, oidcDecodePayload.getSub()); + } + /** oidc decode * */ public OIDCDecodePayload getOIDCDecodePayload(String token) { OIDCPublicKeysResponse oidcPublicKeysResponse = kakaoOauthClient.getKakaoOIDCOpenKeys(); @@ -76,6 +82,15 @@ public OIDCDecodePayload getOIDCDecodePayload(String token) { oidcPublicKeysResponse); } + public OIDCDecodePayload getOIDCDecodePayloadDev(String token) { + OIDCPublicKeysResponse oidcPublicKeysResponse = kakaoOauthClient.getKakaoOIDCOpenKeys(); + return oauthOIDCHelper.getPayloadFromIdToken( + token, + kakaoOauthProperties.getBaseUrl(), + kakaoOauthProperties.getWebAppId(), + oidcPublicKeysResponse); + } + /** kakao측 회원 탈퇴 * */ public void withdrawKakaoOauthUser(String oid) { String kakaoAdminKey = kakaoOauthProperties.getAdminKey(); @@ -88,4 +103,11 @@ public void withdrawKakaoOauthUser(String oid) { unlinkKaKaoTarget.getAud()); kakaoInfoClient.unlinkUser(header, unlinkKaKaoTarget); } + + /** 유저 정보 가져오기 * */ + public OauthUserInfoDto getUserInfo(String oauthAccessToken) { + KakaoInformationResponse response = + kakaoInfoClient.kakaoUserInfo(BEARER + oauthAccessToken); + return OauthUserInfoDto.fromKakao(response); + } } diff --git a/Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java b/Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java index b76acbbb..62c9b57d 100644 --- a/Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java +++ b/Api/src/main/java/allchive/server/api/auth/service/helper/OauthHelper.java @@ -1,6 +1,7 @@ package allchive.server.api.auth.service.helper; +import allchive.server.api.auth.model.dto.OauthUserInfoDto; import allchive.server.api.auth.model.dto.response.OauthLoginLinkResponse; import allchive.server.api.auth.model.dto.response.OauthTokenResponse; import allchive.server.core.annotation.Helper; @@ -17,72 +18,80 @@ public class OauthHelper { /** oauth link 가져오기 * */ public OauthLoginLinkResponse getOauthLinkDev(OauthProvider provider) { - switch (provider) { - case KAKAO: - return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkDev()); - case APPLE: - return new OauthLoginLinkResponse(appleOauthHelper.getAppleOauthLinkDev()); - default: - throw InvalidOauthProviderException.EXCEPTION; - } + return switch (provider) { + case KAKAO -> new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLinkDev()); + case APPLE -> new OauthLoginLinkResponse(appleOauthHelper.getAppleOauthLinkDev()); + default -> throw InvalidOauthProviderException.EXCEPTION; + }; } public OauthLoginLinkResponse getOauthLink(OauthProvider provider, String referer) { - switch (provider) { - case KAKAO: - return new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer)); - case APPLE: - return new OauthLoginLinkResponse(appleOauthHelper.getAppleOAuthLink(referer)); - default: - throw InvalidOauthProviderException.EXCEPTION; - } + return switch (provider) { + case KAKAO -> new OauthLoginLinkResponse(kakaoOauthHelper.getKaKaoOauthLink(referer)); + case APPLE -> new OauthLoginLinkResponse(appleOauthHelper.getAppleOAuthLink(referer)); + default -> throw InvalidOauthProviderException.EXCEPTION; + }; } /** idtoken 가져오기 * */ public OauthTokenResponse getCredential(OauthProvider provider, String code, String referer) { - switch (provider) { - case KAKAO: - return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthToken(code, referer)); - case APPLE: - return OauthTokenResponse.from(appleOauthHelper.getAppleOAuthToken(code, referer)); - default: - throw InvalidOauthProviderException.EXCEPTION; - } + return switch (provider) { + case KAKAO -> OauthTokenResponse.from( + kakaoOauthHelper.getKakaoOauthToken(code, referer)); + case APPLE -> OauthTokenResponse.from( + appleOauthHelper.getAppleOAuthToken(code, referer)); + default -> throw InvalidOauthProviderException.EXCEPTION; + }; } public OauthTokenResponse getCredentialDev(OauthProvider provider, String code) { - switch (provider) { - case KAKAO: - return OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthTokenDev(code)); - case APPLE: - return OauthTokenResponse.from(appleOauthHelper.getAppleOAuthTokenDev(code)); - default: - throw InvalidOauthProviderException.EXCEPTION; - } + return switch (provider) { + case KAKAO -> OauthTokenResponse.from(kakaoOauthHelper.getKakaoOauthTokenDev(code)); + case APPLE -> OauthTokenResponse.from(appleOauthHelper.getAppleOAuthTokenDev(code)); + default -> throw InvalidOauthProviderException.EXCEPTION; + }; } /** idtoken 분석 * */ public OauthInfo getOauthInfo(OauthProvider provider, String idToken) { + return switch (provider) { + case KAKAO -> kakaoOauthHelper.getKakaoOauthInfoByIdToken(idToken); + case APPLE -> appleOauthHelper.getAppleOAuthInfoByIdToken(idToken); + default -> throw InvalidOauthProviderException.EXCEPTION; + }; + } + + public OauthInfo getOauthInfoDev(OauthProvider provider, String idToken) { + return switch (provider) { + case KAKAO -> kakaoOauthHelper.getKakaoOauthInfoByIdTokenDev(idToken); + case APPLE -> appleOauthHelper.getAppleOAuthInfoByIdTokenDev(idToken); + default -> throw InvalidOauthProviderException.EXCEPTION; + }; + } + + /** 회원탈퇴 * */ + public void withdraw( + OauthProvider provider, String oid, String appleAccessToken, String referer) { switch (provider) { - case KAKAO: - return kakaoOauthHelper.getKakaoOauthInfoByIdToken(idToken); - case APPLE: - return appleOauthHelper.getAppleOAuthInfoByIdToken(idToken); - default: - throw InvalidOauthProviderException.EXCEPTION; + case KAKAO -> kakaoOauthHelper.withdrawKakaoOauthUser(oid); + case APPLE -> appleOauthHelper.withdrawAppleOauthUser(appleAccessToken, referer); + default -> throw InvalidOauthProviderException.EXCEPTION; } } - /** 회원탈퇴 * */ - public void withdraw(OauthProvider provider, String oid, String appleAccessToken) { + public void withdrawDev(OauthProvider provider, String oid, String appleAccessToken) { switch (provider) { - case KAKAO: - kakaoOauthHelper.withdrawKakaoOauthUser(oid); - break; - case APPLE: - appleOauthHelper.withdrawAppleOauthUser(appleAccessToken); - default: - throw InvalidOauthProviderException.EXCEPTION; + case KAKAO -> kakaoOauthHelper.withdrawKakaoOauthUser(oid); + case APPLE -> appleOauthHelper.withdrawAppleOauthUserDev(appleAccessToken); + default -> throw InvalidOauthProviderException.EXCEPTION; } } + + /** 유저 정보 가져오기 * */ + public OauthUserInfoDto getUserInfo(OauthProvider provider, String oauthAccessToken) { + return switch (provider) { + case KAKAO -> kakaoOauthHelper.getUserInfo(oauthAccessToken); + default -> throw InvalidOauthProviderException.EXCEPTION; + }; + } } diff --git a/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java index fed85f8a..d8200001 100644 --- a/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java +++ b/Api/src/main/java/allchive/server/api/block/service/CreateBlockUseCase.java @@ -10,12 +10,14 @@ import allchive.server.domain.domains.block.service.BlockDomainService; import allchive.server.domain.domains.block.validator.BlockValidator; 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 public class CreateBlockUseCase { + private final UserValidator userValidator; private final BlockValidator blockValidator; private final BlockMapper blockMapper; private final BlockDomainService blockDomainService; @@ -24,10 +26,15 @@ public class CreateBlockUseCase { @Transactional public BlockResponse execute(BlockRequest request) { Long userId = SecurityUtil.getCurrentUserId(); - blockValidator.validateNotDuplicate(userId, request.getUserId()); - blockValidator.validateNotMyself(userId, request.getUserId()); + validateExecution(userId, request); Block block = blockMapper.toEntity(userId, request.getUserId()); blockDomainService.save(block); return BlockResponse.from(userAdaptor.findById(request.getUserId()).getNickname()); } + + private void validateExecution(Long userId, BlockRequest request) { + userValidator.validateExist(request.getUserId()); + blockValidator.validateNotDuplicate(userId, request.getUserId()); + blockValidator.validateNotMyself(userId, request.getUserId()); + } } 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 257ad370..f9b93d56 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 @@ -8,12 +8,14 @@ import allchive.server.domain.domains.block.service.BlockDomainService; import allchive.server.domain.domains.block.validator.BlockValidator; 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 public class DeleteBlockUseCase { + private final UserValidator userValidator; private final BlockValidator blockValidator; private final BlockDomainService blockDomainService; private final UserAdaptor userAdaptor; @@ -21,8 +23,13 @@ public class DeleteBlockUseCase { @Transactional public BlockResponse execute(BlockRequest request) { Long userId = SecurityUtil.getCurrentUserId(); - blockValidator.validateExist(userId, request.getUserId()); + validateExecution(userId, request); blockDomainService.deleteByBlockFromAndBlockUser(userId, request.getUserId()); return BlockResponse.from(userAdaptor.findById(request.getUserId()).getNickname()); } + + private void validateExecution(Long userId, BlockRequest request) { + userValidator.validateExist(request.getUserId()); + blockValidator.validateExist(userId, request.getUserId()); + } } diff --git a/Api/src/main/java/allchive/server/api/common/json/naming/UpperCaseStrategy.java b/Api/src/main/java/allchive/server/api/common/json/naming/UpperCaseStrategy.java new file mode 100644 index 00000000..d6587bed --- /dev/null +++ b/Api/src/main/java/allchive/server/api/common/json/naming/UpperCaseStrategy.java @@ -0,0 +1,20 @@ +package allchive.server.api.common.json.naming; + + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; + +public class UpperCaseStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase { + @Override + public String translate(String input) { + if (input == null || input.isEmpty()) { + return input; + } + StringBuilder sb = new StringBuilder(input); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + char uc = Character.toUpperCase(c); + sb.setCharAt(i, uc); + } + return sb.toString(); + } +} diff --git a/Api/src/main/java/allchive/server/api/common/page/PageResponse.java b/Api/src/main/java/allchive/server/api/common/page/PageResponse.java new file mode 100644 index 00000000..1617d1c5 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/common/page/PageResponse.java @@ -0,0 +1,23 @@ +package allchive.server.api.common.page; + + +import java.util.List; +import org.springframework.data.domain.Page; + +public record PageResponse( + List content, + int page, + int size, + long totalElements, + int totalPages, + boolean hasNextPage) { + public static PageResponse of(Page page) { + return new PageResponse<>( + page.getContent(), + page.getNumber(), + page.getNumberOfElements(), + page.getTotalElements(), + page.getTotalPages(), + page.hasNext()); + } +} 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 new file mode 100644 index 00000000..f4203056 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/common/util/StringParamUtil.java @@ -0,0 +1,14 @@ +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/UrlUtil.java b/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java index 0500a20d..ef4fbc07 100644 --- a/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java +++ b/Api/src/main/java/allchive/server/api/common/util/UrlUtil.java @@ -16,13 +16,20 @@ private UrlUtil(SpringEnvironmentHelper springEnvironmentHelper) { } public static String toAssetUrl(String key) { - if (springEnvironmentHelper.isProdProfile()) { - return PROD_ASSET_URL + key; - } - return STAGING_ASSET_URL + key; + // if (key.equals("")) { + // return ""; + // } + // if (springEnvironmentHelper.isProdProfile()) { + // return PROD_ASSET_URL + key; + // } + // return STAGING_ASSET_URL + key; + return key; } public static String convertUrlToKey(String url) { + if (url.equals("")) { + return ""; + } if (validateUrl(url)) { return url.split("/", 4)[3]; } diff --git a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java index cfd71a98..40746326 100644 --- a/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java +++ b/Api/src/main/java/allchive/server/api/config/security/SecurityConfig.java @@ -2,14 +2,19 @@ import static allchive.server.core.consts.AllchiveConst.SwaggerPatterns; +import allchive.server.core.helper.SpringEnvironmentHelper; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; @@ -17,6 +22,23 @@ @EnableWebSecurity() public class SecurityConfig { private final FilterConfig filterConfig; + private final SpringEnvironmentHelper springEnvironmentHelper; + + @Value("${swagger.user}") + private String swaggerUser; + + @Value("${swagger.password}") + private String swaggerPassword; + + @Bean + public InMemoryUserDetailsManager userDetailsService() { + UserDetails user = + User.withUsername(swaggerUser) + .password(passwordEncoder().encode(swaggerPassword)) + .roles("SWAGGER") + .build(); + return new InMemoryUserDetailsManager(user); + } @Bean public PasswordEncoder passwordEncoder() { @@ -30,6 +52,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.authorizeRequests().expressionHandler(expressionHandler()); + if (springEnvironmentHelper.isProdAndDevProfile()) { + http.authorizeRequests().mvcMatchers(SwaggerPatterns).authenticated().and().httpBasic(); + } + http.authorizeRequests() .antMatchers(SwaggerPatterns) .permitAll() @@ -39,6 +65,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .permitAll() .mvcMatchers("/auth/token/refresh") .permitAll() + .mvcMatchers("/user/image") + .permitAll() .mvcMatchers("/**/health/**") .permitAll() .mvcMatchers("/example/**") diff --git a/Api/src/main/java/allchive/server/api/content/controller/ContentController.java b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java index 284e286e..e9357f64 100644 --- a/Api/src/main/java/allchive/server/api/content/controller/ContentController.java +++ b/Api/src/main/java/allchive/server/api/content/controller/ContentController.java @@ -3,11 +3,9 @@ import allchive.server.api.content.model.dto.request.CreateContentRequest; import allchive.server.api.content.model.dto.request.UpdateContentRequest; +import allchive.server.api.content.model.dto.response.ContentTagInfoResponse; import allchive.server.api.content.model.dto.response.ContentTagResponse; -import allchive.server.api.content.service.CreateContentUseCase; -import allchive.server.api.content.service.DeleteContentUseCase; -import allchive.server.api.content.service.GetContentUseCase; -import allchive.server.api.content.service.UpdateContentUseCase; +import allchive.server.api.content.service.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -24,6 +22,7 @@ public class ContentController { private final GetContentUseCase getContentUseCase; private final DeleteContentUseCase deleteContentUseCase; private final UpdateContentUseCase updateContentUseCase; + private final GetContentInfoUseCase getContentInfoUseCase; @Operation(summary = "컨텐츠를 생성합니다.") @PostMapping() @@ -33,7 +32,7 @@ public void createContent(@RequestBody CreateContentRequest createContentRequest @Operation(summary = "컨텐츠 내용을 가져옵니다.") @GetMapping(value = "/{contentId}") - public ContentTagResponse createContent(@PathVariable Long contentId) { + public ContentTagResponse getContent(@PathVariable Long contentId) { return getContentUseCase.execute(contentId); } @@ -49,4 +48,10 @@ public void updateContent( @PathVariable Long contentId, @RequestBody UpdateContentRequest request) { updateContentUseCase.execute(contentId, request); } + + @Operation(summary = "컨텐츠 정보 수정시 보여줄 정보를 가져옵니다.") + @GetMapping(value = "/{contentId}/info") + public ContentTagInfoResponse getContentInfo(@PathVariable Long contentId) { + return getContentInfoUseCase.execute(contentId); + } } diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java b/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java index ead4b71f..00eddb9b 100644 --- a/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java +++ b/Api/src/main/java/allchive/server/api/content/model/dto/request/CreateContentRequest.java @@ -9,7 +9,7 @@ @Getter public class CreateContentRequest { - @Schema(defaultValue = "image", description = "컨텐츠 타입") + @Schema(defaultValue = "IMAGE", description = "컨텐츠 타입") @ValidEnum(target = ContentType.class) private ContentType contentType; diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java b/Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java index 0298614e..9d29def3 100644 --- a/Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java +++ b/Api/src/main/java/allchive/server/api/content/model/dto/request/UpdateContentRequest.java @@ -9,7 +9,7 @@ @Getter public class UpdateContentRequest { - @Schema(defaultValue = "image", description = "컨텐츠 타입") + @Schema(defaultValue = "IMAGE", description = "컨텐츠 타입") @ValidEnum(target = ContentType.class) private ContentType contentType; diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagInfoResponse.java b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagInfoResponse.java new file mode 100644 index 00000000..1ab6ba09 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagInfoResponse.java @@ -0,0 +1,96 @@ +package allchive.server.api.content.model.dto.response; + + +import allchive.server.api.common.util.UrlUtil; +import allchive.server.api.tag.model.dto.response.TagResponse; +import allchive.server.core.annotation.DateFormat; +import allchive.server.domain.domains.archiving.domain.Archiving; +import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ContentTagInfoResponse { + @Schema(description = "아카이빙 고유번호") + private Long archivingId; + + @Schema(description = "아카이빙 이름") + private String archivingTitle; + + @Schema(description = "컨텐츠 고유번호") + private Long contentId; + + @Schema(description = "컨텐츠 제목") + private String contentTitle; + + @Schema(description = "컨텐츠 종류") + private ContentType contentType; + + @Schema(description = "메모") + private String contentMemo; + + @Schema(defaultValue = "컨텐츠 링크", description = "컨텐츠 링크") + private String link; + + @Schema(defaultValue = "컨텐츠 이미지 url", description = "컨텐츠 이미지 url") + private String imgUrl; + + @Schema( + type = "string", + pattern = "yyyy.MM.dd", + defaultValue = "2023.07.02", + description = "컨텐츠 생성일자") + @DateFormat + private LocalDateTime contentCreatedAt; + + private List tagList; + + private Boolean isMine; + + @Builder + private ContentTagInfoResponse( + Long archivingId, + String archivingTitle, + Long contentId, + String contentTitle, + ContentType contentType, + String contentMemo, + String link, + String imgUrl, + LocalDateTime contentCreatedAt, + List tagList, + Boolean isMine) { + this.archivingId = archivingId; + this.archivingTitle = archivingTitle; + this.contentId = contentId; + this.contentTitle = contentTitle; + this.contentType = contentType; + this.contentMemo = contentMemo; + this.link = link; + this.imgUrl = imgUrl; + this.contentCreatedAt = contentCreatedAt; + this.tagList = tagList; + this.isMine = isMine; + } + + public static ContentTagInfoResponse of( + Archiving archiving, Content content, List tagList, Boolean isMine) { + return ContentTagInfoResponse.builder() + .archivingId(archiving.getId()) + .archivingTitle(archiving.getTitle()) + .contentId(content.getId()) + .contentTitle(content.getTitle()) + .contentType(content.getContentType()) + .contentMemo(content.getMemo()) + .link(content.getLinkUrl()) + .imgUrl(UrlUtil.toAssetUrl(content.getImageUrl())) + .contentCreatedAt(content.getCreatedAt()) + .tagList(tagList) + .isMine(isMine) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java index 398b5259..e9cc6bf8 100644 --- a/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java +++ b/Api/src/main/java/allchive/server/api/content/model/dto/response/ContentTagResponse.java @@ -42,6 +42,10 @@ public class ContentTagResponse { private List tagList; + @Schema(description = "컨텐츠 소유자 고유번호") + private Long ownerId; + + @Schema(description = "유저 소유 여부") private Boolean isMine; @Builder @@ -54,6 +58,7 @@ private ContentTagResponse( String imgUrl, LocalDateTime contentCreatedAt, List tagList, + Long ownerId, Boolean isMine) { this.contentId = contentId; this.contentTitle = contentTitle; @@ -63,11 +68,12 @@ private ContentTagResponse( this.imgUrl = imgUrl; this.contentCreatedAt = contentCreatedAt; this.tagList = tagList; + this.ownerId = ownerId; this.isMine = isMine; } public static ContentTagResponse of( - Content content, List tagList, Boolean isMine) { + Content content, List tagList, Boolean isMine, Long ownerId) { return ContentTagResponse.builder() .contentId(content.getId()) .contentTitle(content.getTitle()) @@ -77,6 +83,7 @@ public static ContentTagResponse of( .imgUrl(UrlUtil.toAssetUrl(content.getImageUrl())) .contentCreatedAt(content.getCreatedAt()) .tagList(tagList) + .ownerId(ownerId) .isMine(isMine) .build(); } diff --git a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java index f4dda5ed..987503bb 100644 --- a/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java +++ b/Api/src/main/java/allchive/server/api/content/model/mapper/ContentMapper.java @@ -4,9 +4,11 @@ import allchive.server.api.common.util.UrlUtil; import allchive.server.api.content.model.dto.request.CreateContentRequest; import allchive.server.api.content.model.dto.response.ContentResponse; +import allchive.server.api.content.model.dto.response.ContentTagInfoResponse; import allchive.server.api.content.model.dto.response.ContentTagResponse; import allchive.server.api.tag.model.dto.response.TagResponse; import allchive.server.core.annotation.Mapper; +import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; import allchive.server.domain.domains.content.domain.Tag; @@ -36,15 +38,30 @@ public Content toEntity(CreateContentRequest request) { } public ContentTagResponse toContentTagResponse( - Content content, List contentTagGroupList, Boolean isMine) { + Content content, + List contentTagGroupList, + Boolean isMine, + Long ownerId) { List tagResponseList = contentTagGroupList.stream() .map(contentTagGroup -> TagResponse.from(contentTagGroup.getTag())) .toList(); - return ContentTagResponse.of(content, tagResponseList, isMine); + return ContentTagResponse.of(content, tagResponseList, isMine, ownerId); } public List toContentTagGroupEntityList(Content content, List tags) { return tags.stream().map(tag -> ContentTagGroup.of(content, tag)).toList(); } + + public ContentTagInfoResponse toContentTagInfoResponse( + Archiving archiving, + Content content, + List contentTagGroupList, + Boolean isMine) { + List tagResponseList = + contentTagGroupList.stream() + .map(contentTagGroup -> TagResponse.from(contentTagGroup.getTag())) + .toList(); + return ContentTagInfoResponse.of(archiving, content, tagResponseList, isMine); + } } diff --git a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java index 2afd7967..b590ab8c 100644 --- a/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/CreateContentUseCase.java @@ -1,10 +1,12 @@ package allchive.server.api.content.service; +import static allchive.server.core.consts.AllchiveConst.PLUS_ONE; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.content.model.dto.request.CreateContentRequest; import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Content; @@ -26,6 +28,7 @@ public class CreateContentUseCase { private final TagValidator tagValidator; private final TagAdaptor tagAdaptor; private final ContentTagGroupDomainService contentTagGroupDomainService; + private final ArchivingDomainService archivingDomainService; @Transactional public void execute(CreateContentRequest request) { @@ -33,12 +36,13 @@ public void execute(CreateContentRequest request) { Content content = contentMapper.toEntity(request); createContentTagGroup(content, request.getTagIds()); contentDomainService.save(content); + archivingDomainService.updateContentCnt( + request.getArchivingId(), request.getContentType(), PLUS_ONE); } private void validateExecution(CreateContentRequest request) { Long userId = SecurityUtil.getCurrentUserId(); - archivingValidator.validateExistById(request.getArchivingId()); - archivingValidator.validateArchivingUser(request.getArchivingId(), userId); + archivingValidator.verifyUser(userId, request.getArchivingId()); tagValidator.validateExistTagsAndUser(request.getTagIds(), userId); } 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 97c4043c..7534bbc3 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 @@ -1,9 +1,13 @@ package allchive.server.api.content.service; +import static allchive.server.core.consts.AllchiveConst.MINUS_ONE; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.recycle.model.mapper.RecycleMapper; import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; +import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.service.ContentDomainService; import allchive.server.domain.domains.content.validator.ContentValidator; import allchive.server.domain.domains.recycle.domain.Recycle; @@ -19,6 +23,8 @@ public class DeleteContentUseCase { private final ContentDomainService contentDomainService; private final RecycleMapper recycleMapper; private final RecycleDomainService recycleDomainService; + private final ArchivingDomainService archivingDomainService; + private final ContentAdaptor contentAdaptor; @Transactional public void execute(Long contentId) { @@ -26,10 +32,12 @@ public void execute(Long contentId) { validateExecution(contentId, userId); contentDomainService.softDeleteById(contentId); createRecycle(userId, contentId); + decreaseArchivingCount(contentId); } private void validateExecution(Long contentId, Long userId) { contentValidator.verifyUser(contentId, userId); + contentValidator.validateNotDelete(contentId); } private void createRecycle(Long userId, Long contentId) { @@ -37,4 +45,10 @@ private void createRecycle(Long userId, Long contentId) { recycleMapper.toContentRecycleEntity(userId, contentId, RecycleType.CONTENT); recycleDomainService.save(recycle); } + + private void decreaseArchivingCount(Long contentId) { + Content content = contentAdaptor.findById(contentId); + archivingDomainService.updateContentCnt( + content.getArchivingId(), content.getContentType(), MINUS_ONE); + } } diff --git a/Api/src/main/java/allchive/server/api/content/service/GetContentInfoUseCase.java b/Api/src/main/java/allchive/server/api/content/service/GetContentInfoUseCase.java new file mode 100644 index 00000000..1229a76c --- /dev/null +++ b/Api/src/main/java/allchive/server/api/content/service/GetContentInfoUseCase.java @@ -0,0 +1,52 @@ +package allchive.server.api.content.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.content.model.dto.response.ContentTagInfoResponse; +import allchive.server.api.content.model.mapper.ContentMapper; +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.ContentAdaptor; +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.validator.ContentValidator; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class GetContentInfoUseCase { + private final ContentValidator contentValidator; + private final ContentAdaptor contentAdaptor; + private final ContentTagGroupAdaptor contentTagGroupAdaptor; + private final ContentMapper contentMapper; + private final ArchivingAdaptor archivingAdaptor; + + @Transactional(readOnly = true) + public ContentTagInfoResponse execute(Long contentId) { + Long userId = SecurityUtil.getCurrentUserId(); + validateExecution(contentId, userId); + Content content = contentAdaptor.findById(contentId); + Archiving archiving = archivingAdaptor.findById(content.getArchivingId()); + List contentTagGroupList = + contentTagGroupAdaptor.queryContentTagGroupByContentWithTag(content); + Boolean isMine = calculateIsMine(archiving, userId); + return contentMapper.toContentTagInfoResponse( + archiving, content, contentTagGroupList, isMine); + } + + private void validateExecution(Long contentId, Long userId) { + contentValidator.validatePublic(contentId, userId); + contentValidator.verifyUser(contentId, userId); + } + + private Boolean calculateIsMine(Archiving archiving, Long userId) { + if (archiving.getUserId().equals(userId)) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } +} diff --git a/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java index e61fc1d7..819cac37 100644 --- a/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java +++ b/Api/src/main/java/allchive/server/api/content/service/GetContentUseCase.java @@ -7,11 +7,11 @@ 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.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; 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.validator.ContentValidator; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -19,7 +19,7 @@ @UseCase @RequiredArgsConstructor public class GetContentUseCase { - private final ArchivingValidator archivingValidator; + private final ContentValidator contentValidator; private final ContentAdaptor contentAdaptor; private final ContentTagGroupAdaptor contentTagGroupAdaptor; private final ContentMapper contentMapper; @@ -28,16 +28,17 @@ public class GetContentUseCase { @Transactional(readOnly = true) public ContentTagResponse execute(Long contentId) { Long userId = SecurityUtil.getCurrentUserId(); + validateExecution(contentId, userId); Content content = contentAdaptor.findById(contentId); - validateExecution(content.getArchivingId(), userId); + Long ownerId = archivingAdaptor.findById(content.getArchivingId()).getUserId(); List contentTagGroupList = contentTagGroupAdaptor.queryContentTagGroupByContentWithTag(content); Boolean isMine = calculateIsMine(content.getArchivingId(), userId); - return contentMapper.toContentTagResponse(content, contentTagGroupList, isMine); + return contentMapper.toContentTagResponse(content, contentTagGroupList, isMine, ownerId); } - private void validateExecution(Long archivingId, Long userId) { - archivingValidator.validatePublicStatus(archivingId, userId); + private void validateExecution(Long contentId, Long userId) { + contentValidator.validatePublic(contentId, userId); } private Boolean calculateIsMine(Long archivingId, Long userId) { 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 b2286199..480d82ec 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 @@ -1,16 +1,21 @@ package allchive.server.api.content.service; +import static allchive.server.core.consts.AllchiveConst.MINUS_ONE; +import static allchive.server.core.consts.AllchiveConst.PLUS_ONE; import allchive.server.api.common.util.UrlUtil; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.content.model.dto.request.UpdateContentRequest; import allchive.server.api.content.model.mapper.ContentMapper; import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; +import allchive.server.domain.domains.archiving.service.ArchivingDomainService; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; import allchive.server.domain.domains.content.domain.Tag; +import allchive.server.domain.domains.content.domain.enums.ContentType; import allchive.server.domain.domains.content.service.ContentDomainService; import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; import allchive.server.domain.domains.content.validator.ContentValidator; @@ -29,14 +34,16 @@ public class UpdateContentUseCase { private final ContentMapper contentMapper; private final ContentDomainService contentDomainService; private final ContentTagGroupDomainService contentTagGroupDomainService; + private final ArchivingDomainService archivingDomainService; + private final ArchivingAdaptor archivingAdaptor; @Transactional public void execute(Long contentId, UpdateContentRequest request) { validateExecution(contentId, request); regenerateContentTagGroup(contentId, request.getTagIds()); + updateArchiving(contentId, request.getArchivingId(), request.getContentType()); contentDomainService.update( contentId, - request.getContentType(), request.getArchivingId(), request.getLink(), request.getMemo(), @@ -44,6 +51,15 @@ public void execute(Long contentId, UpdateContentRequest request) { request.getTitle()); } + private void updateArchiving(Long contentId, Long newArchivingId, ContentType contentType) { + Content content = contentAdaptor.findById(contentId); + if (!content.getArchivingId().equals(newArchivingId)) { + archivingDomainService.updateContentCnt( + content.getArchivingId(), content.getContentType(), MINUS_ONE); + archivingDomainService.updateContentCnt(newArchivingId, contentType, PLUS_ONE); + } + } + private void validateExecution(Long contentId, UpdateContentRequest request) { Long userId = SecurityUtil.getCurrentUserId(); contentValidator.verifyUser(contentId, userId); diff --git a/Api/src/main/java/allchive/server/api/image/controller/ImageController.java b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java index 8077a4d4..5d3257d1 100644 --- a/Api/src/main/java/allchive/server/api/image/controller/ImageController.java +++ b/Api/src/main/java/allchive/server/api/image/controller/ImageController.java @@ -4,7 +4,7 @@ import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.image.model.dto.response.ImageUrlResponse; import allchive.server.infrastructure.s3.PresignedType; -import allchive.server.infrastructure.s3.S3PresignedUrlService; +import allchive.server.infrastructure.s3.service.S3PresignedUrlService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -38,8 +38,6 @@ public ImageUrlResponse getContentPresignedUrl() { @Operation(summary = "컨텐츠 관련 이미지 업로드 url 요청할수 있는 api 입니다.") @GetMapping(value = "/user/image") public ImageUrlResponse getUserPresignedUrl() { - Long userId = SecurityUtil.getCurrentUserId(); - return ImageUrlResponse.from( - s3PresignedUrlService.getPreSignedUrl(userId, PresignedType.USER)); + return ImageUrlResponse.from(s3PresignedUrlService.getPreSignedUrl(0L, PresignedType.USER)); } } 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 453ecc90..85097dbe 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 @@ -8,12 +8,15 @@ import allchive.server.domain.domains.archiving.validator.ArchivingValidator; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; import allchive.server.domain.domains.content.service.ContentDomainService; import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; import allchive.server.domain.domains.content.validator.ContentValidator; import allchive.server.domain.domains.recycle.service.RecycleDomainService; 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.service.S3DeleteObjectService; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; @@ -31,15 +34,36 @@ public class ClearDeletedObjectUseCase { private final ArchivingDomainService archivingDomainService; private final ContentDomainService contentDomainService; private final RecycleDomainService recycleDomainService; + private final ReportDomainService reportDomainService; + private final S3DeleteObjectService s3DeleteObjectService; - // TODO: report 지우기 @Transactional public void execute(ClearDeletedObjectRequest request) { Long userId = SecurityUtil.getCurrentUserId(); - recycleValidator.validateExist(request.getArchivingIds(), request.getContentIds(), userId); - archivingValidator.verifyUserInIdList(userId, request.getArchivingIds()); - contentValidator.verifyUserInIdList(userId, request.getContentIds()); + validateExecution(userId, request); List contents = contentAdaptor.findAllByArchivingIds(request.getArchivingIds()); + List contentsId = getContentsId(contents, request); + scrapDomainService.deleteAllByArchivingIdIn(request.getArchivingIds()); + contentTagGroupDomainService.deleteByContentIn(contents); + contentDomainService.deleteAllById(contentsId); + archivingDomainService.deleteAllById(request.getArchivingIds()); + recycleDomainService.deleteAllByUserIdAndArchivingIdInOrUserIdAndContentIdIn( + request.getArchivingIds(), request.getContentIds(), userId); + reportDomainService.deleteAllByArchivingIdInOrContentIdIn( + request.getArchivingIds(), request.getContentIds()); + deleteS3Object(contents); + } + + private void deleteS3Object(List contents) { + List imageKeys = + contents.stream() + .filter(content -> content.getContentType().equals(ContentType.IMAGE)) + .map(Content::getImageUrl) + .toList(); + s3DeleteObjectService.deleteS3Object(imageKeys); + } + + private List getContentsId(List contents, ClearDeletedObjectRequest request) { List contentsId = contents.stream().map(Content::getId).toList(); if (!request.getContentIds().isEmpty()) { if (contentsId.isEmpty()) { @@ -47,11 +71,12 @@ public void execute(ClearDeletedObjectRequest request) { } contentsId.addAll(request.getContentIds()); } - scrapDomainService.deleteAllByArchivingIdIn(request.getArchivingIds()); - contentTagGroupDomainService.deleteByContentIn(contents); - contentDomainService.deleteAllById(contentsId); - archivingDomainService.deleteAllById(request.getArchivingIds()); - recycleDomainService.deleteAllByUserIdAndArchivingIdOrUserIdAndContentId( - request.getArchivingIds(), request.getContentIds(), userId); + return contentsId; + } + + private void validateExecution(Long userId, ClearDeletedObjectRequest request) { + recycleValidator.validateExist(request.getArchivingIds(), request.getContentIds(), userId); + archivingValidator.verifyUserInIdList(userId, request.getArchivingIds()); + contentValidator.verifyUserInIdList(userId, request.getContentIds()); } } 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 ed855e2f..c673c9aa 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 @@ -5,13 +5,16 @@ import allchive.server.domain.domains.archiving.service.ArchivingDomainService; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.domain.Content; +import allchive.server.domain.domains.content.domain.enums.ContentType; import allchive.server.domain.domains.content.service.ContentDomainService; import allchive.server.domain.domains.content.service.ContentTagGroupDomainService; import allchive.server.domain.domains.recycle.adaptor.RecycleAdaptor; import allchive.server.domain.domains.recycle.domain.Recycle; import allchive.server.domain.domains.recycle.domain.enums.RecycleType; 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.service.S3DeleteObjectService; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -31,6 +34,8 @@ public class ClearOldDeletedObjectUseCase { private final ArchivingDomainService archivingDomainService; private final ContentDomainService contentDomainService; private final RecycleDomainService recycleDomainService; + private final ReportDomainService reportDomainService; + private final S3DeleteObjectService s3DeleteObjectService; /** 삭제 후 30일 지난 항목 제거 스케쥴러 매일 02:30에 수행 */ @Scheduled(cron = "0 30 2 * * *") @@ -39,12 +44,36 @@ public void executeSchedule() { log.info("scheduler on"); LocalDateTime deleteStandard = LocalDateTime.now().minusDays(30); List recycles = recycleAdaptor.findAllByDeletedAtBefore(deleteStandard); + List archivingIds = getArchivingIds(recycles); + List contents = contentAdaptor.findAllByArchivingIds(archivingIds); + List contentIds = getContentsId(recycles, contents); + scrapDomainService.deleteAllByArchivingIdIn(archivingIds); + contentTagGroupDomainService.deleteByContentIn(contents); + contentDomainService.deleteAllById(contentIds); + archivingDomainService.deleteAllById(archivingIds); + recycleDomainService.deleteAll(recycles); + reportDomainService.deleteAllByArchivingIdInOrContentIdIn(archivingIds, contentIds); + deleteS3Object(contents); + log.info("scheduler off"); + } - List archivingIds = - recycles.stream() - .filter(recycle -> recycle.getRecycleType().equals(RecycleType.ARCHIVING)) - .map(Recycle::getArchivingId) + private void deleteS3Object(List contents) { + List imageKeys = + contents.stream() + .filter(content -> content.getContentType().equals(ContentType.IMAGE)) + .map(Content::getImageUrl) .toList(); + s3DeleteObjectService.deleteS3Object(imageKeys); + } + + private List getArchivingIds(List recycles) { + return recycles.stream() + .filter(recycle -> recycle.getRecycleType().equals(RecycleType.ARCHIVING)) + .map(Recycle::getArchivingId) + .toList(); + } + + private List getContentsId(List recycles, List contents) { List contentIds = recycles.stream() .filter(recycle -> recycle.getRecycleType().equals(RecycleType.CONTENT)) @@ -54,16 +83,9 @@ public void executeSchedule() { if (contentIds.isEmpty()) { contentIds = new ArrayList<>(); } - List contents = contentAdaptor.findAllByArchivingIds(archivingIds); for (Content content : contents) { contentIds.add(content.getId()); } - - scrapDomainService.deleteAllByArchivingIdIn(archivingIds); - contentTagGroupDomainService.deleteByContentIn(contents); - contentDomainService.deleteAllById(contentIds); - archivingDomainService.deleteAllById(archivingIds); - recycleDomainService.deleteAll(recycles); - log.info("scheduler off"); + return contentIds; } } diff --git a/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java b/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java index 733b5dff..f966f56f 100644 --- a/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java +++ b/Api/src/main/java/allchive/server/api/recycle/service/GetDeletedObjectUseCase.java @@ -31,21 +31,29 @@ public class GetDeletedObjectUseCase { public DeletedObjectResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); List recycles = recycleAdaptor.findAllByUserId(userId); + List archivings = getArchivings(recycles); + List contents = getContents(recycles); + List contentTagGroups = + contentTagGroupAdaptor.queryContentTagGroupByContentIn(contents); + return recycleMapper.toDeletedObjectResponse( + archivings, userId, contents, contentTagGroups); + } + + private List getArchivings(List recycles) { List archivingIds = recycles.stream() .filter(recycle -> recycle.getRecycleType().equals(RecycleType.ARCHIVING)) .map(Recycle::getArchivingId) .toList(); + return archivingAdaptor.findAllByIdIn(archivingIds); + } + + private List getContents(List recycles) { List contentIds = recycles.stream() .filter(recycle -> recycle.getRecycleType().equals(RecycleType.CONTENT)) .map(Recycle::getContentId) .toList(); - List archivings = archivingAdaptor.findAllByIdIn(archivingIds); - List contents = contentAdaptor.findAllByIdIn(contentIds); - List contentTagGroups = - contentTagGroupAdaptor.queryContentTagGroupByContentIn(contents); - return recycleMapper.toDeletedObjectResponse( - archivings, userId, contents, contentTagGroups); + return contentAdaptor.findAllByIdIn(contentIds); } } 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 aa77aa15..1219e2bf 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 @@ -1,15 +1,18 @@ package allchive.server.api.recycle.service; +import static allchive.server.core.consts.AllchiveConst.PLUS_ONE; import allchive.server.api.config.security.SecurityUtil; import allchive.server.api.recycle.model.dto.request.RestoreDeletedObjectRequest; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.archiving.service.ArchivingDomainService; import allchive.server.domain.domains.archiving.validator.ArchivingValidator; +import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.service.ContentDomainService; import allchive.server.domain.domains.content.validator.ContentValidator; import allchive.server.domain.domains.recycle.service.RecycleDomainService; import allchive.server.domain.domains.recycle.validator.RecycleValidator; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -22,16 +25,33 @@ public class RestoreDeletedObjectUseCase { private final ArchivingDomainService archivingDomainService; private final ContentDomainService contentDomainService; private final RecycleDomainService recycleDomainService; + private final ContentAdaptor contentAdaptor; @Transactional public void execute(RestoreDeletedObjectRequest request) { Long userId = SecurityUtil.getCurrentUserId(); + validateExecution(request, userId); + archivingDomainService.restoreByIdIn(request.getArchivingIds()); + contentDomainService.restoreByIdIn(request.getContentIds()); + increaseArchivingCount(request.getContentIds()); + recycleDomainService.deleteAllByUserIdAndArchivingIdInOrUserIdAndContentIdIn( + request.getArchivingIds(), request.getContentIds(), userId); + } + + private void increaseArchivingCount(List contentIds) { + contentAdaptor + .findAllByIdIn(contentIds) + .forEach( + content -> + archivingDomainService.updateContentCnt( + content.getArchivingId(), + content.getContentType(), + PLUS_ONE)); + } + + private void validateExecution(RestoreDeletedObjectRequest request, Long userId) { recycleValidator.validateExist(request.getArchivingIds(), request.getContentIds(), userId); archivingValidator.validateExistInIdList(request.getArchivingIds()); contentValidator.validateExistInIdList(request.getContentIds()); - archivingDomainService.restoreInIdList(request.getArchivingIds()); - contentDomainService.restoreInIdList(request.getContentIds()); - recycleDomainService.deleteAllByUserIdAndArchivingIdOrUserIdAndContentId( - request.getArchivingIds(), request.getContentIds(), userId); } } diff --git a/Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java b/Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java index de3d048f..a6f91ac0 100644 --- a/Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java +++ b/Api/src/main/java/allchive/server/api/report/model/dto/request/CreateReportRequest.java @@ -11,7 +11,7 @@ public class CreateReportRequest { @Schema(defaultValue = "기타기타기타기타기타", description = "사유 ('기타'에만 해당)") private String reason; - @Schema(defaultValue = "spam", description = "신고 종류") + @Schema(defaultValue = "SPAM", description = "신고 종류") @ValidEnum(target = ReportedType.class) private ReportedType reportedType; diff --git a/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java b/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java index 07681e8b..4603f830 100644 --- a/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java +++ b/Api/src/main/java/allchive/server/api/report/service/CreateReportUseCase.java @@ -30,20 +30,29 @@ public class CreateReportUseCase { @Transactional public void execute(CreateReportRequest request, ReportObjectType type) { Long userId = SecurityUtil.getCurrentUserId(); - reportValidator.validateNotDuplicateReport(userId, request.getId(), type); + validateExecution(userId, request.getId(), type); + Long reportedUserId = getReportedUserId(request.getId(), type); + Report report = reportMapper.toEntity(request, type, userId, reportedUserId); + reportDomainService.save(report); + } + + private void validateExecution(Long userId, Long objId, ReportObjectType type) { + reportValidator.validateNotDuplicateReport(userId, objId, type); + } + + private Long getReportedUserId(Long objId, ReportObjectType type) { Long reportedUserId = 0L; switch (type) { case CONTENT -> { - contentValidator.validateExistById(request.getId()); - Long archivingId = contentAdaptor.findById(request.getId()).getArchivingId(); + contentValidator.validateExistById(objId); + Long archivingId = contentAdaptor.findById(objId).getArchivingId(); reportedUserId = archivingAdaptor.findById(archivingId).getUserId(); } case ARCHIVING -> { - archivingValidator.validateExistById(request.getId()); - reportedUserId = archivingAdaptor.findById(request.getId()).getUserId(); + archivingValidator.validateExistById(objId); + reportedUserId = archivingAdaptor.findById(objId).getUserId(); } } - Report report = reportMapper.toEntity(request, type, userId, reportedUserId); - reportDomainService.save(report); + return reportedUserId; } } diff --git a/Api/src/main/java/allchive/server/api/search/controller/SearchController.java b/Api/src/main/java/allchive/server/api/search/controller/SearchController.java index d476f5a5..2f24ea7e 100644 --- a/Api/src/main/java/allchive/server/api/search/controller/SearchController.java +++ b/Api/src/main/java/allchive/server/api/search/controller/SearchController.java @@ -1,13 +1,12 @@ package allchive.server.api.search.controller; -import allchive.server.api.search.model.dto.request.SearchRequest; +import allchive.server.api.search.model.dto.request.DeleteLatestSearchRequest; import allchive.server.api.search.model.dto.response.SearchListResponse; import allchive.server.api.search.model.dto.response.SearchResponse; +import allchive.server.api.search.model.dto.response.SearchVoListResponse; import allchive.server.api.search.model.enums.ArchivingType; -import allchive.server.api.search.service.GetLatestSearchListUseCase; -import allchive.server.api.search.service.GetRelativeSearchListUseCase; -import allchive.server.api.search.service.SearchArchivingUseCase; +import allchive.server.api.search.service.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -26,25 +25,39 @@ public class SearchController { private final SearchArchivingUseCase searchArchivingUseCase; private final GetLatestSearchListUseCase getLatestSearchListUseCase; private final GetRelativeSearchListUseCase getRelativeSearchListUseCase; + private final DeleteLatestSearchUseCase deleteLatestSearchUseCase; + private final RenewalSearchDataUseCase renewalSearchDataUseCase; @Operation(summary = "검색어를 검색합니다.") - @PostMapping + @GetMapping public SearchResponse searchArchiving( @ParameterObject @PageableDefault(size = 10) Pageable pageable, @RequestParam("type") ArchivingType type, - @RequestBody SearchRequest request) { - return searchArchivingUseCase.execute(pageable, type, request); + @RequestParam("word") String word) { + return searchArchivingUseCase.execute(pageable, type, word); } @Operation(summary = "최근 검색어 목록을 가져옵니다.", description = "5개만 드릴게요") @GetMapping(value = "/latest") - public SearchListResponse getLatestSearchList() { + public SearchVoListResponse getLatestSearchList() { return getLatestSearchListUseCase.execute(); } + @Operation(summary = "최근 검색어를 삭제합니다.") + @DeleteMapping(value = "/latest") + public void deleteLatestSearch(@RequestBody DeleteLatestSearchRequest request) { + deleteLatestSearchUseCase.execute(request); + } + @Operation(summary = "검색어 자동 완성") - @PostMapping(value = "/relation") - public SearchListResponse getRelativeSearchList(@RequestBody SearchRequest request) { - return getRelativeSearchListUseCase.execute(request); + @GetMapping(value = "/relation") + public SearchListResponse getRelativeSearchList(@RequestParam("word") String word) { + return getRelativeSearchListUseCase.execute(word); + } + + @Operation(summary = "자동 완성 데이터 강제 리뉴얼", deprecated = true) + @GetMapping(value = "/relation/force") + public void forceRenewalSearchData() { + renewalSearchDataUseCase.executeForce(); } } diff --git a/Api/src/main/java/allchive/server/api/search/model/dto/request/DeleteLatestSearchRequest.java b/Api/src/main/java/allchive/server/api/search/model/dto/request/DeleteLatestSearchRequest.java new file mode 100644 index 00000000..c7c3b55d --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/model/dto/request/DeleteLatestSearchRequest.java @@ -0,0 +1,12 @@ +package allchive.server.api.search.model.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Getter; + +@Getter +public class DeleteLatestSearchRequest { + @Schema(description = "삭제할 최근 검색어 고유번호 리스트") + private List ids; +} diff --git a/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java index 7d9c7f78..bafccfef 100644 --- a/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java +++ b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchResponse.java @@ -2,34 +2,32 @@ import allchive.server.api.archiving.model.dto.response.ArchivingResponse; -import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.common.page.PageResponse; import lombok.Builder; import lombok.Getter; @Getter public class SearchResponse { - SliceResponse archivings; - SliceResponse community; + PageResponse archivings; + PageResponse community; @Builder private SearchResponse( - SliceResponse archivings, - SliceResponse community) { + PageResponse archivings, PageResponse community) { this.archivings = archivings; this.community = community; } public static SearchResponse forAll( - SliceResponse archivings, - SliceResponse community) { + PageResponse archivings, PageResponse community) { return SearchResponse.builder().archivings(archivings).community(community).build(); } - public static SearchResponse forMy(SliceResponse archivings) { + public static SearchResponse forMy(PageResponse archivings) { return SearchResponse.builder().archivings(archivings).community(null).build(); } - public static SearchResponse forCommunity(SliceResponse community) { + public static SearchResponse forCommunity(PageResponse community) { return SearchResponse.builder().archivings(null).community(community).build(); } } diff --git a/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchVoListResponse.java b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchVoListResponse.java new file mode 100644 index 00000000..b922e9c2 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/model/dto/response/SearchVoListResponse.java @@ -0,0 +1,21 @@ +package allchive.server.api.search.model.dto.response; + + +import allchive.server.api.search.model.vo.LatestSearchVo; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class SearchVoListResponse { + private List keywords; + + @Builder + private SearchVoListResponse(List keywords) { + this.keywords = keywords; + } + + public static SearchVoListResponse from(List keywords) { + return SearchVoListResponse.builder().keywords(keywords).build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/model/vo/LatestSearchVo.java b/Api/src/main/java/allchive/server/api/search/model/vo/LatestSearchVo.java new file mode 100644 index 00000000..ac251aaf --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/model/vo/LatestSearchVo.java @@ -0,0 +1,29 @@ +package allchive.server.api.search.model.vo; + + +import allchive.server.domain.domains.search.domain.LatestSearch; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class LatestSearchVo { + @Schema(description = "최근 검색 내용") + private String word; + + @Schema(description = "최근 검색 내용 고유번호") + private Long latestSearchId; + + @Builder + private LatestSearchVo(String word, Long latestSearchId) { + this.word = word; + this.latestSearchId = latestSearchId; + } + + public static LatestSearchVo from(LatestSearch latestSearch) { + return LatestSearchVo.builder() + .word(latestSearch.getKeyword()) + .latestSearchId(latestSearch.getId()) + .build(); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/service/DeleteLatestSearchUseCase.java b/Api/src/main/java/allchive/server/api/search/service/DeleteLatestSearchUseCase.java new file mode 100644 index 00000000..9637d8b5 --- /dev/null +++ b/Api/src/main/java/allchive/server/api/search/service/DeleteLatestSearchUseCase.java @@ -0,0 +1,30 @@ +package allchive.server.api.search.service; + + +import allchive.server.api.config.security.SecurityUtil; +import allchive.server.api.search.model.dto.request.DeleteLatestSearchRequest; +import allchive.server.core.annotation.UseCase; +import allchive.server.domain.domains.search.service.LatestSearchDomainService; +import allchive.server.domain.domains.search.validator.LatestSearchValidator; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class DeleteLatestSearchUseCase { + private final LatestSearchValidator latestSearchValidator; + private final LatestSearchDomainService latestSearchDomainService; + + @Transactional + public void execute(DeleteLatestSearchRequest request) { + validateExecution(request.getIds()); + latestSearchDomainService.deleteAllByIdIn(request.getIds()); + } + + private void validateExecution(List ids) { + Long userId = SecurityUtil.getCurrentUserId(); + latestSearchValidator.validateExistByIdIn(ids); + latestSearchValidator.verifyUserByIdIn(ids, userId); + } +} diff --git a/Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java b/Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java index a392af40..f1836264 100644 --- a/Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/GetLatestSearchListUseCase.java @@ -2,10 +2,10 @@ import allchive.server.api.config.security.SecurityUtil; -import allchive.server.api.search.model.dto.response.SearchListResponse; +import allchive.server.api.search.model.dto.response.SearchVoListResponse; +import allchive.server.api.search.model.vo.LatestSearchVo; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor; -import allchive.server.domain.domains.search.domain.LatestSearch; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -16,12 +16,12 @@ public class GetLatestSearchListUseCase { private final LatestSearchAdaptor latestSearchAdaptor; @Transactional(readOnly = true) - public SearchListResponse execute() { + public SearchVoListResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); - List keywords = + List keywords = latestSearchAdaptor.findAllByUserIdOrderByCreatedAt(userId).stream() - .map(LatestSearch::getKeyword) + .map(LatestSearchVo::from) .toList(); - return SearchListResponse.from(keywords); + return SearchVoListResponse.from(keywords); } } 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 744c1305..f148d8b5 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 @@ -3,7 +3,7 @@ import static allchive.server.core.consts.AllchiveConst.SEARCH_KEY; import static jodd.util.StringPool.ASTERISK; -import allchive.server.api.search.model.dto.request.SearchRequest; +import allchive.server.api.common.util.StringParamUtil; import allchive.server.api.search.model.dto.response.SearchListResponse; import allchive.server.core.annotation.UseCase; import java.util.ArrayList; @@ -20,17 +20,22 @@ public class GetRelativeSearchListUseCase { private final RedisTemplate redisTemplate; @Transactional - public SearchListResponse execute(SearchRequest request) { + public SearchListResponse execute(String word) { + validateExecution(word); ZSetOperations zSetOperations = redisTemplate.opsForZSet(); List autoCompleteList = new ArrayList<>(); - Long rank = zSetOperations.rank(SEARCH_KEY, request.getKeyword()); + Long rank = zSetOperations.rank(SEARCH_KEY, word); if (rank != null) { Set rangeList = zSetOperations.range(SEARCH_KEY, rank, rank + 1000); - autoCompleteList = getAutoCompleteList(rangeList, request.getKeyword()); + autoCompleteList = getAutoCompleteList(rangeList, word); } return SearchListResponse.from(autoCompleteList); } + private void validateExecution(String word) { + StringParamUtil.checkEmptyString(word); + } + private List getAutoCompleteList(Set rangeList, String keyword) { return rangeList.stream() .filter(value -> value.endsWith(ASTERISK) && value.startsWith(keyword)) diff --git a/Api/src/main/java/allchive/server/api/search/service/RenewalTitleDataUseCase.java b/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java similarity index 60% rename from Api/src/main/java/allchive/server/api/search/service/RenewalTitleDataUseCase.java rename to Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java index 3f204ba7..64dc256e 100644 --- a/Api/src/main/java/allchive/server/api/search/service/RenewalTitleDataUseCase.java +++ b/Api/src/main/java/allchive/server/api/search/service/RenewalSearchDataUseCase.java @@ -6,6 +6,8 @@ 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 lombok.RequiredArgsConstructor; @@ -17,17 +19,44 @@ @UseCase @Slf4j @RequiredArgsConstructor -public class RenewalTitleDataUseCase { +public class RenewalSearchDataUseCase { private final ArchivingAdaptor archivingAdaptor; + private final TagAdaptor tagAdaptor; private final RedisTemplate redisTemplate; - private static final int TIME_LIMIT = 1; - @Scheduled(cron = "0 0 3 * * *") @Transactional(readOnly = true) public void executeSchedule() { log.info("renewal title scheduler on"); + renewalData(); + log.info("renewal title scheduler off"); + } + + @Transactional(readOnly = true) + public void executeForce() { + renewalData(); + } + + private void renewalData() { redisTemplate.delete(SEARCH_KEY); + renewalArchiving(); + renewalTag(); + } + + 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 renewalArchiving() { Set archivings = new HashSet<>(archivingAdaptor.findAllByPublicStatus(Boolean.TRUE)); archivings.forEach( @@ -35,15 +64,14 @@ public void executeSchedule() { redisTemplate .opsForZSet() .add(SEARCH_KEY, archiving.getTitle().trim() + ASTERISK, 0); - for (int index = 1; index < archiving.getTitle().length(); index++) { + for (int index = 0; index <= archiving.getTitle().length(); index++) { redisTemplate .opsForZSet() .add( SEARCH_KEY, - archiving.getTitle().trim().substring(0, index - 1), + archiving.getTitle().trim().substring(0, index), 0); } }); - log.info("renewal title scheduler off"); } } 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 df4bc9ea..168ca036 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 @@ -2,24 +2,29 @@ import allchive.server.api.archiving.model.dto.response.ArchivingResponse; -import allchive.server.api.common.slice.SliceResponse; +import allchive.server.api.common.page.PageResponse; +import allchive.server.api.common.util.StringParamUtil; import allchive.server.api.config.security.SecurityUtil; -import allchive.server.api.search.model.dto.request.SearchRequest; import allchive.server.api.search.model.dto.response.SearchResponse; import allchive.server.api.search.model.enums.ArchivingType; import allchive.server.core.annotation.UseCase; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.block.adaptor.BlockAdaptor; import allchive.server.domain.domains.block.domain.Block; +import allchive.server.domain.domains.content.adaptor.ContentTagGroupAdaptor; +import allchive.server.domain.domains.content.adaptor.TagAdaptor; +import allchive.server.domain.domains.content.domain.Tag; import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor; import allchive.server.domain.domains.search.domain.LatestSearch; import allchive.server.domain.domains.search.service.LatestSearchDomainService; import allchive.server.domain.domains.user.adaptor.ScrapAdaptor; import allchive.server.domain.domains.user.domain.Scrap; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; import org.springframework.transaction.annotation.Transactional; @UseCase @@ -30,60 +35,90 @@ public class SearchArchivingUseCase { private final BlockAdaptor blockAdaptor; private final LatestSearchAdaptor latestSearchAdaptor; private final LatestSearchDomainService latestSearchDomainService; + private final TagAdaptor tagAdaptor; + private final ContentTagGroupAdaptor contentTagGroupAdaptor; @Transactional - public SearchResponse execute(Pageable pageable, ArchivingType type, SearchRequest request) { + public SearchResponse execute(Pageable pageable, ArchivingType type, String word) { + validateExecution(word); Long userId = SecurityUtil.getCurrentUserId(); - SliceResponse my = null; - SliceResponse community = null; - renewalLatestSearch(userId, request.getKeyword()); + Set tagArchivingIds = getTagArchivingIds(userId, word); + PageResponse my; + PageResponse community; + renewalLatestSearch(userId, word); switch (type) { case ALL -> { - my = getMyArchivings(userId, request.getKeyword(), pageable); - community = getCommunityArchivings(userId, request.getKeyword(), pageable); + my = getMyArchivings(userId, word, pageable, tagArchivingIds); + community = getCommunityArchivings(userId, word, pageable, tagArchivingIds); return SearchResponse.forAll(my, community); } case MY -> { - my = getMyArchivings(userId, request.getKeyword(), pageable); + my = getMyArchivings(userId, word, pageable, tagArchivingIds); return SearchResponse.forMy(my); } case COMMUNITY -> { - community = getCommunityArchivings(userId, request.getKeyword(), pageable); + community = getCommunityArchivings(userId, word, pageable, tagArchivingIds); return SearchResponse.forCommunity(community); } } return null; } + private void validateExecution(String word) { + StringParamUtil.checkEmptyString(word); + } + + private Set getTagArchivingIds(Long userId, String word) { + List tags = tagAdaptor.queryTagByUserIdContainName(userId, word); + return contentTagGroupAdaptor.queryContentTagGroupByTagInWithContent(tags).stream() + .map(contentTagGroup -> contentTagGroup.getContent().getArchivingId()) + .collect(Collectors.toSet()); + } + private void renewalLatestSearch(Long userId, String keyword) { List searches = latestSearchAdaptor.findAllByUserIdOrderByCreatedAt(userId); - if (searches.size() == 5) { - latestSearchDomainService.delete(searches.get(0)); + if (!isExistSearchKeyword(searches, keyword)) { + if (searches.size() == 5) { + latestSearchDomainService.delete(searches.get(0)); + } + LatestSearch newSearch = LatestSearch.of(keyword, userId); + latestSearchDomainService.save(newSearch); } - LatestSearch newSearch = LatestSearch.of(keyword, userId); - latestSearchDomainService.save(newSearch); } - private SliceResponse getMyArchivings( - Long userId, String keyword, Pageable pageable) { - Slice archivingSlices = + private boolean isExistSearchKeyword(List searches, String keyword) { + return !searches.stream() + .filter(search -> search.getKeyword().equals(keyword)) + .toList() + .isEmpty(); + } + + private PageResponse getMyArchivings( + Long userId, String keyword, Pageable pageable, Set tagArchivingIds) { + Page archivingPages = archivingAdaptor - .querySliceArchivingByUserIdAndKeywords(userId, keyword, pageable) + .querySliceArchivingByUserIdAndKeywordsOrderByTagArchvingIds( + userId, keyword, pageable, tagArchivingIds) .map(archiving -> ArchivingResponse.of(archiving, Boolean.FALSE)); - return SliceResponse.of(archivingSlices); + return PageResponse.of(archivingPages); } - private SliceResponse getCommunityArchivings( - Long userId, String keyword, Pageable pageable) { + private PageResponse getCommunityArchivings( + Long userId, String keyword, Pageable pageable, Set tagArchivingIds) { List archivingIdList = scrapAdaptor.findAllByUserId(userId).stream().map(Scrap::getArchivingId).toList(); List blockList = blockAdaptor.findByBlockFrom(userId).stream().map(Block::getBlockUser).toList(); - Slice archivingSlices = + Page archivingSlices = archivingAdaptor - .querySliceArchivingByKeywordExceptBlock( - archivingIdList, blockList, keyword, pageable) + .querySliceArchivingByKeywordExceptBlockOrderByTagArchvingIds( + archivingIdList, + blockList, + keyword, + pageable, + tagArchivingIds, + userId) .map(archiving -> ArchivingResponse.of(archiving, Boolean.FALSE)); - return SliceResponse.of(archivingSlices); + return PageResponse.of(archivingSlices); } } 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 a34a8db3..63dc129a 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 @@ -21,10 +21,14 @@ public class DeleteTagUseCase { @Transactional public void execute(Long tagId) { - Long userId = SecurityUtil.getCurrentUserId(); - tagValidator.verifyUser(tagId, userId); + validateExecution(tagId); Tag tag = tagAdaptor.findById(tagId); contentTagGroupDomainService.deleteByTag(tag); tagDomainService.deleteById(tagId); } + + private void validateExecution(Long tagId) { + Long userId = SecurityUtil.getCurrentUserId(); + tagValidator.verifyUser(tagId, userId); + } } 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 881ec389..80866d67 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 @@ -17,8 +17,12 @@ public class UpdateTagUseCase { @Transactional public void execute(Long tagId, UpdateTagRequest request) { + validateExecution(tagId); + tagDomainService.updateTag(tagId, request.getName()); + } + + private void validateExecution(Long tagId) { Long userId = SecurityUtil.getCurrentUserId(); tagValidator.verifyUser(tagId, userId); - tagDomainService.updateTag(tagId, request.getName()); } } diff --git a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java index 7b4ce100..2425d001 100644 --- a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java +++ b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserInfoResponse.java @@ -3,6 +3,7 @@ import allchive.server.api.common.util.UrlUtil; import allchive.server.domain.domains.user.domain.User; +import allchive.server.domain.domains.user.domain.enums.OauthProvider; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Getter; @@ -21,12 +22,21 @@ public class GetUserInfoResponse { @Schema(defaultValue = "닉네임", description = "닉네임") private String nickname; + @Schema(defaultValue = "KAKAO", description = "oauth") + private OauthProvider oauthProvider; + @Builder - public GetUserInfoResponse(String imgUrl, String email, String name, String nickname) { + public GetUserInfoResponse( + String imgUrl, + String email, + String name, + String nickname, + OauthProvider oauthProvider) { this.imgUrl = imgUrl; this.email = email; this.name = name; this.nickname = nickname; + this.oauthProvider = oauthProvider; } public static GetUserInfoResponse from(User user) { @@ -35,6 +45,7 @@ public static GetUserInfoResponse from(User user) { .email(user.getEmail()) .name(user.getName()) .nickname(user.getNickname()) + .oauthProvider(user.getOauthInfo().getProvider()) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java index 45e64bbb..5620b9a9 100644 --- a/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java +++ b/Api/src/main/java/allchive/server/api/user/model/dto/response/GetUserProfileResponse.java @@ -24,24 +24,34 @@ public class GetUserProfileResponse { @Schema(defaultValue = "0", description = "공개 아카이브 개수") private int publicArchivingCount; + @Schema(defaultValue = "0", description = "모든 아카이브 개수") + private int archivingCount; + @Builder - private GetUserProfileResponse( - String nickname, String imgUrl, int linkCount, int imgCount, int publicArchivingCount) { + public GetUserProfileResponse( + String nickname, + String imgUrl, + int linkCount, + int imgCount, + int publicArchivingCount, + int archivingCount) { this.nickname = nickname; this.imgUrl = imgUrl; this.linkCount = linkCount; this.imgCount = imgCount; this.publicArchivingCount = publicArchivingCount; + this.archivingCount = archivingCount; } public static GetUserProfileResponse of( - User user, int linkCount, int imgCount, int publicArchivingCount) { + User user, int linkCount, int imgCount, int publicArchivingCount, int archivingCount) { return GetUserProfileResponse.builder() .nickname(user.getNickname()) .imgUrl(UrlUtil.toAssetUrl(user.getProfileImgUrl())) .linkCount(linkCount) .imgCount(imgCount) .publicArchivingCount(publicArchivingCount) + .archivingCount(archivingCount) .build(); } } diff --git a/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java index 84468718..d7ce03cd 100644 --- a/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java +++ b/Api/src/main/java/allchive/server/api/user/model/mapper/UserMapper.java @@ -1,5 +1,7 @@ package allchive.server.api.user.model.mapper; +import static allchive.server.core.consts.AllchiveConst.PLUS_ONE; +import static allchive.server.core.consts.AllchiveConst.ZERO; import allchive.server.api.user.model.dto.response.GetUserProfileResponse; import allchive.server.core.annotation.Mapper; @@ -15,8 +17,9 @@ public GetUserProfileResponse toGetUserProfileResponse( for (Archiving archiving : archivingList) { linkCount += archiving.getLinkCnt(); imgCount += archiving.getImgCnt(); - publicArchivingCount += archiving.getPublicStatus() ? 1 : 0; + publicArchivingCount += archiving.getPublicStatus() ? PLUS_ONE : ZERO; } - return GetUserProfileResponse.of(user, linkCount, imgCount, publicArchivingCount); + return GetUserProfileResponse.of( + user, linkCount, imgCount, publicArchivingCount, archivingList.size()); } } diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java index 0cb03dca..5a8db184 100644 --- a/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserInfoUseCase.java @@ -19,8 +19,12 @@ public class GetUserInfoUseCase { @Transactional(readOnly = true) public GetUserInfoResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); - userValidator.validateUserStatusNormal(userId); + validateExecution(userId); User user = userAdaptor.findById(userId); return GetUserInfoResponse.from(user); } + + private void validateExecution(Long userId) { + userValidator.validateUserStatusNormal(userId); + } } diff --git a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java index bf30180f..fe42d2cc 100644 --- a/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java +++ b/Api/src/main/java/allchive/server/api/user/service/GetUserProfileUseCase.java @@ -25,9 +25,13 @@ public class GetUserProfileUseCase { @Transactional(readOnly = true) public GetUserProfileResponse execute() { Long userId = SecurityUtil.getCurrentUserId(); - userValidator.validateUserStatusNormal(userId); + validateExecution(userId); User user = userAdaptor.findById(userId); - List archivingList = archivingAdaptor.findAllByUserId(userId); + List archivingList = archivingAdaptor.queryArchivingByUserId(userId); return userMapper.toGetUserProfileResponse(archivingList, user); } + + private void validateExecution(Long userId) { + userValidator.validateUserStatusNormal(userId); + } } 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 9485a883..313810c2 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 @@ -19,9 +19,13 @@ public class UpdateUserInfoUseCase { @Transactional public void execute(UpdateUserInfoRequest request) { Long userId = SecurityUtil.getCurrentUserId(); - userValidator.validateUserStatusNormal(userId); + validateExecution(userId); String imgKey = UrlUtil.convertUrlToKey(request.getImgUrl()); userDomainService.updateUserInfo( userId, request.getName(), request.getEmail(), request.getNickname(), imgKey); } + + private void validateExecution(Long userId) { + userValidator.validateUserStatusNormal(userId); + } } diff --git a/Api/src/main/resources/application.yml b/Api/src/main/resources/application.yml index c4f14e91..33e428bb 100644 --- a/Api/src/main/resources/application.yml +++ b/Api/src/main/resources/application.yml @@ -19,6 +19,10 @@ springdoc: swagger-ui: tags-sorter: alpha +swagger: + user: ${SWAGGER_USER:user} + password: ${SWAGGER_PASSWORD:password} + --- spring: config: 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 2a762c23..e5f1a45e 100644 --- a/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java +++ b/Core/src/main/java/allchive/server/core/consts/AllchiveConst.java @@ -40,4 +40,8 @@ public class AllchiveConst { public static final String SEARCH_KEY = "ARCHIVING_TITLE"; public static final String ASTERISK = "*"; + + public static final int PLUS_ONE = 1; + public static final int MINUS_ONE = -1; + public static final int ZERO = 0; } 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 f73bb7a8..aff876e4 100644 --- a/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java +++ b/Core/src/main/java/allchive/server/core/error/GlobalErrorCode.java @@ -14,24 +14,20 @@ public enum GlobalErrorCode implements BaseErrorCode { /** Server 오류 * */ HTTP_MESSAGE_NOT_READABLE(BAD_REQUEST, "GLOBAL_400_1", "잘못된 형식의 값을 입력했습니다."), + NO_APPLE_CODE(BAD_REQUEST, "GLOBAL_400_2", "애플 코드가 필요합니다."), + EMPTY_PARAM_VALUE(BAD_REQUEST, "GLOBAL_400_3", "빈 파람 값을 입력했습니다."), _INTERNAL_SERVER_ERROR(INTERNAL_SERVER, "GLOBAL_500_1", "서버 오류. 관리자에게 문의 부탁드립니다."), INVALID_OAUTH_PROVIDER(INTERNAL_SERVER, "GLOBAL_500_2", "지원하지 않는 OAuth Provider 입니다."), - SECURITY_CONTEXT_NOT_FOUND(INTERNAL_SERVER, "GLOBAL_500_3", "security context not found"), /** 토큰 에러 * */ - // TODO : 에러 코드 정렬 INVALID_TOKEN(UNAUTHORIZED, "AUTH_401_2", "올바르지 않은 토큰입니다."), INVALID_ACCESS_TOKEN_ERROR(UNAUTHORIZED, "AUTH_401_4", "알맞은 accessToken 을 넣어주세요."), EXPIRED_TOKEN(UNAUTHORIZED, "AUTH_401_3", "만료된 엑세스 토큰입니다"), EXPIRED_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_403_1", "인증 시간이 만료되었습니다. 재 로그인 해주세요."), - INVALID_AUTH_TOKEN(UNAUTHORIZED, "AUTH_401_2", "액세스 토큰이 유효하지 않습니다"), - INVALID_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_4", "리프레시 토큰이 유효하지 않습니다"), - MISMATCH_REFRESH_TOKEN(UNAUTHORIZED, "AUTH_401_6", "리프레시 토큰의 유저 정보가 일치하지 않습니다"), - FORBIDDEN_ADMIN(FORBIDDEN, "AUTH_403_1", "권한이 부여되지 않은 사용자입니다"), - UNSUPPORTED_TOKEN(UNAUTHORIZED, "AUTH_401_7", "지원하지 않는 토큰입니다"), - INVALID_SIGNATURE(UNAUTHORIZED, "AUTH_401_8", "잘못된 JWT 서명입니다"), - NO_TOKEN(UNAUTHORIZED, "AUTH_401_1", "토큰이 존재하지 않습니다"), + + /** s3 오류 */ + S3_OBJECT_NOT_FOUND(BAD_REQUEST, "S3_500_1", "s3 객체가 존재하지 않습니다."), /** Feign Client 오류 */ OTHER_SERVER_BAD_REQUEST(BAD_REQUEST, "FEIGN_400_1", "Other server bad request"), diff --git a/Core/src/main/java/allchive/server/core/error/exception/EmptyParamValueException.java b/Core/src/main/java/allchive/server/core/error/exception/EmptyParamValueException.java new file mode 100644 index 00000000..1f2d37e7 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/EmptyParamValueException.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 EmptyParamValueException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new EmptyParamValueException(); + + private EmptyParamValueException() { + super(GlobalErrorCode.EMPTY_PARAM_VALUE); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/ExampleException.java b/Core/src/main/java/allchive/server/core/error/exception/ExampleException.java deleted file mode 100644 index 478a21d7..00000000 --- a/Core/src/main/java/allchive/server/core/error/exception/ExampleException.java +++ /dev/null @@ -1,14 +0,0 @@ -package allchive.server.core.error.exception; - - -import allchive.server.core.error.BaseErrorException; -import allchive.server.core.error.GlobalErrorCode; - -public class ExampleException extends BaseErrorException { - - public static final BaseErrorException EXCEPTION = new ExampleException(); - - private ExampleException() { - super(GlobalErrorCode.EXAMPLE_ERROR); - } -} diff --git a/Core/src/main/java/allchive/server/core/error/exception/NoAppleCodeException.java b/Core/src/main/java/allchive/server/core/error/exception/NoAppleCodeException.java new file mode 100644 index 00000000..b8b60c03 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/NoAppleCodeException.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 NoAppleCodeException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NoAppleCodeException(); + + private NoAppleCodeException() { + super(GlobalErrorCode.NO_APPLE_CODE); + } +} diff --git a/Core/src/main/java/allchive/server/core/error/exception/S3ObjectNotFoundException.java b/Core/src/main/java/allchive/server/core/error/exception/S3ObjectNotFoundException.java new file mode 100644 index 00000000..9dd31860 --- /dev/null +++ b/Core/src/main/java/allchive/server/core/error/exception/S3ObjectNotFoundException.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 S3ObjectNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new S3ObjectNotFoundException(); + + private S3ObjectNotFoundException() { + super(GlobalErrorCode.S3_OBJECT_NOT_FOUND); + } +} diff --git a/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java b/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java index 9016edfd..a3a92044 100644 --- a/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java +++ b/Core/src/main/java/allchive/server/core/helper/SpringEnvironmentHelper.java @@ -16,14 +16,22 @@ public class SpringEnvironmentHelper { private final Environment environment; public Boolean isProdProfile() { - String[] activeProfiles = environment.getActiveProfiles(); - List currentProfile = Arrays.stream(activeProfiles).toList(); + List currentProfile = getCurrentProfile(); return currentProfile.contains(PROD); } public Boolean isDevProfile() { - String[] activeProfiles = environment.getActiveProfiles(); - List currentProfile = Arrays.stream(activeProfiles).toList(); + List currentProfile = getCurrentProfile(); return currentProfile.contains(DEV); } + + public Boolean isProdAndDevProfile() { + List currentProfile = getCurrentProfile(); + return currentProfile.contains(PROD) || currentProfile.contains(DEV); + } + + private List getCurrentProfile() { + String[] activeProfiles = environment.getActiveProfiles(); + return Arrays.stream(activeProfiles).toList(); + } } diff --git a/Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java b/Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java index 5452eeff..5aef1604 100644 --- a/Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java +++ b/Core/src/main/java/allchive/server/core/properties/AppleOAuthProperties.java @@ -13,12 +13,10 @@ public class AppleOAuthProperties { private String baseUrl; private String clientId; - private String appClientId; + private String webClientId; private String keyId; private String redirectUrl; private String teamId; - private String scope; - private String keyPath; private String webCallbackUrl; - private String appstoreIssuer; + private String authKey; } diff --git a/Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java b/Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java index 42d76217..7b9430c9 100644 --- a/Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java +++ b/Core/src/main/java/allchive/server/core/properties/KakaoOAuthProperties.java @@ -17,4 +17,6 @@ public class KakaoOAuthProperties { private String redirectUrl; private String appId; private String adminKey; + private String webClientId; + private String webAppId; } diff --git a/Core/src/main/resources/application-core.yml b/Core/src/main/resources/application-core.yml index 443a8af9..75df114c 100644 --- a/Core/src/main/resources/application-core.yml +++ b/Core/src/main/resources/application-core.yml @@ -14,15 +14,15 @@ oauth: redirect-url: ${KAKAO_REDIRECT} app-id: ${KAKAO_APP_ID} admin-key: ${KAKAO_ADMIN_KEY} + web-client-id: ${KAKAO_WEB_CLIENT} + web-app-id: ${KAKAO_WEB_APP_ID} apple: baseUrl: ${APPLE_BASE_URL} - clientId: ${APPLE_CLIENT} - appClientId: ${APPLE_APP_CLIENT_ID} + clientId: ${APPLE_CLIENT_ID} + webClientId: ${APPLE_WEB_CLIENT_ID} keyId: ${APPLE_KEY_ID} redirectUrl: ${APPLE_REDIRECT} teamId: ${APPLE_TEAM_ID} - scope: ${APPLE_SCOPE} - keyPath: ${APPLE_KEY_PATH} webCallbackUrl: ${APPLE_WEB_CALLBACK} - appstoreIssuer: ${APPLE_APPSTORE_ISSUER} \ No newline at end of file + authKey: ${APPLE_AUTH_KEY} \ No newline at end of file diff --git a/Domain/src/main/java/allchive/server/domain/common/util/PageUtil.java b/Domain/src/main/java/allchive/server/domain/common/util/PageUtil.java new file mode 100644 index 00000000..c725c81f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/common/util/PageUtil.java @@ -0,0 +1,26 @@ +package allchive.server.domain.common.util; + + +import com.querydsl.core.QueryResults; +import java.util.List; +import org.springframework.data.domain.*; + +public class PageUtil { + public static Page toPage(QueryResults results, Pageable pageable) { + boolean hasNext = hasNext(results.getResults(), pageable); + return new PageImpl<>( + hasNext ? getContent(results.getResults(), pageable) : results.getResults(), + pageable, + results.getTotal()); + } + + // 다음 페이지 있는지 확인 + private static boolean hasNext(List content, Pageable pageable) { + return pageable.isPaged() && content.size() > pageable.getPageSize(); + } + + // 데이터 1개 빼고 반환 + private static List getContent(List content, Pageable pageable) { + return content.subList(0, pageable.getPageSize()); + } +} 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 b5c84f34..bf008e5f 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 @@ -8,7 +8,9 @@ import allchive.server.domain.domains.archiving.exception.exceptions.ArchivingNotFoundException; import allchive.server.domain.domains.archiving.repository.ArchivingRepository; import java.util.List; +import java.util.Set; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -73,19 +75,28 @@ public void deleteAllById(List archivingIds) { archivingRepository.deleteAllById(archivingIds); } - public Slice querySliceArchivingByUserIdAndKeywords( - Long userId, String keyword, Pageable pageable) { - return archivingRepository.querySliceArchivingByUserIdAndKeywords( - userId, keyword, pageable); + public Page querySliceArchivingByUserIdAndKeywordsOrderByTagArchvingIds( + Long userId, String keyword, Pageable pageable, Set tagArchivingIds) { + return archivingRepository.querySliceArchivingByUserIdAndKeywordsOrderByTagArchvingIds( + userId, keyword, pageable, tagArchivingIds); } - public Slice querySliceArchivingByKeywordExceptBlock( - List archivingIdList, List blockList, String keyword, Pageable pageable) { - return archivingRepository.querySliceArchivingByKeywordExceptBlock( - archivingIdList, blockList, keyword, pageable); + public Page querySliceArchivingByKeywordExceptBlockOrderByTagArchvingIds( + List archivingIdList, + List blockList, + String keyword, + Pageable pageable, + Set tagArchivingIds, + Long userId) { + return archivingRepository.querySliceArchivingByKeywordExceptBlockOrderByTagArchvingIds( + archivingIdList, blockList, keyword, pageable, tagArchivingIds, userId); } public List findAllByPublicStatus(Boolean publicStatus) { return archivingRepository.findAllByPublicStatus(publicStatus); } + + public List queryArchivingOrderByScrapCntLimit5() { + return archivingRepository.queryArchivingOrderByScrapCntLimit5(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java index 57919d98..f5ddbb50 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/domain/Archiving.java @@ -3,9 +3,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; import allchive.server.domain.domains.archiving.domain.enums.Category; -import allchive.server.domain.domains.archiving.exception.exceptions.DeletedArchivingException; -import allchive.server.domain.domains.archiving.exception.exceptions.NoAuthurityUpdateArchivingException; -import allchive.server.domain.domains.archiving.exception.exceptions.NotPublicArchivingException; +import allchive.server.domain.domains.archiving.exception.exceptions.*; import java.util.ArrayList; import java.util.List; import javax.persistence.*; @@ -85,13 +83,13 @@ public void validateUser(Long userId) { } } - public void validatePublicStatus(Long userId) { + public void validatePublic(Long userId) { if (!this.publicStatus && !this.userId.equals(userId)) { throw NotPublicArchivingException.EXCEPTION; } } - public void validateDeleteStatus(Long userId) { + public void validateNotDeleteExceptUser(Long userId) { if (this.deleteStatus && !this.userId.equals(userId)) { throw DeletedArchivingException.EXCEPTION; } @@ -116,4 +114,30 @@ public void delete() { public void restore() { this.deleteStatus = Boolean.FALSE; } + + public void updateImgCnt(int i) { + this.imgCnt += i; + } + + public void updateLinkCnt(int i) { + this.linkCnt += i; + } + + public void validateNotDelete() { + if (this.deleteStatus.equals(Boolean.TRUE)) { + throw DeletedArchivingException.EXCEPTION; + } + } + + public void validateAlreadyPinStatus(Long userId) { + if (this.pinUserId.contains(userId)) { + throw AlreadyPinnedArchivingException.EXCEPTION; + } + } + + public void validateNotPinStatus(Long userId) { + if (!this.pinUserId.contains(userId)) { + throw NotPinnedArchivingException.EXCEPTION; + } + } } 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 03946df8..90ff7449 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 @@ -4,6 +4,8 @@ import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.domain.enums.Category; import java.util.List; +import java.util.Set; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -20,9 +22,16 @@ Slice querySliceArchivingByIdIn( boolean queryArchivingExistById(Long archivingId); - Slice querySliceArchivingByUserIdAndKeywords( - Long userId, String keyword, Pageable pageable); + Page querySliceArchivingByUserIdAndKeywordsOrderByTagArchvingIds( + Long userId, String keyword, Pageable pageable, Set tagArchivingIds); - Slice querySliceArchivingByKeywordExceptBlock( - List archivingIdList, List blockList, String keyword, Pageable pageable); + Page querySliceArchivingByKeywordExceptBlockOrderByTagArchvingIds( + List archivingIdList, + List blockList, + String keyword, + Pageable pageable, + Set tagArchivingIds, + Long userId); + + List queryArchivingOrderByScrapCntLimit5(); } 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 c0cae02f..6eb736aa 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 @@ -1,10 +1,13 @@ package allchive.server.domain.domains.archiving.repository; +import static allchive.server.core.consts.AllchiveConst.PLUS_ONE; import static allchive.server.domain.domains.archiving.domain.QArchiving.archiving; +import allchive.server.domain.common.util.PageUtil; import allchive.server.domain.common.util.SliceUtil; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.domain.enums.Category; +import com.querydsl.core.QueryResults; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.CaseBuilder; @@ -12,7 +15,9 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import java.time.LocalDateTime; import java.util.List; +import java.util.Set; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -37,7 +42,7 @@ public Slice querySliceArchivingExceptBlock( deleteStatusFalse()) .orderBy(scrabListDesc(archivingIdList), scrapCntDesc(), createdAtDesc()) .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) + .limit(pageable.getPageSize() + PLUS_ONE) .fetch(); return SliceUtil.toSlice(archivings, pageable); } @@ -52,7 +57,7 @@ public Slice querySliceArchivingByUserId( .where(userIdEq(userId), categoryEq(category), deleteStatusFalse()) .orderBy(pinDesc(userId), scrapCntDesc(), createdAtDesc()) .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) + .limit(pageable.getPageSize() + PLUS_ONE) .fetch(); return SliceUtil.toSlice(archivings, pageable); } @@ -71,7 +76,7 @@ public Slice querySliceArchivingByIdIn( deleteStatusFalse()) .orderBy(scrapCntDesc(), createdAtDesc()) .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) + .limit(pageable.getPageSize() + PLUS_ONE) .fetch(); return SliceUtil.toSlice(archivings, pageable); } @@ -93,36 +98,58 @@ public boolean queryArchivingExistById(Long archivingId) { } @Override - public Slice querySliceArchivingByUserIdAndKeywords( - Long userId, String keyword, Pageable pageable) { - List archivings = + public Page querySliceArchivingByUserIdAndKeywordsOrderByTagArchvingIds( + Long userId, String keyword, Pageable pageable, Set tagArchivingIds) { + QueryResults results = queryFactory .selectFrom(archiving) - .where(userIdEq(userId), titleContain(keyword)) - .orderBy(createdAtDesc()) + .where( + userIdEq(userId), + titleContainOrIdIn(keyword, tagArchivingIds), + deleteStatusFalse()) + .orderBy(idIn(tagArchivingIds), createdAtDesc()) .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .fetch(); - return SliceUtil.toSlice(archivings, pageable); + .limit(pageable.getPageSize() + PLUS_ONE) + .fetchResults(); + return PageUtil.toPage(results, pageable); } @Override - public Slice querySliceArchivingByKeywordExceptBlock( - List archivingIdList, List blockList, String keyword, Pageable pageable) { - List archivings = + public Page querySliceArchivingByKeywordExceptBlockOrderByTagArchvingIds( + List archivingIdList, + List blockList, + String keyword, + Pageable pageable, + Set tagArchivingIds, + Long userId) { + QueryResults archivings = queryFactory .select(archiving) .from(archiving) .where( userIdNotIn(blockList), - publicStatusTrue(), + userIdNeAndPublicStatusTrue(userId), deleteStatusFalse(), - titleContain(keyword)) - .orderBy(scrabListDesc(archivingIdList), scrapCntDesc(), createdAtDesc()) + titleContainOrIdIn(keyword, tagArchivingIds)) + .orderBy( + idIn(tagArchivingIds), + scrabListDesc(archivingIdList), + scrapCntDesc(), + createdAtDesc()) .offset(pageable.getOffset()) - .limit(pageable.getPageSize() + 1) - .fetch(); - return SliceUtil.toSlice(archivings, pageable); + .limit(pageable.getPageSize() + PLUS_ONE) + .fetchResults(); + return PageUtil.toPage(archivings, pageable); + } + + @Override + public List queryArchivingOrderByScrapCntLimit5() { + return queryFactory + .selectFrom(archiving) + .where(deleteStatusFalse(), publicStatusTrue()) + .orderBy(scrapCntDesc()) + .limit(5L) + .fetch(); } private BooleanExpression userIdNotIn(List blockList) { @@ -156,8 +183,16 @@ private BooleanExpression archivingIdEq(Long archivingId) { return archiving.id.eq(archivingId); } - private BooleanExpression titleContain(String keyword) { - return archiving.title.contains(keyword); + private BooleanExpression titleContainOrIdIn(String keyword, Set tagArchivingIds) { + return archiving.title.contains(keyword).or(archiving.id.in(tagArchivingIds)); + } + + private BooleanExpression userIdEqOrPublicStatusTrue(Long userId) { + return archiving.userId.eq(userId).or(archiving.publicStatus.eq(Boolean.TRUE)); + } + + private BooleanExpression userIdNeAndPublicStatusTrue(Long userId) { + return archiving.userId.ne(userId).and(archiving.publicStatus.eq(Boolean.TRUE)); } private OrderSpecifier scrabListDesc(List archivingIdList) { @@ -183,4 +218,10 @@ private OrderSpecifier createdAtDesc() { private OrderSpecifier categoryDesc() { return archiving.category.desc(); } + + private OrderSpecifier idIn(Set tagArchivingIds) { + NumberExpression idInStatus = + new CaseBuilder().when(archiving.id.in(tagArchivingIds)).then(1L).otherwise(0L); + return idInStatus.desc(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java index ffcc9f1f..3fae3b49 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/service/ArchivingDomainService.java @@ -1,10 +1,13 @@ package allchive.server.domain.domains.archiving.service; +import static allchive.server.core.consts.AllchiveConst.MINUS_ONE; +import static allchive.server.core.consts.AllchiveConst.PLUS_ONE; import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; import allchive.server.domain.domains.archiving.domain.enums.Category; +import allchive.server.domain.domains.content.domain.enums.ContentType; import java.util.List; import lombok.RequiredArgsConstructor; @@ -40,7 +43,7 @@ public void updatePin(Long archivingId, Long userId, boolean pin) { } else { archiving.deletePinUserId(userId); } - archiving.updateScrapCnt(pin ? 1 : -1); + archiving.updateScrapCnt(pin ? PLUS_ONE : MINUS_ONE); } public void softDeleteById(Long archivingId) { @@ -49,7 +52,7 @@ public void softDeleteById(Long archivingId) { archivingAdaptor.save(archiving); } - public void restoreInIdList(List archivingIds) { + public void restoreByIdIn(List archivingIds) { List archivings = archivingAdaptor.findAllByIdIn(archivingIds); archivings.forEach(Archiving::restore); archivingAdaptor.saveAll(archivings); @@ -58,4 +61,13 @@ public void restoreInIdList(List archivingIds) { public void deleteAllById(List archivingIds) { archivingAdaptor.deleteAllById(archivingIds); } + + public void updateContentCnt(Long archivingId, ContentType contentType, int i) { + Archiving archiving = archivingAdaptor.findById(archivingId); + if (contentType.equals(ContentType.IMAGE)) { + archiving.updateImgCnt(i); + } else { + archiving.updateLinkCnt(i); + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java index 0dd6ec08..f53e8c41 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/archiving/validator/ArchivingValidator.java @@ -4,10 +4,7 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.archiving.adaptor.ArchivingAdaptor; import allchive.server.domain.domains.archiving.domain.Archiving; -import allchive.server.domain.domains.archiving.exception.exceptions.AlreadyPinnedArchivingException; import allchive.server.domain.domains.archiving.exception.exceptions.ArchivingNotFoundException; -import allchive.server.domain.domains.archiving.exception.exceptions.NoAuthurityUpdateArchivingException; -import allchive.server.domain.domains.archiving.exception.exceptions.NotPinnedArchivingException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -20,12 +17,12 @@ public void verifyUser(Long userId, Long archivingId) { archivingAdaptor.findById(archivingId).validateUser(userId); } - public void validatePublicStatus(Long archivingId, Long userId) { - archivingAdaptor.findById(archivingId).validatePublicStatus(userId); + public void validatePublic(Long archivingId, Long userId) { + archivingAdaptor.findById(archivingId).validatePublic(userId); } - public void validateDeleteStatus(Long archivingId, Long userId) { - archivingAdaptor.findById(archivingId).validateDeleteStatus(userId); + public void validateNotDeleteExceptUser(Long archivingId, Long userId) { + archivingAdaptor.findById(archivingId).validateNotDeleteExceptUser(userId); } public void validateExistById(Long archivingId) { @@ -35,19 +32,11 @@ public void validateExistById(Long archivingId) { } public void validateAlreadyPinStatus(Long archivingId, Long userId) { - if (archivingAdaptor.findById(archivingId).getPinUserId().contains(userId)) { - throw AlreadyPinnedArchivingException.EXCEPTION; - } + archivingAdaptor.findById(archivingId).validateAlreadyPinStatus(userId); } public void validateNotPinStatus(Long archivingId, Long userId) { - if (!archivingAdaptor.findById(archivingId).getPinUserId().contains(userId)) { - throw NotPinnedArchivingException.EXCEPTION; - } - } - - public void validateArchivingUser(Long archivingId, Long userId) { - archivingAdaptor.findById(archivingId).validateUser(userId); + archivingAdaptor.findById(archivingId).validateNotPinStatus(userId); } public void validateExistInIdList(List archivingIdList) { @@ -59,11 +48,10 @@ public void validateExistInIdList(List archivingIdList) { public void verifyUserInIdList(Long userId, List archivingIds) { List archivingList = archivingAdaptor.findAllByIdIn(archivingIds); - archivingList.forEach( - archiving -> { - if (!archiving.getUserId().equals(userId)) { - throw NoAuthurityUpdateArchivingException.EXCEPTION; - } - }); + archivingList.forEach(archiving -> archiving.validateUser(userId)); + } + + public void validateNotDeleted(Long archivingId) { + archivingAdaptor.findById(archivingId).validateNotDelete(); } } 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 942c917e..ebbf5bb2 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 @@ -41,4 +41,8 @@ public List queryContentTagGroupByContentWithTag(Content conten public void deleteAllByContent(Content content) { contentTagGroupRepository.deleteAllByContent(content); } + + public List queryContentTagGroupByTagInWithContent(List tags) { + return contentTagGroupRepository.queryContentTagGroupByTagInWithContent(tags); + } } 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 5b6e53ae..0b65c5be 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 @@ -44,4 +44,12 @@ public List findAllByUserId(Long userId) { public void deleteAll(List tagList) { tagRepository.deleteAll(tagList); } + + public List queryTagByUserIdContainName(Long userId, String word) { + return tagRepository.queryTagByUserIdContainName(userId, word); + } + + public List findAll() { + return tagRepository.findAll(); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java index 7d1280d5..8dab4301 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/Content.java @@ -3,6 +3,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; import allchive.server.domain.domains.content.domain.enums.ContentType; +import allchive.server.domain.domains.content.exception.exceptions.AlreadyDeletedContentException; import javax.persistence.*; import lombok.AccessLevel; import lombok.Builder; @@ -76,17 +77,18 @@ public void restore() { this.deleteStatus = Boolean.FALSE; } - public void updateLinkContent(Long archivingId, String link, String title, String memo) { + public void updateContent( + Long archivingId, String imgUrl, String link, String title, String memo) { this.archivingId = archivingId; + this.imageUrl = imgUrl; this.linkUrl = link; this.title = title; this.memo = memo; } - public void updateImageContent(Long archivingId, String imgUrl, String title, String memo) { - this.archivingId = archivingId; - this.imageUrl = imgUrl; - this.title = title; - this.memo = memo; + public void validateNotDelete() { + if (this.isDeleteStatus()) { + throw AlreadyDeletedContentException.EXCEPTION; + } } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java index fbec1562..8cb6ebb8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/domain/enums/ContentType.java @@ -11,8 +11,8 @@ @Getter @AllArgsConstructor public enum ContentType { - IMAGE("image"), - LINK("link"); + IMAGE("IMAGE"), + LINK("LINK"); @JsonValue private String value; diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java index 58d96d31..97656cbd 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/ContentErrorCode.java @@ -11,6 +11,8 @@ @Getter @AllArgsConstructor public enum ContentErrorCode implements BaseErrorCode { + ALREADY_DELETED_CONTENT(NOT_FOUND, "CONTENT_400_1", "이미 삭제된 컨텐츠입니다."), + NOT_PUBLIC_CONTENT(NOT_FOUND, "CONTENT_400_2", "공개되지않은 컨텐츠입니다."), CONTENT_NOT_FOUND(NOT_FOUND, "CONTENT_404_1", "카테고리를 찾을 수 없습니다."), NO_AUTHORITY_UPDATE_CONTENT(FORBIDDEN, "TAG_403_1", "컨텐츠 수정 권한이 없습니다."), diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/AlreadyDeletedContentException.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/AlreadyDeletedContentException.java new file mode 100644 index 00000000..619e6b0e --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/AlreadyDeletedContentException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.content.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.content.exception.ContentErrorCode; + +public class AlreadyDeletedContentException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new AlreadyDeletedContentException(); + + private AlreadyDeletedContentException() { + super(ContentErrorCode.ALREADY_DELETED_CONTENT); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NotPublicContentException.java b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NotPublicContentException.java new file mode 100644 index 00000000..a71cb95e --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/content/exception/exceptions/NotPublicContentException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.content.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.content.exception.ContentErrorCode; + +public class NotPublicContentException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NotPublicContentException(); + + private NotPublicContentException() { + super(ContentErrorCode.NOT_PUBLIC_CONTENT); + } +} 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 91ffbc07..90edcb37 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 @@ -22,7 +22,7 @@ public Slice querySliceContentByArchivingId(Long archivingId, Pageable List archivings = queryFactory .selectFrom(content) - .where(archivingIdEq(archivingId)) + .where(archivingIdEq(archivingId), deleteStatusFalse()) .orderBy(createdAtDesc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize() + 1) @@ -46,6 +46,10 @@ public boolean queryContentExistById(Long id) { return fetchOne != null; } + private BooleanExpression deleteStatusFalse() { + return content.deleteStatus.eq(Boolean.FALSE); + } + 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 546979cb..05105f26 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 @@ -3,10 +3,13 @@ import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.Tag; import java.util.List; public interface ContentTagGroupCustomRepository { public List queryContentTagGroupByContentIn(List contentList); List queryContentTagGroupByContentWithTag(Content content); + + List queryContentTagGroupByTagInWithContent(List tags); } 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 57f47f4a..055f32ce 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 @@ -1,10 +1,12 @@ package allchive.server.domain.domains.content.repository; +import static allchive.server.domain.domains.content.domain.QContent.content; import static allchive.server.domain.domains.content.domain.QContentTagGroup.contentTagGroup; import static allchive.server.domain.domains.content.domain.QTag.tag; import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.domain.ContentTagGroup; +import allchive.server.domain.domains.content.domain.Tag; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -38,6 +40,17 @@ public List queryContentTagGroupByContentWithTag(Content conten .fetch(); } + @Override + public List queryContentTagGroupByTagInWithContent(List tags) { + return queryFactory + .selectFrom(contentTagGroup) + .join(contentTagGroup.content, content) + .fetchJoin() + .where(tagIn(tags)) + .orderBy(createdAtDesc()) + .fetch(); + } + private BooleanExpression contentIdIn(List contentList) { return contentTagGroup.content.in(contentList); } @@ -46,6 +59,10 @@ private BooleanExpression contentEq(Content content) { return contentTagGroup.content.eq(content); } + private BooleanExpression tagIn(List tagList) { + return contentTagGroup.tag.in(tagList); + } + 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 3c93394b..8975dcee 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 @@ -8,4 +8,6 @@ public interface TagCustomRepository { List queryTagByUserIdOrderByUsedAt(Long userId); List queryTagByTagIdIn(List tagIds); + + List queryTagByUserIdContainName(Long userId, String name); } 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 5deb5775..a8f5b9ad 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 @@ -28,6 +28,11 @@ public List queryTagByTagIdIn(List tagIds) { return queryFactory.selectFrom(tag).where(tagIdIn(tagIds)).fetch(); } + @Override + public List queryTagByUserIdContainName(Long userId, String name) { + return queryFactory.selectFrom(tag).where(userIdEq(userId), titleContain(name)).fetch(); + } + private BooleanExpression tagUserIdEq(Long userId) { return tag.userId.eq(userId); } @@ -40,6 +45,14 @@ private BooleanExpression tagIdIn(List tagIds) { return tag.id.in(tagIds); } + private BooleanExpression userIdEq(Long userId) { + return tag.userId.eq(userId); + } + + private BooleanExpression titleContain(String name) { + return tag.name.contains(name); + } + private OrderSpecifier createdAtDesc() { return tag.createdAt.desc(); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java index 47c902ed..20198c8e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/service/ContentDomainService.java @@ -4,7 +4,6 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.content.adaptor.ContentAdaptor; import allchive.server.domain.domains.content.domain.Content; -import allchive.server.domain.domains.content.domain.enums.ContentType; import java.util.List; import lombok.RequiredArgsConstructor; @@ -23,7 +22,7 @@ public void softDeleteById(Long contentId) { save(content); } - public void restoreInIdList(List contentIds) { + public void restoreByIdIn(List contentIds) { List contentList = contentAdaptor.findAllByIdIn(contentIds); contentList.forEach(Content::restore); contentAdaptor.saveAll(contentList); @@ -39,17 +38,13 @@ public void deleteAllByArchivingIdIn(List archivingId) { public void update( Long contentId, - ContentType contentType, Long archivingId, String link, String memo, String imgUrl, String title) { Content content = contentAdaptor.findById(contentId); - switch (contentType) { - case LINK -> content.updateLinkContent(archivingId, link, title, memo); - case IMAGE -> content.updateImageContent(archivingId, imgUrl, title, memo); - } + content.updateContent(archivingId, imgUrl, link, title, memo); contentAdaptor.save(content); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java index 5aea2a35..43b0413a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/ContentValidator.java @@ -8,6 +8,7 @@ import allchive.server.domain.domains.content.domain.Content; import allchive.server.domain.domains.content.exception.exceptions.ContentNotFoundException; import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateContentException; +import allchive.server.domain.domains.content.exception.exceptions.NotPublicContentException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -52,4 +53,17 @@ public void verifyUser(Long contentId, Long userId) { throw NoAuthorityUpdateContentException.EXCEPTION; } } + + public void validateNotDelete(Long contentId) { + contentAdaptor.findById(contentId).validateNotDelete(); + } + + public void validatePublic(Long contentId, Long userId) { + Content content = contentAdaptor.findById(contentId); + Archiving archiving = archivingAdaptor.findById(content.getArchivingId()); + if (archiving.getPublicStatus().equals(Boolean.FALSE) + && !archiving.getUserId().equals(userId)) { + throw NotPublicContentException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java index af25a203..305d7e89 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/content/validator/TagValidator.java @@ -4,7 +4,6 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.content.adaptor.TagAdaptor; import allchive.server.domain.domains.content.domain.Tag; -import allchive.server.domain.domains.content.exception.exceptions.NoAuthorityUpdateTagException; import allchive.server.domain.domains.content.exception.exceptions.TagNotFoundException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -23,11 +22,6 @@ public void validateExistTagsAndUser(List tagIds, Long userId) { if (tagIds.size() != tags.size()) { throw TagNotFoundException.EXCEPTION; } - tags.forEach( - tag -> { - if (!tag.getUserId().equals(userId)) { - throw NoAuthorityUpdateTagException.EXCEPTION; - } - }); + tags.forEach(tag -> tag.validateUser(userId)); } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java index b2055e21..3a1a585a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/adaptor/RecycleAdaptor.java @@ -17,9 +17,9 @@ public void save(Recycle recycle) { recycleRepository.save(recycle); } - public List queryRecycleByUserIdInArchivingIdListAndContentIdList( + public List queryRecycleByUserIdAndArchivingIdInOrUserIdAndContentIdIn( List archivingIds, List contentIds, Long userId) { - return recycleRepository.queryRecycleByUserIdInArchivingIdListAndContentIdList( + return recycleRepository.queryRecycleByUserIdAndArchivingIdInOrUserIdAndContentIdIn( archivingIds, contentIds, userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java index 4b978a54..16ecaad8 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/domain/enums/RecycleType.java @@ -11,8 +11,8 @@ @Getter @AllArgsConstructor public enum RecycleType { - CONTENT("content"), - ARCHIVING("archiving"); + CONTENT("CONTENT"), + ARCHIVING("ARCHIVING"); @JsonValue private String value; diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java index 59d537b8..b52c9e50 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepository.java @@ -5,6 +5,6 @@ import java.util.List; public interface RecycleCustomRepository { - List queryRecycleByUserIdInArchivingIdListAndContentIdList( + List queryRecycleByUserIdAndArchivingIdInOrUserIdAndContentIdIn( List archivingIds, List contentIds, Long userId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java index 682ce446..4c6a0cff 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/repository/RecycleCustomRepositoryImpl.java @@ -15,7 +15,7 @@ public class RecycleCustomRepositoryImpl implements RecycleCustomRepository { private final JPAQueryFactory queryFactory; @Override - public List queryRecycleByUserIdInArchivingIdListAndContentIdList( + public List queryRecycleByUserIdAndArchivingIdInOrUserIdAndContentIdIn( List archivingIds, List contentIds, Long userId) { return queryFactory .selectFrom(recycle) diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java index 9763eae5..318089de 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/service/RecycleDomainService.java @@ -16,10 +16,10 @@ public void save(Recycle recycle) { recycleAdaptor.save(recycle); } - public void deleteAllByUserIdAndArchivingIdOrUserIdAndContentId( + public void deleteAllByUserIdAndArchivingIdInOrUserIdAndContentIdIn( List archivingIds, List contentIds, Long userId) { List recycleList = - recycleAdaptor.queryRecycleByUserIdInArchivingIdListAndContentIdList( + recycleAdaptor.queryRecycleByUserIdAndArchivingIdInOrUserIdAndContentIdIn( archivingIds, contentIds, userId); recycleAdaptor.deleteAll(recycleList); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java index 18c7e55e..385cd26a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/recycle/validator/RecycleValidator.java @@ -17,7 +17,7 @@ public class RecycleValidator { public void validateExist(List archivingIds, List contentIds, Long userId) { List recycleList = - recycleAdaptor.queryRecycleByUserIdInArchivingIdListAndContentIdList( + recycleAdaptor.queryRecycleByUserIdAndArchivingIdInOrUserIdAndContentIdIn( archivingIds, contentIds, userId); Long archivingCnt = recycleList.stream() diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java index 16284ef0..6bab700e 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/adaptor/ReportAdaptor.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.Adaptor; import allchive.server.domain.domains.report.domain.Report; import allchive.server.domain.domains.report.repository.ReportRepository; +import java.util.List; import lombok.RequiredArgsConstructor; @Adaptor @@ -26,4 +27,9 @@ public Boolean queryReportExistByUserIdAndArchivingId(Long userId, Long archivin public void deleteAllByReportedUserId(Long userId) { reportRepository.deleteAllByReportedUserId(userId); } + + public void deleteAllByArchivingIdInOrContentIdIn( + List archivingIds, List contentIds) { + reportRepository.queryDeleteAllByArchivingIdInOrContentIdIn(archivingIds, contentIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportObjectType.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportObjectType.java index 5c90c3c7..46d895a3 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportObjectType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportObjectType.java @@ -11,8 +11,8 @@ @Getter @AllArgsConstructor public enum ReportObjectType { - CONTENT("content"), - ARCHIVING("archiving"); + CONTENT("CONTENT"), + ARCHIVING("ARCHIVING"); @JsonValue private String value; diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java index ad0f8cc4..abe43c72 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/domain/enums/ReportedType.java @@ -11,14 +11,14 @@ @Getter @AllArgsConstructor public enum ReportedType { - SPAM("spam"), // 스팸이에요 - OBSCENE("obscene"), // 음란성 컨텐츠를 담고있어요 - PERSONAL_INFO("personalInfo"), // 개인정보를 노출하고 있어요 - INTELLECTUAL_PROPERTY("intellectualProperty"), // 지식재산권을 침해해요 - VIOLENCE("violence"), // 혐오/폭력 컨텐츠를 담고 있어요 - ILLEGAL_INFO("illegalInformation"), // 불법 정보를 포함하고 있어요 - FRAUD("fraud"), // 사기 또는 피싱성 링크를 포함하고 있어요 - ETC("etc"); // 기타 + SPAM("SPAM"), // 스팸이에요 + OBSCENE("OBSCENE"), // 음란성 컨텐츠를 담고있어요 + PERSONAL_INFO("PERSONAL_INFO"), // 개인정보를 노출하고 있어요 + INTELLECTUAL_PROPERTY("INTELLECTUAL_PROPERTY"), // 지식재산권을 침해해요 + VIOLENCE("VIOLENCE"), // 혐오/폭력 컨텐츠를 담고 있어요 + ILLEGAL_INFO("ILLEGAL_INFO"), // 불법 정보를 포함하고 있어요 + FRAUD("FRAUD"), // 사기 또는 피싱성 링크를 포함하고 있어요 + ETC("ETC"); // 기타 @JsonValue private String value; diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java index 8c7a1f08..51df9782 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepository.java @@ -1,7 +1,12 @@ package allchive.server.domain.domains.report.repository; + +import java.util.List; + public interface ReportCustomRepository { Boolean queryReportExistByUserIdAndContentId(Long userId, Long contentId); Boolean queryReportExistByUserIdAndArchivingId(Long userId, Long archivingId); + + void queryDeleteAllByArchivingIdInOrContentIdIn(List archivingIds, List contentIds); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java index d1ea0a1c..706de473 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/repository/ReportCustomRepositoryImpl.java @@ -4,6 +4,7 @@ import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -32,6 +33,12 @@ public Boolean queryReportExistByUserIdAndArchivingId(Long userId, Long archivin return fetchOne != null; } + @Override + public void queryDeleteAllByArchivingIdInOrContentIdIn( + List archivingIds, List contentIds) { + queryFactory.delete(report).where(archivingIdInOrContentIdIn(archivingIds, contentIds)); + } + private BooleanExpression userIdEq(Long userId) { return report.userId.eq(userId); } @@ -43,4 +50,9 @@ private BooleanExpression contentIdEq(Long contentId) { private BooleanExpression archivingIdEq(Long archivingId) { return report.archivingId.eq(archivingId); } + + private BooleanExpression archivingIdInOrContentIdIn( + List archivingIds, List contentIds) { + return report.archivingId.in(archivingIds).or(report.contentId.in(contentIds)); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java index 00a0e4ad..957f59e9 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/report/service/ReportDomainService.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.report.adaptor.ReportAdaptor; import allchive.server.domain.domains.report.domain.Report; +import java.util.List; import lombok.RequiredArgsConstructor; @DomainService @@ -18,4 +19,9 @@ public void save(Report report) { public void deleteAllByReportedUserId(Long userId) { reportAdaptor.deleteAllByReportedUserId(userId); } + + public void deleteAllByArchivingIdInOrContentIdIn( + List archivingIds, List contentIds) { + reportAdaptor.deleteAllByArchivingIdInOrContentIdIn(archivingIds, contentIds); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java index c1087477..4b0c6cee 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/search/adaptor/LatestSearchAdaptor.java @@ -27,4 +27,12 @@ public void save(LatestSearch newSearch) { public void deleteAllByUserId(Long userId) { latestSearchRepository.deleteAllByUserId(userId); } + + public List findAllByIdIn(List ids) { + return latestSearchRepository.findAllByIdIn(ids); + } + + public void deleteAllByIdIn(List ids) { + latestSearchRepository.deleteAllByIdIn(ids); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java b/Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java index 78b88f36..6f36edbd 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java +++ b/Domain/src/main/java/allchive/server/domain/domains/search/domain/LatestSearch.java @@ -2,6 +2,7 @@ import allchive.server.domain.common.model.BaseTimeEntity; +import allchive.server.domain.domains.search.exception.exceptions.NoAuthorityUpdateLatestSearchException; import javax.persistence.*; import lombok.AccessLevel; import lombok.Builder; @@ -30,4 +31,10 @@ private LatestSearch(String keyword, Long userId) { public static LatestSearch of(String keyword, Long userId) { return LatestSearch.builder().keyword(keyword).userId(userId).build(); } + + public void validateUser(Long userId) { + if (!this.userId.equals(userId)) { + throw NoAuthorityUpdateLatestSearchException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/exception/SearchErrorCode.java b/Domain/src/main/java/allchive/server/domain/domains/search/exception/SearchErrorCode.java new file mode 100644 index 00000000..b0d4154f --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/exception/SearchErrorCode.java @@ -0,0 +1,23 @@ +package allchive.server.domain.domains.search.exception; + +import static allchive.server.core.consts.AllchiveConst.*; + +import allchive.server.core.dto.ErrorReason; +import allchive.server.core.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum SearchErrorCode implements BaseErrorCode { + NO_AUTHORITY_UPDATE_LATEST_SEARCH(FORBIDDEN, "LATESTSEARCH_403_1", "최근 검색어 수정 권한이 없습니다."), + LATEST_SEARCH_NOT_FOUND(NOT_FOUND, "LATESTSEARCH_404_1", "최근 검색어를 찾을 수 없습니다."); + private int status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status, code, reason); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/exception/exceptions/LatestSearchNotFoundException.java b/Domain/src/main/java/allchive/server/domain/domains/search/exception/exceptions/LatestSearchNotFoundException.java new file mode 100644 index 00000000..da24ca92 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/exception/exceptions/LatestSearchNotFoundException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.search.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.search.exception.SearchErrorCode; + +public class LatestSearchNotFoundException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new LatestSearchNotFoundException(); + + private LatestSearchNotFoundException() { + super(SearchErrorCode.LATEST_SEARCH_NOT_FOUND); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/exception/exceptions/NoAuthorityUpdateLatestSearchException.java b/Domain/src/main/java/allchive/server/domain/domains/search/exception/exceptions/NoAuthorityUpdateLatestSearchException.java new file mode 100644 index 00000000..5254a849 --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/exception/exceptions/NoAuthorityUpdateLatestSearchException.java @@ -0,0 +1,14 @@ +package allchive.server.domain.domains.search.exception.exceptions; + + +import allchive.server.core.error.BaseErrorException; +import allchive.server.domain.domains.search.exception.SearchErrorCode; + +public class NoAuthorityUpdateLatestSearchException extends BaseErrorException { + + public static final BaseErrorException EXCEPTION = new NoAuthorityUpdateLatestSearchException(); + + private NoAuthorityUpdateLatestSearchException() { + super(SearchErrorCode.NO_AUTHORITY_UPDATE_LATEST_SEARCH); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java b/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java index 20ca1d86..27636cca 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/search/repository/LatestSearchRepository.java @@ -9,4 +9,8 @@ public interface LatestSearchRepository extends JpaRepository findAllByUserIdOrderByCreatedAt(Long userId); void deleteAllByUserId(Long userId); + + List findAllByIdIn(List ids); + + void deleteAllByIdIn(List ids); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java index 533529ac..2de4ad1c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/search/service/LatestSearchDomainService.java @@ -4,6 +4,7 @@ import allchive.server.core.annotation.DomainService; import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor; import allchive.server.domain.domains.search.domain.LatestSearch; +import java.util.List; import lombok.RequiredArgsConstructor; @DomainService @@ -22,4 +23,8 @@ public void save(LatestSearch newSearch) { public void deleteAllByUserId(Long userId) { latestSearchAdaptor.deleteAllByUserId(userId); } + + public void deleteAllByIdIn(List ids) { + latestSearchAdaptor.deleteAllByIdIn(ids); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/search/validator/LatestSearchValidator.java b/Domain/src/main/java/allchive/server/domain/domains/search/validator/LatestSearchValidator.java new file mode 100644 index 00000000..7e26fd0a --- /dev/null +++ b/Domain/src/main/java/allchive/server/domain/domains/search/validator/LatestSearchValidator.java @@ -0,0 +1,27 @@ +package allchive.server.domain.domains.search.validator; + + +import allchive.server.core.annotation.Validator; +import allchive.server.domain.domains.search.adaptor.LatestSearchAdaptor; +import allchive.server.domain.domains.search.domain.LatestSearch; +import allchive.server.domain.domains.search.exception.exceptions.LatestSearchNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@Validator +@RequiredArgsConstructor +public class LatestSearchValidator { + private final LatestSearchAdaptor latestSearchAdaptor; + + public void validateExistByIdIn(List ids) { + List searches = latestSearchAdaptor.findAllByIdIn(ids); + if (searches.size() != ids.size()) { + throw LatestSearchNotFoundException.EXCEPTION; + } + } + + public void verifyUserByIdIn(List ids, Long userId) { + List searches = latestSearchAdaptor.findAllByIdIn(ids); + searches.forEach(search -> search.validateUser(userId)); + } +} diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java index 533d97eb..2534f4eb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/ScrapAdaptor.java @@ -39,4 +39,8 @@ public void deleteAllByArchivingIdIn(List archivingIds) { public void deleteAllByUser(User user) { scrapRepository.deleteAllByUser(user); } + + public boolean existsByUserAndArchivingId(User user, Long archivingId) { + return scrapRepository.existsByUserAndArchivingId(user, archivingId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java index b118c6ae..d800f01a 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/adaptor/UserAdaptor.java @@ -39,4 +39,8 @@ public Boolean existsByNickname(String nickname) { public List findAllByIdIn(List userIds) { return userRepository.findAllByIdIn(userIds); } + + public Boolean existsById(Long userId) { + return userRepository.existsById(userId); + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java index c5a87470..016e069f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/domain/User.java @@ -46,31 +46,45 @@ public class User extends BaseTimeEntity { @Enumerated(EnumType.STRING) private UserRole userRole = UserRole.USER; + private boolean marketingAgreement = false; + @Convert(converter = StringListConverter.class) private List categories = new ArrayList<>(); @Builder private User( + String name, + String email, String nickname, String profileImgUrl, List categoryList, + boolean marketingAgreement, OauthInfo oauthInfo) { + this.name = name; + this.email = email; this.nickname = nickname; this.profileImgUrl = profileImgUrl; this.categories = categoryList; + this.marketingAgreement = marketingAgreement; this.oauthInfo = oauthInfo; this.lastLoginAt = LocalDateTime.now(); } public static User of( + String name, + String email, String nickname, String profileImgUrl, List categoryList, + boolean marketingAgreement, OauthInfo oauthInfo) { return User.builder() + .name(name) + .email(email) .nickname(nickname) .profileImgUrl(profileImgUrl) .categoryList(categoryList) + .marketingAgreement(marketingAgreement) .oauthInfo(oauthInfo) .build(); } @@ -104,4 +118,10 @@ public void updateInfo(String name, String email, String nickname, String imgUrl this.nickname = nickname; this.profileImgUrl = imgUrl; } + + public void validateUserStateNormal() { + if (!this.userState.equals(UserState.NORMAL)) { + throw ForbiddenUserException.EXCEPTION; + } + } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java index 4ccfba30..7600377c 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/repository/ScrapRepository.java @@ -15,4 +15,6 @@ public interface ScrapRepository extends JpaRepository { void deleteAllByArchivingIdIn(List archivingId); void deleteAllByUser(User user); + + boolean existsByUserAndArchivingId(User user, Long archivingId); } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java index 5c49df2b..e34cad15 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/service/UserDomainService.java @@ -24,12 +24,23 @@ public Boolean checkUserCanLogin(OauthInfo oauthInfo) { @Transactional public User registerUser( + String name, + String email, String nickname, String profileImgUrl, List categoryList, + boolean marketingAgreement, OauthInfo oauthInfo) { userValidator.validUserCanRegister(oauthInfo); - final User newUser = User.of(nickname, profileImgUrl, categoryList, oauthInfo); + final User newUser = + User.of( + name, + email, + nickname, + profileImgUrl, + categoryList, + marketingAgreement, + oauthInfo); userAdaptor.save(newUser); return newUser; } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java index 153ee1e7..1ab5a89f 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/ScrapValidator.java @@ -16,13 +16,13 @@ public class ScrapValidator { public void validateExistScrap(Long userId, Long archivingId) { User user = userAdaptor.findById(userId); - if (isScrapExist(user, archivingId)) { - throw AlreadyExistScrapException.EXCEPTION; - } + scrapAdaptor.findByUserAndArchivingId(user, archivingId); } - private Boolean isScrapExist(User user, Long archivingId) { - scrapAdaptor.findByUserAndArchivingId(user, archivingId); - return Boolean.TRUE; + public void validateNotExistScrap(Long userId, Long archivingId) { + User user = userAdaptor.findById(userId); + if (scrapAdaptor.existsByUserAndArchivingId(user, archivingId)) { + throw AlreadyExistScrapException.EXCEPTION; + } } } diff --git a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java index a79f04f3..866e67fb 100644 --- a/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java +++ b/Domain/src/main/java/allchive/server/domain/domains/user/validator/UserValidator.java @@ -4,9 +4,8 @@ import allchive.server.core.annotation.Validator; import allchive.server.domain.domains.user.adaptor.UserAdaptor; import allchive.server.domain.domains.user.domain.enums.OauthInfo; -import allchive.server.domain.domains.user.domain.enums.UserState; import allchive.server.domain.domains.user.exception.exceptions.AlreadySignUpUserException; -import allchive.server.domain.domains.user.exception.exceptions.ForbiddenUserException; +import allchive.server.domain.domains.user.exception.exceptions.UserNotFoundException; import lombok.RequiredArgsConstructor; @Validator @@ -23,8 +22,12 @@ public Boolean checkUserCanRegister(OauthInfo oauthInfo) { } public void validateUserStatusNormal(Long userId) { - if (!userAdaptor.findById(userId).getUserState().equals(UserState.NORMAL)) { - throw ForbiddenUserException.EXCEPTION; + userAdaptor.findById(userId).validateUserStateNormal(); + } + + public void validateExist(Long userId) { + if (!userAdaptor.existsById(userId)) { + throw UserNotFoundException.EXCEPTION; } } } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java index e55b74e1..a5e0cfb7 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/apple/helper/AppleLoginUtil.java @@ -8,6 +8,7 @@ import com.nimbusds.jose.crypto.ECDSASigner; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -19,18 +20,10 @@ import java.util.Date; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; public class AppleLoginUtil { - /** - * client_secret 생성 Apple Document URL ‣ - * https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens - * - * @return client_secret(jwt) - */ public static String createClientSecret( - String teamId, String clientId, String keyId, String keyPath, String authUrl) { + String teamId, String clientId, String keyId, String authKey, String authUrl) { JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(keyId).build(); JWTClaimsSet claimsSet = new JWTClaimsSet(); @@ -44,7 +37,7 @@ public static String createClientSecret( SignedJWT jwt = new SignedJWT(header, claimsSet); - PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(readPrivateKey(keyPath)); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(readPrivateKey(authKey)); try { KeyFactory kf = KeyFactory.getInstance("EC"); ECPrivateKey ecPrivateKey = (ECPrivateKey) kf.generatePrivate(spec); @@ -64,12 +57,10 @@ public static String createClientSecret( * * @return Private Key */ - private static byte[] readPrivateKey(String keyPath) { - - Resource resource = new ClassPathResource(keyPath); + private static byte[] readPrivateKey(String authKey) { byte[] content = null; - - try (InputStream keyInputStream = resource.getInputStream(); + byte[] byteAuthKey = authKey.replace("((()))", "\n").getBytes(); + try (InputStream keyInputStream = new ByteArrayInputStream(byteAuthKey); InputStreamReader keyReader = new InputStreamReader(keyInputStream); PemReader pemReader = new PemReader(keyReader)) { PemObject pemObject = pemReader.readPemObject(); diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java index 200cb760..9cf5b46d 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/client/KakaoInfoClient.java @@ -2,9 +2,11 @@ import allchive.server.infrastructure.oauth.kakao.config.KakaoInfoConfig; +import allchive.server.infrastructure.oauth.kakao.dto.KakaoInformationResponse; import allchive.server.infrastructure.oauth.kakao.dto.KakaoUnlinkTarget; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; @@ -16,4 +18,7 @@ public interface KakaoInfoClient { @PostMapping(path = "/v1/user/unlink", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) void unlinkUser( @RequestHeader("Authorization") String adminKey, KakaoUnlinkTarget unlinkKaKaoTarget); + + @GetMapping("/v2/user/me") + KakaoInformationResponse kakaoUserInfo(@RequestHeader("Authorization") String accessToken); } diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java index ff4a9e9b..1e5465db 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/oauth/kakao/dto/KakaoInformationResponse.java @@ -13,7 +13,6 @@ public class KakaoInformationResponse { private Properties properties; private String id; - private KakaoAccount kakaoAccount; @Getter @@ -27,41 +26,18 @@ public static class Properties { @NoArgsConstructor @JsonNaming(SnakeCaseStrategy.class) public static class KakaoAccount { - - private Profile profile; private String email; - private String phoneNumber; - private String name; - - @Getter - @NoArgsConstructor - @JsonNaming(SnakeCaseStrategy.class) - public static class Profile { - private String profileImageUrl; - } - - public String getProfileImageUrl() { - return profile.getProfileImageUrl(); - } } public String getId() { return id; } - public String getEmail() { - return kakaoAccount.getEmail(); - } - - public String getPhoneNumber() { - return kakaoAccount.getPhoneNumber(); - } - - public String getName() { - return kakaoAccount.getName() != null ? kakaoAccount.getName() : properties.getNickname(); + public String getNickName() { + return properties.getNickname(); } - public String getProfileUrl() { - return kakaoAccount.getProfileImageUrl(); + public String getEmail() { + return kakaoAccount.getEmail(); } } 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 new file mode 100644 index 00000000..c8a2c15d --- /dev/null +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3DeleteObjectService.java @@ -0,0 +1,32 @@ +package allchive.server.infrastructure.s3.service; + + +import allchive.server.core.error.exception.S3ObjectNotFoundException; +import com.amazonaws.services.s3.AmazonS3; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class S3DeleteObjectService { + private final AmazonS3 amazonS3; + + @Value("${aws.s3.bucket}") + private String bucket; + + public void deleteS3Object(List keys) { + keys.forEach( + key -> { + validateExistObject(key); + amazonS3.deleteObject(bucket, key); + }); + } + + private void validateExistObject(String key) { + if (!amazonS3.doesObjectExist(bucket, key)) { + throw S3ObjectNotFoundException.EXCEPTION; + } + } +} diff --git a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3PresignedUrlService.java similarity index 88% rename from Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java rename to Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3PresignedUrlService.java index 78211363..81bd68ac 100644 --- a/Infrastructure/src/main/java/allchive/server/infrastructure/s3/S3PresignedUrlService.java +++ b/Infrastructure/src/main/java/allchive/server/infrastructure/s3/service/S3PresignedUrlService.java @@ -1,7 +1,9 @@ -package allchive.server.infrastructure.s3; +package allchive.server.infrastructure.s3.service; import allchive.server.core.error.exception.InternalServerError; +import allchive.server.infrastructure.s3.ImageUrlDto; +import allchive.server.infrastructure.s3.PresignedType; import com.amazonaws.HttpMethod; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.Headers; @@ -38,11 +40,11 @@ private String generateFileName(Long id, PresignedType presignedType) { String fileName; switch (presignedType) { case USER -> fileName = baseUrl + "/user/"; - case CONTENT -> fileName = baseUrl + "/content/"; - case ARCHIVING -> fileName = baseUrl + "/archiving/"; + case CONTENT -> fileName = baseUrl + "/content/" + id.toString(); + case ARCHIVING -> fileName = baseUrl + "/archiving/" + id.toString(); default -> throw InternalServerError.EXCEPTION; } - return fileName + id.toString() + "/" + UUID.randomUUID(); + return fileName + "/" + UUID.randomUUID(); } private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String fileName) {