feat(472-6 6-B 노선 바뀐 [저장] 한 본문): 노선 저장 · 관 · 구조물 다시 얹기 · 횡단 고침 넘김을 서버 한 창구로 — 전부 아니면 없음

- POST /design/save 에 route(노선 초안) · carry(옛 노선 세션 칸) 칸 · 서버 = 노선 저장 → Node facility.rebase(rebasePipeDraft · rebaseStructureDraft) → 6-A 차례(새 노선) → Node stations.carry(routeCarryPatches) → 횡단 저장
- 되돌림 범위 = 노선 행 · 자식 표 · 단계 · 배수 폴더 · 노선 파일 · 지표면 확정(설계 지형 바꿈)
- 손 상단측은 안 넘김(요구 274) · 노선 바뀐 [확정] = [저장](새 노선 본 뒤 다시)
- 브라우저 = 노선 초안 + 사용자 칸만 한 본문 · 옛 차례 길(saveRouteDraft 뒤 flush · carryAfterRouteSave · keepDraftsOnNewRoute) · 부름 0 된 flush 창구 여섯 · saveCachedCrossPatches 걷음
- 등록부 R9 stations.carry · F10 facility.rebase · 옛 차례 단언 시험 한 본문 꼴로 · test_472_6_route_save(옛 길 = 새 길 · 실패 되돌림)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 12:07:12 +09:00
co-authored by Claude Opus 5.5
parent de0141b8fc
commit 4b6800e387
48 changed files with 1340 additions and 731 deletions
+2
View File
@@ -29,6 +29,8 @@ NODE_IDS = frozenset(
"facility.follow",
"cross.design",
"cross.berm_spans",
"facility.rebase",
"stations.carry",
}
)
+22 -5
View File
@@ -3,7 +3,8 @@
차례마다 안쪽 창구가 저마다 커넥션 · 파일을 쓰므로 DB 트랜잭션 하나로 못 꿴다. 대신 시작 전에
- DB 행 — `projects` 한 줄 · `project_workflow_stages` · `routes` 와 자식 넷(`route_points` ·
`route_statistics` · `longitudinal_sections` · `cross_sections`)
- storage — 저장 차례가 쓰는 폴더(`STORAGE_SCOPE` · 관 차례가 있으면 배수 폴더도)
- 노선 바뀐 저장(6-B)이면 지표면 모델 확정 상태(`surface_models.status` · 설계 지형 바꿈)
- storage — 저장 차례가 쓰는 폴더(`STORAGE_SCOPE` · 관 차례 · 노선 저장이면 배수 폴더도)
를 떠 두고, 하나라도 실패하면 둘 다 되돌린다. 되돌림 쓰기는 바뀐 것만(시놀로지 동기 — 안 바뀐
파일 · 표는 안 씀). 같은 프로젝트 저장이 겹치면 뒤 요청이 기다림(`project_lock`).
"""
@@ -122,8 +123,11 @@ async def _select(cursor: Any, sql: str, args: tuple | list) -> list[dict[str, A
return [dict(row) for row in await cursor.fetchall()]
async def snapshot_rows(connection: aiomysql.Connection, project_id: str) -> dict[str, list]:
"""프로젝트 한 줄 · 단계 · 노선과 자식 넷의 행(값 그대로)."""
async def snapshot_rows(
connection: aiomysql.Connection, project_id: str, surface: bool = False
) -> dict[str, list]:
"""프로젝트 한 줄 · 단계 · 노선과 자식 넷의 행(값 그대로) · `surface` = 지표면 모델 확정
상태도."""
async with connection.cursor(aiomysql.DictCursor) as cursor:
rows = {
"projects": await _select(cursor, "SELECT * FROM projects WHERE id=%s", (project_id,)),
@@ -142,6 +146,12 @@ async def snapshot_rows(connection: aiomysql.Connection, project_id: str) -> dic
rows[table] = await _select(
cursor, f"SELECT * FROM {table} WHERE route_id IN ({marks}) ORDER BY id", ids
)
if surface:
rows["surface_models"] = await _select(
cursor,
"SELECT id, status FROM surface_models WHERE project_id=%s ORDER BY id",
(project_id,),
)
return rows
@@ -161,12 +171,13 @@ async def restore_rows(
connection: aiomysql.Connection, project_id: str, saved: dict[str, list]
) -> list[str]:
"""바뀐 표만 떠 둔 행으로 — 한 트랜잭션 → 되돌린 표 이름."""
now = await snapshot_rows(connection, project_id)
now = await snapshot_rows(connection, project_id, surface="surface_models" in saved)
tree = ("routes", *ROUTE_CHILDREN)
redo_tree = any(now[table] != saved[table] for table in tree)
redo_stages = now["project_workflow_stages"] != saved["project_workflow_stages"]
redo_project = now["projects"] != saved["projects"] and bool(saved["projects"])
if not (redo_tree or redo_stages or redo_project):
redo_surface = now.get("surface_models") != saved.get("surface_models")
if not (redo_tree or redo_stages or redo_project or redo_surface):
return []
await connection.begin()
try:
@@ -188,6 +199,11 @@ async def restore_rows(
f"UPDATE projects SET {sets} WHERE id=%s",
[row[name] for name in columns] + [project_id],
)
if redo_surface: # 확정 하나 = 다른 모델 COMPLETE(`confirm_surface_model` 짝) · 상태만
await cursor.executemany(
"UPDATE surface_models SET status=%s WHERE id=%s AND project_id=%s",
[(row["status"], row["id"], project_id) for row in saved["surface_models"]],
)
await connection.commit()
except Exception:
await connection.rollback()
@@ -196,4 +212,5 @@ async def restore_rows(
*(tree if redo_tree else ()),
*(("project_workflow_stages",) if redo_stages else ()),
*(("projects",) if redo_project else ()),
*(("surface_models",) if redo_surface else ()),
]