index.go 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  1. package v1
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "net/url"
  7. "reflect"
  8. "strconv"
  9. "sync/atomic"
  10. "time"
  11. v1indexpb "go-common/app/interface/live/app-interface/api/http/v1"
  12. "go-common/app/interface/live/app-interface/conf"
  13. "go-common/app/interface/live/app-interface/dao"
  14. liveuserV1 "go-common/app/service/live/live_user/api/liverpc/v1"
  15. relationV2 "go-common/app/service/live/relation/api/liverpc/v2"
  16. roomV1 "go-common/app/service/live/room/api/liverpc/v1"
  17. roomV2 "go-common/app/service/live/room/api/liverpc/v2"
  18. bannerV1 "go-common/app/service/live/room_ex/api/liverpc/v1"
  19. "go-common/app/service/live/third_api/bvc"
  20. userextV1 "go-common/app/service/live/userext/api/liverpc/v1"
  21. "go-common/library/ecode"
  22. "go-common/library/log"
  23. "go-common/library/net/metadata"
  24. "go-common/library/net/rpc/liverpc"
  25. rpcCtx "go-common/library/net/rpc/liverpc/context"
  26. "go-common/library/sync/errgroup"
  27. "go-common/library/xstr"
  28. "math/rand"
  29. "github.com/bitly/go-simplejson"
  30. "github.com/pkg/errors"
  31. "go-common/library/net/http/blademaster"
  32. )
  33. const (
  34. _bannerType = 1
  35. _navigatorType = 2
  36. _yunyingRecFormType = 3
  37. _yunyingRecSquareType = 4
  38. _recFormType = 6
  39. _recSquareType = 7
  40. _feedType = 8
  41. _parentAreaFormType = 9
  42. _parentAreaSquareType = 10
  43. _myAreaTagType = 12
  44. _seaPatrolType = 14
  45. _myAreaTagListType = 13
  46. _activityType = 11
  47. // _recTypeOnline = 1
  48. // _recTypeIncome = 2
  49. _recTypeForce = 3
  50. _recTypeSkyHorse = 4
  51. _defaultRecNum = 24
  52. _skyHorseRecTimeOut = 100
  53. _areaModuleLink = "https://live.bilibili.com/app/area?parent_area_id=%d&parent_area_name=%s&area_id=%d&area_name=%s"
  54. _activityGo = 0
  55. _activityBook = 1
  56. _activityUnbook = 2
  57. _mobileIndexBadgeColorDefault = "#FB9E60"
  58. )
  59. // Service struct
  60. type Service struct {
  61. conf *conf.Config
  62. // optionally add other properties here, such as dao
  63. dao *dao.Dao
  64. allListInfo atomic.Value
  65. }
  66. type roomItem struct {
  67. RoomId int64 `json:"roomid"`
  68. Title string `json:"title"`
  69. Uname string `json:"uname"`
  70. Online int64 `json:"online"`
  71. Cover string `json:"cover"`
  72. Link string `json:"link"`
  73. Face string `json:"face"`
  74. AreaV2ParentId int64 `json:"area_v2_parent_id"`
  75. AreaV2ParentName string `json:"area_v2_parent_name"`
  76. AreaV2Id int64 `json:"area_v2_id"`
  77. AreaV2Name string `json:"area_v2_name"`
  78. PlayUrl string `json:"play_url"`
  79. PlayUrlH265 string `json:"play_url_h265"`
  80. CurrentQuality int64 `json:"current_quality"`
  81. BroadcastType int64 `json:"broadcast_type"`
  82. PendentRu string `json:"pendent_ru"`
  83. PendentRuPic string `json:"pendent_ru_pic"`
  84. PendentRuColor string `json:"pendent_ru_color"`
  85. RecType int64 `json:"rec_type"`
  86. PkId int64 `json:"pk_id"`
  87. AcceptQuality []int64 `json:"accept_quality"`
  88. }
  89. type offlineItem struct {
  90. Id int `json:"id"`
  91. Name string `json:"name"`
  92. }
  93. type userTagItem struct {
  94. AreaV2Id int `json:"area_v2_id"`
  95. AreaV2Name string `json:"area_v2_name"`
  96. AreaV2ParentId int `json:"area_v2_parent_id"`
  97. AreaV2ParentName string `json:"area_v2_parent_name"`
  98. Pic string `json:"pic"`
  99. Link string `json:"link"`
  100. IsAdvice int `json:"is_advice"`
  101. }
  102. type commonResp struct {
  103. ModuleInfo map[string]interface{}
  104. ExtraInfo map[string]interface{}
  105. List interface{}
  106. }
  107. type ModuleResp struct {
  108. Interval int `json:"interval"`
  109. ModuleList []map[string]interface{} `json:"module_list"`
  110. }
  111. // New init
  112. func New(c *conf.Config) (s *Service) {
  113. s = &Service{
  114. conf: c,
  115. dao: dao.New(c),
  116. }
  117. go s.tickCacheAllList(context.TODO())
  118. return s
  119. }
  120. // GetAllList implementation
  121. // 首页大接口
  122. func (s *Service) GetAllList(ctx context.Context, req *v1indexpb.GetAllListReq) (ret interface{}, err error) {
  123. resp := &ModuleResp{
  124. Interval: 10,
  125. }
  126. build := req.Build
  127. relationTimeout := conf.GetTimeout("relation", 200)
  128. // dao.LiveUserApi.V1UserSetting.GetTag(ctx, &liveuserV1.UserSettingGetTagReq{})
  129. midInterface, isUIDSet := metadata.Value(ctx, metadata.Mid).(int64) // 大多使用header里的mid解析, 框架已封装请求的header
  130. isSkyHorseGray := false
  131. mid := int64(0)
  132. if isUIDSet {
  133. mid = midInterface
  134. // 天马灰度
  135. isSkyHorseGray = s.isSkyHorseRec(mid)
  136. }
  137. buvid := ""
  138. // 主站封好的,可从device里获取到sid、buvid、buvid3、build、channel、device、mobi_app、platform
  139. device, ok := metadata.Value(ctx, metadata.Device).(*blademaster.Device)
  140. if ok {
  141. buvid = device.Buvid
  142. }
  143. // deviceInterface := req.Device
  144. // device := deviceInterface.(*bm.Device)
  145. if req.Platform == "" || req.Device == "" || req.Scale == "" || req.RelationPage == 0 {
  146. err = errors.WithMessage(ecode.InvalidParam, "INVALID PARAM")
  147. return
  148. }
  149. allListTimeout := time.Duration(conf.GetTimeout("allList", 50)) * time.Millisecond
  150. rawModuleList := s.getAllListFromCache(rpcCtx.WithTimeout(ctx, allListTimeout))
  151. if rawModuleList == nil {
  152. err = errors.WithMessage(ecode.GetAllListReturnError, "")
  153. return
  154. }
  155. // 大分区常量定义
  156. parentName := map[int64]string{
  157. 1: "娱乐",
  158. 2: "游戏",
  159. 3: "手游",
  160. 4: "绘画",
  161. 5: "电台",
  162. }
  163. // 天马灰度/保底
  164. defaultRecSlice := make([]map[string]interface{}, 0)
  165. loginRecRoomIDSlice := make([]int64, 0)
  166. // [{module_info:{},list:{}}...]
  167. resp.ModuleList = make([]map[string]interface{}, len(rawModuleList))
  168. for _, m := range rawModuleList {
  169. module := m.(map[string]interface{})
  170. if module["module_info"] == nil {
  171. log.Error("empty_module_info:%+v \n", m)
  172. fmt.Printf("empty_module_info:raw_all:%+v \n", rawModuleList)
  173. }
  174. moduleInfo := module["module_info"].(map[string]interface{})
  175. moduleType := jsonMustInt(moduleInfo["type"], 0)
  176. if moduleType == 0 {
  177. continue
  178. }
  179. list := module["list"].([]interface{})
  180. if moduleType == _recFormType || moduleType == _recSquareType {
  181. for _, r := range list {
  182. recItem := r.(map[string]interface{})
  183. defaultRecSlice = append(defaultRecSlice, recItem)
  184. roomID := jsonMustInt(recItem["roomid"], 0)
  185. if roomID == 0 {
  186. continue
  187. }
  188. // 登录了也有可能请求不到数据,登录的保底用
  189. loginRecRoomIDSlice = append(loginRecRoomIDSlice, roomID)
  190. }
  191. }
  192. }
  193. // 常用标签 roomListMap
  194. myTagRoomListMap := make(map[int64][]*roomV2.AppIndexGetMultiRoomListResp_RoomList)
  195. myTagAreaIds := make([]int64, 0, 4)
  196. myTagAreaInfoMap := make(map[int64]*liveuserV1.UserSettingGetTagResp_Tags)
  197. myTagResp := commonResp{
  198. List: make([]interface{}, 0),
  199. }
  200. attentionResp := commonResp{
  201. List: make([]interface{}, 0),
  202. }
  203. loginRecResp := commonResp{}
  204. seaResp := commonResp{}
  205. bannerResp := commonResp{
  206. List: make([]interface{}, 0),
  207. }
  208. // playurl定义
  209. attentionRoomListPlayURLMap := make(map[int64]*bvc.PlayUrlItem)
  210. loginRecRoomListPlayURLMap := make(map[int64]*bvc.PlayUrlItem)
  211. myTagRoomListPlayURLMap := make(map[int64]*bvc.PlayUrlItem)
  212. otherRoomListPlayURLMap := make(map[int64]*bvc.PlayUrlItem)
  213. otherRoomIDSlice := make([]int64, 0)
  214. isSkyHorseGrayOk := 0
  215. // 此group包含首页的一些任务
  216. // 但是任务之间不能同时cancel
  217. // 不然一个接口出错所有任务都cancel首页就空了
  218. // 所以return固定为nil(一个wg的任务使用的是从bm context继承来的ctx,cancel后一起推出)
  219. // 只有上层ctx(http的ctx)出问题(超时等)才会退出后续任务1
  220. wg, _ := errgroup.WithContext(ctx)
  221. for _, m := range rawModuleList {
  222. // {module_info:xx}
  223. module := m.(map[string]interface{})
  224. // {id:xx,type:xx,pic:xx,title:xx,link:xx,...}
  225. moduleInfo := module["module_info"].(map[string]interface{})
  226. moduleList := module["list"].([]interface{})
  227. moduleType := jsonMustInt(moduleInfo["type"], 0)
  228. if moduleType == 0 {
  229. continue
  230. }
  231. // banner分支 分端分版本
  232. if moduleType == _bannerType {
  233. bannerTimeout := time.Duration(conf.GetTimeout("banner", 100)) * time.Millisecond
  234. wg.Go(func() error {
  235. bannerList, bannerErr := dao.RoomExtApi.V1Banner.GetNewBanner(rpcCtx.WithTimeout(ctx, time.Duration(bannerTimeout)*time.Millisecond), &bannerV1.BannerGetNewBannerReq{UserPlatform: req.Platform, Build: build, UserDevice: req.Device})
  236. if bannerErr != nil {
  237. log.Error("[GetAllList]get banner rpc error, roomex.v1.Banner.GetNewBanner, error:%+v,rpctimeout:%d", bannerErr, bannerTimeout)
  238. return nil
  239. }
  240. if bannerList.Code != 0 || bannerList.Data == nil {
  241. log.Error("[GetAllList]get banner response error, code, %d, msg: %s, error:%+v", bannerList.Code, bannerList.Msg, bannerErr)
  242. return nil
  243. }
  244. if len(bannerList.Data) > 0 {
  245. for _, bannerInfo := range bannerList.Data {
  246. bannerResp.List = append(bannerResp.List.([]interface{}), map[string]interface{}{
  247. "id": bannerInfo.Id,
  248. "pic": bannerInfo.Pic,
  249. "link": bannerInfo.Link,
  250. "title": bannerInfo.Title,
  251. })
  252. }
  253. }
  254. return nil
  255. })
  256. }
  257. // 关注分支
  258. if moduleType == _feedType {
  259. if !isUIDSet {
  260. continue
  261. }
  262. wg.Go(func() error {
  263. currentAttentionRoomMap := make(map[int64]bool)
  264. currentAttentionRoomSlice := make([]int64, 0)
  265. attentionResp.ModuleInfo = moduleInfo
  266. currentAttention, attentionErr := dao.RelationApi.V2App.LiveHomePage(rpcCtx.WithTimeout(ctx, time.Duration(relationTimeout)*time.Millisecond), &relationV2.AppLiveHomePageReq{RelationPage: req.RelationPage})
  267. if attentionErr != nil {
  268. log.Error("[GetAllList]get user attention rpc error, relation.v2.App.liveHomePage, error:%+v,rpctimeout:%d", attentionErr, relationTimeout)
  269. } else if currentAttention.Code != 0 || currentAttention.Data == nil {
  270. log.Error("[GetAllList]get user attention response error, code, %d, msg: %s, error:%+v", currentAttention.Code, currentAttention.Msg, attentionErr)
  271. } else {
  272. attentionResp.ExtraInfo = map[string]interface{}{
  273. "total_count": currentAttention.Data.TotalCount,
  274. "time_desc": currentAttention.Data.TimeDesc,
  275. "uname_desc": currentAttention.Data.UnameDesc,
  276. "tags_desc": currentAttention.Data.TagsDesc,
  277. "relation_page": currentAttention.Data.RelationPage,
  278. }
  279. // 存关注map
  280. for _, attentionCard := range currentAttention.Data.Rooms {
  281. currentAttentionRoomMap[attentionCard.Roomid] = true
  282. currentAttentionRoomSlice = append(currentAttentionRoomSlice, attentionCard.Roomid)
  283. attentionResp.List = append(attentionResp.List.([]interface{}), attentionCard)
  284. }
  285. // playurl
  286. attentionRoomListPlayURLMap = dao.BvcApi.GetPlayUrlMulti(ctx, currentAttentionRoomSlice, 0, 4, build, req.Platform)
  287. }
  288. useDefaultRec := false
  289. isOpen := conf.Conf.SkyHorseStatus
  290. if isOpen && isSkyHorseGray {
  291. // 取天马数据,传入关注当前刷列表roomid+强推,天马会对传入的roomid去重
  292. // duplicates := append(currentAttentionRoomSlice, forceRecSlice...)
  293. // getSkyHorseRoomList已经对强推去重
  294. duplicates := currentAttentionRoomSlice
  295. skyRecResult, skyHorseErr := getSkyHorseRoomList(ctx, mid, buvid, req.Build, req.Platform, duplicates, 1)
  296. if skyHorseErr != nil {
  297. log.Warn("[GetAllList]get data from skyHorse err: %v", skyHorseErr)
  298. useDefaultRec = true
  299. }
  300. if skyRecResult == nil {
  301. log.Warn("[GetAllList]get data from skyHorse empty: %v", skyHorseErr)
  302. useDefaultRec = true
  303. }
  304. if len(skyRecResult) < 6 {
  305. log.Warn("[GetAllList]get data from skyHorse not enough: %v", skyRecResult)
  306. useDefaultRec = true
  307. }
  308. skyRecResultInterface := make([]map[string]interface{}, 0)
  309. for _, item := range skyRecResult {
  310. loginRecRoomIDSlice = append(loginRecRoomIDSlice, item.RoomId)
  311. skyRecResultInterface = append(skyRecResultInterface, map[string]interface{}{
  312. "roomid": item.RoomId,
  313. "title": item.Title,
  314. "uname": item.Uname,
  315. "online": item.Online,
  316. "cover": item.Cover,
  317. "link": item.Link,
  318. "face": item.Face,
  319. "area_v2_parent_id": item.AreaV2ParentId,
  320. "area_v2_parent_name": item.AreaV2ParentName,
  321. "area_v2_id": item.AreaV2Id,
  322. "area_v2_name": item.AreaV2Name,
  323. "play_url": item.PlayUrl,
  324. "current_quality": item.CurrentQuality,
  325. "broadcast_type": item.BroadcastType,
  326. "pendent_ru": item.PendentRu,
  327. "pendent_ru_pic": item.PendentRuPic,
  328. "pendent_ru_color": item.PendentRuColor,
  329. "rec_type": item.RecType,
  330. "pk_id": item.PkId,
  331. "accept_quality": item.AcceptQuality,
  332. })
  333. }
  334. loginRecResp.List = skyRecResultInterface
  335. isSkyHorseGrayOk = 1
  336. }
  337. // 保底逻辑
  338. if !isOpen || !isSkyHorseGray || useDefaultRec {
  339. newDefaultRecSlice := make([]map[string]interface{}, 0)
  340. for _, defaultRecRoom := range defaultRecSlice {
  341. roomID := jsonMustInt(defaultRecRoom["roomid"], 0)
  342. if roomID == 0 {
  343. continue
  344. }
  345. if _, exist := currentAttentionRoomMap[roomID]; !exist {
  346. // 天马没取到保底:默认推荐要对关注当前刷去重
  347. newDefaultRecSlice = append(newDefaultRecSlice, defaultRecRoom)
  348. }
  349. }
  350. // 只返24个
  351. if len(newDefaultRecSlice) > _defaultRecNum {
  352. loginRecResp.List = newDefaultRecSlice[:_defaultRecNum]
  353. } else {
  354. loginRecResp.List = newDefaultRecSlice
  355. }
  356. isSkyHorseGrayOk = 0
  357. }
  358. loginRecRoomListPlayURLMap = dao.BvcApi.GetPlayUrlMulti(ctx, loginRecRoomIDSlice, 0, 4, build, req.Platform)
  359. return nil
  360. })
  361. }
  362. // 常用标签分支
  363. if moduleType == _myAreaTagType {
  364. wg.Go(func() error {
  365. myTagResp.ModuleInfo = moduleInfo
  366. getMyTagTimeout := time.Duration(conf.GetTimeout("getMyTag", 100)) * time.Millisecond
  367. myTagListResp, userTagError := dao.LiveUserApi.V1UserSetting.GetTag(rpcCtx.WithTimeout(ctx, getMyTagTimeout), &liveuserV1.UserSettingGetTagReq{})
  368. if userTagError != nil {
  369. log.Error("[GetAllList]get user tag rpc error, live_user.v1.usersetting.get_tag, error:%+v", userTagError)
  370. return nil // 如果return err 则所有当前group的任务都会cancel
  371. }
  372. if myTagListResp.Code != 0 || myTagListResp.Data == nil {
  373. log.Error("[GetAllList]get user tag return error, code, %d, msg: %s, error:%+v", myTagListResp.Code, myTagListResp.Msg, userTagError)
  374. return nil
  375. }
  376. if myTagListResp.Data != nil {
  377. myTagResp.ExtraInfo = make(map[string]interface{})
  378. myTagResp.ExtraInfo["is_gray"] = myTagListResp.Data.IsGray
  379. myTagResp.ExtraInfo["offline"] = make([]interface{}, 0)
  380. for _, offlineInfo := range myTagListResp.Data.Offline {
  381. myTagResp.ExtraInfo["offline"] = append(myTagResp.ExtraInfo["offline"].([]interface{}), &offlineItem{Id: int(offlineInfo.Id), Name: offlineInfo.Name})
  382. }
  383. }
  384. for _, tagInfo := range myTagListResp.Data.Tags {
  385. myTagAreaIds = append(myTagAreaIds, tagInfo.Id)
  386. myTagAreaInfoMap[tagInfo.Id] = tagInfo
  387. link := fmt.Sprintf("http://live.bilibili.com/app/area?parent_area_id=%d&parent_area_name=%s&area_id=%d&area_name=%s", tagInfo.ParentId, parentName[tagInfo.ParentId], tagInfo.Id, tagInfo.Name)
  388. myTagResp.List = append(myTagResp.List.([]interface{}), &userTagItem{AreaV2Id: int(tagInfo.Id), AreaV2Name: tagInfo.Name, AreaV2ParentId: int(tagInfo.ParentId), AreaV2ParentName: parentName[tagInfo.ParentId], Link: link, Pic: tagInfo.Pic, IsAdvice: int(tagInfo.IsAdvice)})
  389. }
  390. if (req.Platform == "ios" && build > 8220) || (req.Platform == "android" && build > 5333002) {
  391. myTagResp.List = append(myTagResp.List.([]interface{}), &userTagItem{AreaV2Id: int(0), AreaV2Name: "全部标签", AreaV2ParentId: int(0), AreaV2ParentName: "", Pic: "http://i0.hdslb.com/bfs/vc/ff03528785fc8c91491d79e440398484811d6d87.png", Link: "http://live.bilibili.com/app/mytag/", IsAdvice: 1})
  392. }
  393. if len(myTagAreaIds) <= 0 {
  394. log.Error("[GetAllList]get user tag return empty!uid:%d", mid)
  395. return nil
  396. }
  397. // 常用标签房间列表 先生成最后wait替换就好了
  398. getMultiRoomListTimeout := time.Duration(conf.GetTimeout("getMultiRoomList", 100)) * time.Millisecond
  399. myTagRoomListResp, multiRoomListErr := dao.RoomApi.V2AppIndex.GetMultiRoomList(rpcCtx.WithTimeout(ctx, getMultiRoomListTimeout), &roomV2.AppIndexGetMultiRoomListReq{AreaIds: xstr.JoinInts(myTagAreaIds), Platform: req.Platform})
  400. if multiRoomListErr != nil {
  401. log.Error("[GetAllList]get multi list rpc error, room.v2.AppIndex.GetMultiRoomList, error:%+v", multiRoomListErr)
  402. return nil
  403. }
  404. if myTagRoomListResp.Code != 0 || myTagRoomListResp.Data == nil {
  405. log.Error("[GetAllList]get multi list response error, code, %d, msg: %s, error:%+v", myTagRoomListResp.Code, myTagRoomListResp.Msg, multiRoomListErr)
  406. return nil
  407. }
  408. // 保存roomListMap,wait 聚合数据
  409. myTagRoomIDSlice := make([]int64, 0)
  410. for _, myTagRoomItem := range myTagRoomListResp.Data {
  411. myTagRoomListMap[myTagRoomItem.Id] = myTagRoomItem.List
  412. for _, item := range myTagRoomItem.List {
  413. myTagRoomIDSlice = append(myTagRoomIDSlice, item.Roomid)
  414. }
  415. }
  416. myTagRoomListPlayURLMap = dao.BvcApi.GetPlayUrlMulti(ctx, myTagRoomIDSlice, 0, 4, build, req.Platform)
  417. return nil
  418. })
  419. }
  420. if moduleType == _seaPatrolType {
  421. seaPatrolList := make([]interface{}, 0)
  422. if !isUIDSet {
  423. continue
  424. }
  425. // 大航海分支
  426. wg.Go(func() error {
  427. seaPatrolTimeout := time.Duration(conf.GetTimeout("seaPatrol", 100)) * time.Millisecond
  428. seaPatrol, seaPatrolError := dao.LiveUserApi.V1Note.Get(rpcCtx.WithTimeout(ctx, seaPatrolTimeout), &liveuserV1.NoteGetReq{})
  429. if seaPatrolError != nil {
  430. log.Error("[GetAllList]get sea patrol rpc error, liveuser.v1.Note.Get, error:%+v", seaPatrolError)
  431. return nil
  432. }
  433. if seaPatrol.Code != 0 || seaPatrol.Data == nil {
  434. log.Error("[GetAllList]get sea patrol note from liveuser response error, code, %d, msg: %s, error:%+v", seaPatrol.Code, seaPatrol.Msg, seaPatrolError)
  435. return nil
  436. }
  437. if seaPatrol.Data.Title != "" {
  438. seaPatrolList = append(seaPatrolList, map[string]interface{}{
  439. "pic": seaPatrol.Data.Logo,
  440. "title": seaPatrol.Data.Title,
  441. "link": seaPatrol.Data.Link,
  442. "content": seaPatrol.Data.Content,
  443. })
  444. }
  445. seaResp.List = seaPatrolList
  446. seaResp.ModuleInfo = moduleInfo
  447. seaResp.ExtraInfo = make(map[string]interface{})
  448. return nil
  449. })
  450. }
  451. if moduleType == _activityType {
  452. cardList := module["list"].([]interface{})
  453. actyInfo := cardList[0].(map[string]interface{})
  454. bookStatus := jsonMustInt(actyInfo["status"], 0)
  455. // status=0(非预约类型的活动)
  456. if bookStatus == _activityGo {
  457. actyInfo["button_text"] = "去围观"
  458. actyInfo["status"] = _activityGo
  459. continue
  460. }
  461. // 未登入 显示预约
  462. if !isUIDSet {
  463. actyInfo["button_text"] = "预约"
  464. actyInfo["status"] = _activityBook
  465. continue
  466. }
  467. // 登入状态 设置保底数据
  468. actyInfo["button_text"] = "去围观"
  469. actyInfo["status"] = _activityGo
  470. // 获取活动id
  471. materialID := jsonMustInt(moduleInfo["material_id"], 0)
  472. if materialID == 0 {
  473. continue
  474. }
  475. log.Info("[GetAllList]materialID is %v", materialID)
  476. wg.Go(func() error {
  477. activityQueryTimeout := time.Duration(conf.GetTimeout("activityQuery", 100)) * time.Millisecond
  478. bookInfo, userExtError := dao.UserExtApi.V1Remind.Query(rpcCtx.WithTimeout(ctx, activityQueryTimeout), &userextV1.RemindQueryReq{Aid: materialID})
  479. if userExtError != nil {
  480. log.Error("[GetAllList]get activity book info rpc error, userext.v1.Remind.Query, error:%+v", userExtError)
  481. return nil
  482. }
  483. if bookInfo.Code != 0 {
  484. log.Error("[GetAllList]get activity book info response error, code, %d, msg: %s, error:%+v", bookInfo.Code, bookInfo.Msg, userExtError)
  485. return nil
  486. }
  487. log.Info("[GetAllList]materialID is %v and bookInfo.Data.Status is %v", materialID, bookInfo.Data.Status)
  488. switch bookInfo.Data.Status {
  489. case _activityBook:
  490. actyInfo["button_text"] = "已预约"
  491. actyInfo["status"] = _activityUnbook
  492. case _activityUnbook:
  493. actyInfo["button_text"] = "预约"
  494. actyInfo["status"] = _activityBook
  495. default:
  496. actyInfo["button_text"] = "去围观"
  497. actyInfo["status"] = _activityGo
  498. }
  499. return nil
  500. })
  501. }
  502. // 其他playurl,注意这里取的推荐是未登录下的推荐play_url
  503. if moduleType == _yunyingRecFormType || moduleType == _yunyingRecSquareType ||
  504. moduleType == _parentAreaFormType || moduleType == _parentAreaSquareType ||
  505. moduleType == _recFormType || moduleType == _recSquareType {
  506. // append Roomid
  507. for _, item := range moduleList {
  508. itemV := item.(map[string]interface{})
  509. roomID := jsonMustInt(itemV["roomid"], 0)
  510. if roomID == 0 {
  511. continue
  512. }
  513. otherRoomIDSlice = append(otherRoomIDSlice, roomID)
  514. }
  515. }
  516. }
  517. // +其他模块playurl
  518. wg.Go(func() error {
  519. otherRoomListPlayURLMap = dao.BvcApi.GetPlayUrlMulti(ctx, otherRoomIDSlice, 0, 4, build, req.Platform)
  520. return nil
  521. })
  522. waitErr := wg.Wait()
  523. if waitErr != nil {
  524. log.Error("[GetAllList]wait error: %s", waitErr)
  525. return
  526. }
  527. // 封装
  528. tagIndex := 0
  529. for index, m := range rawModuleList {
  530. module := m.(map[string]interface{})
  531. moduleInfo := module["module_info"].(map[string]interface{})
  532. moduleList := module["list"].([]interface{})
  533. moduleType := jsonMustInt(moduleInfo["type"], 0)
  534. if moduleType == 0 {
  535. continue
  536. }
  537. // 初始化
  538. resp.ModuleList[index] = make(map[string]interface{})
  539. resp.ModuleList[index]["list"] = moduleList
  540. resp.ModuleList[index]["module_info"] = moduleInfo
  541. if moduleType == _bannerType {
  542. resp.ModuleList[index]["list"] = bannerResp.List
  543. }
  544. if moduleType == _navigatorType && req.Platform == "android" && build <= 5333002 {
  545. // 分区入口5.33版本还返回4个(前3个+全部),5.34透传后台的5个
  546. if len(moduleList) > 3 {
  547. resp.ModuleList[index]["list"] = append(moduleList[:3], map[string]interface{}{
  548. "id": 12,
  549. "pic": "https://i0.hdslb.com/bfs/vc/ff03528785fc8c91491d79e440398484811d6d87.png",
  550. "link": "https://live.bilibili.com/app/mytag/",
  551. "title": "全部标签",
  552. })
  553. }
  554. }
  555. if moduleType == _seaPatrolType {
  556. if seaResp.List != nil {
  557. resp.ModuleList[index]["list"] = seaResp.List
  558. }
  559. if seaResp.ModuleInfo != nil {
  560. resp.ModuleList[index]["module_info"] = seaResp.ModuleInfo
  561. }
  562. if seaResp.ExtraInfo != nil {
  563. resp.ModuleList[index]["extra_info"] = seaResp.ExtraInfo
  564. }
  565. }
  566. if moduleType == _myAreaTagType {
  567. if myTagResp.List != nil {
  568. resp.ModuleList[index]["list"] = myTagResp.List
  569. }
  570. if myTagResp.ModuleInfo != nil {
  571. resp.ModuleList[index]["module_info"] = myTagResp.ModuleInfo
  572. }
  573. if myTagResp.ExtraInfo != nil {
  574. resp.ModuleList[index]["extra_info"] = myTagResp.ExtraInfo
  575. }
  576. }
  577. var isTagGray int
  578. iTmp, _ := myTagResp.ExtraInfo["is_gray"].(int64)
  579. isTagGray = int(iTmp)
  580. // 常用分区房间列表填充
  581. if moduleType == _myAreaTagListType {
  582. if isTagGray == 0 {
  583. continue
  584. }
  585. if len(myTagAreaIds) == 0 || tagIndex >= len(myTagAreaIds) {
  586. continue
  587. }
  588. mTagAreaID := myTagAreaIds[tagIndex]
  589. if _, ok := myTagRoomListMap[mTagAreaID]; ok {
  590. for _, v := range myTagRoomListMap[mTagAreaID] {
  591. if myTagRoomListPlayURLMap[v.Roomid] != nil {
  592. v.AcceptQuality = myTagRoomListPlayURLMap[v.Roomid].AcceptQuality
  593. v.CurrentQuality = myTagRoomListPlayURLMap[v.Roomid].CurrentQuality
  594. v.PlayUrl = myTagRoomListPlayURLMap[v.Roomid].Url["h264"]
  595. v.PlayUrlH265 = myTagRoomListPlayURLMap[v.Roomid].Url["h265"]
  596. }
  597. }
  598. resp.ModuleList[index]["list"] = myTagRoomListMap[mTagAreaID]
  599. if _, ok := myTagAreaInfoMap[mTagAreaID]; ok {
  600. areaInfo := myTagAreaInfoMap[mTagAreaID]
  601. moduleInfo["title"] = areaInfo.Name
  602. moduleInfo["link"] = fmt.Sprintf(_areaModuleLink, areaInfo.ParentId, parentName[areaInfo.ParentId], areaInfo.Id, areaInfo.Name)
  603. resp.ModuleList[index]["module_info"] = moduleInfo
  604. }
  605. }
  606. tagIndex++
  607. }
  608. // 运营推荐分区对常用分区去重
  609. if moduleType == _yunyingRecFormType || moduleType == _yunyingRecSquareType {
  610. link := moduleInfo["link"]
  611. u, err := url.Parse(link.(string))
  612. if err != nil {
  613. log.Warn("[GetAllList]url.Parse (%s) error: %v", link, err)
  614. continue
  615. }
  616. m, err := url.ParseQuery(u.RawQuery)
  617. if err != nil {
  618. log.Warn("[GetAllList]url.ParseQuery (%s) error: %v", link, err)
  619. continue
  620. }
  621. area, ok := m["area_id"]
  622. if !ok {
  623. log.Warn("[GetAllList]url ((%s) area_id lost: %v", link, ok)
  624. continue
  625. }
  626. trueArea, err := strconv.Atoi(area[0])
  627. if err != nil {
  628. log.Warn("[GetAllList]get trueAreaId error: %v", link, ok)
  629. continue
  630. }
  631. if _, ok := myTagRoomListMap[int64(trueArea)]; ok && isTagGray == 1 {
  632. resp.ModuleList[index]["list"] = nil
  633. } else {
  634. for _, v := range moduleList {
  635. if v == nil {
  636. continue
  637. }
  638. vv := v.(map[string]interface{})
  639. roomID := jsonMustInt(vv["roomid"], 0)
  640. if roomID == 0 {
  641. continue
  642. }
  643. if otherRoomListPlayURLMap[roomID] != nil {
  644. vv["accept_quality"] = otherRoomListPlayURLMap[roomID].AcceptQuality
  645. vv["current_quality"] = otherRoomListPlayURLMap[roomID].CurrentQuality
  646. vv["play_url"] = otherRoomListPlayURLMap[roomID].Url["h264"]
  647. vv["play_url_h265"] = otherRoomListPlayURLMap[roomID].Url["h265"]
  648. }
  649. }
  650. resp.ModuleList[index]["list"] = moduleList
  651. resp.ModuleList[index]["module_info"] = moduleInfo
  652. }
  653. }
  654. if moduleType == _feedType {
  655. if attentionResp.ModuleInfo != nil {
  656. resp.ModuleList[index]["module_info"] = attentionResp.ModuleInfo
  657. }
  658. if attentionResp.ExtraInfo != nil {
  659. resp.ModuleList[index]["extra_info"] = attentionResp.ExtraInfo
  660. }
  661. if attentionResp.List != nil {
  662. for _, v := range attentionResp.List.([]interface{}) {
  663. vv := v.(*relationV2.AppLiveHomePageResp_Rooms)
  664. if attentionRoomListPlayURLMap[vv.Roomid] != nil {
  665. vv.AcceptQuality = attentionRoomListPlayURLMap[vv.Roomid].AcceptQuality
  666. vv.CurrentQuality = attentionRoomListPlayURLMap[vv.Roomid].CurrentQuality
  667. vv.PlayUrl = attentionRoomListPlayURLMap[vv.Roomid].Url["h264"]
  668. vv.PlayUrlH265 = attentionRoomListPlayURLMap[vv.Roomid].Url["h265"]
  669. }
  670. }
  671. resp.ModuleList[index]["list"] = attentionResp.List
  672. }
  673. }
  674. if moduleType == _recFormType || moduleType == _recSquareType {
  675. moduleInfo["is_sky_horse_gray"] = isSkyHorseGrayOk
  676. resp.ModuleList[index]["module_info"] = moduleInfo
  677. if loginRecResp.List != nil {
  678. // is uid set
  679. for _, v := range loginRecResp.List.([]map[string]interface{}) {
  680. roomID := int64(0)
  681. r, ok := v["roomid"].(json.Number)
  682. if ok {
  683. rr, intErr := r.Int64()
  684. if intErr != nil {
  685. continue
  686. }
  687. roomID = rr
  688. } else {
  689. roomID = v["roomid"].(int64)
  690. }
  691. if roomID == 0 {
  692. continue
  693. }
  694. if loginRecRoomListPlayURLMap[roomID] != nil {
  695. v["accept_quality"] = loginRecRoomListPlayURLMap[roomID].AcceptQuality
  696. v["current_quality"] = loginRecRoomListPlayURLMap[roomID].CurrentQuality
  697. v["play_url"] = loginRecRoomListPlayURLMap[roomID].Url["h264"]
  698. v["play_url_h265"] = loginRecRoomListPlayURLMap[roomID].Url["h265"]
  699. }
  700. }
  701. resp.ModuleList[index]["list"] = loginRecResp.List
  702. } else {
  703. for _, v := range moduleList {
  704. if v == nil {
  705. continue
  706. }
  707. vv := v.(map[string]interface{})
  708. roomID := jsonMustInt(vv["roomid"], 0)
  709. if roomID == 0 {
  710. continue
  711. }
  712. if otherRoomListPlayURLMap[roomID] != nil {
  713. vv["accept_quality"] = otherRoomListPlayURLMap[roomID].AcceptQuality
  714. vv["current_quality"] = otherRoomListPlayURLMap[roomID].CurrentQuality
  715. vv["play_url"] = otherRoomListPlayURLMap[roomID].Url["h264"]
  716. vv["play_url_h265"] = otherRoomListPlayURLMap[roomID].Url["h265"]
  717. }
  718. }
  719. // 只返24个,新推荐已在上面做处理
  720. if len(moduleList) > _defaultRecNum {
  721. resp.ModuleList[index]["list"] = moduleList[:_defaultRecNum]
  722. } else {
  723. resp.ModuleList[index]["list"] = moduleList
  724. }
  725. }
  726. }
  727. if moduleType == _parentAreaFormType || moduleType == _parentAreaSquareType {
  728. for _, v := range moduleList {
  729. if v == nil {
  730. continue
  731. }
  732. vv := v.(map[string]interface{})
  733. roomID := jsonMustInt(vv["roomid"], 0)
  734. if roomID == 0 {
  735. continue
  736. }
  737. if otherRoomListPlayURLMap[roomID] != nil {
  738. vv["accept_quality"] = otherRoomListPlayURLMap[roomID].AcceptQuality
  739. vv["current_quality"] = otherRoomListPlayURLMap[roomID].CurrentQuality
  740. vv["play_url"] = otherRoomListPlayURLMap[roomID].Url["h264"]
  741. vv["play_url_h265"] = otherRoomListPlayURLMap[roomID].Url["h265"]
  742. }
  743. }
  744. resp.ModuleList[index]["list"] = moduleList
  745. }
  746. }
  747. ret = resp
  748. return
  749. }
  750. func jsonMustInt(arg interface{}, def int64) int64 {
  751. if arg == nil {
  752. log.Warn("jsonMustInt arg(%v) nil!", arg)
  753. return def
  754. }
  755. r, ok := arg.(json.Number)
  756. if !ok {
  757. log.Warn("jsonMustInt arg(%v) is not json.Number but %v", arg, reflect.TypeOf(arg))
  758. return def
  759. }
  760. rr, err := r.Int64()
  761. if err != nil {
  762. log.Warn("jsonMustInt arg(%v) transfer error: %v", arg, err)
  763. return def
  764. }
  765. return rr
  766. }
  767. // Change implementation
  768. // 首页换一换接口 for 天马
  769. func (s *Service) Change(ctx context.Context, req *v1indexpb.ChangeReq) (resp *v1indexpb.ChangeResp, err error) {
  770. resp = &v1indexpb.ChangeResp{
  771. ModuleList: make([]*v1indexpb.ChangeResp_ModuleList, 0),
  772. }
  773. mid, isUIDSet := metadata.Value(ctx, metadata.Mid).(int64)
  774. var uid int64
  775. if isUIDSet {
  776. uid = mid
  777. }
  778. // deviceInterface, _ := ctx.Get("device")
  779. // device := req.Device
  780. duplicates, _ := xstr.SplitInts(req.AttentionRoomId)
  781. duplicatesMap := make(map[int64]bool)
  782. for _, roomID := range duplicates {
  783. duplicatesMap[roomID] = true
  784. }
  785. build := req.Build
  786. buvid := ""
  787. // 主站封好的,可从device里获取到sid、buvid、buvid3、build、channel、device、mobi_app、platform
  788. device, ok := metadata.Value(ctx, metadata.Device).(*blademaster.Device)
  789. if ok {
  790. buvid = device.Buvid
  791. }
  792. var recModuleInfo *v1indexpb.ChangeResp_ModuleInfo
  793. allListOut, callErr := dao.RoomApi.V2AppIndex.GetAllList(rpcCtx.WithTimeout(ctx, 100*time.Millisecond), &roomV2.AppIndexGetAllListReq{
  794. Platform: req.Platform,
  795. Device: req.Device,
  796. Scale: req.Scale,
  797. Build: int64(build),
  798. ModuleId: req.ModuleId,
  799. })
  800. if callErr != nil {
  801. log.Error("[Change]get all list rpc error, room.v2.AppIndex.setAllList, error:%+v", callErr)
  802. err = errors.WithMessage(ecode.ChangeGetAllListRPCError, "CHANGE GET ALL LIST FAIL#1")
  803. return
  804. }
  805. if allListOut.Code != 0 || allListOut.Data == nil {
  806. log.Error("[Change]get all list return data error, code, %d, msg: %s, error:%+v", allListOut.Code, allListOut.Msg, err)
  807. err = errors.WithMessage(ecode.ChangeGetAllListReturnError, "CHANGE GET ALL LIST FAIL#2")
  808. return
  809. }
  810. if len(allListOut.Data.ModuleList) == 0 {
  811. log.Error("[Change]get all list return empty, code, %d, msg: %s, error:%+v", allListOut.Code, allListOut.Msg, err)
  812. err = errors.WithMessage(ecode.ChangeGetAllListEmptyError, "CHANGE GET ALL LIST FAIL#3")
  813. return
  814. }
  815. m := allListOut.Data.ModuleList[0]
  816. duplicateList := make([]*roomV2.AppIndexGetAllListResp_RoomList, 0)
  817. for _, itemInfo := range m.List {
  818. if _, ok := duplicatesMap[itemInfo.Roomid]; !ok {
  819. duplicateList = append(duplicateList, itemInfo)
  820. }
  821. }
  822. m.List = duplicateList
  823. recModuleInfo = &v1indexpb.ChangeResp_ModuleInfo{
  824. Id: m.ModuleInfo.Id,
  825. Title: m.ModuleInfo.Title,
  826. Pic: m.ModuleInfo.Pic,
  827. Type: m.ModuleInfo.Type,
  828. Link: m.ModuleInfo.Link,
  829. Count: m.ModuleInfo.Count,
  830. IsSkyHorseGray: 0,
  831. }
  832. // 目前只有推荐-天马有换一换,都必须有登陆态
  833. roomIds := make([]int64, 0)
  834. list := make([]*v1indexpb.ChangeResp_List, 0)
  835. for i, itemInfo := range m.List {
  836. if i >= 24 {
  837. break
  838. }
  839. roomIds = append(roomIds, itemInfo.Roomid)
  840. list = append(list, &v1indexpb.ChangeResp_List{
  841. Roomid: itemInfo.Roomid,
  842. Title: itemInfo.Title,
  843. Uname: itemInfo.Uname,
  844. Online: itemInfo.Online,
  845. Cover: itemInfo.Cover,
  846. Link: "/" + strconv.Itoa(int(itemInfo.Roomid)),
  847. Face: itemInfo.Face,
  848. AreaV2ParentId: itemInfo.AreaV2ParentId,
  849. AreaV2ParentName: itemInfo.AreaV2ParentName,
  850. AreaV2Id: itemInfo.AreaV2Id,
  851. AreaV2Name: itemInfo.AreaV2Name,
  852. BroadcastType: itemInfo.BroadcastType,
  853. PendentRu: itemInfo.PendentRu,
  854. PendentRuPic: itemInfo.PendentRuPic,
  855. PendentRuColor: itemInfo.PendentRuColor,
  856. RecType: itemInfo.RecType,
  857. CurrentQuality: itemInfo.CurrentQuality,
  858. AcceptQuality: itemInfo.AcceptQuality,
  859. PlayUrl: itemInfo.PlayUrl,
  860. })
  861. }
  862. resp.ModuleList = append(resp.ModuleList, &v1indexpb.ChangeResp_ModuleList{
  863. List: list,
  864. ModuleInfo: recModuleInfo,
  865. })
  866. isOpen := conf.Conf.SkyHorseStatus
  867. if isOpen && isUIDSet && s.isSkyHorseRec(uid) {
  868. recPage := rand.Intn(4)
  869. if recPage == 1 || recPage == 0 {
  870. recPage = 2
  871. }
  872. recList, skyHorseErr := getSkyHorseRoomList(ctx, uid, buvid, req.Build, req.Platform, duplicates, int64(recPage))
  873. if skyHorseErr != nil {
  874. log.Error("[Change]getSkyHorseRoomList error:%+v", skyHorseErr)
  875. // err = errors.WithMessage(ecode.SkyHorseError, "")
  876. } else if len(recList) <= 0 {
  877. log.Error("[Change]getSkyHorseRoomList empty:%+v", recList)
  878. // err = errors.WithMessage(ecode.ChangeSkyHorseEmptyError, "")
  879. } else {
  880. list := make([]*v1indexpb.ChangeResp_List, 0)
  881. for i, recInfo := range recList {
  882. if i >= 6 {
  883. continue
  884. }
  885. roomIds = append(roomIds, recInfo.RoomId)
  886. list = append(list, &v1indexpb.ChangeResp_List{
  887. Roomid: recInfo.RoomId,
  888. Title: recInfo.Title,
  889. Uname: recInfo.Uname,
  890. Online: recInfo.Online,
  891. Cover: recInfo.Cover,
  892. Link: "/" + strconv.Itoa(int(recInfo.RoomId)),
  893. Face: recInfo.Face,
  894. AreaV2ParentId: recInfo.AreaV2ParentId,
  895. AreaV2ParentName: recInfo.AreaV2ParentName,
  896. AreaV2Id: recInfo.AreaV2Id,
  897. AreaV2Name: recInfo.AreaV2Name,
  898. BroadcastType: recInfo.BroadcastType,
  899. PendentRu: recInfo.PendentRu,
  900. PendentRuPic: recInfo.PendentRuPic,
  901. PendentRuColor: recInfo.PendentRuColor,
  902. RecType: _recTypeSkyHorse,
  903. })
  904. }
  905. skyHorseList := make([]*v1indexpb.ChangeResp_ModuleList, 0)
  906. recModuleInfo.IsSkyHorseGray = 1
  907. skyHorseList = append(skyHorseList, &v1indexpb.ChangeResp_ModuleList{
  908. List: list,
  909. ModuleInfo: recModuleInfo,
  910. })
  911. resp.ModuleList = skyHorseList
  912. }
  913. }
  914. changeRoomListPlayURLMap := dao.BvcApi.GetPlayUrlMulti(ctx, roomIds, 0, 4, build, req.Platform)
  915. for _, v := range resp.ModuleList[0].List {
  916. if changeRoomListPlayURLMap[v.Roomid] != nil {
  917. v.AcceptQuality = changeRoomListPlayURLMap[v.Roomid].AcceptQuality
  918. v.CurrentQuality = changeRoomListPlayURLMap[v.Roomid].CurrentQuality
  919. v.PlayUrl = changeRoomListPlayURLMap[v.Roomid].Url["h264"]
  920. v.PlayUrlH265 = changeRoomListPlayURLMap[v.Roomid].Url["h265"]
  921. }
  922. }
  923. // 赋值
  924. return
  925. }
  926. // 获取天马房间信息列表
  927. // 已将强推roomids传给天马,其他的可通过传duplicates来merge进去
  928. func getSkyHorseRoomList(ctx context.Context, uid int64, buvid string, build int64, platform string, duplicates []int64, recPage int64) (resp []*roomItem, err error) {
  929. clientRecStrongTimeout := time.Duration(conf.GetTimeout("clientRecStrong", 100)) * time.Millisecond
  930. strongRecList, strongRecErr := dao.RoomApi.V1RoomRecommend.ClientRecStrong(rpcCtx.WithTimeout(ctx, clientRecStrongTimeout), &roomV1.RoomRecommendClientRecStrongReq{RecPage: recPage})
  931. // liverpc.NewClient().CallRaw()
  932. recDuplicate := make([]int64, 0)
  933. if strongRecErr != nil {
  934. log.Error("[getSkyHorseRoomList]room.v1.ClientRecStrong rpc error:%+v", strongRecErr)
  935. } else if strongRecList.Code != 0 {
  936. log.Error("[getSkyHorseRoomList]room.v1.ClientRecStrong response error:%+v,code:%d,msg:%s", strongRecErr, strongRecList.Code, strongRecList.Msg)
  937. } else {
  938. for _, strongInfo := range strongRecList.Data.Result {
  939. if strongInfo.Roomid == 0 {
  940. continue
  941. }
  942. recDuplicate = append(recDuplicate, strongInfo.Roomid)
  943. }
  944. }
  945. strongLen := len(recDuplicate)
  946. duplicates = append(duplicates, recDuplicate...)
  947. skyHorseRec, skyHorseErr := dao.SkyHorseApi.GetSkyHorseRec(ctx, uid, buvid, build, platform, duplicates, strongLen, _skyHorseRecTimeOut)
  948. if skyHorseErr != nil {
  949. err = errors.WithMessage(ecode.SkyHorseError, "")
  950. return
  951. }
  952. roomIds := make([]int64, 0)
  953. for _, skyHorseInfo := range skyHorseRec.Data {
  954. roomIds = append(roomIds, int64(skyHorseInfo.Id))
  955. }
  956. indexRoomListFields := []string{
  957. "roomid",
  958. "title",
  959. "uname",
  960. "online",
  961. "cover",
  962. "user_cover",
  963. "link",
  964. "face",
  965. "area_v2_parent_id",
  966. "area_v2_parent_name",
  967. "area_v2_id",
  968. "area_v2_name",
  969. "broadcast_type",
  970. "uid",
  971. }
  972. wg, _ := errgroup.WithContext(ctx)
  973. // 房间基础信息(是map,但是天马返回是无序的)
  974. var multiRoomListResp *roomV2.RoomGetByIdsResp
  975. wg.Go(func() error {
  976. getByIdsTimeout := time.Duration(conf.GetTimeout("getByIds", 50)) * time.Millisecond
  977. multiRoomList, getByIdsError := dao.RoomApi.V2Room.GetByIds(rpcCtx.WithTimeout(ctx, getByIdsTimeout), &roomV2.RoomGetByIdsReq{
  978. Ids: roomIds,
  979. NeedBroadcastType: 1,
  980. NeedUinfo: 1,
  981. Fields: indexRoomListFields,
  982. From: "app-interface.gateway",
  983. })
  984. if getByIdsError != nil {
  985. log.Error("[getSkyHorseRoomList]room.v2.getByIds rpc error:%+v", getByIdsError)
  986. // 这个是推荐房间列表的基础信息,如果失败需要cancel,不然返回值会很奇怪
  987. return errors.WithMessage(ecode.GetRoomError, "room.v2.getByIds rpc error")
  988. }
  989. if multiRoomList.Code != 0 {
  990. log.Error("[getSkyHorseRoomList]room.v2.getByIds response error:%+v,code:%d,msg:%s", getByIdsError, multiRoomList.Code, multiRoomList.Msg)
  991. // 这个是推荐房间列表的基础信息,如果失败需要cancel,不然返回值会很奇怪
  992. return errors.WithMessage(ecode.GetRoomError, "room.v2.getByIds response error")
  993. }
  994. multiRoomListResp = multiRoomList
  995. return nil
  996. })
  997. // 房间角标信息
  998. pendantRoomListResp := &roomV1.RoomPendantGetPendantByIdsResp{}
  999. wg.Go(func() error {
  1000. getPendantByIdsTimeout := time.Duration(conf.GetTimeout("getPendantByIds", 50)) * time.Millisecond
  1001. pendantRoomList, getPendantError := dao.RoomApi.V1RoomPendant.GetPendantByIds(rpcCtx.WithTimeout(ctx, getPendantByIdsTimeout), &roomV1.RoomPendantGetPendantByIdsReq{
  1002. Ids: roomIds,
  1003. Type: "mobile_index_badge",
  1004. Position: 2, // 历史原因,取右上,但客户端展示在左上
  1005. })
  1006. if getPendantError != nil {
  1007. log.Error("[getSkyHorseRoomList]room.v1.getPendantByIds rpc error:%+v", getPendantError)
  1008. return nil
  1009. }
  1010. if pendantRoomList.Code != 0 {
  1011. log.Error("[getSkyHorseRoomList]room.v1.getPendantByIds response error:%+v,code:%d,msg:%s", getPendantError, pendantRoomList.Code, pendantRoomList.Msg)
  1012. return nil
  1013. }
  1014. pendantRoomListResp = pendantRoomList
  1015. return nil
  1016. })
  1017. waitErr := wg.Wait()
  1018. if waitErr != nil {
  1019. log.Error("[getSkyHorseRoomList]wait error(%+v)", waitErr)
  1020. return
  1021. }
  1022. pendantResult := make(map[int64]*roomV1.RoomPendantGetPendantByIdsResp_Result)
  1023. // 天马返回是无序的
  1024. if multiRoomListResp == nil {
  1025. err = errors.WithMessage(ecode.GetRoomEmptyError, "")
  1026. return
  1027. }
  1028. respSlice := make([]*roomV2.RoomGetByIdsResp_RoomInfo, 0)
  1029. for _, roomBaseInfo := range multiRoomListResp.Data {
  1030. respSlice = append(respSlice, roomBaseInfo)
  1031. }
  1032. for i := 0; i < 6; i++ {
  1033. if strongRecList != nil && strongRecList.Data != nil && strongRecList.Data.Result != nil {
  1034. if recInfo, ok := strongRecList.Data.Result[int64(i)]; ok {
  1035. resp = append(resp, &roomItem{
  1036. RoomId: recInfo.Roomid,
  1037. Title: recInfo.Title,
  1038. Uname: recInfo.Uname,
  1039. Online: recInfo.Online,
  1040. Cover: recInfo.Cover,
  1041. Link: "/" + strconv.Itoa(int(recInfo.Roomid)),
  1042. Face: recInfo.Face,
  1043. AreaV2ParentId: recInfo.AreaV2ParentId,
  1044. AreaV2ParentName: recInfo.AreaV2ParentName,
  1045. AreaV2Id: recInfo.AreaV2Id,
  1046. AreaV2Name: recInfo.AreaV2Name,
  1047. BroadcastType: recInfo.BroadcastType,
  1048. PendentRu: recInfo.PendentRu,
  1049. PendentRuPic: recInfo.PendentRuPic,
  1050. PendentRuColor: recInfo.PendentRuColor,
  1051. RecType: _recTypeForce,
  1052. CurrentQuality: recInfo.CurrentQuality,
  1053. AcceptQuality: recInfo.AcceptQuality,
  1054. })
  1055. continue
  1056. }
  1057. }
  1058. if len(respSlice) <= 0 {
  1059. continue
  1060. }
  1061. tmpItem := respSlice[0:1][0]
  1062. respSlice = respSlice[1:]
  1063. pendantValue := ""
  1064. pendantBgPic := ""
  1065. pendantBgColor := ""
  1066. if pendantRoomListResp != nil && pendantRoomListResp.Data != nil {
  1067. pendantResult = pendantRoomListResp.Data.Result
  1068. if pendantResult[tmpItem.Roomid] != nil {
  1069. // 移动端取value, web取name
  1070. pendantValue = pendantResult[tmpItem.Roomid].Value
  1071. pendantBgPic = pendantResult[tmpItem.Roomid].BgPic
  1072. if pendantResult[tmpItem.Roomid].BgColor != "" {
  1073. pendantBgColor = pendantResult[tmpItem.Roomid].BgColor
  1074. } else {
  1075. pendantBgColor = _mobileIndexBadgeColorDefault
  1076. }
  1077. }
  1078. }
  1079. cover := ""
  1080. if tmpItem.UserCover != "" {
  1081. cover = tmpItem.UserCover
  1082. } else {
  1083. cover = tmpItem.Cover
  1084. }
  1085. resp = append(resp, &roomItem{
  1086. RoomId: tmpItem.Roomid,
  1087. Title: tmpItem.Title,
  1088. Uname: tmpItem.Uname,
  1089. Online: tmpItem.Online,
  1090. Cover: cover,
  1091. Link: "/" + strconv.Itoa(int(tmpItem.Roomid)),
  1092. Face: tmpItem.Face,
  1093. AreaV2ParentId: tmpItem.AreaV2ParentId,
  1094. AreaV2ParentName: tmpItem.AreaV2ParentName,
  1095. AreaV2Id: tmpItem.AreaV2Id,
  1096. AreaV2Name: tmpItem.AreaV2Name,
  1097. BroadcastType: tmpItem.BroadcastType,
  1098. PendentRu: pendantValue,
  1099. PendentRuPic: pendantBgPic,
  1100. PendentRuColor: pendantBgColor,
  1101. RecType: _recTypeSkyHorse,
  1102. })
  1103. }
  1104. return
  1105. }
  1106. func (s *Service) isSkyHorseRec(mid int64) bool {
  1107. lastMid := strconv.Itoa(int(mid % 100))
  1108. if len(lastMid) < 2 {
  1109. lastMid = "0" + lastMid
  1110. }
  1111. _, isSkyHorseGray := s.conf.SkyHorseGray[lastMid]
  1112. return isSkyHorseGray
  1113. }
  1114. func (s *Service) getAllList(ctx context.Context) (json.RawMessage, error) {
  1115. allListOut, err := dao.RoomRawApi.CallRaw(ctx, 2,
  1116. "AppIndex.getAllRawList", &liverpc.Args{})
  1117. if err != nil {
  1118. log.Error("[getAllList]get all list rpc error, room.v2.AppIndex.getAllRawList, error:%+v", err)
  1119. err = errors.WithMessage(ecode.GetAllListRPCError, "GET ALL LIST FAIL#1")
  1120. return json.RawMessage{}, err
  1121. }
  1122. if allListOut == nil {
  1123. log.Error("[getAllList]get all list raw data nil, room.v2.AppIndex.getAllRawList")
  1124. err = errors.WithMessage(ecode.GetAllListRPCError, "GET ALL LIST FAIL#2")
  1125. return json.RawMessage{}, err
  1126. }
  1127. if allListOut.Code != 0 || allListOut.Data == nil {
  1128. log.Error("[getAllList]get all list return data error, code, %d, msg: %s, error:%+v", allListOut.Code, allListOut.Message, err)
  1129. err = errors.WithMessage(ecode.GetAllListReturnError, "GET ALL LIST FAIL#3")
  1130. return json.RawMessage{}, err
  1131. }
  1132. allListJSONObj, jsonErr := simplejson.NewJson(allListOut.Data)
  1133. if jsonErr != nil {
  1134. log.Error("[getAllList]get all list simplejson error, error:%+v", err)
  1135. err = errors.WithMessage(ecode.GetAllListReturnError, "GET ALL LIST FAIL#4")
  1136. return json.RawMessage{}, err
  1137. }
  1138. allListCache := allListJSONObj.Get("module_list").MustArray()
  1139. if len(allListCache) > 1 && allListCache[1] == nil {
  1140. log.Error("[getAllList]abnormal module, allListCache:%+v", allListCache)
  1141. err = errors.WithMessage(ecode.GetAllListReturnError, "GET ALL LIST FAIL#5")
  1142. return json.RawMessage{}, err
  1143. }
  1144. return allListOut.Data, nil
  1145. }
  1146. func (s *Service) tickCacheAllList(ctx context.Context) {
  1147. ticker := time.NewTicker(5 * time.Second)
  1148. for {
  1149. select {
  1150. case <-ticker.C:
  1151. allListData, err := s.getAllList(ctx)
  1152. if err != nil {
  1153. log.Error("[tickCacheAllList] setAllList error(%+v)", err)
  1154. continue
  1155. }
  1156. if len(allListData) <= 0 {
  1157. log.Error("[tickCacheAllList] setAllList empty data(%+v)", allListData)
  1158. continue
  1159. }
  1160. log.Info("[tickCacheAllList] setAllList success!")
  1161. s.allListInfo.Store(allListData)
  1162. }
  1163. }
  1164. }
  1165. func (s *Service) getAllListFromCache(ctx context.Context) (allListCache []interface{}) {
  1166. allListRawCache := s.allListInfo.Load()
  1167. if allListRawCache == nil {
  1168. log.Warn("[getAllListFromCache] cache miss!")
  1169. allList, err := s.getAllList(ctx)
  1170. if err != nil {
  1171. log.Error("[getAllListFromCache] pass through error(%+v)", err)
  1172. }
  1173. allListRawCache = allList
  1174. }
  1175. allListJSONObj, err := simplejson.NewJson(allListRawCache.(json.RawMessage))
  1176. if err != nil {
  1177. log.Error("[getAllListFromCache]get all list simplejson error, error:%+v", err)
  1178. return
  1179. }
  1180. allListCache = allListJSONObj.Get("module_list").MustArray()
  1181. if allListCache[1] == nil {
  1182. fmt.Printf("abnormal module, allListRawCache: %+v, module_list: %+v", allListRawCache, allListJSONObj.Get("module_list"))
  1183. }
  1184. log.Info("[getAllListFromCache] cache hit! len: %d", len(allListCache))
  1185. return
  1186. }