267 lines
10 KiB
Go
267 lines
10 KiB
Go
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
|
||
}
|