Migrate remaining legacy operator workflows

This commit is contained in:
2026-07-12 05:39:27 +09:00
parent ffb8f43c19
commit a01836a2d7
132 changed files with 20566 additions and 720 deletions

View File

@@ -134,6 +134,8 @@
mode: null,
manualLocked: false,
available: null,
unavailableCode: "",
unavailableMessage: "",
quarantined: false,
quarantineMessage: "",
audience: "INDIVIDUAL",
@@ -150,6 +152,7 @@
searchMessage: "",
pending: {
status: null,
legacyImport: null,
netRead: null,
netSave: null,
viRead: null,
@@ -167,7 +170,15 @@
}
function canMutate() {
return state.available === true && !state.pending.status && !locked() && !state.quarantined;
return state.available === true && !state.pending.status && !state.pending.legacyImport &&
!locked() && !state.quarantined;
}
function hasPendingManualDataOperation() {
return Boolean(
state.pending.status || state.pending.legacyImport ||
state.pending.netRead || state.pending.netSave ||
state.pending.viRead || state.pending.viSave);
}
function notify(message, kind = "info") {
@@ -197,6 +208,29 @@
send(workflow.createStatusRequest(requestId));
}
function importLegacyManualData() {
if (!canMutate() || hasPendingManualDataOperation()) return false;
const confirmation =
"원본 프로젝트의 개인.dat, 외국인.dat, 기관.dat, VI발동.dat를 " +
"앱 전용 폴더로 한 번만 가져옵니다. 기존 대상 파일이 하나라도 있으면 " +
"아무것도 덮어쓰지 않고 중단합니다. 계속하시겠습니까?";
let confirmed = false;
try {
confirmed = root && typeof root.confirm === "function" && root.confirm(confirmation) === true;
} catch {
confirmed = false;
}
if (!confirmed) return false;
const request = workflow.createLegacyImportRequest(nextId());
state.pending.legacyImport = { requestId: request.payload.requestId };
state.netVerified = null;
state.viVerified = null;
send(request);
render();
return true;
}
function requestNetRead(audience, verification = null) {
state.netVerified = null;
const requestId = nextId();
@@ -281,7 +315,7 @@
const entry = workflow.createNetSellPlaylistEntry(state.audience, {
id: nextId(),
fadeDuration: DEFAULT_FADE_DURATION
});
}, verified.freshRead);
appendPlaylistEntry(entry);
notify("수동 순매도 컷을 playlist에 추가했습니다.", "success");
log(`수동 순매도 컷 추가 · ${state.audience}`);
@@ -381,7 +415,7 @@
const entry = workflow.createViPlaylistEntry(state.viItems, state.viVersion, {
id: nextId(),
fadeDuration: DEFAULT_FADE_DURATION
});
}, verified.freshRead);
appendPlaylistEntry(entry);
notify("VI 발동 컷을 playlist에 추가했습니다.", "success");
log(`VI 발동 컷 추가 · ${state.viItems.length}종목 · ${entry.pageCount}페이지`);
@@ -399,6 +433,8 @@
if (!normalized) return false;
state.pending.status = null;
state.available = normalized.available;
state.unavailableCode = normalized.available ? "" : normalized.code;
state.unavailableMessage = normalized.available ? "" : (normalized.message || "");
if (normalized.writeQuarantined) {
state.quarantined = true;
state.quarantineMessage = normalized.message || "수동 저장 결과를 확인할 수 없습니다.";
@@ -407,6 +443,24 @@
return true;
}
function handleLegacyImport(payload) {
const pending = state.pending.legacyImport;
if (!pending) return false;
const normalized = workflow.normalizeLegacyImportResponse(payload, pending.requestId);
if (!normalized) return false;
state.pending.legacyImport = null;
state.netVerified = null;
state.viVerified = null;
notify(
`원본 수동 데이터를 가져왔습니다. 순매도 ${normalized.netSellRowCount}행 · VI ${normalized.viItemCount}종목`,
"success");
log(`원본 수동 데이터 가져오기 완료 · marker v${normalized.markerVersion}`);
if (state.mode === "net") requestNetRead(state.audience);
if (state.mode === "vi") requestViRead();
render();
return true;
}
function handleNetData(payload) {
const pending = state.pending.netRead;
if (!pending) return false;
@@ -420,7 +474,8 @@
rowsEqual(pending.verification.rows, normalized.rows)) {
state.netVerified = {
audience: state.audience,
rows: copyRows(normalized.rows)
rows: copyRows(normalized.rows),
freshRead: normalized
};
notify("저장 파일 재조회가 입력과 일치합니다.", "success");
} else {
@@ -465,7 +520,8 @@
} else {
state.viVerified = {
version: normalized.version,
items: copyItems(normalized.items)
items: copyItems(normalized.items),
freshRead: normalized
};
if (verification) {
notify(
@@ -501,6 +557,7 @@
const pendingOperations = Object.freeze({
status: "status",
legacyImport: "import-legacy-manual-data",
netRead: "read-net-sell",
netSave: "save-net-sell",
viRead: "read-vi",
@@ -514,7 +571,11 @@
const normalized = workflow.normalizeError(payload, pending.requestId);
if (!normalized || normalized.operation !== operation) return false;
state.pending[key] = null;
if (key === "status") state.available = false;
if (key === "status") {
state.available = false;
state.unavailableCode = normalized.code;
state.unavailableMessage = normalized.message;
}
if (key === "viSave" && normalized.code === "STALE_DATA") {
state.viBaseVersion = null;
state.viVersion = null;
@@ -566,6 +627,7 @@
function handleMessage(type, payload) {
switch (type) {
case workflow.bridgeContract.statusResultType: return handleStatus(payload);
case workflow.bridgeContract.legacyImportResultType: return handleLegacyImport(payload);
case workflow.bridgeContract.netSellDataType: return handleNetData(payload);
case workflow.bridgeContract.netSellSaveResultType: return handleNetSave(payload);
case workflow.bridgeContract.viDataType: return handleViData(payload);
@@ -603,7 +665,12 @@
state.open = false;
render();
}, { className: "manual-lists-close" });
header.append(title, close);
const legacyImport = button("원본 데이터 가져오기", importLegacyManualData, {
className: "manual-lists-button",
disabled: !canMutate() || hasPendingManualDataOperation(),
title: "원본 프로젝트의 FSell/VI 파일을 앱 전용 폴더로 한 번만 가져옵니다."
});
header.append(title, legacyImport, close);
panel.append(header);
}
@@ -624,7 +691,7 @@
panel.append(element(
"div",
"manual-lists-banner manual-lists-banner-danger",
"수동 데이터 저장소를 사용할 수 없습니다."));
state.unavailableMessage || "수동 데이터 저장소를 사용할 수 없습니다."));
}
}
@@ -806,6 +873,8 @@
mode: state.mode,
locked: locked(),
available: state.available,
unavailableCode: state.unavailableCode,
unavailableMessage: state.unavailableMessage,
quarantined: state.quarantined,
audience: state.audience,
netRows: Object.freeze(copyRows(state.netRows)),
@@ -896,6 +965,7 @@
clearViItems,
saveVi,
addViCut,
importLegacyManualData,
requestViRead: () => requestViRead(),
close: () => {
state.open = false;