在上一篇文章中,我们实现了鉴权与加密。这也意味着我们基本实现了用户模块的基础功能(包括注册、登录、用户信息修改、注销)。因此,接下来,我们将实现的是关系模块的基本功能。其中,作为一个聊天网站后端,关系模块包括了好友模块和群聊模块)
PS:以下为了简便起见只会引用部分代码进行说明,完整代码在github仓库的代码文件中(在哪个代码文件中下文有标注)
一、关系表的设计
在关系表的设计中,我们将好友关系和群聊关系统一纳入一张Relations数据表中。因此,该Relations数据表应包含如下字段:
关系拥有者的ID
关系对象的ID
关系类型(好友/群聊)
相关描述
其中,当关系类型是好友时,关系对象的ID应为一个合法的User ID;而当关系类型是群聊时,关系对象的ID应为一个合法的Group ID。相关代码实现在 models/relation.go 中:
// Relation describes the relations between user and user or between user and group
/*
the params are:
* OwnerId is the user id of the relationship owner
* TargetId is the user id of the target user
* Type = 1 means Friends relationship; while Type = 2 means Group relationship
* Desc store the description message
*/
type Relation struct {
gorm.Model
OwnerId uint
TargetId uint
Type int
Desc string
}
二、好友模块的DAO层实现
DAO层需要实现的主要功能函数有:
- 获取好友列表
- 根据OwnerUser和TargetUser查询关系id
- 新增好友关系
- 修改好友关系信息
- 删除好友关系
值得注意的是,在新增、修改好友关系中,需要开启事务管理以保证数据库的一致性。这是因为Relations表将好友关系设计为类似于有向边,因此需要同时新增/修改两条有向边以保持数据库一致性。
查阅GORM技术文档可以知道,一个事务的手动开启需要调用DB.Begin()函数,该函数会返回一个 *DB 类型的值。在该事务的所有操作都必须使用该返回值调用GORM的函数执行;并且在事务结束时需要手动提交(Commit函数);而事务出错时需要手动进行回滚(Rollback函数)。
以新增好友关系为例,代码如下所示:
// open transaction
tx := global.DB.Begin()
relation.OwnerId = userId
relation.TargetId = targetId
relation.Type = 1
if t := tx.Create(&relation); t.RowsAffected == 0 {
tx.Rollback()
zap.S().Info("Failed to Create Relation")
return errors.New("failed to Create Relation")
}
relation = models.Relation{}
relation.OwnerId = targetId
relation.TargetId = userId
relation.Type = 1
if t := tx.Create(&relation); t.RowsAffected == 0 {
tx.Rollback()
zap.S().Info("Failed to Create Relation")
return errors.New("failed to Create Relation")
}
tx.Commit()
具体DAO层代码位于 dao/relation.go 中
三、好友模块Service层API开发
需要提供以下几种API:
- 获取用户列表
- 新增好友关系
- 修改好友关系
- 删除好友关系
具体代码请参见 service/relation.go 文件
并且在实现Service层API后,记得要将API在路由中进行注册,并且使用中间件(midddleware)进行用户鉴权:
// Relation Module
relation := v1.Group("relation").Use(middleware.Authentication())
{
// Friends API
relation.POST("/list", service.FriendList)
relation.POST("/add", service.AddFriendByName)
relation.POST("/update", service.UpdateRelation)
relation.DELETE("/delete", service.DelFriendByName)
}