4.2 KiB
4.2 KiB
Финальный план рефакторинга — store + render
Согласовано: DeepSeek + Opus. Все открытые вопросы закрыты.
Архитектурное решение
Патерн: единый state объект + одна render(state) + actions мутируют state.
action → мутация state → render(state)
- Асинхронные операции (upload, classify, compare-SSE) — actions, пишут в state
- Синхронные трансформации (group, applyParseResult) — чистые функции
- DOM — проекция state, никто не читает из DOM
console.log(state)показывает всё
Структура state
state = {
batchId, contractId,
files: [{ id, name, size, status: { kind, pct, text, count }, parse, classify, supp_id, detailOpen }],
groups: [{
contractNumber, counterparty, documents, unresolved,
compare: { status, sections: { [suppId]: { header, body, ops } }, totalTime, collapsed }
}],
compareAll: { status, sections, totalTime },
ui: { steps: { upload, classify, groups, compare } },
_activeCompare: { es, timer }
}
Фазы
Фаза 0 — Каркас
- Ввести
state,render(state)(пока вызывает существующие рендеры) - Соглашение: мутировал →
render()
Фаза 1 — Файлы
fileQueue→state.filesrenderTable→renderFiles(state)fileInput changeразбит:onFilesSelected(files)— оркестратор (10-15 строк)reconcileSelection(newFiles)— чистая мутация массиваaddZipFiles(file)— async-actionaddRegularFile(file)— async-actionapplyParseResult(entry, parsed)— чистая функция (убирает дубль zip/обычной ветки)finalizeUpload()— refreshSupps + stepDone + syncDB + кнопки
status→ структура{ kind, pct, text, count }, HTML черезstatusToHTML(status)- Убрать
renderTable()из обработчиков — толькоrender()в конце action
Фаза 2 — Группы
loadGroups→loadGroupsAction()(fetch → state.groups) +renderGroups(state)buildGroupCard→renderGroupCard(group)— чистая HTML-функцияmarkGroupDoneудалить — готовность =state.groups[gi].compare.status === 'done'- Убрать
window._groupsData renderGroupsпересобирает ВСЕ карточки целиком
Фаза 3 — Сравнение/SSE
- Унификация через общие функции:
applyCompareEvent(target, event)— чистая мутация SSE-события в targetstartCompareSSE(url, target, { onDone })— жизненный цикл SSErenderCompareSections(sections)— общий рендер таблиц ops
runCompareForGroup→ тонкая обёртка надstartCompareSSEllmBtn→ тонкая обёртка надstartCompareSSE- ~160 строк дублирования → ~60 строк общих + 2 вызова
Фаза 4 — Чистка
contractId,batchId→state- Степпер →
renderStepper(state) - Промпты, чат, showText — вне scope (отдельная итерация)
Верификация
- После каждой правки:
node -cна app.js + app_utils.js - После Фазы 2: node-тесты на чистые функции (
applyParseResult,statusToHTML,renderGroupCard) - Ручной прогон upload→classify→group→compare после git push
- Регресс: «Готово» не перезапускается, параллельные сравнения заблокированы, группы не затираются
Релевантные файлы
contractor/deploy/app.js— все фазы (~1040 строк)contractor/deploy/app_utils.js— removeFile/moveUp/moveDown (Фаза 1)contractor/index.cfm— DOM-скелет, версия v1.0.177,?v=для cache bust