diff --git a/.gitignore b/.gitignore index 07461c14..af84798e 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,8 @@ CODEX.md ### ENV File ### .env + +docs + +### macOS ### +.DS_Store diff --git a/application/build.gradle b/application/build.gradle index 893cbb2c..b831cbec 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -27,6 +27,9 @@ dependencies { // Transaction implementation 'org.springframework:spring-tx' + // Spring Data (Page, Pageable) + implementation 'org.springframework.data:spring-data-commons' + // Thymeleaf implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' diff --git a/application/src/main/java/com/yapp/ndgl/application/domains/admin/controller/AdminUserSuggestedTemplateViewController.java b/application/src/main/java/com/yapp/ndgl/application/domains/admin/controller/AdminUserSuggestedTemplateViewController.java new file mode 100644 index 00000000..0b0e78ff --- /dev/null +++ b/application/src/main/java/com/yapp/ndgl/application/domains/admin/controller/AdminUserSuggestedTemplateViewController.java @@ -0,0 +1,61 @@ +package com.yapp.ndgl.application.domains.admin.controller; + +import java.util.List; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import com.yapp.ndgl.application.domains.travel.controller.dto.AdminUserSuggestedTemplateResponse; +import com.yapp.ndgl.application.domains.travel.facade.UserSuggestedTemplateFacade; +import com.yapp.ndgl.common.response.PageResponse; +import com.yapp.ndgl.common.type.SuggestionStatus; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Controller +@RequestMapping("/admin/user-suggested-templates") +@RequiredArgsConstructor +public class AdminUserSuggestedTemplateViewController { + + private final UserSuggestedTemplateFacade userSuggestedTemplateFacade; + + @GetMapping + public String listPage( + @RequestParam(value = "status", required = false) SuggestionStatus status, + @RequestParam(value = "page", defaultValue = "0") int page, + @RequestParam(value = "size", defaultValue = "12") int size, + Model model + ) { + try { + PageResponse result = + userSuggestedTemplateFacade.readUserSuggestedTemplatesForAdmin(status, page, size); + model.addAttribute("templates", result.getContent()); + model.addAttribute("hasNext", result.isHasNext()); + model.addAttribute("hasPrevious", result.isHasPrevious()); + model.addAttribute("totalElements", result.getTotalElements()); + model.addAttribute("totalPages", result.getTotalPages()); + model.addAttribute("currentPage", page); + model.addAttribute("size", size); + model.addAttribute("status", status); + model.addAttribute("statuses", SuggestionStatus.values()); + } catch (Exception e) { + log.error("사용자 제안 템플릿 목록 조회 실패", e); + model.addAttribute("errorMessage", "목록을 불러오는 중 오류가 발생했습니다."); + model.addAttribute("templates", List.of()); + model.addAttribute("hasNext", false); + model.addAttribute("hasPrevious", false); + model.addAttribute("totalElements", 0L); + model.addAttribute("totalPages", 0); + model.addAttribute("currentPage", page); + model.addAttribute("size", size); + model.addAttribute("status", status); + model.addAttribute("statuses", SuggestionStatus.values()); + } + return "admin/user-suggested-template-list"; + } +} diff --git a/application/src/main/java/com/yapp/ndgl/application/domains/travel/controller/dto/AdminUserSuggestedTemplateResponse.java b/application/src/main/java/com/yapp/ndgl/application/domains/travel/controller/dto/AdminUserSuggestedTemplateResponse.java new file mode 100644 index 00000000..190d6037 --- /dev/null +++ b/application/src/main/java/com/yapp/ndgl/application/domains/travel/controller/dto/AdminUserSuggestedTemplateResponse.java @@ -0,0 +1,35 @@ +package com.yapp.ndgl.application.domains.travel.controller.dto; + +import java.time.LocalDateTime; + +import com.yapp.ndgl.common.type.DomesticRegion; +import com.yapp.ndgl.common.type.SuggestionStatus; +import com.yapp.ndgl.common.type.TravelCategory; +import com.yapp.ndgl.domain.travel.UserSuggestedTemplate; + +public record AdminUserSuggestedTemplateResponse( + Long id, + String videoId, + String videoLink, + String recommendReason, + String suggesterUuid, + TravelCategory category, + DomesticRegion region, + SuggestionStatus status, + LocalDateTime createdAt +) { + + public static AdminUserSuggestedTemplateResponse toResponse(final UserSuggestedTemplate domain) { + return new AdminUserSuggestedTemplateResponse( + domain.getId(), + domain.getVideoId(), + domain.getVideoLink(), + domain.getRecommendReason(), + domain.getSuggesterUuid(), + domain.getCategory(), + domain.getRegion(), + domain.getStatus(), + domain.getCreatedAt() + ); + } +} diff --git a/application/src/main/java/com/yapp/ndgl/application/domains/travel/facade/UserSuggestedTemplateFacade.java b/application/src/main/java/com/yapp/ndgl/application/domains/travel/facade/UserSuggestedTemplateFacade.java index 53b10268..4e99ae6e 100644 --- a/application/src/main/java/com/yapp/ndgl/application/domains/travel/facade/UserSuggestedTemplateFacade.java +++ b/application/src/main/java/com/yapp/ndgl/application/domains/travel/facade/UserSuggestedTemplateFacade.java @@ -1,13 +1,18 @@ package com.yapp.ndgl.application.domains.travel.facade; import com.yapp.ndgl.application.common.annotation.Facade; +import com.yapp.ndgl.application.domains.travel.controller.dto.AdminUserSuggestedTemplateResponse; import com.yapp.ndgl.application.domains.travel.controller.dto.CreateUserSuggestedTemplateRequest; import com.yapp.ndgl.application.domains.travel.event.publisher.UserSuggestedTemplateEventPublisher; import com.yapp.ndgl.application.domains.travel.service.UserSuggestedTemplateService; import com.yapp.ndgl.application.utils.YoutubeUrlParser; +import com.yapp.ndgl.common.response.PageResponse; +import com.yapp.ndgl.common.type.SuggestionStatus; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Facade @RequiredArgsConstructor public class UserSuggestedTemplateFacade { @@ -28,4 +33,11 @@ public void createUserSuggestedTemplate( public void subscribe(final Long templateId, final String uuid) { userSuggestedTemplateService.subscribe(templateId, uuid); } + + public PageResponse readUserSuggestedTemplatesForAdmin( + final SuggestionStatus status, final int page, final int size + ) { + log.info("어드민 사용자 제안 템플릿 목록을 조회합니다. status = {}, page = {}, size = {}", status, page, size); + return userSuggestedTemplateService.readUserSuggestedTemplatesForAdmin(status, page, size); + } } diff --git a/application/src/main/java/com/yapp/ndgl/application/domains/travel/service/UserSuggestedTemplateService.java b/application/src/main/java/com/yapp/ndgl/application/domains/travel/service/UserSuggestedTemplateService.java index b37870a6..d139e68b 100644 --- a/application/src/main/java/com/yapp/ndgl/application/domains/travel/service/UserSuggestedTemplateService.java +++ b/application/src/main/java/com/yapp/ndgl/application/domains/travel/service/UserSuggestedTemplateService.java @@ -1,11 +1,14 @@ package com.yapp.ndgl.application.domains.travel.service; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.yapp.ndgl.application.domains.travel.controller.dto.AdminUserSuggestedTemplateResponse; import com.yapp.ndgl.application.domains.travel.controller.dto.CreateUserSuggestedTemplateRequest; import com.yapp.ndgl.common.exception.GlobalException; import com.yapp.ndgl.common.exception.TravelErrorCode; +import com.yapp.ndgl.common.response.PageResponse; import com.yapp.ndgl.common.type.SuggestionStatus; import com.yapp.ndgl.domain.travel.UserSuggestedTemplate; import com.yapp.ndgl.application.common.annotation.DistributedLock; @@ -64,4 +67,19 @@ public void subscribe(final Long templateId, final String uuid) { log.info("사용자 제안 여행 템플릿 구독을 신청합니다. templateId={}, subscriberUuid={}", templateId, uuid); userSuggestedTemplateDomainService.subscribe(templateId, uuid); } + + public PageResponse readUserSuggestedTemplatesForAdmin( + final SuggestionStatus status, final int page, final int size + ) { + Page result = userSuggestedTemplateDomainService + .findUserSuggestedTemplates(status, page, size) + .map(AdminUserSuggestedTemplateResponse::toResponse); + + return PageResponse.of( + result.getContent(), + result.getNumber(), + result.getSize(), + result.getTotalElements() + ); + } } diff --git a/application/src/main/resources/templates/admin/travel-template-list.html b/application/src/main/resources/templates/admin/travel-template-list.html index 834cbfe0..ba05b749 100644 --- a/application/src/main/resources/templates/admin/travel-template-list.html +++ b/application/src/main/resources/templates/admin/travel-template-list.html @@ -162,6 +162,7 @@

여행 템플릿 목록