index2.go 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  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/bplus"
  9. "go-common/app/interface/main/app-card/model/card"
  10. "go-common/app/interface/main/app-card/model/card/ai"
  11. "go-common/app/interface/main/app-card/model/card/audio"
  12. "go-common/app/interface/main/app-card/model/card/bangumi"
  13. "go-common/app/interface/main/app-card/model/card/banner"
  14. "go-common/app/interface/main/app-card/model/card/cm"
  15. "go-common/app/interface/main/app-card/model/card/live"
  16. "go-common/app/interface/main/app-card/model/card/operate"
  17. "go-common/app/interface/main/app-card/model/card/show"
  18. "go-common/app/interface/main/app-feed/model"
  19. "go-common/app/interface/main/app-feed/model/feed"
  20. tag "go-common/app/interface/main/tag/model"
  21. article "go-common/app/interface/openplatform/article/model"
  22. account "go-common/app/service/main/account/model"
  23. "go-common/app/service/main/archive/model/archive"
  24. locmdl "go-common/app/service/main/location/model"
  25. relation "go-common/app/service/main/relation/model"
  26. episodegrpc "go-common/app/service/openplatform/pgc-season/api/grpc/episode/v1"
  27. "go-common/library/ecode"
  28. "go-common/library/log"
  29. "go-common/library/net/metadata"
  30. "go-common/library/sync/errgroup"
  31. )
  32. const _qn480 = 32
  33. var (
  34. _cardAdAvm = map[int]struct{}{
  35. 1: struct{}{},
  36. }
  37. _cardAdWebm = map[int]struct{}{
  38. 2: struct{}{},
  39. 7: struct{}{},
  40. 20: struct{}{},
  41. }
  42. _cardAdWebSm = map[int]struct{}{
  43. 3: struct{}{},
  44. 26: struct{}{},
  45. }
  46. _followMode = &feed.FollowMode{
  47. Title: "当前为首页推荐 - 关注模式(内测版)",
  48. Option: []*feed.Option{
  49. {Title: "通用模式", Desc: "开启后,推荐你可能感兴趣的内容", Value: 0},
  50. {Title: "关注模式(内测版)", Desc: "开启后,仅显示关注UP主更新的视频", Value: 1},
  51. },
  52. ToastMessage: "关注UP主的内容已经看完啦,请稍后再试",
  53. }
  54. )
  55. func (s *Service) Index2(c context.Context, buvid string, mid int64, plat int8, param *feed.IndexParam, style int, now time.Time) (is []card.Handler, config *feed.Config, infoc *feed.Infoc, err error) {
  56. var (
  57. rs []*ai.Item
  58. adm map[int]*cm.AdInfo
  59. adAidm map[int64]struct{}
  60. banners []*banner.Banner
  61. version string
  62. blackAidm map[int64]struct{}
  63. adInfom map[int]*cm.AdInfo
  64. follow *operate.Card
  65. info *locmdl.Info
  66. )
  67. ip := metadata.String(c, metadata.RemoteIP)
  68. config = s.indexConfig(c, plat, buvid, mid, param)
  69. if config.FollowMode == nil {
  70. param.RecsysMode = 0
  71. }
  72. noCache := param.RecsysMode == 1
  73. followMode := config.FollowMode != nil
  74. infoc = &feed.Infoc{}
  75. infoc.AutoPlayInfoc = fmt.Sprintf("%d|%d", config.AutoplayCard, param.AutoPlayCard)
  76. if info, err = s.loc.Info(c, ip); err != nil {
  77. log.Warn("s.loc.Info(%v) error(%v)", ip, err)
  78. err = nil
  79. }
  80. group := s.group(mid, buvid)
  81. if !s.c.Feed.Index.Abnormal || followMode {
  82. g, ctx := errgroup.WithContext(c)
  83. g.Go(func() error {
  84. rs, infoc.UserFeature, infoc.IsRcmd, infoc.NewUser, infoc.Code = s.indexRcmd2(ctx, plat, buvid, mid, param, group, info, style, infoc.AutoPlayInfoc, noCache, now)
  85. return nil
  86. })
  87. g.Go(func() (err error) {
  88. if banners, version, err = s.indexBanner2(ctx, plat, buvid, mid, param); err != nil {
  89. log.Error("%+v", err)
  90. err = nil
  91. }
  92. return
  93. })
  94. if param.RecsysMode == 0 {
  95. g.Go(func() (err error) {
  96. if adm, adAidm, err = s.indexAd2(ctx, plat, buvid, mid, param, info, style, now); err != nil {
  97. log.Error("%+v", err)
  98. err = nil
  99. }
  100. return
  101. })
  102. g.Go(func() (err error) {
  103. if blackAidm, err = s.BlackList(ctx, mid); err != nil {
  104. log.Error("%+v", err)
  105. err = nil
  106. }
  107. return
  108. })
  109. g.Go(func() (err error) {
  110. if follow, err = s.SearchFollow2(ctx, param.Platform, param.MobiApp, param.Device, buvid, param.Build, mid); err != nil {
  111. log.Error("%+v", err)
  112. err = nil
  113. }
  114. return
  115. })
  116. }
  117. if err = g.Wait(); err != nil {
  118. log.Error("%+v", err)
  119. return
  120. }
  121. if param.RecsysMode == 1 {
  122. var tmp []*ai.Item
  123. for _, r := range rs {
  124. if r.Goto == model.GotoBanner {
  125. continue
  126. }
  127. tmp = append(tmp, r)
  128. }
  129. if len(tmp) == 0 {
  130. is = []card.Handler{}
  131. return
  132. }
  133. }
  134. rs, adInfom = s.mergeItem2(c, plat, mid, rs, adm, adAidm, banners, version, blackAidm, follow, followMode)
  135. } else {
  136. count := s.indexCount(plat)
  137. rs = s.recommendCache(count)
  138. log.Warn("feed index show disaster recovery data len(%d)", len(is))
  139. }
  140. if config.AutoplayCard == 1 && cdm.Columnm[param.Column] == cdm.ColumnSvrSingle {
  141. param.Qn = _qn480
  142. }
  143. is, infoc.IsRcmd = s.dealItem2(c, mid, buvid, plat, rs, param, infoc.IsRcmd, noCache, followMode, follow, now)
  144. s.dealAdLoc(is, param, adInfom, now)
  145. return
  146. }
  147. func (s *Service) indexConfig(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam) (config *feed.Config) {
  148. config = &feed.Config{}
  149. config.Column = cdm.Columnm[param.Column]
  150. // if mid > 0 && mid%20 == 19 {
  151. // config.FeedCleanAbtest = 1
  152. // } else {
  153. // config.FeedCleanAbtest = 0
  154. // }
  155. config.FeedCleanAbtest = 0
  156. if !model.IsIPad(plat) {
  157. if ab, ok := s.abtestCache[_feedgroups]; ok {
  158. if ab.AbTestIn(buvid + _feedgroups) {
  159. switch param.AutoPlayCard {
  160. case 0, 1, 2, 3:
  161. config.AutoplayCard = 1
  162. default:
  163. config.AutoplayCard = 2
  164. }
  165. } else {
  166. config.AutoplayCard = 2
  167. }
  168. } else {
  169. switch param.AutoPlayCard {
  170. case 1, 3:
  171. config.AutoplayCard = 1
  172. default:
  173. config.AutoplayCard = 2
  174. }
  175. }
  176. } else {
  177. // ipad 不允许自动播放
  178. config.AutoplayCard = 2
  179. }
  180. if mid < 1 {
  181. return
  182. }
  183. if _, ok := s.autoplayMidsCache[mid]; ok && param.AutoPlayCard != 4 {
  184. config.AutoplayCard = 1
  185. }
  186. if _, ok := s.followModeList[mid]; ok {
  187. tmpConfig := &feed.FollowMode{}
  188. if s.c.Feed.Index.FollowMode == nil {
  189. *tmpConfig = *_followMode
  190. } else {
  191. *tmpConfig = *s.c.Feed.Index.FollowMode
  192. }
  193. if param.RecsysMode != 1 {
  194. tmpConfig.ToastMessage = ""
  195. }
  196. config.FollowMode = tmpConfig
  197. }
  198. return
  199. }
  200. func (s *Service) indexRcmd2(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam, group int, zone *locmdl.Info, style int, autoPlay string, noCache bool, now time.Time) (is []*ai.Item, userFeature json.RawMessage, isRcmd, newUser bool, code int) {
  201. count := s.indexCount(plat)
  202. if buvid != "" || mid > 0 {
  203. var (
  204. err error
  205. zoneID int64
  206. )
  207. if zone != nil {
  208. zoneID = zone.ZoneID
  209. }
  210. if is, userFeature, code, newUser, err = s.rcmd.Recommend(c, plat, buvid, mid, param.Build, param.LoginEvent, param.ParentMode, param.RecsysMode, zoneID, group, param.Interest, param.Network, style, param.Column, param.Flush, autoPlay, now); err != nil {
  211. log.Error("%+v", err)
  212. }
  213. if noCache {
  214. isRcmd = true
  215. return
  216. }
  217. if len(is) != 0 {
  218. isRcmd = true
  219. }
  220. var fromCache bool
  221. if len(is) == 0 && mid > 0 && !ecode.ServiceUnavailable.Equal(err) {
  222. if is, err = s.indexCache(c, mid, count); err != nil {
  223. log.Error("%+v", err)
  224. }
  225. if len(is) != 0 {
  226. s.pHit.Incr("index_cache")
  227. } else {
  228. s.pMiss.Incr("index_cache")
  229. }
  230. fromCache = true
  231. }
  232. if len(is) == 0 || (fromCache && len(is) < count) {
  233. is = s.recommendCache(count)
  234. }
  235. } else {
  236. is = s.recommendCache(count)
  237. }
  238. return
  239. }
  240. func (s *Service) indexAd2(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam, zone *locmdl.Info, style int, now time.Time) (adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, err error) {
  241. var advert *cm.Ad
  242. resource := s.adResource(plat, param.Build)
  243. if resource == 0 {
  244. return
  245. }
  246. // 兼容老的style逻辑,3为新单列,上报给商业产品的参数定义为:1 单列 2双列
  247. if style == 3 {
  248. style = 1
  249. }
  250. var country, province, city string
  251. if zone != nil {
  252. country = zone.Country
  253. province = zone.Province
  254. city = zone.City
  255. }
  256. if advert, err = s.ad.Ad(c, mid, param.Build, buvid, []int64{resource}, country, province, city, param.Network, param.MobiApp, param.Device, param.OpenEvent, param.AdExtra, style, now); err != nil {
  257. return
  258. }
  259. if advert == nil || len(advert.AdsInfo) == 0 {
  260. return
  261. }
  262. if adsInfo, ok := advert.AdsInfo[resource]; ok {
  263. adm = make(map[int]*cm.AdInfo, len(adsInfo))
  264. adAidm = make(map[int64]struct{}, len(adsInfo))
  265. for source, info := range adsInfo {
  266. if info == nil {
  267. continue
  268. }
  269. var adInfo *cm.AdInfo
  270. if info.AdInfo != nil {
  271. adInfo = info.AdInfo
  272. adInfo.RequestID = advert.RequestID
  273. adInfo.Resource = resource
  274. adInfo.Source = source
  275. adInfo.IsAd = info.IsAd
  276. adInfo.IsAdLoc = true
  277. adInfo.CmMark = info.CmMark
  278. adInfo.Index = info.Index
  279. adInfo.CardIndex = info.CardIndex
  280. adInfo.ClientIP = advert.ClientIP
  281. if adInfo.CreativeID != 0 && adInfo.CardType == _cardAdAv {
  282. adAidm[adInfo.CreativeContent.VideoID] = struct{}{}
  283. }
  284. } else {
  285. 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}
  286. }
  287. adm[adInfo.CardIndex-1] = adInfo
  288. }
  289. }
  290. return
  291. }
  292. func (s *Service) indexBanner2(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam) (banners []*banner.Banner, version string, err error) {
  293. hash := param.BannerHash
  294. if param.LoginEvent != 0 {
  295. hash = ""
  296. }
  297. banners, version, err = s.banners(c, plat, param.Build, mid, buvid, param.Network, param.MobiApp, param.Device, param.OpenEvent, param.AdExtra, hash)
  298. return
  299. }
  300. func (s *Service) mergeItem2(c context.Context, plat int8, mid int64, rs []*ai.Item, adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, banners []*banner.Banner, version string, blackAids map[int64]struct{}, follow *operate.Card, followMode bool) (is []*ai.Item, adInfom map[int]*cm.AdInfo) {
  301. if len(rs) == 0 {
  302. return
  303. }
  304. const (
  305. cardIndex = 7
  306. cardIndexIPad = 17
  307. cardOffset = 2
  308. )
  309. if len(banners) != 0 {
  310. rs = append([]*ai.Item{&ai.Item{Goto: model.GotoBanner, Banners: banners, Version: version}}, rs...)
  311. for index, ad := range adm {
  312. if _, ok := _cardAdWebm[ad.CardType]; ok && ((model.IsIPad(plat) && index <= cardIndexIPad) || index <= cardIndex) {
  313. ad.CardIndex = ad.CardIndex + cardOffset
  314. }
  315. }
  316. }
  317. if follow != nil {
  318. followPos := s.c.Feed.Index.FollowPosition
  319. if followPos-1 >= 0 && followPos-1 <= len(rs) {
  320. rs = append(rs[:followPos-1], append([]*ai.Item{&ai.Item{ID: follow.ID, Goto: model.GotoSearchSubscribe}}, rs[followPos-1:]...)...)
  321. }
  322. }
  323. is = make([]*ai.Item, 0, len(rs)+len(adm))
  324. adInfom = make(map[int]*cm.AdInfo, len(adm))
  325. var existsAdWeb bool
  326. for _, r := range rs {
  327. for {
  328. if ad, ok := adm[len(is)]; ok {
  329. if ad.CreativeID != 0 {
  330. var item *ai.Item
  331. if _, ok := _cardAdAvm[ad.CardType]; ok {
  332. item = &ai.Item{ID: ad.CreativeContent.VideoID, Goto: model.GotoAdAv, Ad: ad}
  333. } else if _, ok := _cardAdWebm[ad.CardType]; ok {
  334. item = &ai.Item{Goto: model.GotoAdWeb, Ad: ad}
  335. existsAdWeb = true
  336. } else if _, ok := _cardAdWebSm[ad.CardType]; ok {
  337. item = &ai.Item{Goto: model.GotoAdWebS, Ad: ad}
  338. } else {
  339. b, _ := json.Marshal(ad)
  340. log.Error("ad---%s", b)
  341. break
  342. }
  343. is = append(is, item)
  344. continue
  345. } else {
  346. adInfom[ad.CardIndex-1] = ad
  347. }
  348. }
  349. break
  350. }
  351. if r.Goto == model.GotoAv {
  352. if _, ok := blackAids[r.ID]; ok {
  353. continue
  354. } else if _, ok := s.blackCache[r.ID]; ok {
  355. continue
  356. }
  357. if _, ok := adAidm[r.ID]; ok {
  358. continue
  359. }
  360. } else if r.Goto == model.GotoBanner && len(is) != 0 {
  361. // banner 必须在第一位
  362. continue
  363. } else if r.Goto == model.GotoRank && existsAdWeb {
  364. continue
  365. } else if r.Goto == model.GotoLogin && mid > 0 {
  366. continue
  367. } else if r.Goto == model.GotoFollowMode && !followMode {
  368. continue
  369. }
  370. is = append(is, r)
  371. }
  372. return
  373. }
  374. func (*Service) dealAdLoc(is []card.Handler, param *feed.IndexParam, adInfom map[int]*cm.AdInfo, now time.Time) {
  375. il := len(is)
  376. if il == 0 {
  377. return
  378. }
  379. if param.Idx < 1 {
  380. param.Idx = now.Unix()
  381. }
  382. for i, h := range is {
  383. if param.Pull {
  384. h.Get().Idx = param.Idx + int64(il-i)
  385. } else {
  386. h.Get().Idx = param.Idx - int64(i+1)
  387. }
  388. if ad, ok := adInfom[i]; ok {
  389. h.Get().AdInfo = ad
  390. } else if h.Get().AdInfo != nil {
  391. h.Get().AdInfo.CardIndex = i + 1
  392. }
  393. }
  394. }
  395. func (s *Service) dealItem2(c context.Context, mid int64, buvid string, plat int8, rs []*ai.Item, param *feed.IndexParam, isRcmd, noCache, followMode bool, follow *operate.Card, now time.Time) (is []card.Handler, isAI bool) {
  396. if len(rs) == 0 {
  397. is = []card.Handler{}
  398. return
  399. }
  400. var (
  401. aids, tids, roomIDs, sids, metaIDs, shopIDs, audioIDs, picIDs []int64
  402. seasonIDs []int32
  403. upIDs, avUpIDs, rmUpIDs, mtUpIDs []int64
  404. am map[int64]*archive.ArchiveWithPlayer
  405. tagm map[int64]*tag.Tag
  406. rm map[int64]*live.Room
  407. sm map[int64]*bangumi.Season
  408. hasUpdate, getBanner bool
  409. update *bangumi.Update
  410. metam map[int64]*article.Meta
  411. shopm map[int64]*show.Shopping
  412. audiom map[int64]*audio.Audio
  413. cardm map[int64]*account.Card
  414. statm map[int64]*relation.Stat
  415. moe *bangumi.Moe
  416. isAtten map[int64]int8
  417. arcOK bool
  418. rank *operate.Card
  419. seasonm map[int32]*episodegrpc.EpisodeCardsProto
  420. banners []*banner.Banner
  421. version string
  422. picm map[int64]*bplus.Picture
  423. )
  424. convergem := map[int64]*operate.Card{}
  425. followm := map[int64]*operate.Card{}
  426. downloadm := map[int64]*operate.Card{}
  427. specialm := map[int64]*operate.Card{}
  428. liveUpm := map[int64][]*live.Card{}
  429. isAI = isRcmd
  430. for _, r := range rs {
  431. if r == nil {
  432. continue
  433. }
  434. switch r.Goto {
  435. case model.GotoBanner:
  436. if len(r.Banners) != 0 {
  437. banners = r.Banners
  438. version = r.Version
  439. } else {
  440. getBanner = true
  441. }
  442. case model.GotoAv, model.GotoAdAv, model.GotoPlayer, model.GotoUpRcmdAv:
  443. if r.ID != 0 {
  444. aids = append(aids, r.ID)
  445. }
  446. if r.Tid != 0 {
  447. tids = append(tids, r.Tid)
  448. }
  449. case model.GotoLive, model.GotoPlayerLive:
  450. if r.ID != 0 {
  451. roomIDs = append(roomIDs, r.ID)
  452. }
  453. case model.GotoBangumi:
  454. if r.ID != 0 {
  455. sids = append(sids, r.ID)
  456. }
  457. case model.GotoPGC:
  458. if r.ID != 0 {
  459. seasonIDs = append(seasonIDs, int32(r.ID))
  460. }
  461. case model.GotoRank:
  462. os, aid := s.RankCard(plat)
  463. rank = &operate.Card{}
  464. rank.FromRank(os)
  465. aids = append(aids, aid...)
  466. case model.GotoBangumiRcmd:
  467. hasUpdate = true
  468. case model.GotoConverge:
  469. cardm, aid, roomID, metaID := s.convergeCard(c, 3, r.ID)
  470. for id, card := range cardm {
  471. convergem[id] = card
  472. }
  473. aids = append(aids, aid...)
  474. roomIDs = append(roomIDs, roomID...)
  475. metaIDs = append(metaIDs, metaID...)
  476. case model.GotoGameDownloadS:
  477. cardm := s.downloadCard(c, r.ID)
  478. for id, card := range cardm {
  479. downloadm[id] = card
  480. }
  481. case model.GotoArticleS:
  482. if r.ID != 0 {
  483. metaIDs = append(metaIDs, r.ID)
  484. }
  485. case model.GotoShoppingS:
  486. if r.ID != 0 {
  487. shopIDs = append(shopIDs, r.ID)
  488. }
  489. case model.GotoAudio:
  490. if r.ID != 0 {
  491. audioIDs = append(audioIDs, r.ID)
  492. }
  493. case model.GotoLiveUpRcmd:
  494. cardm, upID := s.liveUpRcmdCard(c, r.ID)
  495. for id, card := range cardm {
  496. liveUpm[id] = card
  497. }
  498. upIDs = append(upIDs, upID...)
  499. case model.GotoSubscribe:
  500. cardm, upID, tid := s.subscribeCard(c, r.ID)
  501. for id, card := range cardm {
  502. followm[id] = card
  503. }
  504. upIDs = append(upIDs, upID...)
  505. tids = append(tids, tid...)
  506. case model.GotoSearchSubscribe:
  507. if follow != nil {
  508. followm[follow.ID] = follow
  509. for _, item := range follow.Items {
  510. upIDs = append(upIDs, item.ID)
  511. }
  512. }
  513. case model.GotoChannelRcmd:
  514. cardm, aid, tid := s.channelRcmdCard(c, r.ID)
  515. for id, card := range cardm {
  516. followm[id] = card
  517. }
  518. aids = append(aids, aid...)
  519. tids = append(tids, tid...)
  520. case model.GotoSpecial, model.GotoSpecialS:
  521. cardm := s.specialCard(c, r.ID)
  522. for id, card := range cardm {
  523. specialm[id] = card
  524. }
  525. case model.GotoPicture:
  526. if r.ID != 0 {
  527. picIDs = append(picIDs, r.ID)
  528. }
  529. if r.RcmdReason != nil && r.RcmdReason.Style == 4 {
  530. upIDs = append(upIDs, r.RcmdReason.FollowedMid)
  531. }
  532. }
  533. }
  534. g, ctx := errgroup.WithContext(c)
  535. if getBanner {
  536. g.Go(func() (err error) {
  537. if banners, version, err = s.banners(ctx, plat, param.Build, mid, buvid, param.Network, param.MobiApp, param.Device, param.OpenEvent, param.AdExtra, ""); err != nil {
  538. log.Error("%+v", err)
  539. err = nil
  540. }
  541. return
  542. })
  543. }
  544. if len(aids) != 0 {
  545. g.Go(func() (err error) {
  546. if am, err = s.ArchivesWithPlayer(ctx, aids, param.Qn, param.MobiApp, param.Fnver, param.Fnval, param.ForceHost, param.Build); err != nil {
  547. return
  548. }
  549. arcOK = true
  550. for _, a := range am {
  551. avUpIDs = append(avUpIDs, a.Author.Mid)
  552. }
  553. return
  554. })
  555. }
  556. if len(tids) != 0 {
  557. g.Go(func() (err error) {
  558. if tagm, err = s.tg.InfoByIDs(ctx, mid, tids); err != nil {
  559. log.Error("%+v", err)
  560. err = nil
  561. }
  562. return
  563. })
  564. }
  565. if len(roomIDs) != 0 {
  566. g.Go(func() (err error) {
  567. if rm, err = s.lv.AppMRoom(ctx, roomIDs); err != nil {
  568. log.Error("%+v", err)
  569. err = nil
  570. }
  571. for _, r := range rm {
  572. rmUpIDs = append(rmUpIDs, r.UID)
  573. }
  574. return
  575. })
  576. }
  577. if len(sids) != 0 {
  578. g.Go(func() (err error) {
  579. if sm, err = s.bgm.Seasons(ctx, sids, now); err != nil {
  580. log.Error("%+v", err)
  581. err = nil
  582. }
  583. return
  584. })
  585. }
  586. if len(seasonIDs) != 0 {
  587. g.Go(func() (err error) {
  588. if seasonm, err = s.bgm.CardsInfoReply(ctx, seasonIDs); err != nil {
  589. log.Error("%+v", err)
  590. err = nil
  591. }
  592. return
  593. })
  594. }
  595. if hasUpdate && mid > 0 {
  596. g.Go(func() (err error) {
  597. /*
  598. {
  599. "code": 0,
  600. "message": "success",
  601. "result": {
  602. "title": "小埋。。。",
  603. "square_cover": "http://i0.hdslb.com/bfs/bangumi/dd2281c9f1c44e07c835e488ce1e1bae36f533e3.jpg",
  604. "updates": 67
  605. }
  606. }
  607. */
  608. if update, err = s.bgm.Updates(ctx, mid, now); err != nil {
  609. log.Error("%+v", err)
  610. err = nil
  611. }
  612. return
  613. })
  614. }
  615. if len(metaIDs) != 0 {
  616. g.Go(func() (err error) {
  617. if metam, err = s.art.Articles(ctx, metaIDs); err != nil {
  618. log.Error("%+v", err)
  619. err = nil
  620. }
  621. for _, meta := range metam {
  622. if meta.Author != nil {
  623. mtUpIDs = append(mtUpIDs, meta.Author.Mid)
  624. }
  625. }
  626. return
  627. })
  628. }
  629. if len(shopIDs) != 0 {
  630. g.Go(func() (err error) {
  631. if shopm, err = s.show.Card(ctx, shopIDs); err != nil {
  632. log.Error("%+v", err)
  633. err = nil
  634. }
  635. return
  636. })
  637. }
  638. if len(audioIDs) != 0 {
  639. g.Go(func() (err error) {
  640. if audiom, err = s.audio.Audios(ctx, audioIDs); err != nil {
  641. log.Error("%+v", err)
  642. err = nil
  643. }
  644. return
  645. })
  646. }
  647. if len(picIDs) != 0 {
  648. g.Go(func() (err error) {
  649. if picm, err = s.bplus.DynamicDetail(ctx, picIDs...); err != nil {
  650. log.Error("%+v", err)
  651. err = nil
  652. }
  653. return
  654. })
  655. }
  656. // 萌战下线
  657. // if mid > 0 {
  658. // g.Go(func() (err error) {
  659. // if moe, err = s.bgm.FollowPull(ctx, mid, mobiApp, device, now); err != nil {
  660. // log.Error("%+v", err)
  661. // err = nil
  662. // }
  663. // return
  664. // })
  665. // }
  666. if err := g.Wait(); err != nil {
  667. log.Error("%+v", err)
  668. if noCache {
  669. is = []card.Handler{}
  670. return
  671. }
  672. if isRcmd {
  673. count := s.indexCount(plat)
  674. rs = s.recommendCache(count)
  675. }
  676. } else {
  677. upIDs = append(upIDs, avUpIDs...)
  678. upIDs = append(upIDs, rmUpIDs...)
  679. upIDs = append(upIDs, mtUpIDs...)
  680. g, ctx = errgroup.WithContext(c)
  681. if len(upIDs) != 0 {
  682. g.Go(func() (err error) {
  683. if cardm, err = s.acc.Cards3(ctx, upIDs); err != nil {
  684. log.Error("%+v", err)
  685. err = nil
  686. }
  687. return
  688. })
  689. g.Go(func() (err error) {
  690. if statm, err = s.rel.Stats(ctx, upIDs); err != nil {
  691. log.Error("%+v", err)
  692. err = nil
  693. }
  694. return
  695. })
  696. if mid > 0 && param.RecsysMode == 0 {
  697. g.Go(func() error {
  698. isAtten = s.acc.IsAttention(ctx, upIDs, mid)
  699. return nil
  700. })
  701. }
  702. }
  703. g.Wait()
  704. }
  705. isAI = isAI && arcOK
  706. if moe != nil {
  707. moePos := s.c.Feed.Index.MoePosition
  708. if moePos-1 >= 0 && moePos-1 <= len(rs) {
  709. rs = append(rs[:moePos-1], append([]*ai.Item{&ai.Item{ID: moe.ID, Goto: model.GotoMoe}}, rs[moePos-1:]...)...)
  710. }
  711. }
  712. var cardTotal int
  713. is = make([]card.Handler, 0, len(rs))
  714. insert := map[int]card.Handler{}
  715. for _, r := range rs {
  716. if r == nil {
  717. continue
  718. }
  719. var (
  720. main interface{}
  721. cardType cdm.CardType
  722. )
  723. op := &operate.Card{}
  724. op.From(cdm.CardGt(r.Goto), r.ID, r.Tid, plat, param.Build)
  725. // 卡片展示点赞数实验
  726. // if mid%20 == 11 && ((plat == model.PlatIPhone && param.Build >= 8290) || (plat == model.PlatAndroid && param.Build >= 5360000)) {
  727. // op.FromSwitch(cdm.SwitchFeedIndexLike)
  728. // }
  729. // 变化卡片类型
  730. switch r.Goto {
  731. case model.GotoSpecialS, model.GotoGameDownloadS, model.GotoShoppingS:
  732. if r.Style == 2 {
  733. cardType = cdm.LargeCoverV1
  734. }
  735. case model.GotoPicture:
  736. if p, ok := picm[r.ID]; ok {
  737. switch cdm.Columnm[param.Column] {
  738. case cdm.ColumnSvrSingle:
  739. if len(p.Imgs) < 3 {
  740. cardType = cdm.OnePicV1
  741. } else {
  742. cardType = cdm.ThreePicV1
  743. }
  744. case cdm.ColumnSvrDouble:
  745. if len(p.Imgs) < 3 {
  746. // 版本过滤5.37为新卡片
  747. if (plat == model.PlatIPhone && param.Build > 8300) || (plat == model.PlatAndroid && param.Build > 5365000) {
  748. cardType = cdm.OnePicV2
  749. } else {
  750. cardType = cdm.SmallCoverV2
  751. }
  752. } else {
  753. cardType = cdm.ThreePicV2
  754. }
  755. default:
  756. continue
  757. }
  758. } else {
  759. continue
  760. }
  761. case model.GotoInterest:
  762. switch cdm.Columnm[param.Column] {
  763. case cdm.ColumnSvrSingle:
  764. cardType = cdm.OptionsV1
  765. case cdm.ColumnSvrDouble:
  766. cardType = cdm.OptionsV2
  767. default:
  768. continue
  769. }
  770. case model.GotoFollowMode:
  771. cardType = cdm.Select
  772. default:
  773. }
  774. h := card.Handle(plat, cdm.CardGt(r.Goto), cardType, param.Column, r, tagm, isAtten, statm, cardm)
  775. if h == nil {
  776. continue
  777. }
  778. switch r.Goto {
  779. case model.GotoAv, model.GotoUpRcmdAv, model.GotoPlayer:
  780. if !arcOK {
  781. if r.Archive != nil {
  782. am = map[int64]*archive.ArchiveWithPlayer{r.Archive.Aid: &archive.ArchiveWithPlayer{Archive3: r.Archive}}
  783. }
  784. if r.Tag != nil {
  785. tagm = map[int64]*tag.Tag{r.Tag.ID: r.Tag}
  786. op.Tid = r.Tag.ID
  787. }
  788. }
  789. if a, ok := am[r.ID]; ok && (a.AttrVal(archive.AttrBitOverseaLock) == 0 || !model.IsOverseas(plat)) {
  790. main = am
  791. op.TrackID = r.TrackID
  792. }
  793. if plat == model.PlatIPhone && param.Build > 8290 || plat == model.PlatAndroid && param.Build > 5365000 {
  794. op.Switch = cdm.SwitchCooperationShow
  795. } else {
  796. op.Switch = cdm.SwitchCooperationHide
  797. }
  798. case model.GotoLive, model.GotoPlayerLive:
  799. main = rm
  800. case model.GotoBangumi:
  801. main = sm
  802. case model.GotoPGC:
  803. main = seasonm
  804. case model.GotoLogin:
  805. op.FromLogin(r.ID)
  806. case model.GotoSpecial, model.GotoSpecialS:
  807. op = specialm[r.ID]
  808. case model.GotoRank:
  809. main = map[cdm.Gt]interface{}{cdm.GotoAv: am}
  810. op = rank
  811. case model.GotoBangumiRcmd:
  812. main = update
  813. case model.GotoBanner:
  814. op.FromBanner(banners, version)
  815. case model.GotoConverge:
  816. main = map[cdm.Gt]interface{}{cdm.GotoAv: am, cdm.GotoLive: rm, cdm.GotoArticle: metam}
  817. op = convergem[r.ID]
  818. case model.GotoGameDownloadS:
  819. op = downloadm[r.ID]
  820. case model.GotoArticleS:
  821. main = metam
  822. case model.GotoShoppingS:
  823. main = shopm
  824. case model.GotoAudio:
  825. main = audiom
  826. case model.GotoChannelRcmd:
  827. main = am
  828. op = followm[r.ID]
  829. case model.GotoSubscribe, model.GotoSearchSubscribe:
  830. op = followm[r.ID]
  831. case model.GotoLiveUpRcmd:
  832. main = liveUpm
  833. case model.GotoMoe:
  834. main = moe
  835. case model.GotoPicture:
  836. main = picm
  837. case model.GotoAdAv:
  838. main = am
  839. op.FromAdAv(r.Ad)
  840. case model.GotoAdWebS, model.GotoAdWeb:
  841. main = r.Ad
  842. case model.GotoInterest:
  843. main = s.c.Feed.Index.Interest
  844. case model.GotoFollowMode:
  845. var (
  846. title string
  847. desc string
  848. button []string
  849. )
  850. if s.c.Feed.Index.FollowMode != nil && s.c.Feed.Index.FollowMode.Card != nil {
  851. title = s.c.Feed.Index.FollowMode.Card.Title
  852. desc = s.c.Feed.Index.FollowMode.Card.Desc
  853. button = s.c.Feed.Index.FollowMode.Card.Button
  854. }
  855. op.FromFollowMode(title, desc, button)
  856. default:
  857. log.Warn("unexpected goto(%s) %+v", r.Goto, r)
  858. continue
  859. }
  860. if op != nil {
  861. op.Plat = plat
  862. op.Build = param.Build
  863. }
  864. h.From(main, op)
  865. // 卡片不正常要continue
  866. if !h.Get().Right {
  867. continue
  868. }
  869. switch r.Goto {
  870. case model.GotoAdAv, model.GotoAdWebS, model.GotoAdWeb:
  871. // 判断结果列表长度,如果列表的末尾不是广告位,则放到插入队列里
  872. if len(is) != r.Ad.CardIndex-1 {
  873. insert[r.Ad.CardIndex-1] = h
  874. // 插入队列后一定要continue,否则就直接加到队列末尾了
  875. continue
  876. }
  877. }
  878. is, cardTotal = s.appendItem(plat, is, h, param.Column, cardTotal)
  879. // 从插入队列里获取广告
  880. if h, ok := insert[len(is)]; ok {
  881. is, cardTotal = s.appendItem(plat, is, h, param.Column, cardTotal)
  882. }
  883. }
  884. // 双列末尾卡片去空窗
  885. if !model.IsIPad(plat) {
  886. if cdm.Columnm[param.Column] == cdm.ColumnSvrDouble {
  887. is = is[:len(is)-cardTotal%2]
  888. }
  889. } else {
  890. // 复杂的ipad去空窗逻辑
  891. if cardTotal%4 == 3 {
  892. if is[len(is)-2].Get().CardLen == 2 {
  893. is = is[:len(is)-2]
  894. } else {
  895. is = is[:len(is)-3]
  896. }
  897. } else if cardTotal%4 == 2 {
  898. if is[len(is)-1].Get().CardLen == 2 {
  899. is = is[:len(is)-1]
  900. } else {
  901. is = is[:len(is)-2]
  902. }
  903. } else if cardTotal%4 == 1 {
  904. is = is[:len(is)-1]
  905. }
  906. }
  907. if len(is) == 0 {
  908. is = []card.Handler{}
  909. return
  910. }
  911. return
  912. }
  913. func (s *Service) appendItem(plat int8, rs []card.Handler, h card.Handler, column cdm.ColumnStatus, cardTotal int) (is []card.Handler, total int) {
  914. h.Get().ThreePointFrom()
  915. if !model.IsIPad(plat) {
  916. // 双列大小卡换位去空窗
  917. if cdm.Columnm[column] == cdm.ColumnSvrDouble {
  918. // 通栏卡
  919. if h.Get().CardLen == 0 {
  920. if cardTotal%2 == 1 {
  921. is = card.SwapTwoItem(rs, h)
  922. } else {
  923. is = append(rs, h)
  924. }
  925. } else {
  926. is = append(rs, h)
  927. }
  928. } else {
  929. is = append(rs, h)
  930. }
  931. } else {
  932. // ipad卡片不展示标签
  933. h.Get().DescButton = nil
  934. // ipad大小卡换位去空窗
  935. if h.Get().CardLen == 0 {
  936. // 通栏卡
  937. if cardTotal%4 == 3 {
  938. is = card.SwapFourItem(rs, h)
  939. } else if cardTotal%4 == 2 {
  940. is = card.SwapThreeItem(rs, h)
  941. } else if cardTotal%4 == 1 {
  942. is = card.SwapTwoItem(rs, h)
  943. } else {
  944. is = append(rs, h)
  945. }
  946. } else if h.Get().CardLen == 2 {
  947. // 半栏卡
  948. if cardTotal%4 == 3 {
  949. is = card.SwapTwoItem(rs, h)
  950. } else if cardTotal%4 == 2 {
  951. is = append(rs, h)
  952. } else if cardTotal%4 == 1 {
  953. is = card.SwapTwoItem(rs, h)
  954. } else {
  955. is = append(rs, h)
  956. }
  957. } else {
  958. is = append(rs, h)
  959. }
  960. }
  961. total = cardTotal + h.Get().CardLen
  962. return
  963. }
  964. func (s *Service) Converge(c context.Context, mid int64, plat int8, param *feed.ConvergeParam, now time.Time) (is []card.Handler, converge *operate.Card, err error) {
  965. cardm, _, _, _ := s.convergeCard(c, 0, param.ID)
  966. converge, ok := cardm[param.ID]
  967. if !ok {
  968. is = []card.Handler{}
  969. return
  970. }
  971. rs := make([]*ai.Item, 0, len(converge.Items))
  972. for _, item := range converge.Items {
  973. rs = append(rs, &ai.Item{ID: item.ID, Goto: string(item.CardGoto)})
  974. }
  975. indexParam := &feed.IndexParam{
  976. MobiApp: param.MobiApp,
  977. Device: param.Device,
  978. Build: param.Build,
  979. Qn: param.Qn,
  980. Fnver: param.Fnver,
  981. Fnval: param.Fnval,
  982. ForceHost: param.ForceHost,
  983. }
  984. is, _ = s.dealItem2(c, mid, "", plat, rs, indexParam, false, true, false, nil, now)
  985. for _, item := range is {
  986. // 运营tab页没有不感兴趣
  987. item.Get().ThreePointWatchLater()
  988. }
  989. return
  990. }