smarteye-backend / scripts /DB /erd_schema.sql
AkJeond's picture
refactor(database): DB μŠ€ν‚€λ§ˆ μˆ˜μ • 및 μ„±λŠ₯ μ΅œμ ν™” λ¬Έμ„œ μΆ”κ°€
7aae924
-- ============================================================================
-- SmartEyeSsen Database Schema (Final Production Version v2)
-- ============================================================================
-- ν”„λ‘œμ νŠΈλͺ…: SmartEyeSsen - AI 기반 ν•™μŠ΅μ§€ 뢄석 μ‹œμŠ€ν…œ
-- λ°μ΄ν„°λ² μ΄μŠ€: smarteyessen_db
-- λ¬Έμžμ…‹: utf8mb4 (이λͺ¨μ§€, λ‹€κ΅­μ–΄ 지원)
-- μ—”μ§„: InnoDB (νŠΈλžœμž­μ…˜, μ™Έλž˜ν‚€ 지원)
-- 총 ν…Œμ΄λΈ” 수: 12개
-- μ΅œμ’… μˆ˜μ •μΌ: 2025-01-22 (v2)
-- μž‘μ„±μž: SmartEyeSsen Team
-- μ£Όμš” 변경사항: 문제 λ ˆμ΄μ•„μ›ƒ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜ 반영 (액컀/μžμ‹ κ°œλ…)
-- ============================================================================
-- ============================================================================
-- πŸ“‹ ν…Œμ΄λΈ” λͺ©λ‘ 및 관계 (v2)
-- ============================================================================
-- 1. users (μ‚¬μš©μž 관리) - 독립 ν…Œμ΄λΈ”
-- 2. document_types (λ¬Έμ„œ νƒ€μž… μ •μ˜) - 독립 ν…Œμ΄λΈ” [μˆ˜μ •]
-- 3. projects (ν”„λ‘œμ νŠΈ/μ„Έμ…˜) - FK: user_id, doc_type_id
-- 4. pages (νŽ˜μ΄μ§€ 정보) - FK: project_id
-- 5. layout_elements (λ ˆμ΄μ•„μ›ƒ μš”μ†Œ) - FK: page_id [μˆ˜μ •]
-- 6. text_contents (OCR κ²°κ³Ό) - FK: element_id (1:1)
-- 7. ai_descriptions (AI μ„€λͺ…) - FK: element_id (1:1)
-- 8. question_groups (문제 κ·Έλ£Ή) - FK: page_id, anchor_element_id [μˆ˜μ •]
-- 9. question_elements (문제-μš”μ†Œ λ§€ν•‘) - FK: question_group_id, element_id
-- 10. text_versions (ν…μŠ€νŠΈ 버전 관리) - FK: page_id, user_id
-- 11. formatting_rules (ν¬λ§·νŒ… κ·œμΉ™) - FK: doc_type_id [μˆ˜μ •]
-- 12. combined_results (톡합 λ¬Έμ„œ μΊμ‹œ) - FK: project_id (1:1)
-- ============================================================================
-- πŸ”— μ£Όμš” 관계 μš”μ•½ (v2)
-- ============================================================================
-- users β†’ projects (1:N)
-- document_types β†’ projects (1:N)
-- document_types β†’ formatting_rules (1:N)
-- projects β†’ pages (1:N)
-- projects β†’ combined_results (1:1)
-- pages β†’ layout_elements (1:N)
-- pages β†’ question_groups (1:N)
-- pages β†’ text_versions (1:N)
-- layout_elements β†’ text_contents (1:1)
-- layout_elements β†’ ai_descriptions (1:1)
-- layout_elements β†’ question_groups (1:1) [μ‹ κ·œ] - 액컀 관계
-- layout_elements ← question_elements (N:1)
-- question_groups β†’ question_elements (1:N)
-- ============================================================================
-- πŸ†• v2 μ£Όμš” 변경사항
-- ============================================================================
-- 1. document_types.sorting_method: 'coordinate_based' β†’ 'reading_order'둜 톡합
-- 2. layout_elements.order_index: μ‚­μ œ (Y,X μ’Œν‘œλ‘œ 동적 μ •λ ¬)
-- 3. question_groups.question_number: μ‚­μ œ
-- 4. question_groups.anchor_element_id: μΆ”κ°€ (FK β†’ layout_elements)
-- 5. layout_elements ↔ question_groups: 1:1 액컀 관계 μ‹ μ„€
-- 6. formatting_rules: 액컀/μžμ‹ 클래슀 κ·œμΉ™ μΆ”κ°€
-- ============================================================================
-- λ°μ΄ν„°λ² μ΄μŠ€ 생성 (κΈ°μ‘΄ DBκ°€ 있으면 μ‚­μ œ ν›„ μž¬μƒμ„±)
-- ============================================================================
DROP DATABASE IF EXISTS smarteyessen_db;
CREATE DATABASE smarteyessen_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE smarteyessen_db;
-- ============================================================================
-- 1️⃣ Users Table (μ‚¬μš©μž 관리)
-- ============================================================================
-- μ„€λͺ…: μ‹œμŠ€ν…œ μ‚¬μš©μž 정보 (학생, ꡐ사, κ΄€λ¦¬μž)
-- μ£Όμš” κΈ°λŠ₯: νšŒμ›κ°€μž…, 둜그인, κΆŒν•œ 관리, API ν‚€ 관리
-- ============================================================================
CREATE TABLE users (
-- κΈ°λ³Έ 정보
user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'μ‚¬μš©μž 고유 ID',
email VARCHAR(255) NOT NULL UNIQUE COMMENT '이메일 (둜그인 ID)',
name VARCHAR(100) NOT NULL COMMENT 'μ‚¬μš©μž 이름',
role VARCHAR(50) NOT NULL DEFAULT 'user' COMMENT 'μ—­ν•  (admin/teacher/student/user)',
-- λ³΄μ•ˆ 정보
password_hash VARCHAR(255) NOT NULL COMMENT 'bcrypt ν•΄μ‹œλœ λΉ„λ°€λ²ˆν˜Έ',
api_key VARCHAR(255) DEFAULT NULL COMMENT 'OpenAI API ν‚€ (AES-256 μ•”ν˜Έν™” μ €μž₯)',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '계정 생성일',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'λ§ˆμ§€λ§‰ μˆ˜μ •μΌ',
-- 인덱슀
INDEX idx_email (email) COMMENT '이메일 검색 μ΅œμ ν™”',
INDEX idx_role (role) COMMENT '역할별 필터링 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='μ‹œμŠ€ν…œ μ‚¬μš©μž 정보';
-- ============================================================================
-- 2️⃣ Document_Types Table (λ¬Έμ„œ νƒ€μž… μ •μ˜) [μˆ˜μ •]
-- ============================================================================
-- μ„€λͺ…: λ¬Έμ„œ μ’…λ₯˜λ³„ 처리 방식 μ •μ˜ (λ¬Έμ œμ§€/μΌλ°˜λ¬Έμ„œ)
-- μ£Όμš” κΈ°λŠ₯: λͺ¨λΈ 선택, μ •λ ¬ 방식 μ§€μ •, ν¬λ§·νŒ… κ·œμΉ™ μ—°κ²°
-- [v2 λ³€κ²½] sorting_method ENUM: 'coordinate_based' β†’ 'reading_order'둜 톡합
-- ============================================================================
CREATE TABLE document_types (
-- κΈ°λ³Έ 정보
doc_type_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'λ¬Έμ„œ νƒ€μž… 고유 ID',
type_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'νƒ€μž…λͺ… (worksheet/document/form)',
-- 처리 μ„€μ • [μˆ˜μ •]
model_name VARCHAR(100) NOT NULL COMMENT 'AI λͺ¨λΈλͺ… (SmartEyeSsen/DocLayout-YOLO)',
sorting_method ENUM('question_based', 'reading_order') NOT NULL
COMMENT 'μ •λ ¬ 방식: question_based(λ¬Έμ œμ§€, 액컀-μžμ‹ μž¬κ·€), reading_order(μΌλ°˜λ¬Έμ„œ, Y/X μ’Œν‘œ)',
-- λΆ€κ°€ 정보
description TEXT DEFAULT NULL COMMENT 'νƒ€μž… μ„€λͺ…',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'μˆ˜μ •μΌ',
-- 인덱슀
INDEX idx_type_name (type_name) COMMENT 'νƒ€μž…λͺ… 검색 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='λ¬Έμ„œ νƒ€μž… μ •μ˜ (λ¬Έμ œμ§€/μΌλ°˜λ¬Έμ„œ) - v2: μ •λ ¬ 방식 λͺ…ν™•ν™”';
-- ============================================================================
-- 3️⃣ Projects Table (ν”„λ‘œμ νŠΈ/μ„Έμ…˜ 관리)
-- ============================================================================
-- μ„€λͺ…: μ‚¬μš©μžμ˜ 뢄석 ν”„λ‘œμ νŠΈ (μ—¬λŸ¬ νŽ˜μ΄μ§€ 포함)
-- μ£Όμš” κΈ°λŠ₯: ν”„λ‘œμ νŠΈ 생성, μ§„ν–‰λ₯  좔적, μƒνƒœ 관리
-- ============================================================================
CREATE TABLE projects (
-- κΈ°λ³Έ 정보
project_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ν”„λ‘œμ νŠΈ 고유 ID',
user_id INT NOT NULL COMMENT 'μ†Œμœ μž ID (FK: users, ON DELETE CASCADE)',
doc_type_id INT NOT NULL COMMENT 'λ¬Έμ„œ νƒ€μž… ID (FK: document_types, ON DELETE RESTRICT)',
project_name VARCHAR(255) NOT NULL COMMENT 'ν”„λ‘œμ νŠΈ 이름',
-- μ§„ν–‰ μƒνƒœ
total_pages INT DEFAULT 0 COMMENT '총 νŽ˜μ΄μ§€ 수 (트리거둜 μžλ™ 계산)',
analysis_mode ENUM('auto', 'manual', 'hybrid') DEFAULT 'auto'
COMMENT '뢄석 λͺ¨λ“œ: auto(μžλ™), manual(μˆ˜λ™), hybrid(ν˜Όν•©)',
status ENUM('created', 'in_progress', 'completed', 'error') DEFAULT 'created'
COMMENT 'ν”„λ‘œμ νŠΈ μƒνƒœ: created(생성됨), in_progress(진행쀑), completed(μ™„λ£Œ), error(였λ₯˜)',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'ν”„λ‘œμ νŠΈ 생성일',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'λ§ˆμ§€λ§‰ μˆ˜μ •μΌ',
-- μ™Έλž˜ν‚€ μ œμ•½μ‘°κ±΄
CONSTRAINT fk_projects_user
FOREIGN KEY (user_id) REFERENCES users(user_id)
ON DELETE CASCADE,
CONSTRAINT fk_projects_doctype
FOREIGN KEY (doc_type_id) REFERENCES document_types(doc_type_id)
ON DELETE RESTRICT,
-- 인덱슀
INDEX idx_user_id (user_id) COMMENT 'μ‚¬μš©μžλ³„ ν”„λ‘œμ νŠΈ 쑰회 μ΅œμ ν™”',
INDEX idx_doc_type_id (doc_type_id) COMMENT 'νƒ€μž…λ³„ ν”„λ‘œμ νŠΈ 쑰회 μ΅œμ ν™”',
INDEX idx_status (status) COMMENT 'μƒνƒœλ³„ 필터링 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='μ‚¬μš©μž ν”„λ‘œμ νŠΈ ν…Œμ΄λΈ”. 뢄석 μ„Έμ…˜ λ‹¨μœ„ 관리.';
-- ============================================================================
-- 4️⃣ Pages Table (νŽ˜μ΄μ§€ 정보)
-- ============================================================================
-- μ„€λͺ…: ν”„λ‘œμ νŠΈ λ‚΄ κ°œλ³„ νŽ˜μ΄μ§€ (이미지 파일)
-- μ£Όμš” κΈ°λŠ₯: νŽ˜μ΄μ§€ μˆœμ„œ 관리, 뢄석 μƒνƒœ 좔적, 이미지 μ €μž₯
-- ============================================================================
CREATE TABLE pages (
-- κΈ°λ³Έ 정보
page_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'νŽ˜μ΄μ§€ 고유 ID',
project_id INT NOT NULL COMMENT 'μ†Œμ† ν”„λ‘œμ νŠΈ ID (FK: projects, ON DELETE CASCADE)',
page_number INT NOT NULL COMMENT 'νŽ˜μ΄μ§€ 번호 (1λΆ€ν„° μ‹œμž‘)',
-- 이미지 정보
image_path VARCHAR(500) NOT NULL COMMENT '이미지 파일 경둜',
image_width INT DEFAULT NULL COMMENT '이미지 λ„ˆλΉ„ (ν”½μ…€)',
image_height INT DEFAULT NULL COMMENT '이미지 높이 (ν”½μ…€)',
-- 뢄석 μƒνƒœ
analysis_status ENUM('pending', 'processing', 'completed', 'error') DEFAULT 'pending'
COMMENT '뢄석 μƒνƒœ: pending(λŒ€κΈ°), processing(μ²˜λ¦¬μ€‘), completed(μ™„λ£Œ), error(였λ₯˜)',
processing_time FLOAT DEFAULT NULL COMMENT '처리 μ‹œκ°„ (초)',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'νŽ˜μ΄μ§€ 좔가일',
analyzed_at TIMESTAMP NULL DEFAULT NULL COMMENT '뢄석 μ™„λ£ŒμΌ',
-- μ™Έλž˜ν‚€ μ œμ•½μ‘°κ±΄
CONSTRAINT fk_pages_project
FOREIGN KEY (project_id) REFERENCES projects(project_id)
ON DELETE CASCADE,
-- κ³ μœ ν‚€ 및 인덱슀
UNIQUE KEY uk_project_page (project_id, page_number)
COMMENT 'ν”„λ‘œμ νŠΈ λ‚΄ νŽ˜μ΄μ§€ 번호 쀑볡 λ°©μ§€',
INDEX idx_project_id (project_id) COMMENT 'ν”„λ‘œμ νŠΈλ³„ νŽ˜μ΄μ§€ 쑰회 μ΅œμ ν™”',
INDEX idx_analysis_status (analysis_status) COMMENT 'μƒνƒœλ³„ 필터링 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='ν”„λ‘œμ νŠΈ λ‚΄ νŽ˜μ΄μ§€ 정보 ν…Œμ΄λΈ”';
-- ============================================================================
-- 5️⃣ Layout_Elements Table (λ ˆμ΄μ•„μ›ƒ μš”μ†Œ) [μˆ˜μ •]
-- ============================================================================
-- μ„€λͺ…: AI λͺ¨λΈμ΄ κ²€μΆœν•œ λ ˆμ΄μ•„μ›ƒ μš”μ†Œ (제λͺ©, λ³Έλ¬Έ, κ·Έλ¦Ό λ“±)
-- μ£Όμš” κΈ°λŠ₯: λ°”μš΄λ”© λ°•μŠ€ μ €μž₯, 클래슀 λΆ„λ₯˜, μ’Œν‘œ 관리
-- [v2 λ³€κ²½] order_index 컬럼 μ‚­μ œ (Y,X μ’Œν‘œλ‘œ 동적 μ •λ ¬)
-- ============================================================================
CREATE TABLE layout_elements (
-- κΈ°λ³Έ 정보
element_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'μš”μ†Œ 고유 ID',
page_id INT NOT NULL COMMENT 'μ†Œμ† νŽ˜μ΄μ§€ ID (FK: pages, ON DELETE CASCADE)',
-- λΆ„λ₯˜ 정보
class_name VARCHAR(100) NOT NULL COMMENT '클래슀λͺ… (question_number/figure/table/text λ“±)',
confidence FLOAT NOT NULL COMMENT '신뒰도 (0.0~1.0)',
-- λ°”μš΄λ”© λ°•μŠ€ μ’Œν‘œ
bbox_x INT NOT NULL COMMENT 'X μ’Œν‘œ (μ™Όμͺ½ 상단)',
bbox_y INT NOT NULL COMMENT 'Y μ’Œν‘œ (μ™Όμͺ½ 상단)',
bbox_width INT NOT NULL COMMENT 'λ„ˆλΉ„ (ν”½μ…€)',
bbox_height INT NOT NULL COMMENT '높이 (ν”½μ…€)',
-- μžλ™ 계산 컬럼 (GENERATED COLUMN)
area INT GENERATED ALWAYS AS (bbox_width * bbox_height) STORED
COMMENT '면적 (μžλ™ 계산)',
y_position INT GENERATED ALWAYS AS (bbox_y) STORED
COMMENT 'Y μ •λ ¬μš© μ’Œν‘œ (μžλ™ 계산)',
x_position INT GENERATED ALWAYS AS (bbox_x) STORED
COMMENT 'X μ •λ ¬μš© μ’Œν‘œ (μžλ™ 계산)',
-- [v2 μ‚­μ œ] order_index: (Y,X) μ’Œν‘œλ‘œ 동적 μ •λ ¬ν•˜λ―€λ‘œ λΆˆν•„μš”
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_layout_elements_page
FOREIGN KEY (page_id) REFERENCES pages(page_id)
ON DELETE CASCADE,
-- 인덱슀
INDEX idx_page_id (page_id) COMMENT 'νŽ˜μ΄μ§€λ³„ μš”μ†Œ 쑰회 μ΅œμ ν™”',
INDEX idx_class_name (class_name) COMMENT 'ν΄λž˜μŠ€λ³„ 필터링 μ΅œμ ν™”',
INDEX idx_position (page_id, y_position, x_position)
COMMENT 'μ’Œν‘œ 기반 μ •λ ¬ μ΅œμ ν™” (볡합 인덱슀) - 핡심 인덱슀'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='AIκ°€ κ²€μΆœν•œ λ ˆμ΄μ•„μ›ƒ μš”μ†Œ - v2: order_index μ‚­μ œ, (Y,X) 동적 μ •λ ¬';
-- ============================================================================
-- 6️⃣ Text_Contents Table (OCR κ²°κ³Ό)
-- ============================================================================
-- μ„€λͺ…: λ ˆμ΄μ•„μ›ƒ μš”μ†Œμ—μ„œ μΆ”μΆœν•œ ν…μŠ€νŠΈ (OCR κ²°κ³Ό)
-- μ£Όμš” κΈ°λŠ₯: OCR ν…μŠ€νŠΈ μ €μž₯, μ–Έμ–΄ 감지, μ „λ¬Έ 검색
-- 관계: layout_elements와 1:1 관계
-- ============================================================================
CREATE TABLE text_contents (
-- κΈ°λ³Έ 정보
text_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'OCR 결과 고유 ID',
element_id INT NOT NULL COMMENT 'λ ˆμ΄μ•„μ›ƒ μš”μ†Œ ID (1:1 λ§€ν•‘, FK: layout_elements, ON DELETE CASCADE)',
-- OCR κ²°κ³Ό
ocr_text TEXT NOT NULL COMMENT 'OCR μΆ”μΆœ ν…μŠ€νŠΈ',
ocr_engine VARCHAR(50) DEFAULT 'PaddleOCR' COMMENT 'μ‚¬μš©ν•œ OCR μ—”μ§„',
ocr_confidence FLOAT DEFAULT NULL COMMENT 'OCR 신뒰도 (0.0~1.0)',
language VARCHAR(10) DEFAULT 'ko' COMMENT 'μ–Έμ–΄ μ½”λ“œ (ko/en/ja/zh)',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_text_contents_element
FOREIGN KEY (element_id) REFERENCES layout_elements(element_id)
ON DELETE CASCADE,
-- μ œμ•½μ‘°κ±΄ 및 인덱슀
UNIQUE KEY uk_element (element_id) COMMENT '1:1 관계 보μž₯ (쀑볡 λ°©μ§€)',
INDEX idx_language (language) COMMENT '언어별 필터링 μ΅œμ ν™”',
FULLTEXT INDEX ft_ocr_text (ocr_text) WITH PARSER ngram
COMMENT 'ν•œκΈ€/영문 μ „λ¬Έ 검색 (n-gram νŒŒμ„œ μ‚¬μš©)'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='OCR μΆ”μΆœ ν…μŠ€νŠΈ';
-- ============================================================================
-- 7️⃣ AI_Descriptions Table (AI μ„€λͺ…)
-- ============================================================================
-- μ„€λͺ…: κ·Έλ¦Ό/ν‘œμ— λŒ€ν•œ AI 생성 μ„€λͺ… (GPT-4o-mini)
-- μ£Όμš” κΈ°λŠ₯: μ‹œκ° 자료 ν…μŠ€νŠΈ μ„€λͺ…, ν”„λ‘¬ν”„νŠΈ 이λ ₯ 관리
-- 관계: layout_elements와 1:1 관계
-- ============================================================================
CREATE TABLE ai_descriptions (
-- κΈ°λ³Έ 정보
ai_desc_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'AI μ„€λͺ… 고유 ID',
element_id INT NOT NULL COMMENT 'λ ˆμ΄μ•„μ›ƒ μš”μ†Œ ID (1:1 λ§€ν•‘, FK: layout_elements, ON DELETE CASCADE)',
-- AI 생성 κ²°κ³Ό
description TEXT NOT NULL COMMENT 'AIκ°€ μƒμ„±ν•œ μ„€λͺ… ν…μŠ€νŠΈ',
ai_model VARCHAR(100) DEFAULT 'gpt-4o-mini' COMMENT 'μ‚¬μš©ν•œ AI λͺ¨λΈλͺ…',
prompt_used TEXT DEFAULT NULL COMMENT 'μ‚¬μš©ν•œ ν”„λ‘¬ν”„νŠΈ (λ””λ²„κΉ…μš©)',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_ai_descriptions_element
FOREIGN KEY (element_id) REFERENCES layout_elements(element_id)
ON DELETE CASCADE,
-- μ œμ•½μ‘°κ±΄ 및 인덱슀
UNIQUE KEY uk_element (element_id) COMMENT '1:1 관계 보μž₯ (쀑볡 λ°©μ§€)',
INDEX idx_ai_model (ai_model) COMMENT 'λͺ¨λΈλ³„ 필터링 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='κ·Έλ¦Ό/ν‘œμ— λŒ€ν•œ AI 생성 μ„€λͺ…';
-- ============================================================================
-- 8️⃣ Question_Groups Table (문제 κ·Έλ£Ή) [μˆ˜μ •]
-- ============================================================================
-- μ„€λͺ…: λ¬Έμ œμ§€μ—μ„œ κ°μ§€λœ 문제 λ‹¨μœ„ (액컀 μš”μ†Œ κΈ°μ€€)
-- μ£Όμš” κΈ°λŠ₯: 액컀 μš”μ†Œ 관리, Yμ’Œν‘œ λ²”μœ„ μ €μž₯, μš”μ†Œ 카운트
-- 관계: pages와 1:N, layout_elements와 1:1 (액컀)
-- [v2 λ³€κ²½] question_number μ‚­μ œ, anchor_element_id μΆ”κ°€
-- ============================================================================
CREATE TABLE question_groups (
-- κΈ°λ³Έ 정보
question_group_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '문제 그룹 고유 ID',
page_id INT NOT NULL COMMENT 'μ†Œμ† νŽ˜μ΄μ§€ ID (FK: pages, ON DELETE CASCADE)',
-- [v2 μΆ”κ°€] 액컀 μš”μ†Œ μ°Έμ‘°
anchor_element_id INT NOT NULL COMMENT '액컀 μš”μ†Œ ID (FK: layout_elements, ON DELETE CASCADE)',
-- Yμ’Œν‘œ λ²”μœ„ (액컀 Y μ’Œν‘œ ~ λ‹€μŒ 액컀 직전)
start_y INT NOT NULL COMMENT '문제 μ‹œμž‘ Yμ’Œν‘œ',
end_y INT NOT NULL COMMENT '문제 μ’…λ£Œ Yμ’Œν‘œ',
-- 톡계 정보
element_count INT DEFAULT 0 COMMENT 'λ¬Έμ œμ— μ†ν•œ μš”μ†Œ 개수 (μžμ‹ μš”μ†Œ 수)',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_question_groups_page
FOREIGN KEY (page_id) REFERENCES pages(page_id)
ON DELETE CASCADE,
-- [v2 μΆ”κ°€] 액컀 μš”μ†Œμ™€ 1:1 관계
CONSTRAINT fk_question_groups_anchor
FOREIGN KEY (anchor_element_id) REFERENCES layout_elements(element_id)
ON DELETE CASCADE,
-- μ œμ•½μ‘°κ±΄ 및 인덱슀
-- [v2 μˆ˜μ •] anchor_element_idλŠ” μœ λ‹ˆν¬ (ν•˜λ‚˜μ˜ μ•΅μ»€λŠ” ν•˜λ‚˜μ˜ 그룹만 생성)
UNIQUE KEY uk_anchor_element (anchor_element_id)
COMMENT '액컀 μš”μ†Œ 쀑볡 λ°©μ§€ (1:1 관계)',
INDEX idx_page_id (page_id) COMMENT 'νŽ˜μ΄μ§€λ³„ 문제 쑰회 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='문제 κ·Έλ£Ή - v2: 액컀 μš”μ†Œ κΈ°μ€€, question_number μ‚­μ œ';
-- ============================================================================
-- 9️⃣ Question_Elements Table (문제-μš”μ†Œ λ§€ν•‘)
-- ============================================================================
-- μ„€λͺ…: 문제 κ·Έλ£Ήκ³Ό μžμ‹ μš”μ†Œμ˜ λ§€ν•‘ ν…Œμ΄λΈ”
-- μ£Όμš” κΈ°λŠ₯: λ¬Έμ œλ³„ μžμ‹ μš”μ†Œ κ·Έλ£Ήν•‘, μˆœμ„œ 관리
-- 관계: question_groups (1:N) β†’ question_elements β†’ (N:1) layout_elements
-- ============================================================================
CREATE TABLE question_elements (
-- κΈ°λ³Έ 정보
qe_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'λ§€ν•‘ λ ˆμ½”λ“œ 고유 ID',
question_group_id INT NOT NULL COMMENT '문제 그룹 ID (FK: question_groups, ON DELETE CASCADE)',
element_id INT NOT NULL COMMENT 'μžμ‹ μš”μ†Œ ID (FK: layout_elements, ON DELETE CASCADE)',
-- μˆœμ„œ 정보
order_in_question INT NOT NULL COMMENT '문제 λ‚΄ μš”μ†Œ μˆœμ„œ (1, 2, 3, ...) - Yμ’Œν‘œ κΈ°μ€€ μžλ™ μ •λ ¬',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_question_elements_group
FOREIGN KEY (question_group_id) REFERENCES question_groups(question_group_id)
ON DELETE CASCADE,
CONSTRAINT fk_question_elements_element
FOREIGN KEY (element_id) REFERENCES layout_elements(element_id)
ON DELETE CASCADE,
-- μ œμ•½μ‘°κ±΄ 및 인덱슀
UNIQUE KEY uk_question_element (question_group_id, element_id)
COMMENT '문제-μš”μ†Œ 쀑볡 λ§€ν•‘ λ°©μ§€',
INDEX idx_order (question_group_id, order_in_question)
COMMENT 'μˆœμ„œλ³„ μ •λ ¬ μ΅œμ ν™” (볡합 인덱슀)'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='문제-μš”μ†Œ λ§€ν•‘ ν…Œμ΄λΈ” (μžμ‹ μš”μ†Œ 관리)';
-- ============================================================================
-- πŸ”Ÿ Text_Versions Table (ν…μŠ€νŠΈ 버전 관리)
-- ============================================================================
-- μ„€λͺ…: νŽ˜μ΄μ§€λ³„ ν…μŠ€νŠΈ 버전 이λ ₯ (원본/μžλ™ν¬λ§·/μ‚¬μš©μžμˆ˜μ •)
-- μ£Όμš” κΈ°λŠ₯: 버전 관리, μˆ˜μ • 이λ ₯ 좔적, ν˜„μž¬ 버전 ν”Œλž˜κ·Έ
-- 관계: pages와 1:N 관계
-- ============================================================================
CREATE TABLE text_versions (
-- κΈ°λ³Έ 정보
version_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '버전 고유 ID',
page_id INT NOT NULL COMMENT 'μ†Œμ† νŽ˜μ΄μ§€ ID (FK: pages, ON DELETE CASCADE)',
user_id INT DEFAULT NULL COMMENT 'μˆ˜μ •ν•œ μ‚¬μš©μž ID (μ‚¬μš©μž μˆ˜μ • μ‹œ, FK: users, ON DELETE SET NULL)',
-- 버전 정보
content TEXT NOT NULL COMMENT 'ν…μŠ€νŠΈ λ‚΄μš©',
version_number INT NOT NULL COMMENT '버전 번호 (1, 2, 3, ...)',
version_type ENUM('original', 'auto_formatted', 'user_edited') NOT NULL
COMMENT '버전 μœ ν˜•: original(원본), auto_formatted(μžλ™ν¬λ§·), user_edited(μ‚¬μš©μžμˆ˜μ •)',
-- μƒνƒœ ν”Œλž˜κ·Έ
is_current BOOLEAN DEFAULT FALSE COMMENT 'ν˜„μž¬ 버전 μ—¬λΆ€ (TRUE: ν˜„μž¬ 버전)',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '버전 생성일',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_text_versions_page
FOREIGN KEY (page_id) REFERENCES pages(page_id)
ON DELETE CASCADE,
CONSTRAINT fk_text_versions_user
FOREIGN KEY (user_id) REFERENCES users(user_id)
ON DELETE SET NULL,
-- μ œμ•½μ‘°κ±΄ 및 인덱슀
UNIQUE KEY uk_page_version (page_id, version_number)
COMMENT 'νŽ˜μ΄μ§€ λ‚΄ 버전 번호 쀑볡 λ°©μ§€',
INDEX idx_page_id (page_id) COMMENT 'νŽ˜μ΄μ§€λ³„ 버전 쑰회 μ΅œμ ν™”',
INDEX idx_is_current (is_current) COMMENT 'ν˜„μž¬ 버전 λΉ λ₯Έ 쑰회'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='νŽ˜μ΄μ§€λ³„ ν…μŠ€νŠΈ 버전 관리';
-- ============================================================================
-- 1️⃣1️⃣ Formatting_Rules Table (ν¬λ§·νŒ… κ·œμΉ™) [μˆ˜μ •]
-- ============================================================================
-- μ„€λͺ…: λ¬Έμ„œ νƒ€μž…λ³„ ν΄λž˜μŠ€λ³„ ν¬λ§·νŒ… κ·œμΉ™ (접두사/접미사/λ“€μ—¬μ“°κΈ°)
-- μ£Όμš” κΈ°λŠ₯: μžλ™ ν¬λ§·νŒ… κ·œμΉ™ 관리, 동적 κ·œμΉ™ λ³€κ²½
-- 관계: document_types와 1:N 관계
-- [v2 λ³€κ²½] 액컀/μžμ‹ 클래슀 κ·œμΉ™ μΆ”κ°€ (Initial Data μ°Έμ‘°)
-- ============================================================================
CREATE TABLE formatting_rules (
-- κΈ°λ³Έ 정보
rule_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'κ·œμΉ™ 고유 ID',
doc_type_id INT NOT NULL COMMENT 'λ¬Έμ„œ νƒ€μž… ID (FK: document_types, ON DELETE CASCADE)',
class_name VARCHAR(100) NOT NULL COMMENT '적용 클래슀λͺ… (question_number/figure/text λ“±)',
-- ν¬λ§·νŒ… μ„€μ •
prefix VARCHAR(50) DEFAULT '' COMMENT '접두사 (예: "\\n\\n", " ")',
suffix VARCHAR(50) DEFAULT '' COMMENT '접미사 (예: ". ", "\\n")',
indent_level INT DEFAULT 0 COMMENT 'λ“€μ—¬μ“°κΈ° 레벨 (0~10)',
-- μŠ€νƒ€μΌ μ„€μ • (선택 사항)
font_size VARCHAR(20) DEFAULT NULL COMMENT '폰트 크기 (예: "14pt")',
font_weight VARCHAR(20) DEFAULT NULL COMMENT '폰트 λ‘κ»˜ (예: "bold")',
-- νƒ€μž„μŠ€νƒ¬ν”„
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'κ·œμΉ™ 생성일',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'κ·œμΉ™ μˆ˜μ •μΌ',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_formatting_rules_doctype
FOREIGN KEY (doc_type_id) REFERENCES document_types(doc_type_id)
ON DELETE CASCADE,
-- μ œμ•½μ‘°κ±΄ 및 인덱슀
UNIQUE KEY uk_type_class (doc_type_id, class_name)
COMMENT 'νƒ€μž…λ³„ 클래슀 κ·œμΉ™ 쀑볡 λ°©μ§€',
INDEX idx_doc_type_id (doc_type_id) COMMENT 'νƒ€μž…λ³„ κ·œμΉ™ 쑰회 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='λ¬Έμ„œ νƒ€μž…λ³„ ν¬λ§·νŒ… κ·œμΉ™ - v2: 액컀/μžμ‹ 클래슀 κ·œμΉ™ μΆ”κ°€';
-- ============================================================================
-- 1️⃣2️⃣ Combined_Results Table (톡합 λ¬Έμ„œ μΊμ‹œ)
-- ============================================================================
-- μ„€λͺ…: ν”„λ‘œμ νŠΈμ˜ λͺ¨λ“  νŽ˜μ΄μ§€λ₯Ό ν†΅ν•©ν•œ μ΅œμ’… κ²°κ³Ό μΊμ‹œ
-- μ£Όμš” κΈ°λŠ₯: 톡합 ν…μŠ€νŠΈ μ €μž₯, 톡계 정보, λ‹€μš΄λ‘œλ“œ μ΅œμ ν™”
-- 관계: projects와 1:1 관계
-- ============================================================================
CREATE TABLE combined_results (
-- κΈ°λ³Έ 정보
combined_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '톡합 κ²°κ³Ό 고유 ID',
project_id INT NOT NULL COMMENT 'ν”„λ‘œμ νŠΈ ID (1:1 λ§€ν•‘, FK: projects, ON DELETE CASCADE)',
-- 톡합 κ²°κ³Ό
combined_text LONGTEXT NOT NULL COMMENT 'ν†΅ν•©λœ 전체 ν…μŠ€νŠΈ (νŽ˜μ΄μ§€λ³„ κ²°κ³Ό ν•©μΉ¨)',
combined_stats JSON DEFAULT NULL COMMENT '톡계 정보 (JSON ν˜•μ‹: νŽ˜μ΄μ§€μˆ˜, λ‹¨μ–΄μˆ˜, 문제수 λ“±)',
-- νƒ€μž„μŠ€νƒ¬ν”„
generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '졜초 생성일',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'λ§ˆμ§€λ§‰ μ—…λ°μ΄νŠΈμΌ',
-- μ™Έλž˜ν‚€
CONSTRAINT fk_combined_results_project
FOREIGN KEY (project_id) REFERENCES projects(project_id)
ON DELETE CASCADE,
-- μ œμ•½μ‘°κ±΄ 및 인덱슀
UNIQUE KEY uk_project (project_id) COMMENT '1:1 관계 보μž₯ (ν”„λ‘œμ νŠΈλ‹Ή 1개 μΊμ‹œ)',
INDEX idx_project_id (project_id) COMMENT 'ν”„λ‘œμ νŠΈλ³„ μΊμ‹œ 쑰회 μ΅œμ ν™”'
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='ν”„λ‘œμ νŠΈ 톡합 λ¬Έμ„œ μΊμ‹œ';
-- ============================================================================
-- πŸ”§ Triggers (트리거)
-- ============================================================================
-- 트리거 1: νŽ˜μ΄μ§€ μΆ”κ°€ μ‹œ projects.total_pages μžλ™ 증가
-- ============================================================================
DELIMITER //
CREATE TRIGGER trg_update_total_pages
AFTER INSERT ON pages
FOR EACH ROW
BEGIN
-- μƒˆ νŽ˜μ΄μ§€κ°€ μΆ”κ°€λ˜λ©΄ ν•΄λ‹Ή ν”„λ‘œμ νŠΈμ˜ total_pagesλ₯Ό 1 증가
UPDATE projects
SET total_pages = (
SELECT COUNT(*)
FROM pages
WHERE project_id = NEW.project_id
)
WHERE project_id = NEW.project_id;
END//
DELIMITER ;
-- ============================================================================
-- 트리거 2: νŽ˜μ΄μ§€ μ‚­μ œ μ‹œ projects.total_pages μžλ™ κ°μ†Œ
-- ============================================================================
DELIMITER //
CREATE TRIGGER trg_update_total_pages_on_delete
AFTER DELETE ON pages
FOR EACH ROW
BEGIN
-- νŽ˜μ΄μ§€κ°€ μ‚­μ œλ˜λ©΄ ν•΄λ‹Ή ν”„λ‘œμ νŠΈμ˜ total_pagesλ₯Ό μž¬κ³„μ‚°
UPDATE projects
SET total_pages = (
SELECT COUNT(*)
FROM pages
WHERE project_id = OLD.project_id
)
WHERE project_id = OLD.project_id;
END//
DELIMITER ;
-- ============================================================================
-- πŸ“Š Initial Data (초기 데이터) [v2 μˆ˜μ •]
-- ============================================================================
-- μ‹œμŠ€ν…œ κΈ°λ³Έ μ„€μ • 데이터 μ‚½μž…
-- ============================================================================
-- 1. Document Types (λ¬Έμ„œ νƒ€μž… 2개) [μˆ˜μ •]
INSERT INTO document_types (type_name, model_name, sorting_method, description) VALUES
('worksheet', 'SmartEyeSsen', 'question_based', 'μ‹œν—˜ λ¬Έμ œμ§€ - 액컀/μžμ‹ μž¬κ·€ μ •λ ¬ (question_type, question_number κΈ°μ€€)'),
('document', 'DocLayout-YOLO', 'reading_order', '일반 λ¬Έμ„œ - Y/X μ’Œν‘œ κΈ°μ€€ 순차 μ •λ ¬');
-- 2. Formatting Rules - worksheet (λ¬Έμ œμ§€) [v2 μˆ˜μ •]
-- 액컀 클래슀 (Anchors): 그룹을 μƒμ„±ν•˜λŠ” μš”μ†Œ
INSERT INTO formatting_rules (doc_type_id, class_name, prefix, suffix, indent_level, font_size, font_weight) VALUES
-- 액컀 1: 단원/문제 μœ ν˜• (question_type, unit)
(1, 'question_type', '\n\n[', ']\n', 0, '14pt', 'bold'),
(1, 'unit', '\n\n', '\n', 0, '14pt', 'bold'),
-- 액컀 2: λŒ€λ¬Έμ œ 번호 (question_number)
(1, 'question_number', '\n\n', '. ', 0, '14pt', 'bold'),
-- 액컀 3: μ†Œλ¬Έμ œ 번호 (second_question_number)
(1, 'second_question_number', '\n (', ') ', 3, NULL, NULL),
-- 액컀 4: ν•˜μœ„ μ†Œλ¬Έμ œ 번호 (third_question_number, μžˆμ„ 경우)
(1, 'third_question_number', '\n ', '. ', 6, NULL, NULL);
-- μžμ‹ 클래슀 (Children): 액컀에 μ†ν•˜λŠ” μš”μ†Œ
INSERT INTO formatting_rules (doc_type_id, class_name, prefix, suffix, indent_level, font_size, font_weight) VALUES
-- μžμ‹ 1: 문제 λ³Έλ¬Έ
(1, 'question_text', ' ', '\n', 3, NULL, NULL),
-- μžμ‹ 2: λͺ©λ‘
(1, 'list', ' - ', '\n', 3, NULL, NULL),
-- μžμ‹ 3: 선택지
(1, 'choices', ' ', '\n', 3, NULL, NULL),
-- μžμ‹ 4: κ΄„ν˜Έ 빈칸
(1, 'parenthesis_blank', ' ( )', '\n', 3, NULL, NULL),
-- μžμ‹ 5: 밑쀄 빈칸
(1, 'underline_blank', ' __________', '\n', 3, NULL, NULL),
-- μžμ‹ 6: κ·Έλ¦Ό
(1, 'figure', '\n [κ·Έλ¦Ό μ„€λͺ…]\n ', '\n', 3, NULL, NULL),
-- μžμ‹ 7: ν‘œ
(1, 'table', '\n [ν‘œ μ„€λͺ…]\n ', '\n', 3, NULL, NULL),
-- μžμ‹ 8: μˆœμ„œλ„
(1, 'flowchart', '\n [μˆœμ„œλ„ μ„€λͺ…]\n ', '\n', 3, NULL, NULL),
-- μžμ‹ 9: μˆ˜μ‹
(1, 'equation', ' ', '\n', 3, NULL, NULL),
-- μžμ‹ 10: μΊ‘μ…˜
(1, 'caption', ' ', '\n', 3, '10pt', NULL),
-- μžμ‹ 11: 각주
(1, 'footnote', '\n * ', '\n', 3, '9pt', NULL),
-- 특수: 제λͺ© (νŽ˜μ΄μ§€ μ΅œμƒλ‹¨)
(1, 'title', '', '\n\n', 0, '16pt', 'bold'),
-- 특수: νŽ˜μ΄μ§€ 번호 (νŽ˜μ΄μ§€ μ΅œν•˜λ‹¨)
(1, 'page', '\n\n─────────────────────\nνŽ˜μ΄μ§€ ', '\n─────────────────────\n\n', 0, '10pt', NULL);
-- 3. Formatting Rules - document (일반 λ¬Έμ„œ) [κΈ°μ‘΄ μœ μ§€]
INSERT INTO formatting_rules (doc_type_id, class_name, prefix, suffix, indent_level, font_size, font_weight) VALUES
-- 제λͺ©
(2, 'title', '', '\n\n', 0, '18pt', 'bold'),
-- μ†Œμ œλͺ©
(2, 'heading', '\n', '\n\n', 0, '16pt', 'bold'),
-- λ³Έλ¬Έ ν…μŠ€νŠΈ
(2, 'plain text', '', '\n\n', 0, NULL, NULL),
-- κ·Έλ¦Ό
(2, 'figure', '\n[κ·Έλ¦Ό ', ']\n\n', 0, NULL, NULL),
-- κ·Έλ¦Ό μΊ‘μ…˜
(2, 'figure_caption', '', '\n', 2, '10pt', NULL),
-- ν‘œ
(2, 'table', '\n[ν‘œ ', ']\n\n', 0, NULL, NULL),
-- ν‘œ μΊ‘μ…˜
(2, 'table_caption', '', '\n', 2, '10pt', NULL),
-- ν‘œ 각주
(2, 'table_footnote', '\n* ', '\n', 2, '9pt', NULL),
-- μˆ˜μ‹
(2, 'isolate_formula', '\n', '\n\n', 2, NULL, NULL),
-- μˆ˜μ‹ μΊ‘μ…˜
(2, 'formula_caption', '', '\n', 2, '10pt', NULL);
-- ============================================================================
-- πŸŽ‰ λ°μ΄ν„°λ² μ΄μŠ€ 생성 μ™„λ£Œ! (v2)
-- ============================================================================
-- πŸ“‹ λ‹€μŒ 단계:
-- 1. MySQL Workbenchμ—μ„œ erd_schema_v2.sql 파일 μ‹€ν–‰
-- 2. ν…Œμ΄λΈ” 생성 확인: SHOW TABLES;
-- 3. 초기 데이터 확인:
-- - SELECT * FROM document_types;
-- - SELECT * FROM formatting_rules WHERE doc_type_id = 1;
-- 4. λ°±μ—”λ“œ ORM μ—°κ²° (SQLAlchemy)
-- - question_groups.anchor_element_id FK μ„€μ • 확인
-- - layout_elements ↔ question_groups 관계 λ§€ν•‘
-- 5. 문제 λ ˆμ΄μ•„μ›ƒ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜ κ΅¬ν˜„
-- - services/sorting_service.py 생성
-- - 액컀 μš”μ†Œ 필터링 (question_type, question_number, ...)
-- - Yμ’Œν‘œ κΈ°μ€€ μž¬κ·€μ  λΆ„ν• 
-- - μžμ‹ μš”μ†Œ (Y,X) μ •λ ¬ 및 question_elements μ €μž₯
-- 6. API μ—”λ“œν¬μΈνŠΈ 개발
-- - POST /api/pages/{page_id}/sort
-- - GET /api/pages/{page_id}/sorted-result
-- 7. ν…ŒμŠ€νŠΈ
-- - 16νŽ˜μ΄μ§€ (section + question_number)
-- - 42νŽ˜μ΄μ§€ (question_number만)
-- - 14νŽ˜μ΄μ§€ (question_number + second_question_number)
-- ============================================================================
-- ============================================================================
-- πŸ” v2 μ£Όμš” 변경사항 μš”μ•½
-- ============================================================================
-- 1. document_types.sorting_method:
-- - 'coordinate_based' β†’ 'reading_order'둜 톡합
-- - 'question_based': 액컀/μžμ‹ μž¬κ·€ μ •λ ¬
-- - 'reading_order': Y/X μ’Œν‘œ 순차 μ •λ ¬
--
-- 2. layout_elements:
-- - order_index 컬럼 μ‚­μ œ
-- - (Y,X) μ’Œν‘œλ‘œ 동적 μ •λ ¬ (idx_position 인덱슀 ν™œμš©)
--
-- 3. question_groups:
-- - question_number 컬럼 μ‚­μ œ
-- - anchor_element_id 컬럼 μΆ”κ°€ (FK β†’ layout_elements)
-- - layout_elements와 1:1 액컀 관계 μ‹ μ„€
--
-- 4. formatting_rules:
-- - 액컀 클래슀 5개 μΆ”κ°€
-- (question_type, unit, question_number, second_question_number, third_question_number)
-- - μžμ‹ 클래슀 11개 μΆ”κ°€
-- (question_text, list, choices, parenthesis_blank, underline_blank,
-- figure, table, flowchart, equation, caption, footnote)
--
-- 5. 관계 λ³€κ²½:
-- - layout_elements ↔ question_groups: 1:1 액컀 관계 (μ‹ κ·œ)
-- - anchor_element_idλŠ” UNIQUE (ν•˜λ‚˜μ˜ 액컀 = ν•˜λ‚˜μ˜ κ·Έλ£Ή)
-- - ON DELETE CASCADE: 액컀 μ‚­μ œ μ‹œ κ·Έλ£Ή 및 question_elements 연쇄 μ‚­μ œ
--
-- ============================================================================