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; // 充值分层与充值后短时加权必须由调用方传入已确认的用户事实;幸运礼物服务不跨库回查用户或支付明细。 int64 recharge_7d_coins = 15; int64 recharge_30d_coins = 16; int64 last_recharged_at_ms = 17; // gift_income_coins 是 wallet 已实际入账给收礼人的收益金币;dynamic_v3 用它校验规则主播拆账,缺失或比例不一致均拒绝开奖。 int64 gift_income_coins = 18; } 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_day_rtp_max_ppm = 29; int64 jackpot_user_72h_rtp_max_ppm = 30; 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; } 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; } 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; } 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; } message ListLuckyGiftPoolBalancesResponse { repeated LuckyGiftPoolBalance pools = 1; } 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); } // 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; }