index.go 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. package feed
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "time"
  7. cdm "go-common/app/interface/main/app-card/model"
  8. "go-common/app/interface/main/app-card/model/card/ai"
  9. "go-common/app/interface/main/app-card/model/card/audio"
  10. "go-common/app/interface/main/app-card/model/card/bangumi"
  11. "go-common/app/interface/main/app-card/model/card/banner"
  12. "go-common/app/interface/main/app-card/model/card/cm"
  13. "go-common/app/interface/main/app-card/model/card/live"
  14. "go-common/app/interface/main/app-card/model/card/operate"
  15. "go-common/app/interface/main/app-card/model/card/rank"
  16. "go-common/app/interface/main/app-card/model/card/show"
  17. "go-common/app/interface/main/app-feed/model"
  18. "go-common/app/interface/main/app-feed/model/feed"
  19. bustag "go-common/app/interface/main/tag/model"
  20. article "go-common/app/interface/openplatform/article/model"
  21. account "go-common/app/service/main/account/model"
  22. "go-common/app/service/main/archive/model/archive"
  23. locmdl "go-common/app/service/main/location/model"
  24. relation "go-common/app/service/main/relation/model"
  25. episodegrpc "go-common/app/service/openplatform/pgc-season/api/grpc/episode/v1"
  26. "go-common/library/ecode"
  27. "go-common/library/log"
  28. "go-common/library/net/metadata"
  29. "go-common/library/sync/errgroup"
  30. )
  31. const (
  32. _cardAdAv = 1
  33. _cardAdWeb = 2
  34. _cardAdWebS = 3
  35. _cardAdLarge = 7
  36. _feedgroups = "tianma2.0_autoplay_card"
  37. )
  38. // Index is
  39. func (s *Service) Index(c context.Context, mid int64, plat int8, build int, buvid, network, mobiApp, device, platform, openEvent string, loginEvent int, idx int64, pull bool, now time.Time, bannerHash, adExtra string, qn int, interest string, style, flush, fnver, fnval, autoplayCard int) (is []*feed.Item, userFeature json.RawMessage, isRcmd, newUser bool, code, clean int, autoPlayInfoc string, err error) {
  40. var (
  41. ris []*ai.Item
  42. adm map[int]*cm.AdInfo
  43. adAidm map[int64]struct{}
  44. hasBanner bool
  45. bs []*banner.Banner
  46. version string
  47. blackAidm map[int64]struct{}
  48. adInfom map[int]*cm.AdInfo
  49. follow *operate.Follow
  50. autoPlay int
  51. ip = metadata.String(c, metadata.RemoteIP)
  52. info *locmdl.Info
  53. )
  54. //abtest================
  55. // if mid > 0 && mid%20 == 19 {
  56. // clean = 1
  57. // } else {
  58. // clean = 0
  59. // }
  60. clean = 0
  61. if ab, ok := s.abtestCache[_feedgroups]; ok {
  62. if ab.AbTestIn(buvid + _feedgroups) {
  63. switch autoplayCard {
  64. case 0, 1, 2, 3:
  65. autoPlay = 1
  66. default:
  67. autoPlay = 2
  68. }
  69. } else {
  70. autoPlay = 2
  71. }
  72. } else {
  73. switch autoplayCard {
  74. case 1, 3:
  75. autoPlay = 1
  76. default:
  77. autoPlay = 2
  78. }
  79. }
  80. autoPlayInfoc = fmt.Sprintf("%d|%d", autoPlay, autoplayCard)
  81. if info, err = s.loc.Info(c, ip); err != nil {
  82. log.Warn("s.loc.Info(%v) error(%v)", ip, err)
  83. err = nil
  84. }
  85. //abtest================
  86. group := s.group(mid, buvid)
  87. g, ctx := errgroup.WithContext(c)
  88. g.Go(func() error {
  89. ris, userFeature, isRcmd, newUser, code = s.indexRcmd(ctx, plat, build, buvid, mid, group, loginEvent, 0, info, interest, network, style, -1, flush, autoPlayInfoc, now)
  90. return nil
  91. })
  92. // 暂停实验
  93. // if !((group == 18 || group == 19) && style == 3) {
  94. g.Go(func() (err error) {
  95. if adm, adAidm, err = s.indexAd(ctx, plat, build, buvid, mid, network, mobiApp, device, openEvent, info, now, adExtra, style); err != nil {
  96. log.Error("%+v", err)
  97. err = nil
  98. }
  99. return
  100. })
  101. // }
  102. g.Go(func() (err error) {
  103. if hasBanner, bs, version, err = s.indexBanner(ctx, plat, build, buvid, mid, loginEvent, bannerHash, network, mobiApp, device, "", adExtra); err != nil {
  104. log.Error("%+v", err)
  105. err = nil
  106. }
  107. return
  108. })
  109. g.Go(func() (err error) {
  110. if blackAidm, err = s.BlackList(ctx, mid); err != nil {
  111. log.Error("%+v", err)
  112. err = nil
  113. }
  114. return
  115. })
  116. g.Go(func() (err error) {
  117. if follow, err = s.SearchFollow(ctx, platform, mobiApp, device, buvid, build, mid); err != nil {
  118. log.Error("%+v", err)
  119. err = nil
  120. }
  121. return
  122. })
  123. if err = g.Wait(); err != nil {
  124. return
  125. }
  126. ris, adInfom = s.mergeItem(c, mid, ris, adm, adAidm, hasBanner, blackAidm, plat, follow)
  127. is, isRcmd, err = s.dealItem(c, mid, plat, build, buvid, ris, bs, version, isRcmd, network, mobiApp, device, openEvent, idx, pull, qn, now, adExtra, adInfom, fnver, fnval, autoPlay, follow)
  128. return
  129. }
  130. // Dislike is.
  131. func (s *Service) Dislike(c context.Context, mid, id int64, buvid, gt string, reasonID, cmreasonID, feedbackID, upperID, rid, tagID int64, adcb string, now time.Time) (err error) {
  132. if gt == model.GotoAv {
  133. s.blk.AddBlacklist(mid, id)
  134. }
  135. return s.rcmd.PubDislike(c, buvid, gt, id, mid, reasonID, cmreasonID, feedbackID, upperID, rid, tagID, adcb, now)
  136. }
  137. // DislikeCancel is.
  138. func (s *Service) DislikeCancel(c context.Context, mid, id int64, buvid, gt string, reasonID, cmreasonID, feedbackID, upperID, rid, tagID int64, adcb string, now time.Time) (err error) {
  139. if gt == model.GotoAv {
  140. s.blk.DelBlacklist(mid, id)
  141. }
  142. return s.rcmd.PubDislikeCancel(c, buvid, gt, id, mid, reasonID, cmreasonID, feedbackID, upperID, rid, tagID, adcb, now)
  143. }
  144. func (s *Service) indexRcmd(c context.Context, plat int8, build int, buvid string, mid int64, group int, loginEvent, parentMode int, zone *locmdl.Info, interest, network string, style int, column cdm.ColumnStatus, flush int, autoPlay string, now time.Time) (is []*ai.Item, userFeature json.RawMessage, isRcmd, newUser bool, code int) {
  145. count := s.indexCount(plat)
  146. if buvid != "" || mid != 0 {
  147. var (
  148. err error
  149. zoneID int64
  150. )
  151. if zone != nil {
  152. zoneID = zone.ZoneID
  153. }
  154. if is, userFeature, code, newUser, err = s.rcmd.Recommend(c, plat, buvid, mid, build, loginEvent, parentMode, 0, zoneID, group, interest, network, style, column, flush, autoPlay, now); err != nil {
  155. log.Error("%+v", err)
  156. } else if len(is) != 0 {
  157. isRcmd = true
  158. }
  159. var fromCache bool
  160. if len(is) == 0 && mid != 0 && !ecode.ServiceUnavailable.Equal(err) {
  161. if is, err = s.indexCache(c, mid, count); err != nil {
  162. log.Error("%+v", err)
  163. }
  164. if len(is) != 0 {
  165. s.pHit.Incr("index_cache")
  166. } else {
  167. s.pMiss.Incr("index_cache")
  168. }
  169. fromCache = true
  170. }
  171. if len(is) == 0 || (fromCache && len(is) < count) {
  172. is = s.recommendCache(count)
  173. }
  174. } else {
  175. is = s.recommendCache(count)
  176. }
  177. return
  178. }
  179. func (s *Service) indexAd(c context.Context, plat int8, build int, buvid string, mid int64, network, mobiApp, device, openEvent string, zone *locmdl.Info, now time.Time, adExtra string, style int) (adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, err error) {
  180. var advert *cm.Ad
  181. resource := s.adResource(plat, build)
  182. if resource == 0 {
  183. return
  184. }
  185. // 兼容老的style逻辑,3为新单列,上报给商业产品的参数定义为:1 单列 2双列
  186. if style == 3 {
  187. style = 1
  188. }
  189. var country, province, city string
  190. if zone != nil {
  191. country = zone.Country
  192. province = zone.Province
  193. city = zone.City
  194. }
  195. if advert, err = s.ad.Ad(c, mid, build, buvid, []int64{resource}, country, province, city, network, mobiApp, device, openEvent, adExtra, style, now); err != nil {
  196. return
  197. }
  198. if advert == nil || len(advert.AdsInfo) == 0 {
  199. return
  200. }
  201. if adsInfo, ok := advert.AdsInfo[resource]; ok {
  202. adm = make(map[int]*cm.AdInfo, len(adsInfo))
  203. adAidm = make(map[int64]struct{}, len(adsInfo))
  204. for source, info := range adsInfo {
  205. if info == nil {
  206. continue
  207. }
  208. var adInfo *cm.AdInfo
  209. if info.AdInfo != nil {
  210. adInfo = info.AdInfo
  211. adInfo.RequestID = advert.RequestID
  212. adInfo.Resource = resource
  213. adInfo.Source = source
  214. adInfo.IsAd = info.IsAd
  215. adInfo.IsAdLoc = true
  216. adInfo.CmMark = info.CmMark
  217. adInfo.Index = info.Index
  218. adInfo.CardIndex = info.CardIndex
  219. adInfo.ClientIP = advert.ClientIP
  220. if adInfo.CreativeID != 0 && adInfo.CardType == _cardAdAv {
  221. adAidm[adInfo.CreativeContent.VideoID] = struct{}{}
  222. }
  223. } else {
  224. adInfo = &cm.AdInfo{RequestID: advert.RequestID, Resource: resource, Source: source, IsAdLoc: true, IsAd: info.IsAd, CmMark: info.CmMark, Index: info.Index, CardIndex: info.CardIndex, ClientIP: advert.ClientIP}
  225. }
  226. adm[adInfo.CardIndex-1] = adInfo
  227. }
  228. }
  229. return
  230. }
  231. func (s *Service) indexBanner(c context.Context, plat int8, build int, buvid string, mid int64, loginEvent int, hash, network, mobiApp, device, openEvent, adExtra string) (has bool, bs []*banner.Banner, version string, err error) {
  232. const (
  233. _androidBanBannerHash = 515009
  234. _iphoneBanBannerHash = 6120
  235. _ipadBanBannerHash = 6160
  236. )
  237. if (plat == model.PlatAndroid && build > _androidBanBannerHash) || (plat == model.PlatIPhone && build > _iphoneBanBannerHash) || (plat == model.PlatIPad && build > _ipadBanBannerHash) || loginEvent != 0 {
  238. if bs, version, err = s.banners(c, plat, build, mid, buvid, network, mobiApp, device, openEvent, adExtra, ""); err != nil {
  239. return
  240. } else if loginEvent != 0 {
  241. has = true
  242. } else if version != "" {
  243. has = hash != version
  244. }
  245. }
  246. return
  247. }
  248. func (s *Service) mergeItem(c context.Context, mid int64, rs []*ai.Item, adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, hasBanner bool, blackAids map[int64]struct{}, plat int8, follow *operate.Follow) (is []*ai.Item, adInfom map[int]*cm.AdInfo) {
  249. if len(rs) == 0 {
  250. return
  251. }
  252. const (
  253. cardIndex = 7
  254. cardIndexIPad = 17
  255. cardOffset = 2
  256. )
  257. if hasBanner {
  258. rs = append([]*ai.Item{&ai.Item{Goto: model.GotoBanner}}, rs...)
  259. for index, ad := range adm {
  260. if ((model.IsIPad(plat) && index <= cardIndexIPad) || index <= cardIndex) && (ad.CardType == _cardAdWeb || ad.CardType == _cardAdLarge) {
  261. ad.CardIndex = ad.CardIndex + cardOffset
  262. }
  263. }
  264. }
  265. if follow != nil {
  266. followPos := s.c.Feed.Index.FollowPosition
  267. if followPos-1 >= 0 && followPos-1 <= len(rs) {
  268. rs = append(rs[:followPos-1], append([]*ai.Item{&ai.Item{ID: follow.ID, Goto: model.GotoSearchSubscribe}}, rs[followPos-1:]...)...)
  269. }
  270. }
  271. is = make([]*ai.Item, 0, len(rs)+len(adm))
  272. adInfom = make(map[int]*cm.AdInfo, len(adm))
  273. var existsBanner, existsAdWeb bool
  274. for _, r := range rs {
  275. for {
  276. if ad, ok := adm[len(is)]; ok {
  277. if ad.CreativeID != 0 {
  278. var item *ai.Item
  279. if ad.CardType == _cardAdAv {
  280. item = &ai.Item{ID: ad.CreativeContent.VideoID, Goto: model.GotoAdAv, Ad: ad}
  281. } else if ad.CardType == _cardAdWeb {
  282. item = &ai.Item{Goto: model.GotoAdWeb, Ad: ad}
  283. existsAdWeb = true
  284. } else if ad.CardType == _cardAdWebS {
  285. item = &ai.Item{Goto: model.GotoAdWebS, Ad: ad}
  286. } else if ad.CardType == _cardAdLarge {
  287. item = &ai.Item{Goto: model.GotoAdLarge, Ad: ad}
  288. } else {
  289. b, _ := json.Marshal(ad)
  290. log.Error("ad---%s", b)
  291. break
  292. }
  293. is = append(is, item)
  294. continue
  295. } else {
  296. adInfom[len(is)] = ad
  297. }
  298. }
  299. break
  300. }
  301. if r.Goto == model.GotoAv {
  302. if _, ok := blackAids[r.ID]; ok {
  303. continue
  304. } else if _, ok := s.blackCache[r.ID]; ok {
  305. continue
  306. }
  307. if _, ok := adAidm[r.ID]; ok {
  308. continue
  309. }
  310. } else if r.Goto == model.GotoBanner {
  311. if existsBanner {
  312. continue
  313. } else {
  314. existsBanner = true
  315. }
  316. } else if r.Goto == model.GotoRank && existsAdWeb {
  317. continue
  318. } else if r.Goto == model.GotoLogin && mid != 0 {
  319. continue
  320. }
  321. is = append(is, r)
  322. }
  323. return
  324. }
  325. func (s *Service) dealItem(c context.Context, mid int64, plat int8, build int, buvid string, rs []*ai.Item, bs []*banner.Banner, version string, isRcmd bool, network, mobiApp, device, openEvent string, idx int64, pull bool, qn int, now time.Time, adExtra string, adInfom map[int]*cm.AdInfo, fnver, fnval, autoPlay int, follow *operate.Follow) (is []*feed.Item, isAI bool, err error) {
  326. if len(rs) == 0 {
  327. is = _emptyItem
  328. return
  329. }
  330. var (
  331. aids, tids, roomIDs, sids, metaIDs, shopIDs, audioIDs []int64
  332. upIDs, avUpIDs, rmUpIDs, mtUpIDs []int64
  333. seasonIDs []int32
  334. ranks []*rank.Rank
  335. am map[int64]*archive.ArchiveWithPlayer
  336. tagm map[int64]*bustag.Tag
  337. follows map[int64]bool
  338. rm map[int64]*live.Room
  339. sm map[int64]*bangumi.Season
  340. hasBangumiRcmd bool
  341. update *bangumi.Update
  342. atm map[int64]*article.Meta
  343. scm map[int64]*show.Shopping
  344. aum map[int64]*audio.Audio
  345. hasBanner bool
  346. card map[int64]*account.Card
  347. upStatm map[int64]*relation.Stat
  348. arcOK bool
  349. seasonCards map[int32]*episodegrpc.EpisodeCardsProto
  350. )
  351. isAI = isRcmd
  352. convergem := map[int64]*operate.Converge{}
  353. downloadm := map[int64]*operate.Download{}
  354. liveUpm := map[int64][]*live.Card{}
  355. followm := map[int64]*operate.Follow{}
  356. for _, r := range rs {
  357. switch r.Goto {
  358. case model.GotoAv, model.GotoAdAv, model.GotoPlayer, model.GotoUpRcmdAv:
  359. if r.ID != 0 {
  360. aids = append(aids, r.ID)
  361. }
  362. if r.Tid != 0 {
  363. tids = append(tids, r.Tid)
  364. }
  365. case model.GotoLive, model.GotoPlayerLive:
  366. if r.ID != 0 {
  367. roomIDs = append(roomIDs, r.ID)
  368. }
  369. case model.GotoBangumi:
  370. if r.ID != 0 {
  371. sids = append(sids, r.ID)
  372. }
  373. case model.GotoPGC:
  374. if r.ID != 0 {
  375. seasonIDs = append(seasonIDs, int32(r.ID))
  376. }
  377. case model.GotoRank:
  378. card, aid := s.RankCard(model.PlatIPhone)
  379. ranks = card
  380. aids = append(aids, aid...)
  381. case model.GotoBangumiRcmd:
  382. hasBangumiRcmd = true
  383. case model.GotoBanner:
  384. hasBanner = true
  385. case model.GotoConverge:
  386. if card, ok := s.convergeCache[r.ID]; ok {
  387. for _, item := range card.Items {
  388. switch item.Goto {
  389. case model.GotoAv:
  390. if item.Pid != 0 {
  391. aids = append(aids, item.Pid)
  392. }
  393. case model.GotoLive:
  394. if item.Pid != 0 {
  395. roomIDs = append(roomIDs, item.Pid)
  396. }
  397. case model.GotoArticle:
  398. if item.Pid != 0 {
  399. metaIDs = append(metaIDs, item.Pid)
  400. }
  401. }
  402. }
  403. convergem[r.ID] = card
  404. }
  405. case model.GotoGameDownloadS:
  406. if card, ok := s.downloadCache[r.ID]; ok {
  407. downloadm[r.ID] = card
  408. }
  409. case model.GotoArticleS:
  410. if r.ID != 0 {
  411. metaIDs = append(metaIDs, r.ID)
  412. }
  413. case model.GotoShoppingS:
  414. if r.ID != 0 {
  415. shopIDs = append(shopIDs, r.ID)
  416. }
  417. case model.GotoAudio:
  418. if r.ID != 0 {
  419. audioIDs = append(audioIDs, r.ID)
  420. }
  421. case model.GotoLiveUpRcmd:
  422. if r.ID != 0 {
  423. if cs, ok := s.liveCardCache[r.ID]; ok {
  424. for _, c := range cs {
  425. upIDs = append(upIDs, c.UID)
  426. }
  427. }
  428. }
  429. case model.GotoSubscribe:
  430. if r.ID != 0 {
  431. if card, ok := s.followCache[r.ID]; ok {
  432. for _, item := range card.Items {
  433. switch item.Goto {
  434. case cdm.GotoMid:
  435. if item.Pid != 0 {
  436. upIDs = append(upIDs, item.Pid)
  437. }
  438. case cdm.GotoTag:
  439. if item.Pid != 0 {
  440. tids = append(tids, item.Pid)
  441. }
  442. }
  443. }
  444. followm[r.ID] = card
  445. }
  446. }
  447. case model.GotoChannelRcmd:
  448. if r.ID != 0 {
  449. if card, ok := s.followCache[r.ID]; ok {
  450. if card.Pid != 0 {
  451. aids = append(aids, card.Pid)
  452. }
  453. if card.Tid != 0 {
  454. tids = append(tids, card.Tid)
  455. }
  456. followm[r.ID] = card
  457. }
  458. }
  459. case model.GotoSearchSubscribe:
  460. if follow != nil {
  461. followm[follow.ID] = follow
  462. for _, item := range follow.Items {
  463. upIDs = append(upIDs, item.Pid)
  464. }
  465. }
  466. }
  467. }
  468. g, ctx := errgroup.WithContext(c)
  469. if len(aids) != 0 {
  470. g.Go(func() (err error) {
  471. if am, err = s.ArchivesWithPlayer(ctx, aids, qn, mobiApp, fnver, fnval, 0, build); err != nil {
  472. return
  473. }
  474. arcOK = true
  475. for _, a := range am {
  476. avUpIDs = append(avUpIDs, a.Author.Mid)
  477. }
  478. return
  479. })
  480. }
  481. if len(tids) != 0 {
  482. g.Go(func() (err error) {
  483. if tagm, err = s.tg.InfoByIDs(ctx, mid, tids); err != nil {
  484. log.Error("%+v", err)
  485. err = nil
  486. }
  487. return
  488. })
  489. }
  490. if len(roomIDs) != 0 {
  491. g.Go(func() (err error) {
  492. if rm, err = s.lv.AppMRoom(ctx, roomIDs); err != nil {
  493. log.Error("%+v", err)
  494. err = nil
  495. }
  496. for _, r := range rm {
  497. rmUpIDs = append(rmUpIDs, r.UID)
  498. }
  499. return
  500. })
  501. }
  502. if len(sids) != 0 {
  503. g.Go(func() (err error) {
  504. if sm, err = s.bgm.Seasons(ctx, sids, now); err != nil {
  505. log.Error("%+v", err)
  506. err = nil
  507. }
  508. return
  509. })
  510. }
  511. if len(seasonIDs) != 0 {
  512. g.Go(func() (err error) {
  513. if seasonCards, err = s.bgm.CardsInfoReply(ctx, seasonIDs); err != nil {
  514. log.Error("%+v", err)
  515. err = nil
  516. }
  517. return
  518. })
  519. }
  520. // TODO DEL
  521. // if hasBangumiRcmd && mid != 0 {
  522. if hasBangumiRcmd {
  523. g.Go(func() (err error) {
  524. if update, err = s.bgm.Updates(ctx, mid, now); err != nil {
  525. log.Error("%+v", err)
  526. err = nil
  527. }
  528. return
  529. })
  530. }
  531. if hasBanner && version == "" {
  532. g.Go(func() (err error) {
  533. if bs, version, err = s.banners(ctx, plat, build, mid, buvid, network, mobiApp, device, openEvent, adExtra, ""); err != nil {
  534. log.Error("%+v", err)
  535. err = nil
  536. }
  537. return
  538. })
  539. }
  540. if len(metaIDs) != 0 {
  541. g.Go(func() (err error) {
  542. if atm, err = s.art.Articles(ctx, metaIDs); err != nil {
  543. log.Error("%+v", err)
  544. err = nil
  545. }
  546. for _, at := range atm {
  547. if at.Author != nil {
  548. mtUpIDs = append(mtUpIDs, at.Author.Mid)
  549. }
  550. }
  551. return
  552. })
  553. }
  554. if len(shopIDs) != 0 {
  555. g.Go(func() (err error) {
  556. if scm, err = s.show.Card(ctx, shopIDs); err != nil {
  557. log.Error("%+v", err)
  558. err = nil
  559. }
  560. return
  561. })
  562. }
  563. if len(audioIDs) != 0 {
  564. g.Go(func() (err error) {
  565. if aum, err = s.audio.Audios(ctx, audioIDs); err != nil {
  566. log.Error("%+v", err)
  567. err = nil
  568. }
  569. return
  570. })
  571. }
  572. if err = g.Wait(); err != nil {
  573. log.Error("%+v", err)
  574. if isRcmd {
  575. count := s.indexCount(plat)
  576. rs = s.recommendCache(count)
  577. }
  578. } else {
  579. upIDs = append(upIDs, avUpIDs...)
  580. upIDs = append(upIDs, rmUpIDs...)
  581. upIDs = append(upIDs, mtUpIDs...)
  582. g, ctx = errgroup.WithContext(c)
  583. if len(upIDs) != 0 {
  584. g.Go(func() (err error) {
  585. if card, err = s.acc.Cards3(ctx, upIDs); err != nil {
  586. log.Error("%+v", err)
  587. err = nil
  588. }
  589. return
  590. })
  591. g.Go(func() (err error) {
  592. if upStatm, err = s.rel.Stats(ctx, upIDs); err != nil {
  593. log.Error("%+v", err)
  594. err = nil
  595. }
  596. return
  597. })
  598. if mid != 0 {
  599. g.Go(func() error {
  600. follows = s.acc.Relations3(ctx, upIDs, mid)
  601. return nil
  602. })
  603. }
  604. }
  605. g.Wait()
  606. }
  607. isAI = isAI && arcOK
  608. // init feed items
  609. is = make([]*feed.Item, 0, len(rs))
  610. var (
  611. smallCardCnt int
  612. middleCardCnt int
  613. )
  614. ip := metadata.String(c, metadata.RemoteIP)
  615. adm := map[int]*feed.Item{}
  616. isIpad := plat == model.PlatIPad
  617. for _, r := range rs {
  618. il := len(is)
  619. i := &feed.Item{AI: r}
  620. i.FromRcmd(r)
  621. switch r.Goto {
  622. case model.GotoAv, model.GotoUpRcmdAv:
  623. a, ok := am[r.ID]
  624. if !ok && !arcOK {
  625. a = &archive.ArchiveWithPlayer{Archive3: r.Archive}
  626. }
  627. isOsea := model.IsOverseas(plat)
  628. if a != nil && a.Archive3 != nil && a.IsNormal() && (!isOsea || (isOsea && a.AttrVal(archive.AttrBitOverseaLock) == 0)) {
  629. i.FromPlayerAv(a)
  630. if arcOK {
  631. if info, ok := tagm[r.Tid]; ok {
  632. i.Tag = &feed.Tag{TagID: info.ID, TagName: info.Name, IsAtten: info.IsAtten, Count: &feed.TagCount{Atten: info.Count.Atten}}
  633. }
  634. } else if r.Tag != nil {
  635. i.Tag = &feed.Tag{TagID: r.Tag.ID, TagName: r.Tag.Name}
  636. }
  637. i.FromDislikeReason(plat, build)
  638. i.FromRcmdReason(r.RcmdReason)
  639. if follows[i.Mid] {
  640. i.IsAtten = 1
  641. }
  642. if card, ok := card[i.Mid]; ok {
  643. if card.Official.Role != 0 {
  644. i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc}
  645. }
  646. }
  647. // for GotoUpRcmdAv
  648. i.Goto = r.Goto
  649. if i.Goto == model.GotoUpRcmdAv {
  650. // TODO 等待开启
  651. // percent := i.Like / (i.Like + i.Dislike) * 100
  652. // if percent != 0 {
  653. // i.Desc = strconv.Itoa(percent) + "%的人推荐"
  654. // }
  655. i.Desc = ""
  656. }
  657. is = append(is, i)
  658. smallCardCnt++
  659. }
  660. case model.GotoLive:
  661. if r, ok := rm[r.ID]; ok {
  662. i.FromLive(r)
  663. if card, ok := card[i.Mid]; ok {
  664. if card.Official.Role != 0 {
  665. i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc}
  666. }
  667. }
  668. if stat, ok := upStatm[i.Mid]; ok {
  669. i.Fans = stat.Follower
  670. }
  671. if follows[i.Mid] {
  672. i.IsAtten = 1
  673. }
  674. if i.Goto != "" {
  675. is = append(is, i)
  676. smallCardCnt++
  677. }
  678. }
  679. case model.GotoBangumi:
  680. if s, ok := sm[r.ID]; ok {
  681. i.FromSeason(s)
  682. is = append(is, i)
  683. smallCardCnt++
  684. }
  685. case model.GotoPGC:
  686. if s, ok := seasonCards[int32(r.ID)]; ok {
  687. i.FromPGCSeason(s)
  688. is = append(is, i)
  689. smallCardCnt++
  690. }
  691. case model.GotoLogin:
  692. i.FromLogin()
  693. is = append(is, i)
  694. smallCardCnt++
  695. case model.GotoAdAv:
  696. if r.Ad != nil {
  697. if a, ok := am[r.ID]; ok && model.AdAvIsNormal(a) {
  698. i.FromAdAv(r.Ad, a)
  699. if follows[i.Mid] {
  700. i.IsAtten = 1
  701. }
  702. if card, ok := card[i.Mid]; ok {
  703. if card.Official.Role != 0 {
  704. i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc}
  705. }
  706. }
  707. i.ClientIP = ip
  708. adm[i.CardIndex-1] = i
  709. }
  710. }
  711. case model.GotoAdWebS:
  712. if r.Ad != nil {
  713. i.FromAdWebS(r.Ad)
  714. i.ClientIP = ip
  715. adm[i.CardIndex-1] = i
  716. }
  717. case model.GotoAdWeb:
  718. if r.Ad != nil {
  719. i.FromAdWeb(r.Ad)
  720. i.ClientIP = ip
  721. adm[i.CardIndex-1] = i
  722. }
  723. case model.GotoAdLarge:
  724. if r.Ad != nil {
  725. i.FromAdLarge(r.Ad)
  726. i.ClientIP = ip
  727. adm[i.CardIndex-1] = i
  728. }
  729. case model.GotoSpecial:
  730. if sc, ok := s.specialCache[r.ID]; ok {
  731. i.FromSpecial(sc.ID, sc.Title, sc.Cover, sc.Desc, sc.ReValue, sc.ReType, sc.Badge, sc.Size)
  732. }
  733. if i.Goto != "" {
  734. if !isIpad {
  735. if smallCardCnt%2 != 0 {
  736. is = swapTwoItem(is, i)
  737. } else {
  738. is = append(is, i)
  739. }
  740. } else {
  741. if (smallCardCnt+middleCardCnt*2)%2 != 0 {
  742. is = swapTwoItem(is, i)
  743. } else {
  744. is = append(is, i)
  745. }
  746. middleCardCnt++
  747. }
  748. }
  749. case model.GotoSpecialS:
  750. if sc, ok := s.specialCache[r.ID]; ok {
  751. i.FromSpecialS(sc.ID, sc.Title, sc.Cover, sc.SingleCover, sc.Desc, sc.ReValue, sc.ReType, sc.Badge)
  752. }
  753. if i.Goto != "" {
  754. if !isIpad {
  755. is = append(is, i)
  756. smallCardCnt++
  757. }
  758. }
  759. case model.GotoRank:
  760. i.FromRank(ranks, am)
  761. if i.Goto != "" {
  762. if !isIpad {
  763. if smallCardCnt%2 != 0 {
  764. is = swapTwoItem(is, i)
  765. } else {
  766. is = append(is, i)
  767. }
  768. } else {
  769. if (smallCardCnt+middleCardCnt*2)%2 != 0 {
  770. is = swapTwoItem(is, i)
  771. } else {
  772. is = append(is, i)
  773. }
  774. middleCardCnt++
  775. }
  776. }
  777. case model.GotoBangumiRcmd:
  778. if mid != 0 && update != nil && update.Updates != 0 {
  779. i.FromBangumiRcmd(update)
  780. if !isIpad {
  781. if smallCardCnt%2 != 0 {
  782. is = swapTwoItem(is, i)
  783. } else {
  784. is = append(is, i)
  785. }
  786. } else {
  787. is = append(is, i)
  788. smallCardCnt++
  789. }
  790. }
  791. case model.GotoBanner:
  792. if len(bs) != 0 {
  793. i.FromBanner(bs, version)
  794. if !isIpad {
  795. if smallCardCnt%2 != 0 {
  796. is = swapTwoItem(is, i)
  797. } else {
  798. is = append(is, i)
  799. }
  800. } else {
  801. switch (smallCardCnt + middleCardCnt*2) % 4 {
  802. case 0:
  803. is = append(is, i)
  804. case 1:
  805. is = swapTwoItem(is, i)
  806. case 2:
  807. switch is[len(is)-1].Goto {
  808. case model.GotoRank, model.GotoAdWeb, model.GotoAdLarge:
  809. is = swapTwoItem(is, i)
  810. default:
  811. is = swapThreeItem(is, i)
  812. }
  813. case 3:
  814. is = swapThreeItem(is, i)
  815. }
  816. }
  817. }
  818. case model.GotoConverge:
  819. if cc, ok := convergem[r.ID]; ok {
  820. i.FromConverge(cc, am, rm, atm)
  821. if i.Goto != "" {
  822. if !isIpad {
  823. if smallCardCnt%2 != 0 {
  824. is = swapTwoItem(is, i)
  825. } else {
  826. is = append(is, i)
  827. }
  828. }
  829. }
  830. }
  831. case model.GotoGameDownloadS:
  832. if gd, ok := downloadm[r.ID]; ok {
  833. i.FromGameDownloadS(gd, plat, build)
  834. if i.Goto != "" {
  835. if !isIpad {
  836. is = append(is, i)
  837. smallCardCnt++
  838. }
  839. }
  840. }
  841. case model.GotoArticleS:
  842. if m, ok := atm[r.ID]; ok {
  843. i.FromArticleS(m)
  844. if card, ok := card[i.Mid]; ok {
  845. if card.Official.Role != 0 {
  846. i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc}
  847. }
  848. }
  849. if i.Goto != "" {
  850. if !isIpad {
  851. is = append(is, i)
  852. smallCardCnt++
  853. }
  854. }
  855. }
  856. case model.GotoShoppingS:
  857. if c, ok := scm[r.ID]; ok {
  858. i.FromShoppingS(c)
  859. if i.Goto != "" {
  860. if !isIpad {
  861. is = append(is, i)
  862. smallCardCnt++
  863. }
  864. }
  865. }
  866. case model.GotoAudio:
  867. if au, ok := aum[r.ID]; ok {
  868. i.FromAudio(au)
  869. is = append(is, i)
  870. smallCardCnt++
  871. }
  872. case model.GotoPlayer:
  873. if a, ok := am[r.ID]; ok {
  874. i.FromPlayer(a)
  875. if i.Goto != "" {
  876. if info, ok := tagm[r.Tid]; ok {
  877. i.Tag = &feed.Tag{TagID: info.ID, TagName: info.Name, IsAtten: info.IsAtten, Count: &feed.TagCount{Atten: info.Count.Atten}}
  878. }
  879. if follows[i.Mid] {
  880. i.IsAtten = 1
  881. }
  882. if card, ok := card[i.Mid]; ok {
  883. if card.Official.Role != 0 {
  884. i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc}
  885. }
  886. }
  887. i.FromDislikeReason(plat, build)
  888. if !isIpad {
  889. if smallCardCnt%2 != 0 {
  890. is = swapTwoItem(is, i)
  891. } else {
  892. is = append(is, i)
  893. }
  894. }
  895. }
  896. }
  897. case model.GotoPlayerLive:
  898. if r, ok := rm[r.ID]; ok {
  899. i.FromPlayerLive(r)
  900. if i.Goto != "" {
  901. if follows[i.Mid] {
  902. i.IsAtten = 1
  903. }
  904. if card, ok := card[i.Mid]; ok {
  905. if card.Official.Role != 0 {
  906. i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc}
  907. }
  908. }
  909. if stat, ok := upStatm[i.Mid]; ok {
  910. i.Fans = stat.Follower
  911. }
  912. if !isIpad {
  913. if smallCardCnt%2 != 0 {
  914. is = swapTwoItem(is, i)
  915. } else {
  916. is = append(is, i)
  917. }
  918. }
  919. }
  920. }
  921. case model.GotoSubscribe, model.GotoSearchSubscribe:
  922. if c, ok := followm[r.ID]; ok {
  923. if !isIpad {
  924. i.FromSubscribe(c, card, follows, upStatm, tagm)
  925. if i.Goto != "" {
  926. if smallCardCnt%2 != 0 {
  927. is = swapTwoItem(is, i)
  928. } else {
  929. is = append(is, i)
  930. }
  931. }
  932. }
  933. }
  934. case model.GotoChannelRcmd:
  935. if c, ok := followm[r.ID]; ok {
  936. if !isIpad {
  937. i.FromChannelRcmd(c, am, tagm)
  938. if i.Goto != "" {
  939. if !isIpad {
  940. is = append(is, i)
  941. smallCardCnt++
  942. }
  943. }
  944. }
  945. }
  946. case model.GotoLiveUpRcmd:
  947. if c, ok := liveUpm[r.ID]; ok {
  948. if !isIpad {
  949. i.FromLiveUpRcmd(r.ID, c, card)
  950. if i.Goto != "" {
  951. if smallCardCnt%2 != 0 {
  952. is = swapTwoItem(is, i)
  953. } else {
  954. is = append(is, i)
  955. }
  956. }
  957. }
  958. }
  959. default:
  960. log.Warn("unexpected goto(%s) %+v", r.Goto, r)
  961. continue
  962. }
  963. if ad, ok := adm[il]; ok {
  964. switch ad.Goto {
  965. case model.GotoAdAv, model.GotoAdWebS:
  966. is = append(is, ad)
  967. smallCardCnt++
  968. case model.GotoAdWeb, model.GotoAdLarge:
  969. if !isIpad {
  970. if smallCardCnt%2 != 0 {
  971. is = swapTwoItem(is, ad)
  972. } else {
  973. is = append(is, ad)
  974. }
  975. } else {
  976. if (smallCardCnt+middleCardCnt*2)%2 != 0 {
  977. is = swapTwoItem(is, ad)
  978. } else {
  979. is = append(is, ad)
  980. }
  981. middleCardCnt++
  982. }
  983. }
  984. }
  985. }
  986. if !isIpad {
  987. is = is[:len(is)-smallCardCnt%2]
  988. } else {
  989. switch (smallCardCnt + middleCardCnt*2) % 4 {
  990. case 1:
  991. is = is[:len(is)-1]
  992. case 2:
  993. if isMiddleCard(is[len(is)-1].Goto) {
  994. is = is[:len(is)-1]
  995. } else {
  996. is = is[:len(is)-2]
  997. }
  998. case 3:
  999. if isMiddleCard(is[len(is)-1].Goto) {
  1000. is = is[:len(is)-2]
  1001. } else {
  1002. is = is[:len(is)-3]
  1003. }
  1004. }
  1005. }
  1006. rl := len(is)
  1007. if rl == 0 {
  1008. is = _emptyItem
  1009. return
  1010. }
  1011. if idx == 0 {
  1012. idx = now.Unix()
  1013. }
  1014. for i, r := range is {
  1015. if pull {
  1016. r.Idx = idx + int64(rl-i)
  1017. } else {
  1018. r.Idx = idx - int64(i+1)
  1019. }
  1020. if ad, ok := adInfom[i]; ok {
  1021. r.SrcID = ad.Source
  1022. r.RequestID = ad.RequestID
  1023. r.IsAdLoc = ad.IsAdLoc
  1024. r.IsAd = ad.IsAd
  1025. r.CmMark = ad.CmMark
  1026. r.AdIndex = ad.Index
  1027. r.ClientIP = ip
  1028. r.CardIndex = i + 1
  1029. } else if r.IsAd {
  1030. r.CardIndex = i + 1
  1031. }
  1032. if i == 0 {
  1033. r.AutoplayCard = autoPlay
  1034. }
  1035. }
  1036. return
  1037. }
  1038. func (s *Service) adResource(plat int8, build int) (resource int64) {
  1039. const (
  1040. _androidBanAd = 500001
  1041. )
  1042. if plat == model.PlatIPhone || plat == model.PlatIPhoneB || (plat == model.PlatAndroid && build >= _androidBanAd) || plat == model.PlatIPad {
  1043. resource = s.cmResourceMap[plat]
  1044. }
  1045. return
  1046. }
  1047. func swapTwoItem(rs []*feed.Item, i *feed.Item) (is []*feed.Item) {
  1048. rs[len(rs)-1].Idx, i.Idx = i.Idx, rs[len(rs)-1].Idx
  1049. is = append(rs, rs[len(rs)-1])
  1050. is[len(is)-2] = i
  1051. return
  1052. }
  1053. func swapThreeItem(rs []*feed.Item, i *feed.Item) (is []*feed.Item) {
  1054. rs[len(rs)-1].Idx, i.Idx = i.Idx, rs[len(rs)-1].Idx
  1055. rs[len(rs)-2].Idx, rs[len(is)-1].Idx = rs[len(rs)-1].Idx, rs[len(rs)-2].Idx
  1056. is = append(rs, rs[len(rs)-1])
  1057. is[len(is)-2] = i
  1058. is[len(is)-3], is[len(is)-2] = is[len(is)-2], is[len(is)-3]
  1059. return
  1060. }
  1061. func isMiddleCard(gt string) bool {
  1062. return gt == model.GotoRank || gt == model.GotoAdWeb || gt == model.GotoPlayer ||
  1063. gt == model.GotoPlayerLive || gt == model.GotoConverge || gt == model.GotoSpecial || gt == model.GotoAdLarge || gt == model.GotoLiveUpRcmd
  1064. }
  1065. func (s *Service) indexCount(plat int8) (count int) {
  1066. if plat == model.PlatIPad {
  1067. count = s.c.Feed.Index.IPadCount
  1068. } else {
  1069. count = s.c.Feed.Index.Count
  1070. }
  1071. return
  1072. }