Files
document-server/backend/database/migrations/007_fix_canonical_order.sql
Hyungi Ahn f95f67364a feat: 소설 분기 시스템 및 트리 메모장 구현
🌟 주요 기능:
- 트리 구조 메모장 시스템
- 소설 분기 관리 (정사 경로 설정)
- 중앙 배치 트리 다이어그램
- 정사 경로 목차 뷰
- 인라인 편집 기능

📚 백엔드:
- MemoTree, MemoNode 모델 추가
- 정사 경로 자동 순서 관리
- 분기점에서 하나만 선택 가능한 로직
- RESTful API 엔드포인트

🎨 프론트엔드:
- memo-tree.html: 트리 다이어그램 에디터
- story-view.html: 정사 경로 목차 뷰
- SVG 연결선으로 시각적 트리 표현
- Alpine.js 기반 반응형 UI
- Monaco Editor 통합

 특별 기능:
- 정사 경로 황금색 배지 표시
- 확대/축소 및 패닝 지원
- 드래그 앤 드롭 준비
- 내보내기 및 인쇄 기능
- 인라인 편집 모달
2025-08-25 10:25:10 +09:00

98 lines
3.1 KiB
PL/PgSQL

-- 007_fix_canonical_order.sql
-- 정사 경로 순서 계산 로직 수정
-- 기존 트리거 삭제
DROP TRIGGER IF EXISTS trigger_update_canonical_order ON memo_nodes;
DROP FUNCTION IF EXISTS update_canonical_order();
-- 정사 경로 순서를 올바르게 계산하는 함수
CREATE OR REPLACE FUNCTION update_canonical_order()
RETURNS TRIGGER AS $$
BEGIN
-- 정사로 설정될 때
IF NEW.is_canonical = TRUE AND (OLD.is_canonical IS NULL OR OLD.is_canonical = FALSE) THEN
-- 같은 부모를 가진 다른 형제 노드들의 정사 상태 해제 (분기점에서 하나만 선택)
IF NEW.parent_id IS NOT NULL THEN
UPDATE memo_nodes
SET is_canonical = FALSE, canonical_order = NULL, story_path = NULL
WHERE tree_id = NEW.tree_id
AND parent_id = NEW.parent_id
AND id != NEW.id
AND is_canonical = TRUE;
END IF;
-- 정사 경로 업데이트
NEW.story_path = COALESCE(NEW.path, '');
-- 순서는 별도 함수에서 일괄 계산
PERFORM recalculate_canonical_orders(NEW.tree_id);
END IF;
-- 정사에서 제외될 때
IF NEW.is_canonical = FALSE AND OLD.is_canonical = TRUE THEN
NEW.canonical_order = NULL;
NEW.story_path = NULL;
-- 순서 재계산
PERFORM recalculate_canonical_orders(NEW.tree_id);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 트리별 정사 경로 순서를 DFS로 재계산하는 함수
CREATE OR REPLACE FUNCTION recalculate_canonical_orders(tree_uuid UUID)
RETURNS VOID AS $$
DECLARE
current_order INTEGER := 1;
BEGIN
-- 모든 정사 노드의 순서를 NULL로 초기화
UPDATE memo_nodes
SET canonical_order = NULL
WHERE tree_id = tree_uuid AND is_canonical = TRUE;
-- DFS로 순서 할당 (재귀 CTE 사용)
WITH RECURSIVE canonical_path AS (
-- 루트 노드들 (정사인 것만)
SELECT id, parent_id, title, 1 as order_num, ARRAY[id] as path
FROM memo_nodes
WHERE tree_id = tree_uuid
AND parent_id IS NULL
AND is_canonical = TRUE
UNION ALL
-- 자식 노드들 (정사인 것만)
SELECT n.id, n.parent_id, n.title,
cp.order_num + 1 as order_num,
cp.path || n.id
FROM memo_nodes n
INNER JOIN canonical_path cp ON n.parent_id = cp.id
WHERE n.tree_id = tree_uuid
AND n.is_canonical = TRUE
)
UPDATE memo_nodes
SET canonical_order = cp.order_num
FROM canonical_path cp
WHERE memo_nodes.id = cp.id;
END;
$$ LANGUAGE plpgsql;
-- 트리거 다시 생성
CREATE TRIGGER trigger_update_canonical_order
AFTER UPDATE ON memo_nodes
FOR EACH ROW
EXECUTE FUNCTION update_canonical_order();
-- 기존 데이터의 순서 재계산
DO $$
DECLARE
tree_rec RECORD;
BEGIN
FOR tree_rec IN SELECT DISTINCT tree_id FROM memo_nodes WHERE is_canonical = TRUE
LOOP
PERFORM recalculate_canonical_orders(tree_rec.tree_id);
END LOOP;
END $$;