sv.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "go-common/app/service/bbq/common"
  6. "go-common/library/conf/env"
  7. "go-common/library/ecode"
  8. "time"
  9. "go-common/app/interface/bbq/app-bbq/api/http/v1"
  10. "go-common/app/interface/bbq/app-bbq/model"
  11. "go-common/app/interface/bbq/app-bbq/model/grpc"
  12. rec "go-common/app/service/bbq/recsys/api/grpc/v1"
  13. user "go-common/app/service/bbq/user/api"
  14. video "go-common/app/service/bbq/video/api/grpc/v1"
  15. "go-common/library/log"
  16. "go-common/library/net/trace"
  17. )
  18. // SvList 短视屏推荐列表
  19. func (s *Service) SvList(c context.Context, pageSize int64, mid int64, base *v1.Base, deviceID string) (res []*v1.VideoResponse, err error) {
  20. var (
  21. svids []int64
  22. svRes map[int64]*v1.VideoResponse
  23. )
  24. res = make([]*v1.VideoResponse, 0)
  25. //推荐列表
  26. svids, err = s.dao.RawRecList(c, pageSize, mid, base.BUVID)
  27. if err != nil || len(svids) == 0 || env.DeployEnv == env.DeployEnvUat {
  28. log.Warnv(c, log.KV("log", fmt.Sprintf("s.dao.GetList err[%v]", err)))
  29. // 降级
  30. svids, _ = s.dao.GetList(c, pageSize)
  31. }
  32. svRes, err = s.svInfos(c, svids, mid, false)
  33. for _, id := range svids {
  34. if sv, ok := svRes[id]; ok {
  35. if common.IsRecommendSvStateAvailable(int64(sv.State)) {
  36. res = append(res, sv)
  37. } else {
  38. log.Warnw(c, "log", "get error svid in recommend list", "svid", id, "mid", mid, "sv", sv)
  39. }
  40. }
  41. }
  42. return
  43. }
  44. // svPlays 批量获取playurl(相对地址方法)
  45. func (s *Service) svPlays(c context.Context, svids []int64) map[int64]*v1.VideoPlay {
  46. var (
  47. relAddr []string
  48. err error
  49. bvcUrls map[string]*grpc.VideoKeyItem
  50. bvcKeys map[int64][]*model.SVBvcKey
  51. )
  52. playMap := make(map[int64]*v1.VideoPlay)
  53. bvcKeys, err = s.dao.RawSVBvcKey(c, svids)
  54. if err != nil {
  55. log.Error("s.dao.RawSVBvcKey err[%v]", err)
  56. }
  57. for id, keys := range bvcKeys {
  58. playMap[id] = &v1.VideoPlay{
  59. SVID: id,
  60. }
  61. for k, v := range keys {
  62. if k == 0 {
  63. playMap[id].Quality = int64(v.CodeRate)
  64. }
  65. fi := &v1.FileInfo{
  66. TimeLength: v.Duration,
  67. FileSize: v.FileSize,
  68. Path: v.Path,
  69. }
  70. playMap[id].FileInfo = append(playMap[id].FileInfo, fi)
  71. playMap[id].SupportQuality = append(playMap[id].SupportQuality, int64(v.CodeRate))
  72. relAddr = append(relAddr, v.Path)
  73. }
  74. }
  75. bvcUrls, err = s.dao.RelPlayURLs(c, relAddr)
  76. if err != nil {
  77. log.Error("s.dao.RelPlayURLs err[%v]", err)
  78. }
  79. //拼装playurl
  80. for _, svid := range svids {
  81. if play, ok := playMap[svid]; ok {
  82. for fk, f := range play.FileInfo {
  83. if urls, ok := bvcUrls[f.Path]; ok {
  84. playMap[svid].ExpireTime = int64(urls.Etime)
  85. playMap[svid].CurrentTime = time.Now().Unix()
  86. for _, u := range urls.URL {
  87. if playMap[svid].FileInfo[fk].URL == "" {
  88. playMap[svid].FileInfo[fk].URL = u
  89. if playMap[svid].URL == "" {
  90. playMap[svid].URL = u
  91. }
  92. continue
  93. }
  94. if playMap[svid].FileInfo[fk].URLBc == "" {
  95. playMap[svid].FileInfo[fk].URLBc = u
  96. break
  97. }
  98. }
  99. } else {
  100. delete(playMap, svid)
  101. break
  102. }
  103. }
  104. playMap[svid] = play
  105. }
  106. }
  107. return playMap
  108. }
  109. // SvStatistics 视频统计服务
  110. func (s *Service) SvStatistics(c context.Context, mid int64, svids []int64) (res []*v1.SvStatRes, err error) {
  111. var (
  112. stMap map[int64]*model.SvStInfo
  113. ulike map[int64]bool
  114. upIDs []int64
  115. )
  116. svInfos, _ := s.dao.RawVideos(c, svids)
  117. for _, sv := range svInfos {
  118. upIDs = append(upIDs, sv.MID)
  119. }
  120. stMap, err = s.dao.RawVideoStatistic(c, svids)
  121. if err != nil {
  122. log.Error("s.dao.RawVideoStatistic err[%v]", err)
  123. }
  124. //点赞状态
  125. ulike, err = s.dao.CheckUserLike(c, mid, svids)
  126. if err != nil {
  127. log.Error("s.dao.CheckUserLike err[%v]", err)
  128. }
  129. uflw, _ := s.dao.BatchUserInfo(c, mid, upIDs, false, false, true)
  130. if err != nil {
  131. log.Error("s.dao.IsFollow err[%v]", err)
  132. }
  133. for _, id := range svids {
  134. rp := &v1.SvStatRes{}
  135. rp.SVID = id
  136. if st, ok := stMap[id]; ok {
  137. rp.Like = st.Like
  138. rp.Share = st.Share
  139. rp.Play = st.Play
  140. rp.Subtitles = st.Subtitles
  141. rp.Reply = st.Reply
  142. }
  143. if l, ok := ulike[id]; ok {
  144. rp.IsLike = l
  145. }
  146. if sv, ok := svInfos[id]; ok {
  147. if f, ok2 := uflw[sv.MID]; ok2 {
  148. rp.FollowState = f.FollowState
  149. }
  150. }
  151. res = append(res, rp)
  152. }
  153. return
  154. }
  155. // SvCPlays 批量拉取playurl
  156. func (s *Service) SvCPlays(c context.Context, svids []int64, mid int64) (res []*v1.VideoPlay, err error) {
  157. res = make([]*v1.VideoPlay, 0)
  158. //视频列表
  159. svRes, err := s.dao.RawVideos(c, svids)
  160. if err != nil {
  161. log.Error("s.dao.RawVideos err[%v]", err)
  162. return
  163. }
  164. avaliableSvids := make([]int64, 0)
  165. for _, v := range svRes {
  166. if (mid == 0 || v.MID != mid) && common.IsSvStateGuestAvailable(int64(v.State)) {
  167. avaliableSvids = append(avaliableSvids, v.SVID)
  168. } else if v.MID == mid && common.IsSvStateOwnerAvailable(int64(v.State)) {
  169. avaliableSvids = append(avaliableSvids, v.SVID)
  170. }
  171. }
  172. playMap := s.svPlays(c, avaliableSvids)
  173. for _, svid := range avaliableSvids {
  174. var play *v1.VideoPlay
  175. if p, ok := playMap[svid]; !ok {
  176. log.Warn("play不存在 svid[%d]", svid)
  177. continue
  178. } else {
  179. play = p
  180. }
  181. res = append(res, play)
  182. }
  183. return
  184. }
  185. // SvDetail 单条sv的视频详情,暂时只用于评论中转页
  186. func (s *Service) SvDetail(c context.Context, svid int64, mid int64) (res *v1.VideoResponse, err error) {
  187. _, err = s.dao.VideoBase(c, mid, svid)
  188. if err != nil {
  189. return
  190. }
  191. svInfos, err := s.svInfos(c, []int64{svid}, mid, true)
  192. if err != nil {
  193. return
  194. }
  195. if val, exists := svInfos[svid]; exists {
  196. res = val
  197. } else {
  198. err = ecode.VideoUnExists
  199. log.Infow(c, "log", "not sv info", "svid", svid)
  200. }
  201. return
  202. }
  203. // svInfos 批量获取视频信息
  204. // @params allowState 可放出状态,传空为app整体可露出状态
  205. // @params needStInfo 是否需要视频统计数据
  206. func (s *Service) svInfos(c context.Context, ids []int64, mid int64, needStInfo bool) (res map[int64]*v1.VideoResponse, err error) {
  207. var (
  208. mids []int64
  209. svRes map[int64]*model.SvInfo
  210. ulike map[int64]bool
  211. stMap map[int64]*model.SvStInfo
  212. )
  213. res = make(map[int64]*v1.VideoResponse)
  214. stMap = make(map[int64]*model.SvStInfo)
  215. //视频列表
  216. svRes, err = s.dao.RawVideos(c, ids)
  217. if err != nil {
  218. log.Error("s.dao.RawVideos err[%v]", err)
  219. return
  220. }
  221. for _, v := range svRes {
  222. mids = append(mids, v.MID)
  223. }
  224. if mid != 0 {
  225. ulike, err = s.dao.CheckUserLike(c, mid, ids)
  226. if err != nil {
  227. log.Error("s.dao.CheckUserLike err[%v]", err)
  228. }
  229. }
  230. // query id
  231. tracer, _ := trace.FromContext(c)
  232. queryID := fmt.Sprintf("%s", tracer)
  233. //账号
  234. var userMap map[int64]*user.UserBase
  235. userMap, err = s.dao.JustGetUserBase(c, mids)
  236. if err != nil {
  237. log.Error("s.dao.UserBase err[%v]", err)
  238. }
  239. // play信息
  240. playMap := s.svPlays(c, ids)
  241. if needStInfo {
  242. stMap, err = s.dao.RawVideoStatistic(c, ids)
  243. if err != nil {
  244. log.Error("s.dao.RawVideoStatistic err[%v]", err)
  245. }
  246. }
  247. // extension信息
  248. extensions, tmpErr := s.getExtension(c, ids)
  249. if tmpErr != nil {
  250. log.Warnw(c, "log", "get extension fail")
  251. }
  252. for _, v := range svRes {
  253. if common.IsSvStateAvailable(int64(v.State)) {
  254. sv := &v1.VideoResponse{}
  255. if acc, ok := userMap[v.MID]; ok {
  256. sv.UserInfo = *acc
  257. }
  258. if lk, ok := ulike[v.SVID]; ok {
  259. sv.IsLike = lk
  260. }
  261. sv.SVID = v.SVID
  262. sv.Title = v.Title
  263. sv.Content = v.Content
  264. sv.MID = v.MID
  265. sv.Duration = v.Duration
  266. sv.Pubtime = v.Pubtime
  267. sv.Ctime = v.Ctime
  268. sv.AVID = v.AVID
  269. sv.CID = v.CID
  270. sv.From = v.From
  271. sv.CoverURL = v.CoverURL
  272. sv.CoverHeight = v.CoverHeight
  273. sv.CoverWidth = v.CoverWidth
  274. sv.QueryID = queryID
  275. sv.State = v.State
  276. if play, ok := playMap[v.SVID]; ok {
  277. sv.Play = *play
  278. res[v.SVID] = sv
  279. } else {
  280. log.Warn("play不存在 svid[%d],此条记录直接舍弃", v.SVID)
  281. }
  282. if st, ok := stMap[v.SVID]; ok {
  283. sv.SvStInfo = *st
  284. }
  285. if extension, exists := extensions[v.SVID]; exists {
  286. sv.Extension = extension.Extension
  287. }
  288. res[v.SVID] = sv
  289. }
  290. }
  291. return
  292. }
  293. // SvRelRec 相关推荐服务
  294. func (s *Service) SvRelRec(c context.Context, data *v1.SvRelReq) (res map[string]interface{}, err error) {
  295. res = make(map[string]interface{})
  296. var svMap map[int64]*v1.VideoResponse
  297. list := make([]*v1.VideoResponse, 0)
  298. relReq := &rec.RecsysRequest{
  299. SVID: data.SVID,
  300. Offset: data.Offset,
  301. Limit: data.Limit,
  302. QueryID: data.QueryID,
  303. App: data.APP,
  304. AppVersion: data.APPVersion,
  305. BUVID: data.BUVID,
  306. MID: data.MID,
  307. }
  308. IDList, err := s.dao.RelRecList(c, relReq)
  309. if err != nil {
  310. err = nil
  311. return
  312. }
  313. svMap, err = s.svInfos(c, IDList, data.MID, false)
  314. if err != nil {
  315. err = nil
  316. return
  317. }
  318. for _, id := range IDList {
  319. if sv, ok := svMap[id]; ok {
  320. list = append(list, sv)
  321. }
  322. }
  323. res["list"] = list
  324. return
  325. }
  326. // SvDel 视频删除
  327. func (s *Service) SvDel(c context.Context, in *video.VideoDeleteRequest) (interface{}, error) {
  328. return s.dao.SvDel(c, in)
  329. }