Files
PastpaperMaster/supabase/migrations/001_init_schema.sql
Zhao 7a09167261 Initial commit: PastPaper Master full stack
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 12:27:47 +07:00

208 lines
7.2 KiB
PL/PgSQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================
-- PastPaper Master — 初始数据库 Schema
-- Version: 001
-- Date: 2025-03-11
-- ============================================
-- 启用必要的扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- ============================================
-- Table 1: papers — 上传的试卷
-- ============================================
CREATE TABLE papers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
-- 元信息(用户上传时填写)
course_code TEXT NOT NULL, -- "COMP2011"
year INTEGER NOT NULL, -- 2024
term TEXT NOT NULL CHECK (term IN ('fall', 'spring', 'summer')),
exam_type TEXT NOT NULL CHECK (exam_type IN ('midterm', 'final', 'quiz')),
-- 文件 (Supabase Storage)
paper_file_url TEXT NOT NULL, -- 试卷 PDF
answer_file_url TEXT, -- 答案 PDF可选
-- 处理状态
status TEXT NOT NULL DEFAULT 'uploaded'
CHECK (status IN ('uploaded', 'processing', 'ready', 'error')),
error_message TEXT, -- 处理失败时的错误信息
-- 提取的原始文本(缓存)
paper_extracted_text TEXT,
answer_extracted_text TEXT,
-- 整卷概览AI 生成)
total_score INTEGER,
question_count INTEGER,
topics_summary JSONB, -- {"Linked List": 40, "Recursion": 30}
difficulty_level TEXT CHECK (difficulty_level IN ('easy', 'medium', 'hard')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ============================================
-- Table 2: paper_questions — 逐题数据
-- ============================================
CREATE TABLE paper_questions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
paper_id UUID NOT NULL REFERENCES papers(id) ON DELETE CASCADE,
-- 题目标识
question_number TEXT NOT NULL, -- "1", "1a", "2b"
parent_question TEXT, -- 子题的父题号: "1a" → "1"
display_order INTEGER NOT NULL, -- 显示顺序
-- 题目内容
question_type TEXT NOT NULL
CHECK (question_type IN ('mc', 'fill_blank', 'long_question')),
question_text TEXT NOT NULL, -- 题目原文
score INTEGER, -- 分值
page_number INTEGER, -- PDF 页码(左右联动)
-- 选择题专用
options JSONB, -- [{"label":"A","text":"..."},...]
correct_option TEXT, -- "B"
-- 填空题专用
correct_answer TEXT, -- 正确答案
accept_variants TEXT[], -- 等价表达 ["O(nlogn)","O(n log n)"]
-- 答案 PDF 提取的原始答案(所有题型)
raw_answer_text TEXT,
-- 知识点标签
topics TEXT[], -- ["Linked List","Pointer"]
difficulty TEXT CHECK (difficulty IN ('easy', 'medium', 'hard')),
-- AI 三件套HTML + KaTeX
knowledge_reminder TEXT, -- 知识点 Reminder
ai_hint TEXT, -- AI Hint
solution TEXT, -- Solution逐步 derivation
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ============================================
-- Table 3: user_attempts — 用户答题记录
-- Phase 4 实现,先建好表结构
-- ============================================
CREATE TABLE user_attempts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
question_id UUID NOT NULL REFERENCES paper_questions(id) ON DELETE CASCADE,
-- 用户的作答
attempt_type TEXT NOT NULL
CHECK (attempt_type IN ('select', 'input', 'photo')),
user_answer TEXT, -- 选项 / 输入的答案
photo_url TEXT, -- 上传的照片
photo_ocr_text TEXT, -- OCR 识别结果
-- AI 判定
is_correct BOOLEAN,
feedback TEXT, -- HTML — 逐步错误分析
error_at_step INTEGER, -- 第几步开始错
-- 错题本
in_error_book BOOLEAN NOT NULL DEFAULT false,
mastered BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ============================================
-- 索引
-- ============================================
CREATE INDEX idx_papers_user ON papers(user_id);
CREATE INDEX idx_papers_course ON papers(course_code);
CREATE INDEX idx_papers_status ON papers(status);
CREATE INDEX idx_questions_paper ON paper_questions(paper_id);
CREATE INDEX idx_questions_type ON paper_questions(question_type);
CREATE INDEX idx_questions_topics ON paper_questions USING GIN(topics);
CREATE INDEX idx_attempts_user ON user_attempts(user_id);
CREATE INDEX idx_attempts_question ON user_attempts(question_id);
CREATE INDEX idx_attempts_errorbook ON user_attempts(user_id)
WHERE in_error_book = true;
-- ============================================
-- RLS 策略
-- ============================================
ALTER TABLE papers ENABLE ROW LEVEL SECURITY;
ALTER TABLE paper_questions ENABLE ROW LEVEL SECURITY;
ALTER TABLE user_attempts ENABLE ROW LEVEL SECURITY;
-- papers: 用户只能看自己上传的(以后加公共库时再调整)
CREATE POLICY "Users can view own papers"
ON papers FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert own papers"
ON papers FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can update own papers"
ON papers FOR UPDATE
USING (auth.uid() = user_id);
CREATE POLICY "Users can delete own papers"
ON papers FOR DELETE
USING (auth.uid() = user_id);
-- paper_questions: 跟随 paper 的权限
CREATE POLICY "Users can view questions of own papers"
ON paper_questions FOR SELECT
USING (
EXISTS (
SELECT 1 FROM papers
WHERE papers.id = paper_questions.paper_id
AND papers.user_id = auth.uid()
)
);
-- service_role 用于后端写入 questions处理管线用
-- 前端不直接写 questions通过 API 触发后端处理
-- user_attempts: 用户只能看/写自己的
CREATE POLICY "Users can view own attempts"
ON user_attempts FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert own attempts"
ON user_attempts FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can update own attempts"
ON user_attempts FOR UPDATE
USING (auth.uid() = user_id);
-- ============================================
-- updated_at 自动更新触发器
-- ============================================
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER papers_updated_at
BEFORE UPDATE ON papers
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER questions_updated_at
BEFORE UPDATE ON paper_questions
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- ============================================
-- Storage bucket
-- ============================================
-- 在 Supabase Dashboard 中手动创建 bucket: "papers"
-- 或通过 API 创建(后端初始化时处理)