fix: 完成商务迁移复核与接口优化并清理历史资料

This commit is contained in:
Yvan
2026-11-04 14:45:02 +08:00
parent 5a96be6a79
commit 297e80827d
176 changed files with 6803 additions and 6002 deletions
+103 -59
View File
@@ -4,6 +4,8 @@ import (
"context"
"time"
"golang.org/x/sync/errgroup"
"gorm.io/gorm"
"kra/internal/biz/business"
)
@@ -23,13 +25,8 @@ func workbenchAvailable(db *gorm.DB, scope business.AnalyticsScope, sources []st
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
available, err = businessTablesAvailable(db, tables...)
return false, available, err
}
func (r *workbenchRepo) Summary(ctx context.Context, scope business.AnalyticsScope, sources []string, w business.WorkbenchWindow) (*business.WorkbenchTotals, error) {
db := r.data.DB().WithContext(ctx)
@@ -46,47 +43,83 @@ func (r *workbenchRepo) Summary(ctx context.Context, scope business.AnalyticsSco
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
date := func(value time.Time) string { return value.Format("2006-01-02") }
today, yesterday := date(w.Today), date(w.Yesterday)
monthStart, previousStart, previousEnd := date(w.MonthStart), date(w.PreviousMonthStart), date(w.PreviousMonthEnd)
dayFilter := business.AnalyticsFilter{Sources: sources, Begin: yesterday, End: today}
type datedCount struct {
StatDate string
Metric int
Count int64
}
type datedChannel struct {
StatDate string
SourceDB string `gorm:"column:source_db"`
ID *int64 `gorm:"column:entity_id"`
}
var counts []datedCount
var registrations, pays []datedChannel
var amounts struct{ TodayPay, YesterdayPay, MonthPay, PreviousMonthPay string }
var created struct{ TodayCount, YesterdayCount int64 }
group, queryContext := errgroup.WithContext(ctx)
group.SetLimit(2)
queryDB := db.WithContext(queryContext)
group.Go(func() error {
return storageError(analyticsQuery(queryDB, scope, dayFilter, "tab_sw_report_day_key", mode).
Select("CAST(r.stat_date AS CHAR) AS stat_date,r.metric,COUNT(DISTINCT " + portraitIdentity(queryDB, scope.IDCardDedup) + ") AS count").
Where("r.metric IN (1,2)").Group("r.stat_date,r.metric").Scan(&counts).Error)
})
group.Go(func() error {
f := business.AnalyticsFilter{Sources: sources, Begin: previousStart, End: today}
// 同时取得两日和两个自然月区间的金额;月初的昨日须额外纳入扫描范围。
return storageError(analyticsQuery(queryDB, scope, f, "tab_sw_report_day", 0).
Where("((r.stat_date BETWEEN ? AND ?) OR (r.stat_date BETWEEN ? AND ?) OR r.stat_date=?)", monthStart, today, previousStart, previousEnd, yesterday).
Select("COALESCE(SUM(CASE WHEN r.stat_date=? THEN r.total_pay ELSE 0 END),0) AS today_pay,"+
"COALESCE(SUM(CASE WHEN r.stat_date=? THEN r.total_pay ELSE 0 END),0) AS yesterday_pay,"+
"COALESCE(SUM(CASE WHEN r.stat_date BETWEEN ? AND ? THEN r.total_pay ELSE 0 END),0) AS month_pay,"+
"COALESCE(SUM(CASE WHEN r.stat_date BETWEEN ? AND ? THEN r.total_pay ELSE 0 END),0) AS previous_month_pay",
today, yesterday, monthStart, today, previousStart, previousEnd).Scan(&amounts).Error)
})
group.Go(func() error {
// COUNT(DISTINCT identity_key)>0 等价于存在非 NULL 的注册键;保留空字符串的旧语义。
return storageError(analyticsQuery(queryDB, scope, dayFilter, "tab_sw_report_day_key", mode).
Where("r.metric=1 AND r.identity_key IS NOT NULL").Distinct().
Select("CAST(r.stat_date AS CHAR) AS stat_date,r.source_db,r.promote_id AS entity_id").Scan(&registrations).Error)
})
group.Go(func() error {
return storageError(analyticsQuery(queryDB, scope, dayFilter, "tab_sw_report_day", 0).
Select("CAST(r.stat_date AS CHAR) AS stat_date,r.source_db,r.promote_id AS entity_id").
Group("r.stat_date,r.source_db,r.promote_id").Having("SUM(r.total_pay)>0").Scan(&pays).Error)
})
group.Go(func() error {
todayEnd := w.Today.AddDate(0, 0, 1).Unix() - 1
yesterdayEnd := w.Yesterday.AddDate(0, 0, 1).Unix() - 1
return storageError(analyticsBasePromotes(queryDB, scope, dayFilter, "p").
Where("p.create_time BETWEEN ? AND ?", w.Yesterday.Unix(), todayEnd).
Select("COALESCE(SUM(CASE WHEN p.create_time BETWEEN ? AND ? THEN 1 ELSE 0 END),0) AS today_count,"+
"COALESCE(SUM(CASE WHEN p.create_time BETWEEN ? AND ? THEN 1 ELSE 0 END),0) AS yesterday_count",
w.Today.Unix(), todayEnd, w.Yesterday.Unix(), yesterdayEnd).Scan(&created).Error)
})
if err := group.Wait(); err != nil {
return nil, err
}
days := map[string]*business.WorkbenchDay{today: &out.Today, yesterday: &out.Yesterday}
for _, row := range counts {
if day := days[row.StatDate]; day != nil {
putAnalyticsCount(&day.Counts, row.Metric, row.Count)
}
}
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
active := map[string]map[business.AnalyticsKey]bool{today: {}, yesterday: {}}
for _, row := range append(registrations, pays...) {
if keys := active[row.StatDate]; keys != nil && row.ID != nil {
keys[business.AnalyticsKey{SourceDB: row.SourceDB, ID: *row.ID}] = true
}
}
out.Today.ActiveChannels, out.Yesterday.ActiveChannels = int64(len(active[today])), int64(len(active[yesterday]))
out.Today.Pay, out.Yesterday.Pay = amounts.TodayPay, amounts.YesterdayPay
out.Today.NewChannels, out.Yesterday.NewChannels = created.TodayCount, created.YesterdayCount
out.MonthPay, out.PreviousMonthPay = amounts.MonthPay, amounts.PreviousMonthPay
return out, nil
}
@@ -118,12 +151,12 @@ func (r *workbenchRepo) Ranks(ctx context.Context, scope business.AnalyticsScope
}
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 {
if err := analyticsBasePromotes(db, scope, f, "p").Select("p.id,p.source_db,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
row.CreatedUnix, row.LastLoginUnix = po.CreateTime, po.LastLoginTime
}
}
if kind != business.WorkbenchUnlogin {
@@ -144,6 +177,7 @@ func (r *workbenchRepo) Ranks(ctx context.Context, scope business.AnalyticsScope
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 = analyticsPayLeaves(db, q)
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"
}
@@ -160,22 +194,32 @@ func (r *workbenchRepo) Ranks(ctx context.Context, scope business.AnalyticsScope
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
}
func (r *workbenchRepo) FillRankNames(ctx context.Context, rows []*business.WorkbenchRankRow) error {
if len(rows) == 0 {
return nil
}
keys := make([]business.AnalyticsKey, 0, len(rows))
for _, row := range rows {
keys = append(keys, row.AnalyticsKey)
}
var names []workbenchPromotePO
if err := r.data.DB().WithContext(ctx).Table("tab_promote").Select("id,source_db,account").Where("(source_db,id) IN ?", analyticsPairs(keys)).Scan(&names).Error; err != nil {
return business.ErrStorage
}
byKey := make(map[business.AnalyticsKey]string, len(names))
for _, po := range names {
byKey[business.AnalyticsKey{SourceDB: po.SourceDB, ID: po.ID}] = po.Account
}
for _, row := range rows {
row.Name = byKey[row.AnalyticsKey]
}
return nil
}