视频播放接口 feed 的设计思路 | 青训营

本篇笔记旨在记录视频播放接口 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
}

获得评论数量

这个数量获取和点赞数获取相同,此处省略细节,仅仅列出大纲:

  1. 定义视频评论数量结构体 VideoCommentCnt
  2. 通过视频 id 列表结合数据库查找获得他们对应的评论数,用信息构造一个 VideoCommentCnt 列表
  3. 其中的信息并不完整,对未存入的视频,都统一返回0处理

获得当前用户对该视频点赞/该作者关注情况

自然,这个和登录状态有关,如果未登录,直接默认设置为0。由此我们需要检查 token 权鉴,相关查询到的登录结果存入 isLogged 这一标识当中。

在登录状态下,直接从数据库中搜索即可完成点赞和关注两个状态的判断,此处直接根据当前 id 和视频 id, 作者 id 进行数据库搜索即可,实现略。

写入信息

最后只要把上述获得所有信息写入一个列表中,然后把这个列表输出,此处只是一些体力活,细节省略。特别的,此处还有一个需要实现的任务就是根据作者 id 获得作者信息,但这个也只需要封装一个函数 func UserInfoByUserId(userId int64) (userdata entity.UserData, err error),并结合登录状态修改关注状态即可。

总结

以上思路实现了一个 feed 接口的简易模式,实现过程比较繁琐,但是整体流程比较清晰,也具有相当的项目锻练价值。

相关推荐
CallBack8 个月前
Typora+PicGo+阿里云OSS搭建个人图床,纵享丝滑!
前端·青训营笔记
Taonce1 年前
站在Android开发者的角度认识MQTT - 源码篇
android·青训营笔记
AB_IN1 年前
打开抖音会发生什么 | 青训营
青训营笔记
monster1231 年前
结营感受(go) | 青训营
青训营笔记
翼同学1 年前
实践记录:使用Bcrypt进行密码安全性保护和验证 | 青训营
青训营笔记
hu1hu_1 年前
Git 的正确使用姿势与最佳实践(1) | 青训营
青训营笔记
星曈1 年前
详解前端框架中的设计模式 | 青训营
青训营笔记
tuxiaobei1 年前
文件上传漏洞 Upload-lab 实践(中)| 青训营
青训营笔记
yibao1 年前
高质量编程与性能调优实战 | 青训营
青训营笔记
小金先生SG1 年前
阿里云对象存储OSS使用| 青训营
青训营笔记