upper.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. package feed
  2. import (
  3. "context"
  4. "strconv"
  5. "time"
  6. "go-common/app/interface/main/app-feed/model"
  7. "go-common/app/interface/main/app-feed/model/feed"
  8. "go-common/app/interface/main/app-feed/model/live"
  9. "go-common/app/interface/main/app-feed/model/tag"
  10. article "go-common/app/interface/openplatform/article/model"
  11. "go-common/app/service/main/archive/api"
  12. "go-common/app/service/main/archive/model/archive"
  13. busfeed "go-common/app/service/main/feed/model"
  14. "go-common/library/log"
  15. "go-common/library/sync/errgroup"
  16. )
  17. const (
  18. _iosBanBangumi = 4310
  19. _androidBanBangumi = 502000
  20. _androidIBanBangumi = 104000
  21. )
  22. func (s *Service) Upper(c context.Context, mid int64, plat int8, build int, pn, ps int, now time.Time) (is []*feed.Item, lp bool) {
  23. if (plat == model.PlatIPhone && build > _iosBanBangumi) || (plat == model.PlatAndroid && build > _androidBanBangumi) || (plat == model.PlatAndroidI && build >= _androidIBanBangumi) || plat == model.PlatIPhoneB {
  24. is, lp = s.UpperFeed(c, mid, plat, build, pn, ps, now)
  25. } else {
  26. is, lp = s.UpperArchive(c, mid, plat, build, pn, ps, now)
  27. }
  28. return
  29. }
  30. // UpperFeed get the archives and bangumi for feed
  31. // if archives are less then `_minTotalCnt` then will fill with recommended archives
  32. func (s *Service) UpperFeed(c context.Context, mid int64, plat int8, build int, pn, ps int, now time.Time) (is []*feed.Item, lp bool) {
  33. var (
  34. err error
  35. hc bool
  36. uis, fis []*busfeed.Feed
  37. fp = pn == 1
  38. )
  39. if fp {
  40. var (
  41. unread int
  42. count = ps
  43. )
  44. if unread, err = s.upper.UnreadCountCache(c, mid); err != nil {
  45. log.Error("%+v", err)
  46. } else if unread != 0 {
  47. count = s.c.Feed.FeedCacheCount
  48. }
  49. if uis, err = s.upper.Feed(c, mid, pn, count); err != nil {
  50. log.Error("%+v", err)
  51. // get cache from redis
  52. if hc, err = s.upper.ExpireUpItem(c, mid); err != nil {
  53. log.Error("%+v", err)
  54. } else if hc {
  55. if uis, err = s.upperCache(c, mid, plat, build, pn, ps, now); err != nil {
  56. log.Error("%+v", err)
  57. }
  58. }
  59. if len(uis) == 0 {
  60. is = _emptyItem
  61. lp = true
  62. return
  63. }
  64. } else if unread != 0 {
  65. s.addCache(func() {
  66. s.upper.AddUpItemCaches(context.Background(), mid, uis...)
  67. })
  68. }
  69. } else {
  70. if uis, err = s.upper.Feed(c, mid, pn, ps); err != nil {
  71. log.Error("%+v", err)
  72. // get cache from redis
  73. if hc, err = s.upper.ExpireUpItem(c, mid); err != nil {
  74. log.Error("%+v", err)
  75. } else if hc {
  76. if uis, err = s.upperCache(c, mid, plat, build, pn, ps, now); err != nil {
  77. log.Error("%+v", err)
  78. }
  79. }
  80. if len(uis) == 0 {
  81. is = _emptyItem
  82. lp = true
  83. return
  84. }
  85. }
  86. }
  87. // handle feed
  88. if len(uis) > ps {
  89. fis = uis[:ps]
  90. } else {
  91. fis = uis
  92. }
  93. is = s.upperItem(c, fis, mid, now)
  94. if len(is) < ps {
  95. lp = true
  96. }
  97. if len(is) == 0 {
  98. is = _emptyItem
  99. }
  100. return
  101. }
  102. func (s *Service) upperCache(c context.Context, mid int64, plat int8, build, pn, ps int, now time.Time) (uis []*busfeed.Feed, err error) {
  103. var (
  104. start = (pn - 1) * ps
  105. end = start + ps // from slice, end no -1
  106. aids []int64
  107. am map[int64]*api.Arc
  108. seasonIDs []int64
  109. psm map[int64]*busfeed.Bangumi
  110. )
  111. if uis, aids, seasonIDs, err = s.upper.UpItemCaches(c, mid, start, end); err != nil {
  112. log.Error("%+v", err)
  113. return
  114. }
  115. if len(aids) > 0 {
  116. if am, err = s.arc.Archives(c, aids); err != nil {
  117. log.Error("%+v", err)
  118. return
  119. }
  120. }
  121. if len(seasonIDs) > 0 && ((plat == model.PlatIPhone && build > _iosBanBangumi) || (plat == model.PlatAndroid && build > _androidBanBangumi) || plat == model.PlatIPhoneB) {
  122. if psm, err = s.bgm.PullSeasons(c, seasonIDs, now); err != nil {
  123. log.Error("%+v", err)
  124. }
  125. }
  126. for _, ui := range uis {
  127. switch ui.Type {
  128. case busfeed.ArchiveType:
  129. if a, ok := am[ui.ID]; ok {
  130. ui.Archive = a
  131. }
  132. for _, r := range ui.Fold {
  133. if a, ok := am[r.Aid]; ok {
  134. r = a
  135. } else {
  136. r = nil
  137. }
  138. }
  139. case busfeed.BangumiType:
  140. if s, ok := psm[ui.ID]; ok {
  141. ui.Bangumi = s
  142. }
  143. }
  144. }
  145. return
  146. }
  147. func (s *Service) UpperArchive(c context.Context, mid int64, plat int8, build int, pn, ps int, now time.Time) (is []*feed.Item, lp bool) {
  148. var (
  149. err error
  150. uis []*busfeed.Feed
  151. hc bool
  152. )
  153. if uis, err = s.upper.ArchiveFeed(c, mid, pn, ps); err != nil {
  154. log.Error("%+v", err)
  155. // get cache from redis
  156. if hc, err = s.upper.ExpireUpItem(c, mid); err != nil {
  157. log.Error("%+v", err)
  158. } else if hc {
  159. if uis, err = s.upperCache(c, mid, plat, build, pn, ps, now); err != nil {
  160. log.Error("%+v", err)
  161. }
  162. }
  163. if len(uis) == 0 {
  164. is = _emptyItem
  165. lp = true
  166. return
  167. }
  168. }
  169. // handle feed
  170. is = s.upperItem(c, uis, mid, now)
  171. if len(is) < ps {
  172. lp = true
  173. }
  174. if len(is) == 0 {
  175. is = _emptyItem
  176. }
  177. return
  178. }
  179. func (s *Service) UpperBangumi(c context.Context, mid int64, plat int8, build int, pn, ps int, now time.Time) (is []*feed.Item, lp bool) {
  180. var (
  181. err error
  182. uis []*busfeed.Feed
  183. hc bool
  184. )
  185. if uis, err = s.upper.BangumiFeed(c, mid, pn, ps); err != nil {
  186. log.Error("%+v", err)
  187. // get cache from redis
  188. if hc, err = s.upper.ExpireUpItem(c, mid); err != nil {
  189. log.Error("%+v", err)
  190. } else if hc {
  191. if uis, err = s.upperCache(c, mid, plat, build, pn, ps, now); err != nil {
  192. log.Error("%+v", err)
  193. }
  194. }
  195. if len(uis) == 0 {
  196. is = _emptyItem
  197. lp = true
  198. return
  199. }
  200. }
  201. // handle feed
  202. is = s.upperItem(c, uis, mid, now)
  203. if len(is) < ps {
  204. lp = true
  205. }
  206. if len(is) == 0 {
  207. is = _emptyItem
  208. }
  209. return
  210. }
  211. func (s *Service) UpperRecent(c context.Context, mid, upperID, aid int64, now time.Time) (is []*feed.Item) {
  212. var (
  213. err error
  214. uis []*busfeed.Feed
  215. )
  216. if uis, err = s.upper.Recent(c, upperID, aid); err != nil {
  217. log.Error("%+v", err)
  218. }
  219. // handle feed
  220. is = s.upperItem(c, uis, mid, now)
  221. if len(is) == 0 {
  222. is = _emptyItem
  223. }
  224. return
  225. }
  226. func (s *Service) upperItem(c context.Context, uis []*busfeed.Feed, mid int64, now time.Time) (is []*feed.Item) {
  227. var (
  228. g *errgroup.Group
  229. ctx context.Context
  230. owners, aids []int64
  231. follows map[int64]bool
  232. tm map[string][]*tag.Tag
  233. err error
  234. )
  235. owners = make([]int64, 0, len(uis))
  236. for _, ui := range uis {
  237. if ui != nil {
  238. if ui.Archive != nil {
  239. owners = append(owners, ui.Archive.Author.Mid)
  240. aids = append(aids, ui.Archive.Aid)
  241. }
  242. for _, r := range ui.Fold {
  243. if r != nil {
  244. aids = append(aids, r.Aid)
  245. }
  246. }
  247. }
  248. }
  249. g, ctx = errgroup.WithContext(c)
  250. if len(owners) != 0 {
  251. g.Go(func() (err error) {
  252. follows = s.acc.Relations3(ctx, owners, mid)
  253. return
  254. })
  255. }
  256. if len(aids) != 0 {
  257. g.Go(func() (err error) {
  258. if tm, err = s.tg.Tags(ctx, mid, aids, now); err != nil {
  259. log.Error("%+v", err)
  260. err = nil
  261. }
  262. return
  263. })
  264. }
  265. if err = g.Wait(); err != nil {
  266. log.Error("%+v", err)
  267. return
  268. }
  269. is = make([]*feed.Item, 0, len(uis))
  270. for _, ui := range uis {
  271. if ui != nil {
  272. switch ui.Type {
  273. case busfeed.ArchiveType:
  274. if ui.Archive != nil && ui.Archive.IsNormal() {
  275. i := &feed.Item{}
  276. i.FromAv(&archive.ArchiveWithPlayer{Archive3: archive.BuildArchive3(ui.Archive)})
  277. i.RecCnt = len(ui.Fold)
  278. if len(ui.Fold) > 0 {
  279. ris := make([]*feed.Item, 0, len(ui.Fold))
  280. for _, r := range ui.Fold {
  281. if r != nil && r.IsNormal() {
  282. ri := &feed.Item{}
  283. ri.FromAv(&archive.ArchiveWithPlayer{Archive3: archive.BuildArchive3(r)})
  284. if infos, ok := tm[strconv.FormatInt(r.Aid, 10)]; ok {
  285. if len(infos) != 0 {
  286. ri.Tag = &feed.Tag{TagID: infos[0].ID, TagName: infos[0].Name, IsAtten: infos[0].IsAtten, Count: &feed.TagCount{Atten: infos[0].Count.Atten}}
  287. }
  288. }
  289. if follows[i.Mid] {
  290. ri.IsAtten = 1
  291. }
  292. ris = append(ris, ri)
  293. }
  294. }
  295. i.Recent = ris
  296. }
  297. if infos, ok := tm[strconv.FormatInt(ui.Archive.Aid, 10)]; ok {
  298. if len(infos) != 0 {
  299. i.Tag = &feed.Tag{TagID: infos[0].ID, TagName: infos[0].Name, IsAtten: infos[0].IsAtten, Count: &feed.TagCount{Atten: infos[0].Count.Atten}}
  300. }
  301. }
  302. if follows[i.Mid] {
  303. i.IsAtten = 1
  304. }
  305. is = append(is, i)
  306. }
  307. case busfeed.BangumiType:
  308. if ui.Bangumi != nil {
  309. i := &feed.Item{}
  310. i.FromUpBangumi(ui.Bangumi)
  311. is = append(is, i)
  312. }
  313. }
  314. }
  315. }
  316. return
  317. }
  318. func (s *Service) UpperLive(c context.Context, mid int64) (is []*feed.Item, count int) {
  319. var (
  320. err error
  321. fs []*live.Feed
  322. pn = 1
  323. ps = s.c.Feed.LiveFeedCount
  324. )
  325. if fs, count, err = s.lv.FeedList(c, mid, pn, ps); err != nil {
  326. log.Error("%+v", err)
  327. }
  328. for _, f := range fs {
  329. i := &feed.Item{}
  330. i.FromUpLive(f)
  331. is = append(is, i)
  332. }
  333. return
  334. }
  335. func (s *Service) UpperArticle(c context.Context, mid int64, plat int8, build int, pn, ps int, now time.Time) (is []*feed.Item, lp bool) {
  336. var (
  337. err error
  338. uis []*article.Meta
  339. )
  340. if uis, err = s.upper.ArticleFeed(c, mid, pn, ps); err != nil {
  341. log.Error("%+v", err)
  342. return
  343. }
  344. // handle feed
  345. is = s.articleItem(c, uis, mid)
  346. if len(is) < ps {
  347. lp = true
  348. }
  349. if len(is) == 0 {
  350. is = _emptyItem
  351. }
  352. return
  353. }
  354. func (s *Service) articleItem(c context.Context, uis []*article.Meta, mid int64) (is []*feed.Item) {
  355. is = make([]*feed.Item, 0, len(uis))
  356. for _, ui := range uis {
  357. if ui != nil {
  358. i := &feed.Item{}
  359. i.FromUpArticle(ui)
  360. is = append(is, i)
  361. }
  362. }
  363. return
  364. }
  365. func (s *Service) UnreadCount(c context.Context, mid int64, plat int8, build int, now time.Time) (total, feedCount, articleCount int) {
  366. var (
  367. withoutBangumi = true
  368. err error
  369. )
  370. if (plat == model.PlatIPhone && build > _iosBanBangumi) || (plat == model.PlatAndroid && build > _androidBanBangumi) || plat == model.PlatIPhoneB {
  371. withoutBangumi = false
  372. }
  373. if feedCount, err = s.upper.AppUnreadCount(c, mid, withoutBangumi); err != nil {
  374. log.Error("%+v", err)
  375. }
  376. if true {
  377. if articleCount, err = s.upper.ArticleUnreadCount(c, mid); err != nil {
  378. log.Error("%+v", err)
  379. }
  380. }
  381. total = feedCount + articleCount
  382. // add feed unread count cache
  383. if feedCount > 0 {
  384. s.addCache(func() {
  385. s.upper.AddUnreadCountCache(context.Background(), mid, feedCount)
  386. })
  387. }
  388. return
  389. }