From 63ae45e03f7504afeb8f1a4b39acc3e6d8bea09a Mon Sep 17 00:00:00 2001 From: Junmo Date: Wed, 8 Jan 2025 22:05:27 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20Websocket=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/build.gradle | 1 + application.yml | 4 + build.gradle | 4 + collector/build.gradle | 17 +++ .../com/whalewatch/CollectorApplication.java | 11 ++ .../main/java/com/whalewatch/TradeDto.java | 106 ++++++++++++++++++ .../whalewatch/service/FilteringService.java | 33 ++++++ .../whalewatch/service/ParsingService.java | 56 +++++++++ .../whalewatch/service/WebSocketListener.java | 57 ++++++++++ .../whalewatch/service/WebsocketService.java | 52 +++++++++ settings.gradle | 3 +- 11 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 collector/build.gradle create mode 100644 collector/src/main/java/com/whalewatch/CollectorApplication.java create mode 100644 collector/src/main/java/com/whalewatch/TradeDto.java create mode 100644 collector/src/main/java/com/whalewatch/service/FilteringService.java create mode 100644 collector/src/main/java/com/whalewatch/service/ParsingService.java create mode 100644 collector/src/main/java/com/whalewatch/service/WebSocketListener.java create mode 100644 collector/src/main/java/com/whalewatch/service/WebsocketService.java diff --git a/api/build.gradle b/api/build.gradle index 0744cab..bea7767 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -7,6 +7,7 @@ plugins { dependencies { implementation project(':common') implementation project(':service') + implementation project(':collector') implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/application.yml b/application.yml index 37d8925..5acaebf 100644 --- a/application.yml +++ b/application.yml @@ -22,3 +22,7 @@ jwt: secret-key: "${JWT_SECRET_KEY}" access-token-validity-in-seconds: 600 refresh-token-validity-in-seconds: 1209600 + +logging: + level: + com.whalewatch.service: DEBUG diff --git a/build.gradle b/build.gradle index 1c8b338..352f2a1 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,10 @@ java { } } +repositories { + mavenCentral() // Maven Central 레포지토리에서 라이브러리 다운로드 +} + subprojects { // 모든 서브프로젝트에 공통 적용할 설정 apply plugin: 'java' diff --git a/collector/build.gradle b/collector/build.gradle new file mode 100644 index 0000000..bc3f845 --- /dev/null +++ b/collector/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.3.4' + id 'io.spring.dependency-management' version '1.1.6' +} + +dependencies { + // SpringBoot , Websocket + implementation 'org.springframework.boot:spring-boot-starter-websocket' + + // Jackson + implementation 'com.fasterxml.jackson.core:jackson-databind' + + // logging + implementation 'org.slf4j:slf4j-api:2.0.7' + runtimeOnly 'ch.qos.logback:logback-classic:1.4.5' +} diff --git a/collector/src/main/java/com/whalewatch/CollectorApplication.java b/collector/src/main/java/com/whalewatch/CollectorApplication.java new file mode 100644 index 0000000..70b2d83 --- /dev/null +++ b/collector/src/main/java/com/whalewatch/CollectorApplication.java @@ -0,0 +1,11 @@ +package com.whalewatch; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CollectorApplication { + public static void main(String[] args) { + SpringApplication.run(CollectorApplication.class, args); + } +} diff --git a/collector/src/main/java/com/whalewatch/TradeDto.java b/collector/src/main/java/com/whalewatch/TradeDto.java new file mode 100644 index 0000000..4fa602b --- /dev/null +++ b/collector/src/main/java/com/whalewatch/TradeDto.java @@ -0,0 +1,106 @@ +package com.whalewatch; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class TradeDto { + + private String type; + private String code; + + @JsonProperty("trade_price") + private Double tradePrice; + + @JsonProperty("trade_volume") + private Double tradeVolume; + + @JsonProperty("ask_bid") + private String askBid; + + @JsonProperty("change_price") + private Double changePrice; + + private Long timestamp; + @JsonProperty("trade_timestamp") + private Long tradeTimestamp; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Double getTradePrice() { + return tradePrice; + } + + public void setTradePrice(Double tradePrice) { + this.tradePrice = tradePrice; + } + + public Double getTradeVolume() { + return tradeVolume; + } + + public void setTradeVolume(Double tradeVolume) { + this.tradeVolume = tradeVolume; + } + + public String getAskBid() { + return askBid; + } + + public void setAskBid(String askBid) { + this.askBid = askBid; + } + + + public Double getChangePrice() { + return changePrice; + } + + public void setChangePrice(Double changePrice) { + this.changePrice = changePrice; + } + + public Long getTimestamp() { + return timestamp; + } + + public void setTimestamp(Long timestamp) { + this.timestamp = timestamp; + } + + public Long getTradeTimestamp() { + return tradeTimestamp; + } + + public void setTradeTimestamp(Long tradeTimestamp) { + this.tradeTimestamp = tradeTimestamp; + } + + @Override + public String toString() { + return "TradeDto{" + + "type='" + type + '\'' + + ", code='" + code + '\'' + + ", tradePrice=" + tradePrice + + ", tradeVolume=" + tradeVolume + + ", askBid='" + askBid + '\'' + + ", changePrice=" + changePrice + + ", timestamp=" + timestamp + + ", tradeTimestamp=" + tradeTimestamp + + '}'; + } +} diff --git a/collector/src/main/java/com/whalewatch/service/FilteringService.java b/collector/src/main/java/com/whalewatch/service/FilteringService.java new file mode 100644 index 0000000..7600dd6 --- /dev/null +++ b/collector/src/main/java/com/whalewatch/service/FilteringService.java @@ -0,0 +1,33 @@ +package com.whalewatch.service; + +import com.whalewatch.TradeDto; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class FilteringService { + + private final Map volumeThresholdMap = new ConcurrentHashMap<>(); + + public FilteringService() { + // 테스트용 초기값 설정 + volumeThresholdMap.put("KRW-BTC", 0.5); + volumeThresholdMap.put("KRW-ETH", 0.5); + } + + public double getVolumeThreshold(String coin) { + return volumeThresholdMap.get(coin); + } + + public boolean shouldAlert(TradeDto dto) { + if (dto.getCode() == null || dto.getTradeVolume() == null) { + return false; + } + double threshold = getVolumeThreshold(dto.getCode()); + return dto.getTradeVolume() >= threshold; + } + + +} diff --git a/collector/src/main/java/com/whalewatch/service/ParsingService.java b/collector/src/main/java/com/whalewatch/service/ParsingService.java new file mode 100644 index 0000000..4db7346 --- /dev/null +++ b/collector/src/main/java/com/whalewatch/service/ParsingService.java @@ -0,0 +1,56 @@ +package com.whalewatch.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.whalewatch.TradeDto; +import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class ParsingService { + + private static final Logger log = LoggerFactory.getLogger(ParsingService.class); + + private final ObjectMapper objectMapper; + private final FilteringService filteringService; + + public ParsingService(ObjectMapper objectMapper, + FilteringService filteringService) { + this.objectMapper = objectMapper; + this.filteringService = filteringService; + } + + public void parsingMessage(String jsonMessage) { + try { + // JSON → TradeDto + TradeDto tradeDto = objectMapper.readValue(jsonMessage, TradeDto.class); + + // 필터링 + boolean pass = filteringService.shouldAlert(tradeDto); + if (pass) { + log.info("[ALERT] Coin={}, volume={} exceeded threshold => {}", + tradeDto.getCode(), + tradeDto.getTradeVolume(), + tradeDto); + } + + } catch (Exception e) { + log.debug("Exception message: {}", jsonMessage, e); + } + } + + @PostConstruct + public void testHandleMessage() { + // 애플리케이션 시작 시 가짜 데이터 처리 테스트 + String sampleJson = "{\"type\":\"trade\",\"code\":\"KRW-BTC\"," + + "\"trade_price\":50000.0,\"trade_volume\":1.0," + + "\"ask_bid\":\"ASK\",\"change_price\":10.0," + + "\"timestamp\":1620000000000,\"trade_timestamp\":1620000000000}"; + log.info("Testing handleMessage with sample data..."); + parsingMessage(sampleJson); + } + + + +} diff --git a/collector/src/main/java/com/whalewatch/service/WebSocketListener.java b/collector/src/main/java/com/whalewatch/service/WebSocketListener.java new file mode 100644 index 0000000..ac0a6e8 --- /dev/null +++ b/collector/src/main/java/com/whalewatch/service/WebSocketListener.java @@ -0,0 +1,57 @@ +package com.whalewatch.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.AbstractWebSocketHandler; + +public class WebSocketListener extends AbstractWebSocketHandler { + private static final Logger log = LoggerFactory.getLogger(WebSocketListener.class); + + private final ParsingService parsingService; + + public WebSocketListener(ParsingService parsingService) { + this.parsingService = parsingService; + } + + //연결 + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + log.info("WebSocket Connected: {}", session.getRemoteAddress()); + + String subscriptionJson = "[" + + "{\"ticket\":\"test\"}," + + "{\"type\":\"trade\",\"codes\":[\"KRW-BTC\",\"KRW-ETH\"]}," + + "{\"format\":\"DEFAULT\"}" + + "]"; + session.sendMessage(new TextMessage(subscriptionJson)); + log.info("message: {}", subscriptionJson); + } + + //메시지 수신 + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { + String payload = message.getPayload(); + log.info("Received message: {}", payload); + parsingService.parsingMessage(payload); + } + + //에러 발생 + @Override + public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { + log.error("Error: ", exception); + } + + //연결 종료 + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { + log.info("Closed Status: {}", status); + } + + + + + +} diff --git a/collector/src/main/java/com/whalewatch/service/WebsocketService.java b/collector/src/main/java/com/whalewatch/service/WebsocketService.java new file mode 100644 index 0000000..aba2469 --- /dev/null +++ b/collector/src/main/java/com/whalewatch/service/WebsocketService.java @@ -0,0 +1,52 @@ +package com.whalewatch.service; + +import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.socket.WebSocketHttpHeaders; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.client.WebSocketClient; +import org.springframework.web.socket.client.standard.StandardWebSocketClient; + +import java.net.URI; +import java.util.Collections; +import java.util.concurrent.ExecutionException; + +@Service +public class WebsocketService { + private static final Logger log = LoggerFactory.getLogger(WebsocketService.class); + + private final ParsingService parsingService; + private WebSocketSession session; + + public WebsocketService(ParsingService parsingService) { + this.parsingService = parsingService; + } + + @PostConstruct + public void init() { + startConnection(); + } + + public void startConnection() { + try { + WebSocketClient client = new StandardWebSocketClient(); + WebSocketListener listener = new WebSocketListener(parsingService); + WebSocketHttpHeaders headers = new WebSocketHttpHeaders(); + + headers.setSecWebSocketProtocol(Collections.singletonList("json")); + + URI uri = new URI("wss://api.upbit.com/websocket/v1"); + + session = client.doHandshake(listener, headers, uri).get(); + + log.info("WebSocket connection: {}", uri); + } catch (InterruptedException | ExecutionException e) { + log.error("Failed to WebSocket connection", e); + } catch (Exception ex) { + log.error("Unexpected error", ex); + } + } + +} diff --git a/settings.gradle b/settings.gradle index ff29839..aa4fed1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ rootProject.name = 'WhaleWatch' -include 'common', 'api', 'service' \ No newline at end of file +include 'common', 'api', 'service', 'collector' + From 0129bd1cc3f03285d22a01213d6665811b21a689 Mon Sep 17 00:00:00 2001 From: Junmo Date: Thu, 9 Jan 2025 23:00:06 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor:Security=20Config=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/main/java/com/whalewatch/config/SecurityConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/com/whalewatch/config/SecurityConfig.java b/common/src/main/java/com/whalewatch/config/SecurityConfig.java index 726d217..7b7e788 100644 --- a/common/src/main/java/com/whalewatch/config/SecurityConfig.java +++ b/common/src/main/java/com/whalewatch/config/SecurityConfig.java @@ -36,7 +36,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // URL별 권한 설정 .authorizeHttpRequests(auth -> auth .requestMatchers("/h2-console/**").permitAll() - .requestMatchers("/api/users/**").permitAll() + .requestMatchers("/api/users", "/api/users/login").permitAll() // 회원가입 및 로그인 허용 + .requestMatchers("/api/users/**").authenticated() .anyRequest().authenticated() ) // JWT 필터 추가 From 6d1202c829299065b3f7cc3eaf3e441d99cba380 Mon Sep 17 00:00:00 2001 From: Junmo Date: Thu, 9 Jan 2025 22:52:20 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20Websocket=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/whalewatch/service/WebSocketListener.java | 2 +- .../src/main/java/com/whalewatch/service/WebsocketService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/collector/src/main/java/com/whalewatch/service/WebSocketListener.java b/collector/src/main/java/com/whalewatch/service/WebSocketListener.java index ac0a6e8..dc7d0fb 100644 --- a/collector/src/main/java/com/whalewatch/service/WebSocketListener.java +++ b/collector/src/main/java/com/whalewatch/service/WebSocketListener.java @@ -19,7 +19,7 @@ public WebSocketListener(ParsingService parsingService) { //연결 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { - log.info("WebSocket Connected: {}", session.getRemoteAddress()); + log.info("Listener Connected: {}", session.getRemoteAddress()); String subscriptionJson = "[" + "{\"ticket\":\"test\"}," + diff --git a/collector/src/main/java/com/whalewatch/service/WebsocketService.java b/collector/src/main/java/com/whalewatch/service/WebsocketService.java index aba2469..45ae85b 100644 --- a/collector/src/main/java/com/whalewatch/service/WebsocketService.java +++ b/collector/src/main/java/com/whalewatch/service/WebsocketService.java @@ -41,7 +41,7 @@ public void startConnection() { session = client.doHandshake(listener, headers, uri).get(); - log.info("WebSocket connection: {}", uri); + log.info("WebSocket service : {}", uri); } catch (InterruptedException | ExecutionException e) { log.error("Failed to WebSocket connection", e); } catch (Exception ex) { From 0d1b94332099a4c57a7f1142fc5675f1a6c6ecc2 Mon Sep 17 00:00:00 2001 From: Junmo Date: Sat, 11 Jan 2025 17:42:58 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20ConfigurationProperties=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/whalewatch/config/JwtProperties.java | 37 +++++++++++++++++++ .../whalewatch/config/JwtTokenProvider.java | 23 +++++------- 2 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 common/src/main/java/com/whalewatch/config/JwtProperties.java diff --git a/common/src/main/java/com/whalewatch/config/JwtProperties.java b/common/src/main/java/com/whalewatch/config/JwtProperties.java new file mode 100644 index 0000000..139fd26 --- /dev/null +++ b/common/src/main/java/com/whalewatch/config/JwtProperties.java @@ -0,0 +1,37 @@ +package com.whalewatch.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "jwt") +public class JwtProperties { + private String secretKey; + private long accessTokenValidityInSeconds; + private long refreshTokenValidityInSeconds; + + // Getters and Setters + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public long getAccessTokenValidityInSeconds() { + return accessTokenValidityInSeconds; + } + + public void setAccessTokenValidityInSeconds(long accessTokenValidityInSeconds) { + this.accessTokenValidityInSeconds = accessTokenValidityInSeconds; + } + + public long getRefreshTokenValidityInSeconds() { + return refreshTokenValidityInSeconds; + } + + public void setRefreshTokenValidityInSeconds(long refreshTokenValidityInSeconds) { + this.refreshTokenValidityInSeconds = refreshTokenValidityInSeconds; + } +} diff --git a/common/src/main/java/com/whalewatch/config/JwtTokenProvider.java b/common/src/main/java/com/whalewatch/config/JwtTokenProvider.java index 23e247a..ef2c195 100644 --- a/common/src/main/java/com/whalewatch/config/JwtTokenProvider.java +++ b/common/src/main/java/com/whalewatch/config/JwtTokenProvider.java @@ -10,48 +10,43 @@ @Component public class JwtTokenProvider { - @Value("${jwt.secret-key}") - private String secretKey; - @Value("${jwt.access-token-validity-in-seconds}") - private long accessTokenValidity; + private final JwtProperties jwtProperties; - @Value("${jwt.refresh-token-validity-in-seconds}") - private long refreshTokenValidity; - - public JwtTokenProvider() { + public JwtTokenProvider(JwtProperties jwtProperties) { + this.jwtProperties = jwtProperties; } // Access Token 생성 public String generateAccessToken(String email) { Date now = new Date(); - Date expiry = new Date(now.getTime() + accessTokenValidity * 1000); + Date expiry = new Date(now.getTime() + jwtProperties.getAccessTokenValidityInSeconds() * 1000); return Jwts.builder() .setSubject(email) .setIssuedAt(now) .setExpiration(expiry) - .signWith(SignatureAlgorithm.HS256, secretKey) + .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey()) .compact(); } // Refresh Token 생성 public String generateRefreshToken(String email) { Date now = new Date(); - Date expiry = new Date(now.getTime() + refreshTokenValidity * 1000); + Date expiry = new Date(now.getTime() + jwtProperties.getRefreshTokenValidityInSeconds() * 1000); return Jwts.builder() .setSubject(email) .setIssuedAt(now) .setExpiration(expiry) - .signWith(SignatureAlgorithm.HS256, secretKey) + .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey()) .compact(); } // 토큰에서 Subject 추출 public String getEmailFromToken(String token) { return Jwts.parserBuilder() - .setSigningKey(secretKey) + .setSigningKey(jwtProperties.getSecretKey()) .build() .parseClaimsJws(token) .getBody() @@ -62,7 +57,7 @@ public String getEmailFromToken(String token) { public boolean validateToken(String token) { try { Jwts.parserBuilder() - .setSigningKey(secretKey) + .setSigningKey(jwtProperties.getSecretKey()) .build() .parseClaimsJws(token); return true; From c744c8259396949d2432c3041146e7b7a7035e89 Mon Sep 17 00:00:00 2001 From: Junmo Date: Sat, 11 Jan 2025 17:46:14 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20refreshToken=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/whalewatch/dto/AlertSettingsDto.java | 2 -- .../main/java/com/whalewatch/dto/PostDto.java | 2 -- .../com/whalewatch/dto/TokenResponseDto.java | 2 -- .../com/whalewatch/dto/TransactionDto.java | 2 -- .../main/java/com/whalewatch/dto/UserDto.java | 2 -- .../com/whalewatch/service/JwtService.java | 20 +------------------ 6 files changed, 1 insertion(+), 29 deletions(-) diff --git a/common/src/main/java/com/whalewatch/dto/AlertSettingsDto.java b/common/src/main/java/com/whalewatch/dto/AlertSettingsDto.java index e5bf922..f0bee0c 100644 --- a/common/src/main/java/com/whalewatch/dto/AlertSettingsDto.java +++ b/common/src/main/java/com/whalewatch/dto/AlertSettingsDto.java @@ -6,8 +6,6 @@ public class AlertSettingsDto { private int threshold; private boolean notifyByEmail; - public AlertSettingsDto() {} - public AlertSettingsDto(int id,String coin, int threshold, boolean notifyByEmail) { this.id = id; this.coin = coin; diff --git a/common/src/main/java/com/whalewatch/dto/PostDto.java b/common/src/main/java/com/whalewatch/dto/PostDto.java index 125c7e2..6c780e8 100644 --- a/common/src/main/java/com/whalewatch/dto/PostDto.java +++ b/common/src/main/java/com/whalewatch/dto/PostDto.java @@ -5,8 +5,6 @@ public class PostDto { private String title; private String content; - public PostDto() {} - public PostDto(int id, String title, String content) { this.id = id; this.title = title; diff --git a/common/src/main/java/com/whalewatch/dto/TokenResponseDto.java b/common/src/main/java/com/whalewatch/dto/TokenResponseDto.java index df159b8..2c85980 100644 --- a/common/src/main/java/com/whalewatch/dto/TokenResponseDto.java +++ b/common/src/main/java/com/whalewatch/dto/TokenResponseDto.java @@ -4,8 +4,6 @@ public class TokenResponseDto { private String accessToken; private String refreshToken; - public TokenResponseDto() {} - public TokenResponseDto(String accessToken, String refreshToken) { this.accessToken = accessToken; this.refreshToken = refreshToken; diff --git a/common/src/main/java/com/whalewatch/dto/TransactionDto.java b/common/src/main/java/com/whalewatch/dto/TransactionDto.java index 25c5a97..9782780 100644 --- a/common/src/main/java/com/whalewatch/dto/TransactionDto.java +++ b/common/src/main/java/com/whalewatch/dto/TransactionDto.java @@ -6,8 +6,6 @@ public class TransactionDto { private String coin; private int amount; - public TransactionDto() {} - public TransactionDto(int id, String hash, String coin, int amount) { this.id = id; this.hash = hash; diff --git a/common/src/main/java/com/whalewatch/dto/UserDto.java b/common/src/main/java/com/whalewatch/dto/UserDto.java index 689173f..914e3a7 100644 --- a/common/src/main/java/com/whalewatch/dto/UserDto.java +++ b/common/src/main/java/com/whalewatch/dto/UserDto.java @@ -6,8 +6,6 @@ public class UserDto { private String username; private String password; - public UserDto() {} - public UserDto(int id,String email, String username, String password) { this.id = id; this.email = email; diff --git a/service/src/main/java/com/whalewatch/service/JwtService.java b/service/src/main/java/com/whalewatch/service/JwtService.java index d76a14d..5ce8f24 100644 --- a/service/src/main/java/com/whalewatch/service/JwtService.java +++ b/service/src/main/java/com/whalewatch/service/JwtService.java @@ -39,33 +39,15 @@ public TokenResponseDto login(String email, String Password) { String accessToken = tokenProvider.generateAccessToken(user.getEmail()); String refreshToken = tokenProvider.generateRefreshToken(user.getEmail()); - //기존 refreshToken 있으면 제거 - jwtTokenRepository.deleteByEmail(user.getEmail()); - - // refreshToken 저장 - JwtToken refreshTokenEntity = new JwtToken( - refreshToken, - user.getEmail(), - LocalDateTime.now().plusSeconds(1209600) - ); - jwtTokenRepository.save(refreshTokenEntity); return new TokenResponseDto(accessToken, refreshToken); } public TokenResponseDto refreshAccessToken(String refreshToken) { - JwtToken stored = jwtTokenRepository.findByToken(refreshToken) - .orElseThrow(() -> new RuntimeException("Invalid token")); - - if (stored.getExpiry().isBefore(LocalDateTime.now())) { - jwtTokenRepository.delete(stored); - throw new RuntimeException("Token expired."); - } // RefreshToken 자체가 유효한지 if (!tokenProvider.validateToken(refreshToken)) { - jwtTokenRepository.delete(stored); - throw new RuntimeException("Invalid token signature."); + throw new RuntimeException("Invalid or expired refresh token."); } // 새 Access Token 발급 From 69e4441494854821610a06ce5ec55134df92ea5b Mon Sep 17 00:00:00 2001 From: Junmo Date: Sat, 11 Jan 2025 21:18:59 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20BinaryWebsocket=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=ED=95=84=ED=84=B0=EB=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whalewatch/service/FilteringService.java | 9 ++++++--- .../whalewatch/service/ParsingService.java | 18 ++--------------- .../whalewatch/service/WebSocketListener.java | 20 +++++++++++++------ 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/collector/src/main/java/com/whalewatch/service/FilteringService.java b/collector/src/main/java/com/whalewatch/service/FilteringService.java index 7600dd6..7efe88c 100644 --- a/collector/src/main/java/com/whalewatch/service/FilteringService.java +++ b/collector/src/main/java/com/whalewatch/service/FilteringService.java @@ -1,6 +1,8 @@ package com.whalewatch.service; import com.whalewatch.TradeDto; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.Map; @@ -9,12 +11,13 @@ @Service public class FilteringService { + private static final Logger log = LoggerFactory.getLogger(ParsingService.class); private final Map volumeThresholdMap = new ConcurrentHashMap<>(); public FilteringService() { // 테스트용 초기값 설정 - volumeThresholdMap.put("KRW-BTC", 0.5); - volumeThresholdMap.put("KRW-ETH", 0.5); + volumeThresholdMap.put("KRW-BTC", 0.2); + volumeThresholdMap.put("KRW-ETH", 5.0); } public double getVolumeThreshold(String coin) { @@ -26,7 +29,7 @@ public boolean shouldAlert(TradeDto dto) { return false; } double threshold = getVolumeThreshold(dto.getCode()); - return dto.getTradeVolume() >= threshold; + return dto.getTradeVolume() > threshold; } diff --git a/collector/src/main/java/com/whalewatch/service/ParsingService.java b/collector/src/main/java/com/whalewatch/service/ParsingService.java index 4db7346..4d822c4 100644 --- a/collector/src/main/java/com/whalewatch/service/ParsingService.java +++ b/collector/src/main/java/com/whalewatch/service/ParsingService.java @@ -27,8 +27,7 @@ public void parsingMessage(String jsonMessage) { TradeDto tradeDto = objectMapper.readValue(jsonMessage, TradeDto.class); // 필터링 - boolean pass = filteringService.shouldAlert(tradeDto); - if (pass) { + if (filteringService.shouldAlert(tradeDto)) { log.info("[ALERT] Coin={}, volume={} exceeded threshold => {}", tradeDto.getCode(), tradeDto.getTradeVolume(), @@ -36,21 +35,8 @@ public void parsingMessage(String jsonMessage) { } } catch (Exception e) { - log.debug("Exception message: {}", jsonMessage, e); + log.error("Failed to parse JSON message: {}", jsonMessage, e); // error 로그로 변경하여 더욱 눈에 띄게 함 } } - @PostConstruct - public void testHandleMessage() { - // 애플리케이션 시작 시 가짜 데이터 처리 테스트 - String sampleJson = "{\"type\":\"trade\",\"code\":\"KRW-BTC\"," - + "\"trade_price\":50000.0,\"trade_volume\":1.0," - + "\"ask_bid\":\"ASK\",\"change_price\":10.0," - + "\"timestamp\":1620000000000,\"trade_timestamp\":1620000000000}"; - log.info("Testing handleMessage with sample data..."); - parsingMessage(sampleJson); - } - - - } diff --git a/collector/src/main/java/com/whalewatch/service/WebSocketListener.java b/collector/src/main/java/com/whalewatch/service/WebSocketListener.java index dc7d0fb..55f16ac 100644 --- a/collector/src/main/java/com/whalewatch/service/WebSocketListener.java +++ b/collector/src/main/java/com/whalewatch/service/WebSocketListener.java @@ -2,12 +2,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.AbstractWebSocketHandler; +import org.springframework.web.socket.handler.BinaryWebSocketHandler; -public class WebSocketListener extends AbstractWebSocketHandler { +import java.nio.charset.StandardCharsets; + +public class WebSocketListener extends BinaryWebSocketHandler { private static final Logger log = LoggerFactory.getLogger(WebSocketListener.class); private final ParsingService parsingService; @@ -32,10 +35,15 @@ public void afterConnectionEstablished(WebSocketSession session) throws Exceptio //메시지 수신 @Override - protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { - String payload = message.getPayload(); - log.info("Received message: {}", payload); - parsingService.parsingMessage(payload); + protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception { + // Binary 데이터를 String으로 변환 + String payload = new String(message.getPayload().array(), StandardCharsets.UTF_8); + + try { + parsingService.parsingMessage(payload); // JSON 변환 및 필터링 + } catch (Exception e) { + log.error("Error parsing WebSocket message: {}", payload, e); + } } //에러 발생