/** * 삭제·만료된 전략 ID — localStorage·ui_preferences·app-settings 정리 */ import type { StrategyDto, AppSettingsDto } from './backendApi'; import { saveAppSettings } from './backendApi'; import { markStrategyMissingOnServer, reconcileStrategyMissingWithValidIds, } from './strategyMissingCache'; import { getAppSettingsCache } from '../hooks/useAppSettings'; import { getUiPreferences, patchUiPreferences } from './uiPreferencesDb'; import type { UiPreferences } from '../types/uiPreferences'; import type { VirtualSessionConfig, VirtualTargetItem } from './virtualTradingStorage'; const LEGACY_TARGETS_KEY = 'gc_virtual_targets_v1'; function validStrategyIdSet(list: StrategyDto[]): Set { return new Set( list.map(s => s.id).filter((id): id is number => id != null && id > 0), ); } function readLegacyTargets(): VirtualTargetItem[] { try { const raw = localStorage.getItem(LEGACY_TARGETS_KEY); if (!raw) return []; const parsed = JSON.parse(raw) as VirtualTargetItem[]; return Array.isArray(parsed) ? parsed : []; } catch { return []; } } function writeLegacyTargets(items: VirtualTargetItem[]): void { try { if (items.length === 0) localStorage.removeItem(LEGACY_TARGETS_KEY); else localStorage.setItem(LEGACY_TARGETS_KEY, JSON.stringify(items)); } catch { /* ok */ } } function sanitizeLegacyTargets(validIds: Set): boolean { const legacy = readLegacyTargets(); if (legacy.length === 0) return false; let changed = false; const next = legacy.map(t => { if (t.strategyId != null && t.strategyId > 0 && !validIds.has(t.strategyId)) { markStrategyMissingOnServer(t.strategyId); changed = true; return { ...t, strategyId: null }; } return t; }); if (changed) writeLegacyTargets(next); return changed; } function buildVirtualPatch( validIds: Set, ): Partial> | null { const ui = getUiPreferences(); const virtual = ui.virtual; if (!virtual) return null; const patch: Partial> = {}; let changed = false; const session = virtual.session; const gid = session?.globalStrategyId; if (session && gid != null && gid > 0 && !validIds.has(gid)) { markStrategyMissingOnServer(gid); const nextSession: VirtualSessionConfig = { globalStrategyId: null, executionType: session.executionType === 'REALTIME_TICK' ? 'REALTIME_TICK' : 'CANDLE_CLOSE', positionMode: session.positionMode === 'SIGNAL_ONLY' ? 'SIGNAL_ONLY' : 'LONG_ONLY', running: session.running === true, }; patch.session = nextSession; changed = true; } const targets = virtual.targets; if (targets?.length) { let targetsChanged = false; const nextTargets = targets.map(t => { if (t.strategyId != null && t.strategyId > 0 && !validIds.has(t.strategyId)) { markStrategyMissingOnServer(t.strategyId); targetsChanged = true; return { ...t, strategyId: null }; } return t; }); if (targetsChanged) { patch.targets = nextTargets; changed = true; } } return changed ? patch : null; } function buildAppSettingsPatch(validIds: Set): AppSettingsDto | null { const cache = getAppSettingsCache(); const sid = cache.liveStrategyId; if (sid == null || sid <= 0 || validIds.has(sid)) return null; markStrategyMissingOnServer(sid); return { liveStrategyId: null, liveStrategyCheck: false, }; } /** * 전략 목록 로드 직후 호출 — stale ID 제거·missing 캐시 동기화 */ export async function applyStrategyReferenceSanitize(strategies: StrategyDto[]): Promise { const validIds = validStrategyIdSet(strategies); reconcileStrategyMissingWithValidIds(validIds); const legacyChanged = sanitizeLegacyTargets(validIds); const virtualPatch = buildVirtualPatch(validIds); const appPatch = buildAppSettingsPatch(validIds); if (virtualPatch) { patchUiPreferences({ virtual: virtualPatch }, true); } if (appPatch) { try { const saved = await saveAppSettings(appPatch); if (saved) Object.assign(getAppSettingsCache(), saved); } catch (e) { console.warn('[strategySanitize] app-settings 저장 실패:', e); Object.assign(getAppSettingsCache(), appPatch); } } if (legacyChanged || virtualPatch || appPatch) { console.info('[strategySanitize] 삭제된 전략 ID 참조를 정리했습니다.'); } }