From 2f7c691e6dc44a2f2691706eb89e20a6954e37e8 Mon Sep 17 00:00:00 2001 From: KJBig Date: Mon, 7 Oct 2024 02:00:15 +0900 Subject: [PATCH] feat : admin migration --- sluv-admin/build.gradle | 11 +- .../java/com/sluv/admin/AdminApplication.java | 3 + .../controller/BrandDashBoardController.java | 33 +++++ .../sluv/admin/brand/dto/HotBrandResDto.java | 30 ++++ .../admin/brand/service/BrandService.java | 20 +++ .../comment/controller/CommentController.java | 74 ++++++++++ .../CommentDashBoardController.java | 32 ++++ .../dto/CommentBlockCountResponse.java | 42 ++++++ .../dto/CommentReportDetailResponse.java | 51 +++++++ .../comment/dto/CommentReportInfoDto.java | 50 +++++++ .../dto/UpdateCommentReportResDto.java | 20 +++ .../comment/service/CommentReportService.java | 69 +++++++++ .../admin/comment/service/CommentService.java | 47 ++++++ .../exception/GlobalExceptionHandler.java | 139 ++++++++++++++++++ .../admin/common/response/ErrorResponse.java | 32 ++++ .../response/PaginationCountResponse.java | 19 +++ .../common/response/PaginationResponse.java | 31 ++++ .../common/response/SuccessDataResponse.java | 26 ++++ .../common/response/SuccessResponse.java | 24 +++ .../service/FCMNotificationService.java | 46 ++++++ .../service/ReportProcessingService.java | 44 ++++++ .../sluv/admin/config/ComponentConfig.java | 9 ++ .../com/sluv/admin/config/SwaggerConfig.java | 54 +++++++ .../admin/config/security/CorsConfig.java | 30 ++++ .../config/security/SpringSecurityConfig.java | 39 +++++ .../admin/item/controller/ItemController.java | 74 ++++++++++ .../controller/ItemDashBoardController.java | 59 ++++++++ .../admin/item/dto/BrandSearchResDto.java | 32 ++++ .../admin/item/dto/CelebSearchResDto.java | 63 ++++++++ .../sluv/admin/item/dto/HotItemResDto.java | 36 +++++ .../sluv/admin/item/dto/ItemCategoryDto.java | 38 +++++ .../sluv/admin/item/dto/ItemImgResDto.java | 27 ++++ .../sluv/admin/item/dto/ItemLinkResDto.java | 25 ++++ .../admin/item/dto/ItemReportDetailDto.java | 77 ++++++++++ .../admin/item/dto/ItemReportInfoDto.java | 49 ++++++ .../item/dto/UpdateItemReportResDto.java | 20 +++ .../admin/item/service/ItemReportService.java | 80 ++++++++++ .../sluv/admin/item/service/ItemService.java | 51 +++++++ .../controller/QuestionController.java | 74 ++++++++++ .../question/dto/QuestionReportDetailDto.java | 55 +++++++ .../question/dto/QuestionReportInfoDto.java | 49 ++++++ .../dto/UpdateQuestionReportResDto.java | 20 +++ .../service/QuestionReportService.java | 69 +++++++++ .../admin/user/controller/UserController.java | 93 ++++++++++++ .../controller/UserDashBoardController.java | 72 +++++++++ .../sluv/admin/user/dto/HotUserResDto.java | 39 +++++ .../user/dto/UpdateUserReportResDto.java | 20 +++ .../user/dto/UserAccountCountResDto.java | 42 ++++++ .../sluv/admin/user/dto/UserAdminInfoDto.java | 34 +++++ .../user/dto/UserCountByCategoryResDto.java | 51 +++++++ .../admin/user/dto/UserReportInfoDto.java | 48 ++++++ .../admin/user/service/UserReportService.java | 54 +++++++ .../sluv/admin/user/service/UserService.java | 100 +++++++++++++ .../controller/VisitHistoryController.java | 35 +++++ .../visit/dto/VisitHistoryCountResDto.java | 43 ++++++ .../visit/service/VisitHistoryService.java | 47 ++++++ sluv-admin/src/main/resources/application.yml | 8 +- .../api/brand/controller/BrandController.java | 3 +- .../controller/CelebActivityController.java | 3 +- .../api/celeb/controller/CelebController.java | 3 +- .../RecentSelectCelebController.java | 12 +- .../closet/controller/ClosetController.java | 10 +- .../controller/ClosetItemController.java | 9 +- .../comment/controller/CommentController.java | 9 +- .../controller/CommentReportController.java | 6 +- .../dto/reponse/SubCommentPageResponse.java | 3 +- .../item/controller/HashtagController.java | 7 +- .../controller/ItemCategoryController.java | 3 +- .../item/controller/PlaceRankController.java | 11 +- .../sluv/api/item/service/ItemService.java | 2 +- .../controller/QuestionController.java | 23 +-- .../search/controller/SearchController.java | 17 +-- .../search/service/SearchEngineService.java | 7 +- .../api/search/service/SearchService.java | 3 +- .../api/user/service/UserLikeService.java | 3 +- .../sluv/domain/brand/dto/BrandCountDto.java | 25 ++++ .../impl/BrandRepositoryCustom.java | 6 +- .../repository/impl/BrandRepositoryImpl.java | 26 +++- .../brand/service/BrandDomainService.java | 6 + .../domain/comment/entity/CommentReport.java | 17 +-- .../CommentReportNotFoundException.java | 14 ++ .../repository/CommentReportRepository.java | 3 +- .../impl/CommentReportRepositoryCustom.java | 14 ++ .../impl/CommentReportRepositoryImpl.java | 94 ++++++++++++ .../impl/CommentRepositoryCustom.java | 4 + .../impl/CommentRepositoryImpl.java | 17 ++- .../comment/service/CommentDomainService.java | 6 + .../service/CommentReportDomainService.java | 17 +++ .../domain/item/dto/ItemWithCountDto.java | 37 +++++ .../com/sluv/domain/item/entity/Item.java | 19 +-- .../sluv/domain/item/entity/ItemReport.java | 16 +- .../ItemReportNotFoundException.java | 14 ++ .../item/repository/ItemLinkRepository.java | 5 +- .../impl/ItemReportRepositoryCustom.java | 8 + .../impl/ItemReportRepositoryImpl.java | 124 +++++++++++++++- .../repository/impl/ItemRepositoryCustom.java | 5 + .../repository/impl/ItemRepositoryImpl.java | 33 +++++ .../item/service/ItemDomainService.java | 8 + .../item/service/ItemLinkDomainService.java | 4 +- .../item/service/ItemReportDomainService.java | 16 ++ .../question/entity/QuestionReport.java | 17 +-- .../QuestionReportNotFoundException.java | 14 ++ .../repository/QuestionReportRepository.java | 3 +- .../impl/QuestionReportRepositoryCustom.java | 15 ++ .../impl/QuestionReportRepositoryImpl.java | 88 +++++++++++ .../service/QuestionReportDomainService.java | 17 +++ .../domain/user/dto/UserReportStackDto.java | 19 +++ .../user/dto/UserWithFollowerCountDto.java | 27 ++++ .../sluv/domain/user/entity/UserReport.java | 17 +-- .../domain/user/entity/UserReportStack.java | 21 +-- .../InvalidReportStatusException.java | 15 ++ .../UserReportNotFoundException.java | 13 ++ .../repository/UserReportStackRepository.java | 6 + .../impl/UserReportRepositoryCustom.java | 6 + .../impl/UserReportRepositoryImpl.java | 51 ++++++- .../repository/impl/UserRepositoryCustom.java | 6 + .../repository/impl/UserRepositoryImpl.java | 49 ++++++ .../user/service/UserDomainService.java | 14 ++ .../user/service/UserReportDomainService.java | 12 ++ .../service/UserReportStackDomainService.java | 13 ++ .../sluv/domain/visit/entity/DailyVisit.java | 32 ++++ .../repository/DailyVisitRepository.java | 7 + .../repository/VisitHistoryRepository.java | 3 +- .../impl/VisitHistoryRepositoryCustom.java | 10 ++ .../impl/VisitHistoryRepositoryImpl.java | 28 ++++ .../service/DailyVisitDomainService.java | 20 +++ .../service/VisitHistoryDomainService.java | 5 + 127 files changed, 3569 insertions(+), 190 deletions(-) create mode 100644 sluv-admin/src/main/java/com/sluv/admin/brand/controller/BrandDashBoardController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/brand/dto/HotBrandResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/brand/service/BrandService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentDashBoardController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentBlockCountResponse.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportDetailResponse.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportInfoDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/dto/UpdateCommentReportResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentReportService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/exception/GlobalExceptionHandler.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/response/ErrorResponse.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationCountResponse.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationResponse.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessDataResponse.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessResponse.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/service/FCMNotificationService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/common/service/ReportProcessingService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/config/ComponentConfig.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/config/SwaggerConfig.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/config/security/CorsConfig.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/config/security/SpringSecurityConfig.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemDashBoardController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/BrandSearchResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/CelebSearchResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/HotItemResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemCategoryDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemImgResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemLinkResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportDetailDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportInfoDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/dto/UpdateItemReportResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/service/ItemReportService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/item/service/ItemService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/question/controller/QuestionController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportDetailDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportInfoDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/question/dto/UpdateQuestionReportResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/question/service/QuestionReportService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/controller/UserController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/controller/UserDashBoardController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/dto/HotUserResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/dto/UpdateUserReportResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAccountCountResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAdminInfoDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/dto/UserCountByCategoryResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/dto/UserReportInfoDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/service/UserReportService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/user/service/UserService.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/visit/controller/VisitHistoryController.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/visit/dto/VisitHistoryCountResDto.java create mode 100644 sluv-admin/src/main/java/com/sluv/admin/visit/service/VisitHistoryService.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/brand/dto/BrandCountDto.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/comment/exception/CommentReportNotFoundException.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryCustom.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryImpl.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/item/dto/ItemWithCountDto.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/item/exception/ItemReportNotFoundException.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/question/exception/QuestionReportNotFoundException.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryCustom.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryImpl.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/user/dto/UserReportStackDto.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/user/dto/UserWithFollowerCountDto.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/user/exception/InvalidReportStatusException.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/user/exception/UserReportNotFoundException.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/visit/entity/DailyVisit.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/visit/repository/DailyVisitRepository.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryCustom.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryImpl.java create mode 100644 sluv-domain/src/main/java/com/sluv/domain/visit/service/DailyVisitDomainService.java diff --git a/sluv-admin/build.gradle b/sluv-admin/build.gradle index 8bb204ee..6d7258e2 100644 --- a/sluv-admin/build.gradle +++ b/sluv-admin/build.gradle @@ -11,11 +11,18 @@ jar.enabled = false dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - + implementation 'org.springframework.boot:spring-boot-starter-security' + + //swagger + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.3' + + testImplementation 'org.springframework.security:spring-security-test' + implementation project(":sluv-domain") implementation project(":sluv-common") + implementation project(":sluv-infra") } tasks.named('test') { - dependsOn ':sluv-domain:test', ':sluv-common:test' + dependsOn ':sluv-domain:test', ':sluv-common:test', ':sluv-infra:test' } diff --git a/sluv-admin/src/main/java/com/sluv/admin/AdminApplication.java b/sluv-admin/src/main/java/com/sluv/admin/AdminApplication.java index e852f57c..c5d3b1d0 100644 --- a/sluv-admin/src/main/java/com/sluv/admin/AdminApplication.java +++ b/sluv-admin/src/main/java/com/sluv/admin/AdminApplication.java @@ -1,8 +1,11 @@ package com.sluv.admin; +import com.sluv.domain.config.JpaConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; +@Import(JpaConfig.class) @SpringBootApplication public class AdminApplication { diff --git a/sluv-admin/src/main/java/com/sluv/admin/brand/controller/BrandDashBoardController.java b/sluv-admin/src/main/java/com/sluv/admin/brand/controller/BrandDashBoardController.java new file mode 100644 index 00000000..93a6ebd7 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/brand/controller/BrandDashBoardController.java @@ -0,0 +1,33 @@ +package com.sluv.admin.brand.controller; + +import com.sluv.admin.brand.dto.HotBrandResDto; +import com.sluv.admin.brand.service.BrandService; +import com.sluv.admin.common.response.SuccessDataResponse; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/backoffice/item/dashBoard") +public class BrandDashBoardController { + + private final BrandService brandService; + + @Operation( + summary = "대시보드 - 인기 사용 브랜드 조회", + description = "대시보드에서 가장 많이 사용된 브랜드 Top 3을 조회한다." + ) + @GetMapping("/hotBrand") + public ResponseEntity>> getHotBrand() { + List response = brandService.getTop3HotBrand(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/brand/dto/HotBrandResDto.java b/sluv-admin/src/main/java/com/sluv/admin/brand/dto/HotBrandResDto.java new file mode 100644 index 00000000..8e00eada --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/brand/dto/HotBrandResDto.java @@ -0,0 +1,30 @@ +package com.sluv.admin.brand.dto; + + +import com.sluv.domain.brand.entity.Brand; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class HotBrandResDto { + private Long id; + private String name; + private String imgUrl; + @Schema(description = "해당 Brand가 사용되는 횟수") + private Long useCount; + + public static HotBrandResDto of(Brand brand, Long useCount) { + return HotBrandResDto.builder() + .id(brand.getId()) + .name(brand.getBrandKr() + " (" + brand.getBrandEn() + ")") + .imgUrl(brand.getBrandImgUrl()) + .useCount(useCount) + .build(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/brand/service/BrandService.java b/sluv-admin/src/main/java/com/sluv/admin/brand/service/BrandService.java new file mode 100644 index 00000000..debdd882 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/brand/service/BrandService.java @@ -0,0 +1,20 @@ +package com.sluv.admin.brand.service; + +import com.sluv.admin.brand.dto.HotBrandResDto; +import com.sluv.domain.brand.service.BrandDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class BrandService { + private final BrandDomainService brandDomainService; + + public List getTop3HotBrand() { + return brandDomainService.getTopHotBrandWithLimit(3).stream() + .map(brandCountDto -> HotBrandResDto.of(brandCountDto.getBrand(), brandCountDto.getUseCount())) + .toList(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentController.java b/sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentController.java new file mode 100644 index 00000000..f425379c --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentController.java @@ -0,0 +1,74 @@ +package com.sluv.admin.comment.controller; + +import com.sluv.admin.comment.dto.CommentReportDetailResponse; +import com.sluv.admin.comment.dto.CommentReportInfoDto; +import com.sluv.admin.comment.dto.UpdateCommentReportResDto; +import com.sluv.admin.comment.service.CommentReportService; +import com.sluv.admin.common.response.ErrorResponse; +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.response.SuccessDataResponse; +import com.sluv.domain.common.enums.ReportStatus; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/backoffice/comment") +public class CommentController { + + private final CommentReportService commentReportService; + + @Operation( + summary = "댓글 신고 정보 조히", + description = "WAITING, COMPLETED, REJECTED 로 검색 조건, 없으면 전체 검색" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping("/report") + public ResponseEntity>> getAllCommentReport(Pageable pageable, + @RequestParam(required = false) ReportStatus reportStatus) { + PaginationResponse response = commentReportService.getAllCommentReport(pageable, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "댓글 신고 상세 정보 조히", + description = "댓글 신고 id를 통해 댓글 신고 상세 정보 조회" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping("/report/{commentReportId}") + public ResponseEntity> getCommentReportDetail(@PathVariable Long commentReportId) { + CommentReportDetailResponse response = commentReportService.getCommentReportDetail(commentReportId); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "댓글 신고 처리", + description = "댓글 신고 id와 reportStatus(COMPLETED, REJECTED)를 통해 댓글 신고 처리" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @PostMapping("/report/{commentReportId}") + public ResponseEntity> changeCommentReportStatus(@PathVariable Long commentReportId, + @RequestParam ReportStatus reportStatus) { + UpdateCommentReportResDto response = commentReportService.updateCommentReportStatus(commentReportId, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentDashBoardController.java b/sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentDashBoardController.java new file mode 100644 index 00000000..e5606930 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/controller/CommentDashBoardController.java @@ -0,0 +1,32 @@ +package com.sluv.admin.comment.controller; + +import com.sluv.admin.comment.dto.CommentBlockCountResponse; +import com.sluv.admin.comment.service.CommentService; +import com.sluv.admin.common.response.SuccessDataResponse; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/backoffice/comment/dashBoard") +public class CommentDashBoardController { + + private final CommentService commentService; + + @Operation( + summary = "대시보드 - Block된 댓글 통계 조회", + description = "대시보드에서 Block된 댓글 통계를 조회한다." + ) + @GetMapping("/blockCount") + public ResponseEntity> getCommentBlockCount() { + CommentBlockCountResponse response = commentService.getCommentBlockCount(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentBlockCountResponse.java b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentBlockCountResponse.java new file mode 100644 index 00000000..75ce9434 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentBlockCountResponse.java @@ -0,0 +1,42 @@ +package com.sluv.admin.comment.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CommentBlockCountResponse { + @Schema(description = "오늘 기준으로 한달전 대비 증가 비율") + private Double percent; + @Schema(description = "총 Block 댓글 수") + private Long totalCount; + @Schema(description = "이번주 기준으로 10주간 가입자 수 그래프") + private List countGraph; + + public static CommentBlockCountResponse of(Long totalCount, Long recentMonthCount, List countGraph) { + return CommentBlockCountResponse.builder() + .percent(getPercent(recentMonthCount, totalCount)) + .totalCount(totalCount) + .countGraph(countGraph) + .build(); + } + + private static Double getPercent(Long recentCount, Long totalCount) { + if (totalCount == 0) { + return 0.0; + } + // 계산 후 소숫점 2자리까지 반올림1 + return BigDecimal.valueOf((double) recentCount / totalCount * 100) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportDetailResponse.java b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportDetailResponse.java new file mode 100644 index 00000000..26349bc4 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportDetailResponse.java @@ -0,0 +1,51 @@ +package com.sluv.admin.comment.dto; + +import com.sluv.domain.comment.entity.Comment; +import com.sluv.domain.comment.entity.CommentReport; +import com.sluv.domain.comment.enums.CommentReportReason; +import com.sluv.domain.common.enums.ReportStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CommentReportDetailResponse { + + private Long reporterId; + private String reporterNickname; + private Long reportedId; + private String reportedNickname; + private Long reportId; + private CommentReportReason reportReason; + @Schema(description = "신고 상세 내용") + private String content; + @Schema(description = "신고 접수 상태") + private ReportStatus reportStatus; + @Schema(description = "신고 당한 원본 댓글 내용") + private String reportedCommentContent; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static CommentReportDetailResponse of(CommentReport commentReport, Comment comment) { + return CommentReportDetailResponse.builder() + .reporterId(commentReport.getReporter().getId()) + .reporterNickname(commentReport.getReporter().getNickname()) + .reportedId(comment.getUser().getId()) + .reportedNickname(comment.getUser().getNickname()) + .reportReason(commentReport.getCommentReportReason()) + .content(commentReport.getContent()) + .reportStatus(commentReport.getReportStatus()) + .reportedCommentContent(comment.getContent()) + .createdAt(commentReport.getCreatedAt()) + .updatedAt(commentReport.getUpdatedAt()) + .build(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportInfoDto.java b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportInfoDto.java new file mode 100644 index 00000000..317d2dd7 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/CommentReportInfoDto.java @@ -0,0 +1,50 @@ +package com.sluv.admin.comment.dto; + +import com.sluv.domain.comment.entity.Comment; +import com.sluv.domain.comment.entity.CommentReport; +import com.sluv.domain.comment.enums.CommentReportReason; +import com.sluv.domain.common.enums.ReportStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CommentReportInfoDto { + + private Long reporterId; + private String reporterNickname; + private Long reportedId; + private String reportedNickname; + private Long reportId; + @Schema(description = "댓글 신고 이유(enums)") + private CommentReportReason reportReason; + @Schema(description = "신고 상세 내용") + private String content; + @Schema(description = "신고 접수 상태") + private ReportStatus reportStatus; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static CommentReportInfoDto of(CommentReport commentReport, Comment comment) { + return CommentReportInfoDto.builder() + .reporterId(commentReport.getReporter().getId()) + .reporterNickname(commentReport.getReporter().getNickname()) + .reportedId(comment.getUser().getId()) + .reportedNickname(comment.getUser().getNickname()) + .reportId(commentReport.getId()) + .reportReason(commentReport.getCommentReportReason()) + .content(commentReport.getContent()) + .reportStatus(commentReport.getReportStatus()) + .createdAt(commentReport.getCreatedAt()) + .updatedAt(commentReport.getUpdatedAt()) + .build(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/dto/UpdateCommentReportResDto.java b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/UpdateCommentReportResDto.java new file mode 100644 index 00000000..b50bdbf8 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/dto/UpdateCommentReportResDto.java @@ -0,0 +1,20 @@ +package com.sluv.admin.comment.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UpdateCommentReportResDto { + + private ReportStatus reportStatus; + + public static UpdateCommentReportResDto of(ReportStatus reportStatus) { + return new UpdateCommentReportResDto( + reportStatus + ); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentReportService.java b/sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentReportService.java new file mode 100644 index 00000000..e2c5fd4c --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentReportService.java @@ -0,0 +1,69 @@ +package com.sluv.admin.comment.service; + +import com.sluv.admin.comment.dto.CommentReportDetailResponse; +import com.sluv.admin.comment.dto.CommentReportInfoDto; +import com.sluv.admin.comment.dto.UpdateCommentReportResDto; +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.service.ReportProcessingService; +import com.sluv.domain.comment.entity.CommentReport; +import com.sluv.domain.comment.enums.CommentStatus; +import com.sluv.domain.comment.service.CommentReportDomainService; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.exception.InvalidReportStatusException; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional +public class CommentReportService { + + private final CommentReportDomainService commentReportDomainService; + private final ReportProcessingService reportProcessingService; + + @Transactional(readOnly = true) + public PaginationResponse getAllCommentReport(Pageable pageable, ReportStatus reportStatus) { + Page commentReportPage = commentReportDomainService.getAllCommentReport(pageable, reportStatus); + List content = commentReportPage.getContent().stream() + .map(commentReport -> CommentReportInfoDto.of(commentReport, commentReport.getComment())) + .toList(); + return PaginationResponse.create(commentReportPage, content); + } + + @Transactional(readOnly = true) + public CommentReportDetailResponse getCommentReportDetail(Long commentReportId) { + CommentReport commentReport = commentReportDomainService.getCommentReportDetail(commentReportId); + return CommentReportDetailResponse.of(commentReport, commentReport.getComment()); + } + + public UpdateCommentReportResDto updateCommentReportStatus(Long commentReportId, ReportStatus reportStatus) { + if (reportStatus == ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + CommentReport commentReport = commentReportDomainService.findById(commentReportId); + + if (commentReport.getReportStatus() != ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + User reportedUser = commentReport.getComment().getUser(); + User reporterUser = commentReport.getReporter(); + + commentReport.changeCommentReportStatus(reportStatus); + + if (reportStatus == ReportStatus.COMPLETE) { + commentReport.getComment().changeStatus(CommentStatus.BLOCKED); + } + reportProcessingService.processReport(reportedUser, reporterUser, commentReport.getContent(), reportStatus); + + return UpdateCommentReportResDto.of(commentReport.getReportStatus()); + } +} + diff --git a/sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentService.java b/sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentService.java new file mode 100644 index 00000000..3b164981 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/comment/service/CommentService.java @@ -0,0 +1,47 @@ +package com.sluv.admin.comment.service; + +import com.sluv.admin.comment.dto.CommentBlockCountResponse; +import com.sluv.domain.comment.entity.Comment; +import com.sluv.domain.comment.service.CommentDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class CommentService { + + private final CommentDomainService commentDomainService; + + public CommentBlockCountResponse getCommentBlockCount() { + LocalDateTime now = LocalDateTime.now(); + List allBlackComment = commentDomainService.getAllBlockComment(); + long recentMonthCount = getRecentMonthCount(now, allBlackComment); + List countGraph = getCountGraph(now, allBlackComment); + return CommentBlockCountResponse.of(allBlackComment.stream().count(), recentMonthCount, countGraph); + } + + private static List getCountGraph(LocalDateTime now, List allBlockComment) { + List countGraph = new ArrayList<>(); + for (int i = 10; i > 0; i--) { + LocalDateTime startWeek = now.minusWeeks(i); + LocalDateTime endWeek = now.minusWeeks(i - 1); + long count = allBlockComment.stream() + .filter(comment -> + comment.getCreatedAt().isAfter(startWeek) && comment.getCreatedAt().isBefore(endWeek) + ) + .count(); + countGraph.add(count); + } + return countGraph; + } + + private static long getRecentMonthCount(LocalDateTime now, List allBlockComment) { + return allBlockComment.stream() + .filter(comment -> comment.getCreatedAt().isAfter(now.minusMonths(1))) + .count(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/exception/GlobalExceptionHandler.java b/sluv-admin/src/main/java/com/sluv/admin/common/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..eaf7328a --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,139 @@ +package com.sluv.admin.common.exception; + +import com.sluv.admin.common.response.ErrorResponse; +import com.sluv.common.exception.ApplicationException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import static com.sluv.common.exception.ErrorCode.*; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + private static final String LOG_FORMAT = "Error: {}, Class : {}, Message : {}, Stack : {}"; + private static final String LOG_CODE_FORMAT = "Error: {}, Class : {}, Code : {}, Message : {}, Stack : {}"; + + /** + * == Application Exception == + * + * @return Each errorCode + * @throws ApplicationException + */ + @ExceptionHandler(ApplicationException.class) + public ResponseEntity applicationException(ApplicationException exception) { + log.error( + LOG_CODE_FORMAT, + "ApplicationException", + exception.getClass().getSimpleName(), + exception.getErrorCode(), + exception.getMessage(), + exception.getStackTrace() + ); + HttpStatus httpStatus = HttpStatus.valueOf(exception.getHttpStatusCode().getCode()); + + return ResponseEntity + .status(httpStatus) + .body(ErrorResponse.builder() + .code(exception.getErrorCode()) + .message(exception.getMessage()) + .build() + ); + } + + @ExceptionHandler(HttpMessageNotReadableException.class) + public ResponseEntity handleMethodArgumentTypeMismatch(HttpMessageNotReadableException exception) { + log.error( + LOG_FORMAT, + "HttpMessageNotReadableException", + exception.getClass().getSimpleName(), + exception.getMessage(), + exception.getStackTrace() + ); + + return ResponseEntity.badRequest() + .body(ErrorResponse.customBuilder() + .errorCode(ENUM_ERROR) + .build() + ); + } + + + /** + * == 런타임 Exception == + * + * @return INTERNAL_SERVER_ERROR + * @throws RuntimeException + */ + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity runtimeException(RuntimeException exception) { + log.error( + LOG_FORMAT, + "RuntimeException", + exception.getClass().getSimpleName(), + exception.getMessage(), + exception.getStackTrace() + ); + + return ResponseEntity.internalServerError() + .body(ErrorResponse.customBuilder() + .errorCode(INTERNAL_SERVER_ERROR) + .build() + ); + } + + /** + * == DB Exception == + * + * @return DB_ACCESS_ERROR + * @throws DataAccessException + */ + + @ExceptionHandler(DataAccessException.class) + public ResponseEntity dataAccessException(DataAccessException exception) { + log.error( + LOG_FORMAT, + "DataAccessException", + exception.getClass().getSimpleName(), + exception.getMessage(), + exception.getStackTrace() + ); + + return ResponseEntity.badRequest() + .body(ErrorResponse.customBuilder() + .errorCode(DB_ACCESS_ERROR) + .build() + ); + } + + /** + * == 기타 Exception == + * + * @return INTERNAL_SERVER_ERROR + * @throws Exception + */ + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception exception) { + log.error( + LOG_FORMAT, + "Exception", + exception.getClass().getSimpleName(), + exception.getMessage(), + exception.getStackTrace() + ); + return ResponseEntity.internalServerError() + .body(ErrorResponse.customBuilder() + .errorCode(INTERNAL_SERVER_ERROR) + .build() + ); + + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/response/ErrorResponse.java b/sluv-admin/src/main/java/com/sluv/admin/common/response/ErrorResponse.java new file mode 100644 index 00000000..8862a6a1 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/response/ErrorResponse.java @@ -0,0 +1,32 @@ +package com.sluv.admin.common.response; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.sluv.common.exception.ErrorCode; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message"}) +public class ErrorResponse { + + private Boolean isSuccess; + private int code; + private String message; + + @Builder + public ErrorResponse(int code, String message) { + this.isSuccess = false; + this.code = code; + this.message = message; + } + + @Builder(builderClassName = "customBuilder", builderMethodName = "customBuilder") + public ErrorResponse(ErrorCode errorCode) { + this.isSuccess = false; + this.code = errorCode.getCode(); + this.message = errorCode.getMessage(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationCountResponse.java b/sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationCountResponse.java new file mode 100644 index 00000000..e811241a --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationCountResponse.java @@ -0,0 +1,19 @@ +package com.sluv.admin.common.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +public class PaginationCountResponse extends PaginationResponse { + @Schema(description = "전체 개수") + private Long countNum; + + public PaginationCountResponse(Boolean hasNext, Integer page, List content, Long countNum) { + super(hasNext, page, content); + this.countNum = countNum; + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationResponse.java b/sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationResponse.java new file mode 100644 index 00000000..8edf3a71 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/response/PaginationResponse.java @@ -0,0 +1,31 @@ +package com.sluv.admin.common.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.springframework.data.domain.Page; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +public class PaginationResponse { + @Schema(description = "다음 페이지 존재 여부") + private Boolean hasNext; + @Schema(description = "현재 page") + private Integer page; + @Schema(description = "데이터들") + private List content; + + public static PaginationResponse create(Page page, List content) { + return PaginationResponse.builder() + .page(page.getNumber()) + .hasNext(page.hasNext()) + .content(content) + .build(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessDataResponse.java b/sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessDataResponse.java new file mode 100644 index 00000000..b603a71b --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessDataResponse.java @@ -0,0 +1,26 @@ +package com.sluv.admin.common.response; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message", "result"}) +public class SuccessDataResponse extends SuccessResponse { + private T result; + + @Builder(access = AccessLevel.PRIVATE) + public SuccessDataResponse(T result) { + this.result = result; + } + + public static SuccessDataResponse create(T result) { + return SuccessDataResponse.builder() + .result(result) + .build(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessResponse.java b/sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessResponse.java new file mode 100644 index 00000000..3bff4b3e --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/response/SuccessResponse.java @@ -0,0 +1,24 @@ +package com.sluv.admin.common.response; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message"}) +public class SuccessResponse { + private final Boolean isSuccess = true; + + @Schema(defaultValue = "요청성공.") + private final String message = "요청성공."; + + @Schema(defaultValue = "1000") + private final int code = 1000; + + public static SuccessResponse create() { + return new SuccessResponse(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/service/FCMNotificationService.java b/sluv-admin/src/main/java/com/sluv/admin/common/service/FCMNotificationService.java new file mode 100644 index 00000000..4bea4b48 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/service/FCMNotificationService.java @@ -0,0 +1,46 @@ +//package com.sluv.admin.global.common.service; +// +//import com.google.firebase.messaging.FirebaseMessaging; +//import com.google.firebase.messaging.FirebaseMessagingException; +//import com.google.firebase.messaging.Message; +//import com.google.firebase.messaging.Notification; +//import com.sluv.backoffice.domain.user.entity.User; +//import com.sluv.backoffice.domain.user.exception.UserFCMTokenNotFoundException; +//import com.sluv.backoffice.domain.user.exception.UserNotFoundException; +//import com.sluv.backoffice.domain.user.repository.UserRepository; +//import lombok.RequiredArgsConstructor; +//import org.springframework.stereotype.Service; +// +//import java.util.Optional; +// +//@Service +//@RequiredArgsConstructor +//public class FCMNotificationService { +// +// private final FirebaseMessaging firebaseMessaging; +// private final UserRepository userRepository; +// +// public void sendFCMNotification(Long userId, String title, String body) { +// User user = userRepository.findById(userId) +// .orElseThrow(UserNotFoundException::new); +// +// String fcmToken = Optional.ofNullable(user.getFcmToken()) +// .orElseThrow(UserFCMTokenNotFoundException::new); +// +// Notification notification = Notification.builder() +// .setTitle(title) +// .setBody(body) +// .build(); +// +// Message message = Message.builder() +// .setToken(fcmToken) +// .setNotification(notification) +// .build(); +// +// try { +// firebaseMessaging.send(message); +// } catch (FirebaseMessagingException e) { +// e.printStackTrace(); +// } +// } +//} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/common/service/ReportProcessingService.java b/sluv-admin/src/main/java/com/sluv/admin/common/service/ReportProcessingService.java new file mode 100644 index 00000000..52ee0dab --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/common/service/ReportProcessingService.java @@ -0,0 +1,44 @@ +package com.sluv.admin.common.service; + +import com.sluv.domain.alarm.enums.AlarmType; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.enums.UserStatus; +import com.sluv.domain.user.service.UserReportStackDomainService; +import com.sluv.infra.alarm.firebase.FcmNotificationService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +public class ReportProcessingService { + + private final UserReportStackDomainService userReportStackDomainService; + private final FcmNotificationService fcmNotificationService; + + public void processReport(User reportedUser, User reporterUser, String content, ReportStatus status) { + if (status == ReportStatus.COMPLETE) { + userReportStackDomainService.saveReportStack(reportedUser); + + fcmNotificationService.sendFCMNotification(reportedUser.getId(), "당신은 신고 당했습니다.", content, AlarmType.REPORT, null); + fcmNotificationService.sendFCMNotification(reporterUser.getId(), "신고가 접수 되었습니다.", content, AlarmType.REPORT, null); + + blockReportedUser(reportedUser); + } else { + fcmNotificationService.sendFCMNotification(reporterUser.getId(), "신고가 기각되었습니다.", content, AlarmType.REPORT, null); + } + } + + private void blockReportedUser(User reportedUser) { + LocalDateTime oneMonthAgo = LocalDateTime.now().minusMonths(1); + long reportCount = userReportStackDomainService.countByReportedAndCreatedAtAfter(reportedUser, oneMonthAgo); + + if (reportCount >= 3) { + reportedUser.changeUserStatus(UserStatus.BLOCKED); + fcmNotificationService.sendFCMNotification(reportedUser.getId(), "신고 누적으로 인한 계정정지 안내", "3회 이상 신고 누적으로 계정이 일시정지 됩니다.", + AlarmType.REPORT, null); + } + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/config/ComponentConfig.java b/sluv-admin/src/main/java/com/sluv/admin/config/ComponentConfig.java new file mode 100644 index 00000000..eea2c668 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/config/ComponentConfig.java @@ -0,0 +1,9 @@ +package com.sluv.admin.config; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan(basePackages = {"com.sluv.common", "com.sluv.infra"}) +public class ComponentConfig { +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/config/SwaggerConfig.java b/sluv-admin/src/main/java/com/sluv/admin/config/SwaggerConfig.java new file mode 100644 index 00000000..520abca6 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/config/SwaggerConfig.java @@ -0,0 +1,54 @@ +package com.sluv.admin.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/* + * Swagger 기본 설정 + */ + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI api() { + Info info = new Info() + .version("v1.0.0") + .title("Sluv Back-Office") + .description("Sluv Back-Office Swagger"); + + + String jwtSchemeName = "AccessToken"; + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName); + Components components = new Components() + .addSecuritySchemes(jwtSchemeName, new SecurityScheme() + .name(jwtSchemeName) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT")); + + return new OpenAPI() + .info(info) + .addSecurityItem(securityRequirement) + .components(components); + } + + /** + * 모든 그룹탭 + */ + + @Bean + public GroupedOpenApi allGroup() { + return GroupedOpenApi.builder() + .group("All") + .pathsToMatch("/**") + .build(); + } + +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/config/security/CorsConfig.java b/sluv-admin/src/main/java/com/sluv/admin/config/security/CorsConfig.java new file mode 100644 index 00000000..15a2141a --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/config/security/CorsConfig.java @@ -0,0 +1,30 @@ +package com.sluv.admin.config.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +/* + * Cors 설정 + */ + +@Configuration +public class CorsConfig { + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.addAllowedOrigin("http://localhost:3000"); + configuration.addAllowedHeader("Authorization"); + configuration.addAllowedHeader("Content-Type"); + configuration.addAllowedMethod("*"); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/config/security/SpringSecurityConfig.java b/sluv-admin/src/main/java/com/sluv/admin/config/security/SpringSecurityConfig.java new file mode 100644 index 00000000..a9a0453c --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/config/security/SpringSecurityConfig.java @@ -0,0 +1,39 @@ +package com.sluv.admin.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +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.web.SecurityFilterChain; + +/* + * Spring Security 설정 파일 + */ + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(securedEnabled = true) +@RequiredArgsConstructor +public class SpringSecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((request) -> request // 허용 범위 설정 + .anyRequest().permitAll() + ) + .csrf().disable() + .cors() + .and() + .formLogin().disable() + .httpBasic().disable() + .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + + return http.build(); + } + + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemController.java b/sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemController.java new file mode 100644 index 00000000..f708892b --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemController.java @@ -0,0 +1,74 @@ +package com.sluv.admin.item.controller; + +import com.sluv.admin.common.response.ErrorResponse; +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.response.SuccessDataResponse; +import com.sluv.admin.item.dto.ItemReportDetailDto; +import com.sluv.admin.item.dto.ItemReportInfoDto; +import com.sluv.admin.item.dto.UpdateItemReportResDto; +import com.sluv.admin.item.service.ItemReportService; +import com.sluv.domain.common.enums.ReportStatus; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/backoffice/item") +public class ItemController { + + private final ItemReportService itemReportService; + + @Operation( + summary = "아이템 신고 정보 조히", + description = "WAITING, COMPLETED, REJECTED 로 검색 조건, 없으면 전체 검색" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping("/report") + public ResponseEntity>> getAllItemReport(Pageable pageable, + @RequestParam(required = false) ReportStatus reportStatus) { + PaginationResponse response = itemReportService.getAllItemReport(pageable, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "아이템 신고 상세 정보 조히", + description = "아이템 신고 id를 통해 아이템 신고 상세 정보 조회" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping("/report/{itemReportId}") + public ResponseEntity> getItemReportDetail(@PathVariable Long itemReportId) { + ItemReportDetailDto response = itemReportService.getItemReportDetail(itemReportId); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "아이템 신고 처리", + description = "아이템 신고 id와 reportStatus(COMPLETED, REJECTED)를 통해 질문 신고 처리" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @PostMapping("/report/{itemReportId}") + public ResponseEntity> changeItemReportStatus(@PathVariable Long itemReportId, + @RequestParam ReportStatus reportStatus) { + UpdateItemReportResDto response = itemReportService.updateItemReportStatus(itemReportId, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemDashBoardController.java b/sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemDashBoardController.java new file mode 100644 index 00000000..a5055910 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/controller/ItemDashBoardController.java @@ -0,0 +1,59 @@ +package com.sluv.admin.item.controller; + +import com.sluv.admin.common.response.SuccessDataResponse; +import com.sluv.admin.item.dto.HotItemResDto; +import com.sluv.admin.item.service.ItemService; +import com.sluv.admin.user.dto.UserCountByCategoryResDto; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/backoffice/item/dashBoard") +public class ItemDashBoardController { + + private final ItemService itemService; + + @Operation( + summary = "대시보드 - 유저의 연령대 분포", + description = "대시보드에서 유저의 연령대 분포를 출력한다\n" + + "parentCategory == null -> 상위 카테고리로 조회\n" + + "parentCategory != null -> 하위 카테고리로 조회\n" + + "parentCategory -> 배우, 스포츠인, 방송인, 인플루언서" + ) + @GetMapping("/celebCategory") + public ResponseEntity> getItemCountByCelebCategory( + @Nullable @RequestParam("parentCategory") String parentCategory) { + UserCountByCategoryResDto result; + if (parentCategory == null) { + result = itemService.getItemCountByCelebCategoryParent(); + } else { + result = itemService.getItemCountByCelebCategoryChild(parentCategory); + } + + return ResponseEntity.ok().body(SuccessDataResponse.create(result)); + } + + @Operation( + summary = "대시보드 - Top3 인기 아이템 조회", + description = "대시보드에서 Top3 인기 아이템을 조회하여 출력\n" + + "인기의 기준은 (조회수 + 좋아요 수 + 스크랩 수)\n" + + "조회수 > 좋아요 > 스크랩 순서로 중요." + ) + @GetMapping("/hotItem") + public ResponseEntity>> getTop3HotItem() { + List response = itemService.getTop3HotItem(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/BrandSearchResDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/BrandSearchResDto.java new file mode 100644 index 00000000..80f2b529 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/BrandSearchResDto.java @@ -0,0 +1,32 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.brand.entity.Brand; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class BrandSearchResDto { + @Schema(description = "브랜드 Id") + private Long id; + @Schema(description = "브랜드 한글이름") + private String brandKr; + @Schema(description = "브랜드 영어이름") + private String brandEn; + @Schema(description = "브랜드 이미지 URL") + private String brandImgUrl; + + public static BrandSearchResDto from(Brand brand) { + return BrandSearchResDto.builder() + .id(brand.getId()) + .brandKr(brand.getBrandKr()) + .brandEn(brand.getBrandEn()) + .brandImgUrl(brand.getBrandImgUrl()) + .build(); + } +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/CelebSearchResDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/CelebSearchResDto.java new file mode 100644 index 00000000..c1f1e271 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/CelebSearchResDto.java @@ -0,0 +1,63 @@ +package com.sluv.admin.item.dto; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.sluv.domain.celeb.entity.Celeb; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonPropertyOrder({"id", "CelebNameKr", "CelebNameEn"}) +public class CelebSearchResDto { + @Schema(description = "Celeb id") + private Long id; + @Schema(description = "Parent Celeb id") + private Long parentId; + @Schema(description = "Celeb 카테고리") + private String category; + @Schema(description = "Celeb Parent 한글 이름") + private String celebParentNameKr; + @Schema(description = "Celeb Child 한글 이름") + private String celebChildNameKr; + @Schema(description = "total 한글 이름") + private String celebTotalNameKr; + @Schema(description = "total 영어 이름") + private String celebTotalNameEn; + + public static CelebSearchResDto from(Celeb celeb) { + return CelebSearchResDto.builder() + .id(celeb.getId()) + .parentId( + celeb.getParent() != null + ? celeb.getParent().getId() + : null + ) + .category( + celeb.getCelebCategory().getParent() != null + ? celeb.getCelebCategory().getParent().getName() + : celeb.getCelebCategory().getName() + ) + .celebParentNameKr( + celeb.getParent() != null + ? celeb.getParent().getCelebNameKr() + : null + ) + .celebChildNameKr(celeb.getCelebNameKr()) + .celebTotalNameKr( + celeb.getParent() != null + ? celeb.getParent().getCelebNameKr() + " " + celeb.getCelebNameKr() + : celeb.getCelebNameKr() + ) + .celebTotalNameEn( + celeb.getParent() != null + ? celeb.getParent().getCelebNameEn() + " " + celeb.getCelebNameEn() + : celeb.getCelebNameEn() + ) + .build(); + } +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/HotItemResDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/HotItemResDto.java new file mode 100644 index 00000000..c446ab76 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/HotItemResDto.java @@ -0,0 +1,36 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.item.dto.ItemWithCountDto; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HotItemResDto { + private Long id; + private String name; + @Schema(description = "대표 이미지 Url") + private String imgUrl; + @Schema(description = "해당 Item의 조회수") + private Long viewCount; + @Schema(description = "해당 Item의 좋아요 수") + private Long likeCount; + @Schema(description = "해당 Item의 스크랩 수") + private Long scrapCount; + + public static HotItemResDto from(ItemWithCountDto countDto) { + return HotItemResDto.builder() + .id(countDto.getId()) + .name(countDto.getName()) + .imgUrl(countDto.getImgUrl()) + .viewCount(countDto.getViewCount()) + .likeCount(countDto.getLikeCount()) + .scrapCount(countDto.getScrapCount()) + .build(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemCategoryDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemCategoryDto.java new file mode 100644 index 00000000..a1d374dd --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemCategoryDto.java @@ -0,0 +1,38 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.item.entity.ItemCategory; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class ItemCategoryDto { + @Schema(description = "아이템 카테고리 Id") + private Long id; + @Schema(description = "아이템 상위 카테고리 Id") + private Long parentId; + @Schema(description = "아이템 카테고리 이름") + private String name; + @Schema(description = "아이템 상위 카테고리 이름") + private String parentName; + + public static ItemCategoryDto from(ItemCategory itemCategory) { + return ItemCategoryDto.builder() + .id(itemCategory.getId()) + .name(itemCategory.getName()) + .parentId( + itemCategory.getParent() != null + ? itemCategory.getParent().getId() + : null + ) + .parentName( + itemCategory.getParent() != null + ? itemCategory.getParent().getName() + : null + ) + .build(); + } +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemImgResDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemImgResDto.java new file mode 100644 index 00000000..3eb30a7c --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemImgResDto.java @@ -0,0 +1,27 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.item.entity.ItemImg; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class ItemImgResDto { + @Schema(description = "아이템 이미지 URL") + private String imgUrl; + @Schema(description = "대표 이미지 여부(true or false)") + private Boolean representFlag; + @Schema(description = "이미지 순서") + private Integer sortOrder; + + public static ItemImgResDto from(ItemImg itemImg) { + return ItemImgResDto.builder() + .imgUrl(itemImg.getItemImgUrl()) + .representFlag(itemImg.getRepresentFlag()) + .sortOrder(itemImg.getSortOrder()) + .build(); + } +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemLinkResDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemLinkResDto.java new file mode 100644 index 00000000..284fdca6 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemLinkResDto.java @@ -0,0 +1,25 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.item.entity.ItemLink; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class ItemLinkResDto { + + @Schema(description = "아이템 링크 URL") + private String itemLinkUrl; + @Schema(description = "아이템 링크 이름") + private String linkName; + + public static ItemLinkResDto from(ItemLink itemLink) { + return ItemLinkResDto.builder() + .itemLinkUrl(itemLink.getItemLinkUrl()) + .linkName(itemLink.getLinkName()) + .build(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportDetailDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportDetailDto.java new file mode 100644 index 00000000..ee5e0443 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportDetailDto.java @@ -0,0 +1,77 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.item.entity.ItemReport; +import com.sluv.domain.item.enums.ItemReportReason; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ItemReportDetailDto { + + private Long reporterId; + private String reporterNickname; + private Long reportedId; + private String reportedNickname; + private Long reportId; + @Schema(description = "아이템 신고 이유(enums)") + private ItemReportReason reportReason; + @Schema(description = "신고 상세 내용") + private String content; + @Schema(description = "신고 접수 상태") + private ReportStatus reportStatus; + @Schema(description = "item 이미지 리스트") + private List imgList; + @Schema(description = "item 링크 리스트") + private List linkList; + private CelebSearchResDto celeb; + private BrandSearchResDto brand; + private ItemCategoryDto category; + @Schema(description = "추가정보") + private String additional_info; + private String color; + private String name; + private Integer price; + @Schema(description = "발견 시간 ex)2021-11-20T09:10:20") + private LocalDateTime whenDiscovery; + @Schema(description = "발견 장소") + private String whereDiscovery; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static ItemReportDetailDto of(ItemReport itemReport, List imgList, List linkList) { + return ItemReportDetailDto.builder() + .reporterId(itemReport.getReporter().getId()) + .reporterNickname(itemReport.getReporter().getNickname()) + .reportedId(itemReport.getItem().getUser().getId()) + .reportedNickname(itemReport.getItem().getUser().getNickname()) + .reportId(itemReport.getId()) + .reportReason(itemReport.getItemReportReason()) + .content(itemReport.getContent()) + .reportStatus(itemReport.getReportStatus()) + .imgList(imgList) + .linkList(linkList) + .celeb(CelebSearchResDto.from(itemReport.getItem().getCeleb())) + .brand(BrandSearchResDto.from(itemReport.getItem().getBrand())) + .category(ItemCategoryDto.from(itemReport.getItem().getCategory())) + .additional_info(itemReport.getItem().getAdditionalInfo()) + .color(itemReport.getItem().getColor()) + .name(itemReport.getItem().getName()) + .price(itemReport.getItem().getPrice()) + .whenDiscovery(itemReport.getItem().getWhenDiscovery()) + .whereDiscovery(itemReport.getItem().getWhereDiscovery()) + .createdAt(itemReport.getItem().getCreatedAt()) + .updatedAt(itemReport.getItem().getUpdatedAt()) + .build(); + + } +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportInfoDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportInfoDto.java new file mode 100644 index 00000000..0995633e --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/ItemReportInfoDto.java @@ -0,0 +1,49 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.item.entity.ItemReport; +import com.sluv.domain.item.enums.ItemReportReason; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ItemReportInfoDto { + + private Long reporterId; + private String reporterNickname; + private Long reportedId; + private String reportedNickname; + private Long reportId; + @Schema(description = "아이템 신고 이유(enums)") + private ItemReportReason reportReason; + @Schema(description = "신고 상세 내용") + private String content; + @Schema(description = "신고 접수 상태") + private ReportStatus reportStatus; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static ItemReportInfoDto from(ItemReport itemReport) { + return ItemReportInfoDto.builder() + .reporterId(itemReport.getReporter().getId()) + .reporterNickname(itemReport.getReporter().getNickname()) + .reportedId(itemReport.getItem().getUser().getId()) + .reportedNickname(itemReport.getItem().getUser().getNickname()) + .reportId(itemReport.getId()) + .reportReason(itemReport.getItemReportReason()) + .content(itemReport.getContent()) + .reportStatus(itemReport.getReportStatus()) + .createdAt(itemReport.getCreatedAt()) + .updatedAt(itemReport.getUpdatedAt()) + .build(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/dto/UpdateItemReportResDto.java b/sluv-admin/src/main/java/com/sluv/admin/item/dto/UpdateItemReportResDto.java new file mode 100644 index 00000000..cb07333b --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/dto/UpdateItemReportResDto.java @@ -0,0 +1,20 @@ +package com.sluv.admin.item.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UpdateItemReportResDto { + + private ReportStatus reportStatus; + + public static UpdateItemReportResDto of(ReportStatus reportStatus) { + return new UpdateItemReportResDto( + reportStatus + ); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/service/ItemReportService.java b/sluv-admin/src/main/java/com/sluv/admin/item/service/ItemReportService.java new file mode 100644 index 00000000..7d792f99 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/service/ItemReportService.java @@ -0,0 +1,80 @@ +package com.sluv.admin.item.service; + +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.service.ReportProcessingService; +import com.sluv.admin.item.dto.*; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.item.entity.ItemReport; +import com.sluv.domain.item.enums.ItemStatus; +import com.sluv.domain.item.service.ItemImgDomainService; +import com.sluv.domain.item.service.ItemLinkDomainService; +import com.sluv.domain.item.service.ItemReportDomainService; +import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.exception.InvalidReportStatusException; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ItemReportService { + + private final ItemReportDomainService itemDomainService; + private final ItemImgDomainService itemImgDomainService; + private final ItemLinkDomainService itemLinkDomainService; + private final ReportProcessingService reportProcessingService; + + @Transactional(readOnly = true) + public PaginationResponse getAllItemReport(Pageable pageable, ReportStatus reportStatus) { + Page itemReportPage = itemDomainService.getAllItemReport(pageable, reportStatus); + List content = itemReportPage.getContent().stream() + .map(ItemReportInfoDto::from) + .toList(); + return PaginationResponse.create(itemReportPage, content); + } + + @Transactional(readOnly = true) + public ItemReportDetailDto getItemReportDetail(Long itemReportId) { + ItemReport itemReport = itemDomainService.getItemReportDetail(itemReportId); + List imgList = itemImgDomainService.findAllByItemId(itemReport.getItem().getId()) + .stream() + .map(ItemImgResDto::from) + .toList(); + + List linkList = itemLinkDomainService.findAllByItemId(itemReport.getItem().getId()) + .stream() + .map(ItemLinkResDto::from) + .toList(); + + return ItemReportDetailDto.of(itemReport, imgList, linkList); + } + + @Transactional + public UpdateItemReportResDto updateItemReportStatus(Long itemReportId, ReportStatus reportStatus) { + if (reportStatus == ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + ItemReport itemReport = itemDomainService.findById(itemReportId); + + if (itemReport.getReportStatus() != ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + User reportedUser = itemReport.getItem().getUser(); + User reporterUser = itemReport.getReporter(); + + itemReport.changeItemReportStatus(reportStatus); + + if (reportStatus == ReportStatus.COMPLETE) { + itemReport.getItem().changeItemStatus(ItemStatus.BLOCKED); + } + reportProcessingService.processReport(reportedUser, reporterUser, itemReport.getContent(), reportStatus); + + return UpdateItemReportResDto.of(itemReport.getReportStatus()); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/item/service/ItemService.java b/sluv-admin/src/main/java/com/sluv/admin/item/service/ItemService.java new file mode 100644 index 00000000..158b6b38 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/item/service/ItemService.java @@ -0,0 +1,51 @@ +package com.sluv.admin.item.service; + +import com.sluv.admin.item.dto.HotItemResDto; +import com.sluv.admin.user.dto.UserCountByCategoryResDto; +import com.sluv.domain.celeb.entity.Celeb; +import com.sluv.domain.celeb.entity.CelebCategory; +import com.sluv.domain.item.entity.Item; +import com.sluv.domain.item.service.ItemDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class ItemService { + + private final ItemDomainService itemDomainService; + + public UserCountByCategoryResDto getItemCountByCelebCategoryParent() { + List allItem = itemDomainService.getAllItemWithCelebCategory(); + + HashMap collect = allItem.stream().map(Item::getCeleb) + .map(Celeb::getCelebCategory) + .map(celebCategory -> celebCategory.getParent() == null ? celebCategory : celebCategory.getParent()) + .collect(Collectors.groupingBy(CelebCategory::getName, HashMap::new, Collectors.counting())); + + return UserCountByCategoryResDto.of(collect, allItem.stream().count()); + } + + public UserCountByCategoryResDto getItemCountByCelebCategoryChild(String parentCategory) { + List allItem = itemDomainService.getAllItemWithCelebCategory(); + + HashMap collect = allItem.stream().map(Item::getCeleb) + .map(Celeb::getCelebCategory) + .filter(celebCategory -> celebCategory.getParent() != null) + .filter(celebCategory -> celebCategory.getParent().getName().equals(parentCategory)) + .collect(Collectors.groupingBy(CelebCategory::getName, HashMap::new, Collectors.counting())); + + return UserCountByCategoryResDto.of(collect, allItem.stream().count()); + } + + public List getTop3HotItem() { + return itemDomainService.getTop3HotItem().stream() + .map(HotItemResDto::from) + .toList(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/question/controller/QuestionController.java b/sluv-admin/src/main/java/com/sluv/admin/question/controller/QuestionController.java new file mode 100644 index 00000000..48bda09a --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/question/controller/QuestionController.java @@ -0,0 +1,74 @@ +package com.sluv.admin.question.controller; + +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.response.SuccessDataResponse; +import com.sluv.admin.question.dto.QuestionReportDetailDto; +import com.sluv.admin.question.dto.QuestionReportInfoDto; +import com.sluv.admin.question.dto.UpdateQuestionReportResDto; +import com.sluv.admin.question.service.QuestionReportService; +import com.sluv.domain.common.enums.ReportStatus; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.ErrorResponse; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/backoffice/question") +public class QuestionController { + + private final QuestionReportService questionReportService; + + @Operation( + summary = "질문 신고 정보 조히", + description = "WAITING, COMPLETED, REJECTED 로 검색 조건, 없으면 전체 검색" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping("/report") + public ResponseEntity>> getAllQuestionReport(Pageable pageable, + @RequestParam(required = false) ReportStatus reportStatus) { + PaginationResponse response = questionReportService.getAllQuestionReport(pageable, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "질문 신고 상세 정보 조히", + description = "질문 신고 id를 통해 질문 신고 상세 정보 조회" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping("/report/{questionReportId}") + public ResponseEntity> getQuestionReportDetail(@PathVariable Long questionReportId) { + QuestionReportDetailDto response = questionReportService.getQuestionReportDetail(questionReportId); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "질문 신고 처리", + description = "질문 신고 id와 reportStatus(COMPLETED, REJECTED)를 통해 질문 신고 처리" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @PostMapping("/report/{questionReportId}") + public ResponseEntity> changeQuestionReportStatus(@PathVariable Long questionReportId, + @RequestParam ReportStatus reportStatus) { + UpdateQuestionReportResDto response = questionReportService.updateQuestionReportStatus(questionReportId, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportDetailDto.java b/sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportDetailDto.java new file mode 100644 index 00000000..84e5212f --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportDetailDto.java @@ -0,0 +1,55 @@ +package com.sluv.admin.question.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.question.entity.QuestionReport; +import com.sluv.domain.question.enums.QuestionReportReason; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class QuestionReportDetailDto { + + private Long reporterId; + private String reporterNickname; + private Long reportedId; + private String reportedNickname; + private Long reportId; + @Schema(description = "질문 신고 이유(enums)") + private QuestionReportReason reportReason; + @Schema(description = "신고 상세 내용") + private String content; + @Schema(description = "신고 접수 상태") + private ReportStatus reportStatus; + @Schema(description = "신고된 질문 제목") + private String reportedQuestionTitle; + @Schema(description = "신고된 질문 본문") + private String reportedQuestionContent; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static QuestionReportDetailDto from(QuestionReport questionReport) { + return QuestionReportDetailDto.builder() + .reporterId(questionReport.getReporter().getId()) + .reporterNickname(questionReport.getReporter().getNickname()) + .reportedId(questionReport.getQuestion().getUser().getId()) + .reportedNickname(questionReport.getQuestion().getUser().getNickname()) + .reportId(questionReport.getId()) + .reportReason(questionReport.getQuestionReportReason()) + .content(questionReport.getContent()) + .reportStatus(questionReport.getReportStatus()) + .reportedQuestionTitle(questionReport.getQuestion().getTitle()) + .reportedQuestionContent(questionReport.getQuestion().getContent()) + .createdAt(questionReport.getCreatedAt()) + .updatedAt(questionReport.getUpdatedAt()) + .build(); + } + +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportInfoDto.java b/sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportInfoDto.java new file mode 100644 index 00000000..909b31d0 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/question/dto/QuestionReportInfoDto.java @@ -0,0 +1,49 @@ +package com.sluv.admin.question.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.question.entity.QuestionReport; +import com.sluv.domain.question.enums.QuestionReportReason; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QuestionReportInfoDto { + + private Long reporterId; + private String reporterNickname; + private Long reportedId; + private String reportedNickname; + private Long reportId; + @Schema(description = "질문 신고 이유(enums)") + private QuestionReportReason reportReason; + @Schema(description = "신고 상세 내용") + private String content; + @Schema(description = "신고 접수 상태") + private ReportStatus reportStatus; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static QuestionReportInfoDto from(QuestionReport questionReport) { + return QuestionReportInfoDto.builder() + .reporterId(questionReport.getReporter().getId()) + .reporterNickname(questionReport.getReporter().getNickname()) + .reportedId(questionReport.getQuestion().getUser().getId()) + .reportedNickname(questionReport.getQuestion().getUser().getNickname()) + .reportId(questionReport.getId()) + .reportReason(questionReport.getQuestionReportReason()) + .content(questionReport.getContent()) + .reportStatus(questionReport.getReportStatus()) + .createdAt(questionReport.getCreatedAt()) + .updatedAt(questionReport.getUpdatedAt()) + .build(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/question/dto/UpdateQuestionReportResDto.java b/sluv-admin/src/main/java/com/sluv/admin/question/dto/UpdateQuestionReportResDto.java new file mode 100644 index 00000000..fd5a6c9d --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/question/dto/UpdateQuestionReportResDto.java @@ -0,0 +1,20 @@ +package com.sluv.admin.question.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UpdateQuestionReportResDto { + + private ReportStatus reportStatus; + + public static UpdateQuestionReportResDto of(ReportStatus reportStatus) { + return new UpdateQuestionReportResDto( + reportStatus + ); + } +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/question/service/QuestionReportService.java b/sluv-admin/src/main/java/com/sluv/admin/question/service/QuestionReportService.java new file mode 100644 index 00000000..9be0d06c --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/question/service/QuestionReportService.java @@ -0,0 +1,69 @@ +package com.sluv.admin.question.service; + +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.service.ReportProcessingService; +import com.sluv.admin.question.dto.QuestionReportDetailDto; +import com.sluv.admin.question.dto.QuestionReportInfoDto; +import com.sluv.admin.question.dto.UpdateQuestionReportResDto; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.question.entity.QuestionReport; +import com.sluv.domain.question.enums.QuestionStatus; +import com.sluv.domain.question.service.QuestionReportDomainService; +import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.exception.InvalidReportStatusException; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional +public class QuestionReportService { + + private final QuestionReportDomainService questionReportDomainService; + private final ReportProcessingService reportProcessingService; + + @Transactional(readOnly = true) + public PaginationResponse getAllQuestionReport(Pageable pageable, ReportStatus reportStatus) { + Page questionReportPage = questionReportDomainService.getAllQuestionReport(pageable, reportStatus); + List content = questionReportPage.getContent().stream() + .map(QuestionReportInfoDto::from) + .toList(); + return PaginationResponse.create(questionReportPage, content); + } + + @Transactional(readOnly = true) + public QuestionReportDetailDto getQuestionReportDetail(Long questionReportId) { + QuestionReport questionReport = questionReportDomainService.getQuestionReportDetail(questionReportId); + return QuestionReportDetailDto.from(questionReport); + } + + public UpdateQuestionReportResDto updateQuestionReportStatus(Long questionReportId, ReportStatus reportStatus) { + if (reportStatus == ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + QuestionReport questionReport = questionReportDomainService.findById(questionReportId); + + + if (questionReport.getReportStatus() != ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + User reportedUser = questionReport.getQuestion().getUser(); + User reporterUser = questionReport.getReporter(); + + questionReport.changeQuestionReportStatus(reportStatus); + + if (reportStatus == ReportStatus.COMPLETE) { + questionReport.getQuestion().changeQuestionStatus(QuestionStatus.BLOCKED); + } + reportProcessingService.processReport(reportedUser, reporterUser, questionReport.getContent(), reportStatus); + + return UpdateQuestionReportResDto.of(questionReport.getReportStatus()); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/controller/UserController.java b/sluv-admin/src/main/java/com/sluv/admin/user/controller/UserController.java new file mode 100644 index 00000000..9d23b076 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/controller/UserController.java @@ -0,0 +1,93 @@ +package com.sluv.admin.user.controller; + +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.response.SuccessDataResponse; +import com.sluv.admin.common.response.SuccessResponse; +import com.sluv.admin.user.dto.UpdateUserReportResDto; +import com.sluv.admin.user.dto.UserAdminInfoDto; +import com.sluv.admin.user.dto.UserReportInfoDto; +import com.sluv.admin.user.service.UserReportService; +import com.sluv.admin.user.service.UserService; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.user.enums.UserStatus; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.ErrorResponse; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/backoffice/user") +public class UserController { + + private final UserService userService; + private final UserReportService userReportService; + + @Operation( + summary = "전체 유저 조회", + description = "모든 유저의 상태와 신고 수를 포함한 정보를 조회한다" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping + public ResponseEntity>> getAllUserInfo(Pageable pageable) { + PaginationResponse response = userService.getAllUserInfo(pageable); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "유저 상태 변경", + description = "ACTIVE, BLOCKED, DELETED, PENDING_PROFILE, PENDING_CELEB 중에서 유저의 상태를 변경한다." + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @PatchMapping("/{userId}/status/{status}") + public ResponseEntity updateUserStatus(@PathVariable Long userId, @PathVariable UserStatus status) { + userService.updateUserStatus(userId, status); + return ResponseEntity.ok().body(SuccessResponse.create()); + } + + @Operation( + summary = "유저 신고 정보 조회", + description = "WAITING, COMPLETED, REJECTED 로 검색 조건, 없으면 전체 검색" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @GetMapping("/report") + public ResponseEntity>> getAllUserReport(Pageable pageable, + @RequestParam(required = false) ReportStatus reportStatus) { + PaginationResponse response = userReportService.getAllUserReport(pageable, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "유저 신고 처리", + description = "유저 신고 id와 reportStatus(COMPLETED, REJECTED)를 통해 유저 신고 처리" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "1000", description = "요청성공"), + @ApiResponse(responseCode = "5000", description = "서버내부 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = "5001", description = "DB 에러", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @PostMapping("/report/{userReportId}") + public ResponseEntity> updateUserReportStatus(@PathVariable Long userReportId, + @RequestParam ReportStatus reportStatus) { + UpdateUserReportResDto response = userReportService.updateUserReportStatus(userReportId, reportStatus); + return ResponseEntity.ok().body(SuccessDataResponse.create((response))); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/controller/UserDashBoardController.java b/sluv-admin/src/main/java/com/sluv/admin/user/controller/UserDashBoardController.java new file mode 100644 index 00000000..aa9dcbb4 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/controller/UserDashBoardController.java @@ -0,0 +1,72 @@ +package com.sluv.admin.user.controller; + +import com.sluv.admin.common.response.SuccessDataResponse; +import com.sluv.admin.user.dto.HotUserResDto; +import com.sluv.admin.user.dto.UserAccountCountResDto; +import com.sluv.admin.user.dto.UserCountByCategoryResDto; +import com.sluv.admin.user.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/backoffice/user/dashBoard") +public class UserDashBoardController { + + private final UserService userService; + + @Operation( + summary = "대시보드 - 유저의 성별 분포 조회", + description = "대시보드에서 유저의 성별 분포를 출력한다" + ) + @GetMapping("/gender") + public ResponseEntity> getUserCountByGender() { + UserCountByCategoryResDto response = userService.getUserCountByGender(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + + @Operation( + summary = "대시보드 - 유저의 연령대 분포 조회", + description = "대시보드에서 유저의 연령대 분포를 출력한다" + ) + @GetMapping("/age") + public ResponseEntity> getUserCountByAge() { + UserCountByCategoryResDto response = userService.getUserCountByAge(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "대시보드 - 가입자 수 조회 기능", + description = "대시보드에서 가입자 수 조회 기능\n" + + "1. 전체 가입자 수\n" + + "2. 지난 1달간 증가 수\n" + + "3. 지난 10주간 그래프" + + ) + @GetMapping("/accountCount") + public ResponseEntity> getUserAccountCount() { + UserAccountCountResDto response = userService.getUserAccountCount(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + + @Operation( + summary = "대시보드 - 인기 유저 Top3 조회 기능", + description = "대시보드에서 인기 유저 Top3 조회 기능\n" + + "팔로워 수를 기준으로 내림차순" + + ) + @GetMapping("/hotUser") + public ResponseEntity>> getTop3HotUser() { + List response = userService.getTop3HotUser(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/dto/HotUserResDto.java b/sluv-admin/src/main/java/com/sluv/admin/user/dto/HotUserResDto.java new file mode 100644 index 00000000..e45aec60 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/dto/HotUserResDto.java @@ -0,0 +1,39 @@ +package com.sluv.admin.user.dto; + +import com.sluv.domain.user.dto.UserWithFollowerCountDto; +import com.sluv.domain.user.entity.User; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class HotUserResDto { + private Long id; + private String nickName; + private String imgUrl; + @Schema(description = "해당 유저의 Follower 수") + private Long followerCount; + + public static HotUserResDto of(User user, Long followerCount) { + return HotUserResDto.builder() + .id(user.getId()) + .nickName(user.getNickname()) + .imgUrl(user.getProfileImgUrl()) + .followerCount(followerCount) + .build(); + } + + public static HotUserResDto from(UserWithFollowerCountDto countDto) { + return HotUserResDto.builder() + .id(countDto.getId()) + .nickName(countDto.getNickName()) + .imgUrl(countDto.getImgUrl()) + .followerCount(countDto.getFollowerCount()) + .build(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/dto/UpdateUserReportResDto.java b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UpdateUserReportResDto.java new file mode 100644 index 00000000..0ac149b8 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UpdateUserReportResDto.java @@ -0,0 +1,20 @@ +package com.sluv.admin.user.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UpdateUserReportResDto { + + private ReportStatus reportStatus; + + public static UpdateUserReportResDto of(ReportStatus reportStatus) { + return new UpdateUserReportResDto( + reportStatus + ); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAccountCountResDto.java b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAccountCountResDto.java new file mode 100644 index 00000000..8cb1cdb0 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAccountCountResDto.java @@ -0,0 +1,42 @@ +package com.sluv.admin.user.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserAccountCountResDto { + @Schema(description = "오늘 기준으로 한달전 대비 증가 비율") + private Double percent; + @Schema(description = "총 유저 수") + private Long totalCount; + @Schema(description = "이번주 기준으로 10주간 가입자 수 그래프") + private List countGraph; + + public static UserAccountCountResDto of(Long totalCount, Long recentMonthCount, List countGraph) { + return UserAccountCountResDto.builder() + .percent(getPercent(recentMonthCount, totalCount)) + .totalCount(totalCount) + .countGraph(countGraph) + .build(); + } + + private static Double getPercent(Long recentCount, Long totalCount) { + if (totalCount == 0) { + return 0.0; + } + // 계산 후 소숫점 2자리까지 반올림1 + return BigDecimal.valueOf((double) recentCount / totalCount * 100) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAdminInfoDto.java b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAdminInfoDto.java new file mode 100644 index 00000000..57d319ee --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserAdminInfoDto.java @@ -0,0 +1,34 @@ +package com.sluv.admin.user.dto; + +import com.sluv.domain.user.dto.UserReportStackDto; +import com.sluv.domain.user.enums.UserStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserAdminInfoDto { + + private String nickname; + private String profileImgUrl; + private UserStatus userStatus; + @Schema(description = "처리 대기 중인 신고 수") + private Long waitingReportCount; + @Schema(description = "승인된 누적 신고 수") + private Long reportStackCount; + + public static UserAdminInfoDto from(UserReportStackDto dto) { + return UserAdminInfoDto.builder() + .nickname(dto.getNickname()) + .profileImgUrl(dto.getProfileImgUrl()) + .userStatus(dto.getUserStatus()) + .waitingReportCount(dto.getWaitingReportCount()) + .reportStackCount(dto.getReportStackCount()) + .build(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserCountByCategoryResDto.java b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserCountByCategoryResDto.java new file mode 100644 index 00000000..2585f058 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserCountByCategoryResDto.java @@ -0,0 +1,51 @@ +package com.sluv.admin.user.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.HashMap; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserCountByCategoryResDto { + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class UserCountByEachCategoryResDto { + private String category; + @Schema(description = "카테고리에 해당하는 유저의 수") + private Long count; + private Double percent; + } + + @Schema(description = "각 카테고리의 통계") + private List eachCategory; + @Schema(description = "전체 유저의 수") + private Long totalCount; + + public static UserCountByCategoryResDto of(HashMap countByCategory, Long totalCount) { + List userCountByEachCategories = countByCategory.entrySet().stream() + .map(entry -> new UserCountByEachCategoryResDto(entry.getKey().toString(), entry.getValue(), + getPercent(entry.getValue(), totalCount))) + .toList(); + return new UserCountByCategoryResDto(userCountByEachCategories, totalCount); + } + + private static Double getPercent(Long genderCount, Long totalCount) { + if (totalCount == 0) { + return 0.0; + } + + // 계산 후 소숫점 2자리까지 반올림1 + return BigDecimal.valueOf((double) genderCount / totalCount * 100) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserReportInfoDto.java b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserReportInfoDto.java new file mode 100644 index 00000000..b3437301 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/dto/UserReportInfoDto.java @@ -0,0 +1,48 @@ +package com.sluv.admin.user.dto; + +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.user.entity.UserReport; +import com.sluv.domain.user.enums.UserReportReason; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserReportInfoDto { + + private Long reporterId; + private String reporterNickname; + private Long reportedId; + private String reportedNickname; + private Long reportId; + @Schema(description = "유저 신고 이유(enums)") + private UserReportReason reportReason; + @Schema(description = "신고 상세 내용") + private String content; + @Schema(description = "신고 접수 상태") + private ReportStatus reportStatus; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static UserReportInfoDto from(UserReport userReport) { + return UserReportInfoDto.builder() + .reporterId(userReport.getReporter().getId()) + .reporterNickname(userReport.getReporter().getNickname()) + .reportedId(userReport.getReported().getId()) + .reportedNickname(userReport.getReported().getNickname()) + .reportReason(userReport.getUserReportReason()) + .content(userReport.getContent()) + .reportStatus(userReport.getReportStatus()) + .createdAt(userReport.getCreatedAt()) + .updatedAt(userReport.getUpdatedAt()) + .build(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/service/UserReportService.java b/sluv-admin/src/main/java/com/sluv/admin/user/service/UserReportService.java new file mode 100644 index 00000000..adbe25b1 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/service/UserReportService.java @@ -0,0 +1,54 @@ +package com.sluv.admin.user.service; + +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.common.service.ReportProcessingService; +import com.sluv.admin.user.dto.UpdateUserReportResDto; +import com.sluv.admin.user.dto.UserReportInfoDto; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.entity.UserReport; +import com.sluv.domain.user.exception.InvalidReportStatusException; +import com.sluv.domain.user.service.UserReportDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional +public class UserReportService { + + private final UserReportDomainService userReportDomainService; + private final ReportProcessingService reportProcessingService; + + @Transactional(readOnly = true) + public PaginationResponse getAllUserReport(Pageable pageable, ReportStatus reportStatus) { + Page reportPage = userReportDomainService.getAllUserReport(pageable, reportStatus); + List content = reportPage.stream().map(UserReportInfoDto::from).toList(); + return PaginationResponse.create(reportPage, content); + } + + public UpdateUserReportResDto updateUserReportStatus(Long userReportId, ReportStatus reportStatus) { + if (reportStatus == ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + UserReport userReport = userReportDomainService.findById(userReportId); + + if (userReport.getReportStatus() != ReportStatus.WAITING) { + throw new InvalidReportStatusException(); + } + + User reportedUser = userReport.getReported(); + User reporterUser = userReport.getReporter(); + + userReport.changeUserReportStatus(reportStatus); + reportProcessingService.processReport(reportedUser, reporterUser, userReport.getContent(), reportStatus); + + return UpdateUserReportResDto.of(userReport.getReportStatus()); + } +} \ No newline at end of file diff --git a/sluv-admin/src/main/java/com/sluv/admin/user/service/UserService.java b/sluv-admin/src/main/java/com/sluv/admin/user/service/UserService.java new file mode 100644 index 00000000..48a7d342 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/user/service/UserService.java @@ -0,0 +1,100 @@ +package com.sluv.admin.user.service; + +import com.sluv.admin.common.response.PaginationResponse; +import com.sluv.admin.user.dto.HotUserResDto; +import com.sluv.admin.user.dto.UserAccountCountResDto; +import com.sluv.admin.user.dto.UserAdminInfoDto; +import com.sluv.admin.user.dto.UserCountByCategoryResDto; +import com.sluv.domain.user.dto.UserReportStackDto; +import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.enums.UserAge; +import com.sluv.domain.user.enums.UserGender; +import com.sluv.domain.user.enums.UserStatus; +import com.sluv.domain.user.service.UserDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class UserService { + + private final UserDomainService userDomainService; + + @Transactional(readOnly = true) + public PaginationResponse getAllUserInfo(Pageable pageable) { + Page stackDtoPage = userDomainService.getAllUserInfo(pageable); + List content = stackDtoPage.getContent().stream() + .map(UserAdminInfoDto::from) + .toList(); + + return PaginationResponse.create(stackDtoPage, content); + } + + @Transactional + public void updateUserStatus(Long userId, UserStatus userStatus) { + User user = userDomainService.findById(userId); + user.changeUserStatus(userStatus); + } + + @Transactional(readOnly = true) + public UserCountByCategoryResDto getUserCountByGender() { + List allUser = userDomainService.findAll(); + HashMap countByGender = allUser.stream() + .collect(Collectors.groupingBy(User::getGender, HashMap::new, Collectors.counting())); + return UserCountByCategoryResDto.of(countByGender, allUser.stream().count()); + } + + @Transactional(readOnly = true) + public UserCountByCategoryResDto getUserCountByAge() { + List allUser = userDomainService.findAll(); + HashMap countByGender = allUser.stream() + .collect(Collectors.groupingBy(User::getAgeRange, HashMap::new, Collectors.counting())); + + return UserCountByCategoryResDto.of(countByGender, allUser.stream().count()); + } + + @Transactional(readOnly = true) + public UserAccountCountResDto getUserAccountCount() { + LocalDateTime now = LocalDateTime.now(); + List allUser = userDomainService.findAll(); + long recentMonthCount = getRecentMonthCount(now, allUser); + List countGraph = getCountGraph(now, allUser); + return UserAccountCountResDto.of(allUser.stream().count(), recentMonthCount, countGraph); + } + + private static List getCountGraph(LocalDateTime now, List allUser) { + List countGraph = new ArrayList<>(); + for (int i = 10; i > 0; i--) { + LocalDateTime startWeek = now.minusWeeks(i); + LocalDateTime endWeek = now.minusWeeks(i - 1); + long count = allUser.stream() + .filter(user -> user.getCreatedAt().isAfter(startWeek) && user.getCreatedAt().isBefore(endWeek)) + .count(); + countGraph.add(count); + } + return countGraph; + } + + private static long getRecentMonthCount(LocalDateTime now, List allUser) { + return allUser.stream() + .filter(user -> user.getCreatedAt().isAfter(now.minusMonths(1))) + .count(); + } + + @Transactional(readOnly = true) + public List getTop3HotUser() { + return userDomainService.getTop3HotUser().stream() + .map(HotUserResDto::from) + .toList(); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/visit/controller/VisitHistoryController.java b/sluv-admin/src/main/java/com/sluv/admin/visit/controller/VisitHistoryController.java new file mode 100644 index 00000000..a21c30c4 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/visit/controller/VisitHistoryController.java @@ -0,0 +1,35 @@ +package com.sluv.admin.visit.controller; + +import com.sluv.admin.common.response.SuccessDataResponse; +import com.sluv.admin.visit.dto.VisitHistoryCountResDto; +import com.sluv.admin.visit.service.VisitHistoryService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/backoffice/visit-history") +public class VisitHistoryController { + + private final VisitHistoryService visitHistoryService; + + @Operation( + summary = "대시보드 - 일일 누적 접속자 수 조회.", + description = "일일 누적 접속자 수를 조회한다.\n" + + "1. 오늘 누적 접속자 수.\n" + + "2. 어제 동시간 대비 누적 접속자 수 증가 퍼센트." + + "3. 10일간 누적 접속자 수 그래프." + ) + @GetMapping("/dailyCount") + public ResponseEntity> getVisitHistoryCount() { + VisitHistoryCountResDto response = visitHistoryService.getVisitHistoryCount(); + return ResponseEntity.ok().body(SuccessDataResponse.create(response)); + } + +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/visit/dto/VisitHistoryCountResDto.java b/sluv-admin/src/main/java/com/sluv/admin/visit/dto/VisitHistoryCountResDto.java new file mode 100644 index 00000000..15557203 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/visit/dto/VisitHistoryCountResDto.java @@ -0,0 +1,43 @@ +package com.sluv.admin.visit.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class VisitHistoryCountResDto { + @Schema(description = "동일시간 대비 어제와 비교하여 접속자수 증감비율") + private Double percent; + @Schema(description = "오늘 누적 접속자 수") + private Long todayCount; + @Schema(description = "오늘을 기준으로 10일간 일일 누적 접속자 수 그래프") + private List countGraph; + + public static VisitHistoryCountResDto of(Long todayCount, Long yesterdayCount, List countGraph) { + return VisitHistoryCountResDto.builder() + .percent(getPercent(yesterdayCount, todayCount)) + .todayCount(todayCount) + .countGraph(countGraph) + .build(); + } + + private static Double getPercent(Long yesterdayCount, Long todayCount) { + if (todayCount == 0) { + return 0.0; + } + + // 계산 후 소숫점 2자리까지 반올림1 + return BigDecimal.valueOf((double) (yesterdayCount - todayCount) / todayCount * 100) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue(); + } +} diff --git a/sluv-admin/src/main/java/com/sluv/admin/visit/service/VisitHistoryService.java b/sluv-admin/src/main/java/com/sluv/admin/visit/service/VisitHistoryService.java new file mode 100644 index 00000000..58a22698 --- /dev/null +++ b/sluv-admin/src/main/java/com/sluv/admin/visit/service/VisitHistoryService.java @@ -0,0 +1,47 @@ +package com.sluv.admin.visit.service; + +import com.sluv.admin.visit.dto.VisitHistoryCountResDto; +import com.sluv.domain.visit.entity.DailyVisit; +import com.sluv.domain.visit.entity.VisitHistory; +import com.sluv.domain.visit.service.DailyVisitDomainService; +import com.sluv.domain.visit.service.VisitHistoryDomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class VisitHistoryService { + + private final VisitHistoryDomainService visitHistoryDomainService; + private final DailyVisitDomainService dailyVisitDomainService; + + public VisitHistoryCountResDto getVisitHistoryCount() { + List visitHistoryFor10Days = visitHistoryDomainService.getVisitHistoryFor10Days(); + List allDailyVisit = dailyVisitDomainService.findAll(); + LocalDateTime now = LocalDateTime.now(); + Long todayCount = getDayCount(now, allDailyVisit); + Long yesterdayCount = getDayCount(now.minusDays(1), allDailyVisit); + List countGraph = getCountGraph(visitHistoryFor10Days, todayCount); + + return VisitHistoryCountResDto.of(todayCount, yesterdayCount, countGraph); + } + + private Long getDayCount(LocalDateTime now, List allDailyVisit) { + return allDailyVisit.stream() + .filter(dailyVisit -> dailyVisit.getCreatedAt().getDayOfMonth() == now.getDayOfMonth()) + .count(); + } + + private static List getCountGraph(List visitHistoryFor10Days, Long todayCount) { + List countGraph = new ArrayList<>(); + for (VisitHistory visitHistory : visitHistoryFor10Days) { + countGraph.add(visitHistory.getVisitCount()); + } + countGraph.add(todayCount); + return countGraph; + } +} diff --git a/sluv-admin/src/main/resources/application.yml b/sluv-admin/src/main/resources/application.yml index 363321e5..96daac94 100644 --- a/sluv-admin/src/main/resources/application.yml +++ b/sluv-admin/src/main/resources/application.yml @@ -8,10 +8,10 @@ spring: - infra active: dev -# security: -# user: -# name: user -# password: 1234 + security: + user: + name: user + password: 1234 --- spring: config: diff --git a/sluv-api/src/main/java/com/sluv/api/brand/controller/BrandController.java b/sluv-api/src/main/java/com/sluv/api/brand/controller/BrandController.java index b8a5be1b..a69963e2 100644 --- a/sluv-api/src/main/java/com/sluv/api/brand/controller/BrandController.java +++ b/sluv-api/src/main/java/com/sluv/api/brand/controller/BrandController.java @@ -5,7 +5,6 @@ import com.sluv.api.common.response.PaginationResponse; import com.sluv.api.common.response.SuccessDataResponse; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -14,6 +13,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RestController @RequiredArgsConstructor @RequestMapping("/app/brand") diff --git a/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebActivityController.java b/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebActivityController.java index 039510e8..3d7cab96 100644 --- a/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebActivityController.java +++ b/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebActivityController.java @@ -4,7 +4,6 @@ import com.sluv.api.celeb.service.CelebActivityService; import com.sluv.api.common.response.SuccessDataResponse; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -12,6 +11,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RestController @RequiredArgsConstructor @RequestMapping("/app/celeb/activity") diff --git a/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebController.java b/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebController.java index 289f307b..197dab52 100644 --- a/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebController.java +++ b/sluv-api/src/main/java/com/sluv/api/celeb/controller/CelebController.java @@ -6,7 +6,6 @@ import com.sluv.api.common.response.PaginationResponse; import com.sluv.api.common.response.SuccessDataResponse; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -15,6 +14,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RestController @RequiredArgsConstructor @RequestMapping("/app/celeb") diff --git a/sluv-api/src/main/java/com/sluv/api/celeb/controller/RecentSelectCelebController.java b/sluv-api/src/main/java/com/sluv/api/celeb/controller/RecentSelectCelebController.java index 79f2d8d7..db5ef12a 100644 --- a/sluv-api/src/main/java/com/sluv/api/celeb/controller/RecentSelectCelebController.java +++ b/sluv-api/src/main/java/com/sluv/api/celeb/controller/RecentSelectCelebController.java @@ -8,17 +8,11 @@ import com.sluv.api.common.response.SuccessResponse; import com.sluv.common.annotation.CurrentUserId; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequiredArgsConstructor diff --git a/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetController.java b/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetController.java index 71c8762c..58d99546 100644 --- a/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetController.java +++ b/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetController.java @@ -11,15 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/app/closet") diff --git a/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetItemController.java b/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetItemController.java index 6bbcda53..75f921ed 100644 --- a/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetItemController.java +++ b/sluv-api/src/main/java/com/sluv/api/closet/controller/ClosetItemController.java @@ -10,14 +10,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/app/closet") diff --git a/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentController.java b/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentController.java index b2ef1cd8..5a9dea15 100644 --- a/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentController.java +++ b/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentController.java @@ -12,14 +12,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor diff --git a/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentReportController.java b/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentReportController.java index 50908a43..84270039 100644 --- a/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentReportController.java +++ b/sluv-api/src/main/java/com/sluv/api/comment/controller/CommentReportController.java @@ -7,11 +7,7 @@ import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor diff --git a/sluv-api/src/main/java/com/sluv/api/comment/dto/reponse/SubCommentPageResponse.java b/sluv-api/src/main/java/com/sluv/api/comment/dto/reponse/SubCommentPageResponse.java index 43fea899..b3907927 100644 --- a/sluv-api/src/main/java/com/sluv/api/comment/dto/reponse/SubCommentPageResponse.java +++ b/sluv-api/src/main/java/com/sluv/api/comment/dto/reponse/SubCommentPageResponse.java @@ -2,12 +2,13 @@ import com.sluv.api.common.response.PaginationResponse; import com.sluv.domain.comment.entity.Comment; -import java.util.List; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.springframework.data.domain.Page; +import java.util.List; + @NoArgsConstructor @Getter @SuperBuilder diff --git a/sluv-api/src/main/java/com/sluv/api/item/controller/HashtagController.java b/sluv-api/src/main/java/com/sluv/api/item/controller/HashtagController.java index 40659215..4768ee63 100644 --- a/sluv-api/src/main/java/com/sluv/api/item/controller/HashtagController.java +++ b/sluv-api/src/main/java/com/sluv/api/item/controller/HashtagController.java @@ -11,12 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor diff --git a/sluv-api/src/main/java/com/sluv/api/item/controller/ItemCategoryController.java b/sluv-api/src/main/java/com/sluv/api/item/controller/ItemCategoryController.java index 1db5eef6..4f843b36 100644 --- a/sluv-api/src/main/java/com/sluv/api/item/controller/ItemCategoryController.java +++ b/sluv-api/src/main/java/com/sluv/api/item/controller/ItemCategoryController.java @@ -4,13 +4,14 @@ import com.sluv.api.item.dto.ItemCategoryParentResponseDto; import com.sluv.api.item.service.ItemCategoryService; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RestController @RequiredArgsConstructor @RequestMapping("/app/item/category") diff --git a/sluv-api/src/main/java/com/sluv/api/item/controller/PlaceRankController.java b/sluv-api/src/main/java/com/sluv/api/item/controller/PlaceRankController.java index 3da3b407..c81539bb 100644 --- a/sluv-api/src/main/java/com/sluv/api/item/controller/PlaceRankController.java +++ b/sluv-api/src/main/java/com/sluv/api/item/controller/PlaceRankController.java @@ -9,16 +9,11 @@ import com.sluv.api.item.service.PlaceRankService; import com.sluv.common.annotation.CurrentUserId; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequiredArgsConstructor diff --git a/sluv-api/src/main/java/com/sluv/api/item/service/ItemService.java b/sluv-api/src/main/java/com/sluv/api/item/service/ItemService.java index 9ee678dd..1e383ca5 100644 --- a/sluv-api/src/main/java/com/sluv/api/item/service/ItemService.java +++ b/sluv-api/src/main/java/com/sluv/api/item/service/ItemService.java @@ -278,7 +278,7 @@ public ItemDetailFixData getItemDetailFixData(Item item) { .map(ItemImgDto::of).toList(); // 6. Item 링크들 조회 - List linkList = itemLinkDomainService.findByItemId(itemId) + List linkList = itemLinkDomainService.findAllByItemId(itemId) .stream() .map(ItemLinkDto::of).toList(); diff --git a/sluv-api/src/main/java/com/sluv/api/question/controller/QuestionController.java b/sluv-api/src/main/java/com/sluv/api/question/controller/QuestionController.java index 3d7e8d51..43616fa9 100644 --- a/sluv-api/src/main/java/com/sluv/api/question/controller/QuestionController.java +++ b/sluv-api/src/main/java/com/sluv/api/question/controller/QuestionController.java @@ -3,34 +3,19 @@ import com.sluv.api.common.response.PaginationResponse; import com.sluv.api.common.response.SuccessDataResponse; import com.sluv.api.common.response.SuccessResponse; -import com.sluv.api.question.dto.QuestionBuyPostReqDto; -import com.sluv.api.question.dto.QuestionBuySimpleResDto; -import com.sluv.api.question.dto.QuestionFindPostReqDto; -import com.sluv.api.question.dto.QuestionGetDetailResDto; -import com.sluv.api.question.dto.QuestionHomeResDto; -import com.sluv.api.question.dto.QuestionHowaboutPostReqDto; -import com.sluv.api.question.dto.QuestionPostResDto; -import com.sluv.api.question.dto.QuestionRecommendPostReqDto; -import com.sluv.api.question.dto.QuestionReportReqDto; -import com.sluv.api.question.dto.QuestionVoteReqDto; +import com.sluv.api.question.dto.*; import com.sluv.api.question.service.QuestionService; import com.sluv.common.annotation.CurrentUserId; import com.sluv.domain.question.dto.QuestionSimpleResDto; import com.sluv.domain.question.exception.QuestionTypeNotFoundException; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequiredArgsConstructor diff --git a/sluv-api/src/main/java/com/sluv/api/search/controller/SearchController.java b/sluv-api/src/main/java/com/sluv/api/search/controller/SearchController.java index 7cda34c2..d0d1ceb9 100644 --- a/sluv-api/src/main/java/com/sluv/api/search/controller/SearchController.java +++ b/sluv-api/src/main/java/com/sluv/api/search/controller/SearchController.java @@ -3,11 +3,7 @@ import com.sluv.api.common.response.PaginationResponse; import com.sluv.api.common.response.SuccessDataResponse; import com.sluv.api.common.response.SuccessResponse; -import com.sluv.api.search.dto.RecentSearchChipResDto; -import com.sluv.api.search.dto.SearchItemCountResDto; -import com.sluv.api.search.dto.SearchKeywordResDto; -import com.sluv.api.search.dto.SearchKeywordTotalResDto; -import com.sluv.api.search.dto.SearchTotalResDto; +import com.sluv.api.search.dto.*; import com.sluv.api.search.service.SearchEngineService; import com.sluv.api.search.service.SearchEngineTotalService; import com.sluv.api.search.service.SearchService; @@ -17,17 +13,14 @@ import com.sluv.domain.search.dto.SearchFilterReqDto; import com.sluv.domain.user.dto.UserSearchInfoDto; import io.swagger.v3.oas.annotations.Operation; -import java.util.List; -import java.util.concurrent.ExecutionException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.concurrent.ExecutionException; @RestController @RequestMapping("/app/search") diff --git a/sluv-api/src/main/java/com/sluv/api/search/service/SearchEngineService.java b/sluv-api/src/main/java/com/sluv/api/search/service/SearchEngineService.java index 0485a155..5f1ab921 100644 --- a/sluv-api/src/main/java/com/sluv/api/search/service/SearchEngineService.java +++ b/sluv-api/src/main/java/com/sluv/api/search/service/SearchEngineService.java @@ -16,9 +16,6 @@ import com.sluv.domain.user.entity.User; import com.sluv.domain.user.service.FollowDomainService; import com.sluv.domain.user.service.UserDomainService; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -26,6 +23,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + @Service @RequiredArgsConstructor public class SearchEngineService { diff --git a/sluv-api/src/main/java/com/sluv/api/search/service/SearchService.java b/sluv-api/src/main/java/com/sluv/api/search/service/SearchService.java index 901687e7..4d3f9a4e 100644 --- a/sluv-api/src/main/java/com/sluv/api/search/service/SearchService.java +++ b/sluv-api/src/main/java/com/sluv/api/search/service/SearchService.java @@ -17,7 +17,6 @@ import com.sluv.domain.search.service.SearchRankDomainService; import com.sluv.domain.user.entity.User; import com.sluv.domain.user.service.UserDomainService; -import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -26,6 +25,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Slf4j @Service @RequiredArgsConstructor diff --git a/sluv-api/src/main/java/com/sluv/api/user/service/UserLikeService.java b/sluv-api/src/main/java/com/sluv/api/user/service/UserLikeService.java index e960fafb..e704d5c3 100644 --- a/sluv-api/src/main/java/com/sluv/api/user/service/UserLikeService.java +++ b/sluv-api/src/main/java/com/sluv/api/user/service/UserLikeService.java @@ -13,7 +13,6 @@ import com.sluv.domain.question.service.QuestionDomainService; import com.sluv.domain.user.entity.User; import com.sluv.domain.user.service.UserDomainService; -import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -21,6 +20,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @Slf4j @RequiredArgsConstructor diff --git a/sluv-domain/src/main/java/com/sluv/domain/brand/dto/BrandCountDto.java b/sluv-domain/src/main/java/com/sluv/domain/brand/dto/BrandCountDto.java new file mode 100644 index 00000000..d2071e99 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/brand/dto/BrandCountDto.java @@ -0,0 +1,25 @@ +package com.sluv.domain.brand.dto; + +import com.sluv.domain.brand.entity.Brand; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BrandCountDto { + + private Brand brand; + private Long useCount; + + public static BrandCountDto of(Brand brand, Long useCount) { + return BrandCountDto.builder() + .brand(brand) + .useCount(useCount) + .build(); + } + +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryCustom.java index f9f8bcca..cc971e3b 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryCustom.java +++ b/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryCustom.java @@ -1,14 +1,18 @@ package com.sluv.domain.brand.repository.impl; +import com.sluv.domain.brand.dto.BrandCountDto; import com.sluv.domain.brand.entity.Brand; -import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.util.List; + public interface BrandRepositoryCustom { Page findByAllBrandKrOrBrandEnStartingWith(String brandName, Pageable pageable); List findTop10By(); List getBrandContainKeyword(String keyword); + + List getTopHotBrandWithLimit(int limitCount); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryImpl.java index 97911f79..85980a05 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryImpl.java +++ b/sluv-domain/src/main/java/com/sluv/domain/brand/repository/impl/BrandRepositoryImpl.java @@ -1,17 +1,21 @@ package com.sluv.domain.brand.repository.impl; -import static com.sluv.domain.brand.entity.QBrand.brand; -import static com.sluv.domain.brand.entity.QRecentSelectBrand.recentSelectBrand; - +import com.querydsl.core.Tuple; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import com.sluv.domain.brand.dto.BrandCountDto; import com.sluv.domain.brand.entity.Brand; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.support.PageableExecutionUtils; +import java.util.List; + +import static com.sluv.domain.brand.entity.QBrand.brand; +import static com.sluv.domain.brand.entity.QRecentSelectBrand.recentSelectBrand; +import static com.sluv.domain.item.entity.QItem.item; + @RequiredArgsConstructor public class BrandRepositoryImpl implements BrandRepositoryCustom { private final JPAQueryFactory jpaQueryFactory; @@ -55,4 +59,18 @@ public List getBrandContainKeyword(String keyword) { .limit(5) .fetch(); } + + @Override + public List getTopHotBrandWithLimit(int limitCount) { + List fetch = jpaQueryFactory.select(brand, item.brand.count()) + .from(item) + .groupBy(item.brand) + .orderBy(item.brand.count().desc()) + .limit(limitCount) + .fetch(); + + return fetch.stream() + .map(tuple -> BrandCountDto.of(tuple.get(brand), tuple.get(item.brand.count()))) + .toList(); + } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/brand/service/BrandDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/brand/service/BrandDomainService.java index 9f019719..78ac98fe 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/brand/service/BrandDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/brand/service/BrandDomainService.java @@ -1,5 +1,6 @@ package com.sluv.domain.brand.service; +import com.sluv.domain.brand.dto.BrandCountDto; import com.sluv.domain.brand.entity.Brand; import com.sluv.domain.brand.exception.BrandNotFoundException; import com.sluv.domain.brand.repository.BrandRepository; @@ -38,5 +39,10 @@ public Brand findById(Long brandId) { public List getBrandContainKeyword(String keyword) { return brandRepository.getBrandContainKeyword(keyword); } + + public List getTopHotBrandWithLimit(int limitCount) { + return brandRepository.getTopHotBrandWithLimit(limitCount); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/entity/CommentReport.java b/sluv-domain/src/main/java/com/sluv/domain/comment/entity/CommentReport.java index b131b2a8..c4c8ae69 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/comment/entity/CommentReport.java +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/entity/CommentReport.java @@ -4,17 +4,7 @@ import com.sluv.domain.common.entity.BaseEntity; import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.user.entity.User; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -66,4 +56,9 @@ public static CommentReport toEntity(Comment comment, User user, .reportStatus(ReportStatus.WAITING) .build(); } + + public void changeCommentReportStatus(ReportStatus reportStatus) { + this.reportStatus = reportStatus; + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/exception/CommentReportNotFoundException.java b/sluv-domain/src/main/java/com/sluv/domain/comment/exception/CommentReportNotFoundException.java new file mode 100644 index 00000000..8312d484 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/exception/CommentReportNotFoundException.java @@ -0,0 +1,14 @@ +package com.sluv.domain.comment.exception; + +import com.sluv.common.exception.HttpStatusCode; + +public class CommentReportNotFoundException extends CommentException { + + private static final int ERROR_CODE = 2200; + private static final String MESSAGE = "존재하지 않는 댓글 신고입니다."; + private static final HttpStatusCode STATUS = HttpStatusCode.BAD_REQUEST; + + public CommentReportNotFoundException() { + super(ERROR_CODE, STATUS, MESSAGE); + } +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/CommentReportRepository.java b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/CommentReportRepository.java index b8d54d12..e69bb50d 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/CommentReportRepository.java +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/CommentReportRepository.java @@ -1,8 +1,9 @@ package com.sluv.domain.comment.repository; import com.sluv.domain.comment.entity.CommentReport; +import com.sluv.domain.comment.repository.impl.CommentReportRepositoryCustom; import org.springframework.data.jpa.repository.JpaRepository; -public interface CommentReportRepository extends JpaRepository { +public interface CommentReportRepository extends JpaRepository, CommentReportRepositoryCustom { Boolean existsByReporterIdAndCommentId(Long id, Long commentId); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryCustom.java new file mode 100644 index 00000000..0376c1d0 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryCustom.java @@ -0,0 +1,14 @@ +package com.sluv.domain.comment.repository.impl; + +import com.sluv.domain.comment.entity.CommentReport; +import com.sluv.domain.common.enums.ReportStatus; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface CommentReportRepositoryCustom { + Page getAllCommentReport(Pageable pageable, ReportStatus reportStatus); + + Optional getCommentReportDetail(Long commentReportId); +} \ No newline at end of file diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryImpl.java new file mode 100644 index 00000000..ba752233 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentReportRepositoryImpl.java @@ -0,0 +1,94 @@ +package com.sluv.domain.comment.repository.impl; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.sluv.domain.comment.entity.CommentReport; +import com.sluv.domain.comment.entity.QCommentReport; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.user.entity.QUser; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.support.PageableExecutionUtils; + +import java.util.List; +import java.util.Optional; + +import static com.sluv.domain.comment.entity.QComment.comment; +import static com.sluv.domain.comment.entity.QCommentReport.commentReport; + +@RequiredArgsConstructor +public class CommentReportRepositoryImpl implements CommentReportRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + private final QUser reporterUser = new QUser("reporterUser"); + private final QUser reportedUser = new QUser("reportedUser"); + + @Override + public Page getAllCommentReport(Pageable pageable, ReportStatus reportStatus) { + BooleanExpression predicate = commentReport.isNotNull(); + if (reportStatus != null) { + predicate = predicate.and(commentReport.reportStatus.eq(reportStatus)); + } + +// List content = jpaQueryFactory +// .select(Projections.constructor(CommentReportInfoDto.class, +// commentReport.reporter.id, +// commentReport.reporter.nickname, +// comment.user.id, +// comment.user.nickname, +// commentReport.id, +// commentReport.commentReportReason, +// commentReport.content, +// commentReport.reportStatus, +// commentReport.createdAt, +// commentReport.updatedAt)) + List content = jpaQueryFactory.selectFrom(commentReport) + .where(predicate) + .orderBy(commentReport.createdAt.desc()) + .leftJoin(commentReport.comment, comment).fetchJoin() + .leftJoin(commentReport.reporter, reporterUser).fetchJoin() + .leftJoin(comment.user, reportedUser).fetchJoin() + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + JPAQuery countQuery = jpaQueryFactory.selectFrom(commentReport) + .where(predicate); + + return PageableExecutionUtils.getPage(content, pageable, + () -> countQuery.fetch().size()); + } + + @Override + public Optional getCommentReportDetail(Long commentReportId) { +// CommentReportDetailDto detailDto = jpaQueryFactory +// .select(Projections.constructor(CommentReportDetailDto.class, +// commentReport.reporter.id, +// commentReport.reporter.nickname, +// comment.user.id, +// comment.user.nickname, +// commentReport.id, +// commentReport.commentReportReason, +// commentReport.content, +// commentReport.reportStatus, +// comment.content, +// commentReport.createdAt, +// commentReport.updatedAt)) +// .from(commentReport) +// .join(commentReport.comment, comment) +// .join(commentReport.reporter, reporterUser) +// .join(comment.user, reportedUser) +// .where(commentReport.id.eq(commentReportId)) +// .fetchOne(); + CommentReport commentReport = jpaQueryFactory.selectFrom(QCommentReport.commentReport) + .leftJoin(QCommentReport.commentReport.comment, comment).fetchJoin() + .leftJoin(QCommentReport.commentReport.reporter, reporterUser).fetchJoin() + .leftJoin(comment.user, reportedUser).fetchJoin() + .where(QCommentReport.commentReport.id.eq(commentReportId)) + .fetchOne(); + + return Optional.ofNullable(commentReport); + } +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryCustom.java index 9281c271..a4b10e04 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryCustom.java +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryCustom.java @@ -6,6 +6,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.util.List; + public interface CommentRepositoryCustom { Page getAllQuestionComment(Long questionId, Pageable pageable); @@ -16,4 +18,6 @@ public interface CommentRepositoryCustom { Page getUserAllComment(User user, Pageable pageable); Long countCommentByUserIdInActiveQuestion(Long userId, CommentStatus commentStatus); + + List getAllBlockComment(); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryImpl.java index 8d24f366..dcca83a7 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryImpl.java +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/repository/impl/CommentRepositoryImpl.java @@ -1,20 +1,21 @@ package com.sluv.domain.comment.repository.impl; -import static com.sluv.domain.comment.entity.QComment.comment; -import static com.sluv.domain.comment.entity.QCommentLike.commentLike; - import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import com.sluv.domain.comment.entity.Comment; import com.sluv.domain.comment.enums.CommentStatus; import com.sluv.domain.question.enums.QuestionStatus; import com.sluv.domain.user.entity.User; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.support.PageableExecutionUtils; +import java.util.List; + +import static com.sluv.domain.comment.entity.QComment.comment; +import static com.sluv.domain.comment.entity.QCommentLike.commentLike; + @RequiredArgsConstructor public class CommentRepositoryImpl implements CommentRepositoryCustom { @@ -125,4 +126,12 @@ public Long countCommentByUserIdInActiveQuestion(Long userId, CommentStatus comm return comments.stream().count(); } + + @Override + public List getAllBlockComment() { + return jpaQueryFactory.selectFrom(comment) + .where(comment.commentStatus.eq(CommentStatus.BLOCKED)) + .fetch(); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentDomainService.java index 0f68610c..9a62fbb1 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentDomainService.java @@ -11,6 +11,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class CommentDomainService { @@ -63,4 +65,8 @@ public void deleteAllByParentId(Long parentId) { commentRepository.deleteAllByParentId(parentId); } + public List getAllBlockComment() { + return commentRepository.getAllBlockComment(); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentReportDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentReportDomainService.java index f498184a..ccd2e0ff 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentReportDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/comment/service/CommentReportDomainService.java @@ -3,9 +3,13 @@ import com.sluv.domain.comment.entity.Comment; import com.sluv.domain.comment.entity.CommentReport; import com.sluv.domain.comment.enums.CommentReportReason; +import com.sluv.domain.comment.exception.CommentReportNotFoundException; import com.sluv.domain.comment.repository.CommentReportRepository; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.user.entity.User; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @Service @@ -23,4 +27,17 @@ public void saveCommentReport(Comment comment, User user, CommentReportReason re commentReportRepository.save(commentReport); } + public CommentReport getCommentReportDetail(Long commentReportId) { + return commentReportRepository.getCommentReportDetail(commentReportId) + .orElseThrow(CommentReportNotFoundException::new); + } + + + public Page getAllCommentReport(Pageable pageable, ReportStatus reportStatus) { + return commentReportRepository.getAllCommentReport(pageable, reportStatus); + } + + public CommentReport findById(Long commentReportId) { + return commentReportRepository.findById(commentReportId).orElseThrow(CommentReportNotFoundException::new); + } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/dto/ItemWithCountDto.java b/sluv-domain/src/main/java/com/sluv/domain/item/dto/ItemWithCountDto.java new file mode 100644 index 00000000..1a70ec4d --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/item/dto/ItemWithCountDto.java @@ -0,0 +1,37 @@ +package com.sluv.domain.item.dto; + +import com.sluv.domain.item.entity.Item; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ItemWithCountDto { + + private Long id; + private String name; + @Schema(description = "대표 이미지 Url") + private String imgUrl; + @Schema(description = "해당 Item의 조회수") + private Long viewCount; + @Schema(description = "해당 Item의 좋아요 수") + private Long likeCount; + @Schema(description = "해당 Item의 스크랩 수") + private Long scrapCount; + + public static ItemWithCountDto of(Item item, String imgUrl, Long likeCount, Long scrapCount) { + return ItemWithCountDto.builder() + .id(item.getId()) + .name(item.getName()) + .imgUrl(imgUrl) + .viewCount(item.getViewNum()) + .likeCount(likeCount) + .scrapCount(scrapCount) + .build(); + } +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/entity/Item.java b/sluv-domain/src/main/java/com/sluv/domain/item/entity/Item.java index ab0ce158..974a2f98 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/entity/Item.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/entity/Item.java @@ -8,25 +8,16 @@ import com.sluv.domain.item.dto.ItemSaveDto; import com.sluv.domain.item.enums.ItemStatus; import com.sluv.domain.user.entity.User; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.time.LocalDateTime; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; + @Entity @Getter @NoArgsConstructor @@ -154,4 +145,8 @@ public void changeStatus(ItemStatus itemStatus) { public void changeColor(String color) { this.color = color; } + + public void changeItemStatus(ItemStatus itemStatus) { + this.itemStatus = itemStatus; + } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/entity/ItemReport.java b/sluv-domain/src/main/java/com/sluv/domain/item/entity/ItemReport.java index b8d0e03b..9f55e341 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/entity/ItemReport.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/entity/ItemReport.java @@ -4,17 +4,7 @@ import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.item.enums.ItemReportReason; import com.sluv.domain.user.entity.User; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -65,4 +55,8 @@ public static ItemReport toEntity(Item item, User user, ItemReportReason reason, .build(); } + public void changeItemReportStatus(ReportStatus reportStatus) { + this.reportStatus = reportStatus; + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/exception/ItemReportNotFoundException.java b/sluv-domain/src/main/java/com/sluv/domain/item/exception/ItemReportNotFoundException.java new file mode 100644 index 00000000..35108841 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/item/exception/ItemReportNotFoundException.java @@ -0,0 +1,14 @@ +package com.sluv.domain.item.exception; + +import com.sluv.common.exception.HttpStatusCode; + +public class ItemReportNotFoundException extends ItemException { + + private static final int ERROR_CODE = 2300; + private static final String MESSAGE = "존재하지 않는 아이템 게시글 신고입니다."; + private static final HttpStatusCode STATUS = HttpStatusCode.BAD_REQUEST; + + public ItemReportNotFoundException() { + super(ERROR_CODE, STATUS, MESSAGE); + } +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/repository/ItemLinkRepository.java b/sluv-domain/src/main/java/com/sluv/domain/item/repository/ItemLinkRepository.java index d8ac5c4f..27889b72 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/repository/ItemLinkRepository.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/repository/ItemLinkRepository.java @@ -1,11 +1,12 @@ package com.sluv.domain.item.repository; import com.sluv.domain.item.entity.ItemLink; -import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface ItemLinkRepository extends JpaRepository { - List findByItemId(Long itemId); + List findAllByItemId(Long itemId); void deleteAllByItemId(Long itemId); diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryCustom.java index 30effe76..b2ce3d8c 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryCustom.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryCustom.java @@ -1,8 +1,16 @@ package com.sluv.domain.item.repository.impl; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.item.entity.Item; +import com.sluv.domain.item.entity.ItemReport; import com.sluv.domain.user.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; public interface ItemReportRepositoryCustom { Boolean findExistence(User user, Item target); + + Page getAllItemReport(Pageable pageable, ReportStatus reportStatus); + + ItemReport getItemReportDetail(Long itemReportId); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryImpl.java index f1305f04..46f4da84 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryImpl.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemReportRepositoryImpl.java @@ -1,15 +1,33 @@ package com.sluv.domain.item.repository.impl; -import static com.sluv.domain.item.entity.QItemReport.itemReport; - +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.item.entity.Item; +import com.sluv.domain.item.entity.ItemReport; +import com.sluv.domain.user.entity.QUser; import com.sluv.domain.user.entity.User; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.support.PageableExecutionUtils; + +import java.util.List; + +import static com.sluv.domain.brand.entity.QBrand.brand; +import static com.sluv.domain.celeb.entity.QCeleb.celeb; +import static com.sluv.domain.item.entity.QItem.item; +import static com.sluv.domain.item.entity.QItemCategory.itemCategory; +import static com.sluv.domain.item.entity.QItemImg.itemImg; +import static com.sluv.domain.item.entity.QItemLink.itemLink; +import static com.sluv.domain.item.entity.QItemReport.itemReport; @RequiredArgsConstructor public class ItemReportRepositoryImpl implements ItemReportRepositoryCustom { private final JPAQueryFactory jpaQueryFactory; + private final QUser reporterUser = new QUser("reporterUser"); + private final QUser reportedUser = new QUser("reportedUser"); @Override public Boolean findExistence(User user, Item target) { @@ -21,4 +39,106 @@ public Boolean findExistence(User user, Item target) { .fetchFirst() != null; } + @Override + public Page getAllItemReport(Pageable pageable, ReportStatus reportStatus) { + BooleanExpression predicate = itemReport.isNotNull(); + if (reportStatus != null) { + predicate = predicate.and(itemReport.reportStatus.eq(reportStatus)); + } + +// List content = jpaQueryFactory +// .select(Projections.constructor(ItemReportInfoDto.class, +// itemReport.reporter.id, +// itemReport.reporter.nickname, +// item.user.id, +// item.user.nickname, +// itemReport.id, +// itemReport.itemReportReason, +// itemReport.content, +// itemReport.reportStatus, +// itemReport.createdAt, +// itemReport.updatedAt)) + List content = jpaQueryFactory.selectFrom(itemReport) + .where(predicate) + .orderBy(itemReport.createdAt.desc()) + .leftJoin(itemReport.item, item).fetchJoin() + .leftJoin(itemReport.reporter, reporterUser).fetchJoin() + .leftJoin(item.user, reportedUser).fetchJoin() + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + JPAQuery countQuery = jpaQueryFactory.selectFrom(itemReport) + .where(predicate); + + return PageableExecutionUtils.getPage(content, pageable, () -> countQuery.fetch().size()); + } + + @Override + public ItemReport getItemReportDetail(Long itemReportId) { + +// Optional optionalItem = Optional.ofNullable( +// jpaQueryFactory +// .select(item) +// .from(itemReport) +// .join(itemReport.item, item) +// .where(itemReport.id.eq(itemReportId)) +// .fetchOne() +// ); +// Item itemEntity = optionalItem.orElseThrow(ItemNotFoundException::new); +// +// List imgList = itemImgRepository.findAllByItemId(itemEntity.getId()) +// .stream() +// .map(ItemImgResDto::of) +// .toList(); +// +// List linkList = itemLinkRepository.findAllByItemId(itemEntity.getId()) +// .stream() +// .map(ItemLinkResDto::of) +// .toList(); +// +// ItemReportDetailDto detailDto = jpaQueryFactory +// .select(Projections.constructor(ItemReportDetailDto.class, +// itemReport.reporter.id, +// itemReport.reporter.nickname, +// item.user.id, +// item.user.nickname, +// itemReport.id, +// itemReport.itemReportReason, +// itemReport.content, +// itemReport.reportStatus, +// Expressions.constant(imgList), +// Expressions.constant(linkList), +// Expressions.constant(CelebSearchResDto.of(itemEntity.getCeleb())), +// Expressions.constant(BrandSearchResDto.of(itemEntity.getBrand())), +// Expressions.constant(ItemCategoryDto.of(itemEntity.getCategory())), +// item.additionalInfo, +// item.color, +// item.name, +// item.price, +// item.whenDiscovery, +// item.whereDiscovery, +// itemReport.createdAt, +// itemReport.updatedAt +// )) +// .from(itemReport) +// .join(itemReport.item, item) +// .join(itemReport.reporter, reporterUser) +// .join(itemReport.item.user, reportedUser) +// .where(itemReport.id.eq(itemReportId)) +// .fetchOne(); + + ItemReport content = jpaQueryFactory.selectFrom(itemReport) + .leftJoin(itemReport.item, item).fetchJoin() + .leftJoin(item).on(itemImg.item.eq(item)) + .leftJoin(item).on(itemLink.item.eq(item)) + .leftJoin(item.brand, brand).fetchJoin() + .leftJoin(item.celeb, celeb).fetchJoin() + .leftJoin(item.category, itemCategory).fetchJoin() + .where(itemReport.id.eq(itemReportId)) + .fetchOne(); + + return content; + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryCustom.java index 9720393d..8f46b119 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryCustom.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryCustom.java @@ -3,6 +3,7 @@ import com.sluv.domain.celeb.entity.Celeb; import com.sluv.domain.closet.entity.Closet; import com.sluv.domain.item.dto.ItemSimpleDto; +import com.sluv.domain.item.dto.ItemWithCountDto; import com.sluv.domain.item.entity.Item; import com.sluv.domain.search.dto.SearchFilterReqDto; import com.sluv.domain.user.entity.User; @@ -77,4 +78,8 @@ public interface ItemRepositoryCustom { List getItemContainKeyword(String keyword); Page getTrendItems(Pageable pageable); + + List getAllItemWithCelebCategory(); + + List getTop3HotItem(); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryImpl.java index f9f94433..2ae15534 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryImpl.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/repository/impl/ItemRepositoryImpl.java @@ -9,6 +9,7 @@ import com.sluv.domain.celeb.entity.QCeleb; import com.sluv.domain.closet.entity.Closet; import com.sluv.domain.item.dto.ItemSimpleDto; +import com.sluv.domain.item.dto.ItemWithCountDto; import com.sluv.domain.item.entity.Item; import com.sluv.domain.item.entity.QItemCategory; import com.sluv.domain.item.entity.RecentItem; @@ -34,6 +35,7 @@ import static com.sluv.domain.brand.entity.QBrand.brand; import static com.sluv.domain.brand.entity.QNewBrand.newBrand; import static com.sluv.domain.celeb.entity.QCeleb.celeb; +import static com.sluv.domain.celeb.entity.QCelebCategory.celebCategory; import static com.sluv.domain.celeb.entity.QInterestedCeleb.interestedCeleb; import static com.sluv.domain.celeb.entity.QNewCeleb.newCeleb; import static com.sluv.domain.closet.entity.QCloset.closet; @@ -919,4 +921,35 @@ public Page getTrendItems(Pageable pageable) { return PageableExecutionUtils.getPage(content, pageable, () -> countQuery.fetch().size()); } + @Override + public List getAllItemWithCelebCategory() { + return jpaQueryFactory.selectFrom(item) + .join(item.celeb, celeb).fetchJoin() + .join(item.celeb.celebCategory, celebCategory).fetchJoin() + .fetch(); + } + + @Override + public List getTop3HotItem() { + List fetch = jpaQueryFactory.select(item, itemImg, itemLike.count(), itemScrap.count()) + .from(item) + .leftJoin(itemImg).on(itemImg.item.eq(item)).fetchJoin() + .leftJoin(itemLike).on(itemLike.item.eq(item)).fetchJoin() + .leftJoin(itemScrap).on(itemScrap.item.eq(item)).fetchJoin() + .where(itemImg.representFlag.eq(true)) + .groupBy(item, itemLike, itemScrap) + .orderBy(item.viewNum.add(itemLike.count()).add(itemScrap.count()).desc(), + item.viewNum.desc(), + itemLike.count().desc(), + itemScrap.count().desc() + ) + .limit(3) + .fetch(); + + return fetch.stream() + .map(tuple -> ItemWithCountDto.of(tuple.get(item), tuple.get(itemImg).getItemImgUrl(), + tuple.get(itemLike.count()), tuple.get(itemScrap.count()))) + .toList(); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemDomainService.java index a183beaf..95e16303 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemDomainService.java @@ -3,6 +3,7 @@ import com.sluv.domain.celeb.entity.Celeb; import com.sluv.domain.closet.entity.Closet; import com.sluv.domain.item.dto.ItemSimpleDto; +import com.sluv.domain.item.dto.ItemWithCountDto; import com.sluv.domain.item.entity.Item; import com.sluv.domain.item.enums.ItemStatus; import com.sluv.domain.item.exception.ItemNotFoundException; @@ -166,4 +167,11 @@ public Page getTrendItems(Pageable pageable) { return itemRepository.getTrendItems(pageable); } + public List getAllItemWithCelebCategory() { + return itemRepository.getAllItemWithCelebCategory(); + } + + public List getTop3HotItem() { + return itemRepository.getTop3HotItem(); + } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemLinkDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemLinkDomainService.java index 0157ef31..00530cad 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemLinkDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemLinkDomainService.java @@ -19,8 +19,8 @@ public void deleteAllByItemId(Long itemId) { itemLinkRepository.deleteAllByItemId(itemId); } - public List findByItemId(Long itemId) { - return itemLinkRepository.findByItemId(itemId); + public List findAllByItemId(Long itemId) { + return itemLinkRepository.findAllByItemId(itemId); } public void saveItemLink(Item newItem, ItemLinkDto dto) { diff --git a/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemReportDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemReportDomainService.java index b74d1aa0..ee5e2f54 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemReportDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/item/service/ItemReportDomainService.java @@ -1,11 +1,15 @@ package com.sluv.domain.item.service; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.item.entity.Item; import com.sluv.domain.item.entity.ItemReport; import com.sluv.domain.item.enums.ItemReportReason; +import com.sluv.domain.item.exception.ItemReportNotFoundException; import com.sluv.domain.item.repository.ItemReportRepository; import com.sluv.domain.user.entity.User; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @Service @@ -24,4 +28,16 @@ public void saveItemReport(Item item, User user, ItemReportReason reason, String itemReportRepository.save(itemReport); } + public Page getAllItemReport(Pageable pageable, ReportStatus reportStatus) { + return itemReportRepository.getAllItemReport(pageable, reportStatus); + } + + public ItemReport getItemReportDetail(Long itemReportId) { + return itemReportRepository.getItemReportDetail(itemReportId); + } + + public ItemReport findById(Long itemReportId) { + return itemReportRepository.findById(itemReportId).orElseThrow(ItemReportNotFoundException::new); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/question/entity/QuestionReport.java b/sluv-domain/src/main/java/com/sluv/domain/question/entity/QuestionReport.java index 595e10d3..f1f35d6d 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/question/entity/QuestionReport.java +++ b/sluv-domain/src/main/java/com/sluv/domain/question/entity/QuestionReport.java @@ -4,17 +4,7 @@ import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.question.enums.QuestionReportReason; import com.sluv.domain.user.entity.User; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -67,4 +57,9 @@ public static QuestionReport toEntity(User user, Question question, .content(content) .build(); } + + public void changeQuestionReportStatus(ReportStatus reportStatus) { + this.reportStatus = reportStatus; + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/question/exception/QuestionReportNotFoundException.java b/sluv-domain/src/main/java/com/sluv/domain/question/exception/QuestionReportNotFoundException.java new file mode 100644 index 00000000..22ca1f19 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/question/exception/QuestionReportNotFoundException.java @@ -0,0 +1,14 @@ +package com.sluv.domain.question.exception; + +import com.sluv.common.exception.HttpStatusCode; + +public class QuestionReportNotFoundException extends QuestionException { + + private static final int ERROR_CODE = 2100; + private static final String MESSAGE = "존재하지 않는 질문 게시글 신고입니다."; + private static final HttpStatusCode STATUS = HttpStatusCode.BAD_REQUEST; + + public QuestionReportNotFoundException() { + super(ERROR_CODE, STATUS, MESSAGE); + } +} \ No newline at end of file diff --git a/sluv-domain/src/main/java/com/sluv/domain/question/repository/QuestionReportRepository.java b/sluv-domain/src/main/java/com/sluv/domain/question/repository/QuestionReportRepository.java index 98e03b8f..82bfbade 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/question/repository/QuestionReportRepository.java +++ b/sluv-domain/src/main/java/com/sluv/domain/question/repository/QuestionReportRepository.java @@ -1,8 +1,9 @@ package com.sluv.domain.question.repository; import com.sluv.domain.question.entity.QuestionReport; +import com.sluv.domain.question.repository.impl.QuestionReportRepositoryCustom; import org.springframework.data.jpa.repository.JpaRepository; -public interface QuestionReportRepository extends JpaRepository { +public interface QuestionReportRepository extends JpaRepository, QuestionReportRepositoryCustom { Boolean existsByQuestionIdAndReporterId(Long questionId, Long reporterId); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryCustom.java new file mode 100644 index 00000000..5aa30bb5 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryCustom.java @@ -0,0 +1,15 @@ +package com.sluv.domain.question.repository.impl; + +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.question.entity.QuestionReport; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.Optional; + +public interface QuestionReportRepositoryCustom { + + Page getAllQuestionReport(Pageable pageable, ReportStatus reportStatus); + + Optional getQuestionReportDetail(Long questionReportId); +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryImpl.java new file mode 100644 index 00000000..68aec4dc --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/question/repository/impl/QuestionReportRepositoryImpl.java @@ -0,0 +1,88 @@ +package com.sluv.domain.question.repository.impl; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.question.entity.QuestionReport; +import com.sluv.domain.user.entity.QUser; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.support.PageableExecutionUtils; + +import java.util.List; +import java.util.Optional; + +import static com.sluv.domain.question.entity.QQuestion.question; +import static com.sluv.domain.question.entity.QQuestionReport.questionReport; + + +@RequiredArgsConstructor +public class QuestionReportRepositoryImpl implements QuestionReportRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + private final QUser reporterUser = new QUser("reporterUser"); + private final QUser reportedUser = new QUser("reportedUser"); + + @Override + public Page getAllQuestionReport(Pageable pageable, ReportStatus reportStatus) { + BooleanExpression predicate = questionReport.isNotNull(); + if (reportStatus != null) { + predicate = predicate.and(questionReport.reportStatus.eq(reportStatus)); + } + +// List content = jpaQueryFactory +// .select(Projections.constructor(QuestionReportInfoDto.class, +// questionReport.reporter.id, +// questionReport.reporter.nickname, +// question.user.id, +// question.user.nickname, +// questionReport.id, +// questionReport.questionReportReason, +// questionReport.content, +// questionReport.reportStatus, +// questionReport.createdAt, +// questionReport.updatedAt)) + List content = jpaQueryFactory.selectFrom(questionReport) + .where(predicate) + .orderBy(questionReport.createdAt.desc()) + .leftJoin(questionReport.question, question).fetchJoin() + .leftJoin(questionReport.reporter, reporterUser).fetchJoin() + .leftJoin(question.user, reportedUser).fetchJoin() + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + JPAQuery countQuery = jpaQueryFactory.selectFrom(questionReport) + .where(predicate); + + return PageableExecutionUtils.getPage(content, pageable, () -> countQuery.fetch().size()); + } + + @Override + public Optional getQuestionReportDetail(Long questionReportId) { +// QuestionReportDetailDto detailDto = jpaQueryFactory +// .select(Projections.constructor(QuestionReportDetailDto.class, +// questionReport.reporter.id, +// questionReport.reporter.nickname, +// question.user.id, +// question.user.nickname, +// questionReport.id, +// questionReport.questionReportReason, +// questionReport.content, +// questionReport.reportStatus, +// question.title, +// question.content, +// questionReport.createdAt, +// questionReport.updatedAt)) + QuestionReport content = jpaQueryFactory.selectFrom(questionReport) + .where(questionReport.id.eq(questionReportId)) + .leftJoin(questionReport.question, question).fetchJoin() + .leftJoin(questionReport.reporter, reporterUser).fetchJoin() + .leftJoin(question.user, reportedUser).fetchJoin() + .fetchOne(); + + return Optional.ofNullable(content); + } +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/question/service/QuestionReportDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/question/service/QuestionReportDomainService.java index 8c57e1a4..9121e5a1 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/question/service/QuestionReportDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/question/service/QuestionReportDomainService.java @@ -1,11 +1,15 @@ package com.sluv.domain.question.service; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.question.entity.Question; import com.sluv.domain.question.entity.QuestionReport; import com.sluv.domain.question.enums.QuestionReportReason; +import com.sluv.domain.question.exception.QuestionReportNotFoundException; import com.sluv.domain.question.repository.QuestionReportRepository; import com.sluv.domain.user.entity.User; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @Service @@ -23,4 +27,17 @@ public QuestionReport saveQuestionReport(User user, Question question, QuestionR QuestionReport questionReport = QuestionReport.toEntity(user, question, reason, content); return questionReportRepository.save(questionReport); } + + public Page getAllQuestionReport(Pageable pageable, ReportStatus reportStatus) { + return questionReportRepository.getAllQuestionReport(pageable, reportStatus); + } + + public QuestionReport getQuestionReportDetail(Long questionReportId) { + return questionReportRepository.getQuestionReportDetail(questionReportId) + .orElseThrow(QuestionReportNotFoundException::new); + } + + public QuestionReport findById(Long questionReportId) { + return questionReportRepository.findById(questionReportId).orElseThrow(QuestionReportNotFoundException::new); + } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/dto/UserReportStackDto.java b/sluv-domain/src/main/java/com/sluv/domain/user/dto/UserReportStackDto.java new file mode 100644 index 00000000..8c9c6109 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/user/dto/UserReportStackDto.java @@ -0,0 +1,19 @@ +package com.sluv.domain.user.dto; + +import com.sluv.domain.user.enums.UserStatus; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class UserReportStackDto { + + private String nickname; + private String profileImgUrl; + private UserStatus userStatus; + private Long waitingReportCount; + private Long reportStackCount; + +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/dto/UserWithFollowerCountDto.java b/sluv-domain/src/main/java/com/sluv/domain/user/dto/UserWithFollowerCountDto.java new file mode 100644 index 00000000..ef9eaf4a --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/user/dto/UserWithFollowerCountDto.java @@ -0,0 +1,27 @@ +package com.sluv.domain.user.dto; + +import com.sluv.domain.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserWithFollowerCountDto { + private Long id; + private String nickName; + private String imgUrl; + private Long followerCount; + + public static UserWithFollowerCountDto of(User user, Long followerCount) { + return UserWithFollowerCountDto.builder() + .id(user.getId()) + .nickName(user.getNickname()) + .imgUrl(user.getProfileImgUrl()) + .followerCount(followerCount) + .build(); + } +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReport.java b/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReport.java index 3a16ab02..d6ade9cd 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReport.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReport.java @@ -3,17 +3,7 @@ import com.sluv.domain.common.entity.BaseEntity; import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.user.enums.UserReportReason; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -64,4 +54,9 @@ public static UserReport toEntity(User reporter, User target, UserReportReason r .reportStatus(ReportStatus.WAITING) .build(); } + + public void changeUserReportStatus(ReportStatus reportStatus) { + this.reportStatus = reportStatus; + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReportStack.java b/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReportStack.java index 749736e3..a427ff47 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReportStack.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/entity/UserReportStack.java @@ -1,23 +1,18 @@ package com.sluv.domain.user.entity; import com.sluv.domain.common.entity.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter +@Builder @NoArgsConstructor +@AllArgsConstructor @Table(name = "user_report_stack") public class UserReportStack extends BaseEntity { @@ -32,9 +27,9 @@ public class UserReportStack extends BaseEntity { private User reported; - @Builder - public UserReportStack(Long id, User reported) { - this.id = id; - this.reported = reported; + public static UserReportStack toEntity(User reported) { + return UserReportStack.builder() + .reported(reported) + .build(); } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/exception/InvalidReportStatusException.java b/sluv-domain/src/main/java/com/sluv/domain/user/exception/InvalidReportStatusException.java new file mode 100644 index 00000000..34d2057a --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/user/exception/InvalidReportStatusException.java @@ -0,0 +1,15 @@ +package com.sluv.domain.user.exception; + +import com.sluv.common.exception.ApplicationException; +import com.sluv.common.exception.HttpStatusCode; + +public class InvalidReportStatusException extends ApplicationException { + + private static final int ERROR_CODE = 2002; + private static final String MESSAGE = "잘못된 UserReportStatus 입니다."; + private static final HttpStatusCode STATUS = HttpStatusCode.BAD_REQUEST; + + public InvalidReportStatusException() { + super(ERROR_CODE, STATUS, MESSAGE); + } +} \ No newline at end of file diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/exception/UserReportNotFoundException.java b/sluv-domain/src/main/java/com/sluv/domain/user/exception/UserReportNotFoundException.java new file mode 100644 index 00000000..123fd35d --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/user/exception/UserReportNotFoundException.java @@ -0,0 +1,13 @@ +package com.sluv.domain.user.exception; + +import com.sluv.common.exception.HttpStatusCode; + +public class UserReportNotFoundException extends UserException { + private static final int ERROR_CODE = 2001; + private static final String MESSAGE = "존재하지 않는 유저 신고입니다."; + private static final HttpStatusCode STATUS = HttpStatusCode.BAD_REQUEST; + + public UserReportNotFoundException() { + super(ERROR_CODE, STATUS, MESSAGE); + } +} \ No newline at end of file diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/repository/UserReportStackRepository.java b/sluv-domain/src/main/java/com/sluv/domain/user/repository/UserReportStackRepository.java index b438468c..b3774891 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/repository/UserReportStackRepository.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/repository/UserReportStackRepository.java @@ -1,8 +1,14 @@ package com.sluv.domain.user.repository; +import com.sluv.domain.user.entity.User; import com.sluv.domain.user.entity.UserReportStack; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; + public interface UserReportStackRepository extends JpaRepository { void deleteAllByReportedId(Long userId); + + Long countByReportedAndCreatedAtAfter(User reportedUser, LocalDateTime createdAt); + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryCustom.java index bb87c3f1..c2b69192 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryCustom.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryCustom.java @@ -1,9 +1,15 @@ package com.sluv.domain.user.repository.impl; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.entity.UserReport; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; public interface UserReportRepositoryCustom { Boolean findExistence(User user, User target); void withdrawByUserId(Long userId); + + Page getAllUserReport(Pageable pageable, ReportStatus reportStatus); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryImpl.java index fc9b71d1..1ebb5ea8 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryImpl.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserReportRepositoryImpl.java @@ -1,10 +1,20 @@ package com.sluv.domain.user.repository.impl; -import static com.sluv.domain.user.entity.QUserReport.userReport; - +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import com.sluv.domain.common.enums.ReportStatus; +import com.sluv.domain.user.entity.QUser; import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.entity.UserReport; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.support.PageableExecutionUtils; + +import java.util.List; + +import static com.sluv.domain.user.entity.QUserReport.userReport; @RequiredArgsConstructor public class UserReportRepositoryImpl implements UserReportRepositoryCustom { @@ -33,4 +43,41 @@ public void withdrawByUserId(Long userId) { // .where(userReport.reported.id.eq(userId)) // .execute(); } + + @Override + public Page getAllUserReport(Pageable pageable, ReportStatus reportStatus) { + QUser reporterUser = new QUser("reporterUser"); + QUser reportedUser = new QUser("reportedUser"); + + BooleanExpression predicate = userReport.isNotNull(); + if (reportStatus != null) { + predicate = predicate.and(userReport.reportStatus.eq(reportStatus)); + } + +// List content = jpaQueryFactory +// .select(Projections.constructor(UserReportInfoDto.class, +// userReport.reporter.id, +// userReport.reporter.nickname, +// userReport.reported.id, +// userReport.reported.nickname, +// userReport.id, +// userReport.userReportReason, +// userReport.content, +// userReport.reportStatus, +// userReport.createdAt, +// userReport.updatedAt)) + List content = jpaQueryFactory.selectFrom(userReport) + .where(predicate) + .orderBy(userReport.createdAt.desc()) + .leftJoin(userReport.reporter, reporterUser).fetchJoin() + .leftJoin(userReport.reported, reportedUser).fetchJoin() + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + JPAQuery countQuery = jpaQueryFactory.selectFrom(userReport) + .where(predicate); + + return PageableExecutionUtils.getPage(content, pageable, () -> countQuery.fetch().size()); + } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryCustom.java index 3a2d0720..b9408752 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryCustom.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryCustom.java @@ -1,6 +1,8 @@ package com.sluv.domain.user.repository.impl; import com.sluv.domain.auth.enums.SnsType; +import com.sluv.domain.user.dto.UserReportStackDto; +import com.sluv.domain.user.dto.UserWithFollowerCountDto; import com.sluv.domain.user.entity.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -24,4 +26,8 @@ public interface UserRepositoryCustom { List getDeletedUsersAfter7Days(); Optional findBySnsWithEmailOrNull(String email, SnsType snsType); + + Page getAllUserInfo(Pageable pageable); + + List getTop3HotUser(); } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryImpl.java index 0a6bbf6a..b8cdf093 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryImpl.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/repository/impl/UserRepositoryImpl.java @@ -1,9 +1,15 @@ package com.sluv.domain.user.repository.impl; +import com.querydsl.core.Tuple; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import com.sluv.domain.auth.enums.SnsType; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.item.enums.ItemStatus; +import com.sluv.domain.user.dto.UserReportStackDto; +import com.sluv.domain.user.dto.UserWithFollowerCountDto; import com.sluv.domain.user.entity.Follow; import com.sluv.domain.user.entity.QUser; import com.sluv.domain.user.entity.User; @@ -21,6 +27,8 @@ import static com.sluv.domain.item.entity.QItemLike.itemLike; import static com.sluv.domain.user.entity.QFollow.follow; import static com.sluv.domain.user.entity.QUser.user; +import static com.sluv.domain.user.entity.QUserReport.userReport; +import static com.sluv.domain.user.entity.QUserReportStack.userReportStack; import static com.sluv.domain.user.enums.UserStatus.ACTIVE; import static com.sluv.domain.user.enums.UserStatus.DELETED; @@ -176,4 +184,45 @@ public Optional findBySnsWithEmailOrNull(String email, SnsType snsType) { .fetchOne(); return Optional.ofNullable(user); } + + @Override + public Page getAllUserInfo(Pageable pageable) { + + List content = jpaQueryFactory + .select(Projections.constructor(UserReportStackDto.class, + user.nickname, + user.profileImgUrl, + user.userStatus, + JPAExpressions.select(userReport.reported.id.count()) + .from(userReport) + .where(userReport.reported.id.eq(user.id) + .and(userReport.reportStatus.stringValue().eq(ReportStatus.WAITING.name()))), + JPAExpressions.select(userReportStack.reported.id.count()) + .from(userReportStack) + .where(userReportStack.reported.id.eq(user.id)) + )) + .from(user) + .orderBy(user.createdAt.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + return PageableExecutionUtils.getPage(content, pageable, () -> jpaQueryFactory.from(user).fetch().size()); + } + + @Override + public List getTop3HotUser() { + List fetch = jpaQueryFactory.select(user, follow.count()) + .from(user) + .leftJoin(follow).on(follow.followee.eq(user)).fetchJoin() + .groupBy(user) + .orderBy(follow.count().desc()) + .limit(3) + .fetch(); + + return fetch.stream() + .map(tuple -> UserWithFollowerCountDto.of(tuple.get(user), tuple.get(follow.count()))) + .toList(); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/service/UserDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/user/service/UserDomainService.java index eb78a01c..445d0478 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/service/UserDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/service/UserDomainService.java @@ -1,6 +1,8 @@ package com.sluv.domain.user.service; import com.sluv.domain.auth.enums.SnsType; +import com.sluv.domain.user.dto.UserReportStackDto; +import com.sluv.domain.user.dto.UserWithFollowerCountDto; import com.sluv.domain.user.entity.User; import com.sluv.domain.user.exception.UserNotFoundException; import com.sluv.domain.user.repository.UserRepository; @@ -62,4 +64,16 @@ public Page getSearchUser(List searchUserIds, Pageable pageable) { public User findBySnsWithEmailOrNull(String email, SnsType snsType) { return userRepository.findBySnsWithEmailOrNull(email, snsType).orElse(null); } + + public Page getAllUserInfo(Pageable pageable) { + return userRepository.getAllUserInfo(pageable); + } + + public List findAll() { + return userRepository.findAll(); + } + + public List getTop3HotUser() { + return userRepository.getTop3HotUser(); + } } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportDomainService.java index 327004a5..ee0df9ce 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportDomainService.java @@ -1,10 +1,14 @@ package com.sluv.domain.user.service; +import com.sluv.domain.common.enums.ReportStatus; import com.sluv.domain.user.entity.User; import com.sluv.domain.user.entity.UserReport; import com.sluv.domain.user.enums.UserReportReason; +import com.sluv.domain.user.exception.UserReportNotFoundException; import com.sluv.domain.user.repository.UserReportRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @Service @@ -22,4 +26,12 @@ public void saveUserReport(User user, User target, UserReportReason reason, Stri userReportRepository.save(userReport); } + public Page getAllUserReport(Pageable pageable, ReportStatus reportStatus) { + return userReportRepository.getAllUserReport(pageable, reportStatus); + } + + public UserReport findById(Long userReportId) { + return userReportRepository.findById(userReportId).orElseThrow(UserReportNotFoundException::new); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportStackDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportStackDomainService.java index b7c708cb..d54f23ed 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportStackDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/user/service/UserReportStackDomainService.java @@ -1,9 +1,13 @@ package com.sluv.domain.user.service; +import com.sluv.domain.user.entity.User; +import com.sluv.domain.user.entity.UserReportStack; import com.sluv.domain.user.repository.UserReportStackRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; + @Service @RequiredArgsConstructor public class UserReportStackDomainService { @@ -14,4 +18,13 @@ public void deleteAllByReportedId(Long userId) { userReportStackRepository.deleteAllByReportedId(userId); } + public UserReportStack saveReportStack(User reportedUser) { + UserReportStack userReportStack = UserReportStack.toEntity(reportedUser); + return userReportStackRepository.save(userReportStack); + } + + public long countByReportedAndCreatedAtAfter(User reportedUser, LocalDateTime oneMonthAgo) { + return userReportStackRepository.countByReportedAndCreatedAtAfter(reportedUser, oneMonthAgo); + } + } diff --git a/sluv-domain/src/main/java/com/sluv/domain/visit/entity/DailyVisit.java b/sluv-domain/src/main/java/com/sluv/domain/visit/entity/DailyVisit.java new file mode 100644 index 00000000..488d733d --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/visit/entity/DailyVisit.java @@ -0,0 +1,32 @@ +package com.sluv.domain.visit.entity; + +import com.sluv.domain.common.entity.BaseEntity; +import com.sluv.domain.user.entity.User; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Table(name = "daily_visit") +public class DailyVisit extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "daily_visit_id") + private Long id; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + public static DailyVisit of(User user) { + return DailyVisit.builder() + .user(user) + .build(); + } +} \ No newline at end of file diff --git a/sluv-domain/src/main/java/com/sluv/domain/visit/repository/DailyVisitRepository.java b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/DailyVisitRepository.java new file mode 100644 index 00000000..7e40b68b --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/DailyVisitRepository.java @@ -0,0 +1,7 @@ +package com.sluv.domain.visit.repository; + +import com.sluv.domain.visit.entity.DailyVisit; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DailyVisitRepository extends JpaRepository { +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/visit/repository/VisitHistoryRepository.java b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/VisitHistoryRepository.java index c62cea11..41e47925 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/visit/repository/VisitHistoryRepository.java +++ b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/VisitHistoryRepository.java @@ -1,7 +1,8 @@ package com.sluv.domain.visit.repository; import com.sluv.domain.visit.entity.VisitHistory; +import com.sluv.domain.visit.repository.impl.VisitHistoryRepositoryCustom; import org.springframework.data.jpa.repository.JpaRepository; -public interface VisitHistoryRepository extends JpaRepository { +public interface VisitHistoryRepository extends JpaRepository, VisitHistoryRepositoryCustom { } diff --git a/sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryCustom.java b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryCustom.java new file mode 100644 index 00000000..64db6baf --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.sluv.domain.visit.repository.impl; + + +import com.sluv.domain.visit.entity.VisitHistory; + +import java.util.List; + +public interface VisitHistoryRepositoryCustom { + List getVisitHistoryFor10Days(); +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryImpl.java b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryImpl.java new file mode 100644 index 00000000..7db21126 --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/visit/repository/impl/VisitHistoryRepositoryImpl.java @@ -0,0 +1,28 @@ +package com.sluv.domain.visit.repository.impl; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.sluv.domain.visit.entity.VisitHistory; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +import static com.sluv.domain.visit.entity.QVisitHistory.visitHistory; + + +@RequiredArgsConstructor +public class VisitHistoryRepositoryImpl implements VisitHistoryRepositoryCustom { + private final JPAQueryFactory jpaQueryFactory; + + @Override + public List getVisitHistoryFor10Days() { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime toDay = LocalDateTime.of( + now.getYear(), now.getMonth(), now.getDayOfMonth(), 0, 0, 0); + return jpaQueryFactory.selectFrom(visitHistory) + .where(visitHistory.date.between(toDay.minusDays(10), toDay.minusDays(1))) + .orderBy(visitHistory.date.asc()) + .fetch(); + } + +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/visit/service/DailyVisitDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/visit/service/DailyVisitDomainService.java new file mode 100644 index 00000000..7113738c --- /dev/null +++ b/sluv-domain/src/main/java/com/sluv/domain/visit/service/DailyVisitDomainService.java @@ -0,0 +1,20 @@ +package com.sluv.domain.visit.service; + +import com.sluv.domain.visit.entity.DailyVisit; +import com.sluv.domain.visit.repository.DailyVisitRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class DailyVisitDomainService { + + private final DailyVisitRepository dailyVisitRepository; + + public List findAll() { + return dailyVisitRepository.findAll(); + } + +} diff --git a/sluv-domain/src/main/java/com/sluv/domain/visit/service/VisitHistoryDomainService.java b/sluv-domain/src/main/java/com/sluv/domain/visit/service/VisitHistoryDomainService.java index 92e0e4e3..a062f633 100644 --- a/sluv-domain/src/main/java/com/sluv/domain/visit/service/VisitHistoryDomainService.java +++ b/sluv-domain/src/main/java/com/sluv/domain/visit/service/VisitHistoryDomainService.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.List; @Service @RequiredArgsConstructor @@ -18,4 +19,8 @@ public void saveVisitHistory(LocalDateTime localDateTime, Long visitantCount) { visitHistoryRepository.save(visitHistory); } + public List getVisitHistoryFor10Days() { + return visitHistoryRepository.getVisitHistoryFor10Days(); + } + }