package com.goldenchart.service; import com.goldenchart.dto.TradeSignalDto; import com.goldenchart.entity.GcTradeSignal; import com.goldenchart.repository.GcTradeSignalRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * 매매 시그널 이력 서비스. * 실시간 전략 체크(LiveStrategyEvaluator / LiveStrategyScheduler)에서 * BUY/SELL 시그널이 발생하면 이 서비스를 통해 DB에 저장한다. */ @Service @RequiredArgsConstructor @Slf4j @Transactional public class TradeSignalService { private final GcTradeSignalRepository repo; private final FcmPushService fcmPushService; // ── 저장 ───────────────────────────────────────────────────────────────── public TradeSignalDto save(String deviceId, Long userId, String market, Long strategyId, String strategyName, String signalType, double price, long candleTime, String candleType, String executionType) { GcTradeSignal entity = GcTradeSignal.builder() .deviceId(deviceId) .userId(userId) .market(market) .strategyId(strategyId) .strategyName(strategyName) .signalType(signalType) .price(price) .candleTime(candleTime) .candleType(candleType) .executionType(executionType) .build(); GcTradeSignal saved = repo.save(entity); log.info("[TradeSignal] saved {} {} @ {} market={}", signalType, candleType, price, market); if (deviceId != null) { fcmPushService.sendTradeSignalIfEnabled( deviceId, userId, market, signalType, price, strategyName, saved.getId()); } return toDto(saved); } // ── 조회 ───────────────────────────────────────────────────────────────── @Transactional(readOnly = true) public List list(Long userId, String deviceId) { List list = userId != null ? repo.findByUserIdOrderByCreatedAtDesc(userId) : repo.findByDeviceIdOrderByCreatedAtDesc(deviceId != null ? deviceId : ""); return list.stream().map(this::toDto).toList(); } @Transactional(readOnly = true) public List listByMarket(Long userId, String deviceId, String market) { return repo.findByDeviceIdAndMarketOrderByCreatedAtDesc( deviceId != null ? deviceId : "", market) .stream().map(this::toDto).toList(); } // ── 변환 ───────────────────────────────────────────────────────────────── private TradeSignalDto toDto(GcTradeSignal e) { return TradeSignalDto.builder() .id(e.getId()) .market(e.getMarket()) .strategyId(e.getStrategyId()) .strategyName(e.getStrategyName()) .signalType(e.getSignalType()) .price(e.getPrice()) .candleTime(e.getCandleTime()) .candleType(e.getCandleType()) .executionType(e.getExecutionType()) .createdAt(e.getCreatedAt() != null ? e.getCreatedAt().toString() : null) .build(); } }