Skip to content

数据模型设计

本章节将详细介绍聊天应用的数据模型设计,包括用户、消息、聊天室等模型的定义和关联关系。

📋 学习目标

完成本章节后,你将能够:

  • 设计聊天系统的数据库表结构
  • 使用 GORM 定义数据模型
  • 理解消息和聊天室的关系
  • 实现数据库自动迁移
  • 掌握实时系统的数据模型设计

🗄️ 数据库设计

实体关系图

User (用户)
  ├── Messages (消息) 1:N (发送者)
  ├── Messages (消息) 1:N (接收者)
  ├── RoomMembers (聊天室成员) 1:N
  └── UserSessions (用户会话) 1:N

Message (消息)
  ├── User (发送者) N:1
  ├── User (接收者) N:1 (私聊)
  └── Room (聊天室) N:1 (群聊)

Room (聊天室)
  ├── Messages (消息) 1:N
  └── RoomMembers (成员) 1:N

RoomMember (聊天室成员)
  ├── Room (聊天室) N:1
  └── User (用户) N:1

📦 模型定义

1. 用户模型

创建 internal/model/user.go:

go
package model

import (
	"time"
	"gorm.io/gorm"
)

// User 用户模型
type User struct {
	ID        uint      `gorm:"primaryKey" json:"id"`
	Username  string    `gorm:"uniqueIndex;not null;size:50" json:"username"`
	Email     string    `gorm:"uniqueIndex;not null;size:100" json:"email"`
	Password  string    `gorm:"not null;size:255" json:"-"`
	Nickname  string    `gorm:"size:50" json:"nickname"`
	Avatar    string    `gorm:"size:255" json:"avatar"`
	Status    string    `gorm:"default:offline;size:20" json:"status"` // online, offline, away
	LastSeen  *time.Time `json:"last_seen,omitempty"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`

	// 关联
	SentMessages    []Message     `gorm:"foreignKey:SenderID" json:"-"`
	ReceivedMessages []Message    `gorm:"foreignKey:ReceiverID" json:"-"`
	RoomMembers     []RoomMember  `gorm:"foreignKey:UserID" json:"-"`
	Sessions        []UserSession `gorm:"foreignKey:UserID" json:"-"`
}

func (User) TableName() string {
	return "users"
}

2. 消息模型

创建 internal/model/message.go:

go
package model

import (
	"time"
)

// Message 消息模型
type Message struct {
	ID        uint      `gorm:"primaryKey" json:"id"`
	Content   string    `gorm:"type:text;not null" json:"content"`
	Type      string    `gorm:"default:text;size:20" json:"type"` // text, image, file
	Status    string    `gorm:"default:sent;size:20" json:"status"` // sent, delivered, read
	CreatedAt time.Time `json:"created_at"`

	// 外键
	SenderID   uint  `gorm:"not null;index" json:"sender_id"`
	ReceiverID *uint `gorm:"index" json:"receiver_id,omitempty"` // 私聊接收者
	RoomID     *uint `gorm:"index" json:"room_id,omitempty"`     // 群聊房间

	// 关联
	Sender   User  `gorm:"foreignKey:SenderID" json:"sender,omitempty"`
	Receiver *User `gorm:"foreignKey:ReceiverID" json:"receiver,omitempty"`
	Room     *Room `gorm:"foreignKey:RoomID" json:"room,omitempty"`
}

func (Message) TableName() string {
	return "messages"
}

3. 聊天室模型

创建 internal/model/room.go:

go
package model

import (
	"time"
)

// Room 聊天室模型
type Room struct {
	ID          uint      `gorm:"primaryKey" json:"id"`
	Name        string    `gorm:"not null;size:100" json:"name"`
	Description string    `gorm:"size:255" json:"description"`
	Type        string    `gorm:"default:public;size:20" json:"type"` // public, private
	Avatar      string    `gorm:"size:255" json:"avatar"`
	CreatedBy   uint      `gorm:"not null;index" json:"created_by"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`

	// 关联
	Messages     []Message     `gorm:"foreignKey:RoomID" json:"messages,omitempty"`
	Members      []RoomMember  `gorm:"foreignKey:RoomID" json:"members,omitempty"`
	Creator      User          `gorm:"foreignKey:CreatedBy" json:"creator,omitempty"`
}

// RoomMember 聊天室成员
type RoomMember struct {
	ID        uint      `gorm:"primaryKey" json:"id"`
	RoomID    uint      `gorm:"not null;index" json:"room_id"`
	UserID    uint      `gorm:"not null;index" json:"user_id"`
	Role      string    `gorm:"default:member;size:20" json:"role"` // owner, admin, member
	JoinedAt  time.Time `json:"joined_at"`

	Room Room `gorm:"foreignKey:RoomID" json:"room,omitempty"`
	User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
}

func (Room) TableName() string {
	return "rooms"
}

func (RoomMember) TableName() string {
	return "room_members"
}

4. 用户会话模型

创建 internal/model/session.go:

go
package model

import (
	"time"
)

// UserSession 用户会话模型
type UserSession struct {
	ID        uint      `gorm:"primaryKey" json:"id"`
	UserID    uint      `gorm:"not null;index" json:"user_id"`
	Token     string    `gorm:"uniqueIndex;not null;size:255" json:"token"`
	IP        string    `gorm:"size:45" json:"ip"`
	UserAgent string    `gorm:"size:255" json:"user_agent"`
	ExpiresAt time.Time `gorm:"not null;index" json:"expires_at"`
	CreatedAt time.Time `json:"created_at"`

	User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
}

func (UserSession) TableName() string {
	return "user_sessions"
}

🗄️ 数据库迁移

创建 internal/repository/migrate.go:

go
package repository

import (
	"chat-app/internal/model"
	"gorm.io/gorm"
)

func AutoMigrate(db *gorm.DB) error {
	return db.AutoMigrate(
		&model.User{},
		&model.Message{},
		&model.Room{},
		&model.RoomMember{},
		&model.UserSession{},
	)
}

💡 索引优化

创建索引

go
// 消息表索引
db.Exec("CREATE INDEX idx_message_sender ON messages(sender_id)")
db.Exec("CREATE INDEX idx_message_receiver ON messages(receiver_id)")
db.Exec("CREATE INDEX idx_message_room ON messages(room_id)")
db.Exec("CREATE INDEX idx_message_created ON messages(created_at)")

// 聊天室成员索引
db.Exec("CREATE INDEX idx_room_member_room ON room_members(room_id)")
db.Exec("CREATE INDEX idx_room_member_user ON room_members(user_id)")

⏭️ 下一步

数据模型设计完成后,下一步是:


🎉 数据模型设计完成! 现在你可以开始实现WebSocket功能了。

基于 VitePress 构建