diff --git a/.gradle/8.10/executionHistory/executionHistory.bin b/.gradle/8.10/executionHistory/executionHistory.bin index 2615054..d0bce7a 100644 Binary files a/.gradle/8.10/executionHistory/executionHistory.bin and b/.gradle/8.10/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.10/executionHistory/executionHistory.lock b/.gradle/8.10/executionHistory/executionHistory.lock index 6eb6217..8cd6816 100644 Binary files a/.gradle/8.10/executionHistory/executionHistory.lock and b/.gradle/8.10/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.10/fileHashes/fileHashes.bin b/.gradle/8.10/fileHashes/fileHashes.bin index d1fba3b..31237b7 100644 Binary files a/.gradle/8.10/fileHashes/fileHashes.bin and b/.gradle/8.10/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.10/fileHashes/fileHashes.lock b/.gradle/8.10/fileHashes/fileHashes.lock index 15d9991..70ec6d1 100644 Binary files a/.gradle/8.10/fileHashes/fileHashes.lock and b/.gradle/8.10/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.10/fileHashes/resourceHashesCache.bin b/.gradle/8.10/fileHashes/resourceHashesCache.bin index 9b44346..13ffb51 100644 Binary files a/.gradle/8.10/fileHashes/resourceHashesCache.bin and b/.gradle/8.10/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 7594446..83d75df 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 6b17014..1a773a2 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/analytics-service/src/main/java/com/kt/event/analytics/config/SampleDataLoader.java b/analytics-service/src/main/java/com/kt/event/analytics/config/SampleDataLoader.java index a9ce7b5..fd16ea7 100644 --- a/analytics-service/src/main/java/com/kt/event/analytics/config/SampleDataLoader.java +++ b/analytics-service/src/main/java/com/kt/event/analytics/config/SampleDataLoader.java @@ -188,6 +188,11 @@ public class SampleDataLoader implements ApplicationRunner { new BigDecimal("3500000"), new BigDecimal("2000000") }; + int[][] expectedViews = { + {5000, 10000, 3000, 2000}, // 이벤트1: 우리동네TV, 지니TV, 링고비즈, SNS + {3500, 7000, 2000, 1500}, // 이벤트2 + {1500, 3000, 1000, 500} // 이벤트3 + }; for (int i = 0; i < eventIds.length; i++) { String eventId = eventIds[i]; @@ -195,19 +200,19 @@ public class SampleDataLoader implements ApplicationRunner { // 1. 우리동네TV (TV) publishDistributionEvent(eventId, "우리동네TV", "TV", - distributionBudget.multiply(new BigDecimal("0.3"))); + distributionBudget.multiply(new BigDecimal("0.3")), expectedViews[i][0]); // 2. 지니TV (TV) publishDistributionEvent(eventId, "지니TV", "TV", - distributionBudget.multiply(new BigDecimal("0.3"))); + distributionBudget.multiply(new BigDecimal("0.3")), expectedViews[i][1]); // 3. 링고비즈 (CALL) publishDistributionEvent(eventId, "링고비즈", "CALL", - distributionBudget.multiply(new BigDecimal("0.2"))); + distributionBudget.multiply(new BigDecimal("0.2")), expectedViews[i][2]); // 4. SNS (SNS) publishDistributionEvent(eventId, "SNS", "SNS", - distributionBudget.multiply(new BigDecimal("0.2"))); + distributionBudget.multiply(new BigDecimal("0.2")), expectedViews[i][3]); } log.info("✅ DistributionCompleted 이벤트 12건 발행 완료 (3 이벤트 × 4 채널)"); @@ -217,12 +222,13 @@ public class SampleDataLoader implements ApplicationRunner { * 개별 DistributionCompleted 이벤트 발행 */ private void publishDistributionEvent(String eventId, String channelName, String channelType, - BigDecimal distributionCost) throws Exception { + BigDecimal distributionCost, Integer expectedViews) throws Exception { DistributionCompletedEvent event = DistributionCompletedEvent.builder() .eventId(eventId) .channelName(channelName) .channelType(channelType) .distributionCost(distributionCost) + .expectedViews(expectedViews) .build(); publishEvent(DISTRIBUTION_COMPLETED_TOPIC, event); } diff --git a/analytics-service/src/main/java/com/kt/event/analytics/entity/EventStats.java b/analytics-service/src/main/java/com/kt/event/analytics/entity/EventStats.java index 5d24094..4c48a67 100644 --- a/analytics-service/src/main/java/com/kt/event/analytics/entity/EventStats.java +++ b/analytics-service/src/main/java/com/kt/event/analytics/entity/EventStats.java @@ -49,6 +49,13 @@ public class EventStats extends BaseTimeEntity { @Builder.Default private Integer totalParticipants = 0; + /** + * 총 노출 수 (모든 채널의 노출 수 합계) + */ + @Column(nullable = false) + @Builder.Default + private Integer totalViews = 0; + /** * 예상 ROI (%) */ diff --git a/analytics-service/src/main/java/com/kt/event/analytics/messaging/consumer/DistributionCompletedConsumer.java b/analytics-service/src/main/java/com/kt/event/analytics/messaging/consumer/DistributionCompletedConsumer.java index 47770e8..894a584 100644 --- a/analytics-service/src/main/java/com/kt/event/analytics/messaging/consumer/DistributionCompletedConsumer.java +++ b/analytics-service/src/main/java/com/kt/event/analytics/messaging/consumer/DistributionCompletedConsumer.java @@ -3,6 +3,7 @@ package com.kt.event.analytics.messaging.consumer; import com.kt.event.analytics.entity.ChannelStats; import com.kt.event.analytics.messaging.event.DistributionCompletedEvent; import com.kt.event.analytics.repository.ChannelStatsRepository; +import com.kt.event.analytics.repository.EventStatsRepository; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -11,6 +12,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; +import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -25,6 +27,7 @@ import java.util.concurrent.TimeUnit; public class DistributionCompletedConsumer { private final ChannelStatsRepository channelStatsRepository; + private final EventStatsRepository eventStatsRepository; private final ObjectMapper objectMapper; private final RedisTemplate redisTemplate; @@ -64,15 +67,25 @@ public class DistributionCompletedConsumer { .build()); channelStats.setDistributionCost(event.getDistributionCost()); - channelStatsRepository.save(channelStats); - log.info("✅ 채널 통계 업데이트: eventId={}, channel={}", eventId, channelName); - // 3. 캐시 무효화 (다음 조회 시 최신 배포 통계 반영) + // 예상 노출 수 저장 + if (event.getExpectedViews() != null) { + channelStats.setImpressions(event.getExpectedViews()); + } + + channelStatsRepository.save(channelStats); + log.info("✅ 채널 통계 업데이트: eventId={}, channel={}, expectedViews={}", + eventId, channelName, event.getExpectedViews()); + + // 3. EventStats의 totalViews 업데이트 (모든 채널 노출 수 합계) + updateTotalViews(eventId); + + // 4. 캐시 무효화 (다음 조회 시 최신 배포 통계 반영) String cacheKey = CACHE_KEY_PREFIX + eventId; redisTemplate.delete(cacheKey); log.debug("🗑️ 캐시 무효화: {}", cacheKey); - // 4. 멱등성 처리 완료 기록 (7일 TTL) + // 5. 멱등성 처리 완료 기록 (7일 TTL) redisTemplate.opsForSet().add(PROCESSED_DISTRIBUTIONS_KEY, distributionKey); redisTemplate.expire(PROCESSED_DISTRIBUTIONS_KEY, IDEMPOTENCY_TTL_DAYS, TimeUnit.DAYS); log.debug("✅ 멱등성 기록: distributionKey={}", distributionKey); @@ -82,4 +95,32 @@ public class DistributionCompletedConsumer { throw new RuntimeException("DistributionCompleted 처리 실패", e); } } + + /** + * 모든 채널의 예상 노출 수를 합산하여 EventStats.totalViews 업데이트 + */ + private void updateTotalViews(String eventId) { + try { + // 모든 채널 통계 조회 + List channelStatsList = channelStatsRepository.findByEventId(eventId); + + // 총 노출 수 계산 + int totalViews = channelStatsList.stream() + .mapToInt(ChannelStats::getImpressions) + .sum(); + + // EventStats 업데이트 + eventStatsRepository.findByEventId(eventId) + .ifPresentOrElse( + eventStats -> { + eventStats.setTotalViews(totalViews); + eventStatsRepository.save(eventStats); + log.info("✅ 총 노출 수 업데이트: eventId={}, totalViews={}", eventId, totalViews); + }, + () -> log.warn("⚠️ 이벤트 통계 없음: eventId={}", eventId) + ); + } catch (Exception e) { + log.error("❌ totalViews 업데이트 실패: eventId={}", eventId, e); + } + } } diff --git a/analytics-service/src/main/java/com/kt/event/analytics/messaging/event/DistributionCompletedEvent.java b/analytics-service/src/main/java/com/kt/event/analytics/messaging/event/DistributionCompletedEvent.java index c3a6e6f..e890918 100644 --- a/analytics-service/src/main/java/com/kt/event/analytics/messaging/event/DistributionCompletedEvent.java +++ b/analytics-service/src/main/java/com/kt/event/analytics/messaging/event/DistributionCompletedEvent.java @@ -35,4 +35,9 @@ public class DistributionCompletedEvent { * 배포 비용 */ private BigDecimal distributionCost; + + /** + * 예상 노출 수 + */ + private Integer expectedViews; }