幸运礼物

This commit is contained in:
zhx 2026-05-26 03:06:02 +08:00
parent a0ee5962df
commit a68d465272
34 changed files with 1881 additions and 260 deletions

View File

@ -9955,6 +9955,8 @@ type LuckyGiftMeta struct {
CoinSpent int64 `protobuf:"varint,8,opt,name=coin_spent,json=coinSpent,proto3" json:"coin_spent,omitempty"` CoinSpent int64 `protobuf:"varint,8,opt,name=coin_spent,json=coinSpent,proto3" json:"coin_spent,omitempty"`
PaidAtMs int64 `protobuf:"varint,9,opt,name=paid_at_ms,json=paidAtMs,proto3" json:"paid_at_ms,omitempty"` PaidAtMs int64 `protobuf:"varint,9,opt,name=paid_at_ms,json=paidAtMs,proto3" json:"paid_at_ms,omitempty"`
PoolId string `protobuf:"bytes,10,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"` PoolId string `protobuf:"bytes,10,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"`
TargetUserId int64 `protobuf:"varint,11,opt,name=target_user_id,json=targetUserId,proto3" json:"target_user_id,omitempty"`
GiftCount int32 `protobuf:"varint,12,opt,name=gift_count,json=giftCount,proto3" json:"gift_count,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -10059,6 +10061,20 @@ func (x *LuckyGiftMeta) GetPoolId() string {
return "" return ""
} }
func (x *LuckyGiftMeta) GetTargetUserId() int64 {
if x != nil {
return x.TargetUserId
}
return 0
}
func (x *LuckyGiftMeta) GetGiftCount() int32 {
if x != nil {
return x.GiftCount
}
return 0
}
type LuckyGiftTier struct { type LuckyGiftTier struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Pool string `protobuf:"bytes,1,opt,name=pool,proto3" json:"pool,omitempty"` Pool string `protobuf:"bytes,1,opt,name=pool,proto3" json:"pool,omitempty"`
@ -12599,7 +12615,7 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" +
"conditions\"\x8b\x01\n" + "conditions\"\x8b\x01\n" +
"#UpsertAchievementDefinitionResponse\x12J\n" + "#UpsertAchievementDefinitionResponse\x12J\n" +
"\vachievement\x18\x01 \x01(\v2(.hyapp.activity.v1.AchievementDefinitionR\vachievement\x12\x18\n" + "\vachievement\x18\x01 \x01(\v2(.hyapp.activity.v1.AchievementDefinitionR\vachievement\x12\x18\n" +
"\acreated\x18\x02 \x01(\bR\acreated\"\xbd\x02\n" + "\acreated\x18\x02 \x01(\bR\acreated\"\x82\x03\n" +
"\rLuckyGiftMeta\x122\n" + "\rLuckyGiftMeta\x122\n" +
"\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x1d\n" + "\x04meta\x18\x01 \x01(\v2\x1e.hyapp.activity.v1.RequestMetaR\x04meta\x12\x1d\n" +
"\n" + "\n" +
@ -12614,7 +12630,10 @@ const file_proto_activity_v1_activity_proto_rawDesc = "" +
"\n" + "\n" +
"paid_at_ms\x18\t \x01(\x03R\bpaidAtMs\x12\x17\n" + "paid_at_ms\x18\t \x01(\x03R\bpaidAtMs\x12\x17\n" +
"\apool_id\x18\n" + "\apool_id\x18\n" +
" \x01(\tR\x06poolId\"\xe0\x01\n" + " \x01(\tR\x06poolId\x12$\n" +
"\x0etarget_user_id\x18\v \x01(\x03R\ftargetUserId\x12\x1d\n" +
"\n" +
"gift_count\x18\f \x01(\x05R\tgiftCount\"\xe0\x01\n" +
"\rLuckyGiftTier\x12\x12\n" + "\rLuckyGiftTier\x12\x12\n" +
"\x04pool\x18\x01 \x01(\tR\x04pool\x12\x17\n" + "\x04pool\x18\x01 \x01(\tR\x04pool\x12\x17\n" +
"\atier_id\x18\x02 \x01(\tR\x06tierId\x12!\n" + "\atier_id\x18\x02 \x01(\tR\x06tierId\x12!\n" +

View File

@ -1123,6 +1123,8 @@ message LuckyGiftMeta {
int64 coin_spent = 8; int64 coin_spent = 8;
int64 paid_at_ms = 9; int64 paid_at_ms = 9;
string pool_id = 10; string pool_id = 10;
int64 target_user_id = 11;
int32 gift_count = 12;
} }
message LuckyGiftTier { message LuckyGiftTier {

View File

@ -9492,6 +9492,184 @@ func (x *CreditTaskRewardResponse) GetGrantedAtMs() int64 {
return 0 return 0
} }
// CreditLuckyGiftRewardRequest 是 activity-service 抽奖 outbox 补偿 worker 调用的钱包入账命令。
type CreditLuckyGiftRewardRequest 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"`
DrawId string `protobuf:"bytes,5,opt,name=draw_id,json=drawId,proto3" json:"draw_id,omitempty"`
RoomId string `protobuf:"bytes,6,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"`
GiftId string `protobuf:"bytes,7,opt,name=gift_id,json=giftId,proto3" json:"gift_id,omitempty"`
PoolId string `protobuf:"bytes,8,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"`
Reason string `protobuf:"bytes,9,opt,name=reason,proto3" json:"reason,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreditLuckyGiftRewardRequest) Reset() {
*x = CreditLuckyGiftRewardRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[106]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreditLuckyGiftRewardRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreditLuckyGiftRewardRequest) ProtoMessage() {}
func (x *CreditLuckyGiftRewardRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[106]
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 CreditLuckyGiftRewardRequest.ProtoReflect.Descriptor instead.
func (*CreditLuckyGiftRewardRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{106}
}
func (x *CreditLuckyGiftRewardRequest) GetCommandId() string {
if x != nil {
return x.CommandId
}
return ""
}
func (x *CreditLuckyGiftRewardRequest) GetAppCode() string {
if x != nil {
return x.AppCode
}
return ""
}
func (x *CreditLuckyGiftRewardRequest) GetTargetUserId() int64 {
if x != nil {
return x.TargetUserId
}
return 0
}
func (x *CreditLuckyGiftRewardRequest) GetAmount() int64 {
if x != nil {
return x.Amount
}
return 0
}
func (x *CreditLuckyGiftRewardRequest) GetDrawId() string {
if x != nil {
return x.DrawId
}
return ""
}
func (x *CreditLuckyGiftRewardRequest) GetRoomId() string {
if x != nil {
return x.RoomId
}
return ""
}
func (x *CreditLuckyGiftRewardRequest) GetGiftId() string {
if x != nil {
return x.GiftId
}
return ""
}
func (x *CreditLuckyGiftRewardRequest) GetPoolId() string {
if x != nil {
return x.PoolId
}
return ""
}
func (x *CreditLuckyGiftRewardRequest) GetReason() string {
if x != nil {
return x.Reason
}
return ""
}
// CreditLuckyGiftRewardResponse 返回幸运礼物返奖入账流水和用户 COIN 账后余额。
type CreditLuckyGiftRewardResponse 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 *CreditLuckyGiftRewardResponse) Reset() {
*x = CreditLuckyGiftRewardResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[107]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreditLuckyGiftRewardResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreditLuckyGiftRewardResponse) ProtoMessage() {}
func (x *CreditLuckyGiftRewardResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[107]
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 CreditLuckyGiftRewardResponse.ProtoReflect.Descriptor instead.
func (*CreditLuckyGiftRewardResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{107}
}
func (x *CreditLuckyGiftRewardResponse) GetTransactionId() string {
if x != nil {
return x.TransactionId
}
return ""
}
func (x *CreditLuckyGiftRewardResponse) GetBalance() *AssetBalance {
if x != nil {
return x.Balance
}
return nil
}
func (x *CreditLuckyGiftRewardResponse) GetAmount() int64 {
if x != nil {
return x.Amount
}
return 0
}
func (x *CreditLuckyGiftRewardResponse) GetGrantedAtMs() int64 {
if x != nil {
return x.GrantedAtMs
}
return 0
}
// ApplyGameCoinChangeRequest 是 game-service 唯一可以调用的钱包游戏改账入口。 // ApplyGameCoinChangeRequest 是 game-service 唯一可以调用的钱包游戏改账入口。
type ApplyGameCoinChangeRequest struct { type ApplyGameCoinChangeRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
@ -9513,7 +9691,7 @@ type ApplyGameCoinChangeRequest struct {
func (x *ApplyGameCoinChangeRequest) Reset() { func (x *ApplyGameCoinChangeRequest) Reset() {
*x = ApplyGameCoinChangeRequest{} *x = ApplyGameCoinChangeRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[106] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[108]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -9525,7 +9703,7 @@ func (x *ApplyGameCoinChangeRequest) String() string {
func (*ApplyGameCoinChangeRequest) ProtoMessage() {} func (*ApplyGameCoinChangeRequest) ProtoMessage() {}
func (x *ApplyGameCoinChangeRequest) ProtoReflect() protoreflect.Message { func (x *ApplyGameCoinChangeRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[106] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[108]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -9538,7 +9716,7 @@ func (x *ApplyGameCoinChangeRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ApplyGameCoinChangeRequest.ProtoReflect.Descriptor instead. // Deprecated: Use ApplyGameCoinChangeRequest.ProtoReflect.Descriptor instead.
func (*ApplyGameCoinChangeRequest) Descriptor() ([]byte, []int) { func (*ApplyGameCoinChangeRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{106} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{108}
} }
func (x *ApplyGameCoinChangeRequest) GetRequestId() string { func (x *ApplyGameCoinChangeRequest) GetRequestId() string {
@ -9637,7 +9815,7 @@ type ApplyGameCoinChangeResponse struct {
func (x *ApplyGameCoinChangeResponse) Reset() { func (x *ApplyGameCoinChangeResponse) Reset() {
*x = ApplyGameCoinChangeResponse{} *x = ApplyGameCoinChangeResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[107] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[109]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -9649,7 +9827,7 @@ func (x *ApplyGameCoinChangeResponse) String() string {
func (*ApplyGameCoinChangeResponse) ProtoMessage() {} func (*ApplyGameCoinChangeResponse) ProtoMessage() {}
func (x *ApplyGameCoinChangeResponse) ProtoReflect() protoreflect.Message { func (x *ApplyGameCoinChangeResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[107] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[109]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -9662,7 +9840,7 @@ func (x *ApplyGameCoinChangeResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ApplyGameCoinChangeResponse.ProtoReflect.Descriptor instead. // Deprecated: Use ApplyGameCoinChangeResponse.ProtoReflect.Descriptor instead.
func (*ApplyGameCoinChangeResponse) Descriptor() ([]byte, []int) { func (*ApplyGameCoinChangeResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{107} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{109}
} }
func (x *ApplyGameCoinChangeResponse) GetWalletTransactionId() string { func (x *ApplyGameCoinChangeResponse) GetWalletTransactionId() string {
@ -9704,7 +9882,7 @@ type RedPacketConfig struct {
func (x *RedPacketConfig) Reset() { func (x *RedPacketConfig) Reset() {
*x = RedPacketConfig{} *x = RedPacketConfig{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[108] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[110]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -9716,7 +9894,7 @@ func (x *RedPacketConfig) String() string {
func (*RedPacketConfig) ProtoMessage() {} func (*RedPacketConfig) ProtoMessage() {}
func (x *RedPacketConfig) ProtoReflect() protoreflect.Message { func (x *RedPacketConfig) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[108] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[110]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -9729,7 +9907,7 @@ func (x *RedPacketConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use RedPacketConfig.ProtoReflect.Descriptor instead. // Deprecated: Use RedPacketConfig.ProtoReflect.Descriptor instead.
func (*RedPacketConfig) Descriptor() ([]byte, []int) { func (*RedPacketConfig) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{108} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{110}
} }
func (x *RedPacketConfig) GetAppCode() string { func (x *RedPacketConfig) GetAppCode() string {
@ -9821,7 +9999,7 @@ type RedPacketClaim struct {
func (x *RedPacketClaim) Reset() { func (x *RedPacketClaim) Reset() {
*x = RedPacketClaim{} *x = RedPacketClaim{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[109] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[111]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -9833,7 +10011,7 @@ func (x *RedPacketClaim) String() string {
func (*RedPacketClaim) ProtoMessage() {} func (*RedPacketClaim) ProtoMessage() {}
func (x *RedPacketClaim) ProtoReflect() protoreflect.Message { func (x *RedPacketClaim) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[109] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[111]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -9846,7 +10024,7 @@ func (x *RedPacketClaim) ProtoReflect() protoreflect.Message {
// Deprecated: Use RedPacketClaim.ProtoReflect.Descriptor instead. // Deprecated: Use RedPacketClaim.ProtoReflect.Descriptor instead.
func (*RedPacketClaim) Descriptor() ([]byte, []int) { func (*RedPacketClaim) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{109} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{111}
} }
func (x *RedPacketClaim) GetAppCode() string { func (x *RedPacketClaim) GetAppCode() string {
@ -9953,7 +10131,7 @@ type RedPacket struct {
func (x *RedPacket) Reset() { func (x *RedPacket) Reset() {
*x = RedPacket{} *x = RedPacket{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[110] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[112]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -9965,7 +10143,7 @@ func (x *RedPacket) String() string {
func (*RedPacket) ProtoMessage() {} func (*RedPacket) ProtoMessage() {}
func (x *RedPacket) ProtoReflect() protoreflect.Message { func (x *RedPacket) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[110] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[112]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -9978,7 +10156,7 @@ func (x *RedPacket) ProtoReflect() protoreflect.Message {
// Deprecated: Use RedPacket.ProtoReflect.Descriptor instead. // Deprecated: Use RedPacket.ProtoReflect.Descriptor instead.
func (*RedPacket) Descriptor() ([]byte, []int) { func (*RedPacket) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{110} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{112}
} }
func (x *RedPacket) GetAppCode() string { func (x *RedPacket) GetAppCode() string {
@ -10124,7 +10302,7 @@ type GetRedPacketConfigRequest struct {
func (x *GetRedPacketConfigRequest) Reset() { func (x *GetRedPacketConfigRequest) Reset() {
*x = GetRedPacketConfigRequest{} *x = GetRedPacketConfigRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[111] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[113]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10136,7 +10314,7 @@ func (x *GetRedPacketConfigRequest) String() string {
func (*GetRedPacketConfigRequest) ProtoMessage() {} func (*GetRedPacketConfigRequest) ProtoMessage() {}
func (x *GetRedPacketConfigRequest) ProtoReflect() protoreflect.Message { func (x *GetRedPacketConfigRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[111] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[113]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10149,7 +10327,7 @@ func (x *GetRedPacketConfigRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetRedPacketConfigRequest.ProtoReflect.Descriptor instead. // Deprecated: Use GetRedPacketConfigRequest.ProtoReflect.Descriptor instead.
func (*GetRedPacketConfigRequest) Descriptor() ([]byte, []int) { func (*GetRedPacketConfigRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{111} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{113}
} }
func (x *GetRedPacketConfigRequest) GetRequestId() string { func (x *GetRedPacketConfigRequest) GetRequestId() string {
@ -10176,7 +10354,7 @@ type GetRedPacketConfigResponse struct {
func (x *GetRedPacketConfigResponse) Reset() { func (x *GetRedPacketConfigResponse) Reset() {
*x = GetRedPacketConfigResponse{} *x = GetRedPacketConfigResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[112] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[114]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10188,7 +10366,7 @@ func (x *GetRedPacketConfigResponse) String() string {
func (*GetRedPacketConfigResponse) ProtoMessage() {} func (*GetRedPacketConfigResponse) ProtoMessage() {}
func (x *GetRedPacketConfigResponse) ProtoReflect() protoreflect.Message { func (x *GetRedPacketConfigResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[112] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[114]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10201,7 +10379,7 @@ func (x *GetRedPacketConfigResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetRedPacketConfigResponse.ProtoReflect.Descriptor instead. // Deprecated: Use GetRedPacketConfigResponse.ProtoReflect.Descriptor instead.
func (*GetRedPacketConfigResponse) Descriptor() ([]byte, []int) { func (*GetRedPacketConfigResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{112} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{114}
} }
func (x *GetRedPacketConfigResponse) GetConfig() *RedPacketConfig { func (x *GetRedPacketConfigResponse) GetConfig() *RedPacketConfig {
@ -10235,7 +10413,7 @@ type UpdateRedPacketConfigRequest struct {
func (x *UpdateRedPacketConfigRequest) Reset() { func (x *UpdateRedPacketConfigRequest) Reset() {
*x = UpdateRedPacketConfigRequest{} *x = UpdateRedPacketConfigRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[113] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[115]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10247,7 +10425,7 @@ func (x *UpdateRedPacketConfigRequest) String() string {
func (*UpdateRedPacketConfigRequest) ProtoMessage() {} func (*UpdateRedPacketConfigRequest) ProtoMessage() {}
func (x *UpdateRedPacketConfigRequest) ProtoReflect() protoreflect.Message { func (x *UpdateRedPacketConfigRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[113] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[115]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10260,7 +10438,7 @@ func (x *UpdateRedPacketConfigRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateRedPacketConfigRequest.ProtoReflect.Descriptor instead. // Deprecated: Use UpdateRedPacketConfigRequest.ProtoReflect.Descriptor instead.
func (*UpdateRedPacketConfigRequest) Descriptor() ([]byte, []int) { func (*UpdateRedPacketConfigRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{113} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{115}
} }
func (x *UpdateRedPacketConfigRequest) GetRequestId() string { func (x *UpdateRedPacketConfigRequest) GetRequestId() string {
@ -10336,7 +10514,7 @@ type UpdateRedPacketConfigResponse struct {
func (x *UpdateRedPacketConfigResponse) Reset() { func (x *UpdateRedPacketConfigResponse) Reset() {
*x = UpdateRedPacketConfigResponse{} *x = UpdateRedPacketConfigResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[114] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[116]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10348,7 +10526,7 @@ func (x *UpdateRedPacketConfigResponse) String() string {
func (*UpdateRedPacketConfigResponse) ProtoMessage() {} func (*UpdateRedPacketConfigResponse) ProtoMessage() {}
func (x *UpdateRedPacketConfigResponse) ProtoReflect() protoreflect.Message { func (x *UpdateRedPacketConfigResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[114] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[116]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10361,7 +10539,7 @@ func (x *UpdateRedPacketConfigResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateRedPacketConfigResponse.ProtoReflect.Descriptor instead. // Deprecated: Use UpdateRedPacketConfigResponse.ProtoReflect.Descriptor instead.
func (*UpdateRedPacketConfigResponse) Descriptor() ([]byte, []int) { func (*UpdateRedPacketConfigResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{114} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{116}
} }
func (x *UpdateRedPacketConfigResponse) GetConfig() *RedPacketConfig { func (x *UpdateRedPacketConfigResponse) GetConfig() *RedPacketConfig {
@ -10395,7 +10573,7 @@ type CreateRedPacketRequest struct {
func (x *CreateRedPacketRequest) Reset() { func (x *CreateRedPacketRequest) Reset() {
*x = CreateRedPacketRequest{} *x = CreateRedPacketRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[115] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[117]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10407,7 +10585,7 @@ func (x *CreateRedPacketRequest) String() string {
func (*CreateRedPacketRequest) ProtoMessage() {} func (*CreateRedPacketRequest) ProtoMessage() {}
func (x *CreateRedPacketRequest) ProtoReflect() protoreflect.Message { func (x *CreateRedPacketRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[115] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[117]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10420,7 +10598,7 @@ func (x *CreateRedPacketRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateRedPacketRequest.ProtoReflect.Descriptor instead. // Deprecated: Use CreateRedPacketRequest.ProtoReflect.Descriptor instead.
func (*CreateRedPacketRequest) Descriptor() ([]byte, []int) { func (*CreateRedPacketRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{115} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{117}
} }
func (x *CreateRedPacketRequest) GetRequestId() string { func (x *CreateRedPacketRequest) GetRequestId() string {
@ -10497,7 +10675,7 @@ type CreateRedPacketResponse struct {
func (x *CreateRedPacketResponse) Reset() { func (x *CreateRedPacketResponse) Reset() {
*x = CreateRedPacketResponse{} *x = CreateRedPacketResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[116] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[118]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10509,7 +10687,7 @@ func (x *CreateRedPacketResponse) String() string {
func (*CreateRedPacketResponse) ProtoMessage() {} func (*CreateRedPacketResponse) ProtoMessage() {}
func (x *CreateRedPacketResponse) ProtoReflect() protoreflect.Message { func (x *CreateRedPacketResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[116] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[118]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10522,7 +10700,7 @@ func (x *CreateRedPacketResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateRedPacketResponse.ProtoReflect.Descriptor instead. // Deprecated: Use CreateRedPacketResponse.ProtoReflect.Descriptor instead.
func (*CreateRedPacketResponse) Descriptor() ([]byte, []int) { func (*CreateRedPacketResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{116} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{118}
} }
func (x *CreateRedPacketResponse) GetPacket() *RedPacket { func (x *CreateRedPacketResponse) GetPacket() *RedPacket {
@ -10559,7 +10737,7 @@ type ClaimRedPacketRequest struct {
func (x *ClaimRedPacketRequest) Reset() { func (x *ClaimRedPacketRequest) Reset() {
*x = ClaimRedPacketRequest{} *x = ClaimRedPacketRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[117] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[119]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10571,7 +10749,7 @@ func (x *ClaimRedPacketRequest) String() string {
func (*ClaimRedPacketRequest) ProtoMessage() {} func (*ClaimRedPacketRequest) ProtoMessage() {}
func (x *ClaimRedPacketRequest) ProtoReflect() protoreflect.Message { func (x *ClaimRedPacketRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[117] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[119]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10584,7 +10762,7 @@ func (x *ClaimRedPacketRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ClaimRedPacketRequest.ProtoReflect.Descriptor instead. // Deprecated: Use ClaimRedPacketRequest.ProtoReflect.Descriptor instead.
func (*ClaimRedPacketRequest) Descriptor() ([]byte, []int) { func (*ClaimRedPacketRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{117} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{119}
} }
func (x *ClaimRedPacketRequest) GetRequestId() string { func (x *ClaimRedPacketRequest) GetRequestId() string {
@ -10633,7 +10811,7 @@ type ClaimRedPacketResponse struct {
func (x *ClaimRedPacketResponse) Reset() { func (x *ClaimRedPacketResponse) Reset() {
*x = ClaimRedPacketResponse{} *x = ClaimRedPacketResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[118] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[120]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10645,7 +10823,7 @@ func (x *ClaimRedPacketResponse) String() string {
func (*ClaimRedPacketResponse) ProtoMessage() {} func (*ClaimRedPacketResponse) ProtoMessage() {}
func (x *ClaimRedPacketResponse) ProtoReflect() protoreflect.Message { func (x *ClaimRedPacketResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[118] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[120]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10658,7 +10836,7 @@ func (x *ClaimRedPacketResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ClaimRedPacketResponse.ProtoReflect.Descriptor instead. // Deprecated: Use ClaimRedPacketResponse.ProtoReflect.Descriptor instead.
func (*ClaimRedPacketResponse) Descriptor() ([]byte, []int) { func (*ClaimRedPacketResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{118} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{120}
} }
func (x *ClaimRedPacketResponse) GetClaim() *RedPacketClaim { func (x *ClaimRedPacketResponse) GetClaim() *RedPacketClaim {
@ -10702,7 +10880,7 @@ type ListRedPacketsRequest struct {
func (x *ListRedPacketsRequest) Reset() { func (x *ListRedPacketsRequest) Reset() {
*x = ListRedPacketsRequest{} *x = ListRedPacketsRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[119] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[121]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10714,7 +10892,7 @@ func (x *ListRedPacketsRequest) String() string {
func (*ListRedPacketsRequest) ProtoMessage() {} func (*ListRedPacketsRequest) ProtoMessage() {}
func (x *ListRedPacketsRequest) ProtoReflect() protoreflect.Message { func (x *ListRedPacketsRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[119] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[121]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10727,7 +10905,7 @@ func (x *ListRedPacketsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListRedPacketsRequest.ProtoReflect.Descriptor instead. // Deprecated: Use ListRedPacketsRequest.ProtoReflect.Descriptor instead.
func (*ListRedPacketsRequest) Descriptor() ([]byte, []int) { func (*ListRedPacketsRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{119} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{121}
} }
func (x *ListRedPacketsRequest) GetRequestId() string { func (x *ListRedPacketsRequest) GetRequestId() string {
@ -10825,7 +11003,7 @@ type ListRedPacketsResponse struct {
func (x *ListRedPacketsResponse) Reset() { func (x *ListRedPacketsResponse) Reset() {
*x = ListRedPacketsResponse{} *x = ListRedPacketsResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[120] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[122]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10837,7 +11015,7 @@ func (x *ListRedPacketsResponse) String() string {
func (*ListRedPacketsResponse) ProtoMessage() {} func (*ListRedPacketsResponse) ProtoMessage() {}
func (x *ListRedPacketsResponse) ProtoReflect() protoreflect.Message { func (x *ListRedPacketsResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[120] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[122]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10850,7 +11028,7 @@ func (x *ListRedPacketsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListRedPacketsResponse.ProtoReflect.Descriptor instead. // Deprecated: Use ListRedPacketsResponse.ProtoReflect.Descriptor instead.
func (*ListRedPacketsResponse) Descriptor() ([]byte, []int) { func (*ListRedPacketsResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{120} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{122}
} }
func (x *ListRedPacketsResponse) GetPackets() []*RedPacket { func (x *ListRedPacketsResponse) GetPackets() []*RedPacket {
@ -10887,7 +11065,7 @@ type GetRedPacketRequest struct {
func (x *GetRedPacketRequest) Reset() { func (x *GetRedPacketRequest) Reset() {
*x = GetRedPacketRequest{} *x = GetRedPacketRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[121] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[123]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10899,7 +11077,7 @@ func (x *GetRedPacketRequest) String() string {
func (*GetRedPacketRequest) ProtoMessage() {} func (*GetRedPacketRequest) ProtoMessage() {}
func (x *GetRedPacketRequest) ProtoReflect() protoreflect.Message { func (x *GetRedPacketRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[121] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[123]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10912,7 +11090,7 @@ func (x *GetRedPacketRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetRedPacketRequest.ProtoReflect.Descriptor instead. // Deprecated: Use GetRedPacketRequest.ProtoReflect.Descriptor instead.
func (*GetRedPacketRequest) Descriptor() ([]byte, []int) { func (*GetRedPacketRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{121} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{123}
} }
func (x *GetRedPacketRequest) GetRequestId() string { func (x *GetRedPacketRequest) GetRequestId() string {
@ -10960,7 +11138,7 @@ type GetRedPacketResponse struct {
func (x *GetRedPacketResponse) Reset() { func (x *GetRedPacketResponse) Reset() {
*x = GetRedPacketResponse{} *x = GetRedPacketResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[122] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[124]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -10972,7 +11150,7 @@ func (x *GetRedPacketResponse) String() string {
func (*GetRedPacketResponse) ProtoMessage() {} func (*GetRedPacketResponse) ProtoMessage() {}
func (x *GetRedPacketResponse) ProtoReflect() protoreflect.Message { func (x *GetRedPacketResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[122] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[124]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -10985,7 +11163,7 @@ func (x *GetRedPacketResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetRedPacketResponse.ProtoReflect.Descriptor instead. // Deprecated: Use GetRedPacketResponse.ProtoReflect.Descriptor instead.
func (*GetRedPacketResponse) Descriptor() ([]byte, []int) { func (*GetRedPacketResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{122} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{124}
} }
func (x *GetRedPacketResponse) GetPacket() *RedPacket { func (x *GetRedPacketResponse) GetPacket() *RedPacket {
@ -11013,7 +11191,7 @@ type ExpireRedPacketsRequest struct {
func (x *ExpireRedPacketsRequest) Reset() { func (x *ExpireRedPacketsRequest) Reset() {
*x = ExpireRedPacketsRequest{} *x = ExpireRedPacketsRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[123] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[125]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -11025,7 +11203,7 @@ func (x *ExpireRedPacketsRequest) String() string {
func (*ExpireRedPacketsRequest) ProtoMessage() {} func (*ExpireRedPacketsRequest) ProtoMessage() {}
func (x *ExpireRedPacketsRequest) ProtoReflect() protoreflect.Message { func (x *ExpireRedPacketsRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[123] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[125]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -11038,7 +11216,7 @@ func (x *ExpireRedPacketsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ExpireRedPacketsRequest.ProtoReflect.Descriptor instead. // Deprecated: Use ExpireRedPacketsRequest.ProtoReflect.Descriptor instead.
func (*ExpireRedPacketsRequest) Descriptor() ([]byte, []int) { func (*ExpireRedPacketsRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{123} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{125}
} }
func (x *ExpireRedPacketsRequest) GetRequestId() string { func (x *ExpireRedPacketsRequest) GetRequestId() string {
@ -11073,7 +11251,7 @@ type ExpireRedPacketsResponse struct {
func (x *ExpireRedPacketsResponse) Reset() { func (x *ExpireRedPacketsResponse) Reset() {
*x = ExpireRedPacketsResponse{} *x = ExpireRedPacketsResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[124] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[126]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -11085,7 +11263,7 @@ func (x *ExpireRedPacketsResponse) String() string {
func (*ExpireRedPacketsResponse) ProtoMessage() {} func (*ExpireRedPacketsResponse) ProtoMessage() {}
func (x *ExpireRedPacketsResponse) ProtoReflect() protoreflect.Message { func (x *ExpireRedPacketsResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[124] mi := &file_proto_wallet_v1_wallet_proto_msgTypes[126]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -11098,7 +11276,7 @@ func (x *ExpireRedPacketsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ExpireRedPacketsResponse.ProtoReflect.Descriptor instead. // Deprecated: Use ExpireRedPacketsResponse.ProtoReflect.Descriptor instead.
func (*ExpireRedPacketsResponse) Descriptor() ([]byte, []int) { func (*ExpireRedPacketsResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{124} return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{126}
} }
func (x *ExpireRedPacketsResponse) GetExpiredCount() int32 { func (x *ExpireRedPacketsResponse) GetExpiredCount() int32 {
@ -12117,6 +12295,22 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x0etransaction_id\x18\x01 \x01(\tR\rtransactionId\x127\n" + "\x0etransaction_id\x18\x01 \x01(\tR\rtransactionId\x127\n" +
"\abalance\x18\x02 \x01(\v2\x1d.hyapp.wallet.v1.AssetBalanceR\abalance\x12\x16\n" + "\abalance\x18\x02 \x01(\v2\x1d.hyapp.wallet.v1.AssetBalanceR\abalance\x12\x16\n" +
"\x06amount\x18\x03 \x01(\x03R\x06amount\x12\"\n" + "\x06amount\x18\x03 \x01(\x03R\x06amount\x12\"\n" +
"\rgranted_at_ms\x18\x04 \x01(\x03R\vgrantedAtMs\"\x92\x02\n" +
"\x1cCreditLuckyGiftRewardRequest\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\x17\n" +
"\adraw_id\x18\x05 \x01(\tR\x06drawId\x12\x17\n" +
"\aroom_id\x18\x06 \x01(\tR\x06roomId\x12\x17\n" +
"\agift_id\x18\a \x01(\tR\x06giftId\x12\x17\n" +
"\apool_id\x18\b \x01(\tR\x06poolId\x12\x16\n" +
"\x06reason\x18\t \x01(\tR\x06reason\"\xbb\x01\n" +
"\x1dCreditLuckyGiftRewardResponse\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" + "\rgranted_at_ms\x18\x04 \x01(\x03R\vgrantedAtMs\"\x9a\x03\n" +
"\x1aApplyGameCoinChangeRequest\x12\x1d\n" + "\x1aApplyGameCoinChangeRequest\x12\x1d\n" +
"\n" + "\n" +
@ -12280,7 +12474,7 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x18ExpireRedPacketsResponse\x12#\n" + "\x18ExpireRedPacketsResponse\x12#\n" +
"\rexpired_count\x18\x01 \x01(\x05R\fexpiredCount\x12'\n" + "\rexpired_count\x18\x01 \x01(\x05R\fexpiredCount\x12'\n" +
"\x0frefunded_amount\x18\x02 \x01(\x03R\x0erefundedAmount\x12$\n" + "\x0frefunded_amount\x18\x02 \x01(\x03R\x0erefundedAmount\x12$\n" +
"\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs2\x86-\n" + "\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs2\xfe-\n" +
"\rWalletService\x12R\n" + "\rWalletService\x12R\n" +
"\tDebitGift\x12!.hyapp.wallet.v1.DebitGiftRequest\x1a\".hyapp.wallet.v1.DebitGiftResponse\x12X\n" + "\tDebitGift\x12!.hyapp.wallet.v1.DebitGiftRequest\x1a\".hyapp.wallet.v1.DebitGiftResponse\x12X\n" +
"\vGetBalances\x12#.hyapp.wallet.v1.GetBalancesRequest\x1a$.hyapp.wallet.v1.GetBalancesResponse\x12g\n" + "\vGetBalances\x12#.hyapp.wallet.v1.GetBalancesRequest\x1a$.hyapp.wallet.v1.GetBalancesResponse\x12g\n" +
@ -12327,7 +12521,8 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\bGrantVip\x12 .hyapp.wallet.v1.GrantVipRequest\x1a!.hyapp.wallet.v1.GrantVipResponse\x12m\n" + "\bGrantVip\x12 .hyapp.wallet.v1.GrantVipRequest\x1a!.hyapp.wallet.v1.GrantVipResponse\x12m\n" +
"\x12ListAdminVipLevels\x12*.hyapp.wallet.v1.ListAdminVipLevelsRequest\x1a+.hyapp.wallet.v1.ListAdminVipLevelsResponse\x12s\n" + "\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" + "\x14UpdateAdminVipLevels\x12,.hyapp.wallet.v1.UpdateAdminVipLevelsRequest\x1a-.hyapp.wallet.v1.UpdateAdminVipLevelsResponse\x12g\n" +
"\x10CreditTaskReward\x12(.hyapp.wallet.v1.CreditTaskRewardRequest\x1a).hyapp.wallet.v1.CreditTaskRewardResponse\x12p\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" +
"\x13ApplyGameCoinChange\x12+.hyapp.wallet.v1.ApplyGameCoinChangeRequest\x1a,.hyapp.wallet.v1.ApplyGameCoinChangeResponse\x12m\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" + "\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" + "\x15UpdateRedPacketConfig\x12-.hyapp.wallet.v1.UpdateRedPacketConfigRequest\x1a..hyapp.wallet.v1.UpdateRedPacketConfigResponse\x12d\n" +
@ -12349,7 +12544,7 @@ func file_proto_wallet_v1_wallet_proto_rawDescGZIP() []byte {
return file_proto_wallet_v1_wallet_proto_rawDescData return file_proto_wallet_v1_wallet_proto_rawDescData
} }
var file_proto_wallet_v1_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 125) var file_proto_wallet_v1_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 127)
var file_proto_wallet_v1_wallet_proto_goTypes = []any{ var file_proto_wallet_v1_wallet_proto_goTypes = []any{
(*DebitGiftRequest)(nil), // 0: hyapp.wallet.v1.DebitGiftRequest (*DebitGiftRequest)(nil), // 0: hyapp.wallet.v1.DebitGiftRequest
(*DebitGiftResponse)(nil), // 1: hyapp.wallet.v1.DebitGiftResponse (*DebitGiftResponse)(nil), // 1: hyapp.wallet.v1.DebitGiftResponse
@ -12457,25 +12652,27 @@ var file_proto_wallet_v1_wallet_proto_goTypes = []any{
(*UpdateAdminVipLevelsResponse)(nil), // 103: hyapp.wallet.v1.UpdateAdminVipLevelsResponse (*UpdateAdminVipLevelsResponse)(nil), // 103: hyapp.wallet.v1.UpdateAdminVipLevelsResponse
(*CreditTaskRewardRequest)(nil), // 104: hyapp.wallet.v1.CreditTaskRewardRequest (*CreditTaskRewardRequest)(nil), // 104: hyapp.wallet.v1.CreditTaskRewardRequest
(*CreditTaskRewardResponse)(nil), // 105: hyapp.wallet.v1.CreditTaskRewardResponse (*CreditTaskRewardResponse)(nil), // 105: hyapp.wallet.v1.CreditTaskRewardResponse
(*ApplyGameCoinChangeRequest)(nil), // 106: hyapp.wallet.v1.ApplyGameCoinChangeRequest (*CreditLuckyGiftRewardRequest)(nil), // 106: hyapp.wallet.v1.CreditLuckyGiftRewardRequest
(*ApplyGameCoinChangeResponse)(nil), // 107: hyapp.wallet.v1.ApplyGameCoinChangeResponse (*CreditLuckyGiftRewardResponse)(nil), // 107: hyapp.wallet.v1.CreditLuckyGiftRewardResponse
(*RedPacketConfig)(nil), // 108: hyapp.wallet.v1.RedPacketConfig (*ApplyGameCoinChangeRequest)(nil), // 108: hyapp.wallet.v1.ApplyGameCoinChangeRequest
(*RedPacketClaim)(nil), // 109: hyapp.wallet.v1.RedPacketClaim (*ApplyGameCoinChangeResponse)(nil), // 109: hyapp.wallet.v1.ApplyGameCoinChangeResponse
(*RedPacket)(nil), // 110: hyapp.wallet.v1.RedPacket (*RedPacketConfig)(nil), // 110: hyapp.wallet.v1.RedPacketConfig
(*GetRedPacketConfigRequest)(nil), // 111: hyapp.wallet.v1.GetRedPacketConfigRequest (*RedPacketClaim)(nil), // 111: hyapp.wallet.v1.RedPacketClaim
(*GetRedPacketConfigResponse)(nil), // 112: hyapp.wallet.v1.GetRedPacketConfigResponse (*RedPacket)(nil), // 112: hyapp.wallet.v1.RedPacket
(*UpdateRedPacketConfigRequest)(nil), // 113: hyapp.wallet.v1.UpdateRedPacketConfigRequest (*GetRedPacketConfigRequest)(nil), // 113: hyapp.wallet.v1.GetRedPacketConfigRequest
(*UpdateRedPacketConfigResponse)(nil), // 114: hyapp.wallet.v1.UpdateRedPacketConfigResponse (*GetRedPacketConfigResponse)(nil), // 114: hyapp.wallet.v1.GetRedPacketConfigResponse
(*CreateRedPacketRequest)(nil), // 115: hyapp.wallet.v1.CreateRedPacketRequest (*UpdateRedPacketConfigRequest)(nil), // 115: hyapp.wallet.v1.UpdateRedPacketConfigRequest
(*CreateRedPacketResponse)(nil), // 116: hyapp.wallet.v1.CreateRedPacketResponse (*UpdateRedPacketConfigResponse)(nil), // 116: hyapp.wallet.v1.UpdateRedPacketConfigResponse
(*ClaimRedPacketRequest)(nil), // 117: hyapp.wallet.v1.ClaimRedPacketRequest (*CreateRedPacketRequest)(nil), // 117: hyapp.wallet.v1.CreateRedPacketRequest
(*ClaimRedPacketResponse)(nil), // 118: hyapp.wallet.v1.ClaimRedPacketResponse (*CreateRedPacketResponse)(nil), // 118: hyapp.wallet.v1.CreateRedPacketResponse
(*ListRedPacketsRequest)(nil), // 119: hyapp.wallet.v1.ListRedPacketsRequest (*ClaimRedPacketRequest)(nil), // 119: hyapp.wallet.v1.ClaimRedPacketRequest
(*ListRedPacketsResponse)(nil), // 120: hyapp.wallet.v1.ListRedPacketsResponse (*ClaimRedPacketResponse)(nil), // 120: hyapp.wallet.v1.ClaimRedPacketResponse
(*GetRedPacketRequest)(nil), // 121: hyapp.wallet.v1.GetRedPacketRequest (*ListRedPacketsRequest)(nil), // 121: hyapp.wallet.v1.ListRedPacketsRequest
(*GetRedPacketResponse)(nil), // 122: hyapp.wallet.v1.GetRedPacketResponse (*ListRedPacketsResponse)(nil), // 122: hyapp.wallet.v1.ListRedPacketsResponse
(*ExpireRedPacketsRequest)(nil), // 123: hyapp.wallet.v1.ExpireRedPacketsRequest (*GetRedPacketRequest)(nil), // 123: hyapp.wallet.v1.GetRedPacketRequest
(*ExpireRedPacketsResponse)(nil), // 124: hyapp.wallet.v1.ExpireRedPacketsResponse (*GetRedPacketResponse)(nil), // 124: hyapp.wallet.v1.GetRedPacketResponse
(*ExpireRedPacketsRequest)(nil), // 125: hyapp.wallet.v1.ExpireRedPacketsRequest
(*ExpireRedPacketsResponse)(nil), // 126: hyapp.wallet.v1.ExpireRedPacketsResponse
} }
var file_proto_wallet_v1_wallet_proto_depIdxs = []int32{ var file_proto_wallet_v1_wallet_proto_depIdxs = []int32{
2, // 0: hyapp.wallet.v1.GetBalancesResponse.balances:type_name -> hyapp.wallet.v1.AssetBalance 2, // 0: hyapp.wallet.v1.GetBalancesResponse.balances:type_name -> hyapp.wallet.v1.AssetBalance
@ -12527,126 +12724,129 @@ var file_proto_wallet_v1_wallet_proto_depIdxs = []int32{
99, // 46: hyapp.wallet.v1.UpdateAdminVipLevelsRequest.levels:type_name -> hyapp.wallet.v1.AdminVipLevelInput 99, // 46: hyapp.wallet.v1.UpdateAdminVipLevelsRequest.levels:type_name -> hyapp.wallet.v1.AdminVipLevelInput
89, // 47: hyapp.wallet.v1.UpdateAdminVipLevelsResponse.levels:type_name -> hyapp.wallet.v1.VipLevel 89, // 47: hyapp.wallet.v1.UpdateAdminVipLevelsResponse.levels:type_name -> hyapp.wallet.v1.VipLevel
2, // 48: hyapp.wallet.v1.CreditTaskRewardResponse.balance:type_name -> hyapp.wallet.v1.AssetBalance 2, // 48: hyapp.wallet.v1.CreditTaskRewardResponse.balance:type_name -> hyapp.wallet.v1.AssetBalance
109, // 49: hyapp.wallet.v1.RedPacket.claims:type_name -> hyapp.wallet.v1.RedPacketClaim 2, // 49: hyapp.wallet.v1.CreditLuckyGiftRewardResponse.balance:type_name -> hyapp.wallet.v1.AssetBalance
108, // 50: hyapp.wallet.v1.GetRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig 111, // 50: hyapp.wallet.v1.RedPacket.claims:type_name -> hyapp.wallet.v1.RedPacketClaim
108, // 51: hyapp.wallet.v1.UpdateRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig 110, // 51: hyapp.wallet.v1.GetRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig
110, // 52: hyapp.wallet.v1.CreateRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket 110, // 52: hyapp.wallet.v1.UpdateRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig
109, // 53: hyapp.wallet.v1.ClaimRedPacketResponse.claim:type_name -> hyapp.wallet.v1.RedPacketClaim 112, // 53: hyapp.wallet.v1.CreateRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket
110, // 54: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket 111, // 54: hyapp.wallet.v1.ClaimRedPacketResponse.claim:type_name -> hyapp.wallet.v1.RedPacketClaim
110, // 55: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket 112, // 55: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket
0, // 56: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest 112, // 56: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket
3, // 57: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest 0, // 57: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest
5, // 58: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest 3, // 58: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest
7, // 59: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest 5, // 59: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest
9, // 60: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest 7, // 60: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest
20, // 61: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest 9, // 61: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest
22, // 62: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest 20, // 62: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest
24, // 63: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest 22, // 63: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest
25, // 64: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest 24, // 64: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest
26, // 65: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest 25, // 65: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest
28, // 66: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest 26, // 66: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest
30, // 67: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest 28, // 67: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest
32, // 68: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest 30, // 68: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest
33, // 69: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest 32, // 69: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest
34, // 70: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest 33, // 70: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest
36, // 71: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest 34, // 71: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest
38, // 72: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest 36, // 72: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest
42, // 73: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest 38, // 73: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest
43, // 74: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest 42, // 74: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest
44, // 75: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest 43, // 75: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest
40, // 76: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest 44, // 76: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest
46, // 77: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest 40, // 77: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest
47, // 78: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest 46, // 78: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest
49, // 79: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest 47, // 79: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest
51, // 80: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest 49, // 80: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest
53, // 81: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest 51, // 81: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest
56, // 82: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest 53, // 82: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest
59, // 83: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest 56, // 83: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest
62, // 84: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest 59, // 84: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest
65, // 85: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest 62, // 85: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest
68, // 86: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest 65, // 86: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest
70, // 87: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest 68, // 87: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest
72, // 88: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest 70, // 88: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest
74, // 89: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest 72, // 89: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest
75, // 90: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest 74, // 90: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest
76, // 91: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest 75, // 91: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest
80, // 92: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest 76, // 92: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest
83, // 93: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest 80, // 93: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest
86, // 94: hyapp.wallet.v1.WalletService.ApplyWithdrawal:input_type -> hyapp.wallet.v1.ApplyWithdrawalRequest 83, // 94: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest
91, // 95: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest 86, // 95: hyapp.wallet.v1.WalletService.ApplyWithdrawal:input_type -> hyapp.wallet.v1.ApplyWithdrawalRequest
93, // 96: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest 91, // 96: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest
95, // 97: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest 93, // 97: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest
97, // 98: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest 95, // 98: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest
100, // 99: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest 97, // 99: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest
102, // 100: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest 100, // 100: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest
104, // 101: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest 102, // 101: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest
106, // 102: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest 104, // 102: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest
111, // 103: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest 106, // 103: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:input_type -> hyapp.wallet.v1.CreditLuckyGiftRewardRequest
113, // 104: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest 108, // 104: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest
115, // 105: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest 113, // 105: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest
117, // 106: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest 115, // 106: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest
119, // 107: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest 117, // 107: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest
121, // 108: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest 119, // 108: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest
123, // 109: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest 121, // 109: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest
1, // 110: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse 123, // 110: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest
4, // 111: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse 125, // 111: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest
6, // 112: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse 1, // 112: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse
8, // 113: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse 4, // 113: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse
10, // 114: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse 6, // 114: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse
21, // 115: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse 8, // 115: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse
23, // 116: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse 10, // 116: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse
27, // 117: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse 21, // 117: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse
27, // 118: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse 23, // 118: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse
27, // 119: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse 27, // 119: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse
29, // 120: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse 27, // 120: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse
31, // 121: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse 27, // 121: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse
35, // 122: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse 29, // 122: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse
35, // 123: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse 31, // 123: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse
35, // 124: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse 35, // 124: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse
37, // 125: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse 35, // 125: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse
39, // 126: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse 35, // 126: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse
45, // 127: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse 37, // 127: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse
45, // 128: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse 39, // 128: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse
45, // 129: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse 45, // 129: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse
41, // 130: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse 45, // 130: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse
48, // 131: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse 45, // 131: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse
48, // 132: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse 41, // 132: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse
50, // 133: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse 48, // 133: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse
52, // 134: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse 48, // 134: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse
54, // 135: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse 50, // 135: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse
57, // 136: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse 52, // 136: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse
60, // 137: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse 54, // 137: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse
63, // 138: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse 57, // 138: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse
66, // 139: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse 60, // 139: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse
69, // 140: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse 63, // 140: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse
71, // 141: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse 66, // 141: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse
73, // 142: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse 69, // 142: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse
77, // 143: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse 71, // 143: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse
77, // 144: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse 73, // 144: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse
78, // 145: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse 77, // 145: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse
81, // 146: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse 77, // 146: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse
84, // 147: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse 78, // 147: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse
87, // 148: hyapp.wallet.v1.WalletService.ApplyWithdrawal:output_type -> hyapp.wallet.v1.ApplyWithdrawalResponse 81, // 148: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse
92, // 149: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse 84, // 149: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse
94, // 150: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse 87, // 150: hyapp.wallet.v1.WalletService.ApplyWithdrawal:output_type -> hyapp.wallet.v1.ApplyWithdrawalResponse
96, // 151: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse 92, // 151: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse
98, // 152: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse 94, // 152: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse
101, // 153: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse 96, // 153: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse
103, // 154: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse 98, // 154: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse
105, // 155: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse 101, // 155: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse
107, // 156: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse 103, // 156: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse
112, // 157: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse 105, // 157: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse
114, // 158: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse 107, // 158: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:output_type -> hyapp.wallet.v1.CreditLuckyGiftRewardResponse
116, // 159: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse 109, // 159: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse
118, // 160: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse 114, // 160: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse
120, // 161: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse 116, // 161: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse
122, // 162: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse 118, // 162: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse
124, // 163: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse 120, // 163: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse
110, // [110:164] is the sub-list for method output_type 122, // 164: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse
56, // [56:110] is the sub-list for method input_type 124, // 165: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse
56, // [56:56] is the sub-list for extension type_name 126, // 166: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse
56, // [56:56] is the sub-list for extension extendee 112, // [112:167] is the sub-list for method output_type
0, // [0:56] is the sub-list for field type_name 57, // [57:112] is the sub-list for method input_type
57, // [57:57] is the sub-list for extension type_name
57, // [57:57] is the sub-list for extension extendee
0, // [0:57] is the sub-list for field type_name
} }
func init() { file_proto_wallet_v1_wallet_proto_init() } func init() { file_proto_wallet_v1_wallet_proto_init() }
@ -12662,7 +12862,7 @@ func file_proto_wallet_v1_wallet_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 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)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_wallet_v1_wallet_proto_rawDesc), len(file_proto_wallet_v1_wallet_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 125, NumMessages: 127,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -1049,6 +1049,27 @@ message CreditTaskRewardResponse {
int64 granted_at_ms = 4; int64 granted_at_ms = 4;
} }
// CreditLuckyGiftRewardRequest activity-service outbox worker
message CreditLuckyGiftRewardRequest {
string command_id = 1;
string app_code = 2;
int64 target_user_id = 3;
int64 amount = 4;
string draw_id = 5;
string room_id = 6;
string gift_id = 7;
string pool_id = 8;
string reason = 9;
}
// CreditLuckyGiftRewardResponse COIN
message CreditLuckyGiftRewardResponse {
string transaction_id = 1;
AssetBalance balance = 2;
int64 amount = 3;
int64 granted_at_ms = 4;
}
// ApplyGameCoinChangeRequest game-service // ApplyGameCoinChangeRequest game-service
message ApplyGameCoinChangeRequest { message ApplyGameCoinChangeRequest {
string request_id = 1; string request_id = 1;
@ -1274,6 +1295,7 @@ service WalletService {
rpc ListAdminVipLevels(ListAdminVipLevelsRequest) returns (ListAdminVipLevelsResponse); rpc ListAdminVipLevels(ListAdminVipLevelsRequest) returns (ListAdminVipLevelsResponse);
rpc UpdateAdminVipLevels(UpdateAdminVipLevelsRequest) returns (UpdateAdminVipLevelsResponse); rpc UpdateAdminVipLevels(UpdateAdminVipLevelsRequest) returns (UpdateAdminVipLevelsResponse);
rpc CreditTaskReward(CreditTaskRewardRequest) returns (CreditTaskRewardResponse); rpc CreditTaskReward(CreditTaskRewardRequest) returns (CreditTaskRewardResponse);
rpc CreditLuckyGiftReward(CreditLuckyGiftRewardRequest) returns (CreditLuckyGiftRewardResponse);
rpc ApplyGameCoinChange(ApplyGameCoinChangeRequest) returns (ApplyGameCoinChangeResponse); rpc ApplyGameCoinChange(ApplyGameCoinChangeRequest) returns (ApplyGameCoinChangeResponse);
rpc GetRedPacketConfig(GetRedPacketConfigRequest) returns (GetRedPacketConfigResponse); rpc GetRedPacketConfig(GetRedPacketConfigRequest) returns (GetRedPacketConfigResponse);
rpc UpdateRedPacketConfig(UpdateRedPacketConfigRequest) returns (UpdateRedPacketConfigResponse); rpc UpdateRedPacketConfig(UpdateRedPacketConfigRequest) returns (UpdateRedPacketConfigResponse);

View File

@ -65,6 +65,7 @@ const (
WalletService_ListAdminVipLevels_FullMethodName = "/hyapp.wallet.v1.WalletService/ListAdminVipLevels" WalletService_ListAdminVipLevels_FullMethodName = "/hyapp.wallet.v1.WalletService/ListAdminVipLevels"
WalletService_UpdateAdminVipLevels_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateAdminVipLevels" WalletService_UpdateAdminVipLevels_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateAdminVipLevels"
WalletService_CreditTaskReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditTaskReward" WalletService_CreditTaskReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditTaskReward"
WalletService_CreditLuckyGiftReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditLuckyGiftReward"
WalletService_ApplyGameCoinChange_FullMethodName = "/hyapp.wallet.v1.WalletService/ApplyGameCoinChange" WalletService_ApplyGameCoinChange_FullMethodName = "/hyapp.wallet.v1.WalletService/ApplyGameCoinChange"
WalletService_GetRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/GetRedPacketConfig" WalletService_GetRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/GetRedPacketConfig"
WalletService_UpdateRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateRedPacketConfig" WalletService_UpdateRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateRedPacketConfig"
@ -127,6 +128,7 @@ type WalletServiceClient interface {
ListAdminVipLevels(ctx context.Context, in *ListAdminVipLevelsRequest, opts ...grpc.CallOption) (*ListAdminVipLevelsResponse, error) ListAdminVipLevels(ctx context.Context, in *ListAdminVipLevelsRequest, opts ...grpc.CallOption) (*ListAdminVipLevelsResponse, error)
UpdateAdminVipLevels(ctx context.Context, in *UpdateAdminVipLevelsRequest, opts ...grpc.CallOption) (*UpdateAdminVipLevelsResponse, error) UpdateAdminVipLevels(ctx context.Context, in *UpdateAdminVipLevelsRequest, opts ...grpc.CallOption) (*UpdateAdminVipLevelsResponse, error)
CreditTaskReward(ctx context.Context, in *CreditTaskRewardRequest, opts ...grpc.CallOption) (*CreditTaskRewardResponse, error) CreditTaskReward(ctx context.Context, in *CreditTaskRewardRequest, opts ...grpc.CallOption) (*CreditTaskRewardResponse, error)
CreditLuckyGiftReward(ctx context.Context, in *CreditLuckyGiftRewardRequest, opts ...grpc.CallOption) (*CreditLuckyGiftRewardResponse, error)
ApplyGameCoinChange(ctx context.Context, in *ApplyGameCoinChangeRequest, opts ...grpc.CallOption) (*ApplyGameCoinChangeResponse, error) ApplyGameCoinChange(ctx context.Context, in *ApplyGameCoinChangeRequest, opts ...grpc.CallOption) (*ApplyGameCoinChangeResponse, error)
GetRedPacketConfig(ctx context.Context, in *GetRedPacketConfigRequest, opts ...grpc.CallOption) (*GetRedPacketConfigResponse, error) GetRedPacketConfig(ctx context.Context, in *GetRedPacketConfigRequest, opts ...grpc.CallOption) (*GetRedPacketConfigResponse, error)
UpdateRedPacketConfig(ctx context.Context, in *UpdateRedPacketConfigRequest, opts ...grpc.CallOption) (*UpdateRedPacketConfigResponse, error) UpdateRedPacketConfig(ctx context.Context, in *UpdateRedPacketConfigRequest, opts ...grpc.CallOption) (*UpdateRedPacketConfigResponse, error)
@ -605,6 +607,16 @@ func (c *walletServiceClient) CreditTaskReward(ctx context.Context, in *CreditTa
return out, nil return out, nil
} }
func (c *walletServiceClient) CreditLuckyGiftReward(ctx context.Context, in *CreditLuckyGiftRewardRequest, opts ...grpc.CallOption) (*CreditLuckyGiftRewardResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreditLuckyGiftRewardResponse)
err := c.cc.Invoke(ctx, WalletService_CreditLuckyGiftReward_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) { func (c *walletServiceClient) ApplyGameCoinChange(ctx context.Context, in *ApplyGameCoinChangeRequest, opts ...grpc.CallOption) (*ApplyGameCoinChangeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ApplyGameCoinChangeResponse) out := new(ApplyGameCoinChangeResponse)
@ -737,6 +749,7 @@ type WalletServiceServer interface {
ListAdminVipLevels(context.Context, *ListAdminVipLevelsRequest) (*ListAdminVipLevelsResponse, error) ListAdminVipLevels(context.Context, *ListAdminVipLevelsRequest) (*ListAdminVipLevelsResponse, error)
UpdateAdminVipLevels(context.Context, *UpdateAdminVipLevelsRequest) (*UpdateAdminVipLevelsResponse, error) UpdateAdminVipLevels(context.Context, *UpdateAdminVipLevelsRequest) (*UpdateAdminVipLevelsResponse, error)
CreditTaskReward(context.Context, *CreditTaskRewardRequest) (*CreditTaskRewardResponse, error) CreditTaskReward(context.Context, *CreditTaskRewardRequest) (*CreditTaskRewardResponse, error)
CreditLuckyGiftReward(context.Context, *CreditLuckyGiftRewardRequest) (*CreditLuckyGiftRewardResponse, error)
ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error) ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error)
GetRedPacketConfig(context.Context, *GetRedPacketConfigRequest) (*GetRedPacketConfigResponse, error) GetRedPacketConfig(context.Context, *GetRedPacketConfigRequest) (*GetRedPacketConfigResponse, error)
UpdateRedPacketConfig(context.Context, *UpdateRedPacketConfigRequest) (*UpdateRedPacketConfigResponse, error) UpdateRedPacketConfig(context.Context, *UpdateRedPacketConfigRequest) (*UpdateRedPacketConfigResponse, error)
@ -893,6 +906,9 @@ func (UnimplementedWalletServiceServer) UpdateAdminVipLevels(context.Context, *U
func (UnimplementedWalletServiceServer) CreditTaskReward(context.Context, *CreditTaskRewardRequest) (*CreditTaskRewardResponse, error) { func (UnimplementedWalletServiceServer) CreditTaskReward(context.Context, *CreditTaskRewardRequest) (*CreditTaskRewardResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreditTaskReward not implemented") return nil, status.Error(codes.Unimplemented, "method CreditTaskReward not implemented")
} }
func (UnimplementedWalletServiceServer) CreditLuckyGiftReward(context.Context, *CreditLuckyGiftRewardRequest) (*CreditLuckyGiftRewardResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreditLuckyGiftReward not implemented")
}
func (UnimplementedWalletServiceServer) ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error) { func (UnimplementedWalletServiceServer) ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ApplyGameCoinChange not implemented") return nil, status.Error(codes.Unimplemented, "method ApplyGameCoinChange not implemented")
} }
@ -1766,6 +1782,24 @@ func _WalletService_CreditTaskReward_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _WalletService_CreditLuckyGiftReward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreditLuckyGiftRewardRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WalletServiceServer).CreditLuckyGiftReward(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: WalletService_CreditLuckyGiftReward_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WalletServiceServer).CreditLuckyGiftReward(ctx, req.(*CreditLuckyGiftRewardRequest))
}
return interceptor(ctx, in, info, handler)
}
func _WalletService_ApplyGameCoinChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _WalletService_ApplyGameCoinChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ApplyGameCoinChangeRequest) in := new(ApplyGameCoinChangeRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -2101,6 +2135,10 @@ var WalletService_ServiceDesc = grpc.ServiceDesc{
MethodName: "CreditTaskReward", MethodName: "CreditTaskReward",
Handler: _WalletService_CreditTaskReward_Handler, Handler: _WalletService_CreditTaskReward_Handler,
}, },
{
MethodName: "CreditLuckyGiftReward",
Handler: _WalletService_CreditLuckyGiftReward_Handler,
},
{ {
MethodName: "ApplyGameCoinChange", MethodName: "ApplyGameCoinChange",
Handler: _WalletService_ApplyGameCoinChange_Handler, Handler: _WalletService_ApplyGameCoinChange_Handler,

View File

@ -60,19 +60,14 @@ App 不要展示后台 RTP 数值,不要展示实时概率,不要本地推
| --- | --- | | --- | --- |
| `POST /api/v1/rooms/gift/send` 支持 `pool_id` 入参 | 已有 | | `POST /api/v1/rooms/gift/send` 支持 `pool_id` 入参 | 已有 |
| 房间送礼 IM `room_gift_sent` 可携带 `pool_id` | 已有 | | 房间送礼 IM `room_gift_sent` 可携带 `pool_id` | 已有 |
| `POST /api/v1/activities/lucky-gifts/check` | 已有 |
| `activity-service` 内部 gRPC `CheckLuckyGift` / `ExecuteLuckyGiftDraw` | 已有 | | `activity-service` 内部 gRPC `CheckLuckyGift` / `ExecuteLuckyGiftDraw` | 已有 |
| 后台按 `pool_id` 配置多奖池 | 已有 | | 后台按 `pool_id` 配置多奖池 | 已有 |
| 送礼响应直接返回抽奖结果 `data.lucky_gift` | 已有 |
| 房间 IM `lucky_gift_drawn` | 已有 |
| 中奖返奖入账并触发 `WalletBalanceChanged` | 已有 |
Flutter 完整体验还需要后端补齐: 简洁版接入清单见 [幸运礼物接口闭环Flutter对接.md](/Users/hy/Documents/hy/hyapp-server/docs/flutter对接/幸运礼物接口闭环Flutter对接.md)。
| 能力 | 建议 |
| --- | --- |
| App 查询某个奖池是否可抽 | 新增 HTTP `POST /api/v1/activities/lucky-gifts/check` |
| 送礼响应直接返回抽奖结果 | 在 `/rooms/gift/send` 成功 `data` 中增加 `lucky_gift` |
| 中奖房间表现 | 新增房间 IM `lucky_gift_drawn` |
| 返奖到账 | 钱包侧新增幸运礼物返奖入账后继续发 `WalletBalanceChanged` |
下面文档按最终 Flutter 契约编写;已存在字段可直接对接,标注为“建议新增”的字段需要后端继续落地。
## 客户端流程 ## 客户端流程
@ -585,4 +580,3 @@ Payload 示例:
6. 监听房间 IM `room_gift_sent``lucky_gift_drawn`,同步其他用户看到的表现。 6. 监听房间 IM `room_gift_sent``lucky_gift_drawn`,同步其他用户看到的表现。
7. 监听 C2C IM `WalletBalanceChanged`,更新金币余额。 7. 监听 C2C IM `WalletBalanceChanged`,更新金币余额。
8. 任意 IM 丢失、页面重进或余额不一致时,重新拉取礼物面板和钱包余额纠正状态。 8. 任意 IM 丢失、页面重进或余额不一致时,重新拉取礼物面板和钱包余额纠正状态。

View File

@ -0,0 +1,171 @@
# 幸运礼物 Flutter 对接
本文只描述 App 需要接入的 HTTP、送礼返回、错误处理和腾讯云 IM 自定义消息。所有接口响应都使用 gateway 业务 envelope
```json
{
"code": "OK",
"message": "",
"request_id": "req_xxx",
"data": {}
}
```
## 1. 送礼前检查
`POST /api/v1/activities/lucky-gifts/check`
请求头:
| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `Authorization` | 是 | `Bearer <access_token>` |
| `X-App-Code` | 否 | 不传时使用默认 App |
请求体:
| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `room_id` | string | 是 | 当前房间 ID |
| `gift_id` | string | 是 | 实际送出的礼物 ID例如 `rose` |
| `pool_id` | string | 是 | 幸运礼物奖池 ID例如 `lucky``super_lucky` |
返回 `data`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `enabled` | bool | 是否可按幸运礼物发送 |
| `reason` | string | `enabled``not_configured``disabled` |
| `room_id` | string | 请求房间 ID |
| `gift_id` | string | 请求礼物 ID |
| `pool_id` | string | 生效奖池 ID |
| `gift_price` | int | 单次抽奖价格,金币 |
| `rule_version` | int | 后端规则版本 |
| `target_rtp_ppm` | int | RTP 目标ppm |
| `experience_pool` | string | `novice``intermediate``advanced` |
客户端处理:
- `enabled=true`:允许用户继续点击送礼。
- `enabled=false`:不要走幸运礼物送礼;可降级普通礼物或提示当前不可用。
- 检查结果只用于展示和拦截,最终仍以送礼接口为准。
## 2. 发送礼物
`POST /api/v1/rooms/gift/send`
请求体关键字段:
| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `room_id` | string | 是 | 房间 ID |
| `target_user_id` | int/string | 是 | 收礼用户 ID |
| `gift_id` | string | 是 | 礼物 ID例如 `rose` |
| `gift_count` | int | 是 | 数量,必须大于 0 |
| `pool_id` | string | 幸运礼物必填 | `lucky``super_lucky` |
| `client_sequence` | string | 建议 | 客户端幂等序列 |
返回 `data.lucky_gift`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `draw_id` | string | 抽奖 ID客户端去重锚点 |
| `command_id` | string | 送礼命令 ID |
| `pool_id` | string | 生效奖池 |
| `gift_id` | string | 礼物 ID |
| `rule_version` | int | 抽奖规则版本 |
| `experience_pool` | string | 体验池 |
| `selected_tier_id` | string | 命中奖档 |
| `multiplier_ppm` | int | 倍率,`2000000` 表示 2 倍 |
| `base_reward_coins` | int | RTP 基础返奖 |
| `room_atmosphere_reward_coins` | int | 房间气氛池返奖 |
| `activity_subsidy_coins` | int | 活动补贴返奖 |
| `effective_reward_coins` | int | 用户实际获得金币 |
| `reward_status` | string | 初始通常为 `pending`,到账由后端 outbox 补偿 |
| `stage_feedback` | bool | 是否阶段反馈,可播放轻量中奖表现 |
| `high_multiplier` | bool | 是否高倍 |
| `created_at_ms` | int | UTC epoch ms |
客户端处理:
- `data.lucky_gift != null`:立即调用 `_applyLuckyGiftResult()`,播放中奖内容和数量 SVGA。
- `effective_reward_coins > 0`:展示中奖金额;余额不要本地相加,以 `WalletBalanceChanged` 或余额刷新为准。
- `data.lucky_gift == null`:按普通礼物表现处理。
## 3. 错误处理
| code | 场景 | 客户端处理 |
| --- | --- | --- |
| `INVALID_ARGUMENT` | 参数缺失、数量非法、pool_id 缺失 | 停止提交,提示用户重试或刷新礼物面板 |
| `UNAUTHENTICATED` | token 失效 | 重新登录 |
| `INSUFFICIENT_BALANCE` | 金币不足 | 打开充值入口或提示余额不足 |
| `RULE_NOT_ACTIVE` | 幸运礼物规则未启用 | 降级普通礼物或刷新礼物配置 |
| `UPSTREAM_ERROR`/`UNAVAILABLE` | 内部服务或 IM/钱包临时不可用 | 不重复扣本地余额;允许用户稍后重试 |
| `IDEMPOTENCY_CONFLICT`/`CONFLICT` | 同一客户端命令参数变化 | 重新生成 `client_sequence` 后再提交 |
送礼失败时后端不会返回 `lucky_gift`,客户端不要播放中奖 UI。送礼成功但返奖到账延迟时房间 IM 和钱包 IM 会继续补偿。
## 4. 房间 IM
腾讯云 IM 群自定义消息:
| 字段 | 值 |
| --- | --- |
| `MsgType` | `TIMCustomElem` |
| `Desc` | `lucky_gift_drawn` |
| `Ext` | `room_system_message` |
| `Data` | JSON 字符串 |
`Data` 示例:
```json
{
"event_type": "lucky_gift_drawn",
"event_id": "lucky_gift_drawn:lucky_draw_xxx",
"draw_id": "lucky_draw_xxx",
"command_id": "cmd_xxx",
"pool_id": "super_lucky",
"room_id": "room_1",
"gift_id": "rose",
"gift_count": 1,
"user_id": 42,
"sender_user_id": 42,
"target_user_id": 99,
"coin_spent": 100,
"multiplier_ppm": 2000000,
"effective_reward_coins": 200,
"stage_feedback": true,
"high_multiplier": false,
"created_at_ms": 1760000000000
}
```
处理规则:
- 只处理 `Desc == "lucky_gift_drawn"``event_type == "lucky_gift_drawn"`
- 用 `event_id``draw_id` 去重;同一抽奖不要重复播放。
- 当前用户已通过 HTTP 送礼响应播放过同一 `draw_id` 时,收到 IM 只更新状态,不重复播放。
## 5. 钱包余额 IM
中奖金币到账后wallet-service 会写 `WalletBalanceChanged`notice-service 通过腾讯云 IM C2C 投递。
| 字段 | 值 |
| --- | --- |
| `MsgType` | `TIMCustomElem` |
| `Desc` | `WalletBalanceChanged` |
| `Ext` | `wallet_notice` |
客户端处理:
- 只处理 `Desc == "WalletBalanceChanged"`
- 用 `event_id` 去重。
- 当 `asset_type == "COIN"` 时,用 `available_after` 更新金币余额;字段缺失时调用 `GET /api/v1/wallet/me/balances` 刷新。
## 6. 接入顺序
1. 礼物面板拿到 `pool_id` 后调用 check。
2. check 可用后调用送礼接口,并把 `pool_id` 放入请求。
3. 送礼响应含 `data.lucky_gift` 时立即播放本机中奖 UI。
4. 房间内监听 `lucky_gift_drawn`,给其他用户同步中奖 UI。
5. 私聊监听 `WalletBalanceChanged`,更新金币余额。

View File

@ -18,8 +18,11 @@
| 风控计数 | done | user hour/day、device day、room hour、anchor day | | 风控计数 | done | user hour/day、device day、room hour、anchor day |
| 房间气氛池 | done | 独立预算,不污染基础 RTP | | 房间气氛池 | done | 独立预算,不污染基础 RTP |
| 活动预算池 | done | 独立补贴,不污染基础 RTP | | 活动预算池 | done | 独立补贴,不污染基础 RTP |
| 返奖入账 | done | `activity_outbox` worker 调 `wallet-service.CreditLuckyGiftReward`,钱包 outbox 继续触发余额私信 |
| 房间中奖 IM | done | `activity_outbox` worker 投递腾讯云 IM 群消息 `lucky_gift_drawn` |
| App 检查接口 | done | gateway 暴露 `POST /api/v1/activities/lucky-gifts/check` |
暂不在 `activity-service` 事务内调用钱包或房间 RPC。抽奖事务只写事实和 `activity_outbox`;后续由 outbox dispatcher 或房间链路消费结果。 `activity-service` 抽奖事务内仍不调用钱包、房间 RPC 或腾讯云 IM。抽奖事务只写事实和 `activity_outbox`;事务外由 lucky gift worker 抢占 outbox先幂等返奖再投递房间中奖 IM最后标记 draw 和 outbox 状态
## Call Flow ## Call Flow
@ -27,9 +30,12 @@
room-service SendGift room-service SendGift
1. CheckLuckyGift(meta, user_id, room_id, gift_id, pool_id) 1. CheckLuckyGift(meta, user_id, room_id, gift_id, pool_id)
2. wallet-service 扣费成功 2. wallet-service 扣费成功
3. ExecuteLuckyGiftDraw(command_id, user_id, device_id, room_id, anchor_id, gift_id, pool_id, coin_spent, paid_at_ms) 3. ExecuteLuckyGiftDraw(command_id, user_id, target_user_id, gift_count, device_id, room_id, anchor_id, gift_id, pool_id, coin_spent, paid_at_ms)
4. activity-service 本地事务写 draw fact + outbox 4. activity-service 本地事务写 draw fact + outbox
5. 事务后由钱包/房间表现链路处理 reward_status=pending 的结果 5. lucky gift worker 幂等调用 wallet-service.CreditLuckyGiftReward
6. wallet-service 写 WalletBalanceChangednotice-service 投递私信余额通知
7. lucky gift worker 投递房间群 IM lucky_gift_drawn
8. activity-service 标记 reward_status=granted、outbox=delivered
``` ```
`command_id` 是抽奖幂等键。重复 `ExecuteLuckyGiftDraw` 会返回同一条 `lucky_draw_records`,不会重复消耗 RTP、奖池或预算。 `command_id` 是抽奖幂等键。重复 `ExecuteLuckyGiftDraw` 会返回同一条 `lucky_draw_records`,不会重复消耗 RTP、奖池或预算。

View File

@ -17,6 +17,13 @@ first_recharge_reward_worker:
enabled: false enabled: false
red_packet_broadcast_worker: red_packet_broadcast_worker:
enabled: false enabled: false
lucky_gift_worker:
enabled: true
worker_poll_interval: "1s"
worker_batch_size: 100
worker_lock_ttl: "30s"
worker_max_retry: 8
publish_timeout: "5s"
tencent_im: tencent_im:
# Docker 本地联调全局/区域播报群;必须与 gateway/room-service 使用同一组腾讯 IM 应用配置。 # Docker 本地联调全局/区域播报群;必须与 gateway/room-service 使用同一组腾讯 IM 应用配置。
enabled: true enabled: true

View File

@ -17,6 +17,13 @@ first_recharge_reward_worker:
enabled: true enabled: true
red_packet_broadcast_worker: red_packet_broadcast_worker:
enabled: true enabled: true
lucky_gift_worker:
enabled: true
worker_poll_interval: "1s"
worker_batch_size: 100
worker_lock_ttl: "30s"
worker_max_retry: 8
publish_timeout: "5s"
tencent_im: tencent_im:
# 腾讯云 IM 应用配置;必须和 gateway 的 UserSig、room-service 房间群配置属于同一个 SDKAppID。 # 腾讯云 IM 应用配置;必须和 gateway 的 UserSig、room-service 房间群配置属于同一个 SDKAppID。
enabled: true enabled: true

View File

@ -17,6 +17,13 @@ first_recharge_reward_worker:
enabled: false enabled: false
red_packet_broadcast_worker: red_packet_broadcast_worker:
enabled: false enabled: false
lucky_gift_worker:
enabled: true
worker_poll_interval: "1s"
worker_batch_size: 100
worker_lock_ttl: "30s"
worker_max_retry: 8
publish_timeout: "5s"
tencent_im: tencent_im:
# activity-service 只负责全局/区域播报群;必须与 gateway/room-service 使用同一组腾讯 IM 应用配置。 # activity-service 只负责全局/区域播报群;必须与 gateway/room-service 使用同一组腾讯 IM 应用配置。
enabled: true enabled: true

View File

@ -38,6 +38,9 @@ CREATE TABLE IF NOT EXISTS activity_outbox (
status VARCHAR(32) NOT NULL COMMENT '业务状态', status VARCHAR(32) NOT NULL COMMENT '业务状态',
retry_count INT NOT NULL DEFAULT 0 COMMENT '重试次数', retry_count INT NOT NULL DEFAULT 0 COMMENT '重试次数',
next_retry_at_ms BIGINT NOT NULL COMMENT '下一次重试时间UTC epoch ms', next_retry_at_ms BIGINT NOT NULL COMMENT '下一次重试时间UTC epoch ms',
locked_by VARCHAR(128) NOT NULL DEFAULT '' COMMENT '当前锁定 worker',
lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '锁过期时间UTC epoch ms',
last_error VARCHAR(512) NOT NULL DEFAULT '' COMMENT '最后一次失败原因',
created_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', updated_at_ms BIGINT NOT NULL COMMENT '更新时间UTC epoch ms',
PRIMARY KEY (app_code, outbox_id), PRIMARY KEY (app_code, outbox_id),
@ -194,6 +197,8 @@ CREATE TABLE IF NOT EXISTS lucky_draw_records (
pool_snapshot_json JSON NOT NULL COMMENT '奖池快照', pool_snapshot_json JSON NOT NULL COMMENT '奖池快照',
rtp_snapshot_json JSON NOT NULL COMMENT 'RTP 快照', rtp_snapshot_json JSON NOT NULL COMMENT 'RTP 快照',
reward_status VARCHAR(32) NOT NULL COMMENT 'pending/granted/failed', reward_status VARCHAR(32) NOT NULL COMMENT 'pending/granted/failed',
reward_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '返奖钱包交易 ID',
reward_failure_reason VARCHAR(255) NOT NULL DEFAULT '' COMMENT '返奖失败原因',
paid_at_ms BIGINT NOT NULL COMMENT '扣费时间UTC epoch ms', paid_at_ms BIGINT NOT NULL COMMENT '扣费时间UTC epoch ms',
created_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', updated_at_ms BIGINT NOT NULL COMMENT '更新时间UTC epoch ms',

View File

@ -49,9 +49,12 @@ type App struct {
healthHTTP *healthhttp.Server healthHTTP *healthhttp.Server
mysqlRepo *mysqlstorage.Repository mysqlRepo *mysqlstorage.Repository
broadcast *broadcastservice.Service broadcast *broadcastservice.Service
luckyGift *luckygiftservice.Service
firstRechargeReward *firstrechargeservice.Service firstRechargeReward *firstrechargeservice.Service
broadcastWorkerEnabled bool broadcastWorkerEnabled bool
luckyGiftWorkerEnabled bool
workerNodeID string workerNodeID string
luckyGiftWorkerOptions luckygiftservice.WorkerOptions
mqConsumers []*rocketmqx.Consumer mqConsumers []*rocketmqx.Consumer
userConn *grpc.ClientConn userConn *grpc.ClientConn
walletConn *grpc.ClientConn walletConn *grpc.ClientConn
@ -105,10 +108,10 @@ func New(cfg config.Config) (*App, error) {
sevenDayCheckInSvc := sevendaycheckinservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) sevenDayCheckInSvc := sevendaycheckinservice.New(repository, walletv1.NewWalletServiceClient(walletConn))
growthSvc := growthservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) growthSvc := growthservice.New(repository, walletv1.NewWalletServiceClient(walletConn))
achievementSvc := achievementservice.New(repository, walletv1.NewWalletServiceClient(walletConn)) achievementSvc := achievementservice.New(repository, walletv1.NewWalletServiceClient(walletConn))
luckyGiftSvc := luckygiftservice.New(repository) var tencentClient *tencentim.RESTClient
var broadcastPublisher broadcastservice.Publisher var broadcastPublisher broadcastservice.Publisher
if cfg.TencentIM.Enabled { if cfg.TencentIM.Enabled {
tencentClient, err := tencentim.NewRESTClient(cfg.TencentIM.RESTConfig()) tencentClient, err = tencentim.NewRESTClient(cfg.TencentIM.RESTConfig())
if err != nil { if err != nil {
_ = walletConn.Close() _ = walletConn.Close()
_ = userConn.Close() _ = userConn.Close()
@ -118,6 +121,10 @@ func New(cfg config.Config) (*App, error) {
} }
broadcastPublisher = tencentClient broadcastPublisher = tencentClient
} }
luckyGiftSvc := luckygiftservice.New(repository,
luckygiftservice.WithWallet(walletv1.NewWalletServiceClient(walletConn)),
luckygiftservice.WithRoomPublisher(tencentClient),
)
if cfg.Broadcast.Enabled && broadcastPublisher == nil { if cfg.Broadcast.Enabled && broadcastPublisher == nil {
_ = walletConn.Close() _ = walletConn.Close()
_ = userConn.Close() _ = userConn.Close()
@ -263,9 +270,12 @@ func New(cfg config.Config) (*App, error) {
healthHTTP: healthHTTP, healthHTTP: healthHTTP,
mysqlRepo: repository, mysqlRepo: repository,
broadcast: broadcastSvc, broadcast: broadcastSvc,
luckyGift: luckyGiftSvc,
firstRechargeReward: firstRechargeRewardSvc, firstRechargeReward: firstRechargeRewardSvc,
broadcastWorkerEnabled: cfg.Broadcast.Enabled, broadcastWorkerEnabled: cfg.Broadcast.Enabled,
luckyGiftWorkerEnabled: cfg.LuckyGiftWorker.Enabled,
workerNodeID: cfg.NodeID, workerNodeID: cfg.NodeID,
luckyGiftWorkerOptions: luckyGiftWorkerOptions(cfg.NodeID, cfg.LuckyGiftWorker),
mqConsumers: mqConsumers, mqConsumers: mqConsumers,
userConn: userConn, userConn: userConn,
walletConn: walletConn, walletConn: walletConn,
@ -287,6 +297,11 @@ func (a *App) Run() error {
a.broadcast.RunWorker(a.workerCtx, broadcastservice.WorkerOptions{}) a.broadcast.RunWorker(a.workerCtx, broadcastservice.WorkerOptions{})
}) })
} }
if a.luckyGiftWorkerEnabled && a.luckyGift != nil && a.workerCtx != nil {
a.workerWG.Go(func() {
a.luckyGift.RunWorker(a.workerCtx, a.luckyGiftWorkerOptions)
})
}
err := a.server.Serve(a.listener) err := a.server.Serve(a.listener)
if a.workerStop != nil { if a.workerStop != nil {
a.workerStop() a.workerStop()
@ -481,3 +496,14 @@ func redPacketRegionIDFromWalletPayload(payload string) int64 {
return 0 return 0
} }
} }
func luckyGiftWorkerOptions(nodeID string, cfg config.LuckyGiftWorkerConfig) luckygiftservice.WorkerOptions {
return luckygiftservice.WorkerOptions{
WorkerID: nodeID + "-lucky-gift",
PollInterval: cfg.WorkerPollInterval,
BatchSize: cfg.WorkerBatchSize,
LockTTL: cfg.WorkerLockTTL,
MaxRetry: cfg.WorkerMaxRetry,
PublishTimeout: cfg.PublishTimeout,
}
}

View File

@ -29,6 +29,8 @@ type Config struct {
FirstRechargeRewardWorker FirstRechargeRewardWorkerConfig `yaml:"first_recharge_reward_worker"` FirstRechargeRewardWorker FirstRechargeRewardWorkerConfig `yaml:"first_recharge_reward_worker"`
// RedPacketBroadcastWorker 控制红包创建事实转区域飘屏 outbox 的本地消费。 // RedPacketBroadcastWorker 控制红包创建事实转区域飘屏 outbox 的本地消费。
RedPacketBroadcastWorker RedPacketBroadcastWorkerConfig `yaml:"red_packet_broadcast_worker"` RedPacketBroadcastWorker RedPacketBroadcastWorkerConfig `yaml:"red_packet_broadcast_worker"`
// LuckyGiftWorker 控制幸运礼物 draw outbox 的返奖和房间 IM 补偿。
LuckyGiftWorker LuckyGiftWorkerConfig `yaml:"lucky_gift_worker"`
// TencentIM 是 activity-service 发送全局/区域播报群消息的 REST 配置。 // TencentIM 是 activity-service 发送全局/区域播报群消息的 REST 配置。
TencentIM TencentIMConfig `yaml:"tencent_im"` TencentIM TencentIMConfig `yaml:"tencent_im"`
// Broadcast 控制 IM 播报群 reconciler、outbox worker 和贵重礼物阈值。 // Broadcast 控制 IM 播报群 reconciler、outbox worker 和贵重礼物阈值。
@ -65,6 +67,16 @@ type RedPacketBroadcastWorkerConfig struct {
Enabled bool `yaml:"enabled"` Enabled bool `yaml:"enabled"`
} }
// LuckyGiftWorkerConfig 保存幸运礼物抽奖副作用补偿策略。
type LuckyGiftWorkerConfig struct {
Enabled bool `yaml:"enabled"`
WorkerPollInterval time.Duration `yaml:"worker_poll_interval"`
WorkerBatchSize int `yaml:"worker_batch_size"`
WorkerLockTTL time.Duration `yaml:"worker_lock_ttl"`
WorkerMaxRetry int `yaml:"worker_max_retry"`
PublishTimeout time.Duration `yaml:"publish_timeout"`
}
// TencentIMConfig 描述 activity-service 调用腾讯云 IM REST API 的配置。 // TencentIMConfig 描述 activity-service 调用腾讯云 IM REST API 的配置。
type TencentIMConfig struct { type TencentIMConfig struct {
Enabled bool `yaml:"enabled"` Enabled bool `yaml:"enabled"`
@ -153,6 +165,14 @@ func Default() Config {
RedPacketBroadcastWorker: RedPacketBroadcastWorkerConfig{ RedPacketBroadcastWorker: RedPacketBroadcastWorkerConfig{
Enabled: false, Enabled: false,
}, },
LuckyGiftWorker: LuckyGiftWorkerConfig{
Enabled: false,
WorkerPollInterval: time.Second,
WorkerBatchSize: 100,
WorkerLockTTL: 30 * time.Second,
WorkerMaxRetry: 8,
PublishTimeout: 5 * time.Second,
},
TencentIM: TencentIMConfig{ TencentIM: TencentIMConfig{
Enabled: false, Enabled: false,
AdminIdentifier: "administrator", AdminIdentifier: "administrator",
@ -271,6 +291,21 @@ func Load(path string) (Config, error) {
if cfg.Broadcast.WorkerMaxRetry <= 0 { if cfg.Broadcast.WorkerMaxRetry <= 0 {
cfg.Broadcast.WorkerMaxRetry = 8 cfg.Broadcast.WorkerMaxRetry = 8
} }
if cfg.LuckyGiftWorker.WorkerPollInterval <= 0 {
cfg.LuckyGiftWorker.WorkerPollInterval = time.Second
}
if cfg.LuckyGiftWorker.WorkerBatchSize <= 0 {
cfg.LuckyGiftWorker.WorkerBatchSize = 100
}
if cfg.LuckyGiftWorker.WorkerLockTTL <= 0 {
cfg.LuckyGiftWorker.WorkerLockTTL = 30 * time.Second
}
if cfg.LuckyGiftWorker.WorkerMaxRetry <= 0 {
cfg.LuckyGiftWorker.WorkerMaxRetry = 8
}
if cfg.LuckyGiftWorker.PublishTimeout <= 0 {
cfg.LuckyGiftWorker.PublishTimeout = 5 * time.Second
}
rocketMQ, err := normalizeRocketMQConfig(cfg.RocketMQ) rocketMQ, err := normalizeRocketMQConfig(cfg.RocketMQ)
if err != nil { if err != nil {
return Config{}, err return Config{}, err
@ -279,6 +314,9 @@ func Load(path string) (Config, error) {
if (cfg.FirstRechargeRewardWorker.Enabled || cfg.RedPacketBroadcastWorker.Enabled) && !cfg.RocketMQ.WalletOutbox.Enabled { if (cfg.FirstRechargeRewardWorker.Enabled || cfg.RedPacketBroadcastWorker.Enabled) && !cfg.RocketMQ.WalletOutbox.Enabled {
return Config{}, errors.New("wallet outbox workers require rocketmq.wallet_outbox.enabled") return Config{}, errors.New("wallet outbox workers require rocketmq.wallet_outbox.enabled")
} }
if cfg.LuckyGiftWorker.Enabled && !cfg.TencentIM.Enabled {
return Config{}, errors.New("lucky gift worker requires tencent_im.enabled")
}
return cfg, nil return cfg, nil
} }

View File

@ -8,6 +8,8 @@ const (
StatusPaused = "paused" StatusPaused = "paused"
StatusClosed = "closed" StatusClosed = "closed"
StatusPending = "pending" StatusPending = "pending"
StatusGranted = "granted"
StatusFailed = "failed"
PoolNovice = "novice" PoolNovice = "novice"
PoolIntermediate = "intermediate" PoolIntermediate = "intermediate"
@ -82,15 +84,17 @@ type CheckCommand struct {
} }
type DrawCommand struct { type DrawCommand struct {
CommandID string CommandID string
PoolID string PoolID string
UserID int64 UserID int64
DeviceID string TargetUserID int64
RoomID string DeviceID string
AnchorID string RoomID string
GiftID string AnchorID string
CoinSpent int64 GiftID string
PaidAtMS int64 GiftCount int32
CoinSpent int64
PaidAtMS int64
} }
type CheckResult struct { type CheckResult struct {
@ -157,3 +161,13 @@ type DrawQuery struct {
Page int32 Page int32
PageSize int32 PageSize int32
} }
// DrawOutbox 是幸运礼物抽奖事务写出的异步副作用载体worker 只消费这个持久事实。
type DrawOutbox struct {
AppCode string
OutboxID string
EventType string
PayloadJSON string
RetryCount int
CreatedAtMS int64
}

View File

@ -2,10 +2,17 @@ package luckygift
import ( import (
"context" "context"
"encoding/json"
"fmt"
"log/slog"
"strings" "strings"
"time" "time"
"google.golang.org/grpc"
walletv1 "hyapp.local/api/proto/wallet/v1"
"hyapp/pkg/appcode" "hyapp/pkg/appcode"
"hyapp/pkg/logx"
"hyapp/pkg/tencentim"
"hyapp/pkg/xerr" "hyapp/pkg/xerr"
domain "hyapp/services/activity-service/internal/domain/luckygift" domain "hyapp/services/activity-service/internal/domain/luckygift"
) )
@ -19,16 +26,54 @@ type Repository interface {
ExecuteLuckyGiftDraw(ctx context.Context, cmd domain.DrawCommand, nowMS int64) (domain.DrawResult, error) ExecuteLuckyGiftDraw(ctx context.Context, cmd domain.DrawCommand, nowMS int64) (domain.DrawResult, error)
ListLuckyGiftDraws(ctx context.Context, query domain.DrawQuery) ([]domain.DrawResult, int64, error) ListLuckyGiftDraws(ctx context.Context, query domain.DrawQuery) ([]domain.DrawResult, int64, error)
GetLuckyGiftDrawSummary(ctx context.Context, query domain.DrawQuery) (domain.DrawSummary, error) GetLuckyGiftDrawSummary(ctx context.Context, query domain.DrawQuery) (domain.DrawSummary, error)
ClaimPendingLuckyGiftOutbox(ctx context.Context, workerID string, nowMS int64, lockTTL time.Duration, batchSize int) ([]domain.DrawOutbox, error)
MarkLuckyGiftOutboxDelivered(ctx context.Context, event domain.DrawOutbox, nowMS int64) error
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
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
}
// WalletClient 是幸运礼物返奖唯一账务依赖;实际余额变更通知继续由 wallet outbox -> notice-service 投递。
type WalletClient interface {
CreditLuckyGiftReward(ctx context.Context, req *walletv1.CreditLuckyGiftRewardRequest, opts ...grpc.CallOption) (*walletv1.CreditLuckyGiftRewardResponse, error)
}
// RoomPublisher 只负责投递房间群自定义消息,不拥有房间状态。
type RoomPublisher interface {
PublishGroupCustomMessage(ctx context.Context, message tencentim.CustomGroupMessage) error
} }
// Service 承载幸运礼物查询、抽奖命令和后台规则配置。 // Service 承载幸运礼物查询、抽奖命令和后台规则配置。
type Service struct { type Service struct {
repository Repository repository Repository
wallet WalletClient
publisher RoomPublisher
now func() time.Time now func() time.Time
} }
func New(repository Repository) *Service { type Option func(*Service)
return &Service{repository: repository, now: time.Now}
func WithWallet(client WalletClient) Option {
return func(s *Service) {
s.wallet = client
}
}
func WithRoomPublisher(publisher RoomPublisher) Option {
return func(s *Service) {
s.publisher = publisher
}
}
func New(repository Repository, options ...Option) *Service {
service := &Service{repository: repository, now: time.Now}
for _, option := range options {
if option != nil {
option(service)
}
}
return service
} }
func (s *Service) SetClock(now func() time.Time) { func (s *Service) SetClock(now func() time.Time) {
@ -63,12 +108,131 @@ func (s *Service) Draw(ctx context.Context, cmd domain.DrawCommand) (domain.Draw
if cmd.CommandID == "" || cmd.UserID <= 0 || cmd.DeviceID == "" || cmd.RoomID == "" || cmd.AnchorID == "" || cmd.GiftID == "" || cmd.CoinSpent <= 0 { if cmd.CommandID == "" || cmd.UserID <= 0 || cmd.DeviceID == "" || cmd.RoomID == "" || cmd.AnchorID == "" || cmd.GiftID == "" || cmd.CoinSpent <= 0 {
return domain.DrawResult{}, xerr.New(xerr.InvalidArgument, "lucky gift draw command is incomplete") return domain.DrawResult{}, xerr.New(xerr.InvalidArgument, "lucky gift draw command is incomplete")
} }
if cmd.TargetUserID < 0 || cmd.GiftCount < 0 {
return domain.DrawResult{}, xerr.New(xerr.InvalidArgument, "lucky gift draw metadata is invalid")
}
if cmd.PaidAtMS <= 0 { if cmd.PaidAtMS <= 0 {
cmd.PaidAtMS = s.now().UTC().UnixMilli() cmd.PaidAtMS = s.now().UTC().UnixMilli()
} }
return s.repository.ExecuteLuckyGiftDraw(ctx, cmd, s.now().UTC().UnixMilli()) return s.repository.ExecuteLuckyGiftDraw(ctx, cmd, s.now().UTC().UnixMilli())
} }
type WorkerOptions struct {
WorkerID string
PollInterval time.Duration
BatchSize int
LockTTL time.Duration
MaxRetry int
PublishTimeout time.Duration
}
func (s *Service) RunWorker(ctx context.Context, options WorkerOptions) {
options = normalizeWorkerOptions(options)
ticker := time.NewTicker(options.PollInterval)
defer ticker.Stop()
for {
if _, err := s.ProcessPendingDrawOutbox(ctx, options); err != nil && ctx.Err() == nil {
// 批处理失败不删除 outbox锁过期后下一轮或其他实例会继续补偿。
logx.Error(ctx, "lucky_gift_outbox_batch_failed", err, slog.String("worker_id", options.WorkerID))
}
select {
case <-ctx.Done():
return
case <-ticker.C:
}
}
}
func (s *Service) ProcessPendingDrawOutbox(ctx context.Context, options WorkerOptions) (int, error) {
if err := s.requireRepository(); err != nil {
return 0, err
}
options = normalizeWorkerOptions(options)
events, err := s.repository.ClaimPendingLuckyGiftOutbox(ctx, options.WorkerID, s.now().UTC().UnixMilli(), options.LockTTL, options.BatchSize)
if err != nil {
return 0, err
}
processed := 0
for _, event := range events {
if err := s.processDrawOutbox(ctx, event, options); err != nil {
return processed, err
}
processed++
}
return processed, nil
}
func (s *Service) processDrawOutbox(ctx context.Context, event domain.DrawOutbox, options WorkerOptions) error {
payload, err := decodeDrawOutboxPayload(event.PayloadJSON)
if err != nil {
return s.markOutboxFailed(ctx, event, options, err, false)
}
eventCtx := appcode.WithContext(ctx, event.AppCode)
walletTransactionID := ""
credited := false
if payload.EffectiveRewardCoins > 0 {
if s.wallet == nil {
return s.markOutboxFailed(eventCtx, event, options, fmt.Errorf("lucky gift wallet client is not configured"), false)
}
resp, err := s.wallet.CreditLuckyGiftReward(eventCtx, &walletv1.CreditLuckyGiftRewardRequest{
CommandId: "lucky_reward:" + payload.DrawID,
AppCode: event.AppCode,
TargetUserId: payload.UserID,
Amount: payload.EffectiveRewardCoins,
DrawId: payload.DrawID,
RoomId: payload.RoomID,
GiftId: payload.GiftID,
PoolId: payload.PoolID,
Reason: "lucky_gift_reward",
})
if err != nil {
return s.markOutboxFailed(eventCtx, event, options, err, false)
}
walletTransactionID = resp.GetTransactionId()
credited = true
}
if s.publisher == nil {
return s.markOutboxFailed(eventCtx, event, options, fmt.Errorf("lucky gift room publisher is not configured"), credited)
}
if err := s.publishLuckyGiftDrawn(eventCtx, payload, options); err != nil {
return s.markOutboxFailed(eventCtx, event, options, err, credited)
}
nowMS := s.now().UTC().UnixMilli()
if err := s.repository.MarkLuckyGiftDrawGranted(eventCtx, event.AppCode, payload.DrawID, walletTransactionID, nowMS); err != nil {
return err
}
return s.repository.MarkLuckyGiftOutboxDelivered(eventCtx, event, nowMS)
}
func (s *Service) publishLuckyGiftDrawn(ctx context.Context, payload luckyGiftDrawnPayload, options WorkerOptions) error {
body, err := json.Marshal(payload)
if err != nil {
return err
}
publishCtx, cancel := context.WithTimeout(ctx, options.PublishTimeout)
defer cancel()
return s.publisher.PublishGroupCustomMessage(publishCtx, tencentim.CustomGroupMessage{
GroupID: payload.RoomID,
EventID: payload.EventID,
Desc: "lucky_gift_drawn",
Ext: "room_system_message",
PayloadJSON: body,
})
}
func (s *Service) markOutboxFailed(ctx context.Context, event domain.DrawOutbox, options WorkerOptions, cause error, alreadyCredited bool) error {
nowMS := s.now().UTC().UnixMilli()
nextRetryCount := event.RetryCount + 1
if nextRetryCount >= options.MaxRetry {
if !alreadyCredited {
_ = s.repository.MarkLuckyGiftDrawFailed(ctx, event.AppCode, drawIDFromPayload(event.PayloadJSON), cause.Error(), nowMS)
}
return s.repository.MarkLuckyGiftOutboxFailed(ctx, event, nextRetryCount, cause.Error(), nowMS)
}
nextRetryAtMS := nowMS + luckyGiftBackoff(nextRetryCount).Milliseconds()
return s.repository.MarkLuckyGiftOutboxRetryable(ctx, event, nextRetryCount, nextRetryAtMS, cause.Error(), nowMS)
}
func (s *Service) GetConfig(ctx context.Context, poolID string) (domain.Config, error) { func (s *Service) GetConfig(ctx context.Context, poolID string) (domain.Config, error) {
if err := s.requireRepository(); err != nil { if err := s.requireRepository(); err != nil {
return domain.Config{}, err return domain.Config{}, err
@ -143,3 +307,94 @@ func normalizeDrawQuery(query domain.DrawQuery) domain.DrawQuery {
query.Status = strings.TrimSpace(query.Status) query.Status = strings.TrimSpace(query.Status)
return query return query
} }
type luckyGiftDrawnPayload struct {
EventType string `json:"event_type"`
EventID string `json:"event_id"`
AppCode string `json:"app_code"`
DrawID string `json:"draw_id"`
CommandID string `json:"command_id"`
PoolID string `json:"pool_id"`
RoomID string `json:"room_id"`
GiftID string `json:"gift_id"`
GiftCount int32 `json:"gift_count"`
UserID int64 `json:"user_id"`
SenderUserID int64 `json:"sender_user_id"`
TargetUserID int64 `json:"target_user_id"`
CoinSpent int64 `json:"coin_spent"`
RuleVersion int64 `json:"rule_version"`
ExperiencePool string `json:"experience_pool"`
SelectedTierID string `json:"selected_tier_id"`
MultiplierPPM int64 `json:"multiplier_ppm"`
BaseRewardCoins int64 `json:"base_reward_coins"`
RoomAtmosphereRewardCoins int64 `json:"room_atmosphere_reward_coins"`
ActivitySubsidyCoins int64 `json:"activity_subsidy_coins"`
EffectiveRewardCoins int64 `json:"effective_reward_coins"`
StageFeedback bool `json:"stage_feedback"`
HighMultiplier bool `json:"high_multiplier"`
CreatedAtMS int64 `json:"created_at_ms"`
}
func decodeDrawOutboxPayload(payloadJSON string) (luckyGiftDrawnPayload, error) {
var payload luckyGiftDrawnPayload
if err := json.Unmarshal([]byte(payloadJSON), &payload); err != nil {
return luckyGiftDrawnPayload{}, fmt.Errorf("invalid lucky gift outbox payload: %w", err)
}
if payload.EventType == "" {
payload.EventType = "lucky_gift_drawn"
}
if payload.EventID == "" && payload.DrawID != "" {
payload.EventID = "lucky_gift_drawn:" + payload.DrawID
}
if payload.SenderUserID == 0 {
payload.SenderUserID = payload.UserID
}
if payload.DrawID == "" || payload.EventID == "" || payload.RoomID == "" || payload.GiftID == "" || payload.UserID <= 0 {
return luckyGiftDrawnPayload{}, fmt.Errorf("lucky gift outbox payload is incomplete")
}
return payload, nil
}
func drawIDFromPayload(payloadJSON string) string {
payload, err := decodeDrawOutboxPayload(payloadJSON)
if err != nil {
return ""
}
return payload.DrawID
}
func normalizeWorkerOptions(options WorkerOptions) WorkerOptions {
options.WorkerID = strings.TrimSpace(options.WorkerID)
if options.WorkerID == "" {
options.WorkerID = "lucky-gift-worker"
}
if options.PollInterval <= 0 {
options.PollInterval = time.Second
}
if options.BatchSize <= 0 {
options.BatchSize = 100
}
if options.BatchSize > 500 {
options.BatchSize = 500
}
if options.LockTTL <= 0 {
options.LockTTL = 30 * time.Second
}
if options.MaxRetry <= 0 {
options.MaxRetry = 8
}
if options.PublishTimeout <= 0 {
options.PublishTimeout = 5 * time.Second
}
return options
}
func luckyGiftBackoff(retryCount int) time.Duration {
if retryCount < 1 {
retryCount = 1
}
if retryCount > 6 {
retryCount = 6
}
return time.Duration(1<<retryCount) * time.Second
}

View File

@ -2,9 +2,14 @@ package luckygift
import ( import (
"context" "context"
"encoding/json"
"testing" "testing"
"time"
"google.golang.org/grpc"
walletv1 "hyapp.local/api/proto/wallet/v1"
"hyapp/pkg/appcode" "hyapp/pkg/appcode"
"hyapp/pkg/tencentim"
domain "hyapp/services/activity-service/internal/domain/luckygift" domain "hyapp/services/activity-service/internal/domain/luckygift"
) )
@ -38,9 +43,64 @@ func TestUpsertConfigPreservesPoolScope(t *testing.T) {
} }
} }
func TestProcessPendingDrawOutboxCreditsWalletAndPublishesRoomIM(t *testing.T) {
payload, _ := json.Marshal(map[string]any{
"event_type": "lucky_gift_drawn",
"event_id": "lucky_gift_drawn:lucky_draw_test",
"app_code": "lalu",
"draw_id": "lucky_draw_test",
"command_id": "cmd-gift",
"pool_id": "super_lucky",
"user_id": 42,
"sender_user_id": 42,
"target_user_id": 99,
"room_id": "room-1",
"gift_id": "rose",
"gift_count": 1,
"coin_spent": 100,
"effective_reward_coins": 300,
"multiplier_ppm": 3000000,
"stage_feedback": true,
"created_at_ms": 1760000000000,
})
repository := &fakeLuckyGiftRepository{
outbox: []domain.DrawOutbox{{
AppCode: "lalu",
OutboxID: "lucky_lucky_draw_test",
EventType: "LuckyGiftDrawn",
PayloadJSON: string(payload),
}},
}
wallet := &fakeLuckyGiftWallet{}
publisher := &fakeLuckyGiftPublisher{}
service := New(repository, WithWallet(wallet), WithRoomPublisher(publisher))
service.SetClock(func() time.Time { return time.UnixMilli(1760000001000).UTC() })
processed, err := service.ProcessPendingDrawOutbox(context.Background(), WorkerOptions{WorkerID: "worker-1"})
if err != nil {
t.Fatalf("ProcessPendingDrawOutbox failed: %v", err)
}
if processed != 1 {
t.Fatalf("processed count mismatch: %d", processed)
}
if wallet.last == nil || wallet.last.GetDrawId() != "lucky_draw_test" || wallet.last.GetTargetUserId() != 42 || wallet.last.GetAmount() != 300 {
t.Fatalf("wallet reward request mismatch: %+v", wallet.last)
}
if publisher.last.GroupID != "room-1" || publisher.last.Desc != "lucky_gift_drawn" || publisher.last.EventID != "lucky_gift_drawn:lucky_draw_test" {
t.Fatalf("room im message mismatch: %+v", publisher.last)
}
if repository.grantedDrawID != "lucky_draw_test" || repository.deliveredOutboxID != "lucky_lucky_draw_test" {
t.Fatalf("repository settlement mismatch: granted=%s delivered=%s", repository.grantedDrawID, repository.deliveredOutboxID)
}
}
type fakeLuckyGiftRepository struct { type fakeLuckyGiftRepository struct {
getConfigCalls int getConfigCalls int
upserted domain.Config upserted domain.Config
outbox []domain.DrawOutbox
grantedDrawID string
deliveredOutboxID string
} }
func (r *fakeLuckyGiftRepository) GetLuckyGiftConfig(context.Context, string) (domain.Config, bool, error) { func (r *fakeLuckyGiftRepository) GetLuckyGiftConfig(context.Context, string) (domain.Config, bool, error) {
@ -72,3 +132,47 @@ func (r *fakeLuckyGiftRepository) ListLuckyGiftDraws(context.Context, domain.Dra
func (r *fakeLuckyGiftRepository) GetLuckyGiftDrawSummary(context.Context, domain.DrawQuery) (domain.DrawSummary, error) { func (r *fakeLuckyGiftRepository) GetLuckyGiftDrawSummary(context.Context, domain.DrawQuery) (domain.DrawSummary, error) {
return domain.DrawSummary{}, nil return domain.DrawSummary{}, nil
} }
func (r *fakeLuckyGiftRepository) ClaimPendingLuckyGiftOutbox(context.Context, string, int64, time.Duration, int) ([]domain.DrawOutbox, error) {
return r.outbox, nil
}
func (r *fakeLuckyGiftRepository) MarkLuckyGiftOutboxDelivered(_ context.Context, event domain.DrawOutbox, _ int64) error {
r.deliveredOutboxID = event.OutboxID
return nil
}
func (r *fakeLuckyGiftRepository) MarkLuckyGiftOutboxRetryable(context.Context, domain.DrawOutbox, int, int64, string, int64) error {
return nil
}
func (r *fakeLuckyGiftRepository) MarkLuckyGiftOutboxFailed(context.Context, domain.DrawOutbox, int, string, int64) error {
return nil
}
func (r *fakeLuckyGiftRepository) MarkLuckyGiftDrawGranted(_ context.Context, _ string, drawID string, _ string, _ int64) error {
r.grantedDrawID = drawID
return nil
}
func (r *fakeLuckyGiftRepository) MarkLuckyGiftDrawFailed(context.Context, string, string, string, int64) error {
return nil
}
type fakeLuckyGiftWallet struct {
last *walletv1.CreditLuckyGiftRewardRequest
}
func (w *fakeLuckyGiftWallet) CreditLuckyGiftReward(_ context.Context, req *walletv1.CreditLuckyGiftRewardRequest, _ ...grpc.CallOption) (*walletv1.CreditLuckyGiftRewardResponse, error) {
w.last = req
return &walletv1.CreditLuckyGiftRewardResponse{TransactionId: "wallet_tx_lucky", Amount: req.GetAmount()}, nil
}
type fakeLuckyGiftPublisher struct {
last tencentim.CustomGroupMessage
}
func (p *fakeLuckyGiftPublisher) PublishGroupCustomMessage(_ context.Context, message tencentim.CustomGroupMessage) error {
p.last = message
return nil
}

View File

@ -556,33 +556,51 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky
coin_spent, rule_version, experience_pool, rtp_window_index, gift_rtp_window_index, 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, selected_tier_id, base_reward_coins, room_atmosphere_reward_coins, activity_subsidy_coins,
effective_reward_coins, budget_sources_json, stage_feedback, high_multiplier, effective_reward_coins, budget_sources_json, stage_feedback, high_multiplier,
candidate_tiers_json, pool_snapshot_json, rtp_snapshot_json, reward_status, 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 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.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.Config.GiftID, input.Command.GiftID, input.Command.CoinSpent,
input.Config.RuleVersion, input.ExperiencePool, input.GlobalWindow.WindowIndex, input.GiftWindow.WindowIndex, 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.TierID, input.Candidate.BaseReward, input.Candidate.RoomReward, input.Candidate.ActivityReward,
input.Candidate.effectiveReward(), input.BudgetSourcesJSON, input.StageFeedback, input.Candidate.HighMultiplier, input.Candidate.effectiveReward(), input.BudgetSourcesJSON, input.StageFeedback, input.Candidate.HighMultiplier,
string(candidateSnapshot), string(poolSnapshot), string(rtpSnapshot), domain.StatusPending, string(candidateSnapshot), string(poolSnapshot), string(rtpSnapshot), domain.StatusPending, "", "",
input.Command.PaidAtMS, input.NowMS, input.NowMS, input.Command.PaidAtMS, input.NowMS, input.NowMS,
); err != nil { ); err != nil {
return err return err
} }
if input.Candidate.effectiveReward() > 0 || input.StageFeedback || input.Candidate.Presentation { if input.Candidate.effectiveReward() > 0 || input.StageFeedback || input.Candidate.Presentation {
payload, _ := json.Marshal(map[string]any{ payload, _ := json.Marshal(map[string]any{
"draw_id": input.DrawID, "event_type": "lucky_gift_drawn",
"pool_id": input.Config.GiftID, "event_id": "lucky_gift_drawn:" + input.DrawID,
"user_id": input.Command.UserID, "app_code": input.AppCode,
"room_id": input.Command.RoomID, "draw_id": input.DrawID,
"gift_id": input.Command.GiftID, "command_id": input.Command.CommandID,
"effective_reward_coins": input.Candidate.effectiveReward(), "pool_id": input.Config.GiftID,
"stage_feedback": input.StageFeedback, "user_id": input.Command.UserID,
"sender_user_id": input.Command.UserID,
"target_user_id": input.Command.TargetUserID,
"room_id": input.Command.RoomID,
"gift_id": input.Command.GiftID,
"gift_count": input.Command.GiftCount,
"coin_spent": input.Command.CoinSpent,
"rule_version": input.Config.RuleVersion,
"experience_pool": input.ExperiencePool,
"selected_tier_id": input.Candidate.TierID,
"multiplier_ppm": input.Candidate.MultiplierPPM,
"base_reward_coins": input.Candidate.BaseReward,
"room_atmosphere_reward_coins": input.Candidate.RoomReward,
"activity_subsidy_coins": input.Candidate.ActivityReward,
"effective_reward_coins": input.Candidate.effectiveReward(),
"stage_feedback": input.StageFeedback,
"high_multiplier": input.Candidate.HighMultiplier,
"created_at_ms": input.NowMS,
}) })
if _, err := tx.ExecContext(ctx, ` if _, err := tx.ExecContext(ctx, `
INSERT INTO activity_outbox ( INSERT INTO activity_outbox (
app_code, outbox_id, event_type, payload, status, retry_count, next_retry_at_ms, created_at_ms, updated_at_ms app_code, outbox_id, event_type, payload, status, retry_count, next_retry_at_ms,
) VALUES (?, ?, 'LuckyGiftDrawn', ?, 'pending', 0, ?, ?, ?)`, locked_by, lock_until_ms, last_error, created_at_ms, updated_at_ms
) VALUES (?, ?, 'LuckyGiftDrawn', ?, 'pending', 0, ?, '', 0, '', ?, ?)`,
input.AppCode, "lucky_"+input.DrawID, payload, input.NowMS, input.NowMS, input.NowMS, input.AppCode, "lucky_"+input.DrawID, payload, input.NowMS, input.NowMS, input.NowMS,
); err != nil { ); err != nil {
return err return err
@ -591,6 +609,155 @@ func (r *Repository) applyLuckyDraw(ctx context.Context, tx *sql.Tx, input lucky
return nil return nil
} }
// ClaimPendingLuckyGiftOutbox 抢占幸运礼物抽奖副作用。抽奖事实已在同一事务落库worker 只补偿钱包和房间 IM。
func (r *Repository) ClaimPendingLuckyGiftOutbox(ctx context.Context, workerID string, nowMS int64, lockTTL time.Duration, batchSize int) ([]domain.DrawOutbox, error) {
if r == nil || r.db == nil {
return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
}
workerID = strings.TrimSpace(workerID)
if workerID == "" {
return nil, xerr.New(xerr.InvalidArgument, "worker_id is required")
}
if batchSize <= 0 {
batchSize = 100
}
if batchSize > 500 {
batchSize = 500
}
if lockTTL <= 0 {
lockTTL = 30 * time.Second
}
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
defer func() { _ = tx.Rollback() }()
rows, err := tx.QueryContext(ctx, `
SELECT app_code, outbox_id, event_type, COALESCE(CAST(payload AS CHAR), '{}'), retry_count, created_at_ms
FROM activity_outbox
WHERE event_type = 'LuckyGiftDrawn'
AND (
(status IN ('pending', 'retryable') AND next_retry_at_ms <= ?)
OR (status = 'delivering' AND lock_until_ms <= ?)
)
ORDER BY created_at_ms ASC, outbox_id ASC
LIMIT ?
FOR UPDATE`, nowMS, nowMS, batchSize)
if err != nil {
return nil, err
}
defer rows.Close()
events := make([]domain.DrawOutbox, 0, batchSize)
for rows.Next() {
var event domain.DrawOutbox
if err := rows.Scan(&event.AppCode, &event.OutboxID, &event.EventType, &event.PayloadJSON, &event.RetryCount, &event.CreatedAtMS); err != nil {
return nil, err
}
events = append(events, event)
}
if err := rows.Err(); err != nil {
return nil, err
}
lockUntilMS := nowMS + lockTTL.Milliseconds()
for _, event := range events {
if _, err := tx.ExecContext(ctx, `
UPDATE activity_outbox
SET status = 'delivering', locked_by = ?, lock_until_ms = ?, updated_at_ms = ?
WHERE app_code = ? AND outbox_id = ?`,
workerID, lockUntilMS, nowMS, event.AppCode, event.OutboxID,
); err != nil {
return nil, err
}
}
if err := tx.Commit(); err != nil {
return nil, err
}
return events, nil
}
func (r *Repository) MarkLuckyGiftOutboxDelivered(ctx context.Context, event domain.DrawOutbox, 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 activity_outbox
SET status = 'delivered', locked_by = '', lock_until_ms = 0, last_error = '', updated_at_ms = ?
WHERE app_code = ? AND outbox_id = ?`,
nowMS, appcode.Normalize(event.AppCode), event.OutboxID,
)
return err
}
func (r *Repository) MarkLuckyGiftOutboxRetryable(ctx context.Context, event domain.DrawOutbox, retryCount int, nextRetryAtMS int64, lastErr 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 activity_outbox
SET status = 'retryable', retry_count = ?, next_retry_at_ms = ?, locked_by = '', lock_until_ms = 0,
last_error = ?, updated_at_ms = ?
WHERE app_code = ? AND outbox_id = ?`,
retryCount, nextRetryAtMS, trimLuckyLastError(lastErr), nowMS, appcode.Normalize(event.AppCode), event.OutboxID,
)
return err
}
func (r *Repository) MarkLuckyGiftOutboxFailed(ctx context.Context, event domain.DrawOutbox, retryCount int, lastErr 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 activity_outbox
SET status = 'failed', retry_count = ?, locked_by = '', lock_until_ms = 0, last_error = ?, updated_at_ms = ?
WHERE app_code = ? AND outbox_id = ?`,
retryCount, trimLuckyLastError(lastErr), nowMS, appcode.Normalize(event.AppCode), event.OutboxID,
)
return err
}
func (r *Repository) MarkLuckyGiftDrawGranted(ctx context.Context, appCode string, drawID string, walletTransactionID 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 lucky_draw_records
SET reward_status = ?, reward_transaction_id = ?, reward_failure_reason = '', updated_at_ms = ?
WHERE app_code = ? AND draw_id = ?`,
domain.StatusGranted, walletTransactionID, nowMS, appcode.Normalize(appCode), drawID,
)
return err
}
func (r *Repository) MarkLuckyGiftDrawFailed(ctx context.Context, appCode string, drawID string, failureReason string, nowMS int64) error {
if r == nil || r.db == nil || strings.TrimSpace(drawID) == "" {
return nil
}
_, err := r.db.ExecContext(ctx, `
UPDATE lucky_draw_records
SET reward_status = ?, reward_failure_reason = ?, updated_at_ms = ?
WHERE app_code = ? AND draw_id = ? AND reward_status = ?`,
domain.StatusFailed, trimLuckyFailureReason(failureReason), nowMS, appcode.Normalize(appCode), drawID, domain.StatusPending,
)
return err
}
func trimLuckyLastError(value string) string {
value = strings.TrimSpace(value)
if len(value) > 512 {
return value[:512]
}
return value
}
func trimLuckyFailureReason(value string) string {
value = strings.TrimSpace(value)
if len(value) > 255 {
return value[:255]
}
return value
}
func (r *Repository) ListLuckyGiftDraws(ctx context.Context, query domain.DrawQuery) ([]domain.DrawResult, int64, error) { func (r *Repository) ListLuckyGiftDraws(ctx context.Context, query domain.DrawQuery) ([]domain.DrawResult, int64, error) {
if r == nil || r.db == nil { if r == nil || r.db == nil {
return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured") return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured")

View File

@ -45,6 +45,15 @@ func (r *Repository) Migrate(ctx context.Context) error {
if err := r.ensureLuckyDrawPoolID(ctx); err != nil { if err := r.ensureLuckyDrawPoolID(ctx); err != nil {
return err return err
} }
if err := r.ensureLuckyGiftOutboxColumns(ctx); err != nil {
return err
}
if err := r.ensureLuckyDrawRewardSettlementColumns(ctx); err != nil {
return err
}
if err := r.ensureDefaultLuckyGiftRules(ctx); err != nil {
return err
}
return nil return nil
} }
@ -94,6 +103,84 @@ func (r *Repository) ensureLuckyDrawPoolID(ctx context.Context) error {
return nil return nil
} }
func (r *Repository) ensureLuckyGiftOutboxColumns(ctx context.Context) error {
additions := []struct {
name string
sql string
}{
{"locked_by", `ALTER TABLE activity_outbox ADD COLUMN locked_by VARCHAR(128) NOT NULL DEFAULT '' COMMENT '当前锁定 worker' AFTER next_retry_at_ms`},
{"lock_until_ms", `ALTER TABLE activity_outbox ADD COLUMN lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '锁过期时间UTC epoch ms' AFTER locked_by`},
{"last_error", `ALTER TABLE activity_outbox ADD COLUMN last_error VARCHAR(512) NOT NULL DEFAULT '' COMMENT '最后一次失败原因' AFTER lock_until_ms`},
}
for _, addition := range additions {
exists, err := r.columnExists(ctx, "activity_outbox", addition.name)
if err != nil {
return err
}
if !exists {
if _, err := r.db.ExecContext(ctx, addition.sql); err != nil {
return err
}
}
}
return nil
}
func (r *Repository) ensureLuckyDrawRewardSettlementColumns(ctx context.Context) error {
additions := []struct {
name string
sql string
}{
{"reward_transaction_id", `ALTER TABLE lucky_draw_records ADD COLUMN reward_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '返奖钱包交易 ID' AFTER reward_status`},
{"reward_failure_reason", `ALTER TABLE lucky_draw_records ADD COLUMN reward_failure_reason VARCHAR(255) NOT NULL DEFAULT '' COMMENT '返奖失败原因' AFTER reward_transaction_id`},
}
for _, addition := range additions {
exists, err := r.columnExists(ctx, "lucky_draw_records", addition.name)
if err != nil {
return err
}
if !exists {
if _, err := r.db.ExecContext(ctx, addition.sql); err != nil {
return err
}
}
}
return nil
}
func (r *Repository) ensureDefaultLuckyGiftRules(ctx context.Context) error {
// 本地和 Docker 开发库通常保留 MySQL volume这里补齐缺省 lucky/super_lucky 契约,但不覆盖后台已发布规则。
const seedNowMS int64 = 1779259000000
const seedTiersJSON = `[
{"pool":"novice","tier_id":"novice_none","reward_coins":0,"multiplier_ppm":0,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"novice","tier_id":"novice_1x","reward_coins":100,"multiplier_ppm":1000000,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"novice","tier_id":"novice_2x","reward_coins":200,"multiplier_ppm":2000000,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"intermediate","tier_id":"intermediate_none","reward_coins":0,"multiplier_ppm":0,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"intermediate","tier_id":"intermediate_1x","reward_coins":100,"multiplier_ppm":1000000,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"intermediate","tier_id":"intermediate_2x","reward_coins":200,"multiplier_ppm":2000000,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"advanced","tier_id":"advanced_none","reward_coins":0,"multiplier_ppm":0,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"advanced","tier_id":"advanced_1x","reward_coins":100,"multiplier_ppm":1000000,"weight":0,"high_water_only":false,"enabled":true},
{"pool":"advanced","tier_id":"advanced_2x","reward_coins":200,"multiplier_ppm":2000000,"weight":0,"high_water_only":false,"enabled":true}
]`
_, err := r.db.ExecContext(ctx, `
INSERT IGNORE INTO lucky_gift_rules (
app_code, gift_id, enabled, rule_version, gift_price, target_rtp_ppm, pool_rate_ppm,
global_window_draws, gift_window_draws, novice_draw_limit, intermediate_draw_limit,
high_multiplier, high_water_pool_multiple, platform_pool_weight_ppm, room_pool_weight_ppm,
gift_pool_weight_ppm, initial_platform_pool, initial_gift_pool, initial_room_pool,
platform_reserve, gift_reserve, room_reserve, max_single_payout, user_hourly_payout_cap,
user_daily_payout_cap, device_daily_payout_cap, room_hourly_payout_cap, anchor_daily_payout_cap,
room_atmosphere_rate_ppm, room_atmosphere_initial, room_atmosphere_reserve,
activity_budget, activity_daily_limit, large_tier_enabled, tiers_json,
updated_by_admin_id, created_at_ms, updated_at_ms
) VALUES
('lalu', 'lucky', true, 1, 100, 950000, 950000, 100000, 100000, 2000, 20000, 100, 2, 200000, 300000, 500000, 9500000, 23750000, 5000000, 1000000, 1000000, 100000, 50000, 3420000, 61560000, 102600000, 68400000, 1231200000, 10000, 100000, 10000, 0, 0, true, ?, 0, ?, ?),
('lalu', 'super_lucky', true, 1, 100, 950000, 950000, 100000, 100000, 2000, 20000, 100, 2, 200000, 300000, 500000, 9500000, 23750000, 5000000, 1000000, 1000000, 100000, 50000, 3420000, 61560000, 102600000, 68400000, 1231200000, 10000, 100000, 10000, 0, 0, true, ?, 0, ?, ?)`,
seedTiersJSON, seedNowMS, seedNowMS, seedTiersJSON, seedNowMS, seedNowMS,
)
return err
}
func (r *Repository) columnExists(ctx context.Context, table string, column string) (bool, error) { func (r *Repository) columnExists(ctx context.Context, table string, column string) (bool, error) {
database, err := r.currentDatabase(ctx) database, err := r.currentDatabase(ctx)
if err != nil { if err != nil {

View File

@ -48,15 +48,17 @@ func (s *LuckyGiftServer) ExecuteLuckyGiftDraw(ctx context.Context, req *activit
meta := req.GetLuckyGift() meta := req.GetLuckyGift()
ctx = appcode.WithContext(ctx, meta.GetMeta().GetAppCode()) ctx = appcode.WithContext(ctx, meta.GetMeta().GetAppCode())
result, err := s.svc.Draw(ctx, domain.DrawCommand{ result, err := s.svc.Draw(ctx, domain.DrawCommand{
CommandID: meta.GetCommandId(), CommandID: meta.GetCommandId(),
PoolID: meta.GetPoolId(), PoolID: meta.GetPoolId(),
UserID: meta.GetUserId(), UserID: meta.GetUserId(),
DeviceID: meta.GetDeviceId(), TargetUserID: meta.GetTargetUserId(),
RoomID: meta.GetRoomId(), DeviceID: meta.GetDeviceId(),
AnchorID: meta.GetAnchorId(), RoomID: meta.GetRoomId(),
GiftID: meta.GetGiftId(), AnchorID: meta.GetAnchorId(),
CoinSpent: meta.GetCoinSpent(), GiftID: meta.GetGiftId(),
PaidAtMS: meta.GetPaidAtMs(), GiftCount: meta.GetGiftCount(),
CoinSpent: meta.GetCoinSpent(),
PaidAtMS: meta.GetPaidAtMs(),
}) })
if err != nil { if err != nil {
return nil, xerr.ToGRPCError(err) return nil, xerr.ToGRPCError(err)

View File

@ -99,6 +99,7 @@ func New(cfg config.Config) (*App, error) {
var registrationRewardClient client.RegistrationRewardClient = client.NewGRPCRegistrationRewardClient(activityConn) var registrationRewardClient client.RegistrationRewardClient = client.NewGRPCRegistrationRewardClient(activityConn)
var firstRechargeRewardClient client.FirstRechargeRewardClient = client.NewGRPCFirstRechargeRewardClient(activityConn) var firstRechargeRewardClient client.FirstRechargeRewardClient = client.NewGRPCFirstRechargeRewardClient(activityConn)
var sevenDayCheckInClient client.SevenDayCheckInClient = client.NewGRPCSevenDayCheckInClient(activityConn) var sevenDayCheckInClient client.SevenDayCheckInClient = client.NewGRPCSevenDayCheckInClient(activityConn)
var luckyGiftClient client.LuckyGiftClient = client.NewGRPCLuckyGiftClient(activityConn)
var broadcastClient client.BroadcastClient = client.NewGRPCBroadcastClient(activityConn) var broadcastClient client.BroadcastClient = client.NewGRPCBroadcastClient(activityConn)
var gameClient client.GameClient = client.NewGRPCGameClient(gameConn) var gameClient client.GameClient = client.NewGRPCGameClient(gameConn)
appConfigReader, err := openAppConfigReader(cfg.AppConfig) appConfigReader, err := openAppConfigReader(cfg.AppConfig)
@ -143,6 +144,7 @@ func New(cfg config.Config) (*App, error) {
handler.SetRegistrationRewardClient(registrationRewardClient) handler.SetRegistrationRewardClient(registrationRewardClient)
handler.SetFirstRechargeRewardClient(firstRechargeRewardClient) handler.SetFirstRechargeRewardClient(firstRechargeRewardClient)
handler.SetSevenDayCheckInClient(sevenDayCheckInClient) handler.SetSevenDayCheckInClient(sevenDayCheckInClient)
handler.SetLuckyGiftClient(luckyGiftClient)
handler.SetBroadcastClient(broadcastClient) handler.SetBroadcastClient(broadcastClient)
handler.SetGameClient(gameClient) handler.SetGameClient(gameClient)
handler.SetLeaderboardWalletDB(leaderboardWalletDB) handler.SetLeaderboardWalletDB(leaderboardWalletDB)

View File

@ -0,0 +1,25 @@
package client
import (
"context"
"google.golang.org/grpc"
activityv1 "hyapp.local/api/proto/activity/v1"
)
// LuckyGiftClient abstracts gateway access to activity-service lucky gift eligibility checks.
type LuckyGiftClient interface {
CheckLuckyGift(ctx context.Context, req *activityv1.CheckLuckyGiftRequest) (*activityv1.CheckLuckyGiftResponse, error)
}
type grpcLuckyGiftClient struct {
client activityv1.LuckyGiftServiceClient
}
func NewGRPCLuckyGiftClient(conn *grpc.ClientConn) LuckyGiftClient {
return &grpcLuckyGiftClient{client: activityv1.NewLuckyGiftServiceClient(conn)}
}
func (c *grpcLuckyGiftClient) CheckLuckyGift(ctx context.Context, req *activityv1.CheckLuckyGiftRequest) (*activityv1.CheckLuckyGiftResponse, error) {
return c.client.CheckLuckyGift(ctx, req)
}

View File

@ -20,6 +20,7 @@ type Handler struct {
registrationReward client.RegistrationRewardClient registrationReward client.RegistrationRewardClient
firstRechargeReward client.FirstRechargeRewardClient firstRechargeReward client.FirstRechargeRewardClient
sevenDayCheckIn client.SevenDayCheckInClient sevenDayCheckIn client.SevenDayCheckInClient
luckyGift client.LuckyGiftClient
} }
type Config struct { type Config struct {
@ -33,6 +34,7 @@ type Config struct {
RegistrationReward client.RegistrationRewardClient RegistrationReward client.RegistrationRewardClient
FirstRechargeReward client.FirstRechargeRewardClient FirstRechargeReward client.FirstRechargeRewardClient
SevenDayCheckIn client.SevenDayCheckInClient SevenDayCheckIn client.SevenDayCheckInClient
LuckyGift client.LuckyGiftClient
} }
func New(config Config) *Handler { func New(config Config) *Handler {
@ -47,6 +49,7 @@ func New(config Config) *Handler {
registrationReward: config.RegistrationReward, registrationReward: config.RegistrationReward,
firstRechargeReward: config.FirstRechargeReward, firstRechargeReward: config.FirstRechargeReward,
sevenDayCheckIn: config.SevenDayCheckIn, sevenDayCheckIn: config.SevenDayCheckIn,
luckyGift: config.LuckyGift,
} }
} }
@ -59,6 +62,7 @@ func (h *Handler) TaskHandlers() httproutes.TaskHandlers {
GetSevenDayCheckInStatus: h.getSevenDayCheckInStatus, GetSevenDayCheckInStatus: h.getSevenDayCheckInStatus,
SignSevenDayCheckIn: h.signSevenDayCheckIn, SignSevenDayCheckIn: h.signSevenDayCheckIn,
ListUserLeaderboards: h.listUserLeaderboards, ListUserLeaderboards: h.listUserLeaderboards,
CheckLuckyGift: h.checkLuckyGift,
} }
} }

View File

@ -0,0 +1,71 @@
package activityapi
import (
"encoding/json"
"net/http"
"strings"
activityv1 "hyapp.local/api/proto/activity/v1"
"hyapp/services/gateway-service/internal/auth"
"hyapp/services/gateway-service/internal/transport/http/httpkit"
)
type luckyGiftCheckRequest struct {
RoomID string `json:"room_id"`
GiftID string `json:"gift_id"`
PoolID string `json:"pool_id"`
}
type luckyGiftCheckData struct {
Enabled bool `json:"enabled"`
Reason string `json:"reason"`
RoomID string `json:"room_id"`
GiftID string `json:"gift_id"`
PoolID string `json:"pool_id"`
GiftPrice int64 `json:"gift_price"`
RuleVersion int64 `json:"rule_version"`
TargetRTPPPM int64 `json:"target_rtp_ppm"`
ExperiencePool string `json:"experience_pool"`
}
// checkLuckyGift 只做送礼前资格检查;扣费、抽奖和返奖都必须继续走 room-service 送礼主链路。
func (h *Handler) checkLuckyGift(writer http.ResponseWriter, request *http.Request) {
if h.luckyGift == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
var body luckyGiftCheckRequest
if err := json.NewDecoder(request.Body).Decode(&body); err != nil {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid request body")
return
}
body.RoomID = strings.TrimSpace(body.RoomID)
body.GiftID = strings.TrimSpace(body.GiftID)
body.PoolID = strings.TrimSpace(body.PoolID)
if body.RoomID == "" || body.GiftID == "" || body.PoolID == "" {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "room_id, gift_id and pool_id are required")
return
}
resp, err := h.luckyGift.CheckLuckyGift(request.Context(), &activityv1.CheckLuckyGiftRequest{
Meta: httpkit.ActivityMeta(request),
UserId: auth.UserIDFromContext(request.Context()),
RoomId: body.RoomID,
GiftId: body.GiftID,
PoolId: body.PoolID,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, luckyGiftCheckData{
Enabled: resp.GetEnabled(),
Reason: resp.GetReason(),
RoomID: body.RoomID,
GiftID: resp.GetGiftId(),
PoolID: resp.GetPoolId(),
GiftPrice: resp.GetGiftPrice(),
RuleVersion: resp.GetRuleVersion(),
TargetRTPPPM: resp.GetTargetRtpPpm(),
ExperiencePool: resp.GetExperiencePool(),
})
}

View File

@ -0,0 +1,64 @@
package activityapi
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
activityv1 "hyapp.local/api/proto/activity/v1"
"hyapp/pkg/appcode"
"hyapp/services/gateway-service/internal/auth"
)
func TestCheckLuckyGiftForwardsEligibilityRequest(t *testing.T) {
client := &fakeLuckyGiftClient{resp: &activityv1.CheckLuckyGiftResponse{
Enabled: true,
Reason: "enabled",
GiftId: "rose",
PoolId: "super_lucky",
GiftPrice: 100,
RuleVersion: 3,
TargetRtpPpm: 600000,
ExperiencePool: "novice",
}}
handler := New(Config{LuckyGift: client})
request := httptest.NewRequest(http.MethodPost, "/api/v1/activities/lucky-gifts/check", strings.NewReader(`{"room_id":"room-1","gift_id":"rose","pool_id":"super_lucky"}`))
request = request.WithContext(auth.WithUserID(appcode.WithContext(request.Context(), "lalu"), 42))
recorder := httptest.NewRecorder()
handler.checkLuckyGift(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: %d body=%s", recorder.Code, recorder.Body.String())
}
if client.last == nil || client.last.GetUserId() != 42 || client.last.GetRoomId() != "room-1" || client.last.GetGiftId() != "rose" || client.last.GetPoolId() != "super_lucky" {
t.Fatalf("check request mismatch: %+v", client.last)
}
var envelope struct {
Code string `json:"code"`
Data struct {
Enabled bool `json:"enabled"`
Reason string `json:"reason"`
ExperiencePool string `json:"experience_pool"`
} `json:"data"`
}
if err := json.Unmarshal(recorder.Body.Bytes(), &envelope); err != nil {
t.Fatalf("decode response: %v", err)
}
if envelope.Code != "OK" || !envelope.Data.Enabled || envelope.Data.ExperiencePool != "novice" {
t.Fatalf("response mismatch: %+v", envelope)
}
}
type fakeLuckyGiftClient struct {
last *activityv1.CheckLuckyGiftRequest
resp *activityv1.CheckLuckyGiftResponse
}
func (c *fakeLuckyGiftClient) CheckLuckyGift(_ context.Context, req *activityv1.CheckLuckyGiftRequest) (*activityv1.CheckLuckyGiftResponse, error) {
c.last = req
return c.resp, nil
}

View File

@ -41,6 +41,7 @@ type Handler struct {
registrationReward client.RegistrationRewardClient registrationReward client.RegistrationRewardClient
firstRechargeReward client.FirstRechargeRewardClient firstRechargeReward client.FirstRechargeRewardClient
sevenDayCheckIn client.SevenDayCheckInClient sevenDayCheckIn client.SevenDayCheckInClient
luckyGift client.LuckyGiftClient
broadcastClient client.BroadcastClient broadcastClient client.BroadcastClient
gameClient client.GameClient gameClient client.GameClient
appConfigReader appapi.ConfigReader appConfigReader appapi.ConfigReader
@ -204,6 +205,11 @@ func (h *Handler) SetSevenDayCheckInClient(sevenDayCheckIn client.SevenDayCheckI
h.sevenDayCheckIn = sevenDayCheckIn h.sevenDayCheckIn = sevenDayCheckIn
} }
// SetLuckyGiftClient 注入 activity-service 幸运礼物资格检查 client。
func (h *Handler) SetLuckyGiftClient(luckyGift client.LuckyGiftClient) {
h.luckyGift = luckyGift
}
// SetBroadcastClient 注入 activity-service 播报群成员关系 client。 // SetBroadcastClient 注入 activity-service 播报群成员关系 client。
func (h *Handler) SetBroadcastClient(broadcastClient client.BroadcastClient) { func (h *Handler) SetBroadcastClient(broadcastClient client.BroadcastClient) {
h.broadcastClient = broadcastClient h.broadcastClient = broadcastClient

View File

@ -138,6 +138,7 @@ type TaskHandlers struct {
GetSevenDayCheckInStatus http.HandlerFunc GetSevenDayCheckInStatus http.HandlerFunc
SignSevenDayCheckIn http.HandlerFunc SignSevenDayCheckIn http.HandlerFunc
ListUserLeaderboards http.HandlerFunc ListUserLeaderboards http.HandlerFunc
CheckLuckyGift http.HandlerFunc
} }
type LevelHandlers struct { type LevelHandlers struct {
@ -343,6 +344,7 @@ func (r routes) registerTaskRoutes() {
r.profile("/activities/seven-day-checkin", http.MethodGet, h.GetSevenDayCheckInStatus) r.profile("/activities/seven-day-checkin", http.MethodGet, h.GetSevenDayCheckInStatus)
r.profile("/activities/seven-day-checkin/sign", http.MethodPost, h.SignSevenDayCheckIn) r.profile("/activities/seven-day-checkin/sign", http.MethodPost, h.SignSevenDayCheckIn)
r.profile("/activities/user-leaderboards", http.MethodGet, h.ListUserLeaderboards) r.profile("/activities/user-leaderboards", http.MethodGet, h.ListUserLeaderboards)
r.profile("/activities/lucky-gifts/check", http.MethodPost, h.CheckLuckyGift)
r.profile("/tasks/tabs", http.MethodGet, h.ListTaskTabs) r.profile("/tasks/tabs", http.MethodGet, h.ListTaskTabs)
r.profile("/tasks/claim", http.MethodPost, h.ClaimTaskReward) r.profile("/tasks/claim", http.MethodPost, h.ClaimTaskReward)
} }

View File

@ -68,6 +68,7 @@ func (h *Handler) Routes(jwtVerifier *auth.Verifier) http.Handler {
RegistrationReward: h.registrationReward, RegistrationReward: h.registrationReward,
FirstRechargeReward: h.firstRechargeReward, FirstRechargeReward: h.firstRechargeReward,
SevenDayCheckIn: h.sevenDayCheckIn, SevenDayCheckIn: h.sevenDayCheckIn,
LuckyGift: h.luckyGift,
}) })
gameAPI := gameapi.New(gameapi.Config{ gameAPI := gameapi.New(gameapi.Config{
GameClient: h.gameClient, GameClient: h.gameClient,

View File

@ -126,16 +126,18 @@ func (s *Service) SendGift(ctx context.Context, req *roomv1.SendGiftRequest) (*r
if luckyEnabled { if luckyEnabled {
drawResp, err := s.luckyGift.ExecuteLuckyGiftDraw(ctx, &activityv1.ExecuteLuckyGiftDrawRequest{ drawResp, err := s.luckyGift.ExecuteLuckyGiftDraw(ctx, &activityv1.ExecuteLuckyGiftDrawRequest{
LuckyGift: &activityv1.LuckyGiftMeta{ LuckyGift: &activityv1.LuckyGiftMeta{
Meta: activityMetaFromRoom(ctx, req.GetMeta()), Meta: activityMetaFromRoom(ctx, req.GetMeta()),
CommandId: cmd.ID(), CommandId: cmd.ID(),
UserId: cmd.ActorUserID(), UserId: cmd.ActorUserID(),
DeviceId: luckyGiftDeviceID(cmd), TargetUserId: cmd.TargetUserID,
RoomId: cmd.RoomID(), DeviceId: luckyGiftDeviceID(cmd),
AnchorId: luckyGiftAnchorID(roomMeta), RoomId: cmd.RoomID(),
GiftId: cmd.GiftID, AnchorId: luckyGiftAnchorID(roomMeta),
CoinSpent: billing.GetCoinSpent(), GiftId: cmd.GiftID,
PaidAtMs: now.UTC().UnixMilli(), GiftCount: cmd.GiftCount,
PoolId: cmd.PoolID, CoinSpent: billing.GetCoinSpent(),
PaidAtMs: now.UTC().UnixMilli(),
PoolId: cmd.PoolID,
}, },
}) })
if err != nil { if err != nil {

View File

@ -553,6 +553,27 @@ type TaskRewardReceipt struct {
GrantedAtMS int64 GrantedAtMS int64
} }
// LuckyGiftRewardCommand 是 activity-service 用抽奖 draw_id 发起的 COIN 入账命令。
type LuckyGiftRewardCommand struct {
AppCode string
CommandID string
TargetUserID int64
Amount int64
DrawID string
RoomID string
GiftID string
PoolID string
Reason string
}
// LuckyGiftRewardReceipt 是幸运礼物返奖入账后的稳定回执。
type LuckyGiftRewardReceipt struct {
TransactionID string
Balance AssetBalance
Amount int64
GrantedAtMS int64
}
// GameCoinChangeCommand 是 game-service 对钱包发起的游戏专用金币改账命令。 // GameCoinChangeCommand 是 game-service 对钱包发起的游戏专用金币改账命令。
type GameCoinChangeCommand struct { type GameCoinChangeCommand struct {
AppCode string AppCode string

View File

@ -23,6 +23,7 @@ type Repository interface {
AdminCreditCoinSellerStock(ctx context.Context, command ledger.CoinSellerStockCreditCommand) (ledger.CoinSellerStockCreditReceipt, error) AdminCreditCoinSellerStock(ctx context.Context, command ledger.CoinSellerStockCreditCommand) (ledger.CoinSellerStockCreditReceipt, error)
TransferCoinFromSeller(ctx context.Context, command ledger.CoinSellerTransferCommand) (ledger.CoinSellerTransferReceipt, error) TransferCoinFromSeller(ctx context.Context, command ledger.CoinSellerTransferCommand) (ledger.CoinSellerTransferReceipt, error)
CreditTaskReward(ctx context.Context, command ledger.TaskRewardCommand) (ledger.TaskRewardReceipt, error) CreditTaskReward(ctx context.Context, command ledger.TaskRewardCommand) (ledger.TaskRewardReceipt, error)
CreditLuckyGiftReward(ctx context.Context, command ledger.LuckyGiftRewardCommand) (ledger.LuckyGiftRewardReceipt, error)
ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error) ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error)
GetRedPacketConfig(ctx context.Context, appCode string) (ledger.RedPacketConfig, error) GetRedPacketConfig(ctx context.Context, appCode string) (ledger.RedPacketConfig, error)
UpdateRedPacketConfig(ctx context.Context, config ledger.RedPacketConfig) (ledger.RedPacketConfig, error) UpdateRedPacketConfig(ctx context.Context, config ledger.RedPacketConfig) (ledger.RedPacketConfig, error)
@ -269,6 +270,28 @@ func (s *Service) CreditTaskReward(ctx context.Context, command ledger.TaskRewar
return s.repository.CreditTaskReward(ctx, command) return s.repository.CreditTaskReward(ctx, command)
} }
// CreditLuckyGiftReward 发放幸运礼物中奖金币;抽奖资格和金额已由 activity-service 决策,钱包只做幂等入账。
func (s *Service) CreditLuckyGiftReward(ctx context.Context, command ledger.LuckyGiftRewardCommand) (ledger.LuckyGiftRewardReceipt, error) {
if command.CommandID == "" || command.TargetUserID <= 0 || command.Amount <= 0 || command.DrawID == "" || command.RoomID == "" || command.GiftID == "" || command.PoolID == "" {
return ledger.LuckyGiftRewardReceipt{}, xerr.New(xerr.InvalidArgument, "lucky gift reward command is incomplete")
}
command.Reason = strings.TrimSpace(command.Reason)
if command.Reason == "" {
return ledger.LuckyGiftRewardReceipt{}, xerr.New(xerr.InvalidArgument, "reason is required")
}
if s.repository == nil {
return ledger.LuckyGiftRewardReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
command.AppCode = appcode.Normalize(command.AppCode)
command.DrawID = strings.TrimSpace(command.DrawID)
command.RoomID = strings.TrimSpace(command.RoomID)
command.GiftID = strings.TrimSpace(command.GiftID)
command.PoolID = strings.TrimSpace(command.PoolID)
ctx = appcode.WithContext(ctx, command.AppCode)
return s.repository.CreditLuckyGiftReward(ctx, command)
}
// ApplyGameCoinChange 是游戏平台唯一的 COIN 改账入口,方向由 op_type 控制。 // ApplyGameCoinChange 是游戏平台唯一的 COIN 改账入口,方向由 op_type 控制。
func (s *Service) ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error) { 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 == "" { if command.CommandID == "" || command.UserID <= 0 || command.PlatformCode == "" || command.GameID == "" || command.ProviderOrderID == "" {

View File

@ -519,6 +519,43 @@ func TestCreditTaskRewardIsIdempotent(t *testing.T) {
} }
} }
// TestCreditLuckyGiftRewardIsIdempotent 验证幸运礼物返奖只按 draw_id 派一次,并产出余额通知事实。
func TestCreditLuckyGiftRewardIsIdempotent(t *testing.T) {
repository := mysqltest.NewRepository(t)
svc := walletservice.New(repository)
command := ledger.LuckyGiftRewardCommand{
CommandID: "lucky_reward:lucky_draw_1",
TargetUserID: 21002,
Amount: 188,
DrawID: "lucky_draw_1",
RoomID: "room-1",
GiftID: "rose",
PoolID: "super_lucky",
Reason: "lucky_gift_reward",
}
first, err := svc.CreditLuckyGiftReward(context.Background(), command)
if err != nil {
t.Fatalf("CreditLuckyGiftReward failed: %v", err)
}
second, err := svc.CreditLuckyGiftReward(context.Background(), command)
if err != nil {
t.Fatalf("CreditLuckyGiftReward retry failed: %v", err)
}
if first.TransactionID == "" || first.TransactionID != second.TransactionID || second.Balance.AvailableAmount != 188 {
t.Fatalf("lucky reward receipt mismatch: first=%+v second=%+v", first, second)
}
if got := repository.CountRows("wallet_transactions", "command_id = ? AND biz_type = ?", command.CommandID, "lucky_gift_reward"); got != 1 {
t.Fatalf("lucky reward should write one transaction, got %d", got)
}
if got := repository.CountRows("wallet_outbox", "transaction_id = ? AND event_type = ?", first.TransactionID, "WalletBalanceChanged"); got != 1 {
t.Fatalf("lucky reward should write one balance change event, got %d", got)
}
if got := repository.CountRows("wallet_outbox", "transaction_id = ? AND event_type = ?", first.TransactionID, "WalletLuckyGiftRewardCredited"); got != 1 {
t.Fatalf("lucky reward should write one reward fact event, got %d", got)
}
}
// TestTransferCoinFromSellerMovesDedicatedAssetToPlayerCoin 验证币商专用金币和玩家普通金币不会混账。 // TestTransferCoinFromSellerMovesDedicatedAssetToPlayerCoin 验证币商专用金币和玩家普通金币不会混账。
func TestTransferCoinFromSellerMovesDedicatedAssetToPlayerCoin(t *testing.T) { func TestTransferCoinFromSellerMovesDedicatedAssetToPlayerCoin(t *testing.T) {
repository := mysqltest.NewRepository(t) repository := mysqltest.NewRepository(t)

View File

@ -25,6 +25,7 @@ const (
bizTypeGiftDebit = "gift_debit" bizTypeGiftDebit = "gift_debit"
bizTypeManualCredit = "manual_credit" bizTypeManualCredit = "manual_credit"
bizTypeTaskReward = "task_reward" bizTypeTaskReward = "task_reward"
bizTypeLuckyGiftReward = "lucky_gift_reward"
bizTypeCoinSellerTransfer = "coin_seller_transfer" bizTypeCoinSellerTransfer = "coin_seller_transfer"
bizTypeCoinSellerStockPurchase = "coin_seller_stock_purchase" bizTypeCoinSellerStockPurchase = "coin_seller_stock_purchase"
bizTypeCoinSellerCoinCompensation = "coin_seller_coin_compensation" bizTypeCoinSellerCoinCompensation = "coin_seller_coin_compensation"
@ -534,6 +535,80 @@ func (r *Repository) CreditTaskReward(ctx context.Context, command ledger.TaskRe
return receiptFromTaskRewardMetadata(transactionID, metadata), nil return receiptFromTaskRewardMetadata(transactionID, metadata), nil
} }
// CreditLuckyGiftReward 在同一事务里完成幸运礼物中奖 COIN 入账、交易分录和钱包 outbox。
func (r *Repository) CreditLuckyGiftReward(ctx context.Context, command ledger.LuckyGiftRewardCommand) (ledger.LuckyGiftRewardReceipt, error) {
if r == nil || r.db == nil {
return ledger.LuckyGiftRewardReceipt{}, 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.LuckyGiftRewardReceipt{}, err
}
defer func() { _ = tx.Rollback() }()
requestHash := luckyGiftRewardRequestHash(command)
if txRow, exists, err := r.lookupTransaction(ctx, tx, command.CommandID, requestHash, bizTypeLuckyGiftReward); err != nil || exists {
if err != nil || !exists {
return ledger.LuckyGiftRewardReceipt{}, err
}
return r.receiptForLuckyGiftRewardTransaction(ctx, tx, txRow.TransactionID)
}
nowMs := time.Now().UnixMilli()
account, err := r.lockAccount(ctx, tx, command.TargetUserID, ledger.AssetCoin, true, nowMs)
if err != nil {
return ledger.LuckyGiftRewardReceipt{}, err
}
transactionID := transactionID(command.AppCode, command.CommandID)
balanceAfter := account.AvailableAmount + command.Amount
metadata := luckyGiftRewardMetadata{
AppCode: command.AppCode,
TargetUserID: command.TargetUserID,
AssetType: ledger.AssetCoin,
Amount: command.Amount,
DrawID: command.DrawID,
RoomID: command.RoomID,
GiftID: command.GiftID,
PoolID: command.PoolID,
Reason: command.Reason,
BalanceAfter: balanceAfter,
GrantedAtMS: nowMs,
}
if err := r.insertTransaction(ctx, tx, transactionID, command.CommandID, bizTypeLuckyGiftReward, requestHash, command.DrawID, metadata, nowMs); err != nil {
return ledger.LuckyGiftRewardReceipt{}, err
}
if err := r.applyAccountDelta(ctx, tx, account, command.Amount, 0, nowMs); err != nil {
return ledger.LuckyGiftRewardReceipt{}, 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.LuckyGiftRewardReceipt{}, 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),
luckyGiftRewardCreditedEvent(transactionID, command.CommandID, metadata, nowMs),
}); err != nil {
return ledger.LuckyGiftRewardReceipt{}, err
}
if err := tx.Commit(); err != nil {
return ledger.LuckyGiftRewardReceipt{}, err
}
return receiptFromLuckyGiftRewardMetadata(transactionID, metadata), nil
}
// ApplyGameCoinChange 在同一事务内完成游戏 COIN 改账、分录和 outbox。 // ApplyGameCoinChange 在同一事务内完成游戏 COIN 改账、分录和 outbox。
func (r *Repository) ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error) { func (r *Repository) ApplyGameCoinChange(ctx context.Context, command ledger.GameCoinChangeCommand) (ledger.GameCoinChangeReceipt, error) {
if r == nil || r.db == nil { if r == nil || r.db == nil {
@ -1548,6 +1623,20 @@ type taskRewardMetadata struct {
GrantedAtMS int64 `json:"granted_at_ms"` GrantedAtMS int64 `json:"granted_at_ms"`
} }
type luckyGiftRewardMetadata struct {
AppCode string `json:"app_code"`
TargetUserID int64 `json:"target_user_id"`
AssetType string `json:"asset_type"`
Amount int64 `json:"amount"`
DrawID string `json:"draw_id"`
RoomID string `json:"room_id"`
GiftID string `json:"gift_id"`
PoolID string `json:"pool_id"`
Reason string `json:"reason"`
BalanceAfter int64 `json:"balance_after"`
GrantedAtMS int64 `json:"granted_at_ms"`
}
type gameCoinMetadata struct { type gameCoinMetadata struct {
AppCode string `json:"app_code"` AppCode string `json:"app_code"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
@ -1654,6 +1743,38 @@ func receiptFromTaskRewardMetadata(transactionID string, metadata taskRewardMeta
} }
} }
func (r *Repository) receiptForLuckyGiftRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.LuckyGiftRewardReceipt, 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.LuckyGiftRewardReceipt{}, err
}
var metadata luckyGiftRewardMetadata
if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil {
return ledger.LuckyGiftRewardReceipt{}, err
}
return receiptFromLuckyGiftRewardMetadata(transactionID, metadata), nil
}
func receiptFromLuckyGiftRewardMetadata(transactionID string, metadata luckyGiftRewardMetadata) ledger.LuckyGiftRewardReceipt {
return ledger.LuckyGiftRewardReceipt{
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) { func (r *Repository) receiptForGameTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.GameCoinChangeReceipt, error) {
var metadataJSON string var metadataJSON string
if err := tx.QueryRowContext(ctx, if err := tx.QueryRowContext(ctx,
@ -1836,6 +1957,32 @@ func taskRewardCreditedEvent(transactionID string, commandID string, metadata ta
} }
} }
func luckyGiftRewardCreditedEvent(transactionID string, commandID string, metadata luckyGiftRewardMetadata, nowMs int64) walletOutboxEvent {
return walletOutboxEvent{
EventID: eventID(transactionID, "WalletLuckyGiftRewardCredited", metadata.TargetUserID, ledger.AssetCoin),
EventType: "WalletLuckyGiftRewardCredited",
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,
"draw_id": metadata.DrawID,
"room_id": metadata.RoomID,
"gift_id": metadata.GiftID,
"pool_id": metadata.PoolID,
"amount": metadata.Amount,
"reason": metadata.Reason,
"created_at_ms": nowMs,
},
CreatedAtMS: nowMs,
}
}
func coinSellerStockCreditedEvent(transactionID string, commandID string, metadata coinSellerStockMetadata, nowMs int64) walletOutboxEvent { func coinSellerStockCreditedEvent(transactionID string, commandID string, metadata coinSellerStockMetadata, nowMs int64) walletOutboxEvent {
eventType := "WalletCoinSellerCoinCompensated" eventType := "WalletCoinSellerCoinCompensated"
if metadata.StockType == ledger.StockTypeUSDTPurchase { if metadata.StockType == ledger.StockTypeUSDTPurchase {
@ -1965,6 +2112,19 @@ func taskRewardRequestHash(command ledger.TaskRewardCommand) string {
)) ))
} }
func luckyGiftRewardRequestHash(command ledger.LuckyGiftRewardCommand) string {
return stableHash(fmt.Sprintf("lucky_gift_reward|%s|%d|%d|%s|%s|%s|%s|%s",
appcode.Normalize(command.AppCode),
command.TargetUserID,
command.Amount,
strings.TrimSpace(command.DrawID),
strings.TrimSpace(command.RoomID),
strings.TrimSpace(command.GiftID),
strings.TrimSpace(command.PoolID),
strings.TrimSpace(command.Reason),
))
}
func gameBizTypeAndDelta(opType string, coinAmount int64) (string, int64, error) { func gameBizTypeAndDelta(opType string, coinAmount int64) (string, int64, error) {
switch ledger.NormalizeGameOpType(opType) { switch ledger.NormalizeGameOpType(opType) {
case ledger.GameOpDebit: case ledger.GameOpDebit:

View File

@ -651,6 +651,38 @@ func (s *Server) CreditTaskReward(ctx context.Context, req *walletv1.CreditTaskR
}, nil }, nil
} }
// CreditLuckyGiftReward 处理 activity-service 幸运礼物抽奖 outbox 的中奖金币入账。
func (s *Server) CreditLuckyGiftReward(ctx context.Context, req *walletv1.CreditLuckyGiftRewardRequest) (*walletv1.CreditLuckyGiftRewardResponse, error) {
ctx = appcode.WithContext(ctx, req.GetAppCode())
receipt, err := s.svc.CreditLuckyGiftReward(ctx, ledger.LuckyGiftRewardCommand{
AppCode: req.GetAppCode(),
CommandID: req.GetCommandId(),
TargetUserID: req.GetTargetUserId(),
Amount: req.GetAmount(),
DrawID: req.GetDrawId(),
RoomID: req.GetRoomId(),
GiftID: req.GetGiftId(),
PoolID: req.GetPoolId(),
Reason: req.GetReason(),
})
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &walletv1.CreditLuckyGiftRewardResponse{
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 发起的游戏专用金币改账。 // ApplyGameCoinChange 处理 game-service 发起的游戏专用金币改账。
func (s *Server) ApplyGameCoinChange(ctx context.Context, req *walletv1.ApplyGameCoinChangeRequest) (*walletv1.ApplyGameCoinChangeResponse, error) { func (s *Server) ApplyGameCoinChange(ctx context.Context, req *walletv1.ApplyGameCoinChangeRequest) (*walletv1.ApplyGameCoinChangeResponse, error) {
ctx = appcode.WithContext(ctx, req.GetAppCode()) ctx = appcode.WithContext(ctx, req.GetAppCode())