数据库设计和Gorm模型
🪴 写在前面
我们使用数据库来存放用户信息、群组信息、聊天数据、好友关系等。Gorm是Go里面一个很好用的ORM库,我们可以使用它来操作数据库。 首先,对于数据库中的表,一般可以设计成成员带标签的结构体,Gorm会自动根据结构体生成对应的表。所以我们要做的就是:设计结构体、 使用Gorm连接数据库、然后创建表。
💐 参考资料
🌿 数据库设计
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