package kr.itn.itnhub.dashboard; import kr.itn.itnhub.code.CodeService; import org.springframework.stereotype.Service; import java.util.List; /** * 대시보드 화면 하나가 쓰는 서비스. 집계 쿼리는 {@link DashboardMapper#findOrgRows()} 한 번이고, * 상단 KPI는 그 결과를 Java에서 접어 만든다 - 같은 원본에서 계산해야 타일 숫자와 아래 표/칸반 * 숫자가 어긋나지 않는다(쿼리를 따로 두면 두 결과 사이에 시차가 생긴다). * *

어느 단계부터 각 KPI에 세는지는 코드표(STAGE의 kpiFrom 속성)에서 온다. 예전에는 여기 * 상수 세 개로 박혀 있어서, 기준이 바뀔 때마다 배포가 필요했다.

*/ @Service public class DashboardService { /** KPI 이름. 코드표의 kpiFrom 값과 맞물린다. */ static final String KPI_DOC_SUBMITTED = "docSubmitted"; static final String KPI_COMPLETED = "completed"; static final String KPI_REPORT = "reportWriting"; private final DashboardMapper mapper; private final CodeService codeService; public DashboardService(DashboardMapper mapper, CodeService codeService) { this.mapper = mapper; this.codeService = codeService; } /** * 이 KPI에 집계하기 시작하는 단계 번호. 코드표에 지정이 없으면 아무 기관도 세지 않도록 * {@link Integer#MAX_VALUE}를 준다 - 0을 주면 단계가 있는 모든 기관이 잘못 집계된다. */ private int thresholdOf(String kpi) { return codeService.group(kr.itn.itnhub.code.Codes.STAGE).stream() .filter(code -> kpi.equals(code.attr(kr.itn.itnhub.code.Codes.ATTR_KPI_FROM))) .mapToInt(code -> Integer.parseInt(code.code())) .min() .orElse(Integer.MAX_VALUE); } public DashboardResponse dashboard() { List rows = mapper.findOrgRows(); return new DashboardResponse(summarize(rows), rows); } DashboardSummary summarize(List rows) { int docSubmittedFrom = thresholdOf(KPI_DOC_SUBMITTED); int completedFrom = thresholdOf(KPI_COMPLETED); int reportFrom = thresholdOf(KPI_REPORT); int docSubmitted = 0; int reviewTotal = 0; int reviewDone = 0; int processTotal = 0; int processDone = 0; int unresolvedRe = 0; int reportWriting = 0; int completed = 0; for (DashboardOrgRow row : rows) { reviewTotal += row.reviewTotal(); reviewDone += row.reviewDone(); processTotal += row.processTotal(); processDone += row.processDone(); unresolvedRe += row.reCount(); Integer stage = row.stage(); if (stage == null) { continue; } if (stage >= docSubmittedFrom) { docSubmitted++; } if (stage >= completedFrom) { completed++; } if (stage >= reportFrom) { reportWriting++; } } // 신청 기관 수는 명부에 등록된 기관 전체다 - 명부에 올라온 것 자체가 신청이므로 // 단계값 유무와 무관하다(채널 생성 전 기관도 신청은 한 상태다). return new DashboardSummary(rows.size(), docSubmitted, reviewTotal, reviewDone, processTotal, processDone, unresolvedRe, reportWriting, completed); } }