267 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package mysql
import (
"context"
"database/sql"
"strconv"
"strings"
"hyapp/pkg/appcode"
)
type socialUserDayDelta struct {
AppCode string
StatTZ string
StatDay string
UserID int64
DeviceID string
CountryID int64
RegionID int64
UserRole string
UpdatedAt int64
FirstOpen int64
Registered int64
Active int64
RoomJoinOK int64
RoomJoinNG int64
MicUp int64
Gift int64
NormalGift int64
LuckyGift int64
SuperGift int64
Recharge int64
FirstRecharge int64
RepeatRecharge int64
GameBet int64
SelfGameBet int64
ProbabilityBet int64
Chat int64
FollowUser int64
FollowRoom int64
GiftPanel int64
ProbabilityPanel int64
AppStayMS int64
RoomStayMS int64
MicStayMS int64
RechargeCount int64
RechargeUSDMinor int64
GoogleRechargeUSDMinor int64
ThirdPartyRechargeUSDMinor int64
CoinSellerRechargeUSDMinor int64
FirstRechargeUSDMinor int64
RepeatRechargeUSDMinor int64
NormalGiftCoin int64
LuckyGiftCoin int64
SuperGiftCoin int64
LuckyGiftPayoutCoin int64
SuperGiftPayoutCoin int64
GameBetCoin int64
GameBetCount int64
SelfGameBetCoin int64
SelfGameBetCount int64
ProbabilityBetCoin int64
ProbabilityBetCount int64
OtherConsumeCoin int64
OutputCoin int64
}
func applySocialUserDayDelta(ctx context.Context, tx *sql.Tx, delta socialUserDayDelta) error {
subjectKey := socialSubjectKey(delta.UserID, delta.DeviceID)
if subjectKey == "" {
return nil
}
app := appcode.Normalize(delta.AppCode)
statTZ := normalizeStatTZ(delta.StatTZ)
day := strings.TrimSpace(delta.StatDay)
if app == "" || day == "" {
return nil
}
countryID, regionID := normalizeDimension(delta.CountryID, delta.RegionID)
role := normalizeSocialUserRole(delta.UserRole)
// 这张表是 P0-P5 的唯一宽表投影。外层 withEvent 或 app_tracking_events 的 INSERT IGNORE 已经完成事件级幂等;
// 这里允许同一事务内分步骤 upsert+update保证角色和维度快照可以随任意事实补齐但金额类字段只随已幂等的事实累加一次。
if _, err := tx.ExecContext(ctx, `
INSERT INTO stat_social_user_day (
app_code, stat_tz, stat_day, subject_key, user_id, device_id,
country_id, region_id, user_role, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
user_id = CASE WHEN VALUES(user_id) > 0 THEN VALUES(user_id) ELSE user_id END,
device_id = CASE WHEN VALUES(device_id) <> '' THEN VALUES(device_id) ELSE device_id END,
country_id = CASE WHEN VALUES(country_id) > 0 THEN VALUES(country_id) ELSE country_id END,
region_id = CASE WHEN VALUES(region_id) > 0 THEN VALUES(region_id) ELSE region_id END,
user_role = CASE WHEN VALUES(user_role) = 'host' OR user_role = 'host' THEN 'host' ELSE 'user' END,
updated_at_ms = VALUES(updated_at_ms)
`, app, statTZ, day, subjectKey, normalizeID(delta.UserID), strings.TrimSpace(delta.DeviceID), countryID, regionID, role, delta.UpdatedAt); err != nil {
return err
}
_, err := tx.ExecContext(ctx, `
UPDATE stat_social_user_day
SET app_first_open = GREATEST(app_first_open, ?),
registered_success = GREATEST(registered_success, ?),
active_user = GREATEST(active_user, ?),
room_join_success = GREATEST(room_join_success, ?),
room_join_fail = GREATEST(room_join_fail, ?),
mic_up_success = GREATEST(mic_up_success, ?),
gift_sent = GREATEST(gift_sent, ?),
normal_gift_sent = GREATEST(normal_gift_sent, ?),
lucky_gift_sent = GREATEST(lucky_gift_sent, ?),
super_lucky_gift_sent = GREATEST(super_lucky_gift_sent, ?),
recharge_user = GREATEST(recharge_user, ?),
first_recharge_user = GREATEST(first_recharge_user, ?),
repeat_recharge_user = GREATEST(repeat_recharge_user, ?),
game_bet_user = GREATEST(game_bet_user, ?),
self_game_bet_user = GREATEST(self_game_bet_user, ?),
probability_game_bet_user = GREATEST(probability_game_bet_user, ?),
chat_sent = GREATEST(chat_sent, ?),
follow_user = GREATEST(follow_user, ?),
follow_room = GREATEST(follow_room, ?),
gift_panel_open = GREATEST(gift_panel_open, ?),
probability_game_panel_open = GREATEST(probability_game_panel_open, ?),
app_stay_ms = app_stay_ms + ?,
room_stay_ms = room_stay_ms + ?,
mic_stay_ms = mic_stay_ms + ?,
recharge_count = recharge_count + ?,
recharge_usd_minor = recharge_usd_minor + ?,
google_recharge_usd_minor = google_recharge_usd_minor + ?,
third_party_recharge_usd_minor = third_party_recharge_usd_minor + ?,
coin_seller_recharge_usd_minor = coin_seller_recharge_usd_minor + ?,
first_recharge_usd_minor = first_recharge_usd_minor + ?,
repeat_recharge_usd_minor = repeat_recharge_usd_minor + ?,
normal_gift_coin = normal_gift_coin + ?,
lucky_gift_coin = lucky_gift_coin + ?,
super_lucky_gift_coin = super_lucky_gift_coin + ?,
lucky_gift_payout_coin = lucky_gift_payout_coin + ?,
super_lucky_gift_payout_coin = super_lucky_gift_payout_coin + ?,
game_bet_coin = game_bet_coin + ?,
game_bet_count = game_bet_count + ?,
self_game_bet_coin = self_game_bet_coin + ?,
self_game_bet_count = self_game_bet_count + ?,
probability_game_bet_coin = probability_game_bet_coin + ?,
probability_game_bet_count = probability_game_bet_count + ?,
other_consume_coin = other_consume_coin + ?,
output_coin = output_coin + ?,
updated_at_ms = ?
WHERE app_code = ? AND stat_tz = ? AND stat_day = ? AND subject_key = ?
`, flagValue(delta.FirstOpen), flagValue(delta.Registered), flagValue(delta.Active), flagValue(delta.RoomJoinOK), flagValue(delta.RoomJoinNG),
flagValue(delta.MicUp), flagValue(delta.Gift), flagValue(delta.NormalGift), flagValue(delta.LuckyGift), flagValue(delta.SuperGift),
flagValue(delta.Recharge), flagValue(delta.FirstRecharge), flagValue(delta.RepeatRecharge), flagValue(delta.GameBet), flagValue(delta.SelfGameBet),
flagValue(delta.ProbabilityBet), flagValue(delta.Chat), flagValue(delta.FollowUser), flagValue(delta.FollowRoom), flagValue(delta.GiftPanel),
flagValue(delta.ProbabilityPanel), normalizeID(delta.AppStayMS), normalizeID(delta.RoomStayMS), normalizeID(delta.MicStayMS),
normalizeID(delta.RechargeCount), normalizeID(delta.RechargeUSDMinor), normalizeID(delta.GoogleRechargeUSDMinor), normalizeID(delta.ThirdPartyRechargeUSDMinor),
normalizeID(delta.CoinSellerRechargeUSDMinor), normalizeID(delta.FirstRechargeUSDMinor), normalizeID(delta.RepeatRechargeUSDMinor),
normalizeID(delta.NormalGiftCoin), normalizeID(delta.LuckyGiftCoin), normalizeID(delta.SuperGiftCoin), normalizeID(delta.LuckyGiftPayoutCoin),
normalizeID(delta.SuperGiftPayoutCoin), normalizeID(delta.GameBetCoin), normalizeID(delta.GameBetCount), normalizeID(delta.SelfGameBetCoin),
normalizeID(delta.SelfGameBetCount), normalizeID(delta.ProbabilityBetCoin), normalizeID(delta.ProbabilityBetCount), normalizeID(delta.OtherConsumeCoin),
normalizeID(delta.OutputCoin), delta.UpdatedAt, app, statTZ, day, subjectKey)
return err
}
func socialSubjectKey(userID int64, deviceID string) string {
if userID > 0 {
return "u:" + strconv.FormatInt(userID, 10)
}
deviceID = strings.TrimSpace(deviceID)
if deviceID == "" {
return ""
}
return "d:" + deviceID
}
func flagValue(value int64) int64 {
if value > 0 {
return 1
}
return 0
}
func normalizeSocialUserRole(value string) string {
switch strings.ToLower(strings.TrimSpace(value)) {
case "host", "anchor", "owner", "room_owner", "agency", "guild", "guild_leader":
return "host"
default:
return "user"
}
}
func socialGiftKind(event RoomGiftEvent) (normalFlag, luckyFlag, superFlag int64, normalCoin, luckyCoin, superCoin int64) {
coin := normalizeID(event.CoinSpent)
if coin == 0 {
coin = normalizeID(event.GiftValue)
}
switch realRoomRobotGiftKind(event) {
case "super_lucky":
return 0, 0, 1, 0, 0, coin
case "lucky":
return 0, 1, 0, 0, coin, 0
default:
return 1, 0, 0, coin, 0, 0
}
}
func socialDeltaForAppTrackingEvent(event AppTrackingEvent, scope statDayScope, nowMS int64) (socialUserDayDelta, bool) {
delta := socialUserDayDelta{
AppCode: event.AppCode,
StatTZ: scope.tz,
StatDay: scope.day,
UserID: event.UserID,
DeviceID: event.DeviceID,
CountryID: event.CountryID,
RegionID: event.RegionID,
UpdatedAt: nowMS,
}
switch strings.ToLower(strings.TrimSpace(event.EventName)) {
case "app_first_open":
delta.FirstOpen = 1
delta.Active = 1
case "home_view":
delta.Active = 1
case "app_session":
delta.Active = 1
delta.AppStayMS = event.DurationMS
case "room_join_success":
delta.Active = 1
delta.RoomJoinOK = 1
case "room_join_fail":
delta.Active = 1
delta.RoomJoinNG = 1
case "room_stay":
delta.Active = 1
delta.RoomJoinOK = 1
delta.RoomStayMS = event.DurationMS
case "mic_up":
if !event.Success && strings.TrimSpace(event.ErrorCode) != "" {
return socialUserDayDelta{}, false
}
delta.Active = 1
delta.MicUp = 1
case "send_message":
if !event.Success && strings.TrimSpace(event.ErrorCode) != "" {
return socialUserDayDelta{}, false
}
delta.Active = 1
delta.Chat = 1
case "follow_host", "add_friend":
if !event.Success && strings.TrimSpace(event.ErrorCode) != "" {
return socialUserDayDelta{}, false
}
delta.Active = 1
delta.FollowUser = 1
case "follow_room":
if !event.Success && strings.TrimSpace(event.ErrorCode) != "" {
return socialUserDayDelta{}, false
}
delta.Active = 1
delta.FollowRoom = 1
case "gift_panel_open":
delta.Active = 1
delta.GiftPanel = 1
case "probability_game_panel_open":
delta.Active = 1
delta.ProbabilityPanel = 1
default:
return socialUserDayDelta{}, false
}
return delta, true
}