search.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. package search
  2. import (
  3. "context"
  4. "time"
  5. tag "go-common/app/interface/main/app-interface/model/tag"
  6. "go-common/app/interface/main/app-intl/conf"
  7. accdao "go-common/app/interface/main/app-intl/dao/account"
  8. arcdao "go-common/app/interface/main/app-intl/dao/archive"
  9. artdao "go-common/app/interface/main/app-intl/dao/article"
  10. bgmdao "go-common/app/interface/main/app-intl/dao/bangumi"
  11. resdao "go-common/app/interface/main/app-intl/dao/resource"
  12. srchdao "go-common/app/interface/main/app-intl/dao/search"
  13. tagdao "go-common/app/interface/main/app-intl/dao/tag"
  14. "go-common/app/interface/main/app-intl/model"
  15. "go-common/app/interface/main/app-intl/model/bangumi"
  16. "go-common/app/interface/main/app-intl/model/search"
  17. article "go-common/app/interface/openplatform/article/model"
  18. account "go-common/app/service/main/account/model"
  19. "go-common/app/service/main/archive/api"
  20. "go-common/library/log"
  21. "go-common/library/sync/errgroup"
  22. )
  23. var (
  24. _emptyResult = &search.Result{
  25. NavInfo: []*search.NavInfo{},
  26. Page: 0,
  27. }
  28. )
  29. // Service is search service
  30. type Service struct {
  31. c *conf.Config
  32. srchDao *srchdao.Dao
  33. accDao *accdao.Dao
  34. arcDao *arcdao.Dao
  35. artDao *artdao.Dao
  36. // artDao *artdao.Dao
  37. resDao *resdao.Dao
  38. tagDao *tagdao.Dao
  39. bgmDao *bgmdao.Dao
  40. // config
  41. seasonNum int
  42. movieNum int
  43. seasonShowMore int
  44. movieShowMore int
  45. upUserNum int
  46. uvLimit int
  47. userNum int
  48. userVideoLimit int
  49. biliUserNum int
  50. biliUserVideoLimit int
  51. iPadSearchBangumi int
  52. iPadSearchFt int
  53. }
  54. // New is search service initial func
  55. func New(c *conf.Config) (s *Service) {
  56. s = &Service{
  57. c: c,
  58. srchDao: srchdao.New(c),
  59. accDao: accdao.New(c),
  60. arcDao: arcdao.New(c),
  61. artDao: artdao.New(c),
  62. // artDao: artdao.New(c),
  63. resDao: resdao.New(c),
  64. tagDao: tagdao.New(c),
  65. bgmDao: bgmdao.New(c),
  66. seasonNum: c.Search.SeasonNum,
  67. movieNum: c.Search.MovieNum,
  68. seasonShowMore: c.Search.SeasonMore,
  69. movieShowMore: c.Search.MovieMore,
  70. upUserNum: c.Search.UpUserNum,
  71. uvLimit: c.Search.UVLimit,
  72. userNum: c.Search.UpUserNum,
  73. userVideoLimit: c.Search.UVLimit,
  74. biliUserNum: c.Search.BiliUserNum,
  75. biliUserVideoLimit: c.Search.BiliUserVideoLimit,
  76. iPadSearchBangumi: c.Search.IPadSearchBangumi,
  77. iPadSearchFt: c.Search.IPadSearchFt,
  78. }
  79. return
  80. }
  81. // Search get all type search data.
  82. func (s *Service) Search(c context.Context, mid, zoneid int64, mobiApp, device, platform, buvid, keyword, duration, order, filtered, lang, fromSource, recommend string, plat int8, rid, highlight, build, pn, ps int, now time.Time) (res *search.Result, err error) {
  83. var (
  84. aids []int64
  85. am map[int64]*api.Arc
  86. owners []int64
  87. follows map[int64]bool
  88. seasonIDs []int64
  89. bangumis map[string]*bangumi.Card
  90. )
  91. var (
  92. seasonNum int
  93. movieNum int
  94. )
  95. seasonNum = s.seasonNum
  96. movieNum = s.movieNum
  97. all, code, err := s.srchDao.Search(c, mid, zoneid, mobiApp, device, platform, buvid, keyword, duration, order, filtered, fromSource, recommend, plat, seasonNum, movieNum, s.upUserNum, s.uvLimit, s.userNum, s.userVideoLimit, s.biliUserNum, s.biliUserVideoLimit, rid, highlight, build, pn, ps, now)
  98. if err != nil {
  99. log.Error("%+v", err)
  100. return
  101. }
  102. if code == model.ForbidCode || code == model.NoResultCode {
  103. res = _emptyResult
  104. return
  105. }
  106. res = &search.Result{}
  107. res.Trackid = all.Trackid
  108. res.Page = all.Page
  109. res.Array = all.FlowPlaceholder
  110. res.Attribute = all.Attribute
  111. res.NavInfo = s.convertNav(all, plat, build, lang)
  112. if len(all.FlowResult) != 0 {
  113. var item []*search.Item
  114. for _, v := range all.FlowResult {
  115. switch v.Type {
  116. case search.TypeUser, search.TypeBiliUser:
  117. owners = append(owners, v.User.Mid)
  118. for _, vr := range v.User.Res {
  119. aids = append(aids, vr.Aid)
  120. }
  121. case search.TypeVideo:
  122. aids = append(aids, v.Video.ID)
  123. case search.TypeMediaBangumi, search.TypeMediaFt:
  124. seasonIDs = append(seasonIDs, v.Media.SeasonID)
  125. }
  126. }
  127. g, ctx := errgroup.WithContext(c)
  128. if len(owners) != 0 {
  129. if mid > 0 {
  130. g.Go(func() error {
  131. follows = s.accDao.Relations3(ctx, owners, mid)
  132. return nil
  133. })
  134. }
  135. }
  136. if len(aids) != 0 {
  137. g.Go(func() (err error) {
  138. if am, err = s.arcDao.Archives(ctx, aids); err != nil {
  139. log.Error("%+v", err)
  140. err = nil
  141. }
  142. return
  143. })
  144. }
  145. if len(seasonIDs) != 0 {
  146. g.Go(func() (err error) {
  147. if bangumis, err = s.bgmDao.Card(ctx, mid, seasonIDs); err != nil {
  148. log.Error("%+v", err)
  149. err = nil
  150. }
  151. return
  152. })
  153. }
  154. if err = g.Wait(); err != nil {
  155. log.Error("%+v", err)
  156. return
  157. }
  158. if all.SuggestKeyword != "" && pn == 1 {
  159. i := &search.Item{Title: all.SuggestKeyword, Goto: model.GotoSuggestKeyWord}
  160. item = append(item, i)
  161. }
  162. for _, v := range all.FlowResult {
  163. i := &search.Item{TrackID: v.TrackID, LinkType: v.LinkType, Position: v.Position}
  164. switch v.Type {
  165. case search.TypeVideo:
  166. i.FromVideo(v.Video, am[v.Video.ID])
  167. case search.TypeMediaBangumi:
  168. i.FromMedia(v.Media, "", model.GotoBangumi, bangumis)
  169. case search.TypeMediaFt:
  170. i.FromMedia(v.Media, "", model.GotoMovie, bangumis)
  171. case search.TypeSpecial:
  172. i.FromOperate(v.Operate, model.GotoSpecial)
  173. case search.TypeBanner:
  174. i.FromOperate(v.Operate, model.GotoBanner)
  175. case search.TypeUser:
  176. if follows[v.User.Mid] {
  177. i.Attentions = 1
  178. }
  179. i.FromUser(v.User, am)
  180. case search.TypeBiliUser:
  181. if follows[v.User.Mid] {
  182. i.Attentions = 1
  183. }
  184. i.FromUpUser(v.User, am)
  185. case search.TypeSpecialS:
  186. i.FromOperate(v.Operate, model.GotoSpecialS)
  187. case search.TypeQuery:
  188. i.Title = v.TypeName
  189. i.FromQuery(v.Query)
  190. case search.TypeConverge:
  191. var (
  192. avids, artids []int64
  193. avm map[int64]*api.Arc
  194. artm map[int64]*article.Meta
  195. )
  196. for _, c := range v.Operate.ContentList {
  197. switch c.Type {
  198. case 0:
  199. avids = append(avids, c.ID)
  200. case 2:
  201. artids = append(artids, c.ID)
  202. }
  203. }
  204. g, ctx := errgroup.WithContext(c)
  205. if len(aids) != 0 {
  206. g.Go(func() (err error) {
  207. if avm, err = s.arcDao.Archives(ctx, avids); err != nil {
  208. log.Error("%+v", err)
  209. err = nil
  210. }
  211. return
  212. })
  213. }
  214. if len(artids) != 0 {
  215. g.Go(func() (err error) {
  216. if artm, err = s.artDao.Articles(ctx, artids); err != nil {
  217. log.Error("%+v", err)
  218. err = nil
  219. }
  220. return
  221. })
  222. }
  223. if err = g.Wait(); err != nil {
  224. log.Error("%+v", err)
  225. continue
  226. }
  227. i.FromConverge(v.Operate, avm, artm)
  228. case search.TypeTwitter:
  229. i.FromTwitter(v.Twitter)
  230. }
  231. if i.Goto != "" {
  232. item = append(item, i)
  233. }
  234. }
  235. res.Item = item
  236. if all.EggInfo != nil {
  237. res.EasterEgg = &search.EasterEgg{ID: all.EggInfo.Source, ShowCount: all.EggInfo.ShowCount}
  238. }
  239. return
  240. }
  241. // archive
  242. for _, v := range all.Result.Video {
  243. aids = append(aids, v.ID)
  244. }
  245. if duration == "0" && order == "totalrank" && rid == 0 {
  246. for _, v := range all.Result.Movie {
  247. if v.Type == "movie" {
  248. aids = append(aids, v.Aid)
  249. }
  250. }
  251. }
  252. if pn == 1 {
  253. for _, v := range all.Result.User {
  254. for _, vr := range v.Res {
  255. aids = append(aids, vr.Aid)
  256. }
  257. }
  258. for _, v := range all.Result.BiliUser {
  259. for _, vr := range v.Res {
  260. aids = append(aids, vr.Aid)
  261. }
  262. owners = append(owners, v.Mid)
  263. }
  264. }
  265. g, ctx := errgroup.WithContext(c)
  266. if len(owners) != 0 {
  267. if mid > 0 {
  268. g.Go(func() error {
  269. follows = s.accDao.Relations3(ctx, owners, mid)
  270. return nil
  271. })
  272. }
  273. }
  274. if len(aids) != 0 {
  275. g.Go(func() (err error) {
  276. if am, err = s.arcDao.Archives(ctx, aids); err != nil {
  277. log.Error("%+v", err)
  278. err = nil
  279. }
  280. return
  281. })
  282. }
  283. if err = g.Wait(); err != nil {
  284. log.Error("%+v", err)
  285. return
  286. }
  287. return
  288. }
  289. // SearchByType is tag bangumi movie upuser video search
  290. func (s *Service) SearchByType(c context.Context, mid, zoneid int64, mobiApp, device, platform, buvid, sType, keyword, filtered, order string, plat int8, build, highlight, categoryID, userType, orderSort, pn, ps int, now time.Time) (res *search.TypeSearch, err error) {
  291. switch sType {
  292. case "upper":
  293. if res, err = s.upper(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, s.biliUserVideoLimit, highlight, build, userType, orderSort, pn, ps, now); err != nil {
  294. return
  295. }
  296. case "article":
  297. if res, err = s.article(c, mid, zoneid, highlight, keyword, mobiApp, device, platform, buvid, filtered, order, sType, plat, categoryID, build, pn, ps, now); err != nil {
  298. return
  299. }
  300. case "season2":
  301. if res, err = s.srchDao.Season2(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
  302. return
  303. }
  304. case "movie2":
  305. if res, err = s.srchDao.MovieByType2(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
  306. return
  307. }
  308. case "tag":
  309. if res, err = s.channel(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps, highlight); err != nil {
  310. return
  311. }
  312. }
  313. if res == nil {
  314. res = &search.TypeSearch{Items: []*search.Item{}}
  315. }
  316. return
  317. }
  318. // Suggest3 for search suggest
  319. func (s *Service) Suggest3(c context.Context, mid int64, platform, buvid, keyword string, build, highlight int, mobiApp string, now time.Time) (res *search.SuggestionResult3) {
  320. var (
  321. suggest *search.Suggest3
  322. err error
  323. aids []int64
  324. am map[int64]*api.Arc
  325. )
  326. res = &search.SuggestionResult3{}
  327. if suggest, err = s.srchDao.Suggest3(c, mid, platform, buvid, keyword, build, highlight, mobiApp, now); err != nil {
  328. log.Error("%+v", err)
  329. return
  330. }
  331. for _, v := range suggest.Result {
  332. if v.TermType == search.SuggestionJump {
  333. if v.SubType == search.SuggestionAV {
  334. aids = append(aids, v.Ref)
  335. }
  336. }
  337. }
  338. g, ctx := errgroup.WithContext(c)
  339. if len(aids) != 0 {
  340. g.Go(func() (err error) {
  341. if am, err = s.arcDao.Archives(ctx, aids); err != nil {
  342. log.Error("%+v", err)
  343. err = nil
  344. }
  345. return
  346. })
  347. }
  348. if err = g.Wait(); err != nil {
  349. log.Error("%+v", err)
  350. return
  351. }
  352. for _, v := range suggest.Result {
  353. si := &search.Item{}
  354. si.FromSuggest3(v, am)
  355. res.List = append(res.List, si)
  356. }
  357. res.TrackID = suggest.TrackID
  358. return
  359. }
  360. // convertNav deal with old search pageinfo to new.
  361. func (s *Service) convertNav(all *search.Search, plat int8, build int, lang string) (nis []*search.NavInfo) {
  362. const (
  363. _showHide = 0
  364. )
  365. var (
  366. season = "番剧"
  367. upper = "用户"
  368. movie = "影视"
  369. article = "专栏"
  370. )
  371. if lang == model.Hant {
  372. season = "番劇"
  373. upper = "UP主"
  374. movie = "影視"
  375. article = "專欄"
  376. }
  377. nis = make([]*search.NavInfo, 0, 4)
  378. // season
  379. // media season
  380. if all.PageInfo.MediaBangumi != nil {
  381. var nav = &search.NavInfo{
  382. Name: season,
  383. Total: all.PageInfo.MediaBangumi.NumResults,
  384. Pages: all.PageInfo.MediaBangumi.Pages,
  385. Type: 7,
  386. }
  387. if all.PageInfo.MediaBangumi.NumResults > s.seasonNum {
  388. nav.Show = s.seasonShowMore
  389. } else {
  390. nav.Show = _showHide
  391. }
  392. nis = append(nis, nav)
  393. }
  394. // upper
  395. if all.PageInfo.BiliUser != nil {
  396. var nav = &search.NavInfo{
  397. Name: upper,
  398. Total: all.PageInfo.BiliUser.NumResults,
  399. Pages: all.PageInfo.BiliUser.Pages,
  400. Type: 2,
  401. }
  402. nis = append(nis, nav)
  403. }
  404. // media movie
  405. if all.PageInfo.MediaFt != nil {
  406. var nav = &search.NavInfo{
  407. Name: movie,
  408. Total: all.PageInfo.MediaFt.NumResults,
  409. Pages: all.PageInfo.MediaFt.Pages,
  410. Type: 8,
  411. }
  412. if all.PageInfo.MediaFt.NumResults > s.movieNum {
  413. nav.Show = s.movieShowMore
  414. } else {
  415. nav.Show = _showHide
  416. }
  417. nis = append(nis, nav)
  418. }
  419. if all.PageInfo.Article != nil {
  420. var nav = &search.NavInfo{
  421. Name: article,
  422. Total: all.PageInfo.Article.NumResults,
  423. Pages: all.PageInfo.Article.Pages,
  424. Type: 6,
  425. }
  426. nis = append(nis, nav)
  427. }
  428. return
  429. }
  430. // upper search for upper
  431. func (s *Service) upper(c context.Context, mid int64, keyword, mobiApp, device, platform, buvid, filtered, order string, biliUserVL, highlight, build, userType, orderSort, pn, ps int, now time.Time) (res *search.TypeSearch, err error) {
  432. var (
  433. owners []int64
  434. follows map[int64]bool
  435. )
  436. if res, err = s.srchDao.Upper(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, biliUserVL, highlight, build, userType, orderSort, pn, ps, now); err != nil {
  437. return
  438. }
  439. if res == nil || len(res.Items) == 0 {
  440. return
  441. }
  442. owners = make([]int64, 0, len(res.Items))
  443. for _, item := range res.Items {
  444. owners = append(owners, item.Mid)
  445. }
  446. if len(owners) != 0 {
  447. g, ctx := errgroup.WithContext(c)
  448. if mid > 0 {
  449. g.Go(func() error {
  450. follows = s.accDao.Relations3(ctx, owners, mid)
  451. return nil
  452. })
  453. }
  454. if err = g.Wait(); err != nil {
  455. log.Error("%+v", err)
  456. return
  457. }
  458. for _, item := range res.Items {
  459. if follows[item.Mid] {
  460. item.Attentions = 1
  461. }
  462. }
  463. }
  464. return
  465. }
  466. // article search for article
  467. func (s *Service) article(c context.Context, mid, zoneid int64, highlight int, keyword, mobiApp, device, platform, buvid, filtered, order, sType string, plat int8, categoryID, build, pn, ps int, now time.Time) (res *search.TypeSearch, err error) {
  468. if res, err = s.srchDao.ArticleByType(c, mid, zoneid, keyword, mobiApp, device, platform, buvid, filtered, order, sType, plat, categoryID, build, highlight, pn, ps, now); err != nil {
  469. log.Error("%+v", err)
  470. return
  471. }
  472. if res != nil && len(res.Items) != 0 {
  473. return
  474. }
  475. var mids []int64
  476. for _, v := range res.Items {
  477. mids = append(mids, v.Mid)
  478. }
  479. var infom map[int64]*account.Info
  480. if infom, err = s.accDao.Infos3(c, mids); err != nil {
  481. log.Error("%+v", err)
  482. err = nil
  483. return
  484. }
  485. for _, item := range res.Items {
  486. if info, ok := infom[item.Mid]; ok {
  487. item.Name = info.Name
  488. }
  489. }
  490. return
  491. }
  492. // channel search for channel
  493. func (s *Service) channel(c context.Context, mid int64, keyword, mobiApp, platform, buvid, device, order, sType string, build, pn, ps, highlight int) (res *search.TypeSearch, err error) {
  494. var (
  495. g *errgroup.Group
  496. ctx context.Context
  497. tags []int64
  498. tagMyInfos []*tag.Tag
  499. )
  500. if res, err = s.srchDao.Channel(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps, highlight); err != nil {
  501. return
  502. }
  503. if res == nil || len(res.Items) == 0 {
  504. return
  505. }
  506. tags = make([]int64, 0, len(res.Items))
  507. for _, item := range res.Items {
  508. tags = append(tags, item.ID)
  509. }
  510. if len(tags) != 0 {
  511. g, ctx = errgroup.WithContext(c)
  512. if mid > 0 {
  513. g.Go(func() error {
  514. tagMyInfos, _ = s.tagDao.TagInfos(ctx, tags, mid)
  515. return nil
  516. })
  517. }
  518. if err = g.Wait(); err != nil {
  519. log.Error("%+v", err)
  520. return
  521. }
  522. for _, item := range res.Items {
  523. for _, myInfo := range tagMyInfos {
  524. if myInfo != nil && myInfo.TagID == item.ID {
  525. item.IsAttention = myInfo.IsAtten
  526. break
  527. }
  528. }
  529. }
  530. }
  531. return
  532. }