Skip to content

Commit

Permalink
곽두철 main머지 (#32)
Browse files Browse the repository at this point in the history
* feat: 곽두철 뼈대 엔티티 정의

* feat: 곽두철 뼈대 엔티티 재정의

* [Feat] fcm 메시지 알람 기능 구현 (#9)

* chore: 외부통신을 위한 webflux의존성 추가

* feat: KAKAO API를 통해 메시지를 보내는 기능 구현

* feat: 시스템 시간 정의(Clock)

* chore: firebase의존성 주입

* feat: fcm을 사용해 알람기능 구현

* feat: 이벤트 객체 정의(수업 생성)

* feat: lessonid에 따른 객체 찾는 기능구현

* feat: 이벤트 객체 정의(수업 철회)

* feat: 알람 기능 추상화 밑 스케줄러 기능 구현

* feat: Letter기능 및 알람 메시지 구현

* feat: 오후 **시 **분 형식으로 변경하는 메서드 구현

* feat: 알람기능 구현(수업신청, 수업철회)

* feat: 알람기능의 필요한 스케줄 기능 추가

* chore: DB의존성 추가

* feat:서버가 재시작했을때 알림을 초기화해주는 기능구현

* feat: 알람 확인을 위한 프론트코드 구현

* feat: firebase 설정 추가

* feat: 알람 테스트를 위한 컨트롤러 작성

* feat: 스케쥴러 빈 등록

* feat: Redis를 활용하여 분산된서버일 때 중복으로 알람이 발생하는 문제 해결로직 작성

---------

Co-authored-by: yuseonjun <[email protected]>

* refactor: 중복 코드 삭제

* refactor: 잘못된 코드 리팩터링

* chore: mock 의존성 추가

* feat: 수강신청 메서드 구현

* feat: Redis의 setNX를 사용한 수강신청 락 구현

* [Feat] 알람 기능구현 및 spring batch를 사용한 스케줄기능 구현 (#13)

* 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)

* refactor: final 키워드 추가

* [Feat] kakao 소셜 로그인 및 JWT 구현 (#18)

* chore: jwt 의존성 추가

* feat: JWT 및 카카오 소셜 로그인 구현

* feat: 토큰 응답 dto 구현

* Update application.properties

* refactor: 불필요한 코드 리팩터링

* refactor: WebClientConfig 삭제 및 리팩터링

* refactor: Data -> Getter로 변경

* refactor: Builder -> 정적 팩토리 메서드로 변경

* refactor: 유저 인증 및 인가 정보를 담당하는 UserPrincipal 생성 및 코드 리팩터링

* refactor: class에서 record로 변경

* refactor: 종료하고 시작할때마다 자동으로 secretKey가 바뀌게 구현

* refactor: service로 로직 분리

* refactor: 클래스 이름 변경

* refactor: 불필요한 클래스 삭제

* refactor: 전체 코드 리팩터링

* refactor: 헤더에서 쿠키형식으로 변경

* Update application.properties

* Update application.properties

* feat: gitignore 추가

* refactor: 전체 코드 리팩터링

* [Feat] 상품 등록 및 조회 기능 (#22)

* feat: 상품 조회 기능 구현

* feat: 상품 등록 기능 구현

* refactor: 전체 코드 리팩터링

* refactor: ProductRepository -> Product

* refactor: 테스트 코드 삭제

* refactor: 사용하지 않는 메서드 제거 및 리팩터링

* Graceful Shutdown 적용 (#28)

* fact: 비동기 스레드 timeout설정

* fact: graceful shutdown 설정

* [Feat] PT 한개 조회, PT 삭제 기능 추가 (#31)

* feat: product 지우는 메서드 구현

* feat: product 1개 조회 메서드 구현

* feat: product 서비스 테스트 코드 구현

* refactor: PostMapping으로 변경

* refactor:api 변경

* refactor: 멱등성을 고려하여 변경

* [Feat] 라이엇 API에 사용할 API bucket4j 적용 (#24)

* chore: bucket4j 의존성 주입

* chore: Redis 설정 변경

* feat: bucket4j config설정 및 유량제어 정책 정의

* feat: bucket4j config설정 및 유량제어 정책 정의

* refactor: 패키지 이

* refactor: 유량제어 정책 enum 네이밍 변경

---------

Co-authored-by: yuseonjun <[email protected]>
Co-authored-by: SeonJuuuun <[email protected]>
  • Loading branch information
3 people authored May 11, 2024
1 parent e793c3f commit e6c7fc8
Show file tree
Hide file tree
Showing 70 changed files with 2,294 additions and 15 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doochul/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
application.properties
.idea
*.iws
*.iml
Expand Down
42 changes: 28 additions & 14 deletions doochul/build.gradle
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.3'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
id 'org.springframework.boot' version '3.2.3'
id 'io.spring.dependency-management' version '1.1.4'
}

group = 'org'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '17'
sourceCompatibility = '17'
}

configurations {
compileOnly {
extendsFrom annotationProcessor
}
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
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 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'com.google.firebase:firebase-admin:9.2.0'
implementation 'com.bucket4j:bucket4j-redis:8.7.0'

runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
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'
testImplementation 'org.mockito:mockito-core:3.12.4'
}

tasks.named('test') {
useJUnitPlatform()
useJUnitPlatform()
}
39 changes: 39 additions & 0 deletions doochul/src/main/java/org/doochul/application/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.doochul.application;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.doochul.domain.oauth.jwt.JwtProvider;
import org.doochul.domain.user.User;
import org.doochul.domain.user.UserRepository;
import org.doochul.infra.KakaoLoginTokenClient;
import org.doochul.infra.KakaoLoginUserClient;
import org.doochul.ui.dto.KakaoTokenResponse;
import org.doochul.ui.dto.KakaoUserInfoResponse;
import org.doochul.ui.dto.LoginRequest;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
public class AuthService {

private final KakaoLoginTokenClient kakaoLoginTokenClient;
private final KakaoLoginUserClient kakaoLoginUserClient;
private final JwtProvider jwtProvider;
private final UserRepository userRepository;

public String login(final LoginRequest request) {
final KakaoTokenResponse kakaoTokenResponse = kakaoLoginTokenClient.getTokenInfo(request.authorizationCode());
final KakaoUserInfoResponse userInfo = kakaoLoginUserClient.getUserInfo(kakaoTokenResponse.access_token());

final User user = userRepository.findBySocialIdAndSocialType(userInfo.id(), request.socialType())
.orElseGet(() -> initUser(userInfo, userInfo.id()));

return jwtProvider.createToken(user.getId());
}

private User initUser(final KakaoUserInfoResponse userInfo, final Long socialId) {
final User user = User.of(socialId.toString(), userInfo.getName());
return userRepository.save(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.doochul.application;

import java.time.Duration;
import lombok.RequiredArgsConstructor;
import org.doochul.domain.membership.MemberShip;
import org.doochul.domain.membership.MemberShipRepository;
import org.doochul.domain.product.Product;
import org.doochul.domain.product.ProductRepository;
import org.doochul.domain.user.User;
import org.doochul.domain.user.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class MemberShipService {

private final MemberShipRepository memberShipRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final RedisService redisService;

public Long save(final Long userId, final Long productId) {
final Product product = productRepository.findById(productId).orElseThrow();
final User user = userRepository.findById(userId).orElseThrow();

final String key = Long.toString(userId);
if (redisService.setNX(key, "apply", Duration.ofSeconds(5))) {
final Long id = memberShipRepository.save(MemberShip.of(user, product, product.getCount())).getId();
redisService.delete(key);
return id;
}
throw new IllegalArgumentException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.doochul.application;

import org.doochul.infra.dto.Letter;

public interface MessageSendManager {
void sendTo(final Letter letter);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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;

@Slf4j
@Component
@RequiredArgsConstructor
public class NotificationEventListener {
private final NotificationService notificationService;

@EventListener
@Async
public void scheduleLessonNotificationEvent(final LessonCreateEvent event) {
notificationService.applyForLesson(event);
}

@EventListener
@Async
public void withdrawnLessonNotificationEvent1(final LessonWithdrawnEvent event) {
notificationService.withdrawnForLessons(event);
}
}
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);
}
}
}
42 changes: 42 additions & 0 deletions doochul/src/main/java/org/doochul/application/ProductService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.doochul.application;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.doochul.domain.product.Product;
import org.doochul.domain.product.ProductRepository;
import org.doochul.domain.user.User;
import org.doochul.domain.user.UserRepository;
import org.doochul.ui.dto.ProductRegisterRequest;
import org.doochul.ui.dto.ProductResponse;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class ProductService {

private final ProductRepository productRepository;
private final UserRepository userRepository;

public Long save(final Long userId, final ProductRegisterRequest productRegisterRequest) {
final User user = userRepository.findById(userId).orElseThrow();
final Product product = Product.of(user, productRegisterRequest);
productRepository.save(product);
return product.getId();
}

public ProductResponse findProduct(final Long productId) {
final Product product = productRepository.getById(productId);
return ProductResponse.from(product);
}

public List<ProductResponse> findProducts() {
final List<Product> products = productRepository.findAll();
return ProductResponse.to(products);
}

public void deleteProduct(final Long productId) {
productRepository.deleteById(productId);
}
}
24 changes: 24 additions & 0 deletions doochul/src/main/java/org/doochul/application/RedisService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.doochul.application;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;

@Service
public class RedisService {

private final RedisTemplate<String, String> redisTemplate;

public RedisService(final RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}

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));
}
}
25 changes: 25 additions & 0 deletions doochul/src/main/java/org/doochul/application/UserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.doochul.application;

import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.doochul.domain.user.Identity;
import org.doochul.domain.user.User;
import org.doochul.domain.user.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
@RequiredArgsConstructor
public class UserService {

private final UserRepository userRepository;

@Transactional
public String createUser(final String socialId, final String nickname) {
final User user = User.of(socialId, nickname);
userRepository.save(user);
return user.getSocialId().toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.doochul.application.event;

import org.doochul.domain.lesson.Lesson;
import org.doochul.domain.user.User;

public record LessonCreateEvent(
User student,
User teacher,
Lesson lesson
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.doochul.application.event;

import org.doochul.domain.lesson.Lesson;
import org.doochul.domain.user.User;

public record LessonWithdrawnEvent(
User student,
User teacher,
Lesson lesson
) {
}
Loading

0 comments on commit e6c7fc8

Please sign in to comment.