hotspots.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package service
  2. import (
  3. "context"
  4. "time"
  5. "go-common/app/interface/openplatform/article/dao"
  6. "go-common/app/interface/openplatform/article/model"
  7. "go-common/library/ecode"
  8. "go-common/library/log"
  9. )
  10. var _hotspotArtTime = time.Hour * 24 * 30
  11. // UpdateHotspots update all hotspots
  12. func (s *Service) UpdateHotspots(force bool) (err error) {
  13. var c = context.TODO()
  14. hotspots, err := s.dao.Hotspots(c)
  15. if err != nil {
  16. return
  17. }
  18. if len(hotspots) == 0 {
  19. err = s.dao.DelCacheHotspots(c)
  20. return
  21. }
  22. for _, hot := range hotspots {
  23. if err = s.genHotspot(c, hot, force); err != nil {
  24. dao.PromError("hotspots:生成")
  25. log.Error("hotspots s.genHotspot(%v, %v) err:%v", hot.Tag, force, err)
  26. return
  27. }
  28. }
  29. err = s.dao.AddCacheHotspots(c, hotspots)
  30. for _, h := range hotspots {
  31. s.dao.AddCacheHotspot(c, h.ID, h)
  32. }
  33. return
  34. }
  35. func (s *Service) genHotspot(c context.Context, hot *model.Hotspot, force bool) (err error) {
  36. var ok bool
  37. for _, typ := range model.HotspotTypes {
  38. ok, err = s.dao.ExpireHotspotArtsCache(c, typ, hot.ID)
  39. if err != nil {
  40. return
  41. }
  42. if !ok || force {
  43. break
  44. }
  45. }
  46. if ok && !force {
  47. //不重新生成 赋值计数 返回
  48. if s, _ := s.dao.CacheHotspot(c, hot.ID); s != nil {
  49. hot.Stats = s.Stats
  50. }
  51. return
  52. }
  53. ptime := time.Now().Add(-_hotspotArtTime)
  54. arts, err := s.dao.SearchArts(c, ptime.Unix())
  55. if err != nil {
  56. return
  57. }
  58. // filter tags && remove top arts
  59. tops := make(map[int64]bool)
  60. for _, x := range hot.TopArticles {
  61. tops[x] = true
  62. }
  63. var newArts []*model.SearchArt
  64. for _, art := range arts {
  65. for _, t := range art.Tags {
  66. if t == hot.Tag {
  67. hot.Stats.Read += art.StatsView
  68. hot.Stats.Reply += art.StatsReply
  69. if !tops[art.ID] {
  70. newArts = append(newArts, art)
  71. }
  72. break
  73. }
  74. }
  75. }
  76. arts = newArts
  77. if len(arts) == 0 {
  78. return
  79. }
  80. // add cache
  81. for _, typ := range model.HotspotTypes {
  82. var as [][2]int64
  83. for _, art := range arts {
  84. as = append(as, hotspotValue(typ, art))
  85. }
  86. if err = s.dao.AddCacheHotspotArts(c, typ, hot.ID, as, true); err != nil {
  87. return
  88. }
  89. }
  90. return
  91. }
  92. func hotspotValue(typ int8, art *model.SearchArt) [2]int64 {
  93. switch typ {
  94. case model.HotspotTypeView:
  95. return [2]int64{art.ID, art.StatsView}
  96. case model.HotspotTypePtime:
  97. return [2]int64{art.ID, art.PublishTime}
  98. }
  99. return [2]int64{0, 0}
  100. }
  101. // AddCacheHotspotArt check article in hotspots list and add cache
  102. func (s *Service) AddCacheHotspotArt(c context.Context, art *model.SearchArt) (err error) {
  103. if art.PublishTime < time.Now().Add(-_hotspotArtTime).Unix() {
  104. return
  105. }
  106. hots, all, err := s.tagsHotspots(c, art.Tags)
  107. if err != nil {
  108. dao.PromError("hotspots:AddCacheHotspotArt")
  109. return
  110. }
  111. if len(hots) == 0 {
  112. return
  113. }
  114. for _, hot := range hots {
  115. hot.Stats.Read += art.StatsView
  116. hot.Stats.Reply += art.StatsReply
  117. if err = s.addCacheHotspotArt(c, hot.ID, art); err != nil {
  118. dao.PromError("hotspots:addCacheHotspotArt")
  119. return
  120. }
  121. }
  122. err = s.dao.AddCacheHotspots(c, all)
  123. return
  124. }
  125. // DelCacheHotspotArt delete art from hotspots
  126. func (s *Service) DelCacheHotspotArt(c context.Context, aid int64) (err error) {
  127. hots, err := s.dao.CacheHotspots(c)
  128. if err != nil {
  129. dao.PromError("hotspots:DelCacheHotspotArt")
  130. return
  131. }
  132. for _, hot := range hots {
  133. for _, typ := range model.HotspotTypes {
  134. if err = s.dao.DelHotspotArtsCache(c, typ, hot.ID, aid); err != nil {
  135. return
  136. }
  137. }
  138. }
  139. return
  140. }
  141. // tagsHotspots get hotspots form tags
  142. func (s *Service) tagsHotspots(c context.Context, tags []string) (res, all []*model.Hotspot, err error) {
  143. all, err = s.dao.CacheHotspots(c)
  144. if err != nil {
  145. dao.PromError("hotspots:tagsHotspots")
  146. return
  147. }
  148. for _, hot := range all {
  149. for _, t := range tags {
  150. if t == hot.Tag {
  151. res = append(res, hot)
  152. break
  153. }
  154. }
  155. }
  156. return
  157. }
  158. func (s *Service) addCacheHotspotArt(c context.Context, hotID int64, art *model.SearchArt) (err error) {
  159. for _, typ := range model.HotspotTypes {
  160. var ok bool
  161. if ok, err = s.dao.ExpireHotspotArtsCache(c, typ, hotID); err != nil {
  162. return
  163. }
  164. if ok {
  165. if err = s.dao.AddCacheHotspotArts(c, typ, hotID, [][2]int64{hotspotValue(typ, art)}, false); err != nil {
  166. return
  167. }
  168. }
  169. }
  170. return
  171. }
  172. func (s *Service) metaToSearch(c context.Context, m *model.Meta) (res *model.SearchArt) {
  173. if m == nil {
  174. return
  175. }
  176. res = &model.SearchArt{
  177. ID: m.ID,
  178. PublishTime: int64(m.PublishTime),
  179. }
  180. for _, t := range m.Tags {
  181. res.Tags = append(res.Tags, t.Name)
  182. }
  183. stats, _ := s.stat(c, m.ID)
  184. if stats != nil {
  185. res.StatsView = stats.View
  186. res.StatsReply = stats.Reply
  187. }
  188. return
  189. }
  190. // HotspotArts get hotspot articles
  191. func (s *Service) HotspotArts(c context.Context, id int64, pn, ps int, lastAids []int64, sort int8, mid int64) (hotspot *model.Hotspot, res []*model.MetaWithLike, err error) {
  192. if pn <= 0 {
  193. pn = 1
  194. }
  195. var (
  196. start = (pn - 1) * ps
  197. // 多取一些用于去重
  198. end = start + ps - 1 + len(lastAids)
  199. allIDs []int64
  200. metas map[int64]*model.Meta
  201. aidsm map[int64]bool
  202. withRecommend bool
  203. )
  204. if hotspot, err = s.dao.CacheHotspot(c, id); err != nil {
  205. return
  206. }
  207. if hotspot == nil {
  208. err = ecode.NothingFound
  209. return
  210. }
  211. hotspot.Stats.Count, _ = s.dao.HotspotArtsCacheCount(c, sort, id)
  212. if sort == model.HotspotTypeView {
  213. withRecommend = true
  214. }
  215. recommendsLen := len(hotspot.TopArticles)
  216. // 只是最新文章 无推荐
  217. if (start >= recommendsLen) || !withRecommend {
  218. var (
  219. nids []int64
  220. newArtStart = start
  221. newArtEnd = end
  222. )
  223. if withRecommend {
  224. newArtStart = start - recommendsLen
  225. newArtEnd = end - recommendsLen
  226. }
  227. nids, _ = s.dao.HotspotArtsCache(c, sort, id, newArtStart, newArtEnd)
  228. if withRecommend {
  229. allIDs = uniqIDs(nids, hotspot.TopArticles)
  230. } else {
  231. allIDs = nids
  232. }
  233. } else {
  234. if end < recommendsLen {
  235. allIDs = hotspot.TopArticles[start : end+1]
  236. } else {
  237. // 混合推荐和最新文章
  238. var (
  239. nids []int64
  240. rs = hotspot.TopArticles[start:]
  241. )
  242. newArtStart := 0
  243. newArtEnd := (end - start) - len(rs)
  244. nids, _ = s.dao.HotspotArtsCache(c, sort, id, newArtStart, newArtEnd)
  245. nids = uniqIDs(nids, hotspot.TopArticles)
  246. allIDs = append(rs, nids...)
  247. }
  248. }
  249. if len(allIDs) == 0 {
  250. return
  251. }
  252. if metas, err = s.ArticleMetas(c, allIDs); err != nil {
  253. return
  254. }
  255. //过滤禁止显示的稿件
  256. filterNoDistributeArtsMap(metas)
  257. filterNoRegionArts(metas)
  258. //填充数据
  259. aidsm = make(map[int64]bool, len(lastAids))
  260. for _, aid := range lastAids {
  261. aidsm[aid] = true
  262. }
  263. for _, id := range allIDs {
  264. if (metas == nil) || (metas[id] == nil) || aidsm[id] {
  265. continue
  266. }
  267. art := &model.MetaWithLike{Meta: *metas[id]}
  268. res = append(res, art)
  269. }
  270. //截断分页数据
  271. if ps > len(res) {
  272. ps = len(res)
  273. }
  274. res = res[:ps]
  275. // fill like state
  276. aids := []int64{}
  277. for _, m := range res {
  278. aids = append(aids, m.ID)
  279. }
  280. states, _ := s.HadLikesByMid(c, mid, aids)
  281. if states == nil {
  282. return
  283. }
  284. for _, m := range res {
  285. m.LikeState = int(states[m.ID])
  286. }
  287. return
  288. }