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
+515
View File
@@ -0,0 +1,515 @@
import axios from 'axios';
import { getApiBaseUrl } from './apiConfig';
// API 베이스 URL (통합 설정 사용)
const API_BASE_URL = getApiBaseUrl();
const alertApi = axios.create({
baseURL: API_BASE_URL,
timeout: 60000, // 60초로 증가 (외부 DB 연결 시간 고려)
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
withCredentials: false,
});
// ==================== Types ====================
export type IndicatorType =
| 'VOLUME'
| 'MACD'
| 'STOCHASTIC'
| 'STOCHASTIC_K'
| 'STOCHASTIC_D'
| 'RSI'
| 'ADX'
| 'CCI'
| 'TRIX'
| 'DISPARITY'
| 'NEW_PSYCHOLOGICAL'
| 'INVEST_PSYCHOLOGICAL'
| 'WILLIAMS_R'
| 'BWI'
| 'OBV'
| 'VOLUME_OSC'
| 'VR'
| 'DMI';
export type ConditionType =
| 'CROSS_UP'
| 'CROSS_DOWN'
| 'ABOVE'
| 'BELOW'
| 'EQUAL'
| 'BETWEEN'
| 'ZERO_CROSS_UP'
| 'ZERO_CROSS_DOWN'
| 'SIGNAL_CROSS_UP'
| 'SIGNAL_CROSS_DOWN'
| 'HISTOGRAM_POSITIVE'
| 'HISTOGRAM_NEGATIVE'
| 'HISTOGRAM_INCREASE'
| 'HISTOGRAM_DECREASE'
| 'K_CROSS_D_UP'
| 'K_CROSS_D_DOWN'
| 'VOLUME_SPIKE'
| 'VOLUME_ABOVE_AVG'
| 'VOLUME_BELOW_AVG'
| 'INCREASE'
| 'DECREASE'
| 'GOLDEN_CROSS'
| 'DEAD_CROSS';
export interface AlertConditionDto {
id?: number;
cryptoAlertId?: number;
indicatorType: IndicatorType;
conditionType: ConditionType;
targetValue?: number;
secondaryValue?: number;
period?: number;
comparePeriod?: number;
enabled?: boolean;
orderIndex?: number;
description?: string;
logicalOperator?: 'AND' | 'OR';
}
export interface CryptoAlertDto {
id?: number;
symbol: string;
koreanName: string;
englishName?: string;
enabled?: boolean;
userId?: number;
conditions?: AlertConditionDto[]; // 하위 호환성
createdAt?: string;
updatedAt?: string;
}
export interface AlertNotificationDto {
id?: number;
cryptoAlertId?: number;
symbol?: string;
koreanName?: string;
message: string;
conditionDescription?: string;
isRead?: boolean;
triggeredAt?: string;
notifiedAt?: string;
userId?: number;
/** 조건 충족한 봉 시각(백엔드 LocalDateTime → ISO 또는 배열) */
candleTime?: string;
/** 알림 스케줄/조건의 캔들 간격 (예: 3m, 1h, 1d) */
timeInterval?: string;
}
export interface IndicatorDataDto {
time?: string;
open?: number;
high?: number;
low?: number;
price?: number;
ma10?: number;
ma20?: number;
ma60?: number;
ma120?: number;
rsi?: number;
macdLine?: number;
signalLine?: number;
histogram?: number;
stochK?: number;
stochD?: number;
cci?: number;
adx?: number;
plusDI?: number;
minusDI?: number;
williamsR?: number;
newPsy?: number;
investPsy?: number;
bwi?: number;
obv?: number;
obvSignal?: number; // OBV Signal Line (이동평균)
volumeOsc?: number;
vr?: number;
trix?: number;
trixSignal?: number; // TRIX Signal Line
ichimokuTenkan?: number;
ichimokuKijun?: number;
ichimokuSenkouA?: number;
ichimokuSenkouB?: number;
ichimokuChikou?: number;
disparityMid?: number;
volume?: number;
}
export interface AlertNotificationDetailDto extends AlertNotificationDto {
englishName?: string;
indicatorData?: IndicatorDataDto;
/** 알림 발생 시점 전후 지표 시계열 (조건 관련 차트 렌더링용) */
indicatorDataSeries?: IndicatorDataDto[];
satisfiedConditions?: AlertConditionDto[];
currentPrice?: number;
changeRate?: number;
volume?: number;
/** 백엔드: 알림 발생 봉 기준 OHLCV 스냅샷 */
snapshotOpen?: number;
snapshotHigh?: number;
snapshotLow?: number;
snapshotClose?: number;
snapshotVolume?: number;
}
export interface AlertScheduleConfigDto {
id?: number;
userId?: number;
cronExpression?: string;
timeInterval?: string; // 시간봉 간격 (1m, 3m, 5m, 10m, 15m, 30m, 1h, 4h, 1d, 1w)
/** 전략 조건 평가 주기(초) 3~30, 스케줄러 실행 간격 */
strategyCheckIntervalSeconds?: number;
enabled?: boolean;
lastRunTime?: string;
nextRunTime?: string;
createdAt?: string;
updatedAt?: string;
}
// ==================== CryptoAlert APIs ====================
/**
* 전체 알림 목록 조회
*/
export const getAllAlerts = async (): Promise<CryptoAlertDto[]> => {
const response = await alertApi.get<CryptoAlertDto[]>('/alerts');
return response.data;
};
/**
* 활성화된 알림 목록 조회
*/
export const getEnabledAlerts = async (): Promise<CryptoAlertDto[]> => {
const response = await alertApi.get<CryptoAlertDto[]>('/alerts/enabled');
return response.data;
};
/**
* 알림 상세 조회
*/
export const getAlert = async (alertId: number): Promise<CryptoAlertDto> => {
const response = await alertApi.get<CryptoAlertDto>(`/alerts/${alertId}`);
return response.data;
};
/**
* 심볼로 알림 조회
*/
export const getAlertBySymbol = async (symbol: string): Promise<CryptoAlertDto | null> => {
try {
const response = await alertApi.get<CryptoAlertDto>(`/alerts/symbol/${symbol}`);
return response.data;
} catch (error: any) {
if (error.response?.status === 404) {
return null;
}
throw error;
}
};
/**
* 알림 생성
*/
export const createAlert = async (dto: CryptoAlertDto): Promise<CryptoAlertDto> => {
const response = await alertApi.post<CryptoAlertDto>('/alerts', dto);
return response.data;
};
/**
* 알림 ON/OFF 토글
*/
export const toggleAlert = async (alertId: number, enabled: boolean): Promise<CryptoAlertDto> => {
const response = await alertApi.put<CryptoAlertDto>(
`/alerts/${alertId}/toggle`,
null,
{ params: { enabled } }
);
return response.data;
};
/**
* 알림 삭제
*/
export const deleteAlert = async (alertId: number): Promise<void> => {
await alertApi.delete(`/alerts/${alertId}`);
};
// ==================== AlertCondition APIs ====================
/**
* 전역 알림 조건 목록 조회
*/
export const getGlobalConditions = async (): Promise<AlertConditionDto[]> => {
const response = await alertApi.get<AlertConditionDto[]>('/alerts/conditions/global');
return response.data;
};
/**
* 알림 조건 목록 조회 (개별 종목용 - 하위 호환)
*/
export const getConditions = async (alertId: number): Promise<AlertConditionDto[]> => {
const response = await alertApi.get<AlertConditionDto[]>(`/alerts/${alertId}/conditions`);
return response.data;
};
/**
* 전역 조건 추가
*/
export const addGlobalCondition = async (dto: AlertConditionDto): Promise<AlertConditionDto> => {
const response = await alertApi.post<AlertConditionDto>('/alerts/conditions/global', dto);
return response.data;
};
/**
* 조건 추가 (개별 종목용 - 하위 호환)
*/
export const addCondition = async (
alertId: number,
dto: AlertConditionDto
): Promise<AlertConditionDto> => {
const response = await alertApi.post<AlertConditionDto>(
`/alerts/${alertId}/conditions`,
dto
);
return response.data;
};
/**
* 조건 수정
*/
export const updateCondition = async (
conditionId: number,
dto: AlertConditionDto
): Promise<AlertConditionDto> => {
const response = await alertApi.put<AlertConditionDto>(
`/alerts/conditions/${conditionId}`,
dto
);
return response.data;
};
/**
* 조건 삭제
*/
export const deleteCondition = async (conditionId: number): Promise<void> => {
await alertApi.delete(`/alerts/conditions/${conditionId}`);
};
/**
* 조건 활성화/비활성화
*/
export const toggleCondition = async (
conditionId: number,
enabled: boolean
): Promise<AlertConditionDto> => {
const response = await alertApi.put<AlertConditionDto>(
`/alerts/conditions/${conditionId}/toggle`,
null,
{ params: { enabled } }
);
return response.data;
};
// ==================== AlertNotification APIs ====================
/**
* 미읽음 알림 조회
*/
export const getUnreadNotifications = async (userId?: number): Promise<AlertNotificationDto[]> => {
const response = await alertApi.get<AlertNotificationDto[]>(
'/alerts/notifications/unread',
{ params: { userId } }
);
return response.data;
};
/**
* 알림 목록 조회 (페이징)
*/
export const getNotifications = async (
userId?: number,
page: number = 0,
size: number = 20
): Promise<{ content: AlertNotificationDto[]; totalPages: number; totalElements: number }> => {
const response = await alertApi.get('/alerts/notifications', {
params: { userId, page, size },
});
return response.data;
};
/**
* 알림 읽음 처리
*/
export const markAsRead = async (notificationId: number): Promise<void> => {
await alertApi.put(`/alerts/notifications/${notificationId}/read`);
};
/**
* 모든 미읽은 알림 읽음 처리
*/
export const markAllAsRead = async (): Promise<void> => {
await alertApi.put('/alerts/notifications/read-all');
};
/**
* 알림 상세 정보 조회 (분석 데이터 포함)
* @param symbol 통합 알림 등 행별 마켓(예: KRW-BTC). 지정 시 해당 종목 캔들·지표로 스냅샷 (알림 봉 시각은 동일).
*/
export const getNotificationDetail = async (
notificationId: number,
options?: { symbol?: string | null }
): Promise<AlertNotificationDetailDto> => {
const sym = options?.symbol?.trim();
const response = await alertApi.get<AlertNotificationDetailDto>(
`/alerts/notifications/${notificationId}/detail`,
sym ? { params: { symbol: sym } } : undefined
);
return response.data;
};
// ==================== AlertScheduleConfig APIs ====================
/**
* 스케줄 설정 조회
*/
export const getScheduleConfig = async (userId?: number): Promise<AlertScheduleConfigDto> => {
const response = await alertApi.get<AlertScheduleConfigDto>(
'/alerts/schedule',
{ params: { userId } }
);
return response.data;
};
/**
* 스케줄 설정 저장/업데이트
*/
export const saveScheduleConfig = async (
dto: AlertScheduleConfigDto
): Promise<AlertScheduleConfigDto> => {
const response = await alertApi.put<AlertScheduleConfigDto>('/alerts/schedule', dto);
return response.data;
};
/**
* 알림 삭제
*/
export const deleteNotification = async (notificationId: number): Promise<void> => {
await alertApi.delete(`/alerts/notifications/${notificationId}`);
};
/**
* 읽은 알림 모두 삭제
*/
export const deleteReadNotifications = async (userId?: number): Promise<void> => {
await alertApi.delete('/alerts/notifications/read', {
params: { userId },
});
};
// ==================== Scheduler Service APIs ====================
export interface SchedulerStatusDto {
running: boolean;
status?: string;
error?: string;
schedulerRunning?: boolean;
kafkaConnected?: boolean;
}
export interface SchedulerStartResultDto {
success: boolean;
message: string;
manualCommand?: string;
}
/**
* 스케줄러 서비스 상태 조회
*/
export const getSchedulerStatus = async (): Promise<SchedulerStatusDto> => {
const response = await alertApi.get<SchedulerStatusDto>('/scheduler/status');
return response.data;
};
/**
* 스케줄러 서비스 시작 시도
*/
export const startScheduler = async (): Promise<SchedulerStartResultDto> => {
const response = await alertApi.post<SchedulerStartResultDto>('/scheduler/start');
return response.data;
};
/** Internal `/internal/alerts/evaluate` 요청 본문 */
export interface AlertEvaluateRequest {
symbol: string;
strategyRuleId: number;
timeInterval: string;
liveCandles?: Record<string, unknown>[];
useLiveCandleTail?: boolean;
}
export interface AlertEvaluateResponse {
symbol?: string;
strategyRuleId?: number;
conditionMet: boolean;
message?: string;
}
/**
* 알림 전략 조건 평가 (스케줄러와 동일 백엔드 경로)
* `liveCandles`가 있으면 WS 버퍼 기준, 없으면 서버 Redis/REST 200봉.
*/
export const evaluateAlertStrategy = async (body: AlertEvaluateRequest): Promise<AlertEvaluateResponse> => {
const response = await alertApi.post<AlertEvaluateResponse>('/internal/alerts/evaluate', body);
return response.data;
};
/**
* 테스트 알림 생성 (파이프라인 검증용)
* Internal API - 첫 번째 종목으로 테스트 알림 생성
*/
export const createTestNotification = async (): Promise<AlertNotificationDto> => {
const response = await alertApi.post<AlertNotificationDto>('/internal/alerts/test');
return response.data;
};
/**
* 알림 읽음 처리
*/
export const markNotificationAsRead = async (notificationId: number): Promise<void> => {
await alertApi.put(`/alerts/notifications/${notificationId}/read`);
};
export default {
getAllAlerts,
getEnabledAlerts,
getAlert,
getAlertBySymbol,
createAlert,
toggleAlert,
deleteAlert,
getGlobalConditions,
getConditions,
addGlobalCondition,
addCondition,
updateCondition,
deleteCondition,
toggleCondition,
getUnreadNotifications,
getNotifications,
markAsRead,
getScheduleConfig,
saveScheduleConfig,
deleteNotification,
deleteReadNotifications,
markNotificationAsRead,
};