数据库设计和Gorm模型

数据库设计和Gorm模型

🪴 写在前面

我们使用数据库来存放用户信息、群组信息、聊天数据、好友关系等。Gorm是Go里面一个很好用的ORM库,我们可以使用它来操作数据库。 首先,对于数据库中的表,一般可以设计成成员带标签的结构体,Gorm会自动根据结构体生成对应的表。所以我们要做的就是:设计结构体、 使用Gorm连接数据库、然后创建表。

💐 参考资料

  • Gorm:Gorm是一个Go语言编写的ORM库,支持MySQL、PostgreSQL、SQLite等数据库。
  • viper

🌿 数据库设计

1. 项目根目录,创建model文件夹

对于数据库中的表,一般来说我们创建一个model文件夹,然后创建不同的文件来存放不同的表的结构体。接下来我们分别创建user.go、group.go、contact_apply.go、user_contact.go、message.go、session.go等文件。

2. user.go存放用户表结构体

package model

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

type UserInfo struct {
	Id            int64          `gorm:"column:id;primaryKey;comment:自增id"`
	Uuid          string         `gorm:"column:uuid;uniqueIndex;type:char(20);comment:用户唯一id"`
	Nickname      string         `gorm:"column:nickname;type:varchar(20);not null;comment:昵称"`
	Telephone     string         `gorm:"column:telephone;index;not null;type:char(11);comment:电话"`
	Email         string         `gorm:"column:email;type:char(30);comment:邮箱"`
	Avatar        string         `gorm:"column:avatar;type:char(255);default:https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png;not null;comment:头像url"`
	Gender        int8           `gorm:"column:gender;comment:性别,0.男,1.女"`
	Signature     string         `gorm:"column:signature;type:varchar(100);comment:个性签名"`
	Password      string         `gorm:"column:password;type:char(18);not null;comment:密码"`
	Birthday      string         `gorm:"column:birthday;type:char(8);comment:生日"`
	CreatedAt     time.Time      `gorm:"column:created_at;index;type:datetime;not null;comment:创建时间"`
	DeletedAt     gorm.DeletedAt `gorm:"column:deleted_at;type:datetime;comment:删除时间"`
	LastOnlineAt  sql.NullTime   `gorm:"column:last_online_at;type:datetime;comment:上次登录时间"`
	LastOfflineAt sql.NullTime   `gorm:"column:last_offline_at;type:datetime;comment:最近离线时间"`
	IsAdmin       int8           `gorm:"column:is_admin;not null;comment:是否是管理员,0.不是,1.是"`
	Status        int8           `gorm:"column:status;index;not null;comment:状态,0.正常,1.禁用"`
}

func (UserInfo) TableName() string {
	return "user_info"
}

3. group.go存放群组表结构体

注意这里的members字段是json类型,存放的是群组成员的uuid。本来这个地方可以设计成一个[]string。但是gorm 不支持[]string,所以我们使用json.RawMessage来存放json类型的数据。这个地方的json.RawMessage是一个[]byte类型, 也就是把[]string序列化为[]byte进行存储,用的时候再反序列化。

package model

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

type GroupInfo struct {
	Id        int64           `gorm:"column:id;primaryKey;comment:自增id"`
	Uuid      string          `gorm:"column:uuid;uniqueIndex;type:char(20);not null;comment:群组唯一id"`
	Name      string          `gorm:"column:name;type:varchar(20);not null;comment:群名称"`
	Notice    string          `gorm:"column:notice;type:varchar(500);comment:群公告"`
	Members   json.RawMessage `gorm:"column:members;type:json;comment:群组成员"`
	MemberCnt int             `gorm:"column:member_cnt;default:1;comment:群人数"` // 默认群主1人
	OwnerId   string          `gorm:"column:owner_id;type:char(20);not null;comment:群主uuid"`
	AddMode   int8            `gorm:"column:add_mode;default:0;comment:加群方式,0.直接,1.审核"`
	Avatar    string          `gorm:"column:avatar;type:char(255);default:https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png;not null;comment:头像"`
	Status    int8            `gorm:"column:status;default:0;comment:状态,0.正常,1.禁用,2.解散"`
	CreatedAt time.Time       `gorm:"column:created_at;index;type:datetime;not null;comment:创建时间"`
	UpdatedAt time.Time       `gorm:"column:updated_at;type:datetime;not null;comment:更新时间"`
	DeletedAt gorm.DeletedAt  `gorm:"column:deleted_at;index;comment:删除时间"`
}

func (GroupInfo) TableName() string {
	return "group_info"
}

4. user_contact.go存放好友申请表结构体

这里存放的是用户的好友和群组信息。其中的CreatedAt是申请时间,UpdatedAt是更新时间,DeletedAt是删除时间。这三个时间都是由gorm自动维护的。 同时DeletedAt是gorm的软删除字段,当删除数据时,DeletedAt会被设置为删除时间,而不是真正的删除数据。

package model

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

type UserContact struct {
	Id          int64          `gorm:"column:id;primaryKey;comment:自增id"`
	UserId      string         `gorm:"column:user_id;index;type:char(20);not null;comment:用户唯一id"`
	ContactId   string         `gorm:"column:contact_id;index;type:char(20);not null;comment:对应联系id"`
	ContactType int8           `gorm:"column:contact_type;not null;comment:联系类型,0.用户,1.群聊"`
	Status      int8           `gorm:"column:status;not null;comment:联系状态,0.正常,1.拉黑,2.被拉黑,3.删除好友,4.被删除好友,5.被禁言,6.退出群聊,7.被踢出群聊"`
	CreatedAt   time.Time      `gorm:"column:created_at;type:datetime;not null;comment:创建时间"`
	UpdateAt    time.Time      `gorm:"column:update_at;type:datetime;not null;comment:更新时间"`
	DeletedAt   gorm.DeletedAt `gorm:"column:deleted_at;type:datetime;index;comment:删除时间"`
}

func (UserContact) TableName() string {
	return "user_contact"
}

5. contact_apply.go存放好友申请表结构体

这里存放的是用户的好友申请信息。其中的LastApplyAt是最后申请时间,DeletedAt是删除时间。

package model

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

type ContactApply struct {
	Id          int64          `gorm:"column:id;primaryKey;comment:自增id"`
	Uuid        string         `gorm:"column:uuid;uniqueIndex;type:char(20);comment:申请id"`
	UserId      string         `gorm:"column:user_id;index;type:char(20);not null;comment:申请人id"`
	ContactId   string         `gorm:"column:contact_id;index;type:char(20);not null;comment:被申请id"`
	ContactType int8           `gorm:"column:contact_type;not null;comment:被申请类型,0.用户,1.群聊"`
	Status      int8           `gorm:"column:status;not null;comment:申请状态,0.申请中,1.通过,2.拒绝,3.拉黑"`
	Message     string         `gorm:"column:message;type:varchar(100);comment:申请信息"`
	LastApplyAt time.Time      `gorm:"column:last_apply_at;type:datetime;not null;comment:最后申请时间"`
	DeletedAt   gorm.DeletedAt `gorm:"column:deleted_at;index;type:datetime;comment:删除时间"`
}

func (ContactApply) TableName() string {
	return "contact_apply"
}

6. session.go存放用户会话表结构体

这里存放的是用户的会话信息。其中的LastMessage是最后一条消息,LastMessageAt是最后一条消息时间,DeletedAt是删除时间。

package model

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

type Session struct {
	Id            int64          `gorm:"column:id;primaryKey;comment:自增id"`
	Uuid          string         `gorm:"column:uuid;uniqueIndex;type:char(20);comment:会话uuid"`
	SendId        string         `gorm:"column:send_id;Index;type:char(20);not null;comment:创建会话人id"`
	ReceiveId     string         `gorm:"column:receive_id;Index;type:char(20);not null;comment:接受会话人id"`
	ReceiveName   string         `gorm:"column:receive_name;type:varchar(20);not null;comment:名称"`
	Avatar        string         `gorm:"column:avatar;type:char(255);default:default_avatar.png;not null;comment:头像"`
	LastMessage   string         `gorm:"column:last_message;type:TEXT;comment:最新的消息"`
	LastMessageAt sql.NullTime      `gorm:"column:last_message_at;type:datetime;comment:最近接收时间"`
	CreatedAt     time.Time      `gorm:"column:created_at;Index;type:datetime;comment:创建时间"`
	DeletedAt     gorm.DeletedAt `gorm:"column:deleted_at;Index;type:datetime;comment:删除时间"`
}

func (Session) TableName() string {
	return "session"
}

7. message.go存放消息表结构体

包括了消息id、会话id、消息类型和消息内容。

package model

import (
	"database/sql"
	"time"
)

type Message struct {
	Id         int64     `gorm:"column:id;primaryKey;comment:自增id"`
	Uuid       string    `gorm:"column:uuid;uniqueIndex;type:char(20);not null;comment:消息uuid"`
	SessionId  string    `gorm:"column:session_id;index;type:char(20);not null;comment:会话uuid"`
	Type       int8      `gorm:"column:type;not null;comment:消息类型,0.文本,1.语音,2.文件,3.通话"` // 通话不用存消息内容或者url
	Content    string    `gorm:"column:content;type:TEXT;comment:消息内容"`
	Url        string    `gorm:"column:url;type:char(255);comment:消息url"`
	SendId     string    `gorm:"column:send_id;index;type:char(20);not null;comment:发送者uuid"`
	SendName   string    `gorm:"column:send_name;type:varchar(20);not null;comment:发送者昵称"`
	SendAvatar string    `gorm:"column:send_avatar;type:varchar(255);not null;comment:发送者头像"`
	ReceiveId  string    `gorm:"column:receive_id;index;type:char(20);not null;comment:接受者uuid"`
	FileType   string    `gorm:"column:file_type;type:char(10);comment:文件类型"`
	FileName   string    `gorm:"column:file_name;type:varchar(50);comment:文件名"`
	FileSize   string    `gorm:"column:file_size;type:char(20);comment:文件大小"`
	Status     int8      `gorm:"column:status;not null;comment:状态,0.未发送,1.已发送"`
	CreatedAt  time.Time `gorm:"column:created_at;not null;comment:创建时间"`
	SendAt     sql.NullTime `gorm:"column:send_at;comment:发送时间"`
	AVdata     string    `gorm:"column:av_data;comment:通话传递数据"`
}

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

到这里基本数据库的表就设计完全了。接下来我们要使用Gorm来连接数据库,然后创建表。

🌱 创建配置文件,实现读取操作

传统意义的三层架构,是将数据库操作放在dao层,然后service层调用dao层的方法,最后controller层调用service层的方法。dao层负责数据库的增删改查操作。 service层负责业务逻辑的处理。controller层负责接收请求和返回响应。这里我们使用Gorm来操作数据库,所以我们在dao层使用Gorm来操作数据库。另外,项目当中 涉及到很多配置信息,我们可以使用viper来读取配置文件。viper是一个Go语言的配置管理库,支持多种配置文件格式,如json、yaml、toml等。

1. 在项目根目录创建configs文件夹,并填写相关的配置 我们使用toml文件来保存配置信息。在config文件夹下创建config.toml文件,填写数据库的配置信息。这里配置信息不是很多,所以我们直接写在config.toml文件中。 如果很多的话,可以分成不同的文件,然后在config.toml文件中引入。注意修改配置信息为自己的配置信息。

[mainConfig]
appName = "your app name"
host = "0.0.0.0"
port = 8000

[mysqlConfig]
host = "127.0.0.1"
port = 3306
user = "root"
password = "123456"
databaseName = "your database name"

[redisConfig]
host = "127.0.0.1"
port = 6379
password = ""
db = 0

[authCodeConfig]
accessKeyID = "your accessKeyID in alibaba cloud"
accessKeySecret = "your accessKeySecret in alibaba cloud"
signName = "阿里云短信测试"
templateCode = "SMS_154950909"

[logConfig]
logPath = "your log path"

[kafkaConfig]
messageMode = "channel"# 消息模式 channel or kafka
hostPort = "127.0.0.1:9092" # "127.0.0.1:9092,127.0.0.1:9093,127.0.0.1:9094" 多个kafka服务器
loginTopic = "login"
chatTopic = "chat_message"
logoutTopic = "logout"
partition = 0 # kafka partition
timeout = 1 # 单位秒

[staticSrcConfig]
staticAvatarPath = "./static/avatars"
staticFilePath = "./static/files"

2. 新建config.go文件,实现读取配置文件的方法 这里我们使用viper来读取配置文件。首先我们要引入viper包,然后在config.go文件中实现读取配置文件的方法。

package config