contain.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. package view
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. "go-common/app/interface/main/app-intl/model"
  7. "go-common/app/interface/main/app-intl/model/bangumi"
  8. "go-common/app/interface/main/app-intl/model/manager"
  9. "go-common/app/interface/main/app-intl/model/view"
  10. "go-common/app/service/main/archive/api"
  11. "go-common/app/service/main/archive/model/archive"
  12. location "go-common/app/service/main/location/model"
  13. thumbup "go-common/app/service/main/thumbup/model"
  14. "go-common/library/ecode"
  15. "go-common/library/log"
  16. "go-common/library/sync/errgroup"
  17. "go-common/library/text/translate/chinese"
  18. )
  19. var (
  20. _rate = map[int]int64{15: 464, 16: 464, 32: 1028, 48: 1328, 64: 2192, 74: 3192, 80: 3192, 112: 6192, 116: 6192, 66: 1820}
  21. )
  22. const (
  23. _dmformat = "http://comment.bilibili.com/%d.xml"
  24. _qn480 = 32
  25. _qnAndroidBuild = 5325000
  26. _qnIosBuild = 8170
  27. )
  28. // initReqUser init Req User
  29. func (s *Service) initReqUser(c context.Context, v *view.View, mid int64, plat int8, build int) {
  30. // owner ext
  31. g, ctx := errgroup.WithContext(c)
  32. if v.Author.Mid > 0 {
  33. g.Go(func() (err error) {
  34. v.OwnerExt.OfficialVerify.Type = -1
  35. card, err := s.accDao.Card3(ctx, v.Author.Mid)
  36. if err != nil {
  37. log.Error("%+v", err)
  38. err = nil
  39. return
  40. }
  41. if card != nil {
  42. otp := -1
  43. odesc := ""
  44. if card.Official.Role != 0 {
  45. if card.Official.Role <= 2 {
  46. otp = 0
  47. } else {
  48. otp = 1
  49. }
  50. odesc = card.Official.Title
  51. }
  52. v.OwnerExt.OfficialVerify.Type = otp
  53. v.OwnerExt.OfficialVerify.Desc = odesc
  54. v.OwnerExt.Vip.Type = int(card.Vip.Type)
  55. v.OwnerExt.Vip.VipStatus = int(card.Vip.Status)
  56. v.OwnerExt.Vip.DueDate = card.Vip.DueDate
  57. v.Author.Name = card.Name
  58. v.Author.Face = card.Face
  59. }
  60. return
  61. })
  62. g.Go(func() (err error) {
  63. stat, err := s.relDao.Stat(c, v.Author.Mid)
  64. if err != nil {
  65. log.Error("%+v", err)
  66. err = nil
  67. return
  68. }
  69. if stat != nil {
  70. v.OwnerExt.Fans = int(stat.Follower)
  71. }
  72. return
  73. })
  74. g.Go(func() error {
  75. if ass, err := s.assDao.Assist(ctx, v.Author.Mid); err != nil {
  76. log.Error("%+v", err)
  77. } else {
  78. v.OwnerExt.Assists = ass
  79. }
  80. return nil
  81. })
  82. }
  83. // req user
  84. v.ReqUser = &view.ReqUser{Favorite: 0, Attention: -999, Like: 0, Dislike: 0}
  85. // check req user
  86. if mid > 0 {
  87. g.Go(func() error {
  88. if is, _ := s.favDao.IsFav(ctx, mid, v.Aid); is {
  89. v.ReqUser.Favorite = 1
  90. }
  91. return nil
  92. })
  93. g.Go(func() error {
  94. res, err := s.thumbupDao.HasLike(ctx, mid, _businessLike, []int64{v.Aid})
  95. if err != nil {
  96. log.Error("%+v", err)
  97. return nil
  98. }
  99. if typ, ok := res[v.Aid]; ok {
  100. if typ == thumbup.StateLike {
  101. v.ReqUser.Like = 1
  102. } else if typ == thumbup.StateDislike {
  103. v.ReqUser.Dislike = 1
  104. }
  105. }
  106. return nil
  107. })
  108. g.Go(func() (err error) {
  109. res, err := s.coinDao.ArchiveUserCoins(ctx, v.Aid, mid, _avTypeAv)
  110. if err != nil {
  111. log.Error("%+v", err)
  112. err = nil
  113. }
  114. if res != nil && res.Multiply > 0 {
  115. v.ReqUser.Coin = 1
  116. }
  117. return
  118. })
  119. if v.Author.Mid > 0 {
  120. g.Go(func() error {
  121. fl, err := s.accDao.Following3(ctx, mid, v.Author.Mid)
  122. if err != nil {
  123. log.Error("%+v", err)
  124. return nil
  125. }
  126. if fl {
  127. v.ReqUser.Attention = 1
  128. }
  129. return nil
  130. })
  131. }
  132. }
  133. if err := g.Wait(); err != nil {
  134. log.Error("%+v", err)
  135. }
  136. }
  137. // initRelateCMTag is.
  138. func (s *Service) initRelateCMTag(c context.Context, v *view.View, plat int8, build int, mid int64, buvid, mobiApp, device, network, adExtra, from string, now time.Time, isTW bool) {
  139. var (
  140. rls []*view.Relate
  141. mr *manager.Relate
  142. err error
  143. )
  144. tids := s.initTag(c, v, mid)
  145. g, ctx := errgroup.WithContext(c)
  146. g.Go(func() (err error) {
  147. if mid > 0 || buvid != "" {
  148. if rls, v.UserFeature, v.ReturnCode, err = s.newRcmdRelate(ctx, plat, v.Aid, mid, buvid, mobiApp, from, build); err != nil {
  149. log.Error("s.newRcmdRelate(%d) error(%+v)", v.Aid, err)
  150. }
  151. }
  152. if len(rls) == 0 {
  153. rls, err = s.dealRcmdRelate(ctx, plat, v.Aid)
  154. log.Warn("s.dealRcmdRelate aid(%d) mid(%d) buvid(%s)", v.Aid, mid, buvid)
  155. return
  156. }
  157. v.IsRec = 1
  158. log.Warn("s.newRcmdRelate returncode(%s) aid(%d) mid(%d) buvid(%s)", v.ReturnCode, v.Aid, mid, buvid)
  159. return
  160. })
  161. g.Go(func() (err error) {
  162. mr = s.relateCache(ctx, plat, build, now, v.Aid, tids, v.TypeID)
  163. return
  164. })
  165. if err = g.Wait(); err != nil {
  166. log.Error("%+v", err)
  167. return
  168. }
  169. if len(rls) == 0 {
  170. s.prom.Incr("zero_relates")
  171. return
  172. }
  173. var (
  174. r *view.Relate
  175. rm map[int]*view.Relate
  176. )
  177. aidm := map[int64]struct{}{}
  178. if r, err = s.dealManagerRelate(c, plat, mr); err != nil {
  179. log.Error("%+v", err)
  180. }
  181. if r != nil {
  182. // 相关推荐的第一位是运营插入位
  183. rm = map[int]*view.Relate{0: r}
  184. aidm[r.Aid] = struct{}{}
  185. }
  186. // 权重:详情页稿件>相关推荐运营位稿件>AI相关推荐稿件
  187. for _, rl := range rls {
  188. // AI相关推荐稿件不能和详情页稿件重复
  189. if rl.Aid == v.Aid {
  190. continue
  191. }
  192. if len(rm) != 0 {
  193. for {
  194. // 相关推荐运营位稿件不能和详情页稿件重复
  195. if r, ok := rm[len(v.Relates)]; ok && r.Aid != v.Aid {
  196. v.Relates = append(v.Relates, r)
  197. continue
  198. }
  199. // AI相关推荐稿件不能和相关推荐运营位稿件重复
  200. if _, ok := aidm[rl.Aid]; !ok {
  201. v.Relates = append(v.Relates, rl)
  202. }
  203. break
  204. }
  205. } else {
  206. v.Relates = append(v.Relates, rl)
  207. }
  208. }
  209. if isTW {
  210. for _, rl := range v.Relates {
  211. rl.Title = chinese.Convert(c, rl.Title)
  212. }
  213. }
  214. }
  215. // initMovie is.
  216. func (s *Service) initMovie(c context.Context, v *view.View, mid int64, build int, mobiApp, device string, nMovie bool) (err error) {
  217. s.pHit.Incr("is_movie")
  218. var m *bangumi.Movie
  219. if m, err = s.banDao.Movie(c, v.Aid, mid, build, mobiApp, device); err != nil || m == nil {
  220. log.Error("%+v", err)
  221. err = ecode.NothingFound
  222. s.pMiss.Incr("err_is_PGC")
  223. return
  224. }
  225. if v.Rights.HD5 == 1 && m.PayUser.Status == 0 && !s.checkVIP(c, mid) {
  226. v.Rights.HD5 = 0
  227. }
  228. if len(m.List) == 0 {
  229. err = ecode.NothingFound
  230. return
  231. }
  232. vps := make([]*view.Page, 0, len(m.List))
  233. for _, l := range m.List {
  234. vp := &view.Page{
  235. Page3: &archive.Page3{Cid: l.Cid, Page: int32(l.Page), From: l.Type, Part: l.Part, Vid: l.Vid},
  236. }
  237. vps = append(vps, vp)
  238. }
  239. m.List = nil
  240. // view
  241. v.Pages = vps
  242. v.Rights.Download = int32(m.AllowDownload)
  243. m.AllowDownload = 0
  244. v.Rights.Bp = 0
  245. if nMovie {
  246. v.Movie = m
  247. v.Desc = m.Season.Evaluate
  248. }
  249. return
  250. }
  251. // initPGC is.
  252. func (s *Service) initPGC(c context.Context, v *view.View, mid int64, build int, mobiApp, device string) (err error) {
  253. s.pHit.Incr("is_PGC")
  254. var season *bangumi.Season
  255. if season, err = s.banDao.PGC(c, v.Aid, mid, build, mobiApp, device); err != nil {
  256. log.Error("%+v", err)
  257. err = ecode.NothingFound
  258. s.pMiss.Incr("err_is_PGC")
  259. return
  260. }
  261. if season != nil {
  262. if season.Player != nil {
  263. if len(v.Pages) != 0 {
  264. if season.Player.Cid != 0 {
  265. v.Pages[0].Cid = season.Player.Cid
  266. }
  267. if season.Player.From != "" {
  268. v.Pages[0].From = season.Player.From
  269. }
  270. if season.Player.Vid != "" {
  271. v.Pages[0].Vid = season.Player.Vid
  272. }
  273. }
  274. season.Player = nil
  275. }
  276. if season.AllowDownload == "1" {
  277. v.Rights.Download = 1
  278. } else {
  279. v.Rights.Download = 0
  280. }
  281. if season.SeasonID != "" {
  282. season.AllowDownload = ""
  283. v.Season = season
  284. }
  285. }
  286. if v.Rights.HD5 == 1 && !s.checkVIP(c, mid) {
  287. v.Rights.HD5 = 0
  288. }
  289. v.Rights.Bp = 0
  290. return
  291. }
  292. // initPages is.
  293. func (s *Service) initPages(c context.Context, vs *view.ViewStatic, ap []*archive.Page3) {
  294. pages := make([]*view.Page, 0, len(ap))
  295. for _, v := range ap {
  296. page := &view.Page{}
  297. metas := make([]*view.Meta, 0, 4)
  298. for q, r := range _rate {
  299. meta := &view.Meta{
  300. Quality: q,
  301. Size: int64(float64(r*v.Duration) * 1.1 / 8.0),
  302. }
  303. metas = append(metas, meta)
  304. }
  305. if vs.AttrVal(archive.AttrBitIsBangumi) == archive.AttrYes {
  306. v.From = "bangumi"
  307. }
  308. page.Page3 = v
  309. page.Metas = metas
  310. page.DMLink = fmt.Sprintf(_dmformat, v.Cid)
  311. pages = append(pages, page)
  312. }
  313. vs.Pages = pages
  314. }
  315. // initDownload is.
  316. func (s *Service) initDownload(c context.Context, v *view.View, mid int64, cdnIP string) (err error) {
  317. var download int64
  318. if v.AttrVal(archive.AttrBitLimitArea) == archive.AttrYes {
  319. if download, err = s.ipLimit(c, mid, v.Aid, cdnIP); err != nil {
  320. return
  321. }
  322. } else {
  323. download = location.AllowDown
  324. }
  325. if download == location.ForbiddenDown {
  326. v.Rights.Download = int32(download)
  327. return
  328. }
  329. for _, p := range v.Pages {
  330. if p.From == "qq" {
  331. download = location.ForbiddenDown
  332. break
  333. }
  334. }
  335. v.Rights.Download = int32(download)
  336. return
  337. }
  338. // initContributions is.
  339. func (s *Service) initContributions(c context.Context, v *view.View, isTW bool) {
  340. const _count = 5
  341. if v.ReqUser != nil && v.ReqUser.Attention == 1 {
  342. return
  343. }
  344. var hasAudio bool
  345. for _, page := range v.Pages {
  346. if page.Audio != nil {
  347. hasAudio = true
  348. break
  349. }
  350. }
  351. if hasAudio || v.OwnerExt.Archives < 20 {
  352. return
  353. }
  354. aids, err := s.arcDao.ViewContributeCache(c, v.Author.Mid)
  355. if err != nil {
  356. log.Error("%+v", err)
  357. return
  358. }
  359. if len(aids) < _count+1 {
  360. return
  361. }
  362. as, err := s.arcDao.Archives(c, aids)
  363. if err != nil {
  364. log.Error("%+v", err)
  365. return
  366. }
  367. if len(as) == 0 {
  368. return
  369. }
  370. ctbt := make([]*view.Contribution, 0, len(as))
  371. for _, aid := range aids {
  372. if a, ok := as[aid]; ok && a.IsNormal() {
  373. if a.Aid != v.Aid {
  374. if isTW {
  375. a.Title = chinese.Convert(c, a.Title)
  376. }
  377. vc := &view.Contribution{Aid: a.Aid, Title: a.Title, Pic: a.Pic, Author: a.Author, Stat: a.Stat, CTime: a.PubDate}
  378. ctbt = append(ctbt, vc)
  379. }
  380. }
  381. }
  382. if len(ctbt) > _count {
  383. ctbt = ctbt[:_count]
  384. }
  385. if len(ctbt) == _count {
  386. v.Contributions = ctbt
  387. }
  388. }
  389. // initAudios is.
  390. func (s *Service) initAudios(c context.Context, v *view.View) {
  391. pLen := len(v.Pages)
  392. if pLen == 0 || pLen > 100 {
  393. return
  394. }
  395. if pLen > 50 {
  396. pLen = 50
  397. }
  398. cids := make([]int64, 0, len(v.Pages[:pLen]))
  399. for _, p := range v.Pages[:pLen] {
  400. cids = append(cids, p.Cid)
  401. }
  402. vam, err := s.audioDao.AudioByCids(c, cids)
  403. if err != nil {
  404. log.Error("%+v", err)
  405. return
  406. }
  407. if len(vam) != 0 {
  408. for _, p := range v.Pages[:pLen] {
  409. if va, ok := vam[p.Cid]; ok {
  410. p.Audio = va
  411. }
  412. }
  413. if len(v.Pages) == 1 {
  414. if va, ok := vam[v.Pages[0].Cid]; ok {
  415. v.Audio = va
  416. }
  417. }
  418. }
  419. }
  420. // initTag is.
  421. func (s *Service) initTag(c context.Context, v *view.View, mid int64) (tids []int64) {
  422. tags, err := s.tagDao.ArcTags(c, v.Aid, mid)
  423. if err != nil {
  424. log.Error("%+v", err)
  425. return
  426. }
  427. tids = make([]int64, 0, len(tags))
  428. for _, tag := range tags {
  429. tids = append(tids, tag.ID)
  430. }
  431. v.Tag = tags
  432. return
  433. }
  434. // initDM is.
  435. func (s *Service) initDM(c context.Context, v *view.View) {
  436. const (
  437. _dmTypeAv = 1
  438. _dmPlatMobie = 1
  439. )
  440. pLen := len(v.Pages)
  441. if pLen == 0 || pLen > 100 {
  442. return
  443. }
  444. if pLen > 50 {
  445. pLen = 50
  446. }
  447. cids := make([]int64, 0, len(v.Pages[:pLen]))
  448. for _, p := range v.Pages[:pLen] {
  449. cids = append(cids, p.Cid)
  450. }
  451. res, err := s.dmDao.SubjectInfos(c, _dmTypeAv, _dmPlatMobie, cids...)
  452. if err != nil {
  453. log.Error("%+v", err)
  454. return
  455. }
  456. if len(res) == 0 {
  457. return
  458. }
  459. for _, p := range v.Pages[:pLen] {
  460. if r, ok := res[p.Cid]; ok {
  461. p.DM = r
  462. }
  463. }
  464. }
  465. // dealRcmdRelate is.
  466. func (s *Service) dealRcmdRelate(c context.Context, plat int8, aid int64) (rls []*view.Relate, err error) {
  467. if rls, err = s.arcDao.RelatesCache(c, aid); err != nil {
  468. return
  469. }
  470. if len(rls) != 0 {
  471. return
  472. }
  473. s.prom.Incr("need_relates")
  474. var aids []int64
  475. if aids, err = s.arcDao.RelateAids(c, aid); err != nil {
  476. return
  477. }
  478. if len(aids) == 0 {
  479. return
  480. }
  481. var as map[int64]*api.Arc
  482. if as, err = s.arcDao.Archives(c, aids); err != nil {
  483. return
  484. }
  485. for _, aid := range aids {
  486. if a, ok := as[aid]; ok {
  487. if s.overseaCheck(archive.BuildArchive3(a), plat) || !a.IsNormal() {
  488. continue
  489. }
  490. r := &view.Relate{}
  491. r.FromAv(a, "", "", nil)
  492. rls = append(rls, r)
  493. }
  494. }
  495. if len(rls) != 0 {
  496. // 如果是繁体区会修改relate的title,addcache是异步操作,需要深度拷贝,避免并发读写的panci
  497. rels := make([]*view.Relate, 0, len(rls))
  498. for _, rl := range rls {
  499. r := &view.Relate{}
  500. *r = *rl
  501. rels = append(rels, r)
  502. }
  503. s.arcDao.AddRelatesCache(aid, rels)
  504. }
  505. return
  506. }
  507. // dealManagerRelate is.
  508. func (s *Service) dealManagerRelate(c context.Context, plat int8, mr *manager.Relate) (r *view.Relate, err error) {
  509. if mr == nil || mr.Param < 1 {
  510. return
  511. }
  512. switch mr.Goto {
  513. case model.GotoAv:
  514. var a *api.Arc
  515. if a, err = s.arcDao.Archive(c, mr.Param); err != nil {
  516. return
  517. }
  518. if a != nil {
  519. r = &view.Relate{}
  520. r.FromOperate(mr, a, model.FromOperation)
  521. }
  522. }
  523. return
  524. }
  525. // newRcmdRelate is.
  526. func (s *Service) newRcmdRelate(c context.Context, plat int8, aid, mid int64, buvid, mobiApp, from string, build int) (rls []*view.Relate, userFeature, returnCode string, err error) {
  527. recData, userFeature, returnCode, err := s.arcDao.NewRelateAids(c, aid, mid, build, buvid, from, plat)
  528. if err != nil || len(recData) == 0 {
  529. return
  530. }
  531. var (
  532. aids []int64
  533. arcm map[int64]*api.Arc
  534. )
  535. for _, rec := range recData {
  536. switch rec.Goto {
  537. case model.GotoAv:
  538. aids = append(aids, rec.Oid)
  539. }
  540. }
  541. if len(aids) == 0 {
  542. return
  543. }
  544. if arcm, err = s.arcDao.Archives(c, aids); err != nil {
  545. log.Error("%+v", err)
  546. return
  547. }
  548. if len(arcm) == 0 {
  549. return
  550. }
  551. for _, rec := range recData {
  552. switch rec.Goto {
  553. case model.GotoAv:
  554. arc, ok := arcm[rec.Oid]
  555. if !ok || s.overseaCheck(archive.BuildArchive3(arc), plat) || !arc.IsNormal() {
  556. continue
  557. }
  558. r := &view.Relate{AvFeature: rec.AvFeature, Source: rec.Source}
  559. r.FromAv(arc, "", rec.TrackID, nil)
  560. rls = append(rls, r)
  561. }
  562. }
  563. return
  564. }