diff --git a/.gitignore b/.gitignore
index 357cdbe..8d814c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,4 +32,6 @@ build/
### VS Code ###
.vscode/
-application.properties
\ No newline at end of file
+application.properties
+firebase
+firebase/make-delivery-firebase-adminsdk-8jura-1d0b64450e.json
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e031007..6ecde20 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,12 @@
spring-boot-starter-validation
+
+ com.google.firebase
+ firebase-admin
+ 6.8.1
+
+
diff --git a/src/main/java/com/flab/makedel/config/SpringAsyncConfig.java b/src/main/java/com/flab/makedel/config/SpringAsyncConfig.java
new file mode 100644
index 0000000..0b4ee54
--- /dev/null
+++ b/src/main/java/com/flab/makedel/config/SpringAsyncConfig.java
@@ -0,0 +1,44 @@
+package com.flab.makedel.config;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+/*
+ 스프링의 @Async를 사용할 때 비동기처리를 새로운 스레드 풀에서 해주기 위한 설정입니다.
+ 이 설정이 없다면 SimpleAsyncTaskExecutor를 사용하는데 이는 새로운 비동기 작업을
+ 스레드 풀에서 처리하는 것이 아니라 새로운 스레드를 매번 생성하여 작업을 수행시킵니다.
+ 또한 스레드 관리를 직접 할 수 없어 위험할 수 있습니다.
+ 따라서 밑에 설정에서 스레드 풀을 빈으로 설정해서 @Async 로직이 수행될 때
+ 이 스레드 풀을 이용하도록 설정해줍니다.
+ */
+
+@Configuration
+@EnableAsync
+public class SpringAsyncConfig {
+
+ private static final int CORE_POOL_SIZE = 5;
+ private static final int MAX_POOL_SIZE = 500;
+ private static final int QUEUE_CAPACITY = 0;
+ private static final int KEEP_ALIVE_SECONDS = 60;
+ private static final String NAME_PREFIX = "springAsyncTask-";
+
+ @Bean(name = "springAsyncTask")
+ public Executor threadPoolTaskExecutor() {
+ ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
+ taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
+ taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
+ taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
+ taskExecutor.setThreadNamePrefix(NAME_PREFIX);
+ taskExecutor.setWaitForTasksToCompleteOnShutdown(false);
+ taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
+ taskExecutor.setAllowCoreThreadTimeOut(true);
+ taskExecutor.setRejectedExecutionHandler(new AbortPolicy());
+ return taskExecutor;
+ }
+
+}
diff --git a/src/main/java/com/flab/makedel/controller/RiderController.java b/src/main/java/com/flab/makedel/controller/RiderController.java
index 5e485fe..ab809b2 100644
--- a/src/main/java/com/flab/makedel/controller/RiderController.java
+++ b/src/main/java/com/flab/makedel/controller/RiderController.java
@@ -2,8 +2,13 @@
import com.flab.makedel.annotation.LoginCheck;
import com.flab.makedel.annotation.LoginCheck.UserLevel;
+import com.flab.makedel.dto.PushMessageDTO;
import com.flab.makedel.dto.RiderDTO;
+import com.flab.makedel.service.PushService;
import com.flab.makedel.service.RiderService;
+import com.google.firebase.messaging.FirebaseMessagingException;
+import java.io.IOException;
+import java.time.LocalDateTime;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -45,5 +50,5 @@ public void acceptStandbyOrder(@PathVariable long orderId,
public void finishDeliveringOrder(@PathVariable long orderId, RiderDTO rider) {
riderService.finishDeliveringOrder(orderId, rider);
}
-
+
}
diff --git a/src/main/java/com/flab/makedel/controller/StoreController.java b/src/main/java/com/flab/makedel/controller/StoreController.java
index 864eee0..1276cf4 100644
--- a/src/main/java/com/flab/makedel/controller/StoreController.java
+++ b/src/main/java/com/flab/makedel/controller/StoreController.java
@@ -7,6 +7,8 @@
import com.flab.makedel.annotation.LoginCheck.UserLevel;
import com.flab.makedel.dto.StoreDTO;
import com.flab.makedel.service.StoreService;
+import com.google.firebase.messaging.FirebaseMessagingException;
+import java.io.IOException;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
diff --git a/src/main/java/com/flab/makedel/dao/CartItemDAO.java b/src/main/java/com/flab/makedel/dao/CartItemDAO.java
index 9433be5..d50c881 100644
--- a/src/main/java/com/flab/makedel/dao/CartItemDAO.java
+++ b/src/main/java/com/flab/makedel/dao/CartItemDAO.java
@@ -17,9 +17,7 @@
exec 후에 오류가 나지 않은 부분은 실행된다.
exec 이전에 command queue에 적재하는 도중 실패하는 경우 (command 문법오류,메모리 부족오류등,
다른 클라이언트에서 command날려 atomic보장이 안되는 경우) 에는 exec하면 전부 discard된다.
- (실험해보니 multi 후 트랜잭션중 다른 스레드에서 command를 날리면 discard된다.
- 오히려 다른스레드에서 그 키에 command 날린 것만 반영이 되고 원래트랜잭션은 discard된다. 아마도
- 원래 트랜잭션은 어차피 처리가 안되고 discard되니 다른 스레드에서 날린 command는 유효하게 처리하는거같다.)
+ (실험해보니 multi 후 트랜잭션중 다른 스레드에서 command를 날리면 discard된다.)
(레디스 2.6.5이후로 트랜잭션시작 후 오류가 있으면 exec될 때 전부 discard된다.)
트랜잭션 명령어들은 exec되기 위해 큐에서 기다리는데 discard를 이용해 실행을 하지 않을 수 있다.
트랜잭션의 locking은 watch를 이용한 optimistic locking이다. watch로 어떠한 키를 감시하고
diff --git a/src/main/java/com/flab/makedel/dto/PushMessageDTO.java b/src/main/java/com/flab/makedel/dto/PushMessageDTO.java
new file mode 100644
index 0000000..13e2496
--- /dev/null
+++ b/src/main/java/com/flab/makedel/dto/PushMessageDTO.java
@@ -0,0 +1,21 @@
+package com.flab.makedel.dto;
+
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+@Builder
+public class PushMessageDTO {
+
+ private final String title;
+
+ private final String content;
+
+ private final OrderReceiptDTO orderReceipt;
+
+ private final String createdAt;
+
+ public static final String RIDER_MESSAGE_TITLE = "배차 요청";
+ public static final String RIDER_MESSAGE_CONTENT = "근처 가게에서 주문이 승인된 후 배차 요청이 도착했습니다. 승인하시겠습니까?";
+
+}
diff --git a/src/main/java/com/flab/makedel/service/PushService.java b/src/main/java/com/flab/makedel/service/PushService.java
new file mode 100644
index 0000000..0afd629
--- /dev/null
+++ b/src/main/java/com/flab/makedel/service/PushService.java
@@ -0,0 +1,56 @@
+package com.flab.makedel.service;
+
+import com.flab.makedel.dao.DeliveryDAO;
+import com.flab.makedel.dto.PushMessageDTO;
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.messaging.BatchResponse;
+import com.google.firebase.messaging.FirebaseMessaging;
+import com.google.firebase.messaging.FirebaseMessagingException;
+import com.google.firebase.messaging.Message;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.SessionCallback;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+@Service
+@Log4j2
+public class PushService {
+
+ private final String firebaseConfigPath;
+ private final DeliveryDAO deliveryDAO;
+
+ public PushService(@Value("${firebase.config.path}") String firebaseConfigPath,
+ DeliveryDAO deliveryDAO) {
+ this.firebaseConfigPath = firebaseConfigPath;
+ this.deliveryDAO = deliveryDAO;
+ }
+
+ @PostConstruct
+ public void init() throws IOException {
+ FirebaseOptions options = new FirebaseOptions.Builder()
+ .setCredentials(GoogleCredentials
+ .fromStream(new ClassPathResource(firebaseConfigPath).getInputStream()))
+ .build();
+ if (FirebaseApp.getApps().isEmpty()) {
+ FirebaseApp.initializeApp(options);
+ }
+ }
+
+ @Async("springAsyncTask")
+ public void sendMessages(List messages) {
+ FirebaseMessaging.getInstance().sendAllAsync(messages);
+ }
+
+
+}
diff --git a/src/main/java/com/flab/makedel/service/RiderService.java b/src/main/java/com/flab/makedel/service/RiderService.java
index dba3cc6..9a55b74 100644
--- a/src/main/java/com/flab/makedel/service/RiderService.java
+++ b/src/main/java/com/flab/makedel/service/RiderService.java
@@ -2,9 +2,14 @@
import com.flab.makedel.dao.DeliveryDAO;
import com.flab.makedel.dto.OrderDTO.OrderStatus;
+import com.flab.makedel.dto.PushMessageDTO;
import com.flab.makedel.dto.RiderDTO;
import com.flab.makedel.mapper.OrderMapper;
+import com.google.firebase.messaging.FirebaseMessaging;
+import com.google.firebase.messaging.Message;
+import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -15,6 +20,7 @@ public class RiderService {
private final DeliveryDAO deliveryDAO;
private final OrderMapper orderMapper;
+ private final PushService pushService;
public void registerStandbyRiderWhenStartWork(RiderDTO rider) {
deliveryDAO.insertStandbyRiderWhenStartWork(rider);
@@ -36,4 +42,18 @@ public void finishDeliveringOrder(long orderId, RiderDTO rider) {
deliveryDAO.insertStandbyRiderWhenStartWork(rider);
}
+ public void sendMessageToStandbyRidersInSameArea(String address, PushMessageDTO pushMessage) {
+ Set tokenSet = deliveryDAO.selectStandbyRiderTokenList(address);
+ List messages = tokenSet.stream().map(token -> Message.builder()
+ .putData("title", pushMessage.getTitle())
+ .putData("content", pushMessage.getContent())
+ .putData("orderReceipt", pushMessage.getOrderReceipt().toString())
+ .putData("createdAt", pushMessage.getCreatedAt())
+ .setToken(token)
+ .build())
+ .collect(Collectors.toList());
+
+ pushService.sendMessages(messages);
+ }
+
}
diff --git a/src/main/java/com/flab/makedel/service/StoreService.java b/src/main/java/com/flab/makedel/service/StoreService.java
index 391b2d1..f3d2332 100644
--- a/src/main/java/com/flab/makedel/service/StoreService.java
+++ b/src/main/java/com/flab/makedel/service/StoreService.java
@@ -3,9 +3,13 @@
import com.flab.makedel.dto.OrderDTO.OrderStatus;
import com.flab.makedel.dto.OrderDetailDTO;
import com.flab.makedel.dto.OrderReceiptDTO;
+import com.flab.makedel.dto.PushMessageDTO;
import com.flab.makedel.dto.StoreDTO;
import com.flab.makedel.mapper.OrderMapper;
import com.flab.makedel.mapper.StoreMapper;
+import com.google.firebase.messaging.FirebaseMessagingException;
+import java.io.IOException;
+import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
@@ -20,6 +24,7 @@ public class StoreService {
private final StoreMapper storeMapper;
private final OrderMapper orderMapper;
private final DeliveryService deliveryService;
+ private final RiderService riderService;
public void insertStore(StoreDTO store) {
storeMapper.insertStore(store);
@@ -69,6 +74,18 @@ public void approveOrder(long orderId) {
orderMapper.approveOrder(orderId, OrderStatus.APPROVED_ORDER);
OrderReceiptDTO orderReceipt = orderMapper.selectOrderReceipt(orderId);
deliveryService.registerStandbyOrderWhenOrderApprove(orderId, orderReceipt);
+ riderService.sendMessageToStandbyRidersInSameArea(orderReceipt.getStoreInfo().getAddress(),
+ getPushMessage(orderReceipt));
+ }
+
+ private PushMessageDTO getPushMessage(OrderReceiptDTO orderReceipt) {
+ return PushMessageDTO.builder()
+ .title(PushMessageDTO.RIDER_MESSAGE_TITLE)
+ .content(PushMessageDTO.RIDER_MESSAGE_TITLE)
+ .createdAt(LocalDateTime.now().toString())
+ .orderReceipt(orderReceipt)
+ .build();
+
}
}