17 KiB
17 KiB
Home4j 数据库设计文档
版本:v1.0
更新日期:2026年2月3日
数据库:H2(默认)/ MySQL(可选)
一、设计原则
- 简洁优先:MVP 阶段保持表结构简单,避免过度设计
- 扩展预留:关键字段预留扩展空间(如 JSON 类型的 extra 字段)
- 软删除:重要数据支持软删除,便于恢复
- 审计字段:所有表包含创建时间、更新时间
二、实体关系图(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_usernameUNIQUE (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_groupFOREIGN KEY (group_id) REFERENCESt_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_keyUNIQUE (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 |
书签标签关联 | - | ✅ | ✅ |
文档结束
本设计将随项目进展持续更新