Skip to content

Commit

Permalink
Feat: SSE 유저용 test 코드 작성 (#105)
Browse files Browse the repository at this point in the history
Fix: 농산물 정시 시작 및 옥션 디테일시에도 완료 여부 검사
  • Loading branch information
klkim1913 authored Aug 14, 2023
1 parent 46ecece commit eb8511e
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.anywayclear.controller;

import com.anywayclear.service.NotificationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

@RestController
@RequestMapping("/api/notifications")
@Slf4j
public class NotificationController {
private final NotificationService notificationService;

public NotificationController(NotificationService notificationService) {
this.notificationService = notificationService;
}

@GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribe(@AuthenticationPrincipal OAuth2User oAuth2User) {
String userId = (String) oAuth2User.getAttributes().get("userId");
log.debug(userId);
return notificationService.subscribe(userId);
}

@PostMapping("/send-data")
public void sendData(@AuthenticationPrincipal OAuth2User oAuth2User) {
String userId = (String) oAuth2User.getAttributes().get("userId");
notificationService.notify(userId, "data");
// notificationService.notify(id, ProduceResponse.toResponse(new Produce()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public ResponseEntity<MultiResponse<ProduceResponse, Produce>> getProduceList(

@GetMapping("/{id}/auctions")
public ResponseEntity<AuctionResponseList> getAuctionList(@PathVariable long id) {
produceService.updateProduceStatus();
return ResponseEntity.ok(auctionService.getAuctionList(id));
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/anywayclear/repository/EmitterRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.anywayclear.repository;

import org.springframework.stereotype.Repository;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Repository
public class EmitterRepository {
private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();

/**
*
* @param id - 사용자 아이디
* @param emitter - 이벤트 Emitter
*/
public void save(String id, SseEmitter emitter) {
emitters.put(id,emitter);
}

public void deleteById(String id) {
emitters.remove(id);
}

public SseEmitter get(String id) {
return emitters.get(id);
}
}
52 changes: 52 additions & 0 deletions src/main/java/com/anywayclear/service/NotificationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.anywayclear.service;

import com.anywayclear.repository.EmitterRepository;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;

@Service
public class NotificationService {
// 기본 타임아웃 설정
private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60;
private final EmitterRepository emitterRepository;

public NotificationService(EmitterRepository emitterRepository) {
this.emitterRepository = emitterRepository;
}

public SseEmitter subscribe(String userId) {
SseEmitter emitter = createEmitter(userId);

sendToClient(userId, "EventStream Created. [userId=" + userId + "]");
return emitter;
}

public void notify(String userId, Object event) {
sendToClient(userId, event);
}

private void sendToClient(String id, Object data) {
SseEmitter emitter = emitterRepository.get(id);
if (emitter != null) {
try {
emitter.send(SseEmitter.event().id(String.valueOf(id)).name("sse").data(data));
} catch (IOException e) {
emitterRepository.deleteById(id);
emitter.completeWithError(e);
}
}
}

private SseEmitter createEmitter(String id) {
SseEmitter emitter = new SseEmitter(DEFAULT_TIMEOUT);
emitterRepository.save(id, emitter);
// Emitter가 완료될 때(모든 데이터가 성공적으로 전송된 상태) Emitter를 삭제한다.
emitter.onCompletion(() -> emitterRepository.deleteById(id));
// Emitter가 타임아웃 되었을 때(지정된 시간동안 어떠한 이벤트도 전송되지 않았을 때) Emitter를 삭제한다.
emitter.onTimeout(() -> emitterRepository.deleteById(id));

return emitter;
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/anywayclear/util/AuctionScheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public AuctionScheduler(ProduceRepository produceRepository) {
public void updateAuctionStatus() {
log.debug("스케줄링 시작");
for (Produce produce : produceRepository.findAll()) {
if (produce.getStatus() == 0 && LocalDateTime.now().isAfter(produce.getStartDate())) {
if (produce.getStatus() == 0 && LocalDateTime.now().isAfter(produce.getStartDate().minusMinutes(1))) {
produce.setStatus(1);
}
}
Expand Down

0 comments on commit eb8511e

Please sign in to comment.