hyapp-server/api/proto/luckygift/v1/luckygift.proto
zhx bf6d7d4f1e 幸运礼物实验硬约束:两组 v2 高水位加权三元组必须一致
滞回标志按 app+pool 单行存储且由每个用户钉住的组内配置推进;
两组三元组不同(尤其 control 关/treatment 开的灰度形态)会互踩:
control 逐抽清掉 treatment 的标志,阈值不同的组按对方状态加权付奖。
加权特性的灰度必须走常规发布,不能走实验。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:07:24 +08:00

665 lines
22 KiB
Protocol Buffer
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.

syntax = "proto3";
package hyapp.luckygift.v1;
option go_package = "hyapp.local/api/proto/luckygift/v1;luckygiftv1";
// RequestMeta 是 lucky-gift-service 的内部追踪元信息。
// request_id 只用于链路排查,不能被任何业务流程当成幂等键使用。
message RequestMeta {
string request_id = 1;
string caller = 2;
string gateway_node_id = 3;
int64 sent_at_ms = 4;
string app_code = 5;
}
// LuckyGiftMeta 是 HyApp 内部房间送礼扣费成功后的抽奖事实输入。
// 金额、目标用户和 command_id 必须来自 wallet-service 已落账的 receipt不能从客户端 payload 推导。
message LuckyGiftMeta {
RequestMeta meta = 1;
string command_id = 2;
int64 user_id = 3;
string device_id = 4;
string room_id = 5;
string anchor_id = 6;
string gift_id = 7;
int64 coin_spent = 8;
int64 paid_at_ms = 9;
string pool_id = 10;
int64 target_user_id = 11;
int32 gift_count = 12;
int64 visible_region_id = 13;
int64 country_id = 14;
// sender_* 是 room-service 在送礼入口已经确认的展示快照,只进入中奖事实和 IM不能参与抽奖或账务判断。
string sender_name = 15;
string sender_avatar = 16;
string sender_display_user_id = 17;
string sender_pretty_display_user_id = 18;
// 充值分层与充值后短时加权必须由调用方传入已确认的用户事实;幸运礼物服务不跨库回查用户或支付明细。
int64 recharge_7d_coins = 19;
int64 recharge_30d_coins = 20;
int64 last_recharged_at_ms = 21;
// gift_income_coins 是 wallet 已实际入账给收礼人的收益金币dynamic_v3 用它校验规则主播拆账,缺失或比例不一致均拒绝开奖。
int64 gift_income_coins = 22;
// user_registered_at_ms 是 user-service 在送礼入口返回的账号创建事实快照。
// dynamic_v3 只用它判断闭窗时账号是否已满 48 小时;缺失时普通开奖继续,但补偿大奖资格 fail-close。
int64 user_registered_at_ms = 23;
}
message LuckyGiftRuleTier {
string stage = 1;
string tier_id = 2;
int64 multiplier_ppm = 3;
int64 base_weight_ppm = 4;
bool high_water_only = 5;
bool enabled = 6;
}
message LuckyGiftRuleStage {
string stage = 1;
repeated LuckyGiftRuleTier tiers = 2;
// 两个充值门槛共同决定 dynamic_v3 分层novice 固定为 0/0后续阶段逐维单调递增。
int64 min_recharge_7d_coins = 3;
int64 min_recharge_30d_coins = 4;
}
message LuckyGiftRuleConfig {
string app_code = 1;
string pool_id = 2;
int64 rule_version = 3;
bool enabled = 4;
int64 target_rtp_ppm = 5;
int64 pool_rate_ppm = 6;
int64 settlement_window_wager = 7;
int64 control_band_ppm = 8;
int64 gift_price_reference = 9;
int64 novice_max_equivalent_draws = 10;
int64 normal_max_equivalent_draws = 11;
int64 effective_from_ms = 12;
int64 created_by_admin_id = 13;
int64 created_at_ms = 14;
repeated LuckyGiftRuleStage stages = 15;
// strategy_version 隔离历史 fixed_v2 与动态算法 dynamic_v3避免新增约束破坏已经发布的旧规则。
string strategy_version = 16;
int64 profit_rate_ppm = 17;
int64 anchor_rate_ppm = 18;
int64 initial_pool_coins = 19;
int64 loss_streak_guarantee = 20;
int64 low_watermark_coins = 21;
int64 low_water_nonzero_factor_ppm = 22;
int64 high_watermark_coins = 23;
int64 high_water_nonzero_factor_ppm = 24;
int64 recharge_boost_window_ms = 25;
int64 recharge_boost_factor_ppm = 26;
repeated int64 jackpot_multiplier_ppms = 27;
int64 jackpot_global_rtp_max_ppm = 28;
int64 jackpot_user_round_rtp_max_ppm = 29;
int64 jackpot_user_48h_rtp_max_ppm = 30;
// 用户在同一应用、奖池和 UTC 自然日每累计消费满该金币数,生成一次可跨日保留的消费大奖资格。
int64 jackpot_spend_threshold_coins = 31;
int64 max_jackpot_hits_per_user_day = 32;
int64 max_single_payout = 33;
int64 user_hourly_payout_cap = 34;
int64 user_daily_payout_cap = 35;
int64 device_daily_payout_cap = 36;
int64 room_hourly_payout_cap = 37;
int64 anchor_daily_payout_cap = 38;
// 用户严格滚动 24 小时 RTP 超过此值时普通非零奖档按下方因子降权0 表示关闭该策略。
int64 user_24h_rtp_threshold_ppm = 39;
// 触发用户 24 小时 RTP 降权后保留的普通中奖权重1000000 表示保持原概率。
int64 user_24h_ordinary_win_factor_ppm = 40;
// fixed_v2 高水位返奖加权奖池水位越过阈值后中奖返奖按系数放大RTP 等比放大),
// 滞回保持到水位跌破恢复线才退出。0 表示关闭dynamic_v3 必须保持三个字段为 0。
int64 v2_high_water_boost_threshold_coins = 41;
// 加权系数 ppm1500000 表示 RTP 变为原值的 150%。启用时必须大于 1000000。
int64 v2_high_water_boost_factor_ppm = 42;
// 恢复水位金币;水位低于该值后退出加权。启用时必须为正且不高于阈值。
int64 v2_high_water_boost_recover_coins = 43;
}
message CheckLuckyGiftRequest {
RequestMeta meta = 1;
int64 user_id = 2;
string room_id = 3;
string gift_id = 4;
string pool_id = 5;
}
message CheckLuckyGiftResponse {
bool enabled = 1;
string reason = 2;
string gift_id = 3;
int64 gift_price = 4;
int64 rule_version = 5;
int64 target_rtp_ppm = 6;
string experience_pool = 7;
string pool_id = 8;
// strategy_version 由 lucky-gift owner 返回并由 room saga 固化,用于滚动发布时区分 fixed_v2 兼容与 dynamic_v3 强校验。
string strategy_version = 9;
}
message LuckyGiftDrawResult {
string draw_id = 1;
string command_id = 2;
string gift_id = 3;
int64 rule_version = 4;
string experience_pool = 5;
string selected_tier_id = 6;
int64 base_reward_coins = 7;
int64 effective_reward_coins = 8;
string reward_status = 9;
int64 rtp_window_index = 10;
int64 gift_rtp_window_index = 11;
int64 global_base_rtp_ppm = 12;
int64 gift_base_rtp_ppm = 13;
bool stage_feedback = 14;
bool high_multiplier = 15;
int64 created_at_ms = 16;
string pool_id = 17;
int64 multiplier_ppm = 18;
string wallet_transaction_id = 19;
int64 coin_balance_after = 20;
int64 user_id = 21;
string external_user_id = 22;
string app_code = 23;
repeated LuckyGiftHit hits = 24;
}
message ExecuteLuckyGiftDrawRequest {
LuckyGiftMeta lucky_gift = 1;
}
message ExecuteLuckyGiftDrawResponse {
LuckyGiftDrawResult result = 1;
}
message BatchExecuteLuckyGiftDrawRequest {
repeated LuckyGiftMeta lucky_gifts = 1;
}
message BatchExecuteLuckyGiftDrawResponse {
repeated LuckyGiftDrawResult results = 1;
}
// ExternalGiftDrawRequest 是外部 App 接入的抽奖输入。
// App 调用前必须已经完成自己的扣费lucky-gift-service 只校验金额事实并返回中奖结果。
message ExternalGiftDrawRequest {
RequestMeta meta = 1;
string app_code = 3;
string external_user_id = 4;
string request_id = 5;
int64 gift_count = 6;
int64 unit_amount = 7;
int64 total_amount = 8;
string currency = 9;
int64 paid_at_ms = 10;
string metadata_json = 11;
string pool_id = 12;
// dynamic_v3 必填的稳定设备作用域。luck-gateway 不独立证明其真实性,调用方必须在自己的认证/签名边界内绑定该值fixed_v2 允许缺失。
string device_id = 13;
// user_registered_at_ms 由外部 App 按自己的用户主数据提供dynamic_v3 缺失或晚于支付时间时补偿大奖资格 fail-close。
int64 user_registered_at_ms = 14;
}
message ExternalGiftDrawResponse {
string draw_id = 1;
string request_id = 2;
string app_code = 4;
string external_user_id = 5;
int64 gift_count = 6;
int64 unit_amount = 7;
int64 total_amount = 8;
int64 reward_amount = 9;
int64 multiplier_ppm = 10;
string reward_status = 11;
int64 rule_version = 12;
int64 created_at_ms = 13;
}
message GetLuckyGiftConfigRequest {
RequestMeta meta = 1;
string pool_id = 2;
}
message GetLuckyGiftConfigResponse {
LuckyGiftRuleConfig config = 1;
}
message UpsertLuckyGiftConfigRequest {
RequestMeta meta = 1;
LuckyGiftRuleConfig config = 2;
int64 operator_admin_id = 3;
}
message UpsertLuckyGiftConfigResponse {
LuckyGiftRuleConfig config = 1;
}
message ListLuckyGiftConfigsRequest {
RequestMeta meta = 1;
}
message ListLuckyGiftConfigsResponse {
repeated LuckyGiftRuleConfig configs = 1;
}
message ListLuckyGiftDrawsRequest {
RequestMeta meta = 1;
string gift_id = 2;
int64 user_id = 3;
string room_id = 4;
string status = 5;
int32 page = 6;
int32 page_size = 7;
string pool_id = 8;
string external_user_id = 10;
bool external_only = 11;
}
message ListLuckyGiftDrawsResponse {
repeated LuckyGiftDrawResult draws = 1;
int64 total = 2;
}
message LuckyGiftDrawSummary {
string pool_id = 1;
int64 total_draws = 2;
int64 unique_users = 3;
int64 unique_rooms = 4;
int64 total_spent_coins = 5;
int64 total_reward_coins = 6;
int64 base_reward_coins = 7;
int64 actual_rtp_ppm = 8;
int64 pending_draws = 9;
int64 granted_draws = 10;
int64 failed_draws = 11;
}
message LuckyGiftPoolBalance {
string app_code = 1;
string pool_id = 2;
int64 balance = 3;
int64 reserve_floor = 4;
int64 available_balance = 5;
int64 total_in = 6;
int64 total_out = 7;
bool materialized = 8;
int64 updated_at_ms = 9;
string strategy_version = 10;
int64 manual_credit_total = 11;
int64 manual_debit_total = 12;
}
message GetLuckyGiftDrawSummaryRequest {
RequestMeta meta = 1;
string gift_id = 2;
int64 user_id = 3;
string room_id = 4;
string status = 5;
string pool_id = 6;
string external_user_id = 8;
bool external_only = 9;
}
message GetLuckyGiftDrawSummaryResponse {
LuckyGiftDrawSummary summary = 1;
}
message ListLuckyGiftPoolBalancesRequest {
RequestMeta meta = 1;
string pool_id = 2;
string strategy_version = 3;
}
message ListLuckyGiftPoolBalancesResponse {
repeated LuckyGiftPoolBalance pools = 1;
}
// LuckyGiftExperimentOverride 把单个用户强制固定到某个实验组,用于放量前的白名单验证。
// user_key内部用户为十进制 user_id 字符串;外部用户为 "ext:" + external_user_id。
message LuckyGiftExperimentOverride {
string user_key = 1;
string arm = 2;
}
// LuckyGiftExperimentArmStats 是实验开始以来单组的抽奖聚合,由 lucky_draw_records 按 rule_version 现算,非物化口径。
message LuckyGiftExperimentArmStats {
string arm = 1;
int64 rule_version = 2;
string strategy_version = 3;
int64 total_draws = 4;
int64 unique_users = 5;
int64 total_spent_coins = 6;
int64 total_reward_coins = 7;
int64 actual_rtp_ppm = 8;
}
// LuckyGiftExperiment 是单奖池 AB 实验事实:两组各钉住一个不可变 rule_version
// 运行时按 bucket_salt+user 哈希分流;同一奖池同时最多一个未完成实验。
message LuckyGiftExperiment {
string app_code = 1;
string pool_id = 2;
string experiment_id = 3;
string name = 4;
int64 control_rule_version = 5;
int64 treatment_rule_version = 6;
// treatment 组流量比例ppm0..10000000 表示只有白名单 override 能进入 treatment。
int64 treatment_traffic_ppm = 7;
string bucket_salt = 8;
// running / paused / completedpaused 时全部流量(含白名单)回落 control。
string status = 9;
string winner_arm = 10;
int64 winner_rule_version = 11;
// 结束实验时把获胜配置重发布成的新最新版本,全量流量由它承接。
int64 final_rule_version = 12;
int64 created_by_admin_id = 13;
int64 started_at_ms = 14;
int64 ended_at_ms = 15;
int64 updated_at_ms = 16;
repeated LuckyGiftExperimentOverride overrides = 17;
repeated LuckyGiftExperimentArmStats arm_stats = 18;
}
// CreateLuckyGiftExperimentRequest 在一个事务里发布 treatment 新版本并登记实验,
// 避免"先发布后建实验"窗口期内新版本被最新版本语义全量放开。
// control 组自动钉住当前最新已启用版本。
message CreateLuckyGiftExperimentRequest {
RequestMeta meta = 1;
string name = 2;
LuckyGiftRuleConfig treatment_config = 3;
int64 treatment_traffic_ppm = 4;
int64 operator_admin_id = 5;
repeated LuckyGiftExperimentOverride overrides = 6;
}
message CreateLuckyGiftExperimentResponse {
LuckyGiftExperiment experiment = 1;
LuckyGiftRuleConfig treatment_config = 2;
}
message ListLuckyGiftExperimentsRequest {
RequestMeta meta = 1;
string pool_id = 2;
string status = 3;
bool with_stats = 4;
}
message ListLuckyGiftExperimentsResponse {
repeated LuckyGiftExperiment experiments = 1;
}
// UpdateLuckyGiftExperimentRequest 调整放量或暂停/恢复。
// treatment_traffic_ppm 使用显式存在性语义:未设置表示不修改流量,避免 proto3 缺省 0 被误解释成"放量清零"
// status 为空表示不修改状态。
message UpdateLuckyGiftExperimentRequest {
RequestMeta meta = 1;
string experiment_id = 2;
optional int64 treatment_traffic_ppm = 3;
string status = 4;
int64 operator_admin_id = 5;
}
message UpdateLuckyGiftExperimentResponse {
LuckyGiftExperiment experiment = 1;
}
message SetLuckyGiftExperimentOverridesRequest {
RequestMeta meta = 1;
string experiment_id = 2;
repeated LuckyGiftExperimentOverride upserts = 3;
repeated string removes = 4;
int64 operator_admin_id = 5;
}
message SetLuckyGiftExperimentOverridesResponse {
LuckyGiftExperiment experiment = 1;
}
// EndLuckyGiftExperimentRequest 结束实验:把 winner_arm 钉住的配置快照重发布为新的最新版本,
// 同事务内完成实验收口,保证结束瞬间的版本收敛没有中间态。
message EndLuckyGiftExperimentRequest {
RequestMeta meta = 1;
string experiment_id = 2;
string winner_arm = 3;
int64 operator_admin_id = 4;
}
message EndLuckyGiftExperimentResponse {
LuckyGiftExperiment experiment = 1;
LuckyGiftRuleConfig final_config = 2;
}
service LuckyGiftService {
rpc CheckLuckyGift(CheckLuckyGiftRequest) returns (CheckLuckyGiftResponse);
rpc ExecuteLuckyGiftDraw(ExecuteLuckyGiftDrawRequest) returns (ExecuteLuckyGiftDrawResponse);
rpc BatchExecuteLuckyGiftDraw(BatchExecuteLuckyGiftDrawRequest) returns (BatchExecuteLuckyGiftDrawResponse);
rpc ExecuteExternalGiftDraw(ExternalGiftDrawRequest) returns (ExternalGiftDrawResponse);
}
service AdminLuckyGiftService {
rpc GetLuckyGiftConfig(GetLuckyGiftConfigRequest) returns (GetLuckyGiftConfigResponse);
rpc UpsertLuckyGiftConfig(UpsertLuckyGiftConfigRequest) returns (UpsertLuckyGiftConfigResponse);
rpc ListLuckyGiftConfigs(ListLuckyGiftConfigsRequest) returns (ListLuckyGiftConfigsResponse);
rpc ListLuckyGiftDraws(ListLuckyGiftDrawsRequest) returns (ListLuckyGiftDrawsResponse);
rpc GetLuckyGiftDrawSummary(GetLuckyGiftDrawSummaryRequest) returns (GetLuckyGiftDrawSummaryResponse);
rpc ListLuckyGiftPoolBalances(ListLuckyGiftPoolBalancesRequest) returns (ListLuckyGiftPoolBalancesResponse);
rpc AdjustLuckyGiftPoolBalance(AdjustLuckyGiftPoolBalanceRequest) returns (AdjustLuckyGiftPoolBalanceResponse);
rpc CreateLuckyGiftExperiment(CreateLuckyGiftExperimentRequest) returns (CreateLuckyGiftExperimentResponse);
rpc ListLuckyGiftExperiments(ListLuckyGiftExperimentsRequest) returns (ListLuckyGiftExperimentsResponse);
rpc UpdateLuckyGiftExperiment(UpdateLuckyGiftExperimentRequest) returns (UpdateLuckyGiftExperimentResponse);
rpc SetLuckyGiftExperimentOverrides(SetLuckyGiftExperimentOverridesRequest) returns (SetLuckyGiftExperimentOverridesResponse);
rpc EndLuckyGiftExperiment(EndLuckyGiftExperimentRequest) returns (EndLuckyGiftExperimentResponse);
rpc ListLuckyGiftUserProfiles(ListLuckyGiftUserProfilesRequest) returns (ListLuckyGiftUserProfilesResponse);
rpc GetLuckyGiftUserProfile(GetLuckyGiftUserProfileRequest) returns (GetLuckyGiftUserProfileResponse);
rpc ListLuckyGiftDailyStats(ListLuckyGiftDailyStatsRequest) returns (ListLuckyGiftDailyStatsResponse);
}
// LuckyGiftHit 只返回批量逐份开奖中实际中奖的精简位置,不复制全部审计明细。
// 声明追加在文件尾以保持既有 protobuf message index 稳定。
message LuckyGiftHit {
int32 gift_index = 1;
string draw_id = 2;
string selected_tier_id = 3;
int64 multiplier_ppm = 4;
int64 reward_coins = 5;
}
// 调账消息也只追加在文件尾,保持所有既有 protobuf message index 稳定。
// adjustment_id 是业务幂等键RequestMeta.request_id 只做链路追踪,不能替代资金幂等语义。
message AdjustLuckyGiftPoolBalanceRequest {
RequestMeta meta = 1;
string pool_id = 2;
string strategy_version = 3;
string adjustment_id = 4;
string direction = 5;
int64 amount_coins = 6;
string reason = 7;
int64 operator_admin_id = 8;
}
message LuckyGiftPoolAdjustment {
string adjustment_id = 1;
string app_code = 2;
string pool_id = 3;
string strategy_version = 4;
string direction = 5;
int64 amount_coins = 6;
string reason = 7;
int64 operator_admin_id = 8;
int64 balance_before = 9;
int64 balance_after = 10;
int64 created_at_ms = 11;
}
message AdjustLuckyGiftPoolBalanceResponse {
LuckyGiftPoolAdjustment adjustment = 1;
LuckyGiftPoolBalance pool = 2;
bool idempotent_replay = 3;
}
// 用户画像协议追加在文件尾,避免改变既有 message index。外部策略哈希只存在于 owner 数据库,
// 本协议永远只下发 external_user_id防止运营误把不可逆内部键当成外部用户身份。
message LuckyGiftUserProfileWindow {
int64 draws = 1;
int64 wager_coins = 2;
int64 payout_coins = 3;
int64 net_coins = 4;
int64 rtp_ppm = 5;
bool has_rtp = 6;
}
message LuckyGiftUserProfile {
string app_code = 1;
string pool_id = 2;
string identity_type = 3;
int64 internal_user_id = 4;
string external_user_id = 5;
int64 rule_version = 6;
string strategy_version = 7;
string stage = 8;
int64 paid_draws = 9;
int64 equivalent_draws = 10;
int64 loss_streak = 11;
int64 pending_spend_jackpot_tokens = 12;
int64 guarantee_draws_remaining = 13;
bool downweight_active = 14;
int64 downweight_factor_ppm = 15;
LuckyGiftUserProfileWindow one_hour = 16;
LuckyGiftUserProfileWindow twenty_four_hours = 17;
LuckyGiftUserProfileWindow forty_eight_hours = 18;
LuckyGiftUserProfileWindow lifetime = 19;
int64 base_reward_coins = 20;
int64 ordinary_win_count = 21;
int64 high_multiplier_ordinary_win_count = 22;
int64 rtp_compensation_jackpot_count = 23;
int64 cumulative_spend_jackpot_count = 24;
int64 guarantee_hit_count = 25;
int64 zero_draw_count = 26;
int64 granted_draw_count = 27;
int64 pending_draw_count = 28;
int64 failed_draw_count = 29;
int64 max_multiplier_ppm = 30;
int64 max_reward_coins = 31;
int64 first_draw_at_ms = 32;
int64 last_draw_at_ms = 33;
int64 aggregated_at_ms = 34;
int64 data_lag_ms = 35;
int64 user_24h_rtp_threshold_ppm = 36;
}
message ListLuckyGiftUserProfilesRequest {
RequestMeta meta = 1;
string pool_id = 2;
string identity_type = 3;
int64 internal_user_id = 4;
string external_user_id = 5;
string stage = 6;
string status = 7;
string sort_by = 8;
string sort_direction = 9;
int32 page = 10;
int32 page_size = 11;
// cursor 是 owner service 生成的不透明 keyset 游标page 仅保留旧客户端协议兼容,服务端不再执行 OFFSET。
string cursor = 12;
}
message ListLuckyGiftUserProfilesResponse {
repeated LuckyGiftUserProfile profiles = 1;
int64 total = 2;
int64 snapshot_at_ms = 3;
string next_cursor = 4;
bool has_more = 5;
}
message GetLuckyGiftUserProfileRequest {
RequestMeta meta = 1;
string pool_id = 2;
string identity_type = 3;
int64 internal_user_id = 4;
string external_user_id = 5;
int32 jackpot_page = 6;
int32 jackpot_page_size = 7;
// jackpot_cursor 由上一页 next_jackpot_cursor 原样返回jackpot_page 仅保留协议兼容。
string jackpot_cursor = 8;
}
message LuckyGiftUserMultiplierStat {
string hit_type = 1;
int64 multiplier_ppm = 2;
int64 draw_count = 3;
int64 wager_coins = 4;
int64 payout_coins = 5;
}
message LuckyGiftUserJackpotCondition {
string name = 1;
int64 numerator = 2;
int64 denominator = 3;
int64 ratio_ppm = 4;
int64 limit_ppm = 5;
int64 factor_ppm = 6;
bool passed = 7;
string reason = 8;
}
message LuckyGiftUserJackpotHit {
string draw_id = 1;
string request_id = 2;
int64 rule_version = 3;
string stage = 4;
string mechanism = 5;
string reason_code = 6;
string reason_summary = 7;
string tier_id = 8;
int64 multiplier_ppm = 9;
int64 wager_coins = 10;
int64 payout_coins = 11;
int64 occurred_at_ms = 12;
repeated LuckyGiftUserJackpotCondition conditions = 13;
// event_key 只标识这条画像大奖事实,供前端稳定追加去重;不包含用户策略键或用户哈希。
string event_key = 14;
}
message GetLuckyGiftUserProfileResponse {
LuckyGiftUserProfile profile = 1;
repeated LuckyGiftUserMultiplierStat multiplier_distribution = 2;
repeated LuckyGiftUserJackpotHit jackpot_hits = 3;
int64 jackpot_hit_total = 4;
int64 snapshot_at_ms = 5;
int32 jackpot_page = 6;
int32 jackpot_page_size = 7;
string next_jackpot_cursor = 8;
bool jackpot_has_more = 9;
}
// ListLuckyGiftDailyStats 面向 Databi 的按日聚合:只读 lucky_draw_pool_day_stats 读模型
// (北京日 +08:00 分桶,由后台游标增量刷新),不扫抽奖事实表,避免 BI 访问打热事实库。
// strategy_version 过滤用于按策略族拆分展示如幸运礼物V3 tab 只看 dynamic_v3
message ListLuckyGiftDailyStatsRequest {
RequestMeta meta = 1;
// pool_id 为空表示全部奖池汇总。
string pool_id = 2;
// strategy_version 为空表示不按策略过滤;取值 fixed_v2 / dynamic_v3。
string strategy_version = 3;
// [start_ms, end_ms) UTC epoch ms服务端按北京日换算成 stat_day 闭区间。
int64 start_ms = 4;
int64 end_ms = 5;
// region_ids 为空表示全部区域;按房间可见区域 ID 过滤0=GLOBAL 房间只在不过滤时计入)。
repeated int64 region_ids = 6;
}
message LuckyGiftDailyStat {
// stat_day 是北京日(+08:00YYYY-MM-DD。
string stat_day = 1;
int64 draw_count = 2;
int64 turnover_coin = 3;
// payout_coin 为用户可见返奖effective与既有 DrawSummary 口径一致,含未发放/失败行。
int64 payout_coin = 4;
int64 base_reward_coin = 5;
}
message ListLuckyGiftDailyStatsResponse {
repeated LuckyGiftDailyStat stats = 1;
}