feat(schema): v19 卡片/分组组合表与迁移回填
- 新增 card / card_group:卡片只存标题与顺序,内容由勾选分组组成,支持混合格式 - v18→v19 迁移:按现有 section_block 回填卡片与分组勾选(card-<section>) - 新库 seed 完成后同样回填,旧界面暂不受影响 - 测试 59 passed(含 15→19 迁移)
This commit is contained in:
+2
-1
@@ -600,8 +600,9 @@ mod tests {
|
|||||||
let fresh = Connection::open_in_memory().expect("内存库应能打开");
|
let fresh = Connection::open_in_memory().expect("内存库应能打开");
|
||||||
fresh
|
fresh
|
||||||
.execute_batch(
|
.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 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;",
|
PRAGMA user_version = 15;",
|
||||||
)
|
)
|
||||||
.expect("应能造出 v15 老库");
|
.expect("应能造出 v15 老库");
|
||||||
|
|||||||
+66
-1
@@ -141,6 +141,44 @@ CREATE TABLE IF NOT EXISTS audit (
|
|||||||
target TEXT,
|
target TEXT,
|
||||||
detail 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`(乐观锁),新增审计表。
|
/// 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)。
|
/// v15 → v16 的增量(老库升级用;新库直接走 SCHEMA_SQL)。
|
||||||
const V16_SQL: &str = r#"
|
const V16_SQL: &str = r#"
|
||||||
CREATE TABLE IF NOT EXISTS admin (
|
CREATE TABLE IF NOT EXISTS admin (
|
||||||
@@ -213,7 +269,7 @@ pub struct SeedReport {
|
|||||||
///
|
///
|
||||||
/// 从 v16 起有了第一套迁移:`migrate` 会把老库逐级升上来,
|
/// 从 v16 起有了第一套迁移:`migrate` 会把老库逐级升上来,
|
||||||
/// 只有「比程序更新」或「没有迁移路径」的版本才拒绝启动。
|
/// 只有「比程序更新」或「没有迁移路径」的版本才拒绝启动。
|
||||||
pub const SCHEMA_VERSION: i64 = 18;
|
pub const SCHEMA_VERSION: i64 = 19;
|
||||||
|
|
||||||
fn current_version(conn: &Connection) -> Result<i64, StashError> {
|
fn current_version(conn: &Connection) -> Result<i64, StashError> {
|
||||||
conn.pragma_query_value(None, "user_version", |row| row.get(0))
|
conn.pragma_query_value(None, "user_version", |row| row.get(0))
|
||||||
@@ -262,6 +318,12 @@ pub fn migrate(conn: &Connection) -> Result<i64, StashError> {
|
|||||||
version = 18;
|
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)
|
conn.pragma_update(None, "user_version", version)
|
||||||
.map_err(map_error)?;
|
.map_err(map_error)?;
|
||||||
|
|
||||||
@@ -371,6 +433,9 @@ pub fn seed(conn: &mut Connection) -> Result<SeedReport, StashError> {
|
|||||||
report.blocks += 1;
|
report.blocks += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 老结构(分区 → 展示块)同步生成卡片,保证新首页立刻有内容
|
||||||
|
tx.execute_batch(BACKFILL_CARDS_SQL).map_err(map_error)?;
|
||||||
|
|
||||||
tx.commit().map_err(map_error)?;
|
tx.commit().map_err(map_error)?;
|
||||||
|
|
||||||
Ok(report)
|
Ok(report)
|
||||||
|
|||||||
Reference in New Issue
Block a user