Skip to content

Commit

Permalink
Merge pull request #164 from GSM-GOGO/feature/163-match-before-ten-mi…
Browse files Browse the repository at this point in the history
…nute

경기 10분 전 알림
  • Loading branch information
tlsgmltjd authored Apr 21, 2024
2 parents 94854bb + cdd90dd commit 6c873e5
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 1 deletion.
3 changes: 3 additions & 0 deletions gsmgogo-batch/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ jar { enabled = true }
dependencies {
implementation project(':gsmgogo-entity')

/* cool sms */
implementation 'net.nurigo:sdk:4.3.0'

/* spring data jpa */
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package team.gsmgogo.global.config;

import net.nurigo.sdk.message.service.DefaultMessageService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MessageConfig {
@Value("${message.apiKey}")
private String apiKey;

@Value("${message.apiSecretKey}")
private String apiSecretKey;

@Bean
public DefaultMessageService defaultMessageService() {
return new DefaultMessageService(apiKey, apiSecretKey, "https://api.coolsms.co.kr");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package team.gsmgogo.global.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

@Configuration
public class SchedulerConfig implements SchedulingConfigurer {

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler threadPool = new ThreadPoolTaskScheduler();

int processors = Runtime.getRuntime().availableProcessors();
threadPool.setPoolSize(processors);
threadPool.initialize();

taskRegistrar.setTaskScheduler(threadPool);
}
}
93 changes: 93 additions & 0 deletions gsmgogo-batch/src/main/java/team/gsmgogo/job/AlertJob.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package team.gsmgogo.job;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.nurigo.sdk.message.model.Message;
import net.nurigo.sdk.message.request.SingleMessageSendingRequest;
import net.nurigo.sdk.message.service.DefaultMessageService;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import team.gsmgogo.domain.match.entity.MatchEntity;
import team.gsmgogo.domain.match.enums.MatchLevelType;
import team.gsmgogo.domain.match.repository.MatchJpaRepository;
import team.gsmgogo.domain.team.entity.TeamEntity;
import team.gsmgogo.domain.user.entity.UserEntity;
import team.gsmgogo.domain.user.repository.UserQueryDslRepository;

import java.util.List;

@Slf4j
@Configuration
@RequiredArgsConstructor
public class AlertJob implements Job {
private final MatchJpaRepository matchJpaRepository;
private final UserQueryDslRepository userQueryDslRepository;
private final DefaultMessageService messageService;

@Value("${message.send}")
private String sendNumber;

@Override
public void execute(JobExecutionContext context) {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

Long matchId = jobDataMap.getLong("matchId");
MatchEntity match = matchJpaRepository.findByMatchId(matchId)
.orElseThrow(() -> new RuntimeException("배치에서 해당 매치를 찾을 수 없습니다."));

String matchLevel;
if(match.getMatchLevel() == MatchLevelType.TRYOUT){
matchLevel = "예선";
} else if(match.getMatchLevel() == MatchLevelType.SEMI_FINAL){
matchLevel = "준결승";
} else {
matchLevel = "결승";
}

TeamEntity teamA = match.getTeamA();
TeamEntity teamB = match.getTeamB();

List<UserEntity> followAUsers = userQueryDslRepository.findByFollowsTeam(teamA);
followAUsers.forEach(user -> {
Message message = new Message();
message.setFrom(sendNumber);
message.setTo(user.getPhoneNumber());
message.setText(
"""
GSM GOGO 경기 알림
곧 %s팀 VS %s팀 %s 경기가 시작됩니다!
%s팀의 승리를 위해 힘찬 응원 부탁드립니다!
잠시 후 10분 후 경기가 시작됩니다. 아직 배팅에 참여하지 않았다면 서비스에 접속하여 배팅을 진행해주세요!
https://gsmgogo.kr
""".formatted(teamA.getTeamName(), teamB.getTeamName(), matchLevel, teamA.getTeamName())
);
messageService.sendOne(new SingleMessageSendingRequest(message));
});

List<UserEntity> followBUsers = userQueryDslRepository.findByFollowsTeam(teamB);
followBUsers.forEach(user -> {
Message message = new Message();
message.setFrom(sendNumber);
message.setTo(user.getPhoneNumber());
message.setText(
"""
GSM GOGO 경기 알림
곧 %s팀 VS %s팀 %s 경기가 시작됩니다!
%s팀의 승리를 위해 힘찬 응원 부탁드립니다!
잠시 후 10분 후 경기가 시작됩니다. 아직 배팅에 참여하지 않았다면 서비스에 접속하여 배팅을 진행해주세요!
https://gsmgogo.kr
""".formatted(teamB.getTeamName(), teamA.getTeamName(), matchLevel, teamB.getTeamName())
);
messageService.sendOne(new SingleMessageSendingRequest(message));
});
}
}
40 changes: 40 additions & 0 deletions gsmgogo-batch/src/main/java/team/gsmgogo/job/DailyJob.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package team.gsmgogo.job;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.*;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import team.gsmgogo.domain.user.repository.UserQueryDslRepository;

@Slf4j
@Configuration
@RequiredArgsConstructor
public class DailyJob {
private final JobRepository jobRepository;
private final PlatformTransactionManager platformTransactionManager;
private final UserQueryDslRepository userQueryDslRepository;

@Bean(name = "resetCountJob")
public Job resetCountJob(){
return new JobBuilder("reset-count-Job", jobRepository)
.start(resetCountStep(jobRepository, platformTransactionManager))
.build();
}

@Bean
public Step resetCountStep(JobRepository jobRepository, PlatformTransactionManager platformTransactionManager){
return new StepBuilder("reset-count-step", jobRepository)
.tasklet((contribution, chunkContext) -> {
userQueryDslRepository.bulkResetVerifyCount();
return RepeatStatus.FINISHED;
},
platformTransactionManager)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package team.gsmgogo.scheduler;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import team.gsmgogo.domain.match.entity.MatchEntity;
import team.gsmgogo.domain.match.repository.MatchQueryDslRepository;
import team.gsmgogo.job.AlertJob;

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

@Slf4j
@Component
@RequiredArgsConstructor
public class AlertScheduler {
private final Scheduler scheduler;
private final MatchQueryDslRepository matchQueryDslRepository;

@Scheduled(cron = "0 0 0 * * *")
public void start(){
LocalDate today = LocalDate.now();
List<MatchEntity> matches = matchQueryDslRepository.findByMonthAndDay(
today.getMonthValue(),
today.getDayOfMonth()
);

matches.forEach(match -> {
try {
LocalDateTime beforeMatch = match.getStartAt().minusMinutes(10);

JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("matchId", match.getMatchId());

JobDetailImpl detail1 = new JobDetailImpl();
detail1.setName("alert-detail");
detail1.setGroup("alert");
detail1.setJobClass(AlertJob.class);
detail1.setJobDataMap(jobDataMap);

Trigger trigger1 = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule("0 %d %d %d %d ? %d".formatted(
beforeMatch.getMinute(),
beforeMatch.getHour(),
beforeMatch.getDayOfMonth(),
beforeMatch.getMonthValue(),
beforeMatch.getYear()
))).build();

scheduler.scheduleJob(detail1, trigger1);
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package team.gsmgogo.scheduler;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import team.gsmgogo.domain.user.repository.UserQueryDslRepository;
import team.gsmgogo.job.DailyJob;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
@RequiredArgsConstructor
public class DailyScheduler {
private final JobLauncher jobLauncher;
private final JobRepository jobRepository;
private final PlatformTransactionManager platformTransactionManager;
private final UserQueryDslRepository userQueryDslRepository;

@Scheduled(cron = "0 5 1 * * *")
public void resetLoginCount() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
Map<String, JobParameter<?>> confMap = new HashMap<>();
confMap.put("time", new JobParameter(System.currentTimeMillis(), String.class));
JobParameters jobParameters = new JobParameters(confMap);

jobLauncher.run(
new DailyJob(jobRepository, platformTransactionManager, userQueryDslRepository).resetCountJob(),
jobParameters
);
}

@Scheduled(cron = "0 0 0 * * *")
public void registerAlert() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
Map<String, JobParameter<?>> confMap = new HashMap<>();
confMap.put("time", new JobParameter(System.currentTimeMillis(), String.class));
JobParameters jobParameters = new JobParameters(confMap);

jobLauncher.run(
new DailyJob(jobRepository, platformTransactionManager, userQueryDslRepository).resetCountJob(),
jobParameters
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package team.gsmgogo.domain.match.repository;

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

import org.springframework.stereotype.Repository;
Expand All @@ -19,7 +20,11 @@ public List<MatchEntity> findByMonthAndDay(int month, int day) {
QMatchEntity match = QMatchEntity.matchEntity;
return queryFactory
.selectFrom(match)
.where(match.startAt.month().eq(month).and(match.startAt.dayOfMonth().eq(day)).and(match.isEnd.eq(false)))
.where(
match.startAt.month().eq(month)
.and(match.startAt.dayOfMonth().eq(day))
.and(match.isEnd.eq(false))
)
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import team.gsmgogo.domain.follow.entity.QFollowEntity;
import team.gsmgogo.domain.team.entity.QTeamEntity;
import team.gsmgogo.domain.team.entity.TeamEntity;
import team.gsmgogo.domain.user.entity.QUserEntity;
import team.gsmgogo.domain.user.entity.UserEntity;
import team.gsmgogo.domain.user.enums.IsVerify;

import java.util.List;

@Repository
@RequiredArgsConstructor
Expand All @@ -14,4 +21,15 @@ public void bulkResetVerifyCount() {
QUserEntity user = QUserEntity.userEntity;
queryFactory.update(user).set(user.verifyCount, 0L).execute();
}

public List<UserEntity> findByFollowsTeam(TeamEntity team){
QUserEntity user = QUserEntity.userEntity;
QFollowEntity follows = QFollowEntity.followEntity;

return queryFactory
.selectFrom(user)
.join(user.follows, follows)
.where(follows.team.eq(team).and(user.isVerify.eq(IsVerify.VERIFY)))
.fetch();
}
}

0 comments on commit 6c873e5

Please sign in to comment.