146 lines
4.4 KiB
TypeScript
146 lines
4.4 KiB
TypeScript
/**
|
|
* 삭제·만료된 전략 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<number> {
|
|
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<number>): 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<number>,
|
|
): Partial<NonNullable<UiPreferences['virtual']>> | null {
|
|
const ui = getUiPreferences();
|
|
const virtual = ui.virtual;
|
|
if (!virtual) return null;
|
|
|
|
const patch: Partial<NonNullable<UiPreferences['virtual']>> = {};
|
|
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<number>): 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<void> {
|
|
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 참조를 정리했습니다.');
|
|
}
|
|
}
|