209 lines
7.5 KiB
Go
209 lines
7.5 KiB
Go
package business
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
|
|
"gorm.io/gorm"
|
|
"kra/internal/biz/business"
|
|
)
|
|
|
|
func whereMetricChannels(db *gorm.DB, keys []business.ChannelKey) *gorm.DB {
|
|
bySource := map[string][]uint64{}
|
|
for _, key := range keys {
|
|
bySource[key.SourceDB] = append(bySource[key.SourceDB], key.ID)
|
|
}
|
|
sources := make([]string, 0, len(bySource))
|
|
for source := range bySource {
|
|
sources = append(sources, source)
|
|
}
|
|
sort.Strings(sources)
|
|
if len(sources) == 0 {
|
|
return db.Where("1=0")
|
|
}
|
|
var condition *gorm.DB
|
|
for _, source := range sources {
|
|
part := db.Session(&gorm.Session{NewDB: true}).Where("source_db=? AND promote_id IN ?", source, bySource[source])
|
|
if condition == nil {
|
|
condition = part
|
|
} else {
|
|
condition = condition.Or(part)
|
|
}
|
|
}
|
|
return db.Where(condition)
|
|
}
|
|
|
|
func (r *channelRepo) Metrics(ctx context.Context, roots, statKeys []business.ChannelKey, dedup bool) (*business.ChannelMetricData, error) {
|
|
db := r.data.DB().WithContext(ctx)
|
|
metrics := &business.ChannelMetricData{Games: map[business.ChannelKey]int64{}, Players: map[business.ChannelKey]int64{}, Pay: map[business.ChannelKey]string{}}
|
|
var err error
|
|
metrics.GamesAvailable, err = businessTableAvailable(db, "tab_promote_apply")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
metrics.PlayersAvailable, err = businessTableAvailable(db, "tab_user")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
metrics.PayAvailable, err = businessTableAvailable(db, "tab_spend")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
type countRow struct {
|
|
SourceDB string `gorm:"column:source_db"`
|
|
PromoteID uint64
|
|
Count int64
|
|
}
|
|
if metrics.GamesAvailable {
|
|
for start := 0; start < len(roots); start += 300 {
|
|
var rows []countRow
|
|
if err := whereMetricChannels(db, roots[start:min(start+300, len(roots))]).Table("tab_promote_apply").Select("source_db,promote_id,COUNT(DISTINCT game_id) AS count").Where("status = 1").Group("source_db,promote_id").Scan(&rows).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range rows {
|
|
metrics.Games[business.ChannelKey{SourceDB: row.SourceDB, ID: row.PromoteID}] = row.Count
|
|
}
|
|
}
|
|
}
|
|
// 每批按平台/渠道一起查询,避免逐个渠道访问数据库,也避免参数数量无限增长。
|
|
for start := 0; start < len(statKeys); start += 300 {
|
|
keys := statKeys[start:min(start+300, len(statKeys))]
|
|
if metrics.PlayersAvailable {
|
|
q := whereMetricChannels(db, keys).Table("tab_user").Where("puid = 0 OR puid IS NULL")
|
|
if dedup {
|
|
var players []struct {
|
|
SourceDB string `gorm:"column:source_db"`
|
|
PromoteID, UserID uint64
|
|
IDCard string `gorm:"column:idcard"`
|
|
}
|
|
if err := q.Select("source_db,promote_id,id AS user_id,idcard").Scan(&players).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, p := range players {
|
|
metrics.PlayerCards = append(metrics.PlayerCards, business.ChannelPlayer{ChannelKey: business.ChannelKey{SourceDB: p.SourceDB, ID: p.PromoteID}, UserID: p.UserID, IDCard: p.IDCard})
|
|
}
|
|
} else {
|
|
var rows []countRow
|
|
if err := q.Select("source_db,promote_id,COUNT(*) AS count").Group("source_db,promote_id").Scan(&rows).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range rows {
|
|
metrics.Players[business.ChannelKey{SourceDB: row.SourceDB, ID: row.PromoteID}] = row.Count
|
|
}
|
|
}
|
|
}
|
|
if metrics.PayAvailable {
|
|
var rows []struct {
|
|
SourceDB string `gorm:"column:source_db"`
|
|
PromoteID uint64
|
|
Amount string
|
|
}
|
|
if err := whereMetricChannels(db, keys).Table("tab_spend").Select("source_db,promote_id,COALESCE(SUM(pay_amount),0) AS amount").Where("pay_status = 1").Group("source_db,promote_id").Scan(&rows).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range rows {
|
|
metrics.Pay[business.ChannelKey{SourceDB: row.SourceDB, ID: row.PromoteID}] = row.Amount
|
|
}
|
|
}
|
|
}
|
|
return metrics, nil
|
|
}
|
|
|
|
func channelGameTree(db *gorm.DB, scope business.ChannelGameScope) *gorm.DB {
|
|
root := scope.Root
|
|
q := db.Table("tab_promote p").Where("p.source_db = ?", root.SourceDB)
|
|
if !scope.IncludeSub || root.Level == nil || *root.Level >= 3 {
|
|
return q.Where("p.id = ?", root.ID)
|
|
}
|
|
if *root.Level == 2 {
|
|
return q.Where("p.id = ? OR (p.parent_id = ? AND p.promote_level = 3)", root.ID, root.ID)
|
|
}
|
|
return q.Where("p.id = ? OR p.parent_id = ? OR p.top_promote_id = ?", root.ID, root.ID, root.ID)
|
|
}
|
|
func (r *channelRepo) Games(ctx context.Context, scope business.ChannelGameScope) ([]*business.ChannelGame, error) {
|
|
db := r.data.DB().WithContext(ctx)
|
|
available, err := businessTableAvailable(db, "tab_promote_apply")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !available {
|
|
return nil, business.ErrChannelReportsUnavailable
|
|
}
|
|
ids := []uint64{}
|
|
if err := db.Table("tab_promote_apply").Where("source_db = ? AND promote_id = ? AND status = 1 AND game_id IS NOT NULL", scope.Root.SourceDB, scope.Root.ID).Distinct("game_id").Order("game_id").Pluck("game_id", &ids).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
result := []*business.ChannelGame{}
|
|
if len(ids) == 0 {
|
|
return result, nil
|
|
}
|
|
for _, table := range []string{"tab_game", "tab_sw_report_day", "tab_sw_report_day_key"} {
|
|
available, err := businessTableAvailable(db, table)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !available {
|
|
return nil, business.ErrChannelReportsUnavailable
|
|
}
|
|
}
|
|
mode := 0
|
|
if scope.IDCardDedup {
|
|
mode = 1
|
|
}
|
|
query := func(table string, dedup int) *gorm.DB {
|
|
return db.Table(table+" r").Where("r.source_db = ? AND r.game_id > 0 AND r.dedup_mode = ?", scope.Root.SourceDB, dedup).Where("(r.source_db,r.promote_id) IN (?)", channelGameTree(db, scope).Select("p.source_db,p.id"))
|
|
}
|
|
// 累计起点按当前人数口径的日汇总最早日期决定,与原画像查询一致。
|
|
begin := scope.BeginDate
|
|
if begin == "" {
|
|
var minimum struct{ FirstDate *string }
|
|
if err := query("tab_sw_report_day", mode).Select("CAST(MIN(r.stat_date) AS CHAR) AS first_date").Scan(&minimum).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
begin = scope.EndDate
|
|
if minimum.FirstDate != nil && *minimum.FirstDate != "" {
|
|
begin = *minimum.FirstDate
|
|
}
|
|
}
|
|
byID := map[uint64]*business.ChannelGame{}
|
|
for _, id := range ids {
|
|
item := &business.ChannelGame{ID: id, PayAmount: "0.00"}
|
|
byID[id] = item
|
|
result = append(result, item)
|
|
}
|
|
for start := 0; start < len(ids); start += 300 {
|
|
batch := ids[start:min(start+300, len(ids))]
|
|
var names []struct {
|
|
ID uint64
|
|
GameName string
|
|
}
|
|
if err := db.Table("tab_game").Select("id,game_name").Where("source_db = ? AND id IN ?", scope.Root.SourceDB, batch).Scan(&names).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, n := range names {
|
|
byID[n.ID].Name = n.GameName
|
|
}
|
|
var counts []struct {
|
|
GameID uint64
|
|
Count int64
|
|
}
|
|
if err := query("tab_sw_report_day_key", mode).Select("r.game_id,COUNT(DISTINCT r.identity_key) AS count").Where("r.metric = 1 AND r.stat_date >= ? AND r.stat_date <= ? AND r.game_id IN ?", begin, scope.EndDate, batch).Group("r.game_id").Scan(&counts).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, c := range counts {
|
|
byID[c.GameID].RegisterCount = c.Count
|
|
}
|
|
var amounts []struct {
|
|
GameID uint64
|
|
Amount string
|
|
}
|
|
if err := query("tab_sw_report_day", 0).Select("r.game_id,COALESCE(SUM(r.total_pay),0) AS amount").Where("r.stat_date >= ? AND r.stat_date <= ? AND r.game_id IN ?", begin, scope.EndDate, batch).Group("r.game_id").Scan(&amounts).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, a := range amounts {
|
|
byID[a.GameID].PayAmount = a.Amount
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|