You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
501 lines
12 KiB
501 lines
12 KiB
package weeklyhonor |
|
|
|
import ( |
|
"context" |
|
"crypto/md5" |
|
"encoding/hex" |
|
"fmt" |
|
"time" |
|
|
|
"go-common/app/interface/main/creative/conf" |
|
"go-common/app/interface/main/creative/dao/account" |
|
"go-common/app/interface/main/creative/dao/archive" |
|
"go-common/app/interface/main/creative/dao/up" |
|
"go-common/app/interface/main/creative/dao/weeklyhonor" |
|
model "go-common/app/interface/main/creative/model/weeklyhonor" |
|
"go-common/app/interface/main/creative/service" |
|
accmdl "go-common/app/service/main/account/model" |
|
"go-common/app/service/main/archive/api" |
|
upmdl "go-common/app/service/main/up/model" |
|
"go-common/library/cache/memcache" |
|
"go-common/library/ecode" |
|
"go-common/library/log" |
|
"go-common/library/sync/errgroup" |
|
"go-common/library/sync/pipeline/fanout" |
|
xtime "go-common/library/time" |
|
) |
|
|
|
const ( |
|
layout = "20060102" |
|
) |
|
|
|
// Service struct. |
|
type Service struct { |
|
c *conf.Config |
|
honDao *weeklyhonor.Dao |
|
arc *archive.Dao |
|
acc *account.Dao |
|
up *up.Dao |
|
// cache chan |
|
cache *fanout.Fanout |
|
honorMap map[int][]*model.HonorWord |
|
} |
|
|
|
// New get service. |
|
func New(c *conf.Config, rpcdaos *service.RPCDaos) *Service { |
|
s := &Service{ |
|
c: c, |
|
honDao: weeklyhonor.New(c), |
|
arc: rpcdaos.Arc, |
|
acc: rpcdaos.Acc, |
|
up: rpcdaos.Up, |
|
// cache |
|
cache: fanout.New("cache"), |
|
} |
|
s.honorMap = model.HMap() |
|
return s |
|
} |
|
|
|
// Ping service. |
|
func (s *Service) Ping(c context.Context) (err error) { |
|
if err = s.honDao.Ping(c); err != nil { |
|
log.Error("s.honor.Ping err(%v)", err) |
|
} |
|
return |
|
} |
|
|
|
// Close dao. |
|
func (s *Service) Close() { |
|
s.honDao.Close() |
|
} |
|
|
|
// ChangeSubState change subscribe state |
|
func (s *Service) ChangeSubState(c context.Context, mid int64, state uint8) (err error) { |
|
err = s.honDao.ChangeUpSwitch(c, mid, state) |
|
if err != nil { |
|
log.Error("s.honDao.ChangeUpSwitch mid(%d) state(%d) err(%v)", mid, state, err) |
|
} |
|
return |
|
} |
|
|
|
// WeeklyHonor . |
|
func (s *Service) WeeklyHonor(c context.Context, mid, uid int64, token string) (h *model.Honor, err error) { |
|
var upMid = mid |
|
h = &model.Honor{} |
|
if uid != 0 && uid != upMid { |
|
if token != s.genToken(uid) { |
|
return nil, ecode.RequestErr |
|
} |
|
upMid = uid |
|
} |
|
h.MID = upMid |
|
var upInfo *upmdl.UpInfo |
|
upInfo, err = s.up.UpInfo(c, upMid, 0, "") |
|
if err != nil { |
|
log.Error("s.up.UpInfo(%d) error(%v)", upMid, err) |
|
return |
|
} |
|
if upInfo.IsAuthor != 1 { |
|
return nil, ecode.CreativeNotUper |
|
} |
|
if upMid == mid { |
|
h.SubState, err = s.honDao.GetUpSwitch(c, upMid) |
|
if err != nil { |
|
log.Error("s.honDao.GetUpSwitch upMid(%d),err(%v)", upMid, err) |
|
} |
|
go s.addHonorClickCount(context.Background(), mid) |
|
} |
|
var timeUp bool |
|
h.DateBegin, h.DateEnd, timeUp = honTimeFrame() |
|
endStr := time.Unix(int64(h.DateEnd), 0).Format(layout) |
|
h.ShareToken = s.genToken(upMid) |
|
hs, err := s.honorStat(c, upMid, endStr) |
|
if hs == nil || err != nil || s.c.HonorDegradeSwitch { |
|
return s.degrade(c, h) |
|
} |
|
hl, err := s.honDao.HonorMC(c, h.MID, endStr) |
|
if err != nil { |
|
log.Error("s.honDao.HonorMC() error(%v)", err) |
|
return s.degrade(c, h) |
|
} |
|
if hl != nil { |
|
h.HID = hl.HID |
|
h.HonorCount = hl.Count |
|
} else { |
|
h, err = s.genHonor(c, h, endStr, timeUp, hs) |
|
if err != nil { |
|
return |
|
} |
|
} |
|
if h.HID == 0 { |
|
return s.degrade(c, h) |
|
} |
|
if err = s.rpcFill(c, hs, h); err != nil { |
|
return |
|
} |
|
s.wordFill(hs, h) |
|
h.RiseStage = s.stars(hs) |
|
return |
|
} |
|
|
|
func (s *Service) addHonorClickCount(c context.Context, mid int64) { |
|
err := s.honDao.ClickMC(c, mid) |
|
if err != memcache.ErrNotFound { |
|
if err != nil { |
|
log.Error("s.honDao.ClickMC mid(%d) err(%+v)", mid, err) |
|
} |
|
return |
|
} |
|
err = s.honDao.SetClickMC(c, mid) |
|
if err != nil { |
|
log.Error("s.honDao.SetClickMC mid(%d) err(%+v)", mid, err) |
|
return |
|
} |
|
err = s.honDao.UpsertClickCount(c, mid) |
|
if err != nil { |
|
log.Error("failed to add honor click count,mid(%d) err(%+v)", mid, err) |
|
} |
|
} |
|
|
|
func (s *Service) honorStat(c context.Context, mid int64, date string) (hs *model.HonorStat, err error) { |
|
hs, err = s.honDao.StatMC(c, mid, date) |
|
if hs != nil && err == nil { |
|
return |
|
} |
|
for i := 0; i < 3; i++ { |
|
hs, err = s.honDao.HonorStat(c, mid, date) |
|
if err != nil { |
|
log.Error("s.honDao.HonorStat(%d,%v) error(%v)", mid, date, err) |
|
continue |
|
} |
|
if hs != nil { |
|
break |
|
} |
|
} |
|
_ = s.cache.Do(c, func(c context.Context) { |
|
_ = s.honDao.SetStatMC(c, mid, date, hs) |
|
}) |
|
return |
|
} |
|
|
|
func (s *Service) degrade(c context.Context, h *model.Honor) (*model.Honor, error) { |
|
h.Word = s.honorMap[58][0].Word |
|
h.Text = s.honorMap[58][0].Text |
|
h.Priority = s.honorMap[58][0].Priority |
|
us, err := s.acc.Infos(c, []int64{h.MID}, "") |
|
if err != nil { |
|
log.Error("s.acc.Infos(%v) error(%v)", h.MID, err) |
|
return nil, err |
|
} |
|
if u, ok := us[h.MID]; ok { |
|
h.Uname = u.Name |
|
h.Face = u.Face |
|
} |
|
return h, nil |
|
} |
|
|
|
func (s *Service) wordFill(hs *model.HonorStat, h *model.Honor) { |
|
hw := s.wordFormat(h, hs) |
|
h.Word = hw.Word |
|
h.Text = hw.Text |
|
h.Desc = hw.Desc |
|
h.Priority = hw.Priority |
|
} |
|
|
|
func (s *Service) wordFormat(h *model.Honor, stat *model.HonorStat) *model.HonorWord { |
|
hid := h.HID |
|
uname := h.Uname |
|
hm := s.honorMap |
|
hw := new(model.HonorWord) |
|
if _, ok := hm[hid]; !ok { |
|
return hw |
|
} |
|
hwfmt := s.getHWFmt(h) |
|
hw.Word = hwfmt.Word |
|
hw.Text = hwfmt.Text |
|
hw.Desc = hwfmt.Desc |
|
hw.Priority = hwfmt.Priority |
|
switch hid { |
|
case 2, 3, 4, 59: |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, stat.Rank0) |
|
case 5: |
|
var str string |
|
if stat.Play/100000000 > 0 { |
|
str = fmt.Sprintf("%d个亿", stat.Play/100000000) |
|
} else { |
|
str = fmt.Sprintf("%d千万", stat.Play/10000000) |
|
} |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, str) |
|
case 6: |
|
hw.Text = fmt.Sprintf(hwfmt.Text, uname) |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, stat.Fans/1000000) |
|
case 8: |
|
_, partion, rank := stat.PartionRank() |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, partion, rank) |
|
case 9: |
|
num := stat.Play / 100000 |
|
if stat.Play/1000000 > 0 { |
|
num = stat.Play / 1000000 * 10 |
|
} |
|
hw.Text = fmt.Sprintf(hwfmt.Text, num*10*2) |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, num*10) |
|
case 10: |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, stat.Fans/10000) |
|
case 12, 30: |
|
hw.Text = fmt.Sprintf(hwfmt.Text, uname) |
|
case 17: |
|
hw.Text = fmt.Sprintf(hwfmt.Text, stat.Play/10000*2) |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, stat.Play/10000) |
|
case 18: |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, stat.Fans/10000) |
|
case 26: |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, stat.Play/1000) |
|
case 27: |
|
hw.Desc = fmt.Sprintf(hwfmt.Desc, stat.Fans/1000) |
|
} |
|
return hw |
|
} |
|
|
|
func (s *Service) stars(hs *model.HonorStat) *model.RiseStage { |
|
_, stars := hs.PrioritySR() |
|
_, starsR := hs.PriorityR() |
|
stars = s.outputBig(stars, starsR) |
|
_, starsA := hs.PriorityA() |
|
stars = s.outputBig(stars, starsA) |
|
_, starsB := hs.PriorityB() |
|
stars = s.outputBig(stars, starsB) |
|
_, starsC := hs.PriorityC() |
|
stars = s.outputBig(stars, starsC) |
|
return stars |
|
} |
|
|
|
func (s *Service) outputBig(a, b *model.RiseStage) *model.RiseStage { |
|
if b.Coin > a.Coin { |
|
a.Coin = b.Coin |
|
} |
|
if b.Fans > a.Fans { |
|
a.Fans = b.Fans |
|
} |
|
if b.Like > a.Like { |
|
a.Like = b.Like |
|
} |
|
if b.Play > a.Play { |
|
a.Play = b.Play |
|
} |
|
if b.Share > a.Share { |
|
a.Share = b.Share |
|
} |
|
return a |
|
} |
|
|
|
// honTimeFrame get honor time frame return start=last week Sun. end= this week Sat. |
|
func honTimeFrame() (start, end xtime.Time, timeUp bool) { |
|
lastSun := model.LatestSunday() |
|
// saturday |
|
endTime := lastSun.AddDate(0, 0, -1) |
|
timeUp = time.Now().Unix() > lastSun.Unix()+18*3600 |
|
if !timeUp { |
|
endTime = lastSun.AddDate(0, 0, -8) |
|
} |
|
// the week's (end's week-1) sunday |
|
start = xtime.Time(endTime.AddDate(0, 0, -6).Unix()) |
|
end = xtime.Time(endTime.Unix()) |
|
return |
|
} |
|
|
|
// corHW get correspond HonorWord fmt |
|
func (s *Service) getHWFmt(h *model.Honor) (hw *model.HonorWord) { |
|
hm := s.honorMap |
|
hws := hm[h.HID] |
|
if len(hws) == 1 { |
|
hw = hws[0] |
|
return |
|
} |
|
var ( |
|
l = 0 |
|
r = len(hws) - 1 |
|
m = (l + r) / 2 |
|
) |
|
if h.DateEnd > hws[r].Start { |
|
hw = hws[r] |
|
return |
|
} |
|
if h.DateEnd <= hws[l].End { |
|
hw = hws[l] |
|
return |
|
} |
|
for l < r { |
|
if h.DateEnd > hws[m].Start && h.DateEnd <= hws[m].End { |
|
break |
|
} else if h.DateEnd <= hws[m].Start { |
|
r = m - 1 |
|
} else { |
|
l = m + 1 |
|
} |
|
m = (l + r) / 2 |
|
} |
|
hw = hws[m] |
|
return |
|
} |
|
|
|
func (s *Service) genHonor(c context.Context, h *model.Honor, endStr string, timeUp bool, hs *model.HonorStat) (*model.Honor, error) { |
|
hls, err := s.honDao.HonorLogs(c, h.MID) |
|
end := h.DateEnd |
|
if err != nil { |
|
log.Error("s.honDao.HonorLogs(%d) error(%v)", h.MID, err) |
|
return s.degrade(c, h) |
|
} |
|
var lastHid int |
|
for _, v := range hls { |
|
if int64(v.MTime) < int64(end) && int64(v.MTime) > int64(end)-7*24*3600 { |
|
lastHid = v.HID |
|
} |
|
} |
|
h.HID = hs.GenHonor(h.MID, lastHid) |
|
if h.HID == 0 { |
|
return s.degrade(c, h) |
|
} |
|
var needUpdate bool |
|
if v, ok := hls[h.HID]; !ok { |
|
needUpdate = true |
|
h.HonorCount = 1 |
|
} else { |
|
h.HonorCount = v.Count |
|
if int64(v.MTime) < int64(end) { |
|
needUpdate = true |
|
h.HonorCount = v.Count + 1 |
|
} |
|
} |
|
if !timeUp { |
|
return h, nil |
|
} |
|
if needUpdate { |
|
_ = s.cache.Do(c, func(c context.Context) { |
|
if res, err1 := s.honDao.HonorMC(c, h.MID, endStr); err1 != nil || res != nil { |
|
return |
|
} |
|
if err1 := s.honDao.UpsertCount(c, h.MID, h.HID); err1 != nil { |
|
log.Error("s.honDao.UpsertCount() error(%v)", err1) |
|
return |
|
} |
|
hl := &model.HonorLog{ |
|
MID: h.MID, |
|
HID: h.HID, |
|
Count: h.HonorCount, |
|
} |
|
if err1 := s.honDao.SetHonorMC(c, h.MID, endStr, hl); err1 != nil { |
|
log.Error("s.honDao.SetHonorMC(%d,%s,%v) error(%v)", h.MID, endStr, hl, err1) |
|
} |
|
}) |
|
} else { |
|
hl := &model.HonorLog{ |
|
MID: h.MID, |
|
HID: h.HID, |
|
Count: h.HonorCount, |
|
} |
|
if err1 := s.honDao.SetHonorMC(c, h.MID, endStr, hl); err1 != nil { |
|
log.Error("s.honDao.SetHonorMC(%d,%s,%v) error(%v)", h.MID, endStr, hl, err1) |
|
} |
|
} |
|
return h, nil |
|
} |
|
|
|
func (s *Service) rpcFill(c context.Context, hs *model.HonorStat, h *model.Honor) error { |
|
mids := []int64{h.MID} |
|
h.LoveFans = make([]*accmdl.Info, 0) |
|
h.PlayFans = make([]*accmdl.Info, 0) |
|
loveFans := make([]int64, 0) |
|
playFans := make([]int64, 0) |
|
aids := make([]int64, 0) |
|
// users |
|
if hs.Act1 != 0 { |
|
loveFans = append(loveFans, int64(hs.Act1)) |
|
} |
|
if hs.Act2 != 0 { |
|
loveFans = append(loveFans, int64(hs.Act2)) |
|
} |
|
if hs.Act3 != 0 { |
|
loveFans = append(loveFans, int64(hs.Act3)) |
|
} |
|
mids = append(mids, loveFans...) |
|
if hs.Dr1 != 0 { |
|
playFans = append(playFans, int64(hs.Dr1)) |
|
} |
|
if hs.Dr2 != 0 { |
|
playFans = append(playFans, int64(hs.Dr2)) |
|
} |
|
if hs.Dr3 != 0 { |
|
playFans = append(playFans, int64(hs.Dr3)) |
|
} |
|
mids = append(mids, playFans...) |
|
// arcs |
|
if hs.HottestAvNew != 0 { |
|
aids = append(aids, int64(hs.HottestAvNew)) |
|
} |
|
if hs.HottestAvInc != 0 { |
|
aids = append(aids, int64(hs.HottestAvInc)) |
|
} |
|
if hs.HottestAvAll != 0 { |
|
aids = append(aids, int64(hs.HottestAvAll)) |
|
} |
|
g := new(errgroup.Group) |
|
var ( |
|
us map[int64]*accmdl.Info |
|
arcs map[int64]*api.Arc |
|
) |
|
if len(aids) > 0 { |
|
g.Go(func() (err error) { |
|
arcs, err = s.arc.Archives(c, aids, "") |
|
if err != nil { |
|
log.Error("s.arc.Archives(%v) error(%v)", aids, err) |
|
return err |
|
} |
|
return nil |
|
}) |
|
} |
|
g.Go(func() (err error) { |
|
us, err = s.acc.Infos(c, mids, "") |
|
if err != nil { |
|
log.Error("s.acc.Infos(%v) error(%v)", mids, err) |
|
return err |
|
} |
|
return nil |
|
}) |
|
err := g.Wait() |
|
if u, ok := us[h.MID]; ok { |
|
h.Uname = u.Name |
|
h.Face = u.Face |
|
} |
|
for _, uid := range loveFans { |
|
if u, ok := us[uid]; ok { |
|
h.LoveFans = append(h.LoveFans, u) |
|
} |
|
} |
|
for _, uid := range playFans { |
|
if u, ok := us[uid]; ok { |
|
h.PlayFans = append(h.PlayFans, u) |
|
} |
|
} |
|
if hs.HottestAvInc != 0 { |
|
if arc, ok := arcs[int64(hs.HottestAvInc)]; ok { |
|
h.NewArchive = arc |
|
} |
|
} |
|
if hs.HottestAvNew != 0 { |
|
if arc, ok := arcs[int64(hs.HottestAvNew)]; ok { |
|
h.NewArchive = arc |
|
} |
|
} |
|
if hs.HottestAvAll != 0 { |
|
if arc, ok := arcs[int64(hs.HottestAvAll)]; ok { |
|
h.HotArchive = arc |
|
} |
|
} |
|
return err |
|
} |
|
|
|
func (s *Service) genToken(mid int64) string { |
|
bs := []byte(fmt.Sprintf("bili%d", mid*3+223333)) |
|
hs := md5.Sum(bs) |
|
return hex.EncodeToString(hs[:]) |
|
}
|
|
|