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