diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0e8b454..4157904 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -11,7 +11,9 @@ "Bash(./gradlew.bat bootRun --console=plain)", "Bash(echo \"bootRun started in background, PID $!\")", "Bash(git -c user.name=\"hehih\" -c user.email=\"hehihoho86@gmail.com\" commit -q -m \"docs: add discovery page design spec\")", - "Bash(git -c user.name=\"hehih\" -c user.email=\"hehihoho86@gmail.com\" commit -q -m \"docs: add discovery page implementation plan\")" + "Bash(git -c user.name=\"hehih\" -c user.email=\"hehihoho86@gmail.com\" commit -q -m \"docs: add discovery page implementation plan\")", + "PowerShell($env:JAVA_HOME=\"D:\\\\Development\\\\app\\\\JDK\\\\jdk-21.0.5\"; .\\\\gradlew.bat clean compileJava --console=plain 2>&1 | Select-Object -Last 12)", + "Bash(git -c user.name=hehih -c user.email=hehihoho86@gmail.com commit -q -m 'feat\\(discover\\): add discovery page for finding rework candidates *)" ] } } diff --git a/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationController.java b/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationController.java index b0f7eb1..65d6d29 100644 --- a/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationController.java +++ b/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationController.java @@ -40,6 +40,22 @@ public class ChannelVideoCurationController { return ApiResponse.ok(curationService.findOutperformers(limit, minRatio)); } + @GetMapping("/discover") + @Operation(summary = "발굴(Discovery) 조회", + description = "필터: periodDays(최근 N일), minRatio(배율 하한), shortsOnly, source(CHANNEL|SEARCH), " + + "unprocessedOnly(NEW/REVIEWING만). 정렬 sortBy(기본 viewsPerSubRatio↓), limit(기본100). " + + "EXCLUDED 는 항상 제외.") + public ApiResponse> discover( + @RequestParam(required = false) Integer periodDays, + @RequestParam(required = false) java.math.BigDecimal minRatio, + @RequestParam(defaultValue = "false") boolean shortsOnly, + @RequestParam(required = false) String source, + @RequestParam(defaultValue = "false") boolean unprocessedOnly, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) Integer limit) { + return ApiResponse.ok(curationService.discover(periodDays, minRatio, shortsOnly, source, unprocessedOnly, sortBy, limit)); + } + @PostMapping("/backfill") @Operation(summary = "기존 수집 영상 지표 백필", description = "새 컬럼 추가 이전에 수집된 영상의 파생 지표/큐레이션 기본값을 재계산(외부 API 호출 없음, 재실행 안전).") diff --git a/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationService.java b/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationService.java index 62571cd..4136780 100644 --- a/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationService.java +++ b/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoCurationService.java @@ -129,6 +129,29 @@ public class ChannelVideoCurationService { org.springframework.data.domain.PageRequest.of(0, size)); } + /** + * 발굴(Discovery) 조회 — 필터 + 정렬 + 개수 제한. + * @param periodDays 최근 N일만 (null/0이면 전체) + * @param minRatio viewsPerSubRatio 하한 (null이면 미적용) + * @param shortsOnly Shorts만 + * @param source CHANNEL | SEARCH (빈값이면 전체) + * @param unprocessedOnly true면 NEW/REVIEWING 만 + * @param sortBy 정렬 필드(ALLOWED_SORT, 기본 viewsPerSubRatio 내림차순, null은 항상 마지막) + * @param limit 최대 개수(기본 100, 최대 200) + */ + public List discover(Integer periodDays, java.math.BigDecimal minRatio, + boolean shortsOnly, String source, boolean unprocessedOnly, + String sortBy, Integer limit) { + java.time.LocalDateTime publishedAfter = + (periodDays == null || periodDays <= 0) ? null : java.time.LocalDateTime.now().minusDays(periodDays); + String sourceFilter = StringUtils.hasText(source) ? source : null; + String sortField = StringUtils.hasText(sortBy) && ALLOWED_SORT.contains(sortBy) ? sortBy : "viewsPerSubRatio"; + int size = (limit == null || limit <= 0) ? 100 : Math.min(limit, 200); + Sort sort = Sort.by(Sort.Order.desc(sortField).nullsLast()); + return channelVideoRepository.discover(publishedAfter, minRatio, sourceFilter, shortsOnly, unprocessedOnly, + org.springframework.data.domain.PageRequest.of(0, size, sort)); + } + /** * 기존 수집 영상의 파생 지표/큐레이션 기본값 백필. * 새 컬럼 추가 이전에 수집된 행들은 값이 null 이라 통계/필터/떡상 발굴에 잡히지 않으므로, diff --git a/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoRepository.java b/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoRepository.java index 784bfcb..410788d 100644 --- a/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoRepository.java +++ b/src/main/java/com/hlab/yanalyst/domain/channel/ChannelVideoRepository.java @@ -41,4 +41,27 @@ public interface ChannelVideoRepository extends JpaRepository 'EXCLUDED' and " + + "(:publishedAfter is null or v.publishedAt >= :publishedAfter) and " + + "(:minRatio is null or v.viewsPerSubRatio >= :minRatio) and " + + "(:source is null or v.source = :source) and " + + "(:shortsOnly = false or v.isShorts = true) and " + + "(:unprocessedOnly = false or v.interestStatus in ('NEW','REVIEWING'))") + java.util.List discover(@Param("publishedAfter") java.time.LocalDateTime publishedAfter, + @Param("minRatio") java.math.BigDecimal minRatio, + @Param("source") String source, + @Param("shortsOnly") boolean shortsOnly, + @Param("unprocessedOnly") boolean unprocessedOnly, + org.springframework.data.domain.Pageable pageable); } diff --git a/src/main/java/com/hlab/yanalyst/web/WebController.java b/src/main/java/com/hlab/yanalyst/web/WebController.java index e64e008..b3703c3 100644 --- a/src/main/java/com/hlab/yanalyst/web/WebController.java +++ b/src/main/java/com/hlab/yanalyst/web/WebController.java @@ -46,6 +46,12 @@ public class WebController { return "board"; } + @GetMapping("/discover") + public String discover(Model model) { + model.addAttribute("currentPage", "discover"); + return "discover"; + } + @GetMapping("/publish") public String publish(Model model) { model.addAttribute("currentPage", "publish"); diff --git a/src/main/resources/templates/discover.html b/src/main/resources/templates/discover.html new file mode 100644 index 0000000..1c3586a --- /dev/null +++ b/src/main/resources/templates/discover.html @@ -0,0 +1,225 @@ + + + + + h-lab - 발굴 + + + +
+
+

발굴 (Discovery)

+

수집한 영상 중 재가공할 떡상 후보를 빠르게 골라냅니다. (제외 처리한 영상은 숨겨집니다)

+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + + +
썸네일제목채널구독자조회수시간당배율업로드상태관리
로딩 중...
+
+ + + + + + + +
+ + + diff --git a/src/main/resources/templates/layout/sidebar.html b/src/main/resources/templates/layout/sidebar.html index 3b60a47..79c3f6e 100644 --- a/src/main/resources/templates/layout/sidebar.html +++ b/src/main/resources/templates/layout/sidebar.html @@ -57,6 +57,13 @@ 수집함 +
  • + + + 발굴 + +