fix: include inactive channels in negative activity filters
This commit is contained in:
@@ -64,6 +64,13 @@ func (f AnalyticsFilter) MatchesMetrics(row *AnalyticsRow) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inactive channels match a negative activity filter, unless another selected
|
||||||
|
// metric explicitly requires activity.
|
||||||
|
func (f AnalyticsFilter) IncludesInactiveRows() bool {
|
||||||
|
return (f.HasRegister != nil || f.HasPay != nil) &&
|
||||||
|
(f.HasRegister == nil || !*f.HasRegister) && (f.HasPay == nil || !*f.HasPay)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *AnalyticsRow) BusinessName() string {
|
func (r *AnalyticsRow) BusinessName() string {
|
||||||
if value := strings.TrimSpace(r.BindingAccount); value != "" {
|
if value := strings.TrimSpace(r.BindingAccount); value != "" {
|
||||||
return value
|
return value
|
||||||
|
|||||||
@@ -228,6 +228,11 @@ func (r *analyticsRepo) Aggregate(ctx context.Context, kind business.AnalyticsKi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if metricFilter {
|
if metricFilter {
|
||||||
|
if f.IncludesInactiveRows() {
|
||||||
|
if err := includeInactiveAnalyticsRows(db, scope, f, rowMap); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
for key, row := range rowMap {
|
for key, row := range rowMap {
|
||||||
match, err := f.MatchesMetrics(row)
|
match, err := f.MatchesMetrics(row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -313,6 +318,28 @@ func (r *analyticsRepo) Aggregate(ctx context.Context, kind business.AnalyticsKi
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func includeInactiveAnalyticsRows(db *gorm.DB, scope business.AnalyticsScope, f business.AnalyticsFilter, rows map[business.AnalyticsKey]*business.AnalyticsRow) error {
|
||||||
|
q := analyticsPromotes(db, scope, f, "p")
|
||||||
|
if len(f.GameNames) > 0 {
|
||||||
|
applications := db.Table("tab_promote_apply a").Select("1").Joins("JOIN tab_game g ON g.source_db=a.source_db AND g.id=a.game_id").Where("a.source_db=p.source_db AND a.promote_id=p.id AND a.status=1 AND g.game_name IN ?", f.GameNames)
|
||||||
|
q = q.Where("EXISTS (?)", applications)
|
||||||
|
}
|
||||||
|
id := "p.id"
|
||||||
|
if scope.IncludeSub {
|
||||||
|
id = "CASE WHEN p.promote_level=1 THEN p.id WHEN p.top_promote_id>0 THEN p.top_promote_id ELSE p.id END"
|
||||||
|
}
|
||||||
|
var keys []business.AnalyticsKey
|
||||||
|
if err := q.Select("DISTINCT p.source_db," + id + " AS id").Scan(&keys).Error; err != nil {
|
||||||
|
return business.ErrStorage
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
if rows[key] == nil {
|
||||||
|
rows[key] = &business.AnalyticsRow{AnalyticsKey: key, Pay: "0"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *analyticsRepo) FillNames(ctx context.Context, kind business.AnalyticsKind, scope business.AnalyticsScope, f business.AnalyticsFilter, rows []*business.AnalyticsRow) error {
|
func (r *analyticsRepo) FillNames(ctx context.Context, kind business.AnalyticsKind, scope business.AnalyticsScope, f business.AnalyticsFilter, rows []*business.AnalyticsRow) error {
|
||||||
if len(rows) == 0 {
|
if len(rows) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -87,6 +87,49 @@ func TestAnalyticsMetricFiltersPreserveGlobalDedupAndEffectiveCount(t *testing.T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnalyticsNegativeFiltersIncludeInactiveAuthorizedChannels(t *testing.T) {
|
||||||
|
db, repo, scope, filter := prepareAnalyticsData(t)
|
||||||
|
for _, sql := range []string{
|
||||||
|
"INSERT INTO tab_promote(id,source_db,account,promote_level,parent_id,top_promote_id,busier_id) VALUES(200,'lx','inactive',1,0,0,14),(201,'lx','invisible',1,0,0,999),(202,'lx','inactive-child',2,200,200,14)",
|
||||||
|
"INSERT INTO tab_promote_apply(source_db,promote_id,game_id,status) VALUES('lx',202,1,1)",
|
||||||
|
} {
|
||||||
|
if err := db.Exec(sql).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
no := false
|
||||||
|
filter.HasRegister, filter.HasPay = &no, &no
|
||||||
|
filter.Sources = []string{"lx"}
|
||||||
|
for _, games := range [][]string{nil, {"当前游戏甲"}} {
|
||||||
|
filter.GameNames = games
|
||||||
|
// Use the actual game name; the report's saved historical name can differ.
|
||||||
|
if len(games) > 0 {
|
||||||
|
if err := db.Table("tab_game").Where("source_db='lx' AND id=1").Pluck("game_name", &filter.GameNames).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data, err := repo.Aggregate(context.Background(), business.ChannelAnalytics, scope, filter)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
for _, row := range data.Rows {
|
||||||
|
if row.ID == 201 || row.ID == 202 {
|
||||||
|
t.Fatalf("permission or tree aggregation leak: %+v", row)
|
||||||
|
}
|
||||||
|
if row.ID == 200 {
|
||||||
|
found = true
|
||||||
|
if row.Counts.Register != 0 || row.Pay != "0" {
|
||||||
|
t.Fatalf("inactive metrics: %+v", row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("inactive authorized channel missing with games %v", filter.GameNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAnalyticsRowsOnlyComeFromSelectedPeriod(t *testing.T) {
|
func TestAnalyticsRowsOnlyComeFromSelectedPeriod(t *testing.T) {
|
||||||
db, repo, scope, filter := prepareAnalyticsData(t)
|
db, repo, scope, filter := prepareAnalyticsData(t)
|
||||||
for _, sql := range []string{
|
for _, sql := range []string{
|
||||||
|
|||||||
Reference in New Issue
Block a user