refactor(db): 删除旧分区展示块与兼容字段
- v23 迁移删除 section_block 表,以及 node 的 view_mode / empty_text / show_in_sidebar / allow_groups
- 重建 node 表并保留 44 条内容、15 个分组、5 张卡片,外键检查通过
- 种子库直接生成 card / card_group,不再依赖 section_block
- 删除旧 /api/catalog、/api/categories、/api/groups/{id} 与 categories 模块
- 分组接口不再接收/返回 format,前端 GroupNode 同步移除
- README/HANDOFF 更新到 v23;管理页提示改为『格式由每条内容决定』
This commit is contained in:
+16
-52
@@ -1,16 +1,14 @@
|
||||
//! 分组树的管理:多层分组、格式、父级移动与排序。
|
||||
//! 分组树的管理:多层分组、父级移动与排序。
|
||||
//!
|
||||
//! 数据层只有一张 `node` 树:顶层节点是「根分组」(`kind='section'`,带
|
||||
//! `view_mode` 格式),下面的节点都叫分组(`kind='group'`),可以继续嵌套。
|
||||
//! 条目挂在任意节点上,格式由最近的根分组决定。
|
||||
//! 数据层只有一张 `node` 树:顶层节点是「根分组」(`kind='section'`),
|
||||
//! 下面的节点都叫分组(`kind='group'`),可以继续嵌套。
|
||||
//! 条目的展示格式由 `entry.format` 决定,分组不再保存格式。
|
||||
|
||||
use rusqlite::{Connection, OptionalExtension, params};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{db::entries, db::map_error, error::StashError};
|
||||
|
||||
const FORMATS: [&str; 3] = ["apps", "posters", "articles"];
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NewNode {
|
||||
@@ -20,9 +18,6 @@ pub struct NewNode {
|
||||
/// 为空表示新建一个根分组
|
||||
#[serde(default)]
|
||||
pub parent_key: Option<String>,
|
||||
/// 根分组的格式:apps / posters / articles(子分组继承,不需要传)
|
||||
#[serde(default)]
|
||||
pub format: Option<String>,
|
||||
/// 图标:可以是 iconfont 名或字符;空字符串表示不显示
|
||||
#[serde(default)]
|
||||
pub icon: Option<String>,
|
||||
@@ -41,8 +36,6 @@ pub struct UpdateNode {
|
||||
/// 传入即移动到新的父节点;不支持移回根(根分组请直接新建)
|
||||
pub parent_key: Option<String>,
|
||||
pub sort: Option<i64>,
|
||||
/// 只对根分组生效
|
||||
pub format: Option<String>,
|
||||
/// 图标:空字符串表示清除图标
|
||||
pub icon: Option<String>,
|
||||
pub enabled: Option<bool>,
|
||||
@@ -78,7 +71,7 @@ pub fn create(conn: &mut Connection, new: NewNode) -> Result<(), StashError> {
|
||||
return Err(StashError::Conflict(format!("分组已存在: {key}")));
|
||||
}
|
||||
|
||||
let (kind, view_mode) = match new.parent_key.as_deref() {
|
||||
let kind = match new.parent_key.as_deref() {
|
||||
Some(parent) => {
|
||||
let parent_exists: i64 = conn
|
||||
.query_row("SELECT COUNT(*) FROM node WHERE key = ?1", [parent], |row| {
|
||||
@@ -88,15 +81,9 @@ pub fn create(conn: &mut Connection, new: NewNode) -> Result<(), StashError> {
|
||||
if parent_exists == 0 {
|
||||
return Err(StashError::NotFound(format!("父分组不存在: {parent}")));
|
||||
}
|
||||
("group", None)
|
||||
}
|
||||
None => {
|
||||
let format = new.format.unwrap_or_else(|| "apps".into());
|
||||
if !FORMATS.contains(&format.as_str()) {
|
||||
return Err(StashError::InvalidDirectory("格式不合法".into()));
|
||||
}
|
||||
("section", Some(format))
|
||||
"group"
|
||||
}
|
||||
None => "section",
|
||||
};
|
||||
|
||||
let default_sort: i64 = match new.parent_key.as_deref() {
|
||||
@@ -126,14 +113,13 @@ pub fn create(conn: &mut Connection, new: NewNode) -> Result<(), StashError> {
|
||||
|
||||
let tx = conn.transaction().map_err(map_error)?;
|
||||
tx.execute(
|
||||
"INSERT INTO node (key, parent_key, title, kind, view_mode, icon, enabled, sort, show_in_sidebar, allow_groups)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, 1, 1)",
|
||||
"INSERT INTO node (key, parent_key, title, kind, icon, enabled, sort)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||||
params![
|
||||
key,
|
||||
new.parent_key,
|
||||
title,
|
||||
kind,
|
||||
view_mode,
|
||||
icon,
|
||||
enabled,
|
||||
sort
|
||||
@@ -146,9 +132,9 @@ pub fn create(conn: &mut Connection, new: NewNode) -> Result<(), StashError> {
|
||||
}
|
||||
|
||||
pub fn update(conn: &mut Connection, key: &str, patch: UpdateNode) -> Result<(), StashError> {
|
||||
let current: Option<(String, Option<String>, Option<String>, i64, Option<String>, bool)> = conn
|
||||
let current: Option<(String, Option<String>, i64, Option<String>, bool)> = conn
|
||||
.query_row(
|
||||
"SELECT title, parent_key, view_mode, sort, icon, enabled FROM node WHERE key = ?1",
|
||||
"SELECT title, parent_key, sort, icon, enabled FROM node WHERE key = ?1",
|
||||
[key],
|
||||
|row| {
|
||||
Ok((
|
||||
@@ -157,15 +143,13 @@ pub fn update(conn: &mut Connection, key: &str, patch: UpdateNode) -> Result<(),
|
||||
row.get(2)?,
|
||||
row.get(3)?,
|
||||
row.get(4)?,
|
||||
row.get(5)?,
|
||||
))
|
||||
},
|
||||
)
|
||||
.optional()
|
||||
.map_err(map_error)?;
|
||||
|
||||
let Some((current_title, current_parent, current_format, current_sort, current_icon, current_enabled)) =
|
||||
current
|
||||
let Some((current_title, current_parent, current_sort, current_icon, current_enabled)) = current
|
||||
else {
|
||||
return Err(StashError::NotFound(format!("分组不存在: {key}")));
|
||||
};
|
||||
@@ -218,26 +202,6 @@ pub fn update(conn: &mut Connection, key: &str, patch: UpdateNode) -> Result<(),
|
||||
None => current_parent.clone(),
|
||||
};
|
||||
|
||||
let is_root = parent_key.is_none();
|
||||
let format = if is_root {
|
||||
match patch.format {
|
||||
Some(format) => {
|
||||
if !FORMATS.contains(&format.as_str()) {
|
||||
return Err(StashError::InvalidDirectory("格式不合法".into()));
|
||||
}
|
||||
Some(format)
|
||||
}
|
||||
None => current_format,
|
||||
}
|
||||
} else {
|
||||
// 子分组没有自己的格式,跟随根分组
|
||||
None
|
||||
};
|
||||
|
||||
if is_root && format.is_none() {
|
||||
return Err(StashError::InvalidDirectory("根分组缺少格式".into()));
|
||||
}
|
||||
|
||||
let icon = match patch.icon {
|
||||
Some(icon) => {
|
||||
let icon = icon.trim().to_string();
|
||||
@@ -268,10 +232,10 @@ pub fn update(conn: &mut Connection, key: &str, patch: UpdateNode) -> Result<(),
|
||||
};
|
||||
|
||||
tx.execute(
|
||||
"UPDATE node SET title = ?1, parent_key = ?2, view_mode = ?3, sort = ?4,
|
||||
icon = ?5, enabled = ?6
|
||||
WHERE key = ?7",
|
||||
params![title, parent_key, format, final_sort, icon, enabled, key],
|
||||
"UPDATE node SET title = ?1, parent_key = ?2, sort = ?3,
|
||||
icon = ?4, enabled = ?5
|
||||
WHERE key = ?6",
|
||||
params![title, parent_key, final_sort, icon, enabled, key],
|
||||
)
|
||||
.map_err(map_error)?;
|
||||
entries::audit(&tx, "node.update", key, &title)?;
|
||||
|
||||
Reference in New Issue
Block a user