app.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. package data
  2. import (
  3. "context"
  4. "sort"
  5. "sync"
  6. "time"
  7. "go-common/library/ecode"
  8. "go-common/app/interface/main/creative/model/archive"
  9. "go-common/app/interface/main/creative/model/data"
  10. "go-common/app/service/main/archive/api"
  11. "go-common/library/log"
  12. "go-common/library/sync/errgroup"
  13. "math"
  14. )
  15. var (
  16. zeroSummary = map[string]int64{
  17. "total": 0,
  18. "inc": 0,
  19. "play": 0,
  20. "dm": 0,
  21. "reply": 0,
  22. "coin": 0,
  23. "inter": 0,
  24. "vv": 0,
  25. "da": 0,
  26. "re": 0,
  27. "co": 0,
  28. "fv": 0,
  29. "sh": 0,
  30. "lk": 0,
  31. }
  32. )
  33. // UpFansAnalysisForApp get app fans analysis.
  34. func (s *Service) UpFansAnalysisForApp(c context.Context, mid int64, ty int, ip string) (res *data.AppFan, err error) {
  35. var (
  36. g, ctx = errgroup.WithContext(c)
  37. origin *data.AppFan
  38. typeList map[string]int64
  39. tagList []*data.Rank
  40. viewerArea map[string]int64
  41. viewerBase *data.ViewerBase
  42. )
  43. res = &data.AppFan{}
  44. if origin, err = s.data.UpFansAnalysisForApp(c, mid, ty); err != nil {
  45. log.Error("s.data.UpFansAnalysisForApp mid(%d)|ip(%s)|err(%v)", mid, ip, err)
  46. return
  47. }
  48. if origin == nil {
  49. return
  50. }
  51. if origin.Summary == nil {
  52. origin.Summary = zeroSummary
  53. }
  54. log.Info("s.data.UpFansAnalysisForApp origin mid(%d)|origin(%+v)", mid, origin)
  55. g.Go(func() (err error) {
  56. total, ok := origin.Summary["total"]
  57. inc, oki := origin.Summary["inc"]
  58. if !ok || !oki {
  59. return
  60. }
  61. pfl, err := s.acc.ProfileWithStat(ctx, mid)
  62. if err != nil {
  63. err = nil
  64. log.Error("s.acc.ProfileWithStat mid(%d)|err(%v)", mid, err)
  65. return
  66. }
  67. if pfl == nil {
  68. log.Error("s.acc.ProfileWithStat mid(%d)|Follower(%+v) err(%v)", mid, pfl, err)
  69. return
  70. }
  71. origin.Summary["total"] = pfl.Follower
  72. origin.Summary["inc"] = inc + (pfl.Follower - total)
  73. return
  74. })
  75. g.Go(func() (err error) {
  76. if tys, tgs, err := s.fanTrend(ctx, mid); err == nil {
  77. typeList = tys
  78. tagList = tgs
  79. }
  80. return nil
  81. })
  82. g.Go(func() (err error) {
  83. if va, err := s.fanViewerArea(ctx, mid); err == nil {
  84. viewerArea = va
  85. }
  86. return nil
  87. })
  88. g.Go(func() (err error) {
  89. if vb, err := s.fanViewerBase(ctx, mid); err == nil {
  90. viewerBase = vb
  91. }
  92. return nil
  93. })
  94. g.Wait()
  95. res.Summary = origin.Summary
  96. res.TypeList = typeList
  97. res.TagList = tagList
  98. res.ViewerArea = viewerArea
  99. res.ViewerBase = viewerBase
  100. log.Info("UpFansAnalysisForApp mid(%d)|res(%+v)", mid, res)
  101. return
  102. }
  103. //FanRankApp get app fans rank list.
  104. func (s *Service) FanRankApp(c context.Context, mid int64, ty int, ip string) (res map[string][]*data.RankInfo, err error) {
  105. var (
  106. origin *data.AppFan
  107. rkList map[string][]*data.RankInfo
  108. )
  109. if origin, err = s.data.UpFansAnalysisForApp(c, mid, ty); err != nil {
  110. log.Error("s.data.UpFansAnalysisForApp mid(%d)|ip(%s)|err(%v)", mid, ip, err)
  111. return
  112. }
  113. if origin == nil {
  114. return
  115. }
  116. log.Info("s.UpFansRankApp origin mid(%d)|origin(%+v)", mid, origin)
  117. if rkList, err = s.getTopList(c, mid, origin.RankMap, ip); err != nil {
  118. log.Error("s.getTopList err(%v)", err)
  119. }
  120. if len(rkList) == 0 { //排行列表容错,必须返回对应的key
  121. rkList = make(map[string][]*data.RankInfo)
  122. }
  123. for _, key := range rankKeys {
  124. if v, ok := rkList[key]; ok {
  125. rkList[key] = v
  126. } else {
  127. rkList[key] = nil
  128. }
  129. }
  130. res = rkList
  131. log.Info("s.UpFansRankApp mid(%d)|res(%+v)|len[rkList](%d)", mid, res, len(rkList))
  132. return
  133. }
  134. func (s *Service) fanTrend(c context.Context, mid int64) (tys map[string]int64, tags []*data.Rank, err error) {
  135. var (
  136. dt = getDate()
  137. trend map[string]*data.ViewerTrend
  138. )
  139. if trend, err = s.viewerTrend(c, mid, dt); err != nil {
  140. log.Error("fanTrend s.viewerTrend mid(%d)|err(%v)", mid, err)
  141. return
  142. }
  143. if tr, ok := trend["fan"]; ok && tr != nil {
  144. tys = tr.Ty
  145. skeys := []int{2, 1, 4, 3, 6, 5, 8, 7, 10, 9}
  146. for _, k := range skeys {
  147. if v, ok := tr.Tag[k]; ok {
  148. tg := &data.Rank{
  149. Rank: k,
  150. Name: v,
  151. }
  152. tags = append(tags, tg)
  153. }
  154. }
  155. }
  156. return
  157. }
  158. func (s *Service) fanViewerArea(c context.Context, mid int64) (res map[string]int64, err error) {
  159. var (
  160. origin map[string]map[string]int64
  161. )
  162. if origin, err = s.data.ViewerArea(c, mid, getDate()); err != nil {
  163. log.Error("fanViewerBase s.data.ViewerArea mid(%d)|err(%v)", mid, err)
  164. }
  165. if len(origin) == 0 {
  166. return
  167. }
  168. if v, ok := origin["fan"]; ok {
  169. res = v
  170. }
  171. return
  172. }
  173. func (s *Service) fanViewerBase(c context.Context, mid int64) (res *data.ViewerBase, err error) {
  174. var (
  175. origin map[string]*data.ViewerBase
  176. )
  177. if origin, err = s.data.ViewerBase(c, mid, getDate()); err != nil {
  178. log.Error("fanViewerBase s.data.ViewerArea mid(%d)|err(%v)", mid, err)
  179. }
  180. if len(origin) == 0 {
  181. return
  182. }
  183. if v, ok := origin["fan"]; ok {
  184. res = v
  185. }
  186. return
  187. }
  188. //OverView for app data overview.
  189. func (s *Service) OverView(c context.Context, mid int64, ty int8, ip string) (res *data.AppOverView, err error) {
  190. var (
  191. g, ctx = errgroup.WithContext(c)
  192. stat *data.Stat
  193. allIncr []*data.ThirtyDay
  194. singleArcInc []*data.ArcInc
  195. )
  196. g.Go(func() (err error) {
  197. if stat, err = s.NewStat(ctx, mid, ip); err != nil {
  198. err = nil
  199. log.Error("OverView s.ThirtyDayArchive mid(%d)|err(%v)", mid, err)
  200. }
  201. return nil
  202. })
  203. g.Go(func() (err error) {
  204. if allIncr, err = s.ThirtyDayArchive(ctx, mid, ty); err != nil {
  205. err = nil
  206. log.Error("OverView s.ThirtyDayArchive mid(%d)|err(%v)", mid, err)
  207. }
  208. if len(allIncr) >= 7 {
  209. allIncr = allIncr[0:7]
  210. }
  211. return nil
  212. })
  213. g.Go(func() (err error) {
  214. if prop, err := s.AppUpIncr(c, mid, ty, ip); err == nil && len(prop) > 0 {
  215. if prop[len(prop)-1] != nil { //获取最后一天的数据
  216. singleArcInc = prop[len(prop)-1].Arcs
  217. }
  218. }
  219. return nil
  220. })
  221. g.Wait()
  222. res = &data.AppOverView{
  223. Stat: stat,
  224. AllArcIncr: allIncr,
  225. SingleArcInc: singleArcInc,
  226. }
  227. return
  228. }
  229. // ArchiveAnalyze get single archive data.
  230. func (s *Service) ArchiveAnalyze(c context.Context, aid, mid int64, ip string) (stat *data.ArchiveData, err error) {
  231. // check aid valid and owner
  232. isWhite := false
  233. for _, m := range s.c.Whitelist.DataMids {
  234. if m == mid {
  235. isWhite = true
  236. break
  237. }
  238. }
  239. a, re := s.arc.Archive(c, aid, ip)
  240. if re != nil {
  241. err = re
  242. return
  243. }
  244. if a == nil {
  245. err = ecode.AccessDenied
  246. return
  247. }
  248. if !isWhite && a.Author.Mid != mid {
  249. err = ecode.AccessDenied
  250. return
  251. }
  252. stat, err = s.data.ArchiveStat(c, aid)
  253. if err != nil {
  254. log.Error("s.data.ArchiveStat aid(%d)|mid(%d)|error(%v)", aid, mid, err)
  255. return
  256. }
  257. if stat == nil {
  258. return
  259. }
  260. if stat.ArchiveStat.Play >= 100 {
  261. stat.ArchiveAreas, err = s.data.ArchiveArea(c, aid)
  262. }
  263. log.Info("s.ArchiveAnalyze aid(%d)|mid(%d)|stat(%+v)", aid, mid, stat)
  264. return
  265. }
  266. // VideoRetention get video quit data.
  267. func (s *Service) VideoRetention(c context.Context, cid, mid int64, ip string) (res *data.VideoQuit, err error) {
  268. var (
  269. qs []int64
  270. arc *api.Arc
  271. video *archive.Video
  272. isWhite bool
  273. )
  274. for _, m := range s.c.Whitelist.DataMids {
  275. if m == mid {
  276. isWhite = true
  277. break
  278. }
  279. }
  280. if video, err = s.arc.VideoByCid(c, int64(cid), ip); err != nil {
  281. err = ecode.AccessDenied
  282. return
  283. }
  284. if video == nil {
  285. return
  286. }
  287. if arc, err = s.arc.Archive(c, video.Aid, ip); err == nil && !isWhite && arc.Author.Mid != mid {
  288. err = ecode.AccessDenied
  289. return
  290. }
  291. qs, err = s.data.VideoQuitPoints(c, cid)
  292. res = &data.VideoQuit{
  293. Point: appVideoQuit(qs),
  294. Duration: sliceDuration(qs),
  295. }
  296. log.Info("s.VideoRetention cid(%d)|mid(%d)|quitPoints(%+v)|video(%+v)|res(%+v)", cid, mid, qs, video, res)
  297. return
  298. }
  299. // >7 interval=(n/7四舍五入)
  300. func appVideoQuit(qps []int64) []int64 {
  301. cnt := len(qps) + 1
  302. if cnt <= 7 {
  303. return qps
  304. }
  305. nqps := make([]int64, 0)
  306. interval := int64(math.Floor(float64(cnt)/7.0 + 0.5))
  307. for i := 1; i < 8; i++ {
  308. k := interval * int64(i)
  309. if k > int64(cnt)-1 {
  310. break
  311. }
  312. nqps = append(nqps, qps[k-1])
  313. }
  314. return nqps
  315. }
  316. func sliceDuration(qps []int64) (ds []int64) {
  317. cnt := len(qps) + 1
  318. if cnt <= 7 {
  319. for i := 1; i < cnt; i++ {
  320. ds = append(ds, int64(i)*30)
  321. }
  322. return
  323. }
  324. interval := int64(math.Floor(float64(cnt)/7.0 + 0.5))
  325. for i := 1; i < 8; i++ {
  326. k := interval * int64(i)
  327. if k > int64(cnt)-1 {
  328. break
  329. }
  330. ds = append(ds, (k)*30)
  331. }
  332. return
  333. }
  334. // AppVideoQuitPoints get app video quit data.
  335. func (s *Service) AppVideoQuitPoints(c context.Context, cid, mid int64, ip string) (res []int64, err error) {
  336. if res, err = s.VideoQuitPoints(c, cid, mid, ip); err != nil {
  337. return
  338. }
  339. res = appVideoQuit(res)
  340. return
  341. }
  342. // AppUpIncr for Play/Dm/Reply/Fav/Share/Elec/Coin incr.
  343. func (s *Service) AppUpIncr(c context.Context, mid int64, ty int8, ip string) (res []*data.AppViewerIncr, err error) {
  344. incr, err := s.UpIncr(c, mid, ty, ip)
  345. if err != nil {
  346. return
  347. }
  348. if len(incr) == 0 {
  349. return
  350. }
  351. res = make([]*data.AppViewerIncr, 0, len(incr))
  352. sortK := make([]string, 0, len(incr))
  353. for k := range incr {
  354. sortK = append(sortK, k)
  355. }
  356. sort.Strings(sortK)
  357. for _, k := range sortK {
  358. v, ok := incr[k]
  359. if !ok {
  360. continue
  361. }
  362. if v != nil {
  363. av := &data.AppViewerIncr{}
  364. tm, _ := time.Parse("20060102", k)
  365. av.DateKey = tm.Unix()
  366. av.Arcs = v.Arcs
  367. av.TotalIncr = v.TotalIncr
  368. trs := make([]*data.Rank, 0, len(v.TyRank))
  369. for rk, rv := range v.TyRank {
  370. tr := &data.Rank{}
  371. tr.Name = rk
  372. tr.Rank = rv
  373. trs = append(trs, tr)
  374. }
  375. av.TyRank = trs
  376. res = append(res, av)
  377. }
  378. }
  379. return
  380. }
  381. // AppStat get app archive static data.
  382. func (s *Service) AppStat(c context.Context, mid int64) (sts *data.AppStatList, err error) {
  383. sts = &data.AppStatList{}
  384. sts.Show = 1
  385. if sts.Show == 0 {
  386. return
  387. }
  388. var (
  389. wg sync.WaitGroup
  390. viewChan = make(chan *data.AppStat, 6)
  391. fanChan = make(chan *data.AppStat, 6)
  392. comChan = make(chan *data.AppStat, 6)
  393. dmChan = make(chan *data.AppStat, 6)
  394. )
  395. for i := 0; i < 6; i++ {
  396. wg.Add(1)
  397. go func(i int) {
  398. defer wg.Done()
  399. datekey := time.Now().AddDate(0, 0, -1-i).Add(-12 * time.Hour).Format("2006-01-02")
  400. dt := time.Now().AddDate(0, 0, -1-i).Add(-12 * time.Hour).Format("20060102")
  401. res, err := s.data.UpStat(c, mid, dt)
  402. if err != nil {
  403. log.Error("s.data.UpStat mid(%d)|err(%v)", mid, err)
  404. return
  405. }
  406. if res == nil {
  407. return
  408. }
  409. viewChan <- &data.AppStat{Date: datekey, Num: res.View}
  410. fanChan <- &data.AppStat{Date: datekey, Num: res.Fans}
  411. comChan <- &data.AppStat{Date: datekey, Num: res.Reply}
  412. dmChan <- &data.AppStat{Date: datekey, Num: res.Dm}
  413. }(i)
  414. }
  415. wg.Wait()
  416. close(viewChan)
  417. close(fanChan)
  418. close(comChan)
  419. close(dmChan)
  420. for v := range viewChan {
  421. sts.View = append(sts.View, v)
  422. }
  423. for v := range fanChan {
  424. sts.Fans = append(sts.Fans, v)
  425. }
  426. for v := range comChan {
  427. sts.Comment = append(sts.Comment, v)
  428. }
  429. for v := range dmChan {
  430. sts.Danmu = append(sts.Danmu, v)
  431. }
  432. sort.Slice(sts.View, func(i, j int) bool {
  433. return sts.View[i].Date > sts.View[j].Date
  434. })
  435. sort.Slice(sts.Fans, func(i, j int) bool {
  436. return sts.Fans[i].Date > sts.Fans[j].Date
  437. })
  438. sort.Slice(sts.Comment, func(i, j int) bool {
  439. return sts.Comment[i].Date > sts.Comment[j].Date
  440. })
  441. sort.Slice(sts.Danmu, func(i, j int) bool {
  442. return sts.Danmu[i].Date > sts.Danmu[j].Date
  443. })
  444. // set increment num
  445. for i := 0; i < len(sts.View)-1; i++ {
  446. if sts.View[i].Num = sts.View[i].Num - sts.View[i+1].Num; sts.View[i].Num < 0 {
  447. sts.View[i].Num = 0
  448. }
  449. if sts.Fans[i].Num = sts.Fans[i].Num - sts.Fans[i+1].Num; sts.Fans[i].Num < 0 {
  450. sts.Fans[i].Num = 0
  451. }
  452. if sts.Comment[i].Num = sts.Comment[i].Num - sts.Comment[i+1].Num; sts.Comment[i].Num < 0 {
  453. sts.Comment[i].Num = 0
  454. }
  455. if sts.Danmu[i].Num = sts.Danmu[i].Num - sts.Danmu[i+1].Num; sts.Danmu[i].Num < 0 {
  456. sts.Danmu[i].Num = 0
  457. }
  458. }
  459. // rm last element
  460. if len(sts.View) > 0 {
  461. sts.View = sts.View[:len(sts.View)-1]
  462. sts.Fans = sts.Fans[:len(sts.Fans)-1]
  463. sts.Comment = sts.Comment[:len(sts.Comment)-1]
  464. sts.Danmu = sts.Danmu[:len(sts.Danmu)-1]
  465. }
  466. return
  467. }