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

486 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}