第一幕 数据库设计与初始化
2026/5/12大约 2 分钟数据库
项目概述
SLsec 实验室论坛是本课设的核心项目,采用 Client-Server 架构:
- Server:Rust + axum + sqlx,提供 HTTP REST API 和 WebSocket 推送
- Client:Rust + ratatui,纯终端 TUI 界面,支持鼠标和键盘快捷键
- 数据库:MySQL 8.0,通过 Docker 部署
项目路径:~/db_class_design_rs/(Cargo workspace,包含 server 和 client 两个 crate)
ER 图
@startuml
entity users {
* id : INT UNSIGNED <<PK>>
--
username : VARCHAR(32) <<UNIQUE>>
nickname : VARCHAR(64)
password_hash : VARCHAR(255)
role : ENUM('admin','member')
avatar : VARCHAR(255)
created_at : DATETIME
deleted_at : DATETIME
}
entity articles {
* id : INT UNSIGNED <<PK>>
--
author_id : INT UNSIGNED <<FK>>
title : VARCHAR(128)
content : MEDIUMTEXT
summary : VARCHAR(512)
category : VARCHAR(32)
pinned : TINYINT(1)
view_count : INT UNSIGNED
created_at : DATETIME
updated_at : DATETIME
deleted_at : DATETIME
}
entity comments {
* id : INT UNSIGNED <<PK>>
--
article_id : INT UNSIGNED <<FK>>
author_id : INT UNSIGNED <<FK>>
parent_id : INT UNSIGNED <<FK>>
content : TEXT
created_at : DATETIME
deleted_at : DATETIME
}
entity categories {
* id : INT UNSIGNED <<PK>>
--
name : VARCHAR(32) <<UNIQUE>>
description : VARCHAR(255)
sort_order : INT
}
users ||--o{ articles : "发布"
users ||--o{ comments : "发表"
articles ||--o{ comments : "包含"
comments ||--o{ comments : "回复"
@enduml建表 SQL
完整脚本位于 server/sql/init.sql,核心设计要点:
软删除:users、articles、comments 均有 deleted_at 字段,删除时只设置时间戳而不物理删除,保留数据完整性。
索引策略:
| 表 | 索引 | 用途 |
|---|---|---|
| users | username UNIQUE | 登录查询 |
| articles | author_id | 按作者筛选 |
| articles | created_at DESC | 时间倒序列表 |
| articles | category | 分类筛选 |
| comments | article_id | 文章评论列表 |
字符集:全库 utf8mb4 + utf8mb4_unicode_ci,支持 emoji。
数据模型(Rust)
server/src/models.rs 中用 sqlx::FromRow 宏自动映射查询结果:
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct User {
pub id: u32,
pub username: String,
pub nickname: String,
#[serde(skip_serializing)] // 密码哈希不序列化到 JSON
pub password_hash: String,
pub role: String,
pub avatar: Option<String>,
pub created_at: NaiveDateTime,
pub deleted_at: Option<NaiveDateTime>,
}JOIN 查询结果用独立的 ArticleJoinRow 结构体接收,避免与基础模型混用:
#[derive(FromRow)]
struct ArticleJoinRow {
// articles 字段 + JOIN 来的 author_name, author_nickname, comment_count
}数据库初始化流程
Server 启动时自动执行 init_db():
- 先连接
mysql系统库,执行CREATE DATABASE IF NOT EXISTS slsec_forum - 重新连接
slsec_forum,执行init.sql中的建表语句 INSERT IGNORE插入默认管理员账号(admin/admin123)和分类数据
这样做的好处是零配置启动——只要 MySQL 在线,第一次运行就能自动建库建表。
