Skip to content

Commit

Permalink
[Feat] 알람 기능구현 및 spring batch를 사용한 스케줄기능 구현 (#13)
Browse files Browse the repository at this point in the history
* feat: Batch 설정 및 user엔티티 재정

* feat: Batch를 사용해 수업전 리마인드 알람 스케줄 작성

* feat: Batch를 사용해 수업전 리마인드 알람 스케줄 작성(JPA -> 쿼리로 변경)

* feat: Batch를 사용해 수업후 알람 및 이용권 개수 차감

* feat: job을 실행시키는 스케줄러 구현

* feat: batch로 스케줄 로직 이동 및 클래스 네이밍 오타 수정

* feat: TaskExecutor 설정

* feat: 페키지 이동(service -> application)

* feat: Batch 설정 및 user엔티티 재정

* feat: Batch를 사용해 수업전 리마인드 알람 스케줄 작성

* feat: Batch를 사용해 수업전 리마인드 알람 스케줄 작성(JPA -> 쿼리로 변경)

* feat: Batch를 사용해 수업후 알람 및 이용권 개수 차감

* feat: job을 실행시키는 스케줄러 구현

* feat: batch로 스케줄 로직 이동 및 클래스 네이밍 오타 수정

* feat: TaskExecutor 설정

* feat: 페키지 이동(service -> application)
  • Loading branch information
ingpyo authored Mar 31, 2024
1 parent 99650a3 commit 5463e3a
Show file tree
Hide file tree
Showing 29 changed files with 548 additions and 157 deletions.
5 changes: 3 additions & 2 deletions doochul/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'com.google.firebase:firebase-admin:9.2.0'

runtimeOnly 'mysql:mysql-connector-java'
runtimeOnly 'mysql:mysql-connector-java:8.0.28'
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.batch:spring-batch-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package org.doochul.service;
package org.doochul.application;

import org.doochul.infra.dto.Letter;

public interface MessageSendManager {
void sendTo(final Letter letter);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.doochul.service;
package org.doochul.application;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.doochul.application.event.LessonCreateEvent;
import org.doochul.application.event.LessonWithdrawnEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.doochul.application;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.doochul.domain.lesson.Lesson;
import org.doochul.domain.user.User;
import org.doochul.application.event.LessonCreateEvent;
import org.doochul.application.event.LessonWithdrawnEvent;
import org.doochul.infra.dto.Letter;
import org.doochul.support.KeyGenerator;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.Duration;

import static org.doochul.domain.lesson.LessonStatus.SCHEDULED_LESSON;
import static org.doochul.domain.lesson.LessonStatus.WITHDRAWN_LESSON;

@Slf4j
@Service
@RequiredArgsConstructor
public class NotificationService {
private final MessageSendManager messageSendManager;
private final KeyGenerator keyGenerator;
private final RedisService redisService;

public void applyForLesson(final LessonCreateEvent event) {
final User student = event.student();
final User teacher = event.teacher();
final Lesson lesson = event.lesson();
sendNotification(
Letter.of(student.getDeviceToken(),
student.getName(),
teacher.getName(),
lesson.getStartedAt(),
SCHEDULED_LESSON));
}

public void withdrawnForLessons(final LessonWithdrawnEvent event) {
sendNotification(
Letter.of(event.student().getDeviceToken(),
event.student().getName(),
event.teacher().getName(),
event.lesson().getStartedAt(),
WITHDRAWN_LESSON));
}

@Async
public void sendNotification(final Letter letter) {
final String key = keyGenerator.generateAccountKey(letter.targetToken());
if (redisService.setNX(key, "notification", Duration.ofSeconds(5))) {
messageSendManager.sendTo(letter);
redisService.delete(key);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public void delete(final String key) {
redisTemplate.delete(key);
}


public boolean setNX(final String key, final String value, final Duration duration) {
return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, duration));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.doochul.service;
package org.doochul.application.event;

import org.doochul.domain.lesson.Lesson;
import org.doochul.domain.user.User;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.doochul.service;
package org.doochul.application.event;

import org.doochul.domain.lesson.Lesson;
import org.doochul.domain.user.User;
Expand Down
8 changes: 0 additions & 8 deletions doochul/src/main/java/org/doochul/config/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.time.Clock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class AppConfig {
Expand All @@ -12,11 +11,4 @@ public class AppConfig {
public Clock clock() {
return Clock.systemDefaultZone();
}

@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(2);
return scheduler;
}
}
23 changes: 23 additions & 0 deletions doochul/src/main/java/org/doochul/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.doochul.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@EnableAsync
@Configuration
public class AsyncConfig {

@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
19 changes: 19 additions & 0 deletions doochul/src/main/java/org/doochul/config/BatchConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.doochul.config;

import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@EnableBatchProcessing
@Configuration
public class BatchConfig {

@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
}
24 changes: 24 additions & 0 deletions doochul/src/main/java/org/doochul/domain/lesson/Lesson.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import lombok.NoArgsConstructor;
import org.doochul.domain.BaseEntity;
import org.doochul.domain.membership.MemberShip;
import org.doochul.domain.user.User;

@Entity
@Getter
Expand All @@ -26,9 +27,32 @@ public class Lesson extends BaseEntity {
@JoinColumn(name = "membership_id")
private MemberShip memberShip;

@ManyToOne
@JoinColumn(name = "student_id")
private User student;

@ManyToOne
@JoinColumn(name = "teacher_id")
private User teacher;

private LocalDateTime startedAt;

private LocalDateTime endedAt;

private String record;

public Lesson(
final MemberShip memberShip,
final User student,
final User teacher,
final LocalDateTime startedAt,
final LocalDateTime endedAt
) {
this.memberShip = memberShip;
this.student = student;
this.teacher = teacher;
this.startedAt = startedAt;
this.endedAt = endedAt;
this.record = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ default Lesson getById(final Long id) {
}

List<Lesson> findByStartedAtBefore(final LocalDateTime currentServerTime);
List<Lesson> findOngoingLessons(final LocalDateTime now);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package org.doochul.service;
package org.doochul.domain.lesson;

import lombok.Getter;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
Expand All @@ -18,6 +20,7 @@ public enum LessonStatus {
(name, time, teacher) -> formatToLocalTime(time) + " " + name + "님 " + teacher + " 강사님의 수업을 철회했습니다."),
END_LESSON("수업 종료", (name, time, teacher) -> teacher + " 강사님의 수업이 모두 종료되었습니다.");

@Getter
private final String title;
private final LessonStatusMessage message;

Expand All @@ -30,10 +33,6 @@ private static String formatToLocalTime(LocalDateTime time) {
return time.format(DateTimeFormatter.ofPattern("a HH시 mm분"));
}

public String getTitle() {
return title;
}

public String getMessage(String name, LocalDateTime time, String teacher) {
return message.getMessage(name, time, teacher);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class MemberShip extends BaseEntity {
private Long id;

@ManyToOne
@JoinColumn(name = "user_id")
@JoinColumn(name = "student_id")
private User student;

@ManyToOne
Expand All @@ -32,6 +32,12 @@ public class MemberShip extends BaseEntity {

private Integer remainingCount;

public MemberShip(final User student, final Product product, final Integer remainingCount) {
this.student = student;
this.product = product;
this.remainingCount = remainingCount;
}

public void decreasedCount() {
validateMinRemainingCount();
remainingCount -= 1;
Expand Down
7 changes: 7 additions & 0 deletions doochul/src/main/java/org/doochul/domain/product/Product.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,11 @@ public class Product extends BaseEntity {
private User teacher;

private Integer count;

public Product(final String name, final ProductType type, final User teacher, final Integer count) {
this.name = name;
this.type = type;
this.teacher = teacher;
this.count = count;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package org.doochul.domain.product;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findAll();
public interface ProductRepository extends JpaRepository<ProductRepository, Long> {
}
15 changes: 14 additions & 1 deletion doochul/src/main/java/org/doochul/domain/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.doochul.domain.BaseEntity;

@Entity
Expand All @@ -36,4 +35,18 @@ public class User extends BaseEntity {

@Enumerated(EnumType.STRING)
private Identity identity;

public User(
final String name,
final String deviceToken,
final String passWord,
final Gender gender,
final Identity identity
) {
this.name = name;
this.deviceToken = deviceToken;
this.passWord = passWord;
this.gender = gender;
this.identity = identity;
}
}
4 changes: 2 additions & 2 deletions doochul/src/main/java/org/doochul/infra/FcmMessageSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.doochul.service.Letter;
import org.doochul.service.MessageSendManager;
import org.doochul.infra.dto.Letter;
import org.doochul.application.MessageSendManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
Expand Down
22 changes: 22 additions & 0 deletions doochul/src/main/java/org/doochul/infra/dto/Letter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.doochul.infra.dto;

import org.doochul.domain.lesson.LessonStatus;

import java.time.LocalDateTime;

public record Letter(
String targetToken,
String title,
String body
) {
public static Letter of(
final String studentToken,
final String studentName,
final String teacherName,
final LocalDateTime startedAt,
final LessonStatus lessonStatus
) {
final String message = lessonStatus.getMessage(studentName, startedAt, teacherName);
return new Letter(studentToken, lessonStatus.getTitle(), message);
}
}
Loading

0 comments on commit 5463e3a

Please sign in to comment.