11package com .jypLord .domain .trade .service ;
22
33import com .jypLord .api .BrokerageFirm ;
4- import com .jypLord .api .dto .response .AssetPrice ;
54import com .jypLord .api .dto .request .buy .request .BuyRequest ;
65import com .jypLord .api .dto .request .sell .SellRequest ;
6+ import com .jypLord .api .dto .response .AssetPrice ;
77import com .jypLord .api .handler .BrokerClient ;
88import com .jypLord .domain .trade .TradeStatus ;
99import com .jypLord .domain .trade .dto .request .RegisterTradeInfoRequest ;
@@ -47,7 +47,7 @@ public Mono<Void> registerTradeInfo(Long userId, RegisterTradeInfoRequest dto) {
4747 return tradeRepository .findByUserIdAndStockCodeAndStatus (userId , dto .stockCode (), TradeStatus .ACTIVE )
4848 .flatMap (trade -> {
4949 if (trade .getUserSetPrice () == dto .price ()) {
50- return Mono .error (new AlreadyBoundException ("이미 추가한 종목:" + dto .stockCode ()));
50+ return Mono .error (new AlreadyBoundException ("이미 등록된 종목:" + dto .stockCode ()));
5151 }
5252 return Mono .error (new AlreadyBoundException ());
5353 })
@@ -63,17 +63,12 @@ public Flux<AssetPrice> receiveAssetInfo(Long userId, BrokerageFirm firm) {
6363 Mono <User > userCached = userRepository .findById (userId ).cache ();
6464
6565 return tradeRepository .findValidTradeByUserId (userId , firm )
66- .switchIfEmpty (Mono .error (new NoValidTradeException ("저장된 종목이 없음" )))
66+ .switchIfEmpty (Mono .error (new NoValidTradeException ("유효한 종목 데이터가 없음" )))
6767 .take (10 )
68- .doOnNext (trade ->
69- userSubscribeStockMap
70- .computeIfAbsent (userId , key -> ConcurrentHashMap .newKeySet ())
71- .add (trade .getStockCode ())
72- )
7368 .filterWhen (trade -> redisPricePublisher .acquireLockIfAbsent (trade .getStockCode (), userId ))
7469 .flatMap (trade ->
7570 userCached .flatMapMany (user ->
76- brokerClient .receivePrice (
71+ brokerClient .receivePrice (
7772 DTOMapper .toPriceRequest (userId , firm , user .getMarketAccessToken (), trade .getStockCode ())
7873 )
7974 .flatMap (asset ->
@@ -94,13 +89,9 @@ public Flux<Void> manageAsset(Long userId, BrokerageFirm firm) {
9489 Mono <User > userCached = userRepository .findById (userId ).cache ();
9590
9691 return tradeRepository .findValidTradeByUserId (userId , firm )
97- .switchIfEmpty (Mono .error (new NoValidTradeException ("저장된 종목이 없음" )))
92+ .switchIfEmpty (Mono .error (new NoValidTradeException ("저장된 종목데이터가 없음" )))
9893 .take (10 )
99- .doOnNext (trade ->
100- userSubscribeStockMap
101- .computeIfAbsent (userId , key -> ConcurrentHashMap .newKeySet ())
102- .add (trade .getStockCode ())
103- )
94+ .doOnNext (trade -> registerMonitoring (userId , trade .getStockCode ()))
10495 .flatMap (trade ->
10596 userCached .flatMapMany (user ->
10697 losscutMonitoring (
@@ -133,9 +124,14 @@ public Mono<Void> losscutMonitoring(
133124 brokerClient .sell (new SellRequest (firm , stockCode , userSetPrice , quantity , marketAccessToken ))
134125 .and (redisStockEventPublisher .publishLosscutEvent (userId , tradeId , stockCode , firm , userSetPrice , quantity ))
135126 )
127+ .doFinally (signalType -> unregisterMonitoring (userId , stockCode ))
136128 .then ();
137129 }
138130
131+ public int currentMonitoringUserCount () {
132+ return userSubscribeStockMap .size ();
133+ }
134+
139135 public Mono <Boolean > updateTradeStatusForIdempotency (Long tradeId , TradeStatus expectedStatus , TradeStatus newStatus ) {
140136 return tradeRepository .updateTradeStatus (tradeId , expectedStatus , newStatus );
141137 }
@@ -168,4 +164,17 @@ public Mono<Void> reBuyAfterLossCut(
168164 )
169165 .then ();
170166 }
167+
168+ private void registerMonitoring (Long userId , String stockCode ) {
169+ userSubscribeStockMap
170+ .computeIfAbsent (userId , key -> ConcurrentHashMap .newKeySet ())
171+ .add (stockCode );
172+ }
173+
174+ private void unregisterMonitoring (Long userId , String stockCode ) {
175+ userSubscribeStockMap .computeIfPresent (userId , (key , stockCodes ) -> {
176+ stockCodes .remove (stockCode );
177+ return stockCodes .isEmpty () ? null : stockCodes ;
178+ });
179+ }
171180}
0 commit comments