forked from all_user/stat_center
569 lines
16 KiB
Go
569 lines
16 KiB
Go
package crossSer
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"stat/api/proto"
|
|
"stat/common"
|
|
"stat/common/statutils"
|
|
"stat/common/tgbot"
|
|
"stat/common/utils"
|
|
"stat/models/botdailyhourstat"
|
|
"stat/statg"
|
|
"time"
|
|
|
|
"github.com/wcharczuk/go-chart/v2"
|
|
|
|
"github.com/wcharczuk/go-chart/v2/drawing"
|
|
|
|
"github.com/vicanso/go-charts/v2"
|
|
)
|
|
|
|
var Colors = []drawing.Color{
|
|
drawing.ColorFromHex("0780cf"),
|
|
drawing.ColorFromHex("765005"),
|
|
drawing.ColorFromHex("fa6d1d"),
|
|
drawing.ColorFromHex("0e2c82"),
|
|
drawing.ColorFromHex("b6b51f"),
|
|
drawing.ColorFromHex("da1f18"),
|
|
drawing.ColorFromHex("701866"),
|
|
drawing.ColorFromHex("f47a75"),
|
|
drawing.ColorFromHex("009db2"),
|
|
drawing.ColorFromHex("024b51"),
|
|
drawing.ColorFromHex("0780cf"),
|
|
drawing.ColorFromHex("765005"),
|
|
}
|
|
|
|
type CrossStatMethod = func(ctx context.Context, cmd *tgbot.Command) error
|
|
|
|
// 统计方法汇总
|
|
var CrossStatMethodMap = map[string]CrossStatMethod{
|
|
statutils.StatProjectRecharge: CrossStatRecharge,
|
|
statutils.StatProjectRechargeNew: CrossStatRechargeNew,
|
|
statutils.StatProjectRechargeOld: CrossStatRechargeOld,
|
|
statutils.StatProjectRechargeGold: CrossStatRechargeGold,
|
|
statutils.StatProjectRechargeVip: CrossStatRechargeVip,
|
|
statutils.StatProjectRegister: CrossStatRegister,
|
|
statutils.StatProjectVisit: CrossStatVisit,
|
|
statutils.StatProjectOrder: CrossStatOrder,
|
|
}
|
|
|
|
type CrossAppDownloadRes struct {
|
|
Code int32 `json:"code"`
|
|
Msg string `json:"msg"`
|
|
Tip string `json:"tip"`
|
|
Data *proto.PageButtonClickResp `json:"data"`
|
|
Time time.Time `json:"time"`
|
|
}
|
|
|
|
type CrossStatRes struct {
|
|
Code int32 `json:"code"`
|
|
Msg string `json:"msg"`
|
|
Tip string `json:"tip"`
|
|
Data *proto.BotDailyHourListResp `json:"data"`
|
|
Time time.Time `json:"time"`
|
|
}
|
|
|
|
type CrossMonthRes struct {
|
|
Code int32 `json:"code"`
|
|
Msg string `json:"msg"`
|
|
Tip string `json:"tip"`
|
|
Data *proto.BotMonthStatResp `json:"data"`
|
|
Time time.Time `json:"time"`
|
|
}
|
|
|
|
type StatItem struct {
|
|
Value int64
|
|
Stat *botdailyhourstat.BotHourStat
|
|
}
|
|
|
|
type FuncGetStatValue func(item *botdailyhourstat.BotHourStat) int64
|
|
|
|
type StatItemList []*StatItem
|
|
|
|
func (a StatItemList) Len() int { return len(a) }
|
|
func (a StatItemList) Less(i, j int) bool { return a[i].Value > a[j].Value }
|
|
func (a StatItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func CrossMonthStat(ctx context.Context, cmd *tgbot.Command) error {
|
|
appId, _ := cmd.GetFlagInt32Value("-app")
|
|
monthStr := cmd.GetFlagStringValue("-month")
|
|
month, err := time.ParseInLocation("2006-01-02", monthStr, time.Local)
|
|
if err != nil {
|
|
return tgbot.NewUserError(cmd, "解析日期出错, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
|
}
|
|
getValue := func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.Recharge.Value()
|
|
}
|
|
statList := make(StatItemList, 0)
|
|
for _, addr := range statg.Conf.InnerServers {
|
|
req := &proto.BotMonthStatReq{Month: month}
|
|
if appId > 0 {
|
|
req.AppId = &appId
|
|
}
|
|
res := CrossMonthRes{}
|
|
err = InnerRequest(addr+"/api/stat/inner/botMonthStat", req, &res, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if res.Data == nil {
|
|
continue
|
|
}
|
|
for _, stat := range res.Data.List {
|
|
if statg.GlobalEndpoints.ExistsByAppId(stat.AppId) {
|
|
value := getValue(stat)
|
|
statList = append(statList, &StatItem{
|
|
Value: value,
|
|
Stat: stat,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
// sort
|
|
sort.Sort(statList)
|
|
// 显示
|
|
y, m, _ := month.Date()
|
|
for _, item := range statList {
|
|
appName := statg.GlobalEndpoints.ExistsByAppId(item.Stat.AppId)
|
|
title := fmt.Sprintf("%d年%d月【%s】数据汇总", y, m, appName)
|
|
text := item.Stat.DisplaySummaryText(title)
|
|
if err = cmd.SendBack(text); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func CrossStatRecharge(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.Recharge.Value() / 100
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func CrossStatRechargeNew(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "新用户充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.RechargeNew.Value() / 100
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("新用户充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func CrossStatRechargeOld(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "老用户充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.RechargeOld.Value() / 100
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("老用户充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func CrossStatRechargeGold(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "金币充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.RechargeGold.Value() / 100
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("金币充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func CrossStatRechargeVip(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "会员充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.RechargeVip.Value() / 100
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("会员充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func CrossStatRegister(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "注册排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.Register.Value()
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("注册排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func CrossStatVisit(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "日活排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.Visit.Value()
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("日活排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func CrossStatOrder(ctx context.Context, cmd *tgbot.Command) error {
|
|
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
|
if moveBackDays > 0 {
|
|
moveBackDays = moveBackDays * -1
|
|
}
|
|
// get stats
|
|
now := time.Now()
|
|
reportTime := utils.RecentMinute(now, 60)
|
|
if moveBackDays != 0 {
|
|
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
|
}
|
|
title := GetDateDesc(0) + "订单排名:" + reportTime.Format("2006-01-02T15:04:05")
|
|
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
|
return item.TodayStat.Order.Count()
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.PhotoPath = photo
|
|
return cmd.SendBack(fmt.Sprintf("订单排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
|
}
|
|
|
|
func GetRankPhoto(reportTime time.Time, title string, getValue FuncGetStatValue) (string, []byte, error) {
|
|
charts.SetDefaultFont(statutils.Font)
|
|
statList := make(StatItemList, 0)
|
|
registerStatList := make(StatItemList, 0)
|
|
var total, registerTotal, visitTotal int64
|
|
for _, addr := range statg.Conf.InnerServers {
|
|
req := &proto.BotDailyHourListReq{
|
|
Page: common.Page{
|
|
PageNum: 1,
|
|
PageSize: 100,
|
|
},
|
|
SumDateStart: reportTime,
|
|
SumDateStop: reportTime,
|
|
}
|
|
res := CrossStatRes{}
|
|
err := InnerRequest(addr+"/api/stat/inner/botStatList", req, &res, true)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
if res.Data == nil {
|
|
continue
|
|
}
|
|
|
|
for _, stat := range res.Data.List {
|
|
if statg.GlobalEndpoints.GetBotRankEndpoints().ExistsByAppId(stat.AppId) {
|
|
value := getValue(stat)
|
|
total += value
|
|
statList = append(statList, &StatItem{
|
|
Value: value,
|
|
Stat: stat,
|
|
})
|
|
|
|
registerValue := stat.TodayStat.Register.Total.Value()
|
|
registerTotal += registerValue
|
|
visitTotal += stat.TodayStat.Visit.Total.Value()
|
|
registerStatList = append(registerStatList, &StatItem{
|
|
Value: registerValue,
|
|
Stat: stat,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
// sort
|
|
sort.Sort(statList)
|
|
// draw chart
|
|
xNames := make([]string, 0)
|
|
for _, stat := range statList {
|
|
xNames = append(xNames, statg.GlobalEndpoints.GetAppShortNameById(stat.Stat.AppId))
|
|
}
|
|
sDatas := make([]charts.SeriesData, 0)
|
|
rDatas := make([]charts.SeriesData, 0)
|
|
for index, stat := range statList {
|
|
sDatas = append(sDatas, charts.SeriesData{
|
|
Value: float64(stat.Value),
|
|
Style: charts.Style{
|
|
FillColor: Colors[index%len(Colors)],
|
|
TextWrap: chart.TextWrapRune,
|
|
},
|
|
})
|
|
for _, registerStat := range registerStatList {
|
|
if stat.Stat.AppId == registerStat.Stat.AppId {
|
|
rDatas = append(rDatas, charts.SeriesData{
|
|
Value: float64(registerStat.Value),
|
|
Style: charts.Style{
|
|
FillColor: Colors[index%len(Colors)],
|
|
TextWrap: chart.TextWrapRune,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
chartOpt := charts.ChartOption{
|
|
Width: statg.Conf.ChartsConfig.Width,
|
|
Height: statg.Conf.ChartsConfig.Height,
|
|
Type: charts.ChartOutputPNG,
|
|
Title: charts.TitleOption{
|
|
Text: title,
|
|
},
|
|
XAxis: charts.NewXAxisOption(xNames),
|
|
Legend: charts.LegendOption{
|
|
Data: []string{
|
|
fmt.Sprintf("应用:%d", len(xNames)),
|
|
fmt.Sprintf("充值:%d", total),
|
|
fmt.Sprintf("日活:%d", visitTotal),
|
|
fmt.Sprintf("注册:%d", registerTotal),
|
|
},
|
|
Icon: charts.IconRect,
|
|
Left: charts.PositionRight,
|
|
},
|
|
SeriesList: []charts.Series{
|
|
{
|
|
Type: charts.ChartTypeBar,
|
|
Data: sDatas,
|
|
Label: charts.SeriesLabel{
|
|
Show: true,
|
|
Position: charts.PositionBottom,
|
|
},
|
|
MarkLine: charts.NewMarkLine(
|
|
charts.SeriesMarkDataTypeAverage,
|
|
),
|
|
},
|
|
{
|
|
Type: charts.ChartTypeBar,
|
|
Data: rDatas,
|
|
Label: charts.SeriesLabel{
|
|
Show: true,
|
|
Position: charts.PositionBottom,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
show := true
|
|
chartOpt.XAxis.Show = &show
|
|
d, err := charts.Render(chartOpt)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
buf, err := d.Bytes()
|
|
if err != nil {
|
|
return "", buf, err
|
|
}
|
|
photo := fmt.Sprintf("/tmp/%s.png", utils.UUID())
|
|
if err = statutils.WriteBytesToFile(buf, photo); err != nil {
|
|
return "", buf, err
|
|
}
|
|
return photo, buf, nil
|
|
}
|
|
|
|
func GetRankDetail(reportTime time.Time, title string, getValue FuncGetStatValue) (string, error) {
|
|
charts.SetDefaultFont(statutils.Font)
|
|
statList := make(StatItemList, 0)
|
|
registerStatList := make(StatItemList, 0)
|
|
var total, registerTotal int64
|
|
for _, addr := range statg.Conf.InnerServers {
|
|
req := &proto.BotDailyHourListReq{
|
|
Page: common.Page{
|
|
PageNum: 1,
|
|
PageSize: 100,
|
|
},
|
|
SumDateStart: reportTime,
|
|
SumDateStop: reportTime,
|
|
}
|
|
res := CrossStatRes{}
|
|
err := InnerRequest(addr+"/api/stat/inner/botStatList", req, &res, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if res.Data == nil {
|
|
continue
|
|
}
|
|
|
|
for _, stat := range res.Data.List {
|
|
if statg.GlobalEndpoints.GetBotRankEndpoints().ExistsByAppId(stat.AppId) {
|
|
value := getValue(stat)
|
|
total += value
|
|
statList = append(statList, &StatItem{
|
|
Value: value,
|
|
Stat: stat,
|
|
})
|
|
|
|
registerValue := stat.TodayStat.Register.Total.Value()
|
|
registerTotal += registerValue
|
|
registerStatList = append(registerStatList, &StatItem{
|
|
Value: registerValue,
|
|
Stat: stat,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
// sort
|
|
sort.Sort(statList)
|
|
// draw chart
|
|
xNames := make([]string, 0)
|
|
for _, stat := range statList {
|
|
xNames = append(xNames, statg.GlobalEndpoints.GetAppShortNameById(stat.Stat.AppId))
|
|
}
|
|
sDatas := make([]charts.SeriesData, 0)
|
|
rDatas := make([]charts.SeriesData, 0)
|
|
for index, stat := range statList {
|
|
sDatas = append(sDatas, charts.SeriesData{
|
|
Value: float64(stat.Value),
|
|
Style: charts.Style{
|
|
FillColor: Colors[index%len(Colors)],
|
|
TextWrap: chart.TextWrapRune,
|
|
},
|
|
})
|
|
for _, registerStat := range registerStatList {
|
|
if stat.Stat.AppId == registerStat.Stat.AppId {
|
|
rDatas = append(rDatas, charts.SeriesData{
|
|
Value: float64(registerStat.Value),
|
|
Style: charts.Style{
|
|
FillColor: Colors[index%len(Colors)],
|
|
TextWrap: chart.TextWrapRune,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
chartOpt := charts.ChartOption{
|
|
Width: statg.Conf.ChartsConfig.Width,
|
|
Height: statg.Conf.ChartsConfig.Height,
|
|
Type: charts.ChartOutputPNG,
|
|
Title: charts.TitleOption{
|
|
Text: title,
|
|
},
|
|
XAxis: charts.NewXAxisOption(xNames),
|
|
Legend: charts.LegendOption{
|
|
Data: []string{
|
|
fmt.Sprintf("应用:%d", len(xNames)),
|
|
fmt.Sprintf("充值:%d", total),
|
|
fmt.Sprintf("注册:%d", registerTotal),
|
|
},
|
|
Icon: charts.IconRect,
|
|
Left: charts.PositionRight,
|
|
},
|
|
SeriesList: []charts.Series{
|
|
{
|
|
Type: charts.ChartTypeBar,
|
|
Data: sDatas,
|
|
Label: charts.SeriesLabel{
|
|
Show: true,
|
|
Position: charts.PositionBottom,
|
|
},
|
|
MarkLine: charts.NewMarkLine(
|
|
charts.SeriesMarkDataTypeAverage,
|
|
),
|
|
},
|
|
{
|
|
Type: charts.ChartTypeBar,
|
|
Data: rDatas,
|
|
Label: charts.SeriesLabel{
|
|
Show: true,
|
|
Position: charts.PositionBottom,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
show := true
|
|
chartOpt.XAxis.Show = &show
|
|
d, err := charts.Render(chartOpt)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
buf, err := d.Bytes()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
photo := fmt.Sprintf("/tmp/%s.png", utils.UUID())
|
|
if err = statutils.WriteBytesToFile(buf, photo); err != nil {
|
|
return "", err
|
|
}
|
|
return photo, nil
|
|
}
|