-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 전화하기 리뷰 유도 푸시알림 기능 추가 * feat: 초기 데이터 삽입 추가 * feat: 트랜잭션 추가 및 기본데이터 추가 * chore: flyway 파일 버전 변경 * feat: 테스트 코드 추가 * feat: 테스트 충돌로 인한 로직 이동 및 테스트코드 수정 * chore: flyway 개행 추가 * chore: Repository 메서드 순서 변경 * refactor: 불필요한 @transactional 제거 * chore: 라인 포맷팅 수정 * chore: 변수 네이밍 수정 * feat: 매칭되는 메세지가 없는 경우 예외처리 추가 * feat: n+1 문제 해결 및 테스트코드 수정 * chore: 메소드명 변경 및 Clock 추가 * refactor: 이벤트 리스너 삭제 * chore: transactional 어노테이션 추가 * fix: 스테이지 프로덕션 DB 불일치로 인한 변경 * chore: 명칭 통일 * refactor: 리뷰 반영 * chore: flyway 버전 변경
- Loading branch information
1 parent
cb27a9f
commit 46802a9
Showing
25 changed files
with
474 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
...in/java/in/koreatech/koin/domain/shop/exception/NotificationMessageNotFoundException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package in.koreatech.koin.domain.shop.exception; | ||
|
||
import in.koreatech.koin.global.exception.DataNotFoundException; | ||
|
||
public class NotificationMessageNotFoundException extends DataNotFoundException { | ||
|
||
private static final String DEFAULT_MESSAGE = "해당 상점에 해당하는 알림 메세지를 찾지 못했습니다."; | ||
|
||
public NotificationMessageNotFoundException(String message) { | ||
super(message); | ||
} | ||
|
||
public NotificationMessageNotFoundException(String message, String detail) { | ||
super(message, detail); | ||
} | ||
|
||
public static NotificationMessageNotFoundException withDetail(String detail) { | ||
return new NotificationMessageNotFoundException(DEFAULT_MESSAGE, detail); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/main/java/in/koreatech/koin/domain/shop/model/shop/ShopMainCategory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package in.koreatech.koin.domain.shop.model.shop; | ||
|
||
import static lombok.AccessLevel.PROTECTED; | ||
|
||
import in.koreatech.koin.global.domain.BaseEntity; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.OneToOne; | ||
import jakarta.persistence.Table; | ||
import jakarta.validation.constraints.NotNull; | ||
import jakarta.validation.constraints.Size; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@NoArgsConstructor(access = PROTECTED) | ||
@Table(name = "shop_main_categories") | ||
public class ShopMainCategory extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Integer id; | ||
|
||
@NotNull | ||
@Size(max = 255) | ||
@Column(name = "name", nullable = false) | ||
private String name; | ||
|
||
@OneToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "notification_message_id", referencedColumnName = "id", nullable = false) | ||
private ShopNotificationMessage notificationMessage; | ||
} |
50 changes: 50 additions & 0 deletions
50
src/main/java/in/koreatech/koin/domain/shop/model/shop/ShopNotificationBuffer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package in.koreatech.koin.domain.shop.model.shop; | ||
|
||
import static lombok.AccessLevel.PROTECTED; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import in.koreatech.koin.domain.user.model.User; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import jakarta.persistence.Table; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@NoArgsConstructor(access = PROTECTED) | ||
@Table(name = "shop_notification_buffer") | ||
public class ShopNotificationBuffer { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Integer id; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "shop_id", referencedColumnName = "id", nullable = false) | ||
private Shop shop; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false) | ||
private User user; | ||
|
||
@NotNull | ||
@Column(name = "notification_time", columnDefinition = "TIMESTAMP", nullable = false) | ||
private LocalDateTime notificationTime; | ||
|
||
@Builder | ||
private ShopNotificationBuffer(Shop shop, User user, LocalDateTime notificationTime) { | ||
this.shop = shop; | ||
this.user = user; | ||
this.notificationTime = notificationTime; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/in/koreatech/koin/domain/shop/model/shop/ShopNotificationMessage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package in.koreatech.koin.domain.shop.model.shop; | ||
|
||
import static lombok.AccessLevel.PROTECTED; | ||
|
||
import in.koreatech.koin.global.domain.BaseEntity; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.Table; | ||
import jakarta.validation.constraints.Size; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@NoArgsConstructor(access = PROTECTED) | ||
@Table(name = "shop_notification_messages") | ||
public class ShopNotificationMessage extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Integer id; | ||
|
||
@Size(max = 255) | ||
@Column(name = "title", nullable = false) | ||
private String title; | ||
|
||
@Size(max = 255) | ||
@Column(name = "content", nullable = false) | ||
private String content; | ||
} |
38 changes: 38 additions & 0 deletions
38
.../java/in/koreatech/koin/domain/shop/repository/shop/ShopNotificationBufferRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package in.koreatech.koin.domain.shop.repository.shop; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
|
||
import org.springframework.data.jpa.repository.Query; | ||
import org.springframework.data.repository.Repository; | ||
import org.springframework.data.repository.query.Param; | ||
|
||
import in.koreatech.koin.domain.shop.model.shop.ShopNotificationBuffer; | ||
|
||
public interface ShopNotificationBufferRepository extends Repository<ShopNotificationBuffer, Integer> { | ||
|
||
ShopNotificationBuffer save(ShopNotificationBuffer shopNotificationBuffer); | ||
|
||
@Query(""" | ||
SELECT b FROM ShopNotificationBuffer b | ||
JOIN FETCH b.shop s | ||
JOIN FETCH b.user u | ||
JOIN FETCH s.shopCategories scm | ||
JOIN FETCH scm.shopCategory sc | ||
JOIN FETCH sc.mainCategory smc | ||
JOIN FETCH smc.notificationMessage m | ||
WHERE b.notificationTime < :now | ||
AND sc.id = ( | ||
SELECT MIN(sc2.id) | ||
FROM ShopCategory sc2 | ||
JOIN ShopCategoryMap scm2 ON scm2.shopCategory = sc2 | ||
WHERE scm2.shop = s | ||
AND sc2.name != '전체보기' | ||
) | ||
""") | ||
List<ShopNotificationBuffer> findByNotificationTimeBefore(@Param("now") LocalDateTime now); | ||
|
||
List<ShopNotificationBuffer> findAll(); | ||
|
||
int deleteByNotificationTimeBefore(LocalDateTime now); | ||
} |
25 changes: 25 additions & 0 deletions
25
src/main/java/in/koreatech/koin/domain/shop/scheduler/NotificationScheduler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package in.koreatech.koin.domain.shop.scheduler; | ||
|
||
import org.springframework.scheduling.annotation.Scheduled; | ||
import org.springframework.stereotype.Component; | ||
|
||
import in.koreatech.koin.domain.shop.service.NotificationScheduleService; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class NotificationScheduler { | ||
|
||
private final NotificationScheduleService notificationScheduleService; | ||
|
||
@Scheduled(cron = "0 * * * * *") | ||
public void sendDueNotifications() { | ||
try { | ||
notificationScheduleService.sendDueNotifications(); | ||
} catch (Exception e) { | ||
log.warn("리뷰유도 알림 전송 과정에서 오류가 발생했습니다."); | ||
} | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
src/main/java/in/koreatech/koin/domain/shop/service/NotificationScheduleService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package in.koreatech.koin.domain.shop.service; | ||
|
||
import static in.koreatech.koin.global.fcm.MobileAppPath.SHOP; | ||
|
||
import java.time.Clock; | ||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
|
||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import in.koreatech.koin.domain.shop.exception.NotificationMessageNotFoundException; | ||
import in.koreatech.koin.domain.shop.model.shop.Shop; | ||
import in.koreatech.koin.domain.shop.model.shop.ShopCategoryMap; | ||
import in.koreatech.koin.domain.shop.model.shop.ShopNotificationBuffer; | ||
import in.koreatech.koin.domain.shop.model.shop.ShopNotificationMessage; | ||
import in.koreatech.koin.domain.shop.repository.shop.ShopNotificationBufferRepository; | ||
import in.koreatech.koin.domain.user.model.User; | ||
|
||
import in.koreatech.koin.global.domain.notification.model.Notification; | ||
import in.koreatech.koin.global.domain.notification.model.NotificationFactory; | ||
import in.koreatech.koin.global.domain.notification.service.NotificationService; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
public class NotificationScheduleService { | ||
|
||
private final Clock clock; | ||
private final ShopNotificationBufferRepository shopNotificationBufferRepository; | ||
private final NotificationFactory notificationFactory; | ||
private final NotificationService notificationService; | ||
|
||
@Transactional | ||
public void sendDueNotifications() { | ||
LocalDateTime now = LocalDateTime.now(clock); | ||
List<ShopNotificationBuffer> dueNotifications = shopNotificationBufferRepository.findByNotificationTimeBefore(now); | ||
if (dueNotifications.isEmpty()) { | ||
return; | ||
} | ||
|
||
List<Notification> notifications = dueNotifications.stream() | ||
.map(this::createNotification) | ||
.toList(); | ||
shopNotificationBufferRepository.deleteByNotificationTimeBefore(now); | ||
|
||
notificationService.push(notifications); | ||
} | ||
|
||
private Notification createNotification(ShopNotificationBuffer dueNotification) { | ||
Shop shop = dueNotification.getShop(); | ||
User user = dueNotification.getUser(); | ||
|
||
ShopNotificationMessage shopNotificationMessage = shop.getShopCategories().stream() | ||
.findFirst() | ||
.map(ShopCategoryMap::getShopCategory) | ||
.map(shopCategory -> shopCategory.getMainCategory().getNotificationMessage()) | ||
.orElseThrow(() -> NotificationMessageNotFoundException.withDetail("shopId: " + shop.getId())); | ||
|
||
return notificationFactory.generateReviewPromptNotification( | ||
SHOP, | ||
dueNotification.getShop().getId(), | ||
shop.getName(), | ||
shopNotificationMessage.getTitle(), | ||
shopNotificationMessage.getContent(), | ||
user | ||
); | ||
} | ||
} |
Oops, something went wrong.