고객 추이 정합성 역산

This commit is contained in:
Hyojin Ahn 2026-06-24 10:29:18 -04:00
parent e593be0a2d
commit fcbab196cb
1 changed files with 27 additions and 20 deletions

View File

@ -30,29 +30,36 @@ public interface CustomerRepository extends JpaRepository<Customer, Long> {
// 오늘 추가된 고객 : 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<Object[]> findActiveCustomerTrend(@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);