Support salary transfer dashboard metrics

This commit is contained in:
zhx 2026-05-25 13:23:34 +08:00
parent 496e5b895e
commit af1fbde1ac
7 changed files with 89 additions and 18 deletions

View File

@ -36,7 +36,7 @@ func (m *Mapper) Map(ctx context.Context, schema string, table string, action st
case "user_freight_balance_running_water": case "user_freight_balance_running_water":
return m.mapDealerRecharge(ctx, schema, table, action, row) return m.mapDealerRecharge(ctx, schema, table, action, row)
case "user_salary_account_running_water", "user_salary_diamond_running_water": case "user_salary_account_running_water", "user_salary_diamond_running_water":
return m.mapSalaryExchange(ctx, schema, table, action, row) return m.mapSalary(ctx, schema, table, action, row)
default: default:
if strings.HasPrefix(table, "wallet_gold_asset_record_") { if strings.HasPrefix(table, "wallet_gold_asset_record_") {
return m.mapWalletGame(ctx, schema, table, action, row) return m.mapWalletGame(ctx, schema, table, action, row)
@ -126,17 +126,24 @@ func (m *Mapper) mapDealerRecharge(ctx context.Context, schema string, table str
return items, nil return items, nil
} }
func (m *Mapper) mapSalaryExchange(ctx context.Context, schema string, table string, action string, row Row) ([]Contribution, error) { func (m *Mapper) mapSalary(ctx context.Context, schema string, table string, action string, row Row) ([]Contribution, error) {
if row.Int64("type") != 1 { if row.Int64("type") != 1 {
return nil, nil return nil, nil
} }
event := row.String("salary_event") event := row.String("salary_event")
if event != "SALARY_EXCHANGE" && event != "SALARY_EXCHANGE_GOLD_COINS" && isExchange := event == "SALARY_EXCHANGE" || event == "SALARY_EXCHANGE_GOLD_COINS" ||
event != "EXCHANGE_GOLD_COINS" && event != "BILL_EXCHANGE_GOLD_COINS" { event == "EXCHANGE_GOLD_COINS" || event == "BILL_EXCHANGE_GOLD_COINS"
isTransfer := event == "SALARY_TRANSFER"
if !isExchange && !isTransfer {
return nil, nil return nil, nil
} }
return m.amountContribution(ctx, schema, table, action, row, row.Decimal("amount"), func(c *Contribution, amount decimal.Decimal) { return m.amountContribution(ctx, schema, table, action, row, row.Decimal("amount"), func(c *Contribution, amount decimal.Decimal) {
c.SalaryExchange = amount if isExchange {
c.SalaryExchange = amount
}
if isTransfer {
c.SalaryTransfer = amount
}
}) })
} }

View File

@ -29,6 +29,7 @@ func (p *Projector) writeRealtimeRedis(ctx context.Context, contributions []Cont
hincrDecimal(pipe, ctx, key, "google_recharge", c.GoogleRecharge.Mul(sign)) hincrDecimal(pipe, ctx, key, "google_recharge", c.GoogleRecharge.Mul(sign))
hincrDecimal(pipe, ctx, key, "dealer_recharge", c.DealerRecharge.Mul(sign)) hincrDecimal(pipe, ctx, key, "dealer_recharge", c.DealerRecharge.Mul(sign))
hincrDecimal(pipe, ctx, key, "salary_exchange", c.SalaryExchange.Mul(sign)) hincrDecimal(pipe, ctx, key, "salary_exchange", c.SalaryExchange.Mul(sign))
hincrDecimal(pipe, ctx, key, "salary_transfer", c.SalaryTransfer.Mul(sign))
hincrDecimal(pipe, ctx, key, "lucky_gift_total_flow", c.LuckyGiftTotalFlow.Mul(sign)) hincrDecimal(pipe, ctx, key, "lucky_gift_total_flow", c.LuckyGiftTotalFlow.Mul(sign))
hincrDecimal(pipe, ctx, key, "lucky_gift_payout", c.LuckyGiftPayout.Mul(sign)) hincrDecimal(pipe, ctx, key, "lucky_gift_payout", c.LuckyGiftPayout.Mul(sign))
hincrDecimal(pipe, ctx, key, "game_total_flow", c.GameTotalFlow.Mul(sign)) hincrDecimal(pipe, ctx, key, "game_total_flow", c.GameTotalFlow.Mul(sign))

View File

@ -47,6 +47,7 @@ type Contribution struct {
GoogleRecharge decimal.Decimal GoogleRecharge decimal.Decimal
DealerRecharge decimal.Decimal DealerRecharge decimal.Decimal
SalaryExchange decimal.Decimal SalaryExchange decimal.Decimal
SalaryTransfer decimal.Decimal
GiftConsume decimal.Decimal GiftConsume decimal.Decimal
LuckyGiftTotalFlow decimal.Decimal LuckyGiftTotalFlow decimal.Decimal
LuckyGiftUser bool LuckyGiftUser bool
@ -82,6 +83,7 @@ func (c Contribution) Empty() bool {
c.GoogleRecharge.IsZero() && c.GoogleRecharge.IsZero() &&
c.DealerRecharge.IsZero() && c.DealerRecharge.IsZero() &&
c.SalaryExchange.IsZero() && c.SalaryExchange.IsZero() &&
c.SalaryTransfer.IsZero() &&
c.GiftConsume.IsZero() && c.GiftConsume.IsZero() &&
c.LuckyGiftTotalFlow.IsZero() && c.LuckyGiftTotalFlow.IsZero() &&
!c.LuckyGiftUser && !c.LuckyGiftUser &&

View File

@ -100,7 +100,7 @@ func (r *MySQLRepository) upsertDailyAmounts(ctx context.Context, tx *sql.Tx, c
INSERT INTO country_dashboard_daily_metric ( INSERT INTO country_dashboard_daily_metric (
stat_date, date_number, sys_origin, country_code, country_name, stat_date, date_number, sys_origin, country_code, country_name,
new_user_recharge, official_recharge, mifapay_recharge, google_recharge, new_user_recharge, official_recharge, mifapay_recharge, google_recharge,
dealer_recharge, salary_exchange, gift_consume, lucky_gift_total_flow, lucky_gift_payout, dealer_recharge, salary_exchange, salary_transfer, gift_consume, lucky_gift_total_flow, lucky_gift_payout,
game_total_flow, game_payout, refreshed_at game_total_flow, game_payout, refreshed_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
@ -111,6 +111,7 @@ ON DUPLICATE KEY UPDATE
google_recharge = google_recharge + VALUES(google_recharge), google_recharge = google_recharge + VALUES(google_recharge),
dealer_recharge = dealer_recharge + VALUES(dealer_recharge), dealer_recharge = dealer_recharge + VALUES(dealer_recharge),
salary_exchange = salary_exchange + VALUES(salary_exchange), salary_exchange = salary_exchange + VALUES(salary_exchange),
salary_transfer = salary_transfer + VALUES(salary_transfer),
gift_consume = gift_consume + VALUES(gift_consume), gift_consume = gift_consume + VALUES(gift_consume),
lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow), lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow),
lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout), lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout),
@ -119,7 +120,7 @@ ON DUPLICATE KEY UPDATE
refreshed_at = NOW() refreshed_at = NOW()
`, day, dashboard.DateNumber(day), c.SysOrigin, c.Country.Code, c.Country.Name, `, day, dashboard.DateNumber(day), c.SysOrigin, c.Country.Code, c.Country.Name,
newUserRecharge.Mul(sign), c.OfficialRecharge.Mul(sign), c.MifapayRecharge.Mul(sign), c.GoogleRecharge.Mul(sign), newUserRecharge.Mul(sign), c.OfficialRecharge.Mul(sign), c.MifapayRecharge.Mul(sign), c.GoogleRecharge.Mul(sign),
c.DealerRecharge.Mul(sign), c.SalaryExchange.Mul(sign), c.GiftConsume.Mul(sign), c.LuckyGiftTotalFlow.Mul(sign), c.LuckyGiftPayout.Mul(sign), c.DealerRecharge.Mul(sign), c.SalaryExchange.Mul(sign), c.SalaryTransfer.Mul(sign), c.GiftConsume.Mul(sign), c.LuckyGiftTotalFlow.Mul(sign), c.LuckyGiftPayout.Mul(sign),
c.GameTotalFlow.Mul(sign), c.GamePayout.Mul(sign)) c.GameTotalFlow.Mul(sign), c.GamePayout.Mul(sign))
return err return err
} }
@ -130,9 +131,9 @@ func (r *MySQLRepository) upsertPeriodAmounts(ctx context.Context, tx *sql.Tx, c
INSERT INTO country_dashboard_period_metric ( INSERT INTO country_dashboard_period_metric (
period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date, period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date,
sys_origin, country_code, country_name, new_user_recharge, official_recharge, mifapay_recharge, sys_origin, country_code, country_name, new_user_recharge, official_recharge, mifapay_recharge,
google_recharge, dealer_recharge, salary_exchange, gift_consume, lucky_gift_total_flow, lucky_gift_payout, google_recharge, dealer_recharge, salary_exchange, salary_transfer, gift_consume, lucky_gift_total_flow, lucky_gift_payout,
game_total_flow, game_payout, refreshed_at game_total_flow, game_payout, refreshed_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
period_name = VALUES(period_name), period_name = VALUES(period_name),
period_start_date = VALUES(period_start_date), period_start_date = VALUES(period_start_date),
@ -144,6 +145,7 @@ ON DUPLICATE KEY UPDATE
google_recharge = google_recharge + VALUES(google_recharge), google_recharge = google_recharge + VALUES(google_recharge),
dealer_recharge = dealer_recharge + VALUES(dealer_recharge), dealer_recharge = dealer_recharge + VALUES(dealer_recharge),
salary_exchange = salary_exchange + VALUES(salary_exchange), salary_exchange = salary_exchange + VALUES(salary_exchange),
salary_transfer = salary_transfer + VALUES(salary_transfer),
gift_consume = gift_consume + VALUES(gift_consume), gift_consume = gift_consume + VALUES(gift_consume),
lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow), lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow),
lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout), lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout),
@ -153,7 +155,8 @@ ON DUPLICATE KEY UPDATE
`, window.Type, window.Key, statTimezone, window.Name, window.StartDate, window.EndDate, `, window.Type, window.Key, statTimezone, window.Name, window.StartDate, window.EndDate,
c.SysOrigin, c.Country.Code, c.Country.Name, newUserRecharge.Mul(sign), c.OfficialRecharge.Mul(sign), c.SysOrigin, c.Country.Code, c.Country.Name, newUserRecharge.Mul(sign), c.OfficialRecharge.Mul(sign),
c.MifapayRecharge.Mul(sign), c.GoogleRecharge.Mul(sign), c.DealerRecharge.Mul(sign), c.SalaryExchange.Mul(sign), c.MifapayRecharge.Mul(sign), c.GoogleRecharge.Mul(sign), c.DealerRecharge.Mul(sign), c.SalaryExchange.Mul(sign),
c.GiftConsume.Mul(sign), c.LuckyGiftTotalFlow.Mul(sign), c.LuckyGiftPayout.Mul(sign), c.GameTotalFlow.Mul(sign), c.GamePayout.Mul(sign)) c.SalaryTransfer.Mul(sign), c.GiftConsume.Mul(sign), c.LuckyGiftTotalFlow.Mul(sign), c.LuckyGiftPayout.Mul(sign),
c.GameTotalFlow.Mul(sign), c.GamePayout.Mul(sign))
return err return err
} }

View File

@ -20,6 +20,7 @@ type amountTotals struct {
googleRecharge decimal.Decimal googleRecharge decimal.Decimal
dealerRecharge decimal.Decimal dealerRecharge decimal.Decimal
salaryExchange decimal.Decimal salaryExchange decimal.Decimal
salaryTransfer decimal.Decimal
giftConsume decimal.Decimal giftConsume decimal.Decimal
luckyGiftTotalFlow decimal.Decimal luckyGiftTotalFlow decimal.Decimal
luckyGiftPayout decimal.Decimal luckyGiftPayout decimal.Decimal
@ -253,6 +254,7 @@ func (t *amountTotals) add(c dashboard.Contribution, newUserRecharge decimal.Dec
t.googleRecharge = t.googleRecharge.Add(c.GoogleRecharge.Mul(sign)) t.googleRecharge = t.googleRecharge.Add(c.GoogleRecharge.Mul(sign))
t.dealerRecharge = t.dealerRecharge.Add(c.DealerRecharge.Mul(sign)) t.dealerRecharge = t.dealerRecharge.Add(c.DealerRecharge.Mul(sign))
t.salaryExchange = t.salaryExchange.Add(c.SalaryExchange.Mul(sign)) t.salaryExchange = t.salaryExchange.Add(c.SalaryExchange.Mul(sign))
t.salaryTransfer = t.salaryTransfer.Add(c.SalaryTransfer.Mul(sign))
t.giftConsume = t.giftConsume.Add(c.GiftConsume.Mul(sign)) t.giftConsume = t.giftConsume.Add(c.GiftConsume.Mul(sign))
t.luckyGiftTotalFlow = t.luckyGiftTotalFlow.Add(c.LuckyGiftTotalFlow.Mul(sign)) t.luckyGiftTotalFlow = t.luckyGiftTotalFlow.Add(c.LuckyGiftTotalFlow.Mul(sign))
t.luckyGiftPayout = t.luckyGiftPayout.Add(c.LuckyGiftPayout.Mul(sign)) t.luckyGiftPayout = t.luckyGiftPayout.Add(c.LuckyGiftPayout.Mul(sign))
@ -263,6 +265,7 @@ func (t *amountTotals) add(c dashboard.Contribution, newUserRecharge decimal.Dec
func (t amountTotals) empty() bool { func (t amountTotals) empty() bool {
return t.newUserRecharge.IsZero() && t.officialRecharge.IsZero() && t.mifapayRecharge.IsZero() && return t.newUserRecharge.IsZero() && t.officialRecharge.IsZero() && t.mifapayRecharge.IsZero() &&
t.googleRecharge.IsZero() && t.dealerRecharge.IsZero() && t.salaryExchange.IsZero() && t.googleRecharge.IsZero() && t.dealerRecharge.IsZero() && t.salaryExchange.IsZero() &&
t.salaryTransfer.IsZero() &&
t.giftConsume.IsZero() && t.luckyGiftTotalFlow.IsZero() && t.luckyGiftPayout.IsZero() && t.giftConsume.IsZero() && t.luckyGiftTotalFlow.IsZero() && t.luckyGiftPayout.IsZero() &&
t.gameTotalFlow.IsZero() && t.gamePayout.IsZero() t.gameTotalFlow.IsZero() && t.gamePayout.IsZero()
} }

View File

@ -11,7 +11,7 @@ func (r *MySQLRepository) upsertDailyAmountTotals(ctx context.Context, tx *sql.T
INSERT INTO country_dashboard_daily_metric ( INSERT INTO country_dashboard_daily_metric (
stat_date, date_number, sys_origin, country_code, country_name, stat_date, date_number, sys_origin, country_code, country_name,
new_user_recharge, official_recharge, mifapay_recharge, google_recharge, new_user_recharge, official_recharge, mifapay_recharge, google_recharge,
dealer_recharge, salary_exchange, gift_consume, lucky_gift_total_flow, lucky_gift_payout, dealer_recharge, salary_exchange, salary_transfer, gift_consume, lucky_gift_total_flow, lucky_gift_payout,
game_total_flow, game_payout, refreshed_at game_total_flow, game_payout, refreshed_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
@ -22,6 +22,7 @@ ON DUPLICATE KEY UPDATE
google_recharge = google_recharge + VALUES(google_recharge), google_recharge = google_recharge + VALUES(google_recharge),
dealer_recharge = dealer_recharge + VALUES(dealer_recharge), dealer_recharge = dealer_recharge + VALUES(dealer_recharge),
salary_exchange = salary_exchange + VALUES(salary_exchange), salary_exchange = salary_exchange + VALUES(salary_exchange),
salary_transfer = salary_transfer + VALUES(salary_transfer),
gift_consume = gift_consume + VALUES(gift_consume), gift_consume = gift_consume + VALUES(gift_consume),
lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow), lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow),
lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout), lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout),
@ -30,7 +31,7 @@ ON DUPLICATE KEY UPDATE
refreshed_at = NOW() refreshed_at = NOW()
`, key.day, dateNumber(key.day), key.sysOrigin, key.countryCode, key.countryName, `, key.day, dateNumber(key.day), key.sysOrigin, key.countryCode, key.countryName,
totals.newUserRecharge, totals.officialRecharge, totals.mifapayRecharge, totals.googleRecharge, totals.newUserRecharge, totals.officialRecharge, totals.mifapayRecharge, totals.googleRecharge,
totals.dealerRecharge, totals.salaryExchange, totals.giftConsume, totals.luckyGiftTotalFlow, totals.luckyGiftPayout, totals.dealerRecharge, totals.salaryExchange, totals.salaryTransfer, totals.giftConsume, totals.luckyGiftTotalFlow, totals.luckyGiftPayout,
totals.gameTotalFlow, totals.gamePayout) totals.gameTotalFlow, totals.gamePayout)
return err return err
} }
@ -40,9 +41,9 @@ func (r *MySQLRepository) upsertPeriodAmountTotals(ctx context.Context, tx *sql.
INSERT INTO country_dashboard_period_metric ( INSERT INTO country_dashboard_period_metric (
period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date, period_type, period_key, stat_timezone, period_name, period_start_date, period_end_date,
sys_origin, country_code, country_name, new_user_recharge, official_recharge, mifapay_recharge, sys_origin, country_code, country_name, new_user_recharge, official_recharge, mifapay_recharge,
google_recharge, dealer_recharge, salary_exchange, gift_consume, lucky_gift_total_flow, lucky_gift_payout, google_recharge, dealer_recharge, salary_exchange, salary_transfer, gift_consume, lucky_gift_total_flow, lucky_gift_payout,
game_total_flow, game_payout, refreshed_at game_total_flow, game_payout, refreshed_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
period_name = VALUES(period_name), period_name = VALUES(period_name),
period_start_date = VALUES(period_start_date), period_start_date = VALUES(period_start_date),
@ -54,6 +55,7 @@ ON DUPLICATE KEY UPDATE
google_recharge = google_recharge + VALUES(google_recharge), google_recharge = google_recharge + VALUES(google_recharge),
dealer_recharge = dealer_recharge + VALUES(dealer_recharge), dealer_recharge = dealer_recharge + VALUES(dealer_recharge),
salary_exchange = salary_exchange + VALUES(salary_exchange), salary_exchange = salary_exchange + VALUES(salary_exchange),
salary_transfer = salary_transfer + VALUES(salary_transfer),
gift_consume = gift_consume + VALUES(gift_consume), gift_consume = gift_consume + VALUES(gift_consume),
lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow), lucky_gift_total_flow = lucky_gift_total_flow + VALUES(lucky_gift_total_flow),
lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout), lucky_gift_payout = lucky_gift_payout + VALUES(lucky_gift_payout),
@ -63,7 +65,8 @@ ON DUPLICATE KEY UPDATE
`, key.periodType, key.periodKey, key.statTimezone, key.periodName, ptrOrNil(key.startDate), ptrOrNil(key.endDate), `, key.periodType, key.periodKey, key.statTimezone, key.periodName, ptrOrNil(key.startDate), ptrOrNil(key.endDate),
key.sysOrigin, key.countryCode, key.countryName, totals.newUserRecharge, totals.officialRecharge, key.sysOrigin, key.countryCode, key.countryName, totals.newUserRecharge, totals.officialRecharge,
totals.mifapayRecharge, totals.googleRecharge, totals.dealerRecharge, totals.salaryExchange, totals.mifapayRecharge, totals.googleRecharge, totals.dealerRecharge, totals.salaryExchange,
totals.giftConsume, totals.luckyGiftTotalFlow, totals.luckyGiftPayout, totals.gameTotalFlow, totals.gamePayout) totals.salaryTransfer, totals.giftConsume, totals.luckyGiftTotalFlow, totals.luckyGiftPayout,
totals.gameTotalFlow, totals.gamePayout)
return err return err
} }

View File

@ -1,9 +1,15 @@
package storage package storage
import "context" import (
"context"
"errors"
"fmt"
driverMysql "github.com/go-sql-driver/mysql"
)
func (r *MySQLRepository) EnsureSchema(ctx context.Context) error { func (r *MySQLRepository) EnsureSchema(ctx context.Context) error {
return execAll(ctx, r.db, []string{ if err := execAll(ctx, r.db, []string{
`CREATE TABLE IF NOT EXISTS dashboard_cdc_checkpoint ( `CREATE TABLE IF NOT EXISTS dashboard_cdc_checkpoint (
service_name varchar(64) NOT NULL, service_name varchar(64) NOT NULL,
binlog_file varchar(255) NOT NULL, binlog_file varchar(255) NOT NULL,
@ -37,7 +43,10 @@ func (r *MySQLRepository) EnsureSchema(ctx context.Context) error {
createGamePeriodMetricTable, createGamePeriodMetricTable,
createGamePeriodUserMetricTable, createGamePeriodUserMetricTable,
createRechargeDetailTable, createRechargeDetailTable,
}) }); err != nil {
return err
}
return r.ensureMetricColumns(ctx)
} }
const createDailyMetricTable = `CREATE TABLE IF NOT EXISTS country_dashboard_daily_metric ( const createDailyMetricTable = `CREATE TABLE IF NOT EXISTS country_dashboard_daily_metric (
@ -54,6 +63,7 @@ const createDailyMetricTable = `CREATE TABLE IF NOT EXISTS country_dashboard_dai
google_recharge decimal(20,2) NOT NULL DEFAULT 0, google_recharge decimal(20,2) NOT NULL DEFAULT 0,
dealer_recharge decimal(20,2) NOT NULL DEFAULT 0, dealer_recharge decimal(20,2) NOT NULL DEFAULT 0,
salary_exchange decimal(20,2) NOT NULL DEFAULT 0, salary_exchange decimal(20,2) NOT NULL DEFAULT 0,
salary_transfer decimal(20,2) NOT NULL DEFAULT 0,
gift_consume decimal(20,2) NOT NULL DEFAULT 0, gift_consume decimal(20,2) NOT NULL DEFAULT 0,
lucky_gift_total_flow decimal(20,2) NOT NULL DEFAULT 0, lucky_gift_total_flow decimal(20,2) NOT NULL DEFAULT 0,
lucky_gift_user bigint NOT NULL DEFAULT 0, lucky_gift_user bigint NOT NULL DEFAULT 0,
@ -99,6 +109,7 @@ const createPeriodMetricTable = `CREATE TABLE IF NOT EXISTS country_dashboard_pe
google_recharge decimal(20,2) NOT NULL DEFAULT 0, google_recharge decimal(20,2) NOT NULL DEFAULT 0,
dealer_recharge decimal(20,2) NOT NULL DEFAULT 0, dealer_recharge decimal(20,2) NOT NULL DEFAULT 0,
salary_exchange decimal(20,2) NOT NULL DEFAULT 0, salary_exchange decimal(20,2) NOT NULL DEFAULT 0,
salary_transfer decimal(20,2) NOT NULL DEFAULT 0,
gift_consume decimal(20,2) NOT NULL DEFAULT 0, gift_consume decimal(20,2) NOT NULL DEFAULT 0,
lucky_gift_total_flow decimal(20,2) NOT NULL DEFAULT 0, lucky_gift_total_flow decimal(20,2) NOT NULL DEFAULT 0,
lucky_gift_user bigint NOT NULL DEFAULT 0, lucky_gift_user bigint NOT NULL DEFAULT 0,
@ -112,6 +123,47 @@ const createPeriodMetricTable = `CREATE TABLE IF NOT EXISTS country_dashboard_pe
KEY idx_origin_period (sys_origin, stat_timezone, period_type, period_key) KEY idx_origin_period (sys_origin, stat_timezone, period_type, period_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci` ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`
func (r *MySQLRepository) ensureMetricColumns(ctx context.Context) error {
if err := r.addColumnIfMissing(ctx, "country_dashboard_daily_metric", "salary_transfer",
"ALTER TABLE country_dashboard_daily_metric ADD COLUMN salary_transfer decimal(20,2) NOT NULL DEFAULT 0 AFTER salary_exchange"); err != nil {
return err
}
return r.addColumnIfMissing(ctx, "country_dashboard_period_metric", "salary_transfer",
"ALTER TABLE country_dashboard_period_metric ADD COLUMN salary_transfer decimal(20,2) NOT NULL DEFAULT 0 AFTER salary_exchange")
}
func (r *MySQLRepository) addColumnIfMissing(ctx context.Context, table string, column string, ddl string) error {
var exists int
err := r.db.QueryRowContext(ctx, `
SELECT COUNT(1)
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = ?
AND column_name = ?
`, table, column).Scan(&exists)
if err != nil {
return err
}
if exists > 0 {
return nil
}
if _, err := r.db.ExecContext(ctx, ddl); err != nil && !isDuplicateColumnError(err) {
return err
}
return nil
}
func isDuplicateColumnError(err error) bool {
var mysqlErr *driverMysql.MySQLError
if err == nil {
return false
}
if errors.As(err, &mysqlErr) {
return mysqlErr.Number == 1060
}
return fmt.Sprint(err) == "Error 1060 (42S21): Duplicate column name 'salary_transfer'"
}
const createPeriodUserMetricTable = `CREATE TABLE IF NOT EXISTS country_dashboard_period_user_metric ( const createPeriodUserMetricTable = `CREATE TABLE IF NOT EXISTS country_dashboard_period_user_metric (
id bigint NOT NULL AUTO_INCREMENT, id bigint NOT NULL AUTO_INCREMENT,
period_type varchar(16) NOT NULL, period_type varchar(16) NOT NULL,