Files
kra-new/internal/data/business/workbench.go
T

182 lines
7.4 KiB
Go

package business
import (
"context"
"time"
"gorm.io/gorm"
"kra/internal/biz/business"
)
type workbenchRepo struct{ data Provider }
func NewWorkbenchRepo(data Provider) business.WorkbenchRepo { return &workbenchRepo{data: data} }
func workbenchAvailable(db *gorm.DB, scope business.AnalyticsScope, sources []string, kind business.WorkbenchRankKind) (bool, bool, error) {
empty, available, err := prepareAnalytics(db, scope, business.AnalyticsFilter{Sources: sources})
if err != nil || empty || !available {
return empty, available, err
}
tables := []string{}
if kind == "" || kind == business.WorkbenchPayHigh || kind == business.WorkbenchPayLow {
tables = append(tables, "tab_sw_report_day")
}
if kind == "" || kind == business.WorkbenchRegister {
tables = append(tables, "tab_sw_report_day_key")
}
for _, table := range tables {
available, err := businessTableAvailable(db, table)
if err != nil || !available {
return false, available, err
}
}
return false, true, nil
}
func (r *workbenchRepo) Summary(ctx context.Context, scope business.AnalyticsScope, sources []string, w business.WorkbenchWindow) (*business.WorkbenchTotals, error) {
db := r.data.DB().WithContext(ctx)
out := &business.WorkbenchTotals{Available: true}
empty, available, err := workbenchAvailable(db, scope, sources, "")
if err != nil {
return nil, err
}
out.Available = available
if empty || !available {
return out, nil
}
mode := 0
if scope.IDCardDedup {
mode = 1
}
for _, period := range []struct {
day time.Time
target *business.WorkbenchDay
}{{w.Today, &out.Today}, {w.Yesterday, &out.Yesterday}} {
f := business.AnalyticsFilter{Sources: sources, Begin: period.day.Format("2006-01-02"), End: period.day.Format("2006-01-02")}
var counts []portraitCountPO
if err := analyticsQuery(db, scope, f, "tab_sw_report_day_key", mode).Select("r.metric,COUNT(DISTINCT " + portraitIdentity(db, scope.IDCardDedup) + ") AS count").Where("r.metric IN (1,2)").Group("r.metric").Scan(&counts).Error; err != nil {
return nil, business.ErrStorage
}
period.target.Counts = toPortraitCounts(counts)
if err := analyticsQuery(db, scope, f, "tab_sw_report_day", 0).Select("COALESCE(SUM(r.total_pay),0)").Scan(&period.target.Pay).Error; err != nil {
return nil, business.ErrStorage
}
// 活跃渠道按自身注册或正充值统计,不能归并一级渠道后再计数。
active := map[business.AnalyticsKey]bool{}
var registrations, pays []analyticsAggPO
if err := analyticsQuery(db, scope, f, "tab_sw_report_day_key", mode).Where("r.metric=1").Select("r.source_db,r.promote_id AS entity_id").Group("r.source_db,r.promote_id").Having("COUNT(DISTINCT r.identity_key)>0").Scan(&registrations).Error; err != nil {
return nil, business.ErrStorage
}
if err := analyticsQuery(db, scope, f, "tab_sw_report_day", 0).Select("r.source_db,r.promote_id AS entity_id").Group("r.source_db,r.promote_id").Having("SUM(r.total_pay)>0").Scan(&pays).Error; err != nil {
return nil, business.ErrStorage
}
for _, po := range append(registrations, pays...) {
if po.ID != nil {
active[business.AnalyticsKey{SourceDB: po.SourceDB, ID: *po.ID}] = true
}
}
period.target.ActiveChannels = int64(len(active))
if err := analyticsBasePromotes(db, scope, f, "p").Where("p.create_time BETWEEN ? AND ?", period.day.Unix(), period.day.AddDate(0, 0, 1).Unix()-1).Count(&period.target.NewChannels).Error; err != nil {
return nil, business.ErrStorage
}
}
for _, period := range []struct {
begin, end time.Time
target *string
}{{w.MonthStart, w.Today, &out.MonthPay}, {w.PreviousMonthStart, w.PreviousMonthEnd, &out.PreviousMonthPay}} {
f := business.AnalyticsFilter{Sources: sources, Begin: period.begin.Format("2006-01-02"), End: period.end.Format("2006-01-02")}
if err := analyticsQuery(db, scope, f, "tab_sw_report_day", 0).Select("COALESCE(SUM(r.total_pay),0)").Scan(period.target).Error; err != nil {
return nil, business.ErrStorage
}
}
return out, nil
}
type workbenchPromotePO struct {
ID int64
SourceDB string `gorm:"column:source_db"`
Account string
CreateTime, LastLoginTime *int64
}
func (r *workbenchRepo) Ranks(ctx context.Context, scope business.AnalyticsScope, f business.AnalyticsFilter, kind business.WorkbenchRankKind) (*business.WorkbenchRankData, error) {
db := r.data.DB().WithContext(ctx)
out := &business.WorkbenchRankData{Rows: []*business.WorkbenchRankRow{}, Available: true}
empty, available, err := workbenchAvailable(db, scope, f.Sources, kind)
if err != nil {
return nil, err
}
out.Available = available
if empty || !available {
return out, nil
}
rows := map[business.AnalyticsKey]*business.WorkbenchRankRow{}
rowFor := func(source string, id int64) *business.WorkbenchRankRow {
key := business.AnalyticsKey{SourceDB: source, ID: id}
if rows[key] == nil {
rows[key] = &business.WorkbenchRankRow{AnalyticsKey: key, Value: "0"}
}
return rows[key]
}
if kind == business.WorkbenchPayLow || kind == business.WorkbenchUnlogin {
var channels []workbenchPromotePO
if err := analyticsBasePromotes(db, scope, f, "p").Select("p.id,p.source_db,p.account,p.create_time,p.last_login_time").Where("p.promote_level=1").Scan(&channels).Error; err != nil {
return nil, business.ErrStorage
}
for _, po := range channels {
row := rowFor(po.SourceDB, po.ID)
row.Name, row.CreatedUnix, row.LastLoginUnix = po.Account, po.CreateTime, po.LastLoginTime
}
}
if kind != business.WorkbenchUnlogin {
mode, table := 0, "tab_sw_report_day"
if kind == business.WorkbenchRegister {
table = "tab_sw_report_day_key"
if scope.IDCardDedup {
mode = 1
}
}
q := analyticsQuery(db, scope, f, table, mode)
id := "r.promote_id"
if kind == business.WorkbenchRegister {
q = q.Where("r.metric=1")
if scope.IncludeSub {
// 旧工作台的注册榜使用 INNER JOIN + CASE,区别于渠道统计的历史 LEFT JOIN 口径。
q = q.Joins("INNER JOIN tab_promote root_p ON root_p.source_db=r.source_db AND root_p.id=r.promote_id")
id = "CASE WHEN root_p.promote_level=1 THEN root_p.id WHEN root_p.top_promote_id>0 THEN root_p.top_promote_id ELSE root_p.id END"
}
} else {
q = q.Joins("LEFT JOIN tab_promote root_p ON root_p.source_db=r.source_db AND root_p.id=r.promote_id")
id = "CASE WHEN root_p.promote_level=1 THEN root_p.id WHEN root_p.top_promote_id>0 THEN root_p.top_promote_id ELSE COALESCE(root_p.id,r.promote_id) END"
}
value := "COALESCE(SUM(r.total_pay),0)"
if kind == business.WorkbenchRegister {
value = "COUNT(DISTINCT r.identity_key)"
}
var agg []analyticsAggPO
if err := q.Select("r.source_db," + id + " AS entity_id," + value + " AS amount").Group("r.source_db," + id).Scan(&agg).Error; err != nil {
return nil, business.ErrStorage
}
for _, po := range agg {
if po.ID != nil {
rowFor(po.SourceDB, *po.ID).Value = po.Amount
}
}
keys := make([]business.AnalyticsKey, 0, len(rows))
for key := range rows {
keys = append(keys, key)
}
for start := 0; start < len(keys); start += 300 {
var names []workbenchPromotePO
if err := db.Table("tab_promote").Select("id,source_db,account").Where("(source_db,id) IN ?", analyticsPairs(keys[start:min(start+300, len(keys))])).Scan(&names).Error; err != nil {
return nil, business.ErrStorage
}
for _, po := range names {
rowFor(po.SourceDB, po.ID).Name = po.Account
}
}
}
for _, row := range rows {
out.Rows = append(out.Rows, row)
}
return out, nil
}