502 lines
12 KiB
Go
502 lines
12 KiB
Go
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[:])
|
|
}
|