Files
home4j/doc/数据库设计.md
2026-04-29 14:59:24 +08:00

17 KiB
Raw Permalink Blame History

Home4j 数据库设计文档

版本v1.0
更新日期2026年2月3日
数据库H2(默认)/ MySQL(可选)


一、设计原则

  1. 简洁优先:MVP 阶段保持表结构简单,避免过度设计
  2. 扩展预留:关键字段预留扩展空间(如 JSON 类型的 extra 字段)
  3. 软删除:重要数据支持软删除,便于恢复
  4. 审计字段:所有表包含创建时间、更新时间

二、实体关系图(ER Diagram

┌─────────────────┐       ┌─────────────────┐
│     t_user      │       │    t_config     │
├─────────────────┤       ├─────────────────┤
│ id (PK)         │       │ id (PK)         │
│ username        │       │ config_key      │
│ password_hash   │       │ config_value    │
│ role            │       │ config_type     │
│ ...             │       │ ...             │
└─────────────────┘       └─────────────────┘

┌─────────────────┐       ┌─────────────────┐
│    t_group      │       │   t_bookmark    │
├─────────────────┤       ├─────────────────┤
│ id (PK)         │◄──────│ id (PK)         │
│ name            │   1:N │ group_id (FK)   │
│ icon            │       │ name            │
│ sort_order      │       │ url             │
│ ...             │       │ icon            │
└─────────────────┘       │ sort_order      │
                          │ ...             │
                          └─────────────────┘

三、数据表详细设计

3.1 用户表(t_user

存储管理员账号信息,MVP 阶段仅支持单管理员

字段名 类型 约束 默认值 说明
id BIGINT PK, AUTO_INCREMENT - 主键
username VARCHAR(50) NOT NULL, UNIQUE - 用户名
password_hash VARCHAR(255) NOT NULL - 密码哈希(BCrypt
nickname VARCHAR(100) - NULL 显示昵称
email VARCHAR(100) - NULL 邮箱(预留)
role VARCHAR(20) NOT NULL 'ADMIN' 角色:ADMIN / USER
status TINYINT NOT NULL 1 状态:1-正常 0-禁用
last_login_time TIMESTAMP - NULL 最后登录时间
last_login_ip VARCHAR(50) - NULL 最后登录IP
created_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 创建时间
updated_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 更新时间

索引

  • uk_username UNIQUE (username)

SQL

CREATE TABLE t_user (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    nickname VARCHAR(100),
    email VARCHAR(100),
    role VARCHAR(20) NOT NULL DEFAULT 'ADMIN',
    status TINYINT NOT NULL DEFAULT 1,
    last_login_time TIMESTAMP,
    last_login_ip VARCHAR(50),
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT uk_username UNIQUE (username)
);

3.2 分组表(t_group

书签的分类容器,支持排序和折叠

字段名 类型 约束 默认值 说明
id BIGINT PK, AUTO_INCREMENT - 主键
name VARCHAR(100) NOT NULL - 分组名称
icon VARCHAR(255) - NULL 图标(图标名/URL/base64
description VARCHAR(500) - NULL 分组描述
sort_order INT NOT NULL 0 排序序号(升序)
collapsed BOOLEAN NOT NULL FALSE 是否默认折叠
visible BOOLEAN NOT NULL TRUE 是否可见
color VARCHAR(20) - NULL 主题色(预留 v1.0
created_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 创建时间
updated_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 更新时间

索引

  • idx_sort_order (sort_order)

SQL

CREATE TABLE t_group (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    icon VARCHAR(255),
    description VARCHAR(500),
    sort_order INT NOT NULL DEFAULT 0,
    collapsed BOOLEAN NOT NULL DEFAULT FALSE,
    visible BOOLEAN NOT NULL DEFAULT TRUE,
    color VARCHAR(20),
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE INDEX idx_group_sort_order ON t_group (sort_order);

3.3 书签表(t_bookmark

核心实体,存储书签链接信息

字段名 类型 约束 默认值 说明
id BIGINT PK, AUTO_INCREMENT - 主键
group_id BIGINT FK, NOT NULL - 所属分组ID
name VARCHAR(100) NOT NULL - 书签名称
url VARCHAR(2000) NOT NULL - 链接地址
icon VARCHAR(500) - NULL 图标(图标名/URL/base64
icon_type VARCHAR(20) NOT NULL 'TEXT' 图标类型:BUILTIN/URL/UPLOAD/TEXT/EMOJI
description VARCHAR(500) - NULL 书签描述
sort_order INT NOT NULL 0 排序序号(升序)
open_in_new_tab BOOLEAN NOT NULL TRUE 是否新标签页打开
pinned BOOLEAN NOT NULL FALSE 是否置顶
visible BOOLEAN NOT NULL TRUE 是否可见
click_count INT NOT NULL 0 点击次数(预留 v1.0
last_click_time TIMESTAMP - NULL 最后点击时间(预留 v1.0
created_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 创建时间
updated_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 更新时间

索引

  • idx_group_id (group_id)
  • idx_group_sort (group_id, sort_order)

外键

  • fk_bookmark_group FOREIGN KEY (group_id) REFERENCES t_group(id)

SQL

CREATE TABLE t_bookmark (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    group_id BIGINT NOT NULL,
    name VARCHAR(100) NOT NULL,
    url VARCHAR(2000) NOT NULL,
    icon VARCHAR(500),
    icon_type VARCHAR(20) NOT NULL DEFAULT 'TEXT',
    description VARCHAR(500),
    sort_order INT NOT NULL DEFAULT 0,
    open_in_new_tab BOOLEAN NOT NULL DEFAULT TRUE,
    pinned BOOLEAN NOT NULL DEFAULT FALSE,
    visible BOOLEAN NOT NULL DEFAULT TRUE,
    click_count INT NOT NULL DEFAULT 0,
    last_click_time TIMESTAMP,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_bookmark_group FOREIGN KEY (group_id) REFERENCES t_group(id) ON DELETE CASCADE
);

CREATE INDEX idx_bookmark_group_id ON t_bookmark (group_id);
CREATE INDEX idx_bookmark_group_sort ON t_bookmark (group_id, sort_order);

3.4 配置表(t_config

存储应用级配置,Key-Value 结构

字段名 类型 约束 默认值 说明
id BIGINT PK, AUTO_INCREMENT - 主键
config_key VARCHAR(100) NOT NULL, UNIQUE - 配置键
config_value TEXT - NULL 配置值
config_type VARCHAR(20) NOT NULL 'STRING' 值类型:STRING/NUMBER/BOOLEAN/JSON
category VARCHAR(50) NOT NULL 'GENERAL' 分类:GENERAL/APPEARANCE/LAYOUT
description VARCHAR(255) - NULL 配置说明
created_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 创建时间
updated_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 更新时间

索引

  • uk_config_key UNIQUE (config_key)
  • idx_category (category)

SQL

CREATE TABLE t_config (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    config_key VARCHAR(100) NOT NULL,
    config_value TEXT,
    config_type VARCHAR(20) NOT NULL DEFAULT 'STRING',
    category VARCHAR(50) NOT NULL DEFAULT 'GENERAL',
    description VARCHAR(255),
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT uk_config_key UNIQUE (config_key)
);

CREATE INDEX idx_config_category ON t_config (category);

预置配置项

config_key config_value config_type category 说明
app.title Home4j STRING GENERAL 网站标题
app.subtitle Welcome Home STRING GENERAL 副标题
app.logo NULL STRING GENERAL Logo URL
app.favicon NULL STRING GENERAL Favicon URL
app.theme light STRING APPEARANCE 当前主题
app.language zh-CN STRING GENERAL 界面语言
layout.columns 4 NUMBER LAYOUT 每行列数
layout.card_size normal STRING LAYOUT 卡片尺寸
layout.density normal STRING LAYOUT 显示密度
search.enabled true BOOLEAN GENERAL 启用搜索
search.placeholder 搜索书签... STRING GENERAL 搜索框占位符
init.completed false BOOLEAN GENERAL 初始化是否完成

3.5 书签标签表(t_bookmark_tag- 预留 v0.5

书签的标签,支持多对多关系(预留)

字段名 类型 约束 默认值 说明
id BIGINT PK, AUTO_INCREMENT - 主键
name VARCHAR(50) NOT NULL, UNIQUE - 标签名称
color VARCHAR(20) - NULL 标签颜色
created_at TIMESTAMP NOT NULL CURRENT_TIMESTAMP 创建时间

SQL(预留):

CREATE TABLE t_bookmark_tag (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    color VARCHAR(20),
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT uk_tag_name UNIQUE (name)
);

-- 书签-标签关联表
CREATE TABLE t_bookmark_tag_rel (
    bookmark_id BIGINT NOT NULL,
    tag_id BIGINT NOT NULL,
    PRIMARY KEY (bookmark_id, tag_id),
    CONSTRAINT fk_rel_bookmark FOREIGN KEY (bookmark_id) REFERENCES t_bookmark(id) ON DELETE CASCADE,
    CONSTRAINT fk_rel_tag FOREIGN KEY (tag_id) REFERENCES t_bookmark_tag(id) ON DELETE CASCADE
);

四、枚举值定义

4.1 用户角色(UserRole

public enum UserRole {
    ADMIN,  // 管理员,可编辑
    USER    // 普通用户(预留 v1.0
}

4.2 图标类型(IconType

public enum IconType {
    BUILTIN,  // 内置图标库
    URL,      // 外部 URL
    UPLOAD,   // 用户上传
    TEXT,     // 文字首字母
    EMOJI     // Emoji 表情
}

4.3 配置类型(ConfigType

public enum ConfigType {
    STRING,   // 字符串
    NUMBER,   // 数字
    BOOLEAN,  // 布尔
    JSON      // JSON 对象
}

4.4 配置分类(ConfigCategory

public enum ConfigCategory {
    GENERAL,     // 常规设置
    APPEARANCE,  // 外观设置
    LAYOUT       // 布局设置
}

五、Quarkus Panache 实体示例

5.1 Bookmark 实体

package com.home4j.entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "t_bookmark")
public class Bookmark extends PanacheEntity {
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "group_id", nullable = false)
    public BookmarkGroup group;
    
    @Column(nullable = false, length = 100)
    public String name;
    
    @Column(nullable = false, length = 2000)
    public String url;
    
    @Column(length = 500)
    public String icon;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "icon_type", nullable = false, length = 20)
    public IconType iconType = IconType.TEXT;
    
    @Column(length = 500)
    public String description;
    
    @Column(name = "sort_order", nullable = false)
    public Integer sortOrder = 0;
    
    @Column(name = "open_in_new_tab", nullable = false)
    public Boolean openInNewTab = true;
    
    @Column(nullable = false)
    public Boolean pinned = false;
    
    @Column(nullable = false)
    public Boolean visible = true;
    
    @Column(name = "click_count", nullable = false)
    public Integer clickCount = 0;
    
    @Column(name = "last_click_time")
    public LocalDateTime lastClickTime;
    
    @Column(name = "created_at", nullable = false, updatable = false)
    public LocalDateTime createdAt;
    
    @Column(name = "updated_at", nullable = false)
    public LocalDateTime updatedAt;
    
    @PrePersist
    public void prePersist() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
    }
    
    @PreUpdate
    public void preUpdate() {
        updatedAt = LocalDateTime.now();
    }
    
    // 常用查询方法
    public static List<Bookmark> findByGroupId(Long groupId) {
        return find("group.id = ?1 ORDER BY pinned DESC, sortOrder ASC", groupId).list();
    }
    
    public static List<Bookmark> findVisible() {
        return find("visible = true ORDER BY pinned DESC, sortOrder ASC").list();
    }
}

5.2 BookmarkGroup 实体

package com.home4j.entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.List;

@Entity
@Table(name = "t_group")
public class BookmarkGroup extends PanacheEntity {
    
    @Column(nullable = false, length = 100)
    public String name;
    
    @Column(length = 255)
    public String icon;
    
    @Column(length = 500)
    public String description;
    
    @Column(name = "sort_order", nullable = false)
    public Integer sortOrder = 0;
    
    @Column(nullable = false)
    public Boolean collapsed = false;
    
    @Column(nullable = false)
    public Boolean visible = true;
    
    @Column(length = 20)
    public String color;
    
    @OneToMany(mappedBy = "group", cascade = CascadeType.ALL, orphanRemoval = true)
    public List<Bookmark> bookmarks;
    
    @Column(name = "created_at", nullable = false, updatable = false)
    public LocalDateTime createdAt;
    
    @Column(name = "updated_at", nullable = false)
    public LocalDateTime updatedAt;
    
    @PrePersist
    public void prePersist() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
    }
    
    @PreUpdate
    public void preUpdate() {
        updatedAt = LocalDateTime.now();
    }
    
    // 常用查询方法
    public static List<BookmarkGroup> findAllOrdered() {
        return find("ORDER BY sortOrder ASC").list();
    }
    
    public static List<BookmarkGroup> findVisible() {
        return find("visible = true ORDER BY sortOrder ASC").list();
    }
}

六、数据初始化

6.1 首次启动初始化

-- 插入默认分组
INSERT INTO t_group (name, icon, sort_order, collapsed, visible) 
VALUES ('默认分组', 'folder', 0, FALSE, TRUE);

-- 插入默认配置
INSERT INTO t_config (config_key, config_value, config_type, category, description) VALUES
('app.title', 'Home4j', 'STRING', 'GENERAL', '网站标题'),
('app.subtitle', 'Welcome Home', 'STRING', 'GENERAL', '副标题'),
('app.theme', 'light', 'STRING', 'APPEARANCE', '当前主题'),
('app.language', 'zh-CN', 'STRING', 'GENERAL', '界面语言'),
('layout.columns', '4', 'NUMBER', 'LAYOUT', '每行列数'),
('layout.card_size', 'normal', 'STRING', 'LAYOUT', '卡片尺寸'),
('search.enabled', 'true', 'BOOLEAN', 'GENERAL', '启用搜索'),
('init.completed', 'false', 'BOOLEAN', 'GENERAL', '初始化是否完成');

七、数据库配置

7.1 H2 配置(开发/默认)

# application.properties
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:file:./data/home4j;AUTO_SERVER=TRUE
quarkus.datasource.username=sa
quarkus.datasource.password=
quarkus.hibernate-orm.database.generation=update

7.2 MySQL 配置(生产可选)

# application-mysql.properties
quarkus.datasource.db-kind=mysql
quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/home4j?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
quarkus.datasource.username=home4j
quarkus.datasource.password=your_password
quarkus.hibernate-orm.database.generation=update

八、表结构总览

表名 说明 MVP v0.5 v1.0
t_user 用户表
t_group 分组表
t_bookmark 书签表
t_config 配置表
t_bookmark_tag 标签表 -
t_bookmark_tag_rel 书签标签关联 -

文档结束
本设计将随项目进展持续更新