桌面应用(Tauri+React+SQLite):时间线/记录/学年学科成绩梯度/统计/类型与字段模板/附件内容寻址/详情视图/PWA 同步服务端(sync-server):多用户+邀请码/LWW推拉/seq游标/附件去重/Docker部署 多端同步地基:uuid v7/HLC混合逻辑时钟/软删除墓碑/sync_oplog,Node与Rust双端一致并通过自动化测试
216 lines
6.4 KiB
SQL
216 lines
6.4 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 '[]',
|
|
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,
|
|
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);
|