-
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.
release: 0.2.4
- Loading branch information
Showing
54 changed files
with
1,118 additions
and
46 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,3 +27,5 @@ jwtVersion=0.11.5 | |
gatlingVersion=3.9.5.6 | ||
### Data faker ### | ||
datafakerVersion=2.0.2 | ||
### FCM ### | ||
fcmVersion=9.2.0 |
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,4 @@ | ||
dependencies { | ||
implementation "com.google.firebase:firebase-admin:${fcmVersion}" | ||
|
||
} |
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
18 changes: 18 additions & 0 deletions
18
src/main/java/net/teumteum/alert/app/AlertExecutorConfigurer.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,18 @@ | ||
package net.teumteum.alert.app; | ||
|
||
import java.util.concurrent.Executor; | ||
import java.util.concurrent.Executors; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class AlertExecutorConfigurer { | ||
|
||
public static final String ALERT_EXECUTOR = "alertExecutor"; | ||
|
||
@Bean | ||
public Executor alertExecutor() { | ||
return Executors.newSingleThreadScheduledExecutor(); | ||
} | ||
|
||
} |
34 changes: 34 additions & 0 deletions
34
src/main/java/net/teumteum/alert/app/BeforeMeetingAlertHandler.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,34 @@ | ||
package net.teumteum.alert.app; | ||
|
||
import static net.teumteum.alert.app.AlertExecutorConfigurer.ALERT_EXECUTOR; | ||
|
||
import java.time.Instant; | ||
import lombok.RequiredArgsConstructor; | ||
import net.teumteum.alert.domain.AlertPublisher; | ||
import net.teumteum.alert.domain.AlertService; | ||
import net.teumteum.alert.domain.Alertable; | ||
import net.teumteum.alert.domain.BeforeMeetingAlert; | ||
import net.teumteum.meeting.domain.MeetingAlerted; | ||
import org.springframework.context.annotation.Profile; | ||
import org.springframework.context.event.EventListener; | ||
import org.springframework.scheduling.annotation.Async; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@Profile("prod") | ||
@RequiredArgsConstructor | ||
public class BeforeMeetingAlertHandler { | ||
|
||
private final AlertService alertService; | ||
private final AlertPublisher<BeforeMeetingAlert> alertPublisher; | ||
|
||
@Async(ALERT_EXECUTOR) | ||
@EventListener({MeetingAlerted.class}) | ||
public void alert(MeetingAlerted alerted) { | ||
alertService.findAllByUserId(alerted.userIds()) | ||
.stream() | ||
.map(userAlert -> new BeforeMeetingAlert(userAlert.getUserId(), userAlert.getToken(), Instant.now())) | ||
.forEach(alertPublisher::publish); | ||
} | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/net/teumteum/alert/controller/AlertController.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,27 @@ | ||
package net.teumteum.alert.controller; | ||
|
||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import net.teumteum.alert.domain.AlertService; | ||
import net.teumteum.alert.domain.request.RegisterAlertRequest; | ||
import net.teumteum.core.security.service.SecurityService; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.ResponseStatus; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
public class AlertController { | ||
|
||
private final AlertService alertService; | ||
private final SecurityService securityService; | ||
|
||
@PostMapping("/alerts") | ||
@ResponseStatus(HttpStatus.OK) | ||
public void registerAlert(@Valid @RequestBody RegisterAlertRequest registerAlertRequest) { | ||
var loginUserId = securityService.getCurrentUserId(); | ||
alertService.registerAlert(loginUserId, registerAlertRequest); | ||
} | ||
} |
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,8 @@ | ||
package net.teumteum.alert.domain; | ||
|
||
@FunctionalInterface | ||
public interface AlertPublisher<T extends Alertable> { | ||
|
||
void publish(T alertable); | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/net/teumteum/alert/domain/AlertRepository.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,12 @@ | ||
package net.teumteum.alert.domain; | ||
|
||
import java.util.List; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
import org.springframework.data.repository.query.Param; | ||
|
||
public interface AlertRepository extends JpaRepository<UserAlert, Long> { | ||
|
||
@Query("select u from user_alert as u where u.userId in :userIds") | ||
List<UserAlert> findAllByUserId(@Param("userIds") Iterable<Long> userIds); | ||
} |
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,26 @@ | ||
package net.teumteum.alert.domain; | ||
|
||
import java.util.List; | ||
import java.util.Set; | ||
import lombok.RequiredArgsConstructor; | ||
import net.teumteum.alert.domain.request.RegisterAlertRequest; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
public class AlertService { | ||
|
||
private final AlertRepository alertRepository; | ||
|
||
@Transactional | ||
public void registerAlert(Long userId, RegisterAlertRequest registerAlertRequest) { | ||
var alert = new UserAlert(null, userId, registerAlertRequest.token()); | ||
alertRepository.save(alert); | ||
} | ||
|
||
public List<UserAlert> findAllByUserId(Set<Long> userIds) { | ||
return alertRepository.findAllByUserId(userIds); | ||
} | ||
} |
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,10 @@ | ||
package net.teumteum.alert.domain; | ||
|
||
public interface Alertable { | ||
|
||
String token(); | ||
|
||
String title(); | ||
|
||
String body(); | ||
} |
20 changes: 20 additions & 0 deletions
20
src/main/java/net/teumteum/alert/domain/BeforeMeetingAlert.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 net.teumteum.alert.domain; | ||
|
||
import java.time.Instant; | ||
|
||
public record BeforeMeetingAlert( | ||
Long userId, | ||
String token, | ||
Instant publishedAt | ||
) implements Alertable { | ||
|
||
@Override | ||
public String title() { | ||
return "5분 뒤에 모임이 시작돼요!"; | ||
} | ||
|
||
@Override | ||
public String body() { | ||
return "모임 장소로 가서 틈틈 모임을 준비해주세요."; | ||
} | ||
} |
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,34 @@ | ||
package net.teumteum.alert.domain; | ||
|
||
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 lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@Table(name = "user_alert") | ||
@Entity(name = "user_alert") | ||
public class UserAlert { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Column(name = "id") | ||
private Long id; | ||
|
||
@Column(name = "user_id", nullable = false, unique = true) | ||
private Long userId; | ||
|
||
@Column(name = "token", nullable = false) | ||
private String token; | ||
|
||
public String getToken() { | ||
return token; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/net/teumteum/alert/domain/request/RegisterAlertRequest.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,10 @@ | ||
package net.teumteum.alert.domain.request; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record RegisterAlertRequest( | ||
@NotNull | ||
String token | ||
) { | ||
|
||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/net/teumteum/alert/infra/FcmAlertExecutorConfigurer.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,18 @@ | ||
package net.teumteum.alert.infra; | ||
|
||
import java.util.concurrent.Executor; | ||
import java.util.concurrent.Executors; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class FcmAlertExecutorConfigurer { | ||
|
||
public static final String FCM_ALERT_EXECUTOR = "fcmAlertExecutor"; | ||
|
||
@Bean | ||
public Executor fcmAlertExecutor() { | ||
return Executors.newSingleThreadScheduledExecutor(); | ||
} | ||
|
||
} |
97 changes: 97 additions & 0 deletions
97
src/main/java/net/teumteum/alert/infra/FcmAlertPublisher.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,97 @@ | ||
package net.teumteum.alert.infra; | ||
|
||
import static net.teumteum.alert.infra.FcmAlertExecutorConfigurer.FCM_ALERT_EXECUTOR; | ||
|
||
import com.google.auth.oauth2.GoogleCredentials; | ||
import com.google.firebase.ErrorCode; | ||
import com.google.firebase.FirebaseApp; | ||
import com.google.firebase.FirebaseOptions; | ||
import com.google.firebase.messaging.AndroidConfig; | ||
import com.google.firebase.messaging.AndroidNotification; | ||
import com.google.firebase.messaging.FirebaseMessaging; | ||
import com.google.firebase.messaging.FirebaseMessagingException; | ||
import com.google.firebase.messaging.Message; | ||
import com.google.firebase.messaging.Notification; | ||
import jakarta.annotation.PostConstruct; | ||
import java.io.IOException; | ||
import net.teumteum.alert.domain.AlertPublisher; | ||
import net.teumteum.alert.domain.BeforeMeetingAlert; | ||
import org.springframework.context.annotation.Profile; | ||
import org.springframework.core.io.ClassPathResource; | ||
import org.springframework.lang.Nullable; | ||
import org.springframework.scheduling.annotation.Async; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@Profile("prod") | ||
public class FcmAlertPublisher implements AlertPublisher<BeforeMeetingAlert> { | ||
|
||
private static final int MAX_RETRY_COUNT = 5; | ||
private static final String FCM_TOKEN_PATH = "teum-teum-12611-firebase-adminsdk-cjyx3-ea066f25ef.json"; | ||
|
||
@Override | ||
@Async(FCM_ALERT_EXECUTOR) | ||
public void publish(BeforeMeetingAlert beforeMeetingAlert) { | ||
var message = buildMessage(beforeMeetingAlert); | ||
publishWithRetry(0, message, null); | ||
} | ||
|
||
private void publishWithRetry(int currentRetryCount, Message message, @Nullable ErrorCode errorCode) { | ||
if (MAX_RETRY_COUNT == currentRetryCount) { | ||
return; | ||
} | ||
if (errorCode == ErrorCode.INTERNAL | ||
|| errorCode == ErrorCode.CONFLICT | ||
|| errorCode == ErrorCode.UNKNOWN | ||
|| errorCode == ErrorCode.DATA_LOSS) { | ||
try { | ||
FirebaseMessaging.getInstance().send(message); | ||
} catch (FirebaseMessagingException firebaseMessagingException) { | ||
publishWithRetry(currentRetryCount + 1, message, firebaseMessagingException.getErrorCode()); | ||
} | ||
} | ||
} | ||
|
||
private Message buildMessage(BeforeMeetingAlert beforeMeetingAlert) { | ||
return Message.builder() | ||
.setToken(beforeMeetingAlert.token()) | ||
.setNotification(buildNotification(beforeMeetingAlert)) | ||
.setAndroidConfig(buildAndroidConfig(beforeMeetingAlert)) | ||
.putData("publishedAt", beforeMeetingAlert.publishedAt().toString()) | ||
.putData("userId", beforeMeetingAlert.userId().toString()) | ||
.build(); | ||
} | ||
|
||
private Notification buildNotification(BeforeMeetingAlert beforeMeetingAlert) { | ||
return Notification.builder() | ||
.setTitle(beforeMeetingAlert.title()) | ||
.setBody(beforeMeetingAlert.body()) | ||
.build(); | ||
} | ||
|
||
private AndroidConfig buildAndroidConfig(BeforeMeetingAlert beforeMeetingAlert) { | ||
return AndroidConfig.builder() | ||
.setNotification(AndroidNotification.builder() | ||
.setTitle(beforeMeetingAlert.title()) | ||
.setBody(beforeMeetingAlert.body()) | ||
.setClickAction("push_click") | ||
.build()) | ||
.build(); | ||
} | ||
|
||
@PostConstruct | ||
private void fcmCredential() { | ||
try { | ||
var resource = new ClassPathResource(FCM_TOKEN_PATH); | ||
resource.getInputStream(); | ||
|
||
var firebaseOptions = FirebaseOptions.builder() | ||
.setCredentials(GoogleCredentials.fromStream(resource.getInputStream())) | ||
.build(); | ||
|
||
FirebaseApp.initializeApp(firebaseOptions); | ||
} catch (IOException ioException) { | ||
throw new IllegalStateException("애플리케이션을 시작할 수 없습니다.", ioException); | ||
} | ||
} | ||
} |
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
Oops, something went wrong.