index.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. package feed
  2. import (
  3. "context"
  4. "encoding/json"
  5. "time"
  6. cdm "go-common/app/interface/main/app-card/model"
  7. "go-common/app/interface/main/app-card/model/card"
  8. "go-common/app/interface/main/app-card/model/card/ai"
  9. "go-common/app/interface/main/app-card/model/card/banner"
  10. "go-common/app/interface/main/app-card/model/card/cm"
  11. "go-common/app/interface/main/app-card/model/card/operate"
  12. "go-common/app/interface/main/app-intl/model"
  13. "go-common/app/interface/main/app-intl/model/feed"
  14. tag "go-common/app/interface/main/tag/model"
  15. account "go-common/app/service/main/account/model"
  16. "go-common/app/service/main/archive/model/archive"
  17. locmdl "go-common/app/service/main/location/model"
  18. relation "go-common/app/service/main/relation/model"
  19. "go-common/library/ecode"
  20. "go-common/library/log"
  21. "go-common/library/net/metadata"
  22. "go-common/library/sync/errgroup"
  23. "go-common/library/text/translate/chinese"
  24. )
  25. // Index is.
  26. func (s *Service) Index(c context.Context, buvid string, mid int64, plat int8, param *feed.IndexParam, now time.Time, style int) (is []card.Handler, userFeature json.RawMessage, isRcmd, newUser bool, code int, autoPlay, clean int8, autoPlayInfoc string, err error) {
  27. var (
  28. rs []*ai.Item
  29. adm map[int]*cm.AdInfo
  30. adAidm map[int64]struct{}
  31. banners []*banner.Banner
  32. version string
  33. blackAidm map[int64]struct{}
  34. adInfom map[int]*cm.AdInfo
  35. follow *operate.Card
  36. ip = metadata.String(c, metadata.RemoteIP)
  37. info *locmdl.Info
  38. isTW = model.TWLocale(param.Locale)
  39. )
  40. // 国际版不做abtest
  41. clean = 0
  42. autoPlay = 2
  43. group := s.group(mid, buvid)
  44. if info, err = s.loc.Info(c, ip); err != nil {
  45. log.Warn("s.loc.Info(%v) error(%v)", ip, err)
  46. err = nil
  47. }
  48. if !s.c.Feed.Index.Abnormal {
  49. g, ctx := errgroup.WithContext(c)
  50. g.Go(func() error {
  51. rs, userFeature, isRcmd, newUser, code = s.indexRcmd(ctx, plat, param.Build, buvid, mid, group, param.LoginEvent, info, param.Interest, param.Network, style, param.Column, param.Flush, autoPlayInfoc, now)
  52. if isTW {
  53. for _, r := range rs {
  54. if r.RcmdReason != nil {
  55. r.RcmdReason.Content = chinese.Convert(ctx, r.RcmdReason.Content)
  56. }
  57. }
  58. }
  59. return nil
  60. })
  61. g.Go(func() (err error) {
  62. if blackAidm, err = s.BlackList(ctx, mid); err != nil {
  63. log.Error("%+v", err)
  64. err = nil
  65. }
  66. return
  67. })
  68. if err = g.Wait(); err != nil {
  69. return
  70. }
  71. rs, adInfom = s.mergeItem(c, mid, rs, adm, adAidm, banners, version, blackAidm, plat, follow)
  72. } else {
  73. count := s.indexCount(plat)
  74. rs = s.recommendCache(count)
  75. log.Warn("feed index show disaster recovery data len(%d)", len(is))
  76. }
  77. is, isRcmd, err = s.dealItem(c, param.Column, mid, buvid, plat, param.Build, rs, isRcmd, param.MobiApp, param.Device, param.Network, param.OpenEvent, param.AdExtra, param.Qn, param.Fnver, param.Fnval, follow, isTW, now)
  78. s.dealAdLoc(is, param, adInfom, now)
  79. return
  80. }
  81. // indexRcmd is.
  82. func (s *Service) indexRcmd(c context.Context, plat int8, build int, buvid string, mid int64, group int, loginEvent int, info *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) {
  83. count := s.indexCount(plat)
  84. if buvid != "" || mid != 0 {
  85. var (
  86. err error
  87. zoneID int64
  88. )
  89. if info != nil {
  90. zoneID = info.ZoneID
  91. }
  92. if is, userFeature, code, newUser, err = s.rcmd.Recommend(c, plat, buvid, mid, build, loginEvent, zoneID, group, interest, network, style, column, flush, autoPlay, now); err != nil {
  93. log.Error("%+v", err)
  94. } else if len(is) != 0 {
  95. isRcmd = true
  96. }
  97. var fromCache bool
  98. if len(is) == 0 && mid != 0 && !ecode.ServiceUnavailable.Equal(err) {
  99. if is, err = s.indexCache(c, mid, count); err != nil {
  100. log.Error("%+v", err)
  101. }
  102. if len(is) != 0 {
  103. s.pHit.Incr("index_cache")
  104. } else {
  105. s.pMiss.Incr("index_cache")
  106. }
  107. fromCache = true
  108. }
  109. if len(is) == 0 || (fromCache && len(is) < count) {
  110. is = s.recommendCache(count)
  111. }
  112. } else {
  113. is = s.recommendCache(count)
  114. }
  115. return
  116. }
  117. // mergeItem is.
  118. func (s *Service) mergeItem(c context.Context, mid int64, rs []*ai.Item, adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, banners []*banner.Banner, version string, blackAids map[int64]struct{}, plat int8, follow *operate.Card) (is []*ai.Item, adInfom map[int]*cm.AdInfo) {
  119. if len(rs) == 0 {
  120. return
  121. }
  122. if len(banners) != 0 {
  123. rs = append([]*ai.Item{{Goto: model.GotoBanner, Banners: banners, Version: version}}, rs...)
  124. }
  125. is = make([]*ai.Item, 0, len(rs)+len(adm))
  126. adInfom = make(map[int]*cm.AdInfo, len(adm))
  127. for _, r := range rs {
  128. if r.Goto == model.GotoAv {
  129. if _, ok := blackAids[r.ID]; ok {
  130. continue
  131. } else if _, ok := s.blackCache[r.ID]; ok {
  132. continue
  133. }
  134. if _, ok := adAidm[r.ID]; ok {
  135. continue
  136. }
  137. } else if r.Goto == model.GotoBanner && len(is) != 0 {
  138. // banner 必须在第一位
  139. continue
  140. } else if r.Goto == model.GotoLogin && mid != 0 {
  141. continue
  142. }
  143. is = append(is, r)
  144. }
  145. return
  146. }
  147. // dealAdLoc is.
  148. func (*Service) dealAdLoc(is []card.Handler, param *feed.IndexParam, adInfom map[int]*cm.AdInfo, now time.Time) {
  149. il := len(is)
  150. if il == 0 {
  151. return
  152. }
  153. if param.Idx < 1 {
  154. param.Idx = now.Unix()
  155. }
  156. for i, h := range is {
  157. if param.Pull {
  158. h.Get().Idx = param.Idx + int64(il-i)
  159. } else {
  160. h.Get().Idx = param.Idx - int64(i+1)
  161. }
  162. if ad, ok := adInfom[i]; ok {
  163. h.Get().AdInfo = ad
  164. } else if h.Get().AdInfo != nil {
  165. h.Get().AdInfo.CardIndex = i
  166. }
  167. }
  168. }
  169. // dealItem is.
  170. func (s *Service) dealItem(c context.Context, column cdm.ColumnStatus, mid int64, buvid string, plat int8, build int, rs []*ai.Item, isRcmd bool, mobiApp, device, network, openEvent, adExtra string, qn, fnver, fnval int, follow *operate.Card, isTW bool, now time.Time) (is []card.Handler, isAI bool, err error) {
  171. if len(rs) == 0 {
  172. is = []card.Handler{}
  173. return
  174. }
  175. var (
  176. aids, tids []int64
  177. upIDs, avUpIDs, rmUpIDs, mtUpIDs []int64
  178. am map[int64]*archive.ArchiveWithPlayer
  179. tagm map[int64]*tag.Tag
  180. rank *operate.Card
  181. cardm map[int64]*account.Card
  182. statm map[int64]*relation.Stat
  183. isAtten map[int64]int8
  184. arcOK bool
  185. )
  186. followm := map[int64]*operate.Card{}
  187. isAI = isRcmd
  188. for _, r := range rs {
  189. if r == nil {
  190. continue
  191. }
  192. if isTW && r.RcmdReason != nil {
  193. r.RcmdReason.Content = chinese.Convert(c, r.RcmdReason.Content)
  194. }
  195. switch r.Goto {
  196. case model.GotoAv, model.GotoPlayer, model.GotoUpRcmdAv:
  197. if r.ID != 0 {
  198. aids = append(aids, r.ID)
  199. }
  200. if r.Tid != 0 {
  201. tids = append(tids, r.Tid)
  202. }
  203. case model.GotoRank:
  204. os, aid := s.RankCard(plat)
  205. rank = &operate.Card{}
  206. rank.FromRank(os)
  207. aids = append(aids, aid...)
  208. case model.GotoChannelRcmd:
  209. cardm, aid, tid := s.channelRcmdCard(c, r.ID)
  210. for id, card := range cardm {
  211. followm[id] = card
  212. }
  213. aids = append(aids, aid...)
  214. tids = append(tids, tid...)
  215. }
  216. }
  217. g, ctx := errgroup.WithContext(c)
  218. if len(aids) != 0 {
  219. g.Go(func() (err error) {
  220. if am, err = s.ArchivesWithPlayer(ctx, aids, qn, mobiApp, fnver, fnval); err != nil {
  221. return
  222. }
  223. arcOK = true
  224. for _, a := range am {
  225. avUpIDs = append(avUpIDs, a.Author.Mid)
  226. if isTW {
  227. out := chinese.Converts(ctx, a.Title, a.Desc, a.TypeName)
  228. a.Title = out[a.Title]
  229. a.Desc = out[a.Desc]
  230. a.TypeName = out[a.TypeName]
  231. }
  232. }
  233. return
  234. })
  235. }
  236. if len(tids) != 0 {
  237. g.Go(func() (err error) {
  238. if tagm, err = s.tg.InfoByIDs(ctx, mid, tids); err != nil {
  239. log.Error("%+v", err)
  240. err = nil
  241. }
  242. return
  243. })
  244. }
  245. if err = g.Wait(); err != nil {
  246. log.Error("%+v", err)
  247. if isRcmd {
  248. count := s.indexCount(plat)
  249. rs = s.recommendCache(count)
  250. }
  251. } else {
  252. upIDs = append(upIDs, avUpIDs...)
  253. upIDs = append(upIDs, rmUpIDs...)
  254. upIDs = append(upIDs, mtUpIDs...)
  255. g, ctx = errgroup.WithContext(c)
  256. if len(upIDs) != 0 {
  257. g.Go(func() (err error) {
  258. if cardm, err = s.acc.Cards3(ctx, upIDs); err != nil {
  259. log.Error("%+v", err)
  260. err = nil
  261. }
  262. return
  263. })
  264. g.Go(func() (err error) {
  265. if statm, err = s.rel.Stats(ctx, upIDs); err != nil {
  266. log.Error("%+v", err)
  267. err = nil
  268. }
  269. return
  270. })
  271. if mid != 0 {
  272. g.Go(func() error {
  273. isAtten = s.acc.IsAttention(ctx, upIDs, mid)
  274. return nil
  275. })
  276. }
  277. }
  278. g.Wait()
  279. }
  280. isAI = isAI && arcOK
  281. var cardTotal int
  282. is = make([]card.Handler, 0, len(rs))
  283. for _, r := range rs {
  284. if r == nil {
  285. continue
  286. }
  287. var (
  288. main interface{}
  289. cardType cdm.CardType
  290. )
  291. op := &operate.Card{}
  292. op.From(cdm.CardGt(r.Goto), r.ID, r.Tid, plat, build)
  293. h := card.Handle(plat, cdm.CardGt(r.Goto), cardType, column, r, tagm, isAtten, statm, cardm)
  294. if h == nil {
  295. continue
  296. }
  297. switch r.Goto {
  298. case model.GotoAv, model.GotoPlayer, model.GotoUpRcmdAv:
  299. if !arcOK {
  300. if r.Archive != nil {
  301. if isTW {
  302. out := chinese.Converts(c, r.Archive.Title, r.Archive.Desc, r.Archive.TypeName, r.Archive.Author.Name)
  303. r.Archive.Title = out[r.Archive.Title]
  304. r.Archive.Desc = out[r.Archive.Desc]
  305. r.Archive.TypeName = out[r.Archive.TypeName]
  306. }
  307. am = map[int64]*archive.ArchiveWithPlayer{r.Archive.Aid: {Archive3: r.Archive}}
  308. }
  309. if r.Tag != nil {
  310. tagm = map[int64]*tag.Tag{r.Tag.ID: r.Tag}
  311. op.Tid = r.Tag.ID
  312. }
  313. }
  314. if a, ok := am[r.ID]; ok && (a.AttrVal(archive.AttrBitOverseaLock) == 0 || !model.IsOverseas(plat)) {
  315. main = am
  316. op.TrackID = r.TrackID
  317. }
  318. case model.GotoRank:
  319. main = map[cdm.Gt]interface{}{cdm.GotoAv: am}
  320. op = rank
  321. case model.GotoChannelRcmd:
  322. main = am
  323. case model.GotoLogin:
  324. op.FromLogin(r.ID)
  325. default:
  326. log.Warn("unexpected goto(%s) %+v", r.Goto, r)
  327. continue
  328. }
  329. h.From(main, op)
  330. // 卡片不正常要continue
  331. if !h.Get().Right {
  332. continue
  333. }
  334. is, cardTotal = s.appendItem(plat, is, h, column, cardTotal)
  335. }
  336. // 双列末尾卡片去空窗
  337. if !model.IsIPad(plat) {
  338. if cdm.Columnm[column] == cdm.ColumnSvrDouble {
  339. is = is[:len(is)-cardTotal%2]
  340. }
  341. } else {
  342. // 复杂的ipad去空窗逻辑
  343. if cardTotal%4 == 3 {
  344. if is[len(is)-2].Get().CardLen == 2 {
  345. is = is[:len(is)-2]
  346. } else {
  347. is = is[:len(is)-3]
  348. }
  349. } else if cardTotal%4 == 2 {
  350. if is[len(is)-1].Get().CardLen == 2 {
  351. is = is[:len(is)-1]
  352. } else {
  353. is = is[:len(is)-2]
  354. }
  355. } else if cardTotal%4 == 1 {
  356. is = is[:len(is)-1]
  357. }
  358. }
  359. if len(is) == 0 {
  360. is = []card.Handler{}
  361. return
  362. }
  363. return
  364. }
  365. // appendItem is.
  366. func (s *Service) appendItem(plat int8, rs []card.Handler, h card.Handler, column cdm.ColumnStatus, cardTotal int) (is []card.Handler, total int) {
  367. h.Get().ThreePointFrom()
  368. // 国际版暂不支持稿件反馈
  369. if h.Get().ThreePoint != nil {
  370. h.Get().ThreePoint.Feedbacks = nil
  371. }
  372. if !model.IsIPad(plat) {
  373. // 双列大小卡换位去空窗
  374. if cdm.Columnm[column] == cdm.ColumnSvrDouble {
  375. // 通栏卡
  376. if h.Get().CardLen == 0 {
  377. if cardTotal%2 == 1 {
  378. is = card.SwapTwoItem(rs, h)
  379. } else {
  380. is = append(rs, h)
  381. }
  382. } else {
  383. is = append(rs, h)
  384. }
  385. } else {
  386. is = append(rs, h)
  387. }
  388. } else {
  389. // ipad卡片不展示标签
  390. h.Get().DescButton = nil
  391. // ipad大小卡换位去空窗
  392. if h.Get().CardLen == 0 {
  393. // 通栏卡
  394. if cardTotal%4 == 3 {
  395. is = card.SwapFourItem(rs, h)
  396. } else if cardTotal%4 == 2 {
  397. is = card.SwapThreeItem(rs, h)
  398. } else if cardTotal%4 == 1 {
  399. is = card.SwapTwoItem(rs, h)
  400. } else {
  401. is = append(rs, h)
  402. }
  403. } else if h.Get().CardLen == 2 {
  404. // 半栏卡
  405. if cardTotal%4 == 3 {
  406. is = card.SwapTwoItem(rs, h)
  407. } else if cardTotal%4 == 2 {
  408. is = append(rs, h)
  409. } else if cardTotal%4 == 1 {
  410. is = card.SwapTwoItem(rs, h)
  411. } else {
  412. is = append(rs, h)
  413. }
  414. } else {
  415. is = append(rs, h)
  416. }
  417. }
  418. total = cardTotal + h.Get().CardLen
  419. return
  420. }
  421. // indexCount is.
  422. func (s *Service) indexCount(plat int8) (count int) {
  423. if plat == model.PlatIPad {
  424. count = s.c.Feed.Index.IPadCount
  425. } else {
  426. count = s.c.Feed.Index.Count
  427. }
  428. return
  429. }