投放统计

dev_aagame
zhora 1 week ago
parent 1a65a4e9de
commit b5b213f113
  1. 10
      call/config.go
  2. 81
      modules/backend/app/response.go
  3. 2
      modules/backend/bdb/mysql.go
  4. 395
      modules/backend/handler/advertisement/advertisement.go
  5. 1
      modules/backend/routers/routers.go
  6. 14
      modules/backend/routers/routers_advertisement.go
  7. 21
      modules/backend/values/ad.go
  8. 48
      modules/backend/values/tables.go

@ -2728,3 +2728,13 @@ func GetConfigRtpTemplateId() string {
} }
return "" return ""
} }
func GetChannelListByShow(show int) []*common.Channel {
ret := []*common.Channel{}
for _, v := range channels {
if v.Show == show {
ret = append(ret, v)
}
}
return ret
}

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"server/db" "server/db"
mdb "server/db/mysql"
"server/modules/backend/bdb" "server/modules/backend/bdb"
"server/modules/backend/values" "server/modules/backend/values"
"strings" "strings"
@ -23,9 +24,10 @@ type Gin struct {
} }
type R struct { type R struct {
Data interface{} `json:"data"` Data interface{} `json:"data"`
Msg string `json:"msg"` Msg string `json:"msg"`
Code int `json:"code"` Code int `json:"code"`
DB *mdb.MysqlClient `json:"-"`
} }
func NewApp(c *gin.Context) *Gin { func NewApp(c *gin.Context) *Gin {
@ -146,7 +148,11 @@ func (g *Gin) MUpdateAll(updates []map[string]interface{}, element interface{})
reft := reflect.TypeOf(tmp).Elem() reft := reflect.TypeOf(tmp).Elem()
id := reflect.ValueOf(tmp).Elem().FieldByName("ID").Int() id := reflect.ValueOf(tmp).Elem().FieldByName("ID").Int()
if id == 0 { if id == 0 {
if err = db.Mysql().Create(tmp); err != nil { db := db.Mysql()
if g.DB != nil {
db = g.DB
}
if err = db.Create(tmp); err != nil {
g.Code = values.CodeRetry g.Code = values.CodeRetry
return return
} }
@ -169,7 +175,11 @@ func (g *Gin) MUpdateAll(updates []map[string]interface{}, element interface{})
// log.Debug("element:%v", element) // log.Debug("element:%v", element)
log.Debug("update:%v", update) log.Debug("update:%v", update)
// log.Debug("model:%v", model) // log.Debug("model:%v", model)
if err = db.Mysql().Update(model.Interface(), update); err != nil { db := db.Mysql()
if g.DB != nil {
db = g.DB
}
if err = db.Update(model.Interface(), update); err != nil {
g.Code = values.CodeRetry g.Code = values.CodeRetry
return return
} }
@ -182,12 +192,16 @@ func (g *Gin) MUpdateAll(updates []map[string]interface{}, element interface{})
func (g *Gin) MDel(id int, element interface{}) (pass bool) { func (g *Gin) MDel(id int, element interface{}) (pass bool) {
tmp := reflect.New(reflect.TypeOf(element).Elem()) tmp := reflect.New(reflect.TypeOf(element).Elem())
tmp.Elem().FieldByName("ID").SetInt(int64(id)) tmp.Elem().FieldByName("ID").SetInt(int64(id))
if !db.Mysql().Exist(tmp.Interface()) { db := db.Mysql()
if g.DB != nil {
db = g.DB
}
if !db.Exist(tmp.Interface()) {
g.Code = values.CodeParam g.Code = values.CodeParam
g.Msg = "该id不存在" g.Msg = "该id不存在"
return return
} }
if err := db.Mysql().Del(tmp.Interface()); err != nil { if err := db.Del(tmp.Interface()); err != nil {
g.Code = values.CodeRetry g.Code = values.CodeRetry
return return
} }
@ -264,3 +278,56 @@ func (g *Gin) CheckPower(power string) bool {
// } // }
// g.Data = resp // g.Data = resp
//} //}
// 从数据库中读取数据
func (g *Gin) MGetSqlAll(model, tar interface{}, condition map[string]interface{}, page, pageSize int) (count int64, pass bool) {
c := g.DB
if c == nil {
c = db.Mysql()
}
sql, _ := values.CheckQueryControl(model, nil)
mod := reflect.TypeOf(model).Elem()
for k, v := range condition {
tmp, ok := mod.FieldByName(k)
if !ok {
log.Error("invalid model:%v,condition:%v", model, condition)
continue
}
tag := tmp.Tag.Get("web")
if tag == "" || tag == "-" {
log.Error("invalid model:%v,condition:%v", model, condition)
continue
}
reft := reflect.TypeOf(v)
ref := reflect.ValueOf(v)
if reft.Kind() == reflect.Slice {
if ref.Len() != 2 {
log.Error("invalid model:%v,condition:%v", model, condition)
continue
}
if len(sql) > 0 {
sql += " and "
}
sql += fmt.Sprintf("%s >= %v and %s <= %v", tag, ref.Index(0).Interface(), tag, ref.Index(1).Interface())
} else {
if len(sql) > 0 {
sql += " and "
}
sql += fmt.Sprintf("%s = %v", tag, ref.Interface())
}
}
var err error
if page != 0 && pageSize != 0 {
count, err = c.QueryList(page-1, pageSize, sql, "", model, tar)
} else {
count, err = c.QueryAll(sql, "", model, tar)
}
if err != nil {
log.Error("err:%v", err)
g.Code = values.CodeRetry
g.R.Msg = err.Error()
return
}
pass = true
return
}

@ -25,6 +25,8 @@ func MigrateDB() {
new(values.ReviewData), new(values.ReviewData),
new(values.FirstPageData), new(values.FirstPageData),
new(values.PlatformData), new(values.PlatformData),
new(values.ADConfig),
new(values.ADStats),
) )
if err != nil { if err != nil {
panic(err) panic(err)

@ -0,0 +1,395 @@
package handler
import (
"encoding/json"
"fmt"
"server/call"
"server/common"
"server/modules/backend/app"
"server/modules/backend/bdb"
"server/modules/backend/models"
"server/modules/backend/util"
"server/modules/backend/values"
"strings"
"time"
utils "server/util"
"github.com/gin-gonic/gin"
"github.com/liangdas/mqant/log"
"gorm.io/gorm"
)
var (
RechargerFee int64 = 10
WithdrawFee int64 = 5
ChangePer int64 = 95
)
// ADConfig ad配置
func ADConfig(c *gin.Context) {
a := app.NewApp(c)
defer func() {
a.Response()
}()
a.DB = bdb.BackDB
path := c.Request.URL.Path
path = strings.ReplaceAll(path, "/advertisement/config/", "")
all := strings.Split(path, "/")
if len(all) > 1 || len(all) == 0 {
a.Code = values.CodeRetry
return
}
opt := all[0]
element := &values.ADConfig{}
list := &[]values.ADConfig{}
var resp interface{}
if opt == "list" {
req := &values.GMConfigCommonListReq{
Condition: map[string]interface{}{},
}
a.S(req)
count, pass := a.MGetSqlAll(element, list, req.Condition, req.Page, req.PageSize)
if !pass {
return
}
resp = values.GMConfigCommonListResp{
Config: list,
Total: count,
}
} else if opt == "edit" {
req := new(values.GMConfigCommonEditReq)
if !a.S(req) {
return
}
if !a.MUpdateAll(req.Config, element) {
return
}
} else if opt == "del" {
req := new(values.GMConfigCommonDelReq)
if !a.S(req) {
return
}
if !a.MDel(req.ID, element) {
return
}
}
a.Data = resp
}
func AdvertisementStats(c *gin.Context) {
a := app.NewApp(c)
defer func() {
a.Response()
}()
req := new(values.ADReq)
if !a.S(req) {
return
}
su, eu := util.GetQueryUnix(req.Start, req.End)
now := utils.GetZeroTime(time.Now()).Unix()
if su >= now { // 不允许查今天
a.Code = values.CodeParam
a.Msg = "只能查今天之前的数据"
return
}
if eu >= now {
eu = now
}
resp := &values.ADResp{}
a.Data = resp
resp.Count = (eu - su) / (24 * 60 * 60)
cids := []*int{}
// isSC := len(a.User.SChannels) > 0
if len(req.ChannelID) > 0 {
cids = req.ChannelID
} else if req.GroupID > 0 {
adConfig := &values.ADConfig{ID: req.GroupID}
bdb.BackDB.Get(adConfig)
channels := []int{}
json.Unmarshal([]byte(adConfig.Channels), &channels)
if len(channels) == 0 {
a.Code = values.CodeParam
a.Msg = "该投放组未分配包"
return
}
for _, v := range channels {
one := v
cids = append(cids, &one)
}
}
// else if isSC {
// for i := range a.User.SChannels {
// cids = append(cids, &a.User.SChannels[i])
// }
// }
// 如果是查一天的情况,罗列所有包
if resp.Count == 1 {
if len(cids) == 1 {
one := &values.ADStats{Time: su, ChannelID: *cids[0]}
bdb.BackDB.Get(one)
if one.ID == 0 {
one = GetOneAD(su, su+24*60*60, cids)
if one.Time != now {
bdb.BackDB.Create(one)
}
}
CalStats(one)
resp.List = append(resp.List, *one)
one.ChannelID = 0
resp.Total = *one
return
}
if len(cids) == 0 {
channels := call.GetChannelListByShow(2)
for _, v := range channels {
cids = append(cids, &v.ChannelID)
}
}
list := []*values.ADStats{}
bdb.BackDB.QueryAll(fmt.Sprintf("time = %d", su), "", &values.ADStats{}, &list)
for _, v := range cids {
var one *values.ADStats
for _, j := range list {
if j.ChannelID == *v {
one = j
break
}
}
if one == nil {
one = GetOneAD(su, su+24*60*60, []*int{v})
if one.Time != now {
bdb.BackDB.Create(one)
}
}
CalStats(one)
resp.List = append(resp.List, *one)
}
total := &values.ADStats{}
for _, v := range resp.List {
CalTotal(total, &v)
}
CalStats(total)
resp.Total = *total
return
}
listLen := req.Num
if listLen > int(resp.Count) {
listLen = int(resp.Count)
}
resp.List = make([]values.ADStats, listLen)
list := []values.ADStats{}
sql := fmt.Sprintf("time >= %d and time < %d", su, eu)
if len(cids) == 0 {
sql += " and channel_id = 0"
} else if len(cids) == 1 {
sql += fmt.Sprintf(" and channel_id = %d", *cids[0])
}
bdb.BackDB.QueryAll(sql, "time desc", &values.ADStats{}, &list)
index := -1
start := eu - 24*60*60 - int64((req.Page-1)*req.Num)*24*60*60
end := start - int64(req.Num-1)*24*60*60
if end < su {
end = su
}
for i := start; i >= end; i -= 24 * 60 * 60 {
index++
total := &values.ADStats{}
if len(cids) == 0 {
find := false
for _, v := range list {
if v.Time != i {
continue
}
find = true
CalTotal(total, &v)
break
}
if !find {
one := GetOneAD(i, i+24*60*60, nil)
if one.Time != now {
bdb.BackDB.Create(one)
}
CalTotal(total, one)
}
} else {
for _, cid := range cids {
find := false
for _, v := range list {
if v.ChannelID != *cid {
continue
}
if v.Time != i {
continue
}
find = true
CalTotal(total, &v)
if len(cids) == 1 {
total.ID = v.ID
}
break
}
if !find {
one := GetOneAD(i, i+24*60*60, []*int{cid})
if one.Time != now {
bdb.BackDB.Create(one)
}
CalTotal(total, one)
if len(cids) == 1 {
total.ID = one.ID
}
}
}
}
if len(cids) == 1 {
total.ChannelID = *cids[0]
}
CalStats(total)
resp.List[index] = *total
}
if index < listLen-1 {
resp.List = resp.List[:index+1]
}
}
func GetOneAD(start, end int64, cids []*int) *values.ADStats {
one := &values.ADStats{
Time: start,
Date: time.Unix(start, 0).Format("20060102"),
}
if len(cids) == 0 {
one.Cost = bdb.BackDB.Sum(&values.ADStats{}, fmt.Sprintf("channel_id != 0 and time = %d", one.Time), "cost")
one.TotalCost = bdb.BackDB.Sum(&values.ADStats{}, fmt.Sprintf("channel_id != 0 and time <= %d", one.Time), "cost")
} else if len(cids) == 1 {
one.ChannelID = *cids[0]
one.TotalCost = bdb.BackDB.Sum(&values.ADStats{}, fmt.Sprintf("channel_id = %d and time < %d", one.ChannelID, one.Time), "cost")
}
// 需要查的数据
// 新增人数
one.NewPlayers = models.GetNewPlayerCountBySqls(start, end, cids...)
// 总用户
oldPlayers := models.GetOldPlayerCountBySqls(start, end, cids...)
one.TotalPlayers = one.NewPlayers + oldPlayers
// 新增付费人数
one.NewPayPlayers = models.GetNewPayCountBySqls(start, end, cids...)
// 新增付费金额
one.NewPay = models.GetNewPayAmountBySqls(start, end, common.CurrencyINR, cids...)
// 活跃付费金额
one.ActivePay = models.GetOldPayAmountBySqls(start, end, common.CurrencyINR, cids...)
// 活跃付费人数
one.ActivePayPlayers = models.GetOldPayCountBySqls(start, end, cids...)
// 总付费
one.TotalPay = one.NewPay + one.ActivePay
// 总退出
one.TotalWithdraw = models.GetWithdrawAmountTotalBySQLs(start, end, common.CurrencyINR, cids...)
one.NewRegister = int(models.GetNewPlayerCountBySqls(start, end, cids...))
one.ActiveDevice = int(models.GetDownloadCounts(&start, &end, cids...))
one.NewWithdraw = models.GetNewWithdrawAmount(start, end, cids...)
oldActiveUser := models.GetOldPlayerCountBySqls(start, end, cids...)
one.ActiveUser = one.NewRegister + int(oldActiveUser)
newPayCount := models.GetNewPayCountBySqls(start, end, cids...)
oldPayCount := models.GetOldPayCountBySqls(start, end, cids...)
one.RechargeUser = int(newPayCount + oldPayCount)
// 以下为直接计算出来的数值
// 当日收益=当日充值金额*0.92-当日退出*1.05
one.Profit = one.TotalPay*(100-RechargerFee)/100 - one.TotalWithdraw*(100+WithdrawFee)/100
// 历史总收益=总充值*0.92-总退出*1.05
totalPay := models.GetAmountTotalBySQLs(0, end, cids...)
totalWithdraw := models.GetWithdrawAmountTotalBySQLs(0, end, common.CurrencyINR, cids...)
one.TotalProfit = totalPay*(100-RechargerFee)/100 - totalWithdraw*(100+WithdrawFee)/100
// one.WithdrawPer = util.GetPer(one.TotalWithdraw, one.TotalPay)
// one.NewPayPer = util.GetPer(one.NewPayPlayers, one.NewPlayers)
// one.NewPayUnit = util.GetPoint(one.NewPay, one.NewPlayers)
// one.ARPU = util.GetPoint(one.ActivePay, one.ActivePayPlayers)
return one
}
// 修改投入
func AdvertisementEdit(c *gin.Context) {
a := app.NewApp(c)
defer func() {
a.Response()
}()
req := new(values.ADEditReq)
if !a.S(req) {
return
}
if req.Cost < 0 {
a.Code = values.CodeParam
a.Msg = "消耗不能小于0"
return
}
one := &values.ADStats{ID: req.ID}
err := bdb.BackDB.Get(one)
if err != nil {
log.Error("err:%v", err)
a.Code = values.CodeParam
return
}
diff := req.Cost - one.Cost
if diff == 0 {
a.Code = values.CodeParam
a.Msg = "消耗无修改"
return
}
one.Cost = req.Cost
// one.NewCost = util.GetPoint(one.Cost, one.NewPlayers*100)
// one.NewPayCost = util.GetPoint(one.Cost, one.NewPayPlayers*100)
// one.NewROI = util.GetPer(one.Profit*100, one.Cost*93)
// one.NewPayROI = util.GetPer(one.NewPay*100, one.Cost*93)
// totalCost := bdb.BackDB.Sum(&values.ADStats{}, fmt.Sprintf("channel_id = %d and time < %d", one.ChannelID, one.Time), "cost")
// totalCost += one.Cost
// one.TotalROI = util.GetPer(one.TotalProfit*100, totalCost*93)
bdb.BackDB.Update(&values.ADStats{ID: req.ID}, map[string]interface{}{"cost": one.Cost})
bdb.BackDB.UpdateW(&values.ADStats{}, map[string]interface{}{"cost": gorm.Expr("cost + ?", diff)},
fmt.Sprintf("channel_id = 0 and time = %d", one.Time))
bdb.BackDB.UpdateW(&values.ADStats{}, map[string]interface{}{"total_cost": gorm.Expr("total_cost + ?", diff)},
fmt.Sprintf("(channel_id = %d or channel_id = 0) and time >= %d", one.ChannelID, one.Time))
}
func CalTotal(total, one *values.ADStats) {
total.Time = one.Time
total.Date = one.Date
total.Cost += one.Cost
total.NewPlayers += one.NewPlayers
total.TotalPlayers += one.TotalPlayers
total.NewPayPlayers += one.NewPayPlayers
total.NewPay += one.NewPay
total.ActivePay += one.ActivePay
total.ActivePayPlayers += one.ActivePayPlayers
total.TotalPay += one.TotalPay
total.TotalWithdraw += one.TotalWithdraw
total.Profit += one.Profit
total.TotalProfit += one.TotalProfit
total.TotalCost += one.TotalCost
total.NewRegister += one.NewRegister
total.ActiveDevice += one.ActiveDevice
total.NewWithdraw += one.NewWithdraw
total.ActiveUser += one.ActiveUser
total.RechargeUser += one.RechargeUser
}
// 通过自己数据计算相应收益/roi等数据
func CalStats(one *values.ADStats) {
one.NewCost = util.GetPoint(one.Cost, one.NewPlayers)
one.NewPayCost = util.GetPoint(one.Cost, one.NewPayPlayers)
one.NewPayPer = util.GetPer(one.NewPayPlayers, one.NewPlayers)
one.NewPayUnit = util.GetPoint(one.NewPay, one.NewPlayers)
one.ARPU = util.GetPoint(one.ActivePay, one.ActivePayPlayers)
one.WithdrawPer = util.GetPer(one.TotalWithdraw, one.TotalPay)
one.NewROI = util.GetPer(one.Profit*100, one.Cost*ChangePer)
one.TotalROI = util.GetPer(one.TotalProfit*100, one.TotalCost*ChangePer)
one.NewPayROI = util.GetPer(one.NewPay*100, one.Cost*ChangePer)
}

@ -47,5 +47,6 @@ func SetUpRouter() *gin.Engine {
firstPage(r) firstPage(r)
notice(r) notice(r)
tgrobot(r) tgrobot(r)
advertisement(r)
return r return r
} }

@ -0,0 +1,14 @@
// 账号相关的接口
package routers
import (
handler "server/modules/backend/handler/advertisement"
"github.com/gin-gonic/gin"
)
func advertisement(e *gin.Engine) {
e.POST("/advertisement/config/*action", handler.ADConfig)
e.POST("/advertisement/list", handler.AdvertisementStats)
e.POST("/advertisement/edit", handler.AdvertisementEdit)
}

@ -0,0 +1,21 @@
package values
type ADReq struct {
Start string `json:"Start"`
End string `json:"End"`
Page int `json:"Page" binding:"required"`
Num int `json:"Num" binding:"required"`
ChannelID []*int `json:"ChannelID"`
GroupID int `json:"GroupID"` // 投放组ID
}
type ADResp struct {
Count int64
List []ADStats
Total ADStats // 当查的是一天的数据时,改值为当天汇总数据
}
type ADEditReq struct {
ID int `json:"ID" binding:"required"`
Cost int64 `json:"Cost"`
}

@ -160,3 +160,51 @@ type PlatformData struct {
func (u *PlatformData) PlatformData() string { func (u *PlatformData) PlatformData() string {
return "PlatformData" return "PlatformData"
} }
type ADConfig struct {
ID int `gorm:"primarykey"`
GroupName string `gorm:"column:group_name;default:'';type:varchar(64);uniqueIndex:group_name;comment:投放组名称" web:"group_name"`
Channels string `gorm:"column:channels;default:'[]';type:varchar(512);comment:分配的包" web:"channels"`
}
func (r *ADConfig) TableName() string {
return "ad_config"
}
type ADStats struct {
ID int `gorm:"primarykey"`
Date string `gorm:"column:date;default:'';type:varchar(64);comment:日期"`
Time int64 `gorm:"column:time;type:bigint(20);default:0;uniqueIndex:time_channel;comment:记录时间"`
ChannelID int `gorm:"column:channel_id;default:0;type:int(11);uniqueIndex:time_channel"`
Cost int64 `gorm:"column:cost;type:bigint(20);default:0;comment:消耗(美元)" web:"cost"`
TotalCost int64 `gorm:"column:total_cost;type:bigint(20);default:0;comment:累计消耗(美元)" web:"TotalCost"`
NewPlayers int64 `gorm:"column:new_players;type:bigint(20);default:0;comment:新增人数"`
NewCost string `gorm:"column:new_cost;type:varchar(64);default:'';comment:新增成本(美元)"`
NewPayCost string `gorm:"column:new_pay_cost;type:varchar(64);default:'';comment:新增付费成本(美元)"`
TotalPlayers int64 `gorm:"column:total_players;type:bigint(20);default:0;comment:总用户"`
NewPayPlayers int64 `gorm:"column:new_pay_players;type:bigint(20);default:0;comment:新增付费人数"`
NewPayPer string `gorm:"column:new_pay_per;type:varchar(64);default:'';comment:新增付费率"`
NewPay int64 `gorm:"column:new_pay;type:bigint(20);default:0;comment:新增付费"`
NewPayUnit string `gorm:"column:new_pay_unit;type:varchar(64);default:'';comment:人均新增充值(卢比)"`
ActivePay int64 `gorm:"column:activity_pay;type:bigint(20);default:0;comment:活跃付费"`
ActivePayPlayers int64 `gorm:"column:activity_pay_players;type:bigint(20);default:0;comment:活跃付费人数"`
TotalPay int64 `gorm:"column:total_pay;type:bigint(20);default:0;comment:总付费"`
ARPU string `gorm:"column:arpu;type:varchar(64);default:'';comment:活跃付费arpu"`
TotalWithdraw int64 `gorm:"column:total_withdraw;type:bigint(20);default:0;comment:总退出"`
WithdrawPer string `gorm:"column:withdraw_per;type:varchar(64);default:'';comment:退出比"`
Profit int64 `gorm:"column:profit;type:bigint(20);default:0;comment:当日收益"`
TotalProfit int64 `gorm:"column:total_profit;type:bigint(20);default:0;comment:总收益"`
NewROI string `gorm:"column:new_roi;type:varchar(64);default:'';comment:当日roi"`
TotalROI string `gorm:"column:total_roi;type:varchar(64);default:'';comment:总roi"`
NewPayROI string `gorm:"column:new_pay_roi;type:varchar(64);default:'';comment:新增roi"`
NewRegister int `gorm:"column:new_register;default:0;type:int(11);comment:注册用户数"`
ActiveDevice int `gorm:"column:active_device;default:0;type:int(11);comment:设备数"`
NewWithdraw int64 `gorm:"column:new_withdraw;type:bigint(20);default:0;comment:新增退出"`
ActiveUser int `gorm:"column:active_user;default:0;type:int(11);comment:活跃用户数"`
RechargeUser int `gorm:"column:recharge_user;default:0;type:int(11);comment:付费人数"`
}
func (r *ADStats) TableName() string {
return "ad_stats"
}

Loading…
Cancel
Save