196 lines
7.5 KiB
Go

package storage
import (
"context"
"database/sql"
"time"
"dashboard-cdc-worker/internal/dashboard"
)
func (r *MySQLRepository) applyDailyUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, day time.Time, metricType string) error {
if c.DeltaSign < 0 {
return r.deleteDailyUserMetric(ctx, tx, c, day, metricType)
}
inserted, err := insertDailyUserMetric(ctx, tx, c, day, metricType)
if err != nil || !inserted {
return err
}
column := dailyMetricColumn(metricType)
if column == "" {
return nil
}
_, err = tx.ExecContext(ctx, `
INSERT INTO country_dashboard_daily_metric (
stat_date, date_number, sys_origin, country_code, country_name, refreshed_at
, `+column+`
) VALUES (?, ?, ?, ?, ?, NOW(), 1)
ON DUPLICATE KEY UPDATE `+column+` = `+column+` + VALUES(`+column+`),
country_name = VALUES(country_name), refreshed_at = NOW()
`, day, dashboard.DateNumber(day), c.SysOrigin, c.Country.Code, c.Country.Name)
return err
}
func (r *MySQLRepository) applyPeriodUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, window dashboard.PeriodWindow, statTimezone string, metricType string) error {
if c.DeltaSign < 0 {
return r.deletePeriodUserMetric(ctx, tx, c, window, statTimezone, metricType)
}
inserted, err := insertPeriodUserMetric(ctx, tx, c, window, statTimezone, metricType)
if err != nil || !inserted {
return err
}
column := periodMetricColumn(metricType)
if column == "" {
return nil
}
_, err = tx.ExecContext(ctx, `
INSERT INTO country_dashboard_period_metric (
period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date,
sys_origin, country_code, country_name, refreshed_at
, `+column+`
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), 1)
ON DUPLICATE KEY UPDATE `+column+` = `+column+` + VALUES(`+column+`),
country_name = VALUES(country_name), refreshed_at = NOW()
`, window.Type, window.Key, statTimezone, window.Name, window.StartDate, window.EndDate,
c.SysOrigin, c.Country.Code, c.Country.Name)
return err
}
func (r *MySQLRepository) deleteDailyUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, day time.Time, metricType string) error {
deleted, err := deleteDailyUserMetricRow(ctx, tx, c, day, metricType)
if err != nil || !deleted {
return err
}
column := dailyMetricColumn(metricType)
if column == "" {
return nil
}
_, err = tx.ExecContext(ctx, `
UPDATE country_dashboard_daily_metric
SET `+column+` = GREATEST(`+column+` - 1, 0), refreshed_at = NOW()
WHERE stat_date = ? AND sys_origin = ? AND country_code = ?
`, day, c.SysOrigin, c.Country.Code)
return err
}
func (r *MySQLRepository) deletePeriodUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, window dashboard.PeriodWindow, statTimezone string, metricType string) error {
deleted, err := deletePeriodUserMetric(ctx, tx, c, window, statTimezone, metricType)
if err != nil || !deleted {
return err
}
column := periodMetricColumn(metricType)
if column == "" {
return nil
}
_, err = tx.ExecContext(ctx, `
UPDATE country_dashboard_period_metric
SET `+column+` = GREATEST(`+column+` - 1, 0), refreshed_at = NOW()
WHERE period_type = ? AND period_key = ? AND stat_timezone = ? AND sys_origin = ? AND country_code = ?
`, window.Type, window.Key, statTimezone, c.SysOrigin, c.Country.Code)
return err
}
func insertDailyUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, day time.Time, metricType string) (bool, error) {
result, err := tx.ExecContext(ctx, `
INSERT IGNORE INTO country_dashboard_daily_user_metric (
stat_date, sys_origin, country_code, country_name, user_id, metric_type
) VALUES (?, ?, ?, ?, ?, ?)
`, day, c.SysOrigin, c.Country.Code, c.Country.Name, c.UserID, metricType)
if err != nil {
return false, err
}
affected, err := result.RowsAffected()
return affected > 0, err
}
func deleteDailyUserMetricRow(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, day time.Time, metricType string) (bool, error) {
result, err := tx.ExecContext(ctx, `
DELETE FROM country_dashboard_daily_user_metric
WHERE stat_date = ? AND sys_origin = ? AND country_code = ? AND user_id = ? AND metric_type = ?
`, day, c.SysOrigin, c.Country.Code, c.UserID, metricType)
if err != nil {
return false, err
}
affected, err := result.RowsAffected()
return affected > 0, err
}
func (r *MySQLRepository) applyGamePeriodUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, window dashboard.PeriodWindow, statTimezone string) error {
result, err := tx.ExecContext(ctx, `
INSERT IGNORE INTO country_dashboard_game_period_user_metric (
period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date,
sys_origin, country_code, country_name, game_provider, game_id, user_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, window.Type, window.Key, statTimezone, window.Name, window.StartDate, window.EndDate,
c.SysOrigin, c.Country.Code, c.Country.Name, c.GameProvider, c.GameID, c.UserID)
if err != nil {
return err
}
affected, err := result.RowsAffected()
if err != nil || affected == 0 {
return err
}
_, err = tx.ExecContext(ctx, `
INSERT INTO country_dashboard_game_period_metric (
period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date,
sys_origin, country_code, country_name, game_provider, game_id, game_name, user_count, refreshed_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW())
ON DUPLICATE KEY UPDATE user_count = user_count + VALUES(user_count), country_name = VALUES(country_name),
game_name = VALUES(game_name), refreshed_at = NOW()
`, window.Type, window.Key, statTimezone, window.Name, window.StartDate, window.EndDate,
c.SysOrigin, c.Country.Code, c.Country.Name, c.GameProvider, c.GameID, c.GameName)
return err
}
func insertPeriodUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, window dashboard.PeriodWindow, statTimezone string, metricType string) (bool, error) {
result, err := tx.ExecContext(ctx, `
INSERT IGNORE INTO country_dashboard_period_user_metric (
period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date,
sys_origin, country_code, country_name, user_id, metric_type
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, window.Type, window.Key, statTimezone, window.Name, window.StartDate, window.EndDate,
c.SysOrigin, c.Country.Code, c.Country.Name, c.UserID, metricType)
if err != nil {
return false, err
}
affected, err := result.RowsAffected()
return affected > 0, err
}
func deletePeriodUserMetric(ctx context.Context, tx *sql.Tx, c dashboard.Contribution, window dashboard.PeriodWindow, statTimezone string, metricType string) (bool, error) {
result, err := tx.ExecContext(ctx, `
DELETE FROM country_dashboard_period_user_metric
WHERE period_type = ? AND period_key = ? AND stat_timezone = ?
AND sys_origin = ? AND country_code = ? AND user_id = ? AND metric_type = ?
`, window.Type, window.Key, statTimezone, c.SysOrigin, c.Country.Code, c.UserID, metricType)
if err != nil {
return false, err
}
affected, err := result.RowsAffected()
return affected > 0, err
}
func dailyMetricColumn(metricType string) string {
switch metricType {
case dashboard.MetricCountryNewUser:
return "country_new_user"
case dashboard.MetricLuckyGiftUser:
return "lucky_gift_user"
case dashboard.MetricGameUser:
return "game_user"
case dashboard.MetricRechargeUser:
return "daily_recharge_user"
default:
return ""
}
}
func periodMetricColumn(metricType string) string {
return dailyMetricColumn(metricType)
}
func dashboardPtr(value time.Time) *time.Time {
v := value
return &v
}