301 lines
6.9 KiB
Go
301 lines
6.9 KiB
Go
|
package service
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"time"
|
||
|
|
||
|
"go-common/app/interface/openplatform/article/dao"
|
||
|
"go-common/app/interface/openplatform/article/model"
|
||
|
"go-common/library/ecode"
|
||
|
"go-common/library/log"
|
||
|
)
|
||
|
|
||
|
var _hotspotArtTime = time.Hour * 24 * 30
|
||
|
|
||
|
// UpdateHotspots update all hotspots
|
||
|
func (s *Service) UpdateHotspots(force bool) (err error) {
|
||
|
var c = context.TODO()
|
||
|
hotspots, err := s.dao.Hotspots(c)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
if len(hotspots) == 0 {
|
||
|
err = s.dao.DelCacheHotspots(c)
|
||
|
return
|
||
|
}
|
||
|
for _, hot := range hotspots {
|
||
|
if err = s.genHotspot(c, hot, force); err != nil {
|
||
|
dao.PromError("hotspots:生成")
|
||
|
log.Error("hotspots s.genHotspot(%v, %v) err:%v", hot.Tag, force, err)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
err = s.dao.AddCacheHotspots(c, hotspots)
|
||
|
for _, h := range hotspots {
|
||
|
s.dao.AddCacheHotspot(c, h.ID, h)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *Service) genHotspot(c context.Context, hot *model.Hotspot, force bool) (err error) {
|
||
|
var ok bool
|
||
|
for _, typ := range model.HotspotTypes {
|
||
|
ok, err = s.dao.ExpireHotspotArtsCache(c, typ, hot.ID)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
if !ok || force {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if ok && !force {
|
||
|
//不重新生成 赋值计数 返回
|
||
|
if s, _ := s.dao.CacheHotspot(c, hot.ID); s != nil {
|
||
|
hot.Stats = s.Stats
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
ptime := time.Now().Add(-_hotspotArtTime)
|
||
|
arts, err := s.dao.SearchArts(c, ptime.Unix())
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
// filter tags && remove top arts
|
||
|
tops := make(map[int64]bool)
|
||
|
for _, x := range hot.TopArticles {
|
||
|
tops[x] = true
|
||
|
}
|
||
|
var newArts []*model.SearchArt
|
||
|
for _, art := range arts {
|
||
|
for _, t := range art.Tags {
|
||
|
if t == hot.Tag {
|
||
|
hot.Stats.Read += art.StatsView
|
||
|
hot.Stats.Reply += art.StatsReply
|
||
|
if !tops[art.ID] {
|
||
|
newArts = append(newArts, art)
|
||
|
}
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
arts = newArts
|
||
|
if len(arts) == 0 {
|
||
|
return
|
||
|
}
|
||
|
// add cache
|
||
|
for _, typ := range model.HotspotTypes {
|
||
|
var as [][2]int64
|
||
|
for _, art := range arts {
|
||
|
as = append(as, hotspotValue(typ, art))
|
||
|
}
|
||
|
if err = s.dao.AddCacheHotspotArts(c, typ, hot.ID, as, true); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func hotspotValue(typ int8, art *model.SearchArt) [2]int64 {
|
||
|
switch typ {
|
||
|
case model.HotspotTypeView:
|
||
|
return [2]int64{art.ID, art.StatsView}
|
||
|
case model.HotspotTypePtime:
|
||
|
return [2]int64{art.ID, art.PublishTime}
|
||
|
}
|
||
|
return [2]int64{0, 0}
|
||
|
}
|
||
|
|
||
|
// AddCacheHotspotArt check article in hotspots list and add cache
|
||
|
func (s *Service) AddCacheHotspotArt(c context.Context, art *model.SearchArt) (err error) {
|
||
|
if art.PublishTime < time.Now().Add(-_hotspotArtTime).Unix() {
|
||
|
return
|
||
|
}
|
||
|
hots, all, err := s.tagsHotspots(c, art.Tags)
|
||
|
if err != nil {
|
||
|
dao.PromError("hotspots:AddCacheHotspotArt")
|
||
|
return
|
||
|
}
|
||
|
if len(hots) == 0 {
|
||
|
return
|
||
|
}
|
||
|
for _, hot := range hots {
|
||
|
hot.Stats.Read += art.StatsView
|
||
|
hot.Stats.Reply += art.StatsReply
|
||
|
if err = s.addCacheHotspotArt(c, hot.ID, art); err != nil {
|
||
|
dao.PromError("hotspots:addCacheHotspotArt")
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
err = s.dao.AddCacheHotspots(c, all)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// DelCacheHotspotArt delete art from hotspots
|
||
|
func (s *Service) DelCacheHotspotArt(c context.Context, aid int64) (err error) {
|
||
|
hots, err := s.dao.CacheHotspots(c)
|
||
|
if err != nil {
|
||
|
dao.PromError("hotspots:DelCacheHotspotArt")
|
||
|
return
|
||
|
}
|
||
|
for _, hot := range hots {
|
||
|
for _, typ := range model.HotspotTypes {
|
||
|
if err = s.dao.DelHotspotArtsCache(c, typ, hot.ID, aid); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// tagsHotspots get hotspots form tags
|
||
|
func (s *Service) tagsHotspots(c context.Context, tags []string) (res, all []*model.Hotspot, err error) {
|
||
|
all, err = s.dao.CacheHotspots(c)
|
||
|
if err != nil {
|
||
|
dao.PromError("hotspots:tagsHotspots")
|
||
|
return
|
||
|
}
|
||
|
for _, hot := range all {
|
||
|
for _, t := range tags {
|
||
|
if t == hot.Tag {
|
||
|
res = append(res, hot)
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *Service) addCacheHotspotArt(c context.Context, hotID int64, art *model.SearchArt) (err error) {
|
||
|
for _, typ := range model.HotspotTypes {
|
||
|
var ok bool
|
||
|
if ok, err = s.dao.ExpireHotspotArtsCache(c, typ, hotID); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if ok {
|
||
|
if err = s.dao.AddCacheHotspotArts(c, typ, hotID, [][2]int64{hotspotValue(typ, art)}, false); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *Service) metaToSearch(c context.Context, m *model.Meta) (res *model.SearchArt) {
|
||
|
if m == nil {
|
||
|
return
|
||
|
}
|
||
|
res = &model.SearchArt{
|
||
|
ID: m.ID,
|
||
|
PublishTime: int64(m.PublishTime),
|
||
|
}
|
||
|
for _, t := range m.Tags {
|
||
|
res.Tags = append(res.Tags, t.Name)
|
||
|
}
|
||
|
stats, _ := s.stat(c, m.ID)
|
||
|
if stats != nil {
|
||
|
res.StatsView = stats.View
|
||
|
res.StatsReply = stats.Reply
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// HotspotArts get hotspot articles
|
||
|
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) {
|
||
|
if pn <= 0 {
|
||
|
pn = 1
|
||
|
}
|
||
|
var (
|
||
|
start = (pn - 1) * ps
|
||
|
// 多取一些用于去重
|
||
|
end = start + ps - 1 + len(lastAids)
|
||
|
allIDs []int64
|
||
|
metas map[int64]*model.Meta
|
||
|
aidsm map[int64]bool
|
||
|
withRecommend bool
|
||
|
)
|
||
|
if hotspot, err = s.dao.CacheHotspot(c, id); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if hotspot == nil {
|
||
|
err = ecode.NothingFound
|
||
|
return
|
||
|
}
|
||
|
hotspot.Stats.Count, _ = s.dao.HotspotArtsCacheCount(c, sort, id)
|
||
|
if sort == model.HotspotTypeView {
|
||
|
withRecommend = true
|
||
|
}
|
||
|
recommendsLen := len(hotspot.TopArticles)
|
||
|
// 只是最新文章 无推荐
|
||
|
if (start >= recommendsLen) || !withRecommend {
|
||
|
var (
|
||
|
nids []int64
|
||
|
newArtStart = start
|
||
|
newArtEnd = end
|
||
|
)
|
||
|
if withRecommend {
|
||
|
newArtStart = start - recommendsLen
|
||
|
newArtEnd = end - recommendsLen
|
||
|
}
|
||
|
nids, _ = s.dao.HotspotArtsCache(c, sort, id, newArtStart, newArtEnd)
|
||
|
if withRecommend {
|
||
|
allIDs = uniqIDs(nids, hotspot.TopArticles)
|
||
|
} else {
|
||
|
allIDs = nids
|
||
|
}
|
||
|
} else {
|
||
|
if end < recommendsLen {
|
||
|
allIDs = hotspot.TopArticles[start : end+1]
|
||
|
} else {
|
||
|
// 混合推荐和最新文章
|
||
|
var (
|
||
|
nids []int64
|
||
|
rs = hotspot.TopArticles[start:]
|
||
|
)
|
||
|
newArtStart := 0
|
||
|
newArtEnd := (end - start) - len(rs)
|
||
|
nids, _ = s.dao.HotspotArtsCache(c, sort, id, newArtStart, newArtEnd)
|
||
|
nids = uniqIDs(nids, hotspot.TopArticles)
|
||
|
allIDs = append(rs, nids...)
|
||
|
}
|
||
|
}
|
||
|
if len(allIDs) == 0 {
|
||
|
return
|
||
|
}
|
||
|
if metas, err = s.ArticleMetas(c, allIDs); err != nil {
|
||
|
return
|
||
|
}
|
||
|
//过滤禁止显示的稿件
|
||
|
filterNoDistributeArtsMap(metas)
|
||
|
filterNoRegionArts(metas)
|
||
|
//填充数据
|
||
|
aidsm = make(map[int64]bool, len(lastAids))
|
||
|
for _, aid := range lastAids {
|
||
|
aidsm[aid] = true
|
||
|
}
|
||
|
for _, id := range allIDs {
|
||
|
if (metas == nil) || (metas[id] == nil) || aidsm[id] {
|
||
|
continue
|
||
|
}
|
||
|
art := &model.MetaWithLike{Meta: *metas[id]}
|
||
|
res = append(res, art)
|
||
|
}
|
||
|
//截断分页数据
|
||
|
if ps > len(res) {
|
||
|
ps = len(res)
|
||
|
}
|
||
|
res = res[:ps]
|
||
|
// fill like state
|
||
|
aids := []int64{}
|
||
|
for _, m := range res {
|
||
|
aids = append(aids, m.ID)
|
||
|
}
|
||
|
states, _ := s.HadLikesByMid(c, mid, aids)
|
||
|
if states == nil {
|
||
|
return
|
||
|
}
|
||
|
for _, m := range res {
|
||
|
m.LikeState = int(states[m.ID])
|
||
|
}
|
||
|
return
|
||
|
}
|