package grpc import ( "context" activityv1 "hyapp.local/api/proto/activity/v1" "hyapp/pkg/appcode" "hyapp/pkg/xerr" domain "hyapp/services/activity-service/internal/domain/luckygift" service "hyapp/services/activity-service/internal/service/luckygift" ) // LuckyGiftServer 暴露房间送礼主链路调用的幸运礼物查询和抽奖入口。 type LuckyGiftServer struct { activityv1.UnimplementedLuckyGiftServiceServer svc *service.Service } func NewLuckyGiftServer(svc *service.Service) *LuckyGiftServer { return &LuckyGiftServer{svc: svc} } func (s *LuckyGiftServer) CheckLuckyGift(ctx context.Context, req *activityv1.CheckLuckyGiftRequest) (*activityv1.CheckLuckyGiftResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) result, err := s.svc.Check(ctx, domain.CheckCommand{ PoolID: req.GetPoolId(), GiftID: req.GetGiftId(), UserID: req.GetUserId(), RoomID: req.GetRoomId(), }) if err != nil { return nil, xerr.ToGRPCError(err) } return &activityv1.CheckLuckyGiftResponse{ Enabled: result.Enabled, Reason: result.Reason, PoolId: result.PoolID, GiftId: result.GiftID, GiftPrice: result.GiftPrice, RuleVersion: result.RuleVersion, TargetRtpPpm: result.TargetRTPPPM, ExperiencePool: result.ExperiencePool, }, nil } func (s *LuckyGiftServer) ExecuteLuckyGiftDraw(ctx context.Context, req *activityv1.ExecuteLuckyGiftDrawRequest) (*activityv1.ExecuteLuckyGiftDrawResponse, error) { meta := req.GetLuckyGift() ctx = appcode.WithContext(ctx, meta.GetMeta().GetAppCode()) result, err := s.svc.Draw(ctx, luckyDrawCommandFromProto(meta)) if err != nil { return nil, xerr.ToGRPCError(err) } return &activityv1.ExecuteLuckyGiftDrawResponse{Result: luckyDrawResultToProto(result)}, nil } func (s *LuckyGiftServer) BatchExecuteLuckyGiftDraw(ctx context.Context, req *activityv1.BatchExecuteLuckyGiftDrawRequest) (*activityv1.BatchExecuteLuckyGiftDrawResponse, error) { if len(req.GetLuckyGifts()) == 0 { return nil, xerr.ToGRPCError(xerr.New(xerr.InvalidArgument, "lucky gift batch draw commands are required")) } ctx = appcode.WithContext(ctx, req.GetLuckyGifts()[0].GetMeta().GetAppCode()) cmds := make([]domain.DrawCommand, 0, len(req.GetLuckyGifts())) for _, meta := range req.GetLuckyGifts() { cmds = append(cmds, luckyDrawCommandFromProto(meta)) } results, err := s.svc.DrawBatch(ctx, cmds) if err != nil { return nil, xerr.ToGRPCError(err) } resp := &activityv1.BatchExecuteLuckyGiftDrawResponse{Results: make([]*activityv1.LuckyGiftDrawResult, 0, len(results))} for _, result := range results { resp.Results = append(resp.Results, luckyDrawResultToProto(result)) } return resp, nil } func luckyDrawCommandFromProto(meta *activityv1.LuckyGiftMeta) domain.DrawCommand { return domain.DrawCommand{ CommandID: meta.GetCommandId(), PoolID: meta.GetPoolId(), UserID: meta.GetUserId(), TargetUserID: meta.GetTargetUserId(), DeviceID: meta.GetDeviceId(), RoomID: meta.GetRoomId(), AnchorID: meta.GetAnchorId(), GiftID: meta.GetGiftId(), GiftCount: meta.GetGiftCount(), CoinSpent: meta.GetCoinSpent(), PaidAtMS: meta.GetPaidAtMs(), VisibleRegionID: meta.GetVisibleRegionId(), CountryID: meta.GetCountryId(), } } // AdminLuckyGiftServer 暴露后台幸运礼物规则配置和抽奖审计入口。 type AdminLuckyGiftServer struct { activityv1.UnimplementedAdminLuckyGiftServiceServer svc *service.Service } func NewAdminLuckyGiftServer(svc *service.Service) *AdminLuckyGiftServer { return &AdminLuckyGiftServer{svc: svc} } func (s *AdminLuckyGiftServer) GetLuckyGiftConfig(ctx context.Context, req *activityv1.GetLuckyGiftConfigRequest) (*activityv1.GetLuckyGiftConfigResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) config, err := s.svc.GetConfig(ctx, req.GetPoolId()) if err != nil { return nil, xerr.ToGRPCError(err) } return &activityv1.GetLuckyGiftConfigResponse{Config: luckyRuleConfigToProto(config)}, nil } func (s *AdminLuckyGiftServer) UpsertLuckyGiftConfig(ctx context.Context, req *activityv1.UpsertLuckyGiftConfigRequest) (*activityv1.UpsertLuckyGiftConfigResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) config := luckyRuleConfigFromProto(req.GetConfig()) config.CreatedByAdminID = req.GetOperatorAdminId() updated, err := s.svc.UpsertConfig(ctx, config) if err != nil { return nil, xerr.ToGRPCError(err) } return &activityv1.UpsertLuckyGiftConfigResponse{Config: luckyRuleConfigToProto(updated)}, nil } func (s *AdminLuckyGiftServer) ListLuckyGiftConfigs(ctx context.Context, req *activityv1.ListLuckyGiftConfigsRequest) (*activityv1.ListLuckyGiftConfigsResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) configs, err := s.svc.ListConfigs(ctx) if err != nil { return nil, xerr.ToGRPCError(err) } resp := &activityv1.ListLuckyGiftConfigsResponse{Configs: make([]*activityv1.LuckyGiftRuleConfig, 0, len(configs))} for _, config := range configs { resp.Configs = append(resp.Configs, luckyRuleConfigToProto(config)) } return resp, nil } func (s *AdminLuckyGiftServer) ListLuckyGiftDraws(ctx context.Context, req *activityv1.ListLuckyGiftDrawsRequest) (*activityv1.ListLuckyGiftDrawsResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) draws, total, err := s.svc.ListDraws(ctx, domain.DrawQuery{ GiftID: req.GetGiftId(), PoolID: req.GetPoolId(), UserID: req.GetUserId(), RoomID: req.GetRoomId(), Status: req.GetStatus(), Page: req.GetPage(), PageSize: req.GetPageSize(), }) if err != nil { return nil, xerr.ToGRPCError(err) } resp := &activityv1.ListLuckyGiftDrawsResponse{Draws: make([]*activityv1.LuckyGiftDrawResult, 0, len(draws)), Total: total} for _, draw := range draws { resp.Draws = append(resp.Draws, luckyDrawResultToProto(draw)) } return resp, nil } func (s *AdminLuckyGiftServer) GetLuckyGiftDrawSummary(ctx context.Context, req *activityv1.GetLuckyGiftDrawSummaryRequest) (*activityv1.GetLuckyGiftDrawSummaryResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) summary, err := s.svc.GetDrawSummary(ctx, domain.DrawQuery{ GiftID: req.GetGiftId(), PoolID: req.GetPoolId(), UserID: req.GetUserId(), RoomID: req.GetRoomId(), Status: req.GetStatus(), }) if err != nil { return nil, xerr.ToGRPCError(err) } return &activityv1.GetLuckyGiftDrawSummaryResponse{Summary: luckyDrawSummaryToProto(summary)}, nil } func luckyRuleConfigFromProto(config *activityv1.LuckyGiftRuleConfig) domain.RuleConfig { if config == nil { return domain.RuleConfig{} } stages := make([]domain.RuleStage, 0, len(config.GetStages())) for _, stage := range config.GetStages() { tiers := make([]domain.RuleTier, 0, len(stage.GetTiers())) for _, tier := range stage.GetTiers() { tiers = append(tiers, domain.RuleTier{ Stage: tier.GetStage(), TierID: tier.GetTierId(), MultiplierPPM: tier.GetMultiplierPpm(), BaseWeightPPM: tier.GetBaseWeightPpm(), RewardSource: tier.GetRewardSource(), HighWaterOnly: tier.GetHighWaterOnly(), BroadcastLevel: tier.GetBroadcastLevel(), Enabled: tier.GetEnabled(), }) } stages = append(stages, domain.RuleStage{ Stage: stage.GetStage(), Tiers: tiers, }) } return domain.RuleConfig{ AppCode: config.GetAppCode(), PoolID: config.GetPoolId(), RuleVersion: config.GetRuleVersion(), Enabled: config.GetEnabled(), TargetRTPPPM: config.GetTargetRtpPpm(), PoolRatePPM: config.GetPoolRatePpm(), SettlementWindowWager: config.GetSettlementWindowWager(), ControlBandPPM: config.GetControlBandPpm(), GiftPriceReference: config.GetGiftPriceReference(), NoviceMaxEquivalentDraws: config.GetNoviceMaxEquivalentDraws(), NormalMaxEquivalentDraws: config.GetNormalMaxEquivalentDraws(), EffectiveFromMS: config.GetEffectiveFromMs(), CreatedByAdminID: config.GetCreatedByAdminId(), CreatedAtMS: config.GetCreatedAtMs(), MaxSinglePayout: config.GetMaxSinglePayout(), UserHourlyPayoutCap: config.GetUserHourlyPayoutCap(), UserDailyPayoutCap: config.GetUserDailyPayoutCap(), DeviceDailyPayoutCap: config.GetDeviceDailyPayoutCap(), RoomHourlyPayoutCap: config.GetRoomHourlyPayoutCap(), AnchorDailyPayoutCap: config.GetAnchorDailyPayoutCap(), Stages: stages, } } func luckyRuleConfigToProto(config domain.RuleConfig) *activityv1.LuckyGiftRuleConfig { stages := make([]*activityv1.LuckyGiftRuleStage, 0, len(config.Stages)) for _, stage := range config.Stages { tiers := make([]*activityv1.LuckyGiftRuleTier, 0, len(stage.Tiers)) for _, tier := range stage.Tiers { tiers = append(tiers, &activityv1.LuckyGiftRuleTier{ Stage: tier.Stage, TierId: tier.TierID, MultiplierPpm: tier.MultiplierPPM, BaseWeightPpm: tier.BaseWeightPPM, RewardSource: tier.RewardSource, HighWaterOnly: tier.HighWaterOnly, BroadcastLevel: tier.BroadcastLevel, Enabled: tier.Enabled, }) } stages = append(stages, &activityv1.LuckyGiftRuleStage{ Stage: stage.Stage, Tiers: tiers, }) } return &activityv1.LuckyGiftRuleConfig{ AppCode: config.AppCode, PoolId: config.PoolID, RuleVersion: config.RuleVersion, Enabled: config.Enabled, TargetRtpPpm: config.TargetRTPPPM, PoolRatePpm: config.PoolRatePPM, SettlementWindowWager: config.SettlementWindowWager, ControlBandPpm: config.ControlBandPPM, GiftPriceReference: config.GiftPriceReference, NoviceMaxEquivalentDraws: config.NoviceMaxEquivalentDraws, NormalMaxEquivalentDraws: config.NormalMaxEquivalentDraws, EffectiveFromMs: config.EffectiveFromMS, CreatedByAdminId: config.CreatedByAdminID, CreatedAtMs: config.CreatedAtMS, MaxSinglePayout: config.MaxSinglePayout, UserHourlyPayoutCap: config.UserHourlyPayoutCap, UserDailyPayoutCap: config.UserDailyPayoutCap, DeviceDailyPayoutCap: config.DeviceDailyPayoutCap, RoomHourlyPayoutCap: config.RoomHourlyPayoutCap, AnchorDailyPayoutCap: config.AnchorDailyPayoutCap, Stages: stages, } } func luckyDrawResultToProto(result domain.DrawResult) *activityv1.LuckyGiftDrawResult { return &activityv1.LuckyGiftDrawResult{ DrawId: result.DrawID, CommandId: result.CommandID, PoolId: result.PoolID, GiftId: result.GiftID, RuleVersion: result.RuleVersion, ExperiencePool: result.ExperiencePool, SelectedTierId: result.SelectedTierID, MultiplierPpm: result.MultiplierPPM, BaseRewardCoins: result.BaseRewardCoins, RoomAtmosphereRewardCoins: result.RoomAtmosphereRewardCoins, ActivitySubsidyCoins: result.ActivitySubsidyCoins, EffectiveRewardCoins: result.EffectiveRewardCoins, BudgetSourcesJson: result.BudgetSourcesJSON, RewardStatus: result.RewardStatus, RtpWindowIndex: result.RTPWindowIndex, GiftRtpWindowIndex: result.GiftRTPWindowIndex, GlobalBaseRtpPpm: result.GlobalBaseRTPPPM, GiftBaseRtpPpm: result.GiftBaseRTPPPM, StageFeedback: result.StageFeedback, HighMultiplier: result.HighMultiplier, CreatedAtMs: result.CreatedAtMS, WalletTransactionId: result.WalletTransactionID, CoinBalanceAfter: result.CoinBalanceAfter, } } func luckyDrawSummaryToProto(summary domain.DrawSummary) *activityv1.LuckyGiftDrawSummary { return &activityv1.LuckyGiftDrawSummary{ PoolId: summary.PoolID, TotalDraws: summary.TotalDraws, UniqueUsers: summary.UniqueUsers, UniqueRooms: summary.UniqueRooms, TotalSpentCoins: summary.TotalSpentCoins, TotalRewardCoins: summary.TotalRewardCoins, BaseRewardCoins: summary.BaseRewardCoins, RoomAtmosphereRewardCoins: summary.RoomAtmosphereRewardCoins, ActivitySubsidyCoins: summary.ActivitySubsidyCoins, ActualRtpPpm: summary.ActualRTPPPM, PendingDraws: summary.PendingDraws, GrantedDraws: summary.GrantedDraws, FailedDraws: summary.FailedDraws, } }