goldenChat base source add

This commit is contained in:
aidev
2026-05-23 15:11:48 +09:00
commit a4ea7762b5
2081 changed files with 1155760 additions and 0 deletions
@@ -0,0 +1,133 @@
package com.goldenchart.service;
import com.goldenchart.entity.GcFcmToken;
import com.goldenchart.repository.GcFcmTokenRepository;
import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Slf4j
public class FcmPushService {
private final GcFcmTokenRepository tokenRepo;
private final AppSettingsService appSettingsService;
@Value("${firebase.enabled:false}")
private boolean firebaseEnabled;
@Transactional
public void registerToken(Long userId, String deviceId, String token) {
if (token == null || token.isBlank()) return;
String dev = deviceId != null ? deviceId : "anonymous";
Optional<GcFcmToken> existing = tokenRepo.findByToken(token);
if (existing.isPresent()) {
GcFcmToken t = existing.get();
t.setUserId(userId);
t.setDeviceId(dev);
t.setActive(true);
t.setLastUsedAt(LocalDateTime.now());
tokenRepo.save(t);
} else {
tokenRepo.save(GcFcmToken.builder()
.token(token)
.userId(userId)
.deviceId(dev)
.active(true)
.lastUsedAt(LocalDateTime.now())
.build());
}
log.info("[FCM] token registered device={}", dev);
}
@Transactional
public void deleteByDevice(String deviceId) {
if (deviceId != null) tokenRepo.deleteByDeviceId(deviceId);
}
public boolean isAvailable() {
return firebaseEnabled && !FirebaseApp.getApps().isEmpty();
}
/**
* 매매 시그널 FCM — deviceId 일치 토큰 + fcm_push_enabled 설정 시.
*/
public void sendTradeSignalIfEnabled(String deviceId, Long userId, String market,
String signalType, double price,
String strategyName, Long signalId) {
if (!isAvailable() || deviceId == null) return;
var app = appSettingsService.getEntity(userId, deviceId);
if (!Boolean.TRUE.equals(app.getFcmPushEnabled())) return;
List<GcFcmToken> tokens = tokenRepo.findByDeviceIdAndActiveTrue(deviceId);
if (tokens.isEmpty()) {
tokens = tokenRepo.findByActiveTrue();
}
if (tokens.isEmpty()) return;
String title = "BUY".equals(signalType) ? "🟢 매수 시그널" : "🔴 매도 시그널";
String body = String.format("%s · ₩%,.0f%s",
market, price,
strategyName != null ? " · " + strategyName : "");
for (GcFcmToken t : tokens) {
try {
Message msg = Message.builder()
.setToken(t.getToken())
.setNotification(Notification.builder().setTitle(title).setBody(body).build())
.putData("type", "trade_signal")
.putData("signalId", signalId != null ? String.valueOf(signalId) : "")
.putData("market", market)
.putData("signalType", signalType)
.putData("price", String.valueOf(price))
.build();
FirebaseMessaging.getInstance().send(msg);
t.setLastUsedAt(LocalDateTime.now());
tokenRepo.save(t);
} catch (FirebaseMessagingException e) {
log.warn("[FCM] send failed: {}", e.getMessagingErrorCode());
if (e.getMessagingErrorCode() == MessagingErrorCode.UNREGISTERED
|| e.getMessagingErrorCode() == MessagingErrorCode.INVALID_ARGUMENT) {
t.setActive(false);
tokenRepo.save(t);
}
} catch (Exception e) {
log.warn("[FCM] send error: {}", e.getMessage());
}
}
}
public void sendTest(Long userId, String deviceId) {
if (!isAvailable()) {
log.warn("[FCM] test skipped — Firebase not initialized");
return;
}
List<GcFcmToken> tokens = deviceId != null
? tokenRepo.findByDeviceIdAndActiveTrue(deviceId)
: tokenRepo.findByActiveTrue();
for (GcFcmToken t : tokens) {
try {
Message msg = Message.builder()
.setToken(t.getToken())
.setNotification(Notification.builder()
.setTitle("GoldenChart 테스트")
.setBody("FCM 푸시가 정상 동작합니다.")
.build())
.putData("type", "test")
.build();
FirebaseMessaging.getInstance().send(msg);
} catch (Exception e) {
log.warn("[FCM] test send failed: {}", e.getMessage());
}
}
}
}