Skip to content

Commit e6a60b7

Browse files
feat: 최근 28일 내 동정 요청 해결 시간 통계 추가 (#231)
1 parent 4e9d0c5 commit e6a60b7

5 files changed

Lines changed: 49 additions & 4 deletions

File tree

src/main/java/org/devkor/apu/saerok_server/domain/stat/application/StatAggregationService.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public void aggregateFor(LocalDate date, Set<StatMetric> metrics) {
3838
case BIRD_ID_PENDING_COUNT -> aggregatePendingCount(date);
3939
case BIRD_ID_RESOLVED_COUNT -> aggregateResolvedDaily(date);
4040
case BIRD_ID_RESOLUTION_STATS -> aggregateResolutionStatsCumulative(date);
41+
case BIRD_ID_RESOLUTION_STATS_28D -> aggregateResolutionStatsRecent28d(date);
4142

4243
case USER_COMPLETED_TOTAL -> aggregateUserCompletedTotal(date);
4344
case USER_SIGNUP_DAILY -> aggregateUserSignupDaily(date);
@@ -109,6 +110,7 @@ private void aggregateResolvedDaily(LocalDate date) {
109110

110111
/* Metric calculators (멀티값 → payload.{min/max/avg/stddev}_hours) */
111112

113+
/** 누적: 해당 날짜까지 전체 ADOPT 해결 시간 통계 */
112114
private void aggregateResolutionStatsCumulative(LocalDate date) {
113115
var end = endExclusive(date);
114116
Object[] row = histRepo.resolutionStatsCumulativeSecondsAsOf(end); // [min,max,avg,stddev] in seconds
@@ -130,6 +132,29 @@ private void aggregateResolutionStatsCumulative(LocalDate date) {
130132
dailyRepo.upsertPayload(StatMetric.BIRD_ID_RESOLUTION_STATS, date, p);
131133
}
132134

135+
/** 최근 28일: [end-28d, end) ADOPT 해결 시간 통계 */
136+
private void aggregateResolutionStatsRecent28d(LocalDate date) {
137+
var end = endExclusive(date);
138+
var start = end.minusDays(28);
139+
Object[] row = histRepo.resolutionStatsWindowSeconds(start, end); // [min,max,avg,stddev] in seconds
140+
141+
// 최근 28일 내 ADOPT 해결이 하나도 없으면 저장 생략
142+
if (row == null || row[0] == null || row[1] == null || row[2] == null || row[3] == null) return;
143+
144+
double minH = ((Number) row[0]).doubleValue() / 3600.0;
145+
double maxH = ((Number) row[1]).doubleValue() / 3600.0;
146+
double avgH = ((Number) row[2]).doubleValue() / 3600.0;
147+
double stdH = ((Number) row[3]).doubleValue() / 3600.0;
148+
149+
Map<String, Object> p = new HashMap<>();
150+
p.put("min_hours", minH);
151+
p.put("max_hours", maxH);
152+
p.put("avg_hours", avgH);
153+
p.put("stddev_hours", stdH);
154+
155+
dailyRepo.upsertPayload(StatMetric.BIRD_ID_RESOLUTION_STATS_28D, date, p);
156+
}
157+
133158
/** 누적 가입자 수: COMPLETED 이고, day 끝 시점 기준으로 삭제되지 않은 사용자 */
134159
private void aggregateUserCompletedTotal(LocalDate date) {
135160
var end = endExclusive(date);

src/main/java/org/devkor/apu/saerok_server/domain/stat/application/StatBatchScheduler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void runDailyAggregation() {
3434
StatMetric.COLLECTION_PRIVATE_RATIO,
3535
StatMetric.BIRD_ID_PENDING_COUNT,
3636
StatMetric.BIRD_ID_RESOLVED_COUNT,
37-
StatMetric.BIRD_ID_RESOLUTION_STATS,
37+
StatMetric.BIRD_ID_RESOLUTION_STATS_28D,
3838

3939
StatMetric.USER_COMPLETED_TOTAL,
4040
StatMetric.USER_SIGNUP_DAILY,

src/main/java/org/devkor/apu/saerok_server/domain/stat/application/StatQueryService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public StatSeriesResponse getSeries(List<StatMetric> metrics) {
2424
for (StatMetric m : metrics) {
2525
List<DailyStat> rows = dailyRepo.findSeriesByMetric(m);
2626

27-
if (m == StatMetric.BIRD_ID_RESOLUTION_STATS) {
27+
if (m == StatMetric.BIRD_ID_RESOLUTION_STATS || m == StatMetric.BIRD_ID_RESOLUTION_STATS_28D) {
2828
var minSeries = new StatSeriesResponse.ComponentSeries(
2929
"min_hours",
3030
rows.stream().map(s ->

src/main/java/org/devkor/apu/saerok_server/domain/stat/core/entity/StatMetric.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ public enum StatMetric {
55
COLLECTION_PRIVATE_RATIO, // 비공개 새록 비율 0..1 (스냅샷, 단일값)
66
BIRD_ID_PENDING_COUNT, // 진행 중 동정 요청 수 (스냅샷, 단일값)
77
BIRD_ID_RESOLVED_COUNT, // 누적 동정 요청 해결 수 (스냅샷, 단일값) — 동정 제안 채택(ADOPT)으로만 증가
8-
BIRD_ID_RESOLUTION_STATS, // 동정 요청 해결 시간 통계(누적): min/max/avg/stddev (멀티값) — 채택(ADOPT)으로만 집계
8+
BIRD_ID_RESOLUTION_STATS, // 동정 요청 해결 시간 통계(누적): min/max/avg/stddev (멀티값) — 채택(ADOPT)만 집계
9+
BIRD_ID_RESOLUTION_STATS_28D, // 동정 요청 해결 시간 통계(최근 28일): min/max/avg/stddev (멀티값) — 채택(ADOPT)만 집계
910

1011
USER_COMPLETED_TOTAL, // 누적 가입자 수(스냅샷) — COMPLETED && 삭제되지 않음
1112
USER_SIGNUP_DAILY, // 일일 가입자 수 — signup_completed_at 기준

src/main/java/org/devkor/apu/saerok_server/domain/stat/core/repository/BirdIdRequestHistoryRepository.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,26 @@ public Object[] resolutionStatsCumulativeSecondsAsOf(OffsetDateTime endExclusive
8585
return row;
8686
}
8787

88+
/** 윈도우(구간) 해결 시간 통계 — 단위: "초" (ADOPT만) */
89+
public Object[] resolutionStatsWindowSeconds(OffsetDateTime startInclusive, OffsetDateTime endExclusive) {
90+
Object[] row = (Object[]) em.createNativeQuery("""
91+
SELECT
92+
MIN(resolution_seconds) AS min_s,
93+
MAX(resolution_seconds) AS max_s,
94+
AVG(resolution_seconds) AS avg_s,
95+
STDDEV_POP(resolution_seconds) AS stddev_s
96+
FROM bird_id_request_history
97+
WHERE resolved_at IS NOT NULL
98+
AND resolution_kind = 'ADOPT'
99+
AND resolved_at >= ?1
100+
AND resolved_at < ?2
101+
""")
102+
.setParameter(1, startInclusive)
103+
.setParameter(2, endExclusive)
104+
.getSingleResult();
105+
return row;
106+
}
107+
88108
public long countResolvedOnDate(OffsetDateTime startInclusive, OffsetDateTime endExclusive) {
89109
return em.createQuery("""
90110
SELECT COUNT(h) FROM BirdIdRequestHistory h
@@ -98,5 +118,4 @@ SELECT COUNT(h) FROM BirdIdRequestHistory h
98118
.setParameter("end", endExclusive)
99119
.getSingleResult();
100120
}
101-
102121
}

0 commit comments

Comments
 (0)