You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

115 lines
5.2 KiB

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export const up = async knex => {
// 创建分类表
await knex.schema.createTable("categories", function (table) {
table.increments("id").primary()
table.string("name", 100).notNullable().unique()
table.string("description", 500)
table.string("color", 7).defaultTo("#3B82F6") // 十六进制颜色值
table.string("icon", 50) // 图标类名或路径
table.integer("sort_order").defaultTo(0)
table.boolean("is_active").defaultTo(true)
table.integer("user_id").unsigned().references("id").inTable("users").onDelete("CASCADE")
table.timestamp("created_at").defaultTo(knex.fn.now())
table.timestamp("updated_at").defaultTo(knex.fn.now())
})
// 创建标签表
await knex.schema.createTable("tags", function (table) {
table.increments("id").primary()
table.string("name", 100).notNullable()
table.string("description", 500)
table.string("color", 7).defaultTo("#6B7280")
table.integer("user_id").unsigned().references("id").inTable("users").onDelete("CASCADE")
table.timestamp("created_at").defaultTo(knex.fn.now())
table.timestamp("updated_at").defaultTo(knex.fn.now())
// 同一用户下标签名唯一
table.unique(["name", "user_id"])
})
// 创建收藏主表
await knex.schema.createTable("bookmarks", function (table) {
table.increments("id").primary()
table.string("title", 200).notNullable()
table.text("description")
table.string("url", 1000).notNullable()
table.string("favicon", 500) // 网站图标
table.string("screenshot", 500) // 截图路径
table.integer("category_id").unsigned().references("id").inTable("categories").onDelete("SET NULL")
table.integer("user_id").unsigned().references("id").inTable("users").onDelete("CASCADE")
table.boolean("is_public").defaultTo(false) // 是否公开
table.boolean("is_favorite").defaultTo(false) // 是否特别收藏
table.integer("click_count").defaultTo(0) // 点击次数
table.integer("sort_order").defaultTo(0)
table.json("metadata") // 存储额外的元数据,如网站标题、描述等
table.timestamp("last_visited").defaultTo(knex.fn.now()) // 最后访问时间
table.timestamp("created_at").defaultTo(knex.fn.now())
table.timestamp("updated_at").defaultTo(knex.fn.now())
// 索引
table.index(["user_id", "category_id"])
table.index(["user_id", "is_favorite"])
table.index(["user_id", "created_at"])
})
// 创建收藏与标签关联表
await knex.schema.createTable("bookmark_tags", function (table) {
table.increments("id").primary()
table.integer("bookmark_id").unsigned().notNullable().references("id").inTable("bookmarks").onDelete("CASCADE")
table.integer("tag_id").unsigned().notNullable().references("id").inTable("tags").onDelete("CASCADE")
table.timestamp("created_at").defaultTo(knex.fn.now())
// 唯一约束,防止重复关联
table.unique(["bookmark_id", "tag_id"])
table.index(["bookmark_id"])
table.index(["tag_id"])
})
// 创建收藏的多链接表
await knex.schema.createTable("bookmark_links", function (table) {
table.increments("id").primary()
table.integer("bookmark_id").unsigned().notNullable().references("id").inTable("bookmarks").onDelete("CASCADE")
table.string("title", 200).notNullable()
table.string("url", 1000).notNullable()
table.string("description", 500)
table.string("type", 50).defaultTo("link") // link, download, api, etc.
table.boolean("is_active").defaultTo(true)
table.integer("sort_order").defaultTo(0)
table.timestamp("created_at").defaultTo(knex.fn.now())
table.timestamp("updated_at").defaultTo(knex.fn.now())
table.index(["bookmark_id"])
table.index(["type"])
})
// 创建收藏历史表(可选,用于统计和分析)
await knex.schema.createTable("bookmark_history", function (table) {
table.increments("id").primary()
table.integer("bookmark_id").unsigned().notNullable().references("id").inTable("bookmarks").onDelete("CASCADE")
table.integer("user_id").unsigned().notNullable().references("id").inTable("users").onDelete("CASCADE")
table.string("action", 50).notNullable() // visit, favorite, share, etc.
table.json("context") // 存储上下文信息
table.timestamp("created_at").defaultTo(knex.fn.now())
table.index(["bookmark_id"])
table.index(["user_id"])
table.index(["created_at"])
})
}
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export const down = async knex => {
await knex.schema.dropTableIfExists("bookmark_history")
await knex.schema.dropTableIfExists("bookmark_links")
await knex.schema.dropTableIfExists("bookmark_tags")
await knex.schema.dropTableIfExists("bookmarks")
await knex.schema.dropTableIfExists("tags")
await knex.schema.dropTableIfExists("categories")
}