123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- package data
- import (
- "context"
- "sort"
- "sync"
- "time"
- "go-common/library/ecode"
- "go-common/app/interface/main/creative/model/archive"
- "go-common/app/interface/main/creative/model/data"
- "go-common/app/service/main/archive/api"
- "go-common/library/log"
- "go-common/library/sync/errgroup"
- "math"
- )
- var (
- zeroSummary = map[string]int64{
- "total": 0,
- "inc": 0,
- "play": 0,
- "dm": 0,
- "reply": 0,
- "coin": 0,
- "inter": 0,
- "vv": 0,
- "da": 0,
- "re": 0,
- "co": 0,
- "fv": 0,
- "sh": 0,
- "lk": 0,
- }
- )
- // UpFansAnalysisForApp get app fans analysis.
- func (s *Service) UpFansAnalysisForApp(c context.Context, mid int64, ty int, ip string) (res *data.AppFan, err error) {
- var (
- g, ctx = errgroup.WithContext(c)
- origin *data.AppFan
- typeList map[string]int64
- tagList []*data.Rank
- viewerArea map[string]int64
- viewerBase *data.ViewerBase
- )
- res = &data.AppFan{}
- if origin, err = s.data.UpFansAnalysisForApp(c, mid, ty); err != nil {
- log.Error("s.data.UpFansAnalysisForApp mid(%d)|ip(%s)|err(%v)", mid, ip, err)
- return
- }
- if origin == nil {
- return
- }
- if origin.Summary == nil {
- origin.Summary = zeroSummary
- }
- log.Info("s.data.UpFansAnalysisForApp origin mid(%d)|origin(%+v)", mid, origin)
- g.Go(func() (err error) {
- total, ok := origin.Summary["total"]
- inc, oki := origin.Summary["inc"]
- if !ok || !oki {
- return
- }
- pfl, err := s.acc.ProfileWithStat(ctx, mid)
- if err != nil {
- err = nil
- log.Error("s.acc.ProfileWithStat mid(%d)|err(%v)", mid, err)
- return
- }
- if pfl == nil {
- log.Error("s.acc.ProfileWithStat mid(%d)|Follower(%+v) err(%v)", mid, pfl, err)
- return
- }
- origin.Summary["total"] = pfl.Follower
- origin.Summary["inc"] = inc + (pfl.Follower - total)
- return
- })
- g.Go(func() (err error) {
- if tys, tgs, err := s.fanTrend(ctx, mid); err == nil {
- typeList = tys
- tagList = tgs
- }
- return nil
- })
- g.Go(func() (err error) {
- if va, err := s.fanViewerArea(ctx, mid); err == nil {
- viewerArea = va
- }
- return nil
- })
- g.Go(func() (err error) {
- if vb, err := s.fanViewerBase(ctx, mid); err == nil {
- viewerBase = vb
- }
- return nil
- })
- g.Wait()
- res.Summary = origin.Summary
- res.TypeList = typeList
- res.TagList = tagList
- res.ViewerArea = viewerArea
- res.ViewerBase = viewerBase
- log.Info("UpFansAnalysisForApp mid(%d)|res(%+v)", mid, res)
- return
- }
- //FanRankApp get app fans rank list.
- func (s *Service) FanRankApp(c context.Context, mid int64, ty int, ip string) (res map[string][]*data.RankInfo, err error) {
- var (
- origin *data.AppFan
- rkList map[string][]*data.RankInfo
- )
- if origin, err = s.data.UpFansAnalysisForApp(c, mid, ty); err != nil {
- log.Error("s.data.UpFansAnalysisForApp mid(%d)|ip(%s)|err(%v)", mid, ip, err)
- return
- }
- if origin == nil {
- return
- }
- log.Info("s.UpFansRankApp origin mid(%d)|origin(%+v)", mid, origin)
- if rkList, err = s.getTopList(c, mid, origin.RankMap, ip); err != nil {
- log.Error("s.getTopList err(%v)", err)
- }
- if len(rkList) == 0 { //排行列表容错,必须返回对应的key
- rkList = make(map[string][]*data.RankInfo)
- }
- for _, key := range rankKeys {
- if v, ok := rkList[key]; ok {
- rkList[key] = v
- } else {
- rkList[key] = nil
- }
- }
- res = rkList
- log.Info("s.UpFansRankApp mid(%d)|res(%+v)|len[rkList](%d)", mid, res, len(rkList))
- return
- }
- func (s *Service) fanTrend(c context.Context, mid int64) (tys map[string]int64, tags []*data.Rank, err error) {
- var (
- dt = getDate()
- trend map[string]*data.ViewerTrend
- )
- if trend, err = s.viewerTrend(c, mid, dt); err != nil {
- log.Error("fanTrend s.viewerTrend mid(%d)|err(%v)", mid, err)
- return
- }
- if tr, ok := trend["fan"]; ok && tr != nil {
- tys = tr.Ty
- skeys := []int{2, 1, 4, 3, 6, 5, 8, 7, 10, 9}
- for _, k := range skeys {
- if v, ok := tr.Tag[k]; ok {
- tg := &data.Rank{
- Rank: k,
- Name: v,
- }
- tags = append(tags, tg)
- }
- }
- }
- return
- }
- func (s *Service) fanViewerArea(c context.Context, mid int64) (res map[string]int64, err error) {
- var (
- origin map[string]map[string]int64
- )
- if origin, err = s.data.ViewerArea(c, mid, getDate()); err != nil {
- log.Error("fanViewerBase s.data.ViewerArea mid(%d)|err(%v)", mid, err)
- }
- if len(origin) == 0 {
- return
- }
- if v, ok := origin["fan"]; ok {
- res = v
- }
- return
- }
- func (s *Service) fanViewerBase(c context.Context, mid int64) (res *data.ViewerBase, err error) {
- var (
- origin map[string]*data.ViewerBase
- )
- if origin, err = s.data.ViewerBase(c, mid, getDate()); err != nil {
- log.Error("fanViewerBase s.data.ViewerArea mid(%d)|err(%v)", mid, err)
- }
- if len(origin) == 0 {
- return
- }
- if v, ok := origin["fan"]; ok {
- res = v
- }
- return
- }
- //OverView for app data overview.
- func (s *Service) OverView(c context.Context, mid int64, ty int8, ip string) (res *data.AppOverView, err error) {
- var (
- g, ctx = errgroup.WithContext(c)
- stat *data.Stat
- allIncr []*data.ThirtyDay
- singleArcInc []*data.ArcInc
- )
- g.Go(func() (err error) {
- if stat, err = s.NewStat(ctx, mid, ip); err != nil {
- err = nil
- log.Error("OverView s.ThirtyDayArchive mid(%d)|err(%v)", mid, err)
- }
- return nil
- })
- g.Go(func() (err error) {
- if allIncr, err = s.ThirtyDayArchive(ctx, mid, ty); err != nil {
- err = nil
- log.Error("OverView s.ThirtyDayArchive mid(%d)|err(%v)", mid, err)
- }
- if len(allIncr) >= 7 {
- allIncr = allIncr[0:7]
- }
- return nil
- })
- g.Go(func() (err error) {
- if prop, err := s.AppUpIncr(c, mid, ty, ip); err == nil && len(prop) > 0 {
- if prop[len(prop)-1] != nil { //获取最后一天的数据
- singleArcInc = prop[len(prop)-1].Arcs
- }
- }
- return nil
- })
- g.Wait()
- res = &data.AppOverView{
- Stat: stat,
- AllArcIncr: allIncr,
- SingleArcInc: singleArcInc,
- }
- return
- }
- // ArchiveAnalyze get single archive data.
- func (s *Service) ArchiveAnalyze(c context.Context, aid, mid int64, ip string) (stat *data.ArchiveData, err error) {
- // check aid valid and owner
- isWhite := false
- for _, m := range s.c.Whitelist.DataMids {
- if m == mid {
- isWhite = true
- break
- }
- }
- a, re := s.arc.Archive(c, aid, ip)
- if re != nil {
- err = re
- return
- }
- if a == nil {
- err = ecode.AccessDenied
- return
- }
- if !isWhite && a.Author.Mid != mid {
- err = ecode.AccessDenied
- return
- }
- stat, err = s.data.ArchiveStat(c, aid)
- if err != nil {
- log.Error("s.data.ArchiveStat aid(%d)|mid(%d)|error(%v)", aid, mid, err)
- return
- }
- if stat == nil {
- return
- }
- if stat.ArchiveStat.Play >= 100 {
- stat.ArchiveAreas, err = s.data.ArchiveArea(c, aid)
- }
- log.Info("s.ArchiveAnalyze aid(%d)|mid(%d)|stat(%+v)", aid, mid, stat)
- return
- }
- // VideoRetention get video quit data.
- func (s *Service) VideoRetention(c context.Context, cid, mid int64, ip string) (res *data.VideoQuit, err error) {
- var (
- qs []int64
- arc *api.Arc
- video *archive.Video
- isWhite bool
- )
- for _, m := range s.c.Whitelist.DataMids {
- if m == mid {
- isWhite = true
- break
- }
- }
- if video, err = s.arc.VideoByCid(c, int64(cid), ip); err != nil {
- err = ecode.AccessDenied
- return
- }
- if video == nil {
- return
- }
- if arc, err = s.arc.Archive(c, video.Aid, ip); err == nil && !isWhite && arc.Author.Mid != mid {
- err = ecode.AccessDenied
- return
- }
- qs, err = s.data.VideoQuitPoints(c, cid)
- res = &data.VideoQuit{
- Point: appVideoQuit(qs),
- Duration: sliceDuration(qs),
- }
- log.Info("s.VideoRetention cid(%d)|mid(%d)|quitPoints(%+v)|video(%+v)|res(%+v)", cid, mid, qs, video, res)
- return
- }
- // >7 interval=(n/7四舍五入)
- func appVideoQuit(qps []int64) []int64 {
- cnt := len(qps) + 1
- if cnt <= 7 {
- return qps
- }
- nqps := make([]int64, 0)
- interval := int64(math.Floor(float64(cnt)/7.0 + 0.5))
- for i := 1; i < 8; i++ {
- k := interval * int64(i)
- if k > int64(cnt)-1 {
- break
- }
- nqps = append(nqps, qps[k-1])
- }
- return nqps
- }
- func sliceDuration(qps []int64) (ds []int64) {
- cnt := len(qps) + 1
- if cnt <= 7 {
- for i := 1; i < cnt; i++ {
- ds = append(ds, int64(i)*30)
- }
- return
- }
- interval := int64(math.Floor(float64(cnt)/7.0 + 0.5))
- for i := 1; i < 8; i++ {
- k := interval * int64(i)
- if k > int64(cnt)-1 {
- break
- }
- ds = append(ds, (k)*30)
- }
- return
- }
- // AppVideoQuitPoints get app video quit data.
- func (s *Service) AppVideoQuitPoints(c context.Context, cid, mid int64, ip string) (res []int64, err error) {
- if res, err = s.VideoQuitPoints(c, cid, mid, ip); err != nil {
- return
- }
- res = appVideoQuit(res)
- return
- }
- // AppUpIncr for Play/Dm/Reply/Fav/Share/Elec/Coin incr.
- func (s *Service) AppUpIncr(c context.Context, mid int64, ty int8, ip string) (res []*data.AppViewerIncr, err error) {
- incr, err := s.UpIncr(c, mid, ty, ip)
- if err != nil {
- return
- }
- if len(incr) == 0 {
- return
- }
- res = make([]*data.AppViewerIncr, 0, len(incr))
- sortK := make([]string, 0, len(incr))
- for k := range incr {
- sortK = append(sortK, k)
- }
- sort.Strings(sortK)
- for _, k := range sortK {
- v, ok := incr[k]
- if !ok {
- continue
- }
- if v != nil {
- av := &data.AppViewerIncr{}
- tm, _ := time.Parse("20060102", k)
- av.DateKey = tm.Unix()
- av.Arcs = v.Arcs
- av.TotalIncr = v.TotalIncr
- trs := make([]*data.Rank, 0, len(v.TyRank))
- for rk, rv := range v.TyRank {
- tr := &data.Rank{}
- tr.Name = rk
- tr.Rank = rv
- trs = append(trs, tr)
- }
- av.TyRank = trs
- res = append(res, av)
- }
- }
- return
- }
- // AppStat get app archive static data.
- func (s *Service) AppStat(c context.Context, mid int64) (sts *data.AppStatList, err error) {
- sts = &data.AppStatList{}
- sts.Show = 1
- if sts.Show == 0 {
- return
- }
- var (
- wg sync.WaitGroup
- viewChan = make(chan *data.AppStat, 6)
- fanChan = make(chan *data.AppStat, 6)
- comChan = make(chan *data.AppStat, 6)
- dmChan = make(chan *data.AppStat, 6)
- )
- for i := 0; i < 6; i++ {
- wg.Add(1)
- go func(i int) {
- defer wg.Done()
- datekey := time.Now().AddDate(0, 0, -1-i).Add(-12 * time.Hour).Format("2006-01-02")
- dt := time.Now().AddDate(0, 0, -1-i).Add(-12 * time.Hour).Format("20060102")
- res, err := s.data.UpStat(c, mid, dt)
- if err != nil {
- log.Error("s.data.UpStat mid(%d)|err(%v)", mid, err)
- return
- }
- if res == nil {
- return
- }
- viewChan <- &data.AppStat{Date: datekey, Num: res.View}
- fanChan <- &data.AppStat{Date: datekey, Num: res.Fans}
- comChan <- &data.AppStat{Date: datekey, Num: res.Reply}
- dmChan <- &data.AppStat{Date: datekey, Num: res.Dm}
- }(i)
- }
- wg.Wait()
- close(viewChan)
- close(fanChan)
- close(comChan)
- close(dmChan)
- for v := range viewChan {
- sts.View = append(sts.View, v)
- }
- for v := range fanChan {
- sts.Fans = append(sts.Fans, v)
- }
- for v := range comChan {
- sts.Comment = append(sts.Comment, v)
- }
- for v := range dmChan {
- sts.Danmu = append(sts.Danmu, v)
- }
- sort.Slice(sts.View, func(i, j int) bool {
- return sts.View[i].Date > sts.View[j].Date
- })
- sort.Slice(sts.Fans, func(i, j int) bool {
- return sts.Fans[i].Date > sts.Fans[j].Date
- })
- sort.Slice(sts.Comment, func(i, j int) bool {
- return sts.Comment[i].Date > sts.Comment[j].Date
- })
- sort.Slice(sts.Danmu, func(i, j int) bool {
- return sts.Danmu[i].Date > sts.Danmu[j].Date
- })
- // set increment num
- for i := 0; i < len(sts.View)-1; i++ {
- if sts.View[i].Num = sts.View[i].Num - sts.View[i+1].Num; sts.View[i].Num < 0 {
- sts.View[i].Num = 0
- }
- if sts.Fans[i].Num = sts.Fans[i].Num - sts.Fans[i+1].Num; sts.Fans[i].Num < 0 {
- sts.Fans[i].Num = 0
- }
- if sts.Comment[i].Num = sts.Comment[i].Num - sts.Comment[i+1].Num; sts.Comment[i].Num < 0 {
- sts.Comment[i].Num = 0
- }
- if sts.Danmu[i].Num = sts.Danmu[i].Num - sts.Danmu[i+1].Num; sts.Danmu[i].Num < 0 {
- sts.Danmu[i].Num = 0
- }
- }
- // rm last element
- if len(sts.View) > 0 {
- sts.View = sts.View[:len(sts.View)-1]
- sts.Fans = sts.Fans[:len(sts.Fans)-1]
- sts.Comment = sts.Comment[:len(sts.Comment)-1]
- sts.Danmu = sts.Danmu[:len(sts.Danmu)-1]
- }
- return
- }
|