[
  {
    "path": ".gitignore",
    "content": ".idea\n!deprecated/static/1-1.jpg\n!deprecated/static/1-1.mp4\ndeprecated/static/*"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 L_B__\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[写给后续参加字节跳动青训营的同学](https://github.com/ACking-you/byte_douyin_project/issues/10)\n# 抖音极简版\n<!-- PROJECT SHIELDS -->\n\n[![Contributors][contributors-shield]][contributors-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n\n<!-- links -->\n[your-project-path]:ACking-you/byte_douyin_project\n[contributors-shield]: https://img.shields.io/github/contributors/ACking-you/byte_douyin_project.svg?style=flat-square\n[contributors-url]: https://github.com/ACking-you/byte_douyin_project/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/ACking-you/byte_douyin_project.svg?style=flat-square\n[forks-url]: https://github.com/ACking-you/byte_douyin_project/network/members\n[stars-shield]: https://img.shields.io/github/stars/ACking-you/byte_douyin_project.svg?style=flat-square\n[stars-url]: https://github.com/ACking-you/byte_douyin_project/stargazers\n[issues-shield]: https://img.shields.io/github/issues/ACking-you/byte_douyin_project.svg?style=flat-square\n[issues-url]: https://img.shields.io/github/issues/ACking-you/byte_douyin_project.svg\n[license-shield]: https://img.shields.io/github/license/ACking-you/byte_douyin_project?style=flat-square\n[license-url]: https://github.com/ACking-you/byte_douyin_project/blob/master/LICENSE\n\n\n\n* [数据库说明](#数据库说明)\n    * [数据库关系说明](#数据库关系说明)\n    * [数据库建立说明](#数据库建立说明)\n* [架构说明](#架构说明)\n    * [各模块代码详细说明](#各模块代码详细说明)\n        * [Handlers](#handlers)\n        * [Service](#service)\n        * [Models](#models)\n* [遇到的问题及对应解决方案](#遇到的问题及对应解决方案)\n    * [返回json数据的完整性和前端要求的一致性](#返回json数据的完整性和前端要求的一致性)\n    * [is\\_favorite和is\\_follow字段的更新](#is_favorite和is_follow字段的更新)\n    * [视频的保存和封面的切片](#视频的保存和封面的切片)\n        * [视频的保存](#视频的保存)\n        * [封面的截取](#封面的截取)\n* [可改进的地方](#可改进的地方)\n* [项目运行](#项目运行)\n\n## 数据库说明\n\n\n![database.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/892fbbe46695467ebe4fb4a12ebd412e~tplv-k3u1fbpfcp-watermark.image?)\n\n> 单纯看上面的图会感觉很混乱，现在我们来将关系拆解。\n\n### 数据库关系说明\n\n**关系图如下：**\n\n\n![database_relation.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f08918db1ea84126bc21d23fe9401a75~tplv-k3u1fbpfcp-watermark.image?)\n\n> 所有的表都有自己的id主键为唯一的标识。\n\nuser_logins：存下用户的用户名和密码\n\nuser_infos：存下用户的基本信息\n\nvideos：存下视频的基本信息\n\ncomment：存下每个评论的基本信息\n\n**具体的关系索引：**\n\n所有的一对一和一对多关系，只需要在一个表中加入对方的id索引。\n\n* 比如user_infos和user_logins的一对一关系，在user_logins中加入user_id字段设为外键存储user_infos中对应的行的id信息。\n* 比如user_infos和和videos的一对多关系，在videos中加入user_id字段设为外键存储user_infos中对应的行的id信息。\n\n所有的多对多关系，需要多建立一张表，用该表作为媒介存储两个对象的id作为关系的产生，而它们各自表中不需要再存下额外的字段。\n\n* 比如user_infos和videos的多对多关系，创建一张user_favor_videos中间表，然后将该表的字段均设为外键，分别存下user_infos和videos对应行的id。如id为1的用户对id为2的视频点了个赞，那么就把这个1和2存入中间表user_favor_videos即可。\n\n### 数据库建立说明\n\n数据库各表的建立无需自己实现额外的建表操作，一切都由gorm框架自动建表，具体逻辑在models层的代码中。\n\n> gorm官方文档链接：[链接](https://gorm.io/zh_CN/docs/index.html)\n\n建表和初始化操作由init_db.go来执行：\n\n```go\nfunc InitDB() {\n\tvar err error\n\tDB, err = gorm.Open(mysql.Open(config.DBConnectString()), &gorm.Config{\n\t\tPrepareStmt:            true, //缓存预编译命令\n\t\tSkipDefaultTransaction: true, //禁用默认事务操作\n\t\t//Logger:                 logger.Default.LogMode(logger.Info), //打印sql语句\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\terr = DB.AutoMigrate(&UserInfo{}, &Video{}, &Comment{}, &UserLogin{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\n## 架构说明\n\n\n![architecture.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae11d82b8de74787a258ef36f4cf2557~tplv-k3u1fbpfcp-watermark.image?)\n\n> 以用户登录为例共需要经过以下过程：\n\n1. 进入中间件SHAMiddleWare内的函数逻辑，得到password明文加密后再设置password。具体需要调用gin.Context的Set方法设置password。随后调用next()方法继续下层路由。\n2. 进入UserLoginHandler函数逻辑，获取username，并调用gin.Context的Get方法得到中间件设置的password。再调用service层的QueryUserLogin函数。\n3. 进入QueryUserLogin函数逻辑，执行三个过程：checkNum，prepareData，packData。也就是检查参数、准备数据、打包数据，准备数据的过程中会调用models层的UserLoginDAO。\n4. 进入UserLoginDAO的逻辑，执行最终的数据库请求过程，返回给上层。\n\n### 各模块代码详细说明\n\n我开发的过程中是以单个函数为单个文件进行开发，所以代码会比较长，故我根据数据库内的模型对函数文件进行了如下分包：\n\n\n![handlers.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6dc222793d6f4038b1bf2435053bfee4~tplv-k3u1fbpfcp-watermark.image?)\n\nservice层的分包也是一样的。\n\n#### Handlers\n\n对于handlers层级的所有函数实现有如下规范：\n\n所有的逻辑由代理对象进行，完成以下两个逻辑\n\n1. 解析得到参数。\n2. 开始调用下层逻辑。\n\n例如一个关注动作触发的逻辑：\n\n```go\nNewProxyPostFollowAction().Do()\n//其中Do主要包含以下两个逻辑，对应两个方法\np.parseNum() //解析参数\np.startAction() //开始调用下层逻辑\n```\n\n#### Service\n\n对于service层级的函数实现由如下规范：\n\n同样由一个代理对象进行，完成以下三个或两个逻辑\n\n当上层需要返回数据信息，则进行三个逻辑：\n\n1. 检查参数。\n2. 准备数据。\n3. 打包数据。\n\n当上层不需要返回数据信息，则进行两个逻辑：\n\n1. 检查参数。\n2. 执行上层指定的动作。\n\n例如关注动作在service层的逻辑属于第二类：\n\n```go\nNewPostFollowActionFlow(...).Do()\n//Do中包含以下两个逻辑\np.checkNum() //检查参数\np.publish() //执行动作\n```\n\n#### Models\n\n对于models层的各个操作，没有像service和handler层针对前端发来的请求就行对应的处理，models层是面向于数据库的增删改查，不需要考虑和上层的交互。\n\n而service层根据上层的需要来调用models层的不同代码请求数据库内的内容。\n\n## 遇到的问题及对应解决方案\n\n### 返回json数据的完整性和前端要求的一致性\n\n由于数据库内的一对一、一对多、多对多关系是根据id进行映射，所以models层请求得到的字段并不包含前端所需要的直接数据，比如前端要求Comment结构中需要包含UserInfo，而我的Comment结构如下：\n\n```go\ntype Comment struct {\n\tId         int64     `json:\"id\"`\n\tUserInfoId int64     `json:\"-\"` //用于一对多关系的id\n\tVideoId    int64     `json:\"-\"` //一对多，视频对评论\n\tUser       UserInfo  `json:\"user\" gorm:\"-\"`\n\tContent    string    `json:\"content\"`\n\tCreatedAt  time.Time `json:\"-\"`\n\tCreateDate string    `json:\"create_date\" gorm:\"-\"`\n}\n```\n\n很明显，为了与数据库中设计的表一一对应，在原数据的基础上加了几个字段，且在gorm屏蔽了User字段，所以service调用models层得到是Comment数据中User字段还未被填充，还需再填充这部分内容，好在由对应的UserId，故可以正确填充该字段。\n\n为了重用以及不破坏代码的一致性，将填充逻辑写入util包内，比如以上的字段填充函数，同时前端要求的日期格式也能够按要求设置：\n\n```go\nfunc FillCommentListFields(comments *[]*models.Comment) error {\n\tsize := len(*comments)\n\tif comments == nil || size == 0 {\n\t\treturn errors.New(\"util.FillCommentListFields comments为空\")\n\t}\n\tdao := models.NewUserInfoDAO()\n\tfor _, v := range *comments {\n\t\t_ = dao.QueryUserInfoById(v.UserInfoId, &v.User) //填充这条评论的作者信息\n\t\tv.CreateDate = v.CreatedAt.Format(\"1-2\")         //转为前端要求的日期格式\n\t}\n\treturn nil\n}\n```\n\n这里举了Comment这一个例子，其他的Video也是同理。\n\n### is_favorite和is_follow字段的更新\n\n每次为视频点赞都会在数据库的user_favor_videos表中加入用户的id和视频的id，很明显is_favorite字段是针对每个用户来判断的，而我所设计的数据库中的videos表也是包含这个字段的，但这个字段很明显不能直接进行复用，而是需要每次判断用户和此视频的关系来重新更新。\n\n这个更新过程放入util包的填充函数中即可，为了点赞过程的迅速响应，我采取了nosql的方式存储了这个点赞的映射，也就是userId和videoId的映射，也就是用nosql代替了这个中间表的功效。\n\n具体代码逻辑在cache包内。\n\n### 视频的保存和封面的切片\n\n#### 视频的保存\n\n在本地建立static文件夹存储视频和封面图片。\n\n具体逻辑如下：\n\n1. 检查视频格式\n2. 根据userId和该作者发布的视频数量产生唯一的名称，如id为1的用户发布了0个视频，那么本次上传的名称为1-0.mp4\n3. 截取第一帧画面作为封面\n4. 保存视频基本信息到数据库（包括视频链接和封面链接\n\n#### 封面的截取\n\n使用ffmpeg调用命令行对视频进行截取。\n\n设计ffmpeg请求类Video2Image，通过对它内部的参数设置来构造对应的命令行字符串。具体请看util包内的ffmpeg.go的实现。\n\n由于我设计的命令请求字符串是直接的一行字符串，而go语言exec包里面的Command函数执行所需的仅仅是一个个参数。\n\n所以此处我想到用cgo直接调用 system(args)来解决。\n\n代码如下：\n\n```go\n//#include <stdlib.h>\n//int startCmd(const char* cmd){\n//\t  return system(cmd);\n//}\nimport \"C\"\n\nfunc (v *Video2Image) ExecCommand(cmd string) error {\n\tif v.debug {\n\t\tlog.Println(cmd)\n\t}\n\tcCmd := C.CString(cmd)\n\tdefer C.free(unsafe.Pointer(cCmd))\n\tstatus := C.startCmd(cCmd)\n\tif status != 0 {\n\t\treturn errors.New(\"视频切截图失败\")\n\t}\n\treturn nil\n}\n```\n\n## 可改进的地方\n\n1. 写到后面发现很多mysql的数据可以用redis优化。\n2. 很多执行逻辑可以通过并行优化。\n3. 路由分组可以更为详实。\n4. ...\n\n## 项目运行\n\n> 本项目运行不需要手动建表，项目启动后会自动建表。\n\n**运行所需环境**：\n\n* mysql 5.7及以上\n* redis 5.0.14及以上\n* ffmepg（已放入lib自带，用于对视频切片得到封面\n* 需要gcc环境（主要用于cgo，windows请将mingw-w64设置到环境变量\n\n**运行需要更改配置**：\n\n> 进入config目录更改对应的mysql、redis、server、path信息。\n\n* mysql：mysql相关的配置信息\n* redis：redis相关配置信息\n* server：当前服务器（当前启动的机器）的配置信息，用于生成对应的视频和图片链接\n* path：其中ffmpeg_path为lib里的文件路径，static_source_path为本项目的static目录，这里请根据本地的绝对路径进行更改\n\n> 完成config配置文件的更改后，需要再更改conf.go里的解析文件路径为config.toml文件的绝对路径，内容如下：\n>\n> ```go\n> if _, err := toml.DecodeFile(\"你的绝对路径\\\\config.toml\", &Info); err != nil {\n> \t\tpanic(err)\n> \t}\n> ```\n>\n>\n\n**运行所需命令**：\n\n```shell\ncd .\\byte_douyin_project\\\ngo run main.go\n```\n"
  },
  {
    "path": "deprecated/cache/index_map.go",
    "content": "package cache\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"github.com/go-redis/redis/v8\"\n)\n\n// 用户id->被点赞的视频id集合->是否含有该视频id\n\nvar ctx = context.Background()\nvar rdb *redis.Client\n\nconst (\n\tfavor    = \"favor\"\n\trelation = \"relation\"\n)\n\nfunc init() {\n\trdb = redis.NewClient(\n\t\t&redis.Options{\n\t\t\tAddr:     fmt.Sprintf(\"%s:%d\", config.Global.RDB.IP, config.Global.RDB.Port),\n\t\t\tPassword: \"\", //没有设置密码\n\t\t\tDB:       config.Global.RDB.Database,\n\t\t})\n}\n\nvar (\n\tproxyIndexOperation ProxyIndexMap\n)\n\ntype ProxyIndexMap struct {\n}\n\nfunc NewProxyIndexMap() *ProxyIndexMap {\n\treturn &proxyIndexOperation\n}\n\n// UpdateVideoFavorState 更新点赞状态，state:true为点赞，false为取消点赞\nfunc (i *ProxyIndexMap) UpdateVideoFavorState(userId int64, videoId int64, state bool) {\n\tkey := fmt.Sprintf(\"%s:%d\", favor, userId)\n\tif state {\n\t\trdb.SAdd(ctx, key, videoId)\n\t\treturn\n\t}\n\trdb.SRem(ctx, key, videoId)\n}\n\n// GetVideoFavorState 得到点赞状态\nfunc (i *ProxyIndexMap) GetVideoFavorState(userId int64, videoId int64) bool {\n\tkey := fmt.Sprintf(\"%s:%d\", favor, userId)\n\tret := rdb.SIsMember(ctx, key, videoId)\n\treturn ret.Val()\n}\n\n// UpdateUserRelation 更新点赞状态，state:true为点关注，false为取消关注\nfunc (i *ProxyIndexMap) UpdateUserRelation(userId int64, followId int64, state bool) {\n\tkey := fmt.Sprintf(\"%s:%d\", relation, userId)\n\tif state {\n\t\trdb.SAdd(ctx, key, followId)\n\t\treturn\n\t}\n\trdb.SRem(ctx, key, followId)\n}\n\n// GetUserRelation 得到关注状态\nfunc (i *ProxyIndexMap) GetUserRelation(userId int64, followId int64) bool {\n\tkey := fmt.Sprintf(\"%s:%d\", relation, userId)\n\tret := rdb.SIsMember(ctx, key, followId)\n\treturn ret.Val()\n}\n"
  },
  {
    "path": "deprecated/cache/indexmap_test.go",
    "content": "package cache\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestProxyIndexMap_UpdateUserRelation(t *testing.T) {\n\tNewProxyIndexMap().UpdateUserRelation(1, 2, true)\n\tfmt.Println(NewProxyIndexMap().GetUserRelation(1, 3))\n}\n\nfunc TestProxyIndexMap_GetVideoFavorState(t *testing.T) {\n\tfmt.Println(NewProxyIndexMap().GetVideoFavorState(1, 2))\n\tNewProxyIndexMap().UpdateVideoFavorState(1, 2, true)\n\tfmt.Println(NewProxyIndexMap().GetVideoFavorState(1, 2))\n}\n"
  },
  {
    "path": "deprecated/config/conf.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"github.com/BurntSushi/toml\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\ntype Mysql struct {\n\tHost      string\n\tPort      int\n\tDatabase  string\n\tUsername  string\n\tPassword  string\n\tCharset   string\n\tParseTime bool `toml:\"parse_time\"`\n\tLoc       string\n}\n\ntype Redis struct {\n\tIP       string\n\tPort     int\n\tDatabase int\n}\n\ntype Server struct {\n\tIP   string\n\tPort int\n}\n\ntype Path struct {\n\tFfmpegPath       string `toml:\"ffmpeg_path\"`\n\tStaticSourcePath string `toml:\"static_source_path\"`\n}\n\ntype Config struct {\n\tDB     Mysql `toml:\"mysql\"`\n\tRDB    Redis `toml:\"redis\"`\n\tServer `toml:\"server\"`\n\tPath   `toml:\"path\"`\n}\n\nvar Global Config\n\nfunc ensurePathValid() {\n\tvar err error\n\tif _, err = os.Stat(Global.StaticSourcePath); os.IsNotExist(err) {\n\t\tif err = os.Mkdir(Global.StaticSourcePath, 0755); err != nil {\n\t\t\tlog.Fatalf(\"mkdir error:path %s\", Global.StaticSourcePath)\n\t\t}\n\t}\n\tif _, err = os.Stat(Global.FfmpegPath); os.IsNotExist(err) {\n\t\tif _, err = exec.Command(\"ffmpeg\", \"-version\").Output(); err != nil {\n\t\t\tlog.Fatalf(\"ffmpeg not valid %s\", Global.FfmpegPath)\n\t\t} else {\n\t\t\tGlobal.FfmpegPath = \"ffmpeg\"\n\t\t}\n\t} else {\n\t\tGlobal.FfmpegPath, err = filepath.Abs(Global.FfmpegPath)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(\"get abs path failed:\", Global.FfmpegPath)\n\t\t}\n\t}\n\t//把资源路径转化为绝对路径，防止调用ffmpeg命令失效\n\tGlobal.StaticSourcePath, err = filepath.Abs(Global.StaticSourcePath)\n\tif err != nil {\n\t\tlog.Fatalln(\"get abs path failed:\", Global.StaticSourcePath)\n\t}\n}\n\n//包初始化加载时候会调用的函数\nfunc init() {\n\tif _, err := toml.DecodeFile(\"./config/config.toml\", &Global); err != nil {\n\t\tpanic(err)\n\t}\n\t//去除左右的空格\n\tstrings.Trim(Global.Server.IP, \" \")\n\tstrings.Trim(Global.RDB.IP, \" \")\n\tstrings.Trim(Global.DB.Host, \" \")\n\t//保证路径正常\n\tensurePathValid()\n}\n\n// DBConnectString 填充得到数据库连接字符串\nfunc DBConnectString() string {\n\targ := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%v&loc=%s\",\n\t\tGlobal.DB.Username, Global.DB.Password, Global.DB.Host, Global.DB.Port, Global.DB.Database,\n\t\tGlobal.DB.Charset, Global.DB.ParseTime, Global.DB.Loc)\n\tlog.Println(arg)\n\treturn arg\n}\n"
  },
  {
    "path": "deprecated/config/config.toml",
    "content": "#关系型数据库配置\n[mysql]\nhost = \"127.0.0.1\"\nport = 3306\ndatabase = \"douyin\"\nusername = \"root\"\npassword = \"123\"\ncharset = \"utf8mb4\"\nparse_time = true\nloc = \"Local\"\n\n#nosql配置，用于存储每个用户是否对某个视频点赞，以及关注了某个人（主要用于反馈前端情况，比如点了赞后会变成红心\n[redis]\nhost = \"127.0.0.1\"\nport = 49153\ndatabase = 0\n\n#记录当前服务器的ip和启动端口号，当前服务器的ip用于生成对应的视频链接地址\n[server]\nip = \"127.0.0.1\"\nport = 8080\n\n#用于保存资源的路径，以及用于截图工具的路径（截图工具放在lib目录\n[path]\nffmpeg_path = \"./lib/ffmpeg.exe\"\nstatic_source_path = \"./static\""
  },
  {
    "path": "deprecated/go.mod",
    "content": "module github.com/ACking-you/byte_douyin_project\n\ngo 1.16\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.1.0\n\tgithub.com/dgrijalva/jwt-go v3.2.0+incompatible\n\tgithub.com/gin-gonic/gin v1.7.7\n\tgithub.com/go-redis/redis/v8 v8.11.5 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgorm.io/driver/mysql v1.3.3\n\tgorm.io/gorm v1.23.5\n)\n"
  },
  {
    "path": "deprecated/go.sum",
    "content": "github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=\ngithub.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=\ngithub.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=\ngithub.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=\ngithub.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=\ngithub.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=\ngithub.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=\ngithub.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=\ngithub.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=\ngithub.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=\ngithub.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=\ngithub.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8=\ngorm.io/driver/mysql v1.3.3/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=\ngorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=\ngorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM=\ngorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=\n"
  },
  {
    "path": "deprecated/handlers/comment/post_comment_handler.go",
    "content": "package comment\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/comment\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"strconv\"\n)\n\ntype PostCommentResponse struct {\n\tmodels.CommonResponse\n\t*comment.Response\n}\n\nfunc PostCommentHandler(c *gin.Context) {\n\tNewProxyPostCommentHandler(c).Do()\n}\n\ntype ProxyPostCommentHandler struct {\n\t*gin.Context\n\n\tvideoId     int64\n\tuserId      int64\n\tcommentId   int64\n\tactionType  int64\n\tcommentText string\n}\n\nfunc NewProxyPostCommentHandler(context *gin.Context) *ProxyPostCommentHandler {\n\treturn &ProxyPostCommentHandler{Context: context}\n}\n\nfunc (p *ProxyPostCommentHandler) Do() {\n\t//解析参数\n\tif err := p.parseNum(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//正式调用Service层\n\tcommentRes, err := comment.PostComment(p.userId, p.videoId, p.commentId, p.actionType, p.commentText)\n\tif err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//成功返回\n\tp.SendOk(commentRes)\n}\n\nfunc (p *ProxyPostCommentHandler) parseNum() error {\n\trawUserId, _ := p.Get(\"user_id\")\n\tuserId, ok := rawUserId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\tp.userId = userId\n\n\trawVideoId := p.Query(\"video_id\")\n\tvideoId, err := strconv.ParseInt(rawVideoId, 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.videoId = videoId\n\n\t//根据actionType解析对应的可选参数\n\trawActionType := p.Query(\"action_type\")\n\tactionType, err := strconv.ParseInt(rawActionType, 10, 64)\n\tswitch actionType {\n\tcase comment.CREATE:\n\t\tp.commentText = p.Query(\"comment_text\")\n\tcase comment.DELETE:\n\t\tp.commentId, err = strconv.ParseInt(p.Query(\"comment_id\"), 10, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"未定义的行为%d\", actionType)\n\t}\n\tp.actionType = actionType\n\treturn nil\n}\n\nfunc (p *ProxyPostCommentHandler) SendError(msg string) {\n\tp.JSON(http.StatusOK, PostCommentResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg}, Response: &comment.Response{}})\n}\n\nfunc (p *ProxyPostCommentHandler) SendOk(comment *comment.Response) {\n\tp.JSON(http.StatusOK, PostCommentResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 0},\n\t\tResponse:       comment,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/comment/query_commentlist_handler.go",
    "content": "package comment\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/handlers/video\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/comment\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"strconv\"\n)\n\ntype ListResponse struct {\n\tmodels.CommonResponse\n\t*comment.List\n}\n\nfunc QueryCommentListHandler(c *gin.Context) {\n\tNewProxyCommentListHandler(c).Do()\n}\n\ntype ProxyCommentListHandler struct {\n\t*gin.Context\n\n\tvideoId int64\n\tuserId  int64\n}\n\nfunc NewProxyCommentListHandler(context *gin.Context) *ProxyCommentListHandler {\n\treturn &ProxyCommentListHandler{Context: context}\n}\n\nfunc (p *ProxyCommentListHandler) Do() {\n\t//解析参数\n\tif err := p.parseNum(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//正式调用\n\tcommentList, err := comment.QueryCommentList(p.userId, p.videoId)\n\tif err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//成功返回\n\tp.SendOk(commentList)\n}\n\nfunc (p *ProxyCommentListHandler) parseNum() error {\n\trawUserId, _ := p.Get(\"user_id\")\n\tuserId, ok := rawUserId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\tp.userId = userId\n\n\trawVideoId := p.Query(\"video_id\")\n\tvideoId, err := strconv.ParseInt(rawVideoId, 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.videoId = videoId\n\n\treturn nil\n}\n\nfunc (p *ProxyCommentListHandler) SendError(msg string) {\n\tp.JSON(http.StatusOK, video.FavorVideoListResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg}})\n}\n\nfunc (p *ProxyCommentListHandler) SendOk(commentList *comment.List) {\n\tp.JSON(http.StatusOK, ListResponse{CommonResponse: models.CommonResponse{StatusCode: 0},\n\t\tList: commentList,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/user_info/post_follow_action_handler.go",
    "content": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/user_info\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"strconv\"\n)\n\nfunc PostFollowActionHandler(c *gin.Context) {\n\tNewProxyPostFollowAction(c).Do()\n}\n\ntype ProxyPostFollowAction struct {\n\t*gin.Context\n\n\tuserId     int64\n\tfollowId   int64\n\tactionType int\n}\n\nfunc NewProxyPostFollowAction(context *gin.Context) *ProxyPostFollowAction {\n\treturn &ProxyPostFollowAction{Context: context}\n}\n\nfunc (p *ProxyPostFollowAction) Do() {\n\tvar err error\n\tif err = p.prepareNum(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\tif err = p.startAction(); err != nil {\n\t\t//当错误为model层发生的，那么就是重复键值的插入了\n\t\tif errors.Is(err, user_info.ErrIvdAct) || errors.Is(err, user_info.ErrIvdFolUsr) {\n\t\t\tp.SendError(err.Error())\n\t\t} else {\n\t\t\tp.SendError(\"请勿重复关注\")\n\t\t}\n\t\treturn\n\t}\n\tp.SendOk(\"操作成功\")\n}\n\nfunc (p *ProxyPostFollowAction) prepareNum() error {\n\trawUserId, _ := p.Get(\"user_id\")\n\tuserId, ok := rawUserId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\tp.userId = userId\n\n\t//解析需要关注的id\n\tfollowId := p.Query(\"to_user_id\")\n\tparseInt, err := strconv.ParseInt(followId, 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.followId = parseInt\n\n\t//解析action_type\n\tactionType := p.Query(\"action_type\")\n\tparseInt, err = strconv.ParseInt(actionType, 10, 32)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.actionType = int(parseInt)\n\treturn nil\n}\n\nfunc (p *ProxyPostFollowAction) startAction() error {\n\terr := user_info.PostFollowAction(p.userId, p.followId, p.actionType)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (p *ProxyPostFollowAction) SendError(msg string) {\n\tp.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1, StatusMsg: msg})\n}\n\nfunc (p *ProxyPostFollowAction) SendOk(msg string) {\n\tp.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1, StatusMsg: msg})\n}\n"
  },
  {
    "path": "deprecated/handlers/user_info/query_follow_list_handler.go",
    "content": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/user_info\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\ntype FollowListResponse struct {\n\tmodels.CommonResponse\n\t*user_info.FollowList\n}\n\nfunc QueryFollowListHandler(c *gin.Context) {\n\tNewProxyQueryFollowList(c).Do()\n}\n\ntype ProxyQueryFollowList struct {\n\t*gin.Context\n\n\tuserId int64\n\n\t*user_info.FollowList\n}\n\nfunc NewProxyQueryFollowList(context *gin.Context) *ProxyQueryFollowList {\n\treturn &ProxyQueryFollowList{Context: context}\n}\n\nfunc (p *ProxyQueryFollowList) Do() {\n\tvar err error\n\tif err = p.parseNum(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\tif err = p.prepareData(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\tp.SendOk(\"请求成功\")\n}\n\nfunc (p *ProxyQueryFollowList) parseNum() error {\n\trawUserId, _ := p.Get(\"user_id\")\n\tuserId, ok := rawUserId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\tp.userId = userId\n\treturn nil\n}\n\nfunc (p *ProxyQueryFollowList) prepareData() error {\n\tlist, err := user_info.QueryFollowList(p.userId)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.FollowList = list\n\treturn nil\n}\n\nfunc (p *ProxyQueryFollowList) SendError(msg string) {\n\tp.JSON(http.StatusOK, FollowListResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg},\n\t})\n}\n\nfunc (p *ProxyQueryFollowList) SendOk(msg string) {\n\tp.JSON(http.StatusOK, FollowListResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 0, StatusMsg: msg},\n\t\tFollowList:     p.FollowList,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/user_info/query_follower_handler.go",
    "content": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\tuser_info2 \"github.com/ACking-you/byte_douyin_project/service/user_info\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\ntype FollowerListResponse struct {\n\tmodels.CommonResponse\n\t*user_info2.FollowerList\n}\n\nfunc QueryFollowerHandler(c *gin.Context) {\n\tNewProxyQueryFollowerHandler(c).Do()\n}\n\ntype ProxyQueryFollowerHandler struct {\n\t*gin.Context\n\n\tuserId int64\n\n\t*user_info2.FollowerList\n}\n\nfunc NewProxyQueryFollowerHandler(context *gin.Context) *ProxyQueryFollowerHandler {\n\treturn &ProxyQueryFollowerHandler{Context: context}\n}\n\nfunc (p *ProxyQueryFollowerHandler) Do() {\n\tvar err error\n\tif err = p.parseNum(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\tif err = p.prepareData(); err != nil {\n\t\tif errors.Is(err, user_info2.ErrUserNotExist) {\n\t\t\tp.SendError(err.Error())\n\t\t} else {\n\t\t\tp.SendError(\"准备数据出错\")\n\t\t}\n\t\treturn\n\t}\n\tp.SendOk(\"成功\")\n}\n\nfunc (p *ProxyQueryFollowerHandler) parseNum() error {\n\trawUserId, _ := p.Get(\"user_id\")\n\tuserId, ok := rawUserId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\tp.userId = userId\n\treturn nil\n}\n\nfunc (p *ProxyQueryFollowerHandler) prepareData() error {\n\tlist, err := user_info2.QueryFollowerList(p.userId)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.FollowerList = list\n\treturn nil\n}\n\nfunc (p *ProxyQueryFollowerHandler) SendError(msg string) {\n\tp.JSON(http.StatusOK, FollowerListResponse{\n\t\tCommonResponse: models.CommonResponse{\n\t\t\tStatusCode: 1,\n\t\t\tStatusMsg:  msg,\n\t\t},\n\t})\n}\n\nfunc (p *ProxyQueryFollowerHandler) SendOk(msg string) {\n\tp.JSON(http.StatusOK, FollowerListResponse{\n\t\tCommonResponse: models.CommonResponse{\n\t\t\tStatusCode: 1,\n\t\t\tStatusMsg:  msg,\n\t\t},\n\t\tFollowerList: p.FollowerList,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/user_info/userinfo_handler.go",
    "content": "package user_info\n\nimport (\n\t\"errors\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\ntype UserResponse struct {\n\tmodels2.CommonResponse\n\tUser *models2.UserInfo `json:\"user\"`\n}\n\nfunc UserInfoHandler(c *gin.Context) {\n\tp := NewProxyUserInfo(c)\n\t//得到上层中间件根据token解析的userId\n\trawId, ok := c.Get(\"user_id\")\n\tif !ok {\n\t\tp.UserInfoError(\"解析userId出错\")\n\t\treturn\n\t}\n\terr := p.DoQueryUserInfoByUserId(rawId)\n\tif err != nil {\n\t\tp.UserInfoError(err.Error())\n\t}\n}\n\ntype ProxyUserInfo struct {\n\tc *gin.Context\n}\n\nfunc NewProxyUserInfo(c *gin.Context) *ProxyUserInfo {\n\treturn &ProxyUserInfo{c: c}\n}\n\nfunc (p *ProxyUserInfo) DoQueryUserInfoByUserId(rawId interface{}) error {\n\tuserId, ok := rawId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"解析userId失败\")\n\t}\n\t//由于得到userinfo不需要组装model层的数据，所以直接调用model层的接口\n\tuserinfoDAO := models2.NewUserInfoDAO()\n\n\tvar userInfo models2.UserInfo\n\terr := userinfoDAO.QueryUserInfoById(userId, &userInfo)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.UserInfoOk(&userInfo)\n\treturn nil\n}\n\nfunc (p *ProxyUserInfo) UserInfoError(msg string) {\n\tp.c.JSON(http.StatusOK, UserResponse{\n\t\tCommonResponse: models2.CommonResponse{StatusCode: 1, StatusMsg: msg},\n\t})\n}\n\nfunc (p *ProxyUserInfo) UserInfoOk(user *models2.UserInfo) {\n\tp.c.JSON(http.StatusOK, UserResponse{\n\t\tCommonResponse: models2.CommonResponse{StatusCode: 0},\n\t\tUser:           user,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/user_login/user_login_handler.go",
    "content": "package user_login\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/user_login\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\ntype UserLoginResponse struct {\n\tmodels.CommonResponse\n\t*user_login.LoginResponse\n}\n\nfunc UserLoginHandler(c *gin.Context) {\n\tusername := c.Query(\"username\")\n\traw, _ := c.Get(\"password\")\n\tpassword, ok := raw.(string)\n\tif !ok {\n\t\tc.JSON(http.StatusOK, UserLoginResponse{\n\t\t\tCommonResponse: models.CommonResponse{\n\t\t\t\tStatusCode: 1,\n\t\t\t\tStatusMsg:  \"密码解析错误\",\n\t\t\t},\n\t\t})\n\t}\n\tuserLoginResponse, err := user_login.QueryUserLogin(username, password)\n\n\t//用户不存在返回对应的错误\n\tif err != nil {\n\t\tc.JSON(http.StatusOK, UserLoginResponse{\n\t\t\tCommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: err.Error()},\n\t\t})\n\t\treturn\n\t}\n\n\t//用户存在，返回相应的id和token\n\tc.JSON(http.StatusOK, UserLoginResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 0},\n\t\tLoginResponse:  userLoginResponse,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/user_login/user_register_handler.go",
    "content": "package user_login\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\tuser_login2 \"github.com/ACking-you/byte_douyin_project/service/user_login\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\ntype UserRegisterResponse struct {\n\tmodels.CommonResponse\n\t*user_login2.LoginResponse\n}\n\nfunc UserRegisterHandler(c *gin.Context) {\n\tusername := c.Query(\"username\")\n\trawVal, _ := c.Get(\"password\")\n\tpassword, ok := rawVal.(string)\n\tif !ok {\n\t\tc.JSON(http.StatusOK, UserRegisterResponse{\n\t\t\tCommonResponse: models.CommonResponse{\n\t\t\t\tStatusCode: 1,\n\t\t\t\tStatusMsg:  \"密码解析出错\",\n\t\t\t},\n\t\t})\n\t\treturn\n\t}\n\tregisterResponse, err := user_login2.PostUserLogin(username, password)\n\n\tif err != nil {\n\t\tc.JSON(http.StatusOK, UserRegisterResponse{\n\t\t\tCommonResponse: models.CommonResponse{\n\t\t\t\tStatusCode: 1,\n\t\t\t\tStatusMsg:  err.Error(),\n\t\t\t},\n\t\t})\n\t\treturn\n\t}\n\tc.JSON(http.StatusOK, UserRegisterResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 0},\n\t\tLoginResponse:  registerResponse,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/video/feed_videolist_handler.go",
    "content": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/middleware\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/video\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n)\n\ntype FeedResponse struct {\n\tmodels.CommonResponse\n\t*video.FeedVideoList\n}\n\nfunc FeedVideoListHandler(c *gin.Context) {\n\tp := NewProxyFeedVideoList(c)\n\ttoken, ok := c.GetQuery(\"token\")\n\t//无登录状态\n\tif !ok {\n\t\terr := p.DoNoToken()\n\t\tif err != nil {\n\t\t\tp.FeedVideoListError(err.Error())\n\t\t}\n\t\treturn\n\t}\n\n\t//有登录状态\n\terr := p.DoHasToken(token)\n\tif err != nil {\n\t\tp.FeedVideoListError(err.Error())\n\t}\n}\n\ntype ProxyFeedVideoList struct {\n\t*gin.Context\n}\n\nfunc NewProxyFeedVideoList(c *gin.Context) *ProxyFeedVideoList {\n\treturn &ProxyFeedVideoList{Context: c}\n}\n\n// DoNoToken 未登录的视频流推送处理\nfunc (p *ProxyFeedVideoList) DoNoToken() error {\n\trawTimestamp := p.Query(\"latest_time\")\n\tvar latestTime time.Time\n\tintTime, err := strconv.ParseInt(rawTimestamp, 10, 64)\n\tif err == nil {\n\t\tlatestTime = time.Unix(0, intTime*1e6) //注意：前端传来的时间戳是以ms为单位的\n\t}\n\tvideoList, err := video.QueryFeedVideoList(0, latestTime)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.FeedVideoListOk(videoList)\n\treturn nil\n}\n\n// DoHasToken 如果是登录状态，则生成UserId字段\nfunc (p *ProxyFeedVideoList) DoHasToken(token string) error {\n\t//解析成功\n\tif claim, ok := middleware.ParseToken(token); ok {\n\t\t//token超时\n\t\tif time.Now().Unix() > claim.ExpiresAt {\n\t\t\treturn errors.New(\"token超时\")\n\t\t}\n\t\trawTimestamp := p.Query(\"latest_time\")\n\t\tvar latestTime time.Time\n\t\tintTime, err := strconv.ParseInt(rawTimestamp, 10, 64)\n\t\tif err != nil {\n\t\t\tlatestTime = time.Unix(0, intTime*1e6) //注意：前端传来的时间戳是以ms为单位的\n\t\t}\n\t\t//调用service层接口\n\t\tvideoList, err := video.QueryFeedVideoList(claim.UserId, latestTime)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tp.FeedVideoListOk(videoList)\n\t\treturn nil\n\t}\n\t//解析失败\n\treturn errors.New(\"token不正确\")\n}\n\nfunc (p *ProxyFeedVideoList) FeedVideoListError(msg string) {\n\tp.JSON(http.StatusOK, FeedResponse{CommonResponse: models.CommonResponse{\n\t\tStatusCode: 1,\n\t\tStatusMsg:  msg,\n\t}})\n}\n\nfunc (p *ProxyFeedVideoList) FeedVideoListOk(videoList *video.FeedVideoList) {\n\tp.JSON(http.StatusOK, FeedResponse{\n\t\tCommonResponse: models.CommonResponse{\n\t\t\tStatusCode: 0,\n\t\t},\n\t\tFeedVideoList: videoList,\n\t},\n\t)\n}\n"
  },
  {
    "path": "deprecated/handlers/video/post_favor_handler.go",
    "content": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/video\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"strconv\"\n)\n\nfunc PostFavorHandler(c *gin.Context) {\n\tNewProxyPostFavorHandler(c).Do()\n}\n\ntype ProxyPostFavorHandler struct {\n\t*gin.Context\n\n\tuserId     int64\n\tvideoId    int64\n\tactionType int64\n}\n\nfunc NewProxyPostFavorHandler(c *gin.Context) *ProxyPostFavorHandler {\n\treturn &ProxyPostFavorHandler{Context: c}\n}\n\nfunc (p *ProxyPostFavorHandler) Do() {\n\t//解析参数\n\tif err := p.parseNum(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//正式调用\n\terr := video.PostFavorState(p.userId, p.videoId, p.actionType)\n\tif err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//成功返回\n\tp.SendOk()\n}\n\nfunc (p *ProxyPostFavorHandler) parseNum() error {\n\t//解析userId\n\trawUserId, _ := p.Get(\"user_id\")\n\tuserId, ok := rawUserId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\n\trawVideoId := p.Query(\"video_id\")\n\tvideoId, err := strconv.ParseInt(rawVideoId, 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\trawActionType := p.Query(\"action_type\")\n\tactionType, err := strconv.ParseInt(rawActionType, 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.videoId = videoId\n\tp.actionType = actionType\n\tp.userId = userId\n\treturn nil\n}\n\nfunc (p *ProxyPostFavorHandler) SendError(msg string) {\n\tp.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1, StatusMsg: msg})\n}\n\nfunc (p *ProxyPostFavorHandler) SendOk() {\n\tp.JSON(http.StatusOK, models.CommonResponse{StatusCode: 0})\n}\n"
  },
  {
    "path": "deprecated/handlers/video/publish_video_handler.go",
    "content": "package video\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/video\"\n\tutil2 \"github.com/ACking-you/byte_douyin_project/util\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"path/filepath\"\n)\n\nvar (\n\tvideoIndexMap = map[string]struct{}{\n\t\t\".mp4\":  {},\n\t\t\".avi\":  {},\n\t\t\".wmv\":  {},\n\t\t\".flv\":  {},\n\t\t\".mpeg\": {},\n\t\t\".mov\":  {},\n\t}\n\tpictureIndexMap = map[string]struct{}{\n\t\t\".jpg\": {},\n\t\t\".bmp\": {},\n\t\t\".png\": {},\n\t\t\".svg\": {},\n\t}\n)\n\n// PublishVideoHandler 发布视频，并截取一帧画面作为封面\nfunc PublishVideoHandler(c *gin.Context) {\n\t//准备参数\n\trawId, _ := c.Get(\"user_id\")\n\n\tuserId, ok := rawId.(int64)\n\tif !ok {\n\t\tPublishVideoError(c, \"解析UserId出错\")\n\t\treturn\n\t}\n\n\ttitle := c.PostForm(\"title\")\n\n\tform, err := c.MultipartForm()\n\tif err != nil {\n\t\tPublishVideoError(c, err.Error())\n\t\treturn\n\t}\n\n\t//支持多文件上传\n\tfiles := form.File[\"data\"]\n\tfor _, file := range files {\n\t\tsuffix := filepath.Ext(file.Filename)    //得到后缀\n\t\tif _, ok := videoIndexMap[suffix]; !ok { //判断是否为视频格式\n\t\t\tPublishVideoError(c, \"不支持的视频格式\")\n\t\t\tcontinue\n\t\t}\n\t\tname := util2.NewFileName(userId) //根据userId得到唯一的文件名\n\t\tfilename := name + suffix\n\t\tsavePath := filepath.Join(config.Global.StaticSourcePath, filename)\n\t\terr = c.SaveUploadedFile(file, savePath)\n\t\tif err != nil {\n\t\t\tPublishVideoError(c, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\t//截取一帧画面作为封面\n\t\terr = util2.SaveImageFromVideo(name, false)\n\t\tif err != nil {\n\t\t\tPublishVideoError(c, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\t//数据库持久化\n\t\terr := video.PostVideo(userId, filename, name+util2.GetDefaultImageSuffix(), title)\n\t\tif err != nil {\n\t\t\tPublishVideoError(c, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tPublishVideoOk(c, file.Filename+\"上传成功\")\n\t}\n}\n\nfunc PublishVideoError(c *gin.Context, msg string) {\n\tc.JSON(http.StatusOK, models.CommonResponse{StatusCode: 1,\n\t\tStatusMsg: msg})\n}\n\nfunc PublishVideoOk(c *gin.Context, msg string) {\n\tc.JSON(http.StatusOK, models.CommonResponse{StatusCode: 0, StatusMsg: msg})\n}\n"
  },
  {
    "path": "deprecated/handlers/video/query_favor_videolist_handler.go",
    "content": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/video\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\ntype FavorVideoListResponse struct {\n\tmodels.CommonResponse\n\t*video.FavorList\n}\n\nfunc QueryFavorVideoListHandler(c *gin.Context) {\n\tNewProxyFavorVideoListHandler(c).Do()\n}\n\ntype ProxyFavorVideoListHandler struct {\n\t*gin.Context\n\n\tuserId int64\n}\n\nfunc NewProxyFavorVideoListHandler(c *gin.Context) *ProxyFavorVideoListHandler {\n\treturn &ProxyFavorVideoListHandler{Context: c}\n}\n\nfunc (p *ProxyFavorVideoListHandler) Do() {\n\t//解析参数\n\tif err := p.parseNum(); err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//正式调用\n\tfavorVideoList, err := video.QueryFavorVideoList(p.userId)\n\tif err != nil {\n\t\tp.SendError(err.Error())\n\t\treturn\n\t}\n\n\t//成功返回\n\tp.SendOk(favorVideoList)\n}\n\nfunc (p *ProxyFavorVideoListHandler) parseNum() error {\n\trawUserId, _ := p.Get(\"user_id\")\n\tuserId, ok := rawUserId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\tp.userId = userId\n\treturn nil\n}\n\nfunc (p *ProxyFavorVideoListHandler) SendError(msg string) {\n\tp.JSON(http.StatusOK, FavorVideoListResponse{\n\t\tCommonResponse: models.CommonResponse{StatusCode: 1, StatusMsg: msg}})\n}\n\nfunc (p *ProxyFavorVideoListHandler) SendOk(favorList *video.FavorList) {\n\tp.JSON(http.StatusOK, FavorVideoListResponse{CommonResponse: models.CommonResponse{StatusCode: 0},\n\t\tFavorList: favorList,\n\t})\n}\n"
  },
  {
    "path": "deprecated/handlers/video/query_videolist_handler.go",
    "content": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/service/video\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n)\n\ntype ListResponse struct {\n\tmodels.CommonResponse\n\t*video.List\n}\n\nfunc QueryVideoListHandler(c *gin.Context) {\n\tp := NewProxyQueryVideoList(c)\n\trawId, _ := c.Get(\"user_id\")\n\terr := p.DoQueryVideoListByUserId(rawId)\n\tif err != nil {\n\t\tp.QueryVideoListError(err.Error())\n\t}\n}\n\n// ProxyQueryVideoList 代理类\ntype ProxyQueryVideoList struct {\n\tc *gin.Context\n}\n\nfunc NewProxyQueryVideoList(c *gin.Context) *ProxyQueryVideoList {\n\treturn &ProxyQueryVideoList{c: c}\n}\n\n// DoQueryVideoListByUserId 根据userId字段进行查询\nfunc (p *ProxyQueryVideoList) DoQueryVideoListByUserId(rawId interface{}) error {\n\tuserId, ok := rawId.(int64)\n\tif !ok {\n\t\treturn errors.New(\"userId解析出错\")\n\t}\n\n\tvideoList, err := video.QueryVideoListByUserId(userId)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tp.QueryVideoListOk(videoList)\n\treturn nil\n}\n\nfunc (p *ProxyQueryVideoList) QueryVideoListError(msg string) {\n\tp.c.JSON(http.StatusOK, ListResponse{CommonResponse: models.CommonResponse{\n\t\tStatusCode: 1,\n\t\tStatusMsg:  msg,\n\t}})\n}\n\nfunc (p *ProxyQueryVideoList) QueryVideoListOk(videoList *video.List) {\n\tp.c.JSON(http.StatusOK, ListResponse{\n\t\tCommonResponse: models.CommonResponse{\n\t\t\tStatusCode: 0,\n\t\t},\n\t\tList: videoList,\n\t})\n}\n"
  },
  {
    "path": "deprecated/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"github.com/ACking-you/byte_douyin_project/router\"\n)\n\nfunc main() {\n\tr := router.Init()\n\terr := r.Run(fmt.Sprintf(\":%d\", config.Global.Port)) // listen and serve on 0.0.0.0:8080 (for windows \"localhost:8080\")\n\tif err != nil {\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "deprecated/middleware/jwt.go",
    "content": "package middleware\n\nimport (\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/dgrijalva/jwt-go\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"time\"\n)\n\nvar jwtKey = []byte(\"acking-you.xyz\")\n\ntype Claims struct {\n\tUserId int64\n\tjwt.StandardClaims\n}\n\n// ReleaseToken 颁发token\nfunc ReleaseToken(user models2.UserLogin) (string, error) {\n\texpirationTime := time.Now().Add(7 * 24 * time.Hour)\n\tclaims := &Claims{\n\t\tUserId: user.UserInfoId,\n\t\tStandardClaims: jwt.StandardClaims{\n\t\t\tExpiresAt: expirationTime.Unix(),\n\t\t\tIssuedAt:  time.Now().Unix(),\n\t\t\tIssuer:    \"douyin_pro_131\",\n\t\t\tSubject:   \"L_B__\",\n\t\t}}\n\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\ttokenString, err := token.SignedString(jwtKey)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn tokenString, nil\n}\n\n// ParseToken 解析token\nfunc ParseToken(tokenString string) (*Claims, bool) {\n\ttoken, _ := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {\n\t\treturn jwtKey, nil\n\t})\n\tif token != nil {\n\t\tif key, ok := token.Claims.(*Claims); ok {\n\t\t\tif token.Valid {\n\t\t\t\treturn key, true\n\t\t\t} else {\n\t\t\t\treturn key, false\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// JWTMiddleWare 鉴权中间件，鉴权并设置user_id\nfunc JWTMiddleWare() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\ttokenStr := c.Query(\"token\")\n\t\tif tokenStr == \"\" {\n\t\t\ttokenStr = c.PostForm(\"token\")\n\t\t}\n\t\t//用户不存在\n\t\tif tokenStr == \"\" {\n\t\t\tc.JSON(http.StatusOK, models2.CommonResponse{StatusCode: 401, StatusMsg: \"用户不存在\"})\n\t\t\tc.Abort() //阻止执行\n\t\t\treturn\n\t\t}\n\t\t//验证token\n\t\ttokenStruck, ok := ParseToken(tokenStr)\n\t\tif !ok {\n\t\t\tc.JSON(http.StatusOK, models2.CommonResponse{\n\t\t\t\tStatusCode: 403,\n\t\t\t\tStatusMsg:  \"token不正确\",\n\t\t\t})\n\t\t\tc.Abort() //阻止执行\n\t\t\treturn\n\t\t}\n\t\t//token超时\n\t\tif time.Now().Unix() > tokenStruck.ExpiresAt {\n\t\t\tc.JSON(http.StatusOK, models2.CommonResponse{\n\t\t\t\tStatusCode: 402,\n\t\t\t\tStatusMsg:  \"token过期\",\n\t\t\t})\n\t\t\tc.Abort() //阻止执行\n\t\t\treturn\n\t\t}\n\t\tc.Set(\"user_id\", tokenStruck.UserId)\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "deprecated/middleware/jwt_test.go",
    "content": "package middleware\n\nimport (\n\t\"testing\"\n)\n\nfunc TestJwt(t *testing.T) {\n\n}\n"
  },
  {
    "path": "deprecated/middleware/normal.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/gin-gonic/gin\"\n\t\"net/http\"\n\t\"strconv\"\n)\n\nfunc NoAuthToGetUserId() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\trawId := c.Query(\"user_id\")\n\t\tif rawId == \"\" {\n\t\t\trawId = c.PostForm(\"user_id\")\n\t\t}\n\t\t//用户不存在\n\t\tif rawId == \"\" {\n\t\t\tc.JSON(http.StatusOK, models.CommonResponse{StatusCode: 401, StatusMsg: \"用户不存在\"})\n\t\t\tc.Abort() //阻止执行\n\t\t\treturn\n\t\t}\n\t\tuserId, err := strconv.ParseInt(rawId, 10, 64)\n\t\tif err != nil {\n\t\t\tc.JSON(http.StatusOK, models.CommonResponse{StatusCode: 401, StatusMsg: \"用户不存在\"})\n\t\t\tc.Abort() //阻止执行\n\t\t}\n\t\tc.Set(\"user_id\", userId)\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "deprecated/middleware/password.go",
    "content": "package middleware\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SHA1(s string) string {\n\n\to := sha1.New()\n\n\to.Write([]byte(s))\n\n\treturn hex.EncodeToString(o.Sum(nil))\n}\n\nfunc SHAMiddleWare() gin.HandlerFunc {\n\treturn func(context *gin.Context) {\n\t\tpassword := context.Query(\"password\")\n\t\tif password == \"\" {\n\t\t\tpassword = context.PostForm(\"password\")\n\t\t}\n\t\tcontext.Set(\"password\", SHA1(password))\n\t\tcontext.Next()\n\t}\n}\n"
  },
  {
    "path": "deprecated/middleware/password_test.go",
    "content": "package middleware\n\nimport \"testing\"\n\n//40bd001563085fc35165329ea1ff5c5ecbdbbeef\nfunc TestSHA1(t *testing.T) {\n\tprint(SHA1(\"123\"))\n}\n"
  },
  {
    "path": "deprecated/models/comment.go",
    "content": "package models\n\nimport (\n\t\"errors\"\n\t\"gorm.io/gorm\"\n\t\"time\"\n)\n\ntype Comment struct {\n\tId         int64     `json:\"id\"`\n\tUserInfoId int64     `json:\"-\"` //用于一对多关系的id\n\tVideoId    int64     `json:\"-\"` //一对多，视频对评论\n\tUser       UserInfo  `json:\"user\" gorm:\"-\"`\n\tContent    string    `json:\"content\"`\n\tCreatedAt  time.Time `json:\"-\"`\n\tCreateDate string    `json:\"create_date\" gorm:\"-\"`\n}\n\ntype CommentDAO struct {\n}\n\nvar (\n\tcommentDao CommentDAO\n)\n\nfunc NewCommentDAO() *CommentDAO {\n\treturn &commentDao\n}\n\nfunc (c *CommentDAO) AddCommentAndUpdateCount(comment *Comment) error {\n\tif comment == nil {\n\t\treturn errors.New(\"AddCommentAndUpdateCount comment空指针\")\n\t}\n\t//执行事务\n\treturn DB.Transaction(func(tx *gorm.DB) error {\n\t\t//添加评论数据\n\t\tif err := tx.Create(comment).Error; err != nil {\n\t\t\t// 返回任何错误都会回滚事务\n\t\t\treturn err\n\t\t}\n\t\t//增加count\n\t\tif err := tx.Exec(\"UPDATE videos v SET v.comment_count = v.comment_count+1 WHERE v.id=?\", comment.VideoId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 返回 nil 提交事务\n\t\treturn nil\n\t})\n}\n\nfunc (c *CommentDAO) DeleteCommentAndUpdateCountById(commentId, videoId int64) error {\n\t//执行事务\n\treturn DB.Transaction(func(tx *gorm.DB) error {\n\t\t//删除评论\n\t\tif err := tx.Exec(\"DELETE FROM comments WHERE id = ?\", commentId).Error; err != nil {\n\t\t\t// 返回任何错误都会回滚事务\n\t\t\treturn err\n\t\t}\n\t\t//减少count\n\t\tif err := tx.Exec(\"UPDATE videos v SET v.comment_count = v.comment_count-1 WHERE v.id=? AND v.comment_count>0\", videoId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// 返回 nil 提交事务\n\t\treturn nil\n\t})\n}\n\nfunc (c *CommentDAO) QueryCommentById(id int64, comment *Comment) error {\n\tif comment == nil {\n\t\treturn errors.New(\"QueryCommentById comment 空指针\")\n\t}\n\treturn DB.Where(\"id=?\", id).First(comment).Error\n}\n\nfunc (c *CommentDAO) QueryCommentListByVideoId(videoId int64, comments *[]*Comment) error {\n\tif comments == nil {\n\t\treturn errors.New(\"QueryCommentListByVideoId comments空指针\")\n\t}\n\tif err := DB.Model(&Comment{}).Where(\"video_id=?\", videoId).Find(comments).Error; err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/models/common.go",
    "content": "package models\n\ntype CommonResponse struct {\n\tStatusCode int32  `json:\"status_code\"`\n\tStatusMsg  string `json:\"status_msg,omitempty\"`\n}\n"
  },
  {
    "path": "deprecated/models/init_db.go",
    "content": "package models\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\nvar DB *gorm.DB\n\nfunc InitDB() {\n\tvar err error\n\tDB, err = gorm.Open(mysql.Open(config.DBConnectString()), &gorm.Config{\n\t\tPrepareStmt:            true, //缓存预编译命令\n\t\tSkipDefaultTransaction: true, //禁用默认事务操作\n\t\t//Logger:                 logger.Default.LogMode(logger.Global), //打印sql语句\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\terr = DB.AutoMigrate(&UserInfo{}, &Video{}, &Comment{}, &UserLogin{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "deprecated/models/user_info.go",
    "content": "package models\n\nimport (\n\t\"errors\"\n\t\"gorm.io/gorm\"\n\t\"log\"\n\t\"sync\"\n)\n\nvar (\n\tErrIvdPtr        = errors.New(\"空指针错误\")\n\tErrEmptyUserList = errors.New(\"用户列表为空\")\n)\n\ntype UserInfo struct {\n\tId            int64       `json:\"id\" gorm:\"id,omitempty\"`\n\tName          string      `json:\"name\" gorm:\"name,omitempty\"`\n\tFollowCount   int64       `json:\"follow_count\" gorm:\"follow_count,omitempty\"`\n\tFollowerCount int64       `json:\"follower_count\" gorm:\"follower_count,omitempty\"`\n\tIsFollow      bool        `json:\"is_follow\" gorm:\"is_follow,omitempty\"`\n\tUser          *UserLogin  `json:\"-\"`                                     //用户与账号密码之间的一对一\n\tVideos        []*Video    `json:\"-\"`                                     //用户与投稿视频的一对多\n\tFollows       []*UserInfo `json:\"-\" gorm:\"many2many:user_relations;\"`    //用户之间的多对多\n\tFavorVideos   []*Video    `json:\"-\" gorm:\"many2many:user_favor_videos;\"` //用户与点赞视频之间的多对多\n\tComments      []*Comment  `json:\"-\"`                                     //用户与评论的一对多\n}\n\ntype UserInfoDAO struct {\n}\n\nvar (\n\tuserInfoDAO  *UserInfoDAO\n\tuserInfoOnce sync.Once\n)\n\nfunc NewUserInfoDAO() *UserInfoDAO {\n\tuserInfoOnce.Do(func() {\n\t\tuserInfoDAO = new(UserInfoDAO)\n\t})\n\treturn userInfoDAO\n}\n\nfunc (u *UserInfoDAO) QueryUserInfoById(userId int64, userinfo *UserInfo) error {\n\tif userinfo == nil {\n\t\treturn ErrIvdPtr\n\t}\n\t//DB.Where(\"id=?\",userId).First(userinfo)\n\tDB.Where(\"id=?\", userId).Select([]string{\"id\", \"name\", \"follow_count\", \"follower_count\", \"is_follow\"}).First(userinfo)\n\t//id为零值，说明sql执行失败\n\tif userinfo.Id == 0 {\n\t\treturn errors.New(\"该用户不存在\")\n\t}\n\treturn nil\n}\n\nfunc (u *UserInfoDAO) AddUserInfo(userinfo *UserInfo) error {\n\tif userinfo == nil {\n\t\treturn ErrIvdPtr\n\t}\n\treturn DB.Create(userinfo).Error\n}\n\nfunc (u *UserInfoDAO) IsUserExistById(id int64) bool {\n\tvar userinfo UserInfo\n\tif err := DB.Where(\"id=?\", id).Select(\"id\").First(&userinfo).Error; err != nil {\n\t\tlog.Println(err)\n\t}\n\tif userinfo.Id == 0 {\n\t\treturn false\n\t}\n\treturn true\n}\nfunc (u *UserInfoDAO) AddUserFollow(userId, userToId int64) error {\n\treturn DB.Transaction(func(tx *gorm.DB) error {\n\t\tif err := tx.Exec(\"UPDATE user_infos SET follow_count=follow_count+1 WHERE id = ?\", userId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Exec(\"UPDATE user_infos SET follower_count=follower_count+1 WHERE id = ?\", userToId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Exec(\"INSERT INTO `user_relations` (`user_info_id`,`follow_id`) VALUES (?,?)\", userId, userToId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (u *UserInfoDAO) CancelUserFollow(userId, userToId int64) error {\n\treturn DB.Transaction(func(tx *gorm.DB) error {\n\t\tif err := tx.Exec(\"UPDATE user_infos SET follow_count=follow_count-1 WHERE id = ? AND follow_count>0\", userId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Exec(\"UPDATE user_infos SET follower_count=follower_count-1 WHERE id = ? AND follower_count>0\", userToId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Exec(\"DELETE FROM `user_relations` WHERE user_info_id=? AND follow_id=?\", userId, userToId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (u *UserInfoDAO) GetFollowListByUserId(userId int64, userList *[]*UserInfo) error {\n\tif userList == nil {\n\t\treturn ErrIvdPtr\n\t}\n\tvar err error\n\tif err = DB.Raw(\"SELECT u.* FROM user_relations r, user_infos u WHERE r.user_info_id = ? AND r.follow_id = u.id\", userId).Scan(userList).Error; err != nil {\n\t\treturn err\n\t}\n\tif len(*userList) == 0 || (*userList)[0].Id == 0 {\n\t\treturn ErrEmptyUserList\n\t}\n\treturn nil\n}\n\nfunc (u *UserInfoDAO) GetFollowerListByUserId(userId int64, userList *[]*UserInfo) error {\n\tif userList == nil {\n\t\treturn ErrIvdPtr\n\t}\n\tvar err error\n\tif err = DB.Raw(\"SELECT u.* FROM user_relations r, user_infos u WHERE r.follow_id = ? AND r.user_info_id = u.id\", userId).Scan(userList).Error; err != nil {\n\t\treturn err\n\t}\n\t//if len(*userList) == 0 || (*userList)[0].Id == 0 {\n\t//\treturn ErrEmptyUserList\n\t//}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/models/user_info_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestMain(m *testing.M) {\n\tInitDB()\n\tcode := m.Run()\n\tos.Exit(code)\n}\n\nfunc TestUserInfoDAO_GetFollowListByUserId(t *testing.T) {\n\tvar userList []*UserInfo\n\terr := NewUserInfoDAO().GetFollowListByUserId(1, &userList)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfor _, user := range userList {\n\t\tfmt.Printf(\"%#v\\n\", *user)\n\t}\n}\n\nfunc TestUserInfoDAO_GetFollowerListByUserId(t *testing.T) {\n\tvar userList []*UserInfo\n\terr := NewUserInfoDAO().GetFollowerListByUserId(2, &userList)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfor _, user := range userList {\n\t\tfmt.Printf(\"%#v\\n\", *user)\n\t}\n}\n"
  },
  {
    "path": "deprecated/models/user_login.go",
    "content": "package models\n\nimport (\n\t\"errors\"\n\t\"sync\"\n)\n\n// UserLogin 用户登录表，和UserInfo属于一对一关系\ntype UserLogin struct {\n\tId         int64 `gorm:\"primary_key\"`\n\tUserInfoId int64\n\tUsername   string `gorm:\"primary_key\"`\n\tPassword   string `gorm:\"size:200;notnull\"`\n}\n\ntype UserLoginDAO struct {\n}\n\nvar (\n\tuserLoginDao  *UserLoginDAO\n\tuserLoginOnce sync.Once\n)\n\nfunc NewUserLoginDao() *UserLoginDAO {\n\tuserLoginOnce.Do(func() {\n\t\tuserLoginDao = new(UserLoginDAO)\n\t})\n\treturn userLoginDao\n}\n\nfunc (u *UserLoginDAO) QueryUserLogin(username, password string, login *UserLogin) error {\n\tif login == nil {\n\t\treturn errors.New(\"结构体指针为空\")\n\t}\n\tDB.Where(\"username=? and password=?\", username, password).First(login)\n\tif login.Id == 0 {\n\t\treturn errors.New(\"用户不存在，账号或密码出错\")\n\t}\n\treturn nil\n}\n\nfunc (u *UserLoginDAO) IsUserExistByUsername(username string) bool {\n\tvar userLogin UserLogin\n\tDB.Where(\"username=?\", username).First(&userLogin)\n\tif userLogin.Id == 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "deprecated/models/video.go",
    "content": "package models\n\nimport (\n\t\"errors\"\n\t\"gorm.io/gorm\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype Video struct {\n\tId            int64       `json:\"id,omitempty\"`\n\tUserInfoId    int64       `json:\"-\"`\n\tAuthor        UserInfo    `json:\"author,omitempty\" gorm:\"-\"` //这里应该是作者对视频的一对多的关系，而不是视频对作者，故gorm不能存他，但json需要返回它\n\tPlayUrl       string      `json:\"play_url,omitempty\"`\n\tCoverUrl      string      `json:\"cover_url,omitempty\"`\n\tFavoriteCount int64       `json:\"favorite_count,omitempty\"`\n\tCommentCount  int64       `json:\"comment_count,omitempty\"`\n\tIsFavorite    bool        `json:\"is_favorite,omitempty\"`\n\tTitle         string      `json:\"title,omitempty\"`\n\tUsers         []*UserInfo `json:\"-\" gorm:\"many2many:user_favor_videos;\"`\n\tComments      []*Comment  `json:\"-\"`\n\tCreatedAt     time.Time   `json:\"-\"`\n\tUpdatedAt     time.Time   `json:\"-\"`\n}\n\ntype VideoDAO struct {\n}\n\nvar (\n\tvideoDAO  *VideoDAO\n\tvideoOnce sync.Once\n)\n\nfunc NewVideoDAO() *VideoDAO {\n\tvideoOnce.Do(func() {\n\t\tvideoDAO = new(VideoDAO)\n\t})\n\treturn videoDAO\n}\n\n// AddVideo 添加视频\n// 注意：由于视频和userinfo有多对一的关系，所以传入的Video参数一定要进行id的映射处理！\nfunc (v *VideoDAO) AddVideo(video *Video) error {\n\tif video == nil {\n\t\treturn errors.New(\"AddVideo video 空指针\")\n\t}\n\treturn DB.Create(video).Error\n}\n\nfunc (v *VideoDAO) QueryVideoByVideoId(videoId int64, video *Video) error {\n\tif video == nil {\n\t\treturn errors.New(\"QueryVideoByVideoId 空指针\")\n\t}\n\treturn DB.Where(\"id=?\", videoId).\n\t\tSelect([]string{\"id\", \"user_info_id\", \"play_url\", \"cover_url\", \"favorite_count\", \"comment_count\", \"is_favorite\", \"title\"}).\n\t\tFirst(video).Error\n}\n\nfunc (v *VideoDAO) QueryVideoCountByUserId(userId int64, count *int64) error {\n\tif count == nil {\n\t\treturn errors.New(\"QueryVideoCountByUserId count 空指针\")\n\t}\n\treturn DB.Model(&Video{}).Where(\"user_info_id=?\", userId).Count(count).Error\n}\n\nfunc (v *VideoDAO) QueryVideoListByUserId(userId int64, videoList *[]*Video) error {\n\tif videoList == nil {\n\t\treturn errors.New(\"QueryVideoListByUserId videoList 空指针\")\n\t}\n\treturn DB.Where(\"user_info_id=?\", userId).\n\t\tSelect([]string{\"id\", \"user_info_id\", \"play_url\", \"cover_url\", \"favorite_count\", \"comment_count\", \"is_favorite\", \"title\"}).\n\t\tFind(videoList).Error\n}\n\n// QueryVideoListByLimitAndTime  返回按投稿时间倒序的视频列表，并限制为最多limit个\nfunc (v *VideoDAO) QueryVideoListByLimitAndTime(limit int, latestTime time.Time, videoList *[]*Video) error {\n\tif videoList == nil {\n\t\treturn errors.New(\"QueryVideoListByLimit videoList 空指针\")\n\t}\n\treturn DB.Model(&Video{}).Where(\"created_at<?\", latestTime).\n\t\tOrder(\"created_at ASC\").Limit(limit).\n\t\tSelect([]string{\"id\", \"user_info_id\", \"play_url\", \"cover_url\", \"favorite_count\", \"comment_count\", \"is_favorite\", \"title\", \"created_at\", \"updated_at\"}).\n\t\tFind(videoList).Error\n}\n\n// PlusOneFavorByUserIdAndVideoId 增加一个赞\nfunc (v *VideoDAO) PlusOneFavorByUserIdAndVideoId(userId int64, videoId int64) error {\n\treturn DB.Transaction(func(tx *gorm.DB) error {\n\t\tif err := tx.Exec(\"UPDATE videos SET favorite_count=favorite_count+1 WHERE id = ?\", videoId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Exec(\"INSERT INTO `user_favor_videos` (`user_info_id`,`video_id`) VALUES (?,?)\", userId, videoId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// MinusOneFavorByUserIdAndVideoId 减少一个赞\nfunc (v *VideoDAO) MinusOneFavorByUserIdAndVideoId(userId int64, videoId int64) error {\n\treturn DB.Transaction(func(tx *gorm.DB) error {\n\t\t//执行-1之前需要先判断是否合法（不能被减少为负数\n\t\tif err := tx.Exec(\"UPDATE videos SET favorite_count=favorite_count-1 WHERE id = ? AND favorite_count>0\", videoId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Exec(\"DELETE FROM `user_favor_videos`  WHERE `user_info_id` = ? AND `video_id` = ?\", userId, videoId).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc (v *VideoDAO) QueryFavorVideoListByUserId(userId int64, videoList *[]*Video) error {\n\tif videoList == nil {\n\t\treturn errors.New(\"QueryFavorVideoListByUserId videoList 空指针\")\n\t}\n\t//多表查询，左连接得到结果，再映射到数据\n\tif err := DB.Raw(\"SELECT v.* FROM user_favor_videos u , videos v WHERE u.user_info_id = ? AND u.video_id = v.id\", userId).Scan(videoList).Error; err != nil {\n\t\treturn err\n\t}\n\t//如果id为0，则说明没有查到数据\n\tif len(*videoList) == 0 || (*videoList)[0].Id == 0 {\n\t\treturn errors.New(\"点赞列表为空\")\n\t}\n\treturn nil\n}\n\nfunc (v *VideoDAO) IsVideoExistById(id int64) bool {\n\tvar video Video\n\tif err := DB.Where(\"id=?\", id).Select(\"id\").First(&video).Error; err != nil {\n\t\tlog.Println(err)\n\t}\n\tif video.Id == 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "deprecated/models/video_test.go",
    "content": "package models\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestVideoDAO_QueryVideoListByUserId(t *testing.T) {\n\tInitDB()\n\ts := make([]*Video, 8)\n\terr := NewVideoDAO().QueryVideoListByUserId(1, &s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfor _, v := range s {\n\t\tfmt.Printf(\"%#v\\n\", *v)\n\t}\n}\n\nfunc TestVideoDAO_QueryVideoListByLimit(t *testing.T) {\n\tInitDB()\n\ts := make([]*Video, 8)\n\terr := NewVideoDAO().QueryVideoListByLimitAndTime(2, time.Unix(1652895580, 0), &s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfor _, v := range s {\n\t\tfmt.Printf(\"%#v\\n\", *v)\n\t}\n}\n\nfunc TestTime(t *testing.T) {\n\tprintln(time.Now().UnixNano())\n}\n"
  },
  {
    "path": "deprecated/router/router_douyin.go",
    "content": "package router\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\tcomment2 \"github.com/ACking-you/byte_douyin_project/handlers/comment\"\n\tuser_info2 \"github.com/ACking-you/byte_douyin_project/handlers/user_info\"\n\tuser_login2 \"github.com/ACking-you/byte_douyin_project/handlers/user_login\"\n\tvideo2 \"github.com/ACking-you/byte_douyin_project/handlers/video\"\n\tmiddleware2 \"github.com/ACking-you/byte_douyin_project/middleware\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc Init() *gin.Engine {\n\tmodels.InitDB()\n\tr := gin.Default()\n\n\tr.Static(\"static\", config.Global.StaticSourcePath)\n\n\tbaseGroup := r.Group(\"/douyin\")\n\t//根据灵活性考虑是否加入JWT中间件来进行鉴权，还是在之后再做鉴权\n\t// basic apis\n\tbaseGroup.GET(\"/feed/\", video2.FeedVideoListHandler)\n\tbaseGroup.GET(\"/user/\", middleware2.JWTMiddleWare(), user_info2.UserInfoHandler)\n\tbaseGroup.POST(\"/user/login/\", middleware2.SHAMiddleWare(), user_login2.UserLoginHandler)\n\tbaseGroup.POST(\"/user/register/\", middleware2.SHAMiddleWare(), user_login2.UserRegisterHandler)\n\tbaseGroup.POST(\"/publish/action/\", middleware2.JWTMiddleWare(), video2.PublishVideoHandler)\n\tbaseGroup.GET(\"/publish/list/\", middleware2.NoAuthToGetUserId(), video2.QueryVideoListHandler)\n\n\t//extend 1\n\tbaseGroup.POST(\"/favorite/action/\", middleware2.JWTMiddleWare(), video2.PostFavorHandler)\n\tbaseGroup.GET(\"/favorite/list/\", middleware2.NoAuthToGetUserId(), video2.QueryFavorVideoListHandler)\n\tbaseGroup.POST(\"/comment/action/\", middleware2.JWTMiddleWare(), comment2.PostCommentHandler)\n\tbaseGroup.GET(\"/comment/list/\", middleware2.JWTMiddleWare(), comment2.QueryCommentListHandler)\n\n\t//extend 2\n\tbaseGroup.POST(\"/relation/action/\", middleware2.JWTMiddleWare(), user_info2.PostFollowActionHandler)\n\tbaseGroup.GET(\"/relation/follow/list/\", middleware2.NoAuthToGetUserId(), user_info2.QueryFollowListHandler)\n\tbaseGroup.GET(\"/relation/follower/list/\", middleware2.NoAuthToGetUserId(), user_info2.QueryFollowerHandler)\n\treturn r\n}\n"
  },
  {
    "path": "deprecated/service/comment/post_comment.go",
    "content": "package comment\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/util\"\n)\n\nconst (\n\tCREATE = 1\n\tDELETE = 2\n)\n\ntype Response struct {\n\tMyComment *models2.Comment `json:\"comment\"`\n}\n\nfunc PostComment(userId int64, videoId int64, commentId int64, actionType int64, commentText string) (*Response, error) {\n\treturn NewPostCommentFlow(userId, videoId, commentId, actionType, commentText).Do()\n}\n\ntype PostCommentFlow struct {\n\tuserId      int64\n\tvideoId     int64\n\tcommentId   int64\n\tactionType  int64\n\tcommentText string\n\n\tcomment *models2.Comment\n\n\t*Response\n}\n\nfunc NewPostCommentFlow(userId int64, videoId int64, commentId int64, actionType int64, commentText string) *PostCommentFlow {\n\treturn &PostCommentFlow{userId: userId, videoId: videoId, commentId: commentId, actionType: actionType, commentText: commentText}\n}\n\nfunc (p *PostCommentFlow) Do() (*Response, error) {\n\tvar err error\n\tif err = p.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = p.prepareData(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = p.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn p.Response, err\n}\n\n// CreateComment 增加评论\nfunc (p *PostCommentFlow) CreateComment() (*models2.Comment, error) {\n\tcomment := models2.Comment{UserInfoId: p.userId, VideoId: p.videoId, Content: p.commentText}\n\terr := models2.NewCommentDAO().AddCommentAndUpdateCount(&comment)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &comment, nil\n}\n\n// DeleteComment 删除评论\nfunc (p *PostCommentFlow) DeleteComment() (*models2.Comment, error) {\n\t//获取comment\n\tvar comment models2.Comment\n\terr := models2.NewCommentDAO().QueryCommentById(p.commentId, &comment)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t//删除comment\n\terr = models2.NewCommentDAO().DeleteCommentAndUpdateCountById(p.commentId, p.videoId)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &comment, nil\n}\n\nfunc (p *PostCommentFlow) checkNum() error {\n\tif !models2.NewUserInfoDAO().IsUserExistById(p.userId) {\n\t\treturn fmt.Errorf(\"用户%d不存在\", p.userId)\n\t}\n\tif !models2.NewVideoDAO().IsVideoExistById(p.videoId) {\n\t\treturn fmt.Errorf(\"视频%d不存在\", p.videoId)\n\t}\n\tif p.actionType != CREATE && p.actionType != DELETE {\n\t\treturn errors.New(\"未定义的行为\")\n\t}\n\treturn nil\n}\n\nfunc (p *PostCommentFlow) prepareData() error {\n\tvar err error\n\tswitch p.actionType {\n\tcase CREATE:\n\t\tp.comment, err = p.CreateComment()\n\tcase DELETE:\n\t\tp.comment, err = p.DeleteComment()\n\tdefault:\n\t\treturn errors.New(\"未定义的操作\")\n\t}\n\treturn err\n}\n\nfunc (p *PostCommentFlow) packData() error {\n\t//填充字段\n\tuserInfo := models2.UserInfo{}\n\t_ = models2.NewUserInfoDAO().QueryUserInfoById(p.comment.UserInfoId, &userInfo)\n\tp.comment.User = userInfo\n\t_ = util.FillCommentFields(p.comment)\n\n\tp.Response = &Response{MyComment: p.comment}\n\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/comment/query_comment_list.go",
    "content": "package comment\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/util\"\n)\n\ntype List struct {\n\tComments []*models2.Comment `json:\"comment_list\"`\n}\n\nfunc QueryCommentList(userId, videoId int64) (*List, error) {\n\treturn NewQueryCommentListFlow(userId, videoId).Do()\n}\n\ntype QueryCommentListFlow struct {\n\tuserId  int64\n\tvideoId int64\n\n\tcomments []*models2.Comment\n\n\tcommentList *List\n}\n\nfunc NewQueryCommentListFlow(userId, videoId int64) *QueryCommentListFlow {\n\treturn &QueryCommentListFlow{userId: userId, videoId: videoId}\n}\n\nfunc (q *QueryCommentListFlow) Do() (*List, error) {\n\tif err := q.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := q.prepareData(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := q.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.commentList, nil\n}\n\nfunc (q *QueryCommentListFlow) checkNum() error {\n\tif !models2.NewUserInfoDAO().IsUserExistById(q.userId) {\n\t\treturn fmt.Errorf(\"用户%d处于登出状态\", q.userId)\n\t}\n\tif !models2.NewVideoDAO().IsVideoExistById(q.videoId) {\n\t\treturn fmt.Errorf(\"视频%d不存在或已经被删除\", q.videoId)\n\t}\n\treturn nil\n}\n\nfunc (q *QueryCommentListFlow) prepareData() error {\n\terr := models2.NewCommentDAO().QueryCommentListByVideoId(q.videoId, &q.comments)\n\tif err != nil {\n\t\treturn err\n\t}\n\t//根据前端的要求填充正确的时间格式\n\terr = util.FillCommentListFields(&q.comments)\n\tif err != nil {\n\t\treturn errors.New(\"暂时还没有人评论\")\n\t}\n\treturn nil\n}\n\nfunc (q *QueryCommentListFlow) packData() error {\n\tq.commentList = &List{Comments: q.comments}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/user_info/post_follow_action.go",
    "content": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n)\n\nconst (\n\tFOLLOW = 1\n\tCANCEL = 2\n)\n\nvar (\n\tErrIvdAct    = errors.New(\"未定义操作\")\n\tErrIvdFolUsr = errors.New(\"关注用户不存在\")\n)\n\nfunc PostFollowAction(userId, userToId int64, actionType int) error {\n\treturn NewPostFollowActionFlow(userId, userToId, actionType).Do()\n}\n\ntype PostFollowActionFlow struct {\n\tuserId     int64\n\tuserToId   int64\n\tactionType int\n}\n\nfunc NewPostFollowActionFlow(userId int64, userToId int64, actionType int) *PostFollowActionFlow {\n\treturn &PostFollowActionFlow{userId: userId, userToId: userToId, actionType: actionType}\n}\n\nfunc (p *PostFollowActionFlow) Do() error {\n\tvar err error\n\tif err = p.checkNum(); err != nil {\n\t\treturn err\n\t}\n\tif err = p.publish(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (p *PostFollowActionFlow) checkNum() error {\n\t//由于userId是经过乐token鉴权故不需要check，只需要检查userToId\n\tif !models.NewUserInfoDAO().IsUserExistById(p.userToId) {\n\t\treturn ErrIvdFolUsr\n\t}\n\tif p.actionType != FOLLOW && p.actionType != CANCEL {\n\t\treturn ErrIvdAct\n\t}\n\t//自己不能关注自己\n\tif p.userId == p.userToId {\n\t\treturn ErrIvdAct\n\t}\n\treturn nil\n}\n\nfunc (p *PostFollowActionFlow) publish() error {\n\tuserDAO := models.NewUserInfoDAO()\n\tvar err error\n\tswitch p.actionType {\n\tcase FOLLOW:\n\t\terr = userDAO.AddUserFollow(p.userId, p.userToId)\n\t\t//更新redis的关注信息\n\t\tcache.NewProxyIndexMap().UpdateUserRelation(p.userId, p.userToId, true)\n\tcase CANCEL:\n\t\terr = userDAO.CancelUserFollow(p.userId, p.userToId)\n\t\tcache.NewProxyIndexMap().UpdateUserRelation(p.userId, p.userToId, false)\n\tdefault:\n\t\treturn ErrIvdAct\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "deprecated/service/user_info/query_follow_list.go",
    "content": "package user_info\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n)\n\nvar (\n\tErrUserNotExist = errors.New(\"用户不存在或已注销\")\n)\n\ntype FollowList struct {\n\tUserList []*models.UserInfo `json:\"user_list\"`\n}\n\nfunc QueryFollowList(userId int64) (*FollowList, error) {\n\treturn NewQueryFollowListFlow(userId).Do()\n}\n\ntype QueryFollowListFlow struct {\n\tuserId int64\n\n\tuserList []*models.UserInfo\n\n\t*FollowList\n}\n\nfunc NewQueryFollowListFlow(userId int64) *QueryFollowListFlow {\n\treturn &QueryFollowListFlow{userId: userId}\n}\n\nfunc (q *QueryFollowListFlow) Do() (*FollowList, error) {\n\tvar err error\n\tif err = q.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = q.prepareData(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = q.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn q.FollowList, nil\n}\n\nfunc (q *QueryFollowListFlow) checkNum() error {\n\tif !models.NewUserInfoDAO().IsUserExistById(q.userId) {\n\t\treturn ErrUserNotExist\n\t}\n\treturn nil\n}\n\nfunc (q *QueryFollowListFlow) prepareData() error {\n\tvar userList []*models.UserInfo\n\terr := models.NewUserInfoDAO().GetFollowListByUserId(q.userId, &userList)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor i, _ := range userList {\n\t\tuserList[i].IsFollow = true //当前用户的关注列表，故isFollow定为true\n\t}\n\tq.userList = userList\n\treturn nil\n}\n\nfunc (q *QueryFollowListFlow) packData() error {\n\tq.FollowList = &FollowList{UserList: q.userList}\n\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/user_info/query_follower_list.go",
    "content": "package user_info\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n)\n\ntype FollowerList struct {\n\tUserList []*models.UserInfo `json:\"user_list\"`\n}\n\nfunc QueryFollowerList(userId int64) (*FollowerList, error) {\n\treturn NewQueryFollowerListFlow(userId).Do()\n}\n\ntype QueryFollowerListFlow struct {\n\tuserId int64\n\n\tuserList []*models.UserInfo\n\n\t*FollowerList\n}\n\nfunc NewQueryFollowerListFlow(userId int64) *QueryFollowerListFlow {\n\treturn &QueryFollowerListFlow{userId: userId}\n}\n\nfunc (q *QueryFollowerListFlow) Do() (*FollowerList, error) {\n\tvar err error\n\tif err = q.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = q.prepareData(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = q.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.FollowerList, nil\n}\n\nfunc (q *QueryFollowerListFlow) checkNum() error {\n\tif !models.NewUserInfoDAO().IsUserExistById(q.userId) {\n\t\treturn ErrUserNotExist\n\t}\n\treturn nil\n}\n\nfunc (q *QueryFollowerListFlow) prepareData() error {\n\n\terr := models.NewUserInfoDAO().GetFollowerListByUserId(q.userId, &q.userList)\n\tif err != nil {\n\t\treturn err\n\t}\n\t//填充is_follow字段\n\tfor _, v := range q.userList {\n\t\tv.IsFollow = cache.NewProxyIndexMap().GetUserRelation(q.userId, v.Id)\n\t}\n\treturn nil\n}\n\nfunc (q *QueryFollowerListFlow) packData() error {\n\tq.FollowerList = &FollowerList{UserList: q.userList}\n\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/user_login/post_user_login.go",
    "content": "package user_login\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/middleware\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n)\n\n// PostUserLogin 注册用户并得到token和id\nfunc PostUserLogin(username, password string) (*LoginResponse, error) {\n\treturn NewPostUserLoginFlow(username, password).Do()\n}\n\nfunc NewPostUserLoginFlow(username, password string) *PostUserLoginFlow {\n\treturn &PostUserLoginFlow{username: username, password: password}\n}\n\ntype PostUserLoginFlow struct {\n\tusername string\n\tpassword string\n\n\tdata   *LoginResponse\n\tuserid int64\n\ttoken  string\n}\n\nfunc (q *PostUserLoginFlow) Do() (*LoginResponse, error) {\n\t//对参数进行合法性验证\n\tif err := q.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t//更新数据到数据库\n\tif err := q.updateData(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t//打包response\n\tif err := q.packResponse(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.data, nil\n}\n\nfunc (q *PostUserLoginFlow) checkNum() error {\n\tif q.username == \"\" {\n\t\treturn errors.New(\"用户名为空\")\n\t}\n\tif len(q.username) > MaxUsernameLength {\n\t\treturn errors.New(\"用户名长度超出限制\")\n\t}\n\tif q.password == \"\" {\n\t\treturn errors.New(\"密码为空\")\n\t}\n\treturn nil\n}\n\nfunc (q *PostUserLoginFlow) updateData() error {\n\n\t//准备好userInfo,默认name为username\n\tuserLogin := models2.UserLogin{Username: q.username, Password: q.password}\n\tuserinfo := models2.UserInfo{User: &userLogin, Name: q.username}\n\n\t//判断用户名是否已经存在\n\tuserLoginDAO := models2.NewUserLoginDao()\n\tif userLoginDAO.IsUserExistByUsername(q.username) {\n\t\treturn errors.New(\"用户名已存在\")\n\t}\n\n\t//更新操作，由于userLogin属于userInfo，故更新userInfo即可，且由于传入的是指针，所以插入的数据内容也是清楚的\n\tuserInfoDAO := models2.NewUserInfoDAO()\n\terr := userInfoDAO.AddUserInfo(&userinfo)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t//颁发token\n\ttoken, err := middleware.ReleaseToken(userLogin)\n\tif err != nil {\n\t\treturn err\n\t}\n\tq.token = token\n\tq.userid = userinfo.Id\n\treturn nil\n}\n\nfunc (q *PostUserLoginFlow) packResponse() error {\n\tq.data = &LoginResponse{\n\t\tUserId: q.userid,\n\t\tToken:  q.token,\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/user_login/query_user_login.go",
    "content": "package user_login\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/middleware\"\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n)\n\nconst (\n\tMaxUsernameLength = 100\n\tMaxPasswordLength = 20\n\tMinPasswordLength = 8\n)\n\ntype LoginResponse struct {\n\tUserId int64  `json:\"user_id\"`\n\tToken  string `json:\"token\"`\n}\n\n// QueryUserLogin 查询用户是否存在，并返回token和id\nfunc QueryUserLogin(username, password string) (*LoginResponse, error) {\n\treturn NewQueryUserLoginFlow(username, password).Do()\n}\n\nfunc NewQueryUserLoginFlow(username, password string) *QueryUserLoginFlow {\n\treturn &QueryUserLoginFlow{username: username, password: password}\n}\n\ntype QueryUserLoginFlow struct {\n\tusername string\n\tpassword string\n\n\tdata   *LoginResponse\n\tuserid int64\n\ttoken  string\n}\n\nfunc (q *QueryUserLoginFlow) Do() (*LoginResponse, error) {\n\t//对参数进行合法性验证\n\tif err := q.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\t//准备好数据\n\tif err := q.prepareData(); err != nil {\n\t\treturn nil, err\n\t}\n\t//打包最终数据\n\tif err := q.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.data, nil\n}\n\nfunc (q *QueryUserLoginFlow) checkNum() error {\n\tif q.username == \"\" {\n\t\treturn errors.New(\"用户名为空\")\n\t}\n\tif len(q.username) > MaxUsernameLength {\n\t\treturn errors.New(\"用户名长度超出限制\")\n\t}\n\tif q.password == \"\" {\n\t\treturn errors.New(\"密码为空\")\n\t}\n\treturn nil\n}\n\nfunc (q *QueryUserLoginFlow) prepareData() error {\n\tuserLoginDAO := models.NewUserLoginDao()\n\tvar login models.UserLogin\n\t//准备好userid\n\terr := userLoginDAO.QueryUserLogin(q.username, q.password, &login)\n\tif err != nil {\n\t\treturn err\n\t}\n\tq.userid = login.UserInfoId\n\n\t//准备颁发token\n\ttoken, err := middleware.ReleaseToken(login)\n\tif err != nil {\n\t\treturn err\n\t}\n\tq.token = token\n\treturn nil\n}\n\nfunc (q *QueryUserLoginFlow) packData() error {\n\tq.data = &LoginResponse{\n\t\tUserId: q.userid,\n\t\tToken:  q.token,\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/video/feed_videolist.go",
    "content": "package video\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/util\"\n\t\"time\"\n)\n\n// MaxVideoNum 每次最多返回的视频流数量\nconst (\n\tMaxVideoNum = 30\n)\n\ntype FeedVideoList struct {\n\tVideos   []*models.Video `json:\"video_list,omitempty\"`\n\tNextTime int64           `json:\"next_time,omitempty\"`\n}\n\nfunc QueryFeedVideoList(userId int64, latestTime time.Time) (*FeedVideoList, error) {\n\treturn NewQueryFeedVideoListFlow(userId, latestTime).Do()\n}\n\ntype QueryFeedVideoListFlow struct {\n\tuserId     int64\n\tlatestTime time.Time\n\n\tvideos   []*models.Video\n\tnextTime int64\n\n\tfeedVideo *FeedVideoList\n}\n\nfunc NewQueryFeedVideoListFlow(userId int64, latestTime time.Time) *QueryFeedVideoListFlow {\n\treturn &QueryFeedVideoListFlow{userId: userId, latestTime: latestTime}\n}\n\nfunc (q *QueryFeedVideoListFlow) Do() (*FeedVideoList, error) {\n\t//所有传入的参数不填也应该给他正常处理\n\tq.checkNum()\n\n\tif err := q.prepareData(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := q.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.feedVideo, nil\n}\n\nfunc (q *QueryFeedVideoListFlow) checkNum() {\n\t//上层通过把userId置零，表示userId不存在或不需要\n\tif q.userId > 0 {\n\t\t//这里说明userId是有效的，可以定制性的做一些登录用户的专属视频推荐\n\t}\n\n\tif q.latestTime.IsZero() {\n\t\tq.latestTime = time.Now()\n\t}\n}\n\nfunc (q *QueryFeedVideoListFlow) prepareData() error {\n\terr := models.NewVideoDAO().QueryVideoListByLimitAndTime(MaxVideoNum, q.latestTime, &q.videos)\n\tif err != nil {\n\t\treturn err\n\t}\n\t//如果用户为登录状态，则更新该视频是否被该用户点赞的状态\n\tlatestTime, _ := util.FillVideoListFields(q.userId, &q.videos) //不是致命错误，不返回\n\n\t//准备好时间戳\n\tif latestTime != nil {\n\t\tq.nextTime = (*latestTime).UnixNano() / 1e6\n\t\treturn nil\n\t}\n\tq.nextTime = time.Now().Unix() / 1e6\n\treturn nil\n}\n\nfunc (q *QueryFeedVideoListFlow) packData() error {\n\tq.feedVideo = &FeedVideoList{\n\t\tVideos:   q.videos,\n\t\tNextTime: q.nextTime,\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/video/post_favor_state.go",
    "content": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n)\n\nconst (\n\tPLUS  = 1\n\tMINUS = 2\n)\n\nfunc PostFavorState(userId, videoId, actionType int64) error {\n\treturn NewPostFavorStateFlow(userId, videoId, actionType).Do()\n}\n\ntype PostFavorStateFlow struct {\n\tuserId     int64\n\tvideoId    int64\n\tactionType int64\n}\n\nfunc NewPostFavorStateFlow(userId, videoId, action int64) *PostFavorStateFlow {\n\treturn &PostFavorStateFlow{\n\t\tuserId:     userId,\n\t\tvideoId:    videoId,\n\t\tactionType: action,\n\t}\n}\n\nfunc (p *PostFavorStateFlow) Do() error {\n\tvar err error\n\tif err = p.checkNum(); err != nil {\n\t\treturn err\n\t}\n\n\tswitch p.actionType {\n\tcase PLUS:\n\t\terr = p.PlusOperation()\n\tcase MINUS:\n\t\terr = p.MinusOperation()\n\tdefault:\n\t\treturn errors.New(\"未定义的操作\")\n\t}\n\treturn err\n}\n\n// PlusOperation 点赞操作\nfunc (p *PostFavorStateFlow) PlusOperation() error {\n\t//视频点赞数目+1\n\terr := models2.NewVideoDAO().PlusOneFavorByUserIdAndVideoId(p.userId, p.videoId)\n\tif err != nil {\n\t\treturn errors.New(\"不要重复点赞\")\n\t}\n\t//对应的用户是否点赞的映射状态更新\n\tcache.NewProxyIndexMap().UpdateVideoFavorState(p.userId, p.videoId, true)\n\treturn nil\n}\n\n// MinusOperation 取消点赞\nfunc (p *PostFavorStateFlow) MinusOperation() error {\n\t//视频点赞数目-1\n\terr := models2.NewVideoDAO().MinusOneFavorByUserIdAndVideoId(p.userId, p.videoId)\n\tif err != nil {\n\t\treturn errors.New(\"点赞数目已经为0\")\n\t}\n\t//对应的用户是否点赞的映射状态更新\n\tcache.NewProxyIndexMap().UpdateVideoFavorState(p.userId, p.videoId, false)\n\treturn nil\n}\n\nfunc (p *PostFavorStateFlow) checkNum() error {\n\tif !models2.NewUserInfoDAO().IsUserExistById(p.userId) {\n\t\treturn errors.New(\"用户不存在\")\n\t}\n\tif p.actionType != PLUS && p.actionType != MINUS {\n\t\treturn errors.New(\"未定义的行为\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/video/post_video.go",
    "content": "package video\n\nimport (\n\t\"github.com/ACking-you/byte_douyin_project/models\"\n\t\"github.com/ACking-you/byte_douyin_project/util\"\n)\n\n// PostVideo 投稿视频\nfunc PostVideo(userId int64, videoName, coverName, title string) error {\n\treturn NewPostVideoFlow(userId, videoName, coverName, title).Do()\n}\n\nfunc NewPostVideoFlow(userId int64, videoName, coverName, title string) *PostVideoFlow {\n\treturn &PostVideoFlow{\n\t\tvideoName: videoName,\n\t\tcoverName: coverName,\n\t\tuserId:    userId,\n\t\ttitle:     title,\n\t}\n}\n\ntype PostVideoFlow struct {\n\tvideoName string\n\tcoverName string\n\ttitle     string\n\tuserId    int64\n\n\tvideo *models.Video\n}\n\nfunc (f *PostVideoFlow) Do() error {\n\tf.prepareParam()\n\n\tif err := f.publish(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n//准备好参数\nfunc (f *PostVideoFlow) prepareParam() {\n\tf.videoName = util.GetFileUrl(f.videoName)\n\tf.coverName = util.GetFileUrl(f.coverName)\n}\n\n//组合并添加到数据库\nfunc (f *PostVideoFlow) publish() error {\n\tvideo := &models.Video{\n\t\tUserInfoId: f.userId,\n\t\tPlayUrl:    f.videoName,\n\t\tCoverUrl:   f.coverName,\n\t\tTitle:      f.title,\n\t}\n\treturn models.NewVideoDAO().AddVideo(video)\n}\n"
  },
  {
    "path": "deprecated/service/video/query_favor_videolist.go",
    "content": "package video\n\nimport (\n\t\"errors\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n)\n\ntype FavorList struct {\n\tVideos []*models2.Video `json:\"video_list\"`\n}\n\nfunc QueryFavorVideoList(userId int64) (*FavorList, error) {\n\treturn NewQueryFavorVideoListFlow(userId).Do()\n}\n\ntype QueryFavorVideoListFlow struct {\n\tuserId int64\n\n\tvideos []*models2.Video\n\n\tvideoList *FavorList\n}\n\nfunc NewQueryFavorVideoListFlow(userId int64) *QueryFavorVideoListFlow {\n\treturn &QueryFavorVideoListFlow{userId: userId}\n}\n\nfunc (q *QueryFavorVideoListFlow) Do() (*FavorList, error) {\n\tif err := q.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := q.prepareData(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := q.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.videoList, nil\n}\n\nfunc (q *QueryFavorVideoListFlow) checkNum() error {\n\tif !models2.NewUserInfoDAO().IsUserExistById(q.userId) {\n\t\treturn errors.New(\"用户状态异常\")\n\t}\n\treturn nil\n}\n\nfunc (q *QueryFavorVideoListFlow) prepareData() error {\n\terr := models2.NewVideoDAO().QueryFavorVideoListByUserId(q.userId, &q.videos)\n\tif err != nil {\n\t\treturn err\n\t}\n\t//填充信息(Author和IsFavorite字段，由于是点赞列表，故所有的都是点赞状态\n\tfor i := range q.videos {\n\t\t//作者信息查询\n\t\tvar userInfo models2.UserInfo\n\t\terr = models2.NewUserInfoDAO().QueryUserInfoById(q.videos[i].UserInfoId, &userInfo)\n\t\tif err == nil { //若查询未出错则更新，否则不更新作者信息\n\t\t\tq.videos[i].Author = userInfo\n\t\t}\n\t\tq.videos[i].IsFavorite = true\n\t}\n\treturn nil\n}\n\nfunc (q *QueryFavorVideoListFlow) packData() error {\n\tq.videoList = &FavorList{Videos: q.videos}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/service/video/query_videolist.go",
    "content": "package video\n\nimport (\n\t\"errors\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n)\n\ntype List struct {\n\tVideos []*models2.Video `json:\"video_list,omitempty\"`\n}\n\nfunc QueryVideoListByUserId(userId int64) (*List, error) {\n\treturn NewQueryVideoListByUserIdFlow(userId).Do()\n}\n\nfunc NewQueryVideoListByUserIdFlow(userId int64) *QueryVideoListByUserIdFlow {\n\treturn &QueryVideoListByUserIdFlow{userId: userId}\n}\n\ntype QueryVideoListByUserIdFlow struct {\n\tuserId int64\n\tvideos []*models2.Video\n\n\tvideoList *List\n}\n\nfunc (q *QueryVideoListByUserIdFlow) Do() (*List, error) {\n\tif err := q.checkNum(); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := q.packData(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn q.videoList, nil\n}\n\nfunc (q *QueryVideoListByUserIdFlow) checkNum() error {\n\t//检查userId是否存在\n\tif !models2.NewUserInfoDAO().IsUserExistById(q.userId) {\n\t\treturn errors.New(\"用户不存在\")\n\t}\n\n\treturn nil\n}\n\n//注意：Video由于在数据库中没有存储作者信息，所以需要手动填充\nfunc (q *QueryVideoListByUserIdFlow) packData() error {\n\terr := models2.NewVideoDAO().QueryVideoListByUserId(q.userId, &q.videos)\n\tif err != nil {\n\t\treturn err\n\t}\n\t//作者信息查询\n\tvar userInfo models2.UserInfo\n\terr = models2.NewUserInfoDAO().QueryUserInfoById(q.userId, &userInfo)\n\tp := cache.NewProxyIndexMap()\n\tif err != nil {\n\t\treturn err\n\t}\n\t//填充信息(Author和IsFavorite字段\n\tfor i := range q.videos {\n\t\tq.videos[i].Author = userInfo\n\t\tq.videos[i].IsFavorite = p.GetVideoFavorState(q.userId, q.videos[i].Id)\n\t}\n\n\tq.videoList = &List{Videos: q.videos}\n\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/util/comment.go",
    "content": "package util\n\nimport (\n\t\"errors\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n)\n\nfunc FillCommentListFields(comments *[]*models2.Comment) error {\n\tsize := len(*comments)\n\tif comments == nil || size == 0 {\n\t\treturn errors.New(\"util.FillCommentListFields comments为空\")\n\t}\n\tdao := models2.NewUserInfoDAO()\n\tfor _, v := range *comments {\n\t\t_ = dao.QueryUserInfoById(v.UserInfoId, &v.User) //填充这条评论的作者信息\n\t\tv.CreateDate = v.CreatedAt.Format(\"1-2\")         //转为前端要求的日期格式\n\t}\n\treturn nil\n}\n\nfunc FillCommentFields(comment *models2.Comment) error {\n\tif comment == nil {\n\t\treturn errors.New(\"FillCommentFields comments为空\")\n\t}\n\tcomment.CreateDate = comment.CreatedAt.Format(\"1-2\") //转为前端要求的日期格式\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/util/ffmpeg.go",
    "content": "package util\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\t\"log\"\n)\n\n// 可以更改\nvar (\n\tglobalMutex        sync.RWMutex\n\tdefaultVideoSuffix = \".mp4\"\n\tdefaultImageSuffix = \".jpg\"\n)\n\ntype Video2Image struct {\n\tinputPath  string\n\toutputPath string\n\tstartTime  string\n\tkeepTime   string\n\tfilter     string\n\tframeCount int\n\tdebug      bool\n}\n\nfunc NewVideo2Image() *Video2Image {\n\treturn &Video2Image{} // 每次返回新实例，避免共享状态\n}\n\nfunc GetDefaultVideoSuffix() string {\n\tglobalMutex.RLock()\n\tdefer globalMutex.RUnlock()\n\treturn defaultVideoSuffix\n}\n\nfunc GetDefaultImageSuffix() string {\n\tglobalMutex.RLock()\n\tdefer globalMutex.RUnlock()\n\treturn defaultImageSuffix\n}\n\n// ChangeVideoDefaultSuffix 全局配置修改方法（线程安全）\nfunc ChangeVideoDefaultSuffix(suffix string) {\n\tglobalMutex.Lock()\n\tdefer globalMutex.Unlock()\n\tdefaultVideoSuffix = normalizeExtension(suffix)\n}\n\nfunc ChangeImageDefaultSuffix(suffix string) {\n\tglobalMutex.Lock()\n\tdefer globalMutex.Unlock()\n\tdefaultImageSuffix = normalizeExtension(suffix)\n}\n\nfunc normalizeExtension(ext string) string {\n\tif ext == \"\" {\n\t\treturn ext\n\t}\n\tif ext[0] != '.' {\n\t\treturn \".\" + ext\n\t}\n\treturn ext\n}\n\n// SetInputPath 方法链式调用（非共享实例，无需加锁）\nfunc (v *Video2Image) SetInputPath(path string) *Video2Image {\n\tv.inputPath = path\n\treturn v\n}\n\nfunc (v *Video2Image) SetOutputPath(path string) *Video2Image {\n\tv.outputPath = path\n\treturn v\n}\n\nfunc (v *Video2Image) SetTimeOptions(start, duration string) *Video2Image {\n\tv.startTime = start\n\tv.keepTime = duration\n\treturn v\n}\n\nfunc (v *Video2Image) SetFilter(filter string) *Video2Image {\n\tv.filter = filter\n\treturn v\n}\n\nfunc (v *Video2Image) SetFrameCount(count int) *Video2Image {\n\tv.frameCount = count\n\treturn v\n}\n\nfunc (v *Video2Image) SetDebug(debug bool) *Video2Image {\n\tv.debug = debug\n\treturn v\n}\n\nfunc (v *Video2Image) buildArgs() ([]string, error) {\n\tif v.inputPath == \"\" || v.outputPath == \"\" {\n\t\treturn nil, errors.New(\"input and output path must be specified\")\n\t}\n\n\targs := []string{\n\t\t\"-i\", filepath.ToSlash(v.inputPath),\n\t\t\"-f\", \"image2\",\n\t}\n\n\tif v.filter != \"\" {\n\t\targs = append(args, \"-vf\", v.filter)\n\t}\n\tif v.startTime != \"\" {\n\t\targs = append(args, \"-ss\", v.startTime)\n\t}\n\tif v.keepTime != \"\" {\n\t\targs = append(args, \"-t\", v.keepTime)\n\t}\n\tif v.frameCount > 0 {\n\t\targs = append(args, \"-frames:v\", strconv.Itoa(v.frameCount))\n\t}\n\n\targs = append(args, \"-y\", filepath.ToSlash(v.outputPath))\n\treturn args, nil\n}\n\nfunc (v *Video2Image) Execute() error {\n\targs, err := v.buildArgs()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"参数构建失败: %w\", err)\n\t}\n\n\tffmpegPath := filepath.FromSlash(config.Global.FfmpegPath)\n\n\tcmd := exec.Command(ffmpegPath, args...)\n\tif v.debug {\n\t\tlog.Printf(\"执行命令: %q\", cmd.String())\n\t\tcmd.Stdout = log.Writer()\n\t\tcmd.Stderr = log.Writer()\n\t}\n\n\tif err := cmd.Run(); err != nil {\n\t\treturn fmt.Errorf(\"ffmpeg执行失败: %w (命令: %q)\", err, cmd.String())\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "deprecated/util/video.go",
    "content": "package util\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/ACking-you/byte_douyin_project/cache\"\n\t\"github.com/ACking-you/byte_douyin_project/config\"\n\tmodels2 \"github.com/ACking-you/byte_douyin_project/models\"\n\t\"log\"\n\t\"path/filepath\"\n\t\"time\"\n)\n\nfunc GetFileUrl(fileName string) string {\n\tbase := fmt.Sprintf(\"http://%s:%d/static/%s\", config.Global.IP, config.Global.Port, fileName)\n\treturn base\n}\n\n// NewFileName 根据userId+用户发布的视频数量连接成独一无二的文件名\nfunc NewFileName(userId int64) string {\n\tvar count int64\n\n\terr := models2.NewVideoDAO().QueryVideoCountByUserId(userId, &count)\n\tif err != nil {\n\t\tlog.Println(err)\n\t}\n\treturn fmt.Sprintf(\"%d-%d\", userId, count)\n}\n\n// FillVideoListFields 填充每个视频的作者信息（因为作者与视频的一对多关系，数据库中存下的是作者的id\n// 当userId>0时，我们判断当前为登录状态，其余情况为未登录状态，则不需要填充IsFavorite字段\nfunc FillVideoListFields(userId int64, videos *[]*models2.Video) (*time.Time, error) {\n\tif videos == nil || (len(*videos) == 0) {\n\t\treturn nil, errors.New(\"util.FillVideoListFields videos为空\")\n\t}\n\tsize := len(*videos)\n\tdao := models2.NewUserInfoDAO()\n\tp := cache.NewProxyIndexMap()\n\n\tlatestTime := (*videos)[size-1].CreatedAt //获取最近的投稿时间\n\t//添加作者信息，以及is_follow状态\n\tfor i := 0; i < size; i++ {\n\t\tvar userInfo models2.UserInfo\n\t\terr := dao.QueryUserInfoById((*videos)[i].UserInfoId, &userInfo)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tuserInfo.IsFollow = p.GetUserRelation(userId, userInfo.Id) //根据cache更新是否被点赞\n\t\t(*videos)[i].Author = userInfo\n\t\t//填充有登录信息的点赞状态\n\t\tif userId > 0 {\n\t\t\t(*videos)[i].IsFavorite = p.GetVideoFavorState(userId, (*videos)[i].Id)\n\t\t}\n\t}\n\treturn &latestTime, nil\n}\n\n// SaveImageFromVideo 将视频切一帧保存到本地\n// isDebug用于控制是否打印出执行的ffmepg命令\nfunc SaveImageFromVideo(name string, isDebug bool) error {\n\treturn NewVideo2Image().\n\t\tSetInputPath(filepath.Join(config.Global.StaticSourcePath, name+GetDefaultVideoSuffix())).\n\t\tSetOutputPath(filepath.Join(config.Global.StaticSourcePath, name+GetDefaultImageSuffix())).\n\t\tSetFrameCount(1).\n\t\tSetDebug(isDebug).Execute()\n}\n"
  }
]