go-common/app/interface/main/creative/service/weeklyhonor/service.go
2019-04-22 18:49:16 +08:00

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[:])
}