From eb4fded8dbb879b59984c03477fe84128d9d6f08 Mon Sep 17 00:00:00 2001 From: CNWei Date: Fri, 30 Oct 2026 20:32:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(schema):=20v19=20=E5=8D=A1=E7=89=87/?= =?UTF-8?q?=E5=88=86=E7=BB=84=E7=BB=84=E5=90=88=E8=A1=A8=E4=B8=8E=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=9B=9E=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 card / card_group:卡片只存标题与顺序,内容由勾选分组组成,支持混合格式 - v18→v19 迁移:按现有 section_block 回填卡片与分组勾选(card-
) - 新库 seed 完成后同样回填,旧界面暂不受影响 - 测试 59 passed(含 15→19 迁移) --- src/db/mod.rs | 3 ++- src/db/schema.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/db/mod.rs b/src/db/mod.rs index 588f21d..f0f1bc8 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -600,8 +600,9 @@ mod tests { let fresh = Connection::open_in_memory().expect("内存库应能打开"); fresh .execute_batch( - "CREATE TABLE node (key TEXT PRIMARY KEY, parent_key TEXT, kind TEXT, view_mode TEXT); + "CREATE TABLE node (key TEXT PRIMARY KEY, parent_key TEXT, title TEXT, kind TEXT, view_mode TEXT); CREATE TABLE entry (key TEXT PRIMARY KEY, node_key TEXT NOT NULL, name TEXT NOT NULL); + CREATE TABLE section_block (id INTEGER PRIMARY KEY, node_key TEXT NOT NULL, title TEXT, preview_limit INTEGER, sort INTEGER NOT NULL DEFAULT 0); PRAGMA user_version = 15;", ) .expect("应能造出 v15 老库"); diff --git a/src/db/schema.rs b/src/db/schema.rs index 40b3a20..3614ed4 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -141,6 +141,44 @@ CREATE TABLE IF NOT EXISTS audit ( target TEXT, detail TEXT ); + +-- ========== v19:首页卡片(展示层组合) ========== + +-- 一张卡片 = 标题 + 顺序;内容由 card_group 勾选分组组成,卡片不拥有数据 +CREATE TABLE IF NOT EXISTS card ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + sort INTEGER NOT NULL DEFAULT 0 +); + +-- 卡片包含哪些分组(可混合格式),每个分组可单独设预览条数 +CREATE TABLE IF NOT EXISTS card_group ( + card_id INTEGER NOT NULL REFERENCES card(id) ON DELETE CASCADE, + group_key TEXT NOT NULL REFERENCES node(key) ON DELETE CASCADE ON UPDATE CASCADE, + sort INTEGER NOT NULL DEFAULT 0, + preview_limit INTEGER, + PRIMARY KEY (card_id, group_key) +); +"#; + +/// 把老的「分区 → 一个展示块」迁移成「卡片 → 多个分组」。 +const BACKFILL_CARDS_SQL: &str = r#" +INSERT OR IGNORE INTO card (key, title, sort) +SELECT 'card-' || COALESCE(p.key, n.key), + COALESCE(b.title, COALESCE(p.title, n.title)), + MIN(b.sort) +FROM section_block b +JOIN node n ON n.key = b.node_key +LEFT JOIN node p ON p.key = n.parent_key +GROUP BY COALESCE(p.key, n.key); + +INSERT OR IGNORE INTO card_group (card_id, group_key, sort, preview_limit) +SELECT c.id, b.node_key, b.sort, b.preview_limit +FROM section_block b +JOIN node n ON n.key = b.node_key +LEFT JOIN node p ON p.key = n.parent_key +JOIN card c ON c.key = 'card-' || COALESCE(p.key, n.key); "#; /// v16 → v17:条目加 `updated_at`(乐观锁),新增审计表。 @@ -172,6 +210,24 @@ WHERE kind = 'section' AND key IN ( ); "#; +/// v18 → v19:首页卡片改为「卡片 + 勾选分组」。 +const V19_SQL: &str = r#" +CREATE TABLE IF NOT EXISTS card ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + sort INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS card_group ( + card_id INTEGER NOT NULL REFERENCES card(id) ON DELETE CASCADE, + group_key TEXT NOT NULL REFERENCES node(key) ON DELETE CASCADE ON UPDATE CASCADE, + sort INTEGER NOT NULL DEFAULT 0, + preview_limit INTEGER, + PRIMARY KEY (card_id, group_key) +); +"#; + /// v15 → v16 的增量(老库升级用;新库直接走 SCHEMA_SQL)。 const V16_SQL: &str = r#" CREATE TABLE IF NOT EXISTS admin ( @@ -213,7 +269,7 @@ pub struct SeedReport { /// /// 从 v16 起有了第一套迁移:`migrate` 会把老库逐级升上来, /// 只有「比程序更新」或「没有迁移路径」的版本才拒绝启动。 -pub const SCHEMA_VERSION: i64 = 18; +pub const SCHEMA_VERSION: i64 = 19; fn current_version(conn: &Connection) -> Result { conn.pragma_query_value(None, "user_version", |row| row.get(0)) @@ -262,6 +318,12 @@ pub fn migrate(conn: &Connection) -> Result { version = 18; } + if version == 18 { + conn.execute_batch(V19_SQL).map_err(map_error)?; + conn.execute_batch(BACKFILL_CARDS_SQL).map_err(map_error)?; + version = 19; + } + conn.pragma_update(None, "user_version", version) .map_err(map_error)?; @@ -371,6 +433,9 @@ pub fn seed(conn: &mut Connection) -> Result { report.blocks += 1; } + // 老结构(分区 → 展示块)同步生成卡片,保证新首页立刻有内容 + tx.execute_batch(BACKFILL_CARDS_SQL).map_err(map_error)?; + tx.commit().map_err(map_error)?; Ok(report)