Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] 선착순 이벤트 당첨자수 스케줄링 #17

Merged
merged 5 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ out/

### VS Code ###
.vscode/

*.groovy
27 changes: 27 additions & 0 deletions src/main/java/com/softeer/podoarrival/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.softeer.podoarrival.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//http://localhost:8081/swagger-ui/index.html
@Configuration
public class SwaggerConfig {

private final String TITLE = "Softeer-arrival";
private final String DESCRIPTION = "못말리는 현기차 arrival-server swagger";
private final String VERSION = "V1.0.0";

@Bean
public OpenAPI api() {
return new OpenAPI()
.info(new Info()
.title(TITLE)
.description(DESCRIPTION)
.version(VERSION)
);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.softeer.podoarrival.event.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class EventTypeNotExistsException extends RuntimeException {
private String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.softeer.podoarrival.event.model.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.softeer.podoarrival.common.entity.DateEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;

@Entity
@Table(name = "events")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Event extends DateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "event_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinColumn(name = "type_id")
private EventType eventType;

private String title;
private String description;

/**
* 형식은 7자리 0과 1로 이루어진 문자열. 월화수목금토일 의미
*/
@Column(name = "repeat_day")
private String repeatDay;
@JsonFormat(pattern = "HH:mm:ss")
@Column(name = "repeat_time")
private LocalTime repeatTime;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "start_time")
private LocalDateTime startAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "end_time")
private LocalDateTime endAt;

@Column(name = "tag_image")
private String tagImage;

@OneToMany(mappedBy = "event" , orphanRemoval = true, cascade = CascadeType.ALL)
private List<EventReward> eventRewardList;

@OneToOne(mappedBy = "event" , orphanRemoval = true, cascade = CascadeType.ALL)
private EventWeight eventWeight;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.softeer.podoarrival.event.model.entity;

import com.softeer.podoarrival.common.entity.DateEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;


@Entity
@Table(name = "event_rewards")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EventReward extends DateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "reward_id")
private Long id;
@Column(name = "reward_rank")
private int rewardRank;
/**
* 당첨자수
*/
@Column(name = "winner_number")
private int numWinners;
private String reward;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "event_id")
private Event event;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.softeer.podoarrival.event.model.entity;

import com.softeer.podoarrival.common.entity.DateEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "event_types")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EventType extends DateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "type_id")
private Long id;
@Column(name = "type")
private String type;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.softeer.podoarrival.event.model.entity;

import com.softeer.podoarrival.common.entity.DateEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "event_weights")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EventWeight extends DateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "weight_id")
private Long id;
/**
* 가중치 배수
*/
private int times;
@Column(name = "weight_condition")
private String weightCondition;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "event_id")
private Event event;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.softeer.podoarrival.event.repository;


import com.softeer.podoarrival.event.model.entity.Event;
import com.softeer.podoarrival.event.model.entity.EventType;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDateTime;
import java.util.Optional;

public interface EventRepository extends JpaRepository<Event, Long> {
/**
* 이벤트 타입이 일치하고, startAt이 오늘인 이벤트 1개를 반환하는 메서드
* @param eventType 이벤트 타입
* @return 조건에 맞는 이벤트 (없으면 null)
*/
Event findFirstByEventTypeAndStartAtBetween(EventType eventType, LocalDateTime startAtStart, LocalDateTime startAtEnd);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.softeer.podoarrival.event.repository;

import com.softeer.podoarrival.event.model.entity.Event;
import com.softeer.podoarrival.event.model.entity.EventReward;
import org.springframework.data.jpa.repository.JpaRepository;

public interface EventRewardRepository extends JpaRepository<EventReward, Long> {
int countByEvent(Event event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.softeer.podoarrival.event.repository;


import com.softeer.podoarrival.event.model.entity.EventType;
import org.springframework.data.jpa.repository.JpaRepository;

public interface EventTypeRepository extends JpaRepository<EventType, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.softeer.podoarrival.event.scheduler.scheduler;

import com.softeer.podoarrival.event.exception.EventTypeNotExistsException;
import com.softeer.podoarrival.event.model.entity.Event;
import com.softeer.podoarrival.event.model.entity.EventType;
import com.softeer.podoarrival.event.repository.EventRepository;
import com.softeer.podoarrival.event.repository.EventRewardRepository;
import com.softeer.podoarrival.event.repository.EventTypeRepository;
import com.softeer.podoarrival.event.service.ArrivalEventReleaseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;

/**
* 선착순 이벤트의 당첨자 수를 세팅하는 스케줄러
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ArrivalEventMaxArrivalScheduler {

private final EventRepository eventRepository;
private final EventTypeRepository eventTypeRepository;
private final EventRewardRepository eventRewardRepository;

/**
* 특정 시간에 Mysql에서 금일 진행될 선착순 이벤트의 당첨자 수를 읽어옴
*
*/
@Scheduled(cron = "0 25 03 * * *")
public void setEventArrivalCount() {
// 시작일자, 이벤트 종류만 고려하여 이벤트 추출
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
LocalDateTime endOfDay = startOfDay.plusDays(1).minusNanos(1);

// 선착순 이벤트
EventType eventType = eventTypeRepository.findById(1L).orElseThrow(() -> new EventTypeNotExistsException("이벤트 타입이 존재하지 않습니다."));
Event findEvent = eventRepository.findFirstByEventTypeAndStartAtBetween(eventType, startOfDay, endOfDay);

// 찾은 이벤트에 해당하는 reword개수 조회
int rewordCount = eventRewardRepository.countByEvent(findEvent);

ArrivalEventReleaseService.setMaxArrival(rewordCount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

import com.softeer.podoarrival.event.exception.ExistingUserException;
import com.softeer.podoarrival.event.model.dto.ArrivalApplicationResponseDto;
import com.softeer.podoarrival.security.AuthInfo;
import com.softeer.podoarrival.event.model.entity.ArrivalUser;
import com.softeer.podoarrival.event.model.entity.Role;
import com.softeer.podoarrival.event.repository.ArrivalUserRepository;
import com.softeer.podoarrival.security.AuthInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.BatchResult;
import org.redisson.api.RBatch;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
Expand All @@ -28,14 +29,14 @@ public class ArrivalEventReleaseService {
private final String ARRIVAL_SET = "arrivalset";
private boolean CHECK = false;

private final int MAX_ARRIVAL = 100;
private static int MAX_ARRIVAL = 0;

/**
* 비동기로 Redis 호출하는 메서드
* 반환값은 ArrivalEventService에서 받아서 선착순 처리
*/
@Async("arrivalExecutor")
public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo authInfo){
public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo authInfo) {
return CompletableFuture.supplyAsync(() -> {
LocalDate now = LocalDate.now();

Expand All @@ -51,7 +52,6 @@ public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo auth

//첫번째 응답
if(!(boolean) res.getResponses().get(0)){
log.info("전화번호-중복 = {}", authInfo.getPhoneNum());
throw new ExistingUserException("이미 응모한 전화번호입니다.");
}

Expand All @@ -73,4 +73,8 @@ public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo auth
}
});
}

public static void setMaxArrival(int val) {
MAX_ARRIVAL = val;
}
}
Loading