package storage import ( "fmt" "strconv" "strings" "time" "github.com/shopspring/decimal" "go.mongodb.org/mongo-driver/v2/bson" "dashboard-cdc-worker/internal/dashboard" ) func mergeMongoGiftRows(rows map[string]*giftMetricRow, metrics []mongoGiftAmount, anchorShare bool) { for _, item := range metrics { key := giftMetricKey(item.day, "", item.userID) row := rows[key] if row == nil { row = &giftMetricRow{day: item.day, luckyGiftUsers: make(map[int64]struct{})} rows[key] = row } if anchorShare { row.luckyGiftAnchorShare = row.luckyGiftAnchorShare.Add(item.amount) } else { row.giftConsume = row.giftConsume.Add(item.amount) } } } func mergeSQLLuckyGiftRows(rows map[string]*giftMetricRow, metrics []sqlLuckyGiftRow) { for _, item := range metrics { key := giftMetricKey(item.day, item.country.Code, 0) row := rows[key] if row == nil { row = &giftMetricRow{ day: item.day, sysOrigin: item.sysOrigin, country: item.country, luckyGiftUsers: make(map[int64]struct{}), } rows[key] = row } row.sysOrigin = item.sysOrigin row.country = item.country row.luckyGiftTotalFlow = row.luckyGiftTotalFlow.Add(item.payAmount) row.luckyGiftPayout = row.luckyGiftPayout.Add(item.payout) row.luckyGiftUsers[item.userID] = struct{}{} } } func applyGiftUserCountries(rows map[string]*giftMetricRow, users map[int64]dashboard.UserCountry, unknownCountry string) { for key, row := range rows { if row.country.Code != "" { continue } parts := strings.Split(key, "|") if len(parts) != 3 { continue } userID := parseInt64(parts[2]) user, ok := users[userID] if !ok { delete(rows, key) continue } row.sysOrigin = user.SysOrigin row.country = user.Country countryKey := giftMetricKey(row.day, row.country.Code, 0) countryRow := rows[countryKey] if countryRow == nil { countryRow = &giftMetricRow{ day: row.day, sysOrigin: row.sysOrigin, country: row.country, luckyGiftUsers: make(map[int64]struct{}), } rows[countryKey] = countryRow } countryRow.giftConsume = countryRow.giftConsume.Add(row.giftConsume) countryRow.luckyGiftAnchorShare = countryRow.luckyGiftAnchorShare.Add(row.luckyGiftAnchorShare) delete(rows, key) } } func (row *giftMetricRow) empty() bool { return row.giftConsume.IsZero() && row.luckyGiftAnchorShare.IsZero() && row.luckyGiftTotalFlow.IsZero() && row.luckyGiftPayout.IsZero() && len(row.luckyGiftUsers) == 0 } func giftMetricKey(day, countryCode string, userID int64) string { return fmt.Sprintf("%s|%s|%d", day, countryCode, userID) } func int64SetToSlice(values map[int64]struct{}) []int64 { out := make([]int64, 0, len(values)) for value := range values { out = append(out, value) } return out } func bsonInt64(value interface{}) int64 { switch typed := value.(type) { case int64: return typed case int32: return int64(typed) case int: return int64(typed) case float64: return int64(typed) case string: return parseInt64(typed) default: return parseInt64(fmt.Sprint(typed)) } } func bsonDecimal(value interface{}) decimal.Decimal { switch typed := value.(type) { case decimal.Decimal: return typed case bson.Decimal128: parsed, err := decimal.NewFromString(typed.String()) if err == nil { return parsed } case int64: return decimal.NewFromInt(typed) case int32: return decimal.NewFromInt(int64(typed)) case int: return decimal.NewFromInt(int64(typed)) case float64: return decimal.NewFromFloat(typed) case string: parsed, err := decimal.NewFromString(typed) if err == nil { return parsed } } parsed, err := decimal.NewFromString(fmt.Sprint(value)) if err != nil { return decimal.Zero } return parsed } func parseInt64(value string) int64 { parsed, _ := strconv.ParseInt(strings.TrimSpace(value), 10, 64) return parsed } func defaultSysOrigin(values []string) string { if len(values) == 0 { return "" } return strings.TrimSpace(values[0]) } func timezoneOffset(timezone string) string { location, err := time.LoadLocation(timezone) if err != nil { return "+00:00" } _, offset := time.Now().In(location).Zone() sign := "+" if offset < 0 { sign = "-" offset = -offset } hours := offset / 3600 minutes := (offset % 3600) / 60 return fmt.Sprintf("%s%02d:%02d", sign, hours, minutes) } func quoteIdent(value string) string { return "`" + strings.ReplaceAll(value, "`", "``") + "`" }