本篇笔记旨在记录视频播放接口 feed 的设计思路,其可以完成拉取最近30次的视频极其社交互动信息的功能。
相应接口
首先要定义一个处理响应信息的结构体:
go
type feedResponse struct {
Response entity.Response
VideoList []entity.VideoResponse `json:"video_list,omitempty"`
NextTime int64 `json:"next_time,omitempty"`
}
其中 Response
是服务状态信息,VideoList
是含有社交互动信息的视频信息列表,NextTime
是最新视频响应信息。
同时,由于要播放社交信息,包括点赞数,评论数,是否已经点赞等,此处还需要定义一个新的视频信息结构体来存放相关信息:
go
type VideoResponse struct {
Id int64 `json:"id"`
Author UserData `json:"author"`
PlayUrl string `json:"play_url"`
CoverUrl string `json:"cover_url"`
FavoriteCount int64 `json:"favorite_count"`
CommentCount int64 `json:"comment_count"`
IsFavorite bool `json:"is_favorite"`
Title string `json:"title"`
}
接下来 feed 接口任务就是:搜索并且排序所有要播放的视频信息,并且把这些信息按照上述形式存储,再写入 json 传给前端。
feed 接口设计思路
获取 last_time
即获得上次上次视频时间信息,找不到就用当前信息来替代:
go
LastTimeStr := c.DefaultQuery("last_time", "")
var LastTime int64
CurrentTime := time.Now().Unix()
if LastTimeStr != "" {
LastTimeTemp, err := time.Parse(time.RFC3339, LastTimeStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status_code": -1, "status_msg": "Fail to get the last time."})
return
}
LastTime = LastTimeTemp.Unix()
} else {
LastTime = CurrentTime
}
这个时间作用是获得一个合理的搜索时间范围,防止数据库中有不合法的视频信息存在。
获取视频 id 和对应作者 id 列表
此处采用模块化编程思路,具体获取的操作封装为一个函数实现,feed 中直接调用该函数并且获取总共获取到的视频总数:
go
var videoList []entity.Video
var videoIdList []int64
var authorIdList []int64
// 获得接收视频的总量,并且在相关列表填入信息
numVideo, err := service.GetNumVideo(&videoList, &videoIdList, &authorIdList, LastTime, global.MaxNumVideo)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status_code": -1, "status_msg": "Fail to get the number of video."})
return
}
这个列表信息的获取只需要调用 GORM 对数据库进行搜索即可:
go
// GetNumVideo 获取视频列表中符合结果的视频数据,以及相应的视频,作者信息列表
func GetNumVideo(videos *[]entity.Video, videoIdList *[]int64, AuthorIdList *[]int64, LastTime int64, MaxNumVide int) (int, error) {
// 给定搜索条件以及搜素顺序填充视频列表
query := global.DB.Order("created_at desc").
Limit(MaxNumVide).
Where("UNIX_TIMESTAMP(created_at) <= ?", LastTime)
query.Find(videos)
// 获得视频总数
numVideo := len(*videos)
// 统计作者 id 以及视频 id
*AuthorIdList = make([]int64, numVideo)
*videoIdList = make([]int64, numVideo)
for i, video := range *videos {
(*AuthorIdList)[i] = video.UserId
(*videoIdList)[i] = video.VideoId
}
return numVideo, nil
}
其实这里归根到底实现的是一个数据库 sql 语句的操作。
特别的,如果视频总数为0,那么证明此时整个服务中没有视频信息,可以返回了,如果不为0,再进行后续操作。
获得点赞信息
每个视频的点赞信息都依赖数据库检索,以一个结构体进行存储:
go
type VideoLikeCnt struct {
VideoId int64 `gorm:"column:video_id;primary_key;NOT NULL"`
LikeCnt int64 `gorm:"column:like_cnt"`
}
那么只要根据视频 id 获得一个 VideoLikeCnt
的列表,就可以获得每个视频的点赞信息了,获取方式封装在一个函数中
go
func QueryLikeCountListByVideoIdList(videoIdList *[]int64) ([]entity.VideoLikeCnt, error) {
var getLikeCountList []entity.VideoLikeCnt
result := global.DB.Model(&entity.Like{}).Select("video_id", "count(video_id) as like_cnt").Where(map[string]interface{}{"video_id": *videoIdList}).Group("video_id").Find(&getLikeCountList)
if result.Error != nil {
err := errors.New("likesList query failed")
return nil, err
}
return getLikeCountList, nil
}
// Feed 中调用方式
LikeVideoList, err := QueryLikeCountListByVideoIdList(&videoIdList)
特别注意,上述查找方式中,如果一个视频点赞量为0,那么其认为是零值,不会被存储到返回列表中。因此,实际上 LikeVideoList
很可能有数据缺失,因此在实际上填充视频点赞信息时还要多一层处理------即如果对应视频 id 能在 LikeVideoList
中被找到,则返回对应的 LikeCnt
, 否则返回0,这个可以封装在一个函数中:
go
// 在一个给定 VideoLikeCnt 列表中查找给定视频 id 是否存在,不存在返回 0,存在返回点赞值
func FindVideoIdFromVideoLikeCntList(videoId int64, likeCountList *[]entity.VideoLikeCnt) int64 {
for _, element := range *likeCountList {
if videoId == element.VideoId {
return element.LikeCnt
}
}
return 0
}
获得评论数量
这个数量获取和点赞数获取相同,此处省略细节,仅仅列出大纲:
- 定义视频评论数量结构体
VideoCommentCnt
- 通过视频 id 列表结合数据库查找获得他们对应的评论数,用信息构造一个
VideoCommentCnt
列表 - 其中的信息并不完整,对未存入的视频,都统一返回0处理
获得当前用户对该视频点赞/该作者关注情况
自然,这个和登录状态有关,如果未登录,直接默认设置为0。由此我们需要检查 token 权鉴,相关查询到的登录结果存入 isLogged
这一标识当中。
在登录状态下,直接从数据库中搜索即可完成点赞和关注两个状态的判断,此处直接根据当前 id 和视频 id, 作者 id 进行数据库搜索即可,实现略。
写入信息
最后只要把上述获得所有信息写入一个列表中,然后把这个列表输出,此处只是一些体力活,细节省略。特别的,此处还有一个需要实现的任务就是根据作者 id 获得作者信息,但这个也只需要封装一个函数 func UserInfoByUserId(userId int64) (userdata entity.UserData, err error)
,并结合登录状态修改关注状态即可。
总结
以上思路实现了一个 feed 接口的简易模式,实现过程比较繁琐,但是整体流程比较清晰,也具有相当的项目锻练价值。