diff --git a/.gitignore b/.gitignore index 35b9757e..65402c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,7 @@ node_modules/ mysql-data/ redis-data/ -.waylog/ \ No newline at end of file +.waylog/ + +.tmp/ +/tmp/ diff --git a/api/proto/activity/v1/activity.pb.go b/api/proto/activity/v1/activity.pb.go index e4162e8c..cc704a8e 100644 --- a/api/proto/activity/v1/activity.pb.go +++ b/api/proto/activity/v1/activity.pb.go @@ -4524,14 +4524,16 @@ func (x *UpdateRegistrationRewardConfigResponse) GetConfig() *RegistrationReward // ListRegistrationRewardClaimsRequest 是后台领取记录分页筛选;keyword 由 admin-server 结合用户库扩展。 type ListRegistrationRewardClaimsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` - Page int32 `protobuf:"varint,4,opt,name=page,proto3" json:"page,omitempty"` - PageSize int32 `protobuf:"varint,5,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Page int32 `protobuf:"varint,4,opt,name=page,proto3" json:"page,omitempty"` + PageSize int32 `protobuf:"varint,5,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + ClaimedStartMs int64 `protobuf:"varint,6,opt,name=claimed_start_ms,json=claimedStartMs,proto3" json:"claimed_start_ms,omitempty"` + ClaimedEndMs int64 `protobuf:"varint,7,opt,name=claimed_end_ms,json=claimedEndMs,proto3" json:"claimed_end_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ListRegistrationRewardClaimsRequest) Reset() { @@ -4599,12 +4601,27 @@ func (x *ListRegistrationRewardClaimsRequest) GetPageSize() int32 { return 0 } +func (x *ListRegistrationRewardClaimsRequest) GetClaimedStartMs() int64 { + if x != nil { + return x.ClaimedStartMs + } + return 0 +} + +func (x *ListRegistrationRewardClaimsRequest) GetClaimedEndMs() int64 { + if x != nil { + return x.ClaimedEndMs + } + return 0 +} + type ListRegistrationRewardClaimsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Claims []*RegistrationRewardClaim `protobuf:"bytes,1,rep,name=claims,proto3" json:"claims,omitempty"` - Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Claims []*RegistrationRewardClaim `protobuf:"bytes,1,rep,name=claims,proto3" json:"claims,omitempty"` + Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + TodayClaimedCount int64 `protobuf:"varint,3,opt,name=today_claimed_count,json=todayClaimedCount,proto3" json:"today_claimed_count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ListRegistrationRewardClaimsResponse) Reset() { @@ -4651,6 +4668,13 @@ func (x *ListRegistrationRewardClaimsResponse) GetTotal() int64 { return 0 } +func (x *ListRegistrationRewardClaimsResponse) GetTodayClaimedCount() int64 { + if x != nil { + return x.TodayClaimedCount + } + return 0 +} + // FirstRechargeRewardTier 是首冲奖励的一个金币充值档位;max_coin_amount=0 表示无上限。 type FirstRechargeRewardTier struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -5712,6 +5736,2276 @@ func (x *ListFirstRechargeRewardClaimsResponse) GetTotal() int64 { return 0 } +// CumulativeRechargeRewardTier 是累充奖励的单个 USD 档位;threshold_usd_minor 以美分存储。 +type CumulativeRechargeRewardTier struct { + state protoimpl.MessageState `protogen:"open.v1"` + TierId int64 `protobuf:"varint,1,opt,name=tier_id,json=tierId,proto3" json:"tier_id,omitempty"` + TierCode string `protobuf:"bytes,2,opt,name=tier_code,json=tierCode,proto3" json:"tier_code,omitempty"` + TierName string `protobuf:"bytes,3,opt,name=tier_name,json=tierName,proto3" json:"tier_name,omitempty"` + ThresholdUsdMinor int64 `protobuf:"varint,4,opt,name=threshold_usd_minor,json=thresholdUsdMinor,proto3" json:"threshold_usd_minor,omitempty"` + ResourceGroupId int64 `protobuf:"varint,5,opt,name=resource_group_id,json=resourceGroupId,proto3" json:"resource_group_id,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + SortOrder int32 `protobuf:"varint,7,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"` + CreatedAtMs int64 `protobuf:"varint,8,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,9,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CumulativeRechargeRewardTier) Reset() { + *x = CumulativeRechargeRewardTier{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[76] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CumulativeRechargeRewardTier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CumulativeRechargeRewardTier) ProtoMessage() {} + +func (x *CumulativeRechargeRewardTier) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[76] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CumulativeRechargeRewardTier.ProtoReflect.Descriptor instead. +func (*CumulativeRechargeRewardTier) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{76} +} + +func (x *CumulativeRechargeRewardTier) GetTierId() int64 { + if x != nil { + return x.TierId + } + return 0 +} + +func (x *CumulativeRechargeRewardTier) GetTierCode() string { + if x != nil { + return x.TierCode + } + return "" +} + +func (x *CumulativeRechargeRewardTier) GetTierName() string { + if x != nil { + return x.TierName + } + return "" +} + +func (x *CumulativeRechargeRewardTier) GetThresholdUsdMinor() int64 { + if x != nil { + return x.ThresholdUsdMinor + } + return 0 +} + +func (x *CumulativeRechargeRewardTier) GetResourceGroupId() int64 { + if x != nil { + return x.ResourceGroupId + } + return 0 +} + +func (x *CumulativeRechargeRewardTier) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *CumulativeRechargeRewardTier) GetSortOrder() int32 { + if x != nil { + return x.SortOrder + } + return 0 +} + +func (x *CumulativeRechargeRewardTier) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *CumulativeRechargeRewardTier) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +// CumulativeRechargeRewardConfig 是当前 App 的累充奖励配置。 +type CumulativeRechargeRewardConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + AppCode string `protobuf:"bytes,1,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Tiers []*CumulativeRechargeRewardTier `protobuf:"bytes,3,rep,name=tiers,proto3" json:"tiers,omitempty"` + UpdatedByAdminId int64 `protobuf:"varint,4,opt,name=updated_by_admin_id,json=updatedByAdminId,proto3" json:"updated_by_admin_id,omitempty"` + CreatedAtMs int64 `protobuf:"varint,5,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,6,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CumulativeRechargeRewardConfig) Reset() { + *x = CumulativeRechargeRewardConfig{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[77] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CumulativeRechargeRewardConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CumulativeRechargeRewardConfig) ProtoMessage() {} + +func (x *CumulativeRechargeRewardConfig) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[77] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CumulativeRechargeRewardConfig.ProtoReflect.Descriptor instead. +func (*CumulativeRechargeRewardConfig) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{77} +} + +func (x *CumulativeRechargeRewardConfig) GetAppCode() string { + if x != nil { + return x.AppCode + } + return "" +} + +func (x *CumulativeRechargeRewardConfig) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *CumulativeRechargeRewardConfig) GetTiers() []*CumulativeRechargeRewardTier { + if x != nil { + return x.Tiers + } + return nil +} + +func (x *CumulativeRechargeRewardConfig) GetUpdatedByAdminId() int64 { + if x != nil { + return x.UpdatedByAdminId + } + return 0 +} + +func (x *CumulativeRechargeRewardConfig) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *CumulativeRechargeRewardConfig) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +// CumulativeRechargeRewardGrant 是用户在一个 UTC 自然周内达成某个档位后的发放事实。 +type CumulativeRechargeRewardGrant struct { + state protoimpl.MessageState `protogen:"open.v1"` + GrantId string `protobuf:"bytes,1,opt,name=grant_id,json=grantId,proto3" json:"grant_id,omitempty"` + AppCode string `protobuf:"bytes,2,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` + CycleKey string `protobuf:"bytes,3,opt,name=cycle_key,json=cycleKey,proto3" json:"cycle_key,omitempty"` + UserId int64 `protobuf:"varint,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + EventId string `protobuf:"bytes,5,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` + TransactionId string `protobuf:"bytes,6,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` + CommandId string `protobuf:"bytes,7,opt,name=command_id,json=commandId,proto3" json:"command_id,omitempty"` + TierId int64 `protobuf:"varint,8,opt,name=tier_id,json=tierId,proto3" json:"tier_id,omitempty"` + TierCode string `protobuf:"bytes,9,opt,name=tier_code,json=tierCode,proto3" json:"tier_code,omitempty"` + ThresholdUsdMinor int64 `protobuf:"varint,10,opt,name=threshold_usd_minor,json=thresholdUsdMinor,proto3" json:"threshold_usd_minor,omitempty"` + ResourceGroupId int64 `protobuf:"varint,11,opt,name=resource_group_id,json=resourceGroupId,proto3" json:"resource_group_id,omitempty"` + ReachedUsdMinor int64 `protobuf:"varint,12,opt,name=reached_usd_minor,json=reachedUsdMinor,proto3" json:"reached_usd_minor,omitempty"` + QualifyingUsdMinor int64 `protobuf:"varint,13,opt,name=qualifying_usd_minor,json=qualifyingUsdMinor,proto3" json:"qualifying_usd_minor,omitempty"` + RechargeCoinAmount int64 `protobuf:"varint,14,opt,name=recharge_coin_amount,json=rechargeCoinAmount,proto3" json:"recharge_coin_amount,omitempty"` + RechargeType string `protobuf:"bytes,15,opt,name=recharge_type,json=rechargeType,proto3" json:"recharge_type,omitempty"` + Status string `protobuf:"bytes,16,opt,name=status,proto3" json:"status,omitempty"` + WalletCommandId string `protobuf:"bytes,17,opt,name=wallet_command_id,json=walletCommandId,proto3" json:"wallet_command_id,omitempty"` + WalletGrantId string `protobuf:"bytes,18,opt,name=wallet_grant_id,json=walletGrantId,proto3" json:"wallet_grant_id,omitempty"` + FailureReason string `protobuf:"bytes,19,opt,name=failure_reason,json=failureReason,proto3" json:"failure_reason,omitempty"` + GrantedAtMs int64 `protobuf:"varint,20,opt,name=granted_at_ms,json=grantedAtMs,proto3" json:"granted_at_ms,omitempty"` + CreatedAtMs int64 `protobuf:"varint,21,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,22,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CumulativeRechargeRewardGrant) Reset() { + *x = CumulativeRechargeRewardGrant{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[78] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CumulativeRechargeRewardGrant) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CumulativeRechargeRewardGrant) ProtoMessage() {} + +func (x *CumulativeRechargeRewardGrant) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[78] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CumulativeRechargeRewardGrant.ProtoReflect.Descriptor instead. +func (*CumulativeRechargeRewardGrant) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{78} +} + +func (x *CumulativeRechargeRewardGrant) GetGrantId() string { + if x != nil { + return x.GrantId + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetAppCode() string { + if x != nil { + return x.AppCode + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetCycleKey() string { + if x != nil { + return x.CycleKey + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetEventId() string { + if x != nil { + return x.EventId + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetTransactionId() string { + if x != nil { + return x.TransactionId + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetCommandId() string { + if x != nil { + return x.CommandId + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetTierId() int64 { + if x != nil { + return x.TierId + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetTierCode() string { + if x != nil { + return x.TierCode + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetThresholdUsdMinor() int64 { + if x != nil { + return x.ThresholdUsdMinor + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetResourceGroupId() int64 { + if x != nil { + return x.ResourceGroupId + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetReachedUsdMinor() int64 { + if x != nil { + return x.ReachedUsdMinor + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetQualifyingUsdMinor() int64 { + if x != nil { + return x.QualifyingUsdMinor + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetRechargeCoinAmount() int64 { + if x != nil { + return x.RechargeCoinAmount + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetRechargeType() string { + if x != nil { + return x.RechargeType + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetWalletCommandId() string { + if x != nil { + return x.WalletCommandId + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetWalletGrantId() string { + if x != nil { + return x.WalletGrantId + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetFailureReason() string { + if x != nil { + return x.FailureReason + } + return "" +} + +func (x *CumulativeRechargeRewardGrant) GetGrantedAtMs() int64 { + if x != nil { + return x.GrantedAtMs + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *CumulativeRechargeRewardGrant) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +// CumulativeRechargeRewardProgress 是用户在当前 UTC 周期内的累充累计投影。 +type CumulativeRechargeRewardProgress struct { + state protoimpl.MessageState `protogen:"open.v1"` + AppCode string `protobuf:"bytes,1,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` + CycleKey string `protobuf:"bytes,2,opt,name=cycle_key,json=cycleKey,proto3" json:"cycle_key,omitempty"` + UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + TotalUsdMinor int64 `protobuf:"varint,4,opt,name=total_usd_minor,json=totalUsdMinor,proto3" json:"total_usd_minor,omitempty"` + TotalCoinAmount int64 `protobuf:"varint,5,opt,name=total_coin_amount,json=totalCoinAmount,proto3" json:"total_coin_amount,omitempty"` + FirstRechargedAtMs int64 `protobuf:"varint,6,opt,name=first_recharged_at_ms,json=firstRechargedAtMs,proto3" json:"first_recharged_at_ms,omitempty"` + LastRechargedAtMs int64 `protobuf:"varint,7,opt,name=last_recharged_at_ms,json=lastRechargedAtMs,proto3" json:"last_recharged_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,8,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CumulativeRechargeRewardProgress) Reset() { + *x = CumulativeRechargeRewardProgress{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[79] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CumulativeRechargeRewardProgress) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CumulativeRechargeRewardProgress) ProtoMessage() {} + +func (x *CumulativeRechargeRewardProgress) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[79] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CumulativeRechargeRewardProgress.ProtoReflect.Descriptor instead. +func (*CumulativeRechargeRewardProgress) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{79} +} + +func (x *CumulativeRechargeRewardProgress) GetAppCode() string { + if x != nil { + return x.AppCode + } + return "" +} + +func (x *CumulativeRechargeRewardProgress) GetCycleKey() string { + if x != nil { + return x.CycleKey + } + return "" +} + +func (x *CumulativeRechargeRewardProgress) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *CumulativeRechargeRewardProgress) GetTotalUsdMinor() int64 { + if x != nil { + return x.TotalUsdMinor + } + return 0 +} + +func (x *CumulativeRechargeRewardProgress) GetTotalCoinAmount() int64 { + if x != nil { + return x.TotalCoinAmount + } + return 0 +} + +func (x *CumulativeRechargeRewardProgress) GetFirstRechargedAtMs() int64 { + if x != nil { + return x.FirstRechargedAtMs + } + return 0 +} + +func (x *CumulativeRechargeRewardProgress) GetLastRechargedAtMs() int64 { + if x != nil { + return x.LastRechargedAtMs + } + return 0 +} + +func (x *CumulativeRechargeRewardProgress) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +// CumulativeRechargeRewardStatus 是 App 累充奖励 H5 可直接展示的当前状态。 +type CumulativeRechargeRewardStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *CumulativeRechargeRewardConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + Progress *CumulativeRechargeRewardProgress `protobuf:"bytes,2,opt,name=progress,proto3" json:"progress,omitempty"` + Grants []*CumulativeRechargeRewardGrant `protobuf:"bytes,3,rep,name=grants,proto3" json:"grants,omitempty"` + CycleKey string `protobuf:"bytes,4,opt,name=cycle_key,json=cycleKey,proto3" json:"cycle_key,omitempty"` + PeriodStartMs int64 `protobuf:"varint,5,opt,name=period_start_ms,json=periodStartMs,proto3" json:"period_start_ms,omitempty"` + PeriodEndMs int64 `protobuf:"varint,6,opt,name=period_end_ms,json=periodEndMs,proto3" json:"period_end_ms,omitempty"` + ServerTimeMs int64 `protobuf:"varint,7,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CumulativeRechargeRewardStatus) Reset() { + *x = CumulativeRechargeRewardStatus{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[80] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CumulativeRechargeRewardStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CumulativeRechargeRewardStatus) ProtoMessage() {} + +func (x *CumulativeRechargeRewardStatus) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[80] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CumulativeRechargeRewardStatus.ProtoReflect.Descriptor instead. +func (*CumulativeRechargeRewardStatus) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{80} +} + +func (x *CumulativeRechargeRewardStatus) GetConfig() *CumulativeRechargeRewardConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *CumulativeRechargeRewardStatus) GetProgress() *CumulativeRechargeRewardProgress { + if x != nil { + return x.Progress + } + return nil +} + +func (x *CumulativeRechargeRewardStatus) GetGrants() []*CumulativeRechargeRewardGrant { + if x != nil { + return x.Grants + } + return nil +} + +func (x *CumulativeRechargeRewardStatus) GetCycleKey() string { + if x != nil { + return x.CycleKey + } + return "" +} + +func (x *CumulativeRechargeRewardStatus) GetPeriodStartMs() int64 { + if x != nil { + return x.PeriodStartMs + } + return 0 +} + +func (x *CumulativeRechargeRewardStatus) GetPeriodEndMs() int64 { + if x != nil { + return x.PeriodEndMs + } + return 0 +} + +func (x *CumulativeRechargeRewardStatus) GetServerTimeMs() int64 { + if x != nil { + return x.ServerTimeMs + } + return 0 +} + +type GetCumulativeRechargeRewardStatusRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetCumulativeRechargeRewardStatusRequest) Reset() { + *x = GetCumulativeRechargeRewardStatusRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[81] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCumulativeRechargeRewardStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCumulativeRechargeRewardStatusRequest) ProtoMessage() {} + +func (x *GetCumulativeRechargeRewardStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[81] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCumulativeRechargeRewardStatusRequest.ProtoReflect.Descriptor instead. +func (*GetCumulativeRechargeRewardStatusRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{81} +} + +func (x *GetCumulativeRechargeRewardStatusRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *GetCumulativeRechargeRewardStatusRequest) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +type GetCumulativeRechargeRewardStatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status *CumulativeRechargeRewardStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetCumulativeRechargeRewardStatusResponse) Reset() { + *x = GetCumulativeRechargeRewardStatusResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[82] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCumulativeRechargeRewardStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCumulativeRechargeRewardStatusResponse) ProtoMessage() {} + +func (x *GetCumulativeRechargeRewardStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[82] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCumulativeRechargeRewardStatusResponse.ProtoReflect.Descriptor instead. +func (*GetCumulativeRechargeRewardStatusResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{82} +} + +func (x *GetCumulativeRechargeRewardStatusResponse) GetStatus() *CumulativeRechargeRewardStatus { + if x != nil { + return x.Status + } + return nil +} + +// ConsumeCumulativeRechargeRewardRequest 是 wallet 充值事实消费入口;所有成功充值都会按 UTC 周期累计。 +type ConsumeCumulativeRechargeRewardRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + EventId string `protobuf:"bytes,2,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` + TransactionId string `protobuf:"bytes,3,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` + CommandId string `protobuf:"bytes,4,opt,name=command_id,json=commandId,proto3" json:"command_id,omitempty"` + UserId int64 `protobuf:"varint,5,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + RechargeCoinAmount int64 `protobuf:"varint,6,opt,name=recharge_coin_amount,json=rechargeCoinAmount,proto3" json:"recharge_coin_amount,omitempty"` + RechargeSequence int64 `protobuf:"varint,7,opt,name=recharge_sequence,json=rechargeSequence,proto3" json:"recharge_sequence,omitempty"` + RechargeType string `protobuf:"bytes,8,opt,name=recharge_type,json=rechargeType,proto3" json:"recharge_type,omitempty"` + QualifyingUsdMinor int64 `protobuf:"varint,9,opt,name=qualifying_usd_minor,json=qualifyingUsdMinor,proto3" json:"qualifying_usd_minor,omitempty"` + PayloadJson string `protobuf:"bytes,10,opt,name=payload_json,json=payloadJson,proto3" json:"payload_json,omitempty"` + OccurredAtMs int64 `protobuf:"varint,11,opt,name=occurred_at_ms,json=occurredAtMs,proto3" json:"occurred_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConsumeCumulativeRechargeRewardRequest) Reset() { + *x = ConsumeCumulativeRechargeRewardRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[83] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConsumeCumulativeRechargeRewardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConsumeCumulativeRechargeRewardRequest) ProtoMessage() {} + +func (x *ConsumeCumulativeRechargeRewardRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[83] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConsumeCumulativeRechargeRewardRequest.ProtoReflect.Descriptor instead. +func (*ConsumeCumulativeRechargeRewardRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{83} +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetEventId() string { + if x != nil { + return x.EventId + } + return "" +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetTransactionId() string { + if x != nil { + return x.TransactionId + } + return "" +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetCommandId() string { + if x != nil { + return x.CommandId + } + return "" +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetRechargeCoinAmount() int64 { + if x != nil { + return x.RechargeCoinAmount + } + return 0 +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetRechargeSequence() int64 { + if x != nil { + return x.RechargeSequence + } + return 0 +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetRechargeType() string { + if x != nil { + return x.RechargeType + } + return "" +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetQualifyingUsdMinor() int64 { + if x != nil { + return x.QualifyingUsdMinor + } + return 0 +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetPayloadJson() string { + if x != nil { + return x.PayloadJson + } + return "" +} + +func (x *ConsumeCumulativeRechargeRewardRequest) GetOccurredAtMs() int64 { + if x != nil { + return x.OccurredAtMs + } + return 0 +} + +type ConsumeCumulativeRechargeRewardResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Grants []*CumulativeRechargeRewardGrant `protobuf:"bytes,1,rep,name=grants,proto3" json:"grants,omitempty"` + Consumed bool `protobuf:"varint,2,opt,name=consumed,proto3" json:"consumed,omitempty"` + Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConsumeCumulativeRechargeRewardResponse) Reset() { + *x = ConsumeCumulativeRechargeRewardResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[84] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConsumeCumulativeRechargeRewardResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConsumeCumulativeRechargeRewardResponse) ProtoMessage() {} + +func (x *ConsumeCumulativeRechargeRewardResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[84] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConsumeCumulativeRechargeRewardResponse.ProtoReflect.Descriptor instead. +func (*ConsumeCumulativeRechargeRewardResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{84} +} + +func (x *ConsumeCumulativeRechargeRewardResponse) GetGrants() []*CumulativeRechargeRewardGrant { + if x != nil { + return x.Grants + } + return nil +} + +func (x *ConsumeCumulativeRechargeRewardResponse) GetConsumed() bool { + if x != nil { + return x.Consumed + } + return false +} + +func (x *ConsumeCumulativeRechargeRewardResponse) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type GetCumulativeRechargeRewardConfigRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetCumulativeRechargeRewardConfigRequest) Reset() { + *x = GetCumulativeRechargeRewardConfigRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[85] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCumulativeRechargeRewardConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCumulativeRechargeRewardConfigRequest) ProtoMessage() {} + +func (x *GetCumulativeRechargeRewardConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[85] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCumulativeRechargeRewardConfigRequest.ProtoReflect.Descriptor instead. +func (*GetCumulativeRechargeRewardConfigRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{85} +} + +func (x *GetCumulativeRechargeRewardConfigRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +type GetCumulativeRechargeRewardConfigResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *CumulativeRechargeRewardConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetCumulativeRechargeRewardConfigResponse) Reset() { + *x = GetCumulativeRechargeRewardConfigResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[86] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCumulativeRechargeRewardConfigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCumulativeRechargeRewardConfigResponse) ProtoMessage() {} + +func (x *GetCumulativeRechargeRewardConfigResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[86] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCumulativeRechargeRewardConfigResponse.ProtoReflect.Descriptor instead. +func (*GetCumulativeRechargeRewardConfigResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{86} +} + +func (x *GetCumulativeRechargeRewardConfigResponse) GetConfig() *CumulativeRechargeRewardConfig { + if x != nil { + return x.Config + } + return nil +} + +type UpdateCumulativeRechargeRewardConfigRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Tiers []*CumulativeRechargeRewardTier `protobuf:"bytes,3,rep,name=tiers,proto3" json:"tiers,omitempty"` + OperatorAdminId int64 `protobuf:"varint,4,opt,name=operator_admin_id,json=operatorAdminId,proto3" json:"operator_admin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateCumulativeRechargeRewardConfigRequest) Reset() { + *x = UpdateCumulativeRechargeRewardConfigRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[87] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateCumulativeRechargeRewardConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCumulativeRechargeRewardConfigRequest) ProtoMessage() {} + +func (x *UpdateCumulativeRechargeRewardConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[87] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateCumulativeRechargeRewardConfigRequest.ProtoReflect.Descriptor instead. +func (*UpdateCumulativeRechargeRewardConfigRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{87} +} + +func (x *UpdateCumulativeRechargeRewardConfigRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *UpdateCumulativeRechargeRewardConfigRequest) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *UpdateCumulativeRechargeRewardConfigRequest) GetTiers() []*CumulativeRechargeRewardTier { + if x != nil { + return x.Tiers + } + return nil +} + +func (x *UpdateCumulativeRechargeRewardConfigRequest) GetOperatorAdminId() int64 { + if x != nil { + return x.OperatorAdminId + } + return 0 +} + +type UpdateCumulativeRechargeRewardConfigResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *CumulativeRechargeRewardConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateCumulativeRechargeRewardConfigResponse) Reset() { + *x = UpdateCumulativeRechargeRewardConfigResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[88] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateCumulativeRechargeRewardConfigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCumulativeRechargeRewardConfigResponse) ProtoMessage() {} + +func (x *UpdateCumulativeRechargeRewardConfigResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[88] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateCumulativeRechargeRewardConfigResponse.ProtoReflect.Descriptor instead. +func (*UpdateCumulativeRechargeRewardConfigResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{88} +} + +func (x *UpdateCumulativeRechargeRewardConfigResponse) GetConfig() *CumulativeRechargeRewardConfig { + if x != nil { + return x.Config + } + return nil +} + +type ListCumulativeRechargeRewardGrantsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + CycleKey string `protobuf:"bytes,4,opt,name=cycle_key,json=cycleKey,proto3" json:"cycle_key,omitempty"` + Page int32 `protobuf:"varint,5,opt,name=page,proto3" json:"page,omitempty"` + PageSize int32 `protobuf:"varint,6,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) Reset() { + *x = ListCumulativeRechargeRewardGrantsRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[89] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCumulativeRechargeRewardGrantsRequest) ProtoMessage() {} + +func (x *ListCumulativeRechargeRewardGrantsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[89] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListCumulativeRechargeRewardGrantsRequest.ProtoReflect.Descriptor instead. +func (*ListCumulativeRechargeRewardGrantsRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{89} +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) GetCycleKey() string { + if x != nil { + return x.CycleKey + } + return "" +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListCumulativeRechargeRewardGrantsRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +type ListCumulativeRechargeRewardGrantsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Grants []*CumulativeRechargeRewardGrant `protobuf:"bytes,1,rep,name=grants,proto3" json:"grants,omitempty"` + Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListCumulativeRechargeRewardGrantsResponse) Reset() { + *x = ListCumulativeRechargeRewardGrantsResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[90] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListCumulativeRechargeRewardGrantsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListCumulativeRechargeRewardGrantsResponse) ProtoMessage() {} + +func (x *ListCumulativeRechargeRewardGrantsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[90] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListCumulativeRechargeRewardGrantsResponse.ProtoReflect.Descriptor instead. +func (*ListCumulativeRechargeRewardGrantsResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{90} +} + +func (x *ListCumulativeRechargeRewardGrantsResponse) GetGrants() []*CumulativeRechargeRewardGrant { + if x != nil { + return x.Grants + } + return nil +} + +func (x *ListCumulativeRechargeRewardGrantsResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +// RoomTurnoverRewardTier 是房间流水奖励的单个档位;达到 threshold_coin_spent 后可命中奖励。 +type RoomTurnoverRewardTier struct { + state protoimpl.MessageState `protogen:"open.v1"` + TierId int64 `protobuf:"varint,1,opt,name=tier_id,json=tierId,proto3" json:"tier_id,omitempty"` + TierCode string `protobuf:"bytes,2,opt,name=tier_code,json=tierCode,proto3" json:"tier_code,omitempty"` + TierName string `protobuf:"bytes,3,opt,name=tier_name,json=tierName,proto3" json:"tier_name,omitempty"` + ThresholdCoinSpent int64 `protobuf:"varint,4,opt,name=threshold_coin_spent,json=thresholdCoinSpent,proto3" json:"threshold_coin_spent,omitempty"` + RewardCoinAmount int64 `protobuf:"varint,5,opt,name=reward_coin_amount,json=rewardCoinAmount,proto3" json:"reward_coin_amount,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + SortOrder int32 `protobuf:"varint,7,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"` + CreatedAtMs int64 `protobuf:"varint,8,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,9,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RoomTurnoverRewardTier) Reset() { + *x = RoomTurnoverRewardTier{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[91] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RoomTurnoverRewardTier) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoomTurnoverRewardTier) ProtoMessage() {} + +func (x *RoomTurnoverRewardTier) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[91] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RoomTurnoverRewardTier.ProtoReflect.Descriptor instead. +func (*RoomTurnoverRewardTier) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{91} +} + +func (x *RoomTurnoverRewardTier) GetTierId() int64 { + if x != nil { + return x.TierId + } + return 0 +} + +func (x *RoomTurnoverRewardTier) GetTierCode() string { + if x != nil { + return x.TierCode + } + return "" +} + +func (x *RoomTurnoverRewardTier) GetTierName() string { + if x != nil { + return x.TierName + } + return "" +} + +func (x *RoomTurnoverRewardTier) GetThresholdCoinSpent() int64 { + if x != nil { + return x.ThresholdCoinSpent + } + return 0 +} + +func (x *RoomTurnoverRewardTier) GetRewardCoinAmount() int64 { + if x != nil { + return x.RewardCoinAmount + } + return 0 +} + +func (x *RoomTurnoverRewardTier) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *RoomTurnoverRewardTier) GetSortOrder() int32 { + if x != nil { + return x.SortOrder + } + return 0 +} + +func (x *RoomTurnoverRewardTier) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *RoomTurnoverRewardTier) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +// RoomTurnoverRewardConfig 是当前 App 的房间流水奖励配置;结算只命中最高有效档。 +type RoomTurnoverRewardConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + AppCode string `protobuf:"bytes,1,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Tiers []*RoomTurnoverRewardTier `protobuf:"bytes,3,rep,name=tiers,proto3" json:"tiers,omitempty"` + UpdatedByAdminId int64 `protobuf:"varint,4,opt,name=updated_by_admin_id,json=updatedByAdminId,proto3" json:"updated_by_admin_id,omitempty"` + CreatedAtMs int64 `protobuf:"varint,5,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,6,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RoomTurnoverRewardConfig) Reset() { + *x = RoomTurnoverRewardConfig{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[92] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RoomTurnoverRewardConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoomTurnoverRewardConfig) ProtoMessage() {} + +func (x *RoomTurnoverRewardConfig) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[92] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RoomTurnoverRewardConfig.ProtoReflect.Descriptor instead. +func (*RoomTurnoverRewardConfig) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{92} +} + +func (x *RoomTurnoverRewardConfig) GetAppCode() string { + if x != nil { + return x.AppCode + } + return "" +} + +func (x *RoomTurnoverRewardConfig) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *RoomTurnoverRewardConfig) GetTiers() []*RoomTurnoverRewardTier { + if x != nil { + return x.Tiers + } + return nil +} + +func (x *RoomTurnoverRewardConfig) GetUpdatedByAdminId() int64 { + if x != nil { + return x.UpdatedByAdminId + } + return 0 +} + +func (x *RoomTurnoverRewardConfig) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *RoomTurnoverRewardConfig) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +// RoomTurnoverRewardSettlement 是某个房间单个 UTC 周期的结算事实。 +type RoomTurnoverRewardSettlement struct { + state protoimpl.MessageState `protogen:"open.v1"` + SettlementId string `protobuf:"bytes,1,opt,name=settlement_id,json=settlementId,proto3" json:"settlement_id,omitempty"` + AppCode string `protobuf:"bytes,2,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` + RoomId string `protobuf:"bytes,3,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` + OwnerUserId int64 `protobuf:"varint,4,opt,name=owner_user_id,json=ownerUserId,proto3" json:"owner_user_id,omitempty"` + PeriodStartMs int64 `protobuf:"varint,5,opt,name=period_start_ms,json=periodStartMs,proto3" json:"period_start_ms,omitempty"` + PeriodEndMs int64 `protobuf:"varint,6,opt,name=period_end_ms,json=periodEndMs,proto3" json:"period_end_ms,omitempty"` + CoinSpent int64 `protobuf:"varint,7,opt,name=coin_spent,json=coinSpent,proto3" json:"coin_spent,omitempty"` + TierId int64 `protobuf:"varint,8,opt,name=tier_id,json=tierId,proto3" json:"tier_id,omitempty"` + TierCode string `protobuf:"bytes,9,opt,name=tier_code,json=tierCode,proto3" json:"tier_code,omitempty"` + ThresholdCoinSpent int64 `protobuf:"varint,10,opt,name=threshold_coin_spent,json=thresholdCoinSpent,proto3" json:"threshold_coin_spent,omitempty"` + RewardCoinAmount int64 `protobuf:"varint,11,opt,name=reward_coin_amount,json=rewardCoinAmount,proto3" json:"reward_coin_amount,omitempty"` + Status string `protobuf:"bytes,12,opt,name=status,proto3" json:"status,omitempty"` + WalletCommandId string `protobuf:"bytes,13,opt,name=wallet_command_id,json=walletCommandId,proto3" json:"wallet_command_id,omitempty"` + WalletTransactionId string `protobuf:"bytes,14,opt,name=wallet_transaction_id,json=walletTransactionId,proto3" json:"wallet_transaction_id,omitempty"` + FailureReason string `protobuf:"bytes,15,opt,name=failure_reason,json=failureReason,proto3" json:"failure_reason,omitempty"` + SettledAtMs int64 `protobuf:"varint,16,opt,name=settled_at_ms,json=settledAtMs,proto3" json:"settled_at_ms,omitempty"` + CreatedAtMs int64 `protobuf:"varint,17,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,18,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RoomTurnoverRewardSettlement) Reset() { + *x = RoomTurnoverRewardSettlement{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[93] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RoomTurnoverRewardSettlement) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoomTurnoverRewardSettlement) ProtoMessage() {} + +func (x *RoomTurnoverRewardSettlement) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[93] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RoomTurnoverRewardSettlement.ProtoReflect.Descriptor instead. +func (*RoomTurnoverRewardSettlement) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{93} +} + +func (x *RoomTurnoverRewardSettlement) GetSettlementId() string { + if x != nil { + return x.SettlementId + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetAppCode() string { + if x != nil { + return x.AppCode + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetRoomId() string { + if x != nil { + return x.RoomId + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetOwnerUserId() int64 { + if x != nil { + return x.OwnerUserId + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetPeriodStartMs() int64 { + if x != nil { + return x.PeriodStartMs + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetPeriodEndMs() int64 { + if x != nil { + return x.PeriodEndMs + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetCoinSpent() int64 { + if x != nil { + return x.CoinSpent + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetTierId() int64 { + if x != nil { + return x.TierId + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetTierCode() string { + if x != nil { + return x.TierCode + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetThresholdCoinSpent() int64 { + if x != nil { + return x.ThresholdCoinSpent + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetRewardCoinAmount() int64 { + if x != nil { + return x.RewardCoinAmount + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetWalletCommandId() string { + if x != nil { + return x.WalletCommandId + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetWalletTransactionId() string { + if x != nil { + return x.WalletTransactionId + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetFailureReason() string { + if x != nil { + return x.FailureReason + } + return "" +} + +func (x *RoomTurnoverRewardSettlement) GetSettledAtMs() int64 { + if x != nil { + return x.SettledAtMs + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *RoomTurnoverRewardSettlement) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +// RoomTurnoverRewardStatus 给 App H5 返回当前 UTC 周期、房间流水、预计奖励和上一期结算。 +type RoomTurnoverRewardStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *RoomTurnoverRewardConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + RoomId string `protobuf:"bytes,2,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` + OwnerUserId int64 `protobuf:"varint,3,opt,name=owner_user_id,json=ownerUserId,proto3" json:"owner_user_id,omitempty"` + PeriodStartMs int64 `protobuf:"varint,4,opt,name=period_start_ms,json=periodStartMs,proto3" json:"period_start_ms,omitempty"` + PeriodEndMs int64 `protobuf:"varint,5,opt,name=period_end_ms,json=periodEndMs,proto3" json:"period_end_ms,omitempty"` + CurrentCoinSpent int64 `protobuf:"varint,6,opt,name=current_coin_spent,json=currentCoinSpent,proto3" json:"current_coin_spent,omitempty"` + ExpectedRewardCoinAmount int64 `protobuf:"varint,7,opt,name=expected_reward_coin_amount,json=expectedRewardCoinAmount,proto3" json:"expected_reward_coin_amount,omitempty"` + MatchedTier *RoomTurnoverRewardTier `protobuf:"bytes,8,opt,name=matched_tier,json=matchedTier,proto3" json:"matched_tier,omitempty"` + LatestSettlement *RoomTurnoverRewardSettlement `protobuf:"bytes,9,opt,name=latest_settlement,json=latestSettlement,proto3" json:"latest_settlement,omitempty"` + ServerTimeMs int64 `protobuf:"varint,10,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RoomTurnoverRewardStatus) Reset() { + *x = RoomTurnoverRewardStatus{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[94] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RoomTurnoverRewardStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RoomTurnoverRewardStatus) ProtoMessage() {} + +func (x *RoomTurnoverRewardStatus) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[94] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RoomTurnoverRewardStatus.ProtoReflect.Descriptor instead. +func (*RoomTurnoverRewardStatus) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{94} +} + +func (x *RoomTurnoverRewardStatus) GetConfig() *RoomTurnoverRewardConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *RoomTurnoverRewardStatus) GetRoomId() string { + if x != nil { + return x.RoomId + } + return "" +} + +func (x *RoomTurnoverRewardStatus) GetOwnerUserId() int64 { + if x != nil { + return x.OwnerUserId + } + return 0 +} + +func (x *RoomTurnoverRewardStatus) GetPeriodStartMs() int64 { + if x != nil { + return x.PeriodStartMs + } + return 0 +} + +func (x *RoomTurnoverRewardStatus) GetPeriodEndMs() int64 { + if x != nil { + return x.PeriodEndMs + } + return 0 +} + +func (x *RoomTurnoverRewardStatus) GetCurrentCoinSpent() int64 { + if x != nil { + return x.CurrentCoinSpent + } + return 0 +} + +func (x *RoomTurnoverRewardStatus) GetExpectedRewardCoinAmount() int64 { + if x != nil { + return x.ExpectedRewardCoinAmount + } + return 0 +} + +func (x *RoomTurnoverRewardStatus) GetMatchedTier() *RoomTurnoverRewardTier { + if x != nil { + return x.MatchedTier + } + return nil +} + +func (x *RoomTurnoverRewardStatus) GetLatestSettlement() *RoomTurnoverRewardSettlement { + if x != nil { + return x.LatestSettlement + } + return nil +} + +func (x *RoomTurnoverRewardStatus) GetServerTimeMs() int64 { + if x != nil { + return x.ServerTimeMs + } + return 0 +} + +type GetRoomTurnoverRewardStatusRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + RoomId string `protobuf:"bytes,3,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` + OwnerUserId int64 `protobuf:"varint,4,opt,name=owner_user_id,json=ownerUserId,proto3" json:"owner_user_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRoomTurnoverRewardStatusRequest) Reset() { + *x = GetRoomTurnoverRewardStatusRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[95] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRoomTurnoverRewardStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRoomTurnoverRewardStatusRequest) ProtoMessage() {} + +func (x *GetRoomTurnoverRewardStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[95] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRoomTurnoverRewardStatusRequest.ProtoReflect.Descriptor instead. +func (*GetRoomTurnoverRewardStatusRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{95} +} + +func (x *GetRoomTurnoverRewardStatusRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *GetRoomTurnoverRewardStatusRequest) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *GetRoomTurnoverRewardStatusRequest) GetRoomId() string { + if x != nil { + return x.RoomId + } + return "" +} + +func (x *GetRoomTurnoverRewardStatusRequest) GetOwnerUserId() int64 { + if x != nil { + return x.OwnerUserId + } + return 0 +} + +type GetRoomTurnoverRewardStatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status *RoomTurnoverRewardStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRoomTurnoverRewardStatusResponse) Reset() { + *x = GetRoomTurnoverRewardStatusResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[96] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRoomTurnoverRewardStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRoomTurnoverRewardStatusResponse) ProtoMessage() {} + +func (x *GetRoomTurnoverRewardStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[96] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRoomTurnoverRewardStatusResponse.ProtoReflect.Descriptor instead. +func (*GetRoomTurnoverRewardStatusResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{96} +} + +func (x *GetRoomTurnoverRewardStatusResponse) GetStatus() *RoomTurnoverRewardStatus { + if x != nil { + return x.Status + } + return nil +} + +type GetRoomTurnoverRewardConfigRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRoomTurnoverRewardConfigRequest) Reset() { + *x = GetRoomTurnoverRewardConfigRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[97] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRoomTurnoverRewardConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRoomTurnoverRewardConfigRequest) ProtoMessage() {} + +func (x *GetRoomTurnoverRewardConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[97] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRoomTurnoverRewardConfigRequest.ProtoReflect.Descriptor instead. +func (*GetRoomTurnoverRewardConfigRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{97} +} + +func (x *GetRoomTurnoverRewardConfigRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +type GetRoomTurnoverRewardConfigResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *RoomTurnoverRewardConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRoomTurnoverRewardConfigResponse) Reset() { + *x = GetRoomTurnoverRewardConfigResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[98] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRoomTurnoverRewardConfigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRoomTurnoverRewardConfigResponse) ProtoMessage() {} + +func (x *GetRoomTurnoverRewardConfigResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[98] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRoomTurnoverRewardConfigResponse.ProtoReflect.Descriptor instead. +func (*GetRoomTurnoverRewardConfigResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{98} +} + +func (x *GetRoomTurnoverRewardConfigResponse) GetConfig() *RoomTurnoverRewardConfig { + if x != nil { + return x.Config + } + return nil +} + +type UpdateRoomTurnoverRewardConfigRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Tiers []*RoomTurnoverRewardTier `protobuf:"bytes,3,rep,name=tiers,proto3" json:"tiers,omitempty"` + OperatorAdminId int64 `protobuf:"varint,4,opt,name=operator_admin_id,json=operatorAdminId,proto3" json:"operator_admin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateRoomTurnoverRewardConfigRequest) Reset() { + *x = UpdateRoomTurnoverRewardConfigRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[99] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateRoomTurnoverRewardConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRoomTurnoverRewardConfigRequest) ProtoMessage() {} + +func (x *UpdateRoomTurnoverRewardConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[99] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateRoomTurnoverRewardConfigRequest.ProtoReflect.Descriptor instead. +func (*UpdateRoomTurnoverRewardConfigRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{99} +} + +func (x *UpdateRoomTurnoverRewardConfigRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *UpdateRoomTurnoverRewardConfigRequest) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *UpdateRoomTurnoverRewardConfigRequest) GetTiers() []*RoomTurnoverRewardTier { + if x != nil { + return x.Tiers + } + return nil +} + +func (x *UpdateRoomTurnoverRewardConfigRequest) GetOperatorAdminId() int64 { + if x != nil { + return x.OperatorAdminId + } + return 0 +} + +type UpdateRoomTurnoverRewardConfigResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *RoomTurnoverRewardConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateRoomTurnoverRewardConfigResponse) Reset() { + *x = UpdateRoomTurnoverRewardConfigResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[100] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateRoomTurnoverRewardConfigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRoomTurnoverRewardConfigResponse) ProtoMessage() {} + +func (x *UpdateRoomTurnoverRewardConfigResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[100] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateRoomTurnoverRewardConfigResponse.ProtoReflect.Descriptor instead. +func (*UpdateRoomTurnoverRewardConfigResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{100} +} + +func (x *UpdateRoomTurnoverRewardConfigResponse) GetConfig() *RoomTurnoverRewardConfig { + if x != nil { + return x.Config + } + return nil +} + +type ListRoomTurnoverRewardSettlementsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + RoomId string `protobuf:"bytes,3,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` + OwnerUserId int64 `protobuf:"varint,4,opt,name=owner_user_id,json=ownerUserId,proto3" json:"owner_user_id,omitempty"` + PeriodStartMs int64 `protobuf:"varint,5,opt,name=period_start_ms,json=periodStartMs,proto3" json:"period_start_ms,omitempty"` + Page int32 `protobuf:"varint,6,opt,name=page,proto3" json:"page,omitempty"` + PageSize int32 `protobuf:"varint,7,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) Reset() { + *x = ListRoomTurnoverRewardSettlementsRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[101] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRoomTurnoverRewardSettlementsRequest) ProtoMessage() {} + +func (x *ListRoomTurnoverRewardSettlementsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[101] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRoomTurnoverRewardSettlementsRequest.ProtoReflect.Descriptor instead. +func (*ListRoomTurnoverRewardSettlementsRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{101} +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) GetRoomId() string { + if x != nil { + return x.RoomId + } + return "" +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) GetOwnerUserId() int64 { + if x != nil { + return x.OwnerUserId + } + return 0 +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) GetPeriodStartMs() int64 { + if x != nil { + return x.PeriodStartMs + } + return 0 +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListRoomTurnoverRewardSettlementsRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +type ListRoomTurnoverRewardSettlementsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Settlements []*RoomTurnoverRewardSettlement `protobuf:"bytes,1,rep,name=settlements,proto3" json:"settlements,omitempty"` + Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListRoomTurnoverRewardSettlementsResponse) Reset() { + *x = ListRoomTurnoverRewardSettlementsResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[102] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListRoomTurnoverRewardSettlementsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRoomTurnoverRewardSettlementsResponse) ProtoMessage() {} + +func (x *ListRoomTurnoverRewardSettlementsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[102] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRoomTurnoverRewardSettlementsResponse.ProtoReflect.Descriptor instead. +func (*ListRoomTurnoverRewardSettlementsResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{102} +} + +func (x *ListRoomTurnoverRewardSettlementsResponse) GetSettlements() []*RoomTurnoverRewardSettlement { + if x != nil { + return x.Settlements + } + return nil +} + +func (x *ListRoomTurnoverRewardSettlementsResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +type RetryRoomTurnoverRewardSettlementRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + SettlementId string `protobuf:"bytes,2,opt,name=settlement_id,json=settlementId,proto3" json:"settlement_id,omitempty"` + OperatorAdminId int64 `protobuf:"varint,3,opt,name=operator_admin_id,json=operatorAdminId,proto3" json:"operator_admin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RetryRoomTurnoverRewardSettlementRequest) Reset() { + *x = RetryRoomTurnoverRewardSettlementRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[103] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RetryRoomTurnoverRewardSettlementRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RetryRoomTurnoverRewardSettlementRequest) ProtoMessage() {} + +func (x *RetryRoomTurnoverRewardSettlementRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[103] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RetryRoomTurnoverRewardSettlementRequest.ProtoReflect.Descriptor instead. +func (*RetryRoomTurnoverRewardSettlementRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{103} +} + +func (x *RetryRoomTurnoverRewardSettlementRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *RetryRoomTurnoverRewardSettlementRequest) GetSettlementId() string { + if x != nil { + return x.SettlementId + } + return "" +} + +func (x *RetryRoomTurnoverRewardSettlementRequest) GetOperatorAdminId() int64 { + if x != nil { + return x.OperatorAdminId + } + return 0 +} + +type RetryRoomTurnoverRewardSettlementResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Settlement *RoomTurnoverRewardSettlement `protobuf:"bytes,1,opt,name=settlement,proto3" json:"settlement,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RetryRoomTurnoverRewardSettlementResponse) Reset() { + *x = RetryRoomTurnoverRewardSettlementResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[104] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RetryRoomTurnoverRewardSettlementResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RetryRoomTurnoverRewardSettlementResponse) ProtoMessage() {} + +func (x *RetryRoomTurnoverRewardSettlementResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[104] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RetryRoomTurnoverRewardSettlementResponse.ProtoReflect.Descriptor instead. +func (*RetryRoomTurnoverRewardSettlementResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{104} +} + +func (x *RetryRoomTurnoverRewardSettlementResponse) GetSettlement() *RoomTurnoverRewardSettlement { + if x != nil { + return x.Settlement + } + return nil +} + // SevenDayCheckInReward 是七日签到配置中某一天的资源组奖励。 type SevenDayCheckInReward struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -5723,7 +8017,7 @@ type SevenDayCheckInReward struct { func (x *SevenDayCheckInReward) Reset() { *x = SevenDayCheckInReward{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[76] + mi := &file_proto_activity_v1_activity_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5735,7 +8029,7 @@ func (x *SevenDayCheckInReward) String() string { func (*SevenDayCheckInReward) ProtoMessage() {} func (x *SevenDayCheckInReward) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[76] + mi := &file_proto_activity_v1_activity_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5748,7 +8042,7 @@ func (x *SevenDayCheckInReward) ProtoReflect() protoreflect.Message { // Deprecated: Use SevenDayCheckInReward.ProtoReflect.Descriptor instead. func (*SevenDayCheckInReward) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{76} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{105} } func (x *SevenDayCheckInReward) GetDayIndex() int32 { @@ -5781,7 +8075,7 @@ type SevenDayCheckInConfig struct { func (x *SevenDayCheckInConfig) Reset() { *x = SevenDayCheckInConfig{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[77] + mi := &file_proto_activity_v1_activity_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5793,7 +8087,7 @@ func (x *SevenDayCheckInConfig) String() string { func (*SevenDayCheckInConfig) ProtoMessage() {} func (x *SevenDayCheckInConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[77] + mi := &file_proto_activity_v1_activity_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5806,7 +8100,7 @@ func (x *SevenDayCheckInConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SevenDayCheckInConfig.ProtoReflect.Descriptor instead. func (*SevenDayCheckInConfig) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{77} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{106} } func (x *SevenDayCheckInConfig) GetAppCode() string { @@ -5873,7 +8167,7 @@ type SevenDayCheckInRewardStatus struct { func (x *SevenDayCheckInRewardStatus) Reset() { *x = SevenDayCheckInRewardStatus{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[78] + mi := &file_proto_activity_v1_activity_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5885,7 +8179,7 @@ func (x *SevenDayCheckInRewardStatus) String() string { func (*SevenDayCheckInRewardStatus) ProtoMessage() {} func (x *SevenDayCheckInRewardStatus) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[78] + mi := &file_proto_activity_v1_activity_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5898,7 +8192,7 @@ func (x *SevenDayCheckInRewardStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use SevenDayCheckInRewardStatus.ProtoReflect.Descriptor instead. func (*SevenDayCheckInRewardStatus) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{78} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{107} } func (x *SevenDayCheckInRewardStatus) GetDayIndex() int32 { @@ -5953,7 +8247,7 @@ type GetSevenDayCheckInStatusRequest struct { func (x *GetSevenDayCheckInStatusRequest) Reset() { *x = GetSevenDayCheckInStatusRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[79] + mi := &file_proto_activity_v1_activity_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5965,7 +8259,7 @@ func (x *GetSevenDayCheckInStatusRequest) String() string { func (*GetSevenDayCheckInStatusRequest) ProtoMessage() {} func (x *GetSevenDayCheckInStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[79] + mi := &file_proto_activity_v1_activity_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5978,7 +8272,7 @@ func (x *GetSevenDayCheckInStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSevenDayCheckInStatusRequest.ProtoReflect.Descriptor instead. func (*GetSevenDayCheckInStatusRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{79} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{108} } func (x *GetSevenDayCheckInStatusRequest) GetMeta() *RequestMeta { @@ -6015,7 +8309,7 @@ type GetSevenDayCheckInStatusResponse struct { func (x *GetSevenDayCheckInStatusResponse) Reset() { *x = GetSevenDayCheckInStatusResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[80] + mi := &file_proto_activity_v1_activity_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6027,7 +8321,7 @@ func (x *GetSevenDayCheckInStatusResponse) String() string { func (*GetSevenDayCheckInStatusResponse) ProtoMessage() {} func (x *GetSevenDayCheckInStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[80] + mi := &file_proto_activity_v1_activity_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6040,7 +8334,7 @@ func (x *GetSevenDayCheckInStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSevenDayCheckInStatusResponse.ProtoReflect.Descriptor instead. func (*GetSevenDayCheckInStatusResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{80} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{109} } func (x *GetSevenDayCheckInStatusResponse) GetEnabled() bool { @@ -6138,7 +8432,7 @@ type SignSevenDayCheckInRequest struct { func (x *SignSevenDayCheckInRequest) Reset() { *x = SignSevenDayCheckInRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[81] + mi := &file_proto_activity_v1_activity_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6150,7 +8444,7 @@ func (x *SignSevenDayCheckInRequest) String() string { func (*SignSevenDayCheckInRequest) ProtoMessage() {} func (x *SignSevenDayCheckInRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[81] + mi := &file_proto_activity_v1_activity_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6163,7 +8457,7 @@ func (x *SignSevenDayCheckInRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SignSevenDayCheckInRequest.ProtoReflect.Descriptor instead. func (*SignSevenDayCheckInRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{81} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{110} } func (x *SignSevenDayCheckInRequest) GetMeta() *RequestMeta { @@ -6204,7 +8498,7 @@ type SignSevenDayCheckInResponse struct { func (x *SignSevenDayCheckInResponse) Reset() { *x = SignSevenDayCheckInResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[82] + mi := &file_proto_activity_v1_activity_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6216,7 +8510,7 @@ func (x *SignSevenDayCheckInResponse) String() string { func (*SignSevenDayCheckInResponse) ProtoMessage() {} func (x *SignSevenDayCheckInResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[82] + mi := &file_proto_activity_v1_activity_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6229,7 +8523,7 @@ func (x *SignSevenDayCheckInResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SignSevenDayCheckInResponse.ProtoReflect.Descriptor instead. func (*SignSevenDayCheckInResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{82} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{111} } func (x *SignSevenDayCheckInResponse) GetClaimId() string { @@ -6304,7 +8598,7 @@ type GetSevenDayCheckInConfigRequest struct { func (x *GetSevenDayCheckInConfigRequest) Reset() { *x = GetSevenDayCheckInConfigRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[83] + mi := &file_proto_activity_v1_activity_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6316,7 +8610,7 @@ func (x *GetSevenDayCheckInConfigRequest) String() string { func (*GetSevenDayCheckInConfigRequest) ProtoMessage() {} func (x *GetSevenDayCheckInConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[83] + mi := &file_proto_activity_v1_activity_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6329,7 +8623,7 @@ func (x *GetSevenDayCheckInConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSevenDayCheckInConfigRequest.ProtoReflect.Descriptor instead. func (*GetSevenDayCheckInConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{83} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{112} } func (x *GetSevenDayCheckInConfigRequest) GetMeta() *RequestMeta { @@ -6348,7 +8642,7 @@ type GetSevenDayCheckInConfigResponse struct { func (x *GetSevenDayCheckInConfigResponse) Reset() { *x = GetSevenDayCheckInConfigResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[84] + mi := &file_proto_activity_v1_activity_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6360,7 +8654,7 @@ func (x *GetSevenDayCheckInConfigResponse) String() string { func (*GetSevenDayCheckInConfigResponse) ProtoMessage() {} func (x *GetSevenDayCheckInConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[84] + mi := &file_proto_activity_v1_activity_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6373,7 +8667,7 @@ func (x *GetSevenDayCheckInConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSevenDayCheckInConfigResponse.ProtoReflect.Descriptor instead. func (*GetSevenDayCheckInConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{84} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{113} } func (x *GetSevenDayCheckInConfigResponse) GetConfig() *SevenDayCheckInConfig { @@ -6395,7 +8689,7 @@ type UpdateSevenDayCheckInConfigRequest struct { func (x *UpdateSevenDayCheckInConfigRequest) Reset() { *x = UpdateSevenDayCheckInConfigRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[85] + mi := &file_proto_activity_v1_activity_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6407,7 +8701,7 @@ func (x *UpdateSevenDayCheckInConfigRequest) String() string { func (*UpdateSevenDayCheckInConfigRequest) ProtoMessage() {} func (x *UpdateSevenDayCheckInConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[85] + mi := &file_proto_activity_v1_activity_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6420,7 +8714,7 @@ func (x *UpdateSevenDayCheckInConfigRequest) ProtoReflect() protoreflect.Message // Deprecated: Use UpdateSevenDayCheckInConfigRequest.ProtoReflect.Descriptor instead. func (*UpdateSevenDayCheckInConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{85} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{114} } func (x *UpdateSevenDayCheckInConfigRequest) GetMeta() *RequestMeta { @@ -6460,7 +8754,7 @@ type UpdateSevenDayCheckInConfigResponse struct { func (x *UpdateSevenDayCheckInConfigResponse) Reset() { *x = UpdateSevenDayCheckInConfigResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[86] + mi := &file_proto_activity_v1_activity_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6472,7 +8766,7 @@ func (x *UpdateSevenDayCheckInConfigResponse) String() string { func (*UpdateSevenDayCheckInConfigResponse) ProtoMessage() {} func (x *UpdateSevenDayCheckInConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[86] + mi := &file_proto_activity_v1_activity_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6485,7 +8779,7 @@ func (x *UpdateSevenDayCheckInConfigResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use UpdateSevenDayCheckInConfigResponse.ProtoReflect.Descriptor instead. func (*UpdateSevenDayCheckInConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{86} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{115} } func (x *UpdateSevenDayCheckInConfigResponse) GetConfig() *SevenDayCheckInConfig { @@ -6520,7 +8814,7 @@ type SevenDayCheckInClaim struct { func (x *SevenDayCheckInClaim) Reset() { *x = SevenDayCheckInClaim{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[87] + mi := &file_proto_activity_v1_activity_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6532,7 +8826,7 @@ func (x *SevenDayCheckInClaim) String() string { func (*SevenDayCheckInClaim) ProtoMessage() {} func (x *SevenDayCheckInClaim) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[87] + mi := &file_proto_activity_v1_activity_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6545,7 +8839,7 @@ func (x *SevenDayCheckInClaim) ProtoReflect() protoreflect.Message { // Deprecated: Use SevenDayCheckInClaim.ProtoReflect.Descriptor instead. func (*SevenDayCheckInClaim) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{87} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{116} } func (x *SevenDayCheckInClaim) GetClaimId() string { @@ -6675,7 +8969,7 @@ type ListSevenDayCheckInClaimsRequest struct { func (x *ListSevenDayCheckInClaimsRequest) Reset() { *x = ListSevenDayCheckInClaimsRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[88] + mi := &file_proto_activity_v1_activity_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6687,7 +8981,7 @@ func (x *ListSevenDayCheckInClaimsRequest) String() string { func (*ListSevenDayCheckInClaimsRequest) ProtoMessage() {} func (x *ListSevenDayCheckInClaimsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[88] + mi := &file_proto_activity_v1_activity_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6700,7 +8994,7 @@ func (x *ListSevenDayCheckInClaimsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSevenDayCheckInClaimsRequest.ProtoReflect.Descriptor instead. func (*ListSevenDayCheckInClaimsRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{88} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{117} } func (x *ListSevenDayCheckInClaimsRequest) GetMeta() *RequestMeta { @@ -6762,7 +9056,7 @@ type ListSevenDayCheckInClaimsResponse struct { func (x *ListSevenDayCheckInClaimsResponse) Reset() { *x = ListSevenDayCheckInClaimsResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[89] + mi := &file_proto_activity_v1_activity_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6774,7 +9068,7 @@ func (x *ListSevenDayCheckInClaimsResponse) String() string { func (*ListSevenDayCheckInClaimsResponse) ProtoMessage() {} func (x *ListSevenDayCheckInClaimsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[89] + mi := &file_proto_activity_v1_activity_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6787,7 +9081,7 @@ func (x *ListSevenDayCheckInClaimsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ListSevenDayCheckInClaimsResponse.ProtoReflect.Descriptor instead. func (*ListSevenDayCheckInClaimsResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{89} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{118} } func (x *ListSevenDayCheckInClaimsResponse) GetClaims() []*SevenDayCheckInClaim { @@ -6820,7 +9114,7 @@ type LevelTrack struct { func (x *LevelTrack) Reset() { *x = LevelTrack{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[90] + mi := &file_proto_activity_v1_activity_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6832,7 +9126,7 @@ func (x *LevelTrack) String() string { func (*LevelTrack) ProtoMessage() {} func (x *LevelTrack) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[90] + mi := &file_proto_activity_v1_activity_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6845,7 +9139,7 @@ func (x *LevelTrack) ProtoReflect() protoreflect.Message { // Deprecated: Use LevelTrack.ProtoReflect.Descriptor instead. func (*LevelTrack) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{90} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{119} } func (x *LevelTrack) GetTrack() string { @@ -6918,7 +9212,7 @@ type LevelRule struct { func (x *LevelRule) Reset() { *x = LevelRule{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[91] + mi := &file_proto_activity_v1_activity_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6930,7 +9224,7 @@ func (x *LevelRule) String() string { func (*LevelRule) ProtoMessage() {} func (x *LevelRule) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[91] + mi := &file_proto_activity_v1_activity_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6943,7 +9237,7 @@ func (x *LevelRule) ProtoReflect() protoreflect.Message { // Deprecated: Use LevelRule.ProtoReflect.Descriptor instead. func (*LevelRule) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{91} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{120} } func (x *LevelRule) GetTrack() string { @@ -7053,7 +9347,7 @@ type LevelTier struct { func (x *LevelTier) Reset() { *x = LevelTier{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[92] + mi := &file_proto_activity_v1_activity_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7065,7 +9359,7 @@ func (x *LevelTier) String() string { func (*LevelTier) ProtoMessage() {} func (x *LevelTier) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[92] + mi := &file_proto_activity_v1_activity_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7078,7 +9372,7 @@ func (x *LevelTier) ProtoReflect() protoreflect.Message { // Deprecated: Use LevelTier.ProtoReflect.Descriptor instead. func (*LevelTier) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{92} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{121} } func (x *LevelTier) GetTierId() int64 { @@ -7201,7 +9495,7 @@ type LevelTrackOverview struct { func (x *LevelTrackOverview) Reset() { *x = LevelTrackOverview{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[93] + mi := &file_proto_activity_v1_activity_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7213,7 +9507,7 @@ func (x *LevelTrackOverview) String() string { func (*LevelTrackOverview) ProtoMessage() {} func (x *LevelTrackOverview) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[93] + mi := &file_proto_activity_v1_activity_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7226,7 +9520,7 @@ func (x *LevelTrackOverview) ProtoReflect() protoreflect.Message { // Deprecated: Use LevelTrackOverview.ProtoReflect.Descriptor instead. func (*LevelTrackOverview) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{93} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{122} } func (x *LevelTrackOverview) GetTrack() string { @@ -7330,7 +9624,7 @@ type GetMyLevelOverviewRequest struct { func (x *GetMyLevelOverviewRequest) Reset() { *x = GetMyLevelOverviewRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[94] + mi := &file_proto_activity_v1_activity_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7342,7 +9636,7 @@ func (x *GetMyLevelOverviewRequest) String() string { func (*GetMyLevelOverviewRequest) ProtoMessage() {} func (x *GetMyLevelOverviewRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[94] + mi := &file_proto_activity_v1_activity_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7355,7 +9649,7 @@ func (x *GetMyLevelOverviewRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyLevelOverviewRequest.ProtoReflect.Descriptor instead. func (*GetMyLevelOverviewRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{94} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{123} } func (x *GetMyLevelOverviewRequest) GetMeta() *RequestMeta { @@ -7382,7 +9676,7 @@ type GetMyLevelOverviewResponse struct { func (x *GetMyLevelOverviewResponse) Reset() { *x = GetMyLevelOverviewResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[95] + mi := &file_proto_activity_v1_activity_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7394,7 +9688,7 @@ func (x *GetMyLevelOverviewResponse) String() string { func (*GetMyLevelOverviewResponse) ProtoMessage() {} func (x *GetMyLevelOverviewResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[95] + mi := &file_proto_activity_v1_activity_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7407,7 +9701,7 @@ func (x *GetMyLevelOverviewResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyLevelOverviewResponse.ProtoReflect.Descriptor instead. func (*GetMyLevelOverviewResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{95} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{124} } func (x *GetMyLevelOverviewResponse) GetTracks() []*LevelTrackOverview { @@ -7435,7 +9729,7 @@ type GetLevelTrackRequest struct { func (x *GetLevelTrackRequest) Reset() { *x = GetLevelTrackRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[96] + mi := &file_proto_activity_v1_activity_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7447,7 +9741,7 @@ func (x *GetLevelTrackRequest) String() string { func (*GetLevelTrackRequest) ProtoMessage() {} func (x *GetLevelTrackRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[96] + mi := &file_proto_activity_v1_activity_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7460,7 +9754,7 @@ func (x *GetLevelTrackRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLevelTrackRequest.ProtoReflect.Descriptor instead. func (*GetLevelTrackRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{96} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{125} } func (x *GetLevelTrackRequest) GetMeta() *RequestMeta { @@ -7496,7 +9790,7 @@ type GetLevelTrackResponse struct { func (x *GetLevelTrackResponse) Reset() { *x = GetLevelTrackResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[97] + mi := &file_proto_activity_v1_activity_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7508,7 +9802,7 @@ func (x *GetLevelTrackResponse) String() string { func (*GetLevelTrackResponse) ProtoMessage() {} func (x *GetLevelTrackResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[97] + mi := &file_proto_activity_v1_activity_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7521,7 +9815,7 @@ func (x *GetLevelTrackResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLevelTrackResponse.ProtoReflect.Descriptor instead. func (*GetLevelTrackResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{97} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{126} } func (x *GetLevelTrackResponse) GetOverview() *LevelTrackOverview { @@ -7568,7 +9862,7 @@ type LevelDisplayTrackProfile struct { func (x *LevelDisplayTrackProfile) Reset() { *x = LevelDisplayTrackProfile{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[98] + mi := &file_proto_activity_v1_activity_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7580,7 +9874,7 @@ func (x *LevelDisplayTrackProfile) String() string { func (*LevelDisplayTrackProfile) ProtoMessage() {} func (x *LevelDisplayTrackProfile) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[98] + mi := &file_proto_activity_v1_activity_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7593,7 +9887,7 @@ func (x *LevelDisplayTrackProfile) ProtoReflect() protoreflect.Message { // Deprecated: Use LevelDisplayTrackProfile.ProtoReflect.Descriptor instead. func (*LevelDisplayTrackProfile) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{98} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{127} } func (x *LevelDisplayTrackProfile) GetTrack() string { @@ -7658,7 +9952,7 @@ type UserLevelDisplayProfile struct { func (x *UserLevelDisplayProfile) Reset() { *x = UserLevelDisplayProfile{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[99] + mi := &file_proto_activity_v1_activity_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7670,7 +9964,7 @@ func (x *UserLevelDisplayProfile) String() string { func (*UserLevelDisplayProfile) ProtoMessage() {} func (x *UserLevelDisplayProfile) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[99] + mi := &file_proto_activity_v1_activity_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7683,7 +9977,7 @@ func (x *UserLevelDisplayProfile) ProtoReflect() protoreflect.Message { // Deprecated: Use UserLevelDisplayProfile.ProtoReflect.Descriptor instead. func (*UserLevelDisplayProfile) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{99} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{128} } func (x *UserLevelDisplayProfile) GetUserId() int64 { @@ -7724,7 +10018,7 @@ type BatchGetUserLevelDisplayProfilesRequest struct { func (x *BatchGetUserLevelDisplayProfilesRequest) Reset() { *x = BatchGetUserLevelDisplayProfilesRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[100] + mi := &file_proto_activity_v1_activity_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7736,7 +10030,7 @@ func (x *BatchGetUserLevelDisplayProfilesRequest) String() string { func (*BatchGetUserLevelDisplayProfilesRequest) ProtoMessage() {} func (x *BatchGetUserLevelDisplayProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[100] + mi := &file_proto_activity_v1_activity_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7749,7 +10043,7 @@ func (x *BatchGetUserLevelDisplayProfilesRequest) ProtoReflect() protoreflect.Me // Deprecated: Use BatchGetUserLevelDisplayProfilesRequest.ProtoReflect.Descriptor instead. func (*BatchGetUserLevelDisplayProfilesRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{100} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{129} } func (x *BatchGetUserLevelDisplayProfilesRequest) GetMeta() *RequestMeta { @@ -7776,7 +10070,7 @@ type BatchGetUserLevelDisplayProfilesResponse struct { func (x *BatchGetUserLevelDisplayProfilesResponse) Reset() { *x = BatchGetUserLevelDisplayProfilesResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[101] + mi := &file_proto_activity_v1_activity_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7788,7 +10082,7 @@ func (x *BatchGetUserLevelDisplayProfilesResponse) String() string { func (*BatchGetUserLevelDisplayProfilesResponse) ProtoMessage() {} func (x *BatchGetUserLevelDisplayProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[101] + mi := &file_proto_activity_v1_activity_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7801,7 +10095,7 @@ func (x *BatchGetUserLevelDisplayProfilesResponse) ProtoReflect() protoreflect.M // Deprecated: Use BatchGetUserLevelDisplayProfilesResponse.ProtoReflect.Descriptor instead. func (*BatchGetUserLevelDisplayProfilesResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{101} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{130} } func (x *BatchGetUserLevelDisplayProfilesResponse) GetProfiles() []*UserLevelDisplayProfile { @@ -7840,7 +10134,7 @@ type LevelRewardJob struct { func (x *LevelRewardJob) Reset() { *x = LevelRewardJob{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[102] + mi := &file_proto_activity_v1_activity_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7852,7 +10146,7 @@ func (x *LevelRewardJob) String() string { func (*LevelRewardJob) ProtoMessage() {} func (x *LevelRewardJob) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[102] + mi := &file_proto_activity_v1_activity_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7865,7 +10159,7 @@ func (x *LevelRewardJob) ProtoReflect() protoreflect.Message { // Deprecated: Use LevelRewardJob.ProtoReflect.Descriptor instead. func (*LevelRewardJob) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{102} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{131} } func (x *LevelRewardJob) GetRewardJobId() string { @@ -7973,7 +10267,7 @@ type ListLevelRewardsRequest struct { func (x *ListLevelRewardsRequest) Reset() { *x = ListLevelRewardsRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[103] + mi := &file_proto_activity_v1_activity_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7985,7 +10279,7 @@ func (x *ListLevelRewardsRequest) String() string { func (*ListLevelRewardsRequest) ProtoMessage() {} func (x *ListLevelRewardsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[103] + mi := &file_proto_activity_v1_activity_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7998,7 +10292,7 @@ func (x *ListLevelRewardsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLevelRewardsRequest.ProtoReflect.Descriptor instead. func (*ListLevelRewardsRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{103} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{132} } func (x *ListLevelRewardsRequest) GetMeta() *RequestMeta { @@ -8053,7 +10347,7 @@ type ListLevelRewardsResponse struct { func (x *ListLevelRewardsResponse) Reset() { *x = ListLevelRewardsResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[104] + mi := &file_proto_activity_v1_activity_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8065,7 +10359,7 @@ func (x *ListLevelRewardsResponse) String() string { func (*ListLevelRewardsResponse) ProtoMessage() {} func (x *ListLevelRewardsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[104] + mi := &file_proto_activity_v1_activity_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8078,7 +10372,7 @@ func (x *ListLevelRewardsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLevelRewardsResponse.ProtoReflect.Descriptor instead. func (*ListLevelRewardsResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{104} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{133} } func (x *ListLevelRewardsResponse) GetRewards() []*LevelRewardJob { @@ -8115,7 +10409,7 @@ type ConsumeLevelEventRequest struct { func (x *ConsumeLevelEventRequest) Reset() { *x = ConsumeLevelEventRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[105] + mi := &file_proto_activity_v1_activity_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8127,7 +10421,7 @@ func (x *ConsumeLevelEventRequest) String() string { func (*ConsumeLevelEventRequest) ProtoMessage() {} func (x *ConsumeLevelEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[105] + mi := &file_proto_activity_v1_activity_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8140,7 +10434,7 @@ func (x *ConsumeLevelEventRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ConsumeLevelEventRequest.ProtoReflect.Descriptor instead. func (*ConsumeLevelEventRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{105} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{134} } func (x *ConsumeLevelEventRequest) GetMeta() *RequestMeta { @@ -8234,7 +10528,7 @@ type ConsumeLevelEventResponse struct { func (x *ConsumeLevelEventResponse) Reset() { *x = ConsumeLevelEventResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[106] + mi := &file_proto_activity_v1_activity_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8246,7 +10540,7 @@ func (x *ConsumeLevelEventResponse) String() string { func (*ConsumeLevelEventResponse) ProtoMessage() {} func (x *ConsumeLevelEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[106] + mi := &file_proto_activity_v1_activity_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8259,7 +10553,7 @@ func (x *ConsumeLevelEventResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ConsumeLevelEventResponse.ProtoReflect.Descriptor instead. func (*ConsumeLevelEventResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{106} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{135} } func (x *ConsumeLevelEventResponse) GetEventId() string { @@ -8316,7 +10610,7 @@ type RegistrationLevelBadgeGrant struct { func (x *RegistrationLevelBadgeGrant) Reset() { *x = RegistrationLevelBadgeGrant{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[107] + mi := &file_proto_activity_v1_activity_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8328,7 +10622,7 @@ func (x *RegistrationLevelBadgeGrant) String() string { func (*RegistrationLevelBadgeGrant) ProtoMessage() {} func (x *RegistrationLevelBadgeGrant) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[107] + mi := &file_proto_activity_v1_activity_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8341,7 +10635,7 @@ func (x *RegistrationLevelBadgeGrant) ProtoReflect() protoreflect.Message { // Deprecated: Use RegistrationLevelBadgeGrant.ProtoReflect.Descriptor instead. func (*RegistrationLevelBadgeGrant) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{107} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{136} } func (x *RegistrationLevelBadgeGrant) GetTrack() string { @@ -8383,7 +10677,7 @@ type IssueRegistrationLevelBadgesRequest struct { func (x *IssueRegistrationLevelBadgesRequest) Reset() { *x = IssueRegistrationLevelBadgesRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[108] + mi := &file_proto_activity_v1_activity_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8395,7 +10689,7 @@ func (x *IssueRegistrationLevelBadgesRequest) String() string { func (*IssueRegistrationLevelBadgesRequest) ProtoMessage() {} func (x *IssueRegistrationLevelBadgesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[108] + mi := &file_proto_activity_v1_activity_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8408,7 +10702,7 @@ func (x *IssueRegistrationLevelBadgesRequest) ProtoReflect() protoreflect.Messag // Deprecated: Use IssueRegistrationLevelBadgesRequest.ProtoReflect.Descriptor instead. func (*IssueRegistrationLevelBadgesRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{108} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{137} } func (x *IssueRegistrationLevelBadgesRequest) GetMeta() *RequestMeta { @@ -8442,7 +10736,7 @@ type IssueRegistrationLevelBadgesResponse struct { func (x *IssueRegistrationLevelBadgesResponse) Reset() { *x = IssueRegistrationLevelBadgesResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[109] + mi := &file_proto_activity_v1_activity_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8454,7 +10748,7 @@ func (x *IssueRegistrationLevelBadgesResponse) String() string { func (*IssueRegistrationLevelBadgesResponse) ProtoMessage() {} func (x *IssueRegistrationLevelBadgesResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[109] + mi := &file_proto_activity_v1_activity_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8467,7 +10761,7 @@ func (x *IssueRegistrationLevelBadgesResponse) ProtoReflect() protoreflect.Messa // Deprecated: Use IssueRegistrationLevelBadgesResponse.ProtoReflect.Descriptor instead. func (*IssueRegistrationLevelBadgesResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{109} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{138} } func (x *IssueRegistrationLevelBadgesResponse) GetGrants() []*RegistrationLevelBadgeGrant { @@ -8498,7 +10792,7 @@ type UpsertLevelTrackRequest struct { func (x *UpsertLevelTrackRequest) Reset() { *x = UpsertLevelTrackRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[110] + mi := &file_proto_activity_v1_activity_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8510,7 +10804,7 @@ func (x *UpsertLevelTrackRequest) String() string { func (*UpsertLevelTrackRequest) ProtoMessage() {} func (x *UpsertLevelTrackRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[110] + mi := &file_proto_activity_v1_activity_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8523,7 +10817,7 @@ func (x *UpsertLevelTrackRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLevelTrackRequest.ProtoReflect.Descriptor instead. func (*UpsertLevelTrackRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{110} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{139} } func (x *UpsertLevelTrackRequest) GetMeta() *RequestMeta { @@ -8578,7 +10872,7 @@ type UpsertLevelTrackResponse struct { func (x *UpsertLevelTrackResponse) Reset() { *x = UpsertLevelTrackResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[111] + mi := &file_proto_activity_v1_activity_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8590,7 +10884,7 @@ func (x *UpsertLevelTrackResponse) String() string { func (*UpsertLevelTrackResponse) ProtoMessage() {} func (x *UpsertLevelTrackResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[111] + mi := &file_proto_activity_v1_activity_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8603,7 +10897,7 @@ func (x *UpsertLevelTrackResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLevelTrackResponse.ProtoReflect.Descriptor instead. func (*UpsertLevelTrackResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{111} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{140} } func (x *UpsertLevelTrackResponse) GetTrack() *LevelTrack { @@ -8638,7 +10932,7 @@ type UpsertLevelRuleRequest struct { func (x *UpsertLevelRuleRequest) Reset() { *x = UpsertLevelRuleRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[112] + mi := &file_proto_activity_v1_activity_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8650,7 +10944,7 @@ func (x *UpsertLevelRuleRequest) String() string { func (*UpsertLevelRuleRequest) ProtoMessage() {} func (x *UpsertLevelRuleRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[112] + mi := &file_proto_activity_v1_activity_proto_msgTypes[141] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8663,7 +10957,7 @@ func (x *UpsertLevelRuleRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLevelRuleRequest.ProtoReflect.Descriptor instead. func (*UpsertLevelRuleRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{112} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{141} } func (x *UpsertLevelRuleRequest) GetMeta() *RequestMeta { @@ -8746,7 +11040,7 @@ type UpsertLevelRuleResponse struct { func (x *UpsertLevelRuleResponse) Reset() { *x = UpsertLevelRuleResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[113] + mi := &file_proto_activity_v1_activity_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8758,7 +11052,7 @@ func (x *UpsertLevelRuleResponse) String() string { func (*UpsertLevelRuleResponse) ProtoMessage() {} func (x *UpsertLevelRuleResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[113] + mi := &file_proto_activity_v1_activity_proto_msgTypes[142] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8771,7 +11065,7 @@ func (x *UpsertLevelRuleResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLevelRuleResponse.ProtoReflect.Descriptor instead. func (*UpsertLevelRuleResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{113} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{142} } func (x *UpsertLevelRuleResponse) GetRule() *LevelRule { @@ -8808,7 +11102,7 @@ type UpsertLevelTierRequest struct { func (x *UpsertLevelTierRequest) Reset() { *x = UpsertLevelTierRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[114] + mi := &file_proto_activity_v1_activity_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8820,7 +11114,7 @@ func (x *UpsertLevelTierRequest) String() string { func (*UpsertLevelTierRequest) ProtoMessage() {} func (x *UpsertLevelTierRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[114] + mi := &file_proto_activity_v1_activity_proto_msgTypes[143] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8833,7 +11127,7 @@ func (x *UpsertLevelTierRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLevelTierRequest.ProtoReflect.Descriptor instead. func (*UpsertLevelTierRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{114} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{143} } func (x *UpsertLevelTierRequest) GetMeta() *RequestMeta { @@ -8930,7 +11224,7 @@ type UpsertLevelTierResponse struct { func (x *UpsertLevelTierResponse) Reset() { *x = UpsertLevelTierResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[115] + mi := &file_proto_activity_v1_activity_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8942,7 +11236,7 @@ func (x *UpsertLevelTierResponse) String() string { func (*UpsertLevelTierResponse) ProtoMessage() {} func (x *UpsertLevelTierResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[115] + mi := &file_proto_activity_v1_activity_proto_msgTypes[144] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8955,7 +11249,7 @@ func (x *UpsertLevelTierResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLevelTierResponse.ProtoReflect.Descriptor instead. func (*UpsertLevelTierResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{115} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{144} } func (x *UpsertLevelTierResponse) GetTier() *LevelTier { @@ -8984,7 +11278,7 @@ type ListLevelConfigRequest struct { func (x *ListLevelConfigRequest) Reset() { *x = ListLevelConfigRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[116] + mi := &file_proto_activity_v1_activity_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8996,7 +11290,7 @@ func (x *ListLevelConfigRequest) String() string { func (*ListLevelConfigRequest) ProtoMessage() {} func (x *ListLevelConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[116] + mi := &file_proto_activity_v1_activity_proto_msgTypes[145] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9009,7 +11303,7 @@ func (x *ListLevelConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLevelConfigRequest.ProtoReflect.Descriptor instead. func (*ListLevelConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{116} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{145} } func (x *ListLevelConfigRequest) GetMeta() *RequestMeta { @@ -9045,7 +11339,7 @@ type ListLevelConfigResponse struct { func (x *ListLevelConfigResponse) Reset() { *x = ListLevelConfigResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[117] + mi := &file_proto_activity_v1_activity_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9057,7 +11351,7 @@ func (x *ListLevelConfigResponse) String() string { func (*ListLevelConfigResponse) ProtoMessage() {} func (x *ListLevelConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[117] + mi := &file_proto_activity_v1_activity_proto_msgTypes[146] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9070,7 +11364,7 @@ func (x *ListLevelConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLevelConfigResponse.ProtoReflect.Descriptor instead. func (*ListLevelConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{117} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{146} } func (x *ListLevelConfigResponse) GetTracks() []*LevelTrack { @@ -9116,7 +11410,7 @@ type AchievementCondition struct { func (x *AchievementCondition) Reset() { *x = AchievementCondition{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[118] + mi := &file_proto_activity_v1_activity_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9128,7 +11422,7 @@ func (x *AchievementCondition) String() string { func (*AchievementCondition) ProtoMessage() {} func (x *AchievementCondition) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[118] + mi := &file_proto_activity_v1_activity_proto_msgTypes[147] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9141,7 +11435,7 @@ func (x *AchievementCondition) ProtoReflect() protoreflect.Message { // Deprecated: Use AchievementCondition.ProtoReflect.Descriptor instead. func (*AchievementCondition) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{118} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{147} } func (x *AchievementCondition) GetConditionId() string { @@ -9222,7 +11516,7 @@ type AchievementDefinition struct { func (x *AchievementDefinition) Reset() { *x = AchievementDefinition{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[119] + mi := &file_proto_activity_v1_activity_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9234,7 +11528,7 @@ func (x *AchievementDefinition) String() string { func (*AchievementDefinition) ProtoMessage() {} func (x *AchievementDefinition) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[119] + mi := &file_proto_activity_v1_activity_proto_msgTypes[148] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9247,7 +11541,7 @@ func (x *AchievementDefinition) ProtoReflect() protoreflect.Message { // Deprecated: Use AchievementDefinition.ProtoReflect.Descriptor instead. func (*AchievementDefinition) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{119} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{148} } func (x *AchievementDefinition) GetAchievementId() string { @@ -9412,7 +11706,7 @@ type UserAchievement struct { func (x *UserAchievement) Reset() { *x = UserAchievement{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[120] + mi := &file_proto_activity_v1_activity_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9424,7 +11718,7 @@ func (x *UserAchievement) String() string { func (*UserAchievement) ProtoMessage() {} func (x *UserAchievement) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[120] + mi := &file_proto_activity_v1_activity_proto_msgTypes[149] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9437,7 +11731,7 @@ func (x *UserAchievement) ProtoReflect() protoreflect.Message { // Deprecated: Use UserAchievement.ProtoReflect.Descriptor instead. func (*UserAchievement) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{120} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{149} } func (x *UserAchievement) GetDefinition() *AchievementDefinition { @@ -9503,7 +11797,7 @@ type ListAchievementsRequest struct { func (x *ListAchievementsRequest) Reset() { *x = ListAchievementsRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[121] + mi := &file_proto_activity_v1_activity_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9515,7 +11809,7 @@ func (x *ListAchievementsRequest) String() string { func (*ListAchievementsRequest) ProtoMessage() {} func (x *ListAchievementsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[121] + mi := &file_proto_activity_v1_activity_proto_msgTypes[150] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9528,7 +11822,7 @@ func (x *ListAchievementsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAchievementsRequest.ProtoReflect.Descriptor instead. func (*ListAchievementsRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{121} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{150} } func (x *ListAchievementsRequest) GetMeta() *RequestMeta { @@ -9584,7 +11878,7 @@ type ListAchievementsResponse struct { func (x *ListAchievementsResponse) Reset() { *x = ListAchievementsResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[122] + mi := &file_proto_activity_v1_activity_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9596,7 +11890,7 @@ func (x *ListAchievementsResponse) String() string { func (*ListAchievementsResponse) ProtoMessage() {} func (x *ListAchievementsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[122] + mi := &file_proto_activity_v1_activity_proto_msgTypes[151] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9609,7 +11903,7 @@ func (x *ListAchievementsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAchievementsResponse.ProtoReflect.Descriptor instead. func (*ListAchievementsResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{122} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{151} } func (x *ListAchievementsResponse) GetAchievements() []*UserAchievement { @@ -9650,7 +11944,7 @@ type ConsumeAchievementEventRequest struct { func (x *ConsumeAchievementEventRequest) Reset() { *x = ConsumeAchievementEventRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[123] + mi := &file_proto_activity_v1_activity_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9662,7 +11956,7 @@ func (x *ConsumeAchievementEventRequest) String() string { func (*ConsumeAchievementEventRequest) ProtoMessage() {} func (x *ConsumeAchievementEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[123] + mi := &file_proto_activity_v1_activity_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9675,7 +11969,7 @@ func (x *ConsumeAchievementEventRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ConsumeAchievementEventRequest.ProtoReflect.Descriptor instead. func (*ConsumeAchievementEventRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{123} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{152} } func (x *ConsumeAchievementEventRequest) GetMeta() *RequestMeta { @@ -9754,7 +12048,7 @@ type ConsumeAchievementEventResponse struct { func (x *ConsumeAchievementEventResponse) Reset() { *x = ConsumeAchievementEventResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[124] + mi := &file_proto_activity_v1_activity_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9766,7 +12060,7 @@ func (x *ConsumeAchievementEventResponse) String() string { func (*ConsumeAchievementEventResponse) ProtoMessage() {} func (x *ConsumeAchievementEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[124] + mi := &file_proto_activity_v1_activity_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9779,7 +12073,7 @@ func (x *ConsumeAchievementEventResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ConsumeAchievementEventResponse.ProtoReflect.Descriptor instead. func (*ConsumeAchievementEventResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{124} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{153} } func (x *ConsumeAchievementEventResponse) GetEventId() string { @@ -9834,7 +12128,7 @@ type BadgeDisplayItem struct { func (x *BadgeDisplayItem) Reset() { *x = BadgeDisplayItem{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[125] + mi := &file_proto_activity_v1_activity_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9846,7 +12140,7 @@ func (x *BadgeDisplayItem) String() string { func (*BadgeDisplayItem) ProtoMessage() {} func (x *BadgeDisplayItem) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[125] + mi := &file_proto_activity_v1_activity_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9859,7 +12153,7 @@ func (x *BadgeDisplayItem) ProtoReflect() protoreflect.Message { // Deprecated: Use BadgeDisplayItem.ProtoReflect.Descriptor instead. func (*BadgeDisplayItem) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{125} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{154} } func (x *BadgeDisplayItem) GetSlot() string { @@ -9935,7 +12229,7 @@ type ListMyBadgesRequest struct { func (x *ListMyBadgesRequest) Reset() { *x = ListMyBadgesRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[126] + mi := &file_proto_activity_v1_activity_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9947,7 +12241,7 @@ func (x *ListMyBadgesRequest) String() string { func (*ListMyBadgesRequest) ProtoMessage() {} func (x *ListMyBadgesRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[126] + mi := &file_proto_activity_v1_activity_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9960,7 +12254,7 @@ func (x *ListMyBadgesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMyBadgesRequest.ProtoReflect.Descriptor instead. func (*ListMyBadgesRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{126} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{155} } func (x *ListMyBadgesRequest) GetMeta() *RequestMeta { @@ -9989,7 +12283,7 @@ type ListMyBadgesResponse struct { func (x *ListMyBadgesResponse) Reset() { *x = ListMyBadgesResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[127] + mi := &file_proto_activity_v1_activity_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10001,7 +12295,7 @@ func (x *ListMyBadgesResponse) String() string { func (*ListMyBadgesResponse) ProtoMessage() {} func (x *ListMyBadgesResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[127] + mi := &file_proto_activity_v1_activity_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10014,7 +12308,7 @@ func (x *ListMyBadgesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMyBadgesResponse.ProtoReflect.Descriptor instead. func (*ListMyBadgesResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{127} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{156} } func (x *ListMyBadgesResponse) GetStripBadges() []*BadgeDisplayItem { @@ -10058,7 +12352,7 @@ type SetBadgeDisplayRequest struct { func (x *SetBadgeDisplayRequest) Reset() { *x = SetBadgeDisplayRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[128] + mi := &file_proto_activity_v1_activity_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10070,7 +12364,7 @@ func (x *SetBadgeDisplayRequest) String() string { func (*SetBadgeDisplayRequest) ProtoMessage() {} func (x *SetBadgeDisplayRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[128] + mi := &file_proto_activity_v1_activity_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10083,7 +12377,7 @@ func (x *SetBadgeDisplayRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetBadgeDisplayRequest.ProtoReflect.Descriptor instead. func (*SetBadgeDisplayRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{128} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{157} } func (x *SetBadgeDisplayRequest) GetMeta() *RequestMeta { @@ -10130,7 +12424,7 @@ type SetBadgeDisplayResponse struct { func (x *SetBadgeDisplayResponse) Reset() { *x = SetBadgeDisplayResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[129] + mi := &file_proto_activity_v1_activity_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10142,7 +12436,7 @@ func (x *SetBadgeDisplayResponse) String() string { func (*SetBadgeDisplayResponse) ProtoMessage() {} func (x *SetBadgeDisplayResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[129] + mi := &file_proto_activity_v1_activity_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10155,7 +12449,7 @@ func (x *SetBadgeDisplayResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetBadgeDisplayResponse.ProtoReflect.Descriptor instead. func (*SetBadgeDisplayResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{129} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{158} } func (x *SetBadgeDisplayResponse) GetProfile() *ListMyBadgesResponse { @@ -10191,7 +12485,7 @@ type UpsertAchievementDefinitionRequest struct { func (x *UpsertAchievementDefinitionRequest) Reset() { *x = UpsertAchievementDefinitionRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[130] + mi := &file_proto_activity_v1_activity_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10203,7 +12497,7 @@ func (x *UpsertAchievementDefinitionRequest) String() string { func (*UpsertAchievementDefinitionRequest) ProtoMessage() {} func (x *UpsertAchievementDefinitionRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[130] + mi := &file_proto_activity_v1_activity_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10216,7 +12510,7 @@ func (x *UpsertAchievementDefinitionRequest) ProtoReflect() protoreflect.Message // Deprecated: Use UpsertAchievementDefinitionRequest.ProtoReflect.Descriptor instead. func (*UpsertAchievementDefinitionRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{130} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{159} } func (x *UpsertAchievementDefinitionRequest) GetMeta() *RequestMeta { @@ -10355,7 +12649,7 @@ type UpsertAchievementDefinitionResponse struct { func (x *UpsertAchievementDefinitionResponse) Reset() { *x = UpsertAchievementDefinitionResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[131] + mi := &file_proto_activity_v1_activity_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10367,7 +12661,7 @@ func (x *UpsertAchievementDefinitionResponse) String() string { func (*UpsertAchievementDefinitionResponse) ProtoMessage() {} func (x *UpsertAchievementDefinitionResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[131] + mi := &file_proto_activity_v1_activity_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10380,7 +12674,7 @@ func (x *UpsertAchievementDefinitionResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use UpsertAchievementDefinitionResponse.ProtoReflect.Descriptor instead. func (*UpsertAchievementDefinitionResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{131} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{160} } func (x *UpsertAchievementDefinitionResponse) GetAchievement() *AchievementDefinition { @@ -10419,7 +12713,7 @@ type LuckyGiftMeta struct { func (x *LuckyGiftMeta) Reset() { *x = LuckyGiftMeta{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[132] + mi := &file_proto_activity_v1_activity_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10431,7 +12725,7 @@ func (x *LuckyGiftMeta) String() string { func (*LuckyGiftMeta) ProtoMessage() {} func (x *LuckyGiftMeta) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[132] + mi := &file_proto_activity_v1_activity_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10444,7 +12738,7 @@ func (x *LuckyGiftMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftMeta.ProtoReflect.Descriptor instead. func (*LuckyGiftMeta) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{132} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{161} } func (x *LuckyGiftMeta) GetMeta() *RequestMeta { @@ -10553,7 +12847,7 @@ type LuckyGiftTier struct { func (x *LuckyGiftTier) Reset() { *x = LuckyGiftTier{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[133] + mi := &file_proto_activity_v1_activity_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10565,7 +12859,7 @@ func (x *LuckyGiftTier) String() string { func (*LuckyGiftTier) ProtoMessage() {} func (x *LuckyGiftTier) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[133] + mi := &file_proto_activity_v1_activity_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10578,7 +12872,7 @@ func (x *LuckyGiftTier) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftTier.ProtoReflect.Descriptor instead. func (*LuckyGiftTier) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{133} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{162} } func (x *LuckyGiftTier) GetPool() string { @@ -10678,7 +12972,7 @@ type LuckyGiftConfig struct { func (x *LuckyGiftConfig) Reset() { *x = LuckyGiftConfig{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[134] + mi := &file_proto_activity_v1_activity_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10690,7 +12984,7 @@ func (x *LuckyGiftConfig) String() string { func (*LuckyGiftConfig) ProtoMessage() {} func (x *LuckyGiftConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[134] + mi := &file_proto_activity_v1_activity_proto_msgTypes[163] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10703,7 +12997,7 @@ func (x *LuckyGiftConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftConfig.ProtoReflect.Descriptor instead. func (*LuckyGiftConfig) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{134} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{163} } func (x *LuckyGiftConfig) GetAppCode() string { @@ -11002,7 +13296,7 @@ type LuckyGiftRuleTier struct { func (x *LuckyGiftRuleTier) Reset() { *x = LuckyGiftRuleTier{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[135] + mi := &file_proto_activity_v1_activity_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11014,7 +13308,7 @@ func (x *LuckyGiftRuleTier) String() string { func (*LuckyGiftRuleTier) ProtoMessage() {} func (x *LuckyGiftRuleTier) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[135] + mi := &file_proto_activity_v1_activity_proto_msgTypes[164] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11027,7 +13321,7 @@ func (x *LuckyGiftRuleTier) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftRuleTier.ProtoReflect.Descriptor instead. func (*LuckyGiftRuleTier) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{135} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{164} } func (x *LuckyGiftRuleTier) GetStage() string { @@ -11096,7 +13390,7 @@ type LuckyGiftRuleStage struct { func (x *LuckyGiftRuleStage) Reset() { *x = LuckyGiftRuleStage{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[136] + mi := &file_proto_activity_v1_activity_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11108,7 +13402,7 @@ func (x *LuckyGiftRuleStage) String() string { func (*LuckyGiftRuleStage) ProtoMessage() {} func (x *LuckyGiftRuleStage) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[136] + mi := &file_proto_activity_v1_activity_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11121,7 +13415,7 @@ func (x *LuckyGiftRuleStage) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftRuleStage.ProtoReflect.Descriptor instead. func (*LuckyGiftRuleStage) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{136} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{165} } func (x *LuckyGiftRuleStage) GetStage() string { @@ -11167,7 +13461,7 @@ type LuckyGiftRuleConfig struct { func (x *LuckyGiftRuleConfig) Reset() { *x = LuckyGiftRuleConfig{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[137] + mi := &file_proto_activity_v1_activity_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11179,7 +13473,7 @@ func (x *LuckyGiftRuleConfig) String() string { func (*LuckyGiftRuleConfig) ProtoMessage() {} func (x *LuckyGiftRuleConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[137] + mi := &file_proto_activity_v1_activity_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11192,7 +13486,7 @@ func (x *LuckyGiftRuleConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftRuleConfig.ProtoReflect.Descriptor instead. func (*LuckyGiftRuleConfig) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{137} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{166} } func (x *LuckyGiftRuleConfig) GetAppCode() string { @@ -11355,7 +13649,7 @@ type CheckLuckyGiftRequest struct { func (x *CheckLuckyGiftRequest) Reset() { *x = CheckLuckyGiftRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[138] + mi := &file_proto_activity_v1_activity_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11367,7 +13661,7 @@ func (x *CheckLuckyGiftRequest) String() string { func (*CheckLuckyGiftRequest) ProtoMessage() {} func (x *CheckLuckyGiftRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[138] + mi := &file_proto_activity_v1_activity_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11380,7 +13674,7 @@ func (x *CheckLuckyGiftRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckLuckyGiftRequest.ProtoReflect.Descriptor instead. func (*CheckLuckyGiftRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{138} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{167} } func (x *CheckLuckyGiftRequest) GetMeta() *RequestMeta { @@ -11434,7 +13728,7 @@ type CheckLuckyGiftResponse struct { func (x *CheckLuckyGiftResponse) Reset() { *x = CheckLuckyGiftResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[139] + mi := &file_proto_activity_v1_activity_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11446,7 +13740,7 @@ func (x *CheckLuckyGiftResponse) String() string { func (*CheckLuckyGiftResponse) ProtoMessage() {} func (x *CheckLuckyGiftResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[139] + mi := &file_proto_activity_v1_activity_proto_msgTypes[168] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11459,7 +13753,7 @@ func (x *CheckLuckyGiftResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckLuckyGiftResponse.ProtoReflect.Descriptor instead. func (*CheckLuckyGiftResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{139} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{168} } func (x *CheckLuckyGiftResponse) GetEnabled() bool { @@ -11549,7 +13843,7 @@ type LuckyGiftDrawResult struct { func (x *LuckyGiftDrawResult) Reset() { *x = LuckyGiftDrawResult{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[140] + mi := &file_proto_activity_v1_activity_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11561,7 +13855,7 @@ func (x *LuckyGiftDrawResult) String() string { func (*LuckyGiftDrawResult) ProtoMessage() {} func (x *LuckyGiftDrawResult) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[140] + mi := &file_proto_activity_v1_activity_proto_msgTypes[169] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11574,7 +13868,7 @@ func (x *LuckyGiftDrawResult) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftDrawResult.ProtoReflect.Descriptor instead. func (*LuckyGiftDrawResult) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{140} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{169} } func (x *LuckyGiftDrawResult) GetDrawId() string { @@ -11747,7 +14041,7 @@ type ExecuteLuckyGiftDrawRequest struct { func (x *ExecuteLuckyGiftDrawRequest) Reset() { *x = ExecuteLuckyGiftDrawRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[141] + mi := &file_proto_activity_v1_activity_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11759,7 +14053,7 @@ func (x *ExecuteLuckyGiftDrawRequest) String() string { func (*ExecuteLuckyGiftDrawRequest) ProtoMessage() {} func (x *ExecuteLuckyGiftDrawRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[141] + mi := &file_proto_activity_v1_activity_proto_msgTypes[170] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11772,7 +14066,7 @@ func (x *ExecuteLuckyGiftDrawRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecuteLuckyGiftDrawRequest.ProtoReflect.Descriptor instead. func (*ExecuteLuckyGiftDrawRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{141} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{170} } func (x *ExecuteLuckyGiftDrawRequest) GetLuckyGift() *LuckyGiftMeta { @@ -11791,7 +14085,7 @@ type ExecuteLuckyGiftDrawResponse struct { func (x *ExecuteLuckyGiftDrawResponse) Reset() { *x = ExecuteLuckyGiftDrawResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[142] + mi := &file_proto_activity_v1_activity_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11803,7 +14097,7 @@ func (x *ExecuteLuckyGiftDrawResponse) String() string { func (*ExecuteLuckyGiftDrawResponse) ProtoMessage() {} func (x *ExecuteLuckyGiftDrawResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[142] + mi := &file_proto_activity_v1_activity_proto_msgTypes[171] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11816,7 +14110,7 @@ func (x *ExecuteLuckyGiftDrawResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecuteLuckyGiftDrawResponse.ProtoReflect.Descriptor instead. func (*ExecuteLuckyGiftDrawResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{142} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{171} } func (x *ExecuteLuckyGiftDrawResponse) GetResult() *LuckyGiftDrawResult { @@ -11837,7 +14131,7 @@ type GetLuckyGiftConfigRequest struct { func (x *GetLuckyGiftConfigRequest) Reset() { *x = GetLuckyGiftConfigRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[143] + mi := &file_proto_activity_v1_activity_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11849,7 +14143,7 @@ func (x *GetLuckyGiftConfigRequest) String() string { func (*GetLuckyGiftConfigRequest) ProtoMessage() {} func (x *GetLuckyGiftConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[143] + mi := &file_proto_activity_v1_activity_proto_msgTypes[172] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11862,7 +14156,7 @@ func (x *GetLuckyGiftConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLuckyGiftConfigRequest.ProtoReflect.Descriptor instead. func (*GetLuckyGiftConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{143} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{172} } func (x *GetLuckyGiftConfigRequest) GetMeta() *RequestMeta { @@ -11895,7 +14189,7 @@ type GetLuckyGiftConfigResponse struct { func (x *GetLuckyGiftConfigResponse) Reset() { *x = GetLuckyGiftConfigResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[144] + mi := &file_proto_activity_v1_activity_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11907,7 +14201,7 @@ func (x *GetLuckyGiftConfigResponse) String() string { func (*GetLuckyGiftConfigResponse) ProtoMessage() {} func (x *GetLuckyGiftConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[144] + mi := &file_proto_activity_v1_activity_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11920,7 +14214,7 @@ func (x *GetLuckyGiftConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLuckyGiftConfigResponse.ProtoReflect.Descriptor instead. func (*GetLuckyGiftConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{144} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{173} } func (x *GetLuckyGiftConfigResponse) GetConfig() *LuckyGiftRuleConfig { @@ -11941,7 +14235,7 @@ type UpsertLuckyGiftConfigRequest struct { func (x *UpsertLuckyGiftConfigRequest) Reset() { *x = UpsertLuckyGiftConfigRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[145] + mi := &file_proto_activity_v1_activity_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11953,7 +14247,7 @@ func (x *UpsertLuckyGiftConfigRequest) String() string { func (*UpsertLuckyGiftConfigRequest) ProtoMessage() {} func (x *UpsertLuckyGiftConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[145] + mi := &file_proto_activity_v1_activity_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11966,7 +14260,7 @@ func (x *UpsertLuckyGiftConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLuckyGiftConfigRequest.ProtoReflect.Descriptor instead. func (*UpsertLuckyGiftConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{145} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{174} } func (x *UpsertLuckyGiftConfigRequest) GetMeta() *RequestMeta { @@ -11999,7 +14293,7 @@ type UpsertLuckyGiftConfigResponse struct { func (x *UpsertLuckyGiftConfigResponse) Reset() { *x = UpsertLuckyGiftConfigResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[146] + mi := &file_proto_activity_v1_activity_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12011,7 +14305,7 @@ func (x *UpsertLuckyGiftConfigResponse) String() string { func (*UpsertLuckyGiftConfigResponse) ProtoMessage() {} func (x *UpsertLuckyGiftConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[146] + mi := &file_proto_activity_v1_activity_proto_msgTypes[175] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12024,7 +14318,7 @@ func (x *UpsertLuckyGiftConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpsertLuckyGiftConfigResponse.ProtoReflect.Descriptor instead. func (*UpsertLuckyGiftConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{146} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{175} } func (x *UpsertLuckyGiftConfigResponse) GetConfig() *LuckyGiftRuleConfig { @@ -12043,7 +14337,7 @@ type ListLuckyGiftConfigsRequest struct { func (x *ListLuckyGiftConfigsRequest) Reset() { *x = ListLuckyGiftConfigsRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[147] + mi := &file_proto_activity_v1_activity_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12055,7 +14349,7 @@ func (x *ListLuckyGiftConfigsRequest) String() string { func (*ListLuckyGiftConfigsRequest) ProtoMessage() {} func (x *ListLuckyGiftConfigsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[147] + mi := &file_proto_activity_v1_activity_proto_msgTypes[176] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12068,7 +14362,7 @@ func (x *ListLuckyGiftConfigsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLuckyGiftConfigsRequest.ProtoReflect.Descriptor instead. func (*ListLuckyGiftConfigsRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{147} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{176} } func (x *ListLuckyGiftConfigsRequest) GetMeta() *RequestMeta { @@ -12087,7 +14381,7 @@ type ListLuckyGiftConfigsResponse struct { func (x *ListLuckyGiftConfigsResponse) Reset() { *x = ListLuckyGiftConfigsResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[148] + mi := &file_proto_activity_v1_activity_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12099,7 +14393,7 @@ func (x *ListLuckyGiftConfigsResponse) String() string { func (*ListLuckyGiftConfigsResponse) ProtoMessage() {} func (x *ListLuckyGiftConfigsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[148] + mi := &file_proto_activity_v1_activity_proto_msgTypes[177] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12112,7 +14406,7 @@ func (x *ListLuckyGiftConfigsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLuckyGiftConfigsResponse.ProtoReflect.Descriptor instead. func (*ListLuckyGiftConfigsResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{148} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{177} } func (x *ListLuckyGiftConfigsResponse) GetConfigs() []*LuckyGiftRuleConfig { @@ -12138,7 +14432,7 @@ type ListLuckyGiftDrawsRequest struct { func (x *ListLuckyGiftDrawsRequest) Reset() { *x = ListLuckyGiftDrawsRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[149] + mi := &file_proto_activity_v1_activity_proto_msgTypes[178] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12150,7 +14444,7 @@ func (x *ListLuckyGiftDrawsRequest) String() string { func (*ListLuckyGiftDrawsRequest) ProtoMessage() {} func (x *ListLuckyGiftDrawsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[149] + mi := &file_proto_activity_v1_activity_proto_msgTypes[178] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12163,7 +14457,7 @@ func (x *ListLuckyGiftDrawsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLuckyGiftDrawsRequest.ProtoReflect.Descriptor instead. func (*ListLuckyGiftDrawsRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{149} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{178} } func (x *ListLuckyGiftDrawsRequest) GetMeta() *RequestMeta { @@ -12232,7 +14526,7 @@ type ListLuckyGiftDrawsResponse struct { func (x *ListLuckyGiftDrawsResponse) Reset() { *x = ListLuckyGiftDrawsResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[150] + mi := &file_proto_activity_v1_activity_proto_msgTypes[179] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12244,7 +14538,7 @@ func (x *ListLuckyGiftDrawsResponse) String() string { func (*ListLuckyGiftDrawsResponse) ProtoMessage() {} func (x *ListLuckyGiftDrawsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[150] + mi := &file_proto_activity_v1_activity_proto_msgTypes[179] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12257,7 +14551,7 @@ func (x *ListLuckyGiftDrawsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLuckyGiftDrawsResponse.ProtoReflect.Descriptor instead. func (*ListLuckyGiftDrawsResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{150} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{179} } func (x *ListLuckyGiftDrawsResponse) GetDraws() []*LuckyGiftDrawResult { @@ -12295,7 +14589,7 @@ type LuckyGiftDrawSummary struct { func (x *LuckyGiftDrawSummary) Reset() { *x = LuckyGiftDrawSummary{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[151] + mi := &file_proto_activity_v1_activity_proto_msgTypes[180] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12307,7 +14601,7 @@ func (x *LuckyGiftDrawSummary) String() string { func (*LuckyGiftDrawSummary) ProtoMessage() {} func (x *LuckyGiftDrawSummary) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[151] + mi := &file_proto_activity_v1_activity_proto_msgTypes[180] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12320,7 +14614,7 @@ func (x *LuckyGiftDrawSummary) ProtoReflect() protoreflect.Message { // Deprecated: Use LuckyGiftDrawSummary.ProtoReflect.Descriptor instead. func (*LuckyGiftDrawSummary) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{151} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{180} } func (x *LuckyGiftDrawSummary) GetPoolId() string { @@ -12428,7 +14722,7 @@ type GetLuckyGiftDrawSummaryRequest struct { func (x *GetLuckyGiftDrawSummaryRequest) Reset() { *x = GetLuckyGiftDrawSummaryRequest{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[152] + mi := &file_proto_activity_v1_activity_proto_msgTypes[181] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12440,7 +14734,7 @@ func (x *GetLuckyGiftDrawSummaryRequest) String() string { func (*GetLuckyGiftDrawSummaryRequest) ProtoMessage() {} func (x *GetLuckyGiftDrawSummaryRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[152] + mi := &file_proto_activity_v1_activity_proto_msgTypes[181] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12453,7 +14747,7 @@ func (x *GetLuckyGiftDrawSummaryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLuckyGiftDrawSummaryRequest.ProtoReflect.Descriptor instead. func (*GetLuckyGiftDrawSummaryRequest) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{152} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{181} } func (x *GetLuckyGiftDrawSummaryRequest) GetMeta() *RequestMeta { @@ -12507,7 +14801,7 @@ type GetLuckyGiftDrawSummaryResponse struct { func (x *GetLuckyGiftDrawSummaryResponse) Reset() { *x = GetLuckyGiftDrawSummaryResponse{} - mi := &file_proto_activity_v1_activity_proto_msgTypes[153] + mi := &file_proto_activity_v1_activity_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12519,7 +14813,7 @@ func (x *GetLuckyGiftDrawSummaryResponse) String() string { func (*GetLuckyGiftDrawSummaryResponse) ProtoMessage() {} func (x *GetLuckyGiftDrawSummaryResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_activity_v1_activity_proto_msgTypes[153] + mi := &file_proto_activity_v1_activity_proto_msgTypes[182] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12532,7 +14826,7 @@ func (x *GetLuckyGiftDrawSummaryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLuckyGiftDrawSummaryResponse.ProtoReflect.Descriptor instead. func (*GetLuckyGiftDrawSummaryResponse) Descriptor() ([]byte, []int) { - return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{153} + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{182} } func (x *GetLuckyGiftDrawSummaryResponse) GetSummary() *LuckyGiftDrawSummary { @@ -12542,6 +14836,1483 @@ func (x *GetLuckyGiftDrawSummaryResponse) GetSummary() *LuckyGiftDrawSummary { return nil } +// WeeklyStarGift 是周星周期内参与积分的指定礼物。 +type WeeklyStarGift struct { + state protoimpl.MessageState `protogen:"open.v1"` + GiftId string `protobuf:"bytes,1,opt,name=gift_id,json=giftId,proto3" json:"gift_id,omitempty"` + SortOrder int32 `protobuf:"varint,2,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WeeklyStarGift) Reset() { + *x = WeeklyStarGift{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[183] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WeeklyStarGift) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WeeklyStarGift) ProtoMessage() {} + +func (x *WeeklyStarGift) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[183] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WeeklyStarGift.ProtoReflect.Descriptor instead. +func (*WeeklyStarGift) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{183} +} + +func (x *WeeklyStarGift) GetGiftId() string { + if x != nil { + return x.GiftId + } + return "" +} + +func (x *WeeklyStarGift) GetSortOrder() int32 { + if x != nil { + return x.SortOrder + } + return 0 +} + +// WeeklyStarReward 是周星 Top 排名对应的资源组奖励。 +type WeeklyStarReward struct { + state protoimpl.MessageState `protogen:"open.v1"` + RankNo int32 `protobuf:"varint,1,opt,name=rank_no,json=rankNo,proto3" json:"rank_no,omitempty"` + ResourceGroupId int64 `protobuf:"varint,2,opt,name=resource_group_id,json=resourceGroupId,proto3" json:"resource_group_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WeeklyStarReward) Reset() { + *x = WeeklyStarReward{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[184] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WeeklyStarReward) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WeeklyStarReward) ProtoMessage() {} + +func (x *WeeklyStarReward) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[184] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WeeklyStarReward.ProtoReflect.Descriptor instead. +func (*WeeklyStarReward) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{184} +} + +func (x *WeeklyStarReward) GetRankNo() int32 { + if x != nil { + return x.RankNo + } + return 0 +} + +func (x *WeeklyStarReward) GetResourceGroupId() int64 { + if x != nil { + return x.ResourceGroupId + } + return 0 +} + +// WeeklyStarCycle 是一个按 UTC epoch ms 生效的周星配置周期。 +type WeeklyStarCycle struct { + state protoimpl.MessageState `protogen:"open.v1"` + CycleId string `protobuf:"bytes,1,opt,name=cycle_id,json=cycleId,proto3" json:"cycle_id,omitempty"` + ActivityCode string `protobuf:"bytes,2,opt,name=activity_code,json=activityCode,proto3" json:"activity_code,omitempty"` + RegionId int64 `protobuf:"varint,3,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` + Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"` + Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` + StartMs int64 `protobuf:"varint,6,opt,name=start_ms,json=startMs,proto3" json:"start_ms,omitempty"` + EndMs int64 `protobuf:"varint,7,opt,name=end_ms,json=endMs,proto3" json:"end_ms,omitempty"` + Gifts []*WeeklyStarGift `protobuf:"bytes,8,rep,name=gifts,proto3" json:"gifts,omitempty"` + Rewards []*WeeklyStarReward `protobuf:"bytes,9,rep,name=rewards,proto3" json:"rewards,omitempty"` + CreatedByAdminId int64 `protobuf:"varint,10,opt,name=created_by_admin_id,json=createdByAdminId,proto3" json:"created_by_admin_id,omitempty"` + UpdatedByAdminId int64 `protobuf:"varint,11,opt,name=updated_by_admin_id,json=updatedByAdminId,proto3" json:"updated_by_admin_id,omitempty"` + SettledAtMs int64 `protobuf:"varint,12,opt,name=settled_at_ms,json=settledAtMs,proto3" json:"settled_at_ms,omitempty"` + CreatedAtMs int64 `protobuf:"varint,13,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,14,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + AppCode string `protobuf:"bytes,15,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WeeklyStarCycle) Reset() { + *x = WeeklyStarCycle{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[185] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WeeklyStarCycle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WeeklyStarCycle) ProtoMessage() {} + +func (x *WeeklyStarCycle) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[185] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WeeklyStarCycle.ProtoReflect.Descriptor instead. +func (*WeeklyStarCycle) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{185} +} + +func (x *WeeklyStarCycle) GetCycleId() string { + if x != nil { + return x.CycleId + } + return "" +} + +func (x *WeeklyStarCycle) GetActivityCode() string { + if x != nil { + return x.ActivityCode + } + return "" +} + +func (x *WeeklyStarCycle) GetRegionId() int64 { + if x != nil { + return x.RegionId + } + return 0 +} + +func (x *WeeklyStarCycle) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *WeeklyStarCycle) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *WeeklyStarCycle) GetStartMs() int64 { + if x != nil { + return x.StartMs + } + return 0 +} + +func (x *WeeklyStarCycle) GetEndMs() int64 { + if x != nil { + return x.EndMs + } + return 0 +} + +func (x *WeeklyStarCycle) GetGifts() []*WeeklyStarGift { + if x != nil { + return x.Gifts + } + return nil +} + +func (x *WeeklyStarCycle) GetRewards() []*WeeklyStarReward { + if x != nil { + return x.Rewards + } + return nil +} + +func (x *WeeklyStarCycle) GetCreatedByAdminId() int64 { + if x != nil { + return x.CreatedByAdminId + } + return 0 +} + +func (x *WeeklyStarCycle) GetUpdatedByAdminId() int64 { + if x != nil { + return x.UpdatedByAdminId + } + return 0 +} + +func (x *WeeklyStarCycle) GetSettledAtMs() int64 { + if x != nil { + return x.SettledAtMs + } + return 0 +} + +func (x *WeeklyStarCycle) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *WeeklyStarCycle) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +func (x *WeeklyStarCycle) GetAppCode() string { + if x != nil { + return x.AppCode + } + return "" +} + +// WeeklyStarLeaderboardEntry 是一个周期内用户维度的积分排名。 +type WeeklyStarLeaderboardEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + RankNo int32 `protobuf:"varint,1,opt,name=rank_no,json=rankNo,proto3" json:"rank_no,omitempty"` + UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Score int64 `protobuf:"varint,3,opt,name=score,proto3" json:"score,omitempty"` + FirstScoredAtMs int64 `protobuf:"varint,4,opt,name=first_scored_at_ms,json=firstScoredAtMs,proto3" json:"first_scored_at_ms,omitempty"` + LastScoredAtMs int64 `protobuf:"varint,5,opt,name=last_scored_at_ms,json=lastScoredAtMs,proto3" json:"last_scored_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WeeklyStarLeaderboardEntry) Reset() { + *x = WeeklyStarLeaderboardEntry{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[186] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WeeklyStarLeaderboardEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WeeklyStarLeaderboardEntry) ProtoMessage() {} + +func (x *WeeklyStarLeaderboardEntry) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[186] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WeeklyStarLeaderboardEntry.ProtoReflect.Descriptor instead. +func (*WeeklyStarLeaderboardEntry) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{186} +} + +func (x *WeeklyStarLeaderboardEntry) GetRankNo() int32 { + if x != nil { + return x.RankNo + } + return 0 +} + +func (x *WeeklyStarLeaderboardEntry) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *WeeklyStarLeaderboardEntry) GetScore() int64 { + if x != nil { + return x.Score + } + return 0 +} + +func (x *WeeklyStarLeaderboardEntry) GetFirstScoredAtMs() int64 { + if x != nil { + return x.FirstScoredAtMs + } + return 0 +} + +func (x *WeeklyStarLeaderboardEntry) GetLastScoredAtMs() int64 { + if x != nil { + return x.LastScoredAtMs + } + return 0 +} + +// WeeklyStarSettlement 是周期结束后 Top 奖励发放的幂等记录。 +type WeeklyStarSettlement struct { + state protoimpl.MessageState `protogen:"open.v1"` + SettlementId string `protobuf:"bytes,1,opt,name=settlement_id,json=settlementId,proto3" json:"settlement_id,omitempty"` + CycleId string `protobuf:"bytes,2,opt,name=cycle_id,json=cycleId,proto3" json:"cycle_id,omitempty"` + RankNo int32 `protobuf:"varint,3,opt,name=rank_no,json=rankNo,proto3" json:"rank_no,omitempty"` + UserId int64 `protobuf:"varint,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Score int64 `protobuf:"varint,5,opt,name=score,proto3" json:"score,omitempty"` + ResourceGroupId int64 `protobuf:"varint,6,opt,name=resource_group_id,json=resourceGroupId,proto3" json:"resource_group_id,omitempty"` + WalletCommandId string `protobuf:"bytes,7,opt,name=wallet_command_id,json=walletCommandId,proto3" json:"wallet_command_id,omitempty"` + WalletGrantId string `protobuf:"bytes,8,opt,name=wallet_grant_id,json=walletGrantId,proto3" json:"wallet_grant_id,omitempty"` + Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` + FailureReason string `protobuf:"bytes,10,opt,name=failure_reason,json=failureReason,proto3" json:"failure_reason,omitempty"` + AttemptCount int32 `protobuf:"varint,11,opt,name=attempt_count,json=attemptCount,proto3" json:"attempt_count,omitempty"` + CreatedAtMs int64 `protobuf:"varint,12,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` + UpdatedAtMs int64 `protobuf:"varint,13,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WeeklyStarSettlement) Reset() { + *x = WeeklyStarSettlement{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[187] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WeeklyStarSettlement) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WeeklyStarSettlement) ProtoMessage() {} + +func (x *WeeklyStarSettlement) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[187] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WeeklyStarSettlement.ProtoReflect.Descriptor instead. +func (*WeeklyStarSettlement) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{187} +} + +func (x *WeeklyStarSettlement) GetSettlementId() string { + if x != nil { + return x.SettlementId + } + return "" +} + +func (x *WeeklyStarSettlement) GetCycleId() string { + if x != nil { + return x.CycleId + } + return "" +} + +func (x *WeeklyStarSettlement) GetRankNo() int32 { + if x != nil { + return x.RankNo + } + return 0 +} + +func (x *WeeklyStarSettlement) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *WeeklyStarSettlement) GetScore() int64 { + if x != nil { + return x.Score + } + return 0 +} + +func (x *WeeklyStarSettlement) GetResourceGroupId() int64 { + if x != nil { + return x.ResourceGroupId + } + return 0 +} + +func (x *WeeklyStarSettlement) GetWalletCommandId() string { + if x != nil { + return x.WalletCommandId + } + return "" +} + +func (x *WeeklyStarSettlement) GetWalletGrantId() string { + if x != nil { + return x.WalletGrantId + } + return "" +} + +func (x *WeeklyStarSettlement) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *WeeklyStarSettlement) GetFailureReason() string { + if x != nil { + return x.FailureReason + } + return "" +} + +func (x *WeeklyStarSettlement) GetAttemptCount() int32 { + if x != nil { + return x.AttemptCount + } + return 0 +} + +func (x *WeeklyStarSettlement) GetCreatedAtMs() int64 { + if x != nil { + return x.CreatedAtMs + } + return 0 +} + +func (x *WeeklyStarSettlement) GetUpdatedAtMs() int64 { + if x != nil { + return x.UpdatedAtMs + } + return 0 +} + +type ListWeeklyStarCyclesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + RegionId int64 `protobuf:"varint,2,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + StartMs int64 `protobuf:"varint,4,opt,name=start_ms,json=startMs,proto3" json:"start_ms,omitempty"` + EndMs int64 `protobuf:"varint,5,opt,name=end_ms,json=endMs,proto3" json:"end_ms,omitempty"` + Page int32 `protobuf:"varint,6,opt,name=page,proto3" json:"page,omitempty"` + PageSize int32 `protobuf:"varint,7,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarCyclesRequest) Reset() { + *x = ListWeeklyStarCyclesRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[188] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarCyclesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarCyclesRequest) ProtoMessage() {} + +func (x *ListWeeklyStarCyclesRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[188] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarCyclesRequest.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarCyclesRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{188} +} + +func (x *ListWeeklyStarCyclesRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ListWeeklyStarCyclesRequest) GetRegionId() int64 { + if x != nil { + return x.RegionId + } + return 0 +} + +func (x *ListWeeklyStarCyclesRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *ListWeeklyStarCyclesRequest) GetStartMs() int64 { + if x != nil { + return x.StartMs + } + return 0 +} + +func (x *ListWeeklyStarCyclesRequest) GetEndMs() int64 { + if x != nil { + return x.EndMs + } + return 0 +} + +func (x *ListWeeklyStarCyclesRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListWeeklyStarCyclesRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +type ListWeeklyStarCyclesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycles []*WeeklyStarCycle `protobuf:"bytes,1,rep,name=cycles,proto3" json:"cycles,omitempty"` + Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarCyclesResponse) Reset() { + *x = ListWeeklyStarCyclesResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[189] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarCyclesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarCyclesResponse) ProtoMessage() {} + +func (x *ListWeeklyStarCyclesResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[189] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarCyclesResponse.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarCyclesResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{189} +} + +func (x *ListWeeklyStarCyclesResponse) GetCycles() []*WeeklyStarCycle { + if x != nil { + return x.Cycles + } + return nil +} + +func (x *ListWeeklyStarCyclesResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +type GetWeeklyStarCycleRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + CycleId string `protobuf:"bytes,2,opt,name=cycle_id,json=cycleId,proto3" json:"cycle_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetWeeklyStarCycleRequest) Reset() { + *x = GetWeeklyStarCycleRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[190] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetWeeklyStarCycleRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWeeklyStarCycleRequest) ProtoMessage() {} + +func (x *GetWeeklyStarCycleRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[190] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWeeklyStarCycleRequest.ProtoReflect.Descriptor instead. +func (*GetWeeklyStarCycleRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{190} +} + +func (x *GetWeeklyStarCycleRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *GetWeeklyStarCycleRequest) GetCycleId() string { + if x != nil { + return x.CycleId + } + return "" +} + +type GetWeeklyStarCycleResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycle *WeeklyStarCycle `protobuf:"bytes,1,opt,name=cycle,proto3" json:"cycle,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetWeeklyStarCycleResponse) Reset() { + *x = GetWeeklyStarCycleResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[191] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetWeeklyStarCycleResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWeeklyStarCycleResponse) ProtoMessage() {} + +func (x *GetWeeklyStarCycleResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[191] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWeeklyStarCycleResponse.ProtoReflect.Descriptor instead. +func (*GetWeeklyStarCycleResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{191} +} + +func (x *GetWeeklyStarCycleResponse) GetCycle() *WeeklyStarCycle { + if x != nil { + return x.Cycle + } + return nil +} + +type UpsertWeeklyStarCycleRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Cycle *WeeklyStarCycle `protobuf:"bytes,2,opt,name=cycle,proto3" json:"cycle,omitempty"` + OperatorAdminId int64 `protobuf:"varint,3,opt,name=operator_admin_id,json=operatorAdminId,proto3" json:"operator_admin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpsertWeeklyStarCycleRequest) Reset() { + *x = UpsertWeeklyStarCycleRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[192] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpsertWeeklyStarCycleRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpsertWeeklyStarCycleRequest) ProtoMessage() {} + +func (x *UpsertWeeklyStarCycleRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[192] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpsertWeeklyStarCycleRequest.ProtoReflect.Descriptor instead. +func (*UpsertWeeklyStarCycleRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{192} +} + +func (x *UpsertWeeklyStarCycleRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *UpsertWeeklyStarCycleRequest) GetCycle() *WeeklyStarCycle { + if x != nil { + return x.Cycle + } + return nil +} + +func (x *UpsertWeeklyStarCycleRequest) GetOperatorAdminId() int64 { + if x != nil { + return x.OperatorAdminId + } + return 0 +} + +type UpsertWeeklyStarCycleResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycle *WeeklyStarCycle `protobuf:"bytes,1,opt,name=cycle,proto3" json:"cycle,omitempty"` + Created bool `protobuf:"varint,2,opt,name=created,proto3" json:"created,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpsertWeeklyStarCycleResponse) Reset() { + *x = UpsertWeeklyStarCycleResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[193] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpsertWeeklyStarCycleResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpsertWeeklyStarCycleResponse) ProtoMessage() {} + +func (x *UpsertWeeklyStarCycleResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[193] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpsertWeeklyStarCycleResponse.ProtoReflect.Descriptor instead. +func (*UpsertWeeklyStarCycleResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{193} +} + +func (x *UpsertWeeklyStarCycleResponse) GetCycle() *WeeklyStarCycle { + if x != nil { + return x.Cycle + } + return nil +} + +func (x *UpsertWeeklyStarCycleResponse) GetCreated() bool { + if x != nil { + return x.Created + } + return false +} + +type SetWeeklyStarCycleStatusRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + CycleId string `protobuf:"bytes,2,opt,name=cycle_id,json=cycleId,proto3" json:"cycle_id,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + OperatorAdminId int64 `protobuf:"varint,4,opt,name=operator_admin_id,json=operatorAdminId,proto3" json:"operator_admin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SetWeeklyStarCycleStatusRequest) Reset() { + *x = SetWeeklyStarCycleStatusRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[194] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SetWeeklyStarCycleStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetWeeklyStarCycleStatusRequest) ProtoMessage() {} + +func (x *SetWeeklyStarCycleStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[194] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetWeeklyStarCycleStatusRequest.ProtoReflect.Descriptor instead. +func (*SetWeeklyStarCycleStatusRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{194} +} + +func (x *SetWeeklyStarCycleStatusRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *SetWeeklyStarCycleStatusRequest) GetCycleId() string { + if x != nil { + return x.CycleId + } + return "" +} + +func (x *SetWeeklyStarCycleStatusRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *SetWeeklyStarCycleStatusRequest) GetOperatorAdminId() int64 { + if x != nil { + return x.OperatorAdminId + } + return 0 +} + +type SetWeeklyStarCycleStatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycle *WeeklyStarCycle `protobuf:"bytes,1,opt,name=cycle,proto3" json:"cycle,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SetWeeklyStarCycleStatusResponse) Reset() { + *x = SetWeeklyStarCycleStatusResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[195] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SetWeeklyStarCycleStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetWeeklyStarCycleStatusResponse) ProtoMessage() {} + +func (x *SetWeeklyStarCycleStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[195] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetWeeklyStarCycleStatusResponse.ProtoReflect.Descriptor instead. +func (*SetWeeklyStarCycleStatusResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{195} +} + +func (x *SetWeeklyStarCycleStatusResponse) GetCycle() *WeeklyStarCycle { + if x != nil { + return x.Cycle + } + return nil +} + +type ListWeeklyStarLeaderboardRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + CycleId string `protobuf:"bytes,2,opt,name=cycle_id,json=cycleId,proto3" json:"cycle_id,omitempty"` + RegionId int64 `protobuf:"varint,3,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` + PageSize int32 `protobuf:"varint,4,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + PageToken string `protobuf:"bytes,5,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarLeaderboardRequest) Reset() { + *x = ListWeeklyStarLeaderboardRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[196] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarLeaderboardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarLeaderboardRequest) ProtoMessage() {} + +func (x *ListWeeklyStarLeaderboardRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[196] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarLeaderboardRequest.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarLeaderboardRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{196} +} + +func (x *ListWeeklyStarLeaderboardRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ListWeeklyStarLeaderboardRequest) GetCycleId() string { + if x != nil { + return x.CycleId + } + return "" +} + +func (x *ListWeeklyStarLeaderboardRequest) GetRegionId() int64 { + if x != nil { + return x.RegionId + } + return 0 +} + +func (x *ListWeeklyStarLeaderboardRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListWeeklyStarLeaderboardRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +type ListWeeklyStarLeaderboardResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycle *WeeklyStarCycle `protobuf:"bytes,1,opt,name=cycle,proto3" json:"cycle,omitempty"` + Entries []*WeeklyStarLeaderboardEntry `protobuf:"bytes,2,rep,name=entries,proto3" json:"entries,omitempty"` + NextPageToken string `protobuf:"bytes,3,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + Total int64 `protobuf:"varint,4,opt,name=total,proto3" json:"total,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarLeaderboardResponse) Reset() { + *x = ListWeeklyStarLeaderboardResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[197] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarLeaderboardResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarLeaderboardResponse) ProtoMessage() {} + +func (x *ListWeeklyStarLeaderboardResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[197] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarLeaderboardResponse.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarLeaderboardResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{197} +} + +func (x *ListWeeklyStarLeaderboardResponse) GetCycle() *WeeklyStarCycle { + if x != nil { + return x.Cycle + } + return nil +} + +func (x *ListWeeklyStarLeaderboardResponse) GetEntries() []*WeeklyStarLeaderboardEntry { + if x != nil { + return x.Entries + } + return nil +} + +func (x *ListWeeklyStarLeaderboardResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +func (x *ListWeeklyStarLeaderboardResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +type ListWeeklyStarSettlementsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + CycleId string `protobuf:"bytes,2,opt,name=cycle_id,json=cycleId,proto3" json:"cycle_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarSettlementsRequest) Reset() { + *x = ListWeeklyStarSettlementsRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[198] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarSettlementsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarSettlementsRequest) ProtoMessage() {} + +func (x *ListWeeklyStarSettlementsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[198] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarSettlementsRequest.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarSettlementsRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{198} +} + +func (x *ListWeeklyStarSettlementsRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ListWeeklyStarSettlementsRequest) GetCycleId() string { + if x != nil { + return x.CycleId + } + return "" +} + +type ListWeeklyStarSettlementsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Settlements []*WeeklyStarSettlement `protobuf:"bytes,1,rep,name=settlements,proto3" json:"settlements,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarSettlementsResponse) Reset() { + *x = ListWeeklyStarSettlementsResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[199] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarSettlementsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarSettlementsResponse) ProtoMessage() {} + +func (x *ListWeeklyStarSettlementsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[199] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarSettlementsResponse.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarSettlementsResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{199} +} + +func (x *ListWeeklyStarSettlementsResponse) GetSettlements() []*WeeklyStarSettlement { + if x != nil { + return x.Settlements + } + return nil +} + +type GetWeeklyStarCurrentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + RegionId int64 `protobuf:"varint,3,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetWeeklyStarCurrentRequest) Reset() { + *x = GetWeeklyStarCurrentRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[200] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetWeeklyStarCurrentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWeeklyStarCurrentRequest) ProtoMessage() {} + +func (x *GetWeeklyStarCurrentRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[200] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWeeklyStarCurrentRequest.ProtoReflect.Descriptor instead. +func (*GetWeeklyStarCurrentRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{200} +} + +func (x *GetWeeklyStarCurrentRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *GetWeeklyStarCurrentRequest) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *GetWeeklyStarCurrentRequest) GetRegionId() int64 { + if x != nil { + return x.RegionId + } + return 0 +} + +type GetWeeklyStarCurrentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycle *WeeklyStarCycle `protobuf:"bytes,1,opt,name=cycle,proto3" json:"cycle,omitempty"` + TopEntries []*WeeklyStarLeaderboardEntry `protobuf:"bytes,2,rep,name=top_entries,json=topEntries,proto3" json:"top_entries,omitempty"` + MyEntry *WeeklyStarLeaderboardEntry `protobuf:"bytes,3,opt,name=my_entry,json=myEntry,proto3" json:"my_entry,omitempty"` + ServerTimeMs int64 `protobuf:"varint,4,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetWeeklyStarCurrentResponse) Reset() { + *x = GetWeeklyStarCurrentResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[201] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetWeeklyStarCurrentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWeeklyStarCurrentResponse) ProtoMessage() {} + +func (x *GetWeeklyStarCurrentResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[201] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWeeklyStarCurrentResponse.ProtoReflect.Descriptor instead. +func (*GetWeeklyStarCurrentResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{201} +} + +func (x *GetWeeklyStarCurrentResponse) GetCycle() *WeeklyStarCycle { + if x != nil { + return x.Cycle + } + return nil +} + +func (x *GetWeeklyStarCurrentResponse) GetTopEntries() []*WeeklyStarLeaderboardEntry { + if x != nil { + return x.TopEntries + } + return nil +} + +func (x *GetWeeklyStarCurrentResponse) GetMyEntry() *WeeklyStarLeaderboardEntry { + if x != nil { + return x.MyEntry + } + return nil +} + +func (x *GetWeeklyStarCurrentResponse) GetServerTimeMs() int64 { + if x != nil { + return x.ServerTimeMs + } + return 0 +} + +type ListWeeklyStarHistoryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + RegionId int64 `protobuf:"varint,2,opt,name=region_id,json=regionId,proto3" json:"region_id,omitempty"` + Limit int32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarHistoryRequest) Reset() { + *x = ListWeeklyStarHistoryRequest{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[202] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarHistoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarHistoryRequest) ProtoMessage() {} + +func (x *ListWeeklyStarHistoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[202] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarHistoryRequest.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarHistoryRequest) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{202} +} + +func (x *ListWeeklyStarHistoryRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ListWeeklyStarHistoryRequest) GetRegionId() int64 { + if x != nil { + return x.RegionId + } + return 0 +} + +func (x *ListWeeklyStarHistoryRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type WeeklyStarHistoryCycle struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycle *WeeklyStarCycle `protobuf:"bytes,1,opt,name=cycle,proto3" json:"cycle,omitempty"` + TopEntries []*WeeklyStarLeaderboardEntry `protobuf:"bytes,2,rep,name=top_entries,json=topEntries,proto3" json:"top_entries,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WeeklyStarHistoryCycle) Reset() { + *x = WeeklyStarHistoryCycle{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[203] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WeeklyStarHistoryCycle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WeeklyStarHistoryCycle) ProtoMessage() {} + +func (x *WeeklyStarHistoryCycle) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[203] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WeeklyStarHistoryCycle.ProtoReflect.Descriptor instead. +func (*WeeklyStarHistoryCycle) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{203} +} + +func (x *WeeklyStarHistoryCycle) GetCycle() *WeeklyStarCycle { + if x != nil { + return x.Cycle + } + return nil +} + +func (x *WeeklyStarHistoryCycle) GetTopEntries() []*WeeklyStarLeaderboardEntry { + if x != nil { + return x.TopEntries + } + return nil +} + +type ListWeeklyStarHistoryResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cycles []*WeeklyStarHistoryCycle `protobuf:"bytes,1,rep,name=cycles,proto3" json:"cycles,omitempty"` + ServerTimeMs int64 `protobuf:"varint,2,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListWeeklyStarHistoryResponse) Reset() { + *x = ListWeeklyStarHistoryResponse{} + mi := &file_proto_activity_v1_activity_proto_msgTypes[204] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListWeeklyStarHistoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListWeeklyStarHistoryResponse) ProtoMessage() {} + +func (x *ListWeeklyStarHistoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_activity_v1_activity_proto_msgTypes[204] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListWeeklyStarHistoryResponse.ProtoReflect.Descriptor instead. +func (*ListWeeklyStarHistoryResponse) Descriptor() ([]byte, []int) { + return file_proto_activity_v1_activity_proto_rawDescGZIP(), []int{204} +} + +func (x *ListWeeklyStarHistoryResponse) GetCycles() []*WeeklyStarHistoryCycle { + if x != nil { + return x.Cycles + } + return nil +} + +func (x *ListWeeklyStarHistoryResponse) GetServerTimeMs() int64 { + if x != nil { + return x.ServerTimeMs + } + return 0 +} + var File_proto_activity_v1_activity_proto protoreflect.FileDescriptor const file_proto_activity_v1_activity_proto_rawDesc = "" + @@ -12952,16 +16723,19 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" + "dailyLimit\x12*\n" + "\x11operator_admin_id\x18\a \x01(\x03R\x0foperatorAdminId\"m\n" + "&UpdateRegistrationRewardConfigResponse\x12C\n" + - "\x06config\x18\x01 \x01(\v2+.hyapp.activity.v1.RegistrationRewardConfigR\x06config\"\xbb\x01\n" + + "\x06config\x18\x01 \x01(\v2+.hyapp.activity.v1.RegistrationRewardConfigR\x06config\"\x8b\x02\n" + "#ListRegistrationRewardClaimsRequest\x122\n" + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x16\n" + "\x06status\x18\x02 \x01(\tR\x06status\x12\x17\n" + "\auser_id\x18\x03 \x01(\x03R\x06userId\x12\x12\n" + "\x04page\x18\x04 \x01(\x05R\x04page\x12\x1b\n" + - "\tpage_size\x18\x05 \x01(\x05R\bpageSize\"\x80\x01\n" + + "\tpage_size\x18\x05 \x01(\x05R\bpageSize\x12(\n" + + "\x10claimed_start_ms\x18\x06 \x01(\x03R\x0eclaimedStartMs\x12$\n" + + "\x0eclaimed_end_ms\x18\a \x01(\x03R\fclaimedEndMs\"\xb0\x01\n" + "$ListRegistrationRewardClaimsResponse\x12B\n" + "\x06claims\x18\x01 \x03(\v2*.hyapp.activity.v1.RegistrationRewardClaimR\x06claims\x12\x14\n" + - "\x05total\x18\x02 \x01(\x03R\x05total\"\xe7\x02\n" + + "\x05total\x18\x02 \x01(\x03R\x05total\x12.\n" + + "\x13today_claimed_count\x18\x03 \x01(\x03R\x11todayClaimedCount\"\xe7\x02\n" + "\x17FirstRechargeRewardTier\x12\x17\n" + "\atier_id\x18\x01 \x01(\x03R\x06tierId\x12\x1b\n" + "\ttier_code\x18\x02 \x01(\tR\btierCode\x12\x1b\n" + @@ -13049,7 +16823,199 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" + "\tpage_size\x18\x05 \x01(\x05R\bpageSize\"\x82\x01\n" + "%ListFirstRechargeRewardClaimsResponse\x12C\n" + "\x06claims\x18\x01 \x03(\v2+.hyapp.activity.v1.FirstRechargeRewardClaimR\x06claims\x12\x14\n" + - "\x05total\x18\x02 \x01(\x03R\x05total\"`\n" + + "\x05total\x18\x02 \x01(\x03R\x05total\"\xcc\x02\n" + + "\x1cCumulativeRechargeRewardTier\x12\x17\n" + + "\atier_id\x18\x01 \x01(\x03R\x06tierId\x12\x1b\n" + + "\ttier_code\x18\x02 \x01(\tR\btierCode\x12\x1b\n" + + "\ttier_name\x18\x03 \x01(\tR\btierName\x12.\n" + + "\x13threshold_usd_minor\x18\x04 \x01(\x03R\x11thresholdUsdMinor\x12*\n" + + "\x11resource_group_id\x18\x05 \x01(\x03R\x0fresourceGroupId\x12\x16\n" + + "\x06status\x18\x06 \x01(\tR\x06status\x12\x1d\n" + + "\n" + + "sort_order\x18\a \x01(\x05R\tsortOrder\x12\"\n" + + "\rcreated_at_ms\x18\b \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\t \x01(\x03R\vupdatedAtMs\"\x93\x02\n" + + "\x1eCumulativeRechargeRewardConfig\x12\x19\n" + + "\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x18\n" + + "\aenabled\x18\x02 \x01(\bR\aenabled\x12E\n" + + "\x05tiers\x18\x03 \x03(\v2/.hyapp.activity.v1.CumulativeRechargeRewardTierR\x05tiers\x12-\n" + + "\x13updated_by_admin_id\x18\x04 \x01(\x03R\x10updatedByAdminId\x12\"\n" + + "\rcreated_at_ms\x18\x05 \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\x06 \x01(\x03R\vupdatedAtMs\"\xb2\x06\n" + + "\x1dCumulativeRechargeRewardGrant\x12\x19\n" + + "\bgrant_id\x18\x01 \x01(\tR\agrantId\x12\x19\n" + + "\bapp_code\x18\x02 \x01(\tR\aappCode\x12\x1b\n" + + "\tcycle_key\x18\x03 \x01(\tR\bcycleKey\x12\x17\n" + + "\auser_id\x18\x04 \x01(\x03R\x06userId\x12\x19\n" + + "\bevent_id\x18\x05 \x01(\tR\aeventId\x12%\n" + + "\x0etransaction_id\x18\x06 \x01(\tR\rtransactionId\x12\x1d\n" + + "\n" + + "command_id\x18\a \x01(\tR\tcommandId\x12\x17\n" + + "\atier_id\x18\b \x01(\x03R\x06tierId\x12\x1b\n" + + "\ttier_code\x18\t \x01(\tR\btierCode\x12.\n" + + "\x13threshold_usd_minor\x18\n" + + " \x01(\x03R\x11thresholdUsdMinor\x12*\n" + + "\x11resource_group_id\x18\v \x01(\x03R\x0fresourceGroupId\x12*\n" + + "\x11reached_usd_minor\x18\f \x01(\x03R\x0freachedUsdMinor\x120\n" + + "\x14qualifying_usd_minor\x18\r \x01(\x03R\x12qualifyingUsdMinor\x120\n" + + "\x14recharge_coin_amount\x18\x0e \x01(\x03R\x12rechargeCoinAmount\x12#\n" + + "\rrecharge_type\x18\x0f \x01(\tR\frechargeType\x12\x16\n" + + "\x06status\x18\x10 \x01(\tR\x06status\x12*\n" + + "\x11wallet_command_id\x18\x11 \x01(\tR\x0fwalletCommandId\x12&\n" + + "\x0fwallet_grant_id\x18\x12 \x01(\tR\rwalletGrantId\x12%\n" + + "\x0efailure_reason\x18\x13 \x01(\tR\rfailureReason\x12\"\n" + + "\rgranted_at_ms\x18\x14 \x01(\x03R\vgrantedAtMs\x12\"\n" + + "\rcreated_at_ms\x18\x15 \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\x16 \x01(\x03R\vupdatedAtMs\"\xcf\x02\n" + + " CumulativeRechargeRewardProgress\x12\x19\n" + + "\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x1b\n" + + "\tcycle_key\x18\x02 \x01(\tR\bcycleKey\x12\x17\n" + + "\auser_id\x18\x03 \x01(\x03R\x06userId\x12&\n" + + "\x0ftotal_usd_minor\x18\x04 \x01(\x03R\rtotalUsdMinor\x12*\n" + + "\x11total_coin_amount\x18\x05 \x01(\x03R\x0ftotalCoinAmount\x121\n" + + "\x15first_recharged_at_ms\x18\x06 \x01(\x03R\x12firstRechargedAtMs\x12/\n" + + "\x14last_recharged_at_ms\x18\a \x01(\x03R\x11lastRechargedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\b \x01(\x03R\vupdatedAtMs\"\x95\x03\n" + + "\x1eCumulativeRechargeRewardStatus\x12I\n" + + "\x06config\x18\x01 \x01(\v21.hyapp.activity.v1.CumulativeRechargeRewardConfigR\x06config\x12O\n" + + "\bprogress\x18\x02 \x01(\v23.hyapp.activity.v1.CumulativeRechargeRewardProgressR\bprogress\x12H\n" + + "\x06grants\x18\x03 \x03(\v20.hyapp.activity.v1.CumulativeRechargeRewardGrantR\x06grants\x12\x1b\n" + + "\tcycle_key\x18\x04 \x01(\tR\bcycleKey\x12&\n" + + "\x0fperiod_start_ms\x18\x05 \x01(\x03R\rperiodStartMs\x12\"\n" + + "\rperiod_end_ms\x18\x06 \x01(\x03R\vperiodEndMs\x12$\n" + + "\x0eserver_time_ms\x18\a \x01(\x03R\fserverTimeMs\"w\n" + + "(GetCumulativeRechargeRewardStatusRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x17\n" + + "\auser_id\x18\x02 \x01(\x03R\x06userId\"v\n" + + ")GetCumulativeRechargeRewardStatusResponse\x12I\n" + + "\x06status\x18\x01 \x01(\v21.hyapp.activity.v1.CumulativeRechargeRewardStatusR\x06status\"\xd5\x03\n" + + "&ConsumeCumulativeRechargeRewardRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x19\n" + + "\bevent_id\x18\x02 \x01(\tR\aeventId\x12%\n" + + "\x0etransaction_id\x18\x03 \x01(\tR\rtransactionId\x12\x1d\n" + + "\n" + + "command_id\x18\x04 \x01(\tR\tcommandId\x12\x17\n" + + "\auser_id\x18\x05 \x01(\x03R\x06userId\x120\n" + + "\x14recharge_coin_amount\x18\x06 \x01(\x03R\x12rechargeCoinAmount\x12+\n" + + "\x11recharge_sequence\x18\a \x01(\x03R\x10rechargeSequence\x12#\n" + + "\rrecharge_type\x18\b \x01(\tR\frechargeType\x120\n" + + "\x14qualifying_usd_minor\x18\t \x01(\x03R\x12qualifyingUsdMinor\x12!\n" + + "\fpayload_json\x18\n" + + " \x01(\tR\vpayloadJson\x12$\n" + + "\x0eoccurred_at_ms\x18\v \x01(\x03R\foccurredAtMs\"\xa7\x01\n" + + "'ConsumeCumulativeRechargeRewardResponse\x12H\n" + + "\x06grants\x18\x01 \x03(\v20.hyapp.activity.v1.CumulativeRechargeRewardGrantR\x06grants\x12\x1a\n" + + "\bconsumed\x18\x02 \x01(\bR\bconsumed\x12\x16\n" + + "\x06reason\x18\x03 \x01(\tR\x06reason\"^\n" + + "(GetCumulativeRechargeRewardConfigRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\"v\n" + + ")GetCumulativeRechargeRewardConfigResponse\x12I\n" + + "\x06config\x18\x01 \x01(\v21.hyapp.activity.v1.CumulativeRechargeRewardConfigR\x06config\"\xee\x01\n" + + "+UpdateCumulativeRechargeRewardConfigRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x18\n" + + "\aenabled\x18\x02 \x01(\bR\aenabled\x12E\n" + + "\x05tiers\x18\x03 \x03(\v2/.hyapp.activity.v1.CumulativeRechargeRewardTierR\x05tiers\x12*\n" + + "\x11operator_admin_id\x18\x04 \x01(\x03R\x0foperatorAdminId\"y\n" + + ",UpdateCumulativeRechargeRewardConfigResponse\x12I\n" + + "\x06config\x18\x01 \x01(\v21.hyapp.activity.v1.CumulativeRechargeRewardConfigR\x06config\"\xde\x01\n" + + ")ListCumulativeRechargeRewardGrantsRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\x12\x17\n" + + "\auser_id\x18\x03 \x01(\x03R\x06userId\x12\x1b\n" + + "\tcycle_key\x18\x04 \x01(\tR\bcycleKey\x12\x12\n" + + "\x04page\x18\x05 \x01(\x05R\x04page\x12\x1b\n" + + "\tpage_size\x18\x06 \x01(\x05R\bpageSize\"\x8c\x01\n" + + "*ListCumulativeRechargeRewardGrantsResponse\x12H\n" + + "\x06grants\x18\x01 \x03(\v20.hyapp.activity.v1.CumulativeRechargeRewardGrantR\x06grants\x12\x14\n" + + "\x05total\x18\x02 \x01(\x03R\x05total\"\xca\x02\n" + + "\x16RoomTurnoverRewardTier\x12\x17\n" + + "\atier_id\x18\x01 \x01(\x03R\x06tierId\x12\x1b\n" + + "\ttier_code\x18\x02 \x01(\tR\btierCode\x12\x1b\n" + + "\ttier_name\x18\x03 \x01(\tR\btierName\x120\n" + + "\x14threshold_coin_spent\x18\x04 \x01(\x03R\x12thresholdCoinSpent\x12,\n" + + "\x12reward_coin_amount\x18\x05 \x01(\x03R\x10rewardCoinAmount\x12\x16\n" + + "\x06status\x18\x06 \x01(\tR\x06status\x12\x1d\n" + + "\n" + + "sort_order\x18\a \x01(\x05R\tsortOrder\x12\"\n" + + "\rcreated_at_ms\x18\b \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\t \x01(\x03R\vupdatedAtMs\"\x87\x02\n" + + "\x18RoomTurnoverRewardConfig\x12\x19\n" + + "\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x18\n" + + "\aenabled\x18\x02 \x01(\bR\aenabled\x12?\n" + + "\x05tiers\x18\x03 \x03(\v2).hyapp.activity.v1.RoomTurnoverRewardTierR\x05tiers\x12-\n" + + "\x13updated_by_admin_id\x18\x04 \x01(\x03R\x10updatedByAdminId\x12\"\n" + + "\rcreated_at_ms\x18\x05 \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\x06 \x01(\x03R\vupdatedAtMs\"\xa7\x05\n" + + "\x1cRoomTurnoverRewardSettlement\x12#\n" + + "\rsettlement_id\x18\x01 \x01(\tR\fsettlementId\x12\x19\n" + + "\bapp_code\x18\x02 \x01(\tR\aappCode\x12\x17\n" + + "\aroom_id\x18\x03 \x01(\tR\x06roomId\x12\"\n" + + "\rowner_user_id\x18\x04 \x01(\x03R\vownerUserId\x12&\n" + + "\x0fperiod_start_ms\x18\x05 \x01(\x03R\rperiodStartMs\x12\"\n" + + "\rperiod_end_ms\x18\x06 \x01(\x03R\vperiodEndMs\x12\x1d\n" + + "\n" + + "coin_spent\x18\a \x01(\x03R\tcoinSpent\x12\x17\n" + + "\atier_id\x18\b \x01(\x03R\x06tierId\x12\x1b\n" + + "\ttier_code\x18\t \x01(\tR\btierCode\x120\n" + + "\x14threshold_coin_spent\x18\n" + + " \x01(\x03R\x12thresholdCoinSpent\x12,\n" + + "\x12reward_coin_amount\x18\v \x01(\x03R\x10rewardCoinAmount\x12\x16\n" + + "\x06status\x18\f \x01(\tR\x06status\x12*\n" + + "\x11wallet_command_id\x18\r \x01(\tR\x0fwalletCommandId\x122\n" + + "\x15wallet_transaction_id\x18\x0e \x01(\tR\x13walletTransactionId\x12%\n" + + "\x0efailure_reason\x18\x0f \x01(\tR\rfailureReason\x12\"\n" + + "\rsettled_at_ms\x18\x10 \x01(\x03R\vsettledAtMs\x12\"\n" + + "\rcreated_at_ms\x18\x11 \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\x12 \x01(\x03R\vupdatedAtMs\"\xa7\x04\n" + + "\x18RoomTurnoverRewardStatus\x12C\n" + + "\x06config\x18\x01 \x01(\v2+.hyapp.activity.v1.RoomTurnoverRewardConfigR\x06config\x12\x17\n" + + "\aroom_id\x18\x02 \x01(\tR\x06roomId\x12\"\n" + + "\rowner_user_id\x18\x03 \x01(\x03R\vownerUserId\x12&\n" + + "\x0fperiod_start_ms\x18\x04 \x01(\x03R\rperiodStartMs\x12\"\n" + + "\rperiod_end_ms\x18\x05 \x01(\x03R\vperiodEndMs\x12,\n" + + "\x12current_coin_spent\x18\x06 \x01(\x03R\x10currentCoinSpent\x12=\n" + + "\x1bexpected_reward_coin_amount\x18\a \x01(\x03R\x18expectedRewardCoinAmount\x12L\n" + + "\fmatched_tier\x18\b \x01(\v2).hyapp.activity.v1.RoomTurnoverRewardTierR\vmatchedTier\x12\\\n" + + "\x11latest_settlement\x18\t \x01(\v2/.hyapp.activity.v1.RoomTurnoverRewardSettlementR\x10latestSettlement\x12$\n" + + "\x0eserver_time_ms\x18\n" + + " \x01(\x03R\fserverTimeMs\"\xae\x01\n" + + "\"GetRoomTurnoverRewardStatusRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x17\n" + + "\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x17\n" + + "\aroom_id\x18\x03 \x01(\tR\x06roomId\x12\"\n" + + "\rowner_user_id\x18\x04 \x01(\x03R\vownerUserId\"j\n" + + "#GetRoomTurnoverRewardStatusResponse\x12C\n" + + "\x06status\x18\x01 \x01(\v2+.hyapp.activity.v1.RoomTurnoverRewardStatusR\x06status\"X\n" + + "\"GetRoomTurnoverRewardConfigRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\"j\n" + + "#GetRoomTurnoverRewardConfigResponse\x12C\n" + + "\x06config\x18\x01 \x01(\v2+.hyapp.activity.v1.RoomTurnoverRewardConfigR\x06config\"\xe2\x01\n" + + "%UpdateRoomTurnoverRewardConfigRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x18\n" + + "\aenabled\x18\x02 \x01(\bR\aenabled\x12?\n" + + "\x05tiers\x18\x03 \x03(\v2).hyapp.activity.v1.RoomTurnoverRewardTierR\x05tiers\x12*\n" + + "\x11operator_admin_id\x18\x04 \x01(\x03R\x0foperatorAdminId\"m\n" + + "&UpdateRoomTurnoverRewardConfigResponse\x12C\n" + + "\x06config\x18\x01 \x01(\v2+.hyapp.activity.v1.RoomTurnoverRewardConfigR\x06config\"\x8c\x02\n" + + "(ListRoomTurnoverRewardSettlementsRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\x12\x17\n" + + "\aroom_id\x18\x03 \x01(\tR\x06roomId\x12\"\n" + + "\rowner_user_id\x18\x04 \x01(\x03R\vownerUserId\x12&\n" + + "\x0fperiod_start_ms\x18\x05 \x01(\x03R\rperiodStartMs\x12\x12\n" + + "\x04page\x18\x06 \x01(\x05R\x04page\x12\x1b\n" + + "\tpage_size\x18\a \x01(\x05R\bpageSize\"\x94\x01\n" + + ")ListRoomTurnoverRewardSettlementsResponse\x12Q\n" + + "\vsettlements\x18\x01 \x03(\v2/.hyapp.activity.v1.RoomTurnoverRewardSettlementR\vsettlements\x12\x14\n" + + "\x05total\x18\x02 \x01(\x03R\x05total\"\xaf\x01\n" + + "(RetryRoomTurnoverRewardSettlementRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12#\n" + + "\rsettlement_id\x18\x02 \x01(\tR\fsettlementId\x12*\n" + + "\x11operator_admin_id\x18\x03 \x01(\x03R\x0foperatorAdminId\"|\n" + + ")RetryRoomTurnoverRewardSettlementResponse\x12O\n" + + "\n" + + "settlement\x18\x01 \x01(\v2/.hyapp.activity.v1.RoomTurnoverRewardSettlementR\n" + + "settlement\"`\n" + "\x15SevenDayCheckInReward\x12\x1b\n" + "\tday_index\x18\x01 \x01(\x05R\bdayIndex\x12*\n" + "\x11resource_group_id\x18\x02 \x01(\x03R\x0fresourceGroupId\"\xae\x02\n" + @@ -13689,7 +17655,120 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" + "\x06status\x18\x05 \x01(\tR\x06status\x12\x17\n" + "\apool_id\x18\x06 \x01(\tR\x06poolId\"d\n" + "\x1fGetLuckyGiftDrawSummaryResponse\x12A\n" + - "\asummary\x18\x01 \x01(\v2'.hyapp.activity.v1.LuckyGiftDrawSummaryR\asummary2\xe2\x01\n" + + "\asummary\x18\x01 \x01(\v2'.hyapp.activity.v1.LuckyGiftDrawSummaryR\asummary\"H\n" + + "\x0eWeeklyStarGift\x12\x17\n" + + "\agift_id\x18\x01 \x01(\tR\x06giftId\x12\x1d\n" + + "\n" + + "sort_order\x18\x02 \x01(\x05R\tsortOrder\"W\n" + + "\x10WeeklyStarReward\x12\x17\n" + + "\arank_no\x18\x01 \x01(\x05R\x06rankNo\x12*\n" + + "\x11resource_group_id\x18\x02 \x01(\x03R\x0fresourceGroupId\"\xab\x04\n" + + "\x0fWeeklyStarCycle\x12\x19\n" + + "\bcycle_id\x18\x01 \x01(\tR\acycleId\x12#\n" + + "\ractivity_code\x18\x02 \x01(\tR\factivityCode\x12\x1b\n" + + "\tregion_id\x18\x03 \x01(\x03R\bregionId\x12\x14\n" + + "\x05title\x18\x04 \x01(\tR\x05title\x12\x16\n" + + "\x06status\x18\x05 \x01(\tR\x06status\x12\x19\n" + + "\bstart_ms\x18\x06 \x01(\x03R\astartMs\x12\x15\n" + + "\x06end_ms\x18\a \x01(\x03R\x05endMs\x127\n" + + "\x05gifts\x18\b \x03(\v2!.hyapp.activity.v1.WeeklyStarGiftR\x05gifts\x12=\n" + + "\arewards\x18\t \x03(\v2#.hyapp.activity.v1.WeeklyStarRewardR\arewards\x12-\n" + + "\x13created_by_admin_id\x18\n" + + " \x01(\x03R\x10createdByAdminId\x12-\n" + + "\x13updated_by_admin_id\x18\v \x01(\x03R\x10updatedByAdminId\x12\"\n" + + "\rsettled_at_ms\x18\f \x01(\x03R\vsettledAtMs\x12\"\n" + + "\rcreated_at_ms\x18\r \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\x0e \x01(\x03R\vupdatedAtMs\x12\x19\n" + + "\bapp_code\x18\x0f \x01(\tR\aappCode\"\xbc\x01\n" + + "\x1aWeeklyStarLeaderboardEntry\x12\x17\n" + + "\arank_no\x18\x01 \x01(\x05R\x06rankNo\x12\x17\n" + + "\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x14\n" + + "\x05score\x18\x03 \x01(\x03R\x05score\x12+\n" + + "\x12first_scored_at_ms\x18\x04 \x01(\x03R\x0ffirstScoredAtMs\x12)\n" + + "\x11last_scored_at_ms\x18\x05 \x01(\x03R\x0elastScoredAtMs\"\xca\x03\n" + + "\x14WeeklyStarSettlement\x12#\n" + + "\rsettlement_id\x18\x01 \x01(\tR\fsettlementId\x12\x19\n" + + "\bcycle_id\x18\x02 \x01(\tR\acycleId\x12\x17\n" + + "\arank_no\x18\x03 \x01(\x05R\x06rankNo\x12\x17\n" + + "\auser_id\x18\x04 \x01(\x03R\x06userId\x12\x14\n" + + "\x05score\x18\x05 \x01(\x03R\x05score\x12*\n" + + "\x11resource_group_id\x18\x06 \x01(\x03R\x0fresourceGroupId\x12*\n" + + "\x11wallet_command_id\x18\a \x01(\tR\x0fwalletCommandId\x12&\n" + + "\x0fwallet_grant_id\x18\b \x01(\tR\rwalletGrantId\x12\x16\n" + + "\x06status\x18\t \x01(\tR\x06status\x12%\n" + + "\x0efailure_reason\x18\n" + + " \x01(\tR\rfailureReason\x12#\n" + + "\rattempt_count\x18\v \x01(\x05R\fattemptCount\x12\"\n" + + "\rcreated_at_ms\x18\f \x01(\x03R\vcreatedAtMs\x12\"\n" + + "\rupdated_at_ms\x18\r \x01(\x03R\vupdatedAtMs\"\xe9\x01\n" + + "\x1bListWeeklyStarCyclesRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x1b\n" + + "\tregion_id\x18\x02 \x01(\x03R\bregionId\x12\x16\n" + + "\x06status\x18\x03 \x01(\tR\x06status\x12\x19\n" + + "\bstart_ms\x18\x04 \x01(\x03R\astartMs\x12\x15\n" + + "\x06end_ms\x18\x05 \x01(\x03R\x05endMs\x12\x12\n" + + "\x04page\x18\x06 \x01(\x05R\x04page\x12\x1b\n" + + "\tpage_size\x18\a \x01(\x05R\bpageSize\"p\n" + + "\x1cListWeeklyStarCyclesResponse\x12:\n" + + "\x06cycles\x18\x01 \x03(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x06cycles\x12\x14\n" + + "\x05total\x18\x02 \x01(\x03R\x05total\"j\n" + + "\x19GetWeeklyStarCycleRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x19\n" + + "\bcycle_id\x18\x02 \x01(\tR\acycleId\"V\n" + + "\x1aGetWeeklyStarCycleResponse\x128\n" + + "\x05cycle\x18\x01 \x01(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x05cycle\"\xb8\x01\n" + + "\x1cUpsertWeeklyStarCycleRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x128\n" + + "\x05cycle\x18\x02 \x01(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x05cycle\x12*\n" + + "\x11operator_admin_id\x18\x03 \x01(\x03R\x0foperatorAdminId\"s\n" + + "\x1dUpsertWeeklyStarCycleResponse\x128\n" + + "\x05cycle\x18\x01 \x01(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x05cycle\x12\x18\n" + + "\acreated\x18\x02 \x01(\bR\acreated\"\xb4\x01\n" + + "\x1fSetWeeklyStarCycleStatusRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x19\n" + + "\bcycle_id\x18\x02 \x01(\tR\acycleId\x12\x16\n" + + "\x06status\x18\x03 \x01(\tR\x06status\x12*\n" + + "\x11operator_admin_id\x18\x04 \x01(\x03R\x0foperatorAdminId\"\\\n" + + " SetWeeklyStarCycleStatusResponse\x128\n" + + "\x05cycle\x18\x01 \x01(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x05cycle\"\xca\x01\n" + + " ListWeeklyStarLeaderboardRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x19\n" + + "\bcycle_id\x18\x02 \x01(\tR\acycleId\x12\x1b\n" + + "\tregion_id\x18\x03 \x01(\x03R\bregionId\x12\x1b\n" + + "\tpage_size\x18\x04 \x01(\x05R\bpageSize\x12\x1d\n" + + "\n" + + "page_token\x18\x05 \x01(\tR\tpageToken\"\xe4\x01\n" + + "!ListWeeklyStarLeaderboardResponse\x128\n" + + "\x05cycle\x18\x01 \x01(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x05cycle\x12G\n" + + "\aentries\x18\x02 \x03(\v2-.hyapp.activity.v1.WeeklyStarLeaderboardEntryR\aentries\x12&\n" + + "\x0fnext_page_token\x18\x03 \x01(\tR\rnextPageToken\x12\x14\n" + + "\x05total\x18\x04 \x01(\x03R\x05total\"q\n" + + " ListWeeklyStarSettlementsRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x19\n" + + "\bcycle_id\x18\x02 \x01(\tR\acycleId\"n\n" + + "!ListWeeklyStarSettlementsResponse\x12I\n" + + "\vsettlements\x18\x01 \x03(\v2'.hyapp.activity.v1.WeeklyStarSettlementR\vsettlements\"\x87\x01\n" + + "\x1bGetWeeklyStarCurrentRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x17\n" + + "\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x1b\n" + + "\tregion_id\x18\x03 \x01(\x03R\bregionId\"\x98\x02\n" + + "\x1cGetWeeklyStarCurrentResponse\x128\n" + + "\x05cycle\x18\x01 \x01(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x05cycle\x12N\n" + + "\vtop_entries\x18\x02 \x03(\v2-.hyapp.activity.v1.WeeklyStarLeaderboardEntryR\n" + + "topEntries\x12H\n" + + "\bmy_entry\x18\x03 \x01(\v2-.hyapp.activity.v1.WeeklyStarLeaderboardEntryR\amyEntry\x12$\n" + + "\x0eserver_time_ms\x18\x04 \x01(\x03R\fserverTimeMs\"\x85\x01\n" + + "\x1cListWeeklyStarHistoryRequest\x122\n" + + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x1b\n" + + "\tregion_id\x18\x02 \x01(\x03R\bregionId\x12\x14\n" + + "\x05limit\x18\x03 \x01(\x05R\x05limit\"\xa2\x01\n" + + "\x16WeeklyStarHistoryCycle\x128\n" + + "\x05cycle\x18\x01 \x01(\v2\".hyapp.activity.v1.WeeklyStarCycleR\x05cycle\x12N\n" + + "\vtop_entries\x18\x02 \x03(\v2-.hyapp.activity.v1.WeeklyStarLeaderboardEntryR\n" + + "topEntries\"\x88\x01\n" + + "\x1dListWeeklyStarHistoryResponse\x12A\n" + + "\x06cycles\x18\x01 \x03(\v2).hyapp.activity.v1.WeeklyStarHistoryCycleR\x06cycles\x12$\n" + + "\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs2\xe2\x01\n" + "\x0fActivityService\x12_\n" + "\fPingActivity\x12&.hyapp.activity.v1.PingActivityRequest\x1a'.hyapp.activity.v1.PingActivityResponse\x12n\n" + "\x11GetActivityStatus\x12+.hyapp.activity.v1.GetActivityStatusRequest\x1a,.hyapp.activity.v1.GetActivityStatusResponse2\xb1\x06\n" + @@ -13700,11 +17779,13 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" + "\x14MarkInboxSectionRead\x12..hyapp.activity.v1.MarkInboxSectionReadRequest\x1a/.hyapp.activity.v1.MarkInboxSectionReadResponse\x12q\n" + "\x12DeleteInboxMessage\x12,.hyapp.activity.v1.DeleteInboxMessageRequest\x1a-.hyapp.activity.v1.DeleteInboxMessageResponse\x12q\n" + "\x12CreateInboxMessage\x12,.hyapp.activity.v1.CreateInboxMessageRequest\x1a-.hyapp.activity.v1.CreateInboxMessageResponse\x12h\n" + - "\x0fCreateFanoutJob\x12).hyapp.activity.v1.CreateFanoutJobRequest\x1a*.hyapp.activity.v1.CreateFanoutJobResponse2\xcf\x02\n" + + "\x0fCreateFanoutJob\x12).hyapp.activity.v1.CreateFanoutJobRequest\x1a*.hyapp.activity.v1.CreateFanoutJobResponse2\xb5\x04\n" + "\x13ActivityCronService\x12f\n" + "\x19ProcessMessageFanoutBatch\x12#.hyapp.activity.v1.CronBatchRequest\x1a$.hyapp.activity.v1.CronBatchResponse\x12d\n" + "\x17ProcessLevelRewardBatch\x12#.hyapp.activity.v1.CronBatchRequest\x1a$.hyapp.activity.v1.CronBatchResponse\x12j\n" + - "\x1dProcessAchievementRewardBatch\x12#.hyapp.activity.v1.CronBatchRequest\x1a$.hyapp.activity.v1.CronBatchResponse2\xc8\x02\n" + + "\x1dProcessAchievementRewardBatch\x12#.hyapp.activity.v1.CronBatchRequest\x1a$.hyapp.activity.v1.CronBatchResponse\x12u\n" + + "(ProcessRoomTurnoverRewardSettlementBatch\x12#.hyapp.activity.v1.CronBatchRequest\x1a$.hyapp.activity.v1.CronBatchResponse\x12m\n" + + " ProcessWeeklyStarSettlementBatch\x12#.hyapp.activity.v1.CronBatchRequest\x1a$.hyapp.activity.v1.CronBatchResponse2\xc8\x02\n" + "\vTaskService\x12b\n" + "\rListUserTasks\x12'.hyapp.activity.v1.ListUserTasksRequest\x1a(.hyapp.activity.v1.ListUserTasksResponse\x12h\n" + "\x0fClaimTaskReward\x12).hyapp.activity.v1.ClaimTaskRewardRequest\x1a*.hyapp.activity.v1.ClaimTaskRewardResponse\x12k\n" + @@ -13723,7 +17804,13 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" + "\x0fSetBadgeDisplay\x12).hyapp.activity.v1.SetBadgeDisplayRequest\x1a*.hyapp.activity.v1.SetBadgeDisplayResponse2\xf2\x01\n" + "\x10LuckyGiftService\x12e\n" + "\x0eCheckLuckyGift\x12(.hyapp.activity.v1.CheckLuckyGiftRequest\x1a).hyapp.activity.v1.CheckLuckyGiftResponse\x12w\n" + - "\x14ExecuteLuckyGiftDraw\x12..hyapp.activity.v1.ExecuteLuckyGiftDrawRequest\x1a/.hyapp.activity.v1.ExecuteLuckyGiftDrawResponse2\x8b\x05\n" + + "\x14ExecuteLuckyGiftDraw\x12..hyapp.activity.v1.ExecuteLuckyGiftDrawRequest\x1a/.hyapp.activity.v1.ExecuteLuckyGiftDrawResponse2\xaa\x01\n" + + "\x19RoomTurnoverRewardService\x12\x8c\x01\n" + + "\x1bGetRoomTurnoverRewardStatus\x125.hyapp.activity.v1.GetRoomTurnoverRewardStatusRequest\x1a6.hyapp.activity.v1.GetRoomTurnoverRewardStatusResponse2\x91\x03\n" + + "\x11WeeklyStarService\x12w\n" + + "\x14GetWeeklyStarCurrent\x12..hyapp.activity.v1.GetWeeklyStarCurrentRequest\x1a/.hyapp.activity.v1.GetWeeklyStarCurrentResponse\x12\x86\x01\n" + + "\x19ListWeeklyStarLeaderboard\x123.hyapp.activity.v1.ListWeeklyStarLeaderboardRequest\x1a4.hyapp.activity.v1.ListWeeklyStarLeaderboardResponse\x12z\n" + + "\x15ListWeeklyStarHistory\x12/.hyapp.activity.v1.ListWeeklyStarHistoryRequest\x1a0.hyapp.activity.v1.ListWeeklyStarHistoryResponse2\x8b\x05\n" + "\x10BroadcastService\x12z\n" + "\x15EnsureBroadcastGroups\x12/.hyapp.activity.v1.EnsureBroadcastGroupsRequest\x1a0.hyapp.activity.v1.EnsureBroadcastGroupsResponse\x12w\n" + "\x16PublishRegionBroadcast\x120.hyapp.activity.v1.PublishRegionBroadcastRequest\x1a+.hyapp.activity.v1.PublishBroadcastResponse\x12w\n" + @@ -13749,7 +17836,27 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" + "\x1fAdminFirstRechargeRewardService\x12\x8f\x01\n" + "\x1cGetFirstRechargeRewardConfig\x126.hyapp.activity.v1.GetFirstRechargeRewardConfigRequest\x1a7.hyapp.activity.v1.GetFirstRechargeRewardConfigResponse\x12\x98\x01\n" + "\x1fUpdateFirstRechargeRewardConfig\x129.hyapp.activity.v1.UpdateFirstRechargeRewardConfigRequest\x1a:.hyapp.activity.v1.UpdateFirstRechargeRewardConfigResponse\x12\x92\x01\n" + - "\x1dListFirstRechargeRewardClaims\x127.hyapp.activity.v1.ListFirstRechargeRewardClaimsRequest\x1a8.hyapp.activity.v1.ListFirstRechargeRewardClaimsResponse2\x94\x02\n" + + "\x1dListFirstRechargeRewardClaims\x127.hyapp.activity.v1.ListFirstRechargeRewardClaimsRequest\x1a8.hyapp.activity.v1.ListFirstRechargeRewardClaimsResponse2\xdd\x02\n" + + "\x1fCumulativeRechargeRewardService\x12\x9e\x01\n" + + "!GetCumulativeRechargeRewardStatus\x12;.hyapp.activity.v1.GetCumulativeRechargeRewardStatusRequest\x1a<.hyapp.activity.v1.GetCumulativeRechargeRewardStatusResponse\x12\x98\x01\n" + + "\x1fConsumeCumulativeRechargeReward\x129.hyapp.activity.v1.ConsumeCumulativeRechargeRewardRequest\x1a:.hyapp.activity.v1.ConsumeCumulativeRechargeRewardResponse2\x95\x04\n" + + "$AdminCumulativeRechargeRewardService\x12\x9e\x01\n" + + "!GetCumulativeRechargeRewardConfig\x12;.hyapp.activity.v1.GetCumulativeRechargeRewardConfigRequest\x1a<.hyapp.activity.v1.GetCumulativeRechargeRewardConfigResponse\x12\xa7\x01\n" + + "$UpdateCumulativeRechargeRewardConfig\x12>.hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigRequest\x1a?.hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigResponse\x12\xa1\x01\n" + + "\"ListCumulativeRechargeRewardGrants\x12<.hyapp.activity.v1.ListCumulativeRechargeRewardGrantsRequest\x1a=.hyapp.activity.v1.ListCumulativeRechargeRewardGrantsResponse2\x89\x05\n" + + "\x1eAdminRoomTurnoverRewardService\x12\x8c\x01\n" + + "\x1bGetRoomTurnoverRewardConfig\x125.hyapp.activity.v1.GetRoomTurnoverRewardConfigRequest\x1a6.hyapp.activity.v1.GetRoomTurnoverRewardConfigResponse\x12\x95\x01\n" + + "\x1eUpdateRoomTurnoverRewardConfig\x128.hyapp.activity.v1.UpdateRoomTurnoverRewardConfigRequest\x1a9.hyapp.activity.v1.UpdateRoomTurnoverRewardConfigResponse\x12\x9e\x01\n" + + "!ListRoomTurnoverRewardSettlements\x12;.hyapp.activity.v1.ListRoomTurnoverRewardSettlementsRequest\x1a<.hyapp.activity.v1.ListRoomTurnoverRewardSettlementsResponse\x12\x9e\x01\n" + + "!RetryRoomTurnoverRewardSettlement\x12;.hyapp.activity.v1.RetryRoomTurnoverRewardSettlementRequest\x1a<.hyapp.activity.v1.RetryRoomTurnoverRewardSettlementResponse2\x94\a\n" + + "\x16AdminWeeklyStarService\x12w\n" + + "\x14ListWeeklyStarCycles\x12..hyapp.activity.v1.ListWeeklyStarCyclesRequest\x1a/.hyapp.activity.v1.ListWeeklyStarCyclesResponse\x12z\n" + + "\x15CreateWeeklyStarCycle\x12/.hyapp.activity.v1.UpsertWeeklyStarCycleRequest\x1a0.hyapp.activity.v1.UpsertWeeklyStarCycleResponse\x12q\n" + + "\x12GetWeeklyStarCycle\x12,.hyapp.activity.v1.GetWeeklyStarCycleRequest\x1a-.hyapp.activity.v1.GetWeeklyStarCycleResponse\x12z\n" + + "\x15UpdateWeeklyStarCycle\x12/.hyapp.activity.v1.UpsertWeeklyStarCycleRequest\x1a0.hyapp.activity.v1.UpsertWeeklyStarCycleResponse\x12\x83\x01\n" + + "\x18SetWeeklyStarCycleStatus\x122.hyapp.activity.v1.SetWeeklyStarCycleStatusRequest\x1a3.hyapp.activity.v1.SetWeeklyStarCycleStatusResponse\x12\x86\x01\n" + + "\x19ListWeeklyStarLeaderboard\x123.hyapp.activity.v1.ListWeeklyStarLeaderboardRequest\x1a4.hyapp.activity.v1.ListWeeklyStarLeaderboardResponse\x12\x86\x01\n" + + "\x19ListWeeklyStarSettlements\x123.hyapp.activity.v1.ListWeeklyStarSettlementsRequest\x1a4.hyapp.activity.v1.ListWeeklyStarSettlementsResponse2\x94\x02\n" + "\x16SevenDayCheckInService\x12\x83\x01\n" + "\x18GetSevenDayCheckInStatus\x122.hyapp.activity.v1.GetSevenDayCheckInStatusRequest\x1a3.hyapp.activity.v1.GetSevenDayCheckInStatusResponse\x12t\n" + "\x13SignSevenDayCheckIn\x12-.hyapp.activity.v1.SignSevenDayCheckInRequest\x1a..hyapp.activity.v1.SignSevenDayCheckInResponse2\xbb\x03\n" + @@ -13784,163 +17891,214 @@ func file_proto_activity_v1_activity_proto_rawDescGZIP() []byte { return file_proto_activity_v1_activity_proto_rawDescData } -var file_proto_activity_v1_activity_proto_msgTypes = make([]protoimpl.MessageInfo, 154) +var file_proto_activity_v1_activity_proto_msgTypes = make([]protoimpl.MessageInfo, 205) var file_proto_activity_v1_activity_proto_goTypes = []any{ - (*RequestMeta)(nil), // 0: hyapp.activity.v1.RequestMeta - (*PingActivityRequest)(nil), // 1: hyapp.activity.v1.PingActivityRequest - (*PingActivityResponse)(nil), // 2: hyapp.activity.v1.PingActivityResponse - (*GetActivityStatusRequest)(nil), // 3: hyapp.activity.v1.GetActivityStatusRequest - (*GetActivityStatusResponse)(nil), // 4: hyapp.activity.v1.GetActivityStatusResponse - (*MessageTabSection)(nil), // 5: hyapp.activity.v1.MessageTabSection - (*ListMessageTabsRequest)(nil), // 6: hyapp.activity.v1.ListMessageTabsRequest - (*ListMessageTabsResponse)(nil), // 7: hyapp.activity.v1.ListMessageTabsResponse - (*InboxMessage)(nil), // 8: hyapp.activity.v1.InboxMessage - (*ListInboxMessagesRequest)(nil), // 9: hyapp.activity.v1.ListInboxMessagesRequest - (*ListInboxMessagesResponse)(nil), // 10: hyapp.activity.v1.ListInboxMessagesResponse - (*MarkInboxMessageReadRequest)(nil), // 11: hyapp.activity.v1.MarkInboxMessageReadRequest - (*MarkInboxMessageReadResponse)(nil), // 12: hyapp.activity.v1.MarkInboxMessageReadResponse - (*MarkInboxSectionReadRequest)(nil), // 13: hyapp.activity.v1.MarkInboxSectionReadRequest - (*MarkInboxSectionReadResponse)(nil), // 14: hyapp.activity.v1.MarkInboxSectionReadResponse - (*DeleteInboxMessageRequest)(nil), // 15: hyapp.activity.v1.DeleteInboxMessageRequest - (*DeleteInboxMessageResponse)(nil), // 16: hyapp.activity.v1.DeleteInboxMessageResponse - (*CreateInboxMessageRequest)(nil), // 17: hyapp.activity.v1.CreateInboxMessageRequest - (*CreateInboxMessageResponse)(nil), // 18: hyapp.activity.v1.CreateInboxMessageResponse - (*CreateFanoutJobRequest)(nil), // 19: hyapp.activity.v1.CreateFanoutJobRequest - (*CreateFanoutJobResponse)(nil), // 20: hyapp.activity.v1.CreateFanoutJobResponse - (*CronBatchRequest)(nil), // 21: hyapp.activity.v1.CronBatchRequest - (*CronBatchResponse)(nil), // 22: hyapp.activity.v1.CronBatchResponse - (*TaskItem)(nil), // 23: hyapp.activity.v1.TaskItem - (*TaskSection)(nil), // 24: hyapp.activity.v1.TaskSection - (*ListUserTasksRequest)(nil), // 25: hyapp.activity.v1.ListUserTasksRequest - (*ListUserTasksResponse)(nil), // 26: hyapp.activity.v1.ListUserTasksResponse - (*ClaimTaskRewardRequest)(nil), // 27: hyapp.activity.v1.ClaimTaskRewardRequest - (*ClaimTaskRewardResponse)(nil), // 28: hyapp.activity.v1.ClaimTaskRewardResponse - (*ConsumeTaskEventRequest)(nil), // 29: hyapp.activity.v1.ConsumeTaskEventRequest - (*ConsumeTaskEventResponse)(nil), // 30: hyapp.activity.v1.ConsumeTaskEventResponse - (*BroadcastJoinGroup)(nil), // 31: hyapp.activity.v1.BroadcastJoinGroup - (*EnsureBroadcastGroupsRequest)(nil), // 32: hyapp.activity.v1.EnsureBroadcastGroupsRequest - (*EnsureBroadcastGroupsResponse)(nil), // 33: hyapp.activity.v1.EnsureBroadcastGroupsResponse - (*PublishRegionBroadcastRequest)(nil), // 34: hyapp.activity.v1.PublishRegionBroadcastRequest - (*PublishGlobalBroadcastRequest)(nil), // 35: hyapp.activity.v1.PublishGlobalBroadcastRequest - (*PublishBroadcastResponse)(nil), // 36: hyapp.activity.v1.PublishBroadcastResponse - (*RemoveRegionBroadcastMemberRequest)(nil), // 37: hyapp.activity.v1.RemoveRegionBroadcastMemberRequest - (*RemoveRegionBroadcastMemberResponse)(nil), // 38: hyapp.activity.v1.RemoveRegionBroadcastMemberResponse - (*ProcessBroadcastOutboxBatchResponse)(nil), // 39: hyapp.activity.v1.ProcessBroadcastOutboxBatchResponse - (*ConsumeRoomEventRequest)(nil), // 40: hyapp.activity.v1.ConsumeRoomEventRequest - (*ConsumeRoomEventResponse)(nil), // 41: hyapp.activity.v1.ConsumeRoomEventResponse - (*TaskDefinition)(nil), // 42: hyapp.activity.v1.TaskDefinition - (*ListTaskDefinitionsRequest)(nil), // 43: hyapp.activity.v1.ListTaskDefinitionsRequest - (*ListTaskDefinitionsResponse)(nil), // 44: hyapp.activity.v1.ListTaskDefinitionsResponse - (*UpsertTaskDefinitionRequest)(nil), // 45: hyapp.activity.v1.UpsertTaskDefinitionRequest - (*UpsertTaskDefinitionResponse)(nil), // 46: hyapp.activity.v1.UpsertTaskDefinitionResponse - (*SetTaskDefinitionStatusRequest)(nil), // 47: hyapp.activity.v1.SetTaskDefinitionStatusRequest - (*SetTaskDefinitionStatusResponse)(nil), // 48: hyapp.activity.v1.SetTaskDefinitionStatusResponse - (*RegistrationRewardConfig)(nil), // 49: hyapp.activity.v1.RegistrationRewardConfig - (*RegistrationRewardClaim)(nil), // 50: hyapp.activity.v1.RegistrationRewardClaim - (*RegistrationRewardEligibility)(nil), // 51: hyapp.activity.v1.RegistrationRewardEligibility - (*GetRegistrationRewardEligibilityRequest)(nil), // 52: hyapp.activity.v1.GetRegistrationRewardEligibilityRequest - (*GetRegistrationRewardEligibilityResponse)(nil), // 53: hyapp.activity.v1.GetRegistrationRewardEligibilityResponse - (*IssueRegistrationRewardRequest)(nil), // 54: hyapp.activity.v1.IssueRegistrationRewardRequest - (*IssueRegistrationRewardResponse)(nil), // 55: hyapp.activity.v1.IssueRegistrationRewardResponse - (*GetRegistrationRewardConfigRequest)(nil), // 56: hyapp.activity.v1.GetRegistrationRewardConfigRequest - (*GetRegistrationRewardConfigResponse)(nil), // 57: hyapp.activity.v1.GetRegistrationRewardConfigResponse - (*UpdateRegistrationRewardConfigRequest)(nil), // 58: hyapp.activity.v1.UpdateRegistrationRewardConfigRequest - (*UpdateRegistrationRewardConfigResponse)(nil), // 59: hyapp.activity.v1.UpdateRegistrationRewardConfigResponse - (*ListRegistrationRewardClaimsRequest)(nil), // 60: hyapp.activity.v1.ListRegistrationRewardClaimsRequest - (*ListRegistrationRewardClaimsResponse)(nil), // 61: hyapp.activity.v1.ListRegistrationRewardClaimsResponse - (*FirstRechargeRewardTier)(nil), // 62: hyapp.activity.v1.FirstRechargeRewardTier - (*FirstRechargeRewardConfig)(nil), // 63: hyapp.activity.v1.FirstRechargeRewardConfig - (*FirstRechargeRewardClaim)(nil), // 64: hyapp.activity.v1.FirstRechargeRewardClaim - (*FirstRechargeRewardStatus)(nil), // 65: hyapp.activity.v1.FirstRechargeRewardStatus - (*GetFirstRechargeRewardStatusRequest)(nil), // 66: hyapp.activity.v1.GetFirstRechargeRewardStatusRequest - (*GetFirstRechargeRewardStatusResponse)(nil), // 67: hyapp.activity.v1.GetFirstRechargeRewardStatusResponse - (*ConsumeFirstRechargeRewardRequest)(nil), // 68: hyapp.activity.v1.ConsumeFirstRechargeRewardRequest - (*ConsumeFirstRechargeRewardResponse)(nil), // 69: hyapp.activity.v1.ConsumeFirstRechargeRewardResponse - (*GetFirstRechargeRewardConfigRequest)(nil), // 70: hyapp.activity.v1.GetFirstRechargeRewardConfigRequest - (*GetFirstRechargeRewardConfigResponse)(nil), // 71: hyapp.activity.v1.GetFirstRechargeRewardConfigResponse - (*UpdateFirstRechargeRewardConfigRequest)(nil), // 72: hyapp.activity.v1.UpdateFirstRechargeRewardConfigRequest - (*UpdateFirstRechargeRewardConfigResponse)(nil), // 73: hyapp.activity.v1.UpdateFirstRechargeRewardConfigResponse - (*ListFirstRechargeRewardClaimsRequest)(nil), // 74: hyapp.activity.v1.ListFirstRechargeRewardClaimsRequest - (*ListFirstRechargeRewardClaimsResponse)(nil), // 75: hyapp.activity.v1.ListFirstRechargeRewardClaimsResponse - (*SevenDayCheckInReward)(nil), // 76: hyapp.activity.v1.SevenDayCheckInReward - (*SevenDayCheckInConfig)(nil), // 77: hyapp.activity.v1.SevenDayCheckInConfig - (*SevenDayCheckInRewardStatus)(nil), // 78: hyapp.activity.v1.SevenDayCheckInRewardStatus - (*GetSevenDayCheckInStatusRequest)(nil), // 79: hyapp.activity.v1.GetSevenDayCheckInStatusRequest - (*GetSevenDayCheckInStatusResponse)(nil), // 80: hyapp.activity.v1.GetSevenDayCheckInStatusResponse - (*SignSevenDayCheckInRequest)(nil), // 81: hyapp.activity.v1.SignSevenDayCheckInRequest - (*SignSevenDayCheckInResponse)(nil), // 82: hyapp.activity.v1.SignSevenDayCheckInResponse - (*GetSevenDayCheckInConfigRequest)(nil), // 83: hyapp.activity.v1.GetSevenDayCheckInConfigRequest - (*GetSevenDayCheckInConfigResponse)(nil), // 84: hyapp.activity.v1.GetSevenDayCheckInConfigResponse - (*UpdateSevenDayCheckInConfigRequest)(nil), // 85: hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest - (*UpdateSevenDayCheckInConfigResponse)(nil), // 86: hyapp.activity.v1.UpdateSevenDayCheckInConfigResponse - (*SevenDayCheckInClaim)(nil), // 87: hyapp.activity.v1.SevenDayCheckInClaim - (*ListSevenDayCheckInClaimsRequest)(nil), // 88: hyapp.activity.v1.ListSevenDayCheckInClaimsRequest - (*ListSevenDayCheckInClaimsResponse)(nil), // 89: hyapp.activity.v1.ListSevenDayCheckInClaimsResponse - (*LevelTrack)(nil), // 90: hyapp.activity.v1.LevelTrack - (*LevelRule)(nil), // 91: hyapp.activity.v1.LevelRule - (*LevelTier)(nil), // 92: hyapp.activity.v1.LevelTier - (*LevelTrackOverview)(nil), // 93: hyapp.activity.v1.LevelTrackOverview - (*GetMyLevelOverviewRequest)(nil), // 94: hyapp.activity.v1.GetMyLevelOverviewRequest - (*GetMyLevelOverviewResponse)(nil), // 95: hyapp.activity.v1.GetMyLevelOverviewResponse - (*GetLevelTrackRequest)(nil), // 96: hyapp.activity.v1.GetLevelTrackRequest - (*GetLevelTrackResponse)(nil), // 97: hyapp.activity.v1.GetLevelTrackResponse - (*LevelDisplayTrackProfile)(nil), // 98: hyapp.activity.v1.LevelDisplayTrackProfile - (*UserLevelDisplayProfile)(nil), // 99: hyapp.activity.v1.UserLevelDisplayProfile - (*BatchGetUserLevelDisplayProfilesRequest)(nil), // 100: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesRequest - (*BatchGetUserLevelDisplayProfilesResponse)(nil), // 101: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesResponse - (*LevelRewardJob)(nil), // 102: hyapp.activity.v1.LevelRewardJob - (*ListLevelRewardsRequest)(nil), // 103: hyapp.activity.v1.ListLevelRewardsRequest - (*ListLevelRewardsResponse)(nil), // 104: hyapp.activity.v1.ListLevelRewardsResponse - (*ConsumeLevelEventRequest)(nil), // 105: hyapp.activity.v1.ConsumeLevelEventRequest - (*ConsumeLevelEventResponse)(nil), // 106: hyapp.activity.v1.ConsumeLevelEventResponse - (*RegistrationLevelBadgeGrant)(nil), // 107: hyapp.activity.v1.RegistrationLevelBadgeGrant - (*IssueRegistrationLevelBadgesRequest)(nil), // 108: hyapp.activity.v1.IssueRegistrationLevelBadgesRequest - (*IssueRegistrationLevelBadgesResponse)(nil), // 109: hyapp.activity.v1.IssueRegistrationLevelBadgesResponse - (*UpsertLevelTrackRequest)(nil), // 110: hyapp.activity.v1.UpsertLevelTrackRequest - (*UpsertLevelTrackResponse)(nil), // 111: hyapp.activity.v1.UpsertLevelTrackResponse - (*UpsertLevelRuleRequest)(nil), // 112: hyapp.activity.v1.UpsertLevelRuleRequest - (*UpsertLevelRuleResponse)(nil), // 113: hyapp.activity.v1.UpsertLevelRuleResponse - (*UpsertLevelTierRequest)(nil), // 114: hyapp.activity.v1.UpsertLevelTierRequest - (*UpsertLevelTierResponse)(nil), // 115: hyapp.activity.v1.UpsertLevelTierResponse - (*ListLevelConfigRequest)(nil), // 116: hyapp.activity.v1.ListLevelConfigRequest - (*ListLevelConfigResponse)(nil), // 117: hyapp.activity.v1.ListLevelConfigResponse - (*AchievementCondition)(nil), // 118: hyapp.activity.v1.AchievementCondition - (*AchievementDefinition)(nil), // 119: hyapp.activity.v1.AchievementDefinition - (*UserAchievement)(nil), // 120: hyapp.activity.v1.UserAchievement - (*ListAchievementsRequest)(nil), // 121: hyapp.activity.v1.ListAchievementsRequest - (*ListAchievementsResponse)(nil), // 122: hyapp.activity.v1.ListAchievementsResponse - (*ConsumeAchievementEventRequest)(nil), // 123: hyapp.activity.v1.ConsumeAchievementEventRequest - (*ConsumeAchievementEventResponse)(nil), // 124: hyapp.activity.v1.ConsumeAchievementEventResponse - (*BadgeDisplayItem)(nil), // 125: hyapp.activity.v1.BadgeDisplayItem - (*ListMyBadgesRequest)(nil), // 126: hyapp.activity.v1.ListMyBadgesRequest - (*ListMyBadgesResponse)(nil), // 127: hyapp.activity.v1.ListMyBadgesResponse - (*SetBadgeDisplayRequest)(nil), // 128: hyapp.activity.v1.SetBadgeDisplayRequest - (*SetBadgeDisplayResponse)(nil), // 129: hyapp.activity.v1.SetBadgeDisplayResponse - (*UpsertAchievementDefinitionRequest)(nil), // 130: hyapp.activity.v1.UpsertAchievementDefinitionRequest - (*UpsertAchievementDefinitionResponse)(nil), // 131: hyapp.activity.v1.UpsertAchievementDefinitionResponse - (*LuckyGiftMeta)(nil), // 132: hyapp.activity.v1.LuckyGiftMeta - (*LuckyGiftTier)(nil), // 133: hyapp.activity.v1.LuckyGiftTier - (*LuckyGiftConfig)(nil), // 134: hyapp.activity.v1.LuckyGiftConfig - (*LuckyGiftRuleTier)(nil), // 135: hyapp.activity.v1.LuckyGiftRuleTier - (*LuckyGiftRuleStage)(nil), // 136: hyapp.activity.v1.LuckyGiftRuleStage - (*LuckyGiftRuleConfig)(nil), // 137: hyapp.activity.v1.LuckyGiftRuleConfig - (*CheckLuckyGiftRequest)(nil), // 138: hyapp.activity.v1.CheckLuckyGiftRequest - (*CheckLuckyGiftResponse)(nil), // 139: hyapp.activity.v1.CheckLuckyGiftResponse - (*LuckyGiftDrawResult)(nil), // 140: hyapp.activity.v1.LuckyGiftDrawResult - (*ExecuteLuckyGiftDrawRequest)(nil), // 141: hyapp.activity.v1.ExecuteLuckyGiftDrawRequest - (*ExecuteLuckyGiftDrawResponse)(nil), // 142: hyapp.activity.v1.ExecuteLuckyGiftDrawResponse - (*GetLuckyGiftConfigRequest)(nil), // 143: hyapp.activity.v1.GetLuckyGiftConfigRequest - (*GetLuckyGiftConfigResponse)(nil), // 144: hyapp.activity.v1.GetLuckyGiftConfigResponse - (*UpsertLuckyGiftConfigRequest)(nil), // 145: hyapp.activity.v1.UpsertLuckyGiftConfigRequest - (*UpsertLuckyGiftConfigResponse)(nil), // 146: hyapp.activity.v1.UpsertLuckyGiftConfigResponse - (*ListLuckyGiftConfigsRequest)(nil), // 147: hyapp.activity.v1.ListLuckyGiftConfigsRequest - (*ListLuckyGiftConfigsResponse)(nil), // 148: hyapp.activity.v1.ListLuckyGiftConfigsResponse - (*ListLuckyGiftDrawsRequest)(nil), // 149: hyapp.activity.v1.ListLuckyGiftDrawsRequest - (*ListLuckyGiftDrawsResponse)(nil), // 150: hyapp.activity.v1.ListLuckyGiftDrawsResponse - (*LuckyGiftDrawSummary)(nil), // 151: hyapp.activity.v1.LuckyGiftDrawSummary - (*GetLuckyGiftDrawSummaryRequest)(nil), // 152: hyapp.activity.v1.GetLuckyGiftDrawSummaryRequest - (*GetLuckyGiftDrawSummaryResponse)(nil), // 153: hyapp.activity.v1.GetLuckyGiftDrawSummaryResponse - (*v1.EventEnvelope)(nil), // 154: hyapp.events.room.v1.EventEnvelope + (*RequestMeta)(nil), // 0: hyapp.activity.v1.RequestMeta + (*PingActivityRequest)(nil), // 1: hyapp.activity.v1.PingActivityRequest + (*PingActivityResponse)(nil), // 2: hyapp.activity.v1.PingActivityResponse + (*GetActivityStatusRequest)(nil), // 3: hyapp.activity.v1.GetActivityStatusRequest + (*GetActivityStatusResponse)(nil), // 4: hyapp.activity.v1.GetActivityStatusResponse + (*MessageTabSection)(nil), // 5: hyapp.activity.v1.MessageTabSection + (*ListMessageTabsRequest)(nil), // 6: hyapp.activity.v1.ListMessageTabsRequest + (*ListMessageTabsResponse)(nil), // 7: hyapp.activity.v1.ListMessageTabsResponse + (*InboxMessage)(nil), // 8: hyapp.activity.v1.InboxMessage + (*ListInboxMessagesRequest)(nil), // 9: hyapp.activity.v1.ListInboxMessagesRequest + (*ListInboxMessagesResponse)(nil), // 10: hyapp.activity.v1.ListInboxMessagesResponse + (*MarkInboxMessageReadRequest)(nil), // 11: hyapp.activity.v1.MarkInboxMessageReadRequest + (*MarkInboxMessageReadResponse)(nil), // 12: hyapp.activity.v1.MarkInboxMessageReadResponse + (*MarkInboxSectionReadRequest)(nil), // 13: hyapp.activity.v1.MarkInboxSectionReadRequest + (*MarkInboxSectionReadResponse)(nil), // 14: hyapp.activity.v1.MarkInboxSectionReadResponse + (*DeleteInboxMessageRequest)(nil), // 15: hyapp.activity.v1.DeleteInboxMessageRequest + (*DeleteInboxMessageResponse)(nil), // 16: hyapp.activity.v1.DeleteInboxMessageResponse + (*CreateInboxMessageRequest)(nil), // 17: hyapp.activity.v1.CreateInboxMessageRequest + (*CreateInboxMessageResponse)(nil), // 18: hyapp.activity.v1.CreateInboxMessageResponse + (*CreateFanoutJobRequest)(nil), // 19: hyapp.activity.v1.CreateFanoutJobRequest + (*CreateFanoutJobResponse)(nil), // 20: hyapp.activity.v1.CreateFanoutJobResponse + (*CronBatchRequest)(nil), // 21: hyapp.activity.v1.CronBatchRequest + (*CronBatchResponse)(nil), // 22: hyapp.activity.v1.CronBatchResponse + (*TaskItem)(nil), // 23: hyapp.activity.v1.TaskItem + (*TaskSection)(nil), // 24: hyapp.activity.v1.TaskSection + (*ListUserTasksRequest)(nil), // 25: hyapp.activity.v1.ListUserTasksRequest + (*ListUserTasksResponse)(nil), // 26: hyapp.activity.v1.ListUserTasksResponse + (*ClaimTaskRewardRequest)(nil), // 27: hyapp.activity.v1.ClaimTaskRewardRequest + (*ClaimTaskRewardResponse)(nil), // 28: hyapp.activity.v1.ClaimTaskRewardResponse + (*ConsumeTaskEventRequest)(nil), // 29: hyapp.activity.v1.ConsumeTaskEventRequest + (*ConsumeTaskEventResponse)(nil), // 30: hyapp.activity.v1.ConsumeTaskEventResponse + (*BroadcastJoinGroup)(nil), // 31: hyapp.activity.v1.BroadcastJoinGroup + (*EnsureBroadcastGroupsRequest)(nil), // 32: hyapp.activity.v1.EnsureBroadcastGroupsRequest + (*EnsureBroadcastGroupsResponse)(nil), // 33: hyapp.activity.v1.EnsureBroadcastGroupsResponse + (*PublishRegionBroadcastRequest)(nil), // 34: hyapp.activity.v1.PublishRegionBroadcastRequest + (*PublishGlobalBroadcastRequest)(nil), // 35: hyapp.activity.v1.PublishGlobalBroadcastRequest + (*PublishBroadcastResponse)(nil), // 36: hyapp.activity.v1.PublishBroadcastResponse + (*RemoveRegionBroadcastMemberRequest)(nil), // 37: hyapp.activity.v1.RemoveRegionBroadcastMemberRequest + (*RemoveRegionBroadcastMemberResponse)(nil), // 38: hyapp.activity.v1.RemoveRegionBroadcastMemberResponse + (*ProcessBroadcastOutboxBatchResponse)(nil), // 39: hyapp.activity.v1.ProcessBroadcastOutboxBatchResponse + (*ConsumeRoomEventRequest)(nil), // 40: hyapp.activity.v1.ConsumeRoomEventRequest + (*ConsumeRoomEventResponse)(nil), // 41: hyapp.activity.v1.ConsumeRoomEventResponse + (*TaskDefinition)(nil), // 42: hyapp.activity.v1.TaskDefinition + (*ListTaskDefinitionsRequest)(nil), // 43: hyapp.activity.v1.ListTaskDefinitionsRequest + (*ListTaskDefinitionsResponse)(nil), // 44: hyapp.activity.v1.ListTaskDefinitionsResponse + (*UpsertTaskDefinitionRequest)(nil), // 45: hyapp.activity.v1.UpsertTaskDefinitionRequest + (*UpsertTaskDefinitionResponse)(nil), // 46: hyapp.activity.v1.UpsertTaskDefinitionResponse + (*SetTaskDefinitionStatusRequest)(nil), // 47: hyapp.activity.v1.SetTaskDefinitionStatusRequest + (*SetTaskDefinitionStatusResponse)(nil), // 48: hyapp.activity.v1.SetTaskDefinitionStatusResponse + (*RegistrationRewardConfig)(nil), // 49: hyapp.activity.v1.RegistrationRewardConfig + (*RegistrationRewardClaim)(nil), // 50: hyapp.activity.v1.RegistrationRewardClaim + (*RegistrationRewardEligibility)(nil), // 51: hyapp.activity.v1.RegistrationRewardEligibility + (*GetRegistrationRewardEligibilityRequest)(nil), // 52: hyapp.activity.v1.GetRegistrationRewardEligibilityRequest + (*GetRegistrationRewardEligibilityResponse)(nil), // 53: hyapp.activity.v1.GetRegistrationRewardEligibilityResponse + (*IssueRegistrationRewardRequest)(nil), // 54: hyapp.activity.v1.IssueRegistrationRewardRequest + (*IssueRegistrationRewardResponse)(nil), // 55: hyapp.activity.v1.IssueRegistrationRewardResponse + (*GetRegistrationRewardConfigRequest)(nil), // 56: hyapp.activity.v1.GetRegistrationRewardConfigRequest + (*GetRegistrationRewardConfigResponse)(nil), // 57: hyapp.activity.v1.GetRegistrationRewardConfigResponse + (*UpdateRegistrationRewardConfigRequest)(nil), // 58: hyapp.activity.v1.UpdateRegistrationRewardConfigRequest + (*UpdateRegistrationRewardConfigResponse)(nil), // 59: hyapp.activity.v1.UpdateRegistrationRewardConfigResponse + (*ListRegistrationRewardClaimsRequest)(nil), // 60: hyapp.activity.v1.ListRegistrationRewardClaimsRequest + (*ListRegistrationRewardClaimsResponse)(nil), // 61: hyapp.activity.v1.ListRegistrationRewardClaimsResponse + (*FirstRechargeRewardTier)(nil), // 62: hyapp.activity.v1.FirstRechargeRewardTier + (*FirstRechargeRewardConfig)(nil), // 63: hyapp.activity.v1.FirstRechargeRewardConfig + (*FirstRechargeRewardClaim)(nil), // 64: hyapp.activity.v1.FirstRechargeRewardClaim + (*FirstRechargeRewardStatus)(nil), // 65: hyapp.activity.v1.FirstRechargeRewardStatus + (*GetFirstRechargeRewardStatusRequest)(nil), // 66: hyapp.activity.v1.GetFirstRechargeRewardStatusRequest + (*GetFirstRechargeRewardStatusResponse)(nil), // 67: hyapp.activity.v1.GetFirstRechargeRewardStatusResponse + (*ConsumeFirstRechargeRewardRequest)(nil), // 68: hyapp.activity.v1.ConsumeFirstRechargeRewardRequest + (*ConsumeFirstRechargeRewardResponse)(nil), // 69: hyapp.activity.v1.ConsumeFirstRechargeRewardResponse + (*GetFirstRechargeRewardConfigRequest)(nil), // 70: hyapp.activity.v1.GetFirstRechargeRewardConfigRequest + (*GetFirstRechargeRewardConfigResponse)(nil), // 71: hyapp.activity.v1.GetFirstRechargeRewardConfigResponse + (*UpdateFirstRechargeRewardConfigRequest)(nil), // 72: hyapp.activity.v1.UpdateFirstRechargeRewardConfigRequest + (*UpdateFirstRechargeRewardConfigResponse)(nil), // 73: hyapp.activity.v1.UpdateFirstRechargeRewardConfigResponse + (*ListFirstRechargeRewardClaimsRequest)(nil), // 74: hyapp.activity.v1.ListFirstRechargeRewardClaimsRequest + (*ListFirstRechargeRewardClaimsResponse)(nil), // 75: hyapp.activity.v1.ListFirstRechargeRewardClaimsResponse + (*CumulativeRechargeRewardTier)(nil), // 76: hyapp.activity.v1.CumulativeRechargeRewardTier + (*CumulativeRechargeRewardConfig)(nil), // 77: hyapp.activity.v1.CumulativeRechargeRewardConfig + (*CumulativeRechargeRewardGrant)(nil), // 78: hyapp.activity.v1.CumulativeRechargeRewardGrant + (*CumulativeRechargeRewardProgress)(nil), // 79: hyapp.activity.v1.CumulativeRechargeRewardProgress + (*CumulativeRechargeRewardStatus)(nil), // 80: hyapp.activity.v1.CumulativeRechargeRewardStatus + (*GetCumulativeRechargeRewardStatusRequest)(nil), // 81: hyapp.activity.v1.GetCumulativeRechargeRewardStatusRequest + (*GetCumulativeRechargeRewardStatusResponse)(nil), // 82: hyapp.activity.v1.GetCumulativeRechargeRewardStatusResponse + (*ConsumeCumulativeRechargeRewardRequest)(nil), // 83: hyapp.activity.v1.ConsumeCumulativeRechargeRewardRequest + (*ConsumeCumulativeRechargeRewardResponse)(nil), // 84: hyapp.activity.v1.ConsumeCumulativeRechargeRewardResponse + (*GetCumulativeRechargeRewardConfigRequest)(nil), // 85: hyapp.activity.v1.GetCumulativeRechargeRewardConfigRequest + (*GetCumulativeRechargeRewardConfigResponse)(nil), // 86: hyapp.activity.v1.GetCumulativeRechargeRewardConfigResponse + (*UpdateCumulativeRechargeRewardConfigRequest)(nil), // 87: hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigRequest + (*UpdateCumulativeRechargeRewardConfigResponse)(nil), // 88: hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigResponse + (*ListCumulativeRechargeRewardGrantsRequest)(nil), // 89: hyapp.activity.v1.ListCumulativeRechargeRewardGrantsRequest + (*ListCumulativeRechargeRewardGrantsResponse)(nil), // 90: hyapp.activity.v1.ListCumulativeRechargeRewardGrantsResponse + (*RoomTurnoverRewardTier)(nil), // 91: hyapp.activity.v1.RoomTurnoverRewardTier + (*RoomTurnoverRewardConfig)(nil), // 92: hyapp.activity.v1.RoomTurnoverRewardConfig + (*RoomTurnoverRewardSettlement)(nil), // 93: hyapp.activity.v1.RoomTurnoverRewardSettlement + (*RoomTurnoverRewardStatus)(nil), // 94: hyapp.activity.v1.RoomTurnoverRewardStatus + (*GetRoomTurnoverRewardStatusRequest)(nil), // 95: hyapp.activity.v1.GetRoomTurnoverRewardStatusRequest + (*GetRoomTurnoverRewardStatusResponse)(nil), // 96: hyapp.activity.v1.GetRoomTurnoverRewardStatusResponse + (*GetRoomTurnoverRewardConfigRequest)(nil), // 97: hyapp.activity.v1.GetRoomTurnoverRewardConfigRequest + (*GetRoomTurnoverRewardConfigResponse)(nil), // 98: hyapp.activity.v1.GetRoomTurnoverRewardConfigResponse + (*UpdateRoomTurnoverRewardConfigRequest)(nil), // 99: hyapp.activity.v1.UpdateRoomTurnoverRewardConfigRequest + (*UpdateRoomTurnoverRewardConfigResponse)(nil), // 100: hyapp.activity.v1.UpdateRoomTurnoverRewardConfigResponse + (*ListRoomTurnoverRewardSettlementsRequest)(nil), // 101: hyapp.activity.v1.ListRoomTurnoverRewardSettlementsRequest + (*ListRoomTurnoverRewardSettlementsResponse)(nil), // 102: hyapp.activity.v1.ListRoomTurnoverRewardSettlementsResponse + (*RetryRoomTurnoverRewardSettlementRequest)(nil), // 103: hyapp.activity.v1.RetryRoomTurnoverRewardSettlementRequest + (*RetryRoomTurnoverRewardSettlementResponse)(nil), // 104: hyapp.activity.v1.RetryRoomTurnoverRewardSettlementResponse + (*SevenDayCheckInReward)(nil), // 105: hyapp.activity.v1.SevenDayCheckInReward + (*SevenDayCheckInConfig)(nil), // 106: hyapp.activity.v1.SevenDayCheckInConfig + (*SevenDayCheckInRewardStatus)(nil), // 107: hyapp.activity.v1.SevenDayCheckInRewardStatus + (*GetSevenDayCheckInStatusRequest)(nil), // 108: hyapp.activity.v1.GetSevenDayCheckInStatusRequest + (*GetSevenDayCheckInStatusResponse)(nil), // 109: hyapp.activity.v1.GetSevenDayCheckInStatusResponse + (*SignSevenDayCheckInRequest)(nil), // 110: hyapp.activity.v1.SignSevenDayCheckInRequest + (*SignSevenDayCheckInResponse)(nil), // 111: hyapp.activity.v1.SignSevenDayCheckInResponse + (*GetSevenDayCheckInConfigRequest)(nil), // 112: hyapp.activity.v1.GetSevenDayCheckInConfigRequest + (*GetSevenDayCheckInConfigResponse)(nil), // 113: hyapp.activity.v1.GetSevenDayCheckInConfigResponse + (*UpdateSevenDayCheckInConfigRequest)(nil), // 114: hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest + (*UpdateSevenDayCheckInConfigResponse)(nil), // 115: hyapp.activity.v1.UpdateSevenDayCheckInConfigResponse + (*SevenDayCheckInClaim)(nil), // 116: hyapp.activity.v1.SevenDayCheckInClaim + (*ListSevenDayCheckInClaimsRequest)(nil), // 117: hyapp.activity.v1.ListSevenDayCheckInClaimsRequest + (*ListSevenDayCheckInClaimsResponse)(nil), // 118: hyapp.activity.v1.ListSevenDayCheckInClaimsResponse + (*LevelTrack)(nil), // 119: hyapp.activity.v1.LevelTrack + (*LevelRule)(nil), // 120: hyapp.activity.v1.LevelRule + (*LevelTier)(nil), // 121: hyapp.activity.v1.LevelTier + (*LevelTrackOverview)(nil), // 122: hyapp.activity.v1.LevelTrackOverview + (*GetMyLevelOverviewRequest)(nil), // 123: hyapp.activity.v1.GetMyLevelOverviewRequest + (*GetMyLevelOverviewResponse)(nil), // 124: hyapp.activity.v1.GetMyLevelOverviewResponse + (*GetLevelTrackRequest)(nil), // 125: hyapp.activity.v1.GetLevelTrackRequest + (*GetLevelTrackResponse)(nil), // 126: hyapp.activity.v1.GetLevelTrackResponse + (*LevelDisplayTrackProfile)(nil), // 127: hyapp.activity.v1.LevelDisplayTrackProfile + (*UserLevelDisplayProfile)(nil), // 128: hyapp.activity.v1.UserLevelDisplayProfile + (*BatchGetUserLevelDisplayProfilesRequest)(nil), // 129: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesRequest + (*BatchGetUserLevelDisplayProfilesResponse)(nil), // 130: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesResponse + (*LevelRewardJob)(nil), // 131: hyapp.activity.v1.LevelRewardJob + (*ListLevelRewardsRequest)(nil), // 132: hyapp.activity.v1.ListLevelRewardsRequest + (*ListLevelRewardsResponse)(nil), // 133: hyapp.activity.v1.ListLevelRewardsResponse + (*ConsumeLevelEventRequest)(nil), // 134: hyapp.activity.v1.ConsumeLevelEventRequest + (*ConsumeLevelEventResponse)(nil), // 135: hyapp.activity.v1.ConsumeLevelEventResponse + (*RegistrationLevelBadgeGrant)(nil), // 136: hyapp.activity.v1.RegistrationLevelBadgeGrant + (*IssueRegistrationLevelBadgesRequest)(nil), // 137: hyapp.activity.v1.IssueRegistrationLevelBadgesRequest + (*IssueRegistrationLevelBadgesResponse)(nil), // 138: hyapp.activity.v1.IssueRegistrationLevelBadgesResponse + (*UpsertLevelTrackRequest)(nil), // 139: hyapp.activity.v1.UpsertLevelTrackRequest + (*UpsertLevelTrackResponse)(nil), // 140: hyapp.activity.v1.UpsertLevelTrackResponse + (*UpsertLevelRuleRequest)(nil), // 141: hyapp.activity.v1.UpsertLevelRuleRequest + (*UpsertLevelRuleResponse)(nil), // 142: hyapp.activity.v1.UpsertLevelRuleResponse + (*UpsertLevelTierRequest)(nil), // 143: hyapp.activity.v1.UpsertLevelTierRequest + (*UpsertLevelTierResponse)(nil), // 144: hyapp.activity.v1.UpsertLevelTierResponse + (*ListLevelConfigRequest)(nil), // 145: hyapp.activity.v1.ListLevelConfigRequest + (*ListLevelConfigResponse)(nil), // 146: hyapp.activity.v1.ListLevelConfigResponse + (*AchievementCondition)(nil), // 147: hyapp.activity.v1.AchievementCondition + (*AchievementDefinition)(nil), // 148: hyapp.activity.v1.AchievementDefinition + (*UserAchievement)(nil), // 149: hyapp.activity.v1.UserAchievement + (*ListAchievementsRequest)(nil), // 150: hyapp.activity.v1.ListAchievementsRequest + (*ListAchievementsResponse)(nil), // 151: hyapp.activity.v1.ListAchievementsResponse + (*ConsumeAchievementEventRequest)(nil), // 152: hyapp.activity.v1.ConsumeAchievementEventRequest + (*ConsumeAchievementEventResponse)(nil), // 153: hyapp.activity.v1.ConsumeAchievementEventResponse + (*BadgeDisplayItem)(nil), // 154: hyapp.activity.v1.BadgeDisplayItem + (*ListMyBadgesRequest)(nil), // 155: hyapp.activity.v1.ListMyBadgesRequest + (*ListMyBadgesResponse)(nil), // 156: hyapp.activity.v1.ListMyBadgesResponse + (*SetBadgeDisplayRequest)(nil), // 157: hyapp.activity.v1.SetBadgeDisplayRequest + (*SetBadgeDisplayResponse)(nil), // 158: hyapp.activity.v1.SetBadgeDisplayResponse + (*UpsertAchievementDefinitionRequest)(nil), // 159: hyapp.activity.v1.UpsertAchievementDefinitionRequest + (*UpsertAchievementDefinitionResponse)(nil), // 160: hyapp.activity.v1.UpsertAchievementDefinitionResponse + (*LuckyGiftMeta)(nil), // 161: hyapp.activity.v1.LuckyGiftMeta + (*LuckyGiftTier)(nil), // 162: hyapp.activity.v1.LuckyGiftTier + (*LuckyGiftConfig)(nil), // 163: hyapp.activity.v1.LuckyGiftConfig + (*LuckyGiftRuleTier)(nil), // 164: hyapp.activity.v1.LuckyGiftRuleTier + (*LuckyGiftRuleStage)(nil), // 165: hyapp.activity.v1.LuckyGiftRuleStage + (*LuckyGiftRuleConfig)(nil), // 166: hyapp.activity.v1.LuckyGiftRuleConfig + (*CheckLuckyGiftRequest)(nil), // 167: hyapp.activity.v1.CheckLuckyGiftRequest + (*CheckLuckyGiftResponse)(nil), // 168: hyapp.activity.v1.CheckLuckyGiftResponse + (*LuckyGiftDrawResult)(nil), // 169: hyapp.activity.v1.LuckyGiftDrawResult + (*ExecuteLuckyGiftDrawRequest)(nil), // 170: hyapp.activity.v1.ExecuteLuckyGiftDrawRequest + (*ExecuteLuckyGiftDrawResponse)(nil), // 171: hyapp.activity.v1.ExecuteLuckyGiftDrawResponse + (*GetLuckyGiftConfigRequest)(nil), // 172: hyapp.activity.v1.GetLuckyGiftConfigRequest + (*GetLuckyGiftConfigResponse)(nil), // 173: hyapp.activity.v1.GetLuckyGiftConfigResponse + (*UpsertLuckyGiftConfigRequest)(nil), // 174: hyapp.activity.v1.UpsertLuckyGiftConfigRequest + (*UpsertLuckyGiftConfigResponse)(nil), // 175: hyapp.activity.v1.UpsertLuckyGiftConfigResponse + (*ListLuckyGiftConfigsRequest)(nil), // 176: hyapp.activity.v1.ListLuckyGiftConfigsRequest + (*ListLuckyGiftConfigsResponse)(nil), // 177: hyapp.activity.v1.ListLuckyGiftConfigsResponse + (*ListLuckyGiftDrawsRequest)(nil), // 178: hyapp.activity.v1.ListLuckyGiftDrawsRequest + (*ListLuckyGiftDrawsResponse)(nil), // 179: hyapp.activity.v1.ListLuckyGiftDrawsResponse + (*LuckyGiftDrawSummary)(nil), // 180: hyapp.activity.v1.LuckyGiftDrawSummary + (*GetLuckyGiftDrawSummaryRequest)(nil), // 181: hyapp.activity.v1.GetLuckyGiftDrawSummaryRequest + (*GetLuckyGiftDrawSummaryResponse)(nil), // 182: hyapp.activity.v1.GetLuckyGiftDrawSummaryResponse + (*WeeklyStarGift)(nil), // 183: hyapp.activity.v1.WeeklyStarGift + (*WeeklyStarReward)(nil), // 184: hyapp.activity.v1.WeeklyStarReward + (*WeeklyStarCycle)(nil), // 185: hyapp.activity.v1.WeeklyStarCycle + (*WeeklyStarLeaderboardEntry)(nil), // 186: hyapp.activity.v1.WeeklyStarLeaderboardEntry + (*WeeklyStarSettlement)(nil), // 187: hyapp.activity.v1.WeeklyStarSettlement + (*ListWeeklyStarCyclesRequest)(nil), // 188: hyapp.activity.v1.ListWeeklyStarCyclesRequest + (*ListWeeklyStarCyclesResponse)(nil), // 189: hyapp.activity.v1.ListWeeklyStarCyclesResponse + (*GetWeeklyStarCycleRequest)(nil), // 190: hyapp.activity.v1.GetWeeklyStarCycleRequest + (*GetWeeklyStarCycleResponse)(nil), // 191: hyapp.activity.v1.GetWeeklyStarCycleResponse + (*UpsertWeeklyStarCycleRequest)(nil), // 192: hyapp.activity.v1.UpsertWeeklyStarCycleRequest + (*UpsertWeeklyStarCycleResponse)(nil), // 193: hyapp.activity.v1.UpsertWeeklyStarCycleResponse + (*SetWeeklyStarCycleStatusRequest)(nil), // 194: hyapp.activity.v1.SetWeeklyStarCycleStatusRequest + (*SetWeeklyStarCycleStatusResponse)(nil), // 195: hyapp.activity.v1.SetWeeklyStarCycleStatusResponse + (*ListWeeklyStarLeaderboardRequest)(nil), // 196: hyapp.activity.v1.ListWeeklyStarLeaderboardRequest + (*ListWeeklyStarLeaderboardResponse)(nil), // 197: hyapp.activity.v1.ListWeeklyStarLeaderboardResponse + (*ListWeeklyStarSettlementsRequest)(nil), // 198: hyapp.activity.v1.ListWeeklyStarSettlementsRequest + (*ListWeeklyStarSettlementsResponse)(nil), // 199: hyapp.activity.v1.ListWeeklyStarSettlementsResponse + (*GetWeeklyStarCurrentRequest)(nil), // 200: hyapp.activity.v1.GetWeeklyStarCurrentRequest + (*GetWeeklyStarCurrentResponse)(nil), // 201: hyapp.activity.v1.GetWeeklyStarCurrentResponse + (*ListWeeklyStarHistoryRequest)(nil), // 202: hyapp.activity.v1.ListWeeklyStarHistoryRequest + (*WeeklyStarHistoryCycle)(nil), // 203: hyapp.activity.v1.WeeklyStarHistoryCycle + (*ListWeeklyStarHistoryResponse)(nil), // 204: hyapp.activity.v1.ListWeeklyStarHistoryResponse + (*v1.EventEnvelope)(nil), // 205: hyapp.events.room.v1.EventEnvelope } var file_proto_activity_v1_activity_proto_depIdxs = []int32{ 0, // 0: hyapp.activity.v1.PingActivityRequest.meta:type_name -> hyapp.activity.v1.RequestMeta @@ -13965,7 +18123,7 @@ var file_proto_activity_v1_activity_proto_depIdxs = []int32{ 0, // 19: hyapp.activity.v1.PublishGlobalBroadcastRequest.meta:type_name -> hyapp.activity.v1.RequestMeta 0, // 20: hyapp.activity.v1.RemoveRegionBroadcastMemberRequest.meta:type_name -> hyapp.activity.v1.RequestMeta 0, // 21: hyapp.activity.v1.ConsumeRoomEventRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 154, // 22: hyapp.activity.v1.ConsumeRoomEventRequest.envelope:type_name -> hyapp.events.room.v1.EventEnvelope + 205, // 22: hyapp.activity.v1.ConsumeRoomEventRequest.envelope:type_name -> hyapp.events.room.v1.EventEnvelope 0, // 23: hyapp.activity.v1.ListTaskDefinitionsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta 42, // 24: hyapp.activity.v1.ListTaskDefinitionsResponse.tasks:type_name -> hyapp.activity.v1.TaskDefinition 0, // 25: hyapp.activity.v1.UpsertTaskDefinitionRequest.meta:type_name -> hyapp.activity.v1.RequestMeta @@ -13997,205 +18155,303 @@ var file_proto_activity_v1_activity_proto_depIdxs = []int32{ 63, // 51: hyapp.activity.v1.UpdateFirstRechargeRewardConfigResponse.config:type_name -> hyapp.activity.v1.FirstRechargeRewardConfig 0, // 52: hyapp.activity.v1.ListFirstRechargeRewardClaimsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta 64, // 53: hyapp.activity.v1.ListFirstRechargeRewardClaimsResponse.claims:type_name -> hyapp.activity.v1.FirstRechargeRewardClaim - 76, // 54: hyapp.activity.v1.SevenDayCheckInConfig.rewards:type_name -> hyapp.activity.v1.SevenDayCheckInReward - 0, // 55: hyapp.activity.v1.GetSevenDayCheckInStatusRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 78, // 56: hyapp.activity.v1.GetSevenDayCheckInStatusResponse.rewards:type_name -> hyapp.activity.v1.SevenDayCheckInRewardStatus - 0, // 57: hyapp.activity.v1.SignSevenDayCheckInRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 0, // 58: hyapp.activity.v1.GetSevenDayCheckInConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 77, // 59: hyapp.activity.v1.GetSevenDayCheckInConfigResponse.config:type_name -> hyapp.activity.v1.SevenDayCheckInConfig - 0, // 60: hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 76, // 61: hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest.rewards:type_name -> hyapp.activity.v1.SevenDayCheckInReward - 77, // 62: hyapp.activity.v1.UpdateSevenDayCheckInConfigResponse.config:type_name -> hyapp.activity.v1.SevenDayCheckInConfig - 0, // 63: hyapp.activity.v1.ListSevenDayCheckInClaimsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 87, // 64: hyapp.activity.v1.ListSevenDayCheckInClaimsResponse.claims:type_name -> hyapp.activity.v1.SevenDayCheckInClaim - 0, // 65: hyapp.activity.v1.GetMyLevelOverviewRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 93, // 66: hyapp.activity.v1.GetMyLevelOverviewResponse.tracks:type_name -> hyapp.activity.v1.LevelTrackOverview - 0, // 67: hyapp.activity.v1.GetLevelTrackRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 93, // 68: hyapp.activity.v1.GetLevelTrackResponse.overview:type_name -> hyapp.activity.v1.LevelTrackOverview - 91, // 69: hyapp.activity.v1.GetLevelTrackResponse.rules:type_name -> hyapp.activity.v1.LevelRule - 92, // 70: hyapp.activity.v1.GetLevelTrackResponse.tiers:type_name -> hyapp.activity.v1.LevelTier - 98, // 71: hyapp.activity.v1.UserLevelDisplayProfile.wealth:type_name -> hyapp.activity.v1.LevelDisplayTrackProfile - 98, // 72: hyapp.activity.v1.UserLevelDisplayProfile.game:type_name -> hyapp.activity.v1.LevelDisplayTrackProfile - 98, // 73: hyapp.activity.v1.UserLevelDisplayProfile.charm:type_name -> hyapp.activity.v1.LevelDisplayTrackProfile - 0, // 74: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 99, // 75: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesResponse.profiles:type_name -> hyapp.activity.v1.UserLevelDisplayProfile - 0, // 76: hyapp.activity.v1.ListLevelRewardsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 102, // 77: hyapp.activity.v1.ListLevelRewardsResponse.rewards:type_name -> hyapp.activity.v1.LevelRewardJob - 0, // 78: hyapp.activity.v1.ConsumeLevelEventRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 0, // 79: hyapp.activity.v1.IssueRegistrationLevelBadgesRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 107, // 80: hyapp.activity.v1.IssueRegistrationLevelBadgesResponse.grants:type_name -> hyapp.activity.v1.RegistrationLevelBadgeGrant - 0, // 81: hyapp.activity.v1.UpsertLevelTrackRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 90, // 82: hyapp.activity.v1.UpsertLevelTrackResponse.track:type_name -> hyapp.activity.v1.LevelTrack - 0, // 83: hyapp.activity.v1.UpsertLevelRuleRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 91, // 84: hyapp.activity.v1.UpsertLevelRuleResponse.rule:type_name -> hyapp.activity.v1.LevelRule - 0, // 85: hyapp.activity.v1.UpsertLevelTierRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 92, // 86: hyapp.activity.v1.UpsertLevelTierResponse.tier:type_name -> hyapp.activity.v1.LevelTier - 0, // 87: hyapp.activity.v1.ListLevelConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 90, // 88: hyapp.activity.v1.ListLevelConfigResponse.tracks:type_name -> hyapp.activity.v1.LevelTrack - 91, // 89: hyapp.activity.v1.ListLevelConfigResponse.rules:type_name -> hyapp.activity.v1.LevelRule - 92, // 90: hyapp.activity.v1.ListLevelConfigResponse.tiers:type_name -> hyapp.activity.v1.LevelTier - 118, // 91: hyapp.activity.v1.AchievementDefinition.conditions:type_name -> hyapp.activity.v1.AchievementCondition - 119, // 92: hyapp.activity.v1.UserAchievement.definition:type_name -> hyapp.activity.v1.AchievementDefinition - 0, // 93: hyapp.activity.v1.ListAchievementsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 120, // 94: hyapp.activity.v1.ListAchievementsResponse.achievements:type_name -> hyapp.activity.v1.UserAchievement - 0, // 95: hyapp.activity.v1.ConsumeAchievementEventRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 0, // 96: hyapp.activity.v1.ListMyBadgesRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 125, // 97: hyapp.activity.v1.ListMyBadgesResponse.strip_badges:type_name -> hyapp.activity.v1.BadgeDisplayItem - 125, // 98: hyapp.activity.v1.ListMyBadgesResponse.profile_tile_badges:type_name -> hyapp.activity.v1.BadgeDisplayItem - 125, // 99: hyapp.activity.v1.ListMyBadgesResponse.honor_badges:type_name -> hyapp.activity.v1.BadgeDisplayItem - 0, // 100: hyapp.activity.v1.SetBadgeDisplayRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 125, // 101: hyapp.activity.v1.SetBadgeDisplayRequest.items:type_name -> hyapp.activity.v1.BadgeDisplayItem - 127, // 102: hyapp.activity.v1.SetBadgeDisplayResponse.profile:type_name -> hyapp.activity.v1.ListMyBadgesResponse - 0, // 103: hyapp.activity.v1.UpsertAchievementDefinitionRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 118, // 104: hyapp.activity.v1.UpsertAchievementDefinitionRequest.conditions:type_name -> hyapp.activity.v1.AchievementCondition - 119, // 105: hyapp.activity.v1.UpsertAchievementDefinitionResponse.achievement:type_name -> hyapp.activity.v1.AchievementDefinition - 0, // 106: hyapp.activity.v1.LuckyGiftMeta.meta:type_name -> hyapp.activity.v1.RequestMeta - 133, // 107: hyapp.activity.v1.LuckyGiftConfig.tiers:type_name -> hyapp.activity.v1.LuckyGiftTier - 135, // 108: hyapp.activity.v1.LuckyGiftRuleStage.tiers:type_name -> hyapp.activity.v1.LuckyGiftRuleTier - 136, // 109: hyapp.activity.v1.LuckyGiftRuleConfig.stages:type_name -> hyapp.activity.v1.LuckyGiftRuleStage - 0, // 110: hyapp.activity.v1.CheckLuckyGiftRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 132, // 111: hyapp.activity.v1.ExecuteLuckyGiftDrawRequest.lucky_gift:type_name -> hyapp.activity.v1.LuckyGiftMeta - 140, // 112: hyapp.activity.v1.ExecuteLuckyGiftDrawResponse.result:type_name -> hyapp.activity.v1.LuckyGiftDrawResult - 0, // 113: hyapp.activity.v1.GetLuckyGiftConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 137, // 114: hyapp.activity.v1.GetLuckyGiftConfigResponse.config:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig - 0, // 115: hyapp.activity.v1.UpsertLuckyGiftConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 137, // 116: hyapp.activity.v1.UpsertLuckyGiftConfigRequest.config:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig - 137, // 117: hyapp.activity.v1.UpsertLuckyGiftConfigResponse.config:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig - 0, // 118: hyapp.activity.v1.ListLuckyGiftConfigsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 137, // 119: hyapp.activity.v1.ListLuckyGiftConfigsResponse.configs:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig - 0, // 120: hyapp.activity.v1.ListLuckyGiftDrawsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 140, // 121: hyapp.activity.v1.ListLuckyGiftDrawsResponse.draws:type_name -> hyapp.activity.v1.LuckyGiftDrawResult - 0, // 122: hyapp.activity.v1.GetLuckyGiftDrawSummaryRequest.meta:type_name -> hyapp.activity.v1.RequestMeta - 151, // 123: hyapp.activity.v1.GetLuckyGiftDrawSummaryResponse.summary:type_name -> hyapp.activity.v1.LuckyGiftDrawSummary - 1, // 124: hyapp.activity.v1.ActivityService.PingActivity:input_type -> hyapp.activity.v1.PingActivityRequest - 3, // 125: hyapp.activity.v1.ActivityService.GetActivityStatus:input_type -> hyapp.activity.v1.GetActivityStatusRequest - 6, // 126: hyapp.activity.v1.MessageInboxService.ListMessageTabs:input_type -> hyapp.activity.v1.ListMessageTabsRequest - 9, // 127: hyapp.activity.v1.MessageInboxService.ListInboxMessages:input_type -> hyapp.activity.v1.ListInboxMessagesRequest - 11, // 128: hyapp.activity.v1.MessageInboxService.MarkInboxMessageRead:input_type -> hyapp.activity.v1.MarkInboxMessageReadRequest - 13, // 129: hyapp.activity.v1.MessageInboxService.MarkInboxSectionRead:input_type -> hyapp.activity.v1.MarkInboxSectionReadRequest - 15, // 130: hyapp.activity.v1.MessageInboxService.DeleteInboxMessage:input_type -> hyapp.activity.v1.DeleteInboxMessageRequest - 17, // 131: hyapp.activity.v1.MessageInboxService.CreateInboxMessage:input_type -> hyapp.activity.v1.CreateInboxMessageRequest - 19, // 132: hyapp.activity.v1.MessageInboxService.CreateFanoutJob:input_type -> hyapp.activity.v1.CreateFanoutJobRequest - 21, // 133: hyapp.activity.v1.ActivityCronService.ProcessMessageFanoutBatch:input_type -> hyapp.activity.v1.CronBatchRequest - 21, // 134: hyapp.activity.v1.ActivityCronService.ProcessLevelRewardBatch:input_type -> hyapp.activity.v1.CronBatchRequest - 21, // 135: hyapp.activity.v1.ActivityCronService.ProcessAchievementRewardBatch:input_type -> hyapp.activity.v1.CronBatchRequest - 25, // 136: hyapp.activity.v1.TaskService.ListUserTasks:input_type -> hyapp.activity.v1.ListUserTasksRequest - 27, // 137: hyapp.activity.v1.TaskService.ClaimTaskReward:input_type -> hyapp.activity.v1.ClaimTaskRewardRequest - 29, // 138: hyapp.activity.v1.TaskService.ConsumeTaskEvent:input_type -> hyapp.activity.v1.ConsumeTaskEventRequest - 94, // 139: hyapp.activity.v1.GrowthLevelService.GetMyLevelOverview:input_type -> hyapp.activity.v1.GetMyLevelOverviewRequest - 96, // 140: hyapp.activity.v1.GrowthLevelService.GetLevelTrack:input_type -> hyapp.activity.v1.GetLevelTrackRequest - 100, // 141: hyapp.activity.v1.GrowthLevelService.BatchGetUserLevelDisplayProfiles:input_type -> hyapp.activity.v1.BatchGetUserLevelDisplayProfilesRequest - 103, // 142: hyapp.activity.v1.GrowthLevelService.ListLevelRewards:input_type -> hyapp.activity.v1.ListLevelRewardsRequest - 105, // 143: hyapp.activity.v1.GrowthLevelService.ConsumeLevelEvent:input_type -> hyapp.activity.v1.ConsumeLevelEventRequest - 108, // 144: hyapp.activity.v1.GrowthLevelService.IssueRegistrationLevelBadges:input_type -> hyapp.activity.v1.IssueRegistrationLevelBadgesRequest - 121, // 145: hyapp.activity.v1.AchievementService.ListAchievements:input_type -> hyapp.activity.v1.ListAchievementsRequest - 123, // 146: hyapp.activity.v1.AchievementService.ConsumeAchievementEvent:input_type -> hyapp.activity.v1.ConsumeAchievementEventRequest - 126, // 147: hyapp.activity.v1.AchievementService.ListMyBadges:input_type -> hyapp.activity.v1.ListMyBadgesRequest - 128, // 148: hyapp.activity.v1.AchievementService.SetBadgeDisplay:input_type -> hyapp.activity.v1.SetBadgeDisplayRequest - 138, // 149: hyapp.activity.v1.LuckyGiftService.CheckLuckyGift:input_type -> hyapp.activity.v1.CheckLuckyGiftRequest - 141, // 150: hyapp.activity.v1.LuckyGiftService.ExecuteLuckyGiftDraw:input_type -> hyapp.activity.v1.ExecuteLuckyGiftDrawRequest - 32, // 151: hyapp.activity.v1.BroadcastService.EnsureBroadcastGroups:input_type -> hyapp.activity.v1.EnsureBroadcastGroupsRequest - 34, // 152: hyapp.activity.v1.BroadcastService.PublishRegionBroadcast:input_type -> hyapp.activity.v1.PublishRegionBroadcastRequest - 35, // 153: hyapp.activity.v1.BroadcastService.PublishGlobalBroadcast:input_type -> hyapp.activity.v1.PublishGlobalBroadcastRequest - 37, // 154: hyapp.activity.v1.BroadcastService.RemoveRegionBroadcastMember:input_type -> hyapp.activity.v1.RemoveRegionBroadcastMemberRequest - 21, // 155: hyapp.activity.v1.BroadcastService.ProcessBroadcastOutboxBatch:input_type -> hyapp.activity.v1.CronBatchRequest - 40, // 156: hyapp.activity.v1.RoomEventConsumerService.ConsumeRoomEvent:input_type -> hyapp.activity.v1.ConsumeRoomEventRequest - 43, // 157: hyapp.activity.v1.AdminTaskService.ListTaskDefinitions:input_type -> hyapp.activity.v1.ListTaskDefinitionsRequest - 45, // 158: hyapp.activity.v1.AdminTaskService.UpsertTaskDefinition:input_type -> hyapp.activity.v1.UpsertTaskDefinitionRequest - 47, // 159: hyapp.activity.v1.AdminTaskService.SetTaskDefinitionStatus:input_type -> hyapp.activity.v1.SetTaskDefinitionStatusRequest - 52, // 160: hyapp.activity.v1.RegistrationRewardService.GetRegistrationRewardEligibility:input_type -> hyapp.activity.v1.GetRegistrationRewardEligibilityRequest - 54, // 161: hyapp.activity.v1.RegistrationRewardService.IssueRegistrationReward:input_type -> hyapp.activity.v1.IssueRegistrationRewardRequest - 56, // 162: hyapp.activity.v1.AdminRegistrationRewardService.GetRegistrationRewardConfig:input_type -> hyapp.activity.v1.GetRegistrationRewardConfigRequest - 58, // 163: hyapp.activity.v1.AdminRegistrationRewardService.UpdateRegistrationRewardConfig:input_type -> hyapp.activity.v1.UpdateRegistrationRewardConfigRequest - 60, // 164: hyapp.activity.v1.AdminRegistrationRewardService.ListRegistrationRewardClaims:input_type -> hyapp.activity.v1.ListRegistrationRewardClaimsRequest - 66, // 165: hyapp.activity.v1.FirstRechargeRewardService.GetFirstRechargeRewardStatus:input_type -> hyapp.activity.v1.GetFirstRechargeRewardStatusRequest - 68, // 166: hyapp.activity.v1.FirstRechargeRewardService.ConsumeFirstRechargeReward:input_type -> hyapp.activity.v1.ConsumeFirstRechargeRewardRequest - 70, // 167: hyapp.activity.v1.AdminFirstRechargeRewardService.GetFirstRechargeRewardConfig:input_type -> hyapp.activity.v1.GetFirstRechargeRewardConfigRequest - 72, // 168: hyapp.activity.v1.AdminFirstRechargeRewardService.UpdateFirstRechargeRewardConfig:input_type -> hyapp.activity.v1.UpdateFirstRechargeRewardConfigRequest - 74, // 169: hyapp.activity.v1.AdminFirstRechargeRewardService.ListFirstRechargeRewardClaims:input_type -> hyapp.activity.v1.ListFirstRechargeRewardClaimsRequest - 79, // 170: hyapp.activity.v1.SevenDayCheckInService.GetSevenDayCheckInStatus:input_type -> hyapp.activity.v1.GetSevenDayCheckInStatusRequest - 81, // 171: hyapp.activity.v1.SevenDayCheckInService.SignSevenDayCheckIn:input_type -> hyapp.activity.v1.SignSevenDayCheckInRequest - 83, // 172: hyapp.activity.v1.AdminSevenDayCheckInService.GetSevenDayCheckInConfig:input_type -> hyapp.activity.v1.GetSevenDayCheckInConfigRequest - 85, // 173: hyapp.activity.v1.AdminSevenDayCheckInService.UpdateSevenDayCheckInConfig:input_type -> hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest - 88, // 174: hyapp.activity.v1.AdminSevenDayCheckInService.ListSevenDayCheckInClaims:input_type -> hyapp.activity.v1.ListSevenDayCheckInClaimsRequest - 116, // 175: hyapp.activity.v1.AdminGrowthLevelService.ListLevelConfig:input_type -> hyapp.activity.v1.ListLevelConfigRequest - 110, // 176: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTrack:input_type -> hyapp.activity.v1.UpsertLevelTrackRequest - 112, // 177: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelRule:input_type -> hyapp.activity.v1.UpsertLevelRuleRequest - 114, // 178: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTier:input_type -> hyapp.activity.v1.UpsertLevelTierRequest - 121, // 179: hyapp.activity.v1.AdminAchievementService.ListAchievementDefinitions:input_type -> hyapp.activity.v1.ListAchievementsRequest - 130, // 180: hyapp.activity.v1.AdminAchievementService.UpsertAchievementDefinition:input_type -> hyapp.activity.v1.UpsertAchievementDefinitionRequest - 143, // 181: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftConfig:input_type -> hyapp.activity.v1.GetLuckyGiftConfigRequest - 145, // 182: hyapp.activity.v1.AdminLuckyGiftService.UpsertLuckyGiftConfig:input_type -> hyapp.activity.v1.UpsertLuckyGiftConfigRequest - 147, // 183: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftConfigs:input_type -> hyapp.activity.v1.ListLuckyGiftConfigsRequest - 149, // 184: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftDraws:input_type -> hyapp.activity.v1.ListLuckyGiftDrawsRequest - 152, // 185: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftDrawSummary:input_type -> hyapp.activity.v1.GetLuckyGiftDrawSummaryRequest - 2, // 186: hyapp.activity.v1.ActivityService.PingActivity:output_type -> hyapp.activity.v1.PingActivityResponse - 4, // 187: hyapp.activity.v1.ActivityService.GetActivityStatus:output_type -> hyapp.activity.v1.GetActivityStatusResponse - 7, // 188: hyapp.activity.v1.MessageInboxService.ListMessageTabs:output_type -> hyapp.activity.v1.ListMessageTabsResponse - 10, // 189: hyapp.activity.v1.MessageInboxService.ListInboxMessages:output_type -> hyapp.activity.v1.ListInboxMessagesResponse - 12, // 190: hyapp.activity.v1.MessageInboxService.MarkInboxMessageRead:output_type -> hyapp.activity.v1.MarkInboxMessageReadResponse - 14, // 191: hyapp.activity.v1.MessageInboxService.MarkInboxSectionRead:output_type -> hyapp.activity.v1.MarkInboxSectionReadResponse - 16, // 192: hyapp.activity.v1.MessageInboxService.DeleteInboxMessage:output_type -> hyapp.activity.v1.DeleteInboxMessageResponse - 18, // 193: hyapp.activity.v1.MessageInboxService.CreateInboxMessage:output_type -> hyapp.activity.v1.CreateInboxMessageResponse - 20, // 194: hyapp.activity.v1.MessageInboxService.CreateFanoutJob:output_type -> hyapp.activity.v1.CreateFanoutJobResponse - 22, // 195: hyapp.activity.v1.ActivityCronService.ProcessMessageFanoutBatch:output_type -> hyapp.activity.v1.CronBatchResponse - 22, // 196: hyapp.activity.v1.ActivityCronService.ProcessLevelRewardBatch:output_type -> hyapp.activity.v1.CronBatchResponse - 22, // 197: hyapp.activity.v1.ActivityCronService.ProcessAchievementRewardBatch:output_type -> hyapp.activity.v1.CronBatchResponse - 26, // 198: hyapp.activity.v1.TaskService.ListUserTasks:output_type -> hyapp.activity.v1.ListUserTasksResponse - 28, // 199: hyapp.activity.v1.TaskService.ClaimTaskReward:output_type -> hyapp.activity.v1.ClaimTaskRewardResponse - 30, // 200: hyapp.activity.v1.TaskService.ConsumeTaskEvent:output_type -> hyapp.activity.v1.ConsumeTaskEventResponse - 95, // 201: hyapp.activity.v1.GrowthLevelService.GetMyLevelOverview:output_type -> hyapp.activity.v1.GetMyLevelOverviewResponse - 97, // 202: hyapp.activity.v1.GrowthLevelService.GetLevelTrack:output_type -> hyapp.activity.v1.GetLevelTrackResponse - 101, // 203: hyapp.activity.v1.GrowthLevelService.BatchGetUserLevelDisplayProfiles:output_type -> hyapp.activity.v1.BatchGetUserLevelDisplayProfilesResponse - 104, // 204: hyapp.activity.v1.GrowthLevelService.ListLevelRewards:output_type -> hyapp.activity.v1.ListLevelRewardsResponse - 106, // 205: hyapp.activity.v1.GrowthLevelService.ConsumeLevelEvent:output_type -> hyapp.activity.v1.ConsumeLevelEventResponse - 109, // 206: hyapp.activity.v1.GrowthLevelService.IssueRegistrationLevelBadges:output_type -> hyapp.activity.v1.IssueRegistrationLevelBadgesResponse - 122, // 207: hyapp.activity.v1.AchievementService.ListAchievements:output_type -> hyapp.activity.v1.ListAchievementsResponse - 124, // 208: hyapp.activity.v1.AchievementService.ConsumeAchievementEvent:output_type -> hyapp.activity.v1.ConsumeAchievementEventResponse - 127, // 209: hyapp.activity.v1.AchievementService.ListMyBadges:output_type -> hyapp.activity.v1.ListMyBadgesResponse - 129, // 210: hyapp.activity.v1.AchievementService.SetBadgeDisplay:output_type -> hyapp.activity.v1.SetBadgeDisplayResponse - 139, // 211: hyapp.activity.v1.LuckyGiftService.CheckLuckyGift:output_type -> hyapp.activity.v1.CheckLuckyGiftResponse - 142, // 212: hyapp.activity.v1.LuckyGiftService.ExecuteLuckyGiftDraw:output_type -> hyapp.activity.v1.ExecuteLuckyGiftDrawResponse - 33, // 213: hyapp.activity.v1.BroadcastService.EnsureBroadcastGroups:output_type -> hyapp.activity.v1.EnsureBroadcastGroupsResponse - 36, // 214: hyapp.activity.v1.BroadcastService.PublishRegionBroadcast:output_type -> hyapp.activity.v1.PublishBroadcastResponse - 36, // 215: hyapp.activity.v1.BroadcastService.PublishGlobalBroadcast:output_type -> hyapp.activity.v1.PublishBroadcastResponse - 38, // 216: hyapp.activity.v1.BroadcastService.RemoveRegionBroadcastMember:output_type -> hyapp.activity.v1.RemoveRegionBroadcastMemberResponse - 39, // 217: hyapp.activity.v1.BroadcastService.ProcessBroadcastOutboxBatch:output_type -> hyapp.activity.v1.ProcessBroadcastOutboxBatchResponse - 41, // 218: hyapp.activity.v1.RoomEventConsumerService.ConsumeRoomEvent:output_type -> hyapp.activity.v1.ConsumeRoomEventResponse - 44, // 219: hyapp.activity.v1.AdminTaskService.ListTaskDefinitions:output_type -> hyapp.activity.v1.ListTaskDefinitionsResponse - 46, // 220: hyapp.activity.v1.AdminTaskService.UpsertTaskDefinition:output_type -> hyapp.activity.v1.UpsertTaskDefinitionResponse - 48, // 221: hyapp.activity.v1.AdminTaskService.SetTaskDefinitionStatus:output_type -> hyapp.activity.v1.SetTaskDefinitionStatusResponse - 53, // 222: hyapp.activity.v1.RegistrationRewardService.GetRegistrationRewardEligibility:output_type -> hyapp.activity.v1.GetRegistrationRewardEligibilityResponse - 55, // 223: hyapp.activity.v1.RegistrationRewardService.IssueRegistrationReward:output_type -> hyapp.activity.v1.IssueRegistrationRewardResponse - 57, // 224: hyapp.activity.v1.AdminRegistrationRewardService.GetRegistrationRewardConfig:output_type -> hyapp.activity.v1.GetRegistrationRewardConfigResponse - 59, // 225: hyapp.activity.v1.AdminRegistrationRewardService.UpdateRegistrationRewardConfig:output_type -> hyapp.activity.v1.UpdateRegistrationRewardConfigResponse - 61, // 226: hyapp.activity.v1.AdminRegistrationRewardService.ListRegistrationRewardClaims:output_type -> hyapp.activity.v1.ListRegistrationRewardClaimsResponse - 67, // 227: hyapp.activity.v1.FirstRechargeRewardService.GetFirstRechargeRewardStatus:output_type -> hyapp.activity.v1.GetFirstRechargeRewardStatusResponse - 69, // 228: hyapp.activity.v1.FirstRechargeRewardService.ConsumeFirstRechargeReward:output_type -> hyapp.activity.v1.ConsumeFirstRechargeRewardResponse - 71, // 229: hyapp.activity.v1.AdminFirstRechargeRewardService.GetFirstRechargeRewardConfig:output_type -> hyapp.activity.v1.GetFirstRechargeRewardConfigResponse - 73, // 230: hyapp.activity.v1.AdminFirstRechargeRewardService.UpdateFirstRechargeRewardConfig:output_type -> hyapp.activity.v1.UpdateFirstRechargeRewardConfigResponse - 75, // 231: hyapp.activity.v1.AdminFirstRechargeRewardService.ListFirstRechargeRewardClaims:output_type -> hyapp.activity.v1.ListFirstRechargeRewardClaimsResponse - 80, // 232: hyapp.activity.v1.SevenDayCheckInService.GetSevenDayCheckInStatus:output_type -> hyapp.activity.v1.GetSevenDayCheckInStatusResponse - 82, // 233: hyapp.activity.v1.SevenDayCheckInService.SignSevenDayCheckIn:output_type -> hyapp.activity.v1.SignSevenDayCheckInResponse - 84, // 234: hyapp.activity.v1.AdminSevenDayCheckInService.GetSevenDayCheckInConfig:output_type -> hyapp.activity.v1.GetSevenDayCheckInConfigResponse - 86, // 235: hyapp.activity.v1.AdminSevenDayCheckInService.UpdateSevenDayCheckInConfig:output_type -> hyapp.activity.v1.UpdateSevenDayCheckInConfigResponse - 89, // 236: hyapp.activity.v1.AdminSevenDayCheckInService.ListSevenDayCheckInClaims:output_type -> hyapp.activity.v1.ListSevenDayCheckInClaimsResponse - 117, // 237: hyapp.activity.v1.AdminGrowthLevelService.ListLevelConfig:output_type -> hyapp.activity.v1.ListLevelConfigResponse - 111, // 238: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTrack:output_type -> hyapp.activity.v1.UpsertLevelTrackResponse - 113, // 239: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelRule:output_type -> hyapp.activity.v1.UpsertLevelRuleResponse - 115, // 240: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTier:output_type -> hyapp.activity.v1.UpsertLevelTierResponse - 122, // 241: hyapp.activity.v1.AdminAchievementService.ListAchievementDefinitions:output_type -> hyapp.activity.v1.ListAchievementsResponse - 131, // 242: hyapp.activity.v1.AdminAchievementService.UpsertAchievementDefinition:output_type -> hyapp.activity.v1.UpsertAchievementDefinitionResponse - 144, // 243: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftConfig:output_type -> hyapp.activity.v1.GetLuckyGiftConfigResponse - 146, // 244: hyapp.activity.v1.AdminLuckyGiftService.UpsertLuckyGiftConfig:output_type -> hyapp.activity.v1.UpsertLuckyGiftConfigResponse - 148, // 245: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftConfigs:output_type -> hyapp.activity.v1.ListLuckyGiftConfigsResponse - 150, // 246: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftDraws:output_type -> hyapp.activity.v1.ListLuckyGiftDrawsResponse - 153, // 247: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftDrawSummary:output_type -> hyapp.activity.v1.GetLuckyGiftDrawSummaryResponse - 186, // [186:248] is the sub-list for method output_type - 124, // [124:186] is the sub-list for method input_type - 124, // [124:124] is the sub-list for extension type_name - 124, // [124:124] is the sub-list for extension extendee - 0, // [0:124] is the sub-list for field type_name + 76, // 54: hyapp.activity.v1.CumulativeRechargeRewardConfig.tiers:type_name -> hyapp.activity.v1.CumulativeRechargeRewardTier + 77, // 55: hyapp.activity.v1.CumulativeRechargeRewardStatus.config:type_name -> hyapp.activity.v1.CumulativeRechargeRewardConfig + 79, // 56: hyapp.activity.v1.CumulativeRechargeRewardStatus.progress:type_name -> hyapp.activity.v1.CumulativeRechargeRewardProgress + 78, // 57: hyapp.activity.v1.CumulativeRechargeRewardStatus.grants:type_name -> hyapp.activity.v1.CumulativeRechargeRewardGrant + 0, // 58: hyapp.activity.v1.GetCumulativeRechargeRewardStatusRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 80, // 59: hyapp.activity.v1.GetCumulativeRechargeRewardStatusResponse.status:type_name -> hyapp.activity.v1.CumulativeRechargeRewardStatus + 0, // 60: hyapp.activity.v1.ConsumeCumulativeRechargeRewardRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 78, // 61: hyapp.activity.v1.ConsumeCumulativeRechargeRewardResponse.grants:type_name -> hyapp.activity.v1.CumulativeRechargeRewardGrant + 0, // 62: hyapp.activity.v1.GetCumulativeRechargeRewardConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 77, // 63: hyapp.activity.v1.GetCumulativeRechargeRewardConfigResponse.config:type_name -> hyapp.activity.v1.CumulativeRechargeRewardConfig + 0, // 64: hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 76, // 65: hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigRequest.tiers:type_name -> hyapp.activity.v1.CumulativeRechargeRewardTier + 77, // 66: hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigResponse.config:type_name -> hyapp.activity.v1.CumulativeRechargeRewardConfig + 0, // 67: hyapp.activity.v1.ListCumulativeRechargeRewardGrantsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 78, // 68: hyapp.activity.v1.ListCumulativeRechargeRewardGrantsResponse.grants:type_name -> hyapp.activity.v1.CumulativeRechargeRewardGrant + 91, // 69: hyapp.activity.v1.RoomTurnoverRewardConfig.tiers:type_name -> hyapp.activity.v1.RoomTurnoverRewardTier + 92, // 70: hyapp.activity.v1.RoomTurnoverRewardStatus.config:type_name -> hyapp.activity.v1.RoomTurnoverRewardConfig + 91, // 71: hyapp.activity.v1.RoomTurnoverRewardStatus.matched_tier:type_name -> hyapp.activity.v1.RoomTurnoverRewardTier + 93, // 72: hyapp.activity.v1.RoomTurnoverRewardStatus.latest_settlement:type_name -> hyapp.activity.v1.RoomTurnoverRewardSettlement + 0, // 73: hyapp.activity.v1.GetRoomTurnoverRewardStatusRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 94, // 74: hyapp.activity.v1.GetRoomTurnoverRewardStatusResponse.status:type_name -> hyapp.activity.v1.RoomTurnoverRewardStatus + 0, // 75: hyapp.activity.v1.GetRoomTurnoverRewardConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 92, // 76: hyapp.activity.v1.GetRoomTurnoverRewardConfigResponse.config:type_name -> hyapp.activity.v1.RoomTurnoverRewardConfig + 0, // 77: hyapp.activity.v1.UpdateRoomTurnoverRewardConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 91, // 78: hyapp.activity.v1.UpdateRoomTurnoverRewardConfigRequest.tiers:type_name -> hyapp.activity.v1.RoomTurnoverRewardTier + 92, // 79: hyapp.activity.v1.UpdateRoomTurnoverRewardConfigResponse.config:type_name -> hyapp.activity.v1.RoomTurnoverRewardConfig + 0, // 80: hyapp.activity.v1.ListRoomTurnoverRewardSettlementsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 93, // 81: hyapp.activity.v1.ListRoomTurnoverRewardSettlementsResponse.settlements:type_name -> hyapp.activity.v1.RoomTurnoverRewardSettlement + 0, // 82: hyapp.activity.v1.RetryRoomTurnoverRewardSettlementRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 93, // 83: hyapp.activity.v1.RetryRoomTurnoverRewardSettlementResponse.settlement:type_name -> hyapp.activity.v1.RoomTurnoverRewardSettlement + 105, // 84: hyapp.activity.v1.SevenDayCheckInConfig.rewards:type_name -> hyapp.activity.v1.SevenDayCheckInReward + 0, // 85: hyapp.activity.v1.GetSevenDayCheckInStatusRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 107, // 86: hyapp.activity.v1.GetSevenDayCheckInStatusResponse.rewards:type_name -> hyapp.activity.v1.SevenDayCheckInRewardStatus + 0, // 87: hyapp.activity.v1.SignSevenDayCheckInRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 0, // 88: hyapp.activity.v1.GetSevenDayCheckInConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 106, // 89: hyapp.activity.v1.GetSevenDayCheckInConfigResponse.config:type_name -> hyapp.activity.v1.SevenDayCheckInConfig + 0, // 90: hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 105, // 91: hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest.rewards:type_name -> hyapp.activity.v1.SevenDayCheckInReward + 106, // 92: hyapp.activity.v1.UpdateSevenDayCheckInConfigResponse.config:type_name -> hyapp.activity.v1.SevenDayCheckInConfig + 0, // 93: hyapp.activity.v1.ListSevenDayCheckInClaimsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 116, // 94: hyapp.activity.v1.ListSevenDayCheckInClaimsResponse.claims:type_name -> hyapp.activity.v1.SevenDayCheckInClaim + 0, // 95: hyapp.activity.v1.GetMyLevelOverviewRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 122, // 96: hyapp.activity.v1.GetMyLevelOverviewResponse.tracks:type_name -> hyapp.activity.v1.LevelTrackOverview + 0, // 97: hyapp.activity.v1.GetLevelTrackRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 122, // 98: hyapp.activity.v1.GetLevelTrackResponse.overview:type_name -> hyapp.activity.v1.LevelTrackOverview + 120, // 99: hyapp.activity.v1.GetLevelTrackResponse.rules:type_name -> hyapp.activity.v1.LevelRule + 121, // 100: hyapp.activity.v1.GetLevelTrackResponse.tiers:type_name -> hyapp.activity.v1.LevelTier + 127, // 101: hyapp.activity.v1.UserLevelDisplayProfile.wealth:type_name -> hyapp.activity.v1.LevelDisplayTrackProfile + 127, // 102: hyapp.activity.v1.UserLevelDisplayProfile.game:type_name -> hyapp.activity.v1.LevelDisplayTrackProfile + 127, // 103: hyapp.activity.v1.UserLevelDisplayProfile.charm:type_name -> hyapp.activity.v1.LevelDisplayTrackProfile + 0, // 104: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 128, // 105: hyapp.activity.v1.BatchGetUserLevelDisplayProfilesResponse.profiles:type_name -> hyapp.activity.v1.UserLevelDisplayProfile + 0, // 106: hyapp.activity.v1.ListLevelRewardsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 131, // 107: hyapp.activity.v1.ListLevelRewardsResponse.rewards:type_name -> hyapp.activity.v1.LevelRewardJob + 0, // 108: hyapp.activity.v1.ConsumeLevelEventRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 0, // 109: hyapp.activity.v1.IssueRegistrationLevelBadgesRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 136, // 110: hyapp.activity.v1.IssueRegistrationLevelBadgesResponse.grants:type_name -> hyapp.activity.v1.RegistrationLevelBadgeGrant + 0, // 111: hyapp.activity.v1.UpsertLevelTrackRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 119, // 112: hyapp.activity.v1.UpsertLevelTrackResponse.track:type_name -> hyapp.activity.v1.LevelTrack + 0, // 113: hyapp.activity.v1.UpsertLevelRuleRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 120, // 114: hyapp.activity.v1.UpsertLevelRuleResponse.rule:type_name -> hyapp.activity.v1.LevelRule + 0, // 115: hyapp.activity.v1.UpsertLevelTierRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 121, // 116: hyapp.activity.v1.UpsertLevelTierResponse.tier:type_name -> hyapp.activity.v1.LevelTier + 0, // 117: hyapp.activity.v1.ListLevelConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 119, // 118: hyapp.activity.v1.ListLevelConfigResponse.tracks:type_name -> hyapp.activity.v1.LevelTrack + 120, // 119: hyapp.activity.v1.ListLevelConfigResponse.rules:type_name -> hyapp.activity.v1.LevelRule + 121, // 120: hyapp.activity.v1.ListLevelConfigResponse.tiers:type_name -> hyapp.activity.v1.LevelTier + 147, // 121: hyapp.activity.v1.AchievementDefinition.conditions:type_name -> hyapp.activity.v1.AchievementCondition + 148, // 122: hyapp.activity.v1.UserAchievement.definition:type_name -> hyapp.activity.v1.AchievementDefinition + 0, // 123: hyapp.activity.v1.ListAchievementsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 149, // 124: hyapp.activity.v1.ListAchievementsResponse.achievements:type_name -> hyapp.activity.v1.UserAchievement + 0, // 125: hyapp.activity.v1.ConsumeAchievementEventRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 0, // 126: hyapp.activity.v1.ListMyBadgesRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 154, // 127: hyapp.activity.v1.ListMyBadgesResponse.strip_badges:type_name -> hyapp.activity.v1.BadgeDisplayItem + 154, // 128: hyapp.activity.v1.ListMyBadgesResponse.profile_tile_badges:type_name -> hyapp.activity.v1.BadgeDisplayItem + 154, // 129: hyapp.activity.v1.ListMyBadgesResponse.honor_badges:type_name -> hyapp.activity.v1.BadgeDisplayItem + 0, // 130: hyapp.activity.v1.SetBadgeDisplayRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 154, // 131: hyapp.activity.v1.SetBadgeDisplayRequest.items:type_name -> hyapp.activity.v1.BadgeDisplayItem + 156, // 132: hyapp.activity.v1.SetBadgeDisplayResponse.profile:type_name -> hyapp.activity.v1.ListMyBadgesResponse + 0, // 133: hyapp.activity.v1.UpsertAchievementDefinitionRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 147, // 134: hyapp.activity.v1.UpsertAchievementDefinitionRequest.conditions:type_name -> hyapp.activity.v1.AchievementCondition + 148, // 135: hyapp.activity.v1.UpsertAchievementDefinitionResponse.achievement:type_name -> hyapp.activity.v1.AchievementDefinition + 0, // 136: hyapp.activity.v1.LuckyGiftMeta.meta:type_name -> hyapp.activity.v1.RequestMeta + 162, // 137: hyapp.activity.v1.LuckyGiftConfig.tiers:type_name -> hyapp.activity.v1.LuckyGiftTier + 164, // 138: hyapp.activity.v1.LuckyGiftRuleStage.tiers:type_name -> hyapp.activity.v1.LuckyGiftRuleTier + 165, // 139: hyapp.activity.v1.LuckyGiftRuleConfig.stages:type_name -> hyapp.activity.v1.LuckyGiftRuleStage + 0, // 140: hyapp.activity.v1.CheckLuckyGiftRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 161, // 141: hyapp.activity.v1.ExecuteLuckyGiftDrawRequest.lucky_gift:type_name -> hyapp.activity.v1.LuckyGiftMeta + 169, // 142: hyapp.activity.v1.ExecuteLuckyGiftDrawResponse.result:type_name -> hyapp.activity.v1.LuckyGiftDrawResult + 0, // 143: hyapp.activity.v1.GetLuckyGiftConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 166, // 144: hyapp.activity.v1.GetLuckyGiftConfigResponse.config:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig + 0, // 145: hyapp.activity.v1.UpsertLuckyGiftConfigRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 166, // 146: hyapp.activity.v1.UpsertLuckyGiftConfigRequest.config:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig + 166, // 147: hyapp.activity.v1.UpsertLuckyGiftConfigResponse.config:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig + 0, // 148: hyapp.activity.v1.ListLuckyGiftConfigsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 166, // 149: hyapp.activity.v1.ListLuckyGiftConfigsResponse.configs:type_name -> hyapp.activity.v1.LuckyGiftRuleConfig + 0, // 150: hyapp.activity.v1.ListLuckyGiftDrawsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 169, // 151: hyapp.activity.v1.ListLuckyGiftDrawsResponse.draws:type_name -> hyapp.activity.v1.LuckyGiftDrawResult + 0, // 152: hyapp.activity.v1.GetLuckyGiftDrawSummaryRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 180, // 153: hyapp.activity.v1.GetLuckyGiftDrawSummaryResponse.summary:type_name -> hyapp.activity.v1.LuckyGiftDrawSummary + 183, // 154: hyapp.activity.v1.WeeklyStarCycle.gifts:type_name -> hyapp.activity.v1.WeeklyStarGift + 184, // 155: hyapp.activity.v1.WeeklyStarCycle.rewards:type_name -> hyapp.activity.v1.WeeklyStarReward + 0, // 156: hyapp.activity.v1.ListWeeklyStarCyclesRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 185, // 157: hyapp.activity.v1.ListWeeklyStarCyclesResponse.cycles:type_name -> hyapp.activity.v1.WeeklyStarCycle + 0, // 158: hyapp.activity.v1.GetWeeklyStarCycleRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 185, // 159: hyapp.activity.v1.GetWeeklyStarCycleResponse.cycle:type_name -> hyapp.activity.v1.WeeklyStarCycle + 0, // 160: hyapp.activity.v1.UpsertWeeklyStarCycleRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 185, // 161: hyapp.activity.v1.UpsertWeeklyStarCycleRequest.cycle:type_name -> hyapp.activity.v1.WeeklyStarCycle + 185, // 162: hyapp.activity.v1.UpsertWeeklyStarCycleResponse.cycle:type_name -> hyapp.activity.v1.WeeklyStarCycle + 0, // 163: hyapp.activity.v1.SetWeeklyStarCycleStatusRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 185, // 164: hyapp.activity.v1.SetWeeklyStarCycleStatusResponse.cycle:type_name -> hyapp.activity.v1.WeeklyStarCycle + 0, // 165: hyapp.activity.v1.ListWeeklyStarLeaderboardRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 185, // 166: hyapp.activity.v1.ListWeeklyStarLeaderboardResponse.cycle:type_name -> hyapp.activity.v1.WeeklyStarCycle + 186, // 167: hyapp.activity.v1.ListWeeklyStarLeaderboardResponse.entries:type_name -> hyapp.activity.v1.WeeklyStarLeaderboardEntry + 0, // 168: hyapp.activity.v1.ListWeeklyStarSettlementsRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 187, // 169: hyapp.activity.v1.ListWeeklyStarSettlementsResponse.settlements:type_name -> hyapp.activity.v1.WeeklyStarSettlement + 0, // 170: hyapp.activity.v1.GetWeeklyStarCurrentRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 185, // 171: hyapp.activity.v1.GetWeeklyStarCurrentResponse.cycle:type_name -> hyapp.activity.v1.WeeklyStarCycle + 186, // 172: hyapp.activity.v1.GetWeeklyStarCurrentResponse.top_entries:type_name -> hyapp.activity.v1.WeeklyStarLeaderboardEntry + 186, // 173: hyapp.activity.v1.GetWeeklyStarCurrentResponse.my_entry:type_name -> hyapp.activity.v1.WeeklyStarLeaderboardEntry + 0, // 174: hyapp.activity.v1.ListWeeklyStarHistoryRequest.meta:type_name -> hyapp.activity.v1.RequestMeta + 185, // 175: hyapp.activity.v1.WeeklyStarHistoryCycle.cycle:type_name -> hyapp.activity.v1.WeeklyStarCycle + 186, // 176: hyapp.activity.v1.WeeklyStarHistoryCycle.top_entries:type_name -> hyapp.activity.v1.WeeklyStarLeaderboardEntry + 203, // 177: hyapp.activity.v1.ListWeeklyStarHistoryResponse.cycles:type_name -> hyapp.activity.v1.WeeklyStarHistoryCycle + 1, // 178: hyapp.activity.v1.ActivityService.PingActivity:input_type -> hyapp.activity.v1.PingActivityRequest + 3, // 179: hyapp.activity.v1.ActivityService.GetActivityStatus:input_type -> hyapp.activity.v1.GetActivityStatusRequest + 6, // 180: hyapp.activity.v1.MessageInboxService.ListMessageTabs:input_type -> hyapp.activity.v1.ListMessageTabsRequest + 9, // 181: hyapp.activity.v1.MessageInboxService.ListInboxMessages:input_type -> hyapp.activity.v1.ListInboxMessagesRequest + 11, // 182: hyapp.activity.v1.MessageInboxService.MarkInboxMessageRead:input_type -> hyapp.activity.v1.MarkInboxMessageReadRequest + 13, // 183: hyapp.activity.v1.MessageInboxService.MarkInboxSectionRead:input_type -> hyapp.activity.v1.MarkInboxSectionReadRequest + 15, // 184: hyapp.activity.v1.MessageInboxService.DeleteInboxMessage:input_type -> hyapp.activity.v1.DeleteInboxMessageRequest + 17, // 185: hyapp.activity.v1.MessageInboxService.CreateInboxMessage:input_type -> hyapp.activity.v1.CreateInboxMessageRequest + 19, // 186: hyapp.activity.v1.MessageInboxService.CreateFanoutJob:input_type -> hyapp.activity.v1.CreateFanoutJobRequest + 21, // 187: hyapp.activity.v1.ActivityCronService.ProcessMessageFanoutBatch:input_type -> hyapp.activity.v1.CronBatchRequest + 21, // 188: hyapp.activity.v1.ActivityCronService.ProcessLevelRewardBatch:input_type -> hyapp.activity.v1.CronBatchRequest + 21, // 189: hyapp.activity.v1.ActivityCronService.ProcessAchievementRewardBatch:input_type -> hyapp.activity.v1.CronBatchRequest + 21, // 190: hyapp.activity.v1.ActivityCronService.ProcessRoomTurnoverRewardSettlementBatch:input_type -> hyapp.activity.v1.CronBatchRequest + 21, // 191: hyapp.activity.v1.ActivityCronService.ProcessWeeklyStarSettlementBatch:input_type -> hyapp.activity.v1.CronBatchRequest + 25, // 192: hyapp.activity.v1.TaskService.ListUserTasks:input_type -> hyapp.activity.v1.ListUserTasksRequest + 27, // 193: hyapp.activity.v1.TaskService.ClaimTaskReward:input_type -> hyapp.activity.v1.ClaimTaskRewardRequest + 29, // 194: hyapp.activity.v1.TaskService.ConsumeTaskEvent:input_type -> hyapp.activity.v1.ConsumeTaskEventRequest + 123, // 195: hyapp.activity.v1.GrowthLevelService.GetMyLevelOverview:input_type -> hyapp.activity.v1.GetMyLevelOverviewRequest + 125, // 196: hyapp.activity.v1.GrowthLevelService.GetLevelTrack:input_type -> hyapp.activity.v1.GetLevelTrackRequest + 129, // 197: hyapp.activity.v1.GrowthLevelService.BatchGetUserLevelDisplayProfiles:input_type -> hyapp.activity.v1.BatchGetUserLevelDisplayProfilesRequest + 132, // 198: hyapp.activity.v1.GrowthLevelService.ListLevelRewards:input_type -> hyapp.activity.v1.ListLevelRewardsRequest + 134, // 199: hyapp.activity.v1.GrowthLevelService.ConsumeLevelEvent:input_type -> hyapp.activity.v1.ConsumeLevelEventRequest + 137, // 200: hyapp.activity.v1.GrowthLevelService.IssueRegistrationLevelBadges:input_type -> hyapp.activity.v1.IssueRegistrationLevelBadgesRequest + 150, // 201: hyapp.activity.v1.AchievementService.ListAchievements:input_type -> hyapp.activity.v1.ListAchievementsRequest + 152, // 202: hyapp.activity.v1.AchievementService.ConsumeAchievementEvent:input_type -> hyapp.activity.v1.ConsumeAchievementEventRequest + 155, // 203: hyapp.activity.v1.AchievementService.ListMyBadges:input_type -> hyapp.activity.v1.ListMyBadgesRequest + 157, // 204: hyapp.activity.v1.AchievementService.SetBadgeDisplay:input_type -> hyapp.activity.v1.SetBadgeDisplayRequest + 167, // 205: hyapp.activity.v1.LuckyGiftService.CheckLuckyGift:input_type -> hyapp.activity.v1.CheckLuckyGiftRequest + 170, // 206: hyapp.activity.v1.LuckyGiftService.ExecuteLuckyGiftDraw:input_type -> hyapp.activity.v1.ExecuteLuckyGiftDrawRequest + 95, // 207: hyapp.activity.v1.RoomTurnoverRewardService.GetRoomTurnoverRewardStatus:input_type -> hyapp.activity.v1.GetRoomTurnoverRewardStatusRequest + 200, // 208: hyapp.activity.v1.WeeklyStarService.GetWeeklyStarCurrent:input_type -> hyapp.activity.v1.GetWeeklyStarCurrentRequest + 196, // 209: hyapp.activity.v1.WeeklyStarService.ListWeeklyStarLeaderboard:input_type -> hyapp.activity.v1.ListWeeklyStarLeaderboardRequest + 202, // 210: hyapp.activity.v1.WeeklyStarService.ListWeeklyStarHistory:input_type -> hyapp.activity.v1.ListWeeklyStarHistoryRequest + 32, // 211: hyapp.activity.v1.BroadcastService.EnsureBroadcastGroups:input_type -> hyapp.activity.v1.EnsureBroadcastGroupsRequest + 34, // 212: hyapp.activity.v1.BroadcastService.PublishRegionBroadcast:input_type -> hyapp.activity.v1.PublishRegionBroadcastRequest + 35, // 213: hyapp.activity.v1.BroadcastService.PublishGlobalBroadcast:input_type -> hyapp.activity.v1.PublishGlobalBroadcastRequest + 37, // 214: hyapp.activity.v1.BroadcastService.RemoveRegionBroadcastMember:input_type -> hyapp.activity.v1.RemoveRegionBroadcastMemberRequest + 21, // 215: hyapp.activity.v1.BroadcastService.ProcessBroadcastOutboxBatch:input_type -> hyapp.activity.v1.CronBatchRequest + 40, // 216: hyapp.activity.v1.RoomEventConsumerService.ConsumeRoomEvent:input_type -> hyapp.activity.v1.ConsumeRoomEventRequest + 43, // 217: hyapp.activity.v1.AdminTaskService.ListTaskDefinitions:input_type -> hyapp.activity.v1.ListTaskDefinitionsRequest + 45, // 218: hyapp.activity.v1.AdminTaskService.UpsertTaskDefinition:input_type -> hyapp.activity.v1.UpsertTaskDefinitionRequest + 47, // 219: hyapp.activity.v1.AdminTaskService.SetTaskDefinitionStatus:input_type -> hyapp.activity.v1.SetTaskDefinitionStatusRequest + 52, // 220: hyapp.activity.v1.RegistrationRewardService.GetRegistrationRewardEligibility:input_type -> hyapp.activity.v1.GetRegistrationRewardEligibilityRequest + 54, // 221: hyapp.activity.v1.RegistrationRewardService.IssueRegistrationReward:input_type -> hyapp.activity.v1.IssueRegistrationRewardRequest + 56, // 222: hyapp.activity.v1.AdminRegistrationRewardService.GetRegistrationRewardConfig:input_type -> hyapp.activity.v1.GetRegistrationRewardConfigRequest + 58, // 223: hyapp.activity.v1.AdminRegistrationRewardService.UpdateRegistrationRewardConfig:input_type -> hyapp.activity.v1.UpdateRegistrationRewardConfigRequest + 60, // 224: hyapp.activity.v1.AdminRegistrationRewardService.ListRegistrationRewardClaims:input_type -> hyapp.activity.v1.ListRegistrationRewardClaimsRequest + 66, // 225: hyapp.activity.v1.FirstRechargeRewardService.GetFirstRechargeRewardStatus:input_type -> hyapp.activity.v1.GetFirstRechargeRewardStatusRequest + 68, // 226: hyapp.activity.v1.FirstRechargeRewardService.ConsumeFirstRechargeReward:input_type -> hyapp.activity.v1.ConsumeFirstRechargeRewardRequest + 70, // 227: hyapp.activity.v1.AdminFirstRechargeRewardService.GetFirstRechargeRewardConfig:input_type -> hyapp.activity.v1.GetFirstRechargeRewardConfigRequest + 72, // 228: hyapp.activity.v1.AdminFirstRechargeRewardService.UpdateFirstRechargeRewardConfig:input_type -> hyapp.activity.v1.UpdateFirstRechargeRewardConfigRequest + 74, // 229: hyapp.activity.v1.AdminFirstRechargeRewardService.ListFirstRechargeRewardClaims:input_type -> hyapp.activity.v1.ListFirstRechargeRewardClaimsRequest + 81, // 230: hyapp.activity.v1.CumulativeRechargeRewardService.GetCumulativeRechargeRewardStatus:input_type -> hyapp.activity.v1.GetCumulativeRechargeRewardStatusRequest + 83, // 231: hyapp.activity.v1.CumulativeRechargeRewardService.ConsumeCumulativeRechargeReward:input_type -> hyapp.activity.v1.ConsumeCumulativeRechargeRewardRequest + 85, // 232: hyapp.activity.v1.AdminCumulativeRechargeRewardService.GetCumulativeRechargeRewardConfig:input_type -> hyapp.activity.v1.GetCumulativeRechargeRewardConfigRequest + 87, // 233: hyapp.activity.v1.AdminCumulativeRechargeRewardService.UpdateCumulativeRechargeRewardConfig:input_type -> hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigRequest + 89, // 234: hyapp.activity.v1.AdminCumulativeRechargeRewardService.ListCumulativeRechargeRewardGrants:input_type -> hyapp.activity.v1.ListCumulativeRechargeRewardGrantsRequest + 97, // 235: hyapp.activity.v1.AdminRoomTurnoverRewardService.GetRoomTurnoverRewardConfig:input_type -> hyapp.activity.v1.GetRoomTurnoverRewardConfigRequest + 99, // 236: hyapp.activity.v1.AdminRoomTurnoverRewardService.UpdateRoomTurnoverRewardConfig:input_type -> hyapp.activity.v1.UpdateRoomTurnoverRewardConfigRequest + 101, // 237: hyapp.activity.v1.AdminRoomTurnoverRewardService.ListRoomTurnoverRewardSettlements:input_type -> hyapp.activity.v1.ListRoomTurnoverRewardSettlementsRequest + 103, // 238: hyapp.activity.v1.AdminRoomTurnoverRewardService.RetryRoomTurnoverRewardSettlement:input_type -> hyapp.activity.v1.RetryRoomTurnoverRewardSettlementRequest + 188, // 239: hyapp.activity.v1.AdminWeeklyStarService.ListWeeklyStarCycles:input_type -> hyapp.activity.v1.ListWeeklyStarCyclesRequest + 192, // 240: hyapp.activity.v1.AdminWeeklyStarService.CreateWeeklyStarCycle:input_type -> hyapp.activity.v1.UpsertWeeklyStarCycleRequest + 190, // 241: hyapp.activity.v1.AdminWeeklyStarService.GetWeeklyStarCycle:input_type -> hyapp.activity.v1.GetWeeklyStarCycleRequest + 192, // 242: hyapp.activity.v1.AdminWeeklyStarService.UpdateWeeklyStarCycle:input_type -> hyapp.activity.v1.UpsertWeeklyStarCycleRequest + 194, // 243: hyapp.activity.v1.AdminWeeklyStarService.SetWeeklyStarCycleStatus:input_type -> hyapp.activity.v1.SetWeeklyStarCycleStatusRequest + 196, // 244: hyapp.activity.v1.AdminWeeklyStarService.ListWeeklyStarLeaderboard:input_type -> hyapp.activity.v1.ListWeeklyStarLeaderboardRequest + 198, // 245: hyapp.activity.v1.AdminWeeklyStarService.ListWeeklyStarSettlements:input_type -> hyapp.activity.v1.ListWeeklyStarSettlementsRequest + 108, // 246: hyapp.activity.v1.SevenDayCheckInService.GetSevenDayCheckInStatus:input_type -> hyapp.activity.v1.GetSevenDayCheckInStatusRequest + 110, // 247: hyapp.activity.v1.SevenDayCheckInService.SignSevenDayCheckIn:input_type -> hyapp.activity.v1.SignSevenDayCheckInRequest + 112, // 248: hyapp.activity.v1.AdminSevenDayCheckInService.GetSevenDayCheckInConfig:input_type -> hyapp.activity.v1.GetSevenDayCheckInConfigRequest + 114, // 249: hyapp.activity.v1.AdminSevenDayCheckInService.UpdateSevenDayCheckInConfig:input_type -> hyapp.activity.v1.UpdateSevenDayCheckInConfigRequest + 117, // 250: hyapp.activity.v1.AdminSevenDayCheckInService.ListSevenDayCheckInClaims:input_type -> hyapp.activity.v1.ListSevenDayCheckInClaimsRequest + 145, // 251: hyapp.activity.v1.AdminGrowthLevelService.ListLevelConfig:input_type -> hyapp.activity.v1.ListLevelConfigRequest + 139, // 252: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTrack:input_type -> hyapp.activity.v1.UpsertLevelTrackRequest + 141, // 253: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelRule:input_type -> hyapp.activity.v1.UpsertLevelRuleRequest + 143, // 254: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTier:input_type -> hyapp.activity.v1.UpsertLevelTierRequest + 150, // 255: hyapp.activity.v1.AdminAchievementService.ListAchievementDefinitions:input_type -> hyapp.activity.v1.ListAchievementsRequest + 159, // 256: hyapp.activity.v1.AdminAchievementService.UpsertAchievementDefinition:input_type -> hyapp.activity.v1.UpsertAchievementDefinitionRequest + 172, // 257: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftConfig:input_type -> hyapp.activity.v1.GetLuckyGiftConfigRequest + 174, // 258: hyapp.activity.v1.AdminLuckyGiftService.UpsertLuckyGiftConfig:input_type -> hyapp.activity.v1.UpsertLuckyGiftConfigRequest + 176, // 259: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftConfigs:input_type -> hyapp.activity.v1.ListLuckyGiftConfigsRequest + 178, // 260: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftDraws:input_type -> hyapp.activity.v1.ListLuckyGiftDrawsRequest + 181, // 261: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftDrawSummary:input_type -> hyapp.activity.v1.GetLuckyGiftDrawSummaryRequest + 2, // 262: hyapp.activity.v1.ActivityService.PingActivity:output_type -> hyapp.activity.v1.PingActivityResponse + 4, // 263: hyapp.activity.v1.ActivityService.GetActivityStatus:output_type -> hyapp.activity.v1.GetActivityStatusResponse + 7, // 264: hyapp.activity.v1.MessageInboxService.ListMessageTabs:output_type -> hyapp.activity.v1.ListMessageTabsResponse + 10, // 265: hyapp.activity.v1.MessageInboxService.ListInboxMessages:output_type -> hyapp.activity.v1.ListInboxMessagesResponse + 12, // 266: hyapp.activity.v1.MessageInboxService.MarkInboxMessageRead:output_type -> hyapp.activity.v1.MarkInboxMessageReadResponse + 14, // 267: hyapp.activity.v1.MessageInboxService.MarkInboxSectionRead:output_type -> hyapp.activity.v1.MarkInboxSectionReadResponse + 16, // 268: hyapp.activity.v1.MessageInboxService.DeleteInboxMessage:output_type -> hyapp.activity.v1.DeleteInboxMessageResponse + 18, // 269: hyapp.activity.v1.MessageInboxService.CreateInboxMessage:output_type -> hyapp.activity.v1.CreateInboxMessageResponse + 20, // 270: hyapp.activity.v1.MessageInboxService.CreateFanoutJob:output_type -> hyapp.activity.v1.CreateFanoutJobResponse + 22, // 271: hyapp.activity.v1.ActivityCronService.ProcessMessageFanoutBatch:output_type -> hyapp.activity.v1.CronBatchResponse + 22, // 272: hyapp.activity.v1.ActivityCronService.ProcessLevelRewardBatch:output_type -> hyapp.activity.v1.CronBatchResponse + 22, // 273: hyapp.activity.v1.ActivityCronService.ProcessAchievementRewardBatch:output_type -> hyapp.activity.v1.CronBatchResponse + 22, // 274: hyapp.activity.v1.ActivityCronService.ProcessRoomTurnoverRewardSettlementBatch:output_type -> hyapp.activity.v1.CronBatchResponse + 22, // 275: hyapp.activity.v1.ActivityCronService.ProcessWeeklyStarSettlementBatch:output_type -> hyapp.activity.v1.CronBatchResponse + 26, // 276: hyapp.activity.v1.TaskService.ListUserTasks:output_type -> hyapp.activity.v1.ListUserTasksResponse + 28, // 277: hyapp.activity.v1.TaskService.ClaimTaskReward:output_type -> hyapp.activity.v1.ClaimTaskRewardResponse + 30, // 278: hyapp.activity.v1.TaskService.ConsumeTaskEvent:output_type -> hyapp.activity.v1.ConsumeTaskEventResponse + 124, // 279: hyapp.activity.v1.GrowthLevelService.GetMyLevelOverview:output_type -> hyapp.activity.v1.GetMyLevelOverviewResponse + 126, // 280: hyapp.activity.v1.GrowthLevelService.GetLevelTrack:output_type -> hyapp.activity.v1.GetLevelTrackResponse + 130, // 281: hyapp.activity.v1.GrowthLevelService.BatchGetUserLevelDisplayProfiles:output_type -> hyapp.activity.v1.BatchGetUserLevelDisplayProfilesResponse + 133, // 282: hyapp.activity.v1.GrowthLevelService.ListLevelRewards:output_type -> hyapp.activity.v1.ListLevelRewardsResponse + 135, // 283: hyapp.activity.v1.GrowthLevelService.ConsumeLevelEvent:output_type -> hyapp.activity.v1.ConsumeLevelEventResponse + 138, // 284: hyapp.activity.v1.GrowthLevelService.IssueRegistrationLevelBadges:output_type -> hyapp.activity.v1.IssueRegistrationLevelBadgesResponse + 151, // 285: hyapp.activity.v1.AchievementService.ListAchievements:output_type -> hyapp.activity.v1.ListAchievementsResponse + 153, // 286: hyapp.activity.v1.AchievementService.ConsumeAchievementEvent:output_type -> hyapp.activity.v1.ConsumeAchievementEventResponse + 156, // 287: hyapp.activity.v1.AchievementService.ListMyBadges:output_type -> hyapp.activity.v1.ListMyBadgesResponse + 158, // 288: hyapp.activity.v1.AchievementService.SetBadgeDisplay:output_type -> hyapp.activity.v1.SetBadgeDisplayResponse + 168, // 289: hyapp.activity.v1.LuckyGiftService.CheckLuckyGift:output_type -> hyapp.activity.v1.CheckLuckyGiftResponse + 171, // 290: hyapp.activity.v1.LuckyGiftService.ExecuteLuckyGiftDraw:output_type -> hyapp.activity.v1.ExecuteLuckyGiftDrawResponse + 96, // 291: hyapp.activity.v1.RoomTurnoverRewardService.GetRoomTurnoverRewardStatus:output_type -> hyapp.activity.v1.GetRoomTurnoverRewardStatusResponse + 201, // 292: hyapp.activity.v1.WeeklyStarService.GetWeeklyStarCurrent:output_type -> hyapp.activity.v1.GetWeeklyStarCurrentResponse + 197, // 293: hyapp.activity.v1.WeeklyStarService.ListWeeklyStarLeaderboard:output_type -> hyapp.activity.v1.ListWeeklyStarLeaderboardResponse + 204, // 294: hyapp.activity.v1.WeeklyStarService.ListWeeklyStarHistory:output_type -> hyapp.activity.v1.ListWeeklyStarHistoryResponse + 33, // 295: hyapp.activity.v1.BroadcastService.EnsureBroadcastGroups:output_type -> hyapp.activity.v1.EnsureBroadcastGroupsResponse + 36, // 296: hyapp.activity.v1.BroadcastService.PublishRegionBroadcast:output_type -> hyapp.activity.v1.PublishBroadcastResponse + 36, // 297: hyapp.activity.v1.BroadcastService.PublishGlobalBroadcast:output_type -> hyapp.activity.v1.PublishBroadcastResponse + 38, // 298: hyapp.activity.v1.BroadcastService.RemoveRegionBroadcastMember:output_type -> hyapp.activity.v1.RemoveRegionBroadcastMemberResponse + 39, // 299: hyapp.activity.v1.BroadcastService.ProcessBroadcastOutboxBatch:output_type -> hyapp.activity.v1.ProcessBroadcastOutboxBatchResponse + 41, // 300: hyapp.activity.v1.RoomEventConsumerService.ConsumeRoomEvent:output_type -> hyapp.activity.v1.ConsumeRoomEventResponse + 44, // 301: hyapp.activity.v1.AdminTaskService.ListTaskDefinitions:output_type -> hyapp.activity.v1.ListTaskDefinitionsResponse + 46, // 302: hyapp.activity.v1.AdminTaskService.UpsertTaskDefinition:output_type -> hyapp.activity.v1.UpsertTaskDefinitionResponse + 48, // 303: hyapp.activity.v1.AdminTaskService.SetTaskDefinitionStatus:output_type -> hyapp.activity.v1.SetTaskDefinitionStatusResponse + 53, // 304: hyapp.activity.v1.RegistrationRewardService.GetRegistrationRewardEligibility:output_type -> hyapp.activity.v1.GetRegistrationRewardEligibilityResponse + 55, // 305: hyapp.activity.v1.RegistrationRewardService.IssueRegistrationReward:output_type -> hyapp.activity.v1.IssueRegistrationRewardResponse + 57, // 306: hyapp.activity.v1.AdminRegistrationRewardService.GetRegistrationRewardConfig:output_type -> hyapp.activity.v1.GetRegistrationRewardConfigResponse + 59, // 307: hyapp.activity.v1.AdminRegistrationRewardService.UpdateRegistrationRewardConfig:output_type -> hyapp.activity.v1.UpdateRegistrationRewardConfigResponse + 61, // 308: hyapp.activity.v1.AdminRegistrationRewardService.ListRegistrationRewardClaims:output_type -> hyapp.activity.v1.ListRegistrationRewardClaimsResponse + 67, // 309: hyapp.activity.v1.FirstRechargeRewardService.GetFirstRechargeRewardStatus:output_type -> hyapp.activity.v1.GetFirstRechargeRewardStatusResponse + 69, // 310: hyapp.activity.v1.FirstRechargeRewardService.ConsumeFirstRechargeReward:output_type -> hyapp.activity.v1.ConsumeFirstRechargeRewardResponse + 71, // 311: hyapp.activity.v1.AdminFirstRechargeRewardService.GetFirstRechargeRewardConfig:output_type -> hyapp.activity.v1.GetFirstRechargeRewardConfigResponse + 73, // 312: hyapp.activity.v1.AdminFirstRechargeRewardService.UpdateFirstRechargeRewardConfig:output_type -> hyapp.activity.v1.UpdateFirstRechargeRewardConfigResponse + 75, // 313: hyapp.activity.v1.AdminFirstRechargeRewardService.ListFirstRechargeRewardClaims:output_type -> hyapp.activity.v1.ListFirstRechargeRewardClaimsResponse + 82, // 314: hyapp.activity.v1.CumulativeRechargeRewardService.GetCumulativeRechargeRewardStatus:output_type -> hyapp.activity.v1.GetCumulativeRechargeRewardStatusResponse + 84, // 315: hyapp.activity.v1.CumulativeRechargeRewardService.ConsumeCumulativeRechargeReward:output_type -> hyapp.activity.v1.ConsumeCumulativeRechargeRewardResponse + 86, // 316: hyapp.activity.v1.AdminCumulativeRechargeRewardService.GetCumulativeRechargeRewardConfig:output_type -> hyapp.activity.v1.GetCumulativeRechargeRewardConfigResponse + 88, // 317: hyapp.activity.v1.AdminCumulativeRechargeRewardService.UpdateCumulativeRechargeRewardConfig:output_type -> hyapp.activity.v1.UpdateCumulativeRechargeRewardConfigResponse + 90, // 318: hyapp.activity.v1.AdminCumulativeRechargeRewardService.ListCumulativeRechargeRewardGrants:output_type -> hyapp.activity.v1.ListCumulativeRechargeRewardGrantsResponse + 98, // 319: hyapp.activity.v1.AdminRoomTurnoverRewardService.GetRoomTurnoverRewardConfig:output_type -> hyapp.activity.v1.GetRoomTurnoverRewardConfigResponse + 100, // 320: hyapp.activity.v1.AdminRoomTurnoverRewardService.UpdateRoomTurnoverRewardConfig:output_type -> hyapp.activity.v1.UpdateRoomTurnoverRewardConfigResponse + 102, // 321: hyapp.activity.v1.AdminRoomTurnoverRewardService.ListRoomTurnoverRewardSettlements:output_type -> hyapp.activity.v1.ListRoomTurnoverRewardSettlementsResponse + 104, // 322: hyapp.activity.v1.AdminRoomTurnoverRewardService.RetryRoomTurnoverRewardSettlement:output_type -> hyapp.activity.v1.RetryRoomTurnoverRewardSettlementResponse + 189, // 323: hyapp.activity.v1.AdminWeeklyStarService.ListWeeklyStarCycles:output_type -> hyapp.activity.v1.ListWeeklyStarCyclesResponse + 193, // 324: hyapp.activity.v1.AdminWeeklyStarService.CreateWeeklyStarCycle:output_type -> hyapp.activity.v1.UpsertWeeklyStarCycleResponse + 191, // 325: hyapp.activity.v1.AdminWeeklyStarService.GetWeeklyStarCycle:output_type -> hyapp.activity.v1.GetWeeklyStarCycleResponse + 193, // 326: hyapp.activity.v1.AdminWeeklyStarService.UpdateWeeklyStarCycle:output_type -> hyapp.activity.v1.UpsertWeeklyStarCycleResponse + 195, // 327: hyapp.activity.v1.AdminWeeklyStarService.SetWeeklyStarCycleStatus:output_type -> hyapp.activity.v1.SetWeeklyStarCycleStatusResponse + 197, // 328: hyapp.activity.v1.AdminWeeklyStarService.ListWeeklyStarLeaderboard:output_type -> hyapp.activity.v1.ListWeeklyStarLeaderboardResponse + 199, // 329: hyapp.activity.v1.AdminWeeklyStarService.ListWeeklyStarSettlements:output_type -> hyapp.activity.v1.ListWeeklyStarSettlementsResponse + 109, // 330: hyapp.activity.v1.SevenDayCheckInService.GetSevenDayCheckInStatus:output_type -> hyapp.activity.v1.GetSevenDayCheckInStatusResponse + 111, // 331: hyapp.activity.v1.SevenDayCheckInService.SignSevenDayCheckIn:output_type -> hyapp.activity.v1.SignSevenDayCheckInResponse + 113, // 332: hyapp.activity.v1.AdminSevenDayCheckInService.GetSevenDayCheckInConfig:output_type -> hyapp.activity.v1.GetSevenDayCheckInConfigResponse + 115, // 333: hyapp.activity.v1.AdminSevenDayCheckInService.UpdateSevenDayCheckInConfig:output_type -> hyapp.activity.v1.UpdateSevenDayCheckInConfigResponse + 118, // 334: hyapp.activity.v1.AdminSevenDayCheckInService.ListSevenDayCheckInClaims:output_type -> hyapp.activity.v1.ListSevenDayCheckInClaimsResponse + 146, // 335: hyapp.activity.v1.AdminGrowthLevelService.ListLevelConfig:output_type -> hyapp.activity.v1.ListLevelConfigResponse + 140, // 336: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTrack:output_type -> hyapp.activity.v1.UpsertLevelTrackResponse + 142, // 337: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelRule:output_type -> hyapp.activity.v1.UpsertLevelRuleResponse + 144, // 338: hyapp.activity.v1.AdminGrowthLevelService.UpsertLevelTier:output_type -> hyapp.activity.v1.UpsertLevelTierResponse + 151, // 339: hyapp.activity.v1.AdminAchievementService.ListAchievementDefinitions:output_type -> hyapp.activity.v1.ListAchievementsResponse + 160, // 340: hyapp.activity.v1.AdminAchievementService.UpsertAchievementDefinition:output_type -> hyapp.activity.v1.UpsertAchievementDefinitionResponse + 173, // 341: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftConfig:output_type -> hyapp.activity.v1.GetLuckyGiftConfigResponse + 175, // 342: hyapp.activity.v1.AdminLuckyGiftService.UpsertLuckyGiftConfig:output_type -> hyapp.activity.v1.UpsertLuckyGiftConfigResponse + 177, // 343: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftConfigs:output_type -> hyapp.activity.v1.ListLuckyGiftConfigsResponse + 179, // 344: hyapp.activity.v1.AdminLuckyGiftService.ListLuckyGiftDraws:output_type -> hyapp.activity.v1.ListLuckyGiftDrawsResponse + 182, // 345: hyapp.activity.v1.AdminLuckyGiftService.GetLuckyGiftDrawSummary:output_type -> hyapp.activity.v1.GetLuckyGiftDrawSummaryResponse + 262, // [262:346] is the sub-list for method output_type + 178, // [178:262] is the sub-list for method input_type + 178, // [178:178] is the sub-list for extension type_name + 178, // [178:178] is the sub-list for extension extendee + 0, // [0:178] is the sub-list for field type_name } func init() { file_proto_activity_v1_activity_proto_init() } @@ -14209,9 +18465,9 @@ func file_proto_activity_v1_activity_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_activity_v1_activity_proto_rawDesc), len(file_proto_activity_v1_activity_proto_rawDesc)), NumEnums: 0, - NumMessages: 154, + NumMessages: 205, NumExtensions: 0, - NumServices: 19, + NumServices: 25, }, GoTypes: file_proto_activity_v1_activity_proto_goTypes, DependencyIndexes: file_proto_activity_v1_activity_proto_depIdxs, diff --git a/api/proto/activity/v1/activity.proto b/api/proto/activity/v1/activity.proto index 50d6e5c3..a0990557 100644 --- a/api/proto/activity/v1/activity.proto +++ b/api/proto/activity/v1/activity.proto @@ -524,11 +524,14 @@ message ListRegistrationRewardClaimsRequest { int64 user_id = 3; int32 page = 4; int32 page_size = 5; + int64 claimed_start_ms = 6; + int64 claimed_end_ms = 7; } message ListRegistrationRewardClaimsResponse { repeated RegistrationRewardClaim claims = 1; int64 total = 2; + int64 today_claimed_count = 3; } // FirstRechargeRewardTier 是首冲奖励的一个金币充值档位;max_coin_amount=0 表示无上限。 @@ -647,6 +650,255 @@ message ListFirstRechargeRewardClaimsResponse { int64 total = 2; } +// CumulativeRechargeRewardTier 是累充奖励的单个 USD 档位;threshold_usd_minor 以美分存储。 +message CumulativeRechargeRewardTier { + int64 tier_id = 1; + string tier_code = 2; + string tier_name = 3; + int64 threshold_usd_minor = 4; + int64 resource_group_id = 5; + string status = 6; + int32 sort_order = 7; + int64 created_at_ms = 8; + int64 updated_at_ms = 9; +} + +// CumulativeRechargeRewardConfig 是当前 App 的累充奖励配置。 +message CumulativeRechargeRewardConfig { + string app_code = 1; + bool enabled = 2; + repeated CumulativeRechargeRewardTier tiers = 3; + int64 updated_by_admin_id = 4; + int64 created_at_ms = 5; + int64 updated_at_ms = 6; +} + +// CumulativeRechargeRewardGrant 是用户在一个 UTC 自然周内达成某个档位后的发放事实。 +message CumulativeRechargeRewardGrant { + string grant_id = 1; + string app_code = 2; + string cycle_key = 3; + int64 user_id = 4; + string event_id = 5; + string transaction_id = 6; + string command_id = 7; + int64 tier_id = 8; + string tier_code = 9; + int64 threshold_usd_minor = 10; + int64 resource_group_id = 11; + int64 reached_usd_minor = 12; + int64 qualifying_usd_minor = 13; + int64 recharge_coin_amount = 14; + string recharge_type = 15; + string status = 16; + string wallet_command_id = 17; + string wallet_grant_id = 18; + string failure_reason = 19; + int64 granted_at_ms = 20; + int64 created_at_ms = 21; + int64 updated_at_ms = 22; +} + +// CumulativeRechargeRewardProgress 是用户在当前 UTC 周期内的累充累计投影。 +message CumulativeRechargeRewardProgress { + string app_code = 1; + string cycle_key = 2; + int64 user_id = 3; + int64 total_usd_minor = 4; + int64 total_coin_amount = 5; + int64 first_recharged_at_ms = 6; + int64 last_recharged_at_ms = 7; + int64 updated_at_ms = 8; +} + +// CumulativeRechargeRewardStatus 是 App 累充奖励 H5 可直接展示的当前状态。 +message CumulativeRechargeRewardStatus { + CumulativeRechargeRewardConfig config = 1; + CumulativeRechargeRewardProgress progress = 2; + repeated CumulativeRechargeRewardGrant grants = 3; + string cycle_key = 4; + int64 period_start_ms = 5; + int64 period_end_ms = 6; + int64 server_time_ms = 7; +} + +message GetCumulativeRechargeRewardStatusRequest { + RequestMeta meta = 1; + int64 user_id = 2; +} + +message GetCumulativeRechargeRewardStatusResponse { + CumulativeRechargeRewardStatus status = 1; +} + +// ConsumeCumulativeRechargeRewardRequest 是 wallet 充值事实消费入口;所有成功充值都会按 UTC 周期累计。 +message ConsumeCumulativeRechargeRewardRequest { + RequestMeta meta = 1; + string event_id = 2; + string transaction_id = 3; + string command_id = 4; + int64 user_id = 5; + int64 recharge_coin_amount = 6; + int64 recharge_sequence = 7; + string recharge_type = 8; + int64 qualifying_usd_minor = 9; + string payload_json = 10; + int64 occurred_at_ms = 11; +} + +message ConsumeCumulativeRechargeRewardResponse { + repeated CumulativeRechargeRewardGrant grants = 1; + bool consumed = 2; + string reason = 3; +} + +message GetCumulativeRechargeRewardConfigRequest { + RequestMeta meta = 1; +} + +message GetCumulativeRechargeRewardConfigResponse { + CumulativeRechargeRewardConfig config = 1; +} + +message UpdateCumulativeRechargeRewardConfigRequest { + RequestMeta meta = 1; + bool enabled = 2; + repeated CumulativeRechargeRewardTier tiers = 3; + int64 operator_admin_id = 4; +} + +message UpdateCumulativeRechargeRewardConfigResponse { + CumulativeRechargeRewardConfig config = 1; +} + +message ListCumulativeRechargeRewardGrantsRequest { + RequestMeta meta = 1; + string status = 2; + int64 user_id = 3; + string cycle_key = 4; + int32 page = 5; + int32 page_size = 6; +} + +message ListCumulativeRechargeRewardGrantsResponse { + repeated CumulativeRechargeRewardGrant grants = 1; + int64 total = 2; +} + +// RoomTurnoverRewardTier 是房间流水奖励的单个档位;达到 threshold_coin_spent 后可命中奖励。 +message RoomTurnoverRewardTier { + int64 tier_id = 1; + string tier_code = 2; + string tier_name = 3; + int64 threshold_coin_spent = 4; + int64 reward_coin_amount = 5; + string status = 6; + int32 sort_order = 7; + int64 created_at_ms = 8; + int64 updated_at_ms = 9; +} + +// RoomTurnoverRewardConfig 是当前 App 的房间流水奖励配置;结算只命中最高有效档。 +message RoomTurnoverRewardConfig { + string app_code = 1; + bool enabled = 2; + repeated RoomTurnoverRewardTier tiers = 3; + int64 updated_by_admin_id = 4; + int64 created_at_ms = 5; + int64 updated_at_ms = 6; +} + +// RoomTurnoverRewardSettlement 是某个房间单个 UTC 周期的结算事实。 +message RoomTurnoverRewardSettlement { + string settlement_id = 1; + string app_code = 2; + string room_id = 3; + int64 owner_user_id = 4; + int64 period_start_ms = 5; + int64 period_end_ms = 6; + int64 coin_spent = 7; + int64 tier_id = 8; + string tier_code = 9; + int64 threshold_coin_spent = 10; + int64 reward_coin_amount = 11; + string status = 12; + string wallet_command_id = 13; + string wallet_transaction_id = 14; + string failure_reason = 15; + int64 settled_at_ms = 16; + int64 created_at_ms = 17; + int64 updated_at_ms = 18; +} + +// RoomTurnoverRewardStatus 给 App H5 返回当前 UTC 周期、房间流水、预计奖励和上一期结算。 +message RoomTurnoverRewardStatus { + RoomTurnoverRewardConfig config = 1; + string room_id = 2; + int64 owner_user_id = 3; + int64 period_start_ms = 4; + int64 period_end_ms = 5; + int64 current_coin_spent = 6; + int64 expected_reward_coin_amount = 7; + RoomTurnoverRewardTier matched_tier = 8; + RoomTurnoverRewardSettlement latest_settlement = 9; + int64 server_time_ms = 10; +} + +message GetRoomTurnoverRewardStatusRequest { + RequestMeta meta = 1; + int64 user_id = 2; + string room_id = 3; + int64 owner_user_id = 4; +} + +message GetRoomTurnoverRewardStatusResponse { + RoomTurnoverRewardStatus status = 1; +} + +message GetRoomTurnoverRewardConfigRequest { + RequestMeta meta = 1; +} + +message GetRoomTurnoverRewardConfigResponse { + RoomTurnoverRewardConfig config = 1; +} + +message UpdateRoomTurnoverRewardConfigRequest { + RequestMeta meta = 1; + bool enabled = 2; + repeated RoomTurnoverRewardTier tiers = 3; + int64 operator_admin_id = 4; +} + +message UpdateRoomTurnoverRewardConfigResponse { + RoomTurnoverRewardConfig config = 1; +} + +message ListRoomTurnoverRewardSettlementsRequest { + RequestMeta meta = 1; + string status = 2; + string room_id = 3; + int64 owner_user_id = 4; + int64 period_start_ms = 5; + int32 page = 6; + int32 page_size = 7; +} + +message ListRoomTurnoverRewardSettlementsResponse { + repeated RoomTurnoverRewardSettlement settlements = 1; + int64 total = 2; +} + +message RetryRoomTurnoverRewardSettlementRequest { + RequestMeta meta = 1; + string settlement_id = 2; + int64 operator_admin_id = 3; +} + +message RetryRoomTurnoverRewardSettlementResponse { + RoomTurnoverRewardSettlement settlement = 1; +} + // SevenDayCheckInReward 是七日签到配置中某一天的资源组奖励。 message SevenDayCheckInReward { int32 day_index = 1; @@ -1396,6 +1648,162 @@ message GetLuckyGiftDrawSummaryResponse { LuckyGiftDrawSummary summary = 1; } +// WeeklyStarGift 是周星周期内参与积分的指定礼物。 +message WeeklyStarGift { + string gift_id = 1; + int32 sort_order = 2; +} + +// WeeklyStarReward 是周星 Top 排名对应的资源组奖励。 +message WeeklyStarReward { + int32 rank_no = 1; + int64 resource_group_id = 2; +} + +// WeeklyStarCycle 是一个按 UTC epoch ms 生效的周星配置周期。 +message WeeklyStarCycle { + string cycle_id = 1; + string activity_code = 2; + int64 region_id = 3; + string title = 4; + string status = 5; + int64 start_ms = 6; + int64 end_ms = 7; + repeated WeeklyStarGift gifts = 8; + repeated WeeklyStarReward rewards = 9; + int64 created_by_admin_id = 10; + int64 updated_by_admin_id = 11; + int64 settled_at_ms = 12; + int64 created_at_ms = 13; + int64 updated_at_ms = 14; + string app_code = 15; +} + +// WeeklyStarLeaderboardEntry 是一个周期内用户维度的积分排名。 +message WeeklyStarLeaderboardEntry { + int32 rank_no = 1; + int64 user_id = 2; + int64 score = 3; + int64 first_scored_at_ms = 4; + int64 last_scored_at_ms = 5; +} + +// WeeklyStarSettlement 是周期结束后 Top 奖励发放的幂等记录。 +message WeeklyStarSettlement { + string settlement_id = 1; + string cycle_id = 2; + int32 rank_no = 3; + int64 user_id = 4; + int64 score = 5; + int64 resource_group_id = 6; + string wallet_command_id = 7; + string wallet_grant_id = 8; + string status = 9; + string failure_reason = 10; + int32 attempt_count = 11; + int64 created_at_ms = 12; + int64 updated_at_ms = 13; +} + +message ListWeeklyStarCyclesRequest { + RequestMeta meta = 1; + int64 region_id = 2; + string status = 3; + int64 start_ms = 4; + int64 end_ms = 5; + int32 page = 6; + int32 page_size = 7; +} + +message ListWeeklyStarCyclesResponse { + repeated WeeklyStarCycle cycles = 1; + int64 total = 2; +} + +message GetWeeklyStarCycleRequest { + RequestMeta meta = 1; + string cycle_id = 2; +} + +message GetWeeklyStarCycleResponse { + WeeklyStarCycle cycle = 1; +} + +message UpsertWeeklyStarCycleRequest { + RequestMeta meta = 1; + WeeklyStarCycle cycle = 2; + int64 operator_admin_id = 3; +} + +message UpsertWeeklyStarCycleResponse { + WeeklyStarCycle cycle = 1; + bool created = 2; +} + +message SetWeeklyStarCycleStatusRequest { + RequestMeta meta = 1; + string cycle_id = 2; + string status = 3; + int64 operator_admin_id = 4; +} + +message SetWeeklyStarCycleStatusResponse { + WeeklyStarCycle cycle = 1; +} + +message ListWeeklyStarLeaderboardRequest { + RequestMeta meta = 1; + string cycle_id = 2; + int64 region_id = 3; + int32 page_size = 4; + string page_token = 5; +} + +message ListWeeklyStarLeaderboardResponse { + WeeklyStarCycle cycle = 1; + repeated WeeklyStarLeaderboardEntry entries = 2; + string next_page_token = 3; + int64 total = 4; +} + +message ListWeeklyStarSettlementsRequest { + RequestMeta meta = 1; + string cycle_id = 2; +} + +message ListWeeklyStarSettlementsResponse { + repeated WeeklyStarSettlement settlements = 1; +} + +message GetWeeklyStarCurrentRequest { + RequestMeta meta = 1; + int64 user_id = 2; + int64 region_id = 3; +} + +message GetWeeklyStarCurrentResponse { + WeeklyStarCycle cycle = 1; + repeated WeeklyStarLeaderboardEntry top_entries = 2; + WeeklyStarLeaderboardEntry my_entry = 3; + int64 server_time_ms = 4; +} + +message ListWeeklyStarHistoryRequest { + RequestMeta meta = 1; + int64 region_id = 2; + int32 limit = 3; +} + +message WeeklyStarHistoryCycle { + WeeklyStarCycle cycle = 1; + repeated WeeklyStarLeaderboardEntry top_entries = 2; +} + +message ListWeeklyStarHistoryResponse { + repeated WeeklyStarHistoryCycle cycles = 1; + int64 server_time_ms = 2; +} + // ActivityService 只暴露同步查询,不把事件消费伪装成 RPC。 service ActivityService { rpc PingActivity(PingActivityRequest) returns (PingActivityResponse); @@ -1418,6 +1826,8 @@ service ActivityCronService { rpc ProcessMessageFanoutBatch(CronBatchRequest) returns (CronBatchResponse); rpc ProcessLevelRewardBatch(CronBatchRequest) returns (CronBatchResponse); rpc ProcessAchievementRewardBatch(CronBatchRequest) returns (CronBatchResponse); + rpc ProcessRoomTurnoverRewardSettlementBatch(CronBatchRequest) returns (CronBatchResponse); + rpc ProcessWeeklyStarSettlementBatch(CronBatchRequest) returns (CronBatchResponse); } // TaskService 拥有 App 任务查询、事件进度消费和领奖状态。 @@ -1451,6 +1861,18 @@ service LuckyGiftService { rpc ExecuteLuckyGiftDraw(ExecuteLuckyGiftDrawRequest) returns (ExecuteLuckyGiftDrawResponse); } +// RoomTurnoverRewardService owns App reads for the weekly room turnover reward activity. +service RoomTurnoverRewardService { + rpc GetRoomTurnoverRewardStatus(GetRoomTurnoverRewardStatusRequest) returns (GetRoomTurnoverRewardStatusResponse); +} + +// WeeklyStarService owns App reads for the weekly specified-gift score activity. +service WeeklyStarService { + rpc GetWeeklyStarCurrent(GetWeeklyStarCurrentRequest) returns (GetWeeklyStarCurrentResponse); + rpc ListWeeklyStarLeaderboard(ListWeeklyStarLeaderboardRequest) returns (ListWeeklyStarLeaderboardResponse); + rpc ListWeeklyStarHistory(ListWeeklyStarHistoryRequest) returns (ListWeeklyStarHistoryResponse); +} + // BroadcastService owns server-side Tencent IM global/region broadcast delivery. service BroadcastService { rpc EnsureBroadcastGroups(EnsureBroadcastGroupsRequest) returns (EnsureBroadcastGroupsResponse); @@ -1498,6 +1920,38 @@ service AdminFirstRechargeRewardService { rpc ListFirstRechargeRewardClaims(ListFirstRechargeRewardClaimsRequest) returns (ListFirstRechargeRewardClaimsResponse); } +// CumulativeRechargeRewardService 拥有 App 累充奖励查询和 wallet 充值事实消费入口。 +service CumulativeRechargeRewardService { + rpc GetCumulativeRechargeRewardStatus(GetCumulativeRechargeRewardStatusRequest) returns (GetCumulativeRechargeRewardStatusResponse); + rpc ConsumeCumulativeRechargeReward(ConsumeCumulativeRechargeRewardRequest) returns (ConsumeCumulativeRechargeRewardResponse); +} + +// AdminCumulativeRechargeRewardService 是后台活动管理访问累充奖励配置和发放记录的唯一入口。 +service AdminCumulativeRechargeRewardService { + rpc GetCumulativeRechargeRewardConfig(GetCumulativeRechargeRewardConfigRequest) returns (GetCumulativeRechargeRewardConfigResponse); + rpc UpdateCumulativeRechargeRewardConfig(UpdateCumulativeRechargeRewardConfigRequest) returns (UpdateCumulativeRechargeRewardConfigResponse); + rpc ListCumulativeRechargeRewardGrants(ListCumulativeRechargeRewardGrantsRequest) returns (ListCumulativeRechargeRewardGrantsResponse); +} + +// AdminRoomTurnoverRewardService 是后台活动管理访问房间流水奖励配置和结算记录的唯一入口。 +service AdminRoomTurnoverRewardService { + rpc GetRoomTurnoverRewardConfig(GetRoomTurnoverRewardConfigRequest) returns (GetRoomTurnoverRewardConfigResponse); + rpc UpdateRoomTurnoverRewardConfig(UpdateRoomTurnoverRewardConfigRequest) returns (UpdateRoomTurnoverRewardConfigResponse); + rpc ListRoomTurnoverRewardSettlements(ListRoomTurnoverRewardSettlementsRequest) returns (ListRoomTurnoverRewardSettlementsResponse); + rpc RetryRoomTurnoverRewardSettlement(RetryRoomTurnoverRewardSettlementRequest) returns (RetryRoomTurnoverRewardSettlementResponse); +} + +// AdminWeeklyStarService 是后台活动管理访问周星周期、榜单和结算结果的唯一入口。 +service AdminWeeklyStarService { + rpc ListWeeklyStarCycles(ListWeeklyStarCyclesRequest) returns (ListWeeklyStarCyclesResponse); + rpc CreateWeeklyStarCycle(UpsertWeeklyStarCycleRequest) returns (UpsertWeeklyStarCycleResponse); + rpc GetWeeklyStarCycle(GetWeeklyStarCycleRequest) returns (GetWeeklyStarCycleResponse); + rpc UpdateWeeklyStarCycle(UpsertWeeklyStarCycleRequest) returns (UpsertWeeklyStarCycleResponse); + rpc SetWeeklyStarCycleStatus(SetWeeklyStarCycleStatusRequest) returns (SetWeeklyStarCycleStatusResponse); + rpc ListWeeklyStarLeaderboard(ListWeeklyStarLeaderboardRequest) returns (ListWeeklyStarLeaderboardResponse); + rpc ListWeeklyStarSettlements(ListWeeklyStarSettlementsRequest) returns (ListWeeklyStarSettlementsResponse); +} + // SevenDayCheckInService 拥有 App 七日签到查询和签到命令。 service SevenDayCheckInService { rpc GetSevenDayCheckInStatus(GetSevenDayCheckInStatusRequest) returns (GetSevenDayCheckInStatusResponse); diff --git a/api/proto/activity/v1/activity_grpc.pb.go b/api/proto/activity/v1/activity_grpc.pb.go index d19e7524..34cbe40a 100644 --- a/api/proto/activity/v1/activity_grpc.pb.go +++ b/api/proto/activity/v1/activity_grpc.pb.go @@ -497,9 +497,11 @@ var MessageInboxService_ServiceDesc = grpc.ServiceDesc{ } const ( - ActivityCronService_ProcessMessageFanoutBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessMessageFanoutBatch" - ActivityCronService_ProcessLevelRewardBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessLevelRewardBatch" - ActivityCronService_ProcessAchievementRewardBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessAchievementRewardBatch" + ActivityCronService_ProcessMessageFanoutBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessMessageFanoutBatch" + ActivityCronService_ProcessLevelRewardBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessLevelRewardBatch" + ActivityCronService_ProcessAchievementRewardBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessAchievementRewardBatch" + ActivityCronService_ProcessRoomTurnoverRewardSettlementBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessRoomTurnoverRewardSettlementBatch" + ActivityCronService_ProcessWeeklyStarSettlementBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessWeeklyStarSettlementBatch" ) // ActivityCronServiceClient is the client API for ActivityCronService service. @@ -511,6 +513,8 @@ type ActivityCronServiceClient interface { ProcessMessageFanoutBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) ProcessLevelRewardBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) ProcessAchievementRewardBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) + ProcessRoomTurnoverRewardSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) + ProcessWeeklyStarSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) } type activityCronServiceClient struct { @@ -551,6 +555,26 @@ func (c *activityCronServiceClient) ProcessAchievementRewardBatch(ctx context.Co return out, nil } +func (c *activityCronServiceClient) ProcessRoomTurnoverRewardSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CronBatchResponse) + err := c.cc.Invoke(ctx, ActivityCronService_ProcessRoomTurnoverRewardSettlementBatch_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *activityCronServiceClient) ProcessWeeklyStarSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CronBatchResponse) + err := c.cc.Invoke(ctx, ActivityCronService_ProcessWeeklyStarSettlementBatch_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // ActivityCronServiceServer is the server API for ActivityCronService service. // All implementations must embed UnimplementedActivityCronServiceServer // for forward compatibility. @@ -560,6 +584,8 @@ type ActivityCronServiceServer interface { ProcessMessageFanoutBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) ProcessLevelRewardBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) ProcessAchievementRewardBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) + ProcessRoomTurnoverRewardSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) + ProcessWeeklyStarSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) mustEmbedUnimplementedActivityCronServiceServer() } @@ -579,6 +605,12 @@ func (UnimplementedActivityCronServiceServer) ProcessLevelRewardBatch(context.Co func (UnimplementedActivityCronServiceServer) ProcessAchievementRewardBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) { return nil, status.Error(codes.Unimplemented, "method ProcessAchievementRewardBatch not implemented") } +func (UnimplementedActivityCronServiceServer) ProcessRoomTurnoverRewardSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ProcessRoomTurnoverRewardSettlementBatch not implemented") +} +func (UnimplementedActivityCronServiceServer) ProcessWeeklyStarSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ProcessWeeklyStarSettlementBatch not implemented") +} func (UnimplementedActivityCronServiceServer) mustEmbedUnimplementedActivityCronServiceServer() {} func (UnimplementedActivityCronServiceServer) testEmbeddedByValue() {} @@ -654,6 +686,42 @@ func _ActivityCronService_ProcessAchievementRewardBatch_Handler(srv interface{}, return interceptor(ctx, in, info, handler) } +func _ActivityCronService_ProcessRoomTurnoverRewardSettlementBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CronBatchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ActivityCronServiceServer).ProcessRoomTurnoverRewardSettlementBatch(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ActivityCronService_ProcessRoomTurnoverRewardSettlementBatch_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ActivityCronServiceServer).ProcessRoomTurnoverRewardSettlementBatch(ctx, req.(*CronBatchRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ActivityCronService_ProcessWeeklyStarSettlementBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CronBatchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ActivityCronServiceServer).ProcessWeeklyStarSettlementBatch(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ActivityCronService_ProcessWeeklyStarSettlementBatch_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ActivityCronServiceServer).ProcessWeeklyStarSettlementBatch(ctx, req.(*CronBatchRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ActivityCronService_ServiceDesc is the grpc.ServiceDesc for ActivityCronService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -673,6 +741,14 @@ var ActivityCronService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ProcessAchievementRewardBatch", Handler: _ActivityCronService_ProcessAchievementRewardBatch_Handler, }, + { + MethodName: "ProcessRoomTurnoverRewardSettlementBatch", + Handler: _ActivityCronService_ProcessRoomTurnoverRewardSettlementBatch_Handler, + }, + { + MethodName: "ProcessWeeklyStarSettlementBatch", + Handler: _ActivityCronService_ProcessWeeklyStarSettlementBatch_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "proto/activity/v1/activity.proto", @@ -1520,6 +1596,295 @@ var LuckyGiftService_ServiceDesc = grpc.ServiceDesc{ Metadata: "proto/activity/v1/activity.proto", } +const ( + RoomTurnoverRewardService_GetRoomTurnoverRewardStatus_FullMethodName = "/hyapp.activity.v1.RoomTurnoverRewardService/GetRoomTurnoverRewardStatus" +) + +// RoomTurnoverRewardServiceClient is the client API for RoomTurnoverRewardService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// RoomTurnoverRewardService owns App reads for the weekly room turnover reward activity. +type RoomTurnoverRewardServiceClient interface { + GetRoomTurnoverRewardStatus(ctx context.Context, in *GetRoomTurnoverRewardStatusRequest, opts ...grpc.CallOption) (*GetRoomTurnoverRewardStatusResponse, error) +} + +type roomTurnoverRewardServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewRoomTurnoverRewardServiceClient(cc grpc.ClientConnInterface) RoomTurnoverRewardServiceClient { + return &roomTurnoverRewardServiceClient{cc} +} + +func (c *roomTurnoverRewardServiceClient) GetRoomTurnoverRewardStatus(ctx context.Context, in *GetRoomTurnoverRewardStatusRequest, opts ...grpc.CallOption) (*GetRoomTurnoverRewardStatusResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetRoomTurnoverRewardStatusResponse) + err := c.cc.Invoke(ctx, RoomTurnoverRewardService_GetRoomTurnoverRewardStatus_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RoomTurnoverRewardServiceServer is the server API for RoomTurnoverRewardService service. +// All implementations must embed UnimplementedRoomTurnoverRewardServiceServer +// for forward compatibility. +// +// RoomTurnoverRewardService owns App reads for the weekly room turnover reward activity. +type RoomTurnoverRewardServiceServer interface { + GetRoomTurnoverRewardStatus(context.Context, *GetRoomTurnoverRewardStatusRequest) (*GetRoomTurnoverRewardStatusResponse, error) + mustEmbedUnimplementedRoomTurnoverRewardServiceServer() +} + +// UnimplementedRoomTurnoverRewardServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedRoomTurnoverRewardServiceServer struct{} + +func (UnimplementedRoomTurnoverRewardServiceServer) GetRoomTurnoverRewardStatus(context.Context, *GetRoomTurnoverRewardStatusRequest) (*GetRoomTurnoverRewardStatusResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetRoomTurnoverRewardStatus not implemented") +} +func (UnimplementedRoomTurnoverRewardServiceServer) mustEmbedUnimplementedRoomTurnoverRewardServiceServer() { +} +func (UnimplementedRoomTurnoverRewardServiceServer) testEmbeddedByValue() {} + +// UnsafeRoomTurnoverRewardServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to RoomTurnoverRewardServiceServer will +// result in compilation errors. +type UnsafeRoomTurnoverRewardServiceServer interface { + mustEmbedUnimplementedRoomTurnoverRewardServiceServer() +} + +func RegisterRoomTurnoverRewardServiceServer(s grpc.ServiceRegistrar, srv RoomTurnoverRewardServiceServer) { + // If the following call panics, it indicates UnimplementedRoomTurnoverRewardServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&RoomTurnoverRewardService_ServiceDesc, srv) +} + +func _RoomTurnoverRewardService_GetRoomTurnoverRewardStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRoomTurnoverRewardStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RoomTurnoverRewardServiceServer).GetRoomTurnoverRewardStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RoomTurnoverRewardService_GetRoomTurnoverRewardStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RoomTurnoverRewardServiceServer).GetRoomTurnoverRewardStatus(ctx, req.(*GetRoomTurnoverRewardStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// RoomTurnoverRewardService_ServiceDesc is the grpc.ServiceDesc for RoomTurnoverRewardService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var RoomTurnoverRewardService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "hyapp.activity.v1.RoomTurnoverRewardService", + HandlerType: (*RoomTurnoverRewardServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetRoomTurnoverRewardStatus", + Handler: _RoomTurnoverRewardService_GetRoomTurnoverRewardStatus_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/activity/v1/activity.proto", +} + +const ( + WeeklyStarService_GetWeeklyStarCurrent_FullMethodName = "/hyapp.activity.v1.WeeklyStarService/GetWeeklyStarCurrent" + WeeklyStarService_ListWeeklyStarLeaderboard_FullMethodName = "/hyapp.activity.v1.WeeklyStarService/ListWeeklyStarLeaderboard" + WeeklyStarService_ListWeeklyStarHistory_FullMethodName = "/hyapp.activity.v1.WeeklyStarService/ListWeeklyStarHistory" +) + +// WeeklyStarServiceClient is the client API for WeeklyStarService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// WeeklyStarService owns App reads for the weekly specified-gift score activity. +type WeeklyStarServiceClient interface { + GetWeeklyStarCurrent(ctx context.Context, in *GetWeeklyStarCurrentRequest, opts ...grpc.CallOption) (*GetWeeklyStarCurrentResponse, error) + ListWeeklyStarLeaderboard(ctx context.Context, in *ListWeeklyStarLeaderboardRequest, opts ...grpc.CallOption) (*ListWeeklyStarLeaderboardResponse, error) + ListWeeklyStarHistory(ctx context.Context, in *ListWeeklyStarHistoryRequest, opts ...grpc.CallOption) (*ListWeeklyStarHistoryResponse, error) +} + +type weeklyStarServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewWeeklyStarServiceClient(cc grpc.ClientConnInterface) WeeklyStarServiceClient { + return &weeklyStarServiceClient{cc} +} + +func (c *weeklyStarServiceClient) GetWeeklyStarCurrent(ctx context.Context, in *GetWeeklyStarCurrentRequest, opts ...grpc.CallOption) (*GetWeeklyStarCurrentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetWeeklyStarCurrentResponse) + err := c.cc.Invoke(ctx, WeeklyStarService_GetWeeklyStarCurrent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *weeklyStarServiceClient) ListWeeklyStarLeaderboard(ctx context.Context, in *ListWeeklyStarLeaderboardRequest, opts ...grpc.CallOption) (*ListWeeklyStarLeaderboardResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListWeeklyStarLeaderboardResponse) + err := c.cc.Invoke(ctx, WeeklyStarService_ListWeeklyStarLeaderboard_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *weeklyStarServiceClient) ListWeeklyStarHistory(ctx context.Context, in *ListWeeklyStarHistoryRequest, opts ...grpc.CallOption) (*ListWeeklyStarHistoryResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListWeeklyStarHistoryResponse) + err := c.cc.Invoke(ctx, WeeklyStarService_ListWeeklyStarHistory_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WeeklyStarServiceServer is the server API for WeeklyStarService service. +// All implementations must embed UnimplementedWeeklyStarServiceServer +// for forward compatibility. +// +// WeeklyStarService owns App reads for the weekly specified-gift score activity. +type WeeklyStarServiceServer interface { + GetWeeklyStarCurrent(context.Context, *GetWeeklyStarCurrentRequest) (*GetWeeklyStarCurrentResponse, error) + ListWeeklyStarLeaderboard(context.Context, *ListWeeklyStarLeaderboardRequest) (*ListWeeklyStarLeaderboardResponse, error) + ListWeeklyStarHistory(context.Context, *ListWeeklyStarHistoryRequest) (*ListWeeklyStarHistoryResponse, error) + mustEmbedUnimplementedWeeklyStarServiceServer() +} + +// UnimplementedWeeklyStarServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedWeeklyStarServiceServer struct{} + +func (UnimplementedWeeklyStarServiceServer) GetWeeklyStarCurrent(context.Context, *GetWeeklyStarCurrentRequest) (*GetWeeklyStarCurrentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetWeeklyStarCurrent not implemented") +} +func (UnimplementedWeeklyStarServiceServer) ListWeeklyStarLeaderboard(context.Context, *ListWeeklyStarLeaderboardRequest) (*ListWeeklyStarLeaderboardResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarLeaderboard not implemented") +} +func (UnimplementedWeeklyStarServiceServer) ListWeeklyStarHistory(context.Context, *ListWeeklyStarHistoryRequest) (*ListWeeklyStarHistoryResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarHistory not implemented") +} +func (UnimplementedWeeklyStarServiceServer) mustEmbedUnimplementedWeeklyStarServiceServer() {} +func (UnimplementedWeeklyStarServiceServer) testEmbeddedByValue() {} + +// UnsafeWeeklyStarServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WeeklyStarServiceServer will +// result in compilation errors. +type UnsafeWeeklyStarServiceServer interface { + mustEmbedUnimplementedWeeklyStarServiceServer() +} + +func RegisterWeeklyStarServiceServer(s grpc.ServiceRegistrar, srv WeeklyStarServiceServer) { + // If the following call panics, it indicates UnimplementedWeeklyStarServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&WeeklyStarService_ServiceDesc, srv) +} + +func _WeeklyStarService_GetWeeklyStarCurrent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetWeeklyStarCurrentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WeeklyStarServiceServer).GetWeeklyStarCurrent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WeeklyStarService_GetWeeklyStarCurrent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WeeklyStarServiceServer).GetWeeklyStarCurrent(ctx, req.(*GetWeeklyStarCurrentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WeeklyStarService_ListWeeklyStarLeaderboard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListWeeklyStarLeaderboardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WeeklyStarServiceServer).ListWeeklyStarLeaderboard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WeeklyStarService_ListWeeklyStarLeaderboard_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WeeklyStarServiceServer).ListWeeklyStarLeaderboard(ctx, req.(*ListWeeklyStarLeaderboardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WeeklyStarService_ListWeeklyStarHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListWeeklyStarHistoryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WeeklyStarServiceServer).ListWeeklyStarHistory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WeeklyStarService_ListWeeklyStarHistory_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WeeklyStarServiceServer).ListWeeklyStarHistory(ctx, req.(*ListWeeklyStarHistoryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// WeeklyStarService_ServiceDesc is the grpc.ServiceDesc for WeeklyStarService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var WeeklyStarService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "hyapp.activity.v1.WeeklyStarService", + HandlerType: (*WeeklyStarServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetWeeklyStarCurrent", + Handler: _WeeklyStarService_GetWeeklyStarCurrent_Handler, + }, + { + MethodName: "ListWeeklyStarLeaderboard", + Handler: _WeeklyStarService_ListWeeklyStarLeaderboard_Handler, + }, + { + MethodName: "ListWeeklyStarHistory", + Handler: _WeeklyStarService_ListWeeklyStarHistory_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/activity/v1/activity.proto", +} + const ( BroadcastService_EnsureBroadcastGroups_FullMethodName = "/hyapp.activity.v1.BroadcastService/EnsureBroadcastGroups" BroadcastService_PublishRegionBroadcast_FullMethodName = "/hyapp.activity.v1.BroadcastService/PublishRegionBroadcast" @@ -2723,6 +3088,890 @@ var AdminFirstRechargeRewardService_ServiceDesc = grpc.ServiceDesc{ Metadata: "proto/activity/v1/activity.proto", } +const ( + CumulativeRechargeRewardService_GetCumulativeRechargeRewardStatus_FullMethodName = "/hyapp.activity.v1.CumulativeRechargeRewardService/GetCumulativeRechargeRewardStatus" + CumulativeRechargeRewardService_ConsumeCumulativeRechargeReward_FullMethodName = "/hyapp.activity.v1.CumulativeRechargeRewardService/ConsumeCumulativeRechargeReward" +) + +// CumulativeRechargeRewardServiceClient is the client API for CumulativeRechargeRewardService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// CumulativeRechargeRewardService 拥有 App 累充奖励查询和 wallet 充值事实消费入口。 +type CumulativeRechargeRewardServiceClient interface { + GetCumulativeRechargeRewardStatus(ctx context.Context, in *GetCumulativeRechargeRewardStatusRequest, opts ...grpc.CallOption) (*GetCumulativeRechargeRewardStatusResponse, error) + ConsumeCumulativeRechargeReward(ctx context.Context, in *ConsumeCumulativeRechargeRewardRequest, opts ...grpc.CallOption) (*ConsumeCumulativeRechargeRewardResponse, error) +} + +type cumulativeRechargeRewardServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewCumulativeRechargeRewardServiceClient(cc grpc.ClientConnInterface) CumulativeRechargeRewardServiceClient { + return &cumulativeRechargeRewardServiceClient{cc} +} + +func (c *cumulativeRechargeRewardServiceClient) GetCumulativeRechargeRewardStatus(ctx context.Context, in *GetCumulativeRechargeRewardStatusRequest, opts ...grpc.CallOption) (*GetCumulativeRechargeRewardStatusResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetCumulativeRechargeRewardStatusResponse) + err := c.cc.Invoke(ctx, CumulativeRechargeRewardService_GetCumulativeRechargeRewardStatus_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cumulativeRechargeRewardServiceClient) ConsumeCumulativeRechargeReward(ctx context.Context, in *ConsumeCumulativeRechargeRewardRequest, opts ...grpc.CallOption) (*ConsumeCumulativeRechargeRewardResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ConsumeCumulativeRechargeRewardResponse) + err := c.cc.Invoke(ctx, CumulativeRechargeRewardService_ConsumeCumulativeRechargeReward_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CumulativeRechargeRewardServiceServer is the server API for CumulativeRechargeRewardService service. +// All implementations must embed UnimplementedCumulativeRechargeRewardServiceServer +// for forward compatibility. +// +// CumulativeRechargeRewardService 拥有 App 累充奖励查询和 wallet 充值事实消费入口。 +type CumulativeRechargeRewardServiceServer interface { + GetCumulativeRechargeRewardStatus(context.Context, *GetCumulativeRechargeRewardStatusRequest) (*GetCumulativeRechargeRewardStatusResponse, error) + ConsumeCumulativeRechargeReward(context.Context, *ConsumeCumulativeRechargeRewardRequest) (*ConsumeCumulativeRechargeRewardResponse, error) + mustEmbedUnimplementedCumulativeRechargeRewardServiceServer() +} + +// UnimplementedCumulativeRechargeRewardServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedCumulativeRechargeRewardServiceServer struct{} + +func (UnimplementedCumulativeRechargeRewardServiceServer) GetCumulativeRechargeRewardStatus(context.Context, *GetCumulativeRechargeRewardStatusRequest) (*GetCumulativeRechargeRewardStatusResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetCumulativeRechargeRewardStatus not implemented") +} +func (UnimplementedCumulativeRechargeRewardServiceServer) ConsumeCumulativeRechargeReward(context.Context, *ConsumeCumulativeRechargeRewardRequest) (*ConsumeCumulativeRechargeRewardResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ConsumeCumulativeRechargeReward not implemented") +} +func (UnimplementedCumulativeRechargeRewardServiceServer) mustEmbedUnimplementedCumulativeRechargeRewardServiceServer() { +} +func (UnimplementedCumulativeRechargeRewardServiceServer) testEmbeddedByValue() {} + +// UnsafeCumulativeRechargeRewardServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CumulativeRechargeRewardServiceServer will +// result in compilation errors. +type UnsafeCumulativeRechargeRewardServiceServer interface { + mustEmbedUnimplementedCumulativeRechargeRewardServiceServer() +} + +func RegisterCumulativeRechargeRewardServiceServer(s grpc.ServiceRegistrar, srv CumulativeRechargeRewardServiceServer) { + // If the following call panics, it indicates UnimplementedCumulativeRechargeRewardServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&CumulativeRechargeRewardService_ServiceDesc, srv) +} + +func _CumulativeRechargeRewardService_GetCumulativeRechargeRewardStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCumulativeRechargeRewardStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CumulativeRechargeRewardServiceServer).GetCumulativeRechargeRewardStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CumulativeRechargeRewardService_GetCumulativeRechargeRewardStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CumulativeRechargeRewardServiceServer).GetCumulativeRechargeRewardStatus(ctx, req.(*GetCumulativeRechargeRewardStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CumulativeRechargeRewardService_ConsumeCumulativeRechargeReward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConsumeCumulativeRechargeRewardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CumulativeRechargeRewardServiceServer).ConsumeCumulativeRechargeReward(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CumulativeRechargeRewardService_ConsumeCumulativeRechargeReward_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CumulativeRechargeRewardServiceServer).ConsumeCumulativeRechargeReward(ctx, req.(*ConsumeCumulativeRechargeRewardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// CumulativeRechargeRewardService_ServiceDesc is the grpc.ServiceDesc for CumulativeRechargeRewardService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var CumulativeRechargeRewardService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "hyapp.activity.v1.CumulativeRechargeRewardService", + HandlerType: (*CumulativeRechargeRewardServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetCumulativeRechargeRewardStatus", + Handler: _CumulativeRechargeRewardService_GetCumulativeRechargeRewardStatus_Handler, + }, + { + MethodName: "ConsumeCumulativeRechargeReward", + Handler: _CumulativeRechargeRewardService_ConsumeCumulativeRechargeReward_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/activity/v1/activity.proto", +} + +const ( + AdminCumulativeRechargeRewardService_GetCumulativeRechargeRewardConfig_FullMethodName = "/hyapp.activity.v1.AdminCumulativeRechargeRewardService/GetCumulativeRechargeRewardConfig" + AdminCumulativeRechargeRewardService_UpdateCumulativeRechargeRewardConfig_FullMethodName = "/hyapp.activity.v1.AdminCumulativeRechargeRewardService/UpdateCumulativeRechargeRewardConfig" + AdminCumulativeRechargeRewardService_ListCumulativeRechargeRewardGrants_FullMethodName = "/hyapp.activity.v1.AdminCumulativeRechargeRewardService/ListCumulativeRechargeRewardGrants" +) + +// AdminCumulativeRechargeRewardServiceClient is the client API for AdminCumulativeRechargeRewardService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AdminCumulativeRechargeRewardService 是后台活动管理访问累充奖励配置和发放记录的唯一入口。 +type AdminCumulativeRechargeRewardServiceClient interface { + GetCumulativeRechargeRewardConfig(ctx context.Context, in *GetCumulativeRechargeRewardConfigRequest, opts ...grpc.CallOption) (*GetCumulativeRechargeRewardConfigResponse, error) + UpdateCumulativeRechargeRewardConfig(ctx context.Context, in *UpdateCumulativeRechargeRewardConfigRequest, opts ...grpc.CallOption) (*UpdateCumulativeRechargeRewardConfigResponse, error) + ListCumulativeRechargeRewardGrants(ctx context.Context, in *ListCumulativeRechargeRewardGrantsRequest, opts ...grpc.CallOption) (*ListCumulativeRechargeRewardGrantsResponse, error) +} + +type adminCumulativeRechargeRewardServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAdminCumulativeRechargeRewardServiceClient(cc grpc.ClientConnInterface) AdminCumulativeRechargeRewardServiceClient { + return &adminCumulativeRechargeRewardServiceClient{cc} +} + +func (c *adminCumulativeRechargeRewardServiceClient) GetCumulativeRechargeRewardConfig(ctx context.Context, in *GetCumulativeRechargeRewardConfigRequest, opts ...grpc.CallOption) (*GetCumulativeRechargeRewardConfigResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetCumulativeRechargeRewardConfigResponse) + err := c.cc.Invoke(ctx, AdminCumulativeRechargeRewardService_GetCumulativeRechargeRewardConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminCumulativeRechargeRewardServiceClient) UpdateCumulativeRechargeRewardConfig(ctx context.Context, in *UpdateCumulativeRechargeRewardConfigRequest, opts ...grpc.CallOption) (*UpdateCumulativeRechargeRewardConfigResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateCumulativeRechargeRewardConfigResponse) + err := c.cc.Invoke(ctx, AdminCumulativeRechargeRewardService_UpdateCumulativeRechargeRewardConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminCumulativeRechargeRewardServiceClient) ListCumulativeRechargeRewardGrants(ctx context.Context, in *ListCumulativeRechargeRewardGrantsRequest, opts ...grpc.CallOption) (*ListCumulativeRechargeRewardGrantsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListCumulativeRechargeRewardGrantsResponse) + err := c.cc.Invoke(ctx, AdminCumulativeRechargeRewardService_ListCumulativeRechargeRewardGrants_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdminCumulativeRechargeRewardServiceServer is the server API for AdminCumulativeRechargeRewardService service. +// All implementations must embed UnimplementedAdminCumulativeRechargeRewardServiceServer +// for forward compatibility. +// +// AdminCumulativeRechargeRewardService 是后台活动管理访问累充奖励配置和发放记录的唯一入口。 +type AdminCumulativeRechargeRewardServiceServer interface { + GetCumulativeRechargeRewardConfig(context.Context, *GetCumulativeRechargeRewardConfigRequest) (*GetCumulativeRechargeRewardConfigResponse, error) + UpdateCumulativeRechargeRewardConfig(context.Context, *UpdateCumulativeRechargeRewardConfigRequest) (*UpdateCumulativeRechargeRewardConfigResponse, error) + ListCumulativeRechargeRewardGrants(context.Context, *ListCumulativeRechargeRewardGrantsRequest) (*ListCumulativeRechargeRewardGrantsResponse, error) + mustEmbedUnimplementedAdminCumulativeRechargeRewardServiceServer() +} + +// UnimplementedAdminCumulativeRechargeRewardServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAdminCumulativeRechargeRewardServiceServer struct{} + +func (UnimplementedAdminCumulativeRechargeRewardServiceServer) GetCumulativeRechargeRewardConfig(context.Context, *GetCumulativeRechargeRewardConfigRequest) (*GetCumulativeRechargeRewardConfigResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetCumulativeRechargeRewardConfig not implemented") +} +func (UnimplementedAdminCumulativeRechargeRewardServiceServer) UpdateCumulativeRechargeRewardConfig(context.Context, *UpdateCumulativeRechargeRewardConfigRequest) (*UpdateCumulativeRechargeRewardConfigResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateCumulativeRechargeRewardConfig not implemented") +} +func (UnimplementedAdminCumulativeRechargeRewardServiceServer) ListCumulativeRechargeRewardGrants(context.Context, *ListCumulativeRechargeRewardGrantsRequest) (*ListCumulativeRechargeRewardGrantsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListCumulativeRechargeRewardGrants not implemented") +} +func (UnimplementedAdminCumulativeRechargeRewardServiceServer) mustEmbedUnimplementedAdminCumulativeRechargeRewardServiceServer() { +} +func (UnimplementedAdminCumulativeRechargeRewardServiceServer) testEmbeddedByValue() {} + +// UnsafeAdminCumulativeRechargeRewardServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdminCumulativeRechargeRewardServiceServer will +// result in compilation errors. +type UnsafeAdminCumulativeRechargeRewardServiceServer interface { + mustEmbedUnimplementedAdminCumulativeRechargeRewardServiceServer() +} + +func RegisterAdminCumulativeRechargeRewardServiceServer(s grpc.ServiceRegistrar, srv AdminCumulativeRechargeRewardServiceServer) { + // If the following call panics, it indicates UnimplementedAdminCumulativeRechargeRewardServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&AdminCumulativeRechargeRewardService_ServiceDesc, srv) +} + +func _AdminCumulativeRechargeRewardService_GetCumulativeRechargeRewardConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCumulativeRechargeRewardConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminCumulativeRechargeRewardServiceServer).GetCumulativeRechargeRewardConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminCumulativeRechargeRewardService_GetCumulativeRechargeRewardConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminCumulativeRechargeRewardServiceServer).GetCumulativeRechargeRewardConfig(ctx, req.(*GetCumulativeRechargeRewardConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminCumulativeRechargeRewardService_UpdateCumulativeRechargeRewardConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateCumulativeRechargeRewardConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminCumulativeRechargeRewardServiceServer).UpdateCumulativeRechargeRewardConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminCumulativeRechargeRewardService_UpdateCumulativeRechargeRewardConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminCumulativeRechargeRewardServiceServer).UpdateCumulativeRechargeRewardConfig(ctx, req.(*UpdateCumulativeRechargeRewardConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminCumulativeRechargeRewardService_ListCumulativeRechargeRewardGrants_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListCumulativeRechargeRewardGrantsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminCumulativeRechargeRewardServiceServer).ListCumulativeRechargeRewardGrants(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminCumulativeRechargeRewardService_ListCumulativeRechargeRewardGrants_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminCumulativeRechargeRewardServiceServer).ListCumulativeRechargeRewardGrants(ctx, req.(*ListCumulativeRechargeRewardGrantsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AdminCumulativeRechargeRewardService_ServiceDesc is the grpc.ServiceDesc for AdminCumulativeRechargeRewardService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AdminCumulativeRechargeRewardService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "hyapp.activity.v1.AdminCumulativeRechargeRewardService", + HandlerType: (*AdminCumulativeRechargeRewardServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetCumulativeRechargeRewardConfig", + Handler: _AdminCumulativeRechargeRewardService_GetCumulativeRechargeRewardConfig_Handler, + }, + { + MethodName: "UpdateCumulativeRechargeRewardConfig", + Handler: _AdminCumulativeRechargeRewardService_UpdateCumulativeRechargeRewardConfig_Handler, + }, + { + MethodName: "ListCumulativeRechargeRewardGrants", + Handler: _AdminCumulativeRechargeRewardService_ListCumulativeRechargeRewardGrants_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/activity/v1/activity.proto", +} + +const ( + AdminRoomTurnoverRewardService_GetRoomTurnoverRewardConfig_FullMethodName = "/hyapp.activity.v1.AdminRoomTurnoverRewardService/GetRoomTurnoverRewardConfig" + AdminRoomTurnoverRewardService_UpdateRoomTurnoverRewardConfig_FullMethodName = "/hyapp.activity.v1.AdminRoomTurnoverRewardService/UpdateRoomTurnoverRewardConfig" + AdminRoomTurnoverRewardService_ListRoomTurnoverRewardSettlements_FullMethodName = "/hyapp.activity.v1.AdminRoomTurnoverRewardService/ListRoomTurnoverRewardSettlements" + AdminRoomTurnoverRewardService_RetryRoomTurnoverRewardSettlement_FullMethodName = "/hyapp.activity.v1.AdminRoomTurnoverRewardService/RetryRoomTurnoverRewardSettlement" +) + +// AdminRoomTurnoverRewardServiceClient is the client API for AdminRoomTurnoverRewardService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AdminRoomTurnoverRewardService 是后台活动管理访问房间流水奖励配置和结算记录的唯一入口。 +type AdminRoomTurnoverRewardServiceClient interface { + GetRoomTurnoverRewardConfig(ctx context.Context, in *GetRoomTurnoverRewardConfigRequest, opts ...grpc.CallOption) (*GetRoomTurnoverRewardConfigResponse, error) + UpdateRoomTurnoverRewardConfig(ctx context.Context, in *UpdateRoomTurnoverRewardConfigRequest, opts ...grpc.CallOption) (*UpdateRoomTurnoverRewardConfigResponse, error) + ListRoomTurnoverRewardSettlements(ctx context.Context, in *ListRoomTurnoverRewardSettlementsRequest, opts ...grpc.CallOption) (*ListRoomTurnoverRewardSettlementsResponse, error) + RetryRoomTurnoverRewardSettlement(ctx context.Context, in *RetryRoomTurnoverRewardSettlementRequest, opts ...grpc.CallOption) (*RetryRoomTurnoverRewardSettlementResponse, error) +} + +type adminRoomTurnoverRewardServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAdminRoomTurnoverRewardServiceClient(cc grpc.ClientConnInterface) AdminRoomTurnoverRewardServiceClient { + return &adminRoomTurnoverRewardServiceClient{cc} +} + +func (c *adminRoomTurnoverRewardServiceClient) GetRoomTurnoverRewardConfig(ctx context.Context, in *GetRoomTurnoverRewardConfigRequest, opts ...grpc.CallOption) (*GetRoomTurnoverRewardConfigResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetRoomTurnoverRewardConfigResponse) + err := c.cc.Invoke(ctx, AdminRoomTurnoverRewardService_GetRoomTurnoverRewardConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminRoomTurnoverRewardServiceClient) UpdateRoomTurnoverRewardConfig(ctx context.Context, in *UpdateRoomTurnoverRewardConfigRequest, opts ...grpc.CallOption) (*UpdateRoomTurnoverRewardConfigResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateRoomTurnoverRewardConfigResponse) + err := c.cc.Invoke(ctx, AdminRoomTurnoverRewardService_UpdateRoomTurnoverRewardConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminRoomTurnoverRewardServiceClient) ListRoomTurnoverRewardSettlements(ctx context.Context, in *ListRoomTurnoverRewardSettlementsRequest, opts ...grpc.CallOption) (*ListRoomTurnoverRewardSettlementsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListRoomTurnoverRewardSettlementsResponse) + err := c.cc.Invoke(ctx, AdminRoomTurnoverRewardService_ListRoomTurnoverRewardSettlements_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminRoomTurnoverRewardServiceClient) RetryRoomTurnoverRewardSettlement(ctx context.Context, in *RetryRoomTurnoverRewardSettlementRequest, opts ...grpc.CallOption) (*RetryRoomTurnoverRewardSettlementResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RetryRoomTurnoverRewardSettlementResponse) + err := c.cc.Invoke(ctx, AdminRoomTurnoverRewardService_RetryRoomTurnoverRewardSettlement_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdminRoomTurnoverRewardServiceServer is the server API for AdminRoomTurnoverRewardService service. +// All implementations must embed UnimplementedAdminRoomTurnoverRewardServiceServer +// for forward compatibility. +// +// AdminRoomTurnoverRewardService 是后台活动管理访问房间流水奖励配置和结算记录的唯一入口。 +type AdminRoomTurnoverRewardServiceServer interface { + GetRoomTurnoverRewardConfig(context.Context, *GetRoomTurnoverRewardConfigRequest) (*GetRoomTurnoverRewardConfigResponse, error) + UpdateRoomTurnoverRewardConfig(context.Context, *UpdateRoomTurnoverRewardConfigRequest) (*UpdateRoomTurnoverRewardConfigResponse, error) + ListRoomTurnoverRewardSettlements(context.Context, *ListRoomTurnoverRewardSettlementsRequest) (*ListRoomTurnoverRewardSettlementsResponse, error) + RetryRoomTurnoverRewardSettlement(context.Context, *RetryRoomTurnoverRewardSettlementRequest) (*RetryRoomTurnoverRewardSettlementResponse, error) + mustEmbedUnimplementedAdminRoomTurnoverRewardServiceServer() +} + +// UnimplementedAdminRoomTurnoverRewardServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAdminRoomTurnoverRewardServiceServer struct{} + +func (UnimplementedAdminRoomTurnoverRewardServiceServer) GetRoomTurnoverRewardConfig(context.Context, *GetRoomTurnoverRewardConfigRequest) (*GetRoomTurnoverRewardConfigResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetRoomTurnoverRewardConfig not implemented") +} +func (UnimplementedAdminRoomTurnoverRewardServiceServer) UpdateRoomTurnoverRewardConfig(context.Context, *UpdateRoomTurnoverRewardConfigRequest) (*UpdateRoomTurnoverRewardConfigResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateRoomTurnoverRewardConfig not implemented") +} +func (UnimplementedAdminRoomTurnoverRewardServiceServer) ListRoomTurnoverRewardSettlements(context.Context, *ListRoomTurnoverRewardSettlementsRequest) (*ListRoomTurnoverRewardSettlementsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListRoomTurnoverRewardSettlements not implemented") +} +func (UnimplementedAdminRoomTurnoverRewardServiceServer) RetryRoomTurnoverRewardSettlement(context.Context, *RetryRoomTurnoverRewardSettlementRequest) (*RetryRoomTurnoverRewardSettlementResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RetryRoomTurnoverRewardSettlement not implemented") +} +func (UnimplementedAdminRoomTurnoverRewardServiceServer) mustEmbedUnimplementedAdminRoomTurnoverRewardServiceServer() { +} +func (UnimplementedAdminRoomTurnoverRewardServiceServer) testEmbeddedByValue() {} + +// UnsafeAdminRoomTurnoverRewardServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdminRoomTurnoverRewardServiceServer will +// result in compilation errors. +type UnsafeAdminRoomTurnoverRewardServiceServer interface { + mustEmbedUnimplementedAdminRoomTurnoverRewardServiceServer() +} + +func RegisterAdminRoomTurnoverRewardServiceServer(s grpc.ServiceRegistrar, srv AdminRoomTurnoverRewardServiceServer) { + // If the following call panics, it indicates UnimplementedAdminRoomTurnoverRewardServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&AdminRoomTurnoverRewardService_ServiceDesc, srv) +} + +func _AdminRoomTurnoverRewardService_GetRoomTurnoverRewardConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRoomTurnoverRewardConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminRoomTurnoverRewardServiceServer).GetRoomTurnoverRewardConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminRoomTurnoverRewardService_GetRoomTurnoverRewardConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminRoomTurnoverRewardServiceServer).GetRoomTurnoverRewardConfig(ctx, req.(*GetRoomTurnoverRewardConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminRoomTurnoverRewardService_UpdateRoomTurnoverRewardConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateRoomTurnoverRewardConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminRoomTurnoverRewardServiceServer).UpdateRoomTurnoverRewardConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminRoomTurnoverRewardService_UpdateRoomTurnoverRewardConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminRoomTurnoverRewardServiceServer).UpdateRoomTurnoverRewardConfig(ctx, req.(*UpdateRoomTurnoverRewardConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminRoomTurnoverRewardService_ListRoomTurnoverRewardSettlements_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRoomTurnoverRewardSettlementsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminRoomTurnoverRewardServiceServer).ListRoomTurnoverRewardSettlements(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminRoomTurnoverRewardService_ListRoomTurnoverRewardSettlements_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminRoomTurnoverRewardServiceServer).ListRoomTurnoverRewardSettlements(ctx, req.(*ListRoomTurnoverRewardSettlementsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminRoomTurnoverRewardService_RetryRoomTurnoverRewardSettlement_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RetryRoomTurnoverRewardSettlementRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminRoomTurnoverRewardServiceServer).RetryRoomTurnoverRewardSettlement(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminRoomTurnoverRewardService_RetryRoomTurnoverRewardSettlement_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminRoomTurnoverRewardServiceServer).RetryRoomTurnoverRewardSettlement(ctx, req.(*RetryRoomTurnoverRewardSettlementRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AdminRoomTurnoverRewardService_ServiceDesc is the grpc.ServiceDesc for AdminRoomTurnoverRewardService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AdminRoomTurnoverRewardService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "hyapp.activity.v1.AdminRoomTurnoverRewardService", + HandlerType: (*AdminRoomTurnoverRewardServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetRoomTurnoverRewardConfig", + Handler: _AdminRoomTurnoverRewardService_GetRoomTurnoverRewardConfig_Handler, + }, + { + MethodName: "UpdateRoomTurnoverRewardConfig", + Handler: _AdminRoomTurnoverRewardService_UpdateRoomTurnoverRewardConfig_Handler, + }, + { + MethodName: "ListRoomTurnoverRewardSettlements", + Handler: _AdminRoomTurnoverRewardService_ListRoomTurnoverRewardSettlements_Handler, + }, + { + MethodName: "RetryRoomTurnoverRewardSettlement", + Handler: _AdminRoomTurnoverRewardService_RetryRoomTurnoverRewardSettlement_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/activity/v1/activity.proto", +} + +const ( + AdminWeeklyStarService_ListWeeklyStarCycles_FullMethodName = "/hyapp.activity.v1.AdminWeeklyStarService/ListWeeklyStarCycles" + AdminWeeklyStarService_CreateWeeklyStarCycle_FullMethodName = "/hyapp.activity.v1.AdminWeeklyStarService/CreateWeeklyStarCycle" + AdminWeeklyStarService_GetWeeklyStarCycle_FullMethodName = "/hyapp.activity.v1.AdminWeeklyStarService/GetWeeklyStarCycle" + AdminWeeklyStarService_UpdateWeeklyStarCycle_FullMethodName = "/hyapp.activity.v1.AdminWeeklyStarService/UpdateWeeklyStarCycle" + AdminWeeklyStarService_SetWeeklyStarCycleStatus_FullMethodName = "/hyapp.activity.v1.AdminWeeklyStarService/SetWeeklyStarCycleStatus" + AdminWeeklyStarService_ListWeeklyStarLeaderboard_FullMethodName = "/hyapp.activity.v1.AdminWeeklyStarService/ListWeeklyStarLeaderboard" + AdminWeeklyStarService_ListWeeklyStarSettlements_FullMethodName = "/hyapp.activity.v1.AdminWeeklyStarService/ListWeeklyStarSettlements" +) + +// AdminWeeklyStarServiceClient is the client API for AdminWeeklyStarService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// AdminWeeklyStarService 是后台活动管理访问周星周期、榜单和结算结果的唯一入口。 +type AdminWeeklyStarServiceClient interface { + ListWeeklyStarCycles(ctx context.Context, in *ListWeeklyStarCyclesRequest, opts ...grpc.CallOption) (*ListWeeklyStarCyclesResponse, error) + CreateWeeklyStarCycle(ctx context.Context, in *UpsertWeeklyStarCycleRequest, opts ...grpc.CallOption) (*UpsertWeeklyStarCycleResponse, error) + GetWeeklyStarCycle(ctx context.Context, in *GetWeeklyStarCycleRequest, opts ...grpc.CallOption) (*GetWeeklyStarCycleResponse, error) + UpdateWeeklyStarCycle(ctx context.Context, in *UpsertWeeklyStarCycleRequest, opts ...grpc.CallOption) (*UpsertWeeklyStarCycleResponse, error) + SetWeeklyStarCycleStatus(ctx context.Context, in *SetWeeklyStarCycleStatusRequest, opts ...grpc.CallOption) (*SetWeeklyStarCycleStatusResponse, error) + ListWeeklyStarLeaderboard(ctx context.Context, in *ListWeeklyStarLeaderboardRequest, opts ...grpc.CallOption) (*ListWeeklyStarLeaderboardResponse, error) + ListWeeklyStarSettlements(ctx context.Context, in *ListWeeklyStarSettlementsRequest, opts ...grpc.CallOption) (*ListWeeklyStarSettlementsResponse, error) +} + +type adminWeeklyStarServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAdminWeeklyStarServiceClient(cc grpc.ClientConnInterface) AdminWeeklyStarServiceClient { + return &adminWeeklyStarServiceClient{cc} +} + +func (c *adminWeeklyStarServiceClient) ListWeeklyStarCycles(ctx context.Context, in *ListWeeklyStarCyclesRequest, opts ...grpc.CallOption) (*ListWeeklyStarCyclesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListWeeklyStarCyclesResponse) + err := c.cc.Invoke(ctx, AdminWeeklyStarService_ListWeeklyStarCycles_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminWeeklyStarServiceClient) CreateWeeklyStarCycle(ctx context.Context, in *UpsertWeeklyStarCycleRequest, opts ...grpc.CallOption) (*UpsertWeeklyStarCycleResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpsertWeeklyStarCycleResponse) + err := c.cc.Invoke(ctx, AdminWeeklyStarService_CreateWeeklyStarCycle_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminWeeklyStarServiceClient) GetWeeklyStarCycle(ctx context.Context, in *GetWeeklyStarCycleRequest, opts ...grpc.CallOption) (*GetWeeklyStarCycleResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetWeeklyStarCycleResponse) + err := c.cc.Invoke(ctx, AdminWeeklyStarService_GetWeeklyStarCycle_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminWeeklyStarServiceClient) UpdateWeeklyStarCycle(ctx context.Context, in *UpsertWeeklyStarCycleRequest, opts ...grpc.CallOption) (*UpsertWeeklyStarCycleResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpsertWeeklyStarCycleResponse) + err := c.cc.Invoke(ctx, AdminWeeklyStarService_UpdateWeeklyStarCycle_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminWeeklyStarServiceClient) SetWeeklyStarCycleStatus(ctx context.Context, in *SetWeeklyStarCycleStatusRequest, opts ...grpc.CallOption) (*SetWeeklyStarCycleStatusResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SetWeeklyStarCycleStatusResponse) + err := c.cc.Invoke(ctx, AdminWeeklyStarService_SetWeeklyStarCycleStatus_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminWeeklyStarServiceClient) ListWeeklyStarLeaderboard(ctx context.Context, in *ListWeeklyStarLeaderboardRequest, opts ...grpc.CallOption) (*ListWeeklyStarLeaderboardResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListWeeklyStarLeaderboardResponse) + err := c.cc.Invoke(ctx, AdminWeeklyStarService_ListWeeklyStarLeaderboard_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminWeeklyStarServiceClient) ListWeeklyStarSettlements(ctx context.Context, in *ListWeeklyStarSettlementsRequest, opts ...grpc.CallOption) (*ListWeeklyStarSettlementsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListWeeklyStarSettlementsResponse) + err := c.cc.Invoke(ctx, AdminWeeklyStarService_ListWeeklyStarSettlements_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdminWeeklyStarServiceServer is the server API for AdminWeeklyStarService service. +// All implementations must embed UnimplementedAdminWeeklyStarServiceServer +// for forward compatibility. +// +// AdminWeeklyStarService 是后台活动管理访问周星周期、榜单和结算结果的唯一入口。 +type AdminWeeklyStarServiceServer interface { + ListWeeklyStarCycles(context.Context, *ListWeeklyStarCyclesRequest) (*ListWeeklyStarCyclesResponse, error) + CreateWeeklyStarCycle(context.Context, *UpsertWeeklyStarCycleRequest) (*UpsertWeeklyStarCycleResponse, error) + GetWeeklyStarCycle(context.Context, *GetWeeklyStarCycleRequest) (*GetWeeklyStarCycleResponse, error) + UpdateWeeklyStarCycle(context.Context, *UpsertWeeklyStarCycleRequest) (*UpsertWeeklyStarCycleResponse, error) + SetWeeklyStarCycleStatus(context.Context, *SetWeeklyStarCycleStatusRequest) (*SetWeeklyStarCycleStatusResponse, error) + ListWeeklyStarLeaderboard(context.Context, *ListWeeklyStarLeaderboardRequest) (*ListWeeklyStarLeaderboardResponse, error) + ListWeeklyStarSettlements(context.Context, *ListWeeklyStarSettlementsRequest) (*ListWeeklyStarSettlementsResponse, error) + mustEmbedUnimplementedAdminWeeklyStarServiceServer() +} + +// UnimplementedAdminWeeklyStarServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAdminWeeklyStarServiceServer struct{} + +func (UnimplementedAdminWeeklyStarServiceServer) ListWeeklyStarCycles(context.Context, *ListWeeklyStarCyclesRequest) (*ListWeeklyStarCyclesResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarCycles not implemented") +} +func (UnimplementedAdminWeeklyStarServiceServer) CreateWeeklyStarCycle(context.Context, *UpsertWeeklyStarCycleRequest) (*UpsertWeeklyStarCycleResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateWeeklyStarCycle not implemented") +} +func (UnimplementedAdminWeeklyStarServiceServer) GetWeeklyStarCycle(context.Context, *GetWeeklyStarCycleRequest) (*GetWeeklyStarCycleResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetWeeklyStarCycle not implemented") +} +func (UnimplementedAdminWeeklyStarServiceServer) UpdateWeeklyStarCycle(context.Context, *UpsertWeeklyStarCycleRequest) (*UpsertWeeklyStarCycleResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateWeeklyStarCycle not implemented") +} +func (UnimplementedAdminWeeklyStarServiceServer) SetWeeklyStarCycleStatus(context.Context, *SetWeeklyStarCycleStatusRequest) (*SetWeeklyStarCycleStatusResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SetWeeklyStarCycleStatus not implemented") +} +func (UnimplementedAdminWeeklyStarServiceServer) ListWeeklyStarLeaderboard(context.Context, *ListWeeklyStarLeaderboardRequest) (*ListWeeklyStarLeaderboardResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarLeaderboard not implemented") +} +func (UnimplementedAdminWeeklyStarServiceServer) ListWeeklyStarSettlements(context.Context, *ListWeeklyStarSettlementsRequest) (*ListWeeklyStarSettlementsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListWeeklyStarSettlements not implemented") +} +func (UnimplementedAdminWeeklyStarServiceServer) mustEmbedUnimplementedAdminWeeklyStarServiceServer() { +} +func (UnimplementedAdminWeeklyStarServiceServer) testEmbeddedByValue() {} + +// UnsafeAdminWeeklyStarServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdminWeeklyStarServiceServer will +// result in compilation errors. +type UnsafeAdminWeeklyStarServiceServer interface { + mustEmbedUnimplementedAdminWeeklyStarServiceServer() +} + +func RegisterAdminWeeklyStarServiceServer(s grpc.ServiceRegistrar, srv AdminWeeklyStarServiceServer) { + // If the following call panics, it indicates UnimplementedAdminWeeklyStarServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&AdminWeeklyStarService_ServiceDesc, srv) +} + +func _AdminWeeklyStarService_ListWeeklyStarCycles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListWeeklyStarCyclesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminWeeklyStarServiceServer).ListWeeklyStarCycles(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminWeeklyStarService_ListWeeklyStarCycles_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminWeeklyStarServiceServer).ListWeeklyStarCycles(ctx, req.(*ListWeeklyStarCyclesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminWeeklyStarService_CreateWeeklyStarCycle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertWeeklyStarCycleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminWeeklyStarServiceServer).CreateWeeklyStarCycle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminWeeklyStarService_CreateWeeklyStarCycle_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminWeeklyStarServiceServer).CreateWeeklyStarCycle(ctx, req.(*UpsertWeeklyStarCycleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminWeeklyStarService_GetWeeklyStarCycle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetWeeklyStarCycleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminWeeklyStarServiceServer).GetWeeklyStarCycle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminWeeklyStarService_GetWeeklyStarCycle_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminWeeklyStarServiceServer).GetWeeklyStarCycle(ctx, req.(*GetWeeklyStarCycleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminWeeklyStarService_UpdateWeeklyStarCycle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertWeeklyStarCycleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminWeeklyStarServiceServer).UpdateWeeklyStarCycle(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminWeeklyStarService_UpdateWeeklyStarCycle_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminWeeklyStarServiceServer).UpdateWeeklyStarCycle(ctx, req.(*UpsertWeeklyStarCycleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminWeeklyStarService_SetWeeklyStarCycleStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetWeeklyStarCycleStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminWeeklyStarServiceServer).SetWeeklyStarCycleStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminWeeklyStarService_SetWeeklyStarCycleStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminWeeklyStarServiceServer).SetWeeklyStarCycleStatus(ctx, req.(*SetWeeklyStarCycleStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminWeeklyStarService_ListWeeklyStarLeaderboard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListWeeklyStarLeaderboardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminWeeklyStarServiceServer).ListWeeklyStarLeaderboard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminWeeklyStarService_ListWeeklyStarLeaderboard_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminWeeklyStarServiceServer).ListWeeklyStarLeaderboard(ctx, req.(*ListWeeklyStarLeaderboardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminWeeklyStarService_ListWeeklyStarSettlements_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListWeeklyStarSettlementsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminWeeklyStarServiceServer).ListWeeklyStarSettlements(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminWeeklyStarService_ListWeeklyStarSettlements_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminWeeklyStarServiceServer).ListWeeklyStarSettlements(ctx, req.(*ListWeeklyStarSettlementsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AdminWeeklyStarService_ServiceDesc is the grpc.ServiceDesc for AdminWeeklyStarService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AdminWeeklyStarService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "hyapp.activity.v1.AdminWeeklyStarService", + HandlerType: (*AdminWeeklyStarServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListWeeklyStarCycles", + Handler: _AdminWeeklyStarService_ListWeeklyStarCycles_Handler, + }, + { + MethodName: "CreateWeeklyStarCycle", + Handler: _AdminWeeklyStarService_CreateWeeklyStarCycle_Handler, + }, + { + MethodName: "GetWeeklyStarCycle", + Handler: _AdminWeeklyStarService_GetWeeklyStarCycle_Handler, + }, + { + MethodName: "UpdateWeeklyStarCycle", + Handler: _AdminWeeklyStarService_UpdateWeeklyStarCycle_Handler, + }, + { + MethodName: "SetWeeklyStarCycleStatus", + Handler: _AdminWeeklyStarService_SetWeeklyStarCycleStatus_Handler, + }, + { + MethodName: "ListWeeklyStarLeaderboard", + Handler: _AdminWeeklyStarService_ListWeeklyStarLeaderboard_Handler, + }, + { + MethodName: "ListWeeklyStarSettlements", + Handler: _AdminWeeklyStarService_ListWeeklyStarSettlements_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/activity/v1/activity.proto", +} + const ( SevenDayCheckInService_GetSevenDayCheckInStatus_FullMethodName = "/hyapp.activity.v1.SevenDayCheckInService/GetSevenDayCheckInStatus" SevenDayCheckInService_SignSevenDayCheckIn_FullMethodName = "/hyapp.activity.v1.SevenDayCheckInService/SignSevenDayCheckIn" diff --git a/api/proto/wallet/v1/wallet.pb.go b/api/proto/wallet/v1/wallet.pb.go index 31cefea3..0bfed8fb 100644 --- a/api/proto/wallet/v1/wallet.pb.go +++ b/api/proto/wallet/v1/wallet.pb.go @@ -12062,6 +12062,208 @@ func (x *CreditLuckyGiftRewardResponse) GetGrantedAtMs() int64 { return 0 } +// CreditRoomTurnoverRewardRequest 是 activity-service 每周房间流水奖励结算后的金币入账命令。 +type CreditRoomTurnoverRewardRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CommandId string `protobuf:"bytes,1,opt,name=command_id,json=commandId,proto3" json:"command_id,omitempty"` + AppCode string `protobuf:"bytes,2,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` + TargetUserId int64 `protobuf:"varint,3,opt,name=target_user_id,json=targetUserId,proto3" json:"target_user_id,omitempty"` + Amount int64 `protobuf:"varint,4,opt,name=amount,proto3" json:"amount,omitempty"` + SettlementId string `protobuf:"bytes,5,opt,name=settlement_id,json=settlementId,proto3" json:"settlement_id,omitempty"` + RoomId string `protobuf:"bytes,6,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` + PeriodStartMs int64 `protobuf:"varint,7,opt,name=period_start_ms,json=periodStartMs,proto3" json:"period_start_ms,omitempty"` + PeriodEndMs int64 `protobuf:"varint,8,opt,name=period_end_ms,json=periodEndMs,proto3" json:"period_end_ms,omitempty"` + CoinSpent int64 `protobuf:"varint,9,opt,name=coin_spent,json=coinSpent,proto3" json:"coin_spent,omitempty"` + TierId int64 `protobuf:"varint,10,opt,name=tier_id,json=tierId,proto3" json:"tier_id,omitempty"` + TierCode string `protobuf:"bytes,11,opt,name=tier_code,json=tierCode,proto3" json:"tier_code,omitempty"` + Reason string `protobuf:"bytes,12,opt,name=reason,proto3" json:"reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreditRoomTurnoverRewardRequest) Reset() { + *x = CreditRoomTurnoverRewardRequest{} + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[138] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreditRoomTurnoverRewardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreditRoomTurnoverRewardRequest) ProtoMessage() {} + +func (x *CreditRoomTurnoverRewardRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[138] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreditRoomTurnoverRewardRequest.ProtoReflect.Descriptor instead. +func (*CreditRoomTurnoverRewardRequest) Descriptor() ([]byte, []int) { + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{138} +} + +func (x *CreditRoomTurnoverRewardRequest) GetCommandId() string { + if x != nil { + return x.CommandId + } + return "" +} + +func (x *CreditRoomTurnoverRewardRequest) GetAppCode() string { + if x != nil { + return x.AppCode + } + return "" +} + +func (x *CreditRoomTurnoverRewardRequest) GetTargetUserId() int64 { + if x != nil { + return x.TargetUserId + } + return 0 +} + +func (x *CreditRoomTurnoverRewardRequest) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *CreditRoomTurnoverRewardRequest) GetSettlementId() string { + if x != nil { + return x.SettlementId + } + return "" +} + +func (x *CreditRoomTurnoverRewardRequest) GetRoomId() string { + if x != nil { + return x.RoomId + } + return "" +} + +func (x *CreditRoomTurnoverRewardRequest) GetPeriodStartMs() int64 { + if x != nil { + return x.PeriodStartMs + } + return 0 +} + +func (x *CreditRoomTurnoverRewardRequest) GetPeriodEndMs() int64 { + if x != nil { + return x.PeriodEndMs + } + return 0 +} + +func (x *CreditRoomTurnoverRewardRequest) GetCoinSpent() int64 { + if x != nil { + return x.CoinSpent + } + return 0 +} + +func (x *CreditRoomTurnoverRewardRequest) GetTierId() int64 { + if x != nil { + return x.TierId + } + return 0 +} + +func (x *CreditRoomTurnoverRewardRequest) GetTierCode() string { + if x != nil { + return x.TierCode + } + return "" +} + +func (x *CreditRoomTurnoverRewardRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +// CreditRoomTurnoverRewardResponse 返回房间流水奖励入账流水和用户 COIN 账后余额。 +type CreditRoomTurnoverRewardResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + TransactionId string `protobuf:"bytes,1,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` + Balance *AssetBalance `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance,omitempty"` + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` + GrantedAtMs int64 `protobuf:"varint,4,opt,name=granted_at_ms,json=grantedAtMs,proto3" json:"granted_at_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreditRoomTurnoverRewardResponse) Reset() { + *x = CreditRoomTurnoverRewardResponse{} + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[139] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreditRoomTurnoverRewardResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreditRoomTurnoverRewardResponse) ProtoMessage() {} + +func (x *CreditRoomTurnoverRewardResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[139] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreditRoomTurnoverRewardResponse.ProtoReflect.Descriptor instead. +func (*CreditRoomTurnoverRewardResponse) Descriptor() ([]byte, []int) { + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{139} +} + +func (x *CreditRoomTurnoverRewardResponse) GetTransactionId() string { + if x != nil { + return x.TransactionId + } + return "" +} + +func (x *CreditRoomTurnoverRewardResponse) GetBalance() *AssetBalance { + if x != nil { + return x.Balance + } + return nil +} + +func (x *CreditRoomTurnoverRewardResponse) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *CreditRoomTurnoverRewardResponse) GetGrantedAtMs() int64 { + if x != nil { + return x.GrantedAtMs + } + return 0 +} + // ApplyGameCoinChangeRequest 是 game-service 唯一可以调用的钱包游戏改账入口。 type ApplyGameCoinChangeRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -12083,7 +12285,7 @@ type ApplyGameCoinChangeRequest struct { func (x *ApplyGameCoinChangeRequest) Reset() { *x = ApplyGameCoinChangeRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[138] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12095,7 +12297,7 @@ func (x *ApplyGameCoinChangeRequest) String() string { func (*ApplyGameCoinChangeRequest) ProtoMessage() {} func (x *ApplyGameCoinChangeRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[138] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12108,7 +12310,7 @@ func (x *ApplyGameCoinChangeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyGameCoinChangeRequest.ProtoReflect.Descriptor instead. func (*ApplyGameCoinChangeRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{138} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{140} } func (x *ApplyGameCoinChangeRequest) GetRequestId() string { @@ -12207,7 +12409,7 @@ type ApplyGameCoinChangeResponse struct { func (x *ApplyGameCoinChangeResponse) Reset() { *x = ApplyGameCoinChangeResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[139] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12219,7 +12421,7 @@ func (x *ApplyGameCoinChangeResponse) String() string { func (*ApplyGameCoinChangeResponse) ProtoMessage() {} func (x *ApplyGameCoinChangeResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[139] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[141] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12232,7 +12434,7 @@ func (x *ApplyGameCoinChangeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyGameCoinChangeResponse.ProtoReflect.Descriptor instead. func (*ApplyGameCoinChangeResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{139} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{141} } func (x *ApplyGameCoinChangeResponse) GetWalletTransactionId() string { @@ -12275,7 +12477,7 @@ type RedPacketConfig struct { func (x *RedPacketConfig) Reset() { *x = RedPacketConfig{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[140] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12287,7 +12489,7 @@ func (x *RedPacketConfig) String() string { func (*RedPacketConfig) ProtoMessage() {} func (x *RedPacketConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[140] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[142] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12300,7 +12502,7 @@ func (x *RedPacketConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use RedPacketConfig.ProtoReflect.Descriptor instead. func (*RedPacketConfig) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{140} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{142} } func (x *RedPacketConfig) GetAppCode() string { @@ -12399,7 +12601,7 @@ type RedPacketClaim struct { func (x *RedPacketClaim) Reset() { *x = RedPacketClaim{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[141] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12411,7 +12613,7 @@ func (x *RedPacketClaim) String() string { func (*RedPacketClaim) ProtoMessage() {} func (x *RedPacketClaim) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[141] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[143] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12424,7 +12626,7 @@ func (x *RedPacketClaim) ProtoReflect() protoreflect.Message { // Deprecated: Use RedPacketClaim.ProtoReflect.Descriptor instead. func (*RedPacketClaim) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{141} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{143} } func (x *RedPacketClaim) GetAppCode() string { @@ -12534,7 +12736,7 @@ type RedPacket struct { func (x *RedPacket) Reset() { *x = RedPacket{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[142] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12546,7 +12748,7 @@ func (x *RedPacket) String() string { func (*RedPacket) ProtoMessage() {} func (x *RedPacket) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[142] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[144] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12559,7 +12761,7 @@ func (x *RedPacket) ProtoReflect() protoreflect.Message { // Deprecated: Use RedPacket.ProtoReflect.Descriptor instead. func (*RedPacket) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{142} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{144} } func (x *RedPacket) GetAppCode() string { @@ -12726,7 +12928,7 @@ type GetRedPacketConfigRequest struct { func (x *GetRedPacketConfigRequest) Reset() { *x = GetRedPacketConfigRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[143] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12738,7 +12940,7 @@ func (x *GetRedPacketConfigRequest) String() string { func (*GetRedPacketConfigRequest) ProtoMessage() {} func (x *GetRedPacketConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[143] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[145] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12751,7 +12953,7 @@ func (x *GetRedPacketConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRedPacketConfigRequest.ProtoReflect.Descriptor instead. func (*GetRedPacketConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{143} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{145} } func (x *GetRedPacketConfigRequest) GetRequestId() string { @@ -12778,7 +12980,7 @@ type GetRedPacketConfigResponse struct { func (x *GetRedPacketConfigResponse) Reset() { *x = GetRedPacketConfigResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[144] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12790,7 +12992,7 @@ func (x *GetRedPacketConfigResponse) String() string { func (*GetRedPacketConfigResponse) ProtoMessage() {} func (x *GetRedPacketConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[144] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[146] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12803,7 +13005,7 @@ func (x *GetRedPacketConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRedPacketConfigResponse.ProtoReflect.Descriptor instead. func (*GetRedPacketConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{144} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{146} } func (x *GetRedPacketConfigResponse) GetConfig() *RedPacketConfig { @@ -12838,7 +13040,7 @@ type UpdateRedPacketConfigRequest struct { func (x *UpdateRedPacketConfigRequest) Reset() { *x = UpdateRedPacketConfigRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[145] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12850,7 +13052,7 @@ func (x *UpdateRedPacketConfigRequest) String() string { func (*UpdateRedPacketConfigRequest) ProtoMessage() {} func (x *UpdateRedPacketConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[145] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[147] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12863,7 +13065,7 @@ func (x *UpdateRedPacketConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRedPacketConfigRequest.ProtoReflect.Descriptor instead. func (*UpdateRedPacketConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{145} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{147} } func (x *UpdateRedPacketConfigRequest) GetRequestId() string { @@ -12946,7 +13148,7 @@ type UpdateRedPacketConfigResponse struct { func (x *UpdateRedPacketConfigResponse) Reset() { *x = UpdateRedPacketConfigResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[146] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12958,7 +13160,7 @@ func (x *UpdateRedPacketConfigResponse) String() string { func (*UpdateRedPacketConfigResponse) ProtoMessage() {} func (x *UpdateRedPacketConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[146] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[148] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12971,7 +13173,7 @@ func (x *UpdateRedPacketConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRedPacketConfigResponse.ProtoReflect.Descriptor instead. func (*UpdateRedPacketConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{146} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{148} } func (x *UpdateRedPacketConfigResponse) GetConfig() *RedPacketConfig { @@ -13005,7 +13207,7 @@ type CreateRedPacketRequest struct { func (x *CreateRedPacketRequest) Reset() { *x = CreateRedPacketRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[147] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13017,7 +13219,7 @@ func (x *CreateRedPacketRequest) String() string { func (*CreateRedPacketRequest) ProtoMessage() {} func (x *CreateRedPacketRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[147] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[149] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13030,7 +13232,7 @@ func (x *CreateRedPacketRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateRedPacketRequest.ProtoReflect.Descriptor instead. func (*CreateRedPacketRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{147} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{149} } func (x *CreateRedPacketRequest) GetRequestId() string { @@ -13107,7 +13309,7 @@ type CreateRedPacketResponse struct { func (x *CreateRedPacketResponse) Reset() { *x = CreateRedPacketResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[148] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13119,7 +13321,7 @@ func (x *CreateRedPacketResponse) String() string { func (*CreateRedPacketResponse) ProtoMessage() {} func (x *CreateRedPacketResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[148] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[150] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13132,7 +13334,7 @@ func (x *CreateRedPacketResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateRedPacketResponse.ProtoReflect.Descriptor instead. func (*CreateRedPacketResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{148} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{150} } func (x *CreateRedPacketResponse) GetPacket() *RedPacket { @@ -13169,7 +13371,7 @@ type ClaimRedPacketRequest struct { func (x *ClaimRedPacketRequest) Reset() { *x = ClaimRedPacketRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[149] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13181,7 +13383,7 @@ func (x *ClaimRedPacketRequest) String() string { func (*ClaimRedPacketRequest) ProtoMessage() {} func (x *ClaimRedPacketRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[149] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[151] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13194,7 +13396,7 @@ func (x *ClaimRedPacketRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ClaimRedPacketRequest.ProtoReflect.Descriptor instead. func (*ClaimRedPacketRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{149} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{151} } func (x *ClaimRedPacketRequest) GetRequestId() string { @@ -13244,7 +13446,7 @@ type ClaimRedPacketResponse struct { func (x *ClaimRedPacketResponse) Reset() { *x = ClaimRedPacketResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[150] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13256,7 +13458,7 @@ func (x *ClaimRedPacketResponse) String() string { func (*ClaimRedPacketResponse) ProtoMessage() {} func (x *ClaimRedPacketResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[150] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13269,7 +13471,7 @@ func (x *ClaimRedPacketResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ClaimRedPacketResponse.ProtoReflect.Descriptor instead. func (*ClaimRedPacketResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{150} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{152} } func (x *ClaimRedPacketResponse) GetClaim() *RedPacketClaim { @@ -13320,7 +13522,7 @@ type ListRedPacketsRequest struct { func (x *ListRedPacketsRequest) Reset() { *x = ListRedPacketsRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[151] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13332,7 +13534,7 @@ func (x *ListRedPacketsRequest) String() string { func (*ListRedPacketsRequest) ProtoMessage() {} func (x *ListRedPacketsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[151] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13345,7 +13547,7 @@ func (x *ListRedPacketsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRedPacketsRequest.ProtoReflect.Descriptor instead. func (*ListRedPacketsRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{151} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{153} } func (x *ListRedPacketsRequest) GetRequestId() string { @@ -13443,7 +13645,7 @@ type ListRedPacketsResponse struct { func (x *ListRedPacketsResponse) Reset() { *x = ListRedPacketsResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[152] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13455,7 +13657,7 @@ func (x *ListRedPacketsResponse) String() string { func (*ListRedPacketsResponse) ProtoMessage() {} func (x *ListRedPacketsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[152] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13468,7 +13670,7 @@ func (x *ListRedPacketsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListRedPacketsResponse.ProtoReflect.Descriptor instead. func (*ListRedPacketsResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{152} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{154} } func (x *ListRedPacketsResponse) GetPackets() []*RedPacket { @@ -13505,7 +13707,7 @@ type GetRedPacketRequest struct { func (x *GetRedPacketRequest) Reset() { *x = GetRedPacketRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[153] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13517,7 +13719,7 @@ func (x *GetRedPacketRequest) String() string { func (*GetRedPacketRequest) ProtoMessage() {} func (x *GetRedPacketRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[153] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13530,7 +13732,7 @@ func (x *GetRedPacketRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRedPacketRequest.ProtoReflect.Descriptor instead. func (*GetRedPacketRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{153} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{155} } func (x *GetRedPacketRequest) GetRequestId() string { @@ -13578,7 +13780,7 @@ type GetRedPacketResponse struct { func (x *GetRedPacketResponse) Reset() { *x = GetRedPacketResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[154] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13590,7 +13792,7 @@ func (x *GetRedPacketResponse) String() string { func (*GetRedPacketResponse) ProtoMessage() {} func (x *GetRedPacketResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[154] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13603,7 +13805,7 @@ func (x *GetRedPacketResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRedPacketResponse.ProtoReflect.Descriptor instead. func (*GetRedPacketResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{154} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{156} } func (x *GetRedPacketResponse) GetPacket() *RedPacket { @@ -13631,7 +13833,7 @@ type ExpireRedPacketsRequest struct { func (x *ExpireRedPacketsRequest) Reset() { *x = ExpireRedPacketsRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[155] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13643,7 +13845,7 @@ func (x *ExpireRedPacketsRequest) String() string { func (*ExpireRedPacketsRequest) ProtoMessage() {} func (x *ExpireRedPacketsRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[155] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13656,7 +13858,7 @@ func (x *ExpireRedPacketsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExpireRedPacketsRequest.ProtoReflect.Descriptor instead. func (*ExpireRedPacketsRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{155} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{157} } func (x *ExpireRedPacketsRequest) GetRequestId() string { @@ -13691,7 +13893,7 @@ type ExpireRedPacketsResponse struct { func (x *ExpireRedPacketsResponse) Reset() { *x = ExpireRedPacketsResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[156] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13703,7 +13905,7 @@ func (x *ExpireRedPacketsResponse) String() string { func (*ExpireRedPacketsResponse) ProtoMessage() {} func (x *ExpireRedPacketsResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[156] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13716,7 +13918,7 @@ func (x *ExpireRedPacketsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ExpireRedPacketsResponse.ProtoReflect.Descriptor instead. func (*ExpireRedPacketsResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{156} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{158} } func (x *ExpireRedPacketsResponse) GetExpiredCount() int32 { @@ -13752,7 +13954,7 @@ type RetryRedPacketRefundRequest struct { func (x *RetryRedPacketRefundRequest) Reset() { *x = RetryRedPacketRefundRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[157] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13764,7 +13966,7 @@ func (x *RetryRedPacketRefundRequest) String() string { func (*RetryRedPacketRefundRequest) ProtoMessage() {} func (x *RetryRedPacketRefundRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[157] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13777,7 +13979,7 @@ func (x *RetryRedPacketRefundRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RetryRedPacketRefundRequest.ProtoReflect.Descriptor instead. func (*RetryRedPacketRefundRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{157} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{159} } func (x *RetryRedPacketRefundRequest) GetRequestId() string { @@ -13819,7 +14021,7 @@ type RetryRedPacketRefundResponse struct { func (x *RetryRedPacketRefundResponse) Reset() { *x = RetryRedPacketRefundResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[158] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13831,7 +14033,7 @@ func (x *RetryRedPacketRefundResponse) String() string { func (*RetryRedPacketRefundResponse) ProtoMessage() {} func (x *RetryRedPacketRefundResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[158] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13844,7 +14046,7 @@ func (x *RetryRedPacketRefundResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RetryRedPacketRefundResponse.ProtoReflect.Descriptor instead. func (*RetryRedPacketRefundResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{158} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{160} } func (x *RetryRedPacketRefundResponse) GetPacket() *RedPacket { @@ -13883,7 +14085,7 @@ type CronBatchRequest struct { func (x *CronBatchRequest) Reset() { *x = CronBatchRequest{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[159] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13895,7 +14097,7 @@ func (x *CronBatchRequest) String() string { func (*CronBatchRequest) ProtoMessage() {} func (x *CronBatchRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[159] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13908,7 +14110,7 @@ func (x *CronBatchRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CronBatchRequest.ProtoReflect.Descriptor instead. func (*CronBatchRequest) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{159} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{161} } func (x *CronBatchRequest) GetRequestId() string { @@ -13967,7 +14169,7 @@ type CronBatchResponse struct { func (x *CronBatchResponse) Reset() { *x = CronBatchResponse{} - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[160] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13979,7 +14181,7 @@ func (x *CronBatchResponse) String() string { func (*CronBatchResponse) ProtoMessage() {} func (x *CronBatchResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_wallet_v1_wallet_proto_msgTypes[160] + mi := &file_proto_wallet_v1_wallet_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13992,7 +14194,7 @@ func (x *CronBatchResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CronBatchResponse.ProtoReflect.Descriptor instead. func (*CronBatchResponse) Descriptor() ([]byte, []int) { - return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{160} + return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{162} } func (x *CronBatchResponse) GetClaimedCount() int32 { @@ -15273,6 +15475,27 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" + "\x0etransaction_id\x18\x01 \x01(\tR\rtransactionId\x127\n" + "\abalance\x18\x02 \x01(\v2\x1d.hyapp.wallet.v1.AssetBalanceR\abalance\x12\x16\n" + "\x06amount\x18\x03 \x01(\x03R\x06amount\x12\"\n" + + "\rgranted_at_ms\x18\x04 \x01(\x03R\vgrantedAtMs\"\x90\x03\n" + + "\x1fCreditRoomTurnoverRewardRequest\x12\x1d\n" + + "\n" + + "command_id\x18\x01 \x01(\tR\tcommandId\x12\x19\n" + + "\bapp_code\x18\x02 \x01(\tR\aappCode\x12$\n" + + "\x0etarget_user_id\x18\x03 \x01(\x03R\ftargetUserId\x12\x16\n" + + "\x06amount\x18\x04 \x01(\x03R\x06amount\x12#\n" + + "\rsettlement_id\x18\x05 \x01(\tR\fsettlementId\x12\x17\n" + + "\aroom_id\x18\x06 \x01(\tR\x06roomId\x12&\n" + + "\x0fperiod_start_ms\x18\a \x01(\x03R\rperiodStartMs\x12\"\n" + + "\rperiod_end_ms\x18\b \x01(\x03R\vperiodEndMs\x12\x1d\n" + + "\n" + + "coin_spent\x18\t \x01(\x03R\tcoinSpent\x12\x17\n" + + "\atier_id\x18\n" + + " \x01(\x03R\x06tierId\x12\x1b\n" + + "\ttier_code\x18\v \x01(\tR\btierCode\x12\x16\n" + + "\x06reason\x18\f \x01(\tR\x06reason\"\xbe\x01\n" + + " CreditRoomTurnoverRewardResponse\x12%\n" + + "\x0etransaction_id\x18\x01 \x01(\tR\rtransactionId\x127\n" + + "\abalance\x18\x02 \x01(\v2\x1d.hyapp.wallet.v1.AssetBalanceR\abalance\x12\x16\n" + + "\x06amount\x18\x03 \x01(\x03R\x06amount\x12\"\n" + "\rgranted_at_ms\x18\x04 \x01(\x03R\vgrantedAtMs\"\x9a\x03\n" + "\x1aApplyGameCoinChangeRequest\x12\x1d\n" + "\n" + @@ -15472,7 +15695,7 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" + "\x11WalletCronService\x12n\n" + "%ProcessHostSalaryDailySettlementBatch\x12!.hyapp.wallet.v1.CronBatchRequest\x1a\".hyapp.wallet.v1.CronBatchResponse\x12r\n" + ")ProcessHostSalaryHalfMonthSettlementBatch\x12!.hyapp.wallet.v1.CronBatchRequest\x1a\".hyapp.wallet.v1.CronBatchResponse\x12g\n" + - "\x1eProcessHostSalaryMonthEndBatch\x12!.hyapp.wallet.v1.CronBatchRequest\x1a\".hyapp.wallet.v1.CronBatchResponse2\x88:\n" + + "\x1eProcessHostSalaryMonthEndBatch\x12!.hyapp.wallet.v1.CronBatchRequest\x1a\".hyapp.wallet.v1.CronBatchResponse2\x89;\n" + "\rWalletService\x12R\n" + "\tDebitGift\x12!.hyapp.wallet.v1.DebitGiftRequest\x1a\".hyapp.wallet.v1.DebitGiftResponse\x12a\n" + "\x0eBatchDebitGift\x12&.hyapp.wallet.v1.BatchDebitGiftRequest\x1a'.hyapp.wallet.v1.BatchDebitGiftResponse\x12X\n" + @@ -15531,7 +15754,8 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" + "\x12ListAdminVipLevels\x12*.hyapp.wallet.v1.ListAdminVipLevelsRequest\x1a+.hyapp.wallet.v1.ListAdminVipLevelsResponse\x12s\n" + "\x14UpdateAdminVipLevels\x12,.hyapp.wallet.v1.UpdateAdminVipLevelsRequest\x1a-.hyapp.wallet.v1.UpdateAdminVipLevelsResponse\x12g\n" + "\x10CreditTaskReward\x12(.hyapp.wallet.v1.CreditTaskRewardRequest\x1a).hyapp.wallet.v1.CreditTaskRewardResponse\x12v\n" + - "\x15CreditLuckyGiftReward\x12-.hyapp.wallet.v1.CreditLuckyGiftRewardRequest\x1a..hyapp.wallet.v1.CreditLuckyGiftRewardResponse\x12p\n" + + "\x15CreditLuckyGiftReward\x12-.hyapp.wallet.v1.CreditLuckyGiftRewardRequest\x1a..hyapp.wallet.v1.CreditLuckyGiftRewardResponse\x12\x7f\n" + + "\x18CreditRoomTurnoverReward\x120.hyapp.wallet.v1.CreditRoomTurnoverRewardRequest\x1a1.hyapp.wallet.v1.CreditRoomTurnoverRewardResponse\x12p\n" + "\x13ApplyGameCoinChange\x12+.hyapp.wallet.v1.ApplyGameCoinChangeRequest\x1a,.hyapp.wallet.v1.ApplyGameCoinChangeResponse\x12m\n" + "\x12GetRedPacketConfig\x12*.hyapp.wallet.v1.GetRedPacketConfigRequest\x1a+.hyapp.wallet.v1.GetRedPacketConfigResponse\x12v\n" + "\x15UpdateRedPacketConfig\x12-.hyapp.wallet.v1.UpdateRedPacketConfigRequest\x1a..hyapp.wallet.v1.UpdateRedPacketConfigResponse\x12d\n" + @@ -15554,7 +15778,7 @@ func file_proto_wallet_v1_wallet_proto_rawDescGZIP() []byte { return file_proto_wallet_v1_wallet_proto_rawDescData } -var file_proto_wallet_v1_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 161) +var file_proto_wallet_v1_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 163) var file_proto_wallet_v1_wallet_proto_goTypes = []any{ (*DebitGiftRequest)(nil), // 0: hyapp.wallet.v1.DebitGiftRequest (*DebitGiftResponse)(nil), // 1: hyapp.wallet.v1.DebitGiftResponse @@ -15694,29 +15918,31 @@ var file_proto_wallet_v1_wallet_proto_goTypes = []any{ (*CreditTaskRewardResponse)(nil), // 135: hyapp.wallet.v1.CreditTaskRewardResponse (*CreditLuckyGiftRewardRequest)(nil), // 136: hyapp.wallet.v1.CreditLuckyGiftRewardRequest (*CreditLuckyGiftRewardResponse)(nil), // 137: hyapp.wallet.v1.CreditLuckyGiftRewardResponse - (*ApplyGameCoinChangeRequest)(nil), // 138: hyapp.wallet.v1.ApplyGameCoinChangeRequest - (*ApplyGameCoinChangeResponse)(nil), // 139: hyapp.wallet.v1.ApplyGameCoinChangeResponse - (*RedPacketConfig)(nil), // 140: hyapp.wallet.v1.RedPacketConfig - (*RedPacketClaim)(nil), // 141: hyapp.wallet.v1.RedPacketClaim - (*RedPacket)(nil), // 142: hyapp.wallet.v1.RedPacket - (*GetRedPacketConfigRequest)(nil), // 143: hyapp.wallet.v1.GetRedPacketConfigRequest - (*GetRedPacketConfigResponse)(nil), // 144: hyapp.wallet.v1.GetRedPacketConfigResponse - (*UpdateRedPacketConfigRequest)(nil), // 145: hyapp.wallet.v1.UpdateRedPacketConfigRequest - (*UpdateRedPacketConfigResponse)(nil), // 146: hyapp.wallet.v1.UpdateRedPacketConfigResponse - (*CreateRedPacketRequest)(nil), // 147: hyapp.wallet.v1.CreateRedPacketRequest - (*CreateRedPacketResponse)(nil), // 148: hyapp.wallet.v1.CreateRedPacketResponse - (*ClaimRedPacketRequest)(nil), // 149: hyapp.wallet.v1.ClaimRedPacketRequest - (*ClaimRedPacketResponse)(nil), // 150: hyapp.wallet.v1.ClaimRedPacketResponse - (*ListRedPacketsRequest)(nil), // 151: hyapp.wallet.v1.ListRedPacketsRequest - (*ListRedPacketsResponse)(nil), // 152: hyapp.wallet.v1.ListRedPacketsResponse - (*GetRedPacketRequest)(nil), // 153: hyapp.wallet.v1.GetRedPacketRequest - (*GetRedPacketResponse)(nil), // 154: hyapp.wallet.v1.GetRedPacketResponse - (*ExpireRedPacketsRequest)(nil), // 155: hyapp.wallet.v1.ExpireRedPacketsRequest - (*ExpireRedPacketsResponse)(nil), // 156: hyapp.wallet.v1.ExpireRedPacketsResponse - (*RetryRedPacketRefundRequest)(nil), // 157: hyapp.wallet.v1.RetryRedPacketRefundRequest - (*RetryRedPacketRefundResponse)(nil), // 158: hyapp.wallet.v1.RetryRedPacketRefundResponse - (*CronBatchRequest)(nil), // 159: hyapp.wallet.v1.CronBatchRequest - (*CronBatchResponse)(nil), // 160: hyapp.wallet.v1.CronBatchResponse + (*CreditRoomTurnoverRewardRequest)(nil), // 138: hyapp.wallet.v1.CreditRoomTurnoverRewardRequest + (*CreditRoomTurnoverRewardResponse)(nil), // 139: hyapp.wallet.v1.CreditRoomTurnoverRewardResponse + (*ApplyGameCoinChangeRequest)(nil), // 140: hyapp.wallet.v1.ApplyGameCoinChangeRequest + (*ApplyGameCoinChangeResponse)(nil), // 141: hyapp.wallet.v1.ApplyGameCoinChangeResponse + (*RedPacketConfig)(nil), // 142: hyapp.wallet.v1.RedPacketConfig + (*RedPacketClaim)(nil), // 143: hyapp.wallet.v1.RedPacketClaim + (*RedPacket)(nil), // 144: hyapp.wallet.v1.RedPacket + (*GetRedPacketConfigRequest)(nil), // 145: hyapp.wallet.v1.GetRedPacketConfigRequest + (*GetRedPacketConfigResponse)(nil), // 146: hyapp.wallet.v1.GetRedPacketConfigResponse + (*UpdateRedPacketConfigRequest)(nil), // 147: hyapp.wallet.v1.UpdateRedPacketConfigRequest + (*UpdateRedPacketConfigResponse)(nil), // 148: hyapp.wallet.v1.UpdateRedPacketConfigResponse + (*CreateRedPacketRequest)(nil), // 149: hyapp.wallet.v1.CreateRedPacketRequest + (*CreateRedPacketResponse)(nil), // 150: hyapp.wallet.v1.CreateRedPacketResponse + (*ClaimRedPacketRequest)(nil), // 151: hyapp.wallet.v1.ClaimRedPacketRequest + (*ClaimRedPacketResponse)(nil), // 152: hyapp.wallet.v1.ClaimRedPacketResponse + (*ListRedPacketsRequest)(nil), // 153: hyapp.wallet.v1.ListRedPacketsRequest + (*ListRedPacketsResponse)(nil), // 154: hyapp.wallet.v1.ListRedPacketsResponse + (*GetRedPacketRequest)(nil), // 155: hyapp.wallet.v1.GetRedPacketRequest + (*GetRedPacketResponse)(nil), // 156: hyapp.wallet.v1.GetRedPacketResponse + (*ExpireRedPacketsRequest)(nil), // 157: hyapp.wallet.v1.ExpireRedPacketsRequest + (*ExpireRedPacketsResponse)(nil), // 158: hyapp.wallet.v1.ExpireRedPacketsResponse + (*RetryRedPacketRefundRequest)(nil), // 159: hyapp.wallet.v1.RetryRedPacketRefundRequest + (*RetryRedPacketRefundResponse)(nil), // 160: hyapp.wallet.v1.RetryRedPacketRefundResponse + (*CronBatchRequest)(nil), // 161: hyapp.wallet.v1.CronBatchRequest + (*CronBatchResponse)(nil), // 162: hyapp.wallet.v1.CronBatchResponse } var file_proto_wallet_v1_wallet_proto_depIdxs = []int32{ 2, // 0: hyapp.wallet.v1.BatchDebitGiftRequest.targets:type_name -> hyapp.wallet.v1.DebitGiftTarget @@ -15785,160 +16011,163 @@ var file_proto_wallet_v1_wallet_proto_depIdxs = []int32{ 119, // 63: hyapp.wallet.v1.UpdateAdminVipLevelsResponse.levels:type_name -> hyapp.wallet.v1.VipLevel 6, // 64: hyapp.wallet.v1.CreditTaskRewardResponse.balance:type_name -> hyapp.wallet.v1.AssetBalance 6, // 65: hyapp.wallet.v1.CreditLuckyGiftRewardResponse.balance:type_name -> hyapp.wallet.v1.AssetBalance - 141, // 66: hyapp.wallet.v1.RedPacket.claims:type_name -> hyapp.wallet.v1.RedPacketClaim - 140, // 67: hyapp.wallet.v1.GetRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig - 140, // 68: hyapp.wallet.v1.UpdateRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig - 142, // 69: hyapp.wallet.v1.CreateRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket - 141, // 70: hyapp.wallet.v1.ClaimRedPacketResponse.claim:type_name -> hyapp.wallet.v1.RedPacketClaim - 142, // 71: hyapp.wallet.v1.ClaimRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket - 142, // 72: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket - 142, // 73: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket - 142, // 74: hyapp.wallet.v1.RetryRedPacketRefundResponse.packet:type_name -> hyapp.wallet.v1.RedPacket - 159, // 75: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryDailySettlementBatch:input_type -> hyapp.wallet.v1.CronBatchRequest - 159, // 76: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryHalfMonthSettlementBatch:input_type -> hyapp.wallet.v1.CronBatchRequest - 159, // 77: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryMonthEndBatch:input_type -> hyapp.wallet.v1.CronBatchRequest - 0, // 78: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest - 3, // 79: hyapp.wallet.v1.WalletService.BatchDebitGift:input_type -> hyapp.wallet.v1.BatchDebitGiftRequest - 7, // 80: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest - 11, // 81: hyapp.wallet.v1.WalletService.GetActiveHostSalaryPolicy:input_type -> hyapp.wallet.v1.GetActiveHostSalaryPolicyRequest - 14, // 82: hyapp.wallet.v1.WalletService.GetHostSalaryProgress:input_type -> hyapp.wallet.v1.GetHostSalaryProgressRequest - 16, // 83: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest - 18, // 84: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest - 20, // 85: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest - 23, // 86: hyapp.wallet.v1.WalletService.ListCoinSellerSalaryExchangeRateTiers:input_type -> hyapp.wallet.v1.ListCoinSellerSalaryExchangeRateTiersRequest - 25, // 87: hyapp.wallet.v1.WalletService.ExchangeSalaryToCoin:input_type -> hyapp.wallet.v1.ExchangeSalaryToCoinRequest - 27, // 88: hyapp.wallet.v1.WalletService.TransferSalaryToCoinSeller:input_type -> hyapp.wallet.v1.TransferSalaryToCoinSellerRequest - 39, // 89: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest - 41, // 90: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest - 43, // 91: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest - 44, // 92: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest - 45, // 93: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest - 47, // 94: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest - 49, // 95: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest - 51, // 96: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest - 52, // 97: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest - 53, // 98: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest - 55, // 99: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest - 57, // 100: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest - 61, // 101: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest - 62, // 102: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest - 63, // 103: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest - 59, // 104: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest - 65, // 105: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest - 66, // 106: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest - 68, // 107: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest - 70, // 108: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest - 72, // 109: hyapp.wallet.v1.WalletService.UnequipUserResource:input_type -> hyapp.wallet.v1.UnequipUserResourceRequest - 74, // 110: hyapp.wallet.v1.WalletService.BatchGetUserEquippedResources:input_type -> hyapp.wallet.v1.BatchGetUserEquippedResourcesRequest - 77, // 111: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest - 80, // 112: hyapp.wallet.v1.WalletService.ListResourceShopItems:input_type -> hyapp.wallet.v1.ListResourceShopItemsRequest - 82, // 113: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:input_type -> hyapp.wallet.v1.UpsertResourceShopItemsRequest - 84, // 114: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:input_type -> hyapp.wallet.v1.SetResourceShopItemStatusRequest - 86, // 115: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:input_type -> hyapp.wallet.v1.PurchaseResourceShopItemRequest - 89, // 116: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest - 92, // 117: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest - 95, // 118: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest - 98, // 119: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest - 101, // 120: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest - 103, // 121: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest - 105, // 122: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest - 107, // 123: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest - 108, // 124: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest - 109, // 125: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest - 113, // 126: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest - 116, // 127: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest - 121, // 128: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest - 123, // 129: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest - 125, // 130: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest - 127, // 131: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest - 130, // 132: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest - 132, // 133: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest - 134, // 134: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest - 136, // 135: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:input_type -> hyapp.wallet.v1.CreditLuckyGiftRewardRequest - 138, // 136: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest - 143, // 137: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest - 145, // 138: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest - 147, // 139: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest - 149, // 140: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest - 151, // 141: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest - 153, // 142: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest - 155, // 143: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest - 157, // 144: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:input_type -> hyapp.wallet.v1.RetryRedPacketRefundRequest - 160, // 145: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryDailySettlementBatch:output_type -> hyapp.wallet.v1.CronBatchResponse - 160, // 146: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryHalfMonthSettlementBatch:output_type -> hyapp.wallet.v1.CronBatchResponse - 160, // 147: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryMonthEndBatch:output_type -> hyapp.wallet.v1.CronBatchResponse - 1, // 148: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse - 5, // 149: hyapp.wallet.v1.WalletService.BatchDebitGift:output_type -> hyapp.wallet.v1.BatchDebitGiftResponse - 8, // 150: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse - 12, // 151: hyapp.wallet.v1.WalletService.GetActiveHostSalaryPolicy:output_type -> hyapp.wallet.v1.GetActiveHostSalaryPolicyResponse - 15, // 152: hyapp.wallet.v1.WalletService.GetHostSalaryProgress:output_type -> hyapp.wallet.v1.GetHostSalaryProgressResponse - 17, // 153: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse - 19, // 154: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse - 21, // 155: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse - 24, // 156: hyapp.wallet.v1.WalletService.ListCoinSellerSalaryExchangeRateTiers:output_type -> hyapp.wallet.v1.ListCoinSellerSalaryExchangeRateTiersResponse - 26, // 157: hyapp.wallet.v1.WalletService.ExchangeSalaryToCoin:output_type -> hyapp.wallet.v1.ExchangeSalaryToCoinResponse - 28, // 158: hyapp.wallet.v1.WalletService.TransferSalaryToCoinSeller:output_type -> hyapp.wallet.v1.TransferSalaryToCoinSellerResponse - 40, // 159: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse - 42, // 160: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse - 46, // 161: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse - 46, // 162: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse - 46, // 163: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse - 48, // 164: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse - 50, // 165: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse - 54, // 166: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse - 54, // 167: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse - 54, // 168: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse - 56, // 169: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse - 58, // 170: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse - 64, // 171: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse - 64, // 172: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse - 64, // 173: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse - 60, // 174: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse - 67, // 175: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse - 67, // 176: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse - 69, // 177: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse - 71, // 178: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse - 73, // 179: hyapp.wallet.v1.WalletService.UnequipUserResource:output_type -> hyapp.wallet.v1.UnequipUserResourceResponse - 76, // 180: hyapp.wallet.v1.WalletService.BatchGetUserEquippedResources:output_type -> hyapp.wallet.v1.BatchGetUserEquippedResourcesResponse - 78, // 181: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse - 81, // 182: hyapp.wallet.v1.WalletService.ListResourceShopItems:output_type -> hyapp.wallet.v1.ListResourceShopItemsResponse - 83, // 183: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:output_type -> hyapp.wallet.v1.UpsertResourceShopItemsResponse - 85, // 184: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:output_type -> hyapp.wallet.v1.ResourceShopItemResponse - 87, // 185: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:output_type -> hyapp.wallet.v1.PurchaseResourceShopItemResponse - 90, // 186: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse - 93, // 187: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse - 96, // 188: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse - 99, // 189: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse - 102, // 190: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse - 104, // 191: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse - 106, // 192: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse - 110, // 193: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse - 110, // 194: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse - 111, // 195: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse - 114, // 196: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse - 117, // 197: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse - 122, // 198: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse - 124, // 199: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse - 126, // 200: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse - 128, // 201: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse - 131, // 202: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse - 133, // 203: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse - 135, // 204: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse - 137, // 205: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:output_type -> hyapp.wallet.v1.CreditLuckyGiftRewardResponse - 139, // 206: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse - 144, // 207: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse - 146, // 208: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse - 148, // 209: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse - 150, // 210: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse - 152, // 211: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse - 154, // 212: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse - 156, // 213: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse - 158, // 214: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:output_type -> hyapp.wallet.v1.RetryRedPacketRefundResponse - 145, // [145:215] is the sub-list for method output_type - 75, // [75:145] is the sub-list for method input_type - 75, // [75:75] is the sub-list for extension type_name - 75, // [75:75] is the sub-list for extension extendee - 0, // [0:75] is the sub-list for field type_name + 6, // 66: hyapp.wallet.v1.CreditRoomTurnoverRewardResponse.balance:type_name -> hyapp.wallet.v1.AssetBalance + 143, // 67: hyapp.wallet.v1.RedPacket.claims:type_name -> hyapp.wallet.v1.RedPacketClaim + 142, // 68: hyapp.wallet.v1.GetRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig + 142, // 69: hyapp.wallet.v1.UpdateRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig + 144, // 70: hyapp.wallet.v1.CreateRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket + 143, // 71: hyapp.wallet.v1.ClaimRedPacketResponse.claim:type_name -> hyapp.wallet.v1.RedPacketClaim + 144, // 72: hyapp.wallet.v1.ClaimRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket + 144, // 73: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket + 144, // 74: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket + 144, // 75: hyapp.wallet.v1.RetryRedPacketRefundResponse.packet:type_name -> hyapp.wallet.v1.RedPacket + 161, // 76: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryDailySettlementBatch:input_type -> hyapp.wallet.v1.CronBatchRequest + 161, // 77: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryHalfMonthSettlementBatch:input_type -> hyapp.wallet.v1.CronBatchRequest + 161, // 78: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryMonthEndBatch:input_type -> hyapp.wallet.v1.CronBatchRequest + 0, // 79: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest + 3, // 80: hyapp.wallet.v1.WalletService.BatchDebitGift:input_type -> hyapp.wallet.v1.BatchDebitGiftRequest + 7, // 81: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest + 11, // 82: hyapp.wallet.v1.WalletService.GetActiveHostSalaryPolicy:input_type -> hyapp.wallet.v1.GetActiveHostSalaryPolicyRequest + 14, // 83: hyapp.wallet.v1.WalletService.GetHostSalaryProgress:input_type -> hyapp.wallet.v1.GetHostSalaryProgressRequest + 16, // 84: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest + 18, // 85: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest + 20, // 86: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest + 23, // 87: hyapp.wallet.v1.WalletService.ListCoinSellerSalaryExchangeRateTiers:input_type -> hyapp.wallet.v1.ListCoinSellerSalaryExchangeRateTiersRequest + 25, // 88: hyapp.wallet.v1.WalletService.ExchangeSalaryToCoin:input_type -> hyapp.wallet.v1.ExchangeSalaryToCoinRequest + 27, // 89: hyapp.wallet.v1.WalletService.TransferSalaryToCoinSeller:input_type -> hyapp.wallet.v1.TransferSalaryToCoinSellerRequest + 39, // 90: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest + 41, // 91: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest + 43, // 92: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest + 44, // 93: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest + 45, // 94: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest + 47, // 95: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest + 49, // 96: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest + 51, // 97: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest + 52, // 98: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest + 53, // 99: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest + 55, // 100: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest + 57, // 101: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest + 61, // 102: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest + 62, // 103: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest + 63, // 104: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest + 59, // 105: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest + 65, // 106: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest + 66, // 107: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest + 68, // 108: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest + 70, // 109: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest + 72, // 110: hyapp.wallet.v1.WalletService.UnequipUserResource:input_type -> hyapp.wallet.v1.UnequipUserResourceRequest + 74, // 111: hyapp.wallet.v1.WalletService.BatchGetUserEquippedResources:input_type -> hyapp.wallet.v1.BatchGetUserEquippedResourcesRequest + 77, // 112: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest + 80, // 113: hyapp.wallet.v1.WalletService.ListResourceShopItems:input_type -> hyapp.wallet.v1.ListResourceShopItemsRequest + 82, // 114: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:input_type -> hyapp.wallet.v1.UpsertResourceShopItemsRequest + 84, // 115: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:input_type -> hyapp.wallet.v1.SetResourceShopItemStatusRequest + 86, // 116: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:input_type -> hyapp.wallet.v1.PurchaseResourceShopItemRequest + 89, // 117: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest + 92, // 118: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest + 95, // 119: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest + 98, // 120: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest + 101, // 121: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest + 103, // 122: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest + 105, // 123: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest + 107, // 124: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest + 108, // 125: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest + 109, // 126: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest + 113, // 127: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest + 116, // 128: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest + 121, // 129: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest + 123, // 130: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest + 125, // 131: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest + 127, // 132: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest + 130, // 133: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest + 132, // 134: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest + 134, // 135: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest + 136, // 136: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:input_type -> hyapp.wallet.v1.CreditLuckyGiftRewardRequest + 138, // 137: hyapp.wallet.v1.WalletService.CreditRoomTurnoverReward:input_type -> hyapp.wallet.v1.CreditRoomTurnoverRewardRequest + 140, // 138: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest + 145, // 139: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest + 147, // 140: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest + 149, // 141: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest + 151, // 142: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest + 153, // 143: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest + 155, // 144: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest + 157, // 145: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest + 159, // 146: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:input_type -> hyapp.wallet.v1.RetryRedPacketRefundRequest + 162, // 147: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryDailySettlementBatch:output_type -> hyapp.wallet.v1.CronBatchResponse + 162, // 148: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryHalfMonthSettlementBatch:output_type -> hyapp.wallet.v1.CronBatchResponse + 162, // 149: hyapp.wallet.v1.WalletCronService.ProcessHostSalaryMonthEndBatch:output_type -> hyapp.wallet.v1.CronBatchResponse + 1, // 150: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse + 5, // 151: hyapp.wallet.v1.WalletService.BatchDebitGift:output_type -> hyapp.wallet.v1.BatchDebitGiftResponse + 8, // 152: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse + 12, // 153: hyapp.wallet.v1.WalletService.GetActiveHostSalaryPolicy:output_type -> hyapp.wallet.v1.GetActiveHostSalaryPolicyResponse + 15, // 154: hyapp.wallet.v1.WalletService.GetHostSalaryProgress:output_type -> hyapp.wallet.v1.GetHostSalaryProgressResponse + 17, // 155: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse + 19, // 156: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse + 21, // 157: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse + 24, // 158: hyapp.wallet.v1.WalletService.ListCoinSellerSalaryExchangeRateTiers:output_type -> hyapp.wallet.v1.ListCoinSellerSalaryExchangeRateTiersResponse + 26, // 159: hyapp.wallet.v1.WalletService.ExchangeSalaryToCoin:output_type -> hyapp.wallet.v1.ExchangeSalaryToCoinResponse + 28, // 160: hyapp.wallet.v1.WalletService.TransferSalaryToCoinSeller:output_type -> hyapp.wallet.v1.TransferSalaryToCoinSellerResponse + 40, // 161: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse + 42, // 162: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse + 46, // 163: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse + 46, // 164: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse + 46, // 165: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse + 48, // 166: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse + 50, // 167: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse + 54, // 168: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse + 54, // 169: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse + 54, // 170: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse + 56, // 171: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse + 58, // 172: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse + 64, // 173: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse + 64, // 174: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse + 64, // 175: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse + 60, // 176: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse + 67, // 177: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse + 67, // 178: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse + 69, // 179: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse + 71, // 180: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse + 73, // 181: hyapp.wallet.v1.WalletService.UnequipUserResource:output_type -> hyapp.wallet.v1.UnequipUserResourceResponse + 76, // 182: hyapp.wallet.v1.WalletService.BatchGetUserEquippedResources:output_type -> hyapp.wallet.v1.BatchGetUserEquippedResourcesResponse + 78, // 183: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse + 81, // 184: hyapp.wallet.v1.WalletService.ListResourceShopItems:output_type -> hyapp.wallet.v1.ListResourceShopItemsResponse + 83, // 185: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:output_type -> hyapp.wallet.v1.UpsertResourceShopItemsResponse + 85, // 186: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:output_type -> hyapp.wallet.v1.ResourceShopItemResponse + 87, // 187: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:output_type -> hyapp.wallet.v1.PurchaseResourceShopItemResponse + 90, // 188: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse + 93, // 189: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse + 96, // 190: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse + 99, // 191: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse + 102, // 192: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse + 104, // 193: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse + 106, // 194: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse + 110, // 195: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse + 110, // 196: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse + 111, // 197: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse + 114, // 198: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse + 117, // 199: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse + 122, // 200: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse + 124, // 201: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse + 126, // 202: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse + 128, // 203: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse + 131, // 204: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse + 133, // 205: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse + 135, // 206: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse + 137, // 207: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:output_type -> hyapp.wallet.v1.CreditLuckyGiftRewardResponse + 139, // 208: hyapp.wallet.v1.WalletService.CreditRoomTurnoverReward:output_type -> hyapp.wallet.v1.CreditRoomTurnoverRewardResponse + 141, // 209: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse + 146, // 210: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse + 148, // 211: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse + 150, // 212: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse + 152, // 213: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse + 154, // 214: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse + 156, // 215: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse + 158, // 216: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse + 160, // 217: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:output_type -> hyapp.wallet.v1.RetryRedPacketRefundResponse + 147, // [147:218] is the sub-list for method output_type + 76, // [76:147] is the sub-list for method input_type + 76, // [76:76] is the sub-list for extension type_name + 76, // [76:76] is the sub-list for extension extendee + 0, // [0:76] is the sub-list for field type_name } func init() { file_proto_wallet_v1_wallet_proto_init() } @@ -15954,7 +16183,7 @@ func file_proto_wallet_v1_wallet_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_wallet_v1_wallet_proto_rawDesc), len(file_proto_wallet_v1_wallet_proto_rawDesc)), NumEnums: 0, - NumMessages: 161, + NumMessages: 163, NumExtensions: 0, NumServices: 2, }, diff --git a/api/proto/wallet/v1/wallet.proto b/api/proto/wallet/v1/wallet.proto index 4daa67e5..24e24c98 100644 --- a/api/proto/wallet/v1/wallet.proto +++ b/api/proto/wallet/v1/wallet.proto @@ -1345,6 +1345,30 @@ message CreditLuckyGiftRewardResponse { int64 granted_at_ms = 4; } +// CreditRoomTurnoverRewardRequest 是 activity-service 每周房间流水奖励结算后的金币入账命令。 +message CreditRoomTurnoverRewardRequest { + string command_id = 1; + string app_code = 2; + int64 target_user_id = 3; + int64 amount = 4; + string settlement_id = 5; + string room_id = 6; + int64 period_start_ms = 7; + int64 period_end_ms = 8; + int64 coin_spent = 9; + int64 tier_id = 10; + string tier_code = 11; + string reason = 12; +} + +// CreditRoomTurnoverRewardResponse 返回房间流水奖励入账流水和用户 COIN 账后余额。 +message CreditRoomTurnoverRewardResponse { + string transaction_id = 1; + AssetBalance balance = 2; + int64 amount = 3; + int64 granted_at_ms = 4; +} + // ApplyGameCoinChangeRequest 是 game-service 唯一可以调用的钱包游戏改账入口。 message ApplyGameCoinChangeRequest { string request_id = 1; @@ -1627,6 +1651,7 @@ service WalletService { rpc UpdateAdminVipLevels(UpdateAdminVipLevelsRequest) returns (UpdateAdminVipLevelsResponse); rpc CreditTaskReward(CreditTaskRewardRequest) returns (CreditTaskRewardResponse); rpc CreditLuckyGiftReward(CreditLuckyGiftRewardRequest) returns (CreditLuckyGiftRewardResponse); + rpc CreditRoomTurnoverReward(CreditRoomTurnoverRewardRequest) returns (CreditRoomTurnoverRewardResponse); rpc ApplyGameCoinChange(ApplyGameCoinChangeRequest) returns (ApplyGameCoinChangeResponse); rpc GetRedPacketConfig(GetRedPacketConfigRequest) returns (GetRedPacketConfigResponse); rpc UpdateRedPacketConfig(UpdateRedPacketConfigRequest) returns (UpdateRedPacketConfigResponse); diff --git a/api/proto/wallet/v1/wallet_grpc.pb.go b/api/proto/wallet/v1/wallet_grpc.pb.go index ad9fcccb..91ff44d8 100644 --- a/api/proto/wallet/v1/wallet_grpc.pb.go +++ b/api/proto/wallet/v1/wallet_grpc.pb.go @@ -259,6 +259,7 @@ const ( WalletService_UpdateAdminVipLevels_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateAdminVipLevels" WalletService_CreditTaskReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditTaskReward" WalletService_CreditLuckyGiftReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditLuckyGiftReward" + WalletService_CreditRoomTurnoverReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditRoomTurnoverReward" WalletService_ApplyGameCoinChange_FullMethodName = "/hyapp.wallet.v1.WalletService/ApplyGameCoinChange" WalletService_GetRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/GetRedPacketConfig" WalletService_UpdateRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateRedPacketConfig" @@ -334,6 +335,7 @@ type WalletServiceClient interface { UpdateAdminVipLevels(ctx context.Context, in *UpdateAdminVipLevelsRequest, opts ...grpc.CallOption) (*UpdateAdminVipLevelsResponse, error) CreditTaskReward(ctx context.Context, in *CreditTaskRewardRequest, opts ...grpc.CallOption) (*CreditTaskRewardResponse, error) CreditLuckyGiftReward(ctx context.Context, in *CreditLuckyGiftRewardRequest, opts ...grpc.CallOption) (*CreditLuckyGiftRewardResponse, error) + CreditRoomTurnoverReward(ctx context.Context, in *CreditRoomTurnoverRewardRequest, opts ...grpc.CallOption) (*CreditRoomTurnoverRewardResponse, error) ApplyGameCoinChange(ctx context.Context, in *ApplyGameCoinChangeRequest, opts ...grpc.CallOption) (*ApplyGameCoinChangeResponse, error) GetRedPacketConfig(ctx context.Context, in *GetRedPacketConfigRequest, opts ...grpc.CallOption) (*GetRedPacketConfigResponse, error) UpdateRedPacketConfig(ctx context.Context, in *UpdateRedPacketConfigRequest, opts ...grpc.CallOption) (*UpdateRedPacketConfigResponse, error) @@ -933,6 +935,16 @@ func (c *walletServiceClient) CreditLuckyGiftReward(ctx context.Context, in *Cre return out, nil } +func (c *walletServiceClient) CreditRoomTurnoverReward(ctx context.Context, in *CreditRoomTurnoverRewardRequest, opts ...grpc.CallOption) (*CreditRoomTurnoverRewardResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreditRoomTurnoverRewardResponse) + err := c.cc.Invoke(ctx, WalletService_CreditRoomTurnoverReward_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *walletServiceClient) ApplyGameCoinChange(ctx context.Context, in *ApplyGameCoinChangeRequest, opts ...grpc.CallOption) (*ApplyGameCoinChangeResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ApplyGameCoinChangeResponse) @@ -1087,6 +1099,7 @@ type WalletServiceServer interface { UpdateAdminVipLevels(context.Context, *UpdateAdminVipLevelsRequest) (*UpdateAdminVipLevelsResponse, error) CreditTaskReward(context.Context, *CreditTaskRewardRequest) (*CreditTaskRewardResponse, error) CreditLuckyGiftReward(context.Context, *CreditLuckyGiftRewardRequest) (*CreditLuckyGiftRewardResponse, error) + CreditRoomTurnoverReward(context.Context, *CreditRoomTurnoverRewardRequest) (*CreditRoomTurnoverRewardResponse, error) ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error) GetRedPacketConfig(context.Context, *GetRedPacketConfigRequest) (*GetRedPacketConfigResponse, error) UpdateRedPacketConfig(context.Context, *UpdateRedPacketConfigRequest) (*UpdateRedPacketConfigResponse, error) @@ -1280,6 +1293,9 @@ func (UnimplementedWalletServiceServer) CreditTaskReward(context.Context, *Credi func (UnimplementedWalletServiceServer) CreditLuckyGiftReward(context.Context, *CreditLuckyGiftRewardRequest) (*CreditLuckyGiftRewardResponse, error) { return nil, status.Error(codes.Unimplemented, "method CreditLuckyGiftReward not implemented") } +func (UnimplementedWalletServiceServer) CreditRoomTurnoverReward(context.Context, *CreditRoomTurnoverRewardRequest) (*CreditRoomTurnoverRewardResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreditRoomTurnoverReward not implemented") +} func (UnimplementedWalletServiceServer) ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error) { return nil, status.Error(codes.Unimplemented, "method ApplyGameCoinChange not implemented") } @@ -2372,6 +2388,24 @@ func _WalletService_CreditLuckyGiftReward_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _WalletService_CreditRoomTurnoverReward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreditRoomTurnoverRewardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WalletServiceServer).CreditRoomTurnoverReward(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WalletService_CreditRoomTurnoverReward_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WalletServiceServer).CreditRoomTurnoverReward(ctx, req.(*CreditRoomTurnoverRewardRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _WalletService_ApplyGameCoinChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ApplyGameCoinChangeRequest) if err := dec(in); err != nil { @@ -2773,6 +2807,10 @@ var WalletService_ServiceDesc = grpc.ServiceDesc{ MethodName: "CreditLuckyGiftReward", Handler: _WalletService_CreditLuckyGiftReward_Handler, }, + { + MethodName: "CreditRoomTurnoverReward", + Handler: _WalletService_CreditRoomTurnoverReward_Handler, + }, { MethodName: "ApplyGameCoinChange", Handler: _WalletService_ApplyGameCoinChange_Handler, diff --git a/server/admin/cmd/server/main.go b/server/admin/cmd/server/main.go index 634a9051..31654d26 100644 --- a/server/admin/cmd/server/main.go +++ b/server/admin/cmd/server/main.go @@ -32,6 +32,7 @@ import ( authmodule "hyapp-admin-server/internal/modules/auth" coinledgermodule "hyapp-admin-server/internal/modules/coinledger" countryregionmodule "hyapp-admin-server/internal/modules/countryregion" + cumulativerechargerewardmodule "hyapp-admin-server/internal/modules/cumulativerechargereward" dailytaskmodule "hyapp-admin-server/internal/modules/dailytask" dashboardmodule "hyapp-admin-server/internal/modules/dashboard" firstrechargerewardmodule "hyapp-admin-server/internal/modules/firstrechargereward" @@ -54,6 +55,7 @@ import ( resourcemodule "hyapp-admin-server/internal/modules/resource" roomadminmodule "hyapp-admin-server/internal/modules/roomadmin" roomrocketmodule "hyapp-admin-server/internal/modules/roomrocket" + roomturnoverrewardmodule "hyapp-admin-server/internal/modules/roomturnoverreward" searchmodule "hyapp-admin-server/internal/modules/search" sevendaycheckinmodule "hyapp-admin-server/internal/modules/sevendaycheckin" teamsalarypolicymodule "hyapp-admin-server/internal/modules/teamsalarypolicy" @@ -61,6 +63,7 @@ import ( uploadmodule "hyapp-admin-server/internal/modules/upload" userleaderboardmodule "hyapp-admin-server/internal/modules/userleaderboard" vipconfigmodule "hyapp-admin-server/internal/modules/vipconfig" + weeklystarmodule "hyapp-admin-server/internal/modules/weeklystar" "hyapp-admin-server/internal/platform/logging" "hyapp-admin-server/internal/platform/tencentcos" "hyapp-admin-server/internal/repository" @@ -227,6 +230,7 @@ func main() { AppUser: appusermodule.New(userclient.NewGRPC(userConn), activityclient.NewGRPC(activityConn), sqlDB, userDB, walletDB, cfg, auditHandler), CoinLedger: coinledgermodule.New(userDB, walletDB, sqlDB, walletclient.NewGRPC(walletConn), auditHandler), CountryRegion: countryregionmodule.New(userclient.NewGRPC(userConn), userDB, cfg, auditHandler), + CumulativeRecharge: cumulativerechargerewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler), DailyTask: dailytaskmodule.New(activityclient.NewGRPC(activityConn), auditHandler), Dashboard: dashboardmodule.New(store, cfg), FirstRechargeReward: firstrechargerewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler), @@ -249,6 +253,7 @@ func main() { Resource: resourcemodule.New(walletclient.NewGRPC(walletConn), store, userDB, cfg.WalletService.RequestTimeout, auditHandler), RoomAdmin: roomadminmodule.New(userDB, roomClient, auditHandler), RoomRocket: roomrocketmodule.New(roomClient, auditHandler), + RoomTurnoverReward: roomturnoverrewardmodule.New(activityclient.NewGRPC(activityConn), auditHandler), Search: searchmodule.New(store), SevenDayCheckIn: sevendaycheckinmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler), TeamSalaryPolicy: teamsalarypolicymodule.New(store, auditHandler), @@ -256,6 +261,7 @@ func main() { Upload: uploadmodule.New(objectUploader, cfg.TencentCOS.ObjectPrefix, auditHandler), UserLeaderboard: userleaderboardmodule.New(walletDB, userDB, roomClient), VIPConfig: vipconfigmodule.New(walletclient.NewGRPC(walletConn), auditHandler), + WeeklyStar: weeklystarmodule.New(activityclient.NewGRPC(activityConn), auditHandler), } engine := router.New(cfg, auth, handlers) diff --git a/server/admin/internal/integration/activityclient/client.go b/server/admin/internal/integration/activityclient/client.go index b1dfe2d7..35d37b21 100644 --- a/server/admin/internal/integration/activityclient/client.go +++ b/server/admin/internal/integration/activityclient/client.go @@ -21,9 +21,23 @@ type Client interface { GetFirstRechargeRewardConfig(ctx context.Context, req *activityv1.GetFirstRechargeRewardConfigRequest) (*activityv1.GetFirstRechargeRewardConfigResponse, error) UpdateFirstRechargeRewardConfig(ctx context.Context, req *activityv1.UpdateFirstRechargeRewardConfigRequest) (*activityv1.UpdateFirstRechargeRewardConfigResponse, error) ListFirstRechargeRewardClaims(ctx context.Context, req *activityv1.ListFirstRechargeRewardClaimsRequest) (*activityv1.ListFirstRechargeRewardClaimsResponse, error) + GetCumulativeRechargeRewardConfig(ctx context.Context, req *activityv1.GetCumulativeRechargeRewardConfigRequest) (*activityv1.GetCumulativeRechargeRewardConfigResponse, error) + UpdateCumulativeRechargeRewardConfig(ctx context.Context, req *activityv1.UpdateCumulativeRechargeRewardConfigRequest) (*activityv1.UpdateCumulativeRechargeRewardConfigResponse, error) + ListCumulativeRechargeRewardGrants(ctx context.Context, req *activityv1.ListCumulativeRechargeRewardGrantsRequest) (*activityv1.ListCumulativeRechargeRewardGrantsResponse, error) GetSevenDayCheckInConfig(ctx context.Context, req *activityv1.GetSevenDayCheckInConfigRequest) (*activityv1.GetSevenDayCheckInConfigResponse, error) UpdateSevenDayCheckInConfig(ctx context.Context, req *activityv1.UpdateSevenDayCheckInConfigRequest) (*activityv1.UpdateSevenDayCheckInConfigResponse, error) ListSevenDayCheckInClaims(ctx context.Context, req *activityv1.ListSevenDayCheckInClaimsRequest) (*activityv1.ListSevenDayCheckInClaimsResponse, error) + GetRoomTurnoverRewardConfig(ctx context.Context, req *activityv1.GetRoomTurnoverRewardConfigRequest) (*activityv1.GetRoomTurnoverRewardConfigResponse, error) + UpdateRoomTurnoverRewardConfig(ctx context.Context, req *activityv1.UpdateRoomTurnoverRewardConfigRequest) (*activityv1.UpdateRoomTurnoverRewardConfigResponse, error) + ListRoomTurnoverRewardSettlements(ctx context.Context, req *activityv1.ListRoomTurnoverRewardSettlementsRequest) (*activityv1.ListRoomTurnoverRewardSettlementsResponse, error) + RetryRoomTurnoverRewardSettlement(ctx context.Context, req *activityv1.RetryRoomTurnoverRewardSettlementRequest) (*activityv1.RetryRoomTurnoverRewardSettlementResponse, error) + ListWeeklyStarCycles(ctx context.Context, req *activityv1.ListWeeklyStarCyclesRequest) (*activityv1.ListWeeklyStarCyclesResponse, error) + CreateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) + GetWeeklyStarCycle(ctx context.Context, req *activityv1.GetWeeklyStarCycleRequest) (*activityv1.GetWeeklyStarCycleResponse, error) + UpdateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) + SetWeeklyStarCycleStatus(ctx context.Context, req *activityv1.SetWeeklyStarCycleStatusRequest) (*activityv1.SetWeeklyStarCycleStatusResponse, error) + ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) + ListWeeklyStarSettlements(ctx context.Context, req *activityv1.ListWeeklyStarSettlementsRequest) (*activityv1.ListWeeklyStarSettlementsResponse, error) GetLuckyGiftConfig(ctx context.Context, req *activityv1.GetLuckyGiftConfigRequest) (*activityv1.GetLuckyGiftConfigResponse, error) UpsertLuckyGiftConfig(ctx context.Context, req *activityv1.UpsertLuckyGiftConfigRequest) (*activityv1.UpsertLuckyGiftConfigResponse, error) ListLuckyGiftConfigs(ctx context.Context, req *activityv1.ListLuckyGiftConfigsRequest) (*activityv1.ListLuckyGiftConfigsResponse, error) @@ -41,7 +55,10 @@ type GRPCClient struct { achievementClient activityv1.AdminAchievementServiceClient registrationRewardClient activityv1.AdminRegistrationRewardServiceClient firstRechargeRewardClient activityv1.AdminFirstRechargeRewardServiceClient + cumulativeRechargeClient activityv1.AdminCumulativeRechargeRewardServiceClient sevenDayCheckInClient activityv1.AdminSevenDayCheckInServiceClient + roomTurnoverRewardClient activityv1.AdminRoomTurnoverRewardServiceClient + weeklyStarClient activityv1.AdminWeeklyStarServiceClient luckyGiftClient activityv1.AdminLuckyGiftServiceClient broadcastClient activityv1.BroadcastServiceClient growthClient activityv1.AdminGrowthLevelServiceClient @@ -53,7 +70,10 @@ func NewGRPC(conn grpc.ClientConnInterface) *GRPCClient { achievementClient: activityv1.NewAdminAchievementServiceClient(conn), registrationRewardClient: activityv1.NewAdminRegistrationRewardServiceClient(conn), firstRechargeRewardClient: activityv1.NewAdminFirstRechargeRewardServiceClient(conn), + cumulativeRechargeClient: activityv1.NewAdminCumulativeRechargeRewardServiceClient(conn), sevenDayCheckInClient: activityv1.NewAdminSevenDayCheckInServiceClient(conn), + roomTurnoverRewardClient: activityv1.NewAdminRoomTurnoverRewardServiceClient(conn), + weeklyStarClient: activityv1.NewAdminWeeklyStarServiceClient(conn), luckyGiftClient: activityv1.NewAdminLuckyGiftServiceClient(conn), broadcastClient: activityv1.NewBroadcastServiceClient(conn), growthClient: activityv1.NewAdminGrowthLevelServiceClient(conn), @@ -104,6 +124,18 @@ func (c *GRPCClient) ListFirstRechargeRewardClaims(ctx context.Context, req *act return c.firstRechargeRewardClient.ListFirstRechargeRewardClaims(ctx, req) } +func (c *GRPCClient) GetCumulativeRechargeRewardConfig(ctx context.Context, req *activityv1.GetCumulativeRechargeRewardConfigRequest) (*activityv1.GetCumulativeRechargeRewardConfigResponse, error) { + return c.cumulativeRechargeClient.GetCumulativeRechargeRewardConfig(ctx, req) +} + +func (c *GRPCClient) UpdateCumulativeRechargeRewardConfig(ctx context.Context, req *activityv1.UpdateCumulativeRechargeRewardConfigRequest) (*activityv1.UpdateCumulativeRechargeRewardConfigResponse, error) { + return c.cumulativeRechargeClient.UpdateCumulativeRechargeRewardConfig(ctx, req) +} + +func (c *GRPCClient) ListCumulativeRechargeRewardGrants(ctx context.Context, req *activityv1.ListCumulativeRechargeRewardGrantsRequest) (*activityv1.ListCumulativeRechargeRewardGrantsResponse, error) { + return c.cumulativeRechargeClient.ListCumulativeRechargeRewardGrants(ctx, req) +} + func (c *GRPCClient) GetSevenDayCheckInConfig(ctx context.Context, req *activityv1.GetSevenDayCheckInConfigRequest) (*activityv1.GetSevenDayCheckInConfigResponse, error) { return c.sevenDayCheckInClient.GetSevenDayCheckInConfig(ctx, req) } @@ -116,6 +148,50 @@ func (c *GRPCClient) ListSevenDayCheckInClaims(ctx context.Context, req *activit return c.sevenDayCheckInClient.ListSevenDayCheckInClaims(ctx, req) } +func (c *GRPCClient) GetRoomTurnoverRewardConfig(ctx context.Context, req *activityv1.GetRoomTurnoverRewardConfigRequest) (*activityv1.GetRoomTurnoverRewardConfigResponse, error) { + return c.roomTurnoverRewardClient.GetRoomTurnoverRewardConfig(ctx, req) +} + +func (c *GRPCClient) UpdateRoomTurnoverRewardConfig(ctx context.Context, req *activityv1.UpdateRoomTurnoverRewardConfigRequest) (*activityv1.UpdateRoomTurnoverRewardConfigResponse, error) { + return c.roomTurnoverRewardClient.UpdateRoomTurnoverRewardConfig(ctx, req) +} + +func (c *GRPCClient) ListRoomTurnoverRewardSettlements(ctx context.Context, req *activityv1.ListRoomTurnoverRewardSettlementsRequest) (*activityv1.ListRoomTurnoverRewardSettlementsResponse, error) { + return c.roomTurnoverRewardClient.ListRoomTurnoverRewardSettlements(ctx, req) +} + +func (c *GRPCClient) RetryRoomTurnoverRewardSettlement(ctx context.Context, req *activityv1.RetryRoomTurnoverRewardSettlementRequest) (*activityv1.RetryRoomTurnoverRewardSettlementResponse, error) { + return c.roomTurnoverRewardClient.RetryRoomTurnoverRewardSettlement(ctx, req) +} + +func (c *GRPCClient) ListWeeklyStarCycles(ctx context.Context, req *activityv1.ListWeeklyStarCyclesRequest) (*activityv1.ListWeeklyStarCyclesResponse, error) { + return c.weeklyStarClient.ListWeeklyStarCycles(ctx, req) +} + +func (c *GRPCClient) CreateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) { + return c.weeklyStarClient.CreateWeeklyStarCycle(ctx, req) +} + +func (c *GRPCClient) GetWeeklyStarCycle(ctx context.Context, req *activityv1.GetWeeklyStarCycleRequest) (*activityv1.GetWeeklyStarCycleResponse, error) { + return c.weeklyStarClient.GetWeeklyStarCycle(ctx, req) +} + +func (c *GRPCClient) UpdateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) { + return c.weeklyStarClient.UpdateWeeklyStarCycle(ctx, req) +} + +func (c *GRPCClient) SetWeeklyStarCycleStatus(ctx context.Context, req *activityv1.SetWeeklyStarCycleStatusRequest) (*activityv1.SetWeeklyStarCycleStatusResponse, error) { + return c.weeklyStarClient.SetWeeklyStarCycleStatus(ctx, req) +} + +func (c *GRPCClient) ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) { + return c.weeklyStarClient.ListWeeklyStarLeaderboard(ctx, req) +} + +func (c *GRPCClient) ListWeeklyStarSettlements(ctx context.Context, req *activityv1.ListWeeklyStarSettlementsRequest) (*activityv1.ListWeeklyStarSettlementsResponse, error) { + return c.weeklyStarClient.ListWeeklyStarSettlements(ctx, req) +} + func (c *GRPCClient) GetLuckyGiftConfig(ctx context.Context, req *activityv1.GetLuckyGiftConfigRequest) (*activityv1.GetLuckyGiftConfigResponse, error) { return c.luckyGiftClient.GetLuckyGiftConfig(ctx, req) } diff --git a/server/admin/internal/integration/userclient/client.go b/server/admin/internal/integration/userclient/client.go index 958e16f1..aef89d21 100644 --- a/server/admin/internal/integration/userclient/client.go +++ b/server/admin/internal/integration/userclient/client.go @@ -393,7 +393,7 @@ type BDProfile struct { ParentLeaderUserID int64 `json:"parentLeaderUserId,string"` PositionAlias string `json:"positionAlias"` Status string `json:"status"` - CreatedByUserID int64 `json:"createdByUserId"` + CreatedByUserID int64 `json:"createdByUserId,string"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` DisplayUserID string `json:"displayUserId"` @@ -410,7 +410,7 @@ type BDProfile struct { } type Agency struct { - AgencyID int64 `json:"agencyId"` + AgencyID int64 `json:"agencyId,string"` OwnerUserID int64 `json:"ownerUserId,string"` RegionID int64 `json:"regionId"` ParentBDUserID int64 `json:"parentBdUserId,string"` @@ -418,7 +418,7 @@ type Agency struct { Status string `json:"status"` JoinEnabled bool `json:"joinEnabled"` MaxHosts int32 `json:"maxHosts"` - CreatedByUserID int64 `json:"createdByUserId"` + CreatedByUserID int64 `json:"createdByUserId,string"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` OwnerDisplayUserID string `json:"ownerDisplayUserId"` @@ -434,8 +434,8 @@ type HostProfile struct { UserID int64 `json:"userId,string"` Status string `json:"status"` RegionID int64 `json:"regionId"` - CurrentAgencyID int64 `json:"currentAgencyId"` - CurrentMembershipID int64 `json:"currentMembershipId"` + CurrentAgencyID int64 `json:"currentAgencyId,string"` + CurrentMembershipID int64 `json:"currentMembershipId,string"` Source string `json:"source"` FirstBecameHostAtMs int64 `json:"firstBecameHostAtMs"` CreatedAtMs int64 `json:"createdAtMs"` @@ -456,14 +456,14 @@ type CoinSellerProfile struct { UserID int64 `json:"userId,string"` Status string `json:"status"` MerchantAssetType string `json:"merchantAssetType"` - CreatedByUserID int64 `json:"createdByUserId"` + CreatedByUserID int64 `json:"createdByUserId,string"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` } type AgencyMembership struct { - MembershipID int64 `json:"membershipId"` - AgencyID int64 `json:"agencyId"` + MembershipID int64 `json:"membershipId,string"` + AgencyID int64 `json:"agencyId,string"` HostUserID int64 `json:"hostUserId,string"` RegionID int64 `json:"regionId"` MembershipType string `json:"membershipType"` diff --git a/server/admin/internal/integration/userclient/client_test.go b/server/admin/internal/integration/userclient/client_test.go new file mode 100644 index 00000000..e1f7309c --- /dev/null +++ b/server/admin/internal/integration/userclient/client_test.go @@ -0,0 +1,34 @@ +package userclient + +import ( + "encoding/json" + "strings" + "testing" +) + +func TestAgencyJSONKeepsSnowflakeIDsAsStrings(t *testing.T) { + agency := Agency{ + AgencyID: 321170072154411009, + OwnerUserID: 316033326332776448, + ParentBDUserID: 320470609978986496, + CreatedByUserID: 320470609978986496, + } + + payload, err := json.Marshal(agency) + if err != nil { + t.Fatalf("marshal agency: %v", err) + } + body := string(payload) + + // 后台前端运行在浏览器里,雪花 ID 超过 JavaScript 安全整数上限;这里必须以字符串返回,避免 321170072154411009 被解析成 321170072154411000 后再提交给删除接口。 + for _, want := range []string{ + `"agencyId":"321170072154411009"`, + `"ownerUserId":"316033326332776448"`, + `"parentBdUserId":"320470609978986496"`, + `"createdByUserId":"320470609978986496"`, + } { + if !strings.Contains(body, want) { + t.Fatalf("agency json should contain %s, got %s", want, body) + } + } +} diff --git a/server/admin/internal/modules/appuser/handler.go b/server/admin/internal/modules/appuser/handler.go index 9aaa38a7..708e4746 100644 --- a/server/admin/internal/modules/appuser/handler.go +++ b/server/admin/internal/modules/appuser/handler.go @@ -154,13 +154,19 @@ func (h *Handler) setUserStatus(c *gin.Context, status string, action string, su func parseListQuery(c *gin.Context) listQuery { options := shared.ListOptions(c) + regionID, regionIDSet := queryOptionalInt64(c, "region_id", "regionId") return normalizeListQuery(listQuery{ Page: options.Page, PageSize: options.PageSize, + Country: firstQuery(c, "country", "country_code", "countryCode"), Keyword: options.Keyword, + RegionID: regionID, + RegionIDSet: regionIDSet, Status: options.Status, SortBy: firstQuery(c, "sort_by", "sortBy"), SortDirection: firstQuery(c, "sort_direction", "sortDirection", "order"), + StartMs: queryInt64(c, "start_ms", "startMs", "start_at_ms", "startAtMs"), + EndMs: queryInt64(c, "end_ms", "endMs", "end_at_ms", "endAtMs"), }) } diff --git a/server/admin/internal/modules/appuser/request.go b/server/admin/internal/modules/appuser/request.go index f87e9aca..cf75fa14 100644 --- a/server/admin/internal/modules/appuser/request.go +++ b/server/admin/internal/modules/appuser/request.go @@ -3,10 +3,15 @@ package appuser type listQuery struct { Page int PageSize int + Country string Keyword string + RegionID int64 + RegionIDSet bool Status string SortBy string SortDirection string + StartMs int64 + EndMs int64 } type loginLogQuery struct { diff --git a/server/admin/internal/modules/appuser/service.go b/server/admin/internal/modules/appuser/service.go index 42e2def1..fac2f31d 100644 --- a/server/admin/internal/modules/appuser/service.go +++ b/server/admin/internal/modules/appuser/service.go @@ -73,17 +73,7 @@ func (s *Service) ListUsers(ctx context.Context, query listQuery) ([]AppUser, in } query = normalizeListQuery(query) appCode := appctx.FromContext(ctx) - whereSQL := "FROM users u WHERE u.app_code = ?" - args := []any{appCode} - if query.Status != "" { - whereSQL += " AND u.status = ?" - args = append(args, query.Status) - } - if query.Keyword != "" { - like := "%" + query.Keyword + "%" - whereSQL += " AND (CAST(u.user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.username LIKE ?)" - args = append(args, like, like, like) - } + whereSQL, args := appUserListWhereSQL(appCode, query) total, err := countRows(ctx, s.userDB, whereSQL, args...) if err != nil { @@ -562,13 +552,73 @@ func normalizeListQuery(query listQuery) listQuery { if query.PageSize > 100 { query.PageSize = 100 } + query.Country = normalizeCountryCode(query.Country) query.Keyword = strings.TrimSpace(query.Keyword) + if query.RegionID < 0 { + query.RegionID = 0 + query.RegionIDSet = false + } + if query.StartMs < 0 { + query.StartMs = 0 + } + if query.EndMs < 0 { + query.EndMs = 0 + } query.Status = strings.ToLower(strings.TrimSpace(query.Status)) query.SortBy = normalizeAppUserSortBy(query.SortBy) query.SortDirection = normalizeSortDirection(query.SortDirection) return query } +func appUserListWhereSQL(appCode string, query listQuery) (string, []any) { + whereSQL := "FROM users u WHERE u.app_code = ?" + args := []any{appCode} + if query.Status != "" { + whereSQL += " AND u.status = ?" + args = append(args, query.Status) + } + if query.Keyword != "" { + like := "%" + query.Keyword + "%" + whereSQL += " AND (CAST(u.user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.username LIKE ?)" + args = append(args, like, like, like) + } + if query.Country != "" { + // 国家筛选用 users.country 的 ISO code 精确命中;前端传展示名时不会被猜测转换,避免误筛到错误国家。 + whereSQL += " AND UPPER(u.country) = ?" + args = append(args, query.Country) + } + if query.RegionIDSet { + if query.RegionID == 0 { + // 列表展示里无区域或无有效业务区域都会显示为 GLOBAL;筛选 GLOBAL 时保持同一套展示口径。 + whereSQL += " AND (COALESCE(u.region_id, 0) = 0 OR NOT " + appUserValidRegionExistsSQL("u") + ")" + } else { + whereSQL += " AND u.region_id = ?" + args = append(args, query.RegionID) + } + } + if query.StartMs > 0 { + // 时间区间筛选的是用户创建时间,和列表“创建 / 活跃”列的第一行保持一致。 + whereSQL += " AND u.created_at_ms >= ?" + args = append(args, query.StartMs) + } + if query.EndMs > 0 { + whereSQL += " AND u.created_at_ms < ?" + args = append(args, query.EndMs) + } + return whereSQL, args +} + +func appUserValidRegionExistsSQL(userAlias string) string { + return fmt.Sprintf(`EXISTS ( + SELECT 1 FROM regions rg + WHERE rg.app_code = %[1]s.app_code + AND rg.region_id = %[1]s.region_id + AND rg.status = 'active' + AND rg.region_code NOT IN ('Australia and New Zealand', 'Caribbean', 'Melanesia', 'Micronesia', 'Polynesia', 'Southern Africa', 'UNSPECIFIED') + LIMIT 1 + )`, userAlias) +} + func normalizeAppUserSortBy(value string) string { switch strings.ToLower(strings.TrimSpace(value)) { case "coin", "coins": diff --git a/server/admin/internal/modules/appuser/service_test.go b/server/admin/internal/modules/appuser/service_test.go index 8f68b1cc..070f222b 100644 --- a/server/admin/internal/modules/appuser/service_test.go +++ b/server/admin/internal/modules/appuser/service_test.go @@ -1,6 +1,9 @@ package appuser -import "testing" +import ( + "strings" + "testing" +) func TestNormalizeListQuerySort(t *testing.T) { query := normalizeListQuery(listQuery{Page: 0, PageSize: 1000, SortBy: "coins", SortDirection: "asc"}) @@ -12,6 +15,46 @@ func TestNormalizeListQuerySort(t *testing.T) { if query.SortBy != "created_at" || query.SortDirection != "desc" { t.Fatalf("created sort default mismatch: %+v", query) } + + query = normalizeListQuery(listQuery{Country: " ph ", RegionID: -3, RegionIDSet: true, StartMs: -1, EndMs: -2}) + if query.Country != "PH" || query.RegionIDSet || query.StartMs != 0 || query.EndMs != 0 { + t.Fatalf("filter normalization mismatch: %+v", query) + } +} + +func TestAppUserListWhereSQLFilters(t *testing.T) { + whereSQL, args := appUserListWhereSQL("lalu", normalizeListQuery(listQuery{ + Country: "ph", + Keyword: "hunter", + RegionID: 3, + RegionIDSet: true, + StartMs: 1000, + EndMs: 2000, + Status: "active", + })) + for _, want := range []string{ + "u.status = ?", + "CAST(u.user_id AS CHAR) LIKE ?", + "UPPER(u.country) = ?", + "u.region_id = ?", + "u.created_at_ms >= ?", + "u.created_at_ms < ?", + } { + if !strings.Contains(whereSQL, want) { + t.Fatalf("where sql missing %q: %s", want, whereSQL) + } + } + if len(args) != 9 || args[0] != "lalu" || args[5] != "PH" || args[6] != int64(3) || args[7] != int64(1000) || args[8] != int64(2000) { + t.Fatalf("where args mismatch: %+v", args) + } + + whereSQL, args = appUserListWhereSQL("lalu", normalizeListQuery(listQuery{RegionID: 0, RegionIDSet: true})) + if !strings.Contains(whereSQL, "COALESCE(u.region_id, 0) = 0") || !strings.Contains(whereSQL, "NOT EXISTS") { + t.Fatalf("global region sql mismatch: %s", whereSQL) + } + if len(args) != 1 || args[0] != "lalu" { + t.Fatalf("global region args mismatch: %+v", args) + } } func TestAppUserOrderSQL(t *testing.T) { diff --git a/server/admin/internal/modules/coinledger/dto.go b/server/admin/internal/modules/coinledger/dto.go index 6d0629c2..a8c97240 100644 --- a/server/admin/internal/modules/coinledger/dto.go +++ b/server/admin/internal/modules/coinledger/dto.go @@ -7,6 +7,12 @@ type coinLedgerUserDTO struct { Avatar string `json:"avatar"` } +type CSVExport struct { + FileName string + Content []byte + Count int +} + type coinLedgerEntryDTO struct { EntryID int64 `json:"entryId"` TransactionID string `json:"transactionId"` @@ -26,20 +32,22 @@ type coinLedgerEntryDTO struct { } type coinSellerLedgerDTO struct { - EntryID int64 `json:"entryId"` - TransactionID string `json:"transactionId"` - CommandID string `json:"commandId"` - LedgerType string `json:"ledgerType"` - BizType string `json:"bizType"` - Seller coinLedgerUserDTO `json:"seller"` - Receiver coinLedgerUserDTO `json:"receiver"` - Amount int64 `json:"amount"` - Direction string `json:"direction"` - AvailableDelta int64 `json:"availableDelta"` - SellerBalanceAfter int64 `json:"sellerBalanceAfter"` - CounterpartyUserID string `json:"counterpartyUserId"` - Metadata map[string]any `json:"metadata"` - CreatedAtMS int64 `json:"createdAtMs"` + EntryID int64 `json:"entryId"` + TransactionID string `json:"transactionId"` + CommandID string `json:"commandId"` + LedgerType string `json:"ledgerType"` + BizType string `json:"bizType"` + Seller coinLedgerUserDTO `json:"seller"` + Receiver coinLedgerUserDTO `json:"receiver"` + Amount int64 `json:"amount"` + Direction string `json:"direction"` + AvailableDelta int64 `json:"availableDelta"` + SellerBalanceAfter int64 `json:"sellerBalanceAfter"` + CounterpartyUserID string `json:"counterpartyUserId"` + OperatorUserID string `json:"operatorUserId"` + Operator coinAdjustmentOperatorDTO `json:"operator"` + Metadata map[string]any `json:"metadata"` + CreatedAtMS int64 `json:"createdAtMs"` } type coinAdjustmentOperatorDTO struct { diff --git a/server/admin/internal/modules/coinledger/handler.go b/server/admin/internal/modules/coinledger/handler.go index 75231536..ac21d6aa 100644 --- a/server/admin/internal/modules/coinledger/handler.go +++ b/server/admin/internal/modules/coinledger/handler.go @@ -55,6 +55,28 @@ func (h *Handler) ListCoinSellerLedger(c *gin.Context) { response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total}) } +func (h *Handler) ExportCoinSellerLedger(c *gin.Context) { + query, ok := parseCoinSellerLedgerQuery(c) + if !ok { + return + } + export, err := h.service.ExportCoinSellerLedger(c.Request.Context(), appctx.FromContext(c.Request.Context()), query) + if err != nil { + if errors.Is(err, errInvalidCoinSellerLedgerType) { + response.BadRequest(c, "流水类型不正确") + return + } + response.ServerError(c, "导出币商流水失败") + return + } + + shared.OperationLog(c, h.audit, "export-coin-seller-ledger", "wallet_entries", "success", fmt.Sprintf("%d entries", export.Count)) + c.Header("Content-Disposition", "attachment; filename="+export.FileName) + c.Header("Content-Type", "text/csv; charset=utf-8") + c.Writer.WriteHeader(200) + _, _ = c.Writer.Write(export.Content) +} + func (h *Handler) ListCoinAdjustments(c *gin.Context) { query, ok := parseListQuery(c) if !ok { diff --git a/server/admin/internal/modules/coinledger/routes.go b/server/admin/internal/modules/coinledger/routes.go index 7ce439be..1b05a107 100644 --- a/server/admin/internal/modules/coinledger/routes.go +++ b/server/admin/internal/modules/coinledger/routes.go @@ -13,6 +13,7 @@ func RegisterRoutes(protected *gin.RouterGroup, h *Handler) { protected.GET("/admin/operations/coin-ledger", middleware.RequirePermission("coin-ledger:view"), h.ListCoinLedger) protected.GET("/admin/operations/coin-seller-ledger", middleware.RequirePermission("coin-seller-ledger:view"), h.ListCoinSellerLedger) + protected.GET("/admin/operations/coin-seller-ledger/export", middleware.RequirePermission("coin-seller-ledger:view"), h.ExportCoinSellerLedger) protected.GET("/admin/operations/coin-adjustments", middleware.RequirePermission("coin-adjustment:view"), h.ListCoinAdjustments) protected.GET("/admin/operations/coin-adjustments/target", middleware.RequirePermission("coin-adjustment:create"), h.LookupCoinAdjustmentTarget) protected.POST("/admin/operations/coin-adjustments", middleware.RequirePermission("coin-adjustment:create"), h.CreateCoinAdjustment) diff --git a/server/admin/internal/modules/coinledger/service.go b/server/admin/internal/modules/coinledger/service.go index a8731ec8..8e44247b 100644 --- a/server/admin/internal/modules/coinledger/service.go +++ b/server/admin/internal/modules/coinledger/service.go @@ -1,8 +1,10 @@ package coinledger import ( + "bytes" "context" "database/sql" + "encoding/csv" "encoding/json" "errors" "fmt" @@ -154,6 +156,41 @@ func (s *Service) ListCoinLedger(ctx context.Context, appCode string, query list } func (s *Service) ListCoinSellerLedger(ctx context.Context, appCode string, query coinSellerLedgerQuery) ([]coinSellerLedgerDTO, int64, error) { + return s.listCoinSellerLedger(ctx, appCode, query, true) +} + +func (s *Service) ExportCoinSellerLedger(ctx context.Context, appCode string, query coinSellerLedgerQuery) (CSVExport, error) { + items, total, err := s.listCoinSellerLedger(ctx, appCode, query, false) + if err != nil { + return CSVExport{}, err + } + var buf bytes.Buffer + writer := csv.NewWriter(&buf) + _ = writer.Write([]string{"币商名称", "币商短ID", "币商长ID", "流水类型", "转账金额", "收款人名称", "收款人短ID", "收款人长ID", "操作人", "操作人ID", "币商余额", "转账时间", "交易ID", "命令ID"}) + for _, item := range items { + operatorName, operatorID := coinSellerLedgerOperatorExportFields(item) + _ = writer.Write([]string{ + item.Seller.Username, + item.Seller.DisplayUserID, + item.Seller.UserID, + coinSellerLedgerLabel(item), + strconv.FormatInt(signedCoinSellerLedgerAmount(item), 10), + item.Receiver.Username, + item.Receiver.DisplayUserID, + item.Receiver.UserID, + operatorName, + operatorID, + strconv.FormatInt(item.SellerBalanceAfter, 10), + time.UnixMilli(item.CreatedAtMS).UTC().Format(time.RFC3339), + item.TransactionID, + item.CommandID, + }) + } + writer.Flush() + return CSVExport{FileName: "hyapp-coin-seller-ledger.csv", Content: buf.Bytes(), Count: int(total)}, writer.Error() +} + +func (s *Service) listCoinSellerLedger(ctx context.Context, appCode string, query coinSellerLedgerQuery, paginated bool) ([]coinSellerLedgerDTO, int64, error) { query = normalizeCoinSellerLedgerQuery(query) if s == nil || s.walletDB == nil { return nil, 0, fmt.Errorf("wallet mysql is not configured") @@ -177,29 +214,35 @@ func (s *Service) ListCoinSellerLedger(ctx context.Context, appCode string, quer return nil, 0, err } var total int64 - if err := s.walletDB.QueryRowContext(ctx, ` - SELECT COUNT(*) - FROM wallet_entries e - JOIN wallet_transactions wt ON wt.app_code = e.app_code AND wt.transaction_id = e.transaction_id - `+whereSQL, - args..., - ).Scan(&total); err != nil { - return nil, 0, err + if paginated { + if err := s.walletDB.QueryRowContext(ctx, ` + SELECT COUNT(*) + FROM wallet_entries e + JOIN wallet_transactions wt ON wt.app_code = e.app_code AND wt.transaction_id = e.transaction_id + `+whereSQL, + args..., + ).Scan(&total); err != nil { + return nil, 0, err + } } // 明细查询只取币商侧 COIN_SELLER_COIN 分录:available_delta 表示币商库存可用变化, // available_after 表示该币商分录落账后的库存余额,receiver 再按 biz_type 从 metadata/counterparty 补出。 - rows, err := s.walletDB.QueryContext(ctx, ` + querySQL := ` SELECT e.entry_id, e.transaction_id, wt.command_id, e.user_id, wt.biz_type, e.available_delta, e.available_after, e.counterparty_user_id, COALESCE(CAST(wt.metadata_json AS CHAR), '{}'), e.created_at_ms FROM wallet_entries e JOIN wallet_transactions wt ON wt.app_code = e.app_code AND wt.transaction_id = e.transaction_id - `+whereSQL+` - ORDER BY e.created_at_ms DESC, e.entry_id DESC - LIMIT ? OFFSET ?`, - append(args, query.PageSize, offset(query.Page, query.PageSize))..., - ) + ` + whereSQL + ` + ORDER BY e.created_at_ms DESC, e.entry_id DESC` + queryArgs := args + if paginated { + querySQL += ` + LIMIT ? OFFSET ?` + queryArgs = append(queryArgs, query.PageSize, offset(query.Page, query.PageSize)) + } + rows, err := s.walletDB.QueryContext(ctx, querySQL, queryArgs...) if err != nil { return nil, 0, err } @@ -207,6 +250,7 @@ func (s *Service) ListCoinSellerLedger(ctx context.Context, appCode string, quer items := make([]coinSellerLedgerDTO, 0, query.PageSize) profileIDs := make([]int64, 0, query.PageSize*2) + operatorIDs := make([]int64, 0, query.PageSize) for rows.Next() { var item coinSellerLedgerDTO var sellerUserID int64 @@ -230,6 +274,7 @@ func (s *Service) ListCoinSellerLedger(ctx context.Context, appCode string, quer if err != nil { return nil, 0, err } + operatorUserID := metadataInt64(metadata, "operator_user_id", "operatorUserId") // 收款人按产品口径取实际收款人:币商转用户优先取 metadata.target_user_id,老数据没有 metadata 时再用 counterparty_user_id; // 后台入账和工资转币商的实际收款人都是币商本人,所以不能被 counterparty 字段误导成付款方或操作方。 receiverUserID := coinSellerLedgerReceiverUserID(item.BizType, sellerUserID, counterpartyUserID, metadata) @@ -237,20 +282,31 @@ func (s *Service) ListCoinSellerLedger(ctx context.Context, appCode string, quer item.Direction = directionForDelta(item.AvailableDelta) item.Amount = absInt64(item.AvailableDelta) item.CounterpartyUserID = formatOptionalID(counterpartyUserID) + item.OperatorUserID = formatOptionalID(operatorUserID) item.Metadata = metadata item.Seller = coinLedgerUserDTO{UserID: strconv.FormatInt(sellerUserID, 10)} item.Receiver = coinLedgerUserDTO{UserID: strconv.FormatInt(receiverUserID, 10)} items = append(items, item) profileIDs = append(profileIDs, sellerUserID, receiverUserID) + if operatorUserID > 0 && item.LedgerType == coinSellerLedgerTypeAdminStockCredit { + operatorIDs = append(operatorIDs, operatorUserID) + } } if err := rows.Err(); err != nil { return nil, 0, err } + if !paginated { + total = int64(len(items)) + } profiles, err := s.userProfiles(ctx, appCode, profileIDs) if err != nil { return nil, 0, err } + operators, err := s.adminProfiles(ctx, operatorIDs) + if err != nil { + return nil, 0, err + } for i := range items { sellerUserID, _ := strconv.ParseInt(items[i].Seller.UserID, 10, 64) receiverUserID, _ := strconv.ParseInt(items[i].Receiver.UserID, 10, 64) @@ -262,6 +318,10 @@ func (s *Service) ListCoinSellerLedger(ctx context.Context, appCode string, quer if profile, ok := profiles[receiverUserID]; ok { items[i].Receiver = userDTOFromProfile(profile) } + operatorUserID, _ := strconv.ParseInt(items[i].OperatorUserID, 10, 64) + if operator, ok := operators[operatorUserID]; ok { + items[i].Operator = operator + } } return items, total, nil } @@ -759,6 +819,45 @@ func coinSellerLedgerTypeForBizType(bizType string) string { } } +func coinSellerLedgerLabel(item coinSellerLedgerDTO) string { + switch item.LedgerType { + case coinSellerLedgerTypeAdminStockCredit: + return "后台入账" + case coinSellerLedgerTypeSellerTransfer: + return "币商转用户" + case coinSellerLedgerTypeSalaryTransferIncome: + return "工资转币商" + default: + switch item.BizType { + case coinSellerStockPurchaseBizType: + return "币商进货" + case coinSellerCoinCompensationBizType: + return "金币补偿" + case coinSellerTransferBizType: + return "币商转用户" + case salaryTransferToCoinSellerBizType: + return "工资转币商" + default: + return firstNonEmpty(item.LedgerType, item.BizType, "-") + } + } +} + +func signedCoinSellerLedgerAmount(item coinSellerLedgerDTO) int64 { + if item.Direction == directionOut || item.AvailableDelta < 0 { + return -absInt64(item.Amount) + } + return absInt64(item.Amount) +} + +func coinSellerLedgerOperatorExportFields(item coinSellerLedgerDTO) (string, string) { + if item.LedgerType != coinSellerLedgerTypeAdminStockCredit { + return "", "" + } + operatorID := firstNonEmpty(item.Operator.AdminID, item.OperatorUserID) + return firstNonEmpty(item.Operator.Username, item.Operator.Name, operatorID), operatorID +} + func coinSellerLedgerReceiverUserID(bizType string, sellerUserID int64, counterpartyUserID int64, metadata map[string]any) int64 { if bizType == coinSellerTransferBizType { // 新数据把真实收款用户写在 metadata.target_user_id,优先使用它,避免 counterparty 语义随老交易实现变化。 @@ -846,6 +945,16 @@ func metadataInt64(metadata map[string]any, keys ...string) int64 { return 0 } +func firstNonEmpty(values ...string) string { + for _, value := range values { + value = strings.TrimSpace(value) + if value != "" { + return value + } + } + return "" +} + func parseFlexibleUserID(value any) (int64, error) { switch typed := value.(type) { case float64: diff --git a/server/admin/internal/modules/coinledger/service_test.go b/server/admin/internal/modules/coinledger/service_test.go index b7aa6aa7..88b8c77d 100644 --- a/server/admin/internal/modules/coinledger/service_test.go +++ b/server/admin/internal/modules/coinledger/service_test.go @@ -59,6 +59,19 @@ func TestCoinSellerLedgerWhereMapsAdminStockCredit(t *testing.T) { } } +func TestCoinSellerLedgerWhereEmptyTypeUsesAllPublicTypes(t *testing.T) { + where, args, err := coinSellerLedgerWhere("lalu", coinSellerLedgerQuery{}, nil) + if err != nil { + t.Fatalf("coin seller ledger where failed: %v", err) + } + if want := "WHERE e.app_code = ? AND e.asset_type = ? AND wt.biz_type IN (?,?,?,?)"; where != want { + t.Fatalf("where mismatch:\nwant %s\n got %s", want, where) + } + if len(args) != 6 || args[0] != "lalu" || args[1] != coinSellerAssetType || args[2] != coinSellerStockPurchaseBizType || args[3] != coinSellerCoinCompensationBizType || args[4] != coinSellerTransferBizType || args[5] != salaryTransferToCoinSellerBizType { + t.Fatalf("args mismatch: %#v", args) + } +} + func TestCoinSellerLedgerWhereRejectsInvalidType(t *testing.T) { _, _, err := coinSellerLedgerWhere("lalu", coinSellerLedgerQuery{LedgerType: "bad_type"}, nil) if err == nil { @@ -79,6 +92,24 @@ func TestCoinSellerLedgerReceiverUserIDUsesActualReceiver(t *testing.T) { } } +func TestCoinSellerLedgerOperatorExportFieldsOnlyForAdminStock(t *testing.T) { + adminItem := coinSellerLedgerDTO{ + LedgerType: coinSellerLedgerTypeAdminStockCredit, + OperatorUserID: "7", + Operator: coinAdjustmentOperatorDTO{AdminID: "7", Username: "hyappadmin", Name: "Admin"}, + } + operatorName, operatorID := coinSellerLedgerOperatorExportFields(adminItem) + if operatorName != "hyappadmin" || operatorID != "7" { + t.Fatalf("operator fields mismatch: name=%q id=%q", operatorName, operatorID) + } + + transferItem := coinSellerLedgerDTO{LedgerType: coinSellerLedgerTypeSellerTransfer, OperatorUserID: "7"} + operatorName, operatorID = coinSellerLedgerOperatorExportFields(transferItem) + if operatorName != "" || operatorID != "" { + t.Fatalf("seller transfer should not export operator: name=%q id=%q", operatorName, operatorID) + } +} + func TestNormalizeCoinSellerLedgerQueryCapsPageSize(t *testing.T) { query := normalizeCoinSellerLedgerQuery(coinSellerLedgerQuery{Page: -1, PageSize: 1000, SellerKeyword: " 164425 ", LedgerType: " seller_transfer "}) if query.Page != 1 || query.PageSize != 100 || query.SellerKeyword != "164425" || query.LedgerType != coinSellerLedgerTypeSellerTransfer { diff --git a/server/admin/internal/modules/cumulativerechargereward/handler.go b/server/admin/internal/modules/cumulativerechargereward/handler.go new file mode 100644 index 00000000..165af9c4 --- /dev/null +++ b/server/admin/internal/modules/cumulativerechargereward/handler.go @@ -0,0 +1,356 @@ +package cumulativerechargereward + +import ( + "context" + "database/sql" + "strings" + "time" + + "hyapp-admin-server/internal/appctx" + "hyapp-admin-server/internal/integration/activityclient" + "hyapp-admin-server/internal/middleware" + "hyapp-admin-server/internal/modules/shared" + "hyapp-admin-server/internal/response" + activityv1 "hyapp.local/api/proto/activity/v1" + + "github.com/gin-gonic/gin" +) + +type Handler struct { + activity activityclient.Client + userDB *sql.DB + audit shared.OperationLogger +} + +func New(activity activityclient.Client, userDB *sql.DB, audit shared.OperationLogger) *Handler { + return &Handler{activity: activity, userDB: userDB, audit: audit} +} + +type configRequest struct { + Enabled bool `json:"enabled"` + Tiers []tierDTO `json:"tiers"` +} + +type configDTO struct { + AppCode string `json:"app_code"` + Enabled bool `json:"enabled"` + Tiers []tierDTO `json:"tiers"` + UpdatedByAdminID int64 `json:"updated_by_admin_id"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type tierDTO struct { + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + TierName string `json:"tier_name"` + ThresholdUSDMinor int64 `json:"threshold_usd_minor"` + ResourceGroupID int64 `json:"resource_group_id"` + Status string `json:"status"` + SortOrder int32 `json:"sort_order"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type grantDTO struct { + GrantID string `json:"grant_id"` + AppCode string `json:"app_code"` + CycleKey string `json:"cycle_key"` + UserID int64 `json:"user_id"` + User *userDTO `json:"user,omitempty"` + EventID string `json:"event_id"` + TransactionID string `json:"transaction_id"` + CommandID string `json:"command_id"` + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + ThresholdUSDMinor int64 `json:"threshold_usd_minor"` + ResourceGroupID int64 `json:"resource_group_id"` + ReachedUSDMinor int64 `json:"reached_usd_minor"` + QualifyingUSDMinor int64 `json:"qualifying_usd_minor"` + RechargeCoinAmount int64 `json:"recharge_coin_amount"` + RechargeType string `json:"recharge_type"` + Status string `json:"status"` + WalletCommandID string `json:"wallet_command_id"` + WalletGrantID string `json:"wallet_grant_id"` + FailureReason string `json:"failure_reason"` + GrantedAtMS int64 `json:"granted_at_ms"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type userDTO struct { + UserID int64 `json:"user_id"` + DisplayUserID string `json:"display_user_id"` + Username string `json:"username"` + Avatar string `json:"avatar"` +} + +func (h *Handler) GetConfig(c *gin.Context) { + resp, err := h.activity.GetCumulativeRechargeRewardConfig(c.Request.Context(), &activityv1.GetCumulativeRechargeRewardConfigRequest{Meta: h.meta(c)}) + if err != nil { + response.ServerError(c, "获取累充奖励配置失败") + return + } + response.OK(c, configFromProto(resp.GetConfig())) +} + +func (h *Handler) UpdateConfig(c *gin.Context) { + var req configRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, "累充奖励配置参数不正确") + return + } + // admin-server 只做输入透传和操作人注入,档位有效性、重复门槛和启用规则统一交给 activity-service 校验。 + tiers := make([]*activityv1.CumulativeRechargeRewardTier, 0, len(req.Tiers)) + for _, tier := range req.Tiers { + tiers = append(tiers, &activityv1.CumulativeRechargeRewardTier{ + TierId: tier.TierID, + TierCode: strings.TrimSpace(tier.TierCode), + TierName: strings.TrimSpace(tier.TierName), + ThresholdUsdMinor: tier.ThresholdUSDMinor, + ResourceGroupId: tier.ResourceGroupID, + Status: strings.TrimSpace(tier.Status), + SortOrder: tier.SortOrder, + }) + } + resp, err := h.activity.UpdateCumulativeRechargeRewardConfig(c.Request.Context(), &activityv1.UpdateCumulativeRechargeRewardConfigRequest{ + Meta: h.meta(c), + Enabled: req.Enabled, + Tiers: tiers, + OperatorAdminId: int64(middleware.CurrentUserID(c)), + }) + if err != nil { + response.BadRequest(c, err.Error()) + return + } + item := configFromProto(resp.GetConfig()) + shared.OperationLogWithResourceID(c, h.audit, "update-cumulative-recharge-reward", "cumulative_recharge_reward_configs", item.AppCode, "success", "") + response.OK(c, item) +} + +func (h *Handler) ListGrants(c *gin.Context) { + options := shared.ListOptions(c) + // 发放记录主表只存 user_id;后台 keyword 先在 users 表解析成精确用户,再下推给 activity-service 查询。 + userID, matched, ok := h.resolveGrantUserID(c.Request.Context(), strings.TrimSpace(options.Keyword)) + if !ok { + response.ServerError(c, "查询用户信息失败") + return + } + if options.Keyword != "" && !matched { + response.OK(c, response.Page{Items: []grantDTO{}, Page: options.Page, PageSize: options.PageSize, Total: 0}) + return + } + resp, err := h.activity.ListCumulativeRechargeRewardGrants(c.Request.Context(), &activityv1.ListCumulativeRechargeRewardGrantsRequest{ + Meta: h.meta(c), + Status: strings.TrimSpace(options.Status), + UserId: userID, + CycleKey: strings.TrimSpace(c.Query("cycle_key")), + Page: int32(options.Page), + PageSize: int32(options.PageSize), + }) + if err != nil { + response.ServerError(c, "获取累充奖励发放记录失败") + return + } + items := make([]grantDTO, 0, len(resp.GetGrants())) + for _, grant := range resp.GetGrants() { + items = append(items, grantFromProto(grant)) + } + if err := h.fillGrantUsers(c.Request.Context(), items); err != nil { + response.ServerError(c, "补全用户信息失败") + return + } + response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) +} + +func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta { + return &activityv1.RequestMeta{ + RequestId: middleware.CurrentRequestID(c), + Caller: "admin-server", + AppCode: appctx.FromContext(c.Request.Context()), + SentAtMs: time.Now().UnixMilli(), + } +} + +func (h *Handler) resolveGrantUserID(ctx context.Context, keyword string) (int64, bool, bool) { + if keyword == "" { + return 0, false, true + } + if h.userDB == nil { + return 0, false, true + } + // keyword 支持真实 user_id、短 ID 和昵称模糊匹配;排序优先精确 ID,避免昵称碰撞查到错误用户。 + appCode := appctx.FromContext(ctx) + rows, err := h.userDB.QueryContext(ctx, ` + SELECT user_id + FROM users + WHERE app_code = ? + AND ( + CAST(user_id AS CHAR) = ? + OR current_display_user_id = ? + OR username LIKE ? + ) + ORDER BY + CASE + WHEN CAST(user_id AS CHAR) = ? THEN 0 + WHEN current_display_user_id = ? THEN 1 + ELSE 2 + END, + user_id DESC + LIMIT 1`, + appCode, keyword, keyword, "%"+keyword+"%", keyword, keyword, + ) + if err != nil { + return 0, false, false + } + defer rows.Close() + if !rows.Next() { + return 0, false, rows.Err() == nil + } + var userID int64 + if err := rows.Scan(&userID); err != nil { + return 0, false, false + } + return userID, true, rows.Err() == nil +} + +func (h *Handler) fillGrantUsers(ctx context.Context, grants []grantDTO) error { + if h.userDB == nil || len(grants) == 0 { + return nil + } + // 列表补用户资料是展示增强,找不到用户时仍返回 grant 原始 user_id,避免审计记录因为用户资料缺失而不可见。 + ids := collectGrantUserIDs(grants) + if len(ids) == 0 { + return nil + } + args := append([]any{appctx.FromContext(ctx)}, int64Args(ids)...) + rows, err := h.userDB.QueryContext(ctx, ` + SELECT user_id, current_display_user_id, COALESCE(username, ''), COALESCE(avatar, '') + FROM users + WHERE app_code = ? AND user_id IN (`+placeholders(len(ids))+`)`, + args..., + ) + if err != nil { + return err + } + defer rows.Close() + users := make(map[int64]userDTO, len(ids)) + for rows.Next() { + var user userDTO + if err := rows.Scan(&user.UserID, &user.DisplayUserID, &user.Username, &user.Avatar); err != nil { + return err + } + users[user.UserID] = user + } + if err := rows.Err(); err != nil { + return err + } + for index := range grants { + if user, ok := users[grants[index].UserID]; ok { + grants[index].User = &user + continue + } + grants[index].User = &userDTO{UserID: grants[index].UserID} + } + return nil +} + +func configFromProto(config *activityv1.CumulativeRechargeRewardConfig) configDTO { + if config == nil { + return configDTO{Tiers: []tierDTO{}} + } + tiers := make([]tierDTO, 0, len(config.GetTiers())) + for _, tier := range config.GetTiers() { + tiers = append(tiers, tierFromProto(tier)) + } + return configDTO{ + AppCode: config.GetAppCode(), + Enabled: config.GetEnabled(), + Tiers: tiers, + UpdatedByAdminID: config.GetUpdatedByAdminId(), + CreatedAtMS: config.GetCreatedAtMs(), + UpdatedAtMS: config.GetUpdatedAtMs(), + } +} + +func tierFromProto(tier *activityv1.CumulativeRechargeRewardTier) tierDTO { + if tier == nil { + return tierDTO{} + } + return tierDTO{ + TierID: tier.GetTierId(), + TierCode: tier.GetTierCode(), + TierName: tier.GetTierName(), + ThresholdUSDMinor: tier.GetThresholdUsdMinor(), + ResourceGroupID: tier.GetResourceGroupId(), + Status: tier.GetStatus(), + SortOrder: tier.GetSortOrder(), + CreatedAtMS: tier.GetCreatedAtMs(), + UpdatedAtMS: tier.GetUpdatedAtMs(), + } +} + +func grantFromProto(grant *activityv1.CumulativeRechargeRewardGrant) grantDTO { + if grant == nil { + return grantDTO{} + } + return grantDTO{ + GrantID: grant.GetGrantId(), + AppCode: grant.GetAppCode(), + CycleKey: grant.GetCycleKey(), + UserID: grant.GetUserId(), + EventID: grant.GetEventId(), + TransactionID: grant.GetTransactionId(), + CommandID: grant.GetCommandId(), + TierID: grant.GetTierId(), + TierCode: grant.GetTierCode(), + ThresholdUSDMinor: grant.GetThresholdUsdMinor(), + ResourceGroupID: grant.GetResourceGroupId(), + ReachedUSDMinor: grant.GetReachedUsdMinor(), + QualifyingUSDMinor: grant.GetQualifyingUsdMinor(), + RechargeCoinAmount: grant.GetRechargeCoinAmount(), + RechargeType: grant.GetRechargeType(), + Status: grant.GetStatus(), + WalletCommandID: grant.GetWalletCommandId(), + WalletGrantID: grant.GetWalletGrantId(), + FailureReason: grant.GetFailureReason(), + GrantedAtMS: grant.GetGrantedAtMs(), + CreatedAtMS: grant.GetCreatedAtMs(), + UpdatedAtMS: grant.GetUpdatedAtMs(), + } +} + +func collectGrantUserIDs(grants []grantDTO) []int64 { + seen := make(map[int64]struct{}, len(grants)) + ids := make([]int64, 0, len(grants)) + for _, grant := range grants { + if grant.UserID <= 0 { + continue + } + if _, ok := seen[grant.UserID]; ok { + continue + } + seen[grant.UserID] = struct{}{} + ids = append(ids, grant.UserID) + } + return ids +} + +func int64Args(ids []int64) []any { + args := make([]any, 0, len(ids)) + for _, id := range ids { + args = append(args, id) + } + return args +} + +func placeholders(count int) string { + if count <= 0 { + return "" + } + parts := make([]string, count) + for i := range parts { + parts[i] = "?" + } + return strings.Join(parts, ",") +} diff --git a/server/admin/internal/modules/cumulativerechargereward/routes.go b/server/admin/internal/modules/cumulativerechargereward/routes.go new file mode 100644 index 00000000..390d662b --- /dev/null +++ b/server/admin/internal/modules/cumulativerechargereward/routes.go @@ -0,0 +1,16 @@ +package cumulativerechargereward + +import ( + "hyapp-admin-server/internal/middleware" + + "github.com/gin-gonic/gin" +) + +func RegisterRoutes(protected *gin.RouterGroup, h *Handler) { + if h == nil { + return + } + protected.GET("/admin/activity/cumulative-recharge-reward/config", middleware.RequirePermission("cumulative-recharge-reward:view"), h.GetConfig) + protected.PUT("/admin/activity/cumulative-recharge-reward/config", middleware.RequirePermission("cumulative-recharge-reward:update"), h.UpdateConfig) + protected.GET("/admin/activity/cumulative-recharge-reward/grants", middleware.RequirePermission("cumulative-recharge-reward:view"), h.ListGrants) +} diff --git a/server/admin/internal/modules/gamemanagement/sync_games.go b/server/admin/internal/modules/gamemanagement/sync_games.go index 98fb4d3c..f4195818 100644 --- a/server/admin/internal/modules/gamemanagement/sync_games.go +++ b/server/admin/internal/modules/gamemanagement/sync_games.go @@ -26,6 +26,7 @@ import ( const ( adapterBaishunV1 = "baishun_v1" adapterZeeOneV1 = "zeeone_v1" + adapterVivaGamesV1 = "vivagames_v1" defaultGameStatus = "disabled" defaultGameCategory = "casino" defaultLaunchMode = "full_screen" @@ -131,6 +132,8 @@ func fetchProviderGameSyncPlan(ctx context.Context, platform *gamev1.GamePlatfor return fetchBaishunGameSyncPlan(ctx, platform, req) case adapterZeeOneV1: return fetchZeeOneGameSyncPlan(platform, req) + case adapterVivaGamesV1: + return fetchVivaGamesGameSyncPlan(platform, req) default: // 不在配置文件写死游戏:每家厂商新增“列表 API”时只加一个 adapter fetcher,后台入口保持不变。 return providerGameSyncPlan{}, fmt.Errorf("当前适配器暂未接入厂商游戏列表 API: %s", platform.GetAdapterType()) @@ -177,6 +180,14 @@ type zeeoneSyncConfig struct { GameCovers map[string]string `json:"game_covers"` } +type vivaGamesSyncConfig struct { + // VIVAGAMES 文档说明游戏 URL 由厂商提供,后台用这个映射把 URL 转成可勾选的本地目录候选项。 + GameURLs map[string]string `json:"game_urls"` + GameNames map[string]string `json:"game_names"` + GameIcons map[string]string `json:"game_icons"` + GameCovers map[string]string `json:"game_covers"` +} + func fetchZeeOneGameSyncPlan(platform *gamev1.GamePlatform, req syncGamesRequest) (providerGameSyncPlan, error) { config, err := decodeZeeOneSyncConfig(platform.GetAdapterConfigJson()) if err != nil { @@ -257,6 +268,67 @@ func zeeoneGameNameFromURL(raw string) string { return titleGameName(candidate) } +func fetchVivaGamesGameSyncPlan(platform *gamev1.GamePlatform, req syncGamesRequest) (providerGameSyncPlan, error) { + config, err := decodeVivaGamesSyncConfig(platform.GetAdapterConfigJson()) + if err != nil { + return providerGameSyncPlan{}, err + } + games, gameURLs := vivaGamesCatalogItems(platform.GetPlatformCode(), config, req) + if len(games) == 0 { + return providerGameSyncPlan{}, fmt.Errorf("VIVAGAMES 游戏列表为空:请在 adapterConfigJson.game_urls 配置游戏 ID 到 H5 URL 的映射") + } + return providerGameSyncPlan{ + Games: games, + GameURLs: gameURLs, + }, nil +} + +func decodeVivaGamesSyncConfig(raw string) (vivaGamesSyncConfig, error) { + if strings.TrimSpace(raw) == "" { + return vivaGamesSyncConfig{}, nil + } + var config vivaGamesSyncConfig + if err := json.Unmarshal([]byte(raw), &config); err != nil { + return vivaGamesSyncConfig{}, fmt.Errorf("VIVAGAMES 适配器配置 JSON 不合法") + } + return config, nil +} + +func vivaGamesCatalogItems(platformCode string, config vivaGamesSyncConfig, req syncGamesRequest) ([]catalogRequest, map[string]string) { + keys := make([]string, 0, len(config.GameURLs)) + for providerGameID, launchURL := range config.GameURLs { + if strings.TrimSpace(providerGameID) != "" && strings.TrimSpace(launchURL) != "" { + keys = append(keys, strings.TrimSpace(providerGameID)) + } + } + sort.Strings(keys) + items := make([]catalogRequest, 0, len(keys)) + gameURLs := make(map[string]string, len(keys)) + for index, providerGameID := range keys { + launchURL := strings.TrimSpace(config.GameURLs[providerGameID]) + gameURLs[providerGameID] = launchURL + name := firstNonEmpty(config.GameNames[providerGameID], zeeoneGameNameFromURL(launchURL), providerGameID) + iconURL := strings.TrimSpace(config.GameIcons[providerGameID]) + coverURL := firstNonEmpty(config.GameCovers[providerGameID], iconURL) + items = append(items, catalogRequest{ + GameID: stableGameID(platformCode, providerGameID), + PlatformCode: strings.TrimSpace(platformCode), + ProviderGameID: providerGameID, + GameName: name, + Category: defaulted(req.Category, defaultGameCategory), + IconURL: iconURL, + CoverURL: coverURL, + LaunchMode: defaulted(req.LaunchMode, defaultLaunchMode), + Orientation: defaultOrientation, + MinCoin: req.MinCoin, + Status: defaulted(req.Status, defaultGameStatus), + SortOrder: int32((index + 1) * 10), + Tags: compactTags(append([]string{strings.TrimSpace(platformCode), adapterVivaGamesV1}, req.Tags...)), + }) + } + return items, gameURLs +} + func titleGameName(value string) string { tokens := splitGameNameTokens(value) if len(tokens) == 0 { diff --git a/server/admin/internal/modules/gamemanagement/sync_games_test.go b/server/admin/internal/modules/gamemanagement/sync_games_test.go index a3a2a1b3..acfd8be6 100644 --- a/server/admin/internal/modules/gamemanagement/sync_games_test.go +++ b/server/admin/internal/modules/gamemanagement/sync_games_test.go @@ -105,6 +105,37 @@ func TestFetchZeeOneGameSyncPlanReadsConfiguredGameURLs(t *testing.T) { } } +func TestFetchVivaGamesGameSyncPlanReadsConfiguredGameURLs(t *testing.T) { + plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{ + PlatformCode: "vivagames", + AdapterType: adapterVivaGamesV1, + AdapterConfigJson: `{ + "game_urls": { + "2": "https://dev-rich2.s3.ap-southeast-1.amazonaws.com/richForever.html?game_id=2&isShow=1" + }, + "game_names": { + "2": "Rich Forever" + } + }`, + }, syncGamesRequest{}) + if err != nil { + t.Fatalf("fetchProviderGameSyncPlan failed: %v", err) + } + if len(plan.Games) != 1 { + t.Fatalf("games len = %d, want 1", len(plan.Games)) + } + game := plan.Games[0] + if game.GameID != "vivagames_2" || game.ProviderGameID != "2" || game.GameName != "Rich Forever" { + t.Fatalf("vivagames game mismatch: %+v", game) + } + if plan.GameURLs["2"] != "https://dev-rich2.s3.ap-southeast-1.amazonaws.com/richForever.html?game_id=2&isShow=1" { + t.Fatalf("game url mismatch: %+v", plan.GameURLs) + } + if game.Status != "disabled" || game.LaunchMode != "full_screen" || game.Orientation != "portrait" { + t.Fatalf("default fields mismatch: %+v", game) + } +} + func TestSelectProviderGamesKeepsOnlySelectedIDs(t *testing.T) { games := []catalogRequest{ {ProviderGameID: "1001", GameName: "one"}, diff --git a/server/admin/internal/modules/hostorg/reader.go b/server/admin/internal/modules/hostorg/reader.go index b3362702..c3585640 100644 --- a/server/admin/internal/modules/hostorg/reader.go +++ b/server/admin/internal/modules/hostorg/reader.go @@ -27,15 +27,17 @@ type CoinSellerListItem struct { Status string `json:"status"` MerchantAssetType string `json:"merchantAssetType"` MerchantBalance int64 `json:"merchantBalance"` - Contact string `json:"contact"` - DisplayUserID string `json:"displayUserId"` - Username string `json:"username"` - Avatar string `json:"avatar"` - RegionID int64 `json:"regionId"` - RegionName string `json:"regionName"` - CreatedByUserID int64 `json:"createdByUserId"` - CreatedAtMs int64 `json:"createdAtMs"` - UpdatedAtMs int64 `json:"updatedAtMs"` + // TotalRechargeUSDTMicro 是币商 USDT 进货累计付款微单位,只统计 counts_as_seller_recharge 的库存记录。 + TotalRechargeUSDTMicro int64 `json:"totalRechargeUsdtMicro"` + Contact string `json:"contact"` + DisplayUserID string `json:"displayUserId"` + Username string `json:"username"` + Avatar string `json:"avatar"` + RegionID int64 `json:"regionId"` + RegionName string `json:"regionName"` + CreatedByUserID int64 `json:"createdByUserId"` + CreatedAtMs int64 `json:"createdAtMs"` + UpdatedAtMs int64 `json:"updatedAtMs"` } type CoinSellerSalaryRateTier struct { @@ -615,6 +617,13 @@ func (r *Reader) ListCoinSellers(ctx context.Context, query listQuery) ([]*CoinS if err != nil { return nil, 0, err } + limitSQL := "LIMIT ? OFFSET ?" + queryArgs := append(args, query.PageSize, offset(query.Page, query.PageSize)) + if isCoinSellerComputedSort(query.SortBy) { + // 币商余额和累充 USDT 都来自 wallet-service 聚合,必须先读取所有匹配币商再排序分页,避免只排序当前页。 + limitSQL = "" + queryArgs = args + } rows, err := r.db.QueryContext(ctx, fmt.Sprintf(` SELECT csp.user_id, csp.status, csp.merchant_asset_type, COALESCE(csp.contact_info, ''), @@ -624,8 +633,8 @@ func (r *Reader) ListCoinSellers(ctx context.Context, query listQuery) ([]*CoinS COALESCE(r.name, '') %s ORDER BY csp.created_at_ms DESC, csp.user_id DESC - LIMIT ? OFFSET ? - `, whereSQL), append(args, query.PageSize, offset(query.Page, query.PageSize))...) + %s + `, whereSQL, limitSQL), queryArgs...) if err != nil { return nil, 0, err } @@ -661,8 +670,17 @@ func (r *Reader) ListCoinSellers(ctx context.Context, query listQuery) ([]*CoinS if err != nil { return nil, 0, err } + rechargeTotals, err := r.coinSellerRechargeTotals(ctx, appCode, userIDs) + if err != nil { + return nil, 0, err + } for _, item := range items { item.MerchantBalance = balances[item.UserID] + item.TotalRechargeUSDTMicro = rechargeTotals[item.UserID] + } + if isCoinSellerComputedSort(query.SortBy) { + sortCoinSellerListItems(items, query.SortBy, query.SortDirection) + items = paginateCoinSellerListItems(items, query.Page, query.PageSize) } return items, total, nil } @@ -851,6 +869,41 @@ func (r *Reader) coinSellerBalances(ctx context.Context, appCode string, userIDs return result, rows.Err() } +// coinSellerRechargeTotals 从币商进货专表汇总 USDT 付款,金币补偿不会计入累充口径。 +func (r *Reader) coinSellerRechargeTotals(ctx context.Context, appCode string, userIDs []int64) (map[int64]int64, error) { + result := make(map[int64]int64, len(userIDs)) + if r == nil || r.walletDB == nil || len(userIDs) == 0 { + return result, nil + } + placeholders := sqlPlaceholders(len(userIDs)) + args := []any{appCode, paidCurrencyUSDT} + for _, userID := range userIDs { + args = append(args, userID) + } + rows, err := r.walletDB.QueryContext(ctx, fmt.Sprintf(` + SELECT seller_user_id, COALESCE(SUM(paid_amount_micro), 0) + FROM coin_seller_stock_records + WHERE app_code = ? + AND counts_as_seller_recharge = TRUE + AND paid_currency_code = ? + AND seller_user_id IN (%s) + GROUP BY seller_user_id + `, placeholders), args...) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + var userID int64 + var amount int64 + if err := rows.Scan(&userID, &amount); err != nil { + return nil, err + } + result[userID] = amount + } + return result, rows.Err() +} + func (r *Reader) hostPeriodDiamonds(ctx context.Context, appCode string, userIDs []int64) (map[int64]int64, error) { result := make(map[int64]int64, len(userIDs)) if r == nil || r.walletDB == nil || len(userIDs) == 0 { @@ -881,6 +934,39 @@ func (r *Reader) hostPeriodDiamonds(ctx context.Context, appCode string, userIDs return result, rows.Err() } +func isCoinSellerComputedSort(sortBy string) bool { + return sortBy == sortByMerchantBalance || sortBy == sortByTotalRechargeUSDT +} + +// sortCoinSellerListItems 只处理 wallet 聚合字段排序,基础身份字段仍使用 SQL 默认创建时间排序。 +func sortCoinSellerListItems(items []*CoinSellerListItem, sortBy string, direction string) { + sort.SliceStable(items, func(i, j int) bool { + left := coinSellerSortValue(items[i], sortBy) + right := coinSellerSortValue(items[j], sortBy) + if left == right { + return false + } + if direction == "asc" { + return left < right + } + return left > right + }) +} + +func coinSellerSortValue(item *CoinSellerListItem, sortBy string) int64 { + if item == nil { + return 0 + } + switch sortBy { + case sortByTotalRechargeUSDT: + return item.TotalRechargeUSDTMicro + case sortByMerchantBalance: + return item.MerchantBalance + default: + return 0 + } +} + func sortHostProfilesByDiamond(items []*userclient.HostProfile, direction string) { sort.SliceStable(items, func(i, j int) bool { if items[i].Diamond == items[j].Diamond { @@ -893,6 +979,24 @@ func sortHostProfilesByDiamond(items []*userclient.HostProfile, direction string }) } +func paginateCoinSellerListItems(items []*CoinSellerListItem, page int, pageSize int) []*CoinSellerListItem { + if page < 1 { + page = 1 + } + if pageSize < 1 { + pageSize = 20 + } + start := (page - 1) * pageSize + if start >= len(items) { + return []*CoinSellerListItem{} + } + end := start + pageSize + if end > len(items) { + end = len(items) + } + return items[start:end] +} + func paginateHostProfiles(items []*userclient.HostProfile, page int, pageSize int) []*userclient.HostProfile { if page < 1 { page = 1 diff --git a/server/admin/internal/modules/hostorg/service.go b/server/admin/internal/modules/hostorg/service.go index fed44301..da1af390 100644 --- a/server/admin/internal/modules/hostorg/service.go +++ b/server/admin/internal/modules/hostorg/service.go @@ -18,6 +18,9 @@ const ( coinSellerStockTypePurchase = "usdt_purchase" coinSellerStockTypeCompensate = "coin_compensation" paidCurrencyUSDT = "USDT" + sortByDiamond = "diamond" + sortByMerchantBalance = "merchant_balance" + sortByTotalRechargeUSDT = "total_recharge_usdt" bdLeaderPositionAliasMaxRunes = 64 ) @@ -524,7 +527,9 @@ func normalizeListQuery(query listQuery) listQuery { query.Keyword = strings.TrimSpace(query.Keyword) query.Status = strings.ToLower(strings.TrimSpace(query.Status)) query.SortBy = strings.ToLower(strings.TrimSpace(query.SortBy)) - if query.SortBy != "diamond" { + switch query.SortBy { + case sortByDiamond, sortByMerchantBalance, sortByTotalRechargeUSDT: + default: query.SortBy = "" query.SortDirection = "" return query diff --git a/server/admin/internal/modules/hostorg/service_test.go b/server/admin/internal/modules/hostorg/service_test.go new file mode 100644 index 00000000..7bd84b4a --- /dev/null +++ b/server/admin/internal/modules/hostorg/service_test.go @@ -0,0 +1,32 @@ +package hostorg + +import "testing" + +// TestNormalizeListQueryAllowsCoinSellerComputedSorts 锁定币商余额和累充 USDT 排序字段会被后台列表查询接受。 +func TestNormalizeListQueryAllowsCoinSellerComputedSorts(t *testing.T) { + for _, sortBy := range []string{sortByMerchantBalance, sortByTotalRechargeUSDT} { + query := normalizeListQuery(listQuery{Page: 1, PageSize: 50, SortBy: sortBy, SortDirection: "asc"}) + if query.SortBy != sortBy || query.SortDirection != "asc" { + t.Fatalf("expected sort %s asc to be kept, got sort_by=%q direction=%q", sortBy, query.SortBy, query.SortDirection) + } + } +} + +// TestSortCoinSellerListItemsComputedFields 验证 wallet 聚合字段排序只改变展示顺序,不改动币商身份数据。 +func TestSortCoinSellerListItemsComputedFields(t *testing.T) { + items := []*CoinSellerListItem{ + {UserID: 1, MerchantBalance: 100, TotalRechargeUSDTMicro: 30_000_000}, + {UserID: 2, MerchantBalance: 300, TotalRechargeUSDTMicro: 10_000_000}, + {UserID: 3, MerchantBalance: 200, TotalRechargeUSDTMicro: 20_000_000}, + } + + sortCoinSellerListItems(items, sortByMerchantBalance, "desc") + if got := []int64{items[0].UserID, items[1].UserID, items[2].UserID}; got[0] != 2 || got[1] != 3 || got[2] != 1 { + t.Fatalf("merchant balance desc order mismatch: %v", got) + } + + sortCoinSellerListItems(items, sortByTotalRechargeUSDT, "asc") + if got := []int64{items[0].UserID, items[1].UserID, items[2].UserID}; got[0] != 2 || got[1] != 3 || got[2] != 1 { + t.Fatalf("total recharge usdt asc order mismatch: %v", got) + } +} diff --git a/server/admin/internal/modules/registrationreward/handler.go b/server/admin/internal/modules/registrationreward/handler.go index 34e50931..474c992a 100644 --- a/server/admin/internal/modules/registrationreward/handler.go +++ b/server/admin/internal/modules/registrationreward/handler.go @@ -3,6 +3,7 @@ package registrationreward import ( "context" "database/sql" + "strconv" "strings" "time" @@ -72,6 +73,14 @@ type userDTO struct { Avatar string `json:"avatar"` } +type claimsPageDTO struct { + Items []claimDTO `json:"items"` + Page int `json:"page"` + PageSize int `json:"pageSize"` + Total int64 `json:"total"` + TodayClaimedCount int64 `json:"today_claimed_count"` +} + func (h *Handler) GetRegistrationRewardConfig(c *gin.Context) { resp, err := h.activity.GetRegistrationRewardConfig(c.Request.Context(), &activityv1.GetRegistrationRewardConfigRequest{Meta: h.meta(c)}) if err != nil { @@ -107,21 +116,29 @@ func (h *Handler) UpdateRegistrationRewardConfig(c *gin.Context) { func (h *Handler) ListRegistrationRewardClaims(c *gin.Context) { options := shared.ListOptions(c) + claimedStartMS := queryInt64(c, "claimed_start_ms", "claimedStartMs", "start_ms", "startMs") + claimedEndMS := queryInt64(c, "claimed_end_ms", "claimedEndMs", "end_ms", "endMs") userID, matched, ok := h.resolveClaimUserID(c.Request.Context(), strings.TrimSpace(options.Keyword)) if !ok { response.ServerError(c, "查询用户信息失败") return } if options.Keyword != "" && !matched { - response.OK(c, response.Page{Items: []claimDTO{}, Page: options.Page, PageSize: options.PageSize, Total: 0}) + todayClaimedCount, ok := h.loadTodayClaimedCount(c) + if !ok { + return + } + response.OK(c, claimsPageDTO{Items: []claimDTO{}, Page: options.Page, PageSize: options.PageSize, Total: 0, TodayClaimedCount: todayClaimedCount}) return } resp, err := h.activity.ListRegistrationRewardClaims(c.Request.Context(), &activityv1.ListRegistrationRewardClaimsRequest{ - Meta: h.meta(c), - Status: strings.TrimSpace(options.Status), - UserId: userID, - Page: int32(options.Page), - PageSize: int32(options.PageSize), + Meta: h.meta(c), + Status: strings.TrimSpace(options.Status), + UserId: userID, + ClaimedStartMs: claimedStartMS, + ClaimedEndMs: claimedEndMS, + Page: int32(options.Page), + PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取注册奖励领取记录失败") @@ -135,7 +152,26 @@ func (h *Handler) ListRegistrationRewardClaims(c *gin.Context) { response.ServerError(c, "补全用户信息失败") return } - response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) + response.OK(c, claimsPageDTO{ + Items: items, + Page: options.Page, + PageSize: options.PageSize, + Total: resp.GetTotal(), + TodayClaimedCount: resp.GetTodayClaimedCount(), + }) +} + +func (h *Handler) loadTodayClaimedCount(c *gin.Context) (int64, bool) { + resp, err := h.activity.ListRegistrationRewardClaims(c.Request.Context(), &activityv1.ListRegistrationRewardClaimsRequest{ + Meta: h.meta(c), + Page: 1, + PageSize: 1, + }) + if err != nil { + response.ServerError(c, "获取注册奖励今日领取数量失败") + return 0, false + } + return resp.GetTodayClaimedCount(), true } func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta { @@ -303,3 +339,17 @@ func int64Args(values []int64) []any { } return args } + +func queryInt64(c *gin.Context, names ...string) int64 { + for _, name := range names { + value := strings.TrimSpace(c.Query(name)) + if value == "" { + continue + } + parsed, err := strconv.ParseInt(value, 10, 64) + if err == nil && parsed > 0 { + return parsed + } + } + return 0 +} diff --git a/server/admin/internal/modules/roomturnoverreward/handler.go b/server/admin/internal/modules/roomturnoverreward/handler.go new file mode 100644 index 00000000..627dcff8 --- /dev/null +++ b/server/admin/internal/modules/roomturnoverreward/handler.go @@ -0,0 +1,227 @@ +package roomturnoverreward + +import ( + "strings" + "time" + + "hyapp-admin-server/internal/appctx" + "hyapp-admin-server/internal/integration/activityclient" + "hyapp-admin-server/internal/middleware" + "hyapp-admin-server/internal/modules/shared" + "hyapp-admin-server/internal/response" + activityv1 "hyapp.local/api/proto/activity/v1" + + "github.com/gin-gonic/gin" +) + +type Handler struct { + activity activityclient.Client + audit shared.OperationLogger +} + +func New(activity activityclient.Client, audit shared.OperationLogger) *Handler { + return &Handler{activity: activity, audit: audit} +} + +type configRequest struct { + Enabled bool `json:"enabled"` + Tiers []tierDTO `json:"tiers"` +} + +type configDTO struct { + AppCode string `json:"app_code"` + Enabled bool `json:"enabled"` + Tiers []tierDTO `json:"tiers"` + UpdatedByAdminID int64 `json:"updated_by_admin_id"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type tierDTO struct { + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + TierName string `json:"tier_name"` + ThresholdCoinSpent int64 `json:"threshold_coin_spent"` + RewardCoinAmount int64 `json:"reward_coin_amount"` + Status string `json:"status"` + SortOrder int32 `json:"sort_order"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type settlementDTO struct { + SettlementID string `json:"settlement_id"` + RoomID string `json:"room_id"` + OwnerUserID int64 `json:"owner_user_id"` + PeriodStartMS int64 `json:"period_start_ms"` + PeriodEndMS int64 `json:"period_end_ms"` + CoinSpent int64 `json:"coin_spent"` + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + ThresholdCoinSpent int64 `json:"threshold_coin_spent"` + RewardCoinAmount int64 `json:"reward_coin_amount"` + Status string `json:"status"` + WalletCommandID string `json:"wallet_command_id"` + WalletTransactionID string `json:"wallet_transaction_id"` + FailureReason string `json:"failure_reason"` + SettledAtMS int64 `json:"settled_at_ms"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +func (h *Handler) GetConfig(c *gin.Context) { + // 后台服务只透传到 activity-service 获取当前 App 配置;活动规则 owner 不在 admin-server 内部复制。 + resp, err := h.activity.GetRoomTurnoverRewardConfig(c.Request.Context(), &activityv1.GetRoomTurnoverRewardConfigRequest{Meta: h.meta(c)}) + if err != nil { + response.ServerError(c, "获取房间流水奖励配置失败") + return + } + response.OK(c, configFromProto(resp.GetConfig())) +} + +func (h *Handler) UpdateConfig(c *gin.Context) { + var req configRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, "房间流水奖励配置参数不正确") + return + } + tiers := make([]*activityv1.RoomTurnoverRewardTier, 0, len(req.Tiers)) + for _, tier := range req.Tiers { + // 后台只做输入清洗和操作者透传;阈值递增、奖励金额和启用状态等业务校验统一由 activity-service 执行。 + tiers = append(tiers, &activityv1.RoomTurnoverRewardTier{ + TierId: tier.TierID, + TierCode: strings.TrimSpace(tier.TierCode), + TierName: strings.TrimSpace(tier.TierName), + ThresholdCoinSpent: tier.ThresholdCoinSpent, + RewardCoinAmount: tier.RewardCoinAmount, + Status: strings.TrimSpace(tier.Status), + SortOrder: tier.SortOrder, + }) + } + resp, err := h.activity.UpdateRoomTurnoverRewardConfig(c.Request.Context(), &activityv1.UpdateRoomTurnoverRewardConfigRequest{ + Meta: h.meta(c), + Enabled: req.Enabled, + Tiers: tiers, + OperatorAdminId: int64(middleware.CurrentUserID(c)), + }) + if err != nil { + response.BadRequest(c, err.Error()) + return + } + item := configFromProto(resp.GetConfig()) + // 配置修改必须留下后台审计资源 ID,后续排查结算金额变化时可以回到具体 App 配置记录。 + shared.OperationLogWithResourceID(c, h.audit, "update-room-turnover-reward", "room_turnover_reward_configs", item.AppCode, "success", "") + response.OK(c, item) +} + +func (h *Handler) ListSettlements(c *gin.Context) { + // 列表筛选沿用通用 ListOptions:status 过滤结算状态,keyword 用作 room_id 精确查询,避免后台接口引入模糊扫表。 + options := shared.ListOptions(c) + resp, err := h.activity.ListRoomTurnoverRewardSettlements(c.Request.Context(), &activityv1.ListRoomTurnoverRewardSettlementsRequest{ + Meta: h.meta(c), + Status: strings.TrimSpace(options.Status), + RoomId: strings.TrimSpace(options.Keyword), + Page: int32(options.Page), + PageSize: int32(options.PageSize), + }) + if err != nil { + response.ServerError(c, "获取房间流水奖励结算记录失败") + return + } + items := make([]settlementDTO, 0, len(resp.GetSettlements())) + for _, settlement := range resp.GetSettlements() { + items = append(items, settlementFromProto(settlement)) + } + response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) +} + +func (h *Handler) RetrySettlement(c *gin.Context) { + settlementID := strings.TrimSpace(c.Param("settlement_id")) + if settlementID == "" { + response.BadRequest(c, "结算记录 ID 不正确") + return + } + // retry 只把指定 settlement 交还 activity-service;是否重新取房主、是否调用钱包和是否幂等都由活动服务闭环处理。 + resp, err := h.activity.RetryRoomTurnoverRewardSettlement(c.Request.Context(), &activityv1.RetryRoomTurnoverRewardSettlementRequest{ + Meta: h.meta(c), + SettlementId: settlementID, + OperatorAdminId: int64(middleware.CurrentUserID(c)), + }) + if err != nil { + response.BadRequest(c, err.Error()) + return + } + item := settlementFromProto(resp.GetSettlement()) + shared.OperationLogWithResourceID(c, h.audit, "retry-room-turnover-reward", "room_turnover_reward_settlements", settlementID, "success", "") + response.OK(c, item) +} + +func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta { + return &activityv1.RequestMeta{ + // 使用后台请求 ID 串起 admin-server、activity-service 和 wallet-service 日志,AppCode 来自当前后台上下文。 + RequestId: middleware.CurrentRequestID(c), + Caller: "admin-server", + AppCode: appctx.FromContext(c.Request.Context()), + SentAtMs: time.Now().UnixMilli(), + } +} + +func configFromProto(config *activityv1.RoomTurnoverRewardConfig) configDTO { + if config == nil { + return configDTO{Tiers: []tierDTO{}} + } + tiers := make([]tierDTO, 0, len(config.GetTiers())) + for _, tier := range config.GetTiers() { + tiers = append(tiers, tierFromProto(tier)) + } + return configDTO{ + AppCode: config.GetAppCode(), + Enabled: config.GetEnabled(), + Tiers: tiers, + UpdatedByAdminID: config.GetUpdatedByAdminId(), + CreatedAtMS: config.GetCreatedAtMs(), + UpdatedAtMS: config.GetUpdatedAtMs(), + } +} + +func tierFromProto(tier *activityv1.RoomTurnoverRewardTier) tierDTO { + if tier == nil { + return tierDTO{} + } + return tierDTO{ + TierID: tier.GetTierId(), + TierCode: tier.GetTierCode(), + TierName: tier.GetTierName(), + ThresholdCoinSpent: tier.GetThresholdCoinSpent(), + RewardCoinAmount: tier.GetRewardCoinAmount(), + Status: tier.GetStatus(), + SortOrder: tier.GetSortOrder(), + CreatedAtMS: tier.GetCreatedAtMs(), + UpdatedAtMS: tier.GetUpdatedAtMs(), + } +} + +func settlementFromProto(settlement *activityv1.RoomTurnoverRewardSettlement) settlementDTO { + if settlement == nil { + return settlementDTO{} + } + return settlementDTO{ + SettlementID: settlement.GetSettlementId(), + RoomID: settlement.GetRoomId(), + OwnerUserID: settlement.GetOwnerUserId(), + PeriodStartMS: settlement.GetPeriodStartMs(), + PeriodEndMS: settlement.GetPeriodEndMs(), + CoinSpent: settlement.GetCoinSpent(), + TierID: settlement.GetTierId(), + TierCode: settlement.GetTierCode(), + ThresholdCoinSpent: settlement.GetThresholdCoinSpent(), + RewardCoinAmount: settlement.GetRewardCoinAmount(), + Status: settlement.GetStatus(), + WalletCommandID: settlement.GetWalletCommandId(), + WalletTransactionID: settlement.GetWalletTransactionId(), + FailureReason: settlement.GetFailureReason(), + SettledAtMS: settlement.GetSettledAtMs(), + CreatedAtMS: settlement.GetCreatedAtMs(), + UpdatedAtMS: settlement.GetUpdatedAtMs(), + } +} diff --git a/server/admin/internal/modules/roomturnoverreward/routes.go b/server/admin/internal/modules/roomturnoverreward/routes.go new file mode 100644 index 00000000..14951334 --- /dev/null +++ b/server/admin/internal/modules/roomturnoverreward/routes.go @@ -0,0 +1,17 @@ +package roomturnoverreward + +import ( + "hyapp-admin-server/internal/middleware" + + "github.com/gin-gonic/gin" +) + +func RegisterRoutes(protected *gin.RouterGroup, h *Handler) { + if h == nil { + return + } + protected.GET("/admin/activity/room-turnover-reward/config", middleware.RequirePermission("room-turnover-reward:view"), h.GetConfig) + protected.PUT("/admin/activity/room-turnover-reward/config", middleware.RequirePermission("room-turnover-reward:update"), h.UpdateConfig) + protected.GET("/admin/activity/room-turnover-reward/settlements", middleware.RequirePermission("room-turnover-reward:view"), h.ListSettlements) + protected.POST("/admin/activity/room-turnover-reward/settlements/:settlement_id/retry", middleware.RequirePermission("room-turnover-reward:retry"), h.RetrySettlement) +} diff --git a/server/admin/internal/modules/weeklystar/handler.go b/server/admin/internal/modules/weeklystar/handler.go new file mode 100644 index 00000000..ae8ce5ae --- /dev/null +++ b/server/admin/internal/modules/weeklystar/handler.go @@ -0,0 +1,350 @@ +package weeklystar + +import ( + "strconv" + "strings" + "time" + + "hyapp-admin-server/internal/appctx" + "hyapp-admin-server/internal/integration/activityclient" + "hyapp-admin-server/internal/middleware" + "hyapp-admin-server/internal/modules/shared" + "hyapp-admin-server/internal/response" + activityv1 "hyapp.local/api/proto/activity/v1" + + "github.com/gin-gonic/gin" +) + +type Handler struct { + activity activityclient.Client + audit shared.OperationLogger +} + +func New(activity activityclient.Client, audit shared.OperationLogger) *Handler { + return &Handler{activity: activity, audit: audit} +} + +type cycleRequest struct { + CycleID string `json:"cycle_id"` + Title string `json:"title"` + Status string `json:"status"` + RegionID int64 `json:"region_id"` + StartMS int64 `json:"start_ms"` + EndMS int64 `json:"end_ms"` + GiftIDs []string `json:"gift_ids"` + Gifts []giftDTO `json:"gifts"` + Rewards []rewardDTO `json:"rewards"` + Top1ResourceGroupID int64 `json:"top1_resource_group_id"` + Top2ResourceGroupID int64 `json:"top2_resource_group_id"` + Top3ResourceGroupID int64 `json:"top3_resource_group_id"` +} + +type statusRequest struct { + Status string `json:"status"` +} + +type cycleDTO struct { + AppCode string `json:"app_code,omitempty"` + CycleID string `json:"cycle_id"` + ActivityCode string `json:"activity_code"` + RegionID int64 `json:"region_id"` + Title string `json:"title"` + Status string `json:"status"` + StartMS int64 `json:"start_ms"` + EndMS int64 `json:"end_ms"` + Gifts []giftDTO `json:"gifts"` + Rewards []rewardDTO `json:"rewards"` + CreatedByAdminID int64 `json:"created_by_admin_id"` + UpdatedByAdminID int64 `json:"updated_by_admin_id"` + SettledAtMS int64 `json:"settled_at_ms"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type giftDTO struct { + GiftID string `json:"gift_id"` + SortOrder int32 `json:"sort_order"` +} + +type rewardDTO struct { + RankNo int32 `json:"rank_no"` + ResourceGroupID int64 `json:"resource_group_id"` +} + +type leaderboardEntryDTO struct { + RankNo int32 `json:"rank_no"` + UserID int64 `json:"user_id"` + Score int64 `json:"score"` + FirstScoredAtMS int64 `json:"first_scored_at_ms"` + LastScoredAtMS int64 `json:"last_scored_at_ms"` + DisplayUserID string `json:"display_user_id,omitempty"` + Username string `json:"username,omitempty"` + Avatar string `json:"avatar,omitempty"` +} + +type settlementDTO struct { + SettlementID string `json:"settlement_id"` + CycleID string `json:"cycle_id"` + RankNo int32 `json:"rank_no"` + UserID int64 `json:"user_id"` + Score int64 `json:"score"` + ResourceGroupID int64 `json:"resource_group_id"` + WalletCommandID string `json:"wallet_command_id"` + WalletGrantID string `json:"wallet_grant_id"` + Status string `json:"status"` + FailureReason string `json:"failure_reason"` + AttemptCount int32 `json:"attempt_count"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +func (h *Handler) ListCycles(c *gin.Context) { + options := shared.ListOptions(c) + resp, err := h.activity.ListWeeklyStarCycles(c.Request.Context(), &activityv1.ListWeeklyStarCyclesRequest{ + Meta: h.meta(c), + Status: strings.TrimSpace(options.Status), + StartMs: parseInt64Query(c, "start_ms"), + EndMs: parseInt64Query(c, "end_ms"), + RegionId: parseInt64Query(c, "region_id"), + Page: int32(options.Page), + PageSize: int32(options.PageSize), + }) + if err != nil { + response.ServerError(c, "获取周星周期失败") + return + } + items := make([]cycleDTO, 0, len(resp.GetCycles())) + for _, cycle := range resp.GetCycles() { + items = append(items, cycleFromProto(cycle)) + } + response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) +} + +func (h *Handler) CreateCycle(c *gin.Context) { + var req cycleRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, "周星周期参数不正确") + return + } + resp, err := h.activity.CreateWeeklyStarCycle(c.Request.Context(), &activityv1.UpsertWeeklyStarCycleRequest{ + Meta: h.meta(c), + Cycle: req.toProto(), + OperatorAdminId: int64(middleware.CurrentUserID(c)), + }) + if err != nil { + response.BadRequest(c, err.Error()) + return + } + item := cycleFromProto(resp.GetCycle()) + shared.OperationLogWithResourceID(c, h.audit, "create-weekly-star-cycle", "weekly_star_cycles", item.CycleID, "success", "") + response.OK(c, item) +} + +func (h *Handler) GetCycle(c *gin.Context) { + cycleID := strings.TrimSpace(c.Param("cycle_id")) + resp, err := h.activity.GetWeeklyStarCycle(c.Request.Context(), &activityv1.GetWeeklyStarCycleRequest{Meta: h.meta(c), CycleId: cycleID}) + if err != nil { + response.ServerError(c, "获取周星周期详情失败") + return + } + response.OK(c, cycleFromProto(resp.GetCycle())) +} + +func (h *Handler) UpdateCycle(c *gin.Context) { + var req cycleRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, "周星周期参数不正确") + return + } + req.CycleID = strings.TrimSpace(c.Param("cycle_id")) + resp, err := h.activity.UpdateWeeklyStarCycle(c.Request.Context(), &activityv1.UpsertWeeklyStarCycleRequest{ + Meta: h.meta(c), + Cycle: req.toProto(), + OperatorAdminId: int64(middleware.CurrentUserID(c)), + }) + if err != nil { + response.BadRequest(c, err.Error()) + return + } + item := cycleFromProto(resp.GetCycle()) + shared.OperationLogWithResourceID(c, h.audit, "update-weekly-star-cycle", "weekly_star_cycles", item.CycleID, "success", "") + response.OK(c, item) +} + +func (h *Handler) SetCycleStatus(c *gin.Context) { + var req statusRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.BadRequest(c, "周星周期状态参数不正确") + return + } + cycleID := strings.TrimSpace(c.Param("cycle_id")) + resp, err := h.activity.SetWeeklyStarCycleStatus(c.Request.Context(), &activityv1.SetWeeklyStarCycleStatusRequest{ + Meta: h.meta(c), + CycleId: cycleID, + Status: strings.TrimSpace(req.Status), + OperatorAdminId: int64(middleware.CurrentUserID(c)), + }) + if err != nil { + response.BadRequest(c, err.Error()) + return + } + item := cycleFromProto(resp.GetCycle()) + shared.OperationLogWithResourceID(c, h.audit, "set-weekly-star-cycle-status", "weekly_star_cycles", item.CycleID, "success", "") + response.OK(c, item) +} + +func (h *Handler) ListLeaderboard(c *gin.Context) { + resp, err := h.activity.ListWeeklyStarLeaderboard(c.Request.Context(), &activityv1.ListWeeklyStarLeaderboardRequest{ + Meta: h.meta(c), + CycleId: strings.TrimSpace(c.Param("cycle_id")), + PageSize: int32(parseIntQuery(c, "page_size", 50)), + PageToken: strings.TrimSpace(c.Query("page_token")), + }) + if err != nil { + response.ServerError(c, "获取周星排行榜失败") + return + } + items := make([]leaderboardEntryDTO, 0, len(resp.GetEntries())) + for _, entry := range resp.GetEntries() { + items = append(items, leaderboardEntryFromProto(entry)) + } + response.OK(c, gin.H{"cycle": cycleFromProto(resp.GetCycle()), "items": items, "total": resp.GetTotal(), "next_page_token": resp.GetNextPageToken()}) +} + +func (h *Handler) ListSettlements(c *gin.Context) { + resp, err := h.activity.ListWeeklyStarSettlements(c.Request.Context(), &activityv1.ListWeeklyStarSettlementsRequest{ + Meta: h.meta(c), + CycleId: strings.TrimSpace(c.Param("cycle_id")), + }) + if err != nil { + response.ServerError(c, "获取周星结算记录失败") + return + } + items := make([]settlementDTO, 0, len(resp.GetSettlements())) + for _, settlement := range resp.GetSettlements() { + items = append(items, settlementFromProto(settlement)) + } + response.OK(c, items) +} + +func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta { + return &activityv1.RequestMeta{ + RequestId: middleware.CurrentRequestID(c), + Caller: "admin-server", + AppCode: appctx.FromContext(c.Request.Context()), + SentAtMs: time.Now().UnixMilli(), + } +} + +func (r cycleRequest) toProto() *activityv1.WeeklyStarCycle { + gifts := make([]*activityv1.WeeklyStarGift, 0, 3) + if len(r.Gifts) > 0 { + for _, gift := range r.Gifts { + gifts = append(gifts, &activityv1.WeeklyStarGift{GiftId: strings.TrimSpace(gift.GiftID), SortOrder: gift.SortOrder}) + } + } else { + for index, giftID := range r.GiftIDs { + gifts = append(gifts, &activityv1.WeeklyStarGift{GiftId: strings.TrimSpace(giftID), SortOrder: int32(index + 1)}) + } + } + rewards := make([]*activityv1.WeeklyStarReward, 0, 3) + if len(r.Rewards) > 0 { + for _, reward := range r.Rewards { + rewards = append(rewards, &activityv1.WeeklyStarReward{RankNo: reward.RankNo, ResourceGroupId: reward.ResourceGroupID}) + } + } else { + rewards = append(rewards, + &activityv1.WeeklyStarReward{RankNo: 1, ResourceGroupId: r.Top1ResourceGroupID}, + &activityv1.WeeklyStarReward{RankNo: 2, ResourceGroupId: r.Top2ResourceGroupID}, + &activityv1.WeeklyStarReward{RankNo: 3, ResourceGroupId: r.Top3ResourceGroupID}, + ) + } + return &activityv1.WeeklyStarCycle{ + CycleId: strings.TrimSpace(r.CycleID), + RegionId: r.RegionID, + Title: strings.TrimSpace(r.Title), + Status: strings.TrimSpace(r.Status), + StartMs: r.StartMS, + EndMs: r.EndMS, + Gifts: gifts, + Rewards: rewards, + } +} + +func cycleFromProto(cycle *activityv1.WeeklyStarCycle) cycleDTO { + if cycle == nil { + return cycleDTO{Gifts: []giftDTO{}, Rewards: []rewardDTO{}} + } + gifts := make([]giftDTO, 0, len(cycle.GetGifts())) + for _, gift := range cycle.GetGifts() { + gifts = append(gifts, giftDTO{GiftID: gift.GetGiftId(), SortOrder: gift.GetSortOrder()}) + } + rewards := make([]rewardDTO, 0, len(cycle.GetRewards())) + for _, reward := range cycle.GetRewards() { + rewards = append(rewards, rewardDTO{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()}) + } + return cycleDTO{ + AppCode: cycle.GetAppCode(), + CycleID: cycle.GetCycleId(), + ActivityCode: cycle.GetActivityCode(), + RegionID: cycle.GetRegionId(), + Title: cycle.GetTitle(), + Status: cycle.GetStatus(), + StartMS: cycle.GetStartMs(), + EndMS: cycle.GetEndMs(), + Gifts: gifts, + Rewards: rewards, + CreatedByAdminID: cycle.GetCreatedByAdminId(), + UpdatedByAdminID: cycle.GetUpdatedByAdminId(), + SettledAtMS: cycle.GetSettledAtMs(), + CreatedAtMS: cycle.GetCreatedAtMs(), + UpdatedAtMS: cycle.GetUpdatedAtMs(), + } +} + +func leaderboardEntryFromProto(entry *activityv1.WeeklyStarLeaderboardEntry) leaderboardEntryDTO { + if entry == nil { + return leaderboardEntryDTO{} + } + return leaderboardEntryDTO{ + RankNo: entry.GetRankNo(), + UserID: entry.GetUserId(), + Score: entry.GetScore(), + FirstScoredAtMS: entry.GetFirstScoredAtMs(), + LastScoredAtMS: entry.GetLastScoredAtMs(), + } +} + +func settlementFromProto(settlement *activityv1.WeeklyStarSettlement) settlementDTO { + if settlement == nil { + return settlementDTO{} + } + return settlementDTO{ + SettlementID: settlement.GetSettlementId(), + CycleID: settlement.GetCycleId(), + RankNo: settlement.GetRankNo(), + UserID: settlement.GetUserId(), + Score: settlement.GetScore(), + ResourceGroupID: settlement.GetResourceGroupId(), + WalletCommandID: settlement.GetWalletCommandId(), + WalletGrantID: settlement.GetWalletGrantId(), + Status: settlement.GetStatus(), + FailureReason: settlement.GetFailureReason(), + AttemptCount: settlement.GetAttemptCount(), + CreatedAtMS: settlement.GetCreatedAtMs(), + UpdatedAtMS: settlement.GetUpdatedAtMs(), + } +} + +func parseInt64Query(c *gin.Context, key string) int64 { + value, _ := strconv.ParseInt(strings.TrimSpace(c.Query(key)), 10, 64) + return value +} + +func parseIntQuery(c *gin.Context, key string, fallback int) int { + value, err := strconv.Atoi(strings.TrimSpace(c.Query(key))) + if err != nil || value <= 0 { + return fallback + } + return value +} diff --git a/server/admin/internal/modules/weeklystar/routes.go b/server/admin/internal/modules/weeklystar/routes.go new file mode 100644 index 00000000..9835d517 --- /dev/null +++ b/server/admin/internal/modules/weeklystar/routes.go @@ -0,0 +1,20 @@ +package weeklystar + +import ( + "hyapp-admin-server/internal/middleware" + + "github.com/gin-gonic/gin" +) + +func RegisterRoutes(protected *gin.RouterGroup, h *Handler) { + if h == nil { + return + } + protected.GET("/admin/activity/weekly-star/cycles", middleware.RequirePermission("weekly-star:view"), h.ListCycles) + protected.POST("/admin/activity/weekly-star/cycles", middleware.RequirePermission("weekly-star:create"), h.CreateCycle) + protected.GET("/admin/activity/weekly-star/cycles/:cycle_id", middleware.RequirePermission("weekly-star:view"), h.GetCycle) + protected.PUT("/admin/activity/weekly-star/cycles/:cycle_id", middleware.RequirePermission("weekly-star:update"), h.UpdateCycle) + protected.PATCH("/admin/activity/weekly-star/cycles/:cycle_id/status", middleware.RequirePermission("weekly-star:update"), h.SetCycleStatus) + protected.GET("/admin/activity/weekly-star/cycles/:cycle_id/leaderboard", middleware.RequirePermission("weekly-star:view"), h.ListLeaderboard) + protected.GET("/admin/activity/weekly-star/cycles/:cycle_id/settlements", middleware.RequirePermission("weekly-star:view"), h.ListSettlements) +} diff --git a/server/admin/internal/repository/seed.go b/server/admin/internal/repository/seed.go index 68fb881e..4e280a4c 100644 --- a/server/admin/internal/repository/seed.go +++ b/server/admin/internal/repository/seed.go @@ -124,6 +124,10 @@ var defaultPermissions = []model.Permission{ {Name: "VIP 配置查看", Code: "vip-config:view", Kind: "menu"}, {Name: "VIP 配置更新", Code: "vip-config:update", Kind: "button"}, {Name: "VIP 赠送", Code: "vip-config:grant", Kind: "button"}, + {Name: "周星配置查看", Code: "weekly-star:view", Kind: "menu"}, + {Name: "周星配置创建", Code: "weekly-star:create", Kind: "button"}, + {Name: "周星配置更新", Code: "weekly-star:update", Kind: "button"}, + {Name: "周星结算查看", Code: "weekly-star:settle", Kind: "button"}, {Name: "角色查看", Code: "role:view", Kind: "menu"}, {Name: "角色创建", Code: "role:create", Kind: "button"}, {Name: "角色更新", Code: "role:update", Kind: "button"}, @@ -174,18 +178,18 @@ func (s *Store) seedMenus() error { gameID := uint(0) menus := []model.Menu{ {Title: "总览", Code: "overview", Path: "/overview", Icon: "dashboard", PermissionCode: "overview:view", Sort: 10, Visible: true}, - {Title: "后台设置", Code: "system", Path: "", Icon: "settings", PermissionCode: "", Sort: 30, Visible: true}, - {Title: "日志审计", Code: "logs", Path: "", Icon: "history", PermissionCode: "", Sort: 40, Visible: false}, - {Title: "用户管理", Code: "app-users", Path: "", Icon: "users", PermissionCode: "", Sort: 60, Visible: true}, - {Title: "房间管理", Code: "rooms", Path: "", Icon: "room", PermissionCode: "", Sort: 65, Visible: true}, - {Title: "APP配置", Code: "app-config", Path: "", Icon: "settings", PermissionCode: "", Sort: 66, Visible: true}, - {Title: "资源管理", Code: "resources", Path: "", Icon: "inventory", PermissionCode: "", Sort: 67, Visible: true}, - {Title: "运营管理", Code: "operations", Path: "", Icon: "operations", PermissionCode: "", Sort: 68, Visible: true}, - {Title: "支付管理", Code: "payment", Path: "", Icon: "wallet", PermissionCode: "", Sort: 69, Visible: true}, - {Title: "活动管理", Code: "activities", Path: "", Icon: "campaign", PermissionCode: "", Sort: 70, Visible: true}, - {Title: "游戏管理", Code: "games", Path: "", Icon: "sports_esports", PermissionCode: "", Sort: 71, Visible: true}, - {Title: "地区管理", Code: "geo", Path: "", Icon: "map", PermissionCode: "", Sort: 72, Visible: true}, - {Title: "团队管理", Code: "host-org", Path: "", Icon: "network", PermissionCode: "", Sort: 80, Visible: true}, + {Title: "用户管理", Code: "app-users", Path: "", Icon: "users", PermissionCode: "", Sort: 20, Visible: true}, + {Title: "团队管理", Code: "host-org", Path: "", Icon: "network", PermissionCode: "", Sort: 30, Visible: true}, + {Title: "房间管理", Code: "rooms", Path: "", Icon: "room", PermissionCode: "", Sort: 40, Visible: true}, + {Title: "APP配置", Code: "app-config", Path: "", Icon: "settings", PermissionCode: "", Sort: 50, Visible: true}, + {Title: "资源管理", Code: "resources", Path: "", Icon: "inventory", PermissionCode: "", Sort: 60, Visible: true}, + {Title: "运营管理", Code: "operations", Path: "", Icon: "operations", PermissionCode: "", Sort: 70, Visible: true}, + {Title: "支付管理", Code: "payment", Path: "", Icon: "wallet", PermissionCode: "", Sort: 80, Visible: true}, + {Title: "活动管理", Code: "activities", Path: "", Icon: "campaign", PermissionCode: "", Sort: 90, Visible: true}, + {Title: "游戏管理", Code: "games", Path: "", Icon: "sports_esports", PermissionCode: "", Sort: 100, Visible: true}, + {Title: "地区管理", Code: "geo", Path: "", Icon: "map", PermissionCode: "", Sort: 110, Visible: true}, + {Title: "后台设置", Code: "system", Path: "", Icon: "settings", PermissionCode: "", Sort: 120, Visible: true}, + {Title: "日志审计", Code: "logs", Path: "", Icon: "history", PermissionCode: "", Sort: 130, Visible: false}, } for _, menu := range menus { syncedMenu, err := s.syncDefaultMenu(s.db, menu) @@ -267,6 +271,7 @@ func (s *Store) seedMenus() error { {ParentID: &activityID, Title: "用户榜单", Code: "user-leaderboard", Path: "/activities/user-leaderboards", Icon: "leaderboard", PermissionCode: "user-leaderboard:view", Sort: 74, Visible: true}, {ParentID: &activityID, Title: "红包配置", Code: "red-packet", Path: "/activities/red-packets", Icon: "redeem", PermissionCode: "red-packet:view", Sort: 75, Visible: true}, {ParentID: &activityID, Title: "VIP配置", Code: "vip-config", Path: "/activities/vip-config", Icon: "workspace_premium", PermissionCode: "vip-config:view", Sort: 76, Visible: true}, + {ParentID: &activityID, Title: "周星配置", Code: "weekly-star", Path: "/activities/weekly-star", Icon: "star", PermissionCode: "weekly-star:view", Sort: 78, Visible: true}, {ParentID: &gameID, Title: "游戏列表", Code: "game-list", Path: "/games", Icon: "sports_esports", PermissionCode: "game:view", Sort: 70, Visible: true}, {ParentID: &geoID, Title: "国家管理", Code: "host-org-countries", Path: "/host/countries", Icon: "public", PermissionCode: "country:view", Sort: 70, Visible: true}, {ParentID: &geoID, Title: "区域管理", Code: "host-org-regions", Path: "/host/regions", Icon: "map", PermissionCode: "region:view", Sort: 71, Visible: true}, @@ -513,12 +518,13 @@ func defaultRolePermissionCodes(code string) []string { "room-rocket:view", "room-rocket:update", "red-packet:view", "red-packet:update", "vip-config:view", "vip-config:update", "vip-config:grant", + "weekly-star:view", "weekly-star:create", "weekly-star:update", "weekly-star:settle", "log:view", "job:view", "job:cancel", "export:create", "upload:create", } case "auditor": - return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "vip-config:view", "role:view", "permission:view", "job:view"} + return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "vip-config:view", "weekly-star:view", "role:view", "permission:view", "job:view"} case "readonly": return []string{ "overview:view", @@ -561,6 +567,7 @@ func defaultRolePermissionCodes(code string) []string { "room-rocket:view", "red-packet:view", "vip-config:view", + "weekly-star:view", "role:view", "permission:view", "menu:view", @@ -602,9 +609,10 @@ func defaultRolePermissionMigrationCodes(code string) []string { "room-rocket:view", "room-rocket:update", "red-packet:view", "red-packet:update", "vip-config:view", "vip-config:update", "vip-config:grant", + "weekly-star:view", "weekly-star:create", "weekly-star:update", "weekly-star:settle", } case "auditor", "readonly": - return []string{"level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-seller:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "vip-config:view"} + return []string{"level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-seller:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "vip-config:view", "weekly-star:view"} default: return nil } diff --git a/server/admin/internal/router/router.go b/server/admin/internal/router/router.go index 40a4b2bc..2565af57 100644 --- a/server/admin/internal/router/router.go +++ b/server/admin/internal/router/router.go @@ -12,6 +12,7 @@ import ( authroutes "hyapp-admin-server/internal/modules/auth" "hyapp-admin-server/internal/modules/coinledger" "hyapp-admin-server/internal/modules/countryregion" + "hyapp-admin-server/internal/modules/cumulativerechargereward" "hyapp-admin-server/internal/modules/dailytask" "hyapp-admin-server/internal/modules/dashboard" "hyapp-admin-server/internal/modules/firstrechargereward" @@ -34,6 +35,7 @@ import ( resourcemodule "hyapp-admin-server/internal/modules/resource" "hyapp-admin-server/internal/modules/roomadmin" "hyapp-admin-server/internal/modules/roomrocket" + "hyapp-admin-server/internal/modules/roomturnoverreward" "hyapp-admin-server/internal/modules/search" "hyapp-admin-server/internal/modules/sevendaycheckin" "hyapp-admin-server/internal/modules/teamsalarypolicy" @@ -41,6 +43,7 @@ import ( "hyapp-admin-server/internal/modules/upload" "hyapp-admin-server/internal/modules/userleaderboard" "hyapp-admin-server/internal/modules/vipconfig" + "hyapp-admin-server/internal/modules/weeklystar" "hyapp-admin-server/internal/platform/logging" "hyapp-admin-server/internal/service" @@ -57,6 +60,7 @@ type Handlers struct { Auth *authroutes.Handler CoinLedger *coinledger.Handler CountryRegion *countryregion.Handler + CumulativeRecharge *cumulativerechargereward.Handler DailyTask *dailytask.Handler Dashboard *dashboard.Handler FirstRechargeReward *firstrechargereward.Handler @@ -79,6 +83,7 @@ type Handlers struct { Resource *resourcemodule.Handler RoomAdmin *roomadmin.Handler RoomRocket *roomrocket.Handler + RoomTurnoverReward *roomturnoverreward.Handler Search *search.Handler SevenDayCheckIn *sevendaycheckin.Handler TeamSalaryPolicy *teamsalarypolicy.Handler @@ -86,6 +91,7 @@ type Handlers struct { Upload *upload.Handler UserLeaderboard *userleaderboard.Handler VIPConfig *vipconfig.Handler + WeeklyStar *weeklystar.Handler } func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine { @@ -112,6 +118,7 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine { appregistry.RegisterRoutes(protected, h.AppRegistry) appconfig.RegisterRoutes(protected, h.AppConfig) countryregion.RegisterRoutes(protected, h.CountryRegion) + cumulativerechargereward.RegisterRoutes(protected, h.CumulativeRecharge) dailytask.RegisterRoutes(protected, h.DailyTask) firstrechargereward.RegisterRoutes(protected, h.FirstRechargeReward) registrationreward.RegisterRoutes(protected, h.RegistrationReward) @@ -120,6 +127,7 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine { giftdiamond.RegisterRoutes(protected, h.GiftDiamond) roomadmin.RegisterRoutes(protected, h.RoomAdmin) roomrocket.RegisterRoutes(protected, h.RoomRocket) + roomturnoverreward.RegisterRoutes(protected, h.RoomTurnoverReward) dashboard.RegisterRoutes(protected, h.Dashboard) hostagencypolicy.RegisterRoutes(protected, h.HostAgencyPolicy) hostsalarysettlement.RegisterRoutes(protected, h.HostSalarySettlement) @@ -136,6 +144,7 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine { teamsalarysettlement.RegisterRoutes(protected, h.TeamSalarySettlement) userleaderboard.RegisterRoutes(protected, h.UserLeaderboard) vipconfig.RegisterRoutes(protected, h.VIPConfig) + weeklystar.RegisterRoutes(protected, h.WeeklyStar) return engine } diff --git a/server/admin/migrations/036_room_turnover_reward_navigation.sql b/server/admin/migrations/036_room_turnover_reward_navigation.sql new file mode 100644 index 00000000..144112ea --- /dev/null +++ b/server/admin/migrations/036_room_turnover_reward_navigation.sql @@ -0,0 +1,60 @@ +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- 房间流水奖励由 activity-service 统计和结算;后台只维护配置、查看记录和失败重试入口。 +SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES + ('房间流水奖励查看', 'room-turnover-reward:view', 'menu', '', @now_ms, @now_ms), + ('房间流水奖励更新', 'room-turnover-reward:update', 'button', '', @now_ms, @now_ms), + ('房间流水奖励重试', 'room-turnover-reward:retry', 'button', '', @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + name = VALUES(name), + kind = VALUES(kind), + description = VALUES(description), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) VALUES + (NULL, '活动管理', 'activities', '', 'campaign', '', 70, TRUE, @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) +SELECT parent.id, '房间流水奖励', 'room-turnover-reward', '/activities/room-turnover-reward', 'paid', 'room-turnover-reward:view', 77, TRUE, @now_ms, @now_ms +FROM admin_menus parent +WHERE parent.code = 'activities' +ON DUPLICATE KEY UPDATE + parent_id = VALUES(parent_id), + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code = 'platform-admin' + AND admin_permission.code IN ('room-turnover-reward:view', 'room-turnover-reward:update', 'room-turnover-reward:retry'); + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code = 'ops-admin' + AND admin_permission.code IN ('room-turnover-reward:view', 'room-turnover-reward:update', 'room-turnover-reward:retry'); + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code IN ('auditor', 'readonly') + AND admin_permission.code = 'room-turnover-reward:view'; diff --git a/server/admin/migrations/037_weekly_star_navigation.sql b/server/admin/migrations/037_weekly_star_navigation.sql new file mode 100644 index 00000000..a520efd7 --- /dev/null +++ b/server/admin/migrations/037_weekly_star_navigation.sql @@ -0,0 +1,61 @@ +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- 周星配置由 activity-service 拥有周期、榜单和结算事实;后台只提供配置和查看入口。 +SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES + ('周星配置查看', 'weekly-star:view', 'menu', '', @now_ms, @now_ms), + ('周星配置创建', 'weekly-star:create', 'button', '', @now_ms, @now_ms), + ('周星配置更新', 'weekly-star:update', 'button', '', @now_ms, @now_ms), + ('周星结算查看', 'weekly-star:settle', 'button', '', @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + name = VALUES(name), + kind = VALUES(kind), + description = VALUES(description), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) VALUES + (NULL, '活动管理', 'activities', '', 'campaign', '', 70, TRUE, @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) +SELECT parent.id, '周星配置', 'weekly-star', '/activities/weekly-star', 'star', 'weekly-star:view', 78, TRUE, @now_ms, @now_ms +FROM admin_menus parent +WHERE parent.code = 'activities' +ON DUPLICATE KEY UPDATE + parent_id = VALUES(parent_id), + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code = 'platform-admin' + AND admin_permission.code IN ('weekly-star:view', 'weekly-star:create', 'weekly-star:update', 'weekly-star:settle'); + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code = 'ops-admin' + AND admin_permission.code IN ('weekly-star:view', 'weekly-star:create', 'weekly-star:update', 'weekly-star:settle'); + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code IN ('auditor', 'readonly') + AND admin_permission.code = 'weekly-star:view'; diff --git a/server/admin/migrations/038_cumulative_recharge_reward_navigation.sql b/server/admin/migrations/038_cumulative_recharge_reward_navigation.sql new file mode 100644 index 00000000..a3e9ea40 --- /dev/null +++ b/server/admin/migrations/038_cumulative_recharge_reward_navigation.sql @@ -0,0 +1,59 @@ +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- 累充奖励事实由 activity-service 按 UTC 周期累计和发奖;后台只维护后续事件使用的档位配置并查看发放记录。 +SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES + ('累充奖励查看', 'cumulative-recharge-reward:view', 'menu', '', @now_ms, @now_ms), + ('累充奖励更新', 'cumulative-recharge-reward:update', 'button', '', @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + name = VALUES(name), + kind = VALUES(kind), + description = VALUES(description), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) VALUES + (NULL, '活动管理', 'activities', '', 'campaign', '', 70, TRUE, @now_ms, @now_ms) +ON DUPLICATE KEY UPDATE + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) +SELECT parent.id, '累充奖励配置', 'cumulative-recharge-reward', '/activities/cumulative-recharge-reward', 'workspace_premium', 'cumulative-recharge-reward:view', 79, TRUE, @now_ms, @now_ms +FROM admin_menus parent +WHERE parent.code = 'activities' +ON DUPLICATE KEY UPDATE + parent_id = VALUES(parent_id), + title = VALUES(title), + path = VALUES(path), + icon = VALUES(icon), + permission_code = VALUES(permission_code), + sort = VALUES(sort), + visible = VALUES(visible), + updated_at_ms = @now_ms; + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code = 'platform-admin' + AND admin_permission.code IN ('cumulative-recharge-reward:view', 'cumulative-recharge-reward:update'); + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code = 'ops-admin' + AND admin_permission.code IN ('cumulative-recharge-reward:view', 'cumulative-recharge-reward:update'); + +INSERT IGNORE INTO admin_role_permissions (role_id, permission_id) +SELECT admin_role.id, admin_permission.id +FROM admin_roles admin_role +JOIN admin_permissions admin_permission +WHERE admin_role.code IN ('auditor', 'readonly') + AND admin_permission.code = 'cumulative-recharge-reward:view'; diff --git a/server/admin/migrations/039_admin_parent_menu_order.sql b/server/admin/migrations/039_admin_parent_menu_order.sql new file mode 100644 index 00000000..946f2cc0 --- /dev/null +++ b/server/admin/migrations/039_admin_parent_menu_order.sql @@ -0,0 +1,39 @@ +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- 左侧父级菜单顺序直接影响后台入口效率:团队管理靠近用户管理,后台设置固定沉到底部。 +SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +UPDATE admin_menus +SET sort = CASE code + WHEN 'overview' THEN 10 + WHEN 'app-users' THEN 20 + WHEN 'host-org' THEN 30 + WHEN 'rooms' THEN 40 + WHEN 'app-config' THEN 50 + WHEN 'resources' THEN 60 + WHEN 'operations' THEN 70 + WHEN 'payment' THEN 80 + WHEN 'activities' THEN 90 + WHEN 'games' THEN 100 + WHEN 'geo' THEN 110 + WHEN 'system' THEN 120 + WHEN 'logs' THEN 130 + ELSE sort + END, + updated_at_ms = @now_ms +WHERE parent_id IS NULL + AND code IN ( + 'overview', + 'app-users', + 'host-org', + 'rooms', + 'app-config', + 'resources', + 'operations', + 'payment', + 'activities', + 'games', + 'geo', + 'system', + 'logs' + ); diff --git a/services/activity-service/configs/config.docker.yaml b/services/activity-service/configs/config.docker.yaml index a3a14c1a..032186fe 100644 --- a/services/activity-service/configs/config.docker.yaml +++ b/services/activity-service/configs/config.docker.yaml @@ -12,9 +12,12 @@ health_http_addr: ":13106" mysql_dsn: "hyapp:hyapp@tcp(mysql:3306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC" user_service_addr: "user-service:13005" wallet_service_addr: "wallet-service:13004" +room_service_addr: "room-service:13001" mysql_auto_migrate: true first_recharge_reward_worker: enabled: false +cumulative_recharge_reward_worker: + enabled: true red_packet_broadcast_worker: enabled: true lucky_gift_worker: @@ -64,6 +67,7 @@ rocketmq: topic: "hyapp_wallet_outbox" realtime_topic: "hyapp_wallet_realtime_outbox" first_recharge_consumer_group: "hyapp-activity-first-recharge-wallet-outbox" + cumulative_recharge_consumer_group: "hyapp-activity-cumulative-recharge-wallet-outbox" red_packet_broadcast_consumer_group: "hyapp-activity-red-packet-wallet-outbox" consumer_max_reconsume_times: 16 user_outbox: diff --git a/services/activity-service/configs/config.tencent.example.yaml b/services/activity-service/configs/config.tencent.example.yaml index 1bedba25..c39902e7 100644 --- a/services/activity-service/configs/config.tencent.example.yaml +++ b/services/activity-service/configs/config.tencent.example.yaml @@ -12,9 +12,12 @@ health_http_addr: ":13106" mysql_dsn: "USER:PASSWORD@tcp(TENCENT_CDB_HOST:3306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC&tls=false" user_service_addr: "user-service.internal:13005" wallet_service_addr: "wallet-service.internal:13004" +room_service_addr: "room-service.internal:13001" mysql_auto_migrate: false first_recharge_reward_worker: enabled: true +cumulative_recharge_reward_worker: + enabled: true red_packet_broadcast_worker: enabled: true lucky_gift_worker: @@ -63,6 +66,7 @@ rocketmq: topic: "hyapp_wallet_outbox" realtime_topic: "hyapp_wallet_realtime_outbox" first_recharge_consumer_group: "hyapp-activity-first-recharge-wallet-outbox" + cumulative_recharge_consumer_group: "hyapp-activity-cumulative-recharge-wallet-outbox" red_packet_broadcast_consumer_group: "hyapp-activity-red-packet-wallet-outbox" consumer_max_reconsume_times: 16 user_outbox: diff --git a/services/activity-service/configs/config.yaml b/services/activity-service/configs/config.yaml index fa217608..29049def 100644 --- a/services/activity-service/configs/config.yaml +++ b/services/activity-service/configs/config.yaml @@ -12,9 +12,12 @@ health_http_addr: ":13106" mysql_dsn: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC" user_service_addr: "127.0.0.1:13005" wallet_service_addr: "127.0.0.1:13004" +room_service_addr: "127.0.0.1:13001" mysql_auto_migrate: true first_recharge_reward_worker: enabled: false +cumulative_recharge_reward_worker: + enabled: false red_packet_broadcast_worker: enabled: false lucky_gift_worker: @@ -63,6 +66,7 @@ rocketmq: topic: "hyapp_wallet_outbox" realtime_topic: "" first_recharge_consumer_group: "hyapp-activity-first-recharge-wallet-outbox" + cumulative_recharge_consumer_group: "hyapp-activity-cumulative-recharge-wallet-outbox" red_packet_broadcast_consumer_group: "hyapp-activity-red-packet-wallet-outbox" consumer_max_reconsume_times: 16 user_outbox: diff --git a/services/activity-service/deploy/mysql/initdb/001_activity_service.sql b/services/activity-service/deploy/mysql/initdb/001_activity_service.sql index 8192c27f..7cc95271 100644 --- a/services/activity-service/deploy/mysql/initdb/001_activity_service.sql +++ b/services/activity-service/deploy/mysql/initdb/001_activity_service.sql @@ -183,6 +183,7 @@ CREATE TABLE IF NOT EXISTS lucky_draw_records ( device_id VARCHAR(128) NOT NULL COMMENT '设备 ID', room_id VARCHAR(96) NOT NULL COMMENT '房间 ID', anchor_id VARCHAR(96) NOT NULL COMMENT '主播关联 ID', + visible_region_id BIGINT NOT NULL DEFAULT 0 COMMENT '房间可见区域 ID', pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID', gift_id VARCHAR(96) NOT NULL COMMENT '礼物 ID', coin_spent BIGINT NOT NULL COMMENT '消耗金币', @@ -213,6 +214,7 @@ CREATE TABLE IF NOT EXISTS lucky_draw_records ( KEY idx_lucky_draw_user (app_code, user_id, created_at_ms), KEY idx_lucky_draw_gift (app_code, gift_id, created_at_ms), KEY idx_lucky_draw_room (app_code, room_id, created_at_ms), + KEY idx_lucky_draw_region_created (app_code, visible_region_id, created_at_ms, draw_id), KEY idx_lucky_draw_status (app_code, reward_status, updated_at_ms), KEY idx_lucky_draw_created (app_code, created_at_ms, draw_id), KEY idx_lucky_draw_pool_status_summary (app_code, pool_id, reward_status, created_at_ms, draw_id), @@ -239,6 +241,24 @@ CREATE TABLE IF NOT EXISTS lucky_draw_pool_stats ( PRIMARY KEY (app_code, pool_id, gift_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物池级汇总统计'; +CREATE TABLE IF NOT EXISTS lucky_draw_pool_day_stats ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + stat_day DATE NOT NULL COMMENT 'UTC 统计日', + visible_region_id BIGINT NOT NULL DEFAULT 0 COMMENT '房间可见区域 ID', + pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID', + gift_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '礼物 ID,空值表示奖池汇总', + draw_count BIGINT NOT NULL DEFAULT 0 COMMENT '抽奖次数', + turnover_coin BIGINT NOT NULL DEFAULT 0 COMMENT '消耗金币', + payout_coin BIGINT NOT NULL DEFAULT 0 COMMENT '用户可见返奖金币', + base_reward_coin BIGINT NOT NULL DEFAULT 0 COMMENT '基础 RTP 返奖', + room_atmosphere_reward_coin BIGINT NOT NULL DEFAULT 0 COMMENT '房间气氛支出', + activity_subsidy_coin BIGINT NOT NULL DEFAULT 0 COMMENT '活动补贴支出', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, stat_day, visible_region_id, pool_id, gift_id), + KEY idx_lucky_draw_pool_day_overview (app_code, stat_day, pool_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物 Databi 日维度汇总'; + CREATE TABLE IF NOT EXISTS lucky_draw_pool_stat_users ( app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID', @@ -483,6 +503,92 @@ CREATE TABLE IF NOT EXISTS first_recharge_reward_claims ( KEY idx_first_recharge_reward_status (app_code, status, updated_at_ms) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='首冲奖励领取表'; +CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_configs ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', + enabled TINYINT(1) NOT NULL DEFAULT 0 COMMENT '启用', + updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新管理员 ID', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励配置表'; + +CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_tiers ( + tier_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '累充奖励档位 ID', + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', + tier_code VARCHAR(64) NOT NULL COMMENT '档位编码', + tier_name VARCHAR(96) NOT NULL COMMENT '档位名称', + threshold_usd_minor BIGINT NOT NULL COMMENT '达标美元金额,美分', + resource_group_id BIGINT NOT NULL COMMENT '奖励资源分组 ID', + status VARCHAR(32) NOT NULL COMMENT '业务状态', + sort_order INT NOT NULL DEFAULT 0 COMMENT '排序权重,数值越小越靠前', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + UNIQUE KEY uk_cumulative_recharge_reward_tier_code (app_code, tier_code), + KEY idx_cumulative_recharge_reward_tier_sort (app_code, status, sort_order, threshold_usd_minor) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励档位表'; + +CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_events ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', + event_id VARCHAR(128) NOT NULL COMMENT 'wallet_outbox 事件 ID', + cycle_key VARCHAR(16) NOT NULL COMMENT 'UTC 自然周', + user_id BIGINT NOT NULL COMMENT '用户 ID', + transaction_id VARCHAR(128) NOT NULL COMMENT '钱包交易 ID', + command_id VARCHAR(128) NOT NULL COMMENT '钱包命令 ID', + recharge_type VARCHAR(64) NOT NULL DEFAULT '' COMMENT '充值来源类型', + recharge_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '本次到账金币', + qualifying_usd_minor BIGINT NOT NULL DEFAULT 0 COMMENT '本次计入累充的美元美分', + payload_json JSON NULL COMMENT 'wallet 事件 payload', + occurred_at_ms BIGINT NOT NULL COMMENT '充值发生时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + PRIMARY KEY (app_code, event_id), + KEY idx_cumulative_recharge_reward_event_user_cycle (app_code, cycle_key, user_id, occurred_at_ms), + KEY idx_cumulative_recharge_reward_event_transaction (app_code, transaction_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励充值事件幂等表'; + +CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_progress ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', + cycle_key VARCHAR(16) NOT NULL COMMENT 'UTC 自然周', + user_id BIGINT NOT NULL COMMENT '用户 ID', + total_usd_minor BIGINT NOT NULL DEFAULT 0 COMMENT '周期累计美元美分', + total_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '周期累计到账金币', + first_recharged_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '周期首笔充值时间', + last_recharged_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '周期最近充值时间', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_key, user_id), + KEY idx_cumulative_recharge_reward_progress_total (app_code, cycle_key, total_usd_minor) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励用户周期累计表'; + +CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_grants ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', + grant_id VARCHAR(128) NOT NULL COMMENT '发放 ID', + cycle_key VARCHAR(16) NOT NULL COMMENT 'UTC 自然周', + user_id BIGINT NOT NULL COMMENT '用户 ID', + event_id VARCHAR(128) NOT NULL COMMENT '触发发放的 wallet_outbox 事件 ID', + transaction_id VARCHAR(128) NOT NULL COMMENT '触发发放的钱包交易 ID', + command_id VARCHAR(128) NOT NULL COMMENT '触发发放的钱包命令 ID', + tier_id BIGINT NOT NULL COMMENT '命中奖励档位 ID', + tier_code VARCHAR(64) NOT NULL COMMENT '命中奖励档位编码', + threshold_usd_minor BIGINT NOT NULL COMMENT '命中档位门槛,美分', + resource_group_id BIGINT NOT NULL COMMENT '奖励资源分组 ID', + reached_usd_minor BIGINT NOT NULL COMMENT '命中时累计金额,美分', + qualifying_usd_minor BIGINT NOT NULL DEFAULT 0 COMMENT '触发事件计入金额,美分', + recharge_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '触发事件到账金币', + recharge_type VARCHAR(64) NOT NULL DEFAULT '' COMMENT '触发事件充值来源类型', + status VARCHAR(32) NOT NULL COMMENT '业务状态', + wallet_command_id VARCHAR(128) NOT NULL COMMENT '钱包发奖命令 ID', + wallet_grant_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '钱包资源发放 ID', + failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因', + granted_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '发放时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, grant_id), + UNIQUE KEY uk_cumulative_recharge_reward_user_tier (app_code, cycle_key, user_id, tier_id), + UNIQUE KEY uk_cumulative_recharge_reward_wallet_command (app_code, wallet_command_id), + KEY idx_cumulative_recharge_reward_grant_event (app_code, event_id, status), + KEY idx_cumulative_recharge_reward_grant_user (app_code, cycle_key, user_id, created_at_ms), + KEY idx_cumulative_recharge_reward_grant_status (app_code, status, created_at_ms) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励发放表'; + CREATE TABLE IF NOT EXISTS seven_day_checkin_configs ( app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', enabled TINYINT(1) NOT NULL DEFAULT 0 COMMENT '启用', @@ -993,3 +1099,169 @@ INSERT IGNORE INTO lucky_gift_stage_tiers ( ('lalu', 'super_lucky', 1, 'advanced', 'advanced_1x', 1000000, 600000, 'base_rtp', false, 'none', true), ('lalu', 'super_lucky', 1, 'advanced', 'advanced_2x', 2000000, 100000, 'base_rtp', false, 'none', true), ('lalu', 'super_lucky', 1, 'advanced', 'advanced_5x', 5000000, 30000, 'base_rtp', false, 'none', true); + +CREATE TABLE IF NOT EXISTS room_turnover_reward_configs ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + enabled TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用', + updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最后更新管理员', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励配置表'; + +CREATE TABLE IF NOT EXISTS room_turnover_reward_tiers ( + tier_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '档位 ID', + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + tier_code VARCHAR(64) NOT NULL COMMENT '档位编码', + tier_name VARCHAR(128) NOT NULL COMMENT '档位名称', + threshold_coin_spent BIGINT NOT NULL COMMENT '命中该档需要的房间送礼金币流水', + reward_coin_amount BIGINT NOT NULL COMMENT '奖励房主金币数', + status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT 'active/inactive', + sort_order INT NOT NULL DEFAULT 0 COMMENT '排序', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (tier_id), + UNIQUE KEY uk_room_turnover_reward_tier_code (app_code, tier_code), + KEY idx_room_turnover_reward_tiers_match (app_code, status, threshold_coin_spent) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励档位表'; + +CREATE TABLE IF NOT EXISTS room_turnover_reward_event_consumption ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + event_id VARCHAR(128) NOT NULL COMMENT 'room-service outbox event_id', + event_type VARCHAR(96) NOT NULL COMMENT '事件类型', + room_id VARCHAR(96) NOT NULL COMMENT '房间 ID', + period_start_ms BIGINT NOT NULL COMMENT 'UTC 周期开始', + coin_spent BIGINT NOT NULL COMMENT '本事件送礼金币消耗', + status VARCHAR(32) NOT NULL COMMENT '消费状态', + consumed_at_ms BIGINT NOT NULL COMMENT '消费时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, event_id), + KEY idx_room_turnover_reward_event_period (app_code, period_start_ms, room_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励事件消费幂等表'; + +CREATE TABLE IF NOT EXISTS room_turnover_reward_progress ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + room_id VARCHAR(96) NOT NULL COMMENT '房间 ID', + period_start_ms BIGINT NOT NULL COMMENT 'UTC 周期开始', + period_end_ms BIGINT NOT NULL COMMENT 'UTC 周期结束', + coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '本周期房间送礼金币流水', + last_event_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '最后事件时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, room_id, period_start_ms), + KEY idx_room_turnover_reward_progress_period (app_code, period_start_ms, coin_spent) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励周期聚合表'; + +CREATE TABLE IF NOT EXISTS room_turnover_reward_settlements ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + settlement_id VARCHAR(96) NOT NULL COMMENT '结算 ID', + room_id VARCHAR(96) NOT NULL COMMENT '房间 ID', + owner_user_id BIGINT NOT NULL DEFAULT 0 COMMENT '结算收款房主', + period_start_ms BIGINT NOT NULL COMMENT 'UTC 周期开始', + period_end_ms BIGINT NOT NULL COMMENT 'UTC 周期结束', + coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '周期房间流水', + tier_id BIGINT NOT NULL DEFAULT 0 COMMENT '命中档位 ID', + tier_code VARCHAR(64) NOT NULL DEFAULT '' COMMENT '命中档位编码', + threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '命中档位阈值', + reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '发放金币', + status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/granted/failed', + wallet_command_id VARCHAR(160) NOT NULL DEFAULT '' COMMENT 'wallet-service 幂等命令 ID', + wallet_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包交易 ID', + failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因', + settled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '成功发放时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, settlement_id), + UNIQUE KEY uk_room_turnover_reward_settlement_room_period (app_code, room_id, period_start_ms), + KEY idx_room_turnover_reward_settlement_status (app_code, status, period_start_ms, created_at_ms), + KEY idx_room_turnover_reward_settlement_owner (app_code, owner_user_id, period_start_ms) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励每周结算表'; + +CREATE TABLE IF NOT EXISTS weekly_star_cycles ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + activity_code VARCHAR(64) NOT NULL DEFAULT 'weekly_star' COMMENT '活动编码', + region_id BIGINT NOT NULL DEFAULT 0 COMMENT '区域 ID,0 表示默认配置', + title VARCHAR(128) NOT NULL DEFAULT '' COMMENT '周期标题', + status VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT '状态', + start_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 生效开始,包含', + end_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 生效结束,不包含', + created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建管理员', + updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新管理员', + settled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算完成时间', + locked_by VARCHAR(96) NOT NULL DEFAULT '' COMMENT '结算 worker 锁', + lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算锁过期时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id), + KEY idx_weekly_star_cycle_current (app_code, activity_code, status, region_id, start_ms, end_ms), + KEY idx_weekly_star_cycle_due (app_code, activity_code, status, end_ms, lock_until_ms) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星指定礼物积分周期'; + +CREATE TABLE IF NOT EXISTS weekly_star_cycle_gifts ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + gift_id VARCHAR(96) NOT NULL COMMENT '参与计分礼物 ID', + sort_order INT NOT NULL DEFAULT 0 COMMENT '展示顺序', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id, gift_id), + KEY idx_weekly_star_gift_match (app_code, gift_id, cycle_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星周期指定礼物'; + +CREATE TABLE IF NOT EXISTS weekly_star_cycle_rewards ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + rank_no INT NOT NULL COMMENT '排名,1/2/3', + resource_group_id BIGINT NOT NULL COMMENT '奖励资源组 ID', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id, rank_no) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星周期 Top 奖励'; + +CREATE TABLE IF NOT EXISTS weekly_star_user_scores ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + user_id BIGINT NOT NULL COMMENT '用户 ID', + score BIGINT NOT NULL DEFAULT 0 COMMENT '累计积分,等于活动礼物消耗金币', + first_scored_at_ms BIGINT NOT NULL COMMENT '首次得分时间', + last_scored_at_ms BIGINT NOT NULL COMMENT '最近得分时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id, user_id), + KEY idx_weekly_star_score_rank (app_code, cycle_id, score, first_scored_at_ms, user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星用户积分榜'; + +CREATE TABLE IF NOT EXISTS weekly_star_score_events ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + source_event_id VARCHAR(128) NOT NULL COMMENT 'room-service outbox event_id', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + user_id BIGINT NOT NULL COMMENT '送礼用户 ID', + gift_id VARCHAR(96) NOT NULL COMMENT '礼物 ID', + score_delta BIGINT NOT NULL COMMENT '本次增加积分', + occurred_at_ms BIGINT NOT NULL COMMENT '事件发生时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + PRIMARY KEY (app_code, source_event_id), + KEY idx_weekly_star_score_event_cycle (app_code, cycle_id, user_id, occurred_at_ms) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星送礼积分事件幂等表'; + +CREATE TABLE IF NOT EXISTS weekly_star_settlements ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + settlement_id VARCHAR(96) NOT NULL COMMENT '周星结算 ID', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + rank_no INT NOT NULL COMMENT '排名,1/2/3', + user_id BIGINT NOT NULL COMMENT '获奖用户 ID', + score BIGINT NOT NULL DEFAULT 0 COMMENT '结算积分', + resource_group_id BIGINT NOT NULL COMMENT '奖励资源组 ID', + wallet_command_id VARCHAR(128) NOT NULL COMMENT 'wallet 幂等命令', + wallet_grant_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'wallet grant ID', + status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT '发放状态', + failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因', + attempt_count INT NOT NULL DEFAULT 0 COMMENT '尝试次数', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, settlement_id), + UNIQUE KEY uk_weekly_star_settlement_rank (app_code, cycle_id, rank_no), + UNIQUE KEY uk_weekly_star_wallet_command (app_code, wallet_command_id), + KEY idx_weekly_star_settlement_cycle (app_code, cycle_id, status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星 Top 奖励结算'; diff --git a/services/activity-service/internal/app/app.go b/services/activity-service/internal/app/app.go index 7db6a46b..d3fce4d6 100644 --- a/services/activity-service/internal/app/app.go +++ b/services/activity-service/internal/app/app.go @@ -2,7 +2,6 @@ package app import ( "context" - "encoding/json" "errors" "log/slog" "net" @@ -10,39 +9,21 @@ import ( "time" "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - healthgrpc "google.golang.org/grpc/health/grpc_health_v1" - activityv1 "hyapp.local/api/proto/activity/v1" - walletv1 "hyapp.local/api/proto/wallet/v1" - "hyapp/pkg/appcode" "hyapp/pkg/grpchealth" "hyapp/pkg/grpcshutdown" "hyapp/pkg/healthhttp" "hyapp/pkg/logx" "hyapp/pkg/rocketmqx" - "hyapp/pkg/roommq" - "hyapp/pkg/tencentim" - "hyapp/pkg/usermq" - "hyapp/pkg/walletmq" - "hyapp/services/activity-service/internal/client" "hyapp/services/activity-service/internal/config" - broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast" - firstrechargedomain "hyapp/services/activity-service/internal/domain/firstrecharge" - achievementservice "hyapp/services/activity-service/internal/service/achievement" - activityservice "hyapp/services/activity-service/internal/service/activity" broadcastservice "hyapp/services/activity-service/internal/service/broadcast" + cumulativerechargeservice "hyapp/services/activity-service/internal/service/cumulativerecharge" firstrechargeservice "hyapp/services/activity-service/internal/service/firstrecharge" - growthservice "hyapp/services/activity-service/internal/service/growth" luckygiftservice "hyapp/services/activity-service/internal/service/luckygift" - messageservice "hyapp/services/activity-service/internal/service/message" - registrationrewardservice "hyapp/services/activity-service/internal/service/registrationreward" - sevendaycheckinservice "hyapp/services/activity-service/internal/service/sevendaycheckin" - taskservice "hyapp/services/activity-service/internal/service/task" mysqlstorage "hyapp/services/activity-service/internal/storage/mysql" - grpcserver "hyapp/services/activity-service/internal/transport/grpc" ) -// App 装配 activity-service gRPC 入口和底座依赖。 +// App 装配 activity-service 的 gRPC 入口、健康检查、后台 worker 和外部连接。 +// 具体业务模块的创建和注册都放在独立文件里,避免启动入口继续膨胀成一个难维护的大函数。 type App struct { server *grpc.Server listener net.Listener @@ -52,6 +33,7 @@ type App struct { broadcast *broadcastservice.Service luckyGift *luckygiftservice.Service firstRechargeReward *firstrechargeservice.Service + cumulativeRecharge *cumulativerechargeservice.Service broadcastWorkerEnabled bool luckyGiftWorkerEnabled bool workerNodeID string @@ -59,6 +41,7 @@ type App struct { mqConsumers []*rocketmqx.Consumer userConn *grpc.ClientConn walletConn *grpc.ClientConn + roomConn *grpc.ClientConn workerCtx context.Context workerStop context.CancelFunc workerWG sync.WaitGroup @@ -70,254 +53,71 @@ func New(cfg config.Config) (*App, error) { startupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // activity-service 的活动状态、消费幂等和 outbox 都必须使用 MySQL。 - repository, err := mysqlstorage.Open(startupCtx, cfg.MySQLDSN) + repository, err := openRepository(startupCtx, cfg) if err != nil { return nil, err } - if cfg.MySQLAutoMigrate { - if err := repository.Migrate(startupCtx); err != nil { - _ = repository.Close() - return nil, err + var listener net.Listener + var clients externalClients + var mqConsumers []*rocketmqx.Consumer + cleanup := func() { + shutdownConsumers(mqConsumers) + clients.Close() + if listener != nil { + _ = listener.Close() } + _ = repository.Close() } - listener, err := net.Listen("tcp", cfg.GRPCAddr) + listener, err = listenGRPC(cfg.GRPCAddr) if err != nil { - _ = repository.Close() + cleanup() return nil, err } - userConn, err := grpc.Dial(cfg.UserServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + clients, err = dialExternalClients(cfg) if err != nil { - _ = listener.Close() - _ = repository.Close() - return nil, err - } - walletConn, err := grpc.Dial(cfg.WalletServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() + cleanup() return nil, err } server := grpc.NewServer(grpc.UnaryInterceptor(logx.UnaryServerInterceptor("activity-service"))) - svc := activityservice.New(activityservice.Config{NodeID: cfg.NodeID}, repository) - messageSvc := messageservice.New(messageservice.Config{NodeID: cfg.NodeID}, repository, messageservice.WithTargetSource(client.NewGRPCUserTargetSource(userConn))) - taskSvc := taskservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) - registrationRewardSvc := registrationrewardservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) - sevenDayCheckInSvc := sevendaycheckinservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) - growthSvc := growthservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) - achievementSvc := achievementservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) - var tencentClient *tencentim.RESTClient - var broadcastPublisher broadcastservice.Publisher - if cfg.TencentIM.Enabled { - tencentClient, err = tencentim.NewRESTClient(cfg.TencentIM.RESTConfig()) - if err != nil { - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - broadcastPublisher = tencentClient - } - if cfg.Broadcast.Enabled && broadcastPublisher == nil { - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, errors.New("broadcast worker requires tencent_im.enabled") - } - broadcastSvc := broadcastservice.New(broadcastservice.Config{ - NodeID: cfg.NodeID, - SuperGiftMinValue: cfg.Broadcast.SuperGiftMinValue, - WorkerBatchSize: cfg.Broadcast.WorkerBatchSize, - WorkerLockTTL: cfg.Broadcast.WorkerLockTTL, - WorkerMaxRetry: cfg.Broadcast.WorkerMaxRetry, - WorkerPollInterval: cfg.Broadcast.WorkerPollInterval, - EnsureGroupsOnStartup: cfg.Broadcast.EnsureGroupsOnStartup, - GroupIDPrefix: cfg.TencentIM.GroupIDPrefix, - }, repository, broadcastPublisher, client.NewGRPCRegionSource(userConn)) - broadcastSvc.SetSenderProfileSource(client.NewGRPCUserProfileSource(userConn)) - luckyGiftSvc := luckygiftservice.New(repository, - luckygiftservice.WithWallet(walletv1.NewWalletServiceClient(walletConn)), - luckygiftservice.WithRoomPublisher(tencentClient), - luckygiftservice.WithRegionBroadcaster(broadcastSvc), - ) - activityv1.RegisterActivityServiceServer(server, grpcserver.NewServer(svc)) - activityv1.RegisterMessageInboxServiceServer(server, grpcserver.NewMessageServer(messageSvc)) - activityv1.RegisterActivityCronServiceServer(server, grpcserver.NewCronServer(messageSvc, growthSvc, achievementSvc)) - activityv1.RegisterTaskServiceServer(server, grpcserver.NewTaskServer(taskSvc)) - activityv1.RegisterAdminTaskServiceServer(server, grpcserver.NewAdminTaskServer(taskSvc)) - activityv1.RegisterGrowthLevelServiceServer(server, grpcserver.NewGrowthLevelServer(growthSvc)) - activityv1.RegisterAdminGrowthLevelServiceServer(server, grpcserver.NewAdminGrowthLevelServer(growthSvc)) - activityv1.RegisterAchievementServiceServer(server, grpcserver.NewAchievementServer(achievementSvc)) - activityv1.RegisterAdminAchievementServiceServer(server, grpcserver.NewAdminAchievementServer(achievementSvc)) - activityv1.RegisterLuckyGiftServiceServer(server, grpcserver.NewLuckyGiftServer(luckyGiftSvc)) - activityv1.RegisterAdminLuckyGiftServiceServer(server, grpcserver.NewAdminLuckyGiftServer(luckyGiftSvc)) - activityv1.RegisterRegistrationRewardServiceServer(server, grpcserver.NewRegistrationRewardServer(registrationRewardSvc)) - activityv1.RegisterAdminRegistrationRewardServiceServer(server, grpcserver.NewAdminRegistrationRewardServer(registrationRewardSvc)) - activityv1.RegisterSevenDayCheckInServiceServer(server, grpcserver.NewSevenDayCheckInServer(sevenDayCheckInSvc)) - activityv1.RegisterAdminSevenDayCheckInServiceServer(server, grpcserver.NewAdminSevenDayCheckInServer(sevenDayCheckInSvc)) - broadcastServer := grpcserver.NewBroadcastServer(broadcastSvc, growthSvc) - activityv1.RegisterBroadcastServiceServer(server, broadcastServer) - activityv1.RegisterRoomEventConsumerServiceServer(server, broadcastServer) - firstRechargeRewardSvc := firstrechargeservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) - activityv1.RegisterFirstRechargeRewardServiceServer(server, grpcserver.NewFirstRechargeRewardServer(firstRechargeRewardSvc)) - activityv1.RegisterAdminFirstRechargeRewardServiceServer(server, grpcserver.NewAdminFirstRechargeRewardServer(firstRechargeRewardSvc)) - mqConsumers := make([]*rocketmqx.Consumer, 0, 4) - if cfg.RocketMQ.RoomOutbox.Enabled { - consumer, err := rocketmqx.NewConsumer(roomOutboxConsumerConfig(cfg.RocketMQ)) - if err != nil { - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - if err := consumer.Subscribe(cfg.RocketMQ.RoomOutbox.Topic, roommq.TagRoomOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { - envelope, _, err := roommq.DecodeRoomOutboxMessage(message.Body) - if err != nil { - return err - } - eventCtx := appcode.WithContext(ctx, envelope.GetAppCode()) - if _, err := broadcastSvc.HandleRoomEvent(eventCtx, envelope); err != nil { - return err - } - _, err = growthSvc.HandleRoomEvent(eventCtx, envelope) - return err - }); err != nil { - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - mqConsumers = append(mqConsumers, consumer) - } - if cfg.RocketMQ.UserOutbox.Enabled && broadcastPublisher == nil { - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, errors.New("rocketmq user_outbox consumer requires tencent_im.enabled") - } - if cfg.RocketMQ.UserOutbox.Enabled { - consumer, err := rocketmqx.NewConsumer(userOutboxConsumerConfig(cfg.RocketMQ)) - if err != nil { - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - if err := consumer.Subscribe(cfg.RocketMQ.UserOutbox.Topic, usermq.TagUserOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { - outboxMessage, err := usermq.DecodeUserOutboxMessage(message.Body) - if err != nil { - return err - } - return consumeUserRegionChangedForBroadcast(ctx, broadcastSvc, outboxMessage) - }); err != nil { - _ = consumer.Shutdown() - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - mqConsumers = append(mqConsumers, consumer) - } - if cfg.FirstRechargeRewardWorker.Enabled && cfg.RocketMQ.WalletOutbox.Enabled { - consumer, err := rocketmqx.NewConsumer(walletOutboxConsumerConfig(cfg.RocketMQ, cfg.RocketMQ.WalletOutbox.FirstRechargeConsumerGroup)) - if err != nil { - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - if err := consumer.Subscribe(cfg.RocketMQ.WalletOutbox.Topic, walletmq.TagWalletOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { - event, ok, err := firstRechargeEventFromWalletMessage(message.Body) - if err != nil || !ok { - return err - } - return firstRechargeRewardSvc.ConsumeWalletRechargeEvent(appcode.WithContext(ctx, event.AppCode), event) - }); err != nil { - _ = consumer.Shutdown() - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - mqConsumers = append(mqConsumers, consumer) - } - if cfg.RedPacketBroadcastWorker.Enabled && cfg.RocketMQ.WalletOutbox.Enabled { - consumer, err := rocketmqx.NewConsumer(walletOutboxConsumerConfig(cfg.RocketMQ, cfg.RocketMQ.WalletOutbox.RedPacketBroadcastConsumerGroup)) - if err != nil { - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - if err := consumer.Subscribe(redPacketWalletOutboxTopic(cfg.RocketMQ.WalletOutbox), walletmq.TagWalletOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { - event, ok, err := redPacketEventFromWalletMessage(message.Body) - if err != nil || !ok { - return err - } - return broadcastSvc.ConsumeRedPacketWalletEvent(appcode.WithContext(ctx, event.AppCode), event) - }); err != nil { - _ = consumer.Shutdown() - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() - return nil, err - } - mqConsumers = append(mqConsumers, consumer) - } - healthDependencies := []grpchealth.Dependency{{ - Name: "mysql", - Check: repository.Ping, - }} - health := grpchealth.NewServingChecker("activity-service", healthDependencies...) - healthgrpc.RegisterHealthServer(server, grpchealth.NewServer(health)) - healthHTTP, err := healthhttp.New(cfg.HealthHTTPAddr, cfg.NodeID, health) + services, err := buildServiceBundle(cfg, repository, clients) if err != nil { - shutdownConsumers(mqConsumers) - _ = walletConn.Close() - _ = userConn.Close() - _ = listener.Close() - _ = repository.Close() + cleanup() return nil, err } - workerCtx, workerStop := context.WithCancel(context.Background()) + registerGRPCServers(server, services) + mqConsumers, err = buildMQConsumers(cfg, services) + if err != nil { + cleanup() + return nil, err + } + health, healthHTTP, err := newHealthServers(cfg, server, repository) + if err != nil { + cleanup() + return nil, err + } + + workerCtx, workerStop := context.WithCancel(context.Background()) return &App{ server: server, listener: listener, health: health, healthHTTP: healthHTTP, mysqlRepo: repository, - broadcast: broadcastSvc, - luckyGift: luckyGiftSvc, - firstRechargeReward: firstRechargeRewardSvc, + broadcast: services.broadcast, + luckyGift: services.luckyGift, + firstRechargeReward: services.firstRechargeReward, + cumulativeRecharge: services.cumulativeRecharge, broadcastWorkerEnabled: cfg.Broadcast.Enabled, luckyGiftWorkerEnabled: cfg.LuckyGiftWorker.Enabled, workerNodeID: cfg.NodeID, luckyGiftWorkerOptions: luckyGiftWorkerOptions(cfg.NodeID, cfg.LuckyGiftWorker), mqConsumers: mqConsumers, - userConn: userConn, - walletConn: walletConn, + userConn: clients.userConn, + walletConn: clients.walletConn, + roomConn: clients.roomConn, workerCtx: workerCtx, workerStop: workerStop, }, nil @@ -372,6 +172,9 @@ func (a *App) Close() { if a.walletConn != nil { _ = a.walletConn.Close() } + if a.roomConn != nil { + _ = a.roomConn.Close() + } if a.mysqlRepo != nil { // MySQL 连接池最后关闭,保证 drain 中的查询或消费提交能完成。 _ = a.mysqlRepo.Close() @@ -416,247 +219,3 @@ func (a *App) shutdownMQ() { } } } - -func roomOutboxConsumerConfig(cfg config.RocketMQConfig) rocketmqx.ConsumerConfig { - return rocketMQConsumerConfig(cfg, cfg.RoomOutbox.ConsumerGroup, cfg.RoomOutbox.ConsumerMaxReconsumeTimes) -} - -func userOutboxConsumerConfig(cfg config.RocketMQConfig) rocketmqx.ConsumerConfig { - return rocketMQConsumerConfig(cfg, cfg.UserOutbox.ConsumerGroup, cfg.UserOutbox.ConsumerMaxReconsumeTimes) -} - -func walletOutboxConsumerConfig(cfg config.RocketMQConfig, group string) rocketmqx.ConsumerConfig { - return rocketMQConsumerConfig(cfg, group, cfg.WalletOutbox.ConsumerMaxReconsumeTimes) -} - -func redPacketWalletOutboxTopic(cfg config.WalletOutboxMQConfig) string { - // 钱包实时 topic 显式配置后,红包 worker 只消费实时通道,避免继续被普通账务 topic 的高频消息拖慢。 - if cfg.RealtimeTopic != "" { - return cfg.RealtimeTopic - } - return cfg.Topic -} - -func rocketMQConsumerConfig(cfg config.RocketMQConfig, group string, maxReconsume int32) rocketmqx.ConsumerConfig { - return rocketmqx.ConsumerConfig{ - EndpointConfig: rocketmqx.EndpointConfig{ - NameServers: cfg.NameServers, - NameServerDomain: cfg.NameServerDomain, - AccessKey: cfg.AccessKey, - SecretKey: cfg.SecretKey, - SecurityToken: cfg.SecurityToken, - Namespace: cfg.Namespace, - VIPChannel: cfg.VIPChannel, - }, - GroupName: group, - MaxReconsumeTimes: maxReconsume, - ConsumeRetryDelay: time.Second, - ConsumePullBatch: 32, - ConsumePullTimeout: 15 * time.Minute, - } -} - -func shutdownConsumers(consumers []*rocketmqx.Consumer) { - for _, consumer := range consumers { - _ = consumer.Shutdown() - } -} - -type userRegionChangedPayload struct { - UserID int64 `json:"user_id"` - OldRegionID int64 `json:"old_region_id"` - NewRegionID int64 `json:"new_region_id"` -} - -func consumeUserRegionChangedForBroadcast(ctx context.Context, service *broadcastservice.Service, message usermq.UserOutboxMessage) error { - if message.EventType != "UserRegionChanged" { - return nil - } - var payload userRegionChangedPayload - if err := json.Unmarshal([]byte(message.PayloadJSON), &payload); err != nil { - return err - } - if payload.UserID <= 0 || payload.OldRegionID <= 0 || payload.OldRegionID == payload.NewRegionID { - return nil - } - // 区域广播群成员关系是当前读模型;用户切区后只移除旧群,下一次 UserSig 会按新 users.region_id 返回新群。 - _, _, err := service.RemoveRegionBroadcastMember(appcode.WithContext(ctx, message.AppCode), payload.UserID, payload.OldRegionID) - return err -} - -func firstRechargeEventFromWalletMessage(body []byte) (firstrechargedomain.RechargeEvent, bool, error) { - message, err := walletmq.DecodeWalletOutboxMessage(body) - if err != nil { - return firstrechargedomain.RechargeEvent{}, false, err - } - if message.EventType != firstrechargedomain.RechargeEventWalletRecorded { - return firstrechargedomain.RechargeEvent{}, false, nil - } - sequence, rechargeType := rechargeFactFromWalletPayload(message.PayloadJSON) - return firstrechargedomain.RechargeEvent{ - AppCode: appcode.Normalize(message.AppCode), - EventID: message.EventID, - EventType: message.EventType, - TransactionID: message.TransactionID, - CommandID: message.CommandID, - UserID: message.UserID, - RechargeCoinAmount: message.AvailableDelta, - RechargeSequence: sequence, - RechargeType: rechargeType, - PayloadJSON: message.PayloadJSON, - OccurredAtMS: message.OccurredAtMS, - }, true, nil -} - -func redPacketEventFromWalletMessage(body []byte) (broadcastdomain.RedPacketWalletEvent, bool, error) { - message, err := walletmq.DecodeWalletOutboxMessage(body) - if err != nil { - return broadcastdomain.RedPacketWalletEvent{}, false, err - } - if message.EventType != "WalletRedPacketCreated" && message.EventType != "WalletRedPacketClaimed" && message.EventType != "WalletRedPacketRefunded" { - return broadcastdomain.RedPacketWalletEvent{}, false, nil - } - fields := redPacketFieldsFromWalletPayload(message.PayloadJSON, message.EventType) - if fields.PacketID == "" { - return broadcastdomain.RedPacketWalletEvent{}, false, nil - } - return broadcastdomain.RedPacketWalletEvent{ - AppCode: appcode.Normalize(message.AppCode), - EventID: message.EventID, - EventType: message.EventType, - TransactionID: message.TransactionID, - CommandID: message.CommandID, - PayloadJSON: message.PayloadJSON, - PacketID: fields.PacketID, - PacketType: fields.PacketType, - RoomID: fields.RoomID, - RegionID: fields.RegionID, - RegionCode: fields.RegionCode, - SenderUserID: fields.SenderUserID, - TotalAmount: fields.TotalAmount, - TotalCount: fields.TotalCount, - RemainingAmount: fields.RemainingAmount, - RemainingCount: fields.RemainingCount, - RefundedAmount: fields.RefundedAmount, - Status: fields.Status, - OpenAtMS: fields.OpenAtMS, - ExpiresAtMS: fields.ExpiresAtMS, - CreatedAtMS: message.OccurredAtMS, - }, true, nil -} - -func rechargeFactFromWalletPayload(payload string) (int64, string) { - var decoded map[string]any - if err := json.Unmarshal([]byte(payload), &decoded); err != nil { - return 0, firstrechargedomain.RechargeTypeCoinSeller - } - var sequence int64 - switch value := decoded["recharge_sequence"].(type) { - case float64: - sequence = int64(value) - case int64: - sequence = value - case json.Number: - sequence, _ = value.Int64() - } - rechargeType := firstrechargedomain.RechargeTypeCoinSeller - if value, ok := decoded["recharge_type"].(string); ok && value != "" { - rechargeType = value - } - return sequence, rechargeType -} - -type redPacketPayloadFields struct { - PacketID string - PacketType string - RoomID string - RegionID int64 - RegionCode string - SenderUserID int64 - TotalAmount int64 - TotalCount int32 - RemainingAmount int64 - RemainingCount int32 - RefundedAmount int64 - Status string - OpenAtMS int64 - ExpiresAtMS int64 -} - -func redPacketFieldsFromWalletPayload(payload string, eventType string) redPacketPayloadFields { - var decoded map[string]any - if err := json.Unmarshal([]byte(payload), &decoded); err != nil { - return redPacketPayloadFields{} - } - packetID := stringFromDecoded(decoded, "packet_id") - if packetID == "" { - packetID = stringFromDecoded(decoded, "packetId") - } - packetType := stringFromDecoded(decoded, "packet_type") - roomID := stringFromDecoded(decoded, "room_id") - regionID := int64FromDecoded(decoded, "region_id") - status := stringFromDecoded(decoded, "status") - openAtMS := int64FromDecoded(decoded, "open_at_ms") - expiresAtMS := int64FromDecoded(decoded, "expires_at_ms") - if eventType == "WalletRedPacketClaimed" || eventType == "WalletRedPacketRefunded" { - status = stringFromDecoded(decoded, "packet_status") - } - totalCount := int32FromDecoded(decoded, "packet_count") - remainingCount := int32FromDecoded(decoded, "remaining_count") - if remainingCount == 0 && eventType == "WalletRedPacketCreated" { - remainingCount = totalCount - } - return redPacketPayloadFields{ - PacketID: packetID, - PacketType: packetType, - RoomID: roomID, - RegionID: regionID, - RegionCode: stringFromDecoded(decoded, "region_code"), - SenderUserID: int64FromDecoded(decoded, "sender_user_id"), - TotalAmount: int64FromDecoded(decoded, "total_amount"), - TotalCount: totalCount, - RemainingAmount: int64FromDecoded(decoded, "remaining_amount"), - RemainingCount: remainingCount, - RefundedAmount: int64FromDecoded(decoded, "refund_amount"), - Status: status, - OpenAtMS: openAtMS, - ExpiresAtMS: expiresAtMS, - } -} - -func stringFromDecoded(decoded map[string]any, key string) string { - value, _ := decoded[key].(string) - return value -} - -func int64FromDecoded(decoded map[string]any, key string) int64 { - switch value := decoded[key].(type) { - case float64: - return int64(value) - case int64: - return value - case json.Number: - parsed, _ := value.Int64() - return parsed - default: - return 0 - } -} - -func int32FromDecoded(decoded map[string]any, key string) int32 { - return int32(int64FromDecoded(decoded, key)) -} - -func luckyGiftWorkerOptions(nodeID string, cfg config.LuckyGiftWorkerConfig) luckygiftservice.WorkerOptions { - return luckygiftservice.WorkerOptions{ - WorkerID: nodeID + "-lucky-gift", - PollInterval: cfg.WorkerPollInterval, - BatchSize: cfg.WorkerBatchSize, - Concurrency: cfg.WorkerConcurrency, - LockTTL: cfg.WorkerLockTTL, - MaxRetry: cfg.WorkerMaxRetry, - PublishTimeout: cfg.PublishTimeout, - StatsInterval: cfg.StatsRefreshInterval, - StatsBatchSize: cfg.StatsBatchSize, - } -} diff --git a/services/activity-service/internal/app/dependencies.go b/services/activity-service/internal/app/dependencies.go new file mode 100644 index 00000000..0cbbec50 --- /dev/null +++ b/services/activity-service/internal/app/dependencies.go @@ -0,0 +1,71 @@ +package app + +import ( + "context" + "net" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "hyapp/services/activity-service/internal/config" + mysqlstorage "hyapp/services/activity-service/internal/storage/mysql" +) + +type externalClients struct { + userConn *grpc.ClientConn + walletConn *grpc.ClientConn + roomConn *grpc.ClientConn +} + +func openRepository(ctx context.Context, cfg config.Config) (*mysqlstorage.Repository, error) { + // activity-service 的活动配置、消费幂等、排行榜和 outbox 都落在 MySQL。 + // migrate 放在启动阶段执行,保证本地新增活动模块时不需要手工补表后才能启动。 + repository, err := mysqlstorage.Open(ctx, cfg.MySQLDSN) + if err != nil { + return nil, err + } + if cfg.MySQLAutoMigrate { + if err := repository.Migrate(ctx); err != nil { + _ = repository.Close() + return nil, err + } + } + return repository, nil +} + +func listenGRPC(addr string) (net.Listener, error) { + return net.Listen("tcp", addr) +} + +func dialExternalClients(cfg config.Config) (externalClients, error) { + // 三个下游连接在 App 级别统一持有;各业务 service 只拿生成后的 typed client。 + // 这样 Close 时可以按连接维度回收资源,新增模块也不会重复创建 gRPC 连接池。 + var clients externalClients + var err error + clients.userConn, err = grpc.Dial(cfg.UserServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return externalClients{}, err + } + clients.walletConn, err = grpc.Dial(cfg.WalletServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + clients.Close() + return externalClients{}, err + } + clients.roomConn, err = grpc.Dial(cfg.RoomServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + clients.Close() + return externalClients{}, err + } + return clients, nil +} + +func (c externalClients) Close() { + if c.roomConn != nil { + _ = c.roomConn.Close() + } + if c.walletConn != nil { + _ = c.walletConn.Close() + } + if c.userConn != nil { + _ = c.userConn.Close() + } +} diff --git a/services/activity-service/internal/app/grpc_registration.go b/services/activity-service/internal/app/grpc_registration.go new file mode 100644 index 00000000..7a19c7b3 --- /dev/null +++ b/services/activity-service/internal/app/grpc_registration.go @@ -0,0 +1,40 @@ +package app + +import ( + "google.golang.org/grpc" + activityv1 "hyapp.local/api/proto/activity/v1" + grpcserver "hyapp/services/activity-service/internal/transport/grpc" +) + +func registerGRPCServers(server *grpc.Server, services *serviceBundle) { + activityv1.RegisterActivityServiceServer(server, grpcserver.NewServer(services.activity)) + activityv1.RegisterMessageInboxServiceServer(server, grpcserver.NewMessageServer(services.message)) + // cron 入口只做调度适配;具体状态机仍在各 activity service 内部。 + // weekly-star 和 room-turnover 都依赖 wallet 发奖,所以这里必须把同一批已装配的 service 注入 cron server。 + activityv1.RegisterActivityCronServiceServer(server, grpcserver.NewCronServer(services.message, services.growth, services.achievement, services.weeklyStar, services.roomTurnoverReward)) + activityv1.RegisterTaskServiceServer(server, grpcserver.NewTaskServer(services.task)) + activityv1.RegisterAdminTaskServiceServer(server, grpcserver.NewAdminTaskServer(services.task)) + activityv1.RegisterGrowthLevelServiceServer(server, grpcserver.NewGrowthLevelServer(services.growth)) + activityv1.RegisterAdminGrowthLevelServiceServer(server, grpcserver.NewAdminGrowthLevelServer(services.growth)) + activityv1.RegisterAchievementServiceServer(server, grpcserver.NewAchievementServer(services.achievement)) + activityv1.RegisterAdminAchievementServiceServer(server, grpcserver.NewAdminAchievementServer(services.achievement)) + activityv1.RegisterLuckyGiftServiceServer(server, grpcserver.NewLuckyGiftServer(services.luckyGift)) + activityv1.RegisterAdminLuckyGiftServiceServer(server, grpcserver.NewAdminLuckyGiftServer(services.luckyGift)) + activityv1.RegisterRegistrationRewardServiceServer(server, grpcserver.NewRegistrationRewardServer(services.registrationReward)) + activityv1.RegisterAdminRegistrationRewardServiceServer(server, grpcserver.NewAdminRegistrationRewardServer(services.registrationReward)) + activityv1.RegisterSevenDayCheckInServiceServer(server, grpcserver.NewSevenDayCheckInServer(services.sevenDayCheckIn)) + activityv1.RegisterAdminSevenDayCheckInServiceServer(server, grpcserver.NewAdminSevenDayCheckInServer(services.sevenDayCheckIn)) + activityv1.RegisterWeeklyStarServiceServer(server, grpcserver.NewWeeklyStarServer(services.weeklyStar)) + activityv1.RegisterAdminWeeklyStarServiceServer(server, grpcserver.NewAdminWeeklyStarServer(services.weeklyStar)) + activityv1.RegisterRoomTurnoverRewardServiceServer(server, grpcserver.NewRoomTurnoverRewardServer(services.roomTurnoverReward)) + activityv1.RegisterAdminRoomTurnoverRewardServiceServer(server, grpcserver.NewAdminRoomTurnoverRewardServer(services.roomTurnoverReward)) + // BroadcastServer 同时承载 BroadcastService 和 RoomEventConsumerService。 + // gRPC 直连投递和 MQ 消费都必须经过相同的 room event fanout 顺序,否则本地验证会通过但线上 MQ 路径漏积分。 + broadcastServer := grpcserver.NewBroadcastServer(services.broadcast, services.growth, services.weeklyStar, services.roomTurnoverReward) + activityv1.RegisterBroadcastServiceServer(server, broadcastServer) + activityv1.RegisterRoomEventConsumerServiceServer(server, broadcastServer) + activityv1.RegisterFirstRechargeRewardServiceServer(server, grpcserver.NewFirstRechargeRewardServer(services.firstRechargeReward)) + activityv1.RegisterAdminFirstRechargeRewardServiceServer(server, grpcserver.NewAdminFirstRechargeRewardServer(services.firstRechargeReward)) + activityv1.RegisterCumulativeRechargeRewardServiceServer(server, grpcserver.NewCumulativeRechargeRewardServer(services.cumulativeRecharge)) + activityv1.RegisterAdminCumulativeRechargeRewardServiceServer(server, grpcserver.NewAdminCumulativeRechargeRewardServer(services.cumulativeRecharge)) +} diff --git a/services/activity-service/internal/app/health.go b/services/activity-service/internal/app/health.go new file mode 100644 index 00000000..faa88951 --- /dev/null +++ b/services/activity-service/internal/app/health.go @@ -0,0 +1,24 @@ +package app + +import ( + "google.golang.org/grpc" + healthgrpc "google.golang.org/grpc/health/grpc_health_v1" + "hyapp/pkg/grpchealth" + "hyapp/pkg/healthhttp" + "hyapp/services/activity-service/internal/config" + mysqlstorage "hyapp/services/activity-service/internal/storage/mysql" +) + +func newHealthServers(cfg config.Config, server *grpc.Server, repository *mysqlstorage.Repository) (*grpchealth.ServingChecker, *healthhttp.Server, error) { + // gRPC health 和 HTTP ready 共用同一个 checker,避免一个入口显示 ready、另一个入口还在 draining。 + health := grpchealth.NewServingChecker("activity-service", grpchealth.Dependency{ + Name: "mysql", + Check: repository.Ping, + }) + healthgrpc.RegisterHealthServer(server, grpchealth.NewServer(health)) + healthHTTP, err := healthhttp.New(cfg.HealthHTTPAddr, cfg.NodeID, health) + if err != nil { + return nil, nil, err + } + return health, healthHTTP, nil +} diff --git a/services/activity-service/internal/app/mq.go b/services/activity-service/internal/app/mq.go new file mode 100644 index 00000000..9db165cf --- /dev/null +++ b/services/activity-service/internal/app/mq.go @@ -0,0 +1,236 @@ +package app + +import ( + "context" + "encoding/json" + "errors" + "time" + + "hyapp/pkg/appcode" + "hyapp/pkg/rocketmqx" + "hyapp/pkg/roommq" + "hyapp/pkg/usermq" + "hyapp/pkg/walletmq" + "hyapp/services/activity-service/internal/config" + broadcastservice "hyapp/services/activity-service/internal/service/broadcast" +) + +func buildMQConsumers(cfg config.Config, services *serviceBundle) ([]*rocketmqx.Consumer, error) { + mqConsumers := make([]*rocketmqx.Consumer, 0, 4) + if cfg.RocketMQ.RoomOutbox.Enabled { + consumer, err := newRoomOutboxConsumer(cfg, services) + if err != nil { + shutdownConsumers(mqConsumers) + return nil, err + } + mqConsumers = append(mqConsumers, consumer) + } + if cfg.RocketMQ.UserOutbox.Enabled && !services.broadcastPublisherAvailable { + shutdownConsumers(mqConsumers) + return nil, errors.New("rocketmq user_outbox consumer requires tencent_im.enabled") + } + if cfg.RocketMQ.UserOutbox.Enabled { + consumer, err := newUserOutboxConsumer(cfg, services.broadcast) + if err != nil { + shutdownConsumers(mqConsumers) + return nil, err + } + mqConsumers = append(mqConsumers, consumer) + } + if cfg.FirstRechargeRewardWorker.Enabled && cfg.RocketMQ.WalletOutbox.Enabled { + consumer, err := newFirstRechargeWalletConsumer(cfg, services) + if err != nil { + shutdownConsumers(mqConsumers) + return nil, err + } + mqConsumers = append(mqConsumers, consumer) + } + if cfg.CumulativeRechargeRewardWorker.Enabled && cfg.RocketMQ.WalletOutbox.Enabled { + consumer, err := newCumulativeRechargeWalletConsumer(cfg, services) + if err != nil { + shutdownConsumers(mqConsumers) + return nil, err + } + mqConsumers = append(mqConsumers, consumer) + } + if cfg.RedPacketBroadcastWorker.Enabled && cfg.RocketMQ.WalletOutbox.Enabled { + consumer, err := newRedPacketWalletConsumer(cfg, services) + if err != nil { + shutdownConsumers(mqConsumers) + return nil, err + } + mqConsumers = append(mqConsumers, consumer) + } + return mqConsumers, nil +} + +func newRoomOutboxConsumer(cfg config.Config, services *serviceBundle) (*rocketmqx.Consumer, error) { + consumer, err := rocketmqx.NewConsumer(roomOutboxConsumerConfig(cfg.RocketMQ)) + if err != nil { + return nil, err + } + if err := consumer.Subscribe(cfg.RocketMQ.RoomOutbox.Topic, roommq.TagRoomOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { + envelope, _, err := roommq.DecodeRoomOutboxMessage(message.Body) + if err != nil { + return err + } + // 一个 room outbox envelope 代表房间状态已经提交;下游模块各自写幂等消费表。 + // 任何模块失败都返回错误给 MQ,让同一个 envelope 重试,避免“播报成功但活动漏计分”的半完成状态。 + eventCtx := appcode.WithContext(ctx, envelope.GetAppCode()) + if _, err := services.broadcast.HandleRoomEvent(eventCtx, envelope); err != nil { + return err + } + if _, err := services.growth.HandleRoomEvent(eventCtx, envelope); err != nil { + return err + } + if _, err := services.weeklyStar.HandleRoomEvent(eventCtx, envelope); err != nil { + return err + } + _, err = services.roomTurnoverReward.HandleRoomEvent(eventCtx, envelope) + return err + }); err != nil { + _ = consumer.Shutdown() + return nil, err + } + return consumer, nil +} + +func newUserOutboxConsumer(cfg config.Config, broadcastSvc *broadcastservice.Service) (*rocketmqx.Consumer, error) { + consumer, err := rocketmqx.NewConsumer(userOutboxConsumerConfig(cfg.RocketMQ)) + if err != nil { + return nil, err + } + if err := consumer.Subscribe(cfg.RocketMQ.UserOutbox.Topic, usermq.TagUserOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { + outboxMessage, err := usermq.DecodeUserOutboxMessage(message.Body) + if err != nil { + return err + } + return consumeUserRegionChangedForBroadcast(ctx, broadcastSvc, outboxMessage) + }); err != nil { + _ = consumer.Shutdown() + return nil, err + } + return consumer, nil +} + +func newFirstRechargeWalletConsumer(cfg config.Config, services *serviceBundle) (*rocketmqx.Consumer, error) { + consumer, err := rocketmqx.NewConsumer(walletOutboxConsumerConfig(cfg.RocketMQ, cfg.RocketMQ.WalletOutbox.FirstRechargeConsumerGroup)) + if err != nil { + return nil, err + } + if err := consumer.Subscribe(cfg.RocketMQ.WalletOutbox.Topic, walletmq.TagWalletOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { + event, ok, err := firstRechargeEventFromWalletMessage(message.Body) + if err != nil || !ok { + return err + } + return services.firstRechargeReward.ConsumeWalletRechargeEvent(appcode.WithContext(ctx, event.AppCode), event) + }); err != nil { + _ = consumer.Shutdown() + return nil, err + } + return consumer, nil +} + +func newCumulativeRechargeWalletConsumer(cfg config.Config, services *serviceBundle) (*rocketmqx.Consumer, error) { + // 累充奖励使用独立 consumer group,避免和首充奖励共享消费位点后互相影响重放和排查。 + consumer, err := rocketmqx.NewConsumer(walletOutboxConsumerConfig(cfg.RocketMQ, cfg.RocketMQ.WalletOutbox.CumulativeRechargeConsumerGroup)) + if err != nil { + return nil, err + } + if err := consumer.Subscribe(cfg.RocketMQ.WalletOutbox.Topic, walletmq.TagWalletOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { + event, ok, err := cumulativeRechargeEventFromWalletMessage(message.Body) + if err != nil || !ok { + return err + } + return services.cumulativeRecharge.ConsumeWalletRechargeEvent(appcode.WithContext(ctx, event.AppCode), event) + }); err != nil { + _ = consumer.Shutdown() + return nil, err + } + return consumer, nil +} + +func newRedPacketWalletConsumer(cfg config.Config, services *serviceBundle) (*rocketmqx.Consumer, error) { + consumer, err := rocketmqx.NewConsumer(walletOutboxConsumerConfig(cfg.RocketMQ, cfg.RocketMQ.WalletOutbox.RedPacketBroadcastConsumerGroup)) + if err != nil { + return nil, err + } + if err := consumer.Subscribe(redPacketWalletOutboxTopic(cfg.RocketMQ.WalletOutbox), walletmq.TagWalletOutboxEvent, func(ctx context.Context, message rocketmqx.ConsumedMessage) error { + event, ok, err := redPacketEventFromWalletMessage(message.Body) + if err != nil || !ok { + return err + } + return services.broadcast.ConsumeRedPacketWalletEvent(appcode.WithContext(ctx, event.AppCode), event) + }); err != nil { + _ = consumer.Shutdown() + return nil, err + } + return consumer, nil +} + +func roomOutboxConsumerConfig(cfg config.RocketMQConfig) rocketmqx.ConsumerConfig { + return rocketMQConsumerConfig(cfg, cfg.RoomOutbox.ConsumerGroup, cfg.RoomOutbox.ConsumerMaxReconsumeTimes) +} + +func userOutboxConsumerConfig(cfg config.RocketMQConfig) rocketmqx.ConsumerConfig { + return rocketMQConsumerConfig(cfg, cfg.UserOutbox.ConsumerGroup, cfg.UserOutbox.ConsumerMaxReconsumeTimes) +} + +func walletOutboxConsumerConfig(cfg config.RocketMQConfig, group string) rocketmqx.ConsumerConfig { + return rocketMQConsumerConfig(cfg, group, cfg.WalletOutbox.ConsumerMaxReconsumeTimes) +} + +func redPacketWalletOutboxTopic(cfg config.WalletOutboxMQConfig) string { + // 钱包实时 topic 显式配置后,红包 worker 只消费实时通道,避免继续被普通账务 topic 的高频消息拖慢。 + if cfg.RealtimeTopic != "" { + return cfg.RealtimeTopic + } + return cfg.Topic +} + +func rocketMQConsumerConfig(cfg config.RocketMQConfig, group string, maxReconsume int32) rocketmqx.ConsumerConfig { + return rocketmqx.ConsumerConfig{ + EndpointConfig: rocketmqx.EndpointConfig{ + NameServers: cfg.NameServers, + NameServerDomain: cfg.NameServerDomain, + AccessKey: cfg.AccessKey, + SecretKey: cfg.SecretKey, + SecurityToken: cfg.SecurityToken, + Namespace: cfg.Namespace, + VIPChannel: cfg.VIPChannel, + }, + GroupName: group, + MaxReconsumeTimes: maxReconsume, + ConsumeRetryDelay: time.Second, + ConsumePullBatch: 32, + ConsumePullTimeout: 15 * time.Minute, + } +} + +func shutdownConsumers(consumers []*rocketmqx.Consumer) { + for _, consumer := range consumers { + _ = consumer.Shutdown() + } +} + +type userRegionChangedPayload struct { + UserID int64 `json:"user_id"` + OldRegionID int64 `json:"old_region_id"` + NewRegionID int64 `json:"new_region_id"` +} + +func consumeUserRegionChangedForBroadcast(ctx context.Context, service *broadcastservice.Service, message usermq.UserOutboxMessage) error { + if message.EventType != "UserRegionChanged" { + return nil + } + var payload userRegionChangedPayload + if err := json.Unmarshal([]byte(message.PayloadJSON), &payload); err != nil { + return err + } + if payload.UserID <= 0 || payload.OldRegionID <= 0 || payload.OldRegionID == payload.NewRegionID { + return nil + } + // 区域广播群成员关系是当前读模型;用户切区后只移除旧群,下一次 UserSig 会按新 users.region_id 返回新群。 + _, _, err := service.RemoveRegionBroadcastMember(appcode.WithContext(ctx, message.AppCode), payload.UserID, payload.OldRegionID) + return err +} diff --git a/services/activity-service/internal/app/services.go b/services/activity-service/internal/app/services.go new file mode 100644 index 00000000..01ce1302 --- /dev/null +++ b/services/activity-service/internal/app/services.go @@ -0,0 +1,110 @@ +package app + +import ( + "errors" + + roomv1 "hyapp.local/api/proto/room/v1" + walletv1 "hyapp.local/api/proto/wallet/v1" + "hyapp/pkg/tencentim" + activityclient "hyapp/services/activity-service/internal/client" + "hyapp/services/activity-service/internal/config" + achievementservice "hyapp/services/activity-service/internal/service/achievement" + activityservice "hyapp/services/activity-service/internal/service/activity" + broadcastservice "hyapp/services/activity-service/internal/service/broadcast" + cumulativerechargeservice "hyapp/services/activity-service/internal/service/cumulativerecharge" + firstrechargeservice "hyapp/services/activity-service/internal/service/firstrecharge" + growthservice "hyapp/services/activity-service/internal/service/growth" + luckygiftservice "hyapp/services/activity-service/internal/service/luckygift" + messageservice "hyapp/services/activity-service/internal/service/message" + registrationrewardservice "hyapp/services/activity-service/internal/service/registrationreward" + roomturnoverrewardservice "hyapp/services/activity-service/internal/service/roomturnoverreward" + sevendaycheckinservice "hyapp/services/activity-service/internal/service/sevendaycheckin" + taskservice "hyapp/services/activity-service/internal/service/task" + weeklystarservice "hyapp/services/activity-service/internal/service/weeklystar" + mysqlstorage "hyapp/services/activity-service/internal/storage/mysql" +) + +type serviceBundle struct { + activity *activityservice.Service + message *messageservice.Service + task *taskservice.Service + registrationReward *registrationrewardservice.Service + sevenDayCheckIn *sevendaycheckinservice.Service + growth *growthservice.Service + achievement *achievementservice.Service + weeklyStar *weeklystarservice.Service + roomTurnoverReward *roomturnoverrewardservice.Service + broadcast *broadcastservice.Service + luckyGift *luckygiftservice.Service + firstRechargeReward *firstrechargeservice.Service + cumulativeRecharge *cumulativerechargeservice.Service + broadcastPublisherAvailable bool +} + +func buildServiceBundle(cfg config.Config, repository *mysqlstorage.Repository, clients externalClients) (*serviceBundle, error) { + walletClient := walletv1.NewWalletServiceClient(clients.walletConn) + roomClient := roomv1.NewRoomQueryServiceClient(clients.roomConn) + + activitySvc := activityservice.New(activityservice.Config{NodeID: cfg.NodeID}, repository) + messageSvc := messageservice.New(messageservice.Config{NodeID: cfg.NodeID}, repository, messageservice.WithTargetSource(activityclient.NewGRPCUserTargetSource(clients.userConn))) + taskSvc := taskservice.New(repository, walletClient) + registrationRewardSvc := registrationrewardservice.New(repository, walletClient) + sevenDayCheckInSvc := sevendaycheckinservice.New(repository, walletClient) + growthSvc := growthservice.New(repository, walletClient) + achievementSvc := achievementservice.New(repository, walletClient) + weeklyStarSvc := weeklystarservice.New(repository, walletClient) + roomTurnoverRewardSvc := roomturnoverrewardservice.New(repository, walletClient, roomClient) + + // Tencent IM 是区域广播、房间礼物播报和红包播报的外部发布器。 + // 如果配置要求启动广播 worker,就必须先确认 publisher 可用,避免 worker 启动后只写 outbox 不发消息。 + var tencentClient *tencentim.RESTClient + var broadcastPublisher broadcastservice.Publisher + if cfg.TencentIM.Enabled { + client, err := tencentim.NewRESTClient(cfg.TencentIM.RESTConfig()) + if err != nil { + return nil, err + } + tencentClient = client + broadcastPublisher = client + } + if cfg.Broadcast.Enabled && broadcastPublisher == nil { + return nil, errors.New("broadcast worker requires tencent_im.enabled") + } + + broadcastSvc := broadcastservice.New(broadcastservice.Config{ + NodeID: cfg.NodeID, + SuperGiftMinValue: cfg.Broadcast.SuperGiftMinValue, + WorkerBatchSize: cfg.Broadcast.WorkerBatchSize, + WorkerLockTTL: cfg.Broadcast.WorkerLockTTL, + WorkerMaxRetry: cfg.Broadcast.WorkerMaxRetry, + WorkerPollInterval: cfg.Broadcast.WorkerPollInterval, + EnsureGroupsOnStartup: cfg.Broadcast.EnsureGroupsOnStartup, + GroupIDPrefix: cfg.TencentIM.GroupIDPrefix, + }, repository, broadcastPublisher, activityclient.NewGRPCRegionSource(clients.userConn)) + broadcastSvc.SetSenderProfileSource(activityclient.NewGRPCUserProfileSource(clients.userConn)) + + luckyGiftSvc := luckygiftservice.New(repository, + luckygiftservice.WithWallet(walletClient), + luckygiftservice.WithRoomPublisher(tencentClient), + luckygiftservice.WithRegionBroadcaster(broadcastSvc), + ) + firstRechargeRewardSvc := firstrechargeservice.New(repository, walletClient) + cumulativeRechargeSvc := cumulativerechargeservice.New(repository, walletClient) + + return &serviceBundle{ + activity: activitySvc, + message: messageSvc, + task: taskSvc, + registrationReward: registrationRewardSvc, + sevenDayCheckIn: sevenDayCheckInSvc, + growth: growthSvc, + achievement: achievementSvc, + weeklyStar: weeklyStarSvc, + roomTurnoverReward: roomTurnoverRewardSvc, + broadcast: broadcastSvc, + luckyGift: luckyGiftSvc, + firstRechargeReward: firstRechargeRewardSvc, + cumulativeRecharge: cumulativeRechargeSvc, + broadcastPublisherAvailable: broadcastPublisher != nil, + }, nil +} diff --git a/services/activity-service/internal/app/wallet_events.go b/services/activity-service/internal/app/wallet_events.go new file mode 100644 index 00000000..08f337c3 --- /dev/null +++ b/services/activity-service/internal/app/wallet_events.go @@ -0,0 +1,296 @@ +package app + +import ( + "encoding/json" + "strings" + + "hyapp/pkg/appcode" + "hyapp/pkg/walletmq" + broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast" + cumulativerechargedomain "hyapp/services/activity-service/internal/domain/cumulativerecharge" + firstrechargedomain "hyapp/services/activity-service/internal/domain/firstrecharge" +) + +func firstRechargeEventFromWalletMessage(body []byte) (firstrechargedomain.RechargeEvent, bool, error) { + message, err := walletmq.DecodeWalletOutboxMessage(body) + if err != nil { + return firstrechargedomain.RechargeEvent{}, false, err + } + if message.EventType != firstrechargedomain.RechargeEventWalletRecorded { + return firstrechargedomain.RechargeEvent{}, false, nil + } + sequence, rechargeType := rechargeFactFromWalletPayload(message.PayloadJSON) + return firstrechargedomain.RechargeEvent{ + AppCode: appcode.Normalize(message.AppCode), + EventID: message.EventID, + EventType: message.EventType, + TransactionID: message.TransactionID, + CommandID: message.CommandID, + UserID: message.UserID, + RechargeCoinAmount: message.AvailableDelta, + RechargeSequence: sequence, + RechargeType: rechargeType, + PayloadJSON: message.PayloadJSON, + OccurredAtMS: message.OccurredAtMS, + }, true, nil +} + +func cumulativeRechargeEventFromWalletMessage(body []byte) (cumulativerechargedomain.RechargeEvent, bool, error) { + message, err := walletmq.DecodeWalletOutboxMessage(body) + if err != nil { + return cumulativerechargedomain.RechargeEvent{}, false, err + } + if message.EventType != cumulativerechargedomain.RechargeEventWalletRecorded { + return cumulativerechargedomain.RechargeEvent{}, false, nil + } + // WalletRechargeRecorded 是累充唯一事实源;payload 里没有可折算 USD 金额时直接跳过,避免把普通钱包流水计入活动。 + fields := cumulativeRechargeFactFromWalletPayload(message.PayloadJSON, message.AvailableDelta) + if fields.qualifyingUSDMinor <= 0 { + return cumulativerechargedomain.RechargeEvent{}, false, nil + } + return cumulativerechargedomain.RechargeEvent{ + AppCode: appcode.Normalize(message.AppCode), + EventID: message.EventID, + EventType: message.EventType, + TransactionID: message.TransactionID, + CommandID: message.CommandID, + UserID: message.UserID, + RechargeCoinAmount: fields.coinAmount, + RechargeSequence: fields.sequence, + RechargeType: fields.rechargeType, + QualifyingUSDMinor: fields.qualifyingUSDMinor, + PayloadJSON: message.PayloadJSON, + OccurredAtMS: message.OccurredAtMS, + }, true, nil +} + +func redPacketEventFromWalletMessage(body []byte) (broadcastdomain.RedPacketWalletEvent, bool, error) { + message, err := walletmq.DecodeWalletOutboxMessage(body) + if err != nil { + return broadcastdomain.RedPacketWalletEvent{}, false, err + } + if message.EventType != "WalletRedPacketCreated" && message.EventType != "WalletRedPacketClaimed" && message.EventType != "WalletRedPacketRefunded" { + return broadcastdomain.RedPacketWalletEvent{}, false, nil + } + fields := redPacketFieldsFromWalletPayload(message.PayloadJSON, message.EventType) + if fields.PacketID == "" { + return broadcastdomain.RedPacketWalletEvent{}, false, nil + } + return broadcastdomain.RedPacketWalletEvent{ + AppCode: appcode.Normalize(message.AppCode), + EventID: message.EventID, + EventType: message.EventType, + TransactionID: message.TransactionID, + CommandID: message.CommandID, + PayloadJSON: message.PayloadJSON, + PacketID: fields.PacketID, + PacketType: fields.PacketType, + RoomID: fields.RoomID, + RegionID: fields.RegionID, + RegionCode: fields.RegionCode, + SenderUserID: fields.SenderUserID, + TotalAmount: fields.TotalAmount, + TotalCount: fields.TotalCount, + RemainingAmount: fields.RemainingAmount, + RemainingCount: fields.RemainingCount, + RefundedAmount: fields.RefundedAmount, + Status: fields.Status, + OpenAtMS: fields.OpenAtMS, + ExpiresAtMS: fields.ExpiresAtMS, + CreatedAtMS: message.OccurredAtMS, + }, true, nil +} + +func rechargeFactFromWalletPayload(payload string) (int64, string) { + var decoded map[string]any + if err := json.Unmarshal([]byte(payload), &decoded); err != nil { + return 0, firstrechargedomain.RechargeTypeCoinSeller + } + var sequence int64 + switch value := decoded["recharge_sequence"].(type) { + case float64: + sequence = int64(value) + case int64: + sequence = value + case json.Number: + sequence, _ = value.Int64() + } + rechargeType := firstrechargedomain.RechargeTypeCoinSeller + if value, ok := decoded["recharge_type"].(string); ok && value != "" { + rechargeType = value + } + return sequence, rechargeType +} + +type cumulativeRechargePayloadFields struct { + sequence int64 + rechargeType string + coinAmount int64 + qualifyingUSDMinor int64 +} + +func cumulativeRechargeFactFromWalletPayload(payload string, availableDelta int64) cumulativeRechargePayloadFields { + var decoded map[string]any + if err := json.Unmarshal([]byte(payload), &decoded); err != nil { + decoded = map[string]any{} + } + // 来源字段兼容 wallet 历史 payload;没有明确来源时按币商转账处理,因为币商链路通常只带金币增量。 + rechargeType := firstNonEmptyString( + stringFromDecoded(decoded, "recharge_type"), + stringFromDecoded(decoded, "channel"), + stringFromDecoded(decoded, "provider"), + cumulativerechargedomain.RechargeTypeCoinSeller, + ) + // coinAmount 用于审计和币商折算,优先使用 payload 明细,最后回退 wallet outbox 的 available_delta。 + coinAmount := firstNonZeroInt64( + int64FromDecoded(decoded, "coin_amount"), + int64FromDecoded(decoded, "amount"), + int64FromDecoded(decoded, "available_delta"), + availableDelta, + ) + qualifyingUSDMinor := cumulativeRechargeUSDMinorFromPayload(decoded, rechargeType, coinAmount) + return cumulativeRechargePayloadFields{ + sequence: int64FromDecoded(decoded, "recharge_sequence"), + rechargeType: rechargeType, + coinAmount: coinAmount, + qualifyingUSDMinor: qualifyingUSDMinor, + } +} + +func cumulativeRechargeUSDMinorFromPayload(decoded map[string]any, rechargeType string, coinAmount int64) int64 { + if isCumulativeCoinSellerRecharge(rechargeType) { + // 币商给用户转账按 90000 coins = 1 USD 折算;这里返回美分并向下取整,尾差只留在原始金币字段审计。 + return coinAmount * 100 / 90000 + } + // Google/Mifapay 充值优先使用支付侧传入的美元美分,避免用金币包配置反推造成汇率误差。 + if value := firstNonZeroInt64( + int64FromDecoded(decoded, "recharge_usd_minor"), + int64FromDecoded(decoded, "usd_minor_amount"), + int64FromDecoded(decoded, "exchange_usd_minor_amount"), + int64FromDecoded(decoded, "amount_usd_minor"), + ); value > 0 { + return value + } + if isCumulativeGoogleRecharge(rechargeType) { + // Google Billing 常见 amount_micro 是 USD 微单位,除以 10000 后得到美分。 + return int64FromDecoded(decoded, "amount_micro") / 10000 + } + return 0 +} + +func isCumulativeCoinSellerRecharge(value string) bool { + switch strings.ToLower(strings.TrimSpace(value)) { + case "coin_seller_transfer", "coin_seller": + return true + default: + return false + } +} + +func isCumulativeGoogleRecharge(value string) bool { + switch strings.ToLower(strings.TrimSpace(value)) { + case "google", "google_play": + return true + default: + return false + } +} + +type redPacketPayloadFields struct { + PacketID string + PacketType string + RoomID string + RegionID int64 + RegionCode string + SenderUserID int64 + TotalAmount int64 + TotalCount int32 + RemainingAmount int64 + RemainingCount int32 + RefundedAmount int64 + Status string + OpenAtMS int64 + ExpiresAtMS int64 +} + +func redPacketFieldsFromWalletPayload(payload string, eventType string) redPacketPayloadFields { + var decoded map[string]any + if err := json.Unmarshal([]byte(payload), &decoded); err != nil { + return redPacketPayloadFields{} + } + packetID := stringFromDecoded(decoded, "packet_id") + if packetID == "" { + packetID = stringFromDecoded(decoded, "packetId") + } + packetType := stringFromDecoded(decoded, "packet_type") + roomID := stringFromDecoded(decoded, "room_id") + regionID := int64FromDecoded(decoded, "region_id") + status := stringFromDecoded(decoded, "status") + openAtMS := int64FromDecoded(decoded, "open_at_ms") + expiresAtMS := int64FromDecoded(decoded, "expires_at_ms") + if eventType == "WalletRedPacketClaimed" || eventType == "WalletRedPacketRefunded" { + status = stringFromDecoded(decoded, "packet_status") + } + totalCount := int32FromDecoded(decoded, "packet_count") + remainingCount := int32FromDecoded(decoded, "remaining_count") + if remainingCount == 0 && eventType == "WalletRedPacketCreated" { + remainingCount = totalCount + } + return redPacketPayloadFields{ + PacketID: packetID, + PacketType: packetType, + RoomID: roomID, + RegionID: regionID, + RegionCode: stringFromDecoded(decoded, "region_code"), + SenderUserID: int64FromDecoded(decoded, "sender_user_id"), + TotalAmount: int64FromDecoded(decoded, "total_amount"), + TotalCount: totalCount, + RemainingAmount: int64FromDecoded(decoded, "remaining_amount"), + RemainingCount: remainingCount, + RefundedAmount: int64FromDecoded(decoded, "refund_amount"), + Status: status, + OpenAtMS: openAtMS, + ExpiresAtMS: expiresAtMS, + } +} + +func stringFromDecoded(decoded map[string]any, key string) string { + value, _ := decoded[key].(string) + return value +} + +func firstNonEmptyString(values ...string) string { + for _, value := range values { + if value = strings.TrimSpace(value); value != "" { + return value + } + } + return "" +} + +func firstNonZeroInt64(values ...int64) int64 { + for _, value := range values { + if value != 0 { + return value + } + } + return 0 +} + +func int64FromDecoded(decoded map[string]any, key string) int64 { + switch value := decoded[key].(type) { + case float64: + return int64(value) + case int64: + return value + case json.Number: + parsed, _ := value.Int64() + return parsed + default: + return 0 + } +} + +func int32FromDecoded(decoded map[string]any, key string) int32 { + return int32(int64FromDecoded(decoded, key)) +} diff --git a/services/activity-service/internal/app/workers.go b/services/activity-service/internal/app/workers.go new file mode 100644 index 00000000..29e8e39e --- /dev/null +++ b/services/activity-service/internal/app/workers.go @@ -0,0 +1,20 @@ +package app + +import ( + "hyapp/services/activity-service/internal/config" + luckygiftservice "hyapp/services/activity-service/internal/service/luckygift" +) + +func luckyGiftWorkerOptions(nodeID string, cfg config.LuckyGiftWorkerConfig) luckygiftservice.WorkerOptions { + return luckygiftservice.WorkerOptions{ + WorkerID: nodeID + "-lucky-gift", + PollInterval: cfg.WorkerPollInterval, + BatchSize: cfg.WorkerBatchSize, + Concurrency: cfg.WorkerConcurrency, + LockTTL: cfg.WorkerLockTTL, + MaxRetry: cfg.WorkerMaxRetry, + PublishTimeout: cfg.PublishTimeout, + StatsInterval: cfg.StatsRefreshInterval, + StatsBatchSize: cfg.StatsBatchSize, + } +} diff --git a/services/activity-service/internal/config/config.go b/services/activity-service/internal/config/config.go index 8d537842..829887d1 100644 --- a/services/activity-service/internal/config/config.go +++ b/services/activity-service/internal/config/config.go @@ -25,8 +25,12 @@ type Config struct { UserServiceAddr string `yaml:"user_service_addr"` // WalletServiceAddr 是任务奖励入账依赖;activity 只发命令,不直接写余额。 WalletServiceAddr string `yaml:"wallet_service_addr"` + // RoomServiceAddr 是房间流水奖励结算读取房主快照的内部 gRPC 地址。 + RoomServiceAddr string `yaml:"room_service_addr"` // FirstRechargeRewardWorker 控制首冲奖励对 wallet_outbox 充值事实的本地消费。 FirstRechargeRewardWorker FirstRechargeRewardWorkerConfig `yaml:"first_recharge_reward_worker"` + // CumulativeRechargeRewardWorker 控制累充奖励对 wallet_outbox 充值事实的本地消费。 + CumulativeRechargeRewardWorker CumulativeRechargeRewardWorkerConfig `yaml:"cumulative_recharge_reward_worker"` // RedPacketBroadcastWorker 控制红包创建事实转区域飘屏 outbox 的本地消费。 RedPacketBroadcastWorker RedPacketBroadcastWorkerConfig `yaml:"red_packet_broadcast_worker"` // LuckyGiftWorker 控制幸运礼物 draw outbox 的返奖和房间 IM 补偿。 @@ -61,6 +65,12 @@ type FirstRechargeRewardWorkerConfig struct { Enabled bool `yaml:"enabled"` } +// CumulativeRechargeRewardWorkerConfig 保存累充奖励钱包充值事实消费策略。 +type CumulativeRechargeRewardWorkerConfig struct { + // Enabled 控制是否启动 wallet_outbox MQ 消费;关闭后仍可通过 gRPC 消费入口补偿。 + Enabled bool `yaml:"enabled"` +} + // RedPacketBroadcastWorkerConfig 保存红包创建事实消费策略。 type RedPacketBroadcastWorkerConfig struct { // Enabled 控制是否启动 wallet_outbox MQ 消费。 @@ -150,6 +160,7 @@ type WalletOutboxMQConfig struct { Topic string `yaml:"topic"` RealtimeTopic string `yaml:"realtime_topic"` FirstRechargeConsumerGroup string `yaml:"first_recharge_consumer_group"` + CumulativeRechargeConsumerGroup string `yaml:"cumulative_recharge_consumer_group"` RedPacketBroadcastConsumerGroup string `yaml:"red_packet_broadcast_consumer_group"` ConsumerMaxReconsumeTimes int32 `yaml:"consumer_max_reconsume_times"` } @@ -173,9 +184,13 @@ func Default() Config { MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC", UserServiceAddr: "127.0.0.1:13005", WalletServiceAddr: "127.0.0.1:13004", + RoomServiceAddr: "127.0.0.1:13001", FirstRechargeRewardWorker: FirstRechargeRewardWorkerConfig{ Enabled: false, }, + CumulativeRechargeRewardWorker: CumulativeRechargeRewardWorkerConfig{ + Enabled: false, + }, RedPacketBroadcastWorker: RedPacketBroadcastWorkerConfig{ Enabled: false, }, @@ -238,6 +253,7 @@ func defaultRocketMQConfig() RocketMQConfig { Topic: "hyapp_wallet_outbox", RealtimeTopic: "", FirstRechargeConsumerGroup: "hyapp-activity-first-recharge-wallet-outbox", + CumulativeRechargeConsumerGroup: "hyapp-activity-cumulative-recharge-wallet-outbox", RedPacketBroadcastConsumerGroup: "hyapp-activity-red-packet-wallet-outbox", ConsumerMaxReconsumeTimes: 16, }, @@ -266,6 +282,9 @@ func Load(path string) (Config, error) { if strings.TrimSpace(cfg.WalletServiceAddr) == "" { cfg.WalletServiceAddr = Default().WalletServiceAddr } + if strings.TrimSpace(cfg.RoomServiceAddr) == "" { + cfg.RoomServiceAddr = Default().RoomServiceAddr + } cfg.ServiceName = strings.TrimSpace(cfg.ServiceName) if cfg.ServiceName == "" { cfg.ServiceName = "activity-service" @@ -342,7 +361,7 @@ func Load(path string) (Config, error) { return Config{}, err } cfg.RocketMQ = rocketMQ - if (cfg.FirstRechargeRewardWorker.Enabled || cfg.RedPacketBroadcastWorker.Enabled) && !cfg.RocketMQ.WalletOutbox.Enabled { + if (cfg.FirstRechargeRewardWorker.Enabled || cfg.CumulativeRechargeRewardWorker.Enabled || cfg.RedPacketBroadcastWorker.Enabled) && !cfg.RocketMQ.WalletOutbox.Enabled { return Config{}, errors.New("wallet outbox workers require rocketmq.wallet_outbox.enabled") } if cfg.LuckyGiftWorker.Enabled && !cfg.TencentIM.Enabled { @@ -376,6 +395,9 @@ func normalizeRocketMQConfig(cfg RocketMQConfig) (RocketMQConfig, error) { if cfg.WalletOutbox.FirstRechargeConsumerGroup = strings.TrimSpace(cfg.WalletOutbox.FirstRechargeConsumerGroup); cfg.WalletOutbox.FirstRechargeConsumerGroup == "" { cfg.WalletOutbox.FirstRechargeConsumerGroup = defaults.WalletOutbox.FirstRechargeConsumerGroup } + if cfg.WalletOutbox.CumulativeRechargeConsumerGroup = strings.TrimSpace(cfg.WalletOutbox.CumulativeRechargeConsumerGroup); cfg.WalletOutbox.CumulativeRechargeConsumerGroup == "" { + cfg.WalletOutbox.CumulativeRechargeConsumerGroup = defaults.WalletOutbox.CumulativeRechargeConsumerGroup + } if cfg.WalletOutbox.RedPacketBroadcastConsumerGroup = strings.TrimSpace(cfg.WalletOutbox.RedPacketBroadcastConsumerGroup); cfg.WalletOutbox.RedPacketBroadcastConsumerGroup == "" { cfg.WalletOutbox.RedPacketBroadcastConsumerGroup = defaults.WalletOutbox.RedPacketBroadcastConsumerGroup } diff --git a/services/activity-service/internal/domain/cumulativerecharge/cumulative_recharge.go b/services/activity-service/internal/domain/cumulativerecharge/cumulative_recharge.go new file mode 100644 index 00000000..4eb4bff3 --- /dev/null +++ b/services/activity-service/internal/domain/cumulativerecharge/cumulative_recharge.go @@ -0,0 +1,130 @@ +package cumulativerecharge + +const ( + TierStatusActive = "active" + TierStatusInactive = "inactive" + + GrantStatusPending = "pending" + GrantStatusGranted = "granted" + GrantStatusFailed = "failed" + + ReasonEligible = "eligible" + ReasonNotConfigured = "not_configured" + ReasonDisabled = "disabled" + ReasonNoQualifyingValue = "no_qualifying_value" + ReasonAlreadyConsumed = "already_consumed" + ReasonNoTierMatched = "no_tier_matched" + ReasonPendingReward = "pending_reward" + + RechargeEventWalletRecorded = "WalletRechargeRecorded" + RechargeTypeCoinSeller = "coin_seller_transfer" +) + +// Tier 是累充奖励的 USD 档位,金额统一用美分保存,避免后台小数金额和发放判断出现精度漂移。 +type Tier struct { + TierID int64 + TierCode string + TierName string + ThresholdUSDMinor int64 + ResourceGroupID int64 + Status string + SortOrder int32 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// Config 是当前 App 的累充奖励配置;activity-service 保存档位快照并负责后续充值事件判定。 +type Config struct { + AppCode string + Enabled bool + Tiers []Tier + UpdatedByAdminID int64 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// Progress 是用户在一个 UTC 自然周内的累充累计投影。 +type Progress struct { + AppCode string + CycleKey string + UserID int64 + TotalUSDMinor int64 + TotalCoinAmount int64 + FirstRechargedAtMS int64 + LastRechargedAtMS int64 + UpdatedAtMS int64 +} + +// Grant 是用户命中某个累充档位后的资源组发放事实;event 字段用于 MQ 重试复用原命令。 +type Grant struct { + GrantID string + AppCode string + CycleKey string + UserID int64 + EventID string + TransactionID string + CommandID string + TierID int64 + TierCode string + ThresholdUSDMinor int64 + ResourceGroupID int64 + ReachedUSDMinor int64 + QualifyingUSDMinor int64 + RechargeCoinAmount int64 + RechargeType string + Status string + WalletCommandID string + WalletGrantID string + FailureReason string + GrantedAtMS int64 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// RechargeEvent 是 wallet_outbox 里进入累充奖励的成功充值事实。 +type RechargeEvent struct { + AppCode string + EventID string + EventType string + TransactionID string + CommandID string + UserID int64 + RechargeCoinAmount int64 + RechargeSequence int64 + RechargeType string + QualifyingUSDMinor int64 + PayloadJSON string + OccurredAtMS int64 +} + +// Cycle 表达 UTC 自然周周期边界。 +type Cycle struct { + Key string + StartMS int64 + EndMS int64 +} + +// StatusResult 是 App H5 页面读取的完整累充状态。 +type StatusResult struct { + Config Config + Progress Progress + Grants []Grant + Cycle Cycle + ServerTimeMS int64 +} + +// PrepareResult 表达一次充值事实在 MySQL 事务内是否产生了需要执行的发放命令。 +type PrepareResult struct { + Grants []Grant + Consumed bool + Reason string +} + +// GrantQuery 是后台发放记录分页查询条件。 +type GrantQuery struct { + Status string + UserID int64 + CycleKey string + Page int32 + PageSize int32 +} diff --git a/services/activity-service/internal/domain/registrationreward/registration_reward.go b/services/activity-service/internal/domain/registrationreward/registration_reward.go index b9ff9393..e10328ef 100644 --- a/services/activity-service/internal/domain/registrationreward/registration_reward.go +++ b/services/activity-service/internal/domain/registrationreward/registration_reward.go @@ -77,10 +77,12 @@ type PrepareResult struct { Reason string } -// ClaimQuery 是后台领取记录分页查询条件;用户搜索由 admin-server 先解析成 user_id。 +// ClaimQuery 是后台领取记录分页查询条件;用户搜索由 admin-server 先解析成 user_id,领取时间筛选按 claimed_at_ms 优先、created_at_ms 兜底的页面展示口径执行。 type ClaimQuery struct { - Status string - UserID int64 - Page int32 - PageSize int32 + Status string + UserID int64 + ClaimedStartMS int64 + ClaimedEndMS int64 + Page int32 + PageSize int32 } diff --git a/services/activity-service/internal/domain/roomturnoverreward/room_turnover_reward.go b/services/activity-service/internal/domain/roomturnoverreward/room_turnover_reward.go new file mode 100644 index 00000000..04cfab4b --- /dev/null +++ b/services/activity-service/internal/domain/roomturnoverreward/room_turnover_reward.go @@ -0,0 +1,120 @@ +package roomturnoverreward + +const ( + TierStatusActive = "active" + TierStatusInactive = "inactive" + + SettlementStatusPending = "pending" + SettlementStatusGranted = "granted" + SettlementStatusFailed = "failed" + + EventStatusConsumed = "consumed" + EventStatusDuplicate = "duplicate" + EventStatusSkipped = "skipped" +) + +// Tier 是后台配置的房间流水奖励档位,结算只发命中的最高有效档。 +type Tier struct { + TierID int64 + TierCode string + TierName string + ThresholdCoinSpent int64 + RewardCoinAmount int64 + Status string + SortOrder int32 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// Config 是当前 App 的房间流水奖励配置。 +type Config struct { + AppCode string + Enabled bool + Tiers []Tier + UpdatedByAdminID int64 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// Progress 是单房间单 UTC 周期的金币流水聚合。 +type Progress struct { + AppCode string + RoomID string + PeriodStartMS int64 + PeriodEndMS int64 + CoinSpent int64 + LastEventAtMS int64 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// RoomGiftEvent 是 room-service RoomGiftSent 事实映射后的最小聚合输入。 +type RoomGiftEvent struct { + EventID string + EventType string + AppCode string + RoomID string + CoinSpent int64 + OccurredAtMS int64 +} + +// EventResult 表达房间礼物事件是否参与流水聚合。 +type EventResult struct { + EventID string + Status string +} + +// Settlement 是某个房间单个 UTC 周期的奖励发放事实。 +type Settlement struct { + SettlementID string + AppCode string + RoomID string + OwnerUserID int64 + PeriodStartMS int64 + PeriodEndMS int64 + CoinSpent int64 + TierID int64 + TierCode string + ThresholdCoinSpent int64 + RewardCoinAmount int64 + Status string + WalletCommandID string + WalletTransactionID string + FailureReason string + SettledAtMS int64 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// StatusResult 是 H5 查询当前用户房间流水奖励活动时的展示投影。 +type StatusResult struct { + Config Config + RoomID string + OwnerUserID int64 + PeriodStartMS int64 + PeriodEndMS int64 + CurrentCoinSpent int64 + ExpectedRewardCoinAmount int64 + MatchedTier Tier + LatestSettlement Settlement + ServerTimeMS int64 +} + +// SettlementQuery 是后台结算记录分页筛选条件。 +type SettlementQuery struct { + Status string + RoomID string + OwnerUserID int64 + PeriodStartMS int64 + Page int32 + PageSize int32 +} + +// BatchResult 汇总 cron 一次处理的 settlement 创建和发币结果。 +type BatchResult struct { + ClaimedCount int32 + ProcessedCount int32 + SuccessCount int32 + FailureCount int32 + HasMore bool +} diff --git a/services/activity-service/internal/domain/weeklystar/weekly_star.go b/services/activity-service/internal/domain/weeklystar/weekly_star.go new file mode 100644 index 00000000..2edb51c2 --- /dev/null +++ b/services/activity-service/internal/domain/weeklystar/weekly_star.go @@ -0,0 +1,126 @@ +package weeklystar + +const ( + // ActivityCode 是通用指定礼物积分活动框架中用于承载周星的活动编码。 + ActivityCode = "weekly_star" + + StatusDraft = "draft" + StatusActive = "active" + StatusDisabled = "disabled" + StatusSettling = "settling" + StatusSettled = "settled" + + EventStatusConsumed = "consumed" + EventStatusSkipped = "skipped" + EventStatusDuplicate = "duplicate" + + SettlementStatusPending = "pending" + SettlementStatusRunning = "running" + SettlementStatusGranted = "granted" + SettlementStatusFailed = "failed" + + GrantSourceWeeklyStar = "weekly_star" +) + +// Gift describes one gift that can add score inside a weekly star cycle. +type Gift struct { + AppCode string + CycleID string + GiftID string + SortOrder int32 +} + +// Reward maps one weekly star rank to the resource group granted at settlement time. +type Reward struct { + AppCode string + CycleID string + RankNo int32 + ResourceGroupID int64 +} + +// Cycle is the region-scoped UTC time window that controls weekly star scoring and rewards. +type Cycle struct { + AppCode string + CycleID string + ActivityCode string + RegionID int64 + Title string + Status string + StartMS int64 + EndMS int64 + CreatedByAdminID int64 + UpdatedByAdminID int64 + SettledAtMS int64 + CreatedAtMS int64 + UpdatedAtMS int64 + Gifts []Gift + Rewards []Reward +} + +// CycleCommand is the admin mutation input after transport normalization. +type CycleCommand struct { + Cycle Cycle + OperatorAdminID int64 + RequireNewRecord bool +} + +// ListQuery filters admin cycle list reads. Region 0 is a real filter for default cycles. +type ListQuery struct { + RegionID *int64 + Status string + StartMS int64 + EndMS int64 + Page int32 + PageSize int32 +} + +// GiftEvent is the committed room-service gift fact consumed by weekly star. +type GiftEvent struct { + EventID string + UserID int64 + GiftID string + ScoreDelta int64 + RegionID int64 + OccurredAtMS int64 +} + +// EventResult describes idempotent score consumption for one source event. +type EventResult struct { + EventID string + Status string + CycleID string + ScoreDelta int64 +} + +// LeaderboardEntry is the score row plus deterministic rank within a cycle. +type LeaderboardEntry struct { + RankNo int32 + UserID int64 + Score int64 + FirstScoredAtMS int64 + LastScoredAtMS int64 +} + +// Settlement is one resource-group grant job for a settled Top rank. +type Settlement struct { + AppCode string + SettlementID string + CycleID string + RankNo int32 + UserID int64 + Score int64 + ResourceGroupID int64 + WalletCommandID string + WalletGrantID string + Status string + FailureReason string + AttemptCount int32 + CreatedAtMS int64 + UpdatedAtMS int64 +} + +// HistoryCycle groups a completed cycle with its Top entries for H5 history reads. +type HistoryCycle struct { + Cycle Cycle + TopEntries []LeaderboardEntry +} diff --git a/services/activity-service/internal/service/cumulativerecharge/service.go b/services/activity-service/internal/service/cumulativerecharge/service.go new file mode 100644 index 00000000..caa93dca --- /dev/null +++ b/services/activity-service/internal/service/cumulativerecharge/service.go @@ -0,0 +1,356 @@ +package cumulativerecharge + +import ( + "context" + "fmt" + "sort" + "strings" + "time" + + "google.golang.org/grpc" + walletv1 "hyapp.local/api/proto/wallet/v1" + "hyapp/pkg/appcode" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/cumulativerecharge" +) + +const ( + grantSource = "cumulative_recharge_reward" + grantReason = "cumulative_recharge_reward" +) + +// Repository 是累充奖励配置、周期累计、event 幂等和发放事实的唯一持久化边界。 +type Repository interface { + GetCumulativeRechargeRewardConfig(ctx context.Context) (domain.Config, bool, error) + UpdateCumulativeRechargeRewardConfig(ctx context.Context, config domain.Config, nowMS int64) (domain.Config, error) + GetCumulativeRechargeRewardProgress(ctx context.Context, cycleKey string, userID int64) (domain.Progress, bool, error) + ListCumulativeRechargeRewardGrantsByUserCycle(ctx context.Context, cycleKey string, userID int64) ([]domain.Grant, error) + PrepareCumulativeRechargeRewardGrants(ctx context.Context, event domain.RechargeEvent, cycle domain.Cycle, nowMS int64) (domain.PrepareResult, error) + MarkCumulativeRechargeRewardGrantGranted(ctx context.Context, grantID string, walletGrantID string, grantedAtMS int64) (domain.Grant, error) + MarkCumulativeRechargeRewardGrantFailed(ctx context.Context, grantID string, failureReason string, nowMS int64) error + ListCumulativeRechargeRewardGrants(ctx context.Context, query domain.GrantQuery) ([]domain.Grant, int64, error) +} + +// WalletClient 是累充奖励唯一外部副作用;资源组展开仍由 wallet-service 原子完成。 +type WalletClient interface { + GrantResourceGroup(ctx context.Context, req *walletv1.GrantResourceGroupRequest, opts ...grpc.CallOption) (*walletv1.ResourceGrantResponse, error) +} + +// Service 承载累充奖励 App 查询、后台配置和 wallet 充值事实消费用例。 +type Service struct { + repository Repository + wallet WalletClient + now func() time.Time +} + +func New(repository Repository, wallet WalletClient) *Service { + return &Service{repository: repository, wallet: wallet, now: time.Now} +} + +func (s *Service) SetClock(now func() time.Time) { + if now != nil { + s.now = now + } +} + +// GetStatus 返回当前 UTC 自然周内的用户累计、档位和发放状态;未配置时返回空配置而不是错误。 +func (s *Service) GetStatus(ctx context.Context, userID int64) (domain.StatusResult, error) { + if err := s.requireRepository(); err != nil { + return domain.StatusResult{}, err + } + if userID <= 0 { + return domain.StatusResult{}, xerr.New(xerr.InvalidArgument, "user_id is required") + } + now := s.now().UTC() + cycle := CycleForTime(now) + result := domain.StatusResult{Cycle: cycle, ServerTimeMS: now.UnixMilli()} + + // H5 只展示当前生效配置里的 active 档位;inactive 档位保留给后台编辑和历史 grant 审计。 + config, exists, err := s.repository.GetCumulativeRechargeRewardConfig(ctx) + if err != nil { + return domain.StatusResult{}, err + } + if exists { + config.Tiers = activeTiers(config.Tiers) + result.Config = config + } else { + result.Config = domain.Config{AppCode: appcode.FromContext(ctx)} + } + progress, _, err := s.repository.GetCumulativeRechargeRewardProgress(ctx, cycle.Key, userID) + if err != nil { + return domain.StatusResult{}, err + } + if progress.UserID == 0 { + progress = domain.Progress{AppCode: appcode.FromContext(ctx), CycleKey: cycle.Key, UserID: userID} + } + result.Progress = progress + grants, err := s.repository.ListCumulativeRechargeRewardGrantsByUserCycle(ctx, cycle.Key, userID) + if err != nil { + return domain.StatusResult{}, err + } + result.Grants = grants + return result, nil +} + +func (s *Service) GetConfig(ctx context.Context) (domain.Config, error) { + if err := s.requireRepository(); err != nil { + return domain.Config{}, err + } + config, exists, err := s.repository.GetCumulativeRechargeRewardConfig(ctx) + if err != nil { + return domain.Config{}, err + } + if !exists { + return domain.Config{AppCode: appcode.FromContext(ctx)}, nil + } + return config, nil +} + +func (s *Service) UpdateConfig(ctx context.Context, config domain.Config) (domain.Config, error) { + if err := s.requireRepository(); err != nil { + return domain.Config{}, err + } + // 后台配置是“未来事件规则”:保存时不扫描历史充值,也不改写已经创建的 grant 快照。 + config.AppCode = appcode.FromContext(ctx) + config.Tiers = normalizeTiers(config.Tiers) + if err := validateConfig(config); err != nil { + return domain.Config{}, err + } + return s.repository.UpdateCumulativeRechargeRewardConfig(ctx, config, s.now().UTC().UnixMilli()) +} + +// Consume 处理成功充值事实;一次充值可能让用户跨过多个累充档位,所以会返回多条 grant。 +func (s *Service) Consume(ctx context.Context, event domain.RechargeEvent) ([]domain.Grant, bool, string, error) { + if err := s.requireRepository(); err != nil { + return nil, false, "", err + } + event = normalizeRechargeEvent(event) + if err := validateRechargeEvent(event); err != nil { + return nil, false, "", err + } + cycle := CycleForTime(time.UnixMilli(event.OccurredAtMS).UTC()) + + // Prepare 在一个 MySQL 事务里完成 event 幂等、周期累计和待发放 grant 创建; + // 这里不直接发资源,避免外部 wallet 副作用进入数据库事务导致长锁或半提交。 + prepared, err := s.repository.PrepareCumulativeRechargeRewardGrants(ctx, event, cycle, s.now().UTC().UnixMilli()) + if err != nil { + return nil, false, "", err + } + if len(prepared.Grants) == 0 { + return nil, prepared.Consumed, prepared.Reason, nil + } + if s.wallet == nil { + return nil, false, "", xerr.New(xerr.Unavailable, "wallet client is not configured") + } + + granted := make([]domain.Grant, 0, len(prepared.Grants)) + for _, grant := range prepared.Grants { + // 每个档位使用稳定 wallet command id;MQ 重投或上次发放失败时复用同一命令,交给 wallet-service 幂等。 + resp, err := s.wallet.GrantResourceGroup(ctx, &walletv1.GrantResourceGroupRequest{ + CommandId: grant.WalletCommandID, + AppCode: appcode.FromContext(ctx), + TargetUserId: grant.UserID, + GroupId: grant.ResourceGroupID, + Reason: grantReason, + OperatorUserId: grant.UserID, + GrantSource: grantSource, + }) + if err != nil { + // wallet 调用失败只把 grant 标记为 failed,不删除记录;下一次同 event 重试会重新置回 pending 后继续发放。 + _ = s.repository.MarkCumulativeRechargeRewardGrantFailed(ctx, grant.GrantID, xerr.MessageOf(err), s.now().UTC().UnixMilli()) + return granted, false, prepared.Reason, err + } + walletGrantID := "" + if resp.GetGrant() != nil { + walletGrantID = resp.GetGrant().GetGrantId() + } + // 只有拿到 wallet 回执后才落 granted,后台记录因此可以区分“已命中但未到账”和“已到账”。 + updated, err := s.repository.MarkCumulativeRechargeRewardGrantGranted(ctx, grant.GrantID, walletGrantID, s.now().UTC().UnixMilli()) + if err != nil { + return granted, false, prepared.Reason, err + } + granted = append(granted, updated) + } + return granted, true, domain.ReasonEligible, nil +} + +func (s *Service) ListGrants(ctx context.Context, query domain.GrantQuery) ([]domain.Grant, int64, error) { + if err := s.requireRepository(); err != nil { + return nil, 0, err + } + query.Status = strings.TrimSpace(query.Status) + query.CycleKey = strings.TrimSpace(query.CycleKey) + if query.Page <= 0 { + query.Page = 1 + } + if query.PageSize <= 0 { + query.PageSize = 20 + } + if query.PageSize > 100 { + query.PageSize = 100 + } + return s.repository.ListCumulativeRechargeRewardGrants(ctx, query) +} + +// ConsumeWalletRechargeEvent handles one MQ-delivered wallet recharge fact. +func (s *Service) ConsumeWalletRechargeEvent(ctx context.Context, event domain.RechargeEvent) error { + if s == nil || s.repository == nil { + return xerr.New(xerr.Unavailable, "cumulative recharge reward repository is not configured") + } + if event.EventType != domain.RechargeEventWalletRecorded { + return nil + } + _, _, _, err := s.Consume(appcode.WithContext(ctx, event.AppCode), event) + return err +} + +func (s *Service) requireRepository() error { + if s == nil || s.repository == nil { + return xerr.New(xerr.Unavailable, "cumulative recharge reward repository is not configured") + } + return nil +} + +// CycleForTime returns the UTC natural-week cycle that contains value. +func CycleForTime(value time.Time) domain.Cycle { + utc := value.UTC() + dayStart := time.Date(utc.Year(), utc.Month(), utc.Day(), 0, 0, 0, 0, time.UTC) + // Go 的 Weekday 从 Sunday 开始,这里转换成 Monday=0,用于计算 UTC 自然周起点。 + daysSinceMonday := (int(dayStart.Weekday()) + 6) % 7 + start := dayStart.AddDate(0, 0, -daysSinceMonday) + end := start.AddDate(0, 0, 7) + isoYear, isoWeek := start.ISOWeek() + return domain.Cycle{ + Key: fmt.Sprintf("%04d-W%02d", isoYear, isoWeek), + StartMS: start.UnixMilli(), + EndMS: end.UnixMilli() - 1, + } +} + +func normalizeTiers(tiers []domain.Tier) []domain.Tier { + items := make([]domain.Tier, 0, len(tiers)) + for _, tier := range tiers { + status := strings.ToLower(strings.TrimSpace(tier.Status)) + if status == "" { + status = domain.TierStatusActive + } + items = append(items, domain.Tier{ + TierID: tier.TierID, + TierCode: strings.TrimSpace(tier.TierCode), + TierName: strings.TrimSpace(tier.TierName), + ThresholdUSDMinor: tier.ThresholdUSDMinor, + ResourceGroupID: tier.ResourceGroupID, + Status: status, + SortOrder: tier.SortOrder, + }) + } + // 排序同时服务后台回显和 H5 展示;sort_order 相同则按门槛金额升序保证跨档发放顺序稳定。 + sort.SliceStable(items, func(i, j int) bool { + if items[i].SortOrder == items[j].SortOrder { + return items[i].ThresholdUSDMinor < items[j].ThresholdUSDMinor + } + return items[i].SortOrder < items[j].SortOrder + }) + return items +} + +func validateConfig(config domain.Config) error { + if config.UpdatedByAdminID <= 0 { + return xerr.New(xerr.InvalidArgument, "operator_admin_id is required") + } + seen := make(map[string]struct{}, len(config.Tiers)) + active := make([]domain.Tier, 0, len(config.Tiers)) + for _, tier := range config.Tiers { + // tier_code 是后台编辑和审计展示标识,不参与唯一发放;真正的发放唯一性由 tier_id 绑定。 + if tier.TierCode == "" { + return xerr.New(xerr.InvalidArgument, "tier_code is required") + } + if _, exists := seen[tier.TierCode]; exists { + return xerr.New(xerr.InvalidArgument, "tier_code is duplicated") + } + seen[tier.TierCode] = struct{}{} + if tier.TierName == "" { + return xerr.New(xerr.InvalidArgument, "tier_name is required") + } + if tier.ThresholdUSDMinor <= 0 { + return xerr.New(xerr.InvalidArgument, "threshold_usd_minor must be greater than zero") + } + if tier.ResourceGroupID <= 0 { + return xerr.New(xerr.InvalidArgument, "resource_group_id is required") + } + switch tier.Status { + case domain.TierStatusActive: + active = append(active, tier) + case domain.TierStatusInactive: + default: + return xerr.New(xerr.InvalidArgument, "tier status is invalid") + } + } + if config.Enabled && len(active) == 0 { + return xerr.New(xerr.InvalidArgument, "enabled config requires at least one active tier") + } + // 同一个生效配置里 active 门槛不能重复,否则用户达标后无法用金额判断哪个档位先展示。 + if hasDuplicateThreshold(active) { + return xerr.New(xerr.InvalidArgument, "active tier threshold_usd_minor is duplicated") + } + return nil +} + +func hasDuplicateThreshold(tiers []domain.Tier) bool { + seen := make(map[int64]struct{}, len(tiers)) + for _, tier := range tiers { + if _, exists := seen[tier.ThresholdUSDMinor]; exists { + return true + } + seen[tier.ThresholdUSDMinor] = struct{}{} + } + return false +} + +func normalizeRechargeEvent(event domain.RechargeEvent) domain.RechargeEvent { + event.AppCode = appcode.Normalize(event.AppCode) + event.EventID = strings.TrimSpace(event.EventID) + event.EventType = strings.TrimSpace(event.EventType) + event.TransactionID = strings.TrimSpace(event.TransactionID) + event.CommandID = strings.TrimSpace(event.CommandID) + event.RechargeType = strings.ToLower(strings.TrimSpace(event.RechargeType)) + if event.RechargeType == "" { + event.RechargeType = domain.RechargeTypeCoinSeller + } + if strings.TrimSpace(event.PayloadJSON) == "" { + event.PayloadJSON = "{}" + } + return event +} + +func validateRechargeEvent(event domain.RechargeEvent) error { + if event.AppCode == "" || event.EventID == "" || event.TransactionID == "" || event.CommandID == "" { + return xerr.New(xerr.InvalidArgument, "recharge event identity is incomplete") + } + if event.UserID <= 0 { + return xerr.New(xerr.InvalidArgument, "user_id is required") + } + if event.QualifyingUSDMinor <= 0 { + return xerr.New(xerr.InvalidArgument, "qualifying_usd_minor must be greater than zero") + } + if event.OccurredAtMS <= 0 { + return xerr.New(xerr.InvalidArgument, "occurred_at_ms is required") + } + return nil +} + +func activeTiers(tiers []domain.Tier) []domain.Tier { + items := make([]domain.Tier, 0, len(tiers)) + for _, tier := range tiers { + if tier.Status == domain.TierStatusActive { + items = append(items, tier) + } + } + sort.SliceStable(items, func(i, j int) bool { + if items[i].SortOrder == items[j].SortOrder { + return items[i].ThresholdUSDMinor < items[j].ThresholdUSDMinor + } + return items[i].SortOrder < items[j].SortOrder + }) + return items +} diff --git a/services/activity-service/internal/service/luckygift/service.go b/services/activity-service/internal/service/luckygift/service.go index 9471b555..18038397 100644 --- a/services/activity-service/internal/service/luckygift/service.go +++ b/services/activity-service/internal/service/luckygift/service.go @@ -36,6 +36,7 @@ type Repository interface { MarkLuckyGiftOutboxRetryable(ctx context.Context, event domain.DrawOutbox, retryCount int, nextRetryAtMS int64, lastErr string, nowMS int64) error MarkLuckyGiftOutboxFailed(ctx context.Context, event domain.DrawOutbox, retryCount int, lastErr string, nowMS int64) error RefreshLuckyGiftAdminStatsSnapshots(ctx context.Context, nowMS int64, batchSize int) (int, error) + RefreshLuckyGiftDatabiStatsSnapshots(ctx context.Context, nowMS int64, batchSize int) (int, error) MarkLuckyGiftDrawGranted(ctx context.Context, appCode string, drawID string, walletTransactionID string, nowMS int64) error MarkLuckyGiftDrawFailed(ctx context.Context, appCode string, drawID string, failureReason string, nowMS int64) error // 批量抽奖只产生一条聚合 outbox;worker 处理后需要用 draw_ids 一次性收敛所有明细状态。 @@ -172,6 +173,9 @@ func (s *Service) RunWorker(ctx context.Context, options WorkerOptions) { if _, err := s.repository.RefreshLuckyGiftAdminStatsSnapshots(ctx, now.UnixMilli(), options.StatsBatchSize); err != nil && ctx.Err() == nil { logx.Error(ctx, "lucky_gift_admin_stats_refresh_failed", err, slog.String("worker_id", options.WorkerID)) } + if _, err := s.repository.RefreshLuckyGiftDatabiStatsSnapshots(ctx, now.UnixMilli(), options.StatsBatchSize); err != nil && ctx.Err() == nil { + logx.Error(ctx, "lucky_gift_databi_stats_refresh_failed", err, slog.String("worker_id", options.WorkerID)) + } nextStatsRefresh = now.Add(options.StatsInterval) } select { diff --git a/services/activity-service/internal/service/luckygift/service_test.go b/services/activity-service/internal/service/luckygift/service_test.go index ec2a6555..a54aba35 100644 --- a/services/activity-service/internal/service/luckygift/service_test.go +++ b/services/activity-service/internal/service/luckygift/service_test.go @@ -491,6 +491,10 @@ func (r *fakeLuckyGiftRepository) RefreshLuckyGiftAdminStatsSnapshots(context.Co return 0, nil } +func (r *fakeLuckyGiftRepository) RefreshLuckyGiftDatabiStatsSnapshots(context.Context, int64, int) (int, error) { + return 0, nil +} + func (r *fakeLuckyGiftRepository) MarkLuckyGiftDrawGranted(_ context.Context, _ string, drawID string, _ string, _ int64) error { r.grantedDrawID = drawID return nil diff --git a/services/activity-service/internal/service/registrationreward/service.go b/services/activity-service/internal/service/registrationreward/service.go index e4d0a256..f6731cab 100644 --- a/services/activity-service/internal/service/registrationreward/service.go +++ b/services/activity-service/internal/service/registrationreward/service.go @@ -26,6 +26,7 @@ type Repository interface { MarkRegistrationRewardClaimGranted(ctx context.Context, claimID string, walletTransactionID string, resourceGrantID string, grantedAtMS int64) (domain.Claim, error) MarkRegistrationRewardClaimFailed(ctx context.Context, claimID string, failureReason string, nowMS int64) error ListRegistrationRewardClaims(ctx context.Context, query domain.ClaimQuery) ([]domain.Claim, int64, error) + CountRegistrationRewardGrantedByDay(ctx context.Context, rewardDay string) (int64, error) } // WalletClient 是注册奖励发放的唯一外部副作用;金币和资源组都依赖 wallet-service 幂等命令。 @@ -176,9 +177,9 @@ func (s *Service) IssueRegistrationReward(ctx context.Context, command domain.Is return claim, eligibility, true, nil } -func (s *Service) ListClaims(ctx context.Context, query domain.ClaimQuery) ([]domain.Claim, int64, error) { +func (s *Service) ListClaims(ctx context.Context, query domain.ClaimQuery) ([]domain.Claim, int64, int64, error) { if err := s.requireRepository(); err != nil { - return nil, 0, err + return nil, 0, 0, err } query.Status = strings.TrimSpace(query.Status) if query.Page <= 0 { @@ -190,7 +191,16 @@ func (s *Service) ListClaims(ctx context.Context, query domain.ClaimQuery) ([]do if query.PageSize > 100 { query.PageSize = 100 } - return s.repository.ListRegistrationRewardClaims(ctx, query) + claims, total, err := s.repository.ListRegistrationRewardClaims(ctx, query) + if err != nil { + return nil, 0, 0, err + } + // 今日已领数量用于后台配置页展示,它必须使用同一套 UTC reward_day 口径,避免后台本地时区和发放额度统计出现错位。 + todayClaimedCount, err := s.repository.CountRegistrationRewardGrantedByDay(ctx, rewardDay(s.now())) + if err != nil { + return nil, 0, 0, err + } + return claims, total, todayClaimedCount, nil } func (s *Service) requireRepository() error { diff --git a/services/activity-service/internal/service/roomturnoverreward/service.go b/services/activity-service/internal/service/roomturnoverreward/service.go new file mode 100644 index 00000000..9a8afbae --- /dev/null +++ b/services/activity-service/internal/service/roomturnoverreward/service.go @@ -0,0 +1,517 @@ +package roomturnoverreward + +import ( + "context" + "sort" + "strings" + "time" + + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" + roomeventsv1 "hyapp.local/api/proto/events/room/v1" + roomv1 "hyapp.local/api/proto/room/v1" + walletv1 "hyapp.local/api/proto/wallet/v1" + "hyapp/pkg/appcode" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/roomturnoverreward" +) + +const rewardReason = "room_turnover_reward" + +// Repository 是房间流水奖励配置、周期聚合和结算记录的唯一持久化边界。 +type Repository interface { + GetRoomTurnoverRewardConfig(ctx context.Context) (domain.Config, bool, error) + UpdateRoomTurnoverRewardConfig(ctx context.Context, config domain.Config, nowMS int64) (domain.Config, error) + ConsumeRoomTurnoverGiftEvent(ctx context.Context, event domain.RoomGiftEvent, periodStartMS int64, periodEndMS int64, nowMS int64) (domain.EventResult, error) + GetRoomTurnoverRewardProgress(ctx context.Context, roomID string, periodStartMS int64) (domain.Progress, bool, error) + GetLatestRoomTurnoverRewardSettlement(ctx context.Context, roomID string) (domain.Settlement, bool, error) + ListRoomTurnoverRewardSettlements(ctx context.Context, query domain.SettlementQuery) ([]domain.Settlement, int64, error) + ListUnsettledRoomTurnoverRewardProgress(ctx context.Context, periodStartMS int64, limit int) ([]domain.Progress, error) + InsertRoomTurnoverRewardSettlement(ctx context.Context, settlement domain.Settlement, nowMS int64) (domain.Settlement, bool, error) + ListPendingRoomTurnoverRewardSettlements(ctx context.Context, limit int) ([]domain.Settlement, error) + GetRoomTurnoverRewardSettlement(ctx context.Context, settlementID string) (domain.Settlement, bool, error) + ResetRoomTurnoverRewardSettlementForRetry(ctx context.Context, settlementID string, ownerUserID int64, nowMS int64) (domain.Settlement, error) + MarkRoomTurnoverRewardSettlementGranted(ctx context.Context, settlementID string, walletTransactionID string, grantedAtMS int64) (domain.Settlement, error) + MarkRoomTurnoverRewardSettlementFailed(ctx context.Context, settlementID string, reason string, nowMS int64) error +} + +// WalletClient 是房间流水奖励唯一外部发币副作用。 +type WalletClient interface { + CreditRoomTurnoverReward(ctx context.Context, req *walletv1.CreditRoomTurnoverRewardRequest, opts ...grpc.CallOption) (*walletv1.CreditRoomTurnoverRewardResponse, error) +} + +// RoomQueryClient 只读取 room-service 的房主快照,不拥有房间状态。 +type RoomQueryClient interface { + AdminGetRoom(ctx context.Context, req *roomv1.AdminGetRoomRequest, opts ...grpc.CallOption) (*roomv1.AdminGetRoomResponse, error) +} + +// Service 承载房间流水奖励 App 查询、后台配置、事件聚合和每周结算。 +type Service struct { + repository Repository + wallet WalletClient + room RoomQueryClient + now func() time.Time +} + +func New(repository Repository, wallet WalletClient, room RoomQueryClient) *Service { + return &Service{repository: repository, wallet: wallet, room: room, now: time.Now} +} + +func (s *Service) SetClock(now func() time.Time) { + if now != nil { + s.now = now + } +} + +func (s *Service) GetConfig(ctx context.Context) (domain.Config, error) { + if err := s.requireRepository(); err != nil { + return domain.Config{}, err + } + config, exists, err := s.repository.GetRoomTurnoverRewardConfig(ctx) + if err != nil { + return domain.Config{}, err + } + if !exists { + return domain.Config{AppCode: appcode.FromContext(ctx)}, nil + } + return config, nil +} + +func (s *Service) UpdateConfig(ctx context.Context, config domain.Config) (domain.Config, error) { + if err := s.requireRepository(); err != nil { + return domain.Config{}, err + } + config.AppCode = appcode.FromContext(ctx) + config.Tiers = normalizeTiers(config.Tiers) + if err := validateConfig(config); err != nil { + return domain.Config{}, err + } + return s.repository.UpdateRoomTurnoverRewardConfig(ctx, config, s.now().UTC().UnixMilli()) +} + +func (s *Service) GetStatus(ctx context.Context, userID int64, roomID string, ownerUserID int64) (domain.StatusResult, error) { + if err := s.requireRepository(); err != nil { + return domain.StatusResult{}, err + } + if userID <= 0 { + return domain.StatusResult{}, xerr.New(xerr.InvalidArgument, "user_id is required") + } + // App 展示和每周结算必须使用同一套 UTC 周窗口;这里用 service clock 统一可测试时间源,避免 H5、本地测试和 cron 看到不同周期。 + now := s.now().UTC() + start, end := WeekWindow(now) + result := domain.StatusResult{ + RoomID: strings.TrimSpace(roomID), + OwnerUserID: ownerUserID, + PeriodStartMS: start.UnixMilli(), + PeriodEndMS: end.UnixMilli(), + ServerTimeMS: now.UnixMilli(), + } + config, exists, err := s.repository.GetRoomTurnoverRewardConfig(ctx) + if err != nil { + return domain.StatusResult{}, err + } + if exists { + config.Tiers = sortTiersForDisplay(config.Tiers) + result.Config = config + } else { + // 未配置不是异常:后台首次进入或活动未上线时,H5 仍需要周期时间和空档位来渲染默认态。 + result.Config = domain.Config{AppCode: appcode.FromContext(ctx)} + } + if result.RoomID == "" { + // 当前用户没有自己的房间时不能查询流水聚合;直接返回配置和周期,让 H5 显示无房间/未参与状态。 + return result, nil + } + progress, found, err := s.repository.GetRoomTurnoverRewardProgress(ctx, result.RoomID, result.PeriodStartMS) + if err != nil { + return domain.StatusResult{}, err + } + if found { + result.CurrentCoinSpent = progress.CoinSpent + } + if tier, matched := MatchHighestTier(activeTiers(result.Config.Tiers), result.CurrentCoinSpent); matched { + result.MatchedTier = tier + result.ExpectedRewardCoinAmount = tier.RewardCoinAmount + } + if settlement, found, err := s.repository.GetLatestRoomTurnoverRewardSettlement(ctx, result.RoomID); err != nil { + return domain.StatusResult{}, err + } else if found { + // 最近结算不限定当前周期;周切换后当前流水会清零,但 H5 仍要展示上一周期是否已经发奖或失败。 + result.LatestSettlement = settlement + } + return result, nil +} + +// HandleRoomEvent 把 room-service 已提交送礼事实聚合到对应 UTC 周期房间流水。 +func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) (domain.EventResult, error) { + if err := s.requireRepository(); err != nil { + return domain.EventResult{}, err + } + if envelope == nil { + return domain.EventResult{}, xerr.New(xerr.InvalidArgument, "room event envelope is required") + } + if envelope.GetEventType() != "RoomGiftSent" { + // activity-service 订阅的是 room outbox 大类事件;非送礼事实不参与流水奖励,直接标记 skipped,避免无意义重试。 + return domain.EventResult{EventID: envelope.GetEventId(), Status: domain.EventStatusSkipped}, nil + } + var gift roomeventsv1.RoomGiftSent + if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil { + return domain.EventResult{}, err + } + if strings.TrimSpace(envelope.GetRoomId()) == "" || gift.GetCoinSpent() <= 0 { + // 缺房间或 0 金币不会形成有效房间贡献;这类事件本身没有可补偿数据,跳过比失败重放更安全。 + return domain.EventResult{EventID: envelope.GetEventId(), Status: domain.EventStatusSkipped}, nil + } + occurred := time.UnixMilli(envelope.GetOccurredAtMs()).UTC() + if envelope.GetOccurredAtMs() <= 0 { + // 老事件缺 occurred_at 时使用消费时间兜底,只影响本地兼容,正常 outbox 事件仍以事实发生时间归属周期。 + occurred = s.now().UTC() + } + start, end := WeekWindow(occurred) + // AppCode 以事件信封为准,防止消费者默认上下文把不同 App 的房间流水写进同一组配置和聚合表。 + eventCtx := appcode.WithContext(ctx, envelope.GetAppCode()) + return s.repository.ConsumeRoomTurnoverGiftEvent(eventCtx, domain.RoomGiftEvent{ + EventID: envelope.GetEventId(), + EventType: envelope.GetEventType(), + AppCode: envelope.GetAppCode(), + RoomID: strings.TrimSpace(envelope.GetRoomId()), + CoinSpent: gift.GetCoinSpent(), + OccurredAtMS: occurred.UnixMilli(), + }, start.UnixMilli(), end.UnixMilli(), s.now().UTC().UnixMilli()) +} + +func (s *Service) ListSettlements(ctx context.Context, query domain.SettlementQuery) ([]domain.Settlement, int64, error) { + if err := s.requireRepository(); err != nil { + return nil, 0, err + } + query.Status = strings.TrimSpace(query.Status) + query.RoomID = strings.TrimSpace(query.RoomID) + if query.Page <= 0 { + query.Page = 1 + } + if query.PageSize <= 0 { + query.PageSize = 20 + } + if query.PageSize > 100 { + query.PageSize = 100 + } + return s.repository.ListRoomTurnoverRewardSettlements(ctx, query) +} + +// ProcessSettlementBatch 先为上一 UTC 周期补齐 settlement,再处理 pending 发币。 +func (s *Service) ProcessSettlementBatch(ctx context.Context, batchSize int) (domain.BatchResult, error) { + if err := s.requireRepository(); err != nil { + return domain.BatchResult{}, err + } + if batchSize <= 0 { + batchSize = 100 + } + config, exists, err := s.repository.GetRoomTurnoverRewardConfig(ctx) + if err != nil { + return domain.BatchResult{}, err + } + if !exists || !config.Enabled { + // 活动未配置或关闭时不创建 settlement,也不触碰历史 pending;运营重新开启后再由 cron 正常补偿。 + return domain.BatchResult{}, nil + } + // 只结算上一个完整 UTC 周期;当前周仍在持续接收 RoomGiftSent,不能提前冻结或清空。 + periodStart, periodEnd := PreviousWeekWindow(s.now().UTC()) + result := domain.BatchResult{} + progresses, err := s.repository.ListUnsettledRoomTurnoverRewardProgress(ctx, periodStart.UnixMilli(), batchSize) + if err != nil { + return domain.BatchResult{}, err + } + nowMS := s.now().UTC().UnixMilli() + for _, progress := range progresses { + // settlement 先固化当周流水和命中档位快照,后续运营改配置不会改变已经进入结算队列的周期结果。 + settlement := s.settlementFromProgress(ctx, progress, periodEnd.UnixMilli(), activeTiers(config.Tiers), nowMS) + if settlement.TierID <= 0 { + // 有流水但未达档位也要落失败记录,后台可以解释为什么该房间本周期没有发奖。 + settlement.Status = domain.SettlementStatusFailed + settlement.FailureReason = "tier_not_matched" + } + if settlement.TierID > 0 && settlement.OwnerUserID <= 0 { + // 房主归属以结算时 room-service 快照为准;activity-service 不复制房间 owner 状态,只在需要发奖时同步读取。 + ownerID, ownerErr := s.fetchRoomOwner(ctx, progress.RoomID) + if ownerErr != nil || ownerID <= 0 { + settlement.Status = domain.SettlementStatusFailed + settlement.FailureReason = "room_owner_not_found" + } else { + settlement.OwnerUserID = ownerID + } + } + if _, created, err := s.repository.InsertRoomTurnoverRewardSettlement(ctx, settlement, nowMS); err != nil { + return result, err + } else if created { + // Insert 使用 room_id + period_start_ms 唯一键兜底;重复 cron 只会读回旧记录,不会重复认领同一周期。 + result.ClaimedCount++ + } + } + remaining := batchSize - int(result.ClaimedCount) + if remaining <= 0 { + remaining = batchSize + } + pending, err := s.repository.ListPendingRoomTurnoverRewardSettlements(ctx, remaining) + if err != nil { + return result, err + } + for _, settlement := range pending { + result.ProcessedCount++ + if _, err := s.grantSettlement(ctx, settlement); err != nil { + // grantSettlement 已把失败原因写回 settlement;batch 继续处理后续房间,避免单房间阻塞整周结算。 + result.FailureCount++ + continue + } + result.SuccessCount++ + } + result.HasMore = len(progresses) >= batchSize || len(pending) >= remaining + return result, nil +} + +func (s *Service) RetrySettlement(ctx context.Context, settlementID string) (domain.Settlement, error) { + if err := s.requireRepository(); err != nil { + return domain.Settlement{}, err + } + settlementID = strings.TrimSpace(settlementID) + if settlementID == "" { + return domain.Settlement{}, xerr.New(xerr.InvalidArgument, "settlement_id is required") + } + settlement, exists, err := s.repository.GetRoomTurnoverRewardSettlement(ctx, settlementID) + if err != nil { + return domain.Settlement{}, err + } + if !exists { + return domain.Settlement{}, xerr.New(xerr.NotFound, "room turnover reward settlement not found") + } + if settlement.Status == domain.SettlementStatusGranted { + // 已发奖记录直接返回,后台重复点击 retry 不会再次调用钱包。 + return settlement, nil + } + if settlement.TierID <= 0 { + // tier_not_matched 不是外部依赖失败,而是该房间当周没有达到任何档位;retry 不能把这类 0 档位记录重置成 granted。 + return settlement, xerr.New(xerr.InvalidArgument, "room turnover reward settlement is not grantable") + } + ownerID := settlement.OwnerUserID + if ownerID <= 0 { + // 失败记录可能是当时没读到房主;retry 时重新取 room-service 快照,允许房间修复后继续发奖。 + ownerID, err = s.fetchRoomOwner(ctx, settlement.RoomID) + if err != nil || ownerID <= 0 { + _ = s.repository.MarkRoomTurnoverRewardSettlementFailed(ctx, settlement.SettlementID, "room_owner_not_found", s.now().UTC().UnixMilli()) + return domain.Settlement{}, xerr.New(xerr.NotFound, "room owner not found") + } + } + settlement, err = s.repository.ResetRoomTurnoverRewardSettlementForRetry(ctx, settlement.SettlementID, ownerID, s.now().UTC().UnixMilli()) + if err != nil { + return domain.Settlement{}, err + } + return s.grantSettlement(ctx, settlement) +} + +func (s *Service) settlementFromProgress(ctx context.Context, progress domain.Progress, periodEndMS int64, tiers []domain.Tier, nowMS int64) domain.Settlement { + tier, matched := MatchHighestTier(tiers, progress.CoinSpent) + settlement := domain.Settlement{ + AppCode: appcode.FromContext(ctx), + RoomID: progress.RoomID, + PeriodStartMS: progress.PeriodStartMS, + PeriodEndMS: periodEndMS, + CoinSpent: progress.CoinSpent, + Status: domain.SettlementStatusPending, + CreatedAtMS: nowMS, + UpdatedAtMS: nowMS, + } + if matched { + // 结算记录保存档位快照;后续配置变更不反推历史奖励金额。 + settlement.TierID = tier.TierID + settlement.TierCode = tier.TierCode + settlement.ThresholdCoinSpent = tier.ThresholdCoinSpent + settlement.RewardCoinAmount = tier.RewardCoinAmount + } + return settlement +} + +func (s *Service) grantSettlement(ctx context.Context, settlement domain.Settlement) (domain.Settlement, error) { + if settlement.Status == domain.SettlementStatusGranted { + // 发奖入口保持幂等,避免 cron 和人工 retry 同时进来时重复入账。 + return settlement, nil + } + if settlement.OwnerUserID <= 0 || settlement.RewardCoinAmount < 0 || settlement.WalletCommandID == "" { + err := xerr.New(xerr.InvalidArgument, "room turnover reward settlement is not grantable") + _ = s.repository.MarkRoomTurnoverRewardSettlementFailed(ctx, settlement.SettlementID, xerr.MessageOf(err), s.now().UTC().UnixMilli()) + return domain.Settlement{}, err + } + if settlement.RewardCoinAmount == 0 { + // 0 金币档位是合法配置,用于运营只记录达标但不发币的周期;钱包不接受 0 金额命令,所以这里直接收敛结算状态。 + nowMS := s.now().UTC().UnixMilli() + return s.repository.MarkRoomTurnoverRewardSettlementGranted(ctx, settlement.SettlementID, "", nowMS) + } + if s.wallet == nil { + err := xerr.New(xerr.Unavailable, "wallet client is not configured") + _ = s.repository.MarkRoomTurnoverRewardSettlementFailed(ctx, settlement.SettlementID, xerr.MessageOf(err), s.now().UTC().UnixMilli()) + return domain.Settlement{}, err + } + // 钱包 command_id 使用 app + 周期 + room_id,保证同一房间同一周期无论 cron 重跑还是 retry,都只产生一笔账务交易。 + resp, err := s.wallet.CreditRoomTurnoverReward(ctx, &walletv1.CreditRoomTurnoverRewardRequest{ + CommandId: settlement.WalletCommandID, + AppCode: appcode.FromContext(ctx), + TargetUserId: settlement.OwnerUserID, + Amount: settlement.RewardCoinAmount, + SettlementId: settlement.SettlementID, + RoomId: settlement.RoomID, + PeriodStartMs: settlement.PeriodStartMS, + PeriodEndMs: settlement.PeriodEndMS, + CoinSpent: settlement.CoinSpent, + TierId: settlement.TierID, + TierCode: settlement.TierCode, + Reason: rewardReason, + }) + if err != nil { + _ = s.repository.MarkRoomTurnoverRewardSettlementFailed(ctx, settlement.SettlementID, xerr.MessageOf(err), s.now().UTC().UnixMilli()) + return domain.Settlement{}, err + } + return s.repository.MarkRoomTurnoverRewardSettlementGranted(ctx, settlement.SettlementID, resp.GetTransactionId(), resp.GetGrantedAtMs()) +} + +func (s *Service) fetchRoomOwner(ctx context.Context, roomID string) (int64, error) { + if s.room == nil { + return 0, xerr.New(xerr.Unavailable, "room query client is not configured") + } + roomID = strings.TrimSpace(roomID) + if roomID == "" { + return 0, xerr.New(xerr.InvalidArgument, "room_id is required") + } + resp, err := s.room.AdminGetRoom(ctx, &roomv1.AdminGetRoomRequest{ + Meta: &roomv1.RequestMeta{ + // 这里不是用户请求链路,使用可读的内部 request_id 方便按房间追踪结算时的 owner 查询。 + RequestId: "room-turnover-reward-owner:" + roomID, + AppCode: appcode.FromContext(ctx), + SentAtMs: s.now().UTC().UnixMilli(), + }, + RoomId: roomID, + }) + if err != nil { + return 0, err + } + return resp.GetRoom().GetOwnerUserId(), nil +} + +func (s *Service) requireRepository() error { + if s == nil || s.repository == nil { + return xerr.New(xerr.Unavailable, "room turnover reward repository is not configured") + } + return nil +} + +func normalizeTiers(tiers []domain.Tier) []domain.Tier { + items := make([]domain.Tier, 0, len(tiers)) + for _, tier := range tiers { + status := strings.ToLower(strings.TrimSpace(tier.Status)) + if status == "" { + // 后台表单可以省略 status;默认 active 保持配置体验简单,但 validate 仍会拦截非法状态。 + status = domain.TierStatusActive + } + items = append(items, domain.Tier{ + TierID: tier.TierID, + TierCode: strings.TrimSpace(tier.TierCode), + TierName: strings.TrimSpace(tier.TierName), + ThresholdCoinSpent: tier.ThresholdCoinSpent, + RewardCoinAmount: tier.RewardCoinAmount, + Status: status, + SortOrder: tier.SortOrder, + }) + } + return sortTiersForDisplay(items) +} + +func sortTiersForDisplay(tiers []domain.Tier) []domain.Tier { + items := append([]domain.Tier(nil), tiers...) + sort.SliceStable(items, func(i, j int) bool { + if items[i].SortOrder == items[j].SortOrder { + return items[i].ThresholdCoinSpent < items[j].ThresholdCoinSpent + } + return items[i].SortOrder < items[j].SortOrder + }) + return items +} + +func validateConfig(config domain.Config) error { + if config.UpdatedByAdminID <= 0 { + return xerr.New(xerr.InvalidArgument, "operator_admin_id is required") + } + // tier_code 是后台配置稳定标识,阈值和奖励可以修改,但同一 App 内不能出现两个相同编码。 + seen := make(map[string]struct{}, len(config.Tiers)) + activeCount := 0 + for _, tier := range config.Tiers { + if tier.TierCode == "" { + return xerr.New(xerr.InvalidArgument, "tier_code is required") + } + if _, exists := seen[tier.TierCode]; exists { + return xerr.New(xerr.InvalidArgument, "tier_code is duplicated") + } + seen[tier.TierCode] = struct{}{} + if tier.TierName == "" { + return xerr.New(xerr.InvalidArgument, "tier_name is required") + } + if tier.ThresholdCoinSpent <= 0 { + return xerr.New(xerr.InvalidArgument, "threshold_coin_spent must be greater than zero") + } + if tier.RewardCoinAmount < 0 { + return xerr.New(xerr.InvalidArgument, "reward_coin_amount must not be negative") + } + switch tier.Status { + case domain.TierStatusActive: + activeCount++ + case domain.TierStatusInactive: + default: + return xerr.New(xerr.InvalidArgument, "tier status is invalid") + } + } + if config.Enabled && activeCount == 0 { + // 开启活动但没有有效档位会让 H5 显示可参与却永远无法发奖,直接在配置入口拒绝。 + return xerr.New(xerr.InvalidArgument, "enabled config requires at least one active tier") + } + return nil +} + +func activeTiers(tiers []domain.Tier) []domain.Tier { + out := make([]domain.Tier, 0, len(tiers)) + for _, tier := range tiers { + if tier.Status == domain.TierStatusActive { + out = append(out, tier) + } + } + return out +} + +// MatchHighestTier 返回达到流水阈值的最高有效档。 +func MatchHighestTier(tiers []domain.Tier, coinSpent int64) (domain.Tier, bool) { + var matched domain.Tier + found := false + for _, tier := range tiers { + if tier.Status != domain.TierStatusActive || tier.ThresholdCoinSpent <= 0 || tier.RewardCoinAmount < 0 { + continue + } + if coinSpent >= tier.ThresholdCoinSpent && (!found || tier.ThresholdCoinSpent > matched.ThresholdCoinSpent) { + matched = tier + found = true + } + } + return matched, found +} + +// WeekWindow 返回给定时间所在 UTC 周一 00:00 到下一周一 00:00 的左闭右开窗口。 +func WeekWindow(now time.Time) (time.Time, time.Time) { + utc := now.UTC() + dayStart := time.Date(utc.Year(), utc.Month(), utc.Day(), 0, 0, 0, 0, time.UTC) + weekday := int(dayStart.Weekday()) + if weekday == 0 { + weekday = 7 + } + start := dayStart.AddDate(0, 0, 1-weekday) + return start, start.AddDate(0, 0, 7) +} + +// PreviousWeekWindow 返回当前时间之前的完整 UTC 周期。 +func PreviousWeekWindow(now time.Time) (time.Time, time.Time) { + start, _ := WeekWindow(now) + previousStart := start.AddDate(0, 0, -7) + return previousStart, start +} diff --git a/services/activity-service/internal/service/roomturnoverreward/service_test.go b/services/activity-service/internal/service/roomturnoverreward/service_test.go new file mode 100644 index 00000000..2858fe66 --- /dev/null +++ b/services/activity-service/internal/service/roomturnoverreward/service_test.go @@ -0,0 +1,219 @@ +package roomturnoverreward + +import ( + "context" + "testing" + "time" + + "google.golang.org/grpc" + roomv1 "hyapp.local/api/proto/room/v1" + walletv1 "hyapp.local/api/proto/wallet/v1" + domain "hyapp/services/activity-service/internal/domain/roomturnoverreward" +) + +func TestWeekWindowUsesUTCMondayBoundary(t *testing.T) { + cases := []struct { + name string + now string + wantStart string + wantEnd string + }{ + { + name: "monday zero belongs to new week", + now: "2026-06-01T00:00:00Z", + wantStart: "2026-06-01T00:00:00Z", + wantEnd: "2026-06-08T00:00:00Z", + }, + { + name: "sunday belongs to current week", + now: "2026-06-07T23:59:59Z", + wantStart: "2026-06-01T00:00:00Z", + wantEnd: "2026-06-08T00:00:00Z", + }, + { + name: "cross month keeps utc week", + now: "2026-07-01T12:30:00Z", + wantStart: "2026-06-29T00:00:00Z", + wantEnd: "2026-07-06T00:00:00Z", + }, + { + name: "cross year keeps utc week", + now: "2027-01-01T08:00:00Z", + wantStart: "2026-12-28T00:00:00Z", + wantEnd: "2027-01-04T00:00:00Z", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + now := mustParseTime(t, tc.now) + start, end := WeekWindow(now) + if got := start.Format(time.RFC3339); got != tc.wantStart { + t.Fatalf("unexpected start: got %s want %s", got, tc.wantStart) + } + if got := end.Format(time.RFC3339); got != tc.wantEnd { + t.Fatalf("unexpected end: got %s want %s", got, tc.wantEnd) + } + }) + } +} + +func TestPreviousWeekWindowReturnsCompletedUTCWeek(t *testing.T) { + start, end := PreviousWeekWindow(mustParseTime(t, "2026-06-08T00:00:00Z")) + if got := start.Format(time.RFC3339); got != "2026-06-01T00:00:00Z" { + t.Fatalf("unexpected previous start: %s", got) + } + if got := end.Format(time.RFC3339); got != "2026-06-08T00:00:00Z" { + t.Fatalf("unexpected previous end: %s", got) + } +} + +func TestMatchHighestTierReturnsHighestThreshold(t *testing.T) { + tiers := []domain.Tier{ + {TierID: 1, TierCode: "bronze", ThresholdCoinSpent: 100, RewardCoinAmount: 10, Status: domain.TierStatusActive}, + {TierID: 2, TierCode: "silver", ThresholdCoinSpent: 500, RewardCoinAmount: 60, Status: domain.TierStatusActive}, + {TierID: 3, TierCode: "gold", ThresholdCoinSpent: 1000, RewardCoinAmount: 160, Status: domain.TierStatusActive}, + {TierID: 4, TierCode: "hidden", ThresholdCoinSpent: 700, RewardCoinAmount: 90, Status: domain.TierStatusInactive}, + } + + tier, ok := MatchHighestTier(tiers, 850) + if !ok { + t.Fatal("expected a matched tier") + } + if tier.TierCode != "silver" { + t.Fatalf("unexpected tier: got %s want silver", tier.TierCode) + } +} + +func TestRetrySettlementRejectsTierNotMatchedWithoutGrantSideEffects(t *testing.T) { + repo := &retryBoundaryRepository{ + settlement: domain.Settlement{ + SettlementID: "settlement-tier-not-matched", + RoomID: "room-1", + Status: domain.SettlementStatusFailed, + FailureReason: "tier_not_matched", + WalletCommandID: "room_turnover_reward:lalu:1:room-1", + RewardCoinAmount: 0, + }, + } + room := &retryBoundaryRoom{ownerID: 10001} + wallet := &retryBoundaryWallet{} + svc := New(repo, wallet, room) + + got, err := svc.RetrySettlement(context.Background(), repo.settlement.SettlementID) + if err == nil { + t.Fatal("expected retry to reject tier_not_matched settlement") + } + if got.Status != domain.SettlementStatusFailed || got.FailureReason != "tier_not_matched" { + t.Fatalf("retry should return original failed settlement, got %+v", got) + } + if repo.resetCalls != 0 || repo.grantCalls != 0 || repo.failCalls != 0 { + t.Fatalf("tier_not_matched retry must not mutate settlement: reset=%d grant=%d fail=%d", repo.resetCalls, repo.grantCalls, repo.failCalls) + } + if room.calls != 0 { + t.Fatalf("tier_not_matched retry must not fetch room owner, calls=%d", room.calls) + } + if wallet.calls != 0 { + t.Fatalf("tier_not_matched retry must not call wallet, calls=%d", wallet.calls) + } +} + +func mustParseTime(t *testing.T, value string) time.Time { + t.Helper() + parsed, err := time.Parse(time.RFC3339, value) + if err != nil { + t.Fatalf("parse time %q: %v", value, err) + } + return parsed +} + +type retryBoundaryRepository struct { + settlement domain.Settlement + resetCalls int + grantCalls int + failCalls int +} + +func (r *retryBoundaryRepository) GetRoomTurnoverRewardConfig(context.Context) (domain.Config, bool, error) { + panic("unexpected GetRoomTurnoverRewardConfig") +} + +func (r *retryBoundaryRepository) UpdateRoomTurnoverRewardConfig(context.Context, domain.Config, int64) (domain.Config, error) { + panic("unexpected UpdateRoomTurnoverRewardConfig") +} + +func (r *retryBoundaryRepository) ConsumeRoomTurnoverGiftEvent(context.Context, domain.RoomGiftEvent, int64, int64, int64) (domain.EventResult, error) { + panic("unexpected ConsumeRoomTurnoverGiftEvent") +} + +func (r *retryBoundaryRepository) GetRoomTurnoverRewardProgress(context.Context, string, int64) (domain.Progress, bool, error) { + panic("unexpected GetRoomTurnoverRewardProgress") +} + +func (r *retryBoundaryRepository) GetLatestRoomTurnoverRewardSettlement(context.Context, string) (domain.Settlement, bool, error) { + panic("unexpected GetLatestRoomTurnoverRewardSettlement") +} + +func (r *retryBoundaryRepository) ListRoomTurnoverRewardSettlements(context.Context, domain.SettlementQuery) ([]domain.Settlement, int64, error) { + panic("unexpected ListRoomTurnoverRewardSettlements") +} + +func (r *retryBoundaryRepository) ListUnsettledRoomTurnoverRewardProgress(context.Context, int64, int) ([]domain.Progress, error) { + panic("unexpected ListUnsettledRoomTurnoverRewardProgress") +} + +func (r *retryBoundaryRepository) InsertRoomTurnoverRewardSettlement(context.Context, domain.Settlement, int64) (domain.Settlement, bool, error) { + panic("unexpected InsertRoomTurnoverRewardSettlement") +} + +func (r *retryBoundaryRepository) ListPendingRoomTurnoverRewardSettlements(context.Context, int) ([]domain.Settlement, error) { + panic("unexpected ListPendingRoomTurnoverRewardSettlements") +} + +func (r *retryBoundaryRepository) GetRoomTurnoverRewardSettlement(_ context.Context, settlementID string) (domain.Settlement, bool, error) { + if settlementID != r.settlement.SettlementID { + return domain.Settlement{}, false, nil + } + return r.settlement, true, nil +} + +func (r *retryBoundaryRepository) ResetRoomTurnoverRewardSettlementForRetry(_ context.Context, _ string, ownerUserID int64, _ int64) (domain.Settlement, error) { + r.resetCalls++ + r.settlement.Status = domain.SettlementStatusPending + r.settlement.OwnerUserID = ownerUserID + r.settlement.FailureReason = "" + return r.settlement, nil +} + +func (r *retryBoundaryRepository) MarkRoomTurnoverRewardSettlementGranted(_ context.Context, _ string, walletTransactionID string, _ int64) (domain.Settlement, error) { + r.grantCalls++ + r.settlement.Status = domain.SettlementStatusGranted + r.settlement.WalletTransactionID = walletTransactionID + return r.settlement, nil +} + +func (r *retryBoundaryRepository) MarkRoomTurnoverRewardSettlementFailed(_ context.Context, _ string, reason string, _ int64) error { + r.failCalls++ + r.settlement.Status = domain.SettlementStatusFailed + r.settlement.FailureReason = reason + return nil +} + +type retryBoundaryRoom struct { + ownerID int64 + calls int +} + +func (r *retryBoundaryRoom) AdminGetRoom(context.Context, *roomv1.AdminGetRoomRequest, ...grpc.CallOption) (*roomv1.AdminGetRoomResponse, error) { + r.calls++ + return &roomv1.AdminGetRoomResponse{Room: &roomv1.AdminRoomListItem{OwnerUserId: r.ownerID}}, nil +} + +type retryBoundaryWallet struct { + calls int +} + +func (w *retryBoundaryWallet) CreditRoomTurnoverReward(context.Context, *walletv1.CreditRoomTurnoverRewardRequest, ...grpc.CallOption) (*walletv1.CreditRoomTurnoverRewardResponse, error) { + w.calls++ + return &walletv1.CreditRoomTurnoverRewardResponse{TransactionId: "wallet-tx-1", GrantedAtMs: 1234}, nil +} diff --git a/services/activity-service/internal/service/weeklystar/service.go b/services/activity-service/internal/service/weeklystar/service.go new file mode 100644 index 00000000..4904ac66 --- /dev/null +++ b/services/activity-service/internal/service/weeklystar/service.go @@ -0,0 +1,359 @@ +package weeklystar + +import ( + "context" + "log/slog" + "sort" + "strings" + "time" + + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" + roomeventsv1 "hyapp.local/api/proto/events/room/v1" + walletv1 "hyapp.local/api/proto/wallet/v1" + "hyapp/pkg/appcode" + "hyapp/pkg/logx" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/weeklystar" +) + +const weeklyStarRewardReason = "weekly star reward" + +// Repository is the storage boundary for weekly-star configuration, score and settlement facts. +type Repository interface { + ListWeeklyStarCycles(ctx context.Context, query domain.ListQuery) ([]domain.Cycle, int64, error) + GetWeeklyStarCycle(ctx context.Context, cycleID string) (domain.Cycle, error) + UpsertWeeklyStarCycle(ctx context.Context, command domain.CycleCommand, nowMS int64) (domain.Cycle, bool, error) + SetWeeklyStarCycleStatus(ctx context.Context, cycleID string, status string, operatorAdminID int64, nowMS int64) (domain.Cycle, error) + FindCurrentWeeklyStarCycle(ctx context.Context, regionID int64, nowMS int64) (domain.Cycle, bool, error) + ConsumeWeeklyStarGiftEvent(ctx context.Context, event domain.GiftEvent, nowMS int64) (domain.EventResult, error) + ListWeeklyStarLeaderboard(ctx context.Context, cycleID string, regionID int64, pageSize int32, pageToken string, nowMS int64) (domain.Cycle, []domain.LeaderboardEntry, string, int64, error) + GetWeeklyStarUserEntry(ctx context.Context, cycleID string, userID int64) (domain.LeaderboardEntry, bool, error) + ListWeeklyStarHistory(ctx context.Context, regionID int64, limit int32, nowMS int64) ([]domain.HistoryCycle, error) + ListWeeklyStarSettlements(ctx context.Context, cycleID string) ([]domain.Settlement, error) + ClaimDueWeeklyStarCycles(ctx context.Context, workerID string, nowMS int64, lockTTL time.Duration, batchSize int32) ([]domain.Cycle, error) + PrepareWeeklyStarSettlements(ctx context.Context, cycleID string, nowMS int64) ([]domain.Settlement, error) + MarkWeeklyStarSettlementGranted(ctx context.Context, settlementID string, walletGrantID string, nowMS int64) error + MarkWeeklyStarSettlementFailed(ctx context.Context, settlementID string, reason string, nowMS int64) error + FinishWeeklyStarCycleIfComplete(ctx context.Context, cycleID string, nowMS int64) (bool, error) +} + +// WalletClient is the small wallet-service surface needed by weekly-star settlement. +type WalletClient interface { + GrantResourceGroup(ctx context.Context, req *walletv1.GrantResourceGroupRequest, opts ...grpc.CallOption) (*walletv1.ResourceGrantResponse, error) +} + +// Service owns weekly-star business rules; transports and cron only adapt inputs into this API. +type Service struct { + repository Repository + wallet WalletClient + now func() time.Time +} + +// New creates the weekly-star service with activity-service storage as the source of truth. +func New(repository Repository, wallet WalletClient) *Service { + return &Service{ + repository: repository, + wallet: wallet, + now: time.Now, + } +} + +// ListCycles returns configured cycles for admin pages without leaking storage paging rules to transport. +func (s *Service) ListCycles(ctx context.Context, query domain.ListQuery) ([]domain.Cycle, int64, error) { + if err := s.requireRepository(); err != nil { + return nil, 0, err + } + query.Status = normalizeStatus(query.Status) + return s.repository.ListWeeklyStarCycles(ctx, query) +} + +// GetCycle returns a single admin cycle, including the three gifts and Top rewards. +func (s *Service) GetCycle(ctx context.Context, cycleID string) (domain.Cycle, error) { + if err := s.requireRepository(); err != nil { + return domain.Cycle{}, err + } + cycleID = strings.TrimSpace(cycleID) + if cycleID == "" { + return domain.Cycle{}, xerr.New(xerr.InvalidArgument, "cycle_id is required") + } + return s.repository.GetWeeklyStarCycle(ctx, cycleID) +} + +// CreateCycle validates the full weekly-star contract and stores a new UTC cycle. +func (s *Service) CreateCycle(ctx context.Context, command domain.CycleCommand) (domain.Cycle, bool, error) { + command.RequireNewRecord = true + return s.upsertCycle(ctx, command) +} + +// UpdateCycle replaces an existing weekly-star cycle after the same create-time validations pass. +func (s *Service) UpdateCycle(ctx context.Context, command domain.CycleCommand) (domain.Cycle, bool, error) { + command.RequireNewRecord = false + return s.upsertCycle(ctx, command) +} + +// SetCycleStatus switches admin-visible status; active cycles are the only ones matched by gift events. +func (s *Service) SetCycleStatus(ctx context.Context, cycleID string, status string, operatorAdminID int64) (domain.Cycle, error) { + if err := s.requireRepository(); err != nil { + return domain.Cycle{}, err + } + status = normalizeStatus(status) + if !validStatus(status) || status == domain.StatusSettling { + return domain.Cycle{}, xerr.New(xerr.InvalidArgument, "status is invalid") + } + if operatorAdminID <= 0 { + return domain.Cycle{}, xerr.New(xerr.InvalidArgument, "operator_admin_id is required") + } + return s.repository.SetWeeklyStarCycleStatus(ctx, strings.TrimSpace(cycleID), status, operatorAdminID, s.now().UnixMilli()) +} + +// GetCurrent resolves the current cycle by region, using the storage-level concrete-region-first/default fallback. +func (s *Service) GetCurrent(ctx context.Context, userID int64, regionID int64) (domain.Cycle, []domain.LeaderboardEntry, domain.LeaderboardEntry, bool, error) { + if err := s.requireRepository(); err != nil { + return domain.Cycle{}, nil, domain.LeaderboardEntry{}, false, err + } + cycle, ok, err := s.repository.FindCurrentWeeklyStarCycle(ctx, regionID, s.now().UnixMilli()) + if err != nil || !ok { + return cycle, nil, domain.LeaderboardEntry{}, false, err + } + _, top, _, _, err := s.repository.ListWeeklyStarLeaderboard(ctx, cycle.CycleID, regionID, 50, "", s.now().UnixMilli()) + if err != nil { + return domain.Cycle{}, nil, domain.LeaderboardEntry{}, false, err + } + myEntry, found, err := s.repository.GetWeeklyStarUserEntry(ctx, cycle.CycleID, userID) + return cycle, top, myEntry, found, err +} + +// ListLeaderboard returns deterministic score order for either a concrete cycle or current region cycle. +func (s *Service) ListLeaderboard(ctx context.Context, cycleID string, regionID int64, pageSize int32, pageToken string) (domain.Cycle, []domain.LeaderboardEntry, string, int64, error) { + if err := s.requireRepository(); err != nil { + return domain.Cycle{}, nil, "", 0, err + } + return s.repository.ListWeeklyStarLeaderboard(ctx, strings.TrimSpace(cycleID), regionID, pageSize, strings.TrimSpace(pageToken), s.now().UnixMilli()) +} + +// ListHistory returns settled cycles for the user region with default-cycle fallback handled by storage. +func (s *Service) ListHistory(ctx context.Context, regionID int64, limit int32) ([]domain.HistoryCycle, error) { + if err := s.requireRepository(); err != nil { + return nil, err + } + return s.repository.ListWeeklyStarHistory(ctx, regionID, limit, s.now().UnixMilli()) +} + +// ListSettlements returns admin-visible settlement grant facts for one cycle. +func (s *Service) ListSettlements(ctx context.Context, cycleID string) ([]domain.Settlement, error) { + if err := s.requireRepository(); err != nil { + return nil, err + } + cycleID = strings.TrimSpace(cycleID) + if cycleID == "" { + return nil, xerr.New(xerr.InvalidArgument, "cycle_id is required") + } + return s.repository.ListWeeklyStarSettlements(ctx, cycleID) +} + +// HandleRoomEvent consumes room-service committed gift facts; score always equals the actual coin_spent. +// 这里不读取 gift 配置价格,也不相信客户端传入积分,因为钱包扣费后的 room outbox 才是送礼成功事实。 +// 非 RoomGiftSent、无用户、无礼物或无扣费金额的 envelope 直接跳过,避免让普通房间事件污染活动积分表。 +func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) (int32, error) { + if s == nil || s.repository == nil || envelope == nil || envelope.GetEventType() != "RoomGiftSent" { + return 0, nil + } + var gift roomeventsv1.RoomGiftSent + if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil { + return 0, err + } + if gift.GetSenderUserId() <= 0 || gift.GetGiftId() == "" || gift.GetCoinSpent() <= 0 { + return 0, nil + } + // region_id 和 occurred_at_ms 都来自 room-service 的稳定事实;repository 会按事件发生时间匹配 [start_ms,end_ms) 周期。 + // source_event_id 是幂等键,同一个 room outbox 重试时只会插入一次 score event,并且不会重复累加榜单分数。 + event := domain.GiftEvent{ + EventID: strings.TrimSpace(envelope.GetEventId()), + UserID: gift.GetSenderUserId(), + GiftID: strings.TrimSpace(gift.GetGiftId()), + ScoreDelta: gift.GetCoinSpent(), + RegionID: gift.GetVisibleRegionId(), + OccurredAtMS: envelope.GetOccurredAtMs(), + } + result, err := s.repository.ConsumeWeeklyStarGiftEvent(appcode.WithContext(ctx, envelope.GetAppCode()), event, s.now().UnixMilli()) + if err != nil { + return 0, err + } + if result.Status == domain.EventStatusConsumed { + return 1, nil + } + return 0, nil +} + +// ProcessSettlementBatch locks due cycles, materializes Top1/Top2/Top3, and grants each resource group idempotently. +// 状态机是 active -> settling -> settled:先锁到期周期,再把榜单快照固化为 settlement,最后逐条调用 wallet 发资源组。 +// wallet_command_id 使用 cycle/rank/user 组成,cron 重试同一 settlement 时只会命中 wallet 幂等,不会重复发奖励。 +func (s *Service) ProcessSettlementBatch(ctx context.Context, runID string, workerID string, batchSize int32, lockTTL time.Duration) (claimed int32, processed int32, success int32, failure int32, hasMore bool, err error) { + if err := s.requireRepository(); err != nil { + return 0, 0, 0, 0, false, err + } + if s.wallet == nil { + return 0, 0, 0, 0, false, xerr.New(xerr.Unavailable, "wallet client is not configured") + } + if batchSize <= 0 { + batchSize = 50 + } + if lockTTL <= 0 { + lockTTL = 30 * time.Second + } + if strings.TrimSpace(workerID) == "" { + workerID = "activity-weekly-star" + } + cycles, err := s.repository.ClaimDueWeeklyStarCycles(ctx, workerID, s.now().UnixMilli(), lockTTL, batchSize) + if err != nil { + return 0, 0, 0, 0, false, err + } + for _, cycle := range cycles { + settlements, prepareErr := s.repository.PrepareWeeklyStarSettlements(ctx, cycle.CycleID, s.now().UnixMilli()) + if prepareErr != nil { + logx.Error(ctx, "weekly_star_prepare_settlements_failed", prepareErr, slog.String("cycle_id", cycle.CycleID), slog.String("run_id", runID), slog.String("worker_id", workerID)) + failure++ + continue + } + if len(settlements) == 0 { + // 没有人得分的周期不会生成 settlement;只要没有 pending 记录,就可以安全标记为 settled。 + if _, finishErr := s.repository.FinishWeeklyStarCycleIfComplete(ctx, cycle.CycleID, s.now().UnixMilli()); finishErr != nil { + failure++ + continue + } + success++ + processed++ + continue + } + for _, settlement := range settlements { + processed++ + // activity-service 只决定获奖人和资源组,真实发放由 wallet-service 原子展开资源组并写 grant/账务流水。 + // 如果 wallet 临时失败,settlement 会退回 failed 状态,下一轮 cron 继续用同一个 wallet_command_id 重试。 + resp, grantErr := s.wallet.GrantResourceGroup(ctx, &walletv1.GrantResourceGroupRequest{ + CommandId: settlement.WalletCommandID, + AppCode: appcode.FromContext(ctx), + TargetUserId: settlement.UserID, + GroupId: settlement.ResourceGroupID, + Reason: weeklyStarRewardReason, + OperatorUserId: settlement.UserID, + GrantSource: domain.GrantSourceWeeklyStar, + }) + if grantErr != nil { + logx.Error(ctx, "weekly_star_grant_resource_group_failed", grantErr, slog.String("cycle_id", cycle.CycleID), slog.String("settlement_id", settlement.SettlementID), slog.String("wallet_command_id", settlement.WalletCommandID)) + failure++ + _ = s.repository.MarkWeeklyStarSettlementFailed(ctx, settlement.SettlementID, xerr.MessageOf(grantErr), s.now().UnixMilli()) + continue + } + walletGrantID := "" + if resp.GetGrant() != nil { + walletGrantID = resp.GetGrant().GetGrantId() + } + if markErr := s.repository.MarkWeeklyStarSettlementGranted(ctx, settlement.SettlementID, walletGrantID, s.now().UnixMilli()); markErr != nil { + logx.Error(ctx, "weekly_star_mark_settlement_granted_failed", markErr, slog.String("cycle_id", cycle.CycleID), slog.String("settlement_id", settlement.SettlementID), slog.String("wallet_grant_id", walletGrantID)) + failure++ + continue + } + success++ + } + if _, finishErr := s.repository.FinishWeeklyStarCycleIfComplete(ctx, cycle.CycleID, s.now().UnixMilli()); finishErr != nil { + // Finish 会再次检查所有 settlement 是否 granted,防止部分奖励失败时把周期提前关掉。 + failure++ + } + } + return int32(len(cycles)), processed, success, failure, len(cycles) == int(batchSize), nil +} + +func (s *Service) upsertCycle(ctx context.Context, command domain.CycleCommand) (domain.Cycle, bool, error) { + if err := s.requireRepository(); err != nil { + return domain.Cycle{}, false, err + } + command.Cycle = normalizeCycle(command.Cycle) + command.OperatorAdminID = command.OperatorAdminID + if command.OperatorAdminID <= 0 { + return domain.Cycle{}, false, xerr.New(xerr.InvalidArgument, "operator_admin_id is required") + } + if err := validateCycle(command.Cycle); err != nil { + return domain.Cycle{}, false, err + } + return s.repository.UpsertWeeklyStarCycle(ctx, command, s.now().UnixMilli()) +} + +func (s *Service) requireRepository() error { + if s == nil || s.repository == nil { + return xerr.New(xerr.Unavailable, "weekly star repository is not configured") + } + return nil +} + +func normalizeCycle(cycle domain.Cycle) domain.Cycle { + cycle.ActivityCode = domain.ActivityCode + cycle.CycleID = strings.TrimSpace(cycle.CycleID) + cycle.Title = strings.TrimSpace(cycle.Title) + cycle.Status = normalizeStatus(cycle.Status) + if cycle.Status == "" { + cycle.Status = domain.StatusDraft + } + seenGifts := map[string]bool{} + gifts := make([]domain.Gift, 0, len(cycle.Gifts)) + for _, gift := range cycle.Gifts { + gift.GiftID = strings.TrimSpace(gift.GiftID) + if gift.GiftID == "" || seenGifts[gift.GiftID] { + continue + } + seenGifts[gift.GiftID] = true + gifts = append(gifts, gift) + } + sort.SliceStable(gifts, func(i, j int) bool { return gifts[i].SortOrder < gifts[j].SortOrder }) + for index := range gifts { + if gifts[index].SortOrder <= 0 { + gifts[index].SortOrder = int32(index + 1) + } + } + cycle.Gifts = gifts + sort.SliceStable(cycle.Rewards, func(i, j int) bool { return cycle.Rewards[i].RankNo < cycle.Rewards[j].RankNo }) + return cycle +} + +func validateCycle(cycle domain.Cycle) error { + if cycle.Title == "" { + return xerr.New(xerr.InvalidArgument, "title is required") + } + if cycle.RegionID < 0 { + return xerr.New(xerr.InvalidArgument, "region_id is invalid") + } + if cycle.StartMS <= 0 || cycle.EndMS <= cycle.StartMS { + return xerr.New(xerr.InvalidArgument, "start_ms and end_ms are invalid") + } + if !validStatus(cycle.Status) || cycle.Status == domain.StatusSettling || cycle.Status == domain.StatusSettled { + return xerr.New(xerr.InvalidArgument, "status is invalid") + } + if len(cycle.Gifts) != 3 { + return xerr.New(xerr.InvalidArgument, "weekly star requires exactly 3 gifts") + } + rankToGroup := map[int32]int64{} + for _, reward := range cycle.Rewards { + if reward.RankNo >= 1 && reward.RankNo <= 3 && reward.ResourceGroupID > 0 { + rankToGroup[reward.RankNo] = reward.ResourceGroupID + } + } + for rank := int32(1); rank <= 3; rank++ { + if rankToGroup[rank] <= 0 { + return xerr.New(xerr.InvalidArgument, "top1/top2/top3 resource groups are required") + } + } + return nil +} + +func normalizeStatus(value string) string { + return strings.ToLower(strings.TrimSpace(value)) +} + +func validStatus(value string) bool { + switch value { + case domain.StatusDraft, domain.StatusActive, domain.StatusDisabled, domain.StatusSettling, domain.StatusSettled: + return true + default: + return false + } +} diff --git a/services/activity-service/internal/storage/mysql/cumulative_recharge_reward_migration.go b/services/activity-service/internal/storage/mysql/cumulative_recharge_reward_migration.go new file mode 100644 index 00000000..80b507b3 --- /dev/null +++ b/services/activity-service/internal/storage/mysql/cumulative_recharge_reward_migration.go @@ -0,0 +1,96 @@ +package mysql + +import "context" + +func (r *Repository) ensureCumulativeRechargeRewardTables(ctx context.Context) error { + statements := []string{ + `CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_configs ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + enabled BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否启用累充奖励', + updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最近更新管理员', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励配置'`, + `CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_tiers ( + tier_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '档位自增 ID', + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + tier_code VARCHAR(64) NOT NULL COMMENT '档位编码', + tier_name VARCHAR(128) NOT NULL COMMENT '档位展示名称', + threshold_usd_minor BIGINT NOT NULL COMMENT '达标美元金额,美分', + resource_group_id BIGINT NOT NULL COMMENT '达标后发放资源组 ID', + status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT 'active/inactive', + sort_order INT NOT NULL DEFAULT 0 COMMENT '展示排序', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (tier_id), + UNIQUE KEY uk_cumulative_recharge_reward_tier_code (app_code, tier_code), + KEY idx_cumulative_recharge_reward_tier_sort (app_code, status, sort_order, threshold_usd_minor) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励档位'`, + `CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_events ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + event_id VARCHAR(128) NOT NULL COMMENT 'wallet_outbox event_id', + cycle_key VARCHAR(16) NOT NULL COMMENT 'UTC 自然周,如 2026-W23', + user_id BIGINT NOT NULL COMMENT '充值用户', + transaction_id VARCHAR(128) NOT NULL COMMENT '钱包交易 ID', + command_id VARCHAR(128) NOT NULL COMMENT '钱包命令 ID', + recharge_type VARCHAR(64) NOT NULL DEFAULT '' COMMENT '充值来源', + recharge_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '本次到账金币', + qualifying_usd_minor BIGINT NOT NULL DEFAULT 0 COMMENT '本次计入累充的美元美分', + payload_json JSON NULL COMMENT '原 wallet payload', + occurred_at_ms BIGINT NOT NULL COMMENT '充值发生时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + PRIMARY KEY (app_code, event_id), + KEY idx_cumulative_recharge_reward_event_user_cycle (app_code, cycle_key, user_id, occurred_at_ms), + KEY idx_cumulative_recharge_reward_event_transaction (app_code, transaction_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励充值事件幂等表'`, + `CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_progress ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_key VARCHAR(16) NOT NULL COMMENT 'UTC 自然周,如 2026-W23', + user_id BIGINT NOT NULL COMMENT '用户 ID', + total_usd_minor BIGINT NOT NULL DEFAULT 0 COMMENT '周期累计美元美分', + total_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '周期累计到账金币', + first_recharged_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '周期首笔充值时间', + last_recharged_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '周期最近充值时间', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_key, user_id), + KEY idx_cumulative_recharge_reward_progress_total (app_code, cycle_key, total_usd_minor) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励用户周期累计'`, + `CREATE TABLE IF NOT EXISTS cumulative_recharge_reward_grants ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + grant_id VARCHAR(128) NOT NULL COMMENT '累充奖励发放 ID', + cycle_key VARCHAR(16) NOT NULL COMMENT 'UTC 自然周,如 2026-W23', + user_id BIGINT NOT NULL COMMENT '用户 ID', + event_id VARCHAR(128) NOT NULL COMMENT '触发发放的 wallet event_id', + transaction_id VARCHAR(128) NOT NULL COMMENT '触发发放的钱包交易 ID', + command_id VARCHAR(128) NOT NULL COMMENT '触发发放的钱包命令 ID', + tier_id BIGINT NOT NULL COMMENT '命中档位 ID', + tier_code VARCHAR(64) NOT NULL COMMENT '命中档位编码', + threshold_usd_minor BIGINT NOT NULL COMMENT '命中档位门槛,美分', + resource_group_id BIGINT NOT NULL COMMENT '发放资源组 ID', + reached_usd_minor BIGINT NOT NULL COMMENT '命中时周期累计金额,美分', + qualifying_usd_minor BIGINT NOT NULL DEFAULT 0 COMMENT '触发事件计入金额,美分', + recharge_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '触发事件到账金币', + recharge_type VARCHAR(64) NOT NULL DEFAULT '' COMMENT '触发事件充值来源', + status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/granted/failed', + wallet_command_id VARCHAR(128) NOT NULL COMMENT 'wallet 资源组发放命令', + wallet_grant_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'wallet 发放回执', + failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因', + granted_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '发放成功时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, grant_id), + UNIQUE KEY uk_cumulative_recharge_reward_user_tier (app_code, cycle_key, user_id, tier_id), + UNIQUE KEY uk_cumulative_recharge_reward_wallet_command (app_code, wallet_command_id), + KEY idx_cumulative_recharge_reward_grant_event (app_code, event_id, status), + KEY idx_cumulative_recharge_reward_grant_user (app_code, cycle_key, user_id, created_at_ms), + KEY idx_cumulative_recharge_reward_grant_status (app_code, status, created_at_ms) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='累充奖励资源组发放记录'`, + } + for _, statement := range statements { + if _, err := r.db.ExecContext(ctx, statement); err != nil { + return err + } + } + return nil +} diff --git a/services/activity-service/internal/storage/mysql/cumulative_recharge_reward_repository.go b/services/activity-service/internal/storage/mysql/cumulative_recharge_reward_repository.go new file mode 100644 index 00000000..caf6f9ec --- /dev/null +++ b/services/activity-service/internal/storage/mysql/cumulative_recharge_reward_repository.go @@ -0,0 +1,696 @@ +package mysql + +import ( + "context" + "database/sql" + "errors" + "fmt" + "strings" + + "hyapp/pkg/appcode" + "hyapp/pkg/idgen" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/cumulativerecharge" +) + +// GetCumulativeRechargeRewardConfig 读取累充奖励当前配置;未配置时后台可展示空草稿。 +func (r *Repository) GetCumulativeRechargeRewardConfig(ctx context.Context) (domain.Config, bool, error) { + if r == nil || r.db == nil { + return domain.Config{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + row := r.db.QueryRowContext(ctx, cumulativeRechargeRewardConfigSelectSQL()+` + WHERE app_code = ?`, + appcode.FromContext(ctx), + ) + config, err := scanCumulativeRechargeRewardConfig(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Config{}, false, nil + } + if err != nil { + return domain.Config{}, false, err + } + tiers, err := r.listCumulativeRechargeRewardTiers(ctx, nil) + if err != nil { + return domain.Config{}, false, err + } + config.Tiers = tiers + return config, true, nil +} + +// UpdateCumulativeRechargeRewardConfig 原子替换配置档位;已创建的 grant 保留命中时的档位快照。 +func (r *Repository) UpdateCumulativeRechargeRewardConfig(ctx context.Context, config domain.Config, nowMS int64) (domain.Config, error) { + if r == nil || r.db == nil { + return domain.Config{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return domain.Config{}, err + } + defer func() { _ = tx.Rollback() }() + + _, err = tx.ExecContext(ctx, ` + INSERT INTO cumulative_recharge_reward_configs ( + app_code, enabled, updated_by_admin_id, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + enabled = VALUES(enabled), + updated_by_admin_id = VALUES(updated_by_admin_id), + updated_at_ms = VALUES(updated_at_ms)`, + appcode.FromContext(ctx), config.Enabled, config.UpdatedByAdminID, nowMS, nowMS, + ) + if err != nil { + return domain.Config{}, err + } + // 配置保存采用整组替换,避免后台删除/重排档位时保留脏行;历史 grant 已保存 tier 快照,不依赖当前档位表。 + if _, err := tx.ExecContext(ctx, ` + DELETE FROM cumulative_recharge_reward_tiers + WHERE app_code = ?`, + appcode.FromContext(ctx), + ); err != nil { + return domain.Config{}, err + } + for i := range config.Tiers { + tier := config.Tiers[i] + tier.CreatedAtMS = nowMS + tier.UpdatedAtMS = nowMS + if tier.TierID > 0 { + if _, err := tx.ExecContext(ctx, ` + INSERT INTO cumulative_recharge_reward_tiers ( + tier_id, app_code, tier_code, tier_name, threshold_usd_minor, + resource_group_id, status, sort_order, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + tier.TierID, appcode.FromContext(ctx), tier.TierCode, tier.TierName, tier.ThresholdUSDMinor, + tier.ResourceGroupID, tier.Status, tier.SortOrder, tier.CreatedAtMS, tier.UpdatedAtMS, + ); err != nil { + return domain.Config{}, err + } + continue + } + result, err := tx.ExecContext(ctx, ` + INSERT INTO cumulative_recharge_reward_tiers ( + app_code, tier_code, tier_name, threshold_usd_minor, + resource_group_id, status, sort_order, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + appcode.FromContext(ctx), tier.TierCode, tier.TierName, tier.ThresholdUSDMinor, + tier.ResourceGroupID, tier.Status, tier.SortOrder, tier.CreatedAtMS, tier.UpdatedAtMS, + ) + if err != nil { + return domain.Config{}, err + } + tierID, err := result.LastInsertId() + if err != nil { + return domain.Config{}, err + } + config.Tiers[i].TierID = tierID + } + if err := tx.Commit(); err != nil { + return domain.Config{}, err + } + updated, exists, err := r.GetCumulativeRechargeRewardConfig(ctx) + if err != nil { + return domain.Config{}, err + } + if !exists { + return domain.Config{}, xerr.New(xerr.NotFound, "cumulative recharge reward config not found") + } + return updated, nil +} + +// GetCumulativeRechargeRewardProgress 读取用户当前周期累计;缺失代表本周还没有符合条件的充值。 +func (r *Repository) GetCumulativeRechargeRewardProgress(ctx context.Context, cycleKey string, userID int64) (domain.Progress, bool, error) { + if r == nil || r.db == nil { + return domain.Progress{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + row := r.db.QueryRowContext(ctx, cumulativeRechargeRewardProgressSelectSQL()+` + WHERE app_code = ? AND cycle_key = ? AND user_id = ?`, + appcode.FromContext(ctx), cycleKey, userID, + ) + progress, err := scanCumulativeRechargeRewardProgress(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Progress{}, false, nil + } + return progress, err == nil, err +} + +// ListCumulativeRechargeRewardGrantsByUserCycle 返回 H5 当前周期需要展示的用户发放状态。 +func (r *Repository) ListCumulativeRechargeRewardGrantsByUserCycle(ctx context.Context, cycleKey string, userID int64) ([]domain.Grant, error) { + if r == nil || r.db == nil { + return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + rows, err := r.db.QueryContext(ctx, cumulativeRechargeRewardGrantSelectSQL()+` + WHERE app_code = ? AND cycle_key = ? AND user_id = ? + ORDER BY threshold_usd_minor ASC, created_at_ms ASC, grant_id ASC`, + appcode.FromContext(ctx), cycleKey, userID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + grants, _, err := scanCumulativeRechargeRewardGrants(rows, 0) + return grants, err +} + +// PrepareCumulativeRechargeRewardGrants 在 activity 事务内完成 event 幂等、周期累计和待发放 grant 创建。 +func (r *Repository) PrepareCumulativeRechargeRewardGrants(ctx context.Context, event domain.RechargeEvent, cycle domain.Cycle, nowMS int64) (domain.PrepareResult, error) { + if r == nil || r.db == nil { + return domain.PrepareResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return domain.PrepareResult{}, err + } + defer func() { _ = tx.Rollback() }() + + // event_id 是 wallet_outbox 事实源幂等键;重复 MQ 消息不能再次增加 progress。 + inserted, err := r.insertCumulativeRechargeRewardEvent(ctx, tx, event, cycle.Key, nowMS) + if err != nil { + return domain.PrepareResult{}, err + } + if !inserted { + // 重复 event 只恢复该 event 上还没成功的 grant,不重新计算累计,也不根据新配置补发历史档位。 + grants, err := r.pendingCumulativeRechargeRewardGrantsByEventForUpdate(ctx, tx, event.EventID, nowMS) + if err != nil { + return domain.PrepareResult{}, err + } + if err := tx.Commit(); err != nil { + return domain.PrepareResult{}, err + } + if len(grants) > 0 { + return domain.PrepareResult{Grants: grants, Consumed: true, Reason: domain.ReasonPendingReward}, nil + } + return domain.PrepareResult{Consumed: true, Reason: domain.ReasonAlreadyConsumed}, nil + } + + config, exists, err := r.getCumulativeRechargeRewardConfigForUpdate(ctx, tx) + if err != nil { + return domain.PrepareResult{}, err + } + if !exists { + if err := tx.Commit(); err != nil { + return domain.PrepareResult{}, err + } + // 未配置时只记录 event 幂等事实,保证之后同一 event 不会因为补配置而被误当成新充值。 + return domain.PrepareResult{Consumed: true, Reason: domain.ReasonNotConfigured}, nil + } + if !config.Enabled { + if err := tx.Commit(); err != nil { + return domain.PrepareResult{}, err + } + // 禁用期间的充值不会进入累计;这是配置只影响后续事件的明确边界。 + return domain.PrepareResult{Consumed: true, Reason: domain.ReasonDisabled}, nil + } + tiers, err := r.listCumulativeRechargeRewardTiersForUpdate(ctx, tx) + if err != nil { + return domain.PrepareResult{}, err + } + // 只有配置存在且启用后才累加 progress,避免后台开启活动时追溯禁用期事件。 + progress, err := r.upsertCumulativeRechargeRewardProgress(ctx, tx, event, cycle.Key, nowMS) + if err != nil { + return domain.PrepareResult{}, err + } + // 当前充值可能一次跨过多个档位,所有命中的 active 档位都会在同一事务里创建 pending grant。 + grants, err := r.prepareMatchedCumulativeRechargeRewardGrants(ctx, tx, event, progress, tiers, nowMS) + if err != nil { + return domain.PrepareResult{}, err + } + if err := tx.Commit(); err != nil { + return domain.PrepareResult{}, err + } + if len(grants) == 0 { + return domain.PrepareResult{Consumed: true, Reason: domain.ReasonNoTierMatched}, nil + } + return domain.PrepareResult{Grants: grants, Consumed: true, Reason: domain.ReasonEligible}, nil +} + +// MarkCumulativeRechargeRewardGrantGranted 写回 wallet-service 资源组发放回执。 +func (r *Repository) MarkCumulativeRechargeRewardGrantGranted(ctx context.Context, grantID string, walletGrantID string, grantedAtMS int64) (domain.Grant, error) { + if r == nil || r.db == nil { + return domain.Grant{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + _, err := r.db.ExecContext(ctx, ` + UPDATE cumulative_recharge_reward_grants + SET status = 'granted', + wallet_grant_id = ?, + failure_reason = '', + granted_at_ms = ?, + updated_at_ms = ? + WHERE app_code = ? AND grant_id = ? AND status <> 'granted'`, + walletGrantID, grantedAtMS, grantedAtMS, appcode.FromContext(ctx), grantID, + ) + if err != nil { + return domain.Grant{}, err + } + grant, exists, err := r.getCumulativeRechargeRewardGrantByID(ctx, grantID) + if err != nil { + return domain.Grant{}, err + } + if !exists { + return domain.Grant{}, xerr.New(xerr.NotFound, "cumulative recharge reward grant not found") + } + return grant, nil +} + +// MarkCumulativeRechargeRewardGrantFailed 保留 grant,后续同一 wallet event 重试会复用原钱包命令。 +func (r *Repository) MarkCumulativeRechargeRewardGrantFailed(ctx context.Context, grantID string, failureReason string, nowMS int64) error { + if r == nil || r.db == nil { + return xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + _, err := r.db.ExecContext(ctx, ` + UPDATE cumulative_recharge_reward_grants + SET status = 'failed', failure_reason = ?, updated_at_ms = ? + WHERE app_code = ? AND grant_id = ? AND status <> 'granted'`, + truncateTaskFailure(failureReason), nowMS, appcode.FromContext(ctx), grantID, + ) + return err +} + +// ListCumulativeRechargeRewardGrants 返回后台发放记录,按创建时间倒序。 +func (r *Repository) ListCumulativeRechargeRewardGrants(ctx context.Context, query domain.GrantQuery) ([]domain.Grant, int64, error) { + if r == nil || r.db == nil { + return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + where, args := cumulativeRechargeRewardGrantWhere(ctx, query) + var total int64 + if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM cumulative_recharge_reward_grants `+where, args...).Scan(&total); err != nil { + return nil, 0, err + } + pageSize := normalizePageSize(query.PageSize) + page := query.Page + if page <= 0 { + page = 1 + } + args = append(args, pageSize, (page-1)*pageSize) + rows, err := r.db.QueryContext(ctx, cumulativeRechargeRewardGrantSelectSQL()+` + `+where+` + ORDER BY created_at_ms DESC, grant_id DESC + LIMIT ? OFFSET ?`, args...) + if err != nil { + return nil, 0, err + } + defer rows.Close() + return scanCumulativeRechargeRewardGrants(rows, total) +} + +func (r *Repository) insertCumulativeRechargeRewardEvent(ctx context.Context, tx *sql.Tx, event domain.RechargeEvent, cycleKey string, nowMS int64) (bool, error) { + // INSERT IGNORE 让 wallet event 成为幂等边界:插入成功才允许进入累计和档位判定。 + result, err := tx.ExecContext(ctx, ` + INSERT IGNORE INTO cumulative_recharge_reward_events ( + app_code, event_id, cycle_key, user_id, transaction_id, command_id, + recharge_type, recharge_coin_amount, qualifying_usd_minor, payload_json, + occurred_at_ms, created_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + appcode.FromContext(ctx), event.EventID, cycleKey, event.UserID, event.TransactionID, event.CommandID, + event.RechargeType, event.RechargeCoinAmount, event.QualifyingUSDMinor, event.PayloadJSON, event.OccurredAtMS, nowMS, + ) + if err != nil { + return false, err + } + affected, err := result.RowsAffected() + if err != nil { + return false, err + } + return affected > 0, nil +} + +func (r *Repository) upsertCumulativeRechargeRewardProgress(ctx context.Context, tx *sql.Tx, event domain.RechargeEvent, cycleKey string, nowMS int64) (domain.Progress, error) { + // progress 是按 app + UTC 周 + user 聚合的金额投影;event 表先幂等后,这里可以安全做增量累加。 + _, err := tx.ExecContext(ctx, ` + INSERT INTO cumulative_recharge_reward_progress ( + app_code, cycle_key, user_id, total_usd_minor, total_coin_amount, + first_recharged_at_ms, last_recharged_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + total_usd_minor = total_usd_minor + VALUES(total_usd_minor), + total_coin_amount = total_coin_amount + VALUES(total_coin_amount), + last_recharged_at_ms = GREATEST(last_recharged_at_ms, VALUES(last_recharged_at_ms)), + updated_at_ms = VALUES(updated_at_ms)`, + appcode.FromContext(ctx), cycleKey, event.UserID, event.QualifyingUSDMinor, event.RechargeCoinAmount, + event.OccurredAtMS, event.OccurredAtMS, nowMS, + ) + if err != nil { + return domain.Progress{}, err + } + row := tx.QueryRowContext(ctx, cumulativeRechargeRewardProgressSelectSQL()+` + WHERE app_code = ? AND cycle_key = ? AND user_id = ? + FOR UPDATE`, + appcode.FromContext(ctx), cycleKey, event.UserID, + ) + return scanCumulativeRechargeRewardProgress(row) +} + +func (r *Repository) prepareMatchedCumulativeRechargeRewardGrants(ctx context.Context, tx *sql.Tx, event domain.RechargeEvent, progress domain.Progress, tiers []domain.Tier, nowMS int64) ([]domain.Grant, error) { + prepared := make([]domain.Grant, 0) + for _, tier := range tiers { + // 只有 active 且资源组有效的档位可以发放;inactive 档位仍保留给后台展示和历史审计。 + if tier.Status != domain.TierStatusActive || tier.ResourceGroupID <= 0 || tier.ThresholdUSDMinor <= 0 { + continue + } + if progress.TotalUSDMinor < tier.ThresholdUSDMinor { + continue + } + // 唯一键 app/cycle/user/tier 保证每个 UTC 周每个档位最多创建一条 grant。 + existing, exists, err := r.getCumulativeRechargeRewardGrantByTierForUpdate(ctx, tx, progress.CycleKey, event.UserID, tier.TierID) + if err != nil { + return nil, err + } + if exists { + if existing.Status == domain.GrantStatusFailed { + // failed grant 代表 wallet 副作用没有确认成功,可以更新触发事件快照后再次尝试同一档位。 + if err := r.updateCumulativeRechargeRewardGrantPending(ctx, tx, existing.GrantID, event, progress.TotalUSDMinor, nowMS); err != nil { + return nil, err + } + existing.Status = domain.GrantStatusPending + existing.EventID = event.EventID + existing.TransactionID = event.TransactionID + existing.CommandID = event.CommandID + existing.ReachedUSDMinor = progress.TotalUSDMinor + existing.QualifyingUSDMinor = event.QualifyingUSDMinor + existing.RechargeCoinAmount = event.RechargeCoinAmount + existing.RechargeType = event.RechargeType + existing.FailureReason = "" + existing.UpdatedAtMS = nowMS + prepared = append(prepared, existing) + } + if existing.Status == domain.GrantStatusPending { + // pending grant 可能来自上次 MQ 消费已建单但还未完成 wallet 发放,直接返回给调用方继续执行。 + prepared = append(prepared, existing) + } + continue + } + grant := domain.Grant{ + GrantID: idgen.New("crgrant"), + AppCode: appcode.FromContext(ctx), + CycleKey: progress.CycleKey, + UserID: event.UserID, + EventID: event.EventID, + TransactionID: event.TransactionID, + CommandID: event.CommandID, + TierID: tier.TierID, + TierCode: tier.TierCode, + ThresholdUSDMinor: tier.ThresholdUSDMinor, + ResourceGroupID: tier.ResourceGroupID, + ReachedUSDMinor: progress.TotalUSDMinor, + QualifyingUSDMinor: event.QualifyingUSDMinor, + RechargeCoinAmount: event.RechargeCoinAmount, + RechargeType: event.RechargeType, + Status: domain.GrantStatusPending, + CreatedAtMS: nowMS, + UpdatedAtMS: nowMS, + } + // wallet_command_id 从 grant_id 派生,数据库唯一键和 wallet 幂等命令共同防止重复到账。 + grant.WalletCommandID = walletCumulativeRechargeRewardCommandID(grant.GrantID) + if err := r.insertCumulativeRechargeRewardGrant(ctx, tx, grant); err != nil { + return nil, err + } + prepared = append(prepared, grant) + } + return prepared, nil +} + +func (r *Repository) pendingCumulativeRechargeRewardGrantsByEventForUpdate(ctx context.Context, tx *sql.Tx, eventID string, nowMS int64) ([]domain.Grant, error) { + // 重复 event 只查同一 event 创建过的 pending/failed grant,避免借 MQ 重投触发新配置下的额外发放。 + rows, err := tx.QueryContext(ctx, cumulativeRechargeRewardGrantSelectSQL()+` + WHERE app_code = ? AND event_id = ? AND status IN ('pending', 'failed') + ORDER BY threshold_usd_minor ASC, grant_id ASC + FOR UPDATE`, + appcode.FromContext(ctx), eventID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + grants, _, err := scanCumulativeRechargeRewardGrants(rows, 0) + if err != nil { + return nil, err + } + for index := range grants { + if grants[index].Status != domain.GrantStatusFailed { + continue + } + // failed 回到 pending 时不生成新 grant_id 或新 wallet_command_id,重试路径仍保持幂等。 + if err := r.updateCumulativeRechargeRewardGrantPendingByID(ctx, tx, grants[index].GrantID, nowMS); err != nil { + return nil, err + } + grants[index].Status = domain.GrantStatusPending + grants[index].FailureReason = "" + grants[index].UpdatedAtMS = nowMS + } + return grants, nil +} + +func (r *Repository) getCumulativeRechargeRewardConfigForUpdate(ctx context.Context, tx *sql.Tx) (domain.Config, bool, error) { + row := tx.QueryRowContext(ctx, cumulativeRechargeRewardConfigSelectSQL()+` + WHERE app_code = ? + FOR UPDATE`, + appcode.FromContext(ctx), + ) + config, err := scanCumulativeRechargeRewardConfig(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Config{}, false, nil + } + return config, err == nil, err +} + +func (r *Repository) listCumulativeRechargeRewardTiers(ctx context.Context, tx *sql.Tx) ([]domain.Tier, error) { + query := cumulativeRechargeRewardTierSelectSQL() + ` + WHERE app_code = ? + ORDER BY sort_order ASC, threshold_usd_minor ASC, tier_id ASC` + var rows *sql.Rows + var err error + if tx != nil { + rows, err = tx.QueryContext(ctx, query, appcode.FromContext(ctx)) + } else { + rows, err = r.db.QueryContext(ctx, query, appcode.FromContext(ctx)) + } + if err != nil { + return nil, err + } + defer rows.Close() + return scanCumulativeRechargeRewardTiers(rows) +} + +func (r *Repository) listCumulativeRechargeRewardTiersForUpdate(ctx context.Context, tx *sql.Tx) ([]domain.Tier, error) { + rows, err := tx.QueryContext(ctx, cumulativeRechargeRewardTierSelectSQL()+` + WHERE app_code = ? + ORDER BY sort_order ASC, threshold_usd_minor ASC, tier_id ASC + FOR UPDATE`, + appcode.FromContext(ctx), + ) + if err != nil { + return nil, err + } + defer rows.Close() + return scanCumulativeRechargeRewardTiers(rows) +} + +func (r *Repository) getCumulativeRechargeRewardGrantByTierForUpdate(ctx context.Context, tx *sql.Tx, cycleKey string, userID int64, tierID int64) (domain.Grant, bool, error) { + row := tx.QueryRowContext(ctx, cumulativeRechargeRewardGrantSelectSQL()+` + WHERE app_code = ? AND cycle_key = ? AND user_id = ? AND tier_id = ? + FOR UPDATE`, + appcode.FromContext(ctx), cycleKey, userID, tierID, + ) + grant, err := scanCumulativeRechargeRewardGrant(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Grant{}, false, nil + } + return grant, err == nil, err +} + +func (r *Repository) getCumulativeRechargeRewardGrantByID(ctx context.Context, grantID string) (domain.Grant, bool, error) { + row := r.db.QueryRowContext(ctx, cumulativeRechargeRewardGrantSelectSQL()+` + WHERE app_code = ? AND grant_id = ?`, + appcode.FromContext(ctx), grantID, + ) + grant, err := scanCumulativeRechargeRewardGrant(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Grant{}, false, nil + } + return grant, err == nil, err +} + +func (r *Repository) insertCumulativeRechargeRewardGrant(ctx context.Context, tx *sql.Tx, grant domain.Grant) error { + _, err := tx.ExecContext(ctx, ` + INSERT INTO cumulative_recharge_reward_grants ( + app_code, grant_id, cycle_key, user_id, event_id, transaction_id, command_id, + tier_id, tier_code, threshold_usd_minor, resource_group_id, reached_usd_minor, + qualifying_usd_minor, recharge_coin_amount, recharge_type, status, wallet_command_id, + wallet_grant_id, failure_reason, granted_at_ms, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '', '', 0, ?, ?)`, + appcode.FromContext(ctx), grant.GrantID, grant.CycleKey, grant.UserID, grant.EventID, grant.TransactionID, grant.CommandID, + grant.TierID, grant.TierCode, grant.ThresholdUSDMinor, grant.ResourceGroupID, grant.ReachedUSDMinor, + grant.QualifyingUSDMinor, grant.RechargeCoinAmount, grant.RechargeType, grant.Status, grant.WalletCommandID, + grant.CreatedAtMS, grant.UpdatedAtMS, + ) + return err +} + +func (r *Repository) updateCumulativeRechargeRewardGrantPending(ctx context.Context, tx *sql.Tx, grantID string, event domain.RechargeEvent, reachedUSDMinor int64, nowMS int64) error { + _, err := tx.ExecContext(ctx, ` + UPDATE cumulative_recharge_reward_grants + SET event_id = ?, + transaction_id = ?, + command_id = ?, + reached_usd_minor = ?, + qualifying_usd_minor = ?, + recharge_coin_amount = ?, + recharge_type = ?, + status = 'pending', + wallet_grant_id = '', + failure_reason = '', + granted_at_ms = 0, + updated_at_ms = ? + WHERE app_code = ? AND grant_id = ? AND status = 'failed'`, + event.EventID, event.TransactionID, event.CommandID, reachedUSDMinor, event.QualifyingUSDMinor, + event.RechargeCoinAmount, event.RechargeType, nowMS, appcode.FromContext(ctx), grantID, + ) + return err +} + +func (r *Repository) updateCumulativeRechargeRewardGrantPendingByID(ctx context.Context, tx *sql.Tx, grantID string, nowMS int64) error { + _, err := tx.ExecContext(ctx, ` + UPDATE cumulative_recharge_reward_grants + SET status = 'pending', + wallet_grant_id = '', + failure_reason = '', + granted_at_ms = 0, + updated_at_ms = ? + WHERE app_code = ? AND grant_id = ? AND status = 'failed'`, + nowMS, appcode.FromContext(ctx), grantID, + ) + return err +} + +func scanCumulativeRechargeRewardConfig(row rowScanner) (domain.Config, error) { + var config domain.Config + err := row.Scan(&config.AppCode, &config.Enabled, &config.UpdatedByAdminID, &config.CreatedAtMS, &config.UpdatedAtMS) + return config, err +} + +func scanCumulativeRechargeRewardTiers(rows *sql.Rows) ([]domain.Tier, error) { + items := make([]domain.Tier, 0) + for rows.Next() { + var item domain.Tier + if err := rows.Scan( + &item.TierID, + &item.TierCode, + &item.TierName, + &item.ThresholdUSDMinor, + &item.ResourceGroupID, + &item.Status, + &item.SortOrder, + &item.CreatedAtMS, + &item.UpdatedAtMS, + ); err != nil { + return nil, err + } + items = append(items, item) + } + return items, rows.Err() +} + +func scanCumulativeRechargeRewardProgress(row rowScanner) (domain.Progress, error) { + var item domain.Progress + err := row.Scan( + &item.AppCode, + &item.CycleKey, + &item.UserID, + &item.TotalUSDMinor, + &item.TotalCoinAmount, + &item.FirstRechargedAtMS, + &item.LastRechargedAtMS, + &item.UpdatedAtMS, + ) + return item, err +} + +func scanCumulativeRechargeRewardGrants(rows *sql.Rows, total int64) ([]domain.Grant, int64, error) { + items := make([]domain.Grant, 0) + for rows.Next() { + item, err := scanCumulativeRechargeRewardGrant(rows) + if err != nil { + return nil, 0, err + } + items = append(items, item) + } + if err := rows.Err(); err != nil { + return nil, 0, err + } + return items, total, nil +} + +func scanCumulativeRechargeRewardGrant(row rowScanner) (domain.Grant, error) { + var item domain.Grant + err := row.Scan( + &item.GrantID, + &item.AppCode, + &item.CycleKey, + &item.UserID, + &item.EventID, + &item.TransactionID, + &item.CommandID, + &item.TierID, + &item.TierCode, + &item.ThresholdUSDMinor, + &item.ResourceGroupID, + &item.ReachedUSDMinor, + &item.QualifyingUSDMinor, + &item.RechargeCoinAmount, + &item.RechargeType, + &item.Status, + &item.WalletCommandID, + &item.WalletGrantID, + &item.FailureReason, + &item.GrantedAtMS, + &item.CreatedAtMS, + &item.UpdatedAtMS, + ) + return item, err +} + +func cumulativeRechargeRewardConfigSelectSQL() string { + return `SELECT app_code, enabled, updated_by_admin_id, created_at_ms, updated_at_ms + FROM cumulative_recharge_reward_configs ` +} + +func cumulativeRechargeRewardTierSelectSQL() string { + return `SELECT tier_id, tier_code, tier_name, threshold_usd_minor, + resource_group_id, status, sort_order, created_at_ms, updated_at_ms + FROM cumulative_recharge_reward_tiers ` +} + +func cumulativeRechargeRewardProgressSelectSQL() string { + return `SELECT app_code, cycle_key, user_id, total_usd_minor, total_coin_amount, + first_recharged_at_ms, last_recharged_at_ms, updated_at_ms + FROM cumulative_recharge_reward_progress ` +} + +func cumulativeRechargeRewardGrantSelectSQL() string { + return `SELECT grant_id, app_code, cycle_key, user_id, event_id, transaction_id, command_id, + tier_id, tier_code, threshold_usd_minor, resource_group_id, reached_usd_minor, + qualifying_usd_minor, recharge_coin_amount, recharge_type, status, wallet_command_id, + wallet_grant_id, failure_reason, granted_at_ms, created_at_ms, updated_at_ms + FROM cumulative_recharge_reward_grants ` +} + +func cumulativeRechargeRewardGrantWhere(ctx context.Context, query domain.GrantQuery) (string, []any) { + conditions := []string{"app_code = ?"} + args := []any{appcode.FromContext(ctx)} + if query.Status != "" { + conditions = append(conditions, "status = ?") + args = append(args, query.Status) + } + if query.UserID > 0 { + conditions = append(conditions, "user_id = ?") + args = append(args, query.UserID) + } + if query.CycleKey != "" { + conditions = append(conditions, "cycle_key = ?") + args = append(args, query.CycleKey) + } + return "WHERE " + strings.Join(conditions, " AND "), args +} + +func walletCumulativeRechargeRewardCommandID(grantID string) string { + // 前缀区分累充奖励和其他 wallet 资源组发放来源,便于钱包侧排查命令来源。 + return fmt.Sprintf("wcr_%s", grantID) +} diff --git a/services/activity-service/internal/storage/mysql/lucky_gift_repository.go b/services/activity-service/internal/storage/mysql/lucky_gift_repository.go index fc10e550..b81c11f3 100644 --- a/services/activity-service/internal/storage/mysql/lucky_gift_repository.go +++ b/services/activity-service/internal/storage/mysql/lucky_gift_repository.go @@ -184,7 +184,7 @@ func (r *Repository) CheckLuckyGift(ctx context.Context, cmd domain.CheckCommand return domain.CheckResult{ Enabled: config.Enabled, Reason: luckyEnabledReason(config.Enabled), - PoolID: config.GiftID, + PoolID: config.PoolID, GiftID: cmd.GiftID, GiftPrice: config.GiftPrice, RuleVersion: config.RuleVersion, @@ -466,7 +466,7 @@ func (r *Repository) stageLuckyBatchDraw(appCode string, baseConfig domain.Confi return domain.DrawResult{ DrawID: drawID, CommandID: cmd.CommandID, - PoolID: config.GiftID, + PoolID: config.PoolID, GiftID: cmd.GiftID, RuleVersion: config.RuleVersion, ExperiencePool: experiencePool, @@ -627,15 +627,15 @@ func (r *Repository) insertLuckyDrawRecords(ctx context.Context, tx *sql.Tx, rec var sqlBuilder strings.Builder sqlBuilder.WriteString(` INSERT INTO lucky_draw_records ( - app_code, draw_id, command_id, user_id, device_id, room_id, anchor_id, pool_id, gift_id, + app_code, draw_id, command_id, user_id, device_id, room_id, anchor_id, visible_region_id, pool_id, gift_id, coin_spent, rule_version, experience_pool, rtp_window_index, gift_rtp_window_index, selected_tier_id, base_reward_coins, room_atmosphere_reward_coins, activity_subsidy_coins, effective_reward_coins, budget_sources_json, stage_feedback, high_multiplier, candidate_tiers_json, pool_snapshot_json, rtp_snapshot_json, reward_status, reward_transaction_id, reward_failure_reason, paid_at_ms, created_at_ms, updated_at_ms ) VALUES `) - args := make([]any, 0, len(batch)*31) - rowPlaceholders := "(" + luckySQLPlaceholders(31) + ")" + args := make([]any, 0, len(batch)*32) + rowPlaceholders := "(" + luckySQLPlaceholders(32) + ")" for index, record := range batch { if index > 0 { sqlBuilder.WriteString(",") @@ -741,6 +741,7 @@ type luckyDrawStatsRecord struct { DrawID string PoolID string GiftID string + VisibleRegionID int64 UserID int64 RoomID string TotalSpentCoins int64 @@ -875,6 +876,148 @@ func (r *Repository) RefreshLuckyGiftAdminStatsSnapshots(ctx context.Context, no return len(records), nil } +type luckyDrawPoolDayStatKey struct { + AppCode string + StatDay string + VisibleRegionID int64 + PoolID string + GiftID string +} + +type luckyDrawPoolDayStatDelta struct { + DrawCount int64 + TurnoverCoin int64 + PayoutCoin int64 + BaseRewardCoin int64 + RoomAtmosphereRewardCoin int64 + ActivitySubsidyCoin int64 +} + +// RefreshLuckyGiftDatabiStatsSnapshots 用独立游标把抽奖事实小批量转成 Databi 日维度汇总。 +// Databi 页面只读 lucky_draw_pool_day_stats,不直接扫 lucky_draw_records,避免页面访问把事实表打成查询热点。 +func (r *Repository) RefreshLuckyGiftDatabiStatsSnapshots(ctx context.Context, nowMS int64, batchSize int) (int, error) { + if r == nil || r.db == nil { + return 0, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + if batchSize <= 0 { + batchSize = 5000 + } + if batchSize > 50000 { + batchSize = 50000 + } + appCode := appcode.FromContext(ctx) + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return 0, err + } + defer func() { _ = tx.Rollback() }() + + cursor, err := r.lockLuckyDrawDatabiStatsCursor(ctx, tx, appCode, nowMS) + if err != nil { + return 0, err + } + rows, err := tx.QueryContext(ctx, ` + SELECT app_code, draw_id, visible_region_id, pool_id, gift_id, user_id, room_id, coin_spent, + effective_reward_coins, base_reward_coins, room_atmosphere_reward_coins, activity_subsidy_coins, + reward_status, + created_at_ms + FROM lucky_draw_records FORCE INDEX (idx_lucky_draw_created) + WHERE app_code = ? + AND (created_at_ms > ? OR (created_at_ms = ? AND draw_id > ?)) + ORDER BY created_at_ms ASC, draw_id ASC + LIMIT ?`, appCode, cursor.LastDrawCreatedAtMS, cursor.LastDrawCreatedAtMS, cursor.LastDrawID, batchSize) + if err != nil { + return 0, err + } + defer rows.Close() + records := make([]luckyDrawStatsRecord, 0, batchSize) + for rows.Next() { + var record luckyDrawStatsRecord + if err := rows.Scan(&record.AppCode, &record.DrawID, &record.VisibleRegionID, &record.PoolID, &record.GiftID, &record.UserID, &record.RoomID, + &record.TotalSpentCoins, &record.TotalRewardCoins, &record.BaseRewardCoins, + &record.RoomAtmosphereRewardCoins, &record.ActivitySubsidyCoins, &record.RewardStatus, &record.CreatedAtMS); err != nil { + return 0, err + } + records = append(records, record) + } + if err := rows.Err(); err != nil { + return 0, err + } + if len(records) == 0 { + if err := tx.Commit(); err != nil { + return 0, err + } + return 0, nil + } + grouped := map[luckyDrawPoolDayStatKey]*luckyDrawPoolDayStatDelta{} + last := records[len(records)-1] + for _, record := range records { + regionID := record.VisibleRegionID + if regionID < 0 { + regionID = 0 + } + day := luckyDrawStatDay(record.CreatedAtMS) + keys := []luckyDrawPoolDayStatKey{{AppCode: record.AppCode, StatDay: day, VisibleRegionID: regionID, PoolID: record.PoolID, GiftID: ""}} + if giftID := strings.TrimSpace(record.GiftID); giftID != "" { + keys = append(keys, luckyDrawPoolDayStatKey{AppCode: record.AppCode, StatDay: day, VisibleRegionID: regionID, PoolID: record.PoolID, GiftID: giftID}) + } + for _, key := range keys { + delta := grouped[key] + if delta == nil { + delta = &luckyDrawPoolDayStatDelta{} + grouped[key] = delta + } + delta.DrawCount++ + delta.TurnoverCoin += record.TotalSpentCoins + delta.PayoutCoin += record.TotalRewardCoins + delta.BaseRewardCoin += record.BaseRewardCoins + delta.RoomAtmosphereRewardCoin += record.RoomAtmosphereRewardCoins + delta.ActivitySubsidyCoin += record.ActivitySubsidyCoins + } + } + for key, delta := range grouped { + if err := r.upsertLuckyDrawPoolDayStatsDelta(ctx, tx, key, *delta, nowMS); err != nil { + return 0, err + } + } + if _, err := tx.ExecContext(ctx, ` + UPDATE lucky_draw_pool_stat_cursors + SET last_draw_created_at_ms = ?, last_draw_id = ?, updated_at_ms = ? + WHERE app_code = ? AND cursor_name = 'draw_records_databi_day'`, + last.CreatedAtMS, last.DrawID, nowMS, appCode, + ); err != nil { + return 0, err + } + if err := tx.Commit(); err != nil { + return 0, err + } + return len(records), nil +} + +func (r *Repository) upsertLuckyDrawPoolDayStatsDelta(ctx context.Context, tx *sql.Tx, key luckyDrawPoolDayStatKey, delta luckyDrawPoolDayStatDelta, nowMS int64) error { + _, err := tx.ExecContext(ctx, ` + INSERT INTO lucky_draw_pool_day_stats ( + app_code, stat_day, visible_region_id, pool_id, gift_id, draw_count, turnover_coin, payout_coin, + base_reward_coin, room_atmosphere_reward_coin, activity_subsidy_coin, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + draw_count = draw_count + VALUES(draw_count), + turnover_coin = turnover_coin + VALUES(turnover_coin), + payout_coin = payout_coin + VALUES(payout_coin), + base_reward_coin = base_reward_coin + VALUES(base_reward_coin), + room_atmosphere_reward_coin = room_atmosphere_reward_coin + VALUES(room_atmosphere_reward_coin), + activity_subsidy_coin = activity_subsidy_coin + VALUES(activity_subsidy_coin), + updated_at_ms = VALUES(updated_at_ms)`, + key.AppCode, key.StatDay, key.VisibleRegionID, key.PoolID, key.GiftID, delta.DrawCount, delta.TurnoverCoin, delta.PayoutCoin, + delta.BaseRewardCoin, delta.RoomAtmosphereRewardCoin, delta.ActivitySubsidyCoin, nowMS, nowMS, + ) + return err +} + +func luckyDrawStatDay(ms int64) string { + return time.UnixMilli(ms).UTC().Format("2006-01-02") +} + func (r *Repository) lockLuckyDrawStatsCursor(ctx context.Context, tx *sql.Tx, appCode string, nowMS int64) (luckyDrawStatsCursor, bool, error) { var cursor luckyDrawStatsCursor err := tx.QueryRowContext(ctx, ` @@ -909,12 +1052,38 @@ func (r *Repository) lockLuckyDrawStatsCursor(ctx context.Context, tx *sql.Tx, a return cursor, true, nil } +func (r *Repository) lockLuckyDrawDatabiStatsCursor(ctx context.Context, tx *sql.Tx, appCode string, nowMS int64) (luckyDrawStatsCursor, error) { + var cursor luckyDrawStatsCursor + err := tx.QueryRowContext(ctx, ` + SELECT last_draw_created_at_ms, last_draw_id + FROM lucky_draw_pool_stat_cursors + WHERE app_code = ? AND cursor_name = 'draw_records_databi_day' + FOR UPDATE`, appCode, + ).Scan(&cursor.LastDrawCreatedAtMS, &cursor.LastDrawID) + if err == nil { + return cursor, nil + } + if !errors.Is(err, sql.ErrNoRows) { + return luckyDrawStatsCursor{}, err + } + // Databi 日汇总是后加的统计口径,首次启动必须从 0 游标小批量回补;不要像后台全量汇总那样跳到最新记录。 + if _, err := tx.ExecContext(ctx, ` + INSERT INTO lucky_draw_pool_stat_cursors ( + app_code, cursor_name, last_draw_created_at_ms, last_draw_id, created_at_ms, updated_at_ms + ) VALUES (?, 'draw_records_databi_day', 0, '', ?, ?)`, + appCode, nowMS, nowMS, + ); err != nil { + return luckyDrawStatsCursor{}, err + } + return cursor, nil +} + // luckyDrawRecordArgs 固定 lucky_draw_records 的列顺序;集中维护可以避免批量 INSERT 和单条 INSERT 语义漂移。 func luckyDrawRecordArgs(input luckyDrawRecordInput) []any { candidateSnapshot, poolSnapshot, rtpSnapshot := luckyDrawRecordSnapshots(input) return []any{ input.AppCode, input.DrawID, input.Command.CommandID, input.Command.UserID, input.Command.DeviceID, - input.Command.RoomID, input.Command.AnchorID, input.Config.GiftID, input.Command.GiftID, input.Command.CoinSpent, + input.Command.RoomID, input.Command.AnchorID, input.Command.VisibleRegionID, input.Config.PoolID, input.Command.GiftID, input.Command.CoinSpent, input.Config.RuleVersion, input.ExperiencePool, input.GlobalWindow.WindowIndex, input.GiftWindow.WindowIndex, input.Candidate.TierID, input.Candidate.BaseReward, input.Candidate.RoomReward, input.Candidate.ActivityReward, input.Candidate.effectiveReward(), input.BudgetSourcesJSON, input.StageFeedback, input.Candidate.HighMultiplier, @@ -1102,7 +1271,7 @@ func (r *Repository) executeSingleLuckyGiftDraw(ctx context.Context, tx *sql.Tx, return domain.DrawResult{ DrawID: drawID, CommandID: cmd.CommandID, - PoolID: config.GiftID, + PoolID: config.PoolID, GiftID: cmd.GiftID, RuleVersion: config.RuleVersion, ExperiencePool: experiencePool, @@ -1370,15 +1539,15 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky }) if _, err := tx.ExecContext(ctx, ` INSERT INTO lucky_draw_records ( - app_code, draw_id, command_id, user_id, device_id, room_id, anchor_id, pool_id, gift_id, + app_code, draw_id, command_id, user_id, device_id, room_id, anchor_id, visible_region_id, pool_id, gift_id, coin_spent, rule_version, experience_pool, rtp_window_index, gift_rtp_window_index, selected_tier_id, base_reward_coins, room_atmosphere_reward_coins, activity_subsidy_coins, effective_reward_coins, budget_sources_json, stage_feedback, high_multiplier, candidate_tiers_json, pool_snapshot_json, rtp_snapshot_json, reward_status, reward_transaction_id, reward_failure_reason, paid_at_ms, created_at_ms, updated_at_ms - ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, + ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, input.AppCode, input.DrawID, input.Command.CommandID, input.Command.UserID, input.Command.DeviceID, - input.Command.RoomID, input.Command.AnchorID, input.Config.GiftID, input.Command.GiftID, input.Command.CoinSpent, + input.Command.RoomID, input.Command.AnchorID, input.Command.VisibleRegionID, input.Config.PoolID, input.Command.GiftID, input.Command.CoinSpent, input.Config.RuleVersion, input.ExperiencePool, input.GlobalWindow.WindowIndex, input.GiftWindow.WindowIndex, input.Candidate.TierID, input.Candidate.BaseReward, input.Candidate.RoomReward, input.Candidate.ActivityReward, input.Candidate.effectiveReward(), input.BudgetSourcesJSON, input.StageFeedback, input.Candidate.HighMultiplier, @@ -1395,7 +1564,7 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky "app_code": input.AppCode, "draw_id": input.DrawID, "command_id": input.Command.CommandID, - "pool_id": input.Config.GiftID, + "pool_id": input.Config.PoolID, "user_id": input.Command.UserID, "sender_user_id": input.Command.UserID, "target_user_id": input.Command.TargetUserID, diff --git a/services/activity-service/internal/storage/mysql/lucky_gift_repository_test.go b/services/activity-service/internal/storage/mysql/lucky_gift_repository_test.go index e8909bd6..ba546c0e 100644 --- a/services/activity-service/internal/storage/mysql/lucky_gift_repository_test.go +++ b/services/activity-service/internal/storage/mysql/lucky_gift_repository_test.go @@ -117,6 +117,14 @@ func TestRefreshLuckyGiftAdminStatsSnapshotsAdvancesCursorByBatch(t *testing.T) assertLuckyStatsTotal(t, repository, "", 1, 100, 300) assertLuckyStatsStatus(t, repository, "", 0, 1, 0) assertLuckyStatsCursor(t, repository, 100, "draw_001") + processed, err = repository.RefreshLuckyGiftDatabiStatsSnapshots(ctx, 310, 1) + if err != nil { + t.Fatalf("first databi refresh failed: %v", err) + } + if processed != 1 { + t.Fatalf("first databi refresh should process one row, got %d", processed) + } + assertLuckyDatabiDayStatsTotal(t, repository, "1970-01-01", "", 1, 100, 300) processed, err = repository.RefreshLuckyGiftAdminStatsSnapshots(ctx, 400, 10) if err != nil { @@ -128,6 +136,14 @@ func TestRefreshLuckyGiftAdminStatsSnapshotsAdvancesCursorByBatch(t *testing.T) assertLuckyStatsTotal(t, repository, "", 2, 250, 300) assertLuckyStatsStatus(t, repository, "", 1, 1, 0) assertLuckyStatsCursor(t, repository, 200, "draw_002") + processed, err = repository.RefreshLuckyGiftDatabiStatsSnapshots(ctx, 410, 10) + if err != nil { + t.Fatalf("second databi refresh failed: %v", err) + } + if processed != 1 { + t.Fatalf("second databi refresh should process remaining row, got %d", processed) + } + assertLuckyDatabiDayStatsTotal(t, repository, "1970-01-01", "", 2, 250, 300) } func seedLuckyDrawRecordForStats(t *testing.T, repository *Repository, drawID string, commandID string, status string, createdAtMS int64, spent int64, reward int64) { @@ -183,6 +199,21 @@ func assertLuckyStatsStatus(t *testing.T, repository *Repository, giftID string, } } +func assertLuckyDatabiDayStatsTotal(t *testing.T, repository *Repository, day string, giftID string, wantDraws int64, wantSpent int64, wantReward int64) { + t.Helper() + var draws, spent, reward int64 + if err := repository.db.QueryRowContext(context.Background(), ` + SELECT draw_count, turnover_coin, payout_coin + FROM lucky_draw_pool_day_stats + WHERE app_code = 'lalu' AND stat_day = ? AND visible_region_id = 0 AND pool_id = 'lucky' AND gift_id = ?`, day, giftID, + ).Scan(&draws, &spent, &reward); err != nil { + t.Fatalf("read databi day stats total: %v", err) + } + if draws != wantDraws || spent != wantSpent || reward != wantReward { + t.Fatalf("databi day stats mismatch: draws=%d spent=%d reward=%d want draws=%d spent=%d reward=%d", draws, spent, reward, wantDraws, wantSpent, wantReward) + } +} + func assertLuckyStatsCursor(t *testing.T, repository *Repository, wantCreatedAtMS int64, wantDrawID string) { t.Helper() var createdAtMS int64 diff --git a/services/activity-service/internal/storage/mysql/registration_reward_repository.go b/services/activity-service/internal/storage/mysql/registration_reward_repository.go index 6af3bf8f..ee8532c6 100644 --- a/services/activity-service/internal/storage/mysql/registration_reward_repository.go +++ b/services/activity-service/internal/storage/mysql/registration_reward_repository.go @@ -13,6 +13,8 @@ import ( domain "hyapp/services/activity-service/internal/domain/registrationreward" ) +const registrationRewardClaimDisplayTimeSQL = "COALESCE(NULLIF(claimed_at_ms, 0), created_at_ms)" + // GetRegistrationRewardConfig 读取注册奖励当前配置;未配置是正常后台首屏状态。 func (r *Repository) GetRegistrationRewardConfig(ctx context.Context) (domain.Config, bool, error) { if r == nil || r.db == nil { @@ -335,7 +337,7 @@ func (r *Repository) ListRegistrationRewardClaims(ctx context.Context, query dom args = append(args, pageSize, (page-1)*pageSize) rows, err := r.db.QueryContext(ctx, registrationRewardClaimSelectSQL()+` `+where+` - ORDER BY created_at_ms DESC, claim_id DESC + ORDER BY `+registrationRewardClaimDisplayTimeSQL+` DESC, claim_id DESC LIMIT ? OFFSET ?`, args...) if err != nil { return nil, 0, err @@ -344,6 +346,28 @@ func (r *Repository) ListRegistrationRewardClaims(ctx context.Context, query dom return scanRegistrationRewardClaims(rows, total) } +// CountRegistrationRewardGrantedByDay 读取 UTC 当天已发放份数;后台只展示成功发放数量,不把 pending 预留量算作已领取。 +func (r *Repository) CountRegistrationRewardGrantedByDay(ctx context.Context, rewardDay string) (int64, error) { + if r == nil || r.db == nil { + return 0, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + rewardDay = strings.TrimSpace(rewardDay) + if rewardDay == "" { + return 0, nil + } + var count int64 + err := r.db.QueryRowContext(ctx, ` + SELECT granted_count + FROM registration_reward_daily_counters + WHERE app_code = ? AND reward_day = ?`, + appcode.FromContext(ctx), rewardDay, + ).Scan(&count) + if errors.Is(err, sql.ErrNoRows) { + return 0, nil + } + return count, err +} + func (r *Repository) getRegistrationRewardConfigForUpdate(ctx context.Context, tx *sql.Tx) (domain.Config, bool, error) { row := tx.QueryRowContext(ctx, registrationRewardConfigSelectSQL()+` WHERE app_code = ? @@ -571,6 +595,14 @@ func registrationRewardClaimWhere(ctx context.Context, query domain.ClaimQuery) conditions = append(conditions, "user_id = ?") args = append(args, query.UserID) } + if query.ClaimedStartMS > 0 { + conditions = append(conditions, registrationRewardClaimDisplayTimeSQL+" >= ?") + args = append(args, query.ClaimedStartMS) + } + if query.ClaimedEndMS > 0 { + conditions = append(conditions, registrationRewardClaimDisplayTimeSQL+" <= ?") + args = append(args, query.ClaimedEndMS) + } return "WHERE " + strings.Join(conditions, " AND "), args } diff --git a/services/activity-service/internal/storage/mysql/repository.go b/services/activity-service/internal/storage/mysql/repository.go index 2bebaebc..b2e19581 100644 --- a/services/activity-service/internal/storage/mysql/repository.go +++ b/services/activity-service/internal/storage/mysql/repository.go @@ -45,6 +45,9 @@ func (r *Repository) Migrate(ctx context.Context) error { if err := r.ensureLuckyDrawPoolID(ctx); err != nil { return err } + if err := r.ensureLuckyDrawVisibleRegion(ctx); err != nil { + return err + } if err := r.ensureLuckyGiftOutboxColumns(ctx); err != nil { return err } @@ -63,12 +66,21 @@ func (r *Repository) Migrate(ctx context.Context) error { if err := r.ensureLuckyGiftRuleVersionTables(ctx); err != nil { return err } + if err := r.ensureWeeklyStarTables(ctx); err != nil { + return err + } if err := r.ensureLuckyUserStateFlowColumns(ctx); err != nil { return err } if err := r.ensureDefaultLuckyGiftRules(ctx); err != nil { return err } + if err := r.ensureRoomTurnoverRewardTables(ctx); err != nil { + return err + } + if err := r.ensureCumulativeRechargeRewardTables(ctx); err != nil { + return err + } return nil } @@ -93,6 +105,23 @@ func (r *Repository) ensureLuckyDrawPoolStatsTables(ctx context.Context) error { updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', PRIMARY KEY (app_code, pool_id, gift_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物池级汇总统计'`, + `CREATE TABLE IF NOT EXISTS lucky_draw_pool_day_stats ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + stat_day DATE NOT NULL COMMENT 'UTC 统计日', + visible_region_id BIGINT NOT NULL DEFAULT 0 COMMENT '房间可见区域 ID', + pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID', + gift_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '礼物 ID,空值表示奖池汇总', + draw_count BIGINT NOT NULL DEFAULT 0 COMMENT '抽奖次数', + turnover_coin BIGINT NOT NULL DEFAULT 0 COMMENT '消耗金币', + payout_coin BIGINT NOT NULL DEFAULT 0 COMMENT '用户可见返奖金币', + base_reward_coin BIGINT NOT NULL DEFAULT 0 COMMENT '基础 RTP 返奖', + room_atmosphere_reward_coin BIGINT NOT NULL DEFAULT 0 COMMENT '房间气氛支出', + activity_subsidy_coin BIGINT NOT NULL DEFAULT 0 COMMENT '活动补贴支出', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, stat_day, visible_region_id, pool_id, gift_id), + KEY idx_lucky_draw_pool_day_overview (app_code, stat_day, pool_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='幸运礼物 Databi 日维度汇总'`, `CREATE TABLE IF NOT EXISTS lucky_draw_pool_stat_users ( app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', pool_id VARCHAR(96) NOT NULL COMMENT '幸运礼物奖池 ID', @@ -216,6 +245,31 @@ func (r *Repository) ensureLuckyDrawPoolID(ctx context.Context) error { return nil } +func (r *Repository) ensureLuckyDrawVisibleRegion(ctx context.Context) error { + const table = "lucky_draw_records" + hasVisibleRegion, err := r.columnExists(ctx, table, "visible_region_id") + if err != nil { + return err + } + if !hasVisibleRegion { + if _, err := r.db.ExecContext(ctx, ` + ALTER TABLE lucky_draw_records + ADD COLUMN visible_region_id BIGINT NOT NULL DEFAULT 0 COMMENT '房间可见区域 ID' AFTER anchor_id`); err != nil { + return err + } + } + hasIndex, err := r.indexExists(ctx, table, "idx_lucky_draw_region_created") + if err != nil { + return err + } + if !hasIndex { + if _, err := r.db.ExecContext(ctx, `CREATE INDEX idx_lucky_draw_region_created ON lucky_draw_records (app_code, visible_region_id, created_at_ms, draw_id)`); err != nil { + return err + } + } + return nil +} + func (r *Repository) ensureLuckyGiftOutboxColumns(ctx context.Context) error { additions := []struct { name string diff --git a/services/activity-service/internal/storage/mysql/repository_migration_test.go b/services/activity-service/internal/storage/mysql/repository_migration_test.go index 76e8b8b4..accc5985 100644 --- a/services/activity-service/internal/storage/mysql/repository_migration_test.go +++ b/services/activity-service/internal/storage/mysql/repository_migration_test.go @@ -19,6 +19,15 @@ func TestMigrateAddsLuckyDrawPoolIDToLegacyTable(t *testing.T) { if _, err := schema.DB.ExecContext(context.Background(), `ALTER TABLE lucky_draw_records DROP COLUMN pool_id`); err != nil { t.Fatalf("drop legacy pool column: %v", err) } + if _, err := schema.DB.ExecContext(context.Background(), `ALTER TABLE lucky_draw_records DROP INDEX idx_lucky_draw_region_created`); err != nil { + t.Fatalf("drop legacy region index: %v", err) + } + if _, err := schema.DB.ExecContext(context.Background(), `ALTER TABLE lucky_draw_records DROP COLUMN visible_region_id`); err != nil { + t.Fatalf("drop legacy region column: %v", err) + } + if _, err := schema.DB.ExecContext(context.Background(), `DROP TABLE lucky_draw_pool_day_stats`); err != nil { + t.Fatalf("drop legacy databi day stats table: %v", err) + } repository, err := Open(context.Background(), schema.DSN) if err != nil { @@ -34,4 +43,23 @@ func TestMigrateAddsLuckyDrawPoolIDToLegacyTable(t *testing.T) { if hasIndex, err := repository.indexExists(context.Background(), "lucky_draw_records", "idx_lucky_draw_pool"); err != nil || !hasIndex { t.Fatalf("idx_lucky_draw_pool missing after migrate: has=%v err=%v", hasIndex, err) } + if hasColumn, err := repository.columnExists(context.Background(), "lucky_draw_records", "visible_region_id"); err != nil || !hasColumn { + t.Fatalf("visible_region_id column missing after migrate: has=%v err=%v", hasColumn, err) + } + if hasIndex, err := repository.indexExists(context.Background(), "lucky_draw_records", "idx_lucky_draw_region_created"); err != nil || !hasIndex { + t.Fatalf("idx_lucky_draw_region_created missing after migrate: has=%v err=%v", hasIndex, err) + } + if hasTable, err := activityTableExists(context.Background(), repository, "lucky_draw_pool_day_stats"); err != nil || !hasTable { + t.Fatalf("lucky_draw_pool_day_stats table missing after migrate: has=%v err=%v", hasTable, err) + } +} + +func activityTableExists(ctx context.Context, repository *Repository, tableName string) (bool, error) { + var count int + err := repository.db.QueryRowContext(ctx, ` + SELECT COUNT(*) + FROM information_schema.tables + WHERE table_schema = DATABASE() AND table_name = ?`, tableName, + ).Scan(&count) + return count > 0, err } diff --git a/services/activity-service/internal/storage/mysql/room_turnover_reward_migration.go b/services/activity-service/internal/storage/mysql/room_turnover_reward_migration.go new file mode 100644 index 00000000..bbebcdee --- /dev/null +++ b/services/activity-service/internal/storage/mysql/room_turnover_reward_migration.go @@ -0,0 +1,87 @@ +package mysql + +import "context" + +func (r *Repository) ensureRoomTurnoverRewardTables(ctx context.Context) error { + statements := []string{ + `CREATE TABLE IF NOT EXISTS room_turnover_reward_configs ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + enabled TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用', + updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最后更新管理员', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励配置表'`, + `CREATE TABLE IF NOT EXISTS room_turnover_reward_tiers ( + tier_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '档位 ID', + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + tier_code VARCHAR(64) NOT NULL COMMENT '档位编码', + tier_name VARCHAR(128) NOT NULL COMMENT '档位名称', + threshold_coin_spent BIGINT NOT NULL COMMENT '命中该档需要的房间送礼金币流水', + reward_coin_amount BIGINT NOT NULL COMMENT '奖励房主金币数', + status VARCHAR(32) NOT NULL DEFAULT 'active' COMMENT 'active/inactive', + sort_order INT NOT NULL DEFAULT 0 COMMENT '排序', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (tier_id), + UNIQUE KEY uk_room_turnover_reward_tier_code (app_code, tier_code), + KEY idx_room_turnover_reward_tiers_match (app_code, status, threshold_coin_spent) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励档位表'`, + `CREATE TABLE IF NOT EXISTS room_turnover_reward_event_consumption ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + event_id VARCHAR(128) NOT NULL COMMENT 'room-service outbox event_id', + event_type VARCHAR(96) NOT NULL COMMENT '事件类型', + room_id VARCHAR(96) NOT NULL COMMENT '房间 ID', + period_start_ms BIGINT NOT NULL COMMENT 'UTC 周期开始', + coin_spent BIGINT NOT NULL COMMENT '本事件送礼金币消耗', + status VARCHAR(32) NOT NULL COMMENT '消费状态', + consumed_at_ms BIGINT NOT NULL COMMENT '消费时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, event_id), + KEY idx_room_turnover_reward_event_period (app_code, period_start_ms, room_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励事件消费幂等表'`, + `CREATE TABLE IF NOT EXISTS room_turnover_reward_progress ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + room_id VARCHAR(96) NOT NULL COMMENT '房间 ID', + period_start_ms BIGINT NOT NULL COMMENT 'UTC 周期开始', + period_end_ms BIGINT NOT NULL COMMENT 'UTC 周期结束', + coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '本周期房间送礼金币流水', + last_event_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '最后事件时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, room_id, period_start_ms), + KEY idx_room_turnover_reward_progress_period (app_code, period_start_ms, coin_spent) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励周期聚合表'`, + `CREATE TABLE IF NOT EXISTS room_turnover_reward_settlements ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + settlement_id VARCHAR(96) NOT NULL COMMENT '结算 ID', + room_id VARCHAR(96) NOT NULL COMMENT '房间 ID', + owner_user_id BIGINT NOT NULL DEFAULT 0 COMMENT '结算收款房主', + period_start_ms BIGINT NOT NULL COMMENT 'UTC 周期开始', + period_end_ms BIGINT NOT NULL COMMENT 'UTC 周期结束', + coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '周期房间流水', + tier_id BIGINT NOT NULL DEFAULT 0 COMMENT '命中档位 ID', + tier_code VARCHAR(64) NOT NULL DEFAULT '' COMMENT '命中档位编码', + threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '命中档位阈值', + reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '发放金币', + status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT 'pending/granted/failed', + wallet_command_id VARCHAR(160) NOT NULL DEFAULT '' COMMENT 'wallet-service 幂等命令 ID', + wallet_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包交易 ID', + failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因', + settled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '成功发放时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, settlement_id), + UNIQUE KEY uk_room_turnover_reward_settlement_room_period (app_code, room_id, period_start_ms), + KEY idx_room_turnover_reward_settlement_status (app_code, status, period_start_ms, created_at_ms), + KEY idx_room_turnover_reward_settlement_owner (app_code, owner_user_id, period_start_ms) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='房间流水奖励每周结算表'`, + } + for _, statement := range statements { + if _, err := r.db.ExecContext(ctx, statement); err != nil { + return err + } + } + return nil +} diff --git a/services/activity-service/internal/storage/mysql/room_turnover_reward_repository.go b/services/activity-service/internal/storage/mysql/room_turnover_reward_repository.go new file mode 100644 index 00000000..ced3f976 --- /dev/null +++ b/services/activity-service/internal/storage/mysql/room_turnover_reward_repository.go @@ -0,0 +1,562 @@ +package mysql + +import ( + "context" + "database/sql" + "errors" + "strconv" + "strings" + + "hyapp/pkg/appcode" + "hyapp/pkg/idgen" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/roomturnoverreward" +) + +// GetRoomTurnoverRewardConfig 读取房间流水奖励当前配置;未配置是后台首屏正常状态。 +func (r *Repository) GetRoomTurnoverRewardConfig(ctx context.Context) (domain.Config, bool, error) { + if r == nil || r.db == nil { + return domain.Config{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + row := r.db.QueryRowContext(ctx, roomTurnoverRewardConfigSelectSQL()+` + WHERE app_code = ?`, + appcode.FromContext(ctx), + ) + config, err := scanRoomTurnoverRewardConfig(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Config{}, false, nil + } + if err != nil { + return domain.Config{}, false, err + } + tiers, err := r.listRoomTurnoverRewardTiers(ctx, nil) + if err != nil { + return domain.Config{}, false, err + } + config.Tiers = tiers + return config, true, nil +} + +// UpdateRoomTurnoverRewardConfig 原子替换配置档位;已生成的 settlement 保留命中档位快照。 +func (r *Repository) UpdateRoomTurnoverRewardConfig(ctx context.Context, config domain.Config, nowMS int64) (domain.Config, error) { + if r == nil || r.db == nil { + return domain.Config{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return domain.Config{}, err + } + defer func() { _ = tx.Rollback() }() + + if _, err := tx.ExecContext(ctx, ` + INSERT INTO room_turnover_reward_configs ( + app_code, enabled, updated_by_admin_id, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + enabled = VALUES(enabled), + updated_by_admin_id = VALUES(updated_by_admin_id), + updated_at_ms = VALUES(updated_at_ms)`, + appcode.FromContext(ctx), config.Enabled, config.UpdatedByAdminID, nowMS, nowMS, + ); err != nil { + return domain.Config{}, err + } + if _, err := tx.ExecContext(ctx, ` + DELETE FROM room_turnover_reward_tiers + WHERE app_code = ?`, + appcode.FromContext(ctx), + ); err != nil { + return domain.Config{}, err + } + // 配置保存采用“配置行 upsert + 档位整组替换”,让后台表单提交成为完整快照,避免删除档位后旧行继续参与后续匹配。 + for i := range config.Tiers { + tier := config.Tiers[i] + tier.CreatedAtMS = nowMS + tier.UpdatedAtMS = nowMS + if tier.TierID > 0 { + if _, err := tx.ExecContext(ctx, ` + INSERT INTO room_turnover_reward_tiers ( + tier_id, app_code, tier_code, tier_name, threshold_coin_spent, + reward_coin_amount, status, sort_order, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + tier.TierID, appcode.FromContext(ctx), tier.TierCode, tier.TierName, tier.ThresholdCoinSpent, + tier.RewardCoinAmount, tier.Status, tier.SortOrder, tier.CreatedAtMS, tier.UpdatedAtMS, + ); err != nil { + return domain.Config{}, err + } + continue + } + result, err := tx.ExecContext(ctx, ` + INSERT INTO room_turnover_reward_tiers ( + app_code, tier_code, tier_name, threshold_coin_spent, + reward_coin_amount, status, sort_order, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + appcode.FromContext(ctx), tier.TierCode, tier.TierName, tier.ThresholdCoinSpent, + tier.RewardCoinAmount, tier.Status, tier.SortOrder, tier.CreatedAtMS, tier.UpdatedAtMS, + ) + if err != nil { + return domain.Config{}, err + } + tierID, err := result.LastInsertId() + if err != nil { + return domain.Config{}, err + } + config.Tiers[i].TierID = tierID + } + if err := tx.Commit(); err != nil { + return domain.Config{}, err + } + updated, exists, err := r.GetRoomTurnoverRewardConfig(ctx) + if err != nil { + return domain.Config{}, err + } + if !exists { + return domain.Config{}, xerr.New(xerr.NotFound, "room turnover reward config not found") + } + return updated, nil +} + +// ConsumeRoomTurnoverGiftEvent 幂等消费 RoomGiftSent,并累加到房间所在 UTC 周期流水。 +func (r *Repository) ConsumeRoomTurnoverGiftEvent(ctx context.Context, event domain.RoomGiftEvent, periodStartMS int64, periodEndMS int64, nowMS int64) (domain.EventResult, error) { + if r == nil || r.db == nil { + return domain.EventResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return domain.EventResult{}, err + } + defer func() { _ = tx.Rollback() }() + + result, err := tx.ExecContext(ctx, ` + INSERT IGNORE INTO room_turnover_reward_event_consumption ( + app_code, event_id, event_type, room_id, period_start_ms, coin_spent, + status, consumed_at_ms, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, 'consumed', ?, ?, ?)`, + appcode.FromContext(ctx), event.EventID, event.EventType, event.RoomID, periodStartMS, event.CoinSpent, nowMS, nowMS, nowMS, + ) + if err != nil { + return domain.EventResult{}, err + } + affected, err := result.RowsAffected() + if err != nil { + return domain.EventResult{}, err + } + if affected == 0 { + // event_id 是 room outbox 事实幂等键;重复投递只提交幂等事务,不再累加 progress,保证贡献值不会翻倍。 + if err := tx.Commit(); err != nil { + return domain.EventResult{}, err + } + return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusDuplicate}, nil + } + // progress 是 App H5 和结算任务共用的读模型;同一事务先落消费幂等,再按 UTC 周期原子累加房间流水。 + if _, err := tx.ExecContext(ctx, ` + INSERT INTO room_turnover_reward_progress ( + app_code, room_id, period_start_ms, period_end_ms, coin_spent, + last_event_at_ms, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + coin_spent = coin_spent + VALUES(coin_spent), + period_end_ms = VALUES(period_end_ms), + last_event_at_ms = GREATEST(last_event_at_ms, VALUES(last_event_at_ms)), + updated_at_ms = VALUES(updated_at_ms)`, + appcode.FromContext(ctx), event.RoomID, periodStartMS, periodEndMS, event.CoinSpent, event.OccurredAtMS, nowMS, nowMS, + ); err != nil { + return domain.EventResult{}, err + } + if err := tx.Commit(); err != nil { + return domain.EventResult{}, err + } + return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusConsumed}, nil +} + +// GetRoomTurnoverRewardProgress 返回指定房间和 UTC 周期的当前流水。 +func (r *Repository) GetRoomTurnoverRewardProgress(ctx context.Context, roomID string, periodStartMS int64) (domain.Progress, bool, error) { + if r == nil || r.db == nil { + return domain.Progress{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + row := r.db.QueryRowContext(ctx, roomTurnoverRewardProgressSelectSQL()+` + WHERE app_code = ? AND room_id = ? AND period_start_ms = ?`, + appcode.FromContext(ctx), strings.TrimSpace(roomID), periodStartMS, + ) + item, err := scanRoomTurnoverRewardProgress(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Progress{}, false, nil + } + return item, err == nil, err +} + +func (r *Repository) GetLatestRoomTurnoverRewardSettlement(ctx context.Context, roomID string) (domain.Settlement, bool, error) { + if r == nil || r.db == nil { + return domain.Settlement{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + // 最新结算给 H5 展示上一周期结果;按 period_start_ms 倒序而不是 status 排序,保证失败和成功都能被用户看到。 + row := r.db.QueryRowContext(ctx, roomTurnoverRewardSettlementSelectSQL()+` + WHERE app_code = ? AND room_id = ? + ORDER BY period_start_ms DESC, created_at_ms DESC + LIMIT 1`, + appcode.FromContext(ctx), strings.TrimSpace(roomID), + ) + item, err := scanRoomTurnoverRewardSettlement(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Settlement{}, false, nil + } + return item, err == nil, err +} + +// ListRoomTurnoverRewardSettlements 返回后台结算记录页;查询只命中 settlement 快照,不回扫事件或 progress。 +func (r *Repository) ListRoomTurnoverRewardSettlements(ctx context.Context, query domain.SettlementQuery) ([]domain.Settlement, int64, error) { + if r == nil || r.db == nil { + return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + // 后台列表只在 settlement 表上分页筛选;流水明细来自周期聚合快照,避免管理页回扫礼物事件。 + where := []string{"app_code = ?"} + args := []any{appcode.FromContext(ctx)} + if query.Status != "" { + where = append(where, "status = ?") + args = append(args, query.Status) + } + if strings.TrimSpace(query.RoomID) != "" { + where = append(where, "room_id = ?") + args = append(args, strings.TrimSpace(query.RoomID)) + } + if query.OwnerUserID > 0 { + where = append(where, "owner_user_id = ?") + args = append(args, query.OwnerUserID) + } + if query.PeriodStartMS > 0 { + where = append(where, "period_start_ms = ?") + args = append(args, query.PeriodStartMS) + } + clause := "WHERE " + strings.Join(where, " AND ") + var total int64 + if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM room_turnover_reward_settlements `+clause, args...).Scan(&total); err != nil { + return nil, 0, err + } + pageSize := int(query.PageSize) + if pageSize <= 0 { + pageSize = 20 + } + if pageSize > 100 { + pageSize = 100 + } + page := int(query.Page) + if page <= 0 { + page = 1 + } + args = append(args, pageSize, (page-1)*pageSize) + rows, err := r.db.QueryContext(ctx, roomTurnoverRewardSettlementSelectSQL()+clause+` + ORDER BY period_start_ms DESC, created_at_ms DESC + LIMIT ? OFFSET ?`, args...) + if err != nil { + return nil, 0, err + } + defer rows.Close() + items, err := scanRoomTurnoverRewardSettlements(rows) + return items, total, err +} + +// ListUnsettledRoomTurnoverRewardProgress 找出上一周期有流水但还没有 settlement 的房间,用于 cron 补齐结算记录。 +func (r *Repository) ListUnsettledRoomTurnoverRewardProgress(ctx context.Context, periodStartMS int64, limit int) ([]domain.Progress, error) { + if r == nil || r.db == nil { + return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + if limit <= 0 { + limit = 100 + } + // LEFT JOIN settlement 只认领还没有结算快照的 progress;唯一键再兜底一次,防止并发 worker 同时扫描到同一房间。 + rows, err := r.db.QueryContext(ctx, roomTurnoverRewardProgressSelectSQL()+` + LEFT JOIN room_turnover_reward_settlements settlement + ON settlement.app_code = progress.app_code + AND settlement.room_id = progress.room_id + AND settlement.period_start_ms = progress.period_start_ms + WHERE progress.app_code = ? AND progress.period_start_ms = ? + AND progress.coin_spent > 0 AND settlement.settlement_id IS NULL + ORDER BY progress.coin_spent DESC, progress.room_id ASC + LIMIT ?`, + appcode.FromContext(ctx), periodStartMS, limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + return scanRoomTurnoverRewardProgressRows(rows) +} + +// InsertRoomTurnoverRewardSettlement 创建单房间单周期 settlement;已存在时读回原记录并返回 created=false。 +func (r *Repository) InsertRoomTurnoverRewardSettlement(ctx context.Context, settlement domain.Settlement, nowMS int64) (domain.Settlement, bool, error) { + if r == nil || r.db == nil { + return domain.Settlement{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + if settlement.SettlementID == "" { + settlement.SettlementID = idgen.New("rtrsettle") + } + if settlement.WalletCommandID == "" { + settlement.WalletCommandID = walletRoomTurnoverRewardCommandID(settlement.AppCode, settlement.PeriodStartMS, settlement.RoomID) + } + settlement.CreatedAtMS = nowMS + settlement.UpdatedAtMS = nowMS + if settlement.Status == "" { + settlement.Status = domain.SettlementStatusPending + } + // room_id + period_start_ms 是业务唯一键;cron 重跑、并发 worker 或人工补偿都只能为同一房间周期保留一条 settlement。 + result, err := r.db.ExecContext(ctx, ` + INSERT IGNORE INTO room_turnover_reward_settlements ( + app_code, settlement_id, room_id, owner_user_id, period_start_ms, period_end_ms, + coin_spent, tier_id, tier_code, threshold_coin_spent, reward_coin_amount, + status, wallet_command_id, wallet_transaction_id, failure_reason, + settled_at_ms, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '', ?, 0, ?, ?)`, + appcode.FromContext(ctx), settlement.SettlementID, settlement.RoomID, settlement.OwnerUserID, + settlement.PeriodStartMS, settlement.PeriodEndMS, settlement.CoinSpent, settlement.TierID, + settlement.TierCode, settlement.ThresholdCoinSpent, settlement.RewardCoinAmount, + settlement.Status, settlement.WalletCommandID, settlement.FailureReason, nowMS, nowMS, + ) + if err != nil { + return domain.Settlement{}, false, err + } + affected, err := result.RowsAffected() + if err != nil { + return domain.Settlement{}, false, err + } + item, exists, err := r.getRoomTurnoverRewardSettlementByRoomPeriod(ctx, settlement.RoomID, settlement.PeriodStartMS) + return item, affected > 0 && exists, err +} + +// ListPendingRoomTurnoverRewardSettlements 返回等待发奖的 settlement;按老周期优先处理,避免历史失败恢复后长期排队。 +func (r *Repository) ListPendingRoomTurnoverRewardSettlements(ctx context.Context, limit int) ([]domain.Settlement, error) { + if r == nil || r.db == nil { + return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + if limit <= 0 { + limit = 100 + } + rows, err := r.db.QueryContext(ctx, roomTurnoverRewardSettlementSelectSQL()+` + WHERE app_code = ? AND status = ? + ORDER BY period_start_ms ASC, created_at_ms ASC + LIMIT ?`, + appcode.FromContext(ctx), domain.SettlementStatusPending, limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + return scanRoomTurnoverRewardSettlements(rows) +} + +// GetRoomTurnoverRewardSettlement 按后台传入的 settlement_id 读取单条结算记录。 +func (r *Repository) GetRoomTurnoverRewardSettlement(ctx context.Context, settlementID string) (domain.Settlement, bool, error) { + if r == nil || r.db == nil { + return domain.Settlement{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + row := r.db.QueryRowContext(ctx, roomTurnoverRewardSettlementSelectSQL()+` + WHERE app_code = ? AND settlement_id = ?`, + appcode.FromContext(ctx), strings.TrimSpace(settlementID), + ) + item, err := scanRoomTurnoverRewardSettlement(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Settlement{}, false, nil + } + return item, err == nil, err +} + +// ResetRoomTurnoverRewardSettlementForRetry 把失败记录重新放回 pending,并在 retry 时写入最新可用房主。 +func (r *Repository) ResetRoomTurnoverRewardSettlementForRetry(ctx context.Context, settlementID string, ownerUserID int64, nowMS int64) (domain.Settlement, error) { + if r == nil || r.db == nil { + return domain.Settlement{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + if _, err := r.db.ExecContext(ctx, ` + UPDATE room_turnover_reward_settlements + SET status = ?, owner_user_id = ?, failure_reason = '', updated_at_ms = ? + WHERE app_code = ? AND settlement_id = ? AND status <> ?`, + domain.SettlementStatusPending, ownerUserID, nowMS, appcode.FromContext(ctx), strings.TrimSpace(settlementID), domain.SettlementStatusGranted, + ); err != nil { + return domain.Settlement{}, err + } + // retry 只能把未发奖记录重新放回 pending;已经 granted 的记录不会被改回去,防止后台误点造成二次发奖。 + item, exists, err := r.GetRoomTurnoverRewardSettlement(ctx, settlementID) + if err != nil { + return domain.Settlement{}, err + } + if !exists { + return domain.Settlement{}, xerr.New(xerr.NotFound, "room turnover reward settlement not found") + } + return item, nil +} + +// MarkRoomTurnoverRewardSettlementGranted 记录钱包成功入账结果,是 activity 侧发奖状态的最终成功态。 +func (r *Repository) MarkRoomTurnoverRewardSettlementGranted(ctx context.Context, settlementID string, walletTransactionID string, grantedAtMS int64) (domain.Settlement, error) { + if r == nil || r.db == nil { + return domain.Settlement{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + // 只有钱包 RPC 成功后才写 granted,并保存 wallet_transaction_id,后台和对账都以这条链路追踪真实入账。 + if _, err := r.db.ExecContext(ctx, ` + UPDATE room_turnover_reward_settlements + SET status = ?, wallet_transaction_id = ?, failure_reason = '', settled_at_ms = ?, updated_at_ms = ? + WHERE app_code = ? AND settlement_id = ?`, + domain.SettlementStatusGranted, walletTransactionID, grantedAtMS, grantedAtMS, appcode.FromContext(ctx), strings.TrimSpace(settlementID), + ); err != nil { + return domain.Settlement{}, err + } + item, exists, err := r.GetRoomTurnoverRewardSettlement(ctx, settlementID) + if err != nil { + return domain.Settlement{}, err + } + if !exists { + return domain.Settlement{}, xerr.New(xerr.NotFound, "room turnover reward settlement not found") + } + return item, nil +} + +// MarkRoomTurnoverRewardSettlementFailed 写入可展示、可 retry 的失败原因。 +func (r *Repository) MarkRoomTurnoverRewardSettlementFailed(ctx context.Context, settlementID string, reason string, nowMS int64) error { + if r == nil || r.db == nil { + return xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + // 失败标记不会覆盖 granted,避免钱包已成功但 activity-service 更新重试时被后续错误回写成失败。 + _, err := r.db.ExecContext(ctx, ` + UPDATE room_turnover_reward_settlements + SET status = ?, failure_reason = ?, updated_at_ms = ? + WHERE app_code = ? AND settlement_id = ? AND status <> ?`, + domain.SettlementStatusFailed, trimFailureReason(reason), nowMS, appcode.FromContext(ctx), strings.TrimSpace(settlementID), domain.SettlementStatusGranted, + ) + return err +} + +func (r *Repository) getRoomTurnoverRewardSettlementByRoomPeriod(ctx context.Context, roomID string, periodStartMS int64) (domain.Settlement, bool, error) { + row := r.db.QueryRowContext(ctx, roomTurnoverRewardSettlementSelectSQL()+` + WHERE app_code = ? AND room_id = ? AND period_start_ms = ?`, + appcode.FromContext(ctx), strings.TrimSpace(roomID), periodStartMS, + ) + item, err := scanRoomTurnoverRewardSettlement(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Settlement{}, false, nil + } + return item, err == nil, err +} + +func roomTurnoverRewardConfigSelectSQL() string { + return `SELECT app_code, enabled, updated_by_admin_id, created_at_ms, updated_at_ms + FROM room_turnover_reward_configs ` +} + +func roomTurnoverRewardTierSelectSQL() string { + return `SELECT tier_id, app_code, tier_code, tier_name, threshold_coin_spent, + reward_coin_amount, status, sort_order, created_at_ms, updated_at_ms + FROM room_turnover_reward_tiers ` +} + +func roomTurnoverRewardProgressSelectSQL() string { + return `SELECT progress.app_code, progress.room_id, progress.period_start_ms, progress.period_end_ms, + progress.coin_spent, progress.last_event_at_ms, progress.created_at_ms, progress.updated_at_ms + FROM room_turnover_reward_progress progress ` +} + +func roomTurnoverRewardSettlementSelectSQL() string { + return `SELECT app_code, settlement_id, room_id, owner_user_id, period_start_ms, period_end_ms, + coin_spent, tier_id, tier_code, threshold_coin_spent, reward_coin_amount, + status, wallet_command_id, wallet_transaction_id, failure_reason, + settled_at_ms, created_at_ms, updated_at_ms + FROM room_turnover_reward_settlements ` +} + +type roomTurnoverRewardConfigScanner interface { + Scan(dest ...any) error +} + +func scanRoomTurnoverRewardConfig(scanner roomTurnoverRewardConfigScanner) (domain.Config, error) { + var item domain.Config + err := scanner.Scan(&item.AppCode, &item.Enabled, &item.UpdatedByAdminID, &item.CreatedAtMS, &item.UpdatedAtMS) + return item, err +} + +func (r *Repository) listRoomTurnoverRewardTiers(ctx context.Context, tx *sql.Tx) ([]domain.Tier, error) { + query := roomTurnoverRewardTierSelectSQL() + ` + WHERE app_code = ? + ORDER BY sort_order ASC, threshold_coin_spent ASC, tier_id ASC` + var rows *sql.Rows + var err error + if tx != nil { + rows, err = tx.QueryContext(ctx, query, appcode.FromContext(ctx)) + } else { + rows, err = r.db.QueryContext(ctx, query, appcode.FromContext(ctx)) + } + if err != nil { + return nil, err + } + defer rows.Close() + items := make([]domain.Tier, 0) + for rows.Next() { + var item domain.Tier + var app string + if err := rows.Scan(&item.TierID, &app, &item.TierCode, &item.TierName, &item.ThresholdCoinSpent, &item.RewardCoinAmount, &item.Status, &item.SortOrder, &item.CreatedAtMS, &item.UpdatedAtMS); err != nil { + return nil, err + } + items = append(items, item) + } + return items, rows.Err() +} + +type roomTurnoverRewardProgressScanner interface { + Scan(dest ...any) error +} + +func scanRoomTurnoverRewardProgress(scanner roomTurnoverRewardProgressScanner) (domain.Progress, error) { + var item domain.Progress + err := scanner.Scan(&item.AppCode, &item.RoomID, &item.PeriodStartMS, &item.PeriodEndMS, &item.CoinSpent, &item.LastEventAtMS, &item.CreatedAtMS, &item.UpdatedAtMS) + return item, err +} + +func scanRoomTurnoverRewardProgressRows(rows *sql.Rows) ([]domain.Progress, error) { + items := make([]domain.Progress, 0) + for rows.Next() { + item, err := scanRoomTurnoverRewardProgress(rows) + if err != nil { + return nil, err + } + items = append(items, item) + } + return items, rows.Err() +} + +type roomTurnoverRewardSettlementScanner interface { + Scan(dest ...any) error +} + +func scanRoomTurnoverRewardSettlement(scanner roomTurnoverRewardSettlementScanner) (domain.Settlement, error) { + var item domain.Settlement + err := scanner.Scan( + &item.AppCode, &item.SettlementID, &item.RoomID, &item.OwnerUserID, + &item.PeriodStartMS, &item.PeriodEndMS, &item.CoinSpent, + &item.TierID, &item.TierCode, &item.ThresholdCoinSpent, &item.RewardCoinAmount, + &item.Status, &item.WalletCommandID, &item.WalletTransactionID, &item.FailureReason, + &item.SettledAtMS, &item.CreatedAtMS, &item.UpdatedAtMS, + ) + return item, err +} + +func scanRoomTurnoverRewardSettlements(rows *sql.Rows) ([]domain.Settlement, error) { + items := make([]domain.Settlement, 0) + for rows.Next() { + item, err := scanRoomTurnoverRewardSettlement(rows) + if err != nil { + return nil, err + } + items = append(items, item) + } + return items, rows.Err() +} + +func walletRoomTurnoverRewardCommandID(app string, periodStartMS int64, roomID string) string { + return "room_turnover_reward:" + appcode.Normalize(app) + ":" + int64String(periodStartMS) + ":" + strings.TrimSpace(roomID) +} + +func int64String(value int64) string { + return strconv.FormatInt(value, 10) +} + +func trimFailureReason(reason string) string { + reason = strings.TrimSpace(reason) + if len(reason) > 512 { + return reason[:512] + } + return reason +} diff --git a/services/activity-service/internal/storage/mysql/weekly_star_migration.go b/services/activity-service/internal/storage/mysql/weekly_star_migration.go new file mode 100644 index 00000000..74be0013 --- /dev/null +++ b/services/activity-service/internal/storage/mysql/weekly_star_migration.go @@ -0,0 +1,96 @@ +package mysql + +import "context" + +func (r *Repository) ensureWeeklyStarTables(ctx context.Context) error { + statements := []string{ + `CREATE TABLE IF NOT EXISTS weekly_star_cycles ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + activity_code VARCHAR(64) NOT NULL DEFAULT 'weekly_star' COMMENT '活动编码', + region_id BIGINT NOT NULL DEFAULT 0 COMMENT '区域 ID,0 表示默认配置', + title VARCHAR(128) NOT NULL DEFAULT '' COMMENT '周期标题', + status VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT '状态', + start_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 生效开始,包含', + end_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 生效结束,不包含', + created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建管理员', + updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新管理员', + settled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算完成时间', + locked_by VARCHAR(96) NOT NULL DEFAULT '' COMMENT '结算 worker 锁', + lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算锁过期时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id), + KEY idx_weekly_star_cycle_current (app_code, activity_code, status, region_id, start_ms, end_ms), + KEY idx_weekly_star_cycle_due (app_code, activity_code, status, end_ms, lock_until_ms) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星指定礼物积分周期'`, + `CREATE TABLE IF NOT EXISTS weekly_star_cycle_gifts ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + gift_id VARCHAR(96) NOT NULL COMMENT '参与计分礼物 ID', + sort_order INT NOT NULL DEFAULT 0 COMMENT '展示顺序', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id, gift_id), + KEY idx_weekly_star_gift_match (app_code, gift_id, cycle_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星周期指定礼物'`, + `CREATE TABLE IF NOT EXISTS weekly_star_cycle_rewards ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + rank_no INT NOT NULL COMMENT '排名,1/2/3', + resource_group_id BIGINT NOT NULL COMMENT '奖励资源组 ID', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id, rank_no) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星周期 Top 奖励'`, + `CREATE TABLE IF NOT EXISTS weekly_star_user_scores ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + user_id BIGINT NOT NULL COMMENT '用户 ID', + score BIGINT NOT NULL DEFAULT 0 COMMENT '累计积分,等于活动礼物消耗金币', + first_scored_at_ms BIGINT NOT NULL COMMENT '首次得分时间', + last_scored_at_ms BIGINT NOT NULL COMMENT '最近得分时间', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, cycle_id, user_id), + KEY idx_weekly_star_score_rank (app_code, cycle_id, score, first_scored_at_ms, user_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星用户积分榜'`, + `CREATE TABLE IF NOT EXISTS weekly_star_score_events ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + source_event_id VARCHAR(128) NOT NULL COMMENT 'room-service outbox event_id', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + user_id BIGINT NOT NULL COMMENT '送礼用户 ID', + gift_id VARCHAR(96) NOT NULL COMMENT '礼物 ID', + score_delta BIGINT NOT NULL COMMENT '本次增加积分', + occurred_at_ms BIGINT NOT NULL COMMENT '事件发生时间,UTC epoch ms', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + PRIMARY KEY (app_code, source_event_id), + KEY idx_weekly_star_score_event_cycle (app_code, cycle_id, user_id, occurred_at_ms) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星送礼积分事件幂等表'`, + `CREATE TABLE IF NOT EXISTS weekly_star_settlements ( + app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码', + settlement_id VARCHAR(96) NOT NULL COMMENT '周星结算 ID', + cycle_id VARCHAR(96) NOT NULL COMMENT '周星周期 ID', + rank_no INT NOT NULL COMMENT '排名,1/2/3', + user_id BIGINT NOT NULL COMMENT '获奖用户 ID', + score BIGINT NOT NULL DEFAULT 0 COMMENT '结算积分', + resource_group_id BIGINT NOT NULL COMMENT '奖励资源组 ID', + wallet_command_id VARCHAR(128) NOT NULL COMMENT 'wallet 幂等命令', + wallet_grant_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'wallet grant ID', + status VARCHAR(32) NOT NULL DEFAULT 'pending' COMMENT '发放状态', + failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因', + attempt_count INT NOT NULL DEFAULT 0 COMMENT '尝试次数', + created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms', + updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms', + PRIMARY KEY (app_code, settlement_id), + UNIQUE KEY uk_weekly_star_settlement_rank (app_code, cycle_id, rank_no), + UNIQUE KEY uk_weekly_star_wallet_command (app_code, wallet_command_id), + KEY idx_weekly_star_settlement_cycle (app_code, cycle_id, status) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星 Top 奖励结算'`, + } + for _, statement := range statements { + if _, err := r.db.ExecContext(ctx, statement); err != nil { + return err + } + } + return nil +} diff --git a/services/activity-service/internal/storage/mysql/weekly_star_repository.go b/services/activity-service/internal/storage/mysql/weekly_star_repository.go new file mode 100644 index 00000000..42f8a243 --- /dev/null +++ b/services/activity-service/internal/storage/mysql/weekly_star_repository.go @@ -0,0 +1,876 @@ +package mysql + +import ( + "context" + "database/sql" + "errors" + "fmt" + "strconv" + "strings" + "time" + + "hyapp/pkg/appcode" + "hyapp/pkg/idgen" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/weeklystar" +) + +// ListWeeklyStarCycles returns admin-configured cycles with gifts and rewards attached for editing. +func (r *Repository) ListWeeklyStarCycles(ctx context.Context, query domain.ListQuery) ([]domain.Cycle, int64, error) { + if r == nil || r.db == nil { + return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + where, args := weeklyStarCycleWhere(ctx, query) + var total int64 + if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM weekly_star_cycles `+where, args...).Scan(&total); err != nil { + return nil, 0, err + } + pageSize := normalizePageSize(query.PageSize) + page := query.Page + if page <= 0 { + page = 1 + } + rows, err := r.db.QueryContext(ctx, ` + SELECT app_code, cycle_id, activity_code, region_id, title, status, start_ms, end_ms, + created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms + FROM weekly_star_cycles `+where+` + ORDER BY start_ms DESC, region_id ASC, cycle_id DESC + LIMIT ? OFFSET ?`, append(args, int(pageSize), int((page-1)*pageSize))...) + if err != nil { + return nil, 0, err + } + defer rows.Close() + cycles := make([]domain.Cycle, 0) + for rows.Next() { + cycle, scanErr := scanWeeklyStarCycle(rows) + if scanErr != nil { + return nil, 0, scanErr + } + cycles = append(cycles, cycle) + } + if err := rows.Err(); err != nil { + return nil, 0, err + } + if err := r.attachWeeklyStarChildren(ctx, cycles); err != nil { + return nil, 0, err + } + return cycles, total, nil +} + +// GetWeeklyStarCycle returns the authoritative cycle row and its editable child rows. +func (r *Repository) GetWeeklyStarCycle(ctx context.Context, cycleID string) (domain.Cycle, error) { + if r == nil || r.db == nil { + return domain.Cycle{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + cycle, err := r.getWeeklyStarCycle(ctx, r.db, cycleID) + if err != nil { + return domain.Cycle{}, err + } + cycles := []domain.Cycle{cycle} + if err := r.attachWeeklyStarChildren(ctx, cycles); err != nil { + return domain.Cycle{}, err + } + return cycles[0], nil +} + +// UpsertWeeklyStarCycle replaces a full cycle configuration in one transaction so gifts and rewards stay consistent. +func (r *Repository) UpsertWeeklyStarCycle(ctx context.Context, command domain.CycleCommand, nowMS int64) (domain.Cycle, bool, error) { + if r == nil || r.db == nil { + return domain.Cycle{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return domain.Cycle{}, false, err + } + defer tx.Rollback() + cycle := command.Cycle + cycle.AppCode = appcode.FromContext(ctx) + cycle.ActivityCode = domain.ActivityCode + if cycle.Status == "" { + cycle.Status = domain.StatusDraft + } + created := strings.TrimSpace(cycle.CycleID) == "" + if !created { + if _, err := r.getWeeklyStarCycle(ctx, tx, cycle.CycleID); err != nil { + return domain.Cycle{}, false, err + } + } else if !command.RequireNewRecord { + return domain.Cycle{}, false, xerr.New(xerr.InvalidArgument, "cycle_id is required") + } + if created { + cycle.CycleID = idgen.New("wstar") + cycle.CreatedAtMS = nowMS + cycle.CreatedByAdminID = command.OperatorAdminID + } + cycle.UpdatedAtMS = nowMS + cycle.UpdatedByAdminID = command.OperatorAdminID + if err := r.checkWeeklyStarCycleOverlap(ctx, tx, cycle); err != nil { + return domain.Cycle{}, false, err + } + if created { + if _, err := tx.ExecContext(ctx, ` + INSERT INTO weekly_star_cycles ( + app_code, cycle_id, activity_code, region_id, title, status, start_ms, end_ms, + created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)`, + cycle.AppCode, cycle.CycleID, cycle.ActivityCode, cycle.RegionID, cycle.Title, cycle.Status, cycle.StartMS, cycle.EndMS, + cycle.CreatedByAdminID, cycle.UpdatedByAdminID, cycle.CreatedAtMS, cycle.UpdatedAtMS, + ); err != nil { + return domain.Cycle{}, false, err + } + } else { + if _, err := tx.ExecContext(ctx, ` + UPDATE weekly_star_cycles + SET region_id = ?, title = ?, status = ?, start_ms = ?, end_ms = ?, + updated_by_admin_id = ?, updated_at_ms = ? + WHERE app_code = ? AND cycle_id = ?`, + cycle.RegionID, cycle.Title, cycle.Status, cycle.StartMS, cycle.EndMS, + cycle.UpdatedByAdminID, cycle.UpdatedAtMS, cycle.AppCode, cycle.CycleID, + ); err != nil { + return domain.Cycle{}, false, err + } + } + if _, err := tx.ExecContext(ctx, `DELETE FROM weekly_star_cycle_gifts WHERE app_code = ? AND cycle_id = ?`, cycle.AppCode, cycle.CycleID); err != nil { + return domain.Cycle{}, false, err + } + for index, gift := range cycle.Gifts { + sortOrder := gift.SortOrder + if sortOrder <= 0 { + sortOrder = int32(index + 1) + } + if _, err := tx.ExecContext(ctx, ` + INSERT INTO weekly_star_cycle_gifts (app_code, cycle_id, gift_id, sort_order, created_at_ms) + VALUES (?, ?, ?, ?, ?)`, + cycle.AppCode, cycle.CycleID, gift.GiftID, sortOrder, nowMS, + ); err != nil { + return domain.Cycle{}, false, err + } + } + if _, err := tx.ExecContext(ctx, `DELETE FROM weekly_star_cycle_rewards WHERE app_code = ? AND cycle_id = ?`, cycle.AppCode, cycle.CycleID); err != nil { + return domain.Cycle{}, false, err + } + for _, reward := range cycle.Rewards { + if _, err := tx.ExecContext(ctx, ` + INSERT INTO weekly_star_cycle_rewards (app_code, cycle_id, rank_no, resource_group_id, created_at_ms, updated_at_ms) + VALUES (?, ?, ?, ?, ?, ?)`, + cycle.AppCode, cycle.CycleID, reward.RankNo, reward.ResourceGroupID, nowMS, nowMS, + ); err != nil { + return domain.Cycle{}, false, err + } + } + if err := tx.Commit(); err != nil { + return domain.Cycle{}, false, err + } + stored, err := r.GetWeeklyStarCycle(ctx, cycle.CycleID) + return stored, created, err +} + +// SetWeeklyStarCycleStatus changes visibility without mutating gifts, rewards, or score facts. +func (r *Repository) SetWeeklyStarCycleStatus(ctx context.Context, cycleID string, status string, operatorAdminID int64, nowMS int64) (domain.Cycle, error) { + if r == nil || r.db == nil { + return domain.Cycle{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return domain.Cycle{}, err + } + defer tx.Rollback() + cycle, err := r.getWeeklyStarCycle(ctx, tx, cycleID) + if err != nil { + return domain.Cycle{}, err + } + next := cycle + next.Status = status + if err := r.checkWeeklyStarCycleOverlap(ctx, tx, next); err != nil { + return domain.Cycle{}, err + } + if _, err := tx.ExecContext(ctx, ` + UPDATE weekly_star_cycles + SET status = ?, updated_by_admin_id = ?, updated_at_ms = ? + WHERE app_code = ? AND cycle_id = ?`, + status, operatorAdminID, nowMS, appcode.FromContext(ctx), cycleID, + ); err != nil { + return domain.Cycle{}, err + } + if err := tx.Commit(); err != nil { + return domain.Cycle{}, err + } + return r.GetWeeklyStarCycle(ctx, cycleID) +} + +// FindCurrentWeeklyStarCycle resolves the active concrete-region cycle first and falls back to region 0. +func (r *Repository) FindCurrentWeeklyStarCycle(ctx context.Context, regionID int64, nowMS int64) (domain.Cycle, bool, error) { + if r == nil || r.db == nil { + return domain.Cycle{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + row := r.db.QueryRowContext(ctx, ` + SELECT app_code, cycle_id, activity_code, region_id, title, status, start_ms, end_ms, + created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms + FROM weekly_star_cycles + WHERE app_code = ? AND activity_code = ? AND status = ? AND start_ms <= ? AND end_ms > ? + AND region_id IN (?, 0) + ORDER BY CASE WHEN region_id = ? THEN 0 ELSE 1 END, start_ms DESC, updated_at_ms DESC + LIMIT 1`, + appcode.FromContext(ctx), domain.ActivityCode, domain.StatusActive, nowMS, nowMS, regionID, regionID, + ) + cycle, err := scanWeeklyStarCycle(row) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return domain.Cycle{}, false, nil + } + return domain.Cycle{}, false, err + } + cycles := []domain.Cycle{cycle} + if err := r.attachWeeklyStarChildren(ctx, cycles); err != nil { + return domain.Cycle{}, false, err + } + return cycles[0], true, nil +} + +// ConsumeWeeklyStarGiftEvent stores source-event idempotency before adding the score delta. +func (r *Repository) ConsumeWeeklyStarGiftEvent(ctx context.Context, event domain.GiftEvent, nowMS int64) (domain.EventResult, error) { + if r == nil || r.db == nil { + return domain.EventResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return domain.EventResult{}, err + } + defer tx.Rollback() + cycle, ok, err := r.findCurrentWeeklyStarCycleForGift(ctx, tx, event.RegionID, event.GiftID, event.OccurredAtMS) + if err != nil { + return domain.EventResult{}, err + } + if !ok { + return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusSkipped}, nil + } + if _, err := tx.ExecContext(ctx, ` + INSERT INTO weekly_star_score_events ( + app_code, source_event_id, cycle_id, user_id, gift_id, score_delta, occurred_at_ms, created_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + appcode.FromContext(ctx), event.EventID, cycle.CycleID, event.UserID, event.GiftID, event.ScoreDelta, event.OccurredAtMS, nowMS, + ); err != nil { + if isMySQLDuplicate(err) { + return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusDuplicate, CycleID: cycle.CycleID}, nil + } + return domain.EventResult{}, err + } + if _, err := tx.ExecContext(ctx, ` + INSERT INTO weekly_star_user_scores ( + app_code, cycle_id, user_id, score, first_scored_at_ms, last_scored_at_ms, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + score = score + VALUES(score), + first_scored_at_ms = LEAST(first_scored_at_ms, VALUES(first_scored_at_ms)), + last_scored_at_ms = GREATEST(last_scored_at_ms, VALUES(last_scored_at_ms)), + updated_at_ms = VALUES(updated_at_ms)`, + appcode.FromContext(ctx), cycle.CycleID, event.UserID, event.ScoreDelta, event.OccurredAtMS, event.OccurredAtMS, nowMS, nowMS, + ); err != nil { + return domain.EventResult{}, err + } + if err := tx.Commit(); err != nil { + return domain.EventResult{}, err + } + return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusConsumed, CycleID: cycle.CycleID, ScoreDelta: event.ScoreDelta}, nil +} + +// ListWeeklyStarLeaderboard returns ranked score rows with stable pagination by offset token. +func (r *Repository) ListWeeklyStarLeaderboard(ctx context.Context, cycleID string, regionID int64, pageSize int32, pageToken string, nowMS int64) (domain.Cycle, []domain.LeaderboardEntry, string, int64, error) { + if r == nil || r.db == nil { + return domain.Cycle{}, nil, "", 0, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + var cycle domain.Cycle + var err error + if strings.TrimSpace(cycleID) == "" { + var ok bool + cycle, ok, err = r.FindCurrentWeeklyStarCycle(ctx, regionID, nowMS) + if err != nil || !ok { + return domain.Cycle{}, nil, "", 0, err + } + } else { + cycle, err = r.GetWeeklyStarCycle(ctx, cycleID) + if err != nil { + return domain.Cycle{}, nil, "", 0, err + } + } + size := int(normalizePageSize(pageSize)) + offset := parseWeeklyStarPageToken(pageToken) + var total int64 + if err := r.db.QueryRowContext(ctx, ` + SELECT COUNT(*) FROM weekly_star_user_scores WHERE app_code = ? AND cycle_id = ?`, + appcode.FromContext(ctx), cycle.CycleID, + ).Scan(&total); err != nil { + return domain.Cycle{}, nil, "", 0, err + } + rows, err := r.db.QueryContext(ctx, ` + SELECT user_id, score, first_scored_at_ms, last_scored_at_ms + FROM weekly_star_user_scores + WHERE app_code = ? AND cycle_id = ? + ORDER BY score DESC, first_scored_at_ms ASC, user_id ASC + LIMIT ? OFFSET ?`, + appcode.FromContext(ctx), cycle.CycleID, size, offset, + ) + if err != nil { + return domain.Cycle{}, nil, "", 0, err + } + defer rows.Close() + entries := make([]domain.LeaderboardEntry, 0, size) + for rows.Next() { + var entry domain.LeaderboardEntry + entry.RankNo = int32(offset + len(entries) + 1) + if err := rows.Scan(&entry.UserID, &entry.Score, &entry.FirstScoredAtMS, &entry.LastScoredAtMS); err != nil { + return domain.Cycle{}, nil, "", 0, err + } + entries = append(entries, entry) + } + if err := rows.Err(); err != nil { + return domain.Cycle{}, nil, "", 0, err + } + nextToken := "" + if int64(offset+len(entries)) < total { + nextToken = strconv.Itoa(offset + len(entries)) + } + return cycle, entries, nextToken, total, nil +} + +// GetWeeklyStarUserEntry computes the user's rank using the same score/first-time/user-id ordering as the leaderboard. +func (r *Repository) GetWeeklyStarUserEntry(ctx context.Context, cycleID string, userID int64) (domain.LeaderboardEntry, bool, error) { + if r == nil || r.db == nil { + return domain.LeaderboardEntry{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + var entry domain.LeaderboardEntry + if err := r.db.QueryRowContext(ctx, ` + SELECT user_id, score, first_scored_at_ms, last_scored_at_ms + FROM weekly_star_user_scores + WHERE app_code = ? AND cycle_id = ? AND user_id = ?`, + appcode.FromContext(ctx), cycleID, userID, + ).Scan(&entry.UserID, &entry.Score, &entry.FirstScoredAtMS, &entry.LastScoredAtMS); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return domain.LeaderboardEntry{}, false, nil + } + return domain.LeaderboardEntry{}, false, err + } + if err := r.db.QueryRowContext(ctx, ` + SELECT COUNT(*) + 1 + FROM weekly_star_user_scores + WHERE app_code = ? AND cycle_id = ? + AND (score > ? OR (score = ? AND first_scored_at_ms < ?) OR (score = ? AND first_scored_at_ms = ? AND user_id < ?))`, + appcode.FromContext(ctx), cycleID, entry.Score, entry.Score, entry.FirstScoredAtMS, entry.Score, entry.FirstScoredAtMS, userID, + ).Scan(&entry.RankNo); err != nil { + return domain.LeaderboardEntry{}, false, err + } + return entry, true, nil +} + +// ListWeeklyStarHistory returns completed cycles, prioritizing a concrete region over default when both exist. +func (r *Repository) ListWeeklyStarHistory(ctx context.Context, regionID int64, limit int32, nowMS int64) ([]domain.HistoryCycle, error) { + if r == nil || r.db == nil { + return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + if limit <= 0 { + limit = 10 + } + if limit > 50 { + limit = 50 + } + rows, err := r.db.QueryContext(ctx, ` + SELECT app_code, cycle_id, activity_code, region_id, title, status, start_ms, end_ms, + created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms + FROM weekly_star_cycles + WHERE app_code = ? AND activity_code = ? AND status = ? AND region_id IN (?, 0) + ORDER BY end_ms DESC, CASE WHEN region_id = ? THEN 0 ELSE 1 END, cycle_id DESC + LIMIT ?`, + appcode.FromContext(ctx), domain.ActivityCode, domain.StatusSettled, regionID, regionID, int(limit), + ) + if err != nil { + return nil, err + } + defer rows.Close() + history := make([]domain.HistoryCycle, 0) + for rows.Next() { + cycle, scanErr := scanWeeklyStarCycle(rows) + if scanErr != nil { + return nil, scanErr + } + _, entries, _, _, listErr := r.ListWeeklyStarLeaderboard(ctx, cycle.CycleID, regionID, 3, "", nowMS) + if listErr != nil { + return nil, listErr + } + history = append(history, domain.HistoryCycle{Cycle: cycle, TopEntries: entries}) + } + return history, rows.Err() +} + +// ListWeeklyStarSettlements returns the wallet grant jobs created for a cycle settlement. +func (r *Repository) ListWeeklyStarSettlements(ctx context.Context, cycleID string) ([]domain.Settlement, error) { + if r == nil || r.db == nil { + return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + rows, err := r.db.QueryContext(ctx, ` + SELECT app_code, settlement_id, cycle_id, rank_no, user_id, score, resource_group_id, + wallet_command_id, wallet_grant_id, status, failure_reason, attempt_count, created_at_ms, updated_at_ms + FROM weekly_star_settlements + WHERE app_code = ? AND cycle_id = ? + ORDER BY rank_no ASC`, + appcode.FromContext(ctx), cycleID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + settlements := make([]domain.Settlement, 0) + for rows.Next() { + settlement, scanErr := scanWeeklyStarSettlement(rows) + if scanErr != nil { + return nil, scanErr + } + settlements = append(settlements, settlement) + } + return settlements, rows.Err() +} + +// ClaimDueWeeklyStarCycles locks ended cycles so only one cron worker prepares their Top rewards. +// 这里允许认领 active 到期周期,也允许认领锁过期的 settling 周期;后者用于 worker 崩溃后的自动恢复。 +// FOR UPDATE SKIP LOCKED 保证多 cron worker 并发时互不等待,每个周期最多被一个事务改成 settling。 +func (r *Repository) ClaimDueWeeklyStarCycles(ctx context.Context, workerID string, nowMS int64, lockTTL time.Duration, batchSize int32) ([]domain.Cycle, error) { + if r == nil || r.db == nil { + return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + if batchSize <= 0 { + batchSize = 50 + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + rows, err := tx.QueryContext(ctx, ` + SELECT app_code, cycle_id, activity_code, region_id, title, status, start_ms, end_ms, + created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms + FROM weekly_star_cycles + WHERE app_code = ? AND activity_code = ? AND end_ms <= ? + AND ((status = ?) OR (status = ? AND lock_until_ms <= ?)) + ORDER BY end_ms ASC, cycle_id ASC + LIMIT ? FOR UPDATE SKIP LOCKED`, + appcode.FromContext(ctx), domain.ActivityCode, nowMS, domain.StatusActive, domain.StatusSettling, nowMS, int(batchSize), + ) + if err != nil { + return nil, err + } + // go-sql-driver/mysql 不支持在同一个连接还持有未关闭 rows 时继续执行下一条语句。 + // 所以先把待认领周期完整读入内存并显式 Close,再执行后续 UPDATE,避免出现 driver: bad connection。 + cycles := make([]domain.Cycle, 0) + for rows.Next() { + cycle, scanErr := scanWeeklyStarCycle(rows) + if scanErr != nil { + return nil, scanErr + } + cycles = append(cycles, cycle) + } + if err := rows.Err(); err != nil { + _ = rows.Close() + return nil, err + } + if err := rows.Close(); err != nil { + return nil, err + } + lockUntil := nowMS + lockTTL.Milliseconds() + for _, cycle := range cycles { + // 只更新本事务已经 FOR UPDATE 锁住的行;lock_until_ms 是恢复窗口,不是业务结束时间。 + if _, err := tx.ExecContext(ctx, ` + UPDATE weekly_star_cycles + SET status = ?, locked_by = ?, lock_until_ms = ?, updated_at_ms = ? + WHERE app_code = ? AND cycle_id = ?`, + domain.StatusSettling, workerID, lockUntil, nowMS, appcode.FromContext(ctx), cycle.CycleID, + ); err != nil { + return nil, err + } + } + if err := tx.Commit(); err != nil { + return nil, err + } + return cycles, nil +} + +// PrepareWeeklyStarSettlements materializes Top1/Top2/Top3 exactly once before wallet grants are attempted. +// 第一次进入时把当前榜单固化为 weekly_star_settlements,之后重试只读取 pending/failed 记录。 +// 这保证周期结束后的 Top3 快照不会因为迟到事件、重跑 cron 或后台刷新而变化。 +func (r *Repository) PrepareWeeklyStarSettlements(ctx context.Context, cycleID string, nowMS int64) ([]domain.Settlement, error) { + if r == nil || r.db == nil { + return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + var existing int + if err := tx.QueryRowContext(ctx, ` + SELECT COUNT(*) FROM weekly_star_settlements WHERE app_code = ? AND cycle_id = ?`, + appcode.FromContext(ctx), cycleID, + ).Scan(&existing); err != nil { + return nil, err + } + if existing == 0 { + // settlement 表以 cycle_id+rank_no 做唯一约束;即使 cron 重入,也只会固化一次 Top 排名。 + if err := r.insertWeeklyStarSettlements(ctx, tx, cycleID, nowMS); err != nil { + return nil, err + } + } + rows, err := tx.QueryContext(ctx, ` + SELECT app_code, settlement_id, cycle_id, rank_no, user_id, score, resource_group_id, + wallet_command_id, wallet_grant_id, status, failure_reason, attempt_count, created_at_ms, updated_at_ms + FROM weekly_star_settlements + WHERE app_code = ? AND cycle_id = ? AND status IN (?, ?) + ORDER BY rank_no ASC FOR UPDATE`, + appcode.FromContext(ctx), cycleID, domain.SettlementStatusPending, domain.SettlementStatusFailed, + ) + if err != nil { + return nil, err + } + // 这里同样必须先读完并关闭 rows,再把选中的 settlement 更新为 running。 + // 否则同一事务中的 UPDATE 会复用被 rows 占用的 MySQL 连接,导致 driver: bad connection。 + settlements := make([]domain.Settlement, 0) + for rows.Next() { + settlement, scanErr := scanWeeklyStarSettlement(rows) + if scanErr != nil { + return nil, scanErr + } + settlements = append(settlements, settlement) + } + if err := rows.Err(); err != nil { + _ = rows.Close() + return nil, err + } + if err := rows.Close(); err != nil { + return nil, err + } + for _, settlement := range settlements { + // running 只表示本轮 cron 正在尝试发放;失败会回到 failed,成功会变成 granted。 + // attempt_count 在状态切换时递增,方便后台排查反复失败的奖励。 + if _, err := tx.ExecContext(ctx, ` + UPDATE weekly_star_settlements + SET status = ?, attempt_count = attempt_count + 1, updated_at_ms = ? + WHERE app_code = ? AND settlement_id = ?`, + domain.SettlementStatusRunning, nowMS, appcode.FromContext(ctx), settlement.SettlementID, + ); err != nil { + return nil, err + } + } + if err := tx.Commit(); err != nil { + return nil, err + } + return settlements, nil +} + +// MarkWeeklyStarSettlementGranted records the wallet grant id after wallet-service idempotency succeeds. +// wallet_grant_id 是后续审计和客服排障的跨服务凭证,不能只依赖 activity 侧 settlement_id。 +func (r *Repository) MarkWeeklyStarSettlementGranted(ctx context.Context, settlementID string, walletGrantID string, nowMS int64) error { + if r == nil || r.db == nil { + return xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + _, err := r.db.ExecContext(ctx, ` + UPDATE weekly_star_settlements + SET status = ?, wallet_grant_id = ?, failure_reason = '', updated_at_ms = ? + WHERE app_code = ? AND settlement_id = ?`, + domain.SettlementStatusGranted, walletGrantID, nowMS, appcode.FromContext(ctx), settlementID, + ) + return err +} + +// MarkWeeklyStarSettlementFailed unlocks one grant job for a later cron retry. +// failed 不是终态;下一轮 PrepareWeeklyStarSettlements 会重新锁定它,并用同一 wallet_command_id 继续重试。 +func (r *Repository) MarkWeeklyStarSettlementFailed(ctx context.Context, settlementID string, reason string, nowMS int64) error { + if r == nil || r.db == nil { + return xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + _, err := r.db.ExecContext(ctx, ` + UPDATE weekly_star_settlements + SET status = ?, failure_reason = ?, updated_at_ms = ? + WHERE app_code = ? AND settlement_id = ?`, + domain.SettlementStatusFailed, truncateWeeklyStarFailure(reason), nowMS, appcode.FromContext(ctx), settlementID, + ) + return err +} + +// FinishWeeklyStarCycleIfComplete marks a cycle settled only after every materialized grant succeeded. +// 只要存在非 granted 的 settlement,就释放 cycle 锁但保持 settling,等待下一轮 cron 补偿。 +// 全部发放成功后才写 settled_at_ms,H5 历史榜和后台结算记录才会把这个周期视为已完结。 +func (r *Repository) FinishWeeklyStarCycleIfComplete(ctx context.Context, cycleID string, nowMS int64) (bool, error) { + if r == nil || r.db == nil { + return false, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + var pending int + if err := r.db.QueryRowContext(ctx, ` + SELECT COUNT(*) FROM weekly_star_settlements + WHERE app_code = ? AND cycle_id = ? AND status <> ?`, + appcode.FromContext(ctx), cycleID, domain.SettlementStatusGranted, + ).Scan(&pending); err != nil { + return false, err + } + if pending > 0 { + _, err := r.db.ExecContext(ctx, ` + UPDATE weekly_star_cycles + SET locked_by = '', lock_until_ms = 0, updated_at_ms = ? + WHERE app_code = ? AND cycle_id = ?`, + nowMS, appcode.FromContext(ctx), cycleID, + ) + return false, err + } + _, err := r.db.ExecContext(ctx, ` + UPDATE weekly_star_cycles + SET status = ?, settled_at_ms = ?, locked_by = '', lock_until_ms = 0, updated_at_ms = ? + WHERE app_code = ? AND cycle_id = ?`, + domain.StatusSettled, nowMS, nowMS, appcode.FromContext(ctx), cycleID, + ) + return true, err +} + +func (r *Repository) checkWeeklyStarCycleOverlap(ctx context.Context, tx *sql.Tx, cycle domain.Cycle) error { + if cycle.Status != domain.StatusActive { + return nil + } + // 同一 app/activity/region 下 active 周期不能时间重叠;region_id=0 的默认周期可以和具体区域周期并存。 + // 具体区域优先级由读取 current 时的 ORDER BY 决定,这里只约束同一区域自身不重叠。 + var overlapID string + err := tx.QueryRowContext(ctx, ` + SELECT cycle_id + FROM weekly_star_cycles + WHERE app_code = ? AND activity_code = ? AND region_id = ? AND status = ? AND cycle_id <> ? + AND start_ms < ? AND end_ms > ? + LIMIT 1`, + cycle.AppCode, domain.ActivityCode, cycle.RegionID, domain.StatusActive, cycle.CycleID, cycle.EndMS, cycle.StartMS, + ).Scan(&overlapID) + if errors.Is(err, sql.ErrNoRows) { + return nil + } + if err != nil { + return err + } + return xerr.New(xerr.Conflict, "weekly star active cycle overlaps existing cycle") +} + +func (r *Repository) findCurrentWeeklyStarCycleForGift(ctx context.Context, tx *sql.Tx, regionID int64, giftID string, occurredAtMS int64) (domain.Cycle, bool, error) { + // 送礼事件按 occurred_at_ms 命中周期,而不是按消费时的系统时间命中。 + // 这样 MQ 延迟或手动补偿不会把旧周期的礼物误计到当前周期;具体区域优先于 region_id=0 默认配置。 + row := tx.QueryRowContext(ctx, ` + SELECT c.app_code, c.cycle_id, c.activity_code, c.region_id, c.title, c.status, c.start_ms, c.end_ms, + c.created_by_admin_id, c.updated_by_admin_id, c.settled_at_ms, c.created_at_ms, c.updated_at_ms + FROM weekly_star_cycles c + JOIN weekly_star_cycle_gifts g ON g.app_code = c.app_code AND g.cycle_id = c.cycle_id + WHERE c.app_code = ? AND c.activity_code = ? AND c.status = ? AND c.start_ms <= ? AND c.end_ms > ? + AND c.region_id IN (?, 0) AND g.gift_id = ? + ORDER BY CASE WHEN c.region_id = ? THEN 0 ELSE 1 END, c.start_ms DESC, c.updated_at_ms DESC + LIMIT 1`, + appcode.FromContext(ctx), domain.ActivityCode, domain.StatusActive, occurredAtMS, occurredAtMS, regionID, giftID, regionID, + ) + cycle, err := scanWeeklyStarCycle(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Cycle{}, false, nil + } + return cycle, err == nil, err +} + +func (r *Repository) insertWeeklyStarSettlements(ctx context.Context, tx *sql.Tx, cycleID string, nowMS int64) error { + // 排名规则必须和榜单查询一致:score 高优先,首次得分早优先,user_id 小优先。 + // 子查询先取 Top3,再用同一排序条件反算 rank_no,确保 Top1/2/3 对应正确资源组。 + rows, err := tx.QueryContext(ctx, ` + SELECT s.user_id, s.score, s.first_scored_at_ms, r.rank_no, r.resource_group_id + FROM ( + SELECT user_id, score, first_scored_at_ms + FROM weekly_star_user_scores + WHERE app_code = ? AND cycle_id = ? + ORDER BY score DESC, first_scored_at_ms ASC, user_id ASC + LIMIT 3 + ) s + JOIN weekly_star_cycle_rewards r ON r.app_code = ? AND r.cycle_id = ? AND r.rank_no = + (SELECT COUNT(*) + 1 FROM weekly_star_user_scores x + WHERE x.app_code = ? AND x.cycle_id = ? + AND (x.score > s.score OR (x.score = s.score AND x.first_scored_at_ms < s.first_scored_at_ms) OR (x.score = s.score AND x.first_scored_at_ms = s.first_scored_at_ms AND x.user_id < s.user_id))) + ORDER BY r.rank_no ASC`, + appcode.FromContext(ctx), cycleID, appcode.FromContext(ctx), cycleID, appcode.FromContext(ctx), cycleID, + ) + if err != nil { + return err + } + type topRow struct { + userID int64 + score int64 + rankNo int32 + resourceGroupID int64 + } + // 先缓存 Top3 再插入 settlement,原因同上:同一 MySQL 事务里不能边遍历 rows 边继续写表。 + topRows := make([]topRow, 0, 3) + for rows.Next() { + var firstScoredAtMS int64 + var item topRow + if err := rows.Scan(&item.userID, &item.score, &firstScoredAtMS, &item.rankNo, &item.resourceGroupID); err != nil { + _ = rows.Close() + return err + } + topRows = append(topRows, item) + _ = firstScoredAtMS + } + if err := rows.Err(); err != nil { + _ = rows.Close() + return err + } + if err := rows.Close(); err != nil { + return err + } + for _, item := range topRows { + settlementID := idgen.New("wstars") + // wallet_command_id 必须稳定包含 cycle/rank/user;结算重跑或 wallet 超时重试时依赖它防重发。 + commandID := fmt.Sprintf("weekly_star:%s:%d:%d", cycleID, item.rankNo, item.userID) + if _, err := tx.ExecContext(ctx, ` + INSERT INTO weekly_star_settlements ( + app_code, settlement_id, cycle_id, rank_no, user_id, score, resource_group_id, + wallet_command_id, wallet_grant_id, status, failure_reason, attempt_count, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, '', ?, '', 0, ?, ?)`, + appcode.FromContext(ctx), settlementID, cycleID, item.rankNo, item.userID, item.score, item.resourceGroupID, + commandID, domain.SettlementStatusPending, nowMS, nowMS, + ); err != nil { + return err + } + } + return nil +} + +func (r *Repository) getWeeklyStarCycle(ctx context.Context, q interface { + QueryRowContext(context.Context, string, ...any) *sql.Row +}, cycleID string) (domain.Cycle, error) { + row := q.QueryRowContext(ctx, ` + SELECT app_code, cycle_id, activity_code, region_id, title, status, start_ms, end_ms, + created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms + FROM weekly_star_cycles + WHERE app_code = ? AND cycle_id = ?`, + appcode.FromContext(ctx), strings.TrimSpace(cycleID), + ) + cycle, err := scanWeeklyStarCycle(row) + if errors.Is(err, sql.ErrNoRows) { + return domain.Cycle{}, xerr.New(xerr.NotFound, "weekly star cycle not found") + } + return cycle, err +} + +func (r *Repository) attachWeeklyStarChildren(ctx context.Context, cycles []domain.Cycle) error { + if len(cycles) == 0 { + return nil + } + for index := range cycles { + gifts, err := r.listWeeklyStarGifts(ctx, cycles[index].CycleID) + if err != nil { + return err + } + rewards, err := r.listWeeklyStarRewards(ctx, cycles[index].CycleID) + if err != nil { + return err + } + cycles[index].Gifts = gifts + cycles[index].Rewards = rewards + } + return nil +} + +func (r *Repository) listWeeklyStarGifts(ctx context.Context, cycleID string) ([]domain.Gift, error) { + rows, err := r.db.QueryContext(ctx, ` + SELECT app_code, cycle_id, gift_id, sort_order + FROM weekly_star_cycle_gifts + WHERE app_code = ? AND cycle_id = ? + ORDER BY sort_order ASC, gift_id ASC`, + appcode.FromContext(ctx), cycleID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + gifts := make([]domain.Gift, 0) + for rows.Next() { + var gift domain.Gift + if err := rows.Scan(&gift.AppCode, &gift.CycleID, &gift.GiftID, &gift.SortOrder); err != nil { + return nil, err + } + gifts = append(gifts, gift) + } + return gifts, rows.Err() +} + +func (r *Repository) listWeeklyStarRewards(ctx context.Context, cycleID string) ([]domain.Reward, error) { + rows, err := r.db.QueryContext(ctx, ` + SELECT app_code, cycle_id, rank_no, resource_group_id + FROM weekly_star_cycle_rewards + WHERE app_code = ? AND cycle_id = ? + ORDER BY rank_no ASC`, + appcode.FromContext(ctx), cycleID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + rewards := make([]domain.Reward, 0) + for rows.Next() { + var reward domain.Reward + if err := rows.Scan(&reward.AppCode, &reward.CycleID, &reward.RankNo, &reward.ResourceGroupID); err != nil { + return nil, err + } + rewards = append(rewards, reward) + } + return rewards, rows.Err() +} + +type weeklyStarCycleScanner interface { + Scan(dest ...any) error +} + +func scanWeeklyStarCycle(row weeklyStarCycleScanner) (domain.Cycle, error) { + var cycle domain.Cycle + err := row.Scan(&cycle.AppCode, &cycle.CycleID, &cycle.ActivityCode, &cycle.RegionID, &cycle.Title, &cycle.Status, + &cycle.StartMS, &cycle.EndMS, &cycle.CreatedByAdminID, &cycle.UpdatedByAdminID, &cycle.SettledAtMS, &cycle.CreatedAtMS, &cycle.UpdatedAtMS) + return cycle, err +} + +func scanWeeklyStarSettlement(row weeklyStarCycleScanner) (domain.Settlement, error) { + var settlement domain.Settlement + err := row.Scan(&settlement.AppCode, &settlement.SettlementID, &settlement.CycleID, &settlement.RankNo, &settlement.UserID, + &settlement.Score, &settlement.ResourceGroupID, &settlement.WalletCommandID, &settlement.WalletGrantID, &settlement.Status, + &settlement.FailureReason, &settlement.AttemptCount, &settlement.CreatedAtMS, &settlement.UpdatedAtMS) + return settlement, err +} + +func weeklyStarCycleWhere(ctx context.Context, query domain.ListQuery) (string, []any) { + conditions := []string{"app_code = ?", "activity_code = ?"} + args := []any{appcode.FromContext(ctx), domain.ActivityCode} + if query.RegionID != nil { + conditions = append(conditions, "region_id = ?") + args = append(args, *query.RegionID) + } + if query.Status != "" { + conditions = append(conditions, "status = ?") + args = append(args, query.Status) + } + if query.StartMS > 0 { + conditions = append(conditions, "end_ms > ?") + args = append(args, query.StartMS) + } + if query.EndMS > 0 { + conditions = append(conditions, "start_ms < ?") + args = append(args, query.EndMS) + } + return "WHERE " + strings.Join(conditions, " AND "), args +} + +func parseWeeklyStarPageToken(value string) int { + offset, err := strconv.Atoi(strings.TrimSpace(value)) + if err != nil || offset < 0 { + return 0 + } + return offset +} + +func truncateWeeklyStarFailure(value string) string { + value = strings.TrimSpace(value) + if len(value) <= 512 { + return value + } + return value[:512] +} diff --git a/services/activity-service/internal/transport/grpc/broadcast_server.go b/services/activity-service/internal/transport/grpc/broadcast_server.go index 7a4cba1c..c6580c8b 100644 --- a/services/activity-service/internal/transport/grpc/broadcast_server.go +++ b/services/activity-service/internal/transport/grpc/broadcast_server.go @@ -9,6 +9,8 @@ import ( "hyapp/pkg/xerr" broadcastservice "hyapp/services/activity-service/internal/service/broadcast" growthservice "hyapp/services/activity-service/internal/service/growth" + roomturnoverrewardservice "hyapp/services/activity-service/internal/service/roomturnoverreward" + weeklystarservice "hyapp/services/activity-service/internal/service/weeklystar" ) // BroadcastServer 把 IM 播报能力暴露为内部 gRPC。 @@ -17,15 +19,22 @@ type BroadcastServer struct { activityv1.UnimplementedBroadcastServiceServer activityv1.UnimplementedRoomEventConsumerServiceServer - svc *broadcastservice.Service - growth *growthservice.Service + svc *broadcastservice.Service + growth *growthservice.Service + roomReward *roomturnoverrewardservice.Service + weeklyStar *weeklystarservice.Service } // NewBroadcastServer 创建播报和 room event 消费共用的 gRPC adapter。 -func NewBroadcastServer(svc *broadcastservice.Service, growthSvc ...*growthservice.Service) *BroadcastServer { +// room-service 的 outbox 事件只投递一次,但 activity-service 内部有多条独立投影: +// 播报负责 IM 展示,growth 负责等级进度,weekly-star 负责指定礼物榜单,roomReward 负责房间流水。 +// 这里把这些模块都挂到同一个 gRPC 入口,保证本地直连投递和 MQ 投递使用同一组业务副作用。 +func NewBroadcastServer(svc *broadcastservice.Service, growthSvc *growthservice.Service, weeklyStarSvc *weeklystarservice.Service, roomRewardSvc ...*roomturnoverrewardservice.Service) *BroadcastServer { server := &BroadcastServer{svc: svc} - if len(growthSvc) > 0 { - server.growth = growthSvc[0] + server.growth = growthSvc + server.weeklyStar = weeklyStarSvc + if len(roomRewardSvc) > 0 { + server.roomReward = roomRewardSvc[0] } return server } @@ -99,7 +108,9 @@ func (s *BroadcastServer) ProcessBroadcastOutboxBatch(ctx context.Context, req * } func (s *BroadcastServer) ConsumeRoomEvent(ctx context.Context, req *activityv1.ConsumeRoomEventRequest) (*activityv1.ConsumeRoomEventResponse, error) { - // room-service 只推送已提交的 outbox envelope;播报和等级都以同一个 event_id 幂等消费。 + // room-service 只推送已提交的 outbox envelope;每个下游模块都以同一个 event_id 做自己的幂等表。 + // 调用顺序保持和 MQ consumer 一致:先处理可见播报,再处理用户成长,再处理运营活动统计。 + // 任一模块返回错误时直接失败,让 room-service/MQ 重试同一个 envelope,而不是吞掉部分副作用。 ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) result, err := s.svc.HandleRoomEvent(ctx, req.GetEnvelope()) if err != nil { @@ -110,6 +121,16 @@ func (s *BroadcastServer) ConsumeRoomEvent(ctx context.Context, req *activityv1. return nil, xerr.ToGRPCError(err) } } + if s.weeklyStar != nil { + if _, err := s.weeklyStar.HandleRoomEvent(ctx, req.GetEnvelope()); err != nil { + return nil, xerr.ToGRPCError(err) + } + } + if s.roomReward != nil { + if _, err := s.roomReward.HandleRoomEvent(ctx, req.GetEnvelope()); err != nil { + return nil, xerr.ToGRPCError(err) + } + } return &activityv1.ConsumeRoomEventResponse{ EventId: result.EventID, Status: result.Status, diff --git a/services/activity-service/internal/transport/grpc/cron_server.go b/services/activity-service/internal/transport/grpc/cron_server.go index a5b8b254..aa768a7f 100644 --- a/services/activity-service/internal/transport/grpc/cron_server.go +++ b/services/activity-service/internal/transport/grpc/cron_server.go @@ -10,6 +10,8 @@ import ( achievementservice "hyapp/services/activity-service/internal/service/achievement" growthservice "hyapp/services/activity-service/internal/service/growth" messageservice "hyapp/services/activity-service/internal/service/message" + roomturnoverrewardservice "hyapp/services/activity-service/internal/service/roomturnoverreward" + weeklystarservice "hyapp/services/activity-service/internal/service/weeklystar" ) // CronServer exposes activity-service owned background batches to cron-service. @@ -19,14 +21,18 @@ type CronServer struct { message *messageservice.Service growth *growthservice.Service achievement *achievementservice.Service + weeklyStar *weeklystarservice.Service + roomReward *roomturnoverrewardservice.Service } // NewCronServer creates the internal cron adapter without exposing message storage. -func NewCronServer(messageSvc *messageservice.Service, growthSvc *growthservice.Service, achievementSvc ...*achievementservice.Service) *CronServer { +func NewCronServer(messageSvc *messageservice.Service, growthSvc *growthservice.Service, achievementSvc *achievementservice.Service, weeklyStarSvc *weeklystarservice.Service, roomRewardSvc ...*roomturnoverrewardservice.Service) *CronServer { server := &CronServer{message: messageSvc} server.growth = growthSvc - if len(achievementSvc) > 0 { - server.achievement = achievementSvc[0] + server.achievement = achievementSvc + server.weeklyStar = weeklyStarSvc + if len(roomRewardSvc) > 0 { + server.roomReward = roomRewardSvc[0] } return server } @@ -94,6 +100,45 @@ func (s *CronServer) ProcessAchievementRewardBatch(ctx context.Context, req *act }, nil } +// ProcessWeeklyStarSettlementBatch claims ended weekly-star cycles and grants Top rewards. +func (s *CronServer) ProcessWeeklyStarSettlementBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + if s.weeklyStar == nil { + return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "weekly star service is not configured")) + } + claimed, processed, success, failure, hasMore, err := s.weeklyStar.ProcessSettlementBatch(ctx, req.GetRunId(), req.GetWorkerId(), req.GetBatchSize(), durationFromMillis(req.GetLockTtlMs())) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.CronBatchResponse{ + ClaimedCount: claimed, + ProcessedCount: processed, + SuccessCount: success, + FailureCount: failure, + HasMore: hasMore, + }, nil +} + +// ProcessRoomTurnoverRewardSettlementBatch closes the previous UTC week and grants pending room rewards. +func (s *CronServer) ProcessRoomTurnoverRewardSettlementBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) { + // cron-service 只负责调度,activity-service 才拥有上一 UTC 周期认领 settlement 和发奖状态机。 + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + if s.roomReward == nil { + return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "room turnover reward service is not configured")) + } + result, err := s.roomReward.ProcessSettlementBatch(ctx, int(req.GetBatchSize())) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.CronBatchResponse{ + ClaimedCount: result.ClaimedCount, + ProcessedCount: result.ProcessedCount, + SuccessCount: result.SuccessCount, + FailureCount: result.FailureCount, + HasMore: result.HasMore, + }, nil +} + func durationFromMillis(value int64) time.Duration { if value <= 0 { return 0 diff --git a/services/activity-service/internal/transport/grpc/cumulative_recharge_reward_server.go b/services/activity-service/internal/transport/grpc/cumulative_recharge_reward_server.go new file mode 100644 index 00000000..fb0266d6 --- /dev/null +++ b/services/activity-service/internal/transport/grpc/cumulative_recharge_reward_server.go @@ -0,0 +1,212 @@ +package grpc + +import ( + "context" + + activityv1 "hyapp.local/api/proto/activity/v1" + "hyapp/pkg/appcode" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/cumulativerecharge" + service "hyapp/services/activity-service/internal/service/cumulativerecharge" +) + +// CumulativeRechargeRewardServer 暴露 App 累充奖励查询和内部充值事实消费入口。 +type CumulativeRechargeRewardServer struct { + activityv1.UnimplementedCumulativeRechargeRewardServiceServer + + svc *service.Service +} + +func NewCumulativeRechargeRewardServer(svc *service.Service) *CumulativeRechargeRewardServer { + return &CumulativeRechargeRewardServer{svc: svc} +} + +func (s *CumulativeRechargeRewardServer) GetCumulativeRechargeRewardStatus(ctx context.Context, req *activityv1.GetCumulativeRechargeRewardStatusRequest) (*activityv1.GetCumulativeRechargeRewardStatusResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + status, err := s.svc.GetStatus(ctx, req.GetUserId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.GetCumulativeRechargeRewardStatusResponse{Status: cumulativeRechargeRewardStatusToProto(status)}, nil +} + +func (s *CumulativeRechargeRewardServer) ConsumeCumulativeRechargeReward(ctx context.Context, req *activityv1.ConsumeCumulativeRechargeRewardRequest) (*activityv1.ConsumeCumulativeRechargeRewardResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + grants, consumed, reason, err := s.svc.Consume(ctx, domain.RechargeEvent{ + AppCode: req.GetMeta().GetAppCode(), + EventID: req.GetEventId(), + TransactionID: req.GetTransactionId(), + CommandID: req.GetCommandId(), + UserID: req.GetUserId(), + RechargeCoinAmount: req.GetRechargeCoinAmount(), + RechargeSequence: req.GetRechargeSequence(), + RechargeType: req.GetRechargeType(), + QualifyingUSDMinor: req.GetQualifyingUsdMinor(), + PayloadJSON: req.GetPayloadJson(), + OccurredAtMS: req.GetOccurredAtMs(), + }) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + resp := &activityv1.ConsumeCumulativeRechargeRewardResponse{ + Grants: make([]*activityv1.CumulativeRechargeRewardGrant, 0, len(grants)), + Consumed: consumed, + Reason: reason, + } + for _, grant := range grants { + resp.Grants = append(resp.Grants, cumulativeRechargeRewardGrantToProto(grant)) + } + return resp, nil +} + +// AdminCumulativeRechargeRewardServer 暴露后台配置和发放记录查询入口。 +type AdminCumulativeRechargeRewardServer struct { + activityv1.UnimplementedAdminCumulativeRechargeRewardServiceServer + + svc *service.Service +} + +func NewAdminCumulativeRechargeRewardServer(svc *service.Service) *AdminCumulativeRechargeRewardServer { + return &AdminCumulativeRechargeRewardServer{svc: svc} +} + +func (s *AdminCumulativeRechargeRewardServer) GetCumulativeRechargeRewardConfig(ctx context.Context, req *activityv1.GetCumulativeRechargeRewardConfigRequest) (*activityv1.GetCumulativeRechargeRewardConfigResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + config, err := s.svc.GetConfig(ctx) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.GetCumulativeRechargeRewardConfigResponse{Config: cumulativeRechargeRewardConfigToProto(config)}, nil +} + +func (s *AdminCumulativeRechargeRewardServer) UpdateCumulativeRechargeRewardConfig(ctx context.Context, req *activityv1.UpdateCumulativeRechargeRewardConfigRequest) (*activityv1.UpdateCumulativeRechargeRewardConfigResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + tiers := make([]domain.Tier, 0, len(req.GetTiers())) + for _, tier := range req.GetTiers() { + tiers = append(tiers, domain.Tier{ + TierID: tier.GetTierId(), + TierCode: tier.GetTierCode(), + TierName: tier.GetTierName(), + ThresholdUSDMinor: tier.GetThresholdUsdMinor(), + ResourceGroupID: tier.GetResourceGroupId(), + Status: tier.GetStatus(), + SortOrder: tier.GetSortOrder(), + }) + } + config, err := s.svc.UpdateConfig(ctx, domain.Config{ + Enabled: req.GetEnabled(), + Tiers: tiers, + UpdatedByAdminID: req.GetOperatorAdminId(), + }) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.UpdateCumulativeRechargeRewardConfigResponse{Config: cumulativeRechargeRewardConfigToProto(config)}, nil +} + +func (s *AdminCumulativeRechargeRewardServer) ListCumulativeRechargeRewardGrants(ctx context.Context, req *activityv1.ListCumulativeRechargeRewardGrantsRequest) (*activityv1.ListCumulativeRechargeRewardGrantsResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + grants, total, err := s.svc.ListGrants(ctx, domain.GrantQuery{ + Status: req.GetStatus(), + UserID: req.GetUserId(), + CycleKey: req.GetCycleKey(), + Page: req.GetPage(), + PageSize: req.GetPageSize(), + }) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + resp := &activityv1.ListCumulativeRechargeRewardGrantsResponse{Grants: make([]*activityv1.CumulativeRechargeRewardGrant, 0, len(grants)), Total: total} + for _, grant := range grants { + resp.Grants = append(resp.Grants, cumulativeRechargeRewardGrantToProto(grant)) + } + return resp, nil +} + +func cumulativeRechargeRewardStatusToProto(status domain.StatusResult) *activityv1.CumulativeRechargeRewardStatus { + resp := &activityv1.CumulativeRechargeRewardStatus{ + Config: cumulativeRechargeRewardConfigToProto(status.Config), + Progress: cumulativeRechargeRewardProgressToProto(status.Progress), + Grants: make([]*activityv1.CumulativeRechargeRewardGrant, 0, len(status.Grants)), + CycleKey: status.Cycle.Key, + PeriodStartMs: status.Cycle.StartMS, + PeriodEndMs: status.Cycle.EndMS, + ServerTimeMs: status.ServerTimeMS, + } + for _, grant := range status.Grants { + resp.Grants = append(resp.Grants, cumulativeRechargeRewardGrantToProto(grant)) + } + return resp +} + +func cumulativeRechargeRewardConfigToProto(config domain.Config) *activityv1.CumulativeRechargeRewardConfig { + resp := &activityv1.CumulativeRechargeRewardConfig{ + AppCode: config.AppCode, + Enabled: config.Enabled, + Tiers: make([]*activityv1.CumulativeRechargeRewardTier, 0, len(config.Tiers)), + UpdatedByAdminId: config.UpdatedByAdminID, + CreatedAtMs: config.CreatedAtMS, + UpdatedAtMs: config.UpdatedAtMS, + } + for _, tier := range config.Tiers { + resp.Tiers = append(resp.Tiers, cumulativeRechargeRewardTierToProto(tier)) + } + return resp +} + +func cumulativeRechargeRewardTierToProto(tier domain.Tier) *activityv1.CumulativeRechargeRewardTier { + return &activityv1.CumulativeRechargeRewardTier{ + TierId: tier.TierID, + TierCode: tier.TierCode, + TierName: tier.TierName, + ThresholdUsdMinor: tier.ThresholdUSDMinor, + ResourceGroupId: tier.ResourceGroupID, + Status: tier.Status, + SortOrder: tier.SortOrder, + CreatedAtMs: tier.CreatedAtMS, + UpdatedAtMs: tier.UpdatedAtMS, + } +} + +func cumulativeRechargeRewardProgressToProto(progress domain.Progress) *activityv1.CumulativeRechargeRewardProgress { + return &activityv1.CumulativeRechargeRewardProgress{ + AppCode: progress.AppCode, + CycleKey: progress.CycleKey, + UserId: progress.UserID, + TotalUsdMinor: progress.TotalUSDMinor, + TotalCoinAmount: progress.TotalCoinAmount, + FirstRechargedAtMs: progress.FirstRechargedAtMS, + LastRechargedAtMs: progress.LastRechargedAtMS, + UpdatedAtMs: progress.UpdatedAtMS, + } +} + +func cumulativeRechargeRewardGrantToProto(grant domain.Grant) *activityv1.CumulativeRechargeRewardGrant { + if grant.GrantID == "" { + return nil + } + return &activityv1.CumulativeRechargeRewardGrant{ + GrantId: grant.GrantID, + AppCode: grant.AppCode, + CycleKey: grant.CycleKey, + UserId: grant.UserID, + EventId: grant.EventID, + TransactionId: grant.TransactionID, + CommandId: grant.CommandID, + TierId: grant.TierID, + TierCode: grant.TierCode, + ThresholdUsdMinor: grant.ThresholdUSDMinor, + ResourceGroupId: grant.ResourceGroupID, + ReachedUsdMinor: grant.ReachedUSDMinor, + QualifyingUsdMinor: grant.QualifyingUSDMinor, + RechargeCoinAmount: grant.RechargeCoinAmount, + RechargeType: grant.RechargeType, + Status: grant.Status, + WalletCommandId: grant.WalletCommandID, + WalletGrantId: grant.WalletGrantID, + FailureReason: grant.FailureReason, + GrantedAtMs: grant.GrantedAtMS, + CreatedAtMs: grant.CreatedAtMS, + UpdatedAtMs: grant.UpdatedAtMS, + } +} diff --git a/services/activity-service/internal/transport/grpc/registration_reward_server.go b/services/activity-service/internal/transport/grpc/registration_reward_server.go index 88268790..39f07f93 100644 --- a/services/activity-service/internal/transport/grpc/registration_reward_server.go +++ b/services/activity-service/internal/transport/grpc/registration_reward_server.go @@ -84,16 +84,22 @@ func (s *AdminRegistrationRewardServer) UpdateRegistrationRewardConfig(ctx conte func (s *AdminRegistrationRewardServer) ListRegistrationRewardClaims(ctx context.Context, req *activityv1.ListRegistrationRewardClaimsRequest) (*activityv1.ListRegistrationRewardClaimsResponse, error) { ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) - claims, total, err := s.svc.ListClaims(ctx, domain.ClaimQuery{ - Status: req.GetStatus(), - UserID: req.GetUserId(), - Page: req.GetPage(), - PageSize: req.GetPageSize(), + claims, total, todayClaimedCount, err := s.svc.ListClaims(ctx, domain.ClaimQuery{ + Status: req.GetStatus(), + UserID: req.GetUserId(), + ClaimedStartMS: req.GetClaimedStartMs(), + ClaimedEndMS: req.GetClaimedEndMs(), + Page: req.GetPage(), + PageSize: req.GetPageSize(), }) if err != nil { return nil, xerr.ToGRPCError(err) } - resp := &activityv1.ListRegistrationRewardClaimsResponse{Claims: make([]*activityv1.RegistrationRewardClaim, 0, len(claims)), Total: total} + resp := &activityv1.ListRegistrationRewardClaimsResponse{ + Claims: make([]*activityv1.RegistrationRewardClaim, 0, len(claims)), + Total: total, + TodayClaimedCount: todayClaimedCount, + } for _, claim := range claims { resp.Claims = append(resp.Claims, registrationRewardClaimToProto(claim)) } diff --git a/services/activity-service/internal/transport/grpc/room_turnover_reward_server.go b/services/activity-service/internal/transport/grpc/room_turnover_reward_server.go new file mode 100644 index 00000000..663b0b16 --- /dev/null +++ b/services/activity-service/internal/transport/grpc/room_turnover_reward_server.go @@ -0,0 +1,185 @@ +package grpc + +import ( + "context" + + activityv1 "hyapp.local/api/proto/activity/v1" + "hyapp/pkg/appcode" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/roomturnoverreward" + service "hyapp/services/activity-service/internal/service/roomturnoverreward" +) + +// RoomTurnoverRewardServer 暴露 App 房间流水奖励查询入口。 +type RoomTurnoverRewardServer struct { + activityv1.UnimplementedRoomTurnoverRewardServiceServer + + svc *service.Service +} + +func NewRoomTurnoverRewardServer(svc *service.Service) *RoomTurnoverRewardServer { + return &RoomTurnoverRewardServer{svc: svc} +} + +func (s *RoomTurnoverRewardServer) GetRoomTurnoverRewardStatus(ctx context.Context, req *activityv1.GetRoomTurnoverRewardStatusRequest) (*activityv1.GetRoomTurnoverRewardStatusResponse, error) { + // AppCode 从 protobuf meta 写入 context,下面 service 和 repository 都按这个上下文隔离配置、流水和结算记录。 + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + status, err := s.svc.GetStatus(ctx, req.GetUserId(), req.GetRoomId(), req.GetOwnerUserId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.GetRoomTurnoverRewardStatusResponse{Status: roomTurnoverRewardStatusToProto(status)}, nil +} + +// AdminRoomTurnoverRewardServer 暴露后台配置、结算记录和 retry 入口。 +type AdminRoomTurnoverRewardServer struct { + activityv1.UnimplementedAdminRoomTurnoverRewardServiceServer + + svc *service.Service +} + +func NewAdminRoomTurnoverRewardServer(svc *service.Service) *AdminRoomTurnoverRewardServer { + return &AdminRoomTurnoverRewardServer{svc: svc} +} + +func (s *AdminRoomTurnoverRewardServer) GetRoomTurnoverRewardConfig(ctx context.Context, req *activityv1.GetRoomTurnoverRewardConfigRequest) (*activityv1.GetRoomTurnoverRewardConfigResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + config, err := s.svc.GetConfig(ctx) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.GetRoomTurnoverRewardConfigResponse{Config: roomTurnoverRewardConfigToProto(config)}, nil +} + +func (s *AdminRoomTurnoverRewardServer) UpdateRoomTurnoverRewardConfig(ctx context.Context, req *activityv1.UpdateRoomTurnoverRewardConfigRequest) (*activityv1.UpdateRoomTurnoverRewardConfigResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + tiers := make([]domain.Tier, 0, len(req.GetTiers())) + for _, tier := range req.GetTiers() { + // protobuf 层只做字段转换,具体默认值、排序和合法性都留给 service,避免入口之间校验规则分叉。 + tiers = append(tiers, domain.Tier{ + TierID: tier.GetTierId(), + TierCode: tier.GetTierCode(), + TierName: tier.GetTierName(), + ThresholdCoinSpent: tier.GetThresholdCoinSpent(), + RewardCoinAmount: tier.GetRewardCoinAmount(), + Status: tier.GetStatus(), + SortOrder: tier.GetSortOrder(), + }) + } + config, err := s.svc.UpdateConfig(ctx, domain.Config{ + Enabled: req.GetEnabled(), + Tiers: tiers, + UpdatedByAdminID: req.GetOperatorAdminId(), + }) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.UpdateRoomTurnoverRewardConfigResponse{Config: roomTurnoverRewardConfigToProto(config)}, nil +} + +func (s *AdminRoomTurnoverRewardServer) ListRoomTurnoverRewardSettlements(ctx context.Context, req *activityv1.ListRoomTurnoverRewardSettlementsRequest) (*activityv1.ListRoomTurnoverRewardSettlementsResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + // 查询条件完整透传到 domain query,分页上限由 service 统一收敛,后台和潜在内部工具不会各自实现分页规则。 + items, total, err := s.svc.ListSettlements(ctx, domain.SettlementQuery{ + Status: req.GetStatus(), + RoomID: req.GetRoomId(), + OwnerUserID: req.GetOwnerUserId(), + PeriodStartMS: req.GetPeriodStartMs(), + Page: req.GetPage(), + PageSize: req.GetPageSize(), + }) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + resp := &activityv1.ListRoomTurnoverRewardSettlementsResponse{ + Settlements: make([]*activityv1.RoomTurnoverRewardSettlement, 0, len(items)), + Total: total, + } + for _, item := range items { + resp.Settlements = append(resp.Settlements, roomTurnoverRewardSettlementToProto(item)) + } + return resp, nil +} + +func (s *AdminRoomTurnoverRewardServer) RetryRoomTurnoverRewardSettlement(ctx context.Context, req *activityv1.RetryRoomTurnoverRewardSettlementRequest) (*activityv1.RetryRoomTurnoverRewardSettlementResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + // operator_admin_id 当前只由 admin-server 审计使用;retry 的业务幂等仍由 settlement 和 wallet command_id 保证。 + settlement, err := s.svc.RetrySettlement(ctx, req.GetSettlementId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.RetryRoomTurnoverRewardSettlementResponse{Settlement: roomTurnoverRewardSettlementToProto(settlement)}, nil +} + +func roomTurnoverRewardStatusToProto(status domain.StatusResult) *activityv1.RoomTurnoverRewardStatus { + return &activityv1.RoomTurnoverRewardStatus{ + Config: roomTurnoverRewardConfigToProto(status.Config), + RoomId: status.RoomID, + OwnerUserId: status.OwnerUserID, + PeriodStartMs: status.PeriodStartMS, + PeriodEndMs: status.PeriodEndMS, + CurrentCoinSpent: status.CurrentCoinSpent, + ExpectedRewardCoinAmount: status.ExpectedRewardCoinAmount, + MatchedTier: roomTurnoverRewardTierToProto(status.MatchedTier), + LatestSettlement: roomTurnoverRewardSettlementToProto(status.LatestSettlement), + ServerTimeMs: status.ServerTimeMS, + } +} + +func roomTurnoverRewardConfigToProto(config domain.Config) *activityv1.RoomTurnoverRewardConfig { + resp := &activityv1.RoomTurnoverRewardConfig{ + AppCode: config.AppCode, + Enabled: config.Enabled, + Tiers: make([]*activityv1.RoomTurnoverRewardTier, 0, len(config.Tiers)), + UpdatedByAdminId: config.UpdatedByAdminID, + CreatedAtMs: config.CreatedAtMS, + UpdatedAtMs: config.UpdatedAtMS, + } + for _, tier := range config.Tiers { + resp.Tiers = append(resp.Tiers, roomTurnoverRewardTierToProto(tier)) + } + return resp +} + +func roomTurnoverRewardTierToProto(tier domain.Tier) *activityv1.RoomTurnoverRewardTier { + if tier.TierID == 0 && tier.TierCode == "" { + return nil + } + return &activityv1.RoomTurnoverRewardTier{ + TierId: tier.TierID, + TierCode: tier.TierCode, + TierName: tier.TierName, + ThresholdCoinSpent: tier.ThresholdCoinSpent, + RewardCoinAmount: tier.RewardCoinAmount, + Status: tier.Status, + SortOrder: tier.SortOrder, + CreatedAtMs: tier.CreatedAtMS, + UpdatedAtMs: tier.UpdatedAtMS, + } +} + +func roomTurnoverRewardSettlementToProto(item domain.Settlement) *activityv1.RoomTurnoverRewardSettlement { + if item.SettlementID == "" { + return nil + } + return &activityv1.RoomTurnoverRewardSettlement{ + SettlementId: item.SettlementID, + AppCode: item.AppCode, + RoomId: item.RoomID, + OwnerUserId: item.OwnerUserID, + PeriodStartMs: item.PeriodStartMS, + PeriodEndMs: item.PeriodEndMS, + CoinSpent: item.CoinSpent, + TierId: item.TierID, + TierCode: item.TierCode, + ThresholdCoinSpent: item.ThresholdCoinSpent, + RewardCoinAmount: item.RewardCoinAmount, + Status: item.Status, + WalletCommandId: item.WalletCommandID, + WalletTransactionId: item.WalletTransactionID, + FailureReason: item.FailureReason, + SettledAtMs: item.SettledAtMS, + CreatedAtMs: item.CreatedAtMS, + UpdatedAtMs: item.UpdatedAtMS, + } +} diff --git a/services/activity-service/internal/transport/grpc/weekly_star_server.go b/services/activity-service/internal/transport/grpc/weekly_star_server.go new file mode 100644 index 00000000..b424689d --- /dev/null +++ b/services/activity-service/internal/transport/grpc/weekly_star_server.go @@ -0,0 +1,250 @@ +package grpc + +import ( + "context" + "time" + + activityv1 "hyapp.local/api/proto/activity/v1" + "hyapp/pkg/appcode" + "hyapp/pkg/xerr" + domain "hyapp/services/activity-service/internal/domain/weeklystar" + service "hyapp/services/activity-service/internal/service/weeklystar" +) + +// WeeklyStarServer exposes App/H5 weekly-star reads. +type WeeklyStarServer struct { + activityv1.UnimplementedWeeklyStarServiceServer + svc *service.Service +} + +func NewWeeklyStarServer(svc *service.Service) *WeeklyStarServer { + return &WeeklyStarServer{svc: svc} +} + +func (s *WeeklyStarServer) GetWeeklyStarCurrent(ctx context.Context, req *activityv1.GetWeeklyStarCurrentRequest) (*activityv1.GetWeeklyStarCurrentResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + cycle, topEntries, myEntry, found, err := s.svc.GetCurrent(ctx, req.GetUserId(), req.GetRegionId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + resp := &activityv1.GetWeeklyStarCurrentResponse{ + Cycle: weeklyStarCycleToProto(cycle), + TopEntries: weeklyStarEntriesToProto(topEntries), + ServerTimeMs: time.Now().UnixMilli(), + } + if found { + resp.MyEntry = weeklyStarEntryToProto(myEntry) + } + return resp, nil +} + +func (s *WeeklyStarServer) ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + cycle, entries, nextToken, total, err := s.svc.ListLeaderboard(ctx, req.GetCycleId(), req.GetRegionId(), req.GetPageSize(), req.GetPageToken()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.ListWeeklyStarLeaderboardResponse{ + Cycle: weeklyStarCycleToProto(cycle), + Entries: weeklyStarEntriesToProto(entries), + NextPageToken: nextToken, + Total: total, + }, nil +} + +func (s *WeeklyStarServer) ListWeeklyStarHistory(ctx context.Context, req *activityv1.ListWeeklyStarHistoryRequest) (*activityv1.ListWeeklyStarHistoryResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + cycles, err := s.svc.ListHistory(ctx, req.GetRegionId(), req.GetLimit()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + resp := &activityv1.ListWeeklyStarHistoryResponse{Cycles: make([]*activityv1.WeeklyStarHistoryCycle, 0, len(cycles)), ServerTimeMs: time.Now().UnixMilli()} + for _, cycle := range cycles { + resp.Cycles = append(resp.Cycles, &activityv1.WeeklyStarHistoryCycle{ + Cycle: weeklyStarCycleToProto(cycle.Cycle), + TopEntries: weeklyStarEntriesToProto(cycle.TopEntries), + }) + } + return resp, nil +} + +// AdminWeeklyStarServer exposes admin weekly-star configuration and audit reads. +type AdminWeeklyStarServer struct { + activityv1.UnimplementedAdminWeeklyStarServiceServer + svc *service.Service +} + +func NewAdminWeeklyStarServer(svc *service.Service) *AdminWeeklyStarServer { + return &AdminWeeklyStarServer{svc: svc} +} + +func (s *AdminWeeklyStarServer) ListWeeklyStarCycles(ctx context.Context, req *activityv1.ListWeeklyStarCyclesRequest) (*activityv1.ListWeeklyStarCyclesResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + var regionID *int64 + if req.GetRegionId() >= 0 && req.RegionId != 0 { + value := req.GetRegionId() + regionID = &value + } + cycles, total, err := s.svc.ListCycles(ctx, domain.ListQuery{ + RegionID: regionID, + Status: req.GetStatus(), + StartMS: req.GetStartMs(), + EndMS: req.GetEndMs(), + Page: req.GetPage(), + PageSize: req.GetPageSize(), + }) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + resp := &activityv1.ListWeeklyStarCyclesResponse{Cycles: make([]*activityv1.WeeklyStarCycle, 0, len(cycles)), Total: total} + for _, cycle := range cycles { + resp.Cycles = append(resp.Cycles, weeklyStarCycleToProto(cycle)) + } + return resp, nil +} + +func (s *AdminWeeklyStarServer) CreateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + cycle, created, err := s.svc.CreateCycle(ctx, domain.CycleCommand{Cycle: weeklyStarCycleFromProto(req.GetCycle()), OperatorAdminID: req.GetOperatorAdminId()}) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.UpsertWeeklyStarCycleResponse{Cycle: weeklyStarCycleToProto(cycle), Created: created}, nil +} + +func (s *AdminWeeklyStarServer) GetWeeklyStarCycle(ctx context.Context, req *activityv1.GetWeeklyStarCycleRequest) (*activityv1.GetWeeklyStarCycleResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + cycle, err := s.svc.GetCycle(ctx, req.GetCycleId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.GetWeeklyStarCycleResponse{Cycle: weeklyStarCycleToProto(cycle)}, nil +} + +func (s *AdminWeeklyStarServer) UpdateWeeklyStarCycle(ctx context.Context, req *activityv1.UpsertWeeklyStarCycleRequest) (*activityv1.UpsertWeeklyStarCycleResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + cycle, created, err := s.svc.UpdateCycle(ctx, domain.CycleCommand{Cycle: weeklyStarCycleFromProto(req.GetCycle()), OperatorAdminID: req.GetOperatorAdminId()}) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.UpsertWeeklyStarCycleResponse{Cycle: weeklyStarCycleToProto(cycle), Created: created}, nil +} + +func (s *AdminWeeklyStarServer) SetWeeklyStarCycleStatus(ctx context.Context, req *activityv1.SetWeeklyStarCycleStatusRequest) (*activityv1.SetWeeklyStarCycleStatusResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + cycle, err := s.svc.SetCycleStatus(ctx, req.GetCycleId(), req.GetStatus(), req.GetOperatorAdminId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + return &activityv1.SetWeeklyStarCycleStatusResponse{Cycle: weeklyStarCycleToProto(cycle)}, nil +} + +func (s *AdminWeeklyStarServer) ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) { + return NewWeeklyStarServer(s.svc).ListWeeklyStarLeaderboard(ctx, req) +} + +func (s *AdminWeeklyStarServer) ListWeeklyStarSettlements(ctx context.Context, req *activityv1.ListWeeklyStarSettlementsRequest) (*activityv1.ListWeeklyStarSettlementsResponse, error) { + ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode()) + settlements, err := s.svc.ListSettlements(ctx, req.GetCycleId()) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + resp := &activityv1.ListWeeklyStarSettlementsResponse{Settlements: make([]*activityv1.WeeklyStarSettlement, 0, len(settlements))} + for _, settlement := range settlements { + resp.Settlements = append(resp.Settlements, weeklyStarSettlementToProto(settlement)) + } + return resp, nil +} + +func weeklyStarCycleFromProto(item *activityv1.WeeklyStarCycle) domain.Cycle { + if item == nil { + return domain.Cycle{} + } + cycle := domain.Cycle{ + CycleID: item.GetCycleId(), + ActivityCode: item.GetActivityCode(), + RegionID: item.GetRegionId(), + Title: item.GetTitle(), + Status: item.GetStatus(), + StartMS: item.GetStartMs(), + EndMS: item.GetEndMs(), + CreatedByAdminID: item.GetCreatedByAdminId(), + UpdatedByAdminID: item.GetUpdatedByAdminId(), + } + for _, gift := range item.GetGifts() { + cycle.Gifts = append(cycle.Gifts, domain.Gift{GiftID: gift.GetGiftId(), SortOrder: gift.GetSortOrder()}) + } + for _, reward := range item.GetRewards() { + cycle.Rewards = append(cycle.Rewards, domain.Reward{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()}) + } + return cycle +} + +func weeklyStarCycleToProto(item domain.Cycle) *activityv1.WeeklyStarCycle { + if item.CycleID == "" { + return nil + } + resp := &activityv1.WeeklyStarCycle{ + AppCode: item.AppCode, + CycleId: item.CycleID, + ActivityCode: item.ActivityCode, + RegionId: item.RegionID, + Title: item.Title, + Status: item.Status, + StartMs: item.StartMS, + EndMs: item.EndMS, + Gifts: make([]*activityv1.WeeklyStarGift, 0, len(item.Gifts)), + Rewards: make([]*activityv1.WeeklyStarReward, 0, len(item.Rewards)), + CreatedByAdminId: item.CreatedByAdminID, + UpdatedByAdminId: item.UpdatedByAdminID, + SettledAtMs: item.SettledAtMS, + CreatedAtMs: item.CreatedAtMS, + UpdatedAtMs: item.UpdatedAtMS, + } + for _, gift := range item.Gifts { + resp.Gifts = append(resp.Gifts, &activityv1.WeeklyStarGift{GiftId: gift.GiftID, SortOrder: gift.SortOrder}) + } + for _, reward := range item.Rewards { + resp.Rewards = append(resp.Rewards, &activityv1.WeeklyStarReward{RankNo: reward.RankNo, ResourceGroupId: reward.ResourceGroupID}) + } + return resp +} + +func weeklyStarEntryToProto(item domain.LeaderboardEntry) *activityv1.WeeklyStarLeaderboardEntry { + if item.UserID <= 0 { + return nil + } + return &activityv1.WeeklyStarLeaderboardEntry{ + RankNo: item.RankNo, + UserId: item.UserID, + Score: item.Score, + FirstScoredAtMs: item.FirstScoredAtMS, + LastScoredAtMs: item.LastScoredAtMS, + } +} + +func weeklyStarEntriesToProto(items []domain.LeaderboardEntry) []*activityv1.WeeklyStarLeaderboardEntry { + resp := make([]*activityv1.WeeklyStarLeaderboardEntry, 0, len(items)) + for _, item := range items { + resp = append(resp, weeklyStarEntryToProto(item)) + } + return resp +} + +func weeklyStarSettlementToProto(item domain.Settlement) *activityv1.WeeklyStarSettlement { + return &activityv1.WeeklyStarSettlement{ + SettlementId: item.SettlementID, + CycleId: item.CycleID, + RankNo: item.RankNo, + UserId: item.UserID, + Score: item.Score, + ResourceGroupId: item.ResourceGroupID, + WalletCommandId: item.WalletCommandID, + WalletGrantId: item.WalletGrantID, + Status: item.Status, + FailureReason: item.FailureReason, + AttemptCount: item.AttemptCount, + CreatedAtMs: item.CreatedAtMS, + UpdatedAtMs: item.UpdatedAtMS, + } +} diff --git a/services/cron-service/configs/config.docker.yaml b/services/cron-service/configs/config.docker.yaml index 197b706e..f9f70c6b 100644 --- a/services/cron-service/configs/config.docker.yaml +++ b/services/cron-service/configs/config.docker.yaml @@ -43,6 +43,20 @@ tasks: lock_ttl: "30s" batch_size: 100 app_codes: ["lalu"] + weekly_star_settlement: + enabled: true + interval: "5s" + timeout: "20s" + lock_ttl: "1m" + batch_size: 50 + app_codes: ["lalu"] + room_turnover_reward_settlement: + enabled: true + interval: "5m" + timeout: "30s" + lock_ttl: "5m" + batch_size: 100 + app_codes: ["lalu"] game_level_event_relay: enabled: true interval: "5s" diff --git a/services/cron-service/configs/config.tencent.example.yaml b/services/cron-service/configs/config.tencent.example.yaml index ea41edf8..ec05f4da 100644 --- a/services/cron-service/configs/config.tencent.example.yaml +++ b/services/cron-service/configs/config.tencent.example.yaml @@ -43,6 +43,20 @@ tasks: lock_ttl: "30s" batch_size: 100 app_codes: ["lalu"] + weekly_star_settlement: + enabled: true + interval: "5s" + timeout: "20s" + lock_ttl: "1m" + batch_size: 50 + app_codes: ["lalu"] + room_turnover_reward_settlement: + enabled: true + interval: "5m" + timeout: "30s" + lock_ttl: "5m" + batch_size: 100 + app_codes: ["lalu"] game_level_event_relay: enabled: true interval: "5s" diff --git a/services/cron-service/configs/config.yaml b/services/cron-service/configs/config.yaml index e240b07e..fbadb058 100644 --- a/services/cron-service/configs/config.yaml +++ b/services/cron-service/configs/config.yaml @@ -43,6 +43,20 @@ tasks: lock_ttl: "30s" batch_size: 100 app_codes: ["lalu"] + weekly_star_settlement: + enabled: true + interval: "5s" + timeout: "20s" + lock_ttl: "1m" + batch_size: 50 + app_codes: ["lalu"] + room_turnover_reward_settlement: + enabled: true + interval: "5m" + timeout: "30s" + lock_ttl: "5m" + batch_size: 100 + app_codes: ["lalu"] game_level_event_relay: enabled: true interval: "5s" diff --git a/services/cron-service/internal/app/app.go b/services/cron-service/internal/app/app.go index 1ffc9789..7c220bf9 100644 --- a/services/cron-service/internal/app/app.go +++ b/services/cron-service/internal/app/app.go @@ -119,6 +119,8 @@ func New(cfg config.Config) (*App, error) { "message_fanout": activityCron.ProcessMessageFanoutBatch, "growth_level_reward": activityCron.ProcessLevelRewardBatch, "achievement_reward": activityCron.ProcessAchievementRewardBatch, + "weekly_star_settlement": activityCron.ProcessWeeklyStarSettlementBatch, + "room_turnover_reward_settlement": activityCron.ProcessRoomTurnoverRewardSettlementBatch, "game_level_event_relay": gameCron.ProcessLevelEventOutboxBatch, "host_salary_daily_settlement": walletCron.ProcessHostSalaryDailySettlementBatch, "host_salary_half_month_settlement": walletCron.ProcessHostSalaryHalfMonthSettlementBatch, diff --git a/services/cron-service/internal/config/config.go b/services/cron-service/internal/config/config.go index edf5e743..aa155232 100644 --- a/services/cron-service/internal/config/config.go +++ b/services/cron-service/internal/config/config.go @@ -164,6 +164,22 @@ func defaultTasks() map[string]TaskConfig { BatchSize: 100, AppCodes: []string{defaultAppCode}, }, + "weekly_star_settlement": { + Enabled: true, + Interval: "5s", + Timeout: "20s", + LockTTL: "1m", + BatchSize: 50, + AppCodes: []string{defaultAppCode}, + }, + "room_turnover_reward_settlement": { + Enabled: true, + Interval: "5m", + Timeout: "30s", + LockTTL: "5m", + BatchSize: 100, + AppCodes: []string{defaultAppCode}, + }, "game_level_event_relay": { Enabled: true, Interval: "5s", diff --git a/services/cron-service/internal/integration/activity.go b/services/cron-service/internal/integration/activity.go index b15b9b9c..dbf8dc56 100644 --- a/services/cron-service/internal/integration/activity.go +++ b/services/cron-service/internal/integration/activity.go @@ -45,6 +45,25 @@ func (c *ActivityCronClient) ProcessAchievementRewardBatch(ctx context.Context, return activityCronBatchResult(resp), nil } +// ProcessWeeklyStarSettlementBatch handles ended weekly-star cycle settlements. +func (c *ActivityCronClient) ProcessWeeklyStarSettlementBatch(ctx context.Context, req scheduler.BatchRequest) (scheduler.BatchResult, error) { + resp, err := c.client.ProcessWeeklyStarSettlementBatch(ctx, activityCronBatchRequest(req)) + if err != nil { + return scheduler.BatchResult{}, err + } + return activityCronBatchResult(resp), nil +} + +// ProcessRoomTurnoverRewardSettlementBatch handles ended room-turnover-reward cycle settlements. +func (c *ActivityCronClient) ProcessRoomTurnoverRewardSettlementBatch(ctx context.Context, req scheduler.BatchRequest) (scheduler.BatchResult, error) { + // cron-service 不直连 activity MySQL;它只把 run_id、worker_id 和 batch_size 传给活动服务,让 owner service 自己处理幂等和状态推进。 + resp, err := c.client.ProcessRoomTurnoverRewardSettlementBatch(ctx, activityCronBatchRequest(req)) + if err != nil { + return scheduler.BatchResult{}, err + } + return activityCronBatchResult(resp), nil +} + func activityCronBatchRequest(req scheduler.BatchRequest) *activityv1.CronBatchRequest { return &activityv1.CronBatchRequest{ Meta: &activityv1.RequestMeta{ diff --git a/services/game-service/internal/domain/game/game.go b/services/game-service/internal/domain/game/game.go index fb67d132..6b6319de 100644 --- a/services/game-service/internal/domain/game/game.go +++ b/services/game-service/internal/domain/game/game.go @@ -13,11 +13,12 @@ const ( LaunchModeH5Popup = "h5_popup" SessionActive = "active" - AdapterDemo = "demo" - AdapterYomiV4 = "yomi_v4" - AdapterLeaderCCV1 = "leadercc_v1" - AdapterZeeOneV1 = "zeeone_v1" - AdapterBaishunV1 = "baishun_v1" + AdapterDemo = "demo" + AdapterYomiV4 = "yomi_v4" + AdapterLeaderCCV1 = "leadercc_v1" + AdapterZeeOneV1 = "zeeone_v1" + AdapterBaishunV1 = "baishun_v1" + AdapterVivaGamesV1 = "vivagames_v1" OrderStatusWalletApplying = "wallet_applying" OrderStatusSucceeded = "succeeded" diff --git a/services/game-service/internal/service/game/service.go b/services/game-service/internal/service/game/service.go index e7bd221d..8484c192 100644 --- a/services/game-service/internal/service/game/service.go +++ b/services/game-service/internal/service/game/service.go @@ -203,7 +203,7 @@ func (s *Service) LaunchGame(ctx context.Context, command LaunchCommand) (Launch sessionID := "game_sess_" + strconv.FormatInt(now.UnixMilli(), 10) + "_" + randomHex(6) token := strings.TrimSpace(command.AccessToken) if token == "" { - if strings.EqualFold(game.AdapterType, gamedomain.AdapterYomiV4) || strings.EqualFold(game.AdapterType, gamedomain.AdapterLeaderCCV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterZeeOneV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterBaishunV1) { + if strings.EqualFold(game.AdapterType, gamedomain.AdapterYomiV4) || strings.EqualFold(game.AdapterType, gamedomain.AdapterLeaderCCV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterZeeOneV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterBaishunV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterVivaGamesV1) { return LaunchResult{}, xerr.New(xerr.InvalidArgument, "access token is required for provider game launch") } token = "gt_" + randomHex(24) @@ -218,6 +218,8 @@ func (s *Service) LaunchGame(ctx context.Context, command LaunchCommand) (Launch sessionTTL = maxDuration(sessionTTL, zeeoneConfigFromPlatform(game).TokenTTL()) } else if strings.EqualFold(game.AdapterType, gamedomain.AdapterBaishunV1) { sessionTTL = maxDuration(sessionTTL, baishunConfigFromPlatform(game).TokenTTL()) + } else if strings.EqualFold(game.AdapterType, gamedomain.AdapterVivaGamesV1) { + sessionTTL = maxDuration(sessionTTL, vivaGamesConfigFromPlatform(game).TokenTTL()) } expiresAt := now.Add(sessionTTL).UnixMilli() session := gamedomain.LaunchSession{ @@ -288,6 +290,8 @@ func (s *Service) HandleCallback(ctx context.Context, req *gamev1.CallbackReques responseBody, contentType, statusCode, signatureValid, providerRequestID, err = s.handleZeeOneOperation(ctx, app, platform, req, operation, requestHash) case strings.EqualFold(platform.AdapterType, gamedomain.AdapterBaishunV1): responseBody, contentType, statusCode, signatureValid, providerRequestID, err = s.handleBaishunOperation(ctx, app, platform, req, operation, requestHash) + case strings.EqualFold(platform.AdapterType, gamedomain.AdapterVivaGamesV1): + responseBody, contentType, statusCode, signatureValid, providerRequestID, err = s.handleVivaGamesOperation(ctx, app, platform, req, operation, requestHash) default: responseBody, contentType, statusCode, err = s.handleDemoOperation(ctx, app, req, operation, requestHash) } @@ -616,6 +620,12 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes base = configuredBase } } + if strings.EqualFold(game.AdapterType, gamedomain.AdapterVivaGamesV1) { + // VIVAGAMES 文档说明游戏 URL 由厂商提供;后台按 game_id 保存 URL,启动时只拼客户端 getConfig 需要的热配置。 + if configuredBase := vivaGamesLaunchBaseURL(game, vivaGamesConfigFromPlatform(game)); configuredBase != "" { + base = configuredBase + } + } if base == "" { if strings.EqualFold(game.AdapterType, gamedomain.AdapterYomiV4) { return "", xerr.New(xerr.InvalidArgument, "yomi auth url is required") @@ -626,6 +636,9 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes if strings.EqualFold(game.AdapterType, gamedomain.AdapterBaishunV1) { return "", xerr.New(xerr.InvalidArgument, "baishun launch url is required") } + if strings.EqualFold(game.AdapterType, gamedomain.AdapterVivaGamesV1) { + return "", xerr.New(xerr.InvalidArgument, "vivagames launch url is required") + } // demo 平台可以没有真实厂商域名,保留本地默认地址用于开发自测。 base = "https://game.example.local/h5" } @@ -727,6 +740,29 @@ func buildLaunchURL(game gamedomain.LaunchableGame, session gamedomain.LaunchSes parsed.RawQuery = query.Encode() return parsed.String(), nil } + if strings.EqualFold(game.AdapterType, gamedomain.AdapterVivaGamesV1) { + // VIVAGAMES H5 通过 NativeBridge.getConfig 取 appId/userId/code;这里把同一份配置拼到 URL,供 App 壳层回写给 H5。 + config := vivaGamesConfigFromPlatform(game) + query.Set("bridge_keys", "appId,userId,code,metadata,language,coinType,amountRate,bgmSwitch,gsp,currencyIcon,gameConfig,gameId") + if config.AppID > 0 { + query.Set("appId", strconv.FormatInt(config.AppID, 10)) + } + query.Set("userId", externalUserID(session, config.UIDMode)) + query.Set("code", token) + query.Set("gameId", strings.TrimSpace(session.ProviderGameID)) + query.Set("metadata", config.MetadataValue(session)) + query.Set("language", config.LanguageValue()) + query.Set("coinType", strconv.FormatInt(config.CoinType, 10)) + query.Set("amountRate", strconv.FormatInt(config.AmountRateValue(), 10)) + query.Set("bgmSwitch", strconv.FormatInt(config.BGMSwitchValue(), 10)) + query.Set("gsp", strconv.FormatInt(config.GSPValue(), 10)) + if config.CurrencyIcon != "" { + query.Set("currencyIcon", config.CurrencyIcon) + query.Set("gameConfig", vivaGamesGameConfigQuery(config.CurrencyIcon)) + } + parsed.RawQuery = query.Encode() + return parsed.String(), nil + } // demo adapter 保持内部调试参数完整,方便用一个简单 H5 验证 session 和订单链路。 query.Set("session_id", session.SessionID) query.Set("session_token", token) diff --git a/services/game-service/internal/service/game/service_test.go b/services/game-service/internal/service/game/service_test.go index 43e92722..fa932c96 100644 --- a/services/game-service/internal/service/game/service_test.go +++ b/services/game-service/internal/service/game/service_test.go @@ -335,6 +335,81 @@ func TestLaunchGameBuildsBaishunURL(t *testing.T) { } } +func TestLaunchGameBuildsVivaGamesURL(t *testing.T) { + repo := &fakeRepository{ + launchable: gamedomain.LaunchableGame{ + CatalogItem: gamedomain.CatalogItem{ + AppCode: "lalu", + GameID: "vivagames_2", + PlatformCode: "vivagames", + ProviderGameID: "2", + GameName: "Viva Demo", + Status: gamedomain.StatusActive, + Orientation: "portrait", + }, + PlatformStatus: gamedomain.StatusActive, + APIBaseURL: "https://fallback.example.invalid/game.html", + AdapterType: gamedomain.AdapterVivaGamesV1, + AdapterConfigJSON: `{ + "app_id":1012127974, + "default_lang":"en-US", + "token_ttl_seconds":86400, + "uid_mode":"display_user_id", + "amount_rate":1, + "bgm_switch":0, + "gsp":101, + "currency_icon":"https://cdn.example/coin.png", + "game_urls":{"2":"https://dev-rich2.s3.ap-southeast-1.amazonaws.com/richForever.html?game_id=2&isShow=1"} + }`, + }, + } + svc := New(Config{LaunchSessionTTL: 15 * time.Minute}, repo, &fakeWallet{}, &fakeUser{}) + svc.now = func() time.Time { return time.UnixMilli(1700000000000) } + + result, err := svc.LaunchGame(context.Background(), LaunchCommand{ + AppCode: "lalu", + UserID: 42, + DisplayUserID: "420001", + GameID: "vivagames_2", + RoomID: "room_1", + AccessToken: "app_access_token_viva", + }) + if err != nil { + t.Fatalf("LaunchGame failed: %v", err) + } + if result.ExpiresAtMS != 1700086400000 { + t.Fatalf("vivagames launch must use adapter token ttl, got %+v", result) + } + parsed, err := url.Parse(result.LaunchURL) + if err != nil { + t.Fatalf("parse vivagames launch url failed: %v", err) + } + if parsed.Scheme+"://"+parsed.Host+parsed.Path != "https://dev-rich2.s3.ap-southeast-1.amazonaws.com/richForever.html" { + t.Fatalf("vivagames launch url must use per-game configured url: %s", result.LaunchURL) + } + query := parsed.Query() + expected := map[string]string{ + "appId": "1012127974", + "userId": "420001", + "code": "app_access_token_viva", + "gameId": "2", + "metadata": `{"room_id":"room_1"}`, + "language": "en-US", + "coinType": "0", + "amountRate": "1", + "bgmSwitch": "0", + "gsp": "101", + "currencyIcon": "https://cdn.example/coin.png", + "gameConfig": `{"currencyIcon":"https://cdn.example/coin.png"}`, + "bridge_keys": "appId,userId,code,metadata,language,coinType,amountRate,bgmSwitch,gsp,currencyIcon,gameConfig,gameId", + } + for key, want := range expected { + if got := query.Get(key); got != want { + t.Fatalf("vivagames launch query %s mismatch: got %q want %q url=%s", key, got, want, result.LaunchURL) + } + } +} + func TestLaunchGameRequiresBaishunLaunchURL(t *testing.T) { repo := &fakeRepository{ launchable: gamedomain.LaunchableGame{ @@ -1002,6 +1077,95 @@ func TestHandleBaishunSSTokenUserInfoUpdateAndChangeBalance(t *testing.T) { } } +func TestHandleVivaGamesTokenUserInfoUpdateAndChangeBalance(t *testing.T) { + secret := "vivagames-test-app-key" + token := "app_access_token_viva" + repo := &fakeRepository{ + platform: gamedomain.Platform{ + PlatformCode: "vivagames", + AdapterType: gamedomain.AdapterVivaGamesV1, + CallbackSecretCiphertext: secret, + AdapterConfigJSON: `{"app_id":1012127974,"uid_mode":"display_user_id"}`, + }, + session: gamedomain.LaunchSession{ + AppCode: "lalu", + SessionID: "game_sess_viva", + UserID: 42, + DisplayUserID: "420001", + RoomID: "room_1", + PlatformCode: "vivagames", + GameID: "vivagames_2", + ProviderGameID: "2", + LaunchTokenHash: stableHash(token), + Status: gamedomain.SessionActive, + ExpiresAtMS: 1700086400000, + }, + } + wallet := &fakeWallet{balanceAfter: 1880} + user := &fakeUser{} + svc := New(Config{}, repo, wallet, user) + svc.now = func() time.Time { return time.UnixMilli(1700000000000) } + + raw, contentType, err := svc.HandleCallback(context.Background(), &gamev1.CallbackRequest{ + Meta: &gamev1.RequestMeta{RequestId: "req-viva-token", AppCode: "lalu"}, + PlatformCode: "vivagames", + Operation: "get-token", + RawBody: vivaGamesTestBody(t, secret, `{"app_id":1012127974,"user_id":"420001","code":"app_access_token_viva"}`, 1700000000), + RemoteAddr: "3.1.174.194:443", + }) + if err != nil { + t.Fatalf("HandleCallback get-token failed: %v", err) + } + if contentType != "application/json" || !strings.Contains(string(raw), `"code":0`) || !strings.Contains(string(raw), `"token":"app_access_token_viva"`) || !strings.Contains(string(raw), `"expire_at":1700086400000`) { + t.Fatalf("vivagames get-token response mismatch: %s", raw) + } + + raw, _, err = svc.HandleCallback(context.Background(), &gamev1.CallbackRequest{ + Meta: &gamev1.RequestMeta{RequestId: "req-viva-userinfo", AppCode: "lalu"}, + PlatformCode: "vivagames", + Operation: "get-user-info", + RawBody: vivaGamesTestBody(t, secret, `{"app_id":1012127974,"user_id":"420001","token":"app_access_token_viva","client_ip":"110.86.1.130","game_id":2}`, 1700000000), + RemoteAddr: "3.1.174.194:443", + }) + if err != nil { + t.Fatalf("HandleCallback get-user-info failed: %v", err) + } + if !strings.Contains(string(raw), `"code":0`) || !strings.Contains(string(raw), `"user_id":"420001"`) || !strings.Contains(string(raw), `"avatar_url":"https://cdn.example/avatar.png"`) || !strings.Contains(string(raw), `"balance":1880`) { + t.Fatalf("vivagames userinfo response mismatch: %s", raw) + } + + raw, _, err = svc.HandleCallback(context.Background(), &gamev1.CallbackRequest{ + Meta: &gamev1.RequestMeta{RequestId: "req-viva-update", AppCode: "lalu"}, + PlatformCode: "vivagames", + Operation: "update-token", + RawBody: vivaGamesTestBody(t, secret, `{"app_id":1012127974,"user_id":"420001","token":"app_access_token_viva"}`, 1700000000), + RemoteAddr: "3.1.174.194:443", + }) + if err != nil { + t.Fatalf("HandleCallback update-token failed: %v", err) + } + if !strings.Contains(string(raw), `"code":0`) || !strings.Contains(string(raw), `"expire_at":1700086400000`) { + t.Fatalf("vivagames update-token response mismatch: %s", raw) + } + + raw, _, err = svc.HandleCallback(context.Background(), &gamev1.CallbackRequest{ + Meta: &gamev1.RequestMeta{RequestId: "req-viva-change", AppCode: "lalu"}, + PlatformCode: "vivagames", + Operation: "change-balance", + RawBody: vivaGamesTestBody(t, secret, `{"app_id":1012127974,"user_id":"420001","token":"app_access_token_viva","order_id":"viva_order_1","game_round_id":"round_1","game_id":2,"total_bet":120,"change_amount":-120,"change_reason":"bet","change_time_at":1700000000,"metadata":"{\"room_id\":\"room_1\"}"}`, 1700000000), + RemoteAddr: "3.1.174.194:443", + }) + if err != nil { + t.Fatalf("HandleCallback change-balance failed: %v", err) + } + if !strings.Contains(string(raw), `"code":0`) || !strings.Contains(string(raw), `"balance":1880`) { + t.Fatalf("vivagames change response mismatch: %s", raw) + } + if wallet.lastApply.GetCommandId() != "game:vivagames:viva_order_1" || wallet.lastApply.GetOpType() != "debit" || wallet.lastApply.GetCoinAmount() != 120 || wallet.lastApply.GetGameId() != "vivagames_2" { + t.Fatalf("vivagames wallet command mismatch: %+v", wallet.lastApply) + } +} + func TestHandleBaishunAccessTokenIgnoresStaleLaunchSessionGameID(t *testing.T) { secret := "baishun-test-app-key" userID := int64(312900923573673984) @@ -1458,3 +1622,20 @@ func baishunTestBody(t *testing.T, secret string, body string, timestamp int64) } return encoded } + +func vivaGamesTestBody(t *testing.T, secret string, body string, timestamp int64) []byte { + t.Helper() + var payload map[string]any + decoder := json.NewDecoder(strings.NewReader(body)) + decoder.UseNumber() + if err := decoder.Decode(&payload); err != nil { + t.Fatalf("decode vivagames test body: %v", err) + } + payload["timestamp"] = json.Number(strconv.FormatInt(timestamp, 10)) + payload["sign"] = vivaGamesSign(payload, secret) + encoded, err := json.Marshal(payload) + if err != nil { + t.Fatalf("marshal vivagames test body: %v", err) + } + return encoded +} diff --git a/services/game-service/internal/service/game/vivagames_adapter.go b/services/game-service/internal/service/game/vivagames_adapter.go new file mode 100644 index 00000000..2dd97fbe --- /dev/null +++ b/services/game-service/internal/service/game/vivagames_adapter.go @@ -0,0 +1,494 @@ +package game + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "sort" + "strconv" + "strings" + "time" + + gamev1 "hyapp.local/api/proto/game/v1" + "hyapp/pkg/xerr" + gamedomain "hyapp/services/game-service/internal/domain/game" +) + +const ( + // VIVAGAMES 文档定义 code=0 成功;游戏侧按 JSON code 判断业务结果,HTTP 200 只表示回调已被接收。 + vivaGamesCodeOK = 0 + vivaGamesCodeAppNotFound = 1001 + vivaGamesCodeInvalidArgument = 1002 + vivaGamesCodeSignatureInvalid = 1003 + vivaGamesCodeCodeInvalid = 1004 + vivaGamesCodeTokenInvalid = 1005 + vivaGamesCodeInternalError = 1006 + vivaGamesCodeGameMaintenance = 1007 + vivaGamesCodeInsufficientBalance = 1008 + vivaGamesCodeUserDisabled = 1009 + vivaGamesCodeRequestExpired = 1010 + + vivaGamesDefaultTokenTTL = 24 * time.Hour + vivaGamesSignatureValidWindow = 5 * time.Minute +) + +type vivaGamesAdapterConfig struct { + // app_id 来自 VIVAGAMES 后台;callbackSecret 存放 PDF 签名算法使用的 AppKey。 + AppID int64 `json:"app_id"` + AppIDAlias int64 `json:"appId"` + AppSecret string `json:"app_secret"` + UIDMode string `json:"uid_mode"` + DefaultLang string `json:"default_lang"` + // token_ttl_seconds 控制启动 session 至少保留多久,避免 H5 游戏中途回调时 token 已过期。 + TokenTTLSeconds int64 `json:"token_ttl_seconds"` + + // 以下字段直接对应 NativeBridge.getConfig,后台热改后下一次启动生效。 + Metadata string `json:"metadata"` + CoinType int64 `json:"coin_type"` + AmountRate int64 `json:"amount_rate"` + BGMSwitch *int64 `json:"bgm_switch"` + GSP int64 `json:"gsp"` + CurrencyIcon string `json:"currency_icon"` + + // VIVAGAMES 文档没有游戏列表 API,game_urls 用后台配置保存厂商提供的逐游戏 H5 URL。 + GameURLs map[string]string `json:"game_urls"` + LaunchURLTemplate string `json:"launch_url_template"` +} + +func vivaGamesConfigFromPlatform(value any) vivaGamesAdapterConfig { + var raw string + switch typed := value.(type) { + case gamedomain.Platform: + raw = typed.AdapterConfigJSON + case gamedomain.LaunchableGame: + raw = typed.AdapterConfigJSON + } + var config vivaGamesAdapterConfig + _ = json.Unmarshal([]byte(strings.TrimSpace(raw)), &config) + if config.AppID == 0 { + config.AppID = config.AppIDAlias + } + config.AppSecret = strings.TrimSpace(config.AppSecret) + config.UIDMode = strings.ToLower(strings.TrimSpace(config.UIDMode)) + config.DefaultLang = strings.TrimSpace(config.DefaultLang) + config.Metadata = strings.TrimSpace(config.Metadata) + config.CurrencyIcon = strings.TrimSpace(config.CurrencyIcon) + config.LaunchURLTemplate = strings.TrimSpace(config.LaunchURLTemplate) + config.GameURLs = normalizeStringMap(config.GameURLs) + return config +} + +func (c vivaGamesAdapterConfig) TokenTTL() time.Duration { + if c.TokenTTLSeconds > 0 { + return time.Duration(c.TokenTTLSeconds) * time.Second + } + return vivaGamesDefaultTokenTTL +} + +func (c vivaGamesAdapterConfig) LanguageValue() string { + if c.DefaultLang != "" { + return c.DefaultLang + } + return "en-US" +} + +func (c vivaGamesAdapterConfig) AmountRateValue() int64 { + if c.AmountRate > 0 { + return c.AmountRate + } + return 1 +} + +func (c vivaGamesAdapterConfig) BGMSwitchValue() int64 { + if c.BGMSwitch != nil { + return *c.BGMSwitch + } + return 1 +} + +func (c vivaGamesAdapterConfig) GSPValue() int64 { + if c.GSP > 0 { + return c.GSP + } + return 101 +} + +func (c vivaGamesAdapterConfig) MetadataValue(session gamedomain.LaunchSession) string { + if c.Metadata != "" { + return c.Metadata + } + if strings.TrimSpace(session.RoomID) == "" { + return "" + } + raw, err := json.Marshal(map[string]string{"room_id": strings.TrimSpace(session.RoomID)}) + if err != nil { + return "" + } + return string(raw) +} + +func vivaGamesLaunchBaseURL(game gamedomain.LaunchableGame, config vivaGamesAdapterConfig) string { + for _, key := range []string{game.ProviderGameID, game.GameID, strings.ToLower(game.GameID)} { + if raw := strings.TrimSpace(config.GameURLs[strings.TrimSpace(key)]); raw != "" { + return raw + } + } + if config.LaunchURLTemplate == "" { + return "" + } + replacer := strings.NewReplacer( + "{provider_game_id}", strings.TrimSpace(game.ProviderGameID), + "{providerGameId}", strings.TrimSpace(game.ProviderGameID), + "{game_id}", strings.TrimSpace(game.GameID), + "{gameId}", strings.TrimSpace(game.GameID), + ) + return strings.TrimSpace(replacer.Replace(config.LaunchURLTemplate)) +} + +func vivaGamesGameConfigQuery(currencyIcon string) string { + raw, err := json.Marshal(map[string]string{"currencyIcon": strings.TrimSpace(currencyIcon)}) + if err != nil { + return "" + } + return string(raw) +} + +func (s *Service) handleVivaGamesOperation(ctx context.Context, app string, platform gamedomain.Platform, req *gamev1.CallbackRequest, operation string, requestHash string) ([]byte, string, string, bool, string, error) { + if !callbackIPAllowed(req, platform.CallbackIPWhitelist) { + raw, contentType := vivaGamesJSON(vivaGamesCodeSignatureInvalid, "ip restricted", map[string]any{}) + return raw, contentType, strconv.Itoa(vivaGamesCodeSignatureInvalid), false, "", nil + } + config := vivaGamesConfigFromPlatform(platform) + body, signed, decoded, expired, providerRequestID := s.decodeVivaGamesBody(req, platform.CallbackSecretCiphertext) + if !decoded { + raw, contentType := vivaGamesJSON(vivaGamesCodeInvalidArgument, "invalid argument", map[string]any{}) + return raw, contentType, strconv.Itoa(vivaGamesCodeInvalidArgument), false, providerRequestID, nil + } + if expired { + raw, contentType := vivaGamesJSON(vivaGamesCodeRequestExpired, "request expired", map[string]any{}) + return raw, contentType, strconv.Itoa(vivaGamesCodeRequestExpired), false, providerRequestID, nil + } + if !signed { + raw, contentType := vivaGamesJSON(vivaGamesCodeSignatureInvalid, "sign error", map[string]any{}) + return raw, contentType, strconv.Itoa(vivaGamesCodeSignatureInvalid), false, providerRequestID, nil + } + appID := body.AppIDInt64() + if appID <= 0 { + raw, contentType := vivaGamesJSON(vivaGamesCodeInvalidArgument, "invalid app_id", map[string]any{}) + return raw, contentType, strconv.Itoa(vivaGamesCodeInvalidArgument), signed, providerRequestID, nil + } + if config.AppID > 0 && appID != config.AppID { + raw, contentType := vivaGamesJSON(vivaGamesCodeAppNotFound, "appId not found", map[string]any{}) + return raw, contentType, strconv.Itoa(vivaGamesCodeAppNotFound), signed, providerRequestID, nil + } + switch operation { + case "get-token", "get_token", "gettoken", "api/get-token", "api/get_token", "v1/api/get-token": + return s.handleVivaGamesGetToken(ctx, app, config, req, body) + case "get-user-info", "get_user_info", "userinfo", "user_info", "api/get-user-info", "api/get_user_info", "v1/api/get-user-info": + return s.handleVivaGamesUserInfo(ctx, app, config, req, body) + case "update-token", "update_token", "updatetoken", "api/update-token", "api/update_token", "v1/api/update-token": + return s.handleVivaGamesUpdateToken(ctx, app, config, body) + case "change-balance", "change_balance", "changebalance", "api/change-balance", "api/change_balance", "v1/api/change-balance": + return s.handleVivaGamesChangeBalance(ctx, app, config, req, body, requestHash) + default: + raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{}) + return raw, contentType, strconv.Itoa(vivaGamesCodeOK), signed, providerRequestID, nil + } +} + +type vivaGamesBody struct { + AppID json.Number `json:"app_id"` + UserID string `json:"user_id"` + Code string `json:"code"` + Token string `json:"token"` + ClientIP string `json:"client_ip"` + GameID json.Number `json:"game_id"` + OrderID string `json:"order_id"` + GameRoundID string `json:"game_round_id"` + TotalBet int64 `json:"total_bet"` + ChangeAmount int64 `json:"change_amount"` + ChangeReason string `json:"change_reason"` + ChangeTimeAt int64 `json:"change_time_at"` + Metadata string `json:"metadata"` + ExtendType string `json:"extend_type"` + Extend string `json:"extend"` + IgnoreExpire bool `json:"ignore_expire"` + Timestamp int64 `json:"timestamp"` + Signature string `json:"sign"` +} + +func (s *Service) decodeVivaGamesBody(req *gamev1.CallbackRequest, secret string) (vivaGamesBody, bool, bool, bool, string) { + var body vivaGamesBody + decoder := json.NewDecoder(strings.NewReader(string(req.GetRawBody()))) + decoder.UseNumber() + if err := decoder.Decode(&body); err != nil { + return vivaGamesBody{}, false, false, false, "" + } + body.normalize() + providerRequestID := strings.TrimSpace(body.OrderID) + params, err := vivaGamesParamsForSign(req.GetRawBody()) + if err != nil { + return vivaGamesBody{}, false, false, false, providerRequestID + } + signed, expired := s.vivaGamesSignatureValid(params, body.Signature, body.Timestamp, secret) + return body, signed, true, expired, providerRequestID +} + +func vivaGamesParamsForSign(raw []byte) (map[string]any, error) { + var params map[string]any + decoder := json.NewDecoder(strings.NewReader(string(raw))) + decoder.UseNumber() + if err := decoder.Decode(¶ms); err != nil { + return nil, err + } + delete(params, "sign") + return params, nil +} + +func (s *Service) vivaGamesSignatureValid(params map[string]any, signature string, timestamp int64, secret string) (bool, bool) { + secret = strings.TrimSpace(secret) + if secret == "" || strings.TrimSpace(signature) == "" || timestamp <= 0 { + return false, false + } + requestAt := time.Unix(timestamp, 0) + now := s.now() + if requestAt.After(now.Add(vivaGamesSignatureValidWindow)) || now.Sub(requestAt) > vivaGamesSignatureValidWindow { + return false, true + } + expected := vivaGamesSign(params, secret) + return strings.EqualFold(strings.TrimSpace(signature), expected), false +} + +func vivaGamesSign(params map[string]any, secret string) string { + keys := make([]string, 0, len(params)) + for key, value := range params { + if value == nil || strings.TrimSpace(key) == "" || strings.EqualFold(key, "sign") { + continue + } + keys = append(keys, key) + } + sort.Strings(keys) + parts := make([]string, 0, len(keys)+1) + for _, key := range keys { + parts = append(parts, fmt.Sprintf("%s=%s", key, vivaGamesSignValue(params[key]))) + } + parts = append(parts, "key="+strings.TrimSpace(secret)) + sum := sha256.Sum256([]byte(strings.Join(parts, "&"))) + return hex.EncodeToString(sum[:]) +} + +func vivaGamesSignValue(value any) string { + switch typed := value.(type) { + case json.Number: + return typed.String() + case string: + return typed + case bool: + if typed { + return "true" + } + return "false" + default: + return strings.TrimSpace(fmt.Sprint(typed)) + } +} + +func (body *vivaGamesBody) normalize() { + body.UserID = strings.TrimSpace(body.UserID) + body.Code = strings.TrimSpace(body.Code) + body.Token = strings.TrimSpace(body.Token) + body.ClientIP = strings.TrimSpace(body.ClientIP) + body.OrderID = strings.TrimSpace(body.OrderID) + body.GameRoundID = strings.TrimSpace(body.GameRoundID) + body.ChangeReason = strings.ToLower(strings.TrimSpace(body.ChangeReason)) + body.Metadata = strings.TrimSpace(body.Metadata) + body.ExtendType = strings.TrimSpace(body.ExtendType) + body.Extend = strings.TrimSpace(body.Extend) + body.Signature = strings.TrimSpace(body.Signature) +} + +func (body vivaGamesBody) AppIDInt64() int64 { + value, _ := strconv.ParseInt(strings.TrimSpace(body.AppID.String()), 10, 64) + return value +} + +func (body vivaGamesBody) GameIDInt64() int64 { + value, _ := strconv.ParseInt(strings.TrimSpace(body.GameID.String()), 10, 64) + return value +} + +func (s *Service) handleVivaGamesGetToken(ctx context.Context, app string, config vivaGamesAdapterConfig, req *gamev1.CallbackRequest, body vivaGamesBody) ([]byte, string, string, bool, string, error) { + if body.UserID == "" || body.Code == "" { + return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, "") + } + session, err := s.validVivaGamesSession(ctx, app, body.Code, false) + if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, 0) { + return vivaGamesAdapterError(vivaGamesCodeCodeInvalid, "code error", true, "") + } + raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{ + "token": body.Code, + "expire_at": session.ExpiresAtMS, + }) + return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, "", nil +} + +func (s *Service) handleVivaGamesUserInfo(ctx context.Context, app string, config vivaGamesAdapterConfig, req *gamev1.CallbackRequest, body vivaGamesBody) ([]byte, string, string, bool, string, error) { + if body.UserID == "" || body.Token == "" { + return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, "") + } + session, err := s.validVivaGamesSession(ctx, app, body.Token, false) + if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, body.GameIDInt64()) { + return vivaGamesAdapterError(vivaGamesCodeTokenInvalid, "token error", true, "") + } + snapshot, err := s.yomiUserSnapshot(ctx, app, req, session.UserID) + if err != nil { + return vivaGamesAdapterError(vivaGamesCodeFromError(err), vivaGamesMessageFromError(err), true, "") + } + raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{ + "user_id": externalUserID(session, config.UIDMode), + "nickname": snapshot.Nickname, + "avatar_url": snapshot.Avatar, + "balance": snapshot.Balance, + "status": 0, + }) + return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, "", nil +} + +func (s *Service) handleVivaGamesUpdateToken(ctx context.Context, app string, config vivaGamesAdapterConfig, body vivaGamesBody) ([]byte, string, string, bool, string, error) { + if body.UserID == "" || body.Token == "" { + return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, "") + } + session, err := s.validVivaGamesSession(ctx, app, body.Token, false) + if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, 0) { + return vivaGamesAdapterError(vivaGamesCodeTokenInvalid, "token error", true, "") + } + raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{ + "token": body.Token, + "expire_at": session.ExpiresAtMS, + }) + return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, "", nil +} + +func (s *Service) handleVivaGamesChangeBalance(ctx context.Context, app string, config vivaGamesAdapterConfig, req *gamev1.CallbackRequest, body vivaGamesBody, requestHash string) ([]byte, string, string, bool, string, error) { + if body.UserID == "" || body.Token == "" || body.OrderID == "" || body.GameIDInt64() <= 0 || body.ChangeReason == "" { + return vivaGamesAdapterError(vivaGamesCodeInvalidArgument, "invalid argument", true, body.OrderID) + } + session, err := s.validVivaGamesSession(ctx, app, body.Token, body.IgnoreExpire) + if err != nil || !vivaGamesSessionMatches(session, config, body.UserID, body.GameIDInt64()) { + return vivaGamesAdapterError(vivaGamesCodeTokenInvalid, "token error", true, body.OrderID) + } + if session.ProviderGameID == "" { + session.ProviderGameID = strconv.FormatInt(body.GameIDInt64(), 10) + } + if session.GameID == "" { + session.GameID = session.ProviderGameID + } + opType, amount := vivaGamesOpType(body.ChangeAmount) + result, err := s.applyYomiCoinChange(ctx, app, req, session, body.OrderID, body.GameRoundID, opType, amount, "", requestHash) + if err != nil { + code := vivaGamesCodeFromError(err) + balance := s.bestEffortCoinBalance(ctx, app, req.GetMeta().GetRequestId(), session.UserID) + raw, contentType := vivaGamesJSON(code, vivaGamesMessageFromError(err), map[string]any{"balance": balance}) + return raw, contentType, strconv.Itoa(code), true, body.OrderID, nil + } + raw, contentType := vivaGamesJSON(vivaGamesCodeOK, "", map[string]any{"balance": result.BalanceAfter}) + return raw, contentType, strconv.Itoa(vivaGamesCodeOK), true, body.OrderID, nil +} + +func (s *Service) validVivaGamesSession(ctx context.Context, app string, token string, ignoreExpire bool) (gamedomain.LaunchSession, error) { + token = strings.TrimSpace(token) + if token == "" { + return gamedomain.LaunchSession{}, xerr.New(xerr.Unauthorized, "token invalid") + } + // VIVAGAMES token 直接复用 App access token;先解析 JWT 可以避免同一登录 token 多游戏启动时命中过期旧 session。 + if strings.Count(token, ".") == 2 && !ignoreExpire { + if session, err := s.appSessionFromAccessToken(app, token); err == nil { + return session, nil + } + } + if session, err := s.repository.GetLaunchSessionByToken(ctx, app, token); err == nil { + if session.Status != gamedomain.SessionActive { + return gamedomain.LaunchSession{}, xerr.New(xerr.SessionExpired, "token expired") + } + if !ignoreExpire && session.ExpiresAtMS <= s.now().UnixMilli() { + return gamedomain.LaunchSession{}, xerr.New(xerr.SessionExpired, "token expired") + } + return session, nil + } + if strings.Count(token, ".") == 2 { + return s.appSessionFromAccessToken(app, token) + } + return gamedomain.LaunchSession{}, xerr.New(xerr.Unauthorized, "token invalid") +} + +func vivaGamesSessionMatches(session gamedomain.LaunchSession, config vivaGamesAdapterConfig, userID string, gameID int64) bool { + if gameID > 0 && strings.TrimSpace(session.ProviderGameID) != "" && strconv.FormatInt(gameID, 10) != strings.TrimSpace(session.ProviderGameID) { + return false + } + userID = strings.TrimSpace(userID) + return userID == externalUserID(session, config.UIDMode) || + userID == strings.TrimSpace(session.DisplayUserID) || + userID == strconv.FormatInt(session.UserID, 10) +} + +func vivaGamesOpType(changeAmount int64) (string, int64) { + if changeAmount < 0 { + return "debit", -changeAmount + } + return "credit", changeAmount +} + +func vivaGamesAdapterError(code int, message string, signatureValid bool, providerRequestID string) ([]byte, string, string, bool, string, error) { + raw, contentType := vivaGamesJSON(code, message, map[string]any{}) + return raw, contentType, strconv.Itoa(code), signatureValid, providerRequestID, nil +} + +func vivaGamesJSON(code int, message string, data any) ([]byte, string) { + if data == nil { + data = map[string]any{} + } + return jsonResponse(map[string]any{ + "code": code, + "msg": strings.TrimSpace(message), + "data": data, + }) +} + +func vivaGamesCodeFromError(err error) int { + switch { + case err == nil: + return vivaGamesCodeOK + case xerr.IsCode(err, xerr.InsufficientBalance): + return vivaGamesCodeInsufficientBalance + case xerr.IsCode(err, xerr.SessionExpired), xerr.IsCode(err, xerr.Unauthorized): + return vivaGamesCodeTokenInvalid + case xerr.IsCode(err, xerr.InvalidArgument): + return vivaGamesCodeInvalidArgument + case xerr.IsCode(err, xerr.Conflict): + return vivaGamesCodeGameMaintenance + case xerr.IsCode(err, xerr.NotFound): + return vivaGamesCodeTokenInvalid + default: + return vivaGamesCodeInternalError + } +} + +func vivaGamesMessageFromError(err error) string { + switch vivaGamesCodeFromError(err) { + case vivaGamesCodeInsufficientBalance: + return "balance not enough" + case vivaGamesCodeTokenInvalid: + return "token error" + case vivaGamesCodeInvalidArgument: + return "invalid argument" + case vivaGamesCodeGameMaintenance: + return "game maintenance" + default: + if err != nil && strings.TrimSpace(xerr.MessageOf(err)) != "" { + return xerr.MessageOf(err) + } + return "internal error" + } +} diff --git a/services/gateway-service/internal/app/app.go b/services/gateway-service/internal/app/app.go index a7731e1f..23abc979 100644 --- a/services/gateway-service/internal/app/app.go +++ b/services/gateway-service/internal/app/app.go @@ -99,8 +99,11 @@ func New(cfg config.Config) (*App, error) { var achievementClient client.AchievementClient = client.NewGRPCAchievementClient(activityConn) var registrationRewardClient client.RegistrationRewardClient = client.NewGRPCRegistrationRewardClient(activityConn) var firstRechargeRewardClient client.FirstRechargeRewardClient = client.NewGRPCFirstRechargeRewardClient(activityConn) + var cumulativeRechargeRewardClient client.CumulativeRechargeRewardClient = client.NewGRPCCumulativeRechargeRewardClient(activityConn) var sevenDayCheckInClient client.SevenDayCheckInClient = client.NewGRPCSevenDayCheckInClient(activityConn) var luckyGiftClient client.LuckyGiftClient = client.NewGRPCLuckyGiftClient(activityConn) + var roomTurnoverRewardClient client.RoomTurnoverRewardClient = client.NewGRPCRoomTurnoverRewardClient(activityConn) + var weeklyStarClient client.WeeklyStarClient = client.NewGRPCWeeklyStarClient(activityConn) var broadcastClient client.BroadcastClient = client.NewGRPCBroadcastClient(activityConn) var gameClient client.GameClient = client.NewGRPCGameClient(gameConn) appConfigReader, err := openAppConfigReader(cfg.AppConfig) @@ -163,8 +166,11 @@ func New(cfg config.Config) (*App, error) { handler.SetAchievementClient(achievementClient) handler.SetRegistrationRewardClient(registrationRewardClient) handler.SetFirstRechargeRewardClient(firstRechargeRewardClient) + handler.SetCumulativeRechargeRewardClient(cumulativeRechargeRewardClient) handler.SetSevenDayCheckInClient(sevenDayCheckInClient) handler.SetLuckyGiftClient(luckyGiftClient) + handler.SetRoomTurnoverRewardClient(roomTurnoverRewardClient) + handler.SetWeeklyStarClient(weeklyStarClient) handler.SetBroadcastClient(broadcastClient) handler.SetGameClient(gameClient) handler.SetLeaderboardWalletDB(leaderboardWalletDB) diff --git a/services/gateway-service/internal/client/cumulative_recharge_reward_client.go b/services/gateway-service/internal/client/cumulative_recharge_reward_client.go new file mode 100644 index 00000000..731c8fcb --- /dev/null +++ b/services/gateway-service/internal/client/cumulative_recharge_reward_client.go @@ -0,0 +1,25 @@ +package client + +import ( + "context" + + "google.golang.org/grpc" + activityv1 "hyapp.local/api/proto/activity/v1" +) + +// CumulativeRechargeRewardClient abstracts gateway reads from activity-service cumulative recharge reward APIs. +type CumulativeRechargeRewardClient interface { + GetCumulativeRechargeRewardStatus(ctx context.Context, req *activityv1.GetCumulativeRechargeRewardStatusRequest) (*activityv1.GetCumulativeRechargeRewardStatusResponse, error) +} + +type grpcCumulativeRechargeRewardClient struct { + client activityv1.CumulativeRechargeRewardServiceClient +} + +func NewGRPCCumulativeRechargeRewardClient(conn *grpc.ClientConn) CumulativeRechargeRewardClient { + return &grpcCumulativeRechargeRewardClient{client: activityv1.NewCumulativeRechargeRewardServiceClient(conn)} +} + +func (c *grpcCumulativeRechargeRewardClient) GetCumulativeRechargeRewardStatus(ctx context.Context, req *activityv1.GetCumulativeRechargeRewardStatusRequest) (*activityv1.GetCumulativeRechargeRewardStatusResponse, error) { + return c.client.GetCumulativeRechargeRewardStatus(ctx, req) +} diff --git a/services/gateway-service/internal/client/room_turnover_reward_client.go b/services/gateway-service/internal/client/room_turnover_reward_client.go new file mode 100644 index 00000000..c9a7af6f --- /dev/null +++ b/services/gateway-service/internal/client/room_turnover_reward_client.go @@ -0,0 +1,25 @@ +package client + +import ( + "context" + + "google.golang.org/grpc" + activityv1 "hyapp.local/api/proto/activity/v1" +) + +// RoomTurnoverRewardClient abstracts gateway reads from activity-service room turnover reward APIs. +type RoomTurnoverRewardClient interface { + GetRoomTurnoverRewardStatus(ctx context.Context, req *activityv1.GetRoomTurnoverRewardStatusRequest) (*activityv1.GetRoomTurnoverRewardStatusResponse, error) +} + +type grpcRoomTurnoverRewardClient struct { + client activityv1.RoomTurnoverRewardServiceClient +} + +func NewGRPCRoomTurnoverRewardClient(conn *grpc.ClientConn) RoomTurnoverRewardClient { + return &grpcRoomTurnoverRewardClient{client: activityv1.NewRoomTurnoverRewardServiceClient(conn)} +} + +func (c *grpcRoomTurnoverRewardClient) GetRoomTurnoverRewardStatus(ctx context.Context, req *activityv1.GetRoomTurnoverRewardStatusRequest) (*activityv1.GetRoomTurnoverRewardStatusResponse, error) { + return c.client.GetRoomTurnoverRewardStatus(ctx, req) +} diff --git a/services/gateway-service/internal/client/weekly_star_client.go b/services/gateway-service/internal/client/weekly_star_client.go new file mode 100644 index 00000000..a7d1d246 --- /dev/null +++ b/services/gateway-service/internal/client/weekly_star_client.go @@ -0,0 +1,35 @@ +package client + +import ( + "context" + + "google.golang.org/grpc" + activityv1 "hyapp.local/api/proto/activity/v1" +) + +// WeeklyStarClient abstracts gateway access to activity-service weekly-star APIs. +type WeeklyStarClient interface { + GetWeeklyStarCurrent(ctx context.Context, req *activityv1.GetWeeklyStarCurrentRequest) (*activityv1.GetWeeklyStarCurrentResponse, error) + ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) + ListWeeklyStarHistory(ctx context.Context, req *activityv1.ListWeeklyStarHistoryRequest) (*activityv1.ListWeeklyStarHistoryResponse, error) +} + +type grpcWeeklyStarClient struct { + client activityv1.WeeklyStarServiceClient +} + +func NewGRPCWeeklyStarClient(conn *grpc.ClientConn) WeeklyStarClient { + return &grpcWeeklyStarClient{client: activityv1.NewWeeklyStarServiceClient(conn)} +} + +func (c *grpcWeeklyStarClient) GetWeeklyStarCurrent(ctx context.Context, req *activityv1.GetWeeklyStarCurrentRequest) (*activityv1.GetWeeklyStarCurrentResponse, error) { + return c.client.GetWeeklyStarCurrent(ctx, req) +} + +func (c *grpcWeeklyStarClient) ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error) { + return c.client.ListWeeklyStarLeaderboard(ctx, req) +} + +func (c *grpcWeeklyStarClient) ListWeeklyStarHistory(ctx context.Context, req *activityv1.ListWeeklyStarHistoryRequest) (*activityv1.ListWeeklyStarHistoryResponse, error) { + return c.client.ListWeeklyStarHistory(ctx, req) +} diff --git a/services/gateway-service/internal/transport/http/activityapi/cumulative_recharge_reward_handler.go b/services/gateway-service/internal/transport/http/activityapi/cumulative_recharge_reward_handler.go new file mode 100644 index 00000000..8379a6bc --- /dev/null +++ b/services/gateway-service/internal/transport/http/activityapi/cumulative_recharge_reward_handler.go @@ -0,0 +1,225 @@ +package activityapi + +import ( + "net/http" + + activityv1 "hyapp.local/api/proto/activity/v1" + userv1 "hyapp.local/api/proto/user/v1" + "hyapp/services/gateway-service/internal/auth" + "hyapp/services/gateway-service/internal/transport/http/httpkit" +) + +type cumulativeRechargeRewardUserData struct { + UserID int64 `json:"user_id"` + DisplayUserID string `json:"display_user_id"` + Username string `json:"username"` + Avatar string `json:"avatar"` +} + +type cumulativeRechargeRewardPeriodData struct { + StartMS int64 `json:"start_ms"` + EndMS int64 `json:"end_ms"` +} + +type cumulativeRechargeRewardTierData struct { + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + TierName string `json:"tier_name"` + ThresholdUSDMinor int64 `json:"threshold_usd_minor"` + ResourceGroupID int64 `json:"resource_group_id"` + ResourceGroup *checkinResourceGroupData `json:"resource_group,omitempty"` + Status string `json:"status"` + SortOrder int32 `json:"sort_order"` + Reached bool `json:"reached"` + Grant *cumulativeRechargeRewardGrantData `json:"grant,omitempty"` +} + +type cumulativeRechargeRewardProgressData struct { + TotalUSDMinor int64 `json:"total_usd_minor"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type cumulativeRechargeRewardGrantData struct { + GrantID string `json:"grant_id"` + EventID string `json:"event_id"` + TransactionID string `json:"transaction_id"` + CommandID string `json:"command_id"` + UserID int64 `json:"user_id"` + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + ThresholdUSDMinor int64 `json:"threshold_usd_minor"` + ResourceGroupID int64 `json:"resource_group_id"` + ReachedUSDMinor int64 `json:"reached_usd_minor"` + QualifyingUSDMinor int64 `json:"qualifying_usd_minor"` + RechargeCoinAmount int64 `json:"recharge_coin_amount"` + RechargeType string `json:"recharge_type"` + Status string `json:"status"` + WalletCommandID string `json:"wallet_command_id"` + WalletGrantID string `json:"wallet_grant_id"` + FailureReason string `json:"failure_reason"` + GrantedAtMS int64 `json:"granted_at_ms"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type cumulativeRechargeRewardStatusData struct { + Enabled bool `json:"enabled"` + User cumulativeRechargeRewardUserData `json:"user"` + CycleKey string `json:"cycle_key"` + Period cumulativeRechargeRewardPeriodData `json:"period"` + Tiers []cumulativeRechargeRewardTierData `json:"tiers"` + Progress cumulativeRechargeRewardProgressData `json:"progress"` + Grants []cumulativeRechargeRewardGrantData `json:"grants"` + ServerTimeMS int64 `json:"server_time_ms"` +} + +// getCumulativeRechargeRewardStatus 返回当前用户本 UTC 周期累充面板;累计与发放事实只读 activity-service,gateway 只补展示依赖。 +func (h *Handler) getCumulativeRechargeRewardStatus(writer http.ResponseWriter, request *http.Request) { + if h.cumulativeRecharge == nil { + httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") + return + } + userID := auth.UserIDFromContext(request.Context()) + resp, err := h.cumulativeRecharge.GetCumulativeRechargeRewardStatus(request.Context(), &activityv1.GetCumulativeRechargeRewardStatusRequest{ + Meta: httpkit.ActivityMeta(request), + UserId: userID, + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + + status := resp.GetStatus() + // activity-service 只保存 resource_group_id;gateway 负责补资源组展示素材,避免活动服务依赖 H5 展示结构。 + groups, ok := h.resolveCheckinResourceGroups(writer, request, cumulativeRechargeRewardGroupIDs(status)) + if !ok { + return + } + userData := cumulativeRechargeRewardUserData{UserID: userID} + if h.userProfileClient != nil { + // 用户资料是 H5 展示信息,读取失败时不影响活动金额和发放状态返回。 + if userResp, userErr := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{ + Meta: httpkit.UserMeta(request, ""), + UserIds: []int64{userID}, + }); userErr == nil { + userData = cumulativeRechargeRewardUserFromProto(userResp.GetUsers()[userID], userID) + } + } + + httpkit.WriteOK(writer, request, cumulativeRechargeRewardStatusFromProto(status, userData, groups)) +} + +func cumulativeRechargeRewardStatusFromProto(item *activityv1.CumulativeRechargeRewardStatus, user cumulativeRechargeRewardUserData, groups map[int64]checkinResourceGroupData) cumulativeRechargeRewardStatusData { + if item == nil { + return cumulativeRechargeRewardStatusData{ + User: user, + Tiers: []cumulativeRechargeRewardTierData{}, + Grants: []cumulativeRechargeRewardGrantData{}, + } + } + grants := make([]cumulativeRechargeRewardGrantData, 0, len(item.GetGrants())) + grantsByTierID := make(map[int64]cumulativeRechargeRewardGrantData, len(item.GetGrants())) + for _, grant := range item.GetGrants() { + // grant 按 tier_id 回填到档位,H5 可以直接渲染每个档位的 reached/granted/failed 状态。 + converted := cumulativeRechargeRewardGrantFromProto(grant) + grants = append(grants, converted) + grantsByTierID[converted.TierID] = converted + } + tiers := make([]cumulativeRechargeRewardTierData, 0, len(item.GetConfig().GetTiers())) + for _, tier := range item.GetConfig().GetTiers() { + converted := cumulativeRechargeRewardTierFromProto(tier, item.GetProgress().GetTotalUsdMinor(), groups) + if grant, ok := grantsByTierID[converted.TierID]; ok { + converted.Grant = &grant + } + tiers = append(tiers, converted) + } + return cumulativeRechargeRewardStatusData{ + Enabled: item.GetConfig().GetEnabled(), + User: user, + CycleKey: item.GetCycleKey(), + Period: cumulativeRechargeRewardPeriodData{ + StartMS: item.GetPeriodStartMs(), + EndMS: item.GetPeriodEndMs(), + }, + Tiers: tiers, + Progress: cumulativeRechargeRewardProgressData{ + TotalUSDMinor: item.GetProgress().GetTotalUsdMinor(), + UpdatedAtMS: item.GetProgress().GetUpdatedAtMs(), + }, + Grants: grants, + ServerTimeMS: item.GetServerTimeMs(), + } +} + +func cumulativeRechargeRewardTierFromProto(item *activityv1.CumulativeRechargeRewardTier, totalUSDMinor int64, groups map[int64]checkinResourceGroupData) cumulativeRechargeRewardTierData { + if item == nil { + return cumulativeRechargeRewardTierData{} + } + data := cumulativeRechargeRewardTierData{ + TierID: item.GetTierId(), + TierCode: item.GetTierCode(), + TierName: item.GetTierName(), + ThresholdUSDMinor: item.GetThresholdUsdMinor(), + ResourceGroupID: item.GetResourceGroupId(), + Status: item.GetStatus(), + SortOrder: item.GetSortOrder(), + Reached: totalUSDMinor >= item.GetThresholdUsdMinor(), + } + if group, ok := groups[item.GetResourceGroupId()]; ok { + data.ResourceGroup = &group + } + return data +} + +func cumulativeRechargeRewardGrantFromProto(item *activityv1.CumulativeRechargeRewardGrant) cumulativeRechargeRewardGrantData { + if item == nil { + return cumulativeRechargeRewardGrantData{} + } + return cumulativeRechargeRewardGrantData{ + GrantID: item.GetGrantId(), + EventID: item.GetEventId(), + TransactionID: item.GetTransactionId(), + CommandID: item.GetCommandId(), + UserID: item.GetUserId(), + TierID: item.GetTierId(), + TierCode: item.GetTierCode(), + ThresholdUSDMinor: item.GetThresholdUsdMinor(), + ResourceGroupID: item.GetResourceGroupId(), + ReachedUSDMinor: item.GetReachedUsdMinor(), + QualifyingUSDMinor: item.GetQualifyingUsdMinor(), + RechargeCoinAmount: item.GetRechargeCoinAmount(), + RechargeType: item.GetRechargeType(), + Status: item.GetStatus(), + WalletCommandID: item.GetWalletCommandId(), + WalletGrantID: item.GetWalletGrantId(), + FailureReason: item.GetFailureReason(), + GrantedAtMS: item.GetGrantedAtMs(), + CreatedAtMS: item.GetCreatedAtMs(), + UpdatedAtMS: item.GetUpdatedAtMs(), + } +} + +func cumulativeRechargeRewardGroupIDs(item *activityv1.CumulativeRechargeRewardStatus) []int64 { + if item == nil || item.GetConfig() == nil { + return nil + } + ids := make([]int64, 0, len(item.GetConfig().GetTiers())) + for _, tier := range item.GetConfig().GetTiers() { + if tier.GetResourceGroupId() > 0 { + ids = append(ids, tier.GetResourceGroupId()) + } + } + return ids +} + +func cumulativeRechargeRewardUserFromProto(item *userv1.User, fallbackUserID int64) cumulativeRechargeRewardUserData { + if item == nil { + return cumulativeRechargeRewardUserData{UserID: fallbackUserID} + } + return cumulativeRechargeRewardUserData{ + UserID: item.GetUserId(), + DisplayUserID: item.GetDisplayUserId(), + Username: item.GetUsername(), + Avatar: item.GetAvatar(), + } +} diff --git a/services/gateway-service/internal/transport/http/activityapi/handler.go b/services/gateway-service/internal/transport/http/activityapi/handler.go index 144ab475..20444f76 100644 --- a/services/gateway-service/internal/transport/http/activityapi/handler.go +++ b/services/gateway-service/internal/transport/http/activityapi/handler.go @@ -19,8 +19,11 @@ type Handler struct { walletDB *sql.DB registrationReward client.RegistrationRewardClient firstRechargeReward client.FirstRechargeRewardClient + cumulativeRecharge client.CumulativeRechargeRewardClient sevenDayCheckIn client.SevenDayCheckInClient luckyGift client.LuckyGiftClient + roomTurnoverReward client.RoomTurnoverRewardClient + weeklyStar client.WeeklyStarClient } type Config struct { @@ -33,8 +36,11 @@ type Config struct { WalletDB *sql.DB RegistrationReward client.RegistrationRewardClient FirstRechargeReward client.FirstRechargeRewardClient + CumulativeRecharge client.CumulativeRechargeRewardClient SevenDayCheckIn client.SevenDayCheckInClient LuckyGift client.LuckyGiftClient + RoomTurnoverReward client.RoomTurnoverRewardClient + WeeklyStar client.WeeklyStarClient } func New(config Config) *Handler { @@ -48,21 +54,29 @@ func New(config Config) *Handler { walletDB: config.WalletDB, registrationReward: config.RegistrationReward, firstRechargeReward: config.FirstRechargeReward, + cumulativeRecharge: config.CumulativeRecharge, sevenDayCheckIn: config.SevenDayCheckIn, luckyGift: config.LuckyGift, + roomTurnoverReward: config.RoomTurnoverReward, + weeklyStar: config.WeeklyStar, } } func (h *Handler) TaskHandlers() httproutes.TaskHandlers { return httproutes.TaskHandlers{ - ListTaskTabs: h.listTaskTabs, - ClaimTaskReward: h.claimTaskReward, - GetRegistrationRewardEligibility: h.getRegistrationRewardEligibility, - GetFirstRechargeRewardStatus: h.getFirstRechargeRewardStatus, - GetSevenDayCheckInStatus: h.getSevenDayCheckInStatus, - SignSevenDayCheckIn: h.signSevenDayCheckIn, - ListUserLeaderboards: h.listUserLeaderboards, - CheckLuckyGift: h.checkLuckyGift, + ListTaskTabs: h.listTaskTabs, + ClaimTaskReward: h.claimTaskReward, + GetRegistrationRewardEligibility: h.getRegistrationRewardEligibility, + GetFirstRechargeRewardStatus: h.getFirstRechargeRewardStatus, + GetCumulativeRechargeRewardStatus: h.getCumulativeRechargeRewardStatus, + GetSevenDayCheckInStatus: h.getSevenDayCheckInStatus, + SignSevenDayCheckIn: h.signSevenDayCheckIn, + ListUserLeaderboards: h.listUserLeaderboards, + CheckLuckyGift: h.checkLuckyGift, + GetRoomTurnoverRewardStatus: h.getRoomTurnoverRewardStatus, + GetWeeklyStarCurrent: h.getWeeklyStarCurrent, + ListWeeklyStarLeaderboard: h.listWeeklyStarLeaderboard, + ListWeeklyStarHistory: h.listWeeklyStarHistory, } } diff --git a/services/gateway-service/internal/transport/http/activityapi/room_turnover_reward_handler.go b/services/gateway-service/internal/transport/http/activityapi/room_turnover_reward_handler.go new file mode 100644 index 00000000..dca15893 --- /dev/null +++ b/services/gateway-service/internal/transport/http/activityapi/room_turnover_reward_handler.go @@ -0,0 +1,237 @@ +package activityapi + +import ( + "net/http" + + activityv1 "hyapp.local/api/proto/activity/v1" + roomv1 "hyapp.local/api/proto/room/v1" + userv1 "hyapp.local/api/proto/user/v1" + "hyapp/services/gateway-service/internal/auth" + "hyapp/services/gateway-service/internal/transport/http/httpkit" +) + +type roomTurnoverRewardTierData struct { + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + TierName string `json:"tier_name"` + ThresholdCoinSpent int64 `json:"threshold_coin_spent"` + RewardCoinAmount int64 `json:"reward_coin_amount"` + Status string `json:"status"` + SortOrder int32 `json:"sort_order"` +} + +type roomTurnoverRewardUserData struct { + UserID int64 `json:"user_id"` + DisplayUserID string `json:"display_user_id"` + Username string `json:"username"` + Avatar string `json:"avatar"` +} + +type roomTurnoverRewardRoomData struct { + HasRoom bool `json:"has_room"` + RoomID string `json:"room_id"` + OwnerUserID int64 `json:"owner_user_id"` + Title string `json:"title"` + CoverURL string `json:"cover_url"` +} + +type roomTurnoverRewardPeriodData struct { + StartMS int64 `json:"start_ms"` + EndMS int64 `json:"end_ms"` +} + +type roomTurnoverRewardSettlementData struct { + SettlementID string `json:"settlement_id"` + RoomID string `json:"room_id"` + OwnerUserID int64 `json:"owner_user_id"` + PeriodStartMS int64 `json:"period_start_ms"` + PeriodEndMS int64 `json:"period_end_ms"` + CoinSpent int64 `json:"coin_spent"` + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + ThresholdCoinSpent int64 `json:"threshold_coin_spent"` + RewardCoinAmount int64 `json:"reward_coin_amount"` + Status string `json:"status"` + WalletCommandID string `json:"wallet_command_id"` + WalletTransactionID string `json:"wallet_transaction_id"` + FailureReason string `json:"failure_reason"` + SettledAtMS int64 `json:"settled_at_ms"` + CreatedAtMS int64 `json:"created_at_ms"` + UpdatedAtMS int64 `json:"updated_at_ms"` +} + +type roomTurnoverRewardStatusData struct { + Enabled bool `json:"enabled"` + User roomTurnoverRewardUserData `json:"user"` + Room roomTurnoverRewardRoomData `json:"room"` + Period roomTurnoverRewardPeriodData `json:"period"` + Tiers []roomTurnoverRewardTierData `json:"tiers"` + CurrentCoinSpent int64 `json:"current_coin_spent"` + ExpectedRewardCoinAmount int64 `json:"expected_reward_coin_amount"` + MatchedTier *roomTurnoverRewardTierData `json:"matched_tier,omitempty"` + LatestSettlement *roomTurnoverRewardSettlementData `json:"latest_settlement,omitempty"` + ServerTimeMS int64 `json:"server_time_ms"` +} + +func (h *Handler) getRoomTurnoverRewardStatus(writer http.ResponseWriter, request *http.Request) { + if h.roomTurnoverReward == nil || h.roomQueryClient == nil { + httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") + return + } + + userID := auth.UserIDFromContext(request.Context()) + roomData := roomTurnoverRewardRoomData{OwnerUserID: userID} + // App 接口按“当前登录用户自己的房间”展示活动进度;gateway 只编排 room-service 查询,不在本服务缓存房间归属。 + roomResp, err := h.roomQueryClient.GetMyRoom(request.Context(), &roomv1.GetMyRoomRequest{ + Meta: httpkit.RoomMeta(request, "", ""), + OwnerUserId: userID, + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + if roomResp.GetHasRoom() { + roomData = roomTurnoverRewardRoomFromProto(roomResp.GetRoom(), userID) + } + + // activity-service 只需要房间 ID 和 owner_user_id 计算当前周期流水与预计奖励;用户头像、昵称等展示字段由 gateway 统一补齐。 + resp, err := h.roomTurnoverReward.GetRoomTurnoverRewardStatus(request.Context(), &activityv1.GetRoomTurnoverRewardStatusRequest{ + Meta: httpkit.ActivityMeta(request), + UserId: userID, + RoomId: roomData.RoomID, + OwnerUserId: roomData.OwnerUserID, + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + + userData := roomTurnoverRewardUserData{UserID: userID} + if h.userProfileClient != nil { + // 用户资料只服务 H5 展示;读取失败时奖励状态仍应可用,避免头像依赖放大活动接口故障面。 + if userResp, userErr := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{ + Meta: httpkit.UserMeta(request, ""), + UserIds: []int64{userID}, + }); userErr == nil { + userData = roomTurnoverRewardUserFromProto(userResp.GetUsers()[userID], userID) + } + } + + httpkit.WriteOK(writer, request, roomTurnoverRewardStatusFromProto(resp.GetStatus(), roomData, userData)) +} + +func roomTurnoverRewardStatusFromProto(item *activityv1.RoomTurnoverRewardStatus, room roomTurnoverRewardRoomData, user roomTurnoverRewardUserData) roomTurnoverRewardStatusData { + if item == nil { + return roomTurnoverRewardStatusData{ + User: user, + Room: room, + Tiers: []roomTurnoverRewardTierData{}, + } + } + tiers := make([]roomTurnoverRewardTierData, 0, len(item.GetConfig().GetTiers())) + for _, tier := range item.GetConfig().GetTiers() { + tiers = append(tiers, roomTurnoverRewardTierFromProto(tier)) + } + var matched *roomTurnoverRewardTierData + if item.GetMatchedTier() != nil { + // matched_tier 使用指针是为了区分“未达档位”和“达到了 ID 为 0 的空值”,前端可直接按 null 判断未命中。 + converted := roomTurnoverRewardTierFromProto(item.GetMatchedTier()) + matched = &converted + } + var latest *roomTurnoverRewardSettlementData + if item.GetLatestSettlement() != nil { + // latest_settlement 允许跨周期展示上一周发奖状态;当前周期流水清零后,H5 仍能提示最近一次结算结果。 + converted := roomTurnoverRewardSettlementFromProto(item.GetLatestSettlement()) + latest = &converted + } + room.RoomID = item.GetRoomId() + room.OwnerUserID = item.GetOwnerUserId() + if room.OwnerUserID == 0 { + // 没有房间或 activity-service 未返回 owner 时,保留当前登录用户作为展示兜底,避免 H5 出现空 owner。 + room.OwnerUserID = user.UserID + } + return roomTurnoverRewardStatusData{ + Enabled: item.GetConfig().GetEnabled(), + User: user, + Room: room, + Period: roomTurnoverRewardPeriodData{ + StartMS: item.GetPeriodStartMs(), + EndMS: item.GetPeriodEndMs(), + }, + Tiers: tiers, + CurrentCoinSpent: item.GetCurrentCoinSpent(), + ExpectedRewardCoinAmount: item.GetExpectedRewardCoinAmount(), + MatchedTier: matched, + LatestSettlement: latest, + ServerTimeMS: item.GetServerTimeMs(), + } +} + +func roomTurnoverRewardRoomFromProto(item *roomv1.RoomListItem, fallbackOwnerUserID int64) roomTurnoverRewardRoomData { + if item == nil { + return roomTurnoverRewardRoomData{OwnerUserID: fallbackOwnerUserID} + } + ownerID := item.GetOwnerUserId() + if ownerID == 0 { + ownerID = fallbackOwnerUserID + } + return roomTurnoverRewardRoomData{ + HasRoom: true, + RoomID: item.GetRoomId(), + OwnerUserID: ownerID, + Title: item.GetTitle(), + CoverURL: item.GetCoverUrl(), + } +} + +func roomTurnoverRewardUserFromProto(item *userv1.User, fallbackUserID int64) roomTurnoverRewardUserData { + if item == nil { + return roomTurnoverRewardUserData{UserID: fallbackUserID} + } + return roomTurnoverRewardUserData{ + UserID: item.GetUserId(), + DisplayUserID: item.GetDisplayUserId(), + Username: item.GetUsername(), + Avatar: item.GetAvatar(), + } +} + +func roomTurnoverRewardTierFromProto(item *activityv1.RoomTurnoverRewardTier) roomTurnoverRewardTierData { + if item == nil { + return roomTurnoverRewardTierData{} + } + return roomTurnoverRewardTierData{ + TierID: item.GetTierId(), + TierCode: item.GetTierCode(), + TierName: item.GetTierName(), + ThresholdCoinSpent: item.GetThresholdCoinSpent(), + RewardCoinAmount: item.GetRewardCoinAmount(), + Status: item.GetStatus(), + SortOrder: item.GetSortOrder(), + } +} + +func roomTurnoverRewardSettlementFromProto(item *activityv1.RoomTurnoverRewardSettlement) roomTurnoverRewardSettlementData { + if item == nil { + return roomTurnoverRewardSettlementData{} + } + return roomTurnoverRewardSettlementData{ + SettlementID: item.GetSettlementId(), + RoomID: item.GetRoomId(), + OwnerUserID: item.GetOwnerUserId(), + PeriodStartMS: item.GetPeriodStartMs(), + PeriodEndMS: item.GetPeriodEndMs(), + CoinSpent: item.GetCoinSpent(), + TierID: item.GetTierId(), + TierCode: item.GetTierCode(), + ThresholdCoinSpent: item.GetThresholdCoinSpent(), + RewardCoinAmount: item.GetRewardCoinAmount(), + Status: item.GetStatus(), + WalletCommandID: item.GetWalletCommandId(), + WalletTransactionID: item.GetWalletTransactionId(), + FailureReason: item.GetFailureReason(), + SettledAtMS: item.GetSettledAtMs(), + CreatedAtMS: item.GetCreatedAtMs(), + UpdatedAtMS: item.GetUpdatedAtMs(), + } +} diff --git a/services/gateway-service/internal/transport/http/activityapi/weekly_star_handler.go b/services/gateway-service/internal/transport/http/activityapi/weekly_star_handler.go new file mode 100644 index 00000000..febb39a2 --- /dev/null +++ b/services/gateway-service/internal/transport/http/activityapi/weekly_star_handler.go @@ -0,0 +1,282 @@ +package activityapi + +import ( + "net/http" + "strconv" + "strings" + + activityv1 "hyapp.local/api/proto/activity/v1" + userv1 "hyapp.local/api/proto/user/v1" + "hyapp/services/gateway-service/internal/auth" + "hyapp/services/gateway-service/internal/transport/http/httpkit" +) + +type weeklyStarCycleData struct { + CycleID string `json:"cycle_id"` + ActivityCode string `json:"activity_code"` + RegionID int64 `json:"region_id"` + Title string `json:"title"` + Status string `json:"status"` + StartMS int64 `json:"start_ms"` + EndMS int64 `json:"end_ms"` + Gifts []weeklyStarGiftData `json:"gifts"` + Rewards []weeklyStarRewardData `json:"rewards"` + SettledAtMS int64 `json:"settled_at_ms,omitempty"` + CreatedAtMS int64 `json:"created_at_ms,omitempty"` + UpdatedAtMS int64 `json:"updated_at_ms,omitempty"` +} + +type weeklyStarGiftData struct { + GiftID string `json:"gift_id"` + SortOrder int32 `json:"sort_order"` +} + +type weeklyStarRewardData struct { + RankNo int32 `json:"rank_no"` + ResourceGroupID int64 `json:"resource_group_id"` +} + +type weeklyStarEntryData struct { + RankNo int32 `json:"rank_no"` + UserID string `json:"user_id"` + Score int64 `json:"score"` + FirstScoredAtMS int64 `json:"first_scored_at_ms"` + LastScoredAtMS int64 `json:"last_scored_at_ms"` + User *weeklyStarUserData `json:"user,omitempty"` +} + +type weeklyStarUserData struct { + UserID string `json:"user_id"` + DisplayUserID string `json:"display_user_id"` + Username string `json:"username"` + Avatar string `json:"avatar"` + RegionID int64 `json:"region_id,omitempty"` +} + +type weeklyStarCurrentData struct { + Cycle *weeklyStarCycleData `json:"cycle,omitempty"` + TopEntries []weeklyStarEntryData `json:"top_entries"` + MyEntry *weeklyStarEntryData `json:"my_entry,omitempty"` + ServerTimeMS int64 `json:"server_time_ms"` +} + +type weeklyStarLeaderboardData struct { + Cycle *weeklyStarCycleData `json:"cycle,omitempty"` + Entries []weeklyStarEntryData `json:"entries"` + NextPageToken string `json:"next_page_token,omitempty"` + Total int64 `json:"total"` +} + +type weeklyStarHistoryData struct { + Cycles []weeklyStarHistoryCycleData `json:"cycles"` + ServerTimeMS int64 `json:"server_time_ms"` +} + +type weeklyStarHistoryCycleData struct { + Cycle *weeklyStarCycleData `json:"cycle,omitempty"` + TopEntries []weeklyStarEntryData `json:"top_entries"` +} + +// getWeeklyStarCurrent returns the current region/default weekly-star cycle for the logged-in user. +func (h *Handler) getWeeklyStarCurrent(writer http.ResponseWriter, request *http.Request) { + if h.weeklyStar == nil || h.userProfileClient == nil { + httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") + return + } + userID := auth.UserIDFromContext(request.Context()) + regionID, ok := h.resolveWeeklyStarRegionID(writer, request, userID) + if !ok { + return + } + resp, err := h.weeklyStar.GetWeeklyStarCurrent(request.Context(), &activityv1.GetWeeklyStarCurrentRequest{ + Meta: httpkit.ActivityMeta(request), + UserId: userID, + RegionId: regionID, + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + users := h.resolveWeeklyStarUsers(request, weeklyStarUserIDs(resp.GetTopEntries(), resp.GetMyEntry())) + data := weeklyStarCurrentData{ + Cycle: weeklyStarCycleFromProto(resp.GetCycle()), + TopEntries: weeklyStarEntriesFromProto(resp.GetTopEntries(), users), + ServerTimeMS: resp.GetServerTimeMs(), + } + if resp.GetMyEntry() != nil { + entry := weeklyStarEntryFromProto(resp.GetMyEntry(), users) + data.MyEntry = &entry + } + httpkit.WriteOK(writer, request, data) +} + +// listWeeklyStarLeaderboard pages through the selected cycle or current region cycle. +func (h *Handler) listWeeklyStarLeaderboard(writer http.ResponseWriter, request *http.Request) { + if h.weeklyStar == nil || h.userProfileClient == nil { + httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") + return + } + userID := auth.UserIDFromContext(request.Context()) + regionID, ok := h.resolveWeeklyStarRegionID(writer, request, userID) + if !ok { + return + } + resp, err := h.weeklyStar.ListWeeklyStarLeaderboard(request.Context(), &activityv1.ListWeeklyStarLeaderboardRequest{ + Meta: httpkit.ActivityMeta(request), + CycleId: strings.TrimSpace(request.URL.Query().Get("cycle_id")), + RegionId: regionID, + PageSize: int32(queryInt(request, "page_size", 50)), + PageToken: strings.TrimSpace(request.URL.Query().Get("page_token")), + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + users := h.resolveWeeklyStarUsers(request, weeklyStarUserIDs(resp.GetEntries(), nil)) + httpkit.WriteOK(writer, request, weeklyStarLeaderboardData{ + Cycle: weeklyStarCycleFromProto(resp.GetCycle()), + Entries: weeklyStarEntriesFromProto(resp.GetEntries(), users), + NextPageToken: resp.GetNextPageToken(), + Total: resp.GetTotal(), + }) +} + +// listWeeklyStarHistory returns recent settled cycles for the current user's region with default fallback. +func (h *Handler) listWeeklyStarHistory(writer http.ResponseWriter, request *http.Request) { + if h.weeklyStar == nil || h.userProfileClient == nil { + httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") + return + } + userID := auth.UserIDFromContext(request.Context()) + regionID, ok := h.resolveWeeklyStarRegionID(writer, request, userID) + if !ok { + return + } + resp, err := h.weeklyStar.ListWeeklyStarHistory(request.Context(), &activityv1.ListWeeklyStarHistoryRequest{ + Meta: httpkit.ActivityMeta(request), + RegionId: regionID, + Limit: int32(queryInt(request, "limit", 10)), + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + ids := make([]int64, 0) + for _, cycle := range resp.GetCycles() { + ids = append(ids, weeklyStarUserIDs(cycle.GetTopEntries(), nil)...) + } + users := h.resolveWeeklyStarUsers(request, ids) + cycles := make([]weeklyStarHistoryCycleData, 0, len(resp.GetCycles())) + for _, item := range resp.GetCycles() { + cycles = append(cycles, weeklyStarHistoryCycleData{ + Cycle: weeklyStarCycleFromProto(item.GetCycle()), + TopEntries: weeklyStarEntriesFromProto(item.GetTopEntries(), users), + }) + } + httpkit.WriteOK(writer, request, weeklyStarHistoryData{Cycles: cycles, ServerTimeMS: resp.GetServerTimeMs()}) +} + +func (h *Handler) resolveWeeklyStarRegionID(writer http.ResponseWriter, request *http.Request, userID int64) (int64, bool) { + resp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{ + Meta: httpkit.UserMeta(request, ""), + UserId: userID, + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return 0, false + } + return resp.GetUser().GetRegionId(), true +} + +func (h *Handler) resolveWeeklyStarUsers(request *http.Request, ids []int64) map[int64]weeklyStarUserData { + users := make(map[int64]weeklyStarUserData) + ids = uniquePositiveIDs(ids) + if len(ids) == 0 || h.userProfileClient == nil { + return users + } + resp, err := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{ + Meta: httpkit.UserMeta(request, ""), + UserIds: ids, + }) + if err != nil { + return users + } + for _, item := range resp.GetUsers() { + users[item.GetUserId()] = weeklyStarUserData{ + UserID: strconv.FormatInt(item.GetUserId(), 10), + DisplayUserID: item.GetDisplayUserId(), + Username: item.GetUsername(), + Avatar: item.GetAvatar(), + RegionID: item.GetRegionId(), + } + } + return users +} + +func weeklyStarCycleFromProto(item *activityv1.WeeklyStarCycle) *weeklyStarCycleData { + if item == nil { + return nil + } + cycle := &weeklyStarCycleData{ + CycleID: item.GetCycleId(), + ActivityCode: item.GetActivityCode(), + RegionID: item.GetRegionId(), + Title: item.GetTitle(), + Status: item.GetStatus(), + StartMS: item.GetStartMs(), + EndMS: item.GetEndMs(), + Gifts: make([]weeklyStarGiftData, 0, len(item.GetGifts())), + Rewards: make([]weeklyStarRewardData, 0, len(item.GetRewards())), + SettledAtMS: item.GetSettledAtMs(), + CreatedAtMS: item.GetCreatedAtMs(), + UpdatedAtMS: item.GetUpdatedAtMs(), + } + for _, gift := range item.GetGifts() { + cycle.Gifts = append(cycle.Gifts, weeklyStarGiftData{GiftID: gift.GetGiftId(), SortOrder: gift.GetSortOrder()}) + } + for _, reward := range item.GetRewards() { + cycle.Rewards = append(cycle.Rewards, weeklyStarRewardData{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()}) + } + return cycle +} + +func weeklyStarEntriesFromProto(items []*activityv1.WeeklyStarLeaderboardEntry, users map[int64]weeklyStarUserData) []weeklyStarEntryData { + entries := make([]weeklyStarEntryData, 0, len(items)) + for _, item := range items { + entries = append(entries, weeklyStarEntryFromProto(item, users)) + } + return entries +} + +func weeklyStarEntryFromProto(item *activityv1.WeeklyStarLeaderboardEntry, users map[int64]weeklyStarUserData) weeklyStarEntryData { + entry := weeklyStarEntryData{ + RankNo: item.GetRankNo(), + UserID: strconv.FormatInt(item.GetUserId(), 10), + Score: item.GetScore(), + FirstScoredAtMS: item.GetFirstScoredAtMs(), + LastScoredAtMS: item.GetLastScoredAtMs(), + } + if user, ok := users[item.GetUserId()]; ok { + entry.User = &user + } + return entry +} + +func weeklyStarUserIDs(items []*activityv1.WeeklyStarLeaderboardEntry, extra *activityv1.WeeklyStarLeaderboardEntry) []int64 { + ids := make([]int64, 0, len(items)+1) + for _, item := range items { + ids = append(ids, item.GetUserId()) + } + if extra != nil { + ids = append(ids, extra.GetUserId()) + } + return ids +} + +func queryInt(request *http.Request, key string, fallback int) int { + value, err := strconv.Atoi(strings.TrimSpace(request.URL.Query().Get(key))) + if err != nil || value <= 0 { + return fallback + } + return value +} diff --git a/services/gateway-service/internal/transport/http/handler.go b/services/gateway-service/internal/transport/http/handler.go index b3dea9a3..91d55542 100644 --- a/services/gateway-service/internal/transport/http/handler.go +++ b/services/gateway-service/internal/transport/http/handler.go @@ -40,8 +40,11 @@ type Handler struct { achievementClient client.AchievementClient registrationReward client.RegistrationRewardClient firstRechargeReward client.FirstRechargeRewardClient + cumulativeRecharge client.CumulativeRechargeRewardClient sevenDayCheckIn client.SevenDayCheckInClient luckyGift client.LuckyGiftClient + roomTurnoverReward client.RoomTurnoverRewardClient + weeklyStar client.WeeklyStarClient broadcastClient client.BroadcastClient gameClient client.GameClient appConfigReader appapi.ConfigReader @@ -212,6 +215,11 @@ func (h *Handler) SetFirstRechargeRewardClient(firstRechargeReward client.FirstR h.firstRechargeReward = firstRechargeReward } +// SetCumulativeRechargeRewardClient 注入 activity-service 累充奖励查询 client。 +func (h *Handler) SetCumulativeRechargeRewardClient(cumulativeRecharge client.CumulativeRechargeRewardClient) { + h.cumulativeRecharge = cumulativeRecharge +} + // SetSevenDayCheckInClient 注入 activity-service 七日签到 client。 func (h *Handler) SetSevenDayCheckInClient(sevenDayCheckIn client.SevenDayCheckInClient) { h.sevenDayCheckIn = sevenDayCheckIn @@ -222,6 +230,16 @@ func (h *Handler) SetLuckyGiftClient(luckyGift client.LuckyGiftClient) { h.luckyGift = luckyGift } +// SetRoomTurnoverRewardClient 注入 activity-service 房间流水奖励查询 client。 +func (h *Handler) SetRoomTurnoverRewardClient(roomTurnoverReward client.RoomTurnoverRewardClient) { + h.roomTurnoverReward = roomTurnoverReward +} + +// SetWeeklyStarClient 注入 activity-service 周星活动 client。 +func (h *Handler) SetWeeklyStarClient(weeklyStar client.WeeklyStarClient) { + h.weeklyStar = weeklyStar +} + // SetBroadcastClient 注入 activity-service 播报群成员关系 client。 func (h *Handler) SetBroadcastClient(broadcastClient client.BroadcastClient) { h.broadcastClient = broadcastClient diff --git a/services/gateway-service/internal/transport/http/httproutes/router.go b/services/gateway-service/internal/transport/http/httproutes/router.go index 445fa9bb..c781eab7 100644 --- a/services/gateway-service/internal/transport/http/httproutes/router.go +++ b/services/gateway-service/internal/transport/http/httproutes/router.go @@ -159,14 +159,19 @@ type MessageHandlers struct { } type TaskHandlers struct { - ListTaskTabs http.HandlerFunc - ClaimTaskReward http.HandlerFunc - GetRegistrationRewardEligibility http.HandlerFunc - GetFirstRechargeRewardStatus http.HandlerFunc - GetSevenDayCheckInStatus http.HandlerFunc - SignSevenDayCheckIn http.HandlerFunc - ListUserLeaderboards http.HandlerFunc - CheckLuckyGift http.HandlerFunc + ListTaskTabs http.HandlerFunc + ClaimTaskReward http.HandlerFunc + GetRegistrationRewardEligibility http.HandlerFunc + GetFirstRechargeRewardStatus http.HandlerFunc + GetCumulativeRechargeRewardStatus http.HandlerFunc + GetSevenDayCheckInStatus http.HandlerFunc + SignSevenDayCheckIn http.HandlerFunc + ListUserLeaderboards http.HandlerFunc + CheckLuckyGift http.HandlerFunc + GetRoomTurnoverRewardStatus http.HandlerFunc + GetWeeklyStarCurrent http.HandlerFunc + ListWeeklyStarLeaderboard http.HandlerFunc + ListWeeklyStarHistory http.HandlerFunc } type LevelHandlers struct { @@ -409,9 +414,14 @@ func (r routes) registerTaskRoutes() { h := r.config.Task r.profile("/activities/registration-reward/eligibility", http.MethodGet, h.GetRegistrationRewardEligibility) r.profile("/activities/first-recharge-reward", http.MethodGet, h.GetFirstRechargeRewardStatus) + r.profile("/activities/cumulative-recharge-reward", http.MethodGet, h.GetCumulativeRechargeRewardStatus) r.profile("/activities/seven-day-checkin", http.MethodGet, h.GetSevenDayCheckInStatus) r.profile("/activities/seven-day-checkin/sign", http.MethodPost, h.SignSevenDayCheckIn) r.profile("/activities/user-leaderboards", http.MethodGet, h.ListUserLeaderboards) + r.profile("/activities/room-turnover-reward", http.MethodGet, h.GetRoomTurnoverRewardStatus) + r.profile("/activities/weekly-star/current", http.MethodGet, h.GetWeeklyStarCurrent) + r.profile("/activities/weekly-star/leaderboard", http.MethodGet, h.ListWeeklyStarLeaderboard) + r.profile("/activities/weekly-star/history", http.MethodGet, h.ListWeeklyStarHistory) // App 侧接口保持稳定;内部仍按 pool_id 走当前 v2 规则、抽奖和返奖实现。 r.profile("/activities/lucky-gifts/check", http.MethodPost, h.CheckLuckyGift) r.profile("/tasks/tabs", http.MethodGet, h.ListTaskTabs) diff --git a/services/gateway-service/internal/transport/http/router.go b/services/gateway-service/internal/transport/http/router.go index bc0a7ad7..83cbfe8b 100644 --- a/services/gateway-service/internal/transport/http/router.go +++ b/services/gateway-service/internal/transport/http/router.go @@ -74,8 +74,11 @@ func (h *Handler) Routes(jwtVerifier *auth.Verifier) http.Handler { WalletDB: h.leaderboardWalletDB, RegistrationReward: h.registrationReward, FirstRechargeReward: h.firstRechargeReward, + CumulativeRecharge: h.cumulativeRecharge, SevenDayCheckIn: h.sevenDayCheckIn, LuckyGift: h.luckyGift, + RoomTurnoverReward: h.roomTurnoverReward, + WeeklyStar: h.weeklyStar, }) gameAPI := gameapi.New(gameapi.Config{ GameClient: h.gameClient, diff --git a/services/statistics-service/configs/config.docker.yaml b/services/statistics-service/configs/config.docker.yaml index b46a2e93..11e5b9e2 100644 --- a/services/statistics-service/configs/config.docker.yaml +++ b/services/statistics-service/configs/config.docker.yaml @@ -4,6 +4,7 @@ environment: local health_http_addr: ":13110" query_http_addr: ":13010" mysql_dsn: "hyapp:hyapp@tcp(mysql:3306)/hyapp_statistics?parseTime=true&charset=utf8mb4&loc=UTC&multiStatements=true" +activity_mysql_dsn: "hyapp:hyapp@tcp(mysql:3306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC" rocketmq: enabled: true name_servers: ["rocketmq-namesrv:9876"] diff --git a/services/statistics-service/configs/config.tencent.example.yaml b/services/statistics-service/configs/config.tencent.example.yaml index d8529993..679262f7 100644 --- a/services/statistics-service/configs/config.tencent.example.yaml +++ b/services/statistics-service/configs/config.tencent.example.yaml @@ -4,6 +4,7 @@ environment: prod health_http_addr: ":13110" query_http_addr: ":13010" mysql_dsn: "${STATISTICS_MYSQL_DSN}" +activity_mysql_dsn: "${ACTIVITY_MYSQL_DSN}" rocketmq: enabled: true name_servers: [] diff --git a/services/statistics-service/configs/config.yaml b/services/statistics-service/configs/config.yaml index 4c47bf6b..c313274f 100644 --- a/services/statistics-service/configs/config.yaml +++ b/services/statistics-service/configs/config.yaml @@ -4,6 +4,7 @@ environment: local health_http_addr: ":13110" query_http_addr: ":13010" mysql_dsn: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_statistics?parseTime=true&charset=utf8mb4&loc=UTC&multiStatements=true" +activity_mysql_dsn: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC" rocketmq: enabled: false name_servers: [] diff --git a/services/statistics-service/internal/app/app.go b/services/statistics-service/internal/app/app.go index d828b5ee..646c4706 100644 --- a/services/statistics-service/internal/app/app.go +++ b/services/statistics-service/internal/app/app.go @@ -36,7 +36,7 @@ type App struct { func New(cfg config.Config) (*App, error) { startupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - repo, err := mysqlstorage.Open(startupCtx, cfg.MySQLDSN) + repo, err := mysqlstorage.Open(startupCtx, cfg.MySQLDSN, cfg.ActivityMySQLDSN) if err != nil { return nil, err } diff --git a/services/statistics-service/internal/config/config.go b/services/statistics-service/internal/config/config.go index 414b5d87..929d11f4 100644 --- a/services/statistics-service/internal/config/config.go +++ b/services/statistics-service/internal/config/config.go @@ -10,14 +10,15 @@ import ( // Config describes statistics-service dependencies and MQ subscriptions. type Config struct { - ServiceName string `yaml:"service_name"` - NodeID string `yaml:"node_id"` - Environment string `yaml:"environment"` - HealthHTTPAddr string `yaml:"health_http_addr"` - QueryHTTPAddr string `yaml:"query_http_addr"` - MySQLDSN string `yaml:"mysql_dsn"` - RocketMQ RocketMQConfig `yaml:"rocketmq"` - Log logx.Config `yaml:"log"` + ServiceName string `yaml:"service_name"` + NodeID string `yaml:"node_id"` + Environment string `yaml:"environment"` + HealthHTTPAddr string `yaml:"health_http_addr"` + QueryHTTPAddr string `yaml:"query_http_addr"` + MySQLDSN string `yaml:"mysql_dsn"` + ActivityMySQLDSN string `yaml:"activity_mysql_dsn"` + RocketMQ RocketMQConfig `yaml:"rocketmq"` + Log logx.Config `yaml:"log"` } type RocketMQConfig struct { @@ -44,13 +45,14 @@ type ConsumerMQConfig struct { func Default() Config { return Config{ - ServiceName: "statistics-service", - NodeID: "statistics-local", - Environment: "local", - HealthHTTPAddr: ":13110", - QueryHTTPAddr: ":13010", - MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_statistics?parseTime=true&charset=utf8mb4&loc=UTC&multiStatements=true", - RocketMQ: defaultRocketMQConfig(), + ServiceName: "statistics-service", + NodeID: "statistics-local", + Environment: "local", + HealthHTTPAddr: ":13110", + QueryHTTPAddr: ":13010", + MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_statistics?parseTime=true&charset=utf8mb4&loc=UTC&multiStatements=true", + ActivityMySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC", + RocketMQ: defaultRocketMQConfig(), Log: logx.Config{ Level: "info", Format: "json", @@ -86,6 +88,8 @@ func Load(path string) (Config, error) { if cfg.QueryHTTPAddr == "" { cfg.QueryHTTPAddr = ":13010" } + cfg.MySQLDSN = strings.TrimSpace(cfg.MySQLDSN) + cfg.ActivityMySQLDSN = strings.TrimSpace(cfg.ActivityMySQLDSN) cfg.RocketMQ = normalizeRocketMQConfig(cfg.RocketMQ) if cfg.RocketMQ.Enabled { if len(cfg.RocketMQ.NameServers) == 0 && cfg.RocketMQ.NameServerDomain == "" { diff --git a/services/statistics-service/internal/storage/mysql/query.go b/services/statistics-service/internal/storage/mysql/query.go index eda6d6f2..999e080d 100644 --- a/services/statistics-service/internal/storage/mysql/query.go +++ b/services/statistics-service/internal/storage/mysql/query.go @@ -114,9 +114,6 @@ func (r *Repository) QueryOverview(ctx context.Context, query OverviewQuery) (Ov if channelRechargeUSDMinor > overview.RechargeUSDMinor { overview.RechargeUSDMinor = channelRechargeUSDMinor } - overview.LuckyGiftProfit = overview.LuckyGiftTurnover - overview.LuckyGiftPayout - overview.LuckyGiftPayoutRate = ratio(overview.LuckyGiftPayout, overview.LuckyGiftTurnover) - overview.LuckyGiftProfitRate = ratio(overview.LuckyGiftProfit, overview.LuckyGiftTurnover) overview.GameProfit = overview.GameTurnover - overview.GamePayout - overview.GameRefund overview.GameProfitRate = ratio(overview.GameProfit, overview.GameTurnover) overview.ARPUUSDMinor = div(overview.RechargeUSDMinor, overview.ActiveUsers) @@ -138,6 +135,16 @@ func (r *Repository) QueryOverview(ctx context.Context, query OverviewQuery) (Ov return Overview{}, err } overview.LuckyGiftPools = pools + // 幸运礼物流水和返奖以 activity-service 的抽奖事实增量日汇总为准;这里仅读小表,避免 Databi 页面扫描 lucky_draw_records。 + if activityPools, ok, err := r.queryLuckyGiftPoolsFromActivityStats(ctx, app, startDay, endDay, query.CountryID); err != nil { + return Overview{}, err + } else if ok { + overview.LuckyGiftPools = activityPools + overview.LuckyGiftTurnover, overview.LuckyGiftPayout = sumLuckyGiftPools(activityPools) + } + overview.LuckyGiftProfit = overview.LuckyGiftTurnover - overview.LuckyGiftPayout + overview.LuckyGiftPayoutRate = ratio(overview.LuckyGiftPayout, overview.LuckyGiftTurnover) + overview.LuckyGiftProfitRate = ratio(overview.LuckyGiftProfit, overview.LuckyGiftTurnover) return overview, nil } @@ -228,6 +235,55 @@ func (r *Repository) queryLuckyGiftPools(ctx context.Context, app, startDay, end return out, rows.Err() } +func (r *Repository) queryLuckyGiftPoolsFromActivityStats(ctx context.Context, app, startDay, endDay string, countryID int64) ([]LuckyGiftPoolStat, bool, error) { + if r == nil || r.activityDB == nil { + return nil, false, nil + } + args := []any{app, startDay, endDay} + filter := "" + if countryID > 0 { + filter = " AND visible_region_id = ?" + args = append(args, countryID) + } + rows, err := r.activityDB.QueryContext(ctx, ` + SELECT pool_id, COALESCE(SUM(turnover_coin),0), COALESCE(SUM(payout_coin),0) + FROM lucky_draw_pool_day_stats + WHERE app_code = ? AND stat_day BETWEEN ? AND ? AND gift_id = ''`+filter+` + GROUP BY pool_id + ORDER BY COALESCE(SUM(turnover_coin),0) DESC, pool_id ASC`, args...) + if err != nil { + if isMissingTableError(err) { + return nil, false, nil + } + return nil, false, err + } + defer rows.Close() + out := []LuckyGiftPoolStat{} + for rows.Next() { + var item LuckyGiftPoolStat + if err := rows.Scan(&item.PoolID, &item.TurnoverCoin, &item.PayoutCoin); err != nil { + return nil, false, err + } + item.ProfitCoin = item.TurnoverCoin - item.PayoutCoin + item.PayoutRate = ratio(item.PayoutCoin, item.TurnoverCoin) + item.ProfitRate = ratio(item.ProfitCoin, item.TurnoverCoin) + out = append(out, item) + } + if err := rows.Err(); err != nil { + return nil, false, err + } + return out, len(out) > 0, nil +} + +func sumLuckyGiftPools(pools []LuckyGiftPoolStat) (int64, int64) { + var turnover, payout int64 + for _, pool := range pools { + turnover += pool.TurnoverCoin + payout += pool.PayoutCoin + } + return turnover, payout +} + func normalizeRange(startMS, endMS int64) (int64, int64) { if startMS <= 0 { now := time.Now().UTC() diff --git a/services/statistics-service/internal/storage/mysql/query_test.go b/services/statistics-service/internal/storage/mysql/query_test.go new file mode 100644 index 00000000..b0dab2a9 --- /dev/null +++ b/services/statistics-service/internal/storage/mysql/query_test.go @@ -0,0 +1,66 @@ +package mysql + +import ( + "context" + "runtime" + "testing" + "time" + + "hyapp/internal/testutil/mysqlschema" +) + +func TestQueryOverviewUsesActivityLuckyGiftDayStats(t *testing.T) { + ctx := context.Background() + _, file, _, _ := runtime.Caller(0) + statsSchema := mysqlschema.New(t, mysqlschema.Config{ + InitDBPath: mysqlschema.InitDBPath(t, file, "..", "..", "..", "deploy", "mysql", "initdb", "001_statistics_service.sql"), + DatabasePrefix: "hy_stats_query_test", + }) + activitySchema := mysqlschema.New(t, mysqlschema.Config{ + EnvVar: "ACTIVITY_SERVICE_MYSQL_TEST_DSN", + InitDBPath: mysqlschema.InitDBPath(t, file, "..", "..", "..", "..", "activity-service", "deploy", "mysql", "initdb", "001_activity_service.sql"), + DatabasePrefix: "hy_activity_query_test", + }) + repository, err := Open(ctx, statsSchema.DSN, activitySchema.DSN) + if err != nil { + t.Fatalf("open repository: %v", err) + } + t.Cleanup(func() { _ = repository.Close() }) + + if _, err := repository.db.ExecContext(ctx, ` + INSERT INTO stat_app_day_country ( + app_code, stat_day, country_id, lucky_gift_turnover, lucky_gift_payout, updated_at_ms + ) VALUES ('lalu', '2026-06-06', 210, 100, 900, 1)`); err != nil { + t.Fatalf("seed stale statistics overview: %v", err) + } + if _, err := repository.db.ExecContext(ctx, ` + INSERT INTO stat_lucky_gift_pool_day_country ( + app_code, stat_day, country_id, pool_id, turnover_coin, payout_coin, updated_at_ms + ) VALUES ('lalu', '2026-06-06', 210, 'lucky', 100, 900, 1)`); err != nil { + t.Fatalf("seed stale statistics pool: %v", err) + } + if _, err := repository.activityDB.ExecContext(ctx, ` + INSERT INTO lucky_draw_pool_day_stats ( + app_code, stat_day, visible_region_id, pool_id, gift_id, draw_count, turnover_coin, payout_coin, + base_reward_coin, room_atmosphere_reward_coin, activity_subsidy_coin, created_at_ms, updated_at_ms + ) VALUES + ('lalu', '2026-06-06', 210, 'super_lucky', '', 10, 2744832, 2401885, 2401885, 0, 0, 1, 1), + ('lalu', '2026-06-06', 210, 'lucky', '', 5, 148322, 126415, 126415, 0, 0, 1, 1)`); err != nil { + t.Fatalf("seed activity databi stats: %v", err) + } + + start := time.Date(2026, 6, 6, 0, 0, 0, 0, time.UTC).UnixMilli() + overview, err := repository.QueryOverview(ctx, OverviewQuery{AppCode: "lalu", StartMS: start, EndMS: start + int64(24*time.Hour/time.Millisecond), CountryID: 210}) + if err != nil { + t.Fatalf("query overview: %v", err) + } + if overview.LuckyGiftTurnover != 2_893_154 || overview.LuckyGiftPayout != 2_528_300 || overview.LuckyGiftProfit != 364_854 { + t.Fatalf("activity lucky gift overview not applied: %+v", overview) + } + if len(overview.LuckyGiftPools) != 2 { + t.Fatalf("activity lucky gift pools not applied: %+v", overview.LuckyGiftPools) + } + if overview.LuckyGiftPools[0].PoolID != "super_lucky" || overview.LuckyGiftPools[0].TurnoverCoin != 2_744_832 || overview.LuckyGiftPools[0].PayoutCoin != 2_401_885 { + t.Fatalf("super_lucky pool mismatch: %+v", overview.LuckyGiftPools[0]) + } +} diff --git a/services/statistics-service/internal/storage/mysql/repository.go b/services/statistics-service/internal/storage/mysql/repository.go index 205c9a44..e34f76d5 100644 --- a/services/statistics-service/internal/storage/mysql/repository.go +++ b/services/statistics-service/internal/storage/mysql/repository.go @@ -20,13 +20,15 @@ const ( SourceGame = "game" ) -// Repository owns the statistics read model. Online queries must use these -// aggregate tables, never owner-service fact tables. +// Repository owns the statistics read model. Online queries must use aggregate +// tables; the optional activity connection only reads lucky-gift day snapshots, +// never the high-cardinality draw fact table. type Repository struct { - db *sql.DB + db *sql.DB + activityDB *sql.DB } -func Open(ctx context.Context, dsn string) (*Repository, error) { +func Open(ctx context.Context, dsn string, activityDSNs ...string) (*Repository, error) { if strings.TrimSpace(dsn) == "" { return nil, xerr.New(xerr.InvalidArgument, "mysql_dsn is required") } @@ -38,8 +40,24 @@ func Open(ctx context.Context, dsn string) (*Repository, error) { _ = db.Close() return nil, err } - repo := &Repository{db: db} + var activityDB *sql.DB + if len(activityDSNs) > 0 && strings.TrimSpace(activityDSNs[0]) != "" { + activityDB, err = sql.Open("mysql", strings.TrimSpace(activityDSNs[0])) + if err != nil { + _ = db.Close() + return nil, err + } + if err := activityDB.PingContext(ctx); err != nil { + _ = activityDB.Close() + _ = db.Close() + return nil, err + } + } + repo := &Repository{db: db, activityDB: activityDB} if err := repo.Migrate(ctx); err != nil { + if activityDB != nil { + _ = activityDB.Close() + } _ = db.Close() return nil, err } @@ -47,17 +65,32 @@ func Open(ctx context.Context, dsn string) (*Repository, error) { } func (r *Repository) Close() error { - if r == nil || r.db == nil { + if r == nil { return nil } - return r.db.Close() + var err error + if r.activityDB != nil { + err = r.activityDB.Close() + } + if r.db != nil { + if dbErr := r.db.Close(); err == nil { + err = dbErr + } + } + return err } func (r *Repository) Ping(ctx context.Context) error { if r == nil || r.db == nil { return xerr.New(xerr.Unavailable, "mysql repository is not configured") } - return r.db.PingContext(ctx) + if err := r.db.PingContext(ctx); err != nil { + return err + } + if r.activityDB != nil { + return r.activityDB.PingContext(ctx) + } + return nil } func (r *Repository) Migrate(ctx context.Context) error { @@ -473,6 +506,11 @@ func isDuplicateColumnError(err error) bool { return errors.As(err, &mysqlErr) && mysqlErr.Number == 1060 } +func isMissingTableError(err error) bool { + var mysqlErr *mysqlerr.MySQLError + return errors.As(err, &mysqlErr) && mysqlErr.Number == 1146 +} + func applyActive(ctx context.Context, tx *sql.Tx, app string, day string, countryID int64, userID int64, occurredAtMS int64, nowMS int64) error { if err := ensureAppDay(ctx, tx, app, day, countryID, nowMS); err != nil { return err diff --git a/services/wallet-service/internal/domain/ledger/ledger.go b/services/wallet-service/internal/domain/ledger/ledger.go index c6ebf25b..36e675a7 100644 --- a/services/wallet-service/internal/domain/ledger/ledger.go +++ b/services/wallet-service/internal/domain/ledger/ledger.go @@ -750,6 +750,30 @@ type LuckyGiftRewardReceipt struct { GrantedAtMS int64 } +// RoomTurnoverRewardCommand 是 activity-service 每周房间流水奖励的 COIN 入账命令。 +type RoomTurnoverRewardCommand struct { + AppCode string + CommandID string + TargetUserID int64 + Amount int64 + SettlementID string + RoomID string + PeriodStartMS int64 + PeriodEndMS int64 + CoinSpent int64 + TierID int64 + TierCode string + Reason string +} + +// RoomTurnoverRewardReceipt 是房间流水奖励入账后的稳定回执。 +type RoomTurnoverRewardReceipt struct { + TransactionID string + Balance AssetBalance + Amount int64 + GrantedAtMS int64 +} + // GameCoinChangeCommand 是 game-service 对钱包发起的游戏专用金币改账命令。 type GameCoinChangeCommand struct { AppCode string diff --git a/services/wallet-service/internal/service/wallet/service.go b/services/wallet-service/internal/service/wallet/service.go index 32d96f64..58a1841b 100644 --- a/services/wallet-service/internal/service/wallet/service.go +++ b/services/wallet-service/internal/service/wallet/service.go @@ -31,6 +31,7 @@ type Repository interface { TransferSalaryToCoinSeller(ctx context.Context, command ledger.SalaryTransferToCoinSellerCommand) (ledger.SalaryTransferToCoinSellerReceipt, error) CreditTaskReward(ctx context.Context, command ledger.TaskRewardCommand) (ledger.TaskRewardReceipt, error) CreditLuckyGiftReward(ctx context.Context, command ledger.LuckyGiftRewardCommand) (ledger.LuckyGiftRewardReceipt, error) + CreditRoomTurnoverReward(ctx context.Context, command ledger.RoomTurnoverRewardCommand) (ledger.RoomTurnoverRewardReceipt, error) ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error) GetRedPacketConfig(ctx context.Context, appCode string) (ledger.RedPacketConfig, error) UpdateRedPacketConfig(ctx context.Context, config ledger.RedPacketConfig) (ledger.RedPacketConfig, error) @@ -492,6 +493,33 @@ func (s *Service) CreditLuckyGiftReward(ctx context.Context, command ledger.Luck return s.repository.CreditLuckyGiftReward(ctx, command) } +// CreditRoomTurnoverReward 发放每周房间流水奖励金币;结算归 activity-service,钱包只负责幂等入账。 +func (s *Service) CreditRoomTurnoverReward(ctx context.Context, command ledger.RoomTurnoverRewardCommand) (ledger.RoomTurnoverRewardReceipt, error) { + // 钱包只接受 activity-service 已经生成好的结算命令;缺 settlement、room 或正向金额时直接拒绝,避免钱包自己推断活动规则。 + if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.SettlementID == "" || command.RoomID == "" { + return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.InvalidArgument, "room turnover reward command is incomplete") + } + // 周期、流水和档位是账务元数据的一部分,必须随交易落库,后续对账才能从钱包交易反查是哪一周、哪个房间、哪个档位。 + if command.PeriodStartMS <= 0 || command.PeriodEndMS <= command.PeriodStartMS || command.CoinSpent <= 0 || command.TierID <= 0 { + return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.InvalidArgument, "room turnover reward period or tier is invalid") + } + command.Reason = strings.TrimSpace(command.Reason) + if command.Reason == "" { + return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required") + } + if s.repository == nil { + return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured") + } + command.AppCode = appcode.Normalize(command.AppCode) + command.SettlementID = strings.TrimSpace(command.SettlementID) + command.RoomID = strings.TrimSpace(command.RoomID) + command.TierCode = strings.TrimSpace(command.TierCode) + // AppCode 归一化后写入 context,让 repository 的交易、账户和 outbox 都落到同一个 App 分区。 + ctx = appcode.WithContext(ctx, command.AppCode) + + return s.repository.CreditRoomTurnoverReward(ctx, command) +} + // ApplyGameCoinChange 是游戏平台唯一的 COIN 改账入口,方向由 op_type 控制。 func (s *Service) ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error) { if command.CommandID == "" || command.UserID <= 0 || command.PlatformCode == "" || command.GameID == "" || command.ProviderOrderID == "" { diff --git a/services/wallet-service/internal/storage/mysql/repository.go b/services/wallet-service/internal/storage/mysql/repository.go index 43a27c85..0fa8e925 100644 --- a/services/wallet-service/internal/storage/mysql/repository.go +++ b/services/wallet-service/internal/storage/mysql/repository.go @@ -26,6 +26,7 @@ const ( bizTypeManualCredit = "manual_credit" bizTypeTaskReward = "task_reward" bizTypeLuckyGiftReward = "lucky_gift_reward" + bizTypeRoomTurnoverReward = "room_turnover_reward" bizTypeCoinSellerTransfer = "coin_seller_transfer" bizTypeCoinSellerStockPurchase = "coin_seller_stock_purchase" bizTypeCoinSellerCoinCompensation = "coin_seller_coin_compensation" @@ -925,6 +926,87 @@ func (r *Repository) CreditLuckyGiftReward(ctx context.Context, command ledger.L return receiptFromLuckyGiftRewardMetadata(transactionID, metadata), nil } +// CreditRoomTurnoverReward 在同一事务里完成每周房间流水奖励 COIN 入账、交易分录和钱包 outbox。 +func (r *Repository) CreditRoomTurnoverReward(ctx context.Context, command ledger.RoomTurnoverRewardCommand) (ledger.RoomTurnoverRewardReceipt, error) { + if r == nil || r.db == nil { + return ledger.RoomTurnoverRewardReceipt{}, xerr.New(xerr.Unavailable, "mysql repository is not configured") + } + ctx = contextWithCommandApp(ctx, command.AppCode) + command.AppCode = appcode.FromContext(ctx) + + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + defer func() { _ = tx.Rollback() }() + + requestHash := roomTurnoverRewardRequestHash(command) + if txRow, exists, err := r.lookupTransaction(ctx, tx, command.CommandID, requestHash, bizTypeRoomTurnoverReward); err != nil || exists { + if err != nil || !exists { + return ledger.RoomTurnoverRewardReceipt{}, err + } + // command_id 相同且请求摘要一致说明是 activity-service 的重复发奖调用;直接返回原交易回执,不再改余额。 + return r.receiptForRoomTurnoverRewardTransaction(ctx, tx, txRow.TransactionID) + } + + nowMs := time.Now().UnixMilli() + // 锁定房主 COIN 账户后再计算余额,保证同一用户同时收到多笔奖励时 available_after 仍是线性账本结果。 + account, err := r.lockAccount(ctx, tx, command.TargetUserID, ledger.AssetCoin, true, nowMs) + if err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + transactionID := transactionID(command.AppCode, command.CommandID) + balanceAfter := account.AvailableAmount + command.Amount + // metadata 是交易、分录和 outbox 的共享事实快照;后续补发事件或幂等查询都从这里还原原始结算上下文。 + metadata := roomTurnoverRewardMetadata{ + AppCode: command.AppCode, + TargetUserID: command.TargetUserID, + AssetType: ledger.AssetCoin, + Amount: command.Amount, + SettlementID: command.SettlementID, + RoomID: command.RoomID, + PeriodStartMS: command.PeriodStartMS, + PeriodEndMS: command.PeriodEndMS, + CoinSpent: command.CoinSpent, + TierID: command.TierID, + TierCode: command.TierCode, + Reason: command.Reason, + BalanceAfter: balanceAfter, + GrantedAtMS: nowMs, + } + if err := r.insertTransaction(ctx, tx, transactionID, command.CommandID, bizTypeRoomTurnoverReward, requestHash, command.SettlementID, metadata, nowMs); err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + // 交易、账户余额、账本分录和 outbox 必须在同一事务提交;任意一步失败都会回滚,避免出现发奖记录和余额不一致。 + if err := r.applyAccountDelta(ctx, tx, account, command.Amount, 0, nowMs); err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + if err := r.insertEntry(ctx, tx, walletEntry{ + TransactionID: transactionID, + UserID: command.TargetUserID, + AssetType: ledger.AssetCoin, + AvailableDelta: command.Amount, + FrozenDelta: 0, + AvailableAfter: balanceAfter, + FrozenAfter: account.FrozenAmount, + RoomID: command.RoomID, + CreatedAtMS: nowMs, + }); err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + if err := r.insertWalletOutbox(ctx, tx, []walletOutboxEvent{ + balanceChangedEvent(transactionID, command.CommandID, command.TargetUserID, ledger.AssetCoin, command.Amount, 0, balanceAfter, account.FrozenAmount, account.Version+1, metadata, nowMs), + roomTurnoverRewardCreditedEvent(transactionID, command.CommandID, metadata, nowMs), + }); err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + if err := tx.Commit(); err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + + return receiptFromRoomTurnoverRewardMetadata(transactionID, metadata), nil +} + // ApplyGameCoinChange 在同一事务内完成游戏 COIN 改账、分录和 outbox。 func (r *Repository) ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error) { if r == nil || r.db == nil { @@ -2557,6 +2639,23 @@ type luckyGiftRewardMetadata struct { GrantedAtMS int64 `json:"granted_at_ms"` } +type roomTurnoverRewardMetadata struct { + AppCode string `json:"app_code"` + TargetUserID int64 `json:"target_user_id"` + AssetType string `json:"asset_type"` + Amount int64 `json:"amount"` + SettlementID string `json:"settlement_id"` + RoomID string `json:"room_id"` + PeriodStartMS int64 `json:"period_start_ms"` + PeriodEndMS int64 `json:"period_end_ms"` + CoinSpent int64 `json:"coin_spent"` + TierID int64 `json:"tier_id"` + TierCode string `json:"tier_code"` + Reason string `json:"reason"` + BalanceAfter int64 `json:"balance_after"` + GrantedAtMS int64 `json:"granted_at_ms"` +} + type gameCoinMetadata struct { AppCode string `json:"app_code"` UserID int64 `json:"user_id"` @@ -2729,6 +2828,38 @@ func receiptFromLuckyGiftRewardMetadata(transactionID string, metadata luckyGift } } +func (r *Repository) receiptForRoomTurnoverRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.RoomTurnoverRewardReceipt, error) { + var metadataJSON string + if err := tx.QueryRowContext(ctx, + `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') + FROM wallet_transactions + WHERE app_code = ? AND transaction_id = ?`, + appcode.FromContext(ctx), + transactionID, + ).Scan(&metadataJSON); err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + var metadata roomTurnoverRewardMetadata + if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { + return ledger.RoomTurnoverRewardReceipt{}, err + } + return receiptFromRoomTurnoverRewardMetadata(transactionID, metadata), nil +} + +func receiptFromRoomTurnoverRewardMetadata(transactionID string, metadata roomTurnoverRewardMetadata) ledger.RoomTurnoverRewardReceipt { + return ledger.RoomTurnoverRewardReceipt{ + TransactionID: transactionID, + Balance: ledger.AssetBalance{ + AppCode: metadata.AppCode, + UserID: metadata.TargetUserID, + AssetType: metadata.AssetType, + AvailableAmount: metadata.BalanceAfter, + }, + Amount: metadata.Amount, + GrantedAtMS: metadata.GrantedAtMS, + } +} + func (r *Repository) receiptForGameTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.GameCoinChangeReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, @@ -3006,6 +3137,35 @@ func luckyGiftRewardCreditedEvent(transactionID string, commandID string, metada } } +func roomTurnoverRewardCreditedEvent(transactionID string, commandID string, metadata roomTurnoverRewardMetadata, nowMs int64) walletOutboxEvent { + return walletOutboxEvent{ + EventID: eventID(transactionID, "WalletRoomTurnoverRewardCredited", metadata.TargetUserID, ledger.AssetCoin), + EventType: "WalletRoomTurnoverRewardCredited", + TransactionID: transactionID, + CommandID: commandID, + UserID: metadata.TargetUserID, + AssetType: ledger.AssetCoin, + AvailableDelta: metadata.Amount, + FrozenDelta: 0, + Payload: map[string]any{ + "transaction_id": transactionID, + "command_id": commandID, + "user_id": metadata.TargetUserID, + "settlement_id": metadata.SettlementID, + "room_id": metadata.RoomID, + "period_start_ms": metadata.PeriodStartMS, + "period_end_ms": metadata.PeriodEndMS, + "coin_spent": metadata.CoinSpent, + "tier_id": metadata.TierID, + "tier_code": metadata.TierCode, + "amount": metadata.Amount, + "reason": metadata.Reason, + "created_at_ms": nowMs, + }, + CreatedAtMS: nowMs, + } +} + func coinSellerStockCreditedEvent(transactionID string, commandID string, metadata coinSellerStockMetadata, nowMs int64) walletOutboxEvent { eventType := "WalletCoinSellerCoinCompensated" if metadata.StockType == ledger.StockTypeUSDTPurchase { @@ -3258,6 +3418,22 @@ func luckyGiftRewardRequestHash(command ledger.LuckyGiftRewardCommand) string { )) } +func roomTurnoverRewardRequestHash(command ledger.RoomTurnoverRewardCommand) string { + return stableHash(fmt.Sprintf("room_turnover_reward|%s|%d|%d|%s|%s|%d|%d|%d|%d|%s|%s", + appcode.Normalize(command.AppCode), + command.TargetUserID, + command.Amount, + strings.TrimSpace(command.SettlementID), + strings.TrimSpace(command.RoomID), + command.PeriodStartMS, + command.PeriodEndMS, + command.CoinSpent, + command.TierID, + strings.TrimSpace(command.TierCode), + strings.TrimSpace(command.Reason), + )) +} + func gameBizTypeAndDelta(opType string, coinAmount int64) (string, int64, error) { switch ledger.NormalizeGameOpType(opType) { case ledger.GameOpDebit: diff --git a/services/wallet-service/internal/transport/grpc/server.go b/services/wallet-service/internal/transport/grpc/server.go index 90286512..0e7d80d6 100644 --- a/services/wallet-service/internal/transport/grpc/server.go +++ b/services/wallet-service/internal/transport/grpc/server.go @@ -773,6 +773,42 @@ func (s *Server) CreditLuckyGiftReward(ctx context.Context, req *walletv1.Credit }, nil } +// CreditRoomTurnoverReward 处理 activity-service 每周房间流水奖励金币入账。 +func (s *Server) CreditRoomTurnoverReward(ctx context.Context, req *walletv1.CreditRoomTurnoverRewardRequest) (*walletv1.CreditRoomTurnoverRewardResponse, error) { + // gRPC 层不解释活动规则,只把 activity-service 的结算命令转换为钱包领域命令。 + ctx = appcode.WithContext(ctx, req.GetAppCode()) + receipt, err := s.svc.CreditRoomTurnoverReward(ctx, ledger.RoomTurnoverRewardCommand{ + AppCode: req.GetAppCode(), + CommandID: req.GetCommandId(), + TargetUserID: req.GetTargetUserId(), + Amount: req.GetAmount(), + SettlementID: req.GetSettlementId(), + RoomID: req.GetRoomId(), + PeriodStartMS: req.GetPeriodStartMs(), + PeriodEndMS: req.GetPeriodEndMs(), + CoinSpent: req.GetCoinSpent(), + TierID: req.GetTierId(), + TierCode: req.GetTierCode(), + Reason: req.GetReason(), + }) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + + return &walletv1.CreditRoomTurnoverRewardResponse{ + TransactionId: receipt.TransactionID, + Amount: receipt.Amount, + GrantedAtMs: receipt.GrantedAtMS, + Balance: &walletv1.AssetBalance{ + AppCode: receipt.Balance.AppCode, + AssetType: receipt.Balance.AssetType, + AvailableAmount: receipt.Balance.AvailableAmount, + FrozenAmount: receipt.Balance.FrozenAmount, + Version: receipt.Balance.Version, + }, + }, nil +} + // ApplyGameCoinChange 处理 game-service 发起的游戏专用金币改账。 func (s *Server) ApplyGameCoinChange(ctx context.Context, req *walletv1.ApplyGameCoinChangeRequest) (*walletv1.ApplyGameCoinChangeResponse, error) { ctx = appcode.WithContext(ctx, req.GetAppCode()) diff --git a/tools/cumulative-recharge-local-flow/main.go b/tools/cumulative-recharge-local-flow/main.go new file mode 100644 index 00000000..dd38b5f0 --- /dev/null +++ b/tools/cumulative-recharge-local-flow/main.go @@ -0,0 +1,505 @@ +package main + +import ( + "context" + "database/sql" + "encoding/json" + "flag" + "fmt" + "log" + "sort" + "time" + + _ "github.com/go-sql-driver/mysql" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + activityv1 "hyapp.local/api/proto/activity/v1" + walletv1 "hyapp.local/api/proto/wallet/v1" +) + +const ( + appCode = "lalu" + operatorAdminID = int64(900100100) + rechargeSource = "cumulative_recharge_reward" + groupItemWalletAsset = "wallet_asset" + assetCoin = "COIN" + statusActive = "active" + statusDisabled = "disabled" + tierStatusInactive = "inactive" +) + +type walletOutboxFact struct { + EventID string + TransactionID string + CommandID string + PayloadJSON string + CreatedAtMS int64 + AvailableDelta int64 +} + +type cycle struct { + Key string + StartMS int64 + EndMS int64 +} + +func main() { + var ( + walletAddr = flag.String("wallet_addr", "127.0.0.1:13004", "wallet-service gRPC address") + activityAddr = flag.String("activity_addr", "127.0.0.1:13006", "activity-service gRPC address") + activityDSN = flag.String("activity_dsn", "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC", "activity MySQL DSN") + walletDSN = flag.String("wallet_dsn", "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_wallet?parseTime=true&charset=utf8mb4&loc=UTC", "wallet MySQL DSN") + mode = flag.String("mode", "happy", "flow mode: happy or boundary") + ) + flag.Parse() + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + walletConn, err := grpc.DialContext(ctx, *walletAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + must("dial wallet-service", err) + defer walletConn.Close() + activityConn, err := grpc.DialContext(ctx, *activityAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + must("dial activity-service", err) + defer activityConn.Close() + + activityDB, err := sql.Open("mysql", *activityDSN) + must("open activity mysql", err) + defer activityDB.Close() + must("ping activity mysql", activityDB.PingContext(ctx)) + walletDB, err := sql.Open("mysql", *walletDSN) + must("open wallet mysql", err) + defer walletDB.Close() + must("ping wallet mysql", walletDB.PingContext(ctx)) + + walletClient := walletv1.NewWalletServiceClient(walletConn) + rewardClient := activityv1.NewCumulativeRechargeRewardServiceClient(activityConn) + adminClient := activityv1.NewAdminCumulativeRechargeRewardServiceClient(activityConn) + + if *mode == "boundary" { + runBoundaryFlow(ctx, walletClient, rewardClient, adminClient, activityDB, walletDB) + return + } + + runID := fmt.Sprintf("crr_%d", time.Now().UnixMilli()) + targetUserID := int64(760000000000) + time.Now().Unix()%10000000 + sellerUserID := targetUserID + 100000000 + now := time.Now().UTC() + currentCycle := cycleForTime(now) + nextCycle := cycleForTime(time.UnixMilli(currentCycle.EndMS).UTC().Add(2 * time.Hour)) + + group100 := createCoinRewardGroup(ctx, walletClient, runID, "100", 1000) + group200 := createCoinRewardGroup(ctx, walletClient, runID, "200", 2000) + updateConfig(ctx, adminClient, runID, group100, group200) + + google := consumeRecharge(ctx, rewardClient, runID, "google", targetUserID, "google_play", 5_400_000, 6_000, now.Add(1*time.Minute)) + assert(len(google.Grants) == 0 && google.GetReason() == "no_tier_matched", "google $60 should not grant a tier") + statusAfterGoogle := getStatus(ctx, rewardClient, targetUserID) + assert(statusAfterGoogle.GetProgress().GetTotalUsdMinor() == 6_000, "google progress should be $60.00") + + mifapay := consumeRecharge(ctx, rewardClient, runID, "mifapay", targetUserID, "mifapay", 4_500_000, 5_000, now.Add(2*time.Minute)) + assertGrantThresholds("mifapay should unlock only $100 tier", mifapay.GetGrants(), []int64{10_000}) + statusAfterMifapay := getStatus(ctx, rewardClient, targetUserID) + assert(statusAfterMifapay.GetProgress().GetTotalUsdMinor() == 11_000, "google+mifapay progress should be $110.00") + + transfer := runCoinSellerTransfer(ctx, walletClient, walletDB, runID, sellerUserID, targetUserID) + coinSellerUSDMinor := transfer.AvailableDelta * 100 / 90_000 + assert(transfer.AvailableDelta == 9_000_000 && coinSellerUSDMinor == 10_000, "coin seller transfer should convert 9,000,000 coins to $100.00") + coinSeller := consumeRechargeFromWalletFact(ctx, rewardClient, targetUserID, transfer, coinSellerUSDMinor) + assertGrantThresholds("coin seller transfer should unlock only $200 tier", coinSeller.GetGrants(), []int64{20_000}) + statusAfterCoinSeller := getStatus(ctx, rewardClient, targetUserID) + assert(statusAfterCoinSeller.GetProgress().GetTotalUsdMinor() == 21_000, "current cycle progress should be $210.00") + + nextWeek := consumeRecharge(ctx, rewardClient, runID, "nextweek-google", targetUserID, "google_play", 9_000_000, 10_000, time.UnixMilli(nextCycle.StartMS).UTC().Add(time.Hour)) + assertGrantThresholds("next week $100 should unlock only new $100 tier", nextWeek.GetGrants(), []int64{10_000}) + + currentProgress := queryProgress(ctx, activityDB, targetUserID, currentCycle.Key) + nextProgress := queryProgress(ctx, activityDB, targetUserID, nextCycle.Key) + assert(currentProgress == 21_000, "current cycle DB progress should stay $210.00") + assert(nextProgress == 10_000, "next cycle DB progress should reset to $100.00") + + currentGrants := queryGrantSummary(ctx, activityDB, targetUserID, currentCycle.Key) + nextGrants := queryGrantSummary(ctx, activityDB, targetUserID, nextCycle.Key) + assert(currentGrants.Count == 2 && currentGrants.Granted == 2, "current cycle should have two granted tiers") + assert(nextGrants.Count == 1 && nextGrants.Granted == 1, "next cycle should have one granted tier") + + walletReward := queryWalletRewardSummary(ctx, walletDB, targetUserID) + assert(walletReward.Count == 3 && walletReward.CoinDelta == 4_000, "wallet should receive three reward group grants totaling 4,000 COIN") + + out := map[string]any{ + "run_id": runID, + "target_user_id": targetUserID, + "seller_user_id": sellerUserID, + "current_cycle": currentCycle.Key, + "next_cycle": nextCycle.Key, + "events": []map[string]any{ + {"type": "google_play", "usd_minor": 6000, "grant_thresholds": thresholds(google.GetGrants())}, + {"type": "mifapay", "usd_minor": 5000, "grant_thresholds": thresholds(mifapay.GetGrants())}, + {"type": "coin_seller_transfer", "coins": transfer.AvailableDelta, "usd_minor": coinSellerUSDMinor, "grant_thresholds": thresholds(coinSeller.GetGrants())}, + {"type": "next_week_google_play", "usd_minor": 10000, "grant_thresholds": thresholds(nextWeek.GetGrants())}, + }, + "progress_usd_minor": map[string]int64{currentCycle.Key: currentProgress, nextCycle.Key: nextProgress}, + "activity_grants": map[string]grantSummary{currentCycle.Key: currentGrants, nextCycle.Key: nextGrants}, + "wallet_reward": walletReward, + } + encoded, _ := json.MarshalIndent(out, "", " ") + fmt.Println(string(encoded)) +} + +func createCoinRewardGroup(ctx context.Context, client walletv1.WalletServiceClient, runID string, suffix string, coinAmount int64) int64 { + resp, err := client.CreateResourceGroup(ctx, &walletv1.CreateResourceGroupRequest{ + RequestId: fmt.Sprintf("%s_create_group_%s", runID, suffix), + AppCode: appCode, + GroupCode: fmt.Sprintf("%s_group_%s", runID, suffix), + Name: fmt.Sprintf("CRR local reward %s", suffix), + Status: statusActive, + Description: "local cumulative recharge flow verification", + SortOrder: 1, + OperatorUserId: operatorAdminID, + Items: []*walletv1.ResourceGroupItemInput{{ + ItemType: groupItemWalletAsset, + WalletAssetType: assetCoin, + WalletAssetAmount: coinAmount, + SortOrder: 1, + }}, + }) + must("create wallet reward group "+suffix, err) + assert(resp.GetGroup().GetGroupId() > 0, "created resource group id should be positive") + return resp.GetGroup().GetGroupId() +} + +func updateConfig(ctx context.Context, client activityv1.AdminCumulativeRechargeRewardServiceClient, runID string, group100 int64, group200 int64) { + _, err := updateConfigWithTiers(ctx, client, runID, true, []*activityv1.CumulativeRechargeRewardTier{ + {TierCode: runID + "_100", TierName: "Local $100", ThresholdUsdMinor: 10_000, ResourceGroupId: group100, Status: statusActive, SortOrder: 1}, + {TierCode: runID + "_200", TierName: "Local $200", ThresholdUsdMinor: 20_000, ResourceGroupId: group200, Status: statusActive, SortOrder: 2}, + }) + must("update cumulative recharge config", err) +} + +func updateConfigWithTiers(ctx context.Context, client activityv1.AdminCumulativeRechargeRewardServiceClient, runID string, enabled bool, tiers []*activityv1.CumulativeRechargeRewardTier) (*activityv1.UpdateCumulativeRechargeRewardConfigResponse, error) { + return client.UpdateCumulativeRechargeRewardConfig(ctx, &activityv1.UpdateCumulativeRechargeRewardConfigRequest{ + Meta: meta(runID + "_update_config"), + Enabled: enabled, + OperatorAdminId: operatorAdminID, + Tiers: tiers, + }) +} + +func consumeRecharge(ctx context.Context, client activityv1.CumulativeRechargeRewardServiceClient, runID string, name string, userID int64, rechargeType string, coinAmount int64, usdMinor int64, occurredAt time.Time) *activityv1.ConsumeCumulativeRechargeRewardResponse { + resp, err := consumeRechargeAllowError(ctx, client, runID, name, userID, rechargeType, coinAmount, usdMinor, occurredAt) + must("consume recharge "+name, err) + return resp +} + +func consumeRechargeAllowError(ctx context.Context, client activityv1.CumulativeRechargeRewardServiceClient, runID string, name string, userID int64, rechargeType string, coinAmount int64, usdMinor int64, occurredAt time.Time) (*activityv1.ConsumeCumulativeRechargeRewardResponse, error) { + payload, _ := json.Marshal(map[string]any{ + "recharge_type": rechargeType, + "coin_amount": coinAmount, + "recharge_usd_minor": usdMinor, + }) + resp, err := client.ConsumeCumulativeRechargeReward(ctx, &activityv1.ConsumeCumulativeRechargeRewardRequest{ + Meta: meta(runID + "_" + name), + EventId: fmt.Sprintf("%s_event_%s", runID, name), + TransactionId: fmt.Sprintf("%s_tx_%s", runID, name), + CommandId: fmt.Sprintf("%s_cmd_%s", runID, name), + UserId: userID, + RechargeCoinAmount: coinAmount, + RechargeSequence: time.Now().UnixNano(), + RechargeType: rechargeType, + QualifyingUsdMinor: usdMinor, + PayloadJson: string(payload), + OccurredAtMs: occurredAt.UnixMilli(), + }) + return resp, err +} + +func consumeRechargeFromWalletFact(ctx context.Context, client activityv1.CumulativeRechargeRewardServiceClient, userID int64, fact walletOutboxFact, usdMinor int64) *activityv1.ConsumeCumulativeRechargeRewardResponse { + resp, err := client.ConsumeCumulativeRechargeReward(ctx, &activityv1.ConsumeCumulativeRechargeRewardRequest{ + Meta: meta("coin_seller_wallet_fact"), + EventId: fact.EventID, + TransactionId: fact.TransactionID, + CommandId: fact.CommandID, + UserId: userID, + RechargeCoinAmount: fact.AvailableDelta, + RechargeType: "coin_seller_transfer", + QualifyingUsdMinor: usdMinor, + PayloadJson: fact.PayloadJSON, + OccurredAtMs: fact.CreatedAtMS, + }) + must("consume coin seller wallet fact", err) + return resp +} + +func runCoinSellerTransfer(ctx context.Context, client walletv1.WalletServiceClient, db *sql.DB, runID string, sellerUserID int64, targetUserID int64) walletOutboxFact { + _, err := client.AdminCreditCoinSellerStock(ctx, &walletv1.AdminCreditCoinSellerStockRequest{ + CommandId: fmt.Sprintf("%s_stock", runID), + SellerUserId: sellerUserID, + StockType: "usdt_purchase", + CoinAmount: 9_000_000, + PaidCurrencyCode: "USDT", + PaidAmountMicro: 100_000_000, + PaymentRef: fmt.Sprintf("%s_payment", runID), + EvidenceRef: "local-flow", + OperatorUserId: operatorAdminID, + Reason: "local cumulative recharge flow", + AppCode: appCode, + }) + must("credit coin seller stock", err) + transfer, err := client.TransferCoinFromSeller(ctx, &walletv1.TransferCoinFromSellerRequest{ + CommandId: fmt.Sprintf("%s_seller_transfer", runID), + SellerUserId: sellerUserID, + TargetUserId: targetUserID, + Amount: 9_000_000, + Reason: "local cumulative recharge flow", + AppCode: appCode, + SellerRegionId: 1, + TargetRegionId: 1, + }) + must("transfer coin from seller", err) + + var fact walletOutboxFact + err = db.QueryRowContext(ctx, ` + SELECT event_id, transaction_id, command_id, COALESCE(payload, '{}'), created_at_ms, available_delta + FROM wallet_outbox + WHERE app_code = ? AND transaction_id = ? AND user_id = ? AND event_type = 'WalletRechargeRecorded' + LIMIT 1`, + appCode, transfer.GetTransactionId(), targetUserID, + ).Scan(&fact.EventID, &fact.TransactionID, &fact.CommandID, &fact.PayloadJSON, &fact.CreatedAtMS, &fact.AvailableDelta) + must("read coin seller WalletRechargeRecorded outbox", err) + return fact +} + +func setResourceGroupStatus(ctx context.Context, client walletv1.WalletServiceClient, runID string, groupID int64, status string) { + _, err := client.SetResourceGroupStatus(ctx, &walletv1.SetResourceGroupStatusRequest{ + RequestId: fmt.Sprintf("%s_group_status_%d_%s", runID, groupID, status), + AppCode: appCode, + GroupId: groupID, + Status: status, + OperatorUserId: operatorAdminID, + }) + must("set resource group status "+status, err) +} + +func getStatus(ctx context.Context, client activityv1.CumulativeRechargeRewardServiceClient, userID int64) *activityv1.CumulativeRechargeRewardStatus { + resp, err := client.GetCumulativeRechargeRewardStatus(ctx, &activityv1.GetCumulativeRechargeRewardStatusRequest{Meta: meta("get_status"), UserId: userID}) + must("get cumulative recharge status", err) + return resp.GetStatus() +} + +func runBoundaryFlow(ctx context.Context, walletClient walletv1.WalletServiceClient, rewardClient activityv1.CumulativeRechargeRewardServiceClient, adminClient activityv1.AdminCumulativeRechargeRewardServiceClient, activityDB *sql.DB, walletDB *sql.DB) { + runID := fmt.Sprintf("crr_boundary_%d", time.Now().UnixMilli()) + baseUserID := int64(761000000000) + time.Now().Unix()%10000000 + now := time.Now().UTC() + currentCycle := cycleForTime(now) + nextCycle := cycleForTime(time.UnixMilli(currentCycle.EndMS).UTC().Add(time.Millisecond)) + + group100 := createCoinRewardGroup(ctx, walletClient, runID, "100", 1000) + group200 := createCoinRewardGroup(ctx, walletClient, runID, "200", 2000) + + updateConfig(ctx, adminClient, runID, group100, group200) + duplicate := consumeRecharge(ctx, rewardClient, runID, "duplicate", baseUserID+1, "google_play", 13_500_000, 15_000, now.Add(10*time.Minute)) + assertGrantThresholds("duplicate first event should grant $100", duplicate.GetGrants(), []int64{10_000}) + duplicateAgain := consumeRecharge(ctx, rewardClient, runID, "duplicate", baseUserID+1, "google_play", 13_500_000, 15_000, now.Add(10*time.Minute)) + assert(duplicateAgain.GetReason() == "already_consumed" && len(duplicateAgain.GetGrants()) == 0, "duplicate event should be consumed without new grants") + assert(queryProgress(ctx, activityDB, baseUserID+1, currentCycle.Key) == 15_000, "duplicate event must not double progress") + assert(queryGrantSummary(ctx, activityDB, baseUserID+1, currentCycle.Key).Count == 1, "duplicate event must not double grant rows") + + cross := consumeRecharge(ctx, rewardClient, runID, "cross-multiple", baseUserID+2, "mifapay", 22_500_000, 25_000, now.Add(11*time.Minute)) + assertGrantThresholds("single large recharge should cross both tiers", cross.GetGrants(), []int64{10_000, 20_000}) + assert(queryProgress(ctx, activityDB, baseUserID+2, currentCycle.Key) == 25_000, "cross-tier progress should be $250.00") + assert(queryWalletRewardSummary(ctx, walletDB, baseUserID+2).Count == 2, "cross-tier wallet should receive two reward grants") + + _, err := updateConfigWithTiers(ctx, adminClient, runID+"_duplicate_threshold", true, []*activityv1.CumulativeRechargeRewardTier{ + {TierCode: runID + "_dup_a", TierName: "Duplicate A", ThresholdUsdMinor: 10_000, ResourceGroupId: group100, Status: statusActive, SortOrder: 1}, + {TierCode: runID + "_dup_b", TierName: "Duplicate B", ThresholdUsdMinor: 10_000, ResourceGroupId: group200, Status: statusActive, SortOrder: 2}, + }) + assert(err != nil, "duplicate active thresholds must be rejected") + + _, err = updateConfigWithTiers(ctx, adminClient, runID+"_disabled", false, []*activityv1.CumulativeRechargeRewardTier{ + {TierCode: runID + "_disabled_100", TierName: "Disabled $100", ThresholdUsdMinor: 10_000, ResourceGroupId: group100, Status: statusActive, SortOrder: 1}, + {TierCode: runID + "_disabled_200", TierName: "Disabled $200", ThresholdUsdMinor: 20_000, ResourceGroupId: group200, Status: statusActive, SortOrder: 2}, + }) + must("disable cumulative recharge config", err) + disabled := consumeRecharge(ctx, rewardClient, runID, "disabled-event", baseUserID+3, "google_play", 22_500_000, 25_000, now.Add(12*time.Minute)) + assert(disabled.GetReason() == "disabled" && len(disabled.GetGrants()) == 0, "disabled config should consume event without grants") + assert(queryProgress(ctx, activityDB, baseUserID+3, currentCycle.Key) == 0, "disabled config should not update progress") + updateConfig(ctx, adminClient, runID+"_reenable_after_disabled", group100, group200) + disabledAgain := consumeRecharge(ctx, rewardClient, runID, "disabled-event", baseUserID+3, "google_play", 22_500_000, 25_000, now.Add(12*time.Minute)) + assert(disabledAgain.GetReason() == "already_consumed" && queryProgress(ctx, activityDB, baseUserID+3, currentCycle.Key) == 0, "disabled-period event must not backfill after re-enable") + + _, err = updateConfigWithTiers(ctx, adminClient, runID+"_inactive", true, []*activityv1.CumulativeRechargeRewardTier{ + {TierCode: runID + "_inactive_100", TierName: "Inactive $100", ThresholdUsdMinor: 10_000, ResourceGroupId: group100, Status: tierStatusInactive, SortOrder: 1}, + {TierCode: runID + "_active_200", TierName: "Active $200", ThresholdUsdMinor: 20_000, ResourceGroupId: group200, Status: statusActive, SortOrder: 2}, + }) + must("set inactive tier config", err) + inactive := consumeRecharge(ctx, rewardClient, runID, "inactive-tier", baseUserID+4, "mifapay", 22_500_000, 25_000, now.Add(13*time.Minute)) + assertGrantThresholds("inactive $100 tier should be ignored", inactive.GetGrants(), []int64{20_000}) + assert(queryGrantSummary(ctx, activityDB, baseUserID+4, currentCycle.Key).Count == 1, "inactive tier should not create a grant row") + + failingGroup := createCoinRewardGroup(ctx, walletClient, runID, "disabled_group", 1000) + setResourceGroupStatus(ctx, walletClient, runID, failingGroup, statusDisabled) + _, err = updateConfigWithTiers(ctx, adminClient, runID+"_failed_retry", true, []*activityv1.CumulativeRechargeRewardTier{ + {TierCode: runID + "_failed_100", TierName: "Failed then retry", ThresholdUsdMinor: 10_000, ResourceGroupId: failingGroup, Status: statusActive, SortOrder: 1}, + }) + must("set disabled resource group tier", err) + _, err = consumeRechargeAllowError(ctx, rewardClient, runID, "failed-wallet", baseUserID+5, "google_play", 13_500_000, 15_000, now.Add(14*time.Minute)) + assert(err != nil, "disabled wallet resource group should fail grant") + assert(queryGrantStatus(ctx, activityDB, baseUserID+5, currentCycle.Key, 10_000) == "failed", "failed wallet grant should persist failed status") + setResourceGroupStatus(ctx, walletClient, runID, failingGroup, statusActive) + retried := consumeRecharge(ctx, rewardClient, runID, "failed-wallet", baseUserID+5, "google_play", 13_500_000, 15_000, now.Add(14*time.Minute)) + assertGrantThresholds("same event retry should grant after group enabled", retried.GetGrants(), []int64{10_000}) + assert(queryProgress(ctx, activityDB, baseUserID+5, currentCycle.Key) == 15_000, "failed retry must not double progress") + assert(queryGrantStatus(ctx, activityDB, baseUserID+5, currentCycle.Key, 10_000) == "granted", "retried wallet grant should be granted") + + updateConfig(ctx, adminClient, runID+"_week_boundary", group100, group200) + endBoundary := consumeRecharge(ctx, rewardClient, runID, "week-end", baseUserID+6, "google_play", 9_000_000, 10_000, time.UnixMilli(currentCycle.EndMS).UTC()) + assertGrantThresholds("event at cycle end should belong current cycle", endBoundary.GetGrants(), []int64{10_000}) + nextBoundary := consumeRecharge(ctx, rewardClient, runID, "week-next", baseUserID+6, "google_play", 9_000_000, 10_000, time.UnixMilli(currentCycle.EndMS+1).UTC()) + assertGrantThresholds("event after cycle end should belong next cycle", nextBoundary.GetGrants(), []int64{10_000}) + assert(queryProgress(ctx, activityDB, baseUserID+6, currentCycle.Key) == 10_000, "cycle end progress should stay current week") + assert(queryProgress(ctx, activityDB, baseUserID+6, nextCycle.Key) == 10_000, "cycle start progress should reset next week") + + _, err = consumeRechargeAllowError(ctx, rewardClient, runID, "zero-usd", baseUserID+7, "google_play", 10_000, 0, now.Add(15*time.Minute)) + assert(err != nil, "zero qualifying USD event must be rejected by direct consume entry") + assert(queryProgress(ctx, activityDB, baseUserID+7, currentCycle.Key) == 0, "zero qualifying USD event must not write progress") + + updateConfig(ctx, adminClient, runID+"_restore", group100, group200) + out := map[string]any{ + "mode": "boundary", + "run_id": runID, + "base_user_id": baseUserID, + "current_cycle": currentCycle.Key, + "next_cycle": nextCycle.Key, + "cases": map[string]any{ + "duplicate_event": map[string]any{"progress_usd_minor": queryProgress(ctx, activityDB, baseUserID+1, currentCycle.Key), "grant_count": queryGrantSummary(ctx, activityDB, baseUserID+1, currentCycle.Key).Count}, + "cross_multiple_tiers": map[string]any{"grant_thresholds": thresholds(cross.GetGrants()), "wallet_grants": queryWalletRewardSummary(ctx, walletDB, baseUserID+2).Count}, + "disabled_no_backfill": map[string]any{"first_reason": disabled.GetReason(), "retry_reason": disabledAgain.GetReason(), "progress_usd_minor": queryProgress(ctx, activityDB, baseUserID+3, currentCycle.Key)}, + "inactive_tier_ignored": map[string]any{"grant_thresholds": thresholds(inactive.GetGrants())}, + "failed_retry": map[string]any{"final_status": queryGrantStatus(ctx, activityDB, baseUserID+5, currentCycle.Key, 10_000), "progress_usd_minor": queryProgress(ctx, activityDB, baseUserID+5, currentCycle.Key)}, + "week_boundary": map[string]any{"current_progress": queryProgress(ctx, activityDB, baseUserID+6, currentCycle.Key), "next_progress": queryProgress(ctx, activityDB, baseUserID+6, nextCycle.Key)}, + "zero_usd_rejected": map[string]any{"progress_usd_minor": queryProgress(ctx, activityDB, baseUserID+7, currentCycle.Key)}, + }, + } + encoded, _ := json.MarshalIndent(out, "", " ") + fmt.Println(string(encoded)) +} + +func queryProgress(ctx context.Context, db *sql.DB, userID int64, cycleKey string) int64 { + var total sql.NullInt64 + err := db.QueryRowContext(ctx, ` + SELECT total_usd_minor + FROM cumulative_recharge_reward_progress + WHERE app_code = ? AND user_id = ? AND cycle_key = ?`, + appCode, userID, cycleKey, + ).Scan(&total) + if err == sql.ErrNoRows { + return 0 + } + must("query cumulative progress "+cycleKey, err) + return total.Int64 +} + +type grantSummary struct { + Count int64 `json:"count"` + Granted int64 `json:"granted"` +} + +func queryGrantSummary(ctx context.Context, db *sql.DB, userID int64, cycleKey string) grantSummary { + var summary grantSummary + err := db.QueryRowContext(ctx, ` + SELECT COUNT(*), COALESCE(SUM(CASE WHEN status = 'granted' THEN 1 ELSE 0 END), 0) + FROM cumulative_recharge_reward_grants + WHERE app_code = ? AND user_id = ? AND cycle_key = ?`, + appCode, userID, cycleKey, + ).Scan(&summary.Count, &summary.Granted) + must("query cumulative grants "+cycleKey, err) + return summary +} + +func queryGrantStatus(ctx context.Context, db *sql.DB, userID int64, cycleKey string, thresholdUSDMinor int64) string { + var status string + err := db.QueryRowContext(ctx, ` + SELECT status + FROM cumulative_recharge_reward_grants + WHERE app_code = ? AND user_id = ? AND cycle_key = ? AND threshold_usd_minor = ? + ORDER BY created_at_ms DESC + LIMIT 1`, + appCode, userID, cycleKey, thresholdUSDMinor, + ).Scan(&status) + must("query cumulative grant status", err) + return status +} + +type walletRewardSummary struct { + Count int64 `json:"count"` + CoinDelta int64 `json:"coin_delta"` +} + +func queryWalletRewardSummary(ctx context.Context, db *sql.DB, userID int64) walletRewardSummary { + var summary walletRewardSummary + err := db.QueryRowContext(ctx, ` + SELECT COUNT(DISTINCT rg.grant_id), COALESCE(SUM(we.available_delta), 0) + FROM resource_grants rg + JOIN resource_grant_items rgi ON rgi.app_code = rg.app_code AND rgi.grant_id = rg.grant_id + JOIN wallet_entries we ON we.app_code = rgi.app_code AND we.transaction_id = rgi.wallet_transaction_id AND we.user_id = rg.target_user_id + WHERE rg.app_code = ? AND rg.target_user_id = ? AND rg.grant_source = ? AND we.asset_type = ?`, + appCode, userID, rechargeSource, assetCoin, + ).Scan(&summary.Count, &summary.CoinDelta) + must("query wallet reward grants", err) + return summary +} + +func meta(requestID string) *activityv1.RequestMeta { + return &activityv1.RequestMeta{ + RequestId: requestID, + Caller: "local-cumulative-recharge-flow", + AppCode: appCode, + SentAtMs: time.Now().UnixMilli(), + } +} + +func cycleForTime(value time.Time) cycle { + utc := value.UTC() + dayStart := time.Date(utc.Year(), utc.Month(), utc.Day(), 0, 0, 0, 0, time.UTC) + daysSinceMonday := (int(dayStart.Weekday()) + 6) % 7 + start := dayStart.AddDate(0, 0, -daysSinceMonday) + end := start.AddDate(0, 0, 7) + isoYear, isoWeek := start.ISOWeek() + return cycle{Key: fmt.Sprintf("%04d-W%02d", isoYear, isoWeek), StartMS: start.UnixMilli(), EndMS: end.UnixMilli() - 1} +} + +func assertGrantThresholds(message string, grants []*activityv1.CumulativeRechargeRewardGrant, want []int64) { + got := thresholds(grants) + if len(got) != len(want) { + log.Fatalf("%s: got %v want %v", message, got, want) + } + for i := range want { + if got[i] != want[i] { + log.Fatalf("%s: got %v want %v", message, got, want) + } + } +} + +func thresholds(grants []*activityv1.CumulativeRechargeRewardGrant) []int64 { + items := make([]int64, 0, len(grants)) + for _, grant := range grants { + if grant == nil { + continue + } + items = append(items, grant.GetThresholdUsdMinor()) + } + sort.Slice(items, func(i, j int) bool { return items[i] < items[j] }) + return items +} + +func assert(ok bool, message string) { + if !ok { + log.Fatal(message) + } +} + +func must(action string, err error) { + if err != nil { + log.Fatalf("%s: %v", action, err) + } +} diff --git a/tools/weekly-star-local-boundary/main.go b/tools/weekly-star-local-boundary/main.go new file mode 100644 index 00000000..cf32abaf --- /dev/null +++ b/tools/weekly-star-local-boundary/main.go @@ -0,0 +1,660 @@ +package main + +import ( + "context" + "database/sql" + "encoding/json" + "flag" + "fmt" + "log" + "os" + "sort" + "strings" + "time" + + _ "github.com/go-sql-driver/mysql" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + activityv1 "hyapp.local/api/proto/activity/v1" + roomeventsv1 "hyapp.local/api/proto/events/room/v1" +) + +const ( + defaultAppCode = "lalu" + boundaryTitlePrefix = "Local Weekly Star Boundary" + activityCode = "weekly_star" + statusActive = "active" + statusDisabled = "disabled" + rewardGroupTop1ID = int64(930001) + rewardGroupTop2ID = int64(930002) + rewardGroupTop3ID = int64(930003) + testOperatorAdmin = int64(900001) + testTargetAnchorID = int64(990001) + localBoundaryRoomID = "weekly-star-boundary-room" +) + +type config struct { + activityAddr string + activityDSN string + walletDSN string + appCode string + regionID int64 +} + +type clients struct { + admin activityv1.AdminWeeklyStarServiceClient + app activityv1.WeeklyStarServiceClient + room activityv1.RoomEventConsumerServiceClient + cron activityv1.ActivityCronServiceClient +} + +type boundarySummary struct { + RunID string `json:"run_id"` + RegionID int64 `json:"region_id"` + Passed []string `json:"passed"` + Skipped []string `json:"skipped"` + Diagnostics map[string]string `json:"diagnostics"` +} + +type leaderboardEntry struct { + RankNo int32 `json:"rank_no"` + UserID int64 `json:"user_id"` + Score int64 `json:"score"` +} + +type settlementRow struct { + RankNo int32 + UserID int64 + Score int64 + ResourceGroupID int64 + Status string +} + +type cycleWindow struct { + startMS int64 + endMS int64 +} + +func main() { + cfg := config{} + flag.StringVar(&cfg.activityAddr, "activity-addr", "127.0.0.1:13006", "activity-service gRPC address") + flag.StringVar(&cfg.activityDSN, "activity-dsn", "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC", "activity MySQL DSN") + flag.StringVar(&cfg.walletDSN, "wallet-dsn", "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_wallet?parseTime=true&charset=utf8mb4&loc=UTC", "wallet MySQL DSN") + flag.StringVar(&cfg.appCode, "app-code", defaultAppCode, "app code") + flag.Int64Var(&cfg.regionID, "region-id", 991686, "local boundary-test region id") + flag.Parse() + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + activityDB := mustOpenDB(ctx, cfg.activityDSN) + defer activityDB.Close() + walletDB := mustOpenDB(ctx, cfg.walletDSN) + defer walletDB.Close() + + runID := fmt.Sprintf("%d", time.Now().UTC().UnixMilli()) + cleanupBoundaryCycles(ctx, activityDB, cfg) + seedWalletRewardGroups(ctx, walletDB, cfg.appCode) + + conn, err := grpc.DialContext(ctx, cfg.activityAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) + if err != nil { + log.Fatalf("dial activity-service: %v", err) + } + defer conn.Close() + clients := clients{ + admin: activityv1.NewAdminWeeklyStarServiceClient(conn), + app: activityv1.NewWeeklyStarServiceClient(conn), + room: activityv1.NewRoomEventConsumerServiceClient(conn), + cron: activityv1.NewActivityCronServiceClient(conn), + } + + summary := boundarySummary{ + RunID: runID, + RegionID: cfg.regionID, + Passed: make([]string, 0), + Skipped: make([]string, 0), + Diagnostics: make(map[string]string), + } + + runOverlapRejected(ctx, clients, cfg, runID, &summary) + runValidationRejected(ctx, clients, cfg, runID, &summary) + runRegionFallback(ctx, activityDB, clients, cfg, runID, &summary) + runTimeBoundaryAndIgnoredGift(ctx, clients, cfg, runID, &summary) + runTieBreaking(ctx, clients, cfg, runID, &summary) + runDisabledCycleIgnored(ctx, clients, cfg, runID, &summary) + runIdempotentAndBeforeStartIgnored(ctx, clients, cfg, runID, &summary) + runTop3SettlementGrantsOnlyWinners(ctx, activityDB, walletDB, clients, cfg, runID, &summary) + runNoScoreSettlement(ctx, activityDB, walletDB, clients, cfg, runID, &summary) + + encoder := json.NewEncoder(os.Stdout) + encoder.SetIndent("", " ") + if err := encoder.Encode(summary); err != nil { + log.Fatalf("encode boundary summary: %v", err) + } +} + +func runOverlapRejected(ctx context.Context, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + gifts := giftsFor("overlap", runID) + baseCycle := createCycle(ctx, clients.admin, cfg, cfg.regionID+10, fmt.Sprintf("%s overlap base %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, gifts) + _, err := createCycleAllowError(ctx, clients.admin, cfg, cfg.regionID+10, fmt.Sprintf("%s overlap conflict %s", boundaryTitlePrefix, runID), statusActive, window.startMS+1_000, window.endMS-1_000, gifts) + if status.Code(err) != codes.FailedPrecondition { + log.Fatalf("overlap should be rejected with FailedPrecondition, got %v", err) + } + summary.Diagnostics["overlap_base_cycle"] = baseCycle.GetCycleId() + pass(summary, "overlap_active_cycle_rejected") +} + +func runValidationRejected(ctx context.Context, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + twoGifts := []string{"weekly_star_boundary_invalid_a_" + runID, "weekly_star_boundary_invalid_b_" + runID} + _, giftErr := createCycleAllowError(ctx, clients.admin, cfg, cfg.regionID+20, fmt.Sprintf("%s invalid gift count %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, twoGifts) + if status.Code(giftErr) != codes.InvalidArgument { + log.Fatalf("cycle with two gifts should be InvalidArgument, got %v", giftErr) + } + _, rewardErr := createCycleWithRewardsAllowError(ctx, clients.admin, cfg, cfg.regionID+21, fmt.Sprintf("%s missing reward %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, giftsFor("missing_reward", runID), []*activityv1.WeeklyStarReward{ + {RankNo: 1, ResourceGroupId: rewardGroupTop1ID}, + {RankNo: 2, ResourceGroupId: rewardGroupTop2ID}, + }) + if status.Code(rewardErr) != codes.InvalidArgument { + log.Fatalf("cycle missing top3 reward should be InvalidArgument, got %v", rewardErr) + } + pass(summary, "exactly_three_gifts_and_top3_rewards_required") +} + +func runRegionFallback(ctx context.Context, db *sql.DB, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + defaultGifts := giftsFor("default_region", runID) + concreteGifts := giftsFor("concrete_region", runID) + defaultCycle, err := createCycleAllowError(ctx, clients.admin, cfg, 0, fmt.Sprintf("%s default region %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, defaultGifts) + if err != nil { + existingID, ok := currentCycleIDFromDB(ctx, db, cfg, 0, time.Now().UTC().UnixMilli()) + if !ok { + log.Fatalf("create default cycle failed and no existing default current cycle is available: %v", err) + } + summary.Skipped = append(summary.Skipped, "controlled_default_cycle_create: existing active default cycle "+existingID) + defaultCycle = &activityv1.WeeklyStarCycle{CycleId: existingID, RegionId: 0} + } + concreteCycle := createCycle(ctx, clients.admin, cfg, cfg.regionID+30, fmt.Sprintf("%s concrete region %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, concreteGifts) + + concreteCurrent := getCurrent(ctx, clients.app, cfg, cfg.regionID+30, 710001) + if concreteCurrent.GetCycle().GetCycleId() != concreteCycle.GetCycleId() { + log.Fatalf("concrete region should preempt default, got %s want %s", concreteCurrent.GetCycle().GetCycleId(), concreteCycle.GetCycleId()) + } + defaultCurrent := getCurrent(ctx, clients.app, cfg, cfg.regionID+31, 710001) + if defaultCurrent.GetCycle().GetRegionId() != 0 || defaultCurrent.GetCycle().GetCycleId() == concreteCycle.GetCycleId() || defaultCurrent.GetCycle().GetCycleId() == "" { + log.Fatalf("region without concrete cycle should fall back to default, got cycle=%s region=%d", defaultCurrent.GetCycle().GetCycleId(), defaultCurrent.GetCycle().GetRegionId()) + } + summary.Diagnostics["default_cycle_used"] = defaultCycle.GetCycleId() + summary.Diagnostics["concrete_cycle_used"] = concreteCycle.GetCycleId() + pass(summary, "region_specific_cycle_preempts_default_and_default_fallback_works") +} + +func runTimeBoundaryAndIgnoredGift(ctx context.Context, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + regionID := cfg.regionID + 40 + gifts := giftsFor("time_boundary", runID) + cycle := createCycle(ctx, clients.admin, cfg, regionID, fmt.Sprintf("%s time boundary %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, gifts) + + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "time-start"), 720001, gifts[0], 100, window.startMS, 1) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "time-end"), 720002, gifts[0], 200, window.endMS, 2) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "time-non-config"), 720003, "weekly_star_boundary_not_configured_"+runID, 300, window.startMS+1_000, 3) + + entries := listLeaderboard(ctx, clients.admin, cfg, regionID, cycle.GetCycleId()) + assertLeaderboardExact("time boundary", entries, []leaderboardEntry{{RankNo: 1, UserID: 720001, Score: 100}}) + summary.Diagnostics["time_boundary_cycle"] = cycle.GetCycleId() + pass(summary, "start_inclusive_end_exclusive_and_non_configured_gift_ignored") +} + +func runTieBreaking(ctx context.Context, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + regionID := cfg.regionID + 50 + gifts := giftsFor("tie_breaking", runID) + cycle := createCycle(ctx, clients.admin, cfg, regionID, fmt.Sprintf("%s tie breaking %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, gifts) + + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "tie-later-small-id"), 730001, gifts[0], 1000, window.startMS+2_000, 1) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "tie-later-large-id"), 730002, gifts[0], 1000, window.startMS+2_000, 2) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "tie-earliest"), 730003, gifts[0], 1000, window.startMS+1_000, 3) + + entries := listLeaderboard(ctx, clients.admin, cfg, regionID, cycle.GetCycleId()) + assertLeaderboardExact("tie breaking", entries, []leaderboardEntry{ + {RankNo: 1, UserID: 730003, Score: 1000}, + {RankNo: 2, UserID: 730001, Score: 1000}, + {RankNo: 3, UserID: 730002, Score: 1000}, + }) + summary.Diagnostics["tie_breaking_cycle"] = cycle.GetCycleId() + pass(summary, "tie_breaking_score_first_scored_at_user_id") +} + +func runDisabledCycleIgnored(ctx context.Context, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + regionID := cfg.regionID + 60 + gifts := giftsFor("disabled", runID) + cycle := createCycle(ctx, clients.admin, cfg, regionID, fmt.Sprintf("%s disabled ignored %s", boundaryTitlePrefix, runID), statusDisabled, window.startMS, window.endMS, gifts) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "disabled-gift"), 740001, gifts[0], 777, window.startMS+1_000, 1) + entries := listLeaderboard(ctx, clients.admin, cfg, regionID, cycle.GetCycleId()) + if len(entries) != 0 { + log.Fatalf("disabled cycle should ignore gift events, got leaderboard %+v", entries) + } + summary.Diagnostics["disabled_cycle"] = cycle.GetCycleId() + pass(summary, "disabled_cycle_ignored") +} + +func runIdempotentAndBeforeStartIgnored(ctx context.Context, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + regionID := cfg.regionID + 65 + gifts := giftsFor("idempotency", runID) + cycle := createCycle(ctx, clients.admin, cfg, regionID, fmt.Sprintf("%s idempotency %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, gifts) + + // A gift before the configured UTC window must not create score, even when the + // gift id is configured; otherwise delayed or wrongly timestamped events can + // leak into the leaderboard before the cycle actually begins. + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "before-start"), 750001, gifts[0], 900, window.startMS-1, 1) + // The first in-window event should score once, and the second delivery with + // the same source_event_id must be treated as replay protection rather than a + // second gift spend. This is the room event idempotency boundary that protects + // RocketMQ retries and local replay tooling. + duplicateID := eventID(runID, "duplicate-score") + consumeGiftEvent(ctx, clients.room, cfg, regionID, duplicateID, 750002, gifts[0], 600, window.startMS+1_000, 2) + consumeGiftEvent(ctx, clients.room, cfg, regionID, duplicateID, 750002, gifts[0], 600, window.startMS+1_000, 2) + + entries := listLeaderboard(ctx, clients.admin, cfg, regionID, cycle.GetCycleId()) + assertLeaderboardExact("idempotency", entries, []leaderboardEntry{{RankNo: 1, UserID: 750002, Score: 600}}) + summary.Diagnostics["idempotency_cycle"] = cycle.GetCycleId() + pass(summary, "before_start_ignored_and_duplicate_event_id_idempotent") +} + +func runTop3SettlementGrantsOnlyWinners(ctx context.Context, activityDB *sql.DB, walletDB *sql.DB, clients clients, cfg config, runID string, summary *boundarySummary) { + window := currentWindow() + regionID := cfg.regionID + 68 + gifts := giftsFor("top3_settlement", runID) + cycle := createCycle(ctx, clients.admin, cfg, regionID, fmt.Sprintf("%s top3 settlement %s", boundaryTitlePrefix, runID), statusActive, window.startMS, window.endMS, gifts) + + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "settle-top1"), 760001, gifts[0], 1_200, window.startMS+1_000, 1) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "settle-top2"), 760002, gifts[0], 900, window.startMS+2_000, 2) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "settle-top3"), 760003, gifts[0], 800, window.startMS+3_000, 3) + consumeGiftEvent(ctx, clients.room, cfg, regionID, eventID(runID, "settle-fourth"), 760004, gifts[0], 700, window.startMS+4_000, 4) + + // The test first records score in a normal current cycle, then moves only this + // cycle's end time into the past. This keeps the event-consume path realistic + // while making settlement deterministic without waiting for wall-clock time. + nowMS := time.Now().UTC().UnixMilli() + mustExec(ctx, activityDB, ` + UPDATE weekly_star_cycles + SET end_ms = ?, updated_at_ms = ? + WHERE app_code = ? AND cycle_id = ?`, + nowMS-1, nowMS, cfg.appCode, cycle.GetCycleId(), + ) + triggerWeeklyStarSettlementUntilSettled(ctx, activityDB, clients, cfg, runID, "top3-settlement", cycle.GetCycleId()) + + rows := settlementRowsFromDB(ctx, activityDB, cfg, cycle.GetCycleId()) + expected := []settlementRow{ + {RankNo: 1, UserID: 760001, Score: 1_200, ResourceGroupID: rewardGroupTop1ID, Status: "granted"}, + {RankNo: 2, UserID: 760002, Score: 900, ResourceGroupID: rewardGroupTop2ID, Status: "granted"}, + {RankNo: 3, UserID: 760003, Score: 800, ResourceGroupID: rewardGroupTop3ID, Status: "granted"}, + } + if len(rows) != len(expected) { + log.Fatalf("top3 settlement row count mismatch: got %d want %d rows=%+v", len(rows), len(expected), rows) + } + for index, want := range expected { + if rows[index] != want { + log.Fatalf("top3 settlement row %d mismatch: got %+v want %+v", index+1, rows[index], want) + } + } + if countWalletGrantsForCycle(ctx, walletDB, cfg, cycle.GetCycleId()) != 3 { + log.Fatalf("top3 settlement should create exactly 3 wallet grants") + } + summary.Diagnostics["top3_settlement_cycle"] = cycle.GetCycleId() + pass(summary, "settlement_grants_top3_only") +} + +func runNoScoreSettlement(ctx context.Context, activityDB *sql.DB, walletDB *sql.DB, clients clients, cfg config, runID string, summary *boundarySummary) { + // Use an old, isolated window and a single-cycle batch so this boundary check + // settles the cycle created by this run instead of depending on whatever old + // local test data may already be due in the developer database. + oldWindowStart := time.Date(2001, time.January, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(time.Now().UTC().UnixNano()%int64(time.Hour)) * time.Nanosecond) + oldWindowEnd := oldWindowStart.Add(time.Hour) + regionID := cfg.regionID + 70 + gifts := giftsFor("no_score", runID) + cycle := createCycle(ctx, clients.admin, cfg, regionID, fmt.Sprintf("%s no score settlement %s", boundaryTitlePrefix, runID), statusActive, oldWindowStart.UnixMilli(), oldWindowEnd.UnixMilli(), gifts) + + resp, err := clients.cron.ProcessWeeklyStarSettlementBatch(ctx, &activityv1.CronBatchRequest{ + Meta: meta(cfg, "weekly-star-boundary-cron-"+runID), + RunId: "weekly-star-boundary-" + runID, + WorkerId: "weekly-star-boundary", + BatchSize: 1, + LockTtlMs: int64((30 * time.Second) / time.Millisecond), + }) + if err != nil { + log.Fatalf("no-score settlement cron failed: %v", err) + } + if resp.GetClaimedCount() != 1 || resp.GetProcessedCount() != 1 || resp.GetSuccessCount() != 1 || resp.GetFailureCount() != 0 { + log.Fatalf("unexpected no-score cron result: claimed=%d processed=%d success=%d failure=%d", resp.GetClaimedCount(), resp.GetProcessedCount(), resp.GetSuccessCount(), resp.GetFailureCount()) + } + statusValue := cycleStatusFromDB(ctx, activityDB, cfg, cycle.GetCycleId()) + if statusValue != "settled" { + log.Fatalf("no-score cycle should be settled, got %s", statusValue) + } + if countSettlements(ctx, activityDB, cfg, cycle.GetCycleId()) != 0 { + log.Fatalf("no-score cycle should not create settlement rows") + } + if countWalletGrantsForCycle(ctx, walletDB, cfg, cycle.GetCycleId()) != 0 { + log.Fatalf("no-score cycle should not create wallet grants") + } + summary.Diagnostics["no_score_cycle"] = cycle.GetCycleId() + pass(summary, "no_score_cycle_settles_without_grants") +} + +func triggerWeeklyStarSettlementUntilSettled(ctx context.Context, db *sql.DB, clients clients, cfg config, runID string, label string, cycleID string) { + for attempt := 1; attempt <= 8; attempt++ { + _, err := clients.cron.ProcessWeeklyStarSettlementBatch(ctx, &activityv1.CronBatchRequest{ + Meta: meta(cfg, fmt.Sprintf("weekly-star-boundary-cron-%s-%s-%d", label, runID, attempt)), + RunId: fmt.Sprintf("weekly-star-boundary-%s-%s-%d", label, runID, attempt), + WorkerId: "weekly-star-boundary", + BatchSize: 5, + LockTtlMs: int64((30 * time.Second) / time.Millisecond), + }) + if err != nil { + log.Fatalf("trigger weekly star settlement %s: %v", cycleID, err) + } + if cycleStatusFromDB(ctx, db, cfg, cycleID) == "settled" { + return + } + time.Sleep(200 * time.Millisecond) + } + log.Fatalf("cycle %s was not settled after repeated cron attempts", cycleID) +} + +func currentWindow() cycleWindow { + now := time.Now().UTC() + return cycleWindow{startMS: now.Add(-2 * time.Minute).UnixMilli(), endMS: now.Add(30 * time.Minute).UnixMilli()} +} + +func giftsFor(label string, runID string) []string { + return []string{ + "weekly_star_boundary_" + label + "_a_" + runID, + "weekly_star_boundary_" + label + "_b_" + runID, + "weekly_star_boundary_" + label + "_c_" + runID, + } +} + +func cleanupBoundaryCycles(ctx context.Context, db *sql.DB, cfg config) { + mustExec(ctx, db, ` + UPDATE weekly_star_cycles + SET status = 'disabled', locked_by = '', lock_until_ms = 0, updated_at_ms = ? + WHERE app_code = ? AND activity_code = ? AND title LIKE ? AND status IN ('active', 'settling')`, + time.Now().UTC().UnixMilli(), cfg.appCode, activityCode, boundaryTitlePrefix+"%", + ) +} + +func seedWalletRewardGroups(ctx context.Context, db *sql.DB, appCode string) { + nowMS := time.Now().UTC().UnixMilli() + type groupSeed struct { + groupID int64 + code string + name string + coins int64 + } + seeds := []groupSeed{ + {groupID: rewardGroupTop1ID, code: "weekly_star_local_top1", name: "Weekly Star Local Top 1", coins: 111_000}, + {groupID: rewardGroupTop2ID, code: "weekly_star_local_top2", name: "Weekly Star Local Top 2", coins: 66_000}, + {groupID: rewardGroupTop3ID, code: "weekly_star_local_top3", name: "Weekly Star Local Top 3", coins: 33_000}, + } + for index, seed := range seeds { + mustExec(ctx, db, ` + INSERT INTO resource_groups ( + app_code, group_id, group_code, name, status, description, sort_order, + created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, 'active', 'local weekly star boundary reward group', ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + name = VALUES(name), status = 'active', description = VALUES(description), + sort_order = VALUES(sort_order), updated_by_user_id = VALUES(updated_by_user_id), updated_at_ms = VALUES(updated_at_ms)`, + appCode, seed.groupID, seed.code, seed.name, index+1, testOperatorAdmin, testOperatorAdmin, nowMS, nowMS, + ) + mustExec(ctx, db, ` + INSERT INTO resource_group_items ( + app_code, group_id, item_type, resource_id, wallet_asset_type, wallet_asset_amount, + quantity, duration_ms, sort_order, created_at_ms, updated_at_ms + ) VALUES (?, ?, 'wallet_asset', 0, 'COIN', ?, 1, 0, 1, ?, ?) + ON DUPLICATE KEY UPDATE + wallet_asset_amount = VALUES(wallet_asset_amount), sort_order = VALUES(sort_order), updated_at_ms = VALUES(updated_at_ms)`, + appCode, seed.groupID, seed.coins, nowMS, nowMS, + ) + } +} + +func createCycle(ctx context.Context, client activityv1.AdminWeeklyStarServiceClient, cfg config, regionID int64, title string, cycleStatus string, startMS int64, endMS int64, gifts []string) *activityv1.WeeklyStarCycle { + cycle, err := createCycleAllowError(ctx, client, cfg, regionID, title, cycleStatus, startMS, endMS, gifts) + if err != nil { + log.Fatalf("create cycle %q: %v", title, err) + } + return cycle +} + +func createCycleAllowError(ctx context.Context, client activityv1.AdminWeeklyStarServiceClient, cfg config, regionID int64, title string, cycleStatus string, startMS int64, endMS int64, gifts []string) (*activityv1.WeeklyStarCycle, error) { + return createCycleWithRewardsAllowError(ctx, client, cfg, regionID, title, cycleStatus, startMS, endMS, gifts, []*activityv1.WeeklyStarReward{ + {RankNo: 1, ResourceGroupId: rewardGroupTop1ID}, + {RankNo: 2, ResourceGroupId: rewardGroupTop2ID}, + {RankNo: 3, ResourceGroupId: rewardGroupTop3ID}, + }) +} + +func createCycleWithRewardsAllowError(ctx context.Context, client activityv1.AdminWeeklyStarServiceClient, cfg config, regionID int64, title string, cycleStatus string, startMS int64, endMS int64, gifts []string, rewards []*activityv1.WeeklyStarReward) (*activityv1.WeeklyStarCycle, error) { + giftItems := make([]*activityv1.WeeklyStarGift, 0, len(gifts)) + for index, gift := range gifts { + giftItems = append(giftItems, &activityv1.WeeklyStarGift{GiftId: gift, SortOrder: int32(index + 1)}) + } + resp, err := client.CreateWeeklyStarCycle(ctx, &activityv1.UpsertWeeklyStarCycleRequest{ + Meta: meta(cfg, "weekly-star-boundary-create-"+sanitize(title)), + Cycle: &activityv1.WeeklyStarCycle{ + ActivityCode: activityCode, + RegionId: regionID, + Title: title, + Status: cycleStatus, + StartMs: startMS, + EndMs: endMS, + Gifts: giftItems, + Rewards: rewards, + }, + OperatorAdminId: testOperatorAdmin, + }) + if err != nil { + return nil, err + } + if resp.GetCycle().GetCycleId() == "" { + return nil, fmt.Errorf("empty cycle_id") + } + return resp.GetCycle(), nil +} + +func consumeGiftEvent(ctx context.Context, client activityv1.RoomEventConsumerServiceClient, cfg config, regionID int64, eventID string, userID int64, giftID string, score int64, occurredAtMS int64, version int64) { + body, err := proto.Marshal(&roomeventsv1.RoomGiftSent{ + SenderUserId: userID, + TargetUserId: testTargetAnchorID, + GiftId: giftID, + GiftCount: 1, + GiftValue: 1, + BillingReceiptId: eventID + "-receipt", + VisibleRegionId: regionID, + CommandId: eventID + "-command", + CoinSpent: score, + }) + if err != nil { + log.Fatalf("marshal room gift sent: %v", err) + } + _, err = client.ConsumeRoomEvent(ctx, &activityv1.ConsumeRoomEventRequest{ + Meta: meta(cfg, "weekly-star-boundary-event-"+eventID), + Envelope: &roomeventsv1.EventEnvelope{ + EventId: eventID, + RoomId: localBoundaryRoomID, + EventType: "RoomGiftSent", + RoomVersion: version, + OccurredAtMs: occurredAtMS, + Body: body, + AppCode: cfg.appCode, + }, + }) + if err != nil { + log.Fatalf("consume room gift event %s: %v", eventID, err) + } +} + +func getCurrent(ctx context.Context, client activityv1.WeeklyStarServiceClient, cfg config, regionID int64, userID int64) *activityv1.GetWeeklyStarCurrentResponse { + resp, err := client.GetWeeklyStarCurrent(ctx, &activityv1.GetWeeklyStarCurrentRequest{ + Meta: meta(cfg, "weekly-star-boundary-current-"+fmt.Sprint(regionID)), + UserId: userID, + RegionId: regionID, + }) + if err != nil { + log.Fatalf("get current region %d: %v", regionID, err) + } + if resp.GetCycle() == nil { + log.Fatalf("get current region %d returned nil cycle", regionID) + } + return resp +} + +func listLeaderboard(ctx context.Context, client activityv1.AdminWeeklyStarServiceClient, cfg config, regionID int64, cycleID string) []leaderboardEntry { + resp, err := client.ListWeeklyStarLeaderboard(ctx, &activityv1.ListWeeklyStarLeaderboardRequest{ + Meta: meta(cfg, "weekly-star-boundary-leaderboard-"+cycleID), + CycleId: cycleID, + RegionId: regionID, + PageSize: 10, + }) + if err != nil { + log.Fatalf("list leaderboard %s: %v", cycleID, err) + } + out := make([]leaderboardEntry, 0, len(resp.GetEntries())) + for _, entry := range resp.GetEntries() { + out = append(out, leaderboardEntry{RankNo: entry.GetRankNo(), UserID: entry.GetUserId(), Score: entry.GetScore()}) + } + return out +} + +func assertLeaderboardExact(label string, actual []leaderboardEntry, expected []leaderboardEntry) { + if len(actual) != len(expected) { + log.Fatalf("%s leaderboard length mismatch: got %d want %d entries=%+v", label, len(actual), len(expected), actual) + } + for index, want := range expected { + if actual[index] != want { + log.Fatalf("%s leaderboard row %d mismatch: got %+v want %+v", label, index+1, actual[index], want) + } + } +} + +func currentCycleIDFromDB(ctx context.Context, db *sql.DB, cfg config, regionID int64, nowMS int64) (string, bool) { + var cycleID string + err := db.QueryRowContext(ctx, ` + SELECT cycle_id + FROM weekly_star_cycles + WHERE app_code = ? AND activity_code = ? AND region_id = ? AND status = 'active' AND start_ms <= ? AND end_ms > ? + ORDER BY start_ms DESC, updated_at_ms DESC + LIMIT 1`, cfg.appCode, activityCode, regionID, nowMS, nowMS, + ).Scan(&cycleID) + if err == sql.ErrNoRows { + return "", false + } + if err != nil { + log.Fatalf("query current cycle from db: %v", err) + } + return cycleID, true +} + +func cycleStatusFromDB(ctx context.Context, db *sql.DB, cfg config, cycleID string) string { + var statusValue string + err := db.QueryRowContext(ctx, ` + SELECT status FROM weekly_star_cycles WHERE app_code = ? AND cycle_id = ?`, cfg.appCode, cycleID, + ).Scan(&statusValue) + if err != nil { + log.Fatalf("query cycle status %s: %v", cycleID, err) + } + return statusValue +} + +func countSettlements(ctx context.Context, db *sql.DB, cfg config, cycleID string) int { + var count int + if err := db.QueryRowContext(ctx, ` + SELECT COUNT(*) FROM weekly_star_settlements WHERE app_code = ? AND cycle_id = ?`, cfg.appCode, cycleID, + ).Scan(&count); err != nil { + log.Fatalf("count settlements: %v", err) + } + return count +} + +func countWalletGrantsForCycle(ctx context.Context, db *sql.DB, cfg config, cycleID string) int { + var count int + if err := db.QueryRowContext(ctx, ` + SELECT COUNT(*) FROM resource_grants WHERE app_code = ? AND command_id LIKE ?`, cfg.appCode, "weekly_star:"+cycleID+":%", + ).Scan(&count); err != nil { + log.Fatalf("count wallet grants: %v", err) + } + return count +} + +func settlementRowsFromDB(ctx context.Context, db *sql.DB, cfg config, cycleID string) []settlementRow { + rows, err := db.QueryContext(ctx, ` + SELECT rank_no, user_id, score, resource_group_id, status + FROM weekly_star_settlements + WHERE app_code = ? AND cycle_id = ? + ORDER BY rank_no ASC`, cfg.appCode, cycleID, + ) + if err != nil { + log.Fatalf("query settlement rows: %v", err) + } + defer rows.Close() + out := make([]settlementRow, 0, 3) + for rows.Next() { + var row settlementRow + if err := rows.Scan(&row.RankNo, &row.UserID, &row.Score, &row.ResourceGroupID, &row.Status); err != nil { + log.Fatalf("scan settlement row: %v", err) + } + out = append(out, row) + } + if err := rows.Err(); err != nil { + log.Fatalf("iterate settlement rows: %v", err) + } + return out +} + +func mustOpenDB(ctx context.Context, dsn string) *sql.DB { + db, err := sql.Open("mysql", dsn) + if err != nil { + log.Fatalf("open mysql: %v", err) + } + if err := db.PingContext(ctx); err != nil { + _ = db.Close() + log.Fatalf("ping mysql: %v", err) + } + return db +} + +func mustExec(ctx context.Context, db *sql.DB, query string, args ...any) { + if _, err := db.ExecContext(ctx, query, args...); err != nil { + log.Fatalf("exec mysql setup: %v", err) + } +} + +func meta(cfg config, requestID string) *activityv1.RequestMeta { + return &activityv1.RequestMeta{ + RequestId: requestID, + Caller: "weekly-star-local-boundary", + SentAtMs: time.Now().UTC().UnixMilli(), + AppCode: cfg.appCode, + } +} + +func eventID(runID string, label string) string { + return "weekly-star-boundary-" + runID + "-" + label +} + +func sanitize(value string) string { + return strings.NewReplacer(" ", "-", ":", "-", "/", "-").Replace(value) +} + +func pass(summary *boundarySummary, name string) { + summary.Passed = append(summary.Passed, name) + sort.Strings(summary.Passed) +} diff --git a/tools/weekly-star-local-flow/main.go b/tools/weekly-star-local-flow/main.go new file mode 100644 index 00000000..04bb1b19 --- /dev/null +++ b/tools/weekly-star-local-flow/main.go @@ -0,0 +1,473 @@ +package main + +import ( + "context" + "database/sql" + "encoding/json" + "flag" + "fmt" + "log" + "os" + "sort" + "strings" + "time" + + _ "github.com/go-sql-driver/mysql" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/proto" + activityv1 "hyapp.local/api/proto/activity/v1" + roomeventsv1 "hyapp.local/api/proto/events/room/v1" +) + +const ( + defaultAppCode = "lalu" + localTitlePrefix = "Local Weekly Star Flow" + activityCode = "weekly_star" + cycleStatusActive = "active" + rewardGroupTop1ID = int64(930001) + rewardGroupTop2ID = int64(930002) + rewardGroupTop3ID = int64(930003) + testOperatorAdmin = int64(900001) + testTargetAnchorID = int64(990001) +) + +type flowConfig struct { + activityAddr string + activityDSN string + walletDSN string + appCode string + regionID int64 +} + +type flowSummary struct { + RunID string `json:"run_id"` + RegionID int64 `json:"region_id"` + FinishedCycleID string `json:"finished_cycle_id"` + CurrentCycleID string `json:"current_cycle_id"` + SentEvents []string `json:"sent_events"` + Leaderboard []leaderboardSummary `json:"leaderboard"` + Cron cronSummary `json:"cron"` + Settlements []settlementSummary `json:"settlements"` + WalletGrants []walletGrantSummary `json:"wallet_grants"` + CurrentCycleAfter string `json:"current_cycle_after"` + ExpectedAssertions map[string]string `json:"expected_assertions"` +} + +type leaderboardSummary struct { + RankNo int32 `json:"rank_no"` + UserID int64 `json:"user_id"` + Score int64 `json:"score"` +} + +type cronSummary struct { + Claimed int32 `json:"claimed"` + Processed int32 `json:"processed"` + Success int32 `json:"success"` + Failure int32 `json:"failure"` +} + +type settlementSummary struct { + RankNo int32 `json:"rank_no"` + UserID int64 `json:"user_id"` + Score int64 `json:"score"` + ResourceGroupID int64 `json:"resource_group_id"` + WalletCommandID string `json:"wallet_command_id"` + WalletGrantID string `json:"wallet_grant_id"` + Status string `json:"status"` +} + +type walletGrantSummary struct { + CommandID string `json:"command_id"` + GrantID string `json:"grant_id"` + TargetUserID int64 `json:"target_user_id"` + GrantSubjectID string `json:"grant_subject_id"` + Status string `json:"status"` +} + +func main() { + cfg := flowConfig{} + flag.StringVar(&cfg.activityAddr, "activity-addr", "127.0.0.1:13006", "activity-service gRPC address") + flag.StringVar(&cfg.activityDSN, "activity-dsn", "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC", "activity MySQL DSN") + flag.StringVar(&cfg.walletDSN, "wallet-dsn", "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_wallet?parseTime=true&charset=utf8mb4&loc=UTC", "wallet MySQL DSN") + flag.StringVar(&cfg.appCode, "app-code", defaultAppCode, "app code") + flag.Int64Var(&cfg.regionID, "region-id", 990686, "local test region id") + flag.Parse() + if strings.TrimSpace(cfg.appCode) == "" { + log.Fatal("app-code is required") + } + + ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second) + defer cancel() + + // activityDB 只用于准备和交叉验证测试夹具,不能直接写积分、settlement 或周期结果。 + // 真正的周期创建、送礼消费、榜单读取和结算都必须走 activity-service gRPC,才能覆盖线上同一套业务逻辑。 + activityDB := mustOpenDB(ctx, cfg.activityDSN) + defer activityDB.Close() + // walletDB 只用于准备本地资源组和最终审计 wallet grant;奖励发放必须通过 wallet-service GrantResourceGroup。 + walletDB := mustOpenDB(ctx, cfg.walletDSN) + defer walletDB.Close() + + // 只清理本工具创建的本地区域未完成周期,避免旧失败周期被本轮 cron 一起重试。 + mustExec(ctx, activityDB, ` + UPDATE weekly_star_cycles + SET status = 'disabled', locked_by = '', lock_until_ms = 0, updated_at_ms = ? + WHERE app_code = ? AND activity_code = ? AND region_id = ? AND title LIKE ? AND status IN ('active', 'settling')`, + time.Now().UTC().UnixMilli(), cfg.appCode, activityCode, cfg.regionID, localTitlePrefix+"%", + ) + // 奖励资源组由 wallet-service 拥有;这里仅准备本地验证用的 COIN 资源组配置。 + seedWalletRewardGroups(ctx, walletDB, cfg.appCode) + + conn, err := grpc.DialContext(ctx, cfg.activityAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) + if err != nil { + log.Fatalf("dial activity-service: %v", err) + } + defer conn.Close() + + adminClient := activityv1.NewAdminWeeklyStarServiceClient(conn) + appClient := activityv1.NewWeeklyStarServiceClient(conn) + roomClient := activityv1.NewRoomEventConsumerServiceClient(conn) + cronClient := activityv1.NewActivityCronServiceClient(conn) + + runID := fmt.Sprintf("%d", time.Now().UTC().UnixMilli()) + now := time.Now().UTC() + // 第一个周期刻意设置为已经结束,事件 occurred_at_ms 落在它的 [start,end) 内,cron 可以立即结算。 + // 第二个周期从第一个周期 end_ms 开始覆盖当前时间,用来验证旧周期结算后 current 自动进入下一周期。 + finishedStart := now.Add(-10 * time.Minute).UnixMilli() + finishedEnd := now.Add(-1 * time.Minute).UnixMilli() + currentStart := finishedEnd + currentEnd := now.Add(50 * time.Minute).UnixMilli() + gifts := []string{"weekly_star_flow_gift_a", "weekly_star_flow_gift_b", "weekly_star_flow_gift_c"} + + // 后台配置两个不重叠周期:第一个已经到期用于结算,第二个覆盖当前时间用于验证自动进入下一周期。 + finishedCycle := createCycle(ctx, adminClient, cfg, fmt.Sprintf("%s finished %s", localTitlePrefix, runID), finishedStart, finishedEnd, gifts) + currentCycle := createCycle(ctx, adminClient, cfg, fmt.Sprintf("%s current %s", localTitlePrefix, runID), currentStart, currentEnd, gifts) + + eventTime := finishedEnd - 10_000 + events := []struct { + userID int64 + giftID string + score int64 + }{ + {userID: 701001, giftID: gifts[0], score: 5_000}, + {userID: 701002, giftID: gifts[1], score: 8_000}, + {userID: 701003, giftID: gifts[2], score: 6_500}, + } + sentEventIDs := make([]string, 0, len(events)+1) + for index, event := range events { + eventID := fmt.Sprintf("weekly-star-flow-%s-%d", runID, index+1) + // consumeGiftEvent 调的是 RoomEventConsumerService,不直接调用 weekly-star service。 + // 这样可以同时验证 activity-service 的房间事件 fanout wiring 是否把周星模块挂上。 + consumeGiftEvent(ctx, roomClient, cfg, eventID, event.userID, event.giftID, event.score, eventTime, int64(index+1)) + sentEventIDs = append(sentEventIDs, eventID) + } + // 同一个 source_event_id 再投一次,验证 weekly_star_score_events 的幂等约束不会让榜单重复加分。 + consumeGiftEvent(ctx, roomClient, cfg, sentEventIDs[0], events[0].userID, events[0].giftID, events[0].score, eventTime, 99) + sentEventIDs = append(sentEventIDs, sentEventIDs[0]+" (duplicate)") + + leaderboard := listLeaderboard(ctx, adminClient, cfg, finishedCycle.GetCycleId()) + // 先查榜再结算,验证积分累计发生在送礼事件消费阶段,而不是结算时临时计算。 + assertLeaderboard(leaderboard, []leaderboardSummary{ + {RankNo: 1, UserID: 701002, Score: 8_000}, + {RankNo: 2, UserID: 701003, Score: 6_500}, + {RankNo: 3, UserID: 701001, Score: 5_000}, + }) + + cronResp, err := cronClient.ProcessWeeklyStarSettlementBatch(ctx, &activityv1.CronBatchRequest{ + Meta: meta(cfg, "weekly-star-local-flow-cron-"+runID), + RunId: "weekly-star-local-flow-" + runID, + WorkerId: "weekly-star-local-flow", + BatchSize: 10, + LockTtlMs: int64((30 * time.Second) / time.Millisecond), + }) + if err != nil { + log.Fatalf("process weekly star settlement: %v", err) + } + if cronResp.GetClaimedCount() != 1 || cronResp.GetProcessedCount() != 3 || cronResp.GetSuccessCount() != 3 || cronResp.GetFailureCount() != 0 { + log.Fatalf("unexpected settlement cron result: claimed=%d processed=%d success=%d failure=%d", cronResp.GetClaimedCount(), cronResp.GetProcessedCount(), cronResp.GetSuccessCount(), cronResp.GetFailureCount()) + } + + settlements := listSettlements(ctx, adminClient, cfg, finishedCycle.GetCycleId()) + assertSettlements(settlements) + // activity 侧 granted 只能说明它收到了 wallet 返回;这里再查 wallet.resource_grants,确认奖励确实落到 wallet owner 库。 + grants := listWalletGrants(ctx, walletDB, cfg.appCode, walletCommandIDs(settlements)) + if len(grants) != 3 { + log.Fatalf("expected 3 wallet grants, got %d", len(grants)) + } + + currentResp, err := appClient.GetWeeklyStarCurrent(ctx, &activityv1.GetWeeklyStarCurrentRequest{ + Meta: meta(cfg, "weekly-star-local-flow-current-"+runID), + UserId: 701001, + RegionId: cfg.regionID, + }) + if err != nil { + log.Fatalf("get current weekly star cycle: %v", err) + } + if currentResp.GetCycle().GetCycleId() != currentCycle.GetCycleId() { + log.Fatalf("expected current cycle %s, got %s", currentCycle.GetCycleId(), currentResp.GetCycle().GetCycleId()) + } + + summary := flowSummary{ + RunID: runID, + RegionID: cfg.regionID, + FinishedCycleID: finishedCycle.GetCycleId(), + CurrentCycleID: currentCycle.GetCycleId(), + SentEvents: sentEventIDs, + Leaderboard: leaderboard, + Cron: cronSummary{ + Claimed: cronResp.GetClaimedCount(), + Processed: cronResp.GetProcessedCount(), + Success: cronResp.GetSuccessCount(), + Failure: cronResp.GetFailureCount(), + }, + Settlements: settlements, + WalletGrants: grants, + CurrentCycleAfter: currentResp.GetCycle().GetCycleId(), + ExpectedAssertions: map[string]string{ + "multiple_cycles": "created one ended active cycle and one current active cycle in UTC epoch ms", + "gift_score": "configured gift score equals RoomGiftSent.coin_spent", + "duplicate_event": "same source_event_id did not double-count leaderboard score", + "settlement": "ended cycle settled top1/top2/top3 through wallet GrantResourceGroup", + "next_cycle": "GetWeeklyStarCurrent returned the second cycle after settlement", + }, + } + encoder := json.NewEncoder(os.Stdout) + encoder.SetIndent("", " ") + if err := encoder.Encode(summary); err != nil { + log.Fatalf("encode summary: %v", err) + } +} + +func mustOpenDB(ctx context.Context, dsn string) *sql.DB { + db, err := sql.Open("mysql", dsn) + if err != nil { + log.Fatalf("open mysql: %v", err) + } + if err := db.PingContext(ctx); err != nil { + _ = db.Close() + log.Fatalf("ping mysql: %v", err) + } + return db +} + +func mustExec(ctx context.Context, db *sql.DB, query string, args ...any) { + if _, err := db.ExecContext(ctx, query, args...); err != nil { + log.Fatalf("exec mysql setup: %v", err) + } +} + +func seedWalletRewardGroups(ctx context.Context, db *sql.DB, appCode string) { + nowMS := time.Now().UTC().UnixMilli() + type groupSeed struct { + groupID int64 + code string + name string + coins int64 + } + seeds := []groupSeed{ + {groupID: rewardGroupTop1ID, code: "weekly_star_local_top1", name: "Weekly Star Local Top 1", coins: 111_000}, + {groupID: rewardGroupTop2ID, code: "weekly_star_local_top2", name: "Weekly Star Local Top 2", coins: 66_000}, + {groupID: rewardGroupTop3ID, code: "weekly_star_local_top3", name: "Weekly Star Local Top 3", coins: 33_000}, + } + for index, seed := range seeds { + // 固定 group_id 让 activity 周期配置可以稳定引用;ON DUPLICATE 只更新本工具自己的本地资源组。 + mustExec(ctx, db, ` + INSERT INTO resource_groups ( + app_code, group_id, group_code, name, status, description, sort_order, + created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms + ) VALUES (?, ?, ?, ?, 'active', 'local weekly star flow reward group', ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + name = VALUES(name), status = 'active', description = VALUES(description), + sort_order = VALUES(sort_order), updated_by_user_id = VALUES(updated_by_user_id), updated_at_ms = VALUES(updated_at_ms)`, + appCode, seed.groupID, seed.code, seed.name, index+1, testOperatorAdmin, testOperatorAdmin, nowMS, nowMS, + ) + mustExec(ctx, db, ` + -- wallet_asset item 不依赖具体 resource_id,wallet-service 会把它原子展开成 COIN 入账和 resource_grant_item。 + INSERT INTO resource_group_items ( + app_code, group_id, item_type, resource_id, wallet_asset_type, wallet_asset_amount, + quantity, duration_ms, sort_order, created_at_ms, updated_at_ms + ) VALUES (?, ?, 'wallet_asset', 0, 'COIN', ?, 1, 0, 1, ?, ?) + ON DUPLICATE KEY UPDATE + wallet_asset_amount = VALUES(wallet_asset_amount), sort_order = VALUES(sort_order), updated_at_ms = VALUES(updated_at_ms)`, + appCode, seed.groupID, seed.coins, nowMS, nowMS, + ) + } +} + +func createCycle(ctx context.Context, client activityv1.AdminWeeklyStarServiceClient, cfg flowConfig, title string, startMS int64, endMS int64, gifts []string) *activityv1.WeeklyStarCycle { + resp, err := client.CreateWeeklyStarCycle(ctx, &activityv1.UpsertWeeklyStarCycleRequest{ + Meta: meta(cfg, "weekly-star-local-flow-create-"+strings.ReplaceAll(title, " ", "-")), + Cycle: &activityv1.WeeklyStarCycle{ + ActivityCode: activityCode, + RegionId: cfg.regionID, + Title: title, + Status: cycleStatusActive, + StartMs: startMS, + EndMs: endMS, + Gifts: []*activityv1.WeeklyStarGift{ + {GiftId: gifts[0], SortOrder: 1}, + {GiftId: gifts[1], SortOrder: 2}, + {GiftId: gifts[2], SortOrder: 3}, + }, + Rewards: []*activityv1.WeeklyStarReward{ + {RankNo: 1, ResourceGroupId: rewardGroupTop1ID}, + {RankNo: 2, ResourceGroupId: rewardGroupTop2ID}, + {RankNo: 3, ResourceGroupId: rewardGroupTop3ID}, + }, + }, + OperatorAdminId: testOperatorAdmin, + }) + if err != nil { + log.Fatalf("create cycle %q: %v", title, err) + } + if resp.GetCycle().GetCycleId() == "" { + log.Fatalf("create cycle %q returned empty cycle_id", title) + } + return resp.GetCycle() +} + +func consumeGiftEvent(ctx context.Context, client activityv1.RoomEventConsumerServiceClient, cfg flowConfig, eventID string, userID int64, giftID string, score int64, occurredAtMS int64, version int64) { + body, err := proto.Marshal(&roomeventsv1.RoomGiftSent{ + SenderUserId: userID, + TargetUserId: testTargetAnchorID, + GiftId: giftID, + GiftCount: 1, + GiftValue: 1, + BillingReceiptId: eventID + "-receipt", + VisibleRegionId: cfg.regionID, + CommandId: eventID + "-command", + CoinSpent: score, + }) + if err != nil { + log.Fatalf("marshal room gift sent: %v", err) + } + _, err = client.ConsumeRoomEvent(ctx, &activityv1.ConsumeRoomEventRequest{ + Meta: meta(cfg, "weekly-star-local-flow-event-"+eventID), + Envelope: &roomeventsv1.EventEnvelope{ + EventId: eventID, + RoomId: "weekly-star-local-room", + EventType: "RoomGiftSent", + RoomVersion: version, + OccurredAtMs: occurredAtMS, + Body: body, + AppCode: cfg.appCode, + }, + }) + if err != nil { + log.Fatalf("consume room gift event %s: %v", eventID, err) + } +} + +func listLeaderboard(ctx context.Context, client activityv1.AdminWeeklyStarServiceClient, cfg flowConfig, cycleID string) []leaderboardSummary { + resp, err := client.ListWeeklyStarLeaderboard(ctx, &activityv1.ListWeeklyStarLeaderboardRequest{ + Meta: meta(cfg, "weekly-star-local-flow-leaderboard-"+cycleID), + CycleId: cycleID, + RegionId: cfg.regionID, + PageSize: 10, + }) + if err != nil { + log.Fatalf("list leaderboard: %v", err) + } + out := make([]leaderboardSummary, 0, len(resp.GetEntries())) + for _, entry := range resp.GetEntries() { + out = append(out, leaderboardSummary{RankNo: entry.GetRankNo(), UserID: entry.GetUserId(), Score: entry.GetScore()}) + } + return out +} + +func assertLeaderboard(actual []leaderboardSummary, expected []leaderboardSummary) { + if len(actual) < len(expected) { + log.Fatalf("expected at least %d leaderboard entries, got %d", len(expected), len(actual)) + } + for index, want := range expected { + got := actual[index] + if got != want { + log.Fatalf("leaderboard row %d mismatch: got %+v want %+v", index+1, got, want) + } + } +} + +func listSettlements(ctx context.Context, client activityv1.AdminWeeklyStarServiceClient, cfg flowConfig, cycleID string) []settlementSummary { + resp, err := client.ListWeeklyStarSettlements(ctx, &activityv1.ListWeeklyStarSettlementsRequest{ + Meta: meta(cfg, "weekly-star-local-flow-settlements-"+cycleID), + CycleId: cycleID, + }) + if err != nil { + log.Fatalf("list settlements: %v", err) + } + out := make([]settlementSummary, 0, len(resp.GetSettlements())) + for _, settlement := range resp.GetSettlements() { + out = append(out, settlementSummary{ + RankNo: settlement.GetRankNo(), + UserID: settlement.GetUserId(), + Score: settlement.GetScore(), + ResourceGroupID: settlement.GetResourceGroupId(), + WalletCommandID: settlement.GetWalletCommandId(), + WalletGrantID: settlement.GetWalletGrantId(), + Status: settlement.GetStatus(), + }) + } + sort.Slice(out, func(i, j int) bool { return out[i].RankNo < out[j].RankNo }) + return out +} + +func assertSettlements(items []settlementSummary) { + if len(items) != 3 { + log.Fatalf("expected 3 settlements, got %d", len(items)) + } + for _, item := range items { + if item.Status != "granted" || item.WalletCommandID == "" || item.WalletGrantID == "" { + log.Fatalf("settlement was not granted: %+v", item) + } + } +} + +func walletCommandIDs(items []settlementSummary) []string { + ids := make([]string, 0, len(items)) + for _, item := range items { + ids = append(ids, item.WalletCommandID) + } + return ids +} + +func listWalletGrants(ctx context.Context, db *sql.DB, appCode string, commandIDs []string) []walletGrantSummary { + if len(commandIDs) == 0 { + return nil + } + placeholders := strings.TrimRight(strings.Repeat("?,", len(commandIDs)), ",") + args := make([]any, 0, len(commandIDs)+1) + args = append(args, appCode) + for _, id := range commandIDs { + args = append(args, id) + } + rows, err := db.QueryContext(ctx, ` + SELECT command_id, grant_id, target_user_id, grant_subject_id, status + FROM resource_grants + WHERE app_code = ? AND command_id IN (`+placeholders+`) + ORDER BY target_user_id ASC`, args...) + if err != nil { + log.Fatalf("query wallet grants: %v", err) + } + defer rows.Close() + out := make([]walletGrantSummary, 0, len(commandIDs)) + for rows.Next() { + var item walletGrantSummary + if err := rows.Scan(&item.CommandID, &item.GrantID, &item.TargetUserID, &item.GrantSubjectID, &item.Status); err != nil { + log.Fatalf("scan wallet grant: %v", err) + } + out = append(out, item) + } + if err := rows.Err(); err != nil { + log.Fatalf("iterate wallet grants: %v", err) + } + return out +} + +func meta(cfg flowConfig, requestID string) *activityv1.RequestMeta { + return &activityv1.RequestMeta{ + RequestId: requestID, + Caller: "weekly-star-local-flow", + SentAtMs: time.Now().UTC().UnixMilli(), + AppCode: cfg.appCode, + } +}