Files
FootPrint/app/schema.sql
T

222 lines
6.5 KiB
SQL

-- Footprints 数据库 schema(开发桥接与 Rust 后端共用同一份)
-- v2:加入多端同步地基(uuid v7 / 软删除墓碑 / HLC / 变更日志)
CREATE TABLE IF NOT EXISTS footprint_type (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
name TEXT NOT NULL UNIQUE,
icon TEXT NOT NULL DEFAULT 'tag',
color TEXT NOT NULL DEFAULT '#64748b',
sort INTEGER NOT NULL DEFAULT 100,
form_template TEXT NOT NULL DEFAULT '[]',
profile TEXT,
is_builtin INTEGER NOT NULL DEFAULT 0,
closed INTEGER NOT NULL DEFAULT 0,
deleted_at TEXT,
origin TEXT,
hlc TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS taxonomy (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
kind TEXT NOT NULL,
value TEXT NOT NULL,
color TEXT,
sort INTEGER NOT NULL DEFAULT 100,
is_builtin INTEGER NOT NULL DEFAULT 0,
deleted_at TEXT,
origin TEXT,
hlc TEXT,
UNIQUE(kind, value)
);
CREATE TABLE IF NOT EXISTS dimension (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
name TEXT NOT NULL UNIQUE,
color TEXT,
sort INTEGER NOT NULL DEFAULT 100,
deleted_at TEXT,
origin TEXT,
hlc TEXT
);
CREATE TABLE IF NOT EXISTS footprint (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
type_id INTEGER NOT NULL REFERENCES footprint_type(id),
title TEXT NOT NULL,
role TEXT,
level TEXT,
polarity TEXT NOT NULL DEFAULT 'positive',
award_grade TEXT,
rank INTEGER,
organizer TEXT,
start_date TEXT,
end_date TEXT,
status TEXT NOT NULL DEFAULT 'draft',
description TEXT NOT NULL DEFAULT '',
result TEXT NOT NULL DEFAULT '',
metrics TEXT NOT NULL DEFAULT '{}',
extras TEXT NOT NULL DEFAULT '{}',
group_id TEXT,
parent_id INTEGER,
tags TEXT NOT NULL DEFAULT '[]',
weight INTEGER NOT NULL DEFAULT 3,
search_text TEXT NOT NULL DEFAULT '',
deleted_at TEXT,
origin TEXT,
hlc TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now','localtime')),
updated_at TEXT NOT NULL DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS footprint_dimension (
footprint_id INTEGER NOT NULL REFERENCES footprint(id) ON DELETE CASCADE,
dimension_id INTEGER NOT NULL REFERENCES dimension(id) ON DELETE CASCADE,
PRIMARY KEY (footprint_id, dimension_id)
);
CREATE TABLE IF NOT EXISTS attachment (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
owner_type TEXT NOT NULL DEFAULT 'footprint',
owner_id INTEGER NOT NULL,
kind TEXT,
file_name TEXT NOT NULL,
mime TEXT,
size INTEGER NOT NULL DEFAULT 0,
rel_path TEXT NOT NULL,
sha256 TEXT,
thumb_rel_path TEXT,
remote_url TEXT,
remote_thumb TEXT,
remote_key TEXT,
remote_thumb_key TEXT,
taken_at TEXT,
deleted_at TEXT,
origin TEXT,
hlc TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS semester (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
term TEXT NOT NULL UNIQUE,
gpa REAL,
rank INTEGER,
total_students INTEGER,
deleted_at TEXT,
origin TEXT,
hlc TEXT
);
CREATE TABLE IF NOT EXISTS course_grade (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
semester_id INTEGER NOT NULL REFERENCES semester(id) ON DELETE CASCADE,
course_name TEXT NOT NULL,
credit REAL NOT NULL DEFAULT 0,
score REAL,
grade_point REAL,
deleted_at TEXT,
origin TEXT,
hlc TEXT
);
-- 成绩梯度(等级带):用户可配置;单科成绩按 min <= 分数 < max 定级(满分档闭区间)
CREATE TABLE IF NOT EXISTS grade_band (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
label TEXT NOT NULL,
color TEXT NOT NULL DEFAULT '#64748b',
min REAL NOT NULL DEFAULT 0,
max REAL NOT NULL DEFAULT 100,
sort INTEGER NOT NULL DEFAULT 100,
deleted_at TEXT,
origin TEXT,
hlc TEXT
);
CREATE TABLE IF NOT EXISTS metric_def (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
key TEXT NOT NULL UNIQUE,
label TEXT NOT NULL,
unit TEXT,
agg TEXT NOT NULL DEFAULT 'sum',
section TEXT,
deleted_at TEXT,
origin TEXT,
hlc TEXT
);
CREATE TABLE IF NOT EXISTS saved_view (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uuid TEXT,
name TEXT NOT NULL UNIQUE,
filter TEXT NOT NULL DEFAULT '{}',
sort TEXT NOT NULL DEFAULT '{}',
is_default INTEGER NOT NULL DEFAULT 0,
deleted_at TEXT,
origin TEXT,
hlc TEXT,
updated_at TEXT NOT NULL DEFAULT (datetime('now','localtime'))
);
-- ===== 同步基础设施 =====
CREATE TABLE IF NOT EXISTS sync_state (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS sync_oplog (
id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name TEXT NOT NULL,
row_uuid TEXT NOT NULL,
op TEXT NOT NULL,
hlc TEXT NOT NULL,
synced_at TEXT
);
CREATE VIRTUAL TABLE IF NOT EXISTS footprint_fts USING fts5(
search_text,
content='footprint',
content_rowid='id',
tokenize='trigram'
);
CREATE TRIGGER IF NOT EXISTS footprint_ai AFTER INSERT ON footprint BEGIN
INSERT INTO footprint_fts(rowid, search_text) VALUES (new.id, new.search_text);
END;
CREATE TRIGGER IF NOT EXISTS footprint_ad AFTER DELETE ON footprint BEGIN
INSERT INTO footprint_fts(footprint_fts, rowid, search_text)
VALUES ('delete', old.id, old.search_text);
END;
CREATE TRIGGER IF NOT EXISTS footprint_au AFTER UPDATE OF search_text ON footprint BEGIN
INSERT INTO footprint_fts(footprint_fts, rowid, search_text)
VALUES ('delete', old.id, old.search_text);
INSERT INTO footprint_fts(rowid, search_text) VALUES (new.id, new.search_text);
END;
CREATE INDEX IF NOT EXISTS idx_footprint_start ON footprint(start_date);
CREATE INDEX IF NOT EXISTS idx_footprint_type ON footprint(type_id);
CREATE INDEX IF NOT EXISTS idx_footprint_group ON footprint(group_id);
CREATE INDEX IF NOT EXISTS idx_footprint_hlc ON footprint(hlc);
CREATE INDEX IF NOT EXISTS idx_att_owner ON attachment(owner_type, owner_id);
CREATE INDEX IF NOT EXISTS idx_oplog_pending ON sync_oplog(synced_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_footprint_uuid ON footprint(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_footprint_type_uuid ON footprint_type(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_taxonomy_uuid ON taxonomy(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_dimension_uuid ON dimension(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_attachment_uuid ON attachment(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_semester_uuid ON semester(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_course_grade_uuid ON course_grade(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_metric_def_uuid ON metric_def(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_saved_view_uuid ON saved_view(uuid);
CREATE UNIQUE INDEX IF NOT EXISTS idx_grade_band_uuid ON grade_band(uuid);