Skip to content

syqszu/tiktok-demo

Repository files navigation

第六届字节跳动青训营 - 后端大项目 douyin-demo

依赖项

  • FFmpeg

Windows:

winget install FFmpeg

FFmpeg

视频使用了FFmpeg截取了第一秒第一帧作为封面

publish.go

// FFmpeg命令截图
cmd := exec.Command("FFmpeg/ffmpeg.exe", "-i", "public/"+finalName, "-ss" , "1" , "-vframes", "1", "img/"+finalName+".jpg")
err = cmd.Run() //运行
if err != nil {
	fmt.Println(err)
}

异步操作对视频进行压缩转码,使得视频适合播放:

func transcodeVideo(finalName string) {
     //视频转码压缩
	cmd := exec.Command("ffmpeg", "-i", "public/"+finalName, "-c:v","libx264", "-crf" ,"23" ,"-preset" ,"medium", "-c:a" ,"aac", "-b:a", "128k" ,"public/"+"new"+finalName)
	err := cmd.Run() //运行
	if err != nil {
		fmt.Println(err)
	}
	os.Remove("public/"+finalName) //移除原文件
	err = os.Rename("public/"+"new"+finalName,"public/"+finalName) //重命名转码文件
	if err != nil {
        fmt.Println("Error renaming file:", err)
        return
    }

使用Redis实现了关注操作,关注列表,粉丝列表

思路

思路是将要传递的user结构体转换成字符串数据存储在redis里,需要使用的时候再取出来。

token验证和查询用户都可以使用Redis优化数据

使用Redis的存储思路如图:

image-20230820113047495

用用户ID可以访问用户的token进行验证,用户的token可以取出用户本体

关注列表用用户的ID作为key,被关注的用户ID作为field,value为关注用户的json数据

关注列表用用户的token作为key,粉丝用户ID作为field,value为粉丝用户的json数据

user.go 在登录和注册的时候将用户数据注册进redis的哈希结构中,用 HSetNX(分布式锁)的方式存储,防止重复操作

单元测试

需要Mock的外部依赖:

  • 通过GORM框架进行的数据库操作
  • Gin框架实现的HTTP请求与响应

数据库操作

在各个路由的Handler中,我们经常使用GORM进行数据库操作,因此单元测试需要检验数据库操作是否复合预期。由于GORM在内部拼接SQL语句并发送给数据库处理,我们可以使用 go-sqlmock 模拟数据库操作,并对GORM输出给数据库的SQL语句进行检查。

生成模拟的*sql.DB连接:

mockDb, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("Failed to create sqlmock: %s", err)
}
defer mockDb.Close()

由于被测试的函数通常需要接受一个*gorm.DB对象作为参数,或通过*gin.Context获取*gorm.DB对象,我们需要从模拟的数据库连接构建*gorm.DB

gormDb, err := gorm.Open(mysql.New(mysql.Config{
Conn: mockDb,
}), &gorm.Config{})

if err != nil {
t.Fatalf("Failed to create gorm database: %s", err)
}

这个*gorm.DB对象可以被传递给被测试的函数用于测试。

其它相关框架:

HTTP请求与响应

根据 Gin 官方文档,我们可以通过标准库net/http/httptest对HTTP请求和响应进行单元测试。

首先需要构建*gin.Engine对象并配置需要测试的API路由:

func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
return r
}

然后使用net/http构建测试的HTTP请求,并通过gin.Engine.ServeHTTP()模拟接收到的请求:

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPingRoute(t *testing.T) {
	router := setupRouter()

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/ping", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)
	assert.Equal(t, "pong", w.Body.String())
}

其它相关框架:

该方案存在的问题:

  • Gin Handler中的错误不能在单元测试中获取,只能通过调试查看错误。
  • go-sqlmock 直接匹配SQL语句太严格,任何对数据库操作的变更都需要更新单元测试。更好的方式是在数据库操作结束后检查期望发生的更改,但没有找到合适的框架。考虑其它方案:使用针对GORM的Mock框架

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages