Merge pull request #30 from ktds-dg0501/feature/analytics

이벤트별 성과분석 날짜 로직 수정 및 설정 개선
This commit is contained in:
Hyowon Yang 2025-10-30 12:54:56 +09:00 committed by GitHub
commit a3781a279a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 49 additions and 19 deletions

View File

@ -24,7 +24,7 @@
<!-- Kafka Configuration (원격 서버) -->
<entry key="KAFKA_ENABLED" value="true" />
<entry key="KAFKA_BOOTSTRAP_SERVERS" value="20.249.182.13:9095,4.217.131.59:9095" />
<entry key="KAFKA_CONSUMER_GROUP_ID" value="analytics-service-consumers" />
<entry key="KAFKA_CONSUMER_GROUP_ID" value="analytics-service-consumers-v3" />
<!-- Sample Data Configuration (MVP Only) -->
<!-- ⚠️ Kafka Producer로 이벤트 발행 (Consumer가 처리) -->

View File

@ -23,7 +23,7 @@ public class KafkaConsumerConfig {
@Value("${spring.kafka.bootstrap-servers}")
private String bootstrapServers;
@Value("${spring.kafka.consumer.group-id:analytics-service}")
@Value("${spring.kafka.consumer.group-id:analytics-service-consumers-v3}")
private String groupId;
@Bean

View File

@ -19,6 +19,7 @@ import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaAdmin;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
@ -60,7 +61,8 @@ public class SampleDataLoader implements ApplicationRunner {
private final Random random = new Random();
private static final String CONSUMER_GROUP_ID = "analytics-service-consumers-v3";
@Value("${spring.kafka.consumer.group-id}")
private String consumerGroupId;
// Kafka Topic Names (MVP용 샘플 토픽)
private static final String EVENT_CREATED_TOPIC = "sample.event.created";
@ -181,7 +183,7 @@ public class SampleDataLoader implements ApplicationRunner {
*/
private void resetConsumerOffsets() {
try (AdminClient adminClient = AdminClient.create(kafkaAdmin.getConfigurationProperties())) {
log.info("🔄 Kafka Consumer Offset 리셋 시작: group={}", CONSUMER_GROUP_ID);
log.info("🔄 Kafka Consumer Offset 리셋 시작: group={}", consumerGroupId);
// 모든 토픽의 offset 삭제
Set<TopicPartition> partitions = new HashSet<>();
@ -195,7 +197,7 @@ public class SampleDataLoader implements ApplicationRunner {
// Consumer Group Offset 삭제
DeleteConsumerGroupOffsetsResult result = adminClient.deleteConsumerGroupOffsets(
CONSUMER_GROUP_ID,
consumerGroupId,
partitions
);
@ -224,6 +226,8 @@ public class SampleDataLoader implements ApplicationRunner {
.totalInvestment(new BigDecimal("5000000"))
.expectedRevenue(new BigDecimal("15000000")) // 투자 대비 3배 수익
.status("ACTIVE")
.startDate(java.time.LocalDateTime.of(2025, 1, 23, 0, 0)) // 2025-01-23 시작
.endDate(null) // 진행중
.build();
publishEvent(EVENT_CREATED_TOPIC, event1);
@ -235,6 +239,8 @@ public class SampleDataLoader implements ApplicationRunner {
.totalInvestment(new BigDecimal("3500000"))
.expectedRevenue(new BigDecimal("7000000")) // 투자 대비 2배 수익
.status("ACTIVE")
.startDate(java.time.LocalDateTime.of(2025, 2, 1, 0, 0)) // 2025-02-01 시작
.endDate(null) // 진행중
.build();
publishEvent(EVENT_CREATED_TOPIC, event2);
@ -246,6 +252,8 @@ public class SampleDataLoader implements ApplicationRunner {
.totalInvestment(new BigDecimal("2000000"))
.expectedRevenue(new BigDecimal("3000000")) // 투자 대비 1.5배 수익
.status("COMPLETED")
.startDate(java.time.LocalDateTime.of(2025, 1, 15, 0, 0)) // 2025-01-15 시작
.endDate(java.time.LocalDateTime.of(2025, 1, 31, 23, 59)) // 2025-01-31 종료
.build();
publishEvent(EVENT_CREATED_TOPIC, event3);

View File

@ -97,6 +97,18 @@ public class EventStats extends BaseTimeEntity {
@Column(length = 20)
private String status;
/**
* 이벤트 시작일
*/
@Column(name = "start_date")
private java.time.LocalDateTime startDate;
/**
* 이벤트 종료일 (null이면 진행중)
*/
@Column(name = "end_date")
private java.time.LocalDateTime endDate;
/**
* 참여자 증가
*/

View File

@ -64,11 +64,13 @@ public class EventCreatedConsumer {
.totalInvestment(event.getTotalInvestment())
.expectedRevenue(event.getExpectedRevenue() != null ? event.getExpectedRevenue() : BigDecimal.ZERO)
.status(event.getStatus())
.startDate(event.getStartDate())
.endDate(event.getEndDate())
.build();
eventStatsRepository.save(eventStats);
log.info("✅ 이벤트 통계 초기화 완료: eventId={}, userId={}, expectedRevenue={}",
eventId, eventStats.getUserId(), event.getExpectedRevenue());
log.info("✅ 이벤트 통계 초기화 완료: eventId={}, userId={}, startDate={}, endDate={}",
eventId, eventStats.getUserId(), event.getStartDate(), event.getEndDate());
// 3. 캐시 무효화 (다음 조회 최신 데이터 반영)
String cacheKey = CACHE_KEY_PREFIX + eventId;

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 이벤트 생성 이벤트
@ -45,4 +46,14 @@ public class EventCreatedEvent {
* 이벤트 상태
*/
private String status;
/**
* 이벤트 시작일
*/
private LocalDateTime startDate;
/**
* 이벤트 종료일 (null이면 진행중)
*/
private LocalDateTime endDate;
}

View File

@ -146,11 +146,12 @@ public class AnalyticsService {
}
/**
* 기간 정보 구성 (이벤트 생성일 ~ 현재)
* 기간 정보 구성 (이벤트 시작일 ~ 종료일 또는 현재)
*/
private PeriodInfo buildPeriodInfo(EventStats eventStats) {
LocalDateTime start = eventStats.getCreatedAt();
LocalDateTime end = LocalDateTime.now();
LocalDateTime start = eventStats.getStartDate();
LocalDateTime end = eventStats.getEndDate() != null ?
eventStats.getEndDate() : LocalDateTime.now();
long durationDays = ChronoUnit.DAYS.between(start, end);

View File

@ -301,20 +301,17 @@ public class UserAnalyticsService {
/**
* 기간 정보 구성
*/
/**
* 전체 이벤트의 생성/수정 시간 기반으로 period 계산
*
* 전체 이벤트 가장 빠른 시작일 ~ 현재까지의 기간 계산
*/
private PeriodInfo buildPeriodFromEvents(List<EventStats> events) {
LocalDateTime start = events.stream()
.map(EventStats::getCreatedAt)
.map(EventStats::getStartDate)
.filter(Objects::nonNull)
.min(LocalDateTime::compareTo)
.orElse(LocalDateTime.now());
LocalDateTime end = events.stream()
.map(EventStats::getUpdatedAt)
.max(LocalDateTime::compareTo)
.orElse(LocalDateTime.now());
LocalDateTime end = LocalDateTime.now();
return PeriodInfo.builder()
.startDate(start)

View File

@ -80,7 +80,6 @@ server:
charset: UTF-8
enabled: true
force: true
context-path: /api/v1/analytics
# JWT
jwt: