青训营抖音项目实现-1 | 青训营笔记

262 阅读4分钟

青训营抖音项目实现-1 | 青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

一、项目简介

该项目主要是实现一个极简版抖音,根据青训营官方提供的接口,使用golang技术栈实现其后端服务。

二、主要技术

go、gin框架、gorm、mysql、oss对象存储、jwt。

三、主要过程

1、数据表设计

初步实现,一切以简单为主。因此所有的表都没有任何的冗余字段。根据接口的定义我共建立了六张表。

用户表

type User struct {
   gorm.Model
   Name          string
   FollowCount   int
   FollowerCount int
   Password      string
   Salt          string
}

gorm.Model是gorm提供的,主要有以下四个字段,其中deleteAt字段可以让删除操作变成更新操作,只需要设置该字段,gorm查询时就会过滤的这条数据。id则是gorm给我们设置的主键。

type Model struct {
   ID        uint `gorm:"primarykey"`
   CreatedAt time.Time
   UpdatedAt time.Time
   DeletedAt DeletedAt `gorm:"index"`
}

用户表的设计除了前端需要的六个字段外,我只额外增加了一个salt字段。以及gorm增加的三个字段。salt字段主要是为了不让用户的密码明文存储在数据库加的盐值。

点赞表

type Favorite struct {
   gorm.Model
   UserID  uint //用户id
   VideoID uint //视频id
   Exist   bool //是否存在,避免重复的点赞取消点赞使得该表内容变得很大
}

gorm.Model和之前一样,与前端接口需要的相比,也只增加了一个Exist字段,该字段是为了避免用户重复点赞取消点赞造成数据表大小膨胀。不过增加了这个字段提高的编程的复杂性,需要考虑的事情更多了。

评论表

type Comment struct {
   gorm.Model
   Content    string
   CreateTime time.Time
   VideoID    uint
   UserID     uint
}

评论表没有任何多余内容,所有字段都是接口需要的。

消息表

type Message struct {
   gorm.Model
   Content    string
   UserID     uint //发出消息的用户
   TargetID   uint //目标用户
   CreateTime time.Time
}

同样没有任何多余字段。很明显消息字段过于简略,内容也只支持文本,之后可以添加图片,语音,表情包等等。

关系表

type Relation struct {
   gorm.Model
   UserID   uint
   TargetID uint
   Type     int  // 1:UserID关注TargetID   2:互相关注
   Exist    bool // 判断是否存在,避免一个用户频繁关注取消关注造成数据膨胀,并且这样可以使用唯一索引
}

关系表的设计比较复杂。使用Type来确定是否是好友关系,关系有三种:

  1. A关注B,关系表中存在:一条UserID=A,TargetID=B,Type=1,Exist=1的数据,或者存在两条数据:一条UserID=A,TargetID=B,Type=1,Exist=1以及一条UserID=B,TargetID=A,Type=1,Exist=0的数据
  2. B关注A,与A关注B类似
  3. A,B互相关注,两人互为好友。关系表中存在一条UserID=A,TargetID=B,Type=2,Exist=1,以及一条UserID=B,TargetID=A,Type=2,Exist=1的数据 由于关注和取消关注属于很频繁的操作,因此同样有个exist字段。

视频表

type Video struct {
   gorm.Model
   AuthorID      uint   //作者id
   Title         string //视频标题
   CommentCount  int    //评论数
   FavoriteCount int    //点赞数
   PlayURL       string //播放地址
   CoverURL      string //封面地址
}

没有任何多余字段。

2、项目目录

屏幕截图 2023-01-15 181512.png

config

读取和存储配置文件

controllers

主要业务逻辑处理

docs

整合swagger产生的目录,swagger后面之后的文章进行介绍

middleware

中间件层,之后的链路追踪可以在这一层实现。gin的中间件也是一个handler,使用中间件的handler最后一条语句一般是c.Next(),这属于设计模式中的责任链模式,中间件只做一部分工作。目前该层使用处理登录用户的验证工作。

models

数据表的gorm映射

routes

gin的url设置

service

该目录没有内容,考虑之后通过将controllers目录下的代码提炼出重复的部分放在这

sql

形成数据库的sql文件

storage

对象存储客户端,目前只实现了aliyun oss

utils

一些常用的工具函数

3、gin框架下访问路径

r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
home := r.Group("/douyin")
{
   // 首页
   home.GET("/feed", controllers.Feed)
   home.GET("/user/", controllers.UserInfo)

   user := home.Group("/user")
   {
      user.POST("/register/", controllers.UserRegister)
      user.POST("/login/", controllers.UserLogin)
   }

   publish := home.Group("/publish")
   {
      publish.POST("/action/", middleware.PostAuthorization, controllers.PublishAction)
      publish.GET("/list/", middleware.GetAuthorization, controllers.PublishList)
   }
   favorite := home.Group("/favorite", middleware.GetAuthorization)
   {
      favorite.POST("/action/", controllers.FavoriteAction)
      favorite.GET("/list/", controllers.FavoriteList)

   }
   comment := home.Group("/comment", middleware.GetAuthorization)
   {
      comment.POST("/action/", controllers.CommentAction)
      comment.GET("/list/", controllers.CommentList)
   }
   relation := home.Group("/relation", middleware.GetAuthorization)
   {
      relation.POST("/action/", controllers.RelationAction)
      relation.GET("/follow/list/", controllers.RelationFollowList)
      relation.GET("/follower/list/", controllers.RelationFollowerList)
      relation.GET("/friend/list/", controllers.RelationFriendList)
   }
   message := home.Group("/message", middleware.GetAuthorization)
   {
      message.GET("/chat/", controllers.MessageChat)
      message.POST("/action/", controllers.MessageAction)
   }
}

gin框架通过group可以给url分组,需要登录才能访问的加上middleware.Authorization。Get和Post之分主要是为了对应不同token的存储位置。