Add GET /api/dashboard/summary aggregating pipeline status, category distribution, publish summary, and outperformers in one call. Rewrite dashboard.html with 5 KPI cards, pipeline funnel, publish status, and category/source-format breakdowns (CSS bars, no chart lib). Backend: ChannelVideoRepository counts (shorts/uncategorized), PublishPackageRepository.countByStatus, pipelineStats shorts/longForm, CategoryService.distribution, PublishService.dashboardSummary, new DashboardService + DashboardApiController. Fix: PublishService.list(null) hit UnsupportedOperationException because findAll(Sort) uses Criteria, which rejects nullsLast precedence. Route the no-status path through a @Query method so Sort is appended as HQL ORDER BY (supports NULLS LAST). Also fixes the latent bug in /api/v1/publish all-list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
39 lines
1.6 KiB
Java
39 lines
1.6 KiB
Java
package com.hlab.yanalyst.service;
|
|
|
|
import com.hlab.yanalyst.domain.category.CategoryService;
|
|
import com.hlab.yanalyst.domain.channel.ChannelVideo;
|
|
import com.hlab.yanalyst.domain.channel.ChannelVideoCurationService;
|
|
import com.hlab.yanalyst.domain.publish.PublishService;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* 홈 대시보드용 단일 집계 — 파이프라인(수집→큐레이션→발행) 현황을 한 번에 묶어 반환한다.
|
|
* 각 도메인 서비스의 집계를 조합만 한다(리포지토리 직접 접근 없음).
|
|
*/
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
@Transactional(readOnly = true)
|
|
public class DashboardService {
|
|
|
|
private final ChannelVideoCurationService curationService;
|
|
private final CategoryService categoryService;
|
|
private final PublishService publishService;
|
|
|
|
public Map<String, Object> summary() {
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
result.put("pipeline", curationService.pipelineStats()); // total, byStatus, bySource, shorts, longForm
|
|
result.put("categories", categoryService.distribution()); // categories[], uncategorized
|
|
result.put("publish", publishService.dashboardSummary()); // byStatus, total, recent
|
|
List<ChannelVideo> outperformers = curationService.findOutperformers(5, BigDecimal.ONE);
|
|
result.put("outperformers", outperformers);
|
|
return result;
|
|
}
|
|
}
|