diff --git a/src/main/java/com/goi/erp/repository/CustomerRepository.java b/src/main/java/com/goi/erp/repository/CustomerRepository.java index b01a853..4ab3a3a 100644 --- a/src/main/java/com/goi/erp/repository/CustomerRepository.java +++ b/src/main/java/com/goi/erp/repository/CustomerRepository.java @@ -30,29 +30,36 @@ public interface CustomerRepository extends JpaRepository { // 오늘 추가된 고객 수: cus_contract_date = 오늘 long countByCusContractDate(LocalDate cusContractDate); - // 날짜별 활성 고객 수 + 신규/해지 (선그래프·막대그래프용). - // - active_count = cus_status='A' 이고 그 날짜에 계약기간 안에 있던 고객 수 (날짜로 과거 재구성) - // - added_count = 그 날짜에 계약 시작 (cus_contract_date = D) - // - removed_count = 그 날짜에 해지 (cus_terminated_date = D) - // - generate_series 로 [startDate, endDate] 각 날짜 생성 → 빈 날도 0 + // 날짜별 활성 곳 수 + 일별 신규/해지 — "현재 기준 역산(reverse-calc)" 모델. + // - 기준점: 오늘 활성 = cus_status='A' 수 (신뢰 기준, 카드와 동일) + // - 과거 D의 활성 = 오늘 활성 − (D 이후 신규 계약) + (D 이후 해지) + // · D 이후 신규는 D 시점엔 아직 없었으니 빼고 + // · D 이후 해지는 D 시점엔 아직 있었으니 더함 + // - added/removed 는 effective date(cus_contract_date / cus_terminated_date) 기준 일별 이벤트 + // - active(D) = active(D-1) + added(D) − removed(D) 가 정확히 성립. + // 소급(백데이트)된 건도 매 조회마다 현재 날짜값으로 재계산되어 effective date 위치에 반영됨. // - return: [0]=날짜(date), [1]=active(bigint), [2]=added(bigint), [3]=removed(bigint) @Query(value = """ - SELECT CAST(series.day AS date) AS day, - (SELECT COUNT(*) FROM crm.customer c - WHERE c.cus_status = 'A' - AND c.cus_contract_date <= CAST(series.day AS date) - AND (c.cus_terminated_date IS NULL OR c.cus_terminated_date > CAST(series.day AS date)) + SELECT days.day AS day, + ( (SELECT COUNT(*) FROM crm.customer WHERE cus_status = 'A') + - (SELECT COUNT(*) FROM crm.customer + WHERE cus_contract_date > days.day + AND cus_contract_date <= CAST(:endDate AS date)) + + (SELECT COUNT(*) FROM crm.customer + WHERE cus_terminated_date > days.day + AND cus_terminated_date <= CAST(:endDate AS date)) ) AS active_count, - (SELECT COUNT(*) FROM crm.customer a - WHERE a.cus_contract_date = CAST(series.day AS date) - ) AS added_count, - (SELECT COUNT(*) FROM crm.customer r - WHERE r.cus_terminated_date = CAST(series.day AS date) - ) AS removed_count - FROM generate_series(CAST(:startDate AS timestamp), - CAST(:endDate AS timestamp), - INTERVAL '1 day') AS series(day) - ORDER BY series.day + (SELECT COUNT(*) FROM crm.customer + WHERE cus_contract_date = days.day) AS added_count, + (SELECT COUNT(*) FROM crm.customer + WHERE cus_terminated_date = days.day) AS removed_count + FROM ( + SELECT CAST(series.day AS date) AS day + FROM generate_series(CAST(:startDate AS timestamp), + CAST(:endDate AS timestamp), + INTERVAL '1 day') AS series(day) + ) days + ORDER BY days.day """, nativeQuery = true) List findActiveCustomerTrend(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);