feat: add voice room red packet APIs

This commit is contained in:
zhx 2026-05-29 17:36:33 +08:00
parent 7433b24411
commit 880f151feb
20 changed files with 1443 additions and 191 deletions

View File

@ -10676,6 +10676,7 @@ type RedPacketConfig struct {
UpdatedByAdminId int64 `protobuf:"varint,8,opt,name=updated_by_admin_id,json=updatedByAdminId,proto3" json:"updated_by_admin_id,omitempty"` UpdatedByAdminId int64 `protobuf:"varint,8,opt,name=updated_by_admin_id,json=updatedByAdminId,proto3" json:"updated_by_admin_id,omitempty"`
CreatedAtMs int64 `protobuf:"varint,9,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` CreatedAtMs int64 `protobuf:"varint,9,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"`
UpdatedAtMs int64 `protobuf:"varint,10,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"` UpdatedAtMs int64 `protobuf:"varint,10,opt,name=updated_at_ms,json=updatedAtMs,proto3" json:"updated_at_ms,omitempty"`
RuleUrl string `protobuf:"bytes,11,opt,name=rule_url,json=ruleUrl,proto3" json:"rule_url,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -10780,6 +10781,13 @@ func (x *RedPacketConfig) GetUpdatedAtMs() int64 {
return 0 return 0
} }
func (x *RedPacketConfig) GetRuleUrl() string {
if x != nil {
return x.RuleUrl
}
return ""
}
type RedPacketClaim struct { type RedPacketClaim struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
AppCode string `protobuf:"bytes,1,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"` AppCode string `protobuf:"bytes,1,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"`
@ -10925,6 +10933,9 @@ type RedPacket struct {
ClaimedByViewer bool `protobuf:"varint,17,opt,name=claimed_by_viewer,json=claimedByViewer,proto3" json:"claimed_by_viewer,omitempty"` ClaimedByViewer bool `protobuf:"varint,17,opt,name=claimed_by_viewer,json=claimedByViewer,proto3" json:"claimed_by_viewer,omitempty"`
ViewerClaimAmount int64 `protobuf:"varint,18,opt,name=viewer_claim_amount,json=viewerClaimAmount,proto3" json:"viewer_claim_amount,omitempty"` ViewerClaimAmount int64 `protobuf:"varint,18,opt,name=viewer_claim_amount,json=viewerClaimAmount,proto3" json:"viewer_claim_amount,omitempty"`
Claims []*RedPacketClaim `protobuf:"bytes,19,rep,name=claims,proto3" json:"claims,omitempty"` Claims []*RedPacketClaim `protobuf:"bytes,19,rep,name=claims,proto3" json:"claims,omitempty"`
RefundAmount int64 `protobuf:"varint,20,opt,name=refund_amount,json=refundAmount,proto3" json:"refund_amount,omitempty"`
RefundStatus string `protobuf:"bytes,21,opt,name=refund_status,json=refundStatus,proto3" json:"refund_status,omitempty"`
RefundedAtMs int64 `protobuf:"varint,22,opt,name=refunded_at_ms,json=refundedAtMs,proto3" json:"refunded_at_ms,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -11092,6 +11103,27 @@ func (x *RedPacket) GetClaims() []*RedPacketClaim {
return nil return nil
} }
func (x *RedPacket) GetRefundAmount() int64 {
if x != nil {
return x.RefundAmount
}
return 0
}
func (x *RedPacket) GetRefundStatus() string {
if x != nil {
return x.RefundStatus
}
return ""
}
func (x *RedPacket) GetRefundedAtMs() int64 {
if x != nil {
return x.RefundedAtMs
}
return 0
}
type GetRedPacketConfigRequest struct { type GetRedPacketConfigRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
@ -11207,6 +11239,7 @@ type UpdateRedPacketConfigRequest struct {
ExpireSeconds int32 `protobuf:"varint,7,opt,name=expire_seconds,json=expireSeconds,proto3" json:"expire_seconds,omitempty"` ExpireSeconds int32 `protobuf:"varint,7,opt,name=expire_seconds,json=expireSeconds,proto3" json:"expire_seconds,omitempty"`
DailySendLimit int32 `protobuf:"varint,8,opt,name=daily_send_limit,json=dailySendLimit,proto3" json:"daily_send_limit,omitempty"` DailySendLimit int32 `protobuf:"varint,8,opt,name=daily_send_limit,json=dailySendLimit,proto3" json:"daily_send_limit,omitempty"`
OperatorUserId int64 `protobuf:"varint,9,opt,name=operator_user_id,json=operatorUserId,proto3" json:"operator_user_id,omitempty"` OperatorUserId int64 `protobuf:"varint,9,opt,name=operator_user_id,json=operatorUserId,proto3" json:"operator_user_id,omitempty"`
RuleUrl string `protobuf:"bytes,10,opt,name=rule_url,json=ruleUrl,proto3" json:"rule_url,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -11304,6 +11337,13 @@ func (x *UpdateRedPacketConfigRequest) GetOperatorUserId() int64 {
return 0 return 0
} }
func (x *UpdateRedPacketConfigRequest) GetRuleUrl() string {
if x != nil {
return x.RuleUrl
}
return ""
}
type UpdateRedPacketConfigResponse struct { type UpdateRedPacketConfigResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Config *RedPacketConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` Config *RedPacketConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
@ -11605,6 +11645,7 @@ type ClaimRedPacketResponse struct {
Claim *RedPacketClaim `protobuf:"bytes,1,opt,name=claim,proto3" json:"claim,omitempty"` Claim *RedPacketClaim `protobuf:"bytes,1,opt,name=claim,proto3" json:"claim,omitempty"`
BalanceAfter int64 `protobuf:"varint,2,opt,name=balance_after,json=balanceAfter,proto3" json:"balance_after,omitempty"` BalanceAfter int64 `protobuf:"varint,2,opt,name=balance_after,json=balanceAfter,proto3" json:"balance_after,omitempty"`
ServerTimeMs int64 `protobuf:"varint,3,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"` ServerTimeMs int64 `protobuf:"varint,3,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"`
Packet *RedPacket `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -11660,6 +11701,13 @@ func (x *ClaimRedPacketResponse) GetServerTimeMs() int64 {
return 0 return 0
} }
func (x *ClaimRedPacketResponse) GetPacket() *RedPacket {
if x != nil {
return x.Packet
}
return nil
}
type ListRedPacketsRequest struct { type ListRedPacketsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
@ -12100,6 +12148,134 @@ func (x *ExpireRedPacketsResponse) GetServerTimeMs() int64 {
return 0 return 0
} }
type RetryRedPacketRefundRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
AppCode string `protobuf:"bytes,2,opt,name=app_code,json=appCode,proto3" json:"app_code,omitempty"`
PacketId string `protobuf:"bytes,3,opt,name=packet_id,json=packetId,proto3" json:"packet_id,omitempty"`
OperatorUserId int64 `protobuf:"varint,4,opt,name=operator_user_id,json=operatorUserId,proto3" json:"operator_user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RetryRedPacketRefundRequest) Reset() {
*x = RetryRedPacketRefundRequest{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[137]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RetryRedPacketRefundRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RetryRedPacketRefundRequest) ProtoMessage() {}
func (x *RetryRedPacketRefundRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[137]
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 RetryRedPacketRefundRequest.ProtoReflect.Descriptor instead.
func (*RetryRedPacketRefundRequest) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{137}
}
func (x *RetryRedPacketRefundRequest) GetRequestId() string {
if x != nil {
return x.RequestId
}
return ""
}
func (x *RetryRedPacketRefundRequest) GetAppCode() string {
if x != nil {
return x.AppCode
}
return ""
}
func (x *RetryRedPacketRefundRequest) GetPacketId() string {
if x != nil {
return x.PacketId
}
return ""
}
func (x *RetryRedPacketRefundRequest) GetOperatorUserId() int64 {
if x != nil {
return x.OperatorUserId
}
return 0
}
type RetryRedPacketRefundResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Packet *RedPacket `protobuf:"bytes,1,opt,name=packet,proto3" json:"packet,omitempty"`
RefundedAmount int64 `protobuf:"varint,2,opt,name=refunded_amount,json=refundedAmount,proto3" json:"refunded_amount,omitempty"`
ServerTimeMs int64 `protobuf:"varint,3,opt,name=server_time_ms,json=serverTimeMs,proto3" json:"server_time_ms,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RetryRedPacketRefundResponse) Reset() {
*x = RetryRedPacketRefundResponse{}
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[138]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RetryRedPacketRefundResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RetryRedPacketRefundResponse) ProtoMessage() {}
func (x *RetryRedPacketRefundResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_wallet_v1_wallet_proto_msgTypes[138]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RetryRedPacketRefundResponse.ProtoReflect.Descriptor instead.
func (*RetryRedPacketRefundResponse) Descriptor() ([]byte, []int) {
return file_proto_wallet_v1_wallet_proto_rawDescGZIP(), []int{138}
}
func (x *RetryRedPacketRefundResponse) GetPacket() *RedPacket {
if x != nil {
return x.Packet
}
return nil
}
func (x *RetryRedPacketRefundResponse) GetRefundedAmount() int64 {
if x != nil {
return x.RefundedAmount
}
return 0
}
func (x *RetryRedPacketRefundResponse) GetServerTimeMs() int64 {
if x != nil {
return x.ServerTimeMs
}
return 0
}
var File_proto_wallet_v1_wallet_proto protoreflect.FileDescriptor var File_proto_wallet_v1_wallet_proto protoreflect.FileDescriptor
const file_proto_wallet_v1_wallet_proto_rawDesc = "" + const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
@ -13214,7 +13390,7 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x1bApplyGameCoinChangeResponse\x122\n" + "\x1bApplyGameCoinChangeResponse\x122\n" +
"\x15wallet_transaction_id\x18\x01 \x01(\tR\x13walletTransactionId\x12#\n" + "\x15wallet_transaction_id\x18\x01 \x01(\tR\x13walletTransactionId\x12#\n" +
"\rbalance_after\x18\x02 \x01(\x03R\fbalanceAfter\x12+\n" + "\rbalance_after\x18\x02 \x01(\x03R\fbalanceAfter\x12+\n" +
"\x11idempotent_replay\x18\x03 \x01(\bR\x10idempotentReplay\"\x84\x03\n" + "\x11idempotent_replay\x18\x03 \x01(\bR\x10idempotentReplay\"\x9f\x03\n" +
"\x0fRedPacketConfig\x12\x19\n" + "\x0fRedPacketConfig\x12\x19\n" +
"\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x18\n" + "\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x18\n" +
"\aenabled\x18\x02 \x01(\bR\aenabled\x12\x1f\n" + "\aenabled\x18\x02 \x01(\bR\aenabled\x12\x1f\n" +
@ -13227,7 +13403,8 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x13updated_by_admin_id\x18\b \x01(\x03R\x10updatedByAdminId\x12\"\n" + "\x13updated_by_admin_id\x18\b \x01(\x03R\x10updatedByAdminId\x12\"\n" +
"\rcreated_at_ms\x18\t \x01(\x03R\vcreatedAtMs\x12\"\n" + "\rcreated_at_ms\x18\t \x01(\x03R\vcreatedAtMs\x12\"\n" +
"\rupdated_at_ms\x18\n" + "\rupdated_at_ms\x18\n" +
" \x01(\x03R\vupdatedAtMs\"\xee\x02\n" + " \x01(\x03R\vupdatedAtMs\x12\x19\n" +
"\brule_url\x18\v \x01(\tR\aruleUrl\"\xee\x02\n" +
"\x0eRedPacketClaim\x12\x19\n" + "\x0eRedPacketClaim\x12\x19\n" +
"\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x19\n" + "\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x19\n" +
"\bclaim_id\x18\x02 \x01(\tR\aclaimId\x12\x1d\n" + "\bclaim_id\x18\x02 \x01(\tR\aclaimId\x12\x1d\n" +
@ -13241,7 +13418,7 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x0efailure_reason\x18\t \x01(\tR\rfailureReason\x12\"\n" + "\x0efailure_reason\x18\t \x01(\tR\rfailureReason\x12\"\n" +
"\rcreated_at_ms\x18\n" + "\rcreated_at_ms\x18\n" +
" \x01(\x03R\vcreatedAtMs\x12\"\n" + " \x01(\x03R\vcreatedAtMs\x12\"\n" +
"\rupdated_at_ms\x18\v \x01(\x03R\vupdatedAtMs\"\xb0\x05\n" + "\rupdated_at_ms\x18\v \x01(\x03R\vupdatedAtMs\"\xa0\x06\n" +
"\tRedPacket\x12\x19\n" + "\tRedPacket\x12\x19\n" +
"\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x1b\n" + "\bapp_code\x18\x01 \x01(\tR\aappCode\x12\x1b\n" +
"\tpacket_id\x18\x02 \x01(\tR\bpacketId\x12\x1d\n" + "\tpacket_id\x18\x02 \x01(\tR\bpacketId\x12\x1d\n" +
@ -13265,14 +13442,17 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\rupdated_at_ms\x18\x10 \x01(\x03R\vupdatedAtMs\x12*\n" + "\rupdated_at_ms\x18\x10 \x01(\x03R\vupdatedAtMs\x12*\n" +
"\x11claimed_by_viewer\x18\x11 \x01(\bR\x0fclaimedByViewer\x12.\n" + "\x11claimed_by_viewer\x18\x11 \x01(\bR\x0fclaimedByViewer\x12.\n" +
"\x13viewer_claim_amount\x18\x12 \x01(\x03R\x11viewerClaimAmount\x127\n" + "\x13viewer_claim_amount\x18\x12 \x01(\x03R\x11viewerClaimAmount\x127\n" +
"\x06claims\x18\x13 \x03(\v2\x1f.hyapp.wallet.v1.RedPacketClaimR\x06claims\"U\n" + "\x06claims\x18\x13 \x03(\v2\x1f.hyapp.wallet.v1.RedPacketClaimR\x06claims\x12#\n" +
"\rrefund_amount\x18\x14 \x01(\x03R\frefundAmount\x12#\n" +
"\rrefund_status\x18\x15 \x01(\tR\frefundStatus\x12$\n" +
"\x0erefunded_at_ms\x18\x16 \x01(\x03R\frefundedAtMs\"U\n" +
"\x19GetRedPacketConfigRequest\x12\x1d\n" + "\x19GetRedPacketConfigRequest\x12\x1d\n" +
"\n" + "\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" +
"\bapp_code\x18\x02 \x01(\tR\aappCode\"|\n" + "\bapp_code\x18\x02 \x01(\tR\aappCode\"|\n" +
"\x1aGetRedPacketConfigResponse\x128\n" + "\x1aGetRedPacketConfigResponse\x128\n" +
"\x06config\x18\x01 \x01(\v2 .hyapp.wallet.v1.RedPacketConfigR\x06config\x12$\n" + "\x06config\x18\x01 \x01(\v2 .hyapp.wallet.v1.RedPacketConfigR\x06config\x12$\n" +
"\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xe3\x02\n" + "\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xfe\x02\n" +
"\x1cUpdateRedPacketConfigRequest\x12\x1d\n" + "\x1cUpdateRedPacketConfigRequest\x12\x1d\n" +
"\n" + "\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" +
@ -13284,7 +13464,9 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x14delayed_open_seconds\x18\x06 \x01(\x05R\x12delayedOpenSeconds\x12%\n" + "\x14delayed_open_seconds\x18\x06 \x01(\x05R\x12delayedOpenSeconds\x12%\n" +
"\x0eexpire_seconds\x18\a \x01(\x05R\rexpireSeconds\x12(\n" + "\x0eexpire_seconds\x18\a \x01(\x05R\rexpireSeconds\x12(\n" +
"\x10daily_send_limit\x18\b \x01(\x05R\x0edailySendLimit\x12(\n" + "\x10daily_send_limit\x18\b \x01(\x05R\x0edailySendLimit\x12(\n" +
"\x10operator_user_id\x18\t \x01(\x03R\x0eoperatorUserId\"\x7f\n" + "\x10operator_user_id\x18\t \x01(\x03R\x0eoperatorUserId\x12\x19\n" +
"\brule_url\x18\n" +
" \x01(\tR\aruleUrl\"\x7f\n" +
"\x1dUpdateRedPacketConfigResponse\x128\n" + "\x1dUpdateRedPacketConfigResponse\x128\n" +
"\x06config\x18\x01 \x01(\v2 .hyapp.wallet.v1.RedPacketConfigR\x06config\x12$\n" + "\x06config\x18\x01 \x01(\v2 .hyapp.wallet.v1.RedPacketConfigR\x06config\x12$\n" +
"\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xb4\x02\n" + "\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xb4\x02\n" +
@ -13312,11 +13494,12 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\n" + "\n" +
"command_id\x18\x03 \x01(\tR\tcommandId\x12\x1b\n" + "command_id\x18\x03 \x01(\tR\tcommandId\x12\x1b\n" +
"\tpacket_id\x18\x04 \x01(\tR\bpacketId\x12\x17\n" + "\tpacket_id\x18\x04 \x01(\tR\bpacketId\x12\x17\n" +
"\auser_id\x18\x05 \x01(\x03R\x06userId\"\x9a\x01\n" + "\auser_id\x18\x05 \x01(\x03R\x06userId\"\xce\x01\n" +
"\x16ClaimRedPacketResponse\x125\n" + "\x16ClaimRedPacketResponse\x125\n" +
"\x05claim\x18\x01 \x01(\v2\x1f.hyapp.wallet.v1.RedPacketClaimR\x05claim\x12#\n" + "\x05claim\x18\x01 \x01(\v2\x1f.hyapp.wallet.v1.RedPacketClaimR\x05claim\x12#\n" +
"\rbalance_after\x18\x02 \x01(\x03R\fbalanceAfter\x12$\n" + "\rbalance_after\x18\x02 \x01(\x03R\fbalanceAfter\x12$\n" +
"\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs\"\xf9\x02\n" + "\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs\x122\n" +
"\x06packet\x18\x04 \x01(\v2\x1a.hyapp.wallet.v1.RedPacketR\x06packet\"\xf9\x02\n" +
"\x15ListRedPacketsRequest\x12\x1d\n" + "\x15ListRedPacketsRequest\x12\x1d\n" +
"\n" + "\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" +
@ -13356,7 +13539,17 @@ 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\xf01\n" + "\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs\"\x9e\x01\n" +
"\x1bRetryRedPacketRefundRequest\x12\x1d\n" +
"\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" +
"\bapp_code\x18\x02 \x01(\tR\aappCode\x12\x1b\n" +
"\tpacket_id\x18\x03 \x01(\tR\bpacketId\x12(\n" +
"\x10operator_user_id\x18\x04 \x01(\x03R\x0eoperatorUserId\"\xa1\x01\n" +
"\x1cRetryRedPacketRefundResponse\x122\n" +
"\x06packet\x18\x01 \x01(\v2\x1a.hyapp.wallet.v1.RedPacketR\x06packet\x12'\n" +
"\x0frefunded_amount\x18\x02 \x01(\x03R\x0erefundedAmount\x12$\n" +
"\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs2\xe52\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" +
@ -13416,7 +13609,8 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x0eClaimRedPacket\x12&.hyapp.wallet.v1.ClaimRedPacketRequest\x1a'.hyapp.wallet.v1.ClaimRedPacketResponse\x12a\n" + "\x0eClaimRedPacket\x12&.hyapp.wallet.v1.ClaimRedPacketRequest\x1a'.hyapp.wallet.v1.ClaimRedPacketResponse\x12a\n" +
"\x0eListRedPackets\x12&.hyapp.wallet.v1.ListRedPacketsRequest\x1a'.hyapp.wallet.v1.ListRedPacketsResponse\x12[\n" + "\x0eListRedPackets\x12&.hyapp.wallet.v1.ListRedPacketsRequest\x1a'.hyapp.wallet.v1.ListRedPacketsResponse\x12[\n" +
"\fGetRedPacket\x12$.hyapp.wallet.v1.GetRedPacketRequest\x1a%.hyapp.wallet.v1.GetRedPacketResponse\x12g\n" + "\fGetRedPacket\x12$.hyapp.wallet.v1.GetRedPacketRequest\x1a%.hyapp.wallet.v1.GetRedPacketResponse\x12g\n" +
"\x10ExpireRedPackets\x12(.hyapp.wallet.v1.ExpireRedPacketsRequest\x1a).hyapp.wallet.v1.ExpireRedPacketsResponseB*Z(hyapp.local/api/proto/wallet/v1;walletv1b\x06proto3" "\x10ExpireRedPackets\x12(.hyapp.wallet.v1.ExpireRedPacketsRequest\x1a).hyapp.wallet.v1.ExpireRedPacketsResponse\x12s\n" +
"\x14RetryRedPacketRefund\x12,.hyapp.wallet.v1.RetryRedPacketRefundRequest\x1a-.hyapp.wallet.v1.RetryRedPacketRefundResponseB*Z(hyapp.local/api/proto/wallet/v1;walletv1b\x06proto3"
var ( var (
file_proto_wallet_v1_wallet_proto_rawDescOnce sync.Once file_proto_wallet_v1_wallet_proto_rawDescOnce sync.Once
@ -13430,7 +13624,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, 137) var file_proto_wallet_v1_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 139)
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
@ -13569,6 +13763,8 @@ var file_proto_wallet_v1_wallet_proto_goTypes = []any{
(*GetRedPacketResponse)(nil), // 134: hyapp.wallet.v1.GetRedPacketResponse (*GetRedPacketResponse)(nil), // 134: hyapp.wallet.v1.GetRedPacketResponse
(*ExpireRedPacketsRequest)(nil), // 135: hyapp.wallet.v1.ExpireRedPacketsRequest (*ExpireRedPacketsRequest)(nil), // 135: hyapp.wallet.v1.ExpireRedPacketsRequest
(*ExpireRedPacketsResponse)(nil), // 136: hyapp.wallet.v1.ExpireRedPacketsResponse (*ExpireRedPacketsResponse)(nil), // 136: hyapp.wallet.v1.ExpireRedPacketsResponse
(*RetryRedPacketRefundRequest)(nil), // 137: hyapp.wallet.v1.RetryRedPacketRefundRequest
(*RetryRedPacketRefundResponse)(nil), // 138: hyapp.wallet.v1.RetryRedPacketRefundResponse
} }
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
@ -13634,131 +13830,135 @@ var file_proto_wallet_v1_wallet_proto_depIdxs = []int32{
120, // 60: hyapp.wallet.v1.UpdateRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig 120, // 60: hyapp.wallet.v1.UpdateRedPacketConfigResponse.config:type_name -> hyapp.wallet.v1.RedPacketConfig
122, // 61: hyapp.wallet.v1.CreateRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket 122, // 61: hyapp.wallet.v1.CreateRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket
121, // 62: hyapp.wallet.v1.ClaimRedPacketResponse.claim:type_name -> hyapp.wallet.v1.RedPacketClaim 121, // 62: hyapp.wallet.v1.ClaimRedPacketResponse.claim:type_name -> hyapp.wallet.v1.RedPacketClaim
122, // 63: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket 122, // 63: hyapp.wallet.v1.ClaimRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket
122, // 64: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket 122, // 64: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket
0, // 65: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest 122, // 65: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket
3, // 66: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest 122, // 66: hyapp.wallet.v1.RetryRedPacketRefundResponse.packet:type_name -> hyapp.wallet.v1.RedPacket
5, // 67: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest 0, // 67: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest
7, // 68: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest 3, // 68: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest
9, // 69: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest 5, // 69: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest
21, // 70: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest 7, // 70: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest
23, // 71: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest 9, // 71: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest
25, // 72: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest 21, // 72: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest
26, // 73: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest 23, // 73: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest
27, // 74: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest 25, // 74: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest
29, // 75: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest 26, // 75: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest
31, // 76: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest 27, // 76: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest
33, // 77: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest 29, // 77: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest
34, // 78: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest 31, // 78: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest
35, // 79: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest 33, // 79: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest
37, // 80: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest 34, // 80: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest
39, // 81: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest 35, // 81: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest
43, // 82: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest 37, // 82: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest
44, // 83: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest 39, // 83: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest
45, // 84: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest 43, // 84: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest
41, // 85: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest 44, // 85: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest
47, // 86: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest 45, // 86: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest
48, // 87: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest 41, // 87: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest
50, // 88: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest 47, // 88: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest
52, // 89: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest 48, // 89: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest
54, // 90: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest 50, // 90: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest
57, // 91: hyapp.wallet.v1.WalletService.ListResourceShopItems:input_type -> hyapp.wallet.v1.ListResourceShopItemsRequest 52, // 91: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest
59, // 92: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:input_type -> hyapp.wallet.v1.UpsertResourceShopItemsRequest 54, // 92: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest
61, // 93: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:input_type -> hyapp.wallet.v1.SetResourceShopItemStatusRequest 57, // 93: hyapp.wallet.v1.WalletService.ListResourceShopItems:input_type -> hyapp.wallet.v1.ListResourceShopItemsRequest
63, // 94: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:input_type -> hyapp.wallet.v1.PurchaseResourceShopItemRequest 59, // 94: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:input_type -> hyapp.wallet.v1.UpsertResourceShopItemsRequest
66, // 95: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest 61, // 95: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:input_type -> hyapp.wallet.v1.SetResourceShopItemStatusRequest
69, // 96: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest 63, // 96: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:input_type -> hyapp.wallet.v1.PurchaseResourceShopItemRequest
72, // 97: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest 66, // 97: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest
75, // 98: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest 69, // 98: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest
78, // 99: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest 72, // 99: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest
80, // 100: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest 75, // 100: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest
82, // 101: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest 78, // 101: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest
84, // 102: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest 80, // 102: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest
85, // 103: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest 82, // 103: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest
86, // 104: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest 84, // 104: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest
90, // 105: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest 85, // 105: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest
93, // 106: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest 86, // 106: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest
96, // 107: hyapp.wallet.v1.WalletService.ApplyWithdrawal:input_type -> hyapp.wallet.v1.ApplyWithdrawalRequest 90, // 107: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest
101, // 108: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest 93, // 108: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest
103, // 109: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest 96, // 109: hyapp.wallet.v1.WalletService.ApplyWithdrawal:input_type -> hyapp.wallet.v1.ApplyWithdrawalRequest
105, // 110: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest 101, // 110: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest
107, // 111: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest 103, // 111: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest
110, // 112: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest 105, // 112: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest
112, // 113: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest 107, // 113: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest
114, // 114: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest 110, // 114: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest
116, // 115: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:input_type -> hyapp.wallet.v1.CreditLuckyGiftRewardRequest 112, // 115: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest
118, // 116: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest 114, // 116: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest
123, // 117: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest 116, // 117: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:input_type -> hyapp.wallet.v1.CreditLuckyGiftRewardRequest
125, // 118: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest 118, // 118: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest
127, // 119: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest 123, // 119: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest
129, // 120: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest 125, // 120: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest
131, // 121: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest 127, // 121: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest
133, // 122: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest 129, // 122: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest
135, // 123: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest 131, // 123: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest
1, // 124: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse 133, // 124: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest
4, // 125: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse 135, // 125: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest
6, // 126: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse 137, // 126: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:input_type -> hyapp.wallet.v1.RetryRedPacketRefundRequest
8, // 127: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse 1, // 127: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse
10, // 128: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse 4, // 128: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse
22, // 129: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse 6, // 129: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse
24, // 130: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse 8, // 130: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse
28, // 131: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse 10, // 131: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse
28, // 132: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse 22, // 132: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse
28, // 133: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse 24, // 133: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse
30, // 134: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse 28, // 134: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse
32, // 135: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse 28, // 135: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse
36, // 136: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse 28, // 136: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse
36, // 137: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse 30, // 137: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse
36, // 138: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse 32, // 138: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse
38, // 139: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse 36, // 139: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse
40, // 140: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse 36, // 140: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse
46, // 141: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse 36, // 141: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse
46, // 142: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse 38, // 142: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse
46, // 143: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse 40, // 143: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse
42, // 144: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse 46, // 144: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse
49, // 145: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse 46, // 145: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse
49, // 146: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse 46, // 146: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse
51, // 147: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse 42, // 147: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse
53, // 148: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse 49, // 148: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse
55, // 149: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse 49, // 149: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse
58, // 150: hyapp.wallet.v1.WalletService.ListResourceShopItems:output_type -> hyapp.wallet.v1.ListResourceShopItemsResponse 51, // 150: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse
60, // 151: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:output_type -> hyapp.wallet.v1.UpsertResourceShopItemsResponse 53, // 151: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse
62, // 152: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:output_type -> hyapp.wallet.v1.ResourceShopItemResponse 55, // 152: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse
64, // 153: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:output_type -> hyapp.wallet.v1.PurchaseResourceShopItemResponse 58, // 153: hyapp.wallet.v1.WalletService.ListResourceShopItems:output_type -> hyapp.wallet.v1.ListResourceShopItemsResponse
67, // 154: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse 60, // 154: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:output_type -> hyapp.wallet.v1.UpsertResourceShopItemsResponse
70, // 155: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse 62, // 155: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:output_type -> hyapp.wallet.v1.ResourceShopItemResponse
73, // 156: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse 64, // 156: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:output_type -> hyapp.wallet.v1.PurchaseResourceShopItemResponse
76, // 157: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse 67, // 157: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse
79, // 158: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse 70, // 158: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse
81, // 159: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse 73, // 159: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse
83, // 160: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse 76, // 160: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse
87, // 161: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse 79, // 161: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse
87, // 162: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse 81, // 162: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse
88, // 163: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse 83, // 163: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse
91, // 164: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse 87, // 164: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse
94, // 165: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse 87, // 165: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse
97, // 166: hyapp.wallet.v1.WalletService.ApplyWithdrawal:output_type -> hyapp.wallet.v1.ApplyWithdrawalResponse 88, // 166: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse
102, // 167: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse 91, // 167: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse
104, // 168: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse 94, // 168: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse
106, // 169: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse 97, // 169: hyapp.wallet.v1.WalletService.ApplyWithdrawal:output_type -> hyapp.wallet.v1.ApplyWithdrawalResponse
108, // 170: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse 102, // 170: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse
111, // 171: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse 104, // 171: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse
113, // 172: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse 106, // 172: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse
115, // 173: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse 108, // 173: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse
117, // 174: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:output_type -> hyapp.wallet.v1.CreditLuckyGiftRewardResponse 111, // 174: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse
119, // 175: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse 113, // 175: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse
124, // 176: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse 115, // 176: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse
126, // 177: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse 117, // 177: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:output_type -> hyapp.wallet.v1.CreditLuckyGiftRewardResponse
128, // 178: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse 119, // 178: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse
130, // 179: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse 124, // 179: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse
132, // 180: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse 126, // 180: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse
134, // 181: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse 128, // 181: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse
136, // 182: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse 130, // 182: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse
124, // [124:183] is the sub-list for method output_type 132, // 183: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse
65, // [65:124] is the sub-list for method input_type 134, // 184: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse
65, // [65:65] is the sub-list for extension type_name 136, // 185: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse
65, // [65:65] is the sub-list for extension extendee 138, // 186: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:output_type -> hyapp.wallet.v1.RetryRedPacketRefundResponse
0, // [0:65] is the sub-list for field type_name 127, // [127:187] is the sub-list for method output_type
67, // [67:127] is the sub-list for method input_type
67, // [67:67] is the sub-list for extension type_name
67, // [67:67] is the sub-list for extension extendee
0, // [0:67] 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() }
@ -13774,7 +13974,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: 137, NumMessages: 139,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -1189,6 +1189,7 @@ message RedPacketConfig {
int64 updated_by_admin_id = 8; int64 updated_by_admin_id = 8;
int64 created_at_ms = 9; int64 created_at_ms = 9;
int64 updated_at_ms = 10; int64 updated_at_ms = 10;
string rule_url = 11;
} }
message RedPacketClaim { message RedPacketClaim {
@ -1225,6 +1226,9 @@ message RedPacket {
bool claimed_by_viewer = 17; bool claimed_by_viewer = 17;
int64 viewer_claim_amount = 18; int64 viewer_claim_amount = 18;
repeated RedPacketClaim claims = 19; repeated RedPacketClaim claims = 19;
int64 refund_amount = 20;
string refund_status = 21;
int64 refunded_at_ms = 22;
} }
message GetRedPacketConfigRequest { message GetRedPacketConfigRequest {
@ -1247,6 +1251,7 @@ message UpdateRedPacketConfigRequest {
int32 expire_seconds = 7; int32 expire_seconds = 7;
int32 daily_send_limit = 8; int32 daily_send_limit = 8;
int64 operator_user_id = 9; int64 operator_user_id = 9;
string rule_url = 10;
} }
message UpdateRedPacketConfigResponse { message UpdateRedPacketConfigResponse {
@ -1284,6 +1289,7 @@ message ClaimRedPacketResponse {
RedPacketClaim claim = 1; RedPacketClaim claim = 1;
int64 balance_after = 2; int64 balance_after = 2;
int64 server_time_ms = 3; int64 server_time_ms = 3;
RedPacket packet = 4;
} }
message ListRedPacketsRequest { message ListRedPacketsRequest {
@ -1332,6 +1338,19 @@ message ExpireRedPacketsResponse {
int64 server_time_ms = 3; int64 server_time_ms = 3;
} }
message RetryRedPacketRefundRequest {
string request_id = 1;
string app_code = 2;
string packet_id = 3;
int64 operator_user_id = 4;
}
message RetryRedPacketRefundResponse {
RedPacket packet = 1;
int64 refunded_amount = 2;
int64 server_time_ms = 3;
}
// WalletService gRPC HTTP gateway-service // WalletService gRPC HTTP gateway-service
service WalletService { service WalletService {
rpc DebitGift(DebitGiftRequest) returns (DebitGiftResponse); rpc DebitGift(DebitGiftRequest) returns (DebitGiftResponse);
@ -1393,4 +1412,5 @@ service WalletService {
rpc ListRedPackets(ListRedPacketsRequest) returns (ListRedPacketsResponse); rpc ListRedPackets(ListRedPacketsRequest) returns (ListRedPacketsResponse);
rpc GetRedPacket(GetRedPacketRequest) returns (GetRedPacketResponse); rpc GetRedPacket(GetRedPacketRequest) returns (GetRedPacketResponse);
rpc ExpireRedPackets(ExpireRedPacketsRequest) returns (ExpireRedPacketsResponse); rpc ExpireRedPackets(ExpireRedPacketsRequest) returns (ExpireRedPacketsResponse);
rpc RetryRedPacketRefund(RetryRedPacketRefundRequest) returns (RetryRedPacketRefundResponse);
} }

View File

@ -78,6 +78,7 @@ const (
WalletService_ListRedPackets_FullMethodName = "/hyapp.wallet.v1.WalletService/ListRedPackets" WalletService_ListRedPackets_FullMethodName = "/hyapp.wallet.v1.WalletService/ListRedPackets"
WalletService_GetRedPacket_FullMethodName = "/hyapp.wallet.v1.WalletService/GetRedPacket" WalletService_GetRedPacket_FullMethodName = "/hyapp.wallet.v1.WalletService/GetRedPacket"
WalletService_ExpireRedPackets_FullMethodName = "/hyapp.wallet.v1.WalletService/ExpireRedPackets" WalletService_ExpireRedPackets_FullMethodName = "/hyapp.wallet.v1.WalletService/ExpireRedPackets"
WalletService_RetryRedPacketRefund_FullMethodName = "/hyapp.wallet.v1.WalletService/RetryRedPacketRefund"
) )
// WalletServiceClient is the client API for WalletService service. // WalletServiceClient is the client API for WalletService service.
@ -145,6 +146,7 @@ type WalletServiceClient interface {
ListRedPackets(ctx context.Context, in *ListRedPacketsRequest, opts ...grpc.CallOption) (*ListRedPacketsResponse, error) ListRedPackets(ctx context.Context, in *ListRedPacketsRequest, opts ...grpc.CallOption) (*ListRedPacketsResponse, error)
GetRedPacket(ctx context.Context, in *GetRedPacketRequest, opts ...grpc.CallOption) (*GetRedPacketResponse, error) GetRedPacket(ctx context.Context, in *GetRedPacketRequest, opts ...grpc.CallOption) (*GetRedPacketResponse, error)
ExpireRedPackets(ctx context.Context, in *ExpireRedPacketsRequest, opts ...grpc.CallOption) (*ExpireRedPacketsResponse, error) ExpireRedPackets(ctx context.Context, in *ExpireRedPacketsRequest, opts ...grpc.CallOption) (*ExpireRedPacketsResponse, error)
RetryRedPacketRefund(ctx context.Context, in *RetryRedPacketRefundRequest, opts ...grpc.CallOption) (*RetryRedPacketRefundResponse, error)
} }
type walletServiceClient struct { type walletServiceClient struct {
@ -745,6 +747,16 @@ func (c *walletServiceClient) ExpireRedPackets(ctx context.Context, in *ExpireRe
return out, nil return out, nil
} }
func (c *walletServiceClient) RetryRedPacketRefund(ctx context.Context, in *RetryRedPacketRefundRequest, opts ...grpc.CallOption) (*RetryRedPacketRefundResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RetryRedPacketRefundResponse)
err := c.cc.Invoke(ctx, WalletService_RetryRedPacketRefund_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// WalletServiceServer is the server API for WalletService service. // WalletServiceServer is the server API for WalletService service.
// All implementations must embed UnimplementedWalletServiceServer // All implementations must embed UnimplementedWalletServiceServer
// for forward compatibility. // for forward compatibility.
@ -810,6 +822,7 @@ type WalletServiceServer interface {
ListRedPackets(context.Context, *ListRedPacketsRequest) (*ListRedPacketsResponse, error) ListRedPackets(context.Context, *ListRedPacketsRequest) (*ListRedPacketsResponse, error)
GetRedPacket(context.Context, *GetRedPacketRequest) (*GetRedPacketResponse, error) GetRedPacket(context.Context, *GetRedPacketRequest) (*GetRedPacketResponse, error)
ExpireRedPackets(context.Context, *ExpireRedPacketsRequest) (*ExpireRedPacketsResponse, error) ExpireRedPackets(context.Context, *ExpireRedPacketsRequest) (*ExpireRedPacketsResponse, error)
RetryRedPacketRefund(context.Context, *RetryRedPacketRefundRequest) (*RetryRedPacketRefundResponse, error)
mustEmbedUnimplementedWalletServiceServer() mustEmbedUnimplementedWalletServiceServer()
} }
@ -997,6 +1010,9 @@ func (UnimplementedWalletServiceServer) GetRedPacket(context.Context, *GetRedPac
func (UnimplementedWalletServiceServer) ExpireRedPackets(context.Context, *ExpireRedPacketsRequest) (*ExpireRedPacketsResponse, error) { func (UnimplementedWalletServiceServer) ExpireRedPackets(context.Context, *ExpireRedPacketsRequest) (*ExpireRedPacketsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ExpireRedPackets not implemented") return nil, status.Error(codes.Unimplemented, "method ExpireRedPackets not implemented")
} }
func (UnimplementedWalletServiceServer) RetryRedPacketRefund(context.Context, *RetryRedPacketRefundRequest) (*RetryRedPacketRefundResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RetryRedPacketRefund not implemented")
}
func (UnimplementedWalletServiceServer) mustEmbedUnimplementedWalletServiceServer() {} func (UnimplementedWalletServiceServer) mustEmbedUnimplementedWalletServiceServer() {}
func (UnimplementedWalletServiceServer) testEmbeddedByValue() {} func (UnimplementedWalletServiceServer) testEmbeddedByValue() {}
@ -2080,6 +2096,24 @@ func _WalletService_ExpireRedPackets_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _WalletService_RetryRedPacketRefund_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RetryRedPacketRefundRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WalletServiceServer).RetryRedPacketRefund(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: WalletService_RetryRedPacketRefund_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WalletServiceServer).RetryRedPacketRefund(ctx, req.(*RetryRedPacketRefundRequest))
}
return interceptor(ctx, in, info, handler)
}
// WalletService_ServiceDesc is the grpc.ServiceDesc for WalletService service. // WalletService_ServiceDesc is the grpc.ServiceDesc for WalletService service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -2323,6 +2357,10 @@ var WalletService_ServiceDesc = grpc.ServiceDesc{
MethodName: "ExpireRedPackets", MethodName: "ExpireRedPackets",
Handler: _WalletService_ExpireRedPackets_Handler, Handler: _WalletService_ExpireRedPackets_Handler,
}, },
{
MethodName: "RetryRedPacketRefund",
Handler: _WalletService_RetryRedPacketRefund_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "proto/wallet/v1/wallet.proto", Metadata: "proto/wallet/v1/wallet.proto",

View File

@ -46,6 +46,7 @@ type Client interface {
UpdateRedPacketConfig(ctx context.Context, req *walletv1.UpdateRedPacketConfigRequest) (*walletv1.UpdateRedPacketConfigResponse, error) UpdateRedPacketConfig(ctx context.Context, req *walletv1.UpdateRedPacketConfigRequest) (*walletv1.UpdateRedPacketConfigResponse, error)
ListRedPackets(ctx context.Context, req *walletv1.ListRedPacketsRequest) (*walletv1.ListRedPacketsResponse, error) ListRedPackets(ctx context.Context, req *walletv1.ListRedPacketsRequest) (*walletv1.ListRedPacketsResponse, error)
GetRedPacket(ctx context.Context, req *walletv1.GetRedPacketRequest) (*walletv1.GetRedPacketResponse, error) GetRedPacket(ctx context.Context, req *walletv1.GetRedPacketRequest) (*walletv1.GetRedPacketResponse, error)
RetryRedPacketRefund(ctx context.Context, req *walletv1.RetryRedPacketRefundRequest) (*walletv1.RetryRedPacketRefundResponse, error)
} }
type GRPCClient struct { type GRPCClient struct {
@ -199,3 +200,7 @@ func (c *GRPCClient) ListRedPackets(ctx context.Context, req *walletv1.ListRedPa
func (c *GRPCClient) GetRedPacket(ctx context.Context, req *walletv1.GetRedPacketRequest) (*walletv1.GetRedPacketResponse, error) { func (c *GRPCClient) GetRedPacket(ctx context.Context, req *walletv1.GetRedPacketRequest) (*walletv1.GetRedPacketResponse, error) {
return c.client.GetRedPacket(ctx, req) return c.client.GetRedPacket(ctx, req)
} }
func (c *GRPCClient) RetryRedPacketRefund(ctx context.Context, req *walletv1.RetryRedPacketRefundRequest) (*walletv1.RetryRedPacketRefundResponse, error) {
return c.client.RetryRedPacketRefund(ctx, req)
}

View File

@ -26,10 +26,16 @@ func New(wallet walletclient.Client, audit shared.OperationLogger) *Handler {
type configRequest struct { type configRequest struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
CountTiers []int32 `json:"count_tiers"` CountTiers []int32 `json:"count_tiers"`
CountOptions []int32 `json:"countOptions"`
AmountTiers []int64 `json:"amount_tiers"` AmountTiers []int64 `json:"amount_tiers"`
AmountOptions []int64 `json:"amountOptions"`
DelayedOpenSeconds int32 `json:"delayed_open_seconds"` DelayedOpenSeconds int32 `json:"delayed_open_seconds"`
DelaySeconds int32 `json:"delaySeconds"`
ExpireSeconds int32 `json:"expire_seconds"` ExpireSeconds int32 `json:"expire_seconds"`
DailySendLimit int32 `json:"daily_send_limit"` DailySendLimit int32 `json:"daily_send_limit"`
DailySendLimitV2 int32 `json:"dailySendLimit"`
RuleURL string `json:"rule_url"`
RuleURLV2 string `json:"ruleUrl"`
} }
type configDTO struct { type configDTO struct {
@ -40,6 +46,7 @@ type configDTO struct {
DelayedOpenSeconds int32 `json:"delayed_open_seconds"` DelayedOpenSeconds int32 `json:"delayed_open_seconds"`
ExpireSeconds int32 `json:"expire_seconds"` ExpireSeconds int32 `json:"expire_seconds"`
DailySendLimit int32 `json:"daily_send_limit"` DailySendLimit int32 `json:"daily_send_limit"`
RuleURL string `json:"rule_url"`
UpdatedByAdminID int64 `json:"updated_by_admin_id"` UpdatedByAdminID int64 `json:"updated_by_admin_id"`
CreatedAtMS int64 `json:"created_at_ms"` CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"`
@ -62,9 +69,17 @@ type packetDTO struct {
ExpiresAtMS int64 `json:"expires_at_ms"` ExpiresAtMS int64 `json:"expires_at_ms"`
CreatedAtMS int64 `json:"created_at_ms"` CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"`
RefundAmount int64 `json:"refund_amount"`
RefundStatus string `json:"refund_status"`
RefundedAtMS int64 `json:"refunded_at_ms"`
Claims []claimDTO `json:"claims,omitempty"` Claims []claimDTO `json:"claims,omitempty"`
} }
type refundRetryRequest struct {
PacketNo string `json:"packetNo"`
PacketID string `json:"packet_id"`
}
type claimDTO struct { type claimDTO struct {
AppCode string `json:"app_code"` AppCode string `json:"app_code"`
ClaimID string `json:"claim_id"` ClaimID string `json:"claim_id"`
@ -104,15 +119,36 @@ func (h *Handler) UpdateConfig(c *gin.Context) {
response.BadRequest(c, "红包配置参数不正确") response.BadRequest(c, "红包配置参数不正确")
return return
} }
countTiers := req.CountTiers
if len(countTiers) == 0 {
countTiers = req.CountOptions
}
amountTiers := req.AmountTiers
if len(amountTiers) == 0 {
amountTiers = req.AmountOptions
}
delaySeconds := req.DelayedOpenSeconds
if delaySeconds == 0 {
delaySeconds = req.DelaySeconds
}
dailyLimit := req.DailySendLimit
if dailyLimit == 0 {
dailyLimit = req.DailySendLimitV2
}
ruleURL := strings.TrimSpace(req.RuleURL)
if ruleURL == "" {
ruleURL = strings.TrimSpace(req.RuleURLV2)
}
resp, err := h.wallet.UpdateRedPacketConfig(c.Request.Context(), &walletv1.UpdateRedPacketConfigRequest{ resp, err := h.wallet.UpdateRedPacketConfig(c.Request.Context(), &walletv1.UpdateRedPacketConfigRequest{
AppCode: appctx.FromContext(c.Request.Context()), AppCode: appctx.FromContext(c.Request.Context()),
Enabled: req.Enabled, Enabled: req.Enabled,
CountTiers: req.CountTiers, CountTiers: countTiers,
AmountTiers: req.AmountTiers, AmountTiers: amountTiers,
DelayedOpenSeconds: req.DelayedOpenSeconds, DelayedOpenSeconds: delaySeconds,
ExpireSeconds: req.ExpireSeconds, ExpireSeconds: 86400,
DailySendLimit: req.DailySendLimit, DailySendLimit: dailyLimit,
OperatorUserId: int64(middleware.CurrentUserID(c)), OperatorUserId: int64(middleware.CurrentUserID(c)),
RuleUrl: ruleURL,
}) })
if err != nil { if err != nil {
response.BadRequest(c, err.Error()) response.BadRequest(c, err.Error())
@ -136,10 +172,16 @@ func (h *Handler) ListPackets(c *gin.Context) {
Status: strings.TrimSpace(options.Status), Status: strings.TrimSpace(options.Status),
Page: int32(options.Page), Page: int32(options.Page),
PageSize: int32(options.PageSize), PageSize: int32(options.PageSize),
SenderUserId: parseInt64Query(c, "sender_user_id"), SenderUserId: firstInt64Query(c, "senderUserId", "sender_user_id"),
RegionId: parseInt64Query(c, "region_id"), RegionId: parseInt64Query(c, "region_id"),
StartAtMs: parseInt64Query(c, "start_at_ms"), StartAtMs: firstInt64Query(c, "startTime", "start_at_ms"),
EndAtMs: parseInt64Query(c, "end_at_ms"), EndAtMs: firstInt64Query(c, "endTime", "end_at_ms"),
}
if roomID := strings.TrimSpace(c.Query("roomId")); roomID != "" {
req.RoomId = roomID
}
if packetMode := strings.TrimSpace(c.Query("packetMode")); packetMode != "" {
req.PacketType = packetMode
} }
resp, err := h.wallet.ListRedPackets(c.Request.Context(), req) resp, err := h.wallet.ListRedPackets(c.Request.Context(), req)
if err != nil { if err != nil {
@ -175,6 +217,41 @@ func (h *Handler) GetPacket(c *gin.Context) {
response.OK(c, packetFromProto(resp.GetPacket())) response.OK(c, packetFromProto(resp.GetPacket()))
} }
func (h *Handler) RetryRefund(c *gin.Context) {
if h.wallet == nil {
response.ServerError(c, "钱包服务未配置")
return
}
var req refundRetryRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "红包退款参数不正确")
return
}
packetID := strings.TrimSpace(req.PacketNo)
if packetID == "" {
packetID = strings.TrimSpace(req.PacketID)
}
if packetID == "" {
response.BadRequest(c, "红包 ID 不正确")
return
}
resp, err := h.wallet.RetryRedPacketRefund(c.Request.Context(), &walletv1.RetryRedPacketRefundRequest{
AppCode: appctx.FromContext(c.Request.Context()),
PacketId: packetID,
OperatorUserId: int64(middleware.CurrentUserID(c)),
})
if err != nil {
response.BadRequest(c, err.Error())
return
}
shared.OperationLogWithResourceID(c, h.audit, "retry-red-packet-refund", "red_packets", packetID, "success", "")
response.OK(c, gin.H{
"packet": packetFromProto(resp.GetPacket()),
"refundedAmount": resp.GetRefundedAmount(),
"serverTime": resp.GetServerTimeMs(),
})
}
func configFromProto(config *walletv1.RedPacketConfig) configDTO { func configFromProto(config *walletv1.RedPacketConfig) configDTO {
if config == nil { if config == nil {
return configDTO{} return configDTO{}
@ -187,6 +264,7 @@ func configFromProto(config *walletv1.RedPacketConfig) configDTO {
DelayedOpenSeconds: config.GetDelayedOpenSeconds(), DelayedOpenSeconds: config.GetDelayedOpenSeconds(),
ExpireSeconds: config.GetExpireSeconds(), ExpireSeconds: config.GetExpireSeconds(),
DailySendLimit: config.GetDailySendLimit(), DailySendLimit: config.GetDailySendLimit(),
RuleURL: config.GetRuleUrl(),
UpdatedByAdminID: config.GetUpdatedByAdminId(), UpdatedByAdminID: config.GetUpdatedByAdminId(),
CreatedAtMS: config.GetCreatedAtMs(), CreatedAtMS: config.GetCreatedAtMs(),
UpdatedAtMS: config.GetUpdatedAtMs(), UpdatedAtMS: config.GetUpdatedAtMs(),
@ -218,6 +296,9 @@ func packetFromProto(packet *walletv1.RedPacket) packetDTO {
ExpiresAtMS: packet.GetExpiresAtMs(), ExpiresAtMS: packet.GetExpiresAtMs(),
CreatedAtMS: packet.GetCreatedAtMs(), CreatedAtMS: packet.GetCreatedAtMs(),
UpdatedAtMS: packet.GetUpdatedAtMs(), UpdatedAtMS: packet.GetUpdatedAtMs(),
RefundAmount: packet.GetRefundAmount(),
RefundStatus: packet.GetRefundStatus(),
RefundedAtMS: packet.GetRefundedAtMs(),
Claims: claims, Claims: claims,
} }
} }
@ -241,6 +322,15 @@ func claimFromProto(claim *walletv1.RedPacketClaim) claimDTO {
} }
} }
func firstInt64Query(c *gin.Context, keys ...string) int64 {
for _, key := range keys {
if value := parseInt64Query(c, key); value > 0 {
return value
}
}
return 0
}
func parseInt64Query(c *gin.Context, key string) int64 { func parseInt64Query(c *gin.Context, key string) int64 {
value := strings.TrimSpace(c.Query(key)) value := strings.TrimSpace(c.Query(key))
if value == "" { if value == "" {

View File

@ -10,8 +10,9 @@ func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
if h == nil { if h == nil {
return return
} }
protected.GET("/admin/activity/red-packets/config", middleware.RequirePermission("red-packet:view"), h.GetConfig) protected.GET("/resident-activity/voice-room-red-packet/config", middleware.RequirePermission("red-packet:view"), h.GetConfig)
protected.PUT("/admin/activity/red-packets/config", middleware.RequirePermission("red-packet:update"), h.UpdateConfig) protected.POST("/resident-activity/voice-room-red-packet/config", middleware.RequirePermission("red-packet:update"), h.UpdateConfig)
protected.GET("/admin/activity/red-packets", middleware.RequirePermission("red-packet:view"), h.ListPackets) protected.GET("/resident-activity/voice-room-red-packet/records", middleware.RequirePermission("red-packet:view"), h.ListPackets)
protected.GET("/admin/activity/red-packets/:packet_id", middleware.RequirePermission("red-packet:view"), h.GetPacket) protected.GET("/resident-activity/voice-room-red-packet/records/:packet_id", middleware.RequirePermission("red-packet:view"), h.GetPacket)
protected.POST("/resident-activity/voice-room-red-packet/refund/retry", middleware.RequirePermission("red-packet:update"), h.RetryRefund)
} }

View File

@ -235,7 +235,7 @@ func New(cfg config.Config) (*App, error) {
if err != nil || !ok { if err != nil || !ok {
return err return err
} }
return broadcastSvc.ConsumeRedPacketCreatedEvent(appcode.WithContext(ctx, event.AppCode), event) return broadcastSvc.ConsumeRedPacketWalletEvent(appcode.WithContext(ctx, event.AppCode), event)
}); err != nil { }); err != nil {
_ = consumer.Shutdown() _ = consumer.Shutdown()
shutdownConsumers(mqConsumers) shutdownConsumers(mqConsumers)
@ -436,25 +436,32 @@ func firstRechargeEventFromWalletMessage(body []byte) (firstrechargedomain.Recha
}, true, nil }, true, nil
} }
func redPacketEventFromWalletMessage(body []byte) (broadcastdomain.RedPacketCreatedEvent, bool, error) { func redPacketEventFromWalletMessage(body []byte) (broadcastdomain.RedPacketWalletEvent, bool, error) {
message, err := walletmq.DecodeWalletOutboxMessage(body) message, err := walletmq.DecodeWalletOutboxMessage(body)
if err != nil { if err != nil {
return broadcastdomain.RedPacketCreatedEvent{}, false, err return broadcastdomain.RedPacketWalletEvent{}, false, err
} }
if message.EventType != "WalletRedPacketCreated" { if message.EventType != "WalletRedPacketCreated" && message.EventType != "WalletRedPacketClaimed" && message.EventType != "WalletRedPacketRefunded" {
return broadcastdomain.RedPacketCreatedEvent{}, false, nil return broadcastdomain.RedPacketWalletEvent{}, false, nil
} }
regionID := redPacketRegionIDFromWalletPayload(message.PayloadJSON) packetID, packetType, roomID, regionID, status, openAtMS, expiresAtMS := redPacketFieldsFromWalletPayload(message.PayloadJSON, message.EventType)
if regionID <= 0 { if packetID == "" {
return broadcastdomain.RedPacketCreatedEvent{}, false, nil return broadcastdomain.RedPacketWalletEvent{}, false, nil
} }
return broadcastdomain.RedPacketCreatedEvent{ return broadcastdomain.RedPacketWalletEvent{
AppCode: appcode.Normalize(message.AppCode), AppCode: appcode.Normalize(message.AppCode),
EventID: message.EventID, EventID: message.EventID,
EventType: message.EventType,
TransactionID: message.TransactionID, TransactionID: message.TransactionID,
CommandID: message.CommandID, CommandID: message.CommandID,
PayloadJSON: message.PayloadJSON, PayloadJSON: message.PayloadJSON,
PacketID: packetID,
PacketType: packetType,
RoomID: roomID,
RegionID: regionID, RegionID: regionID,
Status: status,
OpenAtMS: openAtMS,
ExpiresAtMS: expiresAtMS,
CreatedAtMS: message.OccurredAtMS, CreatedAtMS: message.OccurredAtMS,
}, true, nil }, true, nil
} }
@ -480,19 +487,41 @@ func rechargeFactFromWalletPayload(payload string) (int64, string) {
return sequence, rechargeType return sequence, rechargeType
} }
func redPacketRegionIDFromWalletPayload(payload string) int64 { func redPacketFieldsFromWalletPayload(payload string, eventType string) (string, string, string, int64, string, int64, int64) {
var decoded map[string]any var decoded map[string]any
if err := json.Unmarshal([]byte(payload), &decoded); err != nil { if err := json.Unmarshal([]byte(payload), &decoded); err != nil {
return 0 return "", "", "", 0, "", 0, 0
} }
switch value := decoded["region_id"].(type) { packetID := stringFromDecoded(decoded, "packet_id")
if packetID == "" {
packetID = stringFromDecoded(decoded, "packetId")
}
packetType := stringFromDecoded(decoded, "packet_type")
roomID := stringFromDecoded(decoded, "room_id")
regionID := int64FromDecoded(decoded, "region_id")
status := stringFromDecoded(decoded, "status")
openAtMS := int64FromDecoded(decoded, "open_at_ms")
expiresAtMS := int64FromDecoded(decoded, "expires_at_ms")
if eventType == "WalletRedPacketClaimed" || eventType == "WalletRedPacketRefunded" {
status = stringFromDecoded(decoded, "packet_status")
}
return packetID, packetType, roomID, regionID, status, openAtMS, expiresAtMS
}
func stringFromDecoded(decoded map[string]any, key string) string {
value, _ := decoded[key].(string)
return value
}
func int64FromDecoded(decoded map[string]any, key string) int64 {
switch value := decoded[key].(type) {
case float64: case float64:
return int64(value) return int64(value)
case int64: case int64:
return value return value
case json.Number: case json.Number:
regionID, _ := value.Int64() parsed, _ := value.Int64()
return regionID return parsed
default: default:
return 0 return 0
} }

View File

@ -7,6 +7,8 @@ const (
ScopeGlobal = "global" ScopeGlobal = "global"
// ScopeRegion 表示用户所在区域群;贵重礼物、区域红包、区域运营通知默认走这个范围。 // ScopeRegion 表示用户所在区域群;贵重礼物、区域红包、区域运营通知默认走这个范围。
ScopeRegion = "region" ScopeRegion = "region"
// ScopeRoom 表示当前语音房 IM 群。
ScopeRoom = "room"
// TypeSuperGift 是贵重礼物播报,来源于 room-service 已提交的 RoomGiftSent 事实。 // TypeSuperGift 是贵重礼物播报,来源于 room-service 已提交的 RoomGiftSent 事实。
TypeSuperGift = "super_gift" TypeSuperGift = "super_gift"
@ -74,13 +76,23 @@ type ConsumeRoomEventResult struct {
BroadcastCreated bool BroadcastCreated bool
} }
// RedPacketCreatedEvent 是 wallet-service 已提交的红包创建事实。 // RedPacketWalletEvent 是 wallet-service 已提交的红包资金事实。
type RedPacketCreatedEvent struct { type RedPacketWalletEvent struct {
AppCode string AppCode string
EventID string EventID string
EventType string
TransactionID string TransactionID string
CommandID string CommandID string
PayloadJSON string PayloadJSON string
PacketID string
PacketType string
RoomID string
RegionID int64 RegionID int64
Status string
OpenAtMS int64
ExpiresAtMS int64
CreatedAtMS int64 CreatedAtMS int64
} }
// RedPacketCreatedEvent keeps older tests and callers compiling while wallet red-packet facts are generalized.
type RedPacketCreatedEvent = RedPacketWalletEvent

View File

@ -2,24 +2,110 @@ package broadcast
import ( import (
"context" "context"
"encoding/json"
"strings"
"hyapp/pkg/appcode" "hyapp/pkg/appcode"
broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast" broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast"
) )
// ConsumeRedPacketCreatedEvent converts one MQ-delivered wallet red-packet fact into a broadcast outbox. // ConsumeRedPacketWalletEvent converts one MQ-delivered wallet red-packet fact into IM outbox records.
func (s *Service) ConsumeRedPacketCreatedEvent(ctx context.Context, event broadcastdomain.RedPacketCreatedEvent) error { func (s *Service) ConsumeRedPacketWalletEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error {
if s == nil { if s == nil {
return nil return nil
} }
if event.RegionID <= 0 { ctx = appcode.WithContext(ctx, event.AppCode)
switch event.EventType {
case "WalletRedPacketCreated":
return s.consumeRedPacketCreated(ctx, event)
case "WalletRedPacketClaimed":
if strings.EqualFold(event.Status, "finished") {
return s.publishRedPacketRoomEvent(ctx, event, "STATUS_CHANGED", event.CreatedAtMS)
}
case "WalletRedPacketRefunded":
return s.publishRedPacketRoomEvent(ctx, event, "STATUS_CHANGED", event.CreatedAtMS)
}
return nil
}
func (s *Service) ConsumeRedPacketCreatedEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error {
if event.EventType == "" {
event.EventType = "WalletRedPacketCreated"
}
return s.ConsumeRedPacketWalletEvent(ctx, event)
}
func (s *Service) consumeRedPacketCreated(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error {
if event.RoomID == "" {
return nil return nil
} }
_, err := s.PublishRegionBroadcast(appcode.WithContext(ctx, event.AppCode), PublishInput{ if err := s.publishRedPacketRoomEvent(ctx, event, "CREATED", event.CreatedAtMS); err != nil {
return err
}
if strings.EqualFold(event.PacketType, "delayed") && event.RegionID > 0 {
if _, err := s.PublishRegionBroadcast(ctx, PublishInput{
EventID: event.EventID + ":region_created",
BroadcastType: broadcastdomain.TypeRedPacket,
RegionID: event.RegionID,
PayloadJSON: redPacketIMPayload(event, "CREATED", "REGION"),
}); err != nil {
return err
}
}
if strings.EqualFold(event.PacketType, "delayed") && event.OpenAtMS > 0 {
_, err := s.PublishRoomBroadcast(ctx, event.RoomID, PublishInput{
EventID: event.EventID + ":open_due",
BroadcastType: broadcastdomain.TypeRedPacket,
RegionID: event.RegionID,
PayloadJSON: redPacketIMPayload(event, "OPEN_DUE", "ROOM"),
DeliverAtMS: event.OpenAtMS,
})
return err
}
return nil
}
func (s *Service) publishRedPacketRoomEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent, eventName string, fallbackDeliverAtMS int64) error {
if event.RoomID == "" {
return nil
}
_, err := s.PublishRoomBroadcast(ctx, event.RoomID, PublishInput{
EventID: event.EventID, EventID: event.EventID,
BroadcastType: broadcastdomain.TypeRedPacket, BroadcastType: broadcastdomain.TypeRedPacket,
RegionID: event.RegionID, RegionID: event.RegionID,
PayloadJSON: event.PayloadJSON, PayloadJSON: redPacketIMPayload(event, eventName, "ROOM"),
DeliverAtMS: fallbackDeliverAtMS,
}) })
return err return err
} }
func redPacketIMPayload(event broadcastdomain.RedPacketWalletEvent, eventName string, scope string) string {
packetMode := "IMMEDIATE"
if strings.EqualFold(event.PacketType, "delayed") {
packetMode = "DELAYED"
}
status := strings.ToUpper(strings.TrimSpace(event.Status))
if status == "" {
status = "ACTIVE"
}
payload := map[string]any{
"type": "ROOM_RED_PACKET",
"data": map[string]any{
"event": eventName,
"scope": scope,
"packetNo": event.PacketID,
"roomId": event.RoomID,
"regionId": event.RegionID,
"packetMode": packetMode,
"status": status,
"claimStartTime": event.OpenAtMS,
"expireTime": event.ExpiresAtMS,
"serverTime": event.CreatedAtMS,
},
}
encoded, err := json.Marshal(payload)
if err != nil {
return `{"type":"ROOM_RED_PACKET","data":{}}`
}
return string(encoded)
}

View File

@ -65,6 +65,7 @@ type PublishInput struct {
BroadcastType string BroadcastType string
RegionID int64 RegionID int64
PayloadJSON string PayloadJSON string
DeliverAtMS int64
} }
// Service 拥有播报群建群补偿、播报入队和 outbox 投递决策。 // Service 拥有播报群建群补偿、播报入队和 outbox 投递决策。
@ -146,6 +147,14 @@ func (s *Service) PublishRegionBroadcast(ctx context.Context, input PublishInput
return s.enqueue(ctx, broadcastdomain.ScopeRegion, groupID, input) return s.enqueue(ctx, broadcastdomain.ScopeRegion, groupID, input)
} }
func (s *Service) PublishRoomBroadcast(ctx context.Context, roomID string, input PublishInput) (broadcastdomain.PublishResult, error) {
roomID = strings.TrimSpace(roomID)
if roomID == "" {
return broadcastdomain.PublishResult{}, xerr.New(xerr.InvalidArgument, "room_id is required")
}
return s.enqueue(ctx, broadcastdomain.ScopeRoom, roomID, input)
}
// PublishGlobalBroadcast 把全局播报写入持久化 outbox。 // PublishGlobalBroadcast 把全局播报写入持久化 outbox。
// 全局播报和区域播报共用同一张表scope/group_id 决定最终投递目标。 // 全局播报和区域播报共用同一张表scope/group_id 决定最终投递目标。
func (s *Service) PublishGlobalBroadcast(ctx context.Context, input PublishInput) (broadcastdomain.PublishResult, error) { func (s *Service) PublishGlobalBroadcast(ctx context.Context, input PublishInput) (broadcastdomain.PublishResult, error) {
@ -209,9 +218,13 @@ func (s *Service) enqueue(ctx context.Context, scope string, groupID string, inp
BroadcastType: input.BroadcastType, BroadcastType: input.BroadcastType,
PayloadJSON: payloadJSON, PayloadJSON: payloadJSON,
Status: broadcastdomain.StatusPending, Status: broadcastdomain.StatusPending,
NextRetryAtMS: nowMS,
CreatedAtMS: nowMS, CreatedAtMS: nowMS,
UpdatedAtMS: nowMS, UpdatedAtMS: nowMS,
} }
if input.DeliverAtMS > nowMS {
record.NextRetryAtMS = input.DeliverAtMS
}
created, saved, err := s.repository.SaveBroadcastOutbox(ctx, record) created, saved, err := s.repository.SaveBroadcastOutbox(ctx, record)
if err != nil { if err != nil {
return broadcastdomain.PublishResult{}, err return broadcastdomain.PublishResult{}, err

View File

@ -176,6 +176,14 @@ type WalletHandlers struct {
CreateRoomRedPacket http.HandlerFunc CreateRoomRedPacket http.HandlerFunc
GetRedPacket http.HandlerFunc GetRedPacket http.HandlerFunc
ClaimRedPacket http.HandlerFunc ClaimRedPacket http.HandlerFunc
GetCurrentRegionIMGroup http.HandlerFunc
GetRedPacketIMGroup http.HandlerFunc
SendVoiceRoomRedPacket http.HandlerFunc
ListActiveRedPackets http.HandlerFunc
ClaimVoiceRoomRedPacket http.HandlerFunc
GetVoiceRoomRedPacket http.HandlerFunc
ListRedPacketClaims http.HandlerFunc
ListSentRedPackets http.HandlerFunc
} }
type VIPHandlers struct { type VIPHandlers struct {
@ -396,6 +404,15 @@ func (r routes) registerWalletRoutes() {
r.profile("/rooms/{room_id}/red-packets/create", http.MethodPost, h.CreateRoomRedPacket) r.profile("/rooms/{room_id}/red-packets/create", http.MethodPost, h.CreateRoomRedPacket)
r.profile("/red-packets/{packet_id}", http.MethodGet, h.GetRedPacket) r.profile("/red-packets/{packet_id}", http.MethodGet, h.GetRedPacket)
r.profile("/red-packets/{packet_id}/claim", http.MethodPost, h.ClaimRedPacket) r.profile("/red-packets/{packet_id}/claim", http.MethodPost, h.ClaimRedPacket)
r.profile("/app/region-im-group/current", http.MethodGet, h.GetCurrentRegionIMGroup)
r.profile("/app/voice-room/red-packet/im-group", http.MethodGet, h.GetRedPacketIMGroup)
r.profile("/app/voice-room/red-packet/config", http.MethodGet, h.GetRedPacketConfig)
r.profile("/app/voice-room/red-packet/send", http.MethodPost, h.SendVoiceRoomRedPacket)
r.profile("/app/voice-room/red-packet/active", http.MethodGet, h.ListActiveRedPackets)
r.profile("/app/voice-room/red-packet/claim", http.MethodPost, h.ClaimVoiceRoomRedPacket)
r.profile("/app/voice-room/red-packet/detail", http.MethodGet, h.GetVoiceRoomRedPacket)
r.profile("/app/voice-room/red-packet/claim-records", http.MethodGet, h.ListRedPacketClaims)
r.profile("/app/voice-room/red-packet/sent-records", http.MethodGet, h.ListSentRedPackets)
} }
func (r routes) registerVIPRoutes() { func (r routes) registerVIPRoutes() {

View File

@ -39,6 +39,7 @@ func (h *Handler) Routes(jwtVerifier *auth.Verifier) http.Handler {
}) })
walletAPI := walletapi.New(walletapi.Config{ walletAPI := walletapi.New(walletapi.Config{
WalletClient: h.walletClient, WalletClient: h.walletClient,
RoomGuardClient: h.roomGuardClient,
RoomQueryClient: h.roomQueryClient, RoomQueryClient: h.roomQueryClient,
UserIdentityClient: h.userIdentityClient, UserIdentityClient: h.userIdentityClient,
UserProfileClient: h.userProfileClient, UserProfileClient: h.userProfileClient,

View File

@ -11,6 +11,7 @@ import (
// It keeps wallet semantics behind wallet-service and only handles HTTP protocol conversion. // It keeps wallet semantics behind wallet-service and only handles HTTP protocol conversion.
type Handler struct { type Handler struct {
walletClient client.WalletClient walletClient client.WalletClient
roomGuardClient client.RoomGuardClient
roomQueryClient client.RoomQueryClient roomQueryClient client.RoomQueryClient
userIdentityClient client.UserIdentityClient userIdentityClient client.UserIdentityClient
userProfileClient client.UserProfileClient userProfileClient client.UserProfileClient
@ -19,6 +20,7 @@ type Handler struct {
type Config struct { type Config struct {
WalletClient client.WalletClient WalletClient client.WalletClient
RoomGuardClient client.RoomGuardClient
RoomQueryClient client.RoomQueryClient RoomQueryClient client.RoomQueryClient
UserIdentityClient client.UserIdentityClient UserIdentityClient client.UserIdentityClient
UserProfileClient client.UserProfileClient UserProfileClient client.UserProfileClient
@ -28,6 +30,7 @@ type Config struct {
func New(config Config) *Handler { func New(config Config) *Handler {
return &Handler{ return &Handler{
walletClient: config.WalletClient, walletClient: config.WalletClient,
roomGuardClient: config.RoomGuardClient,
roomQueryClient: config.RoomQueryClient, roomQueryClient: config.RoomQueryClient,
userIdentityClient: config.UserIdentityClient, userIdentityClient: config.UserIdentityClient,
userProfileClient: config.UserProfileClient, userProfileClient: config.UserProfileClient,
@ -52,6 +55,14 @@ func (h *Handler) WalletHandlers() httproutes.WalletHandlers {
CreateRoomRedPacket: h.createRoomRedPacket, CreateRoomRedPacket: h.createRoomRedPacket,
GetRedPacket: h.getRedPacket, GetRedPacket: h.getRedPacket,
ClaimRedPacket: h.claimRedPacket, ClaimRedPacket: h.claimRedPacket,
GetCurrentRegionIMGroup: h.getCurrentRegionIMGroup,
GetRedPacketIMGroup: h.getCurrentRegionIMGroup,
SendVoiceRoomRedPacket: h.sendVoiceRoomRedPacket,
ListActiveRedPackets: h.listActiveRedPackets,
ClaimVoiceRoomRedPacket: h.claimVoiceRoomRedPacket,
GetVoiceRoomRedPacket: h.getVoiceRoomRedPacket,
ListRedPacketClaims: h.listRedPacketClaims,
ListSentRedPackets: h.listSentRedPackets,
} }
} }

View File

@ -1,14 +1,18 @@
package walletapi package walletapi
import ( import (
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
roomv1 "hyapp.local/api/proto/room/v1" roomv1 "hyapp.local/api/proto/room/v1"
userv1 "hyapp.local/api/proto/user/v1"
walletv1 "hyapp.local/api/proto/wallet/v1" walletv1 "hyapp.local/api/proto/wallet/v1"
"hyapp/pkg/appcode" "hyapp/pkg/appcode"
"hyapp/pkg/imgroup"
"hyapp/pkg/roomid" "hyapp/pkg/roomid"
"hyapp/pkg/xerr"
"hyapp/services/gateway-service/internal/auth" "hyapp/services/gateway-service/internal/auth"
"hyapp/services/gateway-service/internal/transport/http/httpkit" "hyapp/services/gateway-service/internal/transport/http/httpkit"
) )
@ -22,6 +26,7 @@ type redPacketConfigData struct {
ExpireSeconds int32 `json:"expire_seconds"` ExpireSeconds int32 `json:"expire_seconds"`
DailySendLimit int32 `json:"daily_send_limit"` DailySendLimit int32 `json:"daily_send_limit"`
UpdatedByAdminID int64 `json:"updated_by_admin_id"` UpdatedByAdminID int64 `json:"updated_by_admin_id"`
RuleURL string `json:"rule_url"`
CreatedAtMS int64 `json:"created_at_ms"` CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"`
} }
@ -113,6 +118,377 @@ func (b claimRedPacketRequestBody) normalizedCommandID() string {
return strings.TrimSpace(b.CommandIDCamel) return strings.TrimSpace(b.CommandIDCamel)
} }
type voiceRoomRedPacketSendBody struct {
RoomID string `json:"roomId"`
RoomIDSnake string `json:"room_id"`
PacketMode string `json:"packetMode"`
PacketType string `json:"packet_type"`
TotalAmount int64 `json:"totalAmount"`
TotalAmountV2 int64 `json:"total_amount"`
TotalCount int32 `json:"totalCount"`
PacketCount int32 `json:"packet_count"`
RequestID string `json:"requestId"`
RequestIDV2 string `json:"request_id"`
}
func (b voiceRoomRedPacketSendBody) roomID() string {
if value := strings.TrimSpace(b.RoomID); value != "" {
return value
}
return strings.TrimSpace(b.RoomIDSnake)
}
func (b voiceRoomRedPacketSendBody) mode() string {
if value := strings.TrimSpace(b.PacketMode); value != "" {
return value
}
return strings.TrimSpace(b.PacketType)
}
func (b voiceRoomRedPacketSendBody) amount() int64 {
if b.TotalAmount > 0 {
return b.TotalAmount
}
return b.TotalAmountV2
}
func (b voiceRoomRedPacketSendBody) count() int32 {
if b.TotalCount > 0 {
return b.TotalCount
}
return b.PacketCount
}
func (b voiceRoomRedPacketSendBody) requestID() string {
if value := strings.TrimSpace(b.RequestID); value != "" {
return value
}
return strings.TrimSpace(b.RequestIDV2)
}
type voiceRoomRedPacketClaimBody struct {
PacketNo string `json:"packetNo"`
PacketID string `json:"packet_id"`
RequestID string `json:"requestId"`
}
func (b voiceRoomRedPacketClaimBody) packetNo() string {
if value := strings.TrimSpace(b.PacketNo); value != "" {
return value
}
return strings.TrimSpace(b.PacketID)
}
func (h *Handler) getCurrentRegionIMGroup(writer http.ResponseWriter, request *http.Request) {
if h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
user, ok := h.currentUser(writer, request)
if !ok {
return
}
if user.GetRegionId() <= 0 {
httpkit.WriteError(writer, request, http.StatusConflict, "voice_room_region_missing", "conflict")
return
}
groupID, err := imgroup.RegionBroadcastGroupID(appcode.FromContext(request.Context()), user.GetRegionId())
if err != nil {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
httpkit.WriteOK(writer, request, map[string]any{
"regionId": user.GetRegionId(),
"regionCode": user.GetRegionCode(),
"regionName": user.GetRegionName(),
"groupId": groupID,
"groupName": groupID,
})
}
func (h *Handler) sendVoiceRoomRedPacket(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil || h.roomQueryClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
var body voiceRoomRedPacketSendBody
if !httpkit.Decode(writer, request, &body) {
return
}
roomID := body.roomID()
packetType := internalRedPacketType(body.mode())
requestID := body.requestID()
if !roomid.ValidStringID(roomID) || packetType == "" || body.amount() <= 0 || body.count() <= 0 || requestID == "" {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
user, ok := h.currentUser(writer, request)
if !ok {
return
}
if user.GetRegionId() <= 0 {
httpkit.WriteError(writer, request, http.StatusConflict, "voice_room_region_missing", "conflict")
return
}
snapshotResp, err := h.roomQueryClient.GetRoomSnapshot(request.Context(), &roomv1.GetRoomSnapshotRequest{
Meta: httpkit.RoomMeta(request, roomID, ""),
RoomId: roomID,
ViewerUserId: auth.UserIDFromContext(request.Context()),
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
regionID := snapshotResp.GetRoom().GetVisibleRegionId()
if regionID <= 0 {
httpkit.WriteError(writer, request, http.StatusConflict, "voice_room_region_missing", "conflict")
return
}
if regionID != user.GetRegionId() {
httpkit.WriteError(writer, request, http.StatusForbidden, "voice_room_region_not_match", "permission denied")
return
}
commandID := fmt.Sprintf("red_packet_send:%d:%s", auth.UserIDFromContext(request.Context()), requestID)
resp, err := h.walletClient.CreateRedPacket(request.Context(), &walletv1.CreateRedPacketRequest{
RequestId: requestID,
AppCode: appcode.FromContext(request.Context()),
CommandId: commandID,
SenderUserId: auth.UserIDFromContext(request.Context()),
RoomId: roomID,
RegionId: regionID,
PacketType: packetType,
TotalAmount: body.amount(),
PacketCount: body.count(),
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, map[string]any{
"packet": voiceRoomPacketFromProto(resp.GetPacket()),
"balanceAfter": resp.GetBalanceAfter(),
"serverTime": resp.GetServerTimeMs(),
})
}
func (h *Handler) listActiveRedPackets(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
roomID := strings.TrimSpace(request.URL.Query().Get("roomId"))
if roomID == "" {
roomID = strings.TrimSpace(request.URL.Query().Get("room_id"))
}
if !roomid.ValidStringID(roomID) {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
resp, err := h.walletClient.ListRedPackets(request.Context(), &walletv1.ListRedPacketsRequest{
AppCode: appcode.FromContext(request.Context()),
RoomId: roomID,
ViewerUserId: auth.UserIDFromContext(request.Context()),
Page: 1,
PageSize: 100,
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
packets := make([]map[string]any, 0, len(resp.GetPackets()))
for _, packet := range resp.GetPackets() {
if packet.GetStatus() == "active" || packet.GetStatus() == "waiting_open" {
packets = append(packets, voiceRoomPacketFromProto(packet))
}
}
httpkit.WriteOK(writer, request, map[string]any{
"packets": packets,
"serverTime": resp.GetServerTimeMs(),
})
}
func (h *Handler) claimVoiceRoomRedPacket(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil || h.roomGuardClient == nil || h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
var body voiceRoomRedPacketClaimBody
if !httpkit.Decode(writer, request, &body) {
return
}
packetNo := body.packetNo()
if packetNo == "" {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
packetResp, err := h.walletClient.GetRedPacket(request.Context(), &walletv1.GetRedPacketRequest{
AppCode: appcode.FromContext(request.Context()),
PacketId: packetNo,
ViewerUserId: auth.UserIDFromContext(request.Context()),
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
packet := packetResp.GetPacket()
if !h.verifyRedPacketRoomPresence(writer, request, packet.GetRoomId()) {
return
}
user, ok := h.currentUser(writer, request)
if !ok {
return
}
if user.GetRegionId() <= 0 {
httpkit.WriteError(writer, request, http.StatusConflict, "voice_room_region_missing", "conflict")
return
}
if packet.GetRegionId() > 0 && packet.GetRegionId() != user.GetRegionId() {
httpkit.WriteError(writer, request, http.StatusForbidden, "voice_room_region_not_match", "permission denied")
return
}
commandID := fmt.Sprintf("red_packet_claim:%s:%d", packetNo, auth.UserIDFromContext(request.Context()))
resp, err := h.walletClient.ClaimRedPacket(request.Context(), &walletv1.ClaimRedPacketRequest{
AppCode: appcode.FromContext(request.Context()),
CommandId: commandID,
PacketId: packetNo,
UserId: auth.UserIDFromContext(request.Context()),
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, map[string]any{
"claim": voiceRoomClaimFromProto(resp.GetClaim()),
"packet": voiceRoomPacketFromProto(resp.GetPacket()),
"balanceAfter": resp.GetBalanceAfter(),
"serverTime": resp.GetServerTimeMs(),
})
}
func (h *Handler) getVoiceRoomRedPacket(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
packetNo := strings.TrimSpace(request.URL.Query().Get("packetNo"))
if packetNo == "" {
packetNo = strings.TrimSpace(request.URL.Query().Get("packet_id"))
}
if packetNo == "" {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
resp, err := h.walletClient.GetRedPacket(request.Context(), &walletv1.GetRedPacketRequest{
AppCode: appcode.FromContext(request.Context()),
PacketId: packetNo,
ViewerUserId: auth.UserIDFromContext(request.Context()),
IncludeClaims: true,
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, map[string]any{
"packet": voiceRoomPacketFromProto(resp.GetPacket()),
"serverTime": resp.GetServerTimeMs(),
})
}
func (h *Handler) listRedPacketClaims(writer http.ResponseWriter, request *http.Request) {
packetNo := strings.TrimSpace(request.URL.Query().Get("packetNo"))
if packetNo == "" {
packetNo = strings.TrimSpace(request.URL.Query().Get("packet_id"))
}
if packetNo == "" {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
page, ok := httpkit.PositiveInt32Query(request, "page", 1)
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
pageSize, ok := httpkit.PositiveInt32Query(request, "pageSize", 20)
if !ok {
pageSize, ok = httpkit.PositiveInt32Query(request, "page_size", 20)
}
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
resp, err := h.walletClient.GetRedPacket(request.Context(), &walletv1.GetRedPacketRequest{
AppCode: appcode.FromContext(request.Context()),
PacketId: packetNo,
ViewerUserId: auth.UserIDFromContext(request.Context()),
IncludeClaims: true,
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
all := resp.GetPacket().GetClaims()
start := int((page - 1) * pageSize)
end := start + int(pageSize)
if start > len(all) {
start = len(all)
}
if end > len(all) {
end = len(all)
}
items := make([]map[string]any, 0, end-start)
for _, claim := range all[start:end] {
items = append(items, voiceRoomClaimFromProto(claim))
}
httpkit.WriteOK(writer, request, map[string]any{
"items": items,
"total": len(all),
"page": page,
"pageSize": pageSize,
"serverTime": resp.GetServerTimeMs(),
})
}
func (h *Handler) listSentRedPackets(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
page, ok := httpkit.PositiveInt32Query(request, "page", 1)
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
pageSize, ok := httpkit.PositiveInt32Query(request, "pageSize", 20)
if !ok {
pageSize, ok = httpkit.PositiveInt32Query(request, "page_size", 20)
}
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
resp, err := h.walletClient.ListRedPackets(request.Context(), &walletv1.ListRedPacketsRequest{
AppCode: appcode.FromContext(request.Context()),
SenderUserId: auth.UserIDFromContext(request.Context()),
Page: page,
PageSize: pageSize,
})
if err != nil {
writeRedPacketRPCError(writer, request, err)
return
}
items := make([]map[string]any, 0, len(resp.GetPackets()))
for _, packet := range resp.GetPackets() {
items = append(items, voiceRoomPacketFromProto(packet))
}
httpkit.WriteOK(writer, request, map[string]any{
"items": items,
"total": resp.GetTotal(),
"page": page,
"pageSize": pageSize,
"serverTime": resp.GetServerTimeMs(),
})
}
func (h *Handler) getRedPacketConfig(writer http.ResponseWriter, request *http.Request) { func (h *Handler) getRedPacketConfig(writer http.ResponseWriter, request *http.Request) {
if h.walletClient == nil { if h.walletClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
@ -305,6 +681,7 @@ func redPacketConfigFromProto(config *walletv1.RedPacketConfig) redPacketConfigD
ExpireSeconds: config.GetExpireSeconds(), ExpireSeconds: config.GetExpireSeconds(),
DailySendLimit: config.GetDailySendLimit(), DailySendLimit: config.GetDailySendLimit(),
UpdatedByAdminID: config.GetUpdatedByAdminId(), UpdatedByAdminID: config.GetUpdatedByAdminId(),
RuleURL: config.GetRuleUrl(),
CreatedAtMS: config.GetCreatedAtMs(), CreatedAtMS: config.GetCreatedAtMs(),
UpdatedAtMS: config.GetUpdatedAtMs(), UpdatedAtMS: config.GetUpdatedAtMs(),
} }
@ -376,3 +753,165 @@ func boolQuery(request *http.Request, name string) bool {
parsed, err := strconv.ParseBool(value) parsed, err := strconv.ParseBool(value)
return err == nil && parsed return err == nil && parsed
} }
func (h *Handler) currentUser(writer http.ResponseWriter, request *http.Request) (*userv1.User, bool) {
if h.userProfileClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return nil, false
}
resp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
Meta: httpkit.UserMeta(request, ""),
UserId: auth.UserIDFromContext(request.Context()),
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return nil, false
}
user := resp.GetUser()
if user == nil {
httpkit.WriteError(writer, request, http.StatusInternalServerError, httpkit.CodeInternalError, "internal error")
return nil, false
}
return user, true
}
func (h *Handler) verifyRedPacketRoomPresence(writer http.ResponseWriter, request *http.Request, roomID string) bool {
if !roomid.ValidStringID(roomID) {
httpkit.WriteError(writer, request, http.StatusConflict, "voice_room_red_packet_room_presence_required", "conflict")
return false
}
resp, err := h.roomGuardClient.VerifyRoomPresence(request.Context(), &roomv1.VerifyRoomPresenceRequest{
RoomId: roomID,
UserId: auth.UserIDFromContext(request.Context()),
RequestId: httpkit.RequestIDFromContext(request.Context()),
AppCode: appcode.FromContext(request.Context()),
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return false
}
if !resp.GetPresent() {
httpkit.WriteError(writer, request, http.StatusConflict, "voice_room_red_packet_room_presence_required", "conflict")
return false
}
return true
}
func writeRedPacketRPCError(writer http.ResponseWriter, request *http.Request, err error) {
if err == nil {
return
}
reason := xerr.ReasonFromGRPC(err)
statusCode, code, message := httpkit.MapReasonToHTTP(reason)
switch reason {
case xerr.RedPacketDisabled:
code = "voice_room_red_packet_disabled"
case xerr.RedPacketInvalidAmountTier, xerr.RedPacketAmountTooSmall:
code = "voice_room_red_packet_amount_invalid"
case xerr.RedPacketInvalidCountTier:
code = "voice_room_red_packet_count_invalid"
case xerr.RedPacketDailyLimitReached:
code = "voice_room_red_packet_daily_limit_exceeded"
case xerr.InsufficientBalance:
code = "voice_room_red_packet_insufficient_balance"
case xerr.NotFound:
code = "voice_room_red_packet_not_found"
case xerr.RedPacketNotOpen:
code = "voice_room_red_packet_not_started"
case xerr.RedPacketExpired:
code = "voice_room_red_packet_expired"
case xerr.RedPacketSoldOut:
code = "voice_room_red_packet_finished"
case xerr.LedgerConflict:
code = "claim_processing"
}
httpkit.WriteError(writer, request, statusCode, code, message)
}
func internalRedPacketType(mode string) string {
switch strings.ToUpper(strings.TrimSpace(mode)) {
case "IMMEDIATE":
return "normal"
case "DELAYED":
return "delayed"
}
switch strings.ToLower(strings.TrimSpace(mode)) {
case "normal":
return "normal"
case "delayed":
return "delayed"
default:
return ""
}
}
func publicRedPacketMode(packetType string) string {
switch strings.ToLower(strings.TrimSpace(packetType)) {
case "normal":
return "IMMEDIATE"
case "delayed":
return "DELAYED"
default:
return strings.ToUpper(strings.TrimSpace(packetType))
}
}
func publicRedPacketStatus(status string) string {
switch strings.ToLower(strings.TrimSpace(status)) {
case "waiting_open":
return "WAITING_OPEN"
case "active":
return "ACTIVE"
case "finished":
return "FINISHED"
case "refunded":
return "REFUNDED"
default:
return strings.ToUpper(strings.TrimSpace(status))
}
}
func voiceRoomPacketFromProto(packet *walletv1.RedPacket) map[string]any {
if packet == nil {
return map[string]any{}
}
claims := make([]map[string]any, 0, len(packet.GetClaims()))
for _, claim := range packet.GetClaims() {
claims = append(claims, voiceRoomClaimFromProto(claim))
}
return map[string]any{
"packetNo": packet.GetPacketId(),
"roomId": packet.GetRoomId(),
"regionId": packet.GetRegionId(),
"senderUserId": packet.GetSenderUserId(),
"packetMode": publicRedPacketMode(packet.GetPacketType()),
"totalAmount": packet.GetTotalAmount(),
"totalCount": packet.GetPacketCount(),
"remainingAmount": packet.GetRemainingAmount(),
"remainingCount": packet.GetRemainingCount(),
"status": publicRedPacketStatus(packet.GetStatus()),
"claimStartTime": packet.GetOpenAtMs(),
"expireTime": packet.GetExpiresAtMs(),
"createdAt": packet.GetCreatedAtMs(),
"claimedByMe": packet.GetClaimedByViewer(),
"myClaimAmount": packet.GetViewerClaimAmount(),
"refundAmount": packet.GetRefundAmount(),
"refundStatus": packet.GetRefundStatus(),
"refundedAt": packet.GetRefundedAtMs(),
"claims": claims,
}
}
func voiceRoomClaimFromProto(claim *walletv1.RedPacketClaim) map[string]any {
if claim == nil {
return map[string]any{}
}
return map[string]any{
"claimId": claim.GetClaimId(),
"packetNo": claim.GetPacketId(),
"userId": claim.GetUserId(),
"amount": claim.GetAmount(),
"status": claim.GetStatus(),
"createdAt": claim.GetCreatedAtMs(),
}
}

View File

@ -666,8 +666,9 @@ CREATE TABLE IF NOT EXISTS red_packet_configs (
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离', app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
enabled BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否启用红包', enabled BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否启用红包',
delayed_open_seconds INT NOT NULL DEFAULT 0 COMMENT '延迟红包打开秒数', delayed_open_seconds INT NOT NULL DEFAULT 0 COMMENT '延迟红包打开秒数',
expire_seconds INT NOT NULL DEFAULT 300 COMMENT '红包过期秒数', expire_seconds INT NOT NULL DEFAULT 86400 COMMENT '红包过期秒数,房内红包固定 24 小时',
daily_send_limit INT NOT NULL DEFAULT 0 COMMENT '每用户 UTC 日发送上限0 表示禁止发送', daily_send_limit INT NOT NULL DEFAULT 0 COMMENT '每用户 UTC 日发送上限0 表示禁止发送',
rule_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '红包规则说明链接',
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最后更新后台用户 ID', updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最后更新后台用户 ID',
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

@ -80,6 +80,9 @@ const (
RedPacketStatusRefunded = "refunded" RedPacketStatusRefunded = "refunded"
// RedPacketClaimStatusClaimed 表示红包份额领取成功。 // RedPacketClaimStatusClaimed 表示红包份额领取成功。
RedPacketClaimStatusClaimed = "claimed" RedPacketClaimStatusClaimed = "claimed"
// RedPacketExpireSeconds 固定房内红包 24 小时过期退款。
RedPacketExpireSeconds int32 = 24 * 60 * 60
) )
// DebitGiftCommand 是 room-service 送礼扣费的账务命令。 // DebitGiftCommand 是 room-service 送礼扣费的账务命令。
@ -606,6 +609,7 @@ type RedPacketConfig struct {
ExpireSeconds int32 ExpireSeconds int32
DailySendLimit int32 DailySendLimit int32
UpdatedByAdminID int64 UpdatedByAdminID int64
RuleURL string
CreatedAtMS int64 CreatedAtMS int64
UpdatedAtMS int64 UpdatedAtMS int64
} }
@ -631,6 +635,9 @@ type RedPacket struct {
ClaimedByViewer bool ClaimedByViewer bool
ViewerClaimAmount int64 ViewerClaimAmount int64
Claims []RedPacketClaim Claims []RedPacketClaim
RefundAmount int64
RefundStatus string
RefundedAtMS int64
} }
// RedPacketClaim 是单个用户抢红包的到账事实。 // RedPacketClaim 是单个用户抢红包的到账事实。
@ -673,9 +680,15 @@ type RedPacketClaimCommand struct {
type RedPacketClaimReceipt struct { type RedPacketClaimReceipt struct {
Claim RedPacketClaim Claim RedPacketClaim
Packet RedPacket
BalanceAfter int64 BalanceAfter int64
} }
type RedPacketRefundReceipt struct {
Packet RedPacket
RefundedAmount int64
}
type RedPacketQuery struct { type RedPacketQuery struct {
AppCode string AppCode string
RoomID string RoomID string
@ -796,6 +809,12 @@ func NormalizeGameOpType(opType string) string {
} }
func NormalizeRedPacketType(packetType string) string { func NormalizeRedPacketType(packetType string) string {
switch strings.ToUpper(strings.TrimSpace(packetType)) {
case "IMMEDIATE":
return RedPacketTypeNormal
case "DELAYED":
return RedPacketTypeDelayed
}
switch strings.ToLower(strings.TrimSpace(packetType)) { switch strings.ToLower(strings.TrimSpace(packetType)) {
case RedPacketTypeNormal: case RedPacketTypeNormal:
return RedPacketTypeNormal return RedPacketTypeNormal

View File

@ -28,18 +28,23 @@ func (s *Service) UpdateRedPacketConfig(ctx context.Context, config ledger.RedPa
config.AppCode = appcode.Normalize(config.AppCode) config.AppCode = appcode.Normalize(config.AppCode)
config.CountTiers = normalizeCountTiers(config.CountTiers) config.CountTiers = normalizeCountTiers(config.CountTiers)
config.AmountTiers = normalizeAmountTiers(config.AmountTiers) config.AmountTiers = normalizeAmountTiers(config.AmountTiers)
config.RuleURL = strings.TrimSpace(config.RuleURL)
config.ExpireSeconds = ledger.RedPacketExpireSeconds
if config.UpdatedByAdminID <= 0 { if config.UpdatedByAdminID <= 0 {
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required") return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
} }
if len(config.CountTiers) == 0 || len(config.AmountTiers) == 0 { if len(config.CountTiers) == 0 || len(config.AmountTiers) == 0 {
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet tiers are required") return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet tiers are required")
} }
if config.DelayedOpenSeconds < 0 || config.ExpireSeconds <= 0 || config.DailySendLimit < 0 { if config.DelayedOpenSeconds < 0 || config.DailySendLimit < 0 {
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet config is invalid") return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet config is invalid")
} }
if config.DelayedOpenSeconds >= config.ExpireSeconds { if config.DelayedOpenSeconds >= ledger.RedPacketExpireSeconds {
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet expire_seconds must be greater than delayed_open_seconds") return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet expire_seconds must be greater than delayed_open_seconds")
} }
if len(config.CountTiers) > 0 && len(config.AmountTiers) > 0 && config.AmountTiers[0] < int64(config.CountTiers[len(config.CountTiers)-1]) {
return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet amount tiers must cover count tiers")
}
ctx = appcode.WithContext(ctx, config.AppCode) ctx = appcode.WithContext(ctx, config.AppCode)
return s.repository.UpdateRedPacketConfig(ctx, config) return s.repository.UpdateRedPacketConfig(ctx, config)
} }
@ -127,6 +132,19 @@ func (s *Service) ExpireRedPackets(ctx context.Context, appCode string, batchSiz
return s.repository.ExpireRedPackets(ctx, appCode, batchSize) return s.repository.ExpireRedPackets(ctx, appCode, batchSize)
} }
func (s *Service) RetryRedPacketRefund(ctx context.Context, appCode string, packetID string, operatorUserID int64) (ledger.RedPacketRefundReceipt, error) {
if s.repository == nil {
return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
packetID = strings.TrimSpace(packetID)
if packetID == "" || operatorUserID <= 0 {
return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.InvalidArgument, "red packet refund retry command is incomplete")
}
appCode = appcode.Normalize(appCode)
ctx = appcode.WithContext(ctx, appCode)
return s.repository.RetryRedPacketRefund(ctx, appCode, packetID, operatorUserID)
}
func normalizeCountTiers(values []int32) []int32 { func normalizeCountTiers(values []int32) []int32 {
seen := make(map[int32]bool, len(values)) seen := make(map[int32]bool, len(values))
out := make([]int32, 0, len(values)) out := make([]int32, 0, len(values))

View File

@ -32,6 +32,7 @@ type Repository interface {
ListRedPackets(ctx context.Context, query ledger.RedPacketQuery) ([]ledger.RedPacket, int64, error) ListRedPackets(ctx context.Context, query ledger.RedPacketQuery) ([]ledger.RedPacket, int64, error)
GetRedPacket(ctx context.Context, appCode string, packetID string, viewerUserID int64, includeClaims bool) (ledger.RedPacket, error) GetRedPacket(ctx context.Context, appCode string, packetID string, viewerUserID int64, includeClaims bool) (ledger.RedPacket, error)
ExpireRedPackets(ctx context.Context, appCode string, batchSize int32) (ledger.RedPacketExpireResult, error) ExpireRedPackets(ctx context.Context, appCode string, batchSize int32) (ledger.RedPacketExpireResult, error)
RetryRedPacketRefund(ctx context.Context, appCode string, packetID string, operatorUserID int64) (ledger.RedPacketRefundReceipt, error)
ListRechargeBills(ctx context.Context, query ledger.ListRechargeBillsQuery) ([]ledger.RechargeBill, int64, error) ListRechargeBills(ctx context.Context, query ledger.ListRechargeBillsQuery) ([]ledger.RechargeBill, int64, error)
GetWalletOverview(ctx context.Context, userID int64) (ledger.WalletOverview, error) GetWalletOverview(ctx context.Context, userID int64) (ledger.WalletOverview, error)
GetWalletValueSummary(ctx context.Context, userID int64) (ledger.WalletValueSummary, error) GetWalletValueSummary(ctx context.Context, userID int64) (ledger.WalletValueSummary, error)

View File

@ -58,6 +58,12 @@ type redPacketClaimMetadata struct {
PacketID string `json:"packet_id"` PacketID string `json:"packet_id"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
Amount int64 `json:"amount"` Amount int64 `json:"amount"`
RoomID string `json:"room_id"`
RegionID int64 `json:"region_id"`
PacketType string `json:"packet_type"`
RemainingAmount int64 `json:"remaining_amount"`
RemainingCount int32 `json:"remaining_count"`
PacketStatus string `json:"packet_status"`
BalanceAfter int64 `json:"balance_after"` BalanceAfter int64 `json:"balance_after"`
WalletTransaction string `json:"wallet_transaction_id"` WalletTransaction string `json:"wallet_transaction_id"`
ClaimedAtMS int64 `json:"claimed_at_ms"` ClaimedAtMS int64 `json:"claimed_at_ms"`
@ -69,7 +75,11 @@ type redPacketRefundMetadata struct {
CommandID string `json:"command_id"` CommandID string `json:"command_id"`
PacketID string `json:"packet_id"` PacketID string `json:"packet_id"`
SenderUserID int64 `json:"sender_user_id"` SenderUserID int64 `json:"sender_user_id"`
RoomID string `json:"room_id"`
RegionID int64 `json:"region_id"`
PacketType string `json:"packet_type"`
RefundAmount int64 `json:"refund_amount"` RefundAmount int64 `json:"refund_amount"`
PacketStatus string `json:"packet_status"`
BalanceAfter int64 `json:"balance_after"` BalanceAfter int64 `json:"balance_after"`
WalletTransaction string `json:"wallet_transaction_id"` WalletTransaction string `json:"wallet_transaction_id"`
RefundedAtMS int64 `json:"refunded_at_ms"` RefundedAtMS int64 `json:"refunded_at_ms"`
@ -110,21 +120,23 @@ func (r *Repository) UpdateRedPacketConfig(ctx context.Context, config ledger.Re
if _, err := tx.ExecContext(ctx, ` if _, err := tx.ExecContext(ctx, `
INSERT INTO red_packet_configs ( INSERT INTO red_packet_configs (
app_code, enabled, delayed_open_seconds, expire_seconds, daily_send_limit, app_code, enabled, delayed_open_seconds, expire_seconds, daily_send_limit, rule_url,
updated_by_admin_id, created_at_ms, updated_at_ms updated_by_admin_id, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
enabled = VALUES(enabled), enabled = VALUES(enabled),
delayed_open_seconds = VALUES(delayed_open_seconds), delayed_open_seconds = VALUES(delayed_open_seconds),
expire_seconds = VALUES(expire_seconds), expire_seconds = VALUES(expire_seconds),
daily_send_limit = VALUES(daily_send_limit), daily_send_limit = VALUES(daily_send_limit),
rule_url = VALUES(rule_url),
updated_by_admin_id = VALUES(updated_by_admin_id), updated_by_admin_id = VALUES(updated_by_admin_id),
updated_at_ms = VALUES(updated_at_ms)`, updated_at_ms = VALUES(updated_at_ms)`,
config.AppCode, config.AppCode,
config.Enabled, config.Enabled,
config.DelayedOpenSeconds, config.DelayedOpenSeconds,
config.ExpireSeconds, ledger.RedPacketExpireSeconds,
config.DailySendLimit, config.DailySendLimit,
strings.TrimSpace(config.RuleURL),
config.UpdatedByAdminID, config.UpdatedByAdminID,
nowMS, nowMS,
nowMS, nowMS,
@ -202,7 +214,7 @@ func (r *Repository) CreateRedPacket(ctx context.Context, command ledger.RedPack
if command.TotalAmount < int64(command.PacketCount) { if command.TotalAmount < int64(command.PacketCount) {
return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.RedPacketAmountTooSmall, "red packet amount is too small") return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.RedPacketAmountTooSmall, "red packet amount is too small")
} }
if config.ExpireSeconds <= 0 || (command.PacketType == ledger.RedPacketTypeDelayed && config.ExpireSeconds <= config.DelayedOpenSeconds) { if ledger.RedPacketExpireSeconds <= 0 || (command.PacketType == ledger.RedPacketTypeDelayed && ledger.RedPacketExpireSeconds <= config.DelayedOpenSeconds) {
return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet config time range is invalid") return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet config time range is invalid")
} }
if err := r.incrementRedPacketDailyCounter(ctx, tx, command.SenderUserID, config.DailySendLimit, nowMS); err != nil { if err := r.incrementRedPacketDailyCounter(ctx, tx, command.SenderUserID, config.DailySendLimit, nowMS); err != nil {
@ -229,7 +241,7 @@ func (r *Repository) CreateRedPacket(ctx context.Context, command ledger.RedPack
openAtMS = nowMS + int64(config.DelayedOpenSeconds)*1000 openAtMS = nowMS + int64(config.DelayedOpenSeconds)*1000
status = ledger.RedPacketStatusWaitingOpen status = ledger.RedPacketStatusWaitingOpen
} }
expiresAtMS := nowMS + int64(config.ExpireSeconds)*1000 expiresAtMS := nowMS + int64(ledger.RedPacketExpireSeconds)*1000
balanceAfter := sender.AvailableAmount - command.TotalAmount balanceAfter := sender.AvailableAmount - command.TotalAmount
metadata := redPacketMetadata{ metadata := redPacketMetadata{
AppCode: command.AppCode, AppCode: command.AppCode,
@ -322,11 +334,11 @@ func (r *Repository) ClaimRedPacket(ctx context.Context, command ledger.RedPacke
if nowMS < packet.OpenAtMS { if nowMS < packet.OpenAtMS {
return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.RedPacketNotOpen, "red packet is not open") return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.RedPacketNotOpen, "red packet is not open")
} }
if claimed, err := r.userClaimedRedPacket(ctx, tx, command.PacketID, command.UserID); err != nil || claimed { if existingClaim, claimed, err := r.getUserRedPacketClaim(ctx, tx, command.PacketID, command.UserID); err != nil || claimed {
if err != nil { if err != nil {
return ledger.RedPacketClaimReceipt{}, err return ledger.RedPacketClaimReceipt{}, err
} }
return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.RedPacketAlreadyClaimed, "red packet already claimed") return ledger.RedPacketClaimReceipt{Claim: existingClaim, Packet: packet}, nil
} }
allocationNo, amount, err := r.lockOneRedPacketAllocation(ctx, tx, command.PacketID) allocationNo, amount, err := r.lockOneRedPacketAllocation(ctx, tx, command.PacketID)
if err != nil { if err != nil {
@ -350,6 +362,11 @@ func (r *Repository) ClaimRedPacket(ctx context.Context, command ledger.RedPacke
PacketID: command.PacketID, PacketID: command.PacketID,
UserID: command.UserID, UserID: command.UserID,
Amount: amount, Amount: amount,
RoomID: packet.RoomID,
RegionID: packet.RegionID,
PacketType: packet.PacketType,
RemainingAmount: packet.RemainingAmount - amount,
RemainingCount: packet.RemainingCount - 1,
BalanceAfter: balanceAfter, BalanceAfter: balanceAfter,
WalletTransaction: transactionID, WalletTransaction: transactionID,
ClaimedAtMS: nowMS, ClaimedAtMS: nowMS,
@ -376,7 +393,14 @@ func (r *Repository) ClaimRedPacket(ctx context.Context, command ledger.RedPacke
} }
if err := r.insertRedPacketClaim(ctx, tx, metadata); err != nil { if err := r.insertRedPacketClaim(ctx, tx, metadata); err != nil {
if isMySQLDuplicateError(err) { if isMySQLDuplicateError(err) {
return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.RedPacketAlreadyClaimed, "red packet already claimed") existingClaim, claimed, lookupErr := r.getUserRedPacketClaim(ctx, tx, command.PacketID, command.UserID)
if lookupErr != nil {
return ledger.RedPacketClaimReceipt{}, lookupErr
}
if claimed {
return r.redPacketClaimReceiptForTransaction(ctx, tx, existingClaim.WalletTransactionID)
}
return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.LedgerConflict, "red packet claim already exists")
} }
return ledger.RedPacketClaimReceipt{}, err return ledger.RedPacketClaimReceipt{}, err
} }
@ -395,6 +419,7 @@ func (r *Repository) ClaimRedPacket(ctx context.Context, command ledger.RedPacke
if newRemainingCount == 0 { if newRemainingCount == 0 {
newStatus = ledger.RedPacketStatusFinished newStatus = ledger.RedPacketStatusFinished
} }
metadata.PacketStatus = newStatus
if _, err := tx.ExecContext(ctx, ` if _, err := tx.ExecContext(ctx, `
UPDATE red_packets UPDATE red_packets
SET remaining_amount = ?, remaining_count = ?, status = ?, updated_at_ms = ? SET remaining_amount = ?, remaining_count = ?, status = ?, updated_at_ms = ?
@ -412,7 +437,11 @@ func (r *Repository) ClaimRedPacket(ctx context.Context, command ledger.RedPacke
if err := tx.Commit(); err != nil { if err := tx.Commit(); err != nil {
return ledger.RedPacketClaimReceipt{}, err return ledger.RedPacketClaimReceipt{}, err
} }
return ledger.RedPacketClaimReceipt{Claim: redPacketClaimFromMetadata(metadata), BalanceAfter: balanceAfter}, nil packet.RemainingAmount = newRemainingAmount
packet.RemainingCount = newRemainingCount
packet.Status = newStatus
packet.UpdatedAtMS = nowMS
return ledger.RedPacketClaimReceipt{Claim: redPacketClaimFromMetadata(metadata), Packet: packet, BalanceAfter: balanceAfter}, nil
} }
func (r *Repository) ListRedPackets(ctx context.Context, query ledger.RedPacketQuery) ([]ledger.RedPacket, int64, error) { func (r *Repository) ListRedPackets(ctx context.Context, query ledger.RedPacketQuery) ([]ledger.RedPacket, int64, error) {
@ -458,6 +487,9 @@ func (r *Repository) ListRedPackets(ctx context.Context, query ledger.RedPacketQ
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
return nil, 0, err return nil, 0, err
} }
if err := r.attachRefunds(ctx, packets); err != nil {
return nil, 0, err
}
if query.ViewerUserID > 0 { if query.ViewerUserID > 0 {
if err := r.attachViewerClaims(ctx, packets, query.ViewerUserID); err != nil { if err := r.attachViewerClaims(ctx, packets, query.ViewerUserID); err != nil {
return nil, 0, err return nil, 0, err
@ -480,6 +512,15 @@ func (r *Repository) GetRedPacket(ctx context.Context, app string, packetID stri
if err := r.attachViewerClaims(ctx, packets, viewerUserID); err != nil { if err := r.attachViewerClaims(ctx, packets, viewerUserID); err != nil {
return ledger.RedPacket{}, err return ledger.RedPacket{}, err
} }
if err := r.attachRefunds(ctx, packets); err != nil {
return ledger.RedPacket{}, err
}
packet = packets[0]
} else {
packets := []ledger.RedPacket{packet}
if err := r.attachRefunds(ctx, packets); err != nil {
return ledger.RedPacket{}, err
}
packet = packets[0] packet = packets[0]
} }
if includeClaims { if includeClaims {
@ -541,6 +582,30 @@ func (r *Repository) ExpireRedPackets(ctx context.Context, app string, batchSize
return result, nil return result, nil
} }
func (r *Repository) RetryRedPacketRefund(ctx context.Context, app string, packetID string, operatorUserID int64) (ledger.RedPacketRefundReceipt, error) {
if r == nil || r.db == nil {
return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
}
_ = operatorUserID
ctx = contextWithCommandApp(ctx, app)
nowMS := time.Now().UTC().UnixMilli()
refunded, amount, err := r.expireOneRedPacket(ctx, strings.TrimSpace(packetID), nowMS)
if err != nil {
return ledger.RedPacketRefundReceipt{}, err
}
packet, err := r.GetRedPacket(ctx, appcode.FromContext(ctx), packetID, 0, false)
if err != nil {
return ledger.RedPacketRefundReceipt{}, err
}
if !refunded {
if packet.Status == ledger.RedPacketStatusRefunded {
return ledger.RedPacketRefundReceipt{Packet: packet, RefundedAmount: packet.RefundAmount}, nil
}
return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.RedPacketExpired, "red packet is not expired")
}
return ledger.RedPacketRefundReceipt{Packet: packet, RefundedAmount: amount}, nil
}
func (r *Repository) expireOneRedPacket(ctx context.Context, packetID string, nowMS int64) (bool, int64, error) { func (r *Repository) expireOneRedPacket(ctx context.Context, packetID string, nowMS int64) (bool, int64, error) {
tx, err := r.db.BeginTx(ctx, nil) tx, err := r.db.BeginTx(ctx, nil)
if err != nil { if err != nil {
@ -588,7 +653,11 @@ func (r *Repository) expireOneRedPacket(ctx context.Context, packetID string, no
CommandID: commandID, CommandID: commandID,
PacketID: packetID, PacketID: packetID,
SenderUserID: packet.SenderUserID, SenderUserID: packet.SenderUserID,
RoomID: packet.RoomID,
RegionID: packet.RegionID,
PacketType: packet.PacketType,
RefundAmount: refundAmount, RefundAmount: refundAmount,
PacketStatus: ledger.RedPacketStatusRefunded,
BalanceAfter: balanceAfter, BalanceAfter: balanceAfter,
WalletTransaction: transactionID, WalletTransaction: transactionID,
RefundedAtMS: nowMS, RefundedAtMS: nowMS,
@ -649,12 +718,12 @@ func (r *Repository) expireOneRedPacket(ctx context.Context, packetID string, no
func (r *Repository) getRedPacketConfig(ctx context.Context, querier redPacketQuerier) (ledger.RedPacketConfig, error) { func (r *Repository) getRedPacketConfig(ctx context.Context, querier redPacketQuerier) (ledger.RedPacketConfig, error) {
row := querier.QueryRowContext(ctx, ` row := querier.QueryRowContext(ctx, `
SELECT app_code, enabled, delayed_open_seconds, expire_seconds, daily_send_limit, SELECT app_code, enabled, delayed_open_seconds, expire_seconds, daily_send_limit,
updated_by_admin_id, created_at_ms, updated_at_ms updated_by_admin_id, COALESCE(rule_url, ''), created_at_ms, updated_at_ms
FROM red_packet_configs FROM red_packet_configs
WHERE app_code = ?`, WHERE app_code = ?`,
appcode.FromContext(ctx), appcode.FromContext(ctx),
) )
config := ledger.RedPacketConfig{AppCode: appcode.FromContext(ctx), ExpireSeconds: 300} config := ledger.RedPacketConfig{AppCode: appcode.FromContext(ctx), ExpireSeconds: ledger.RedPacketExpireSeconds}
if err := row.Scan( if err := row.Scan(
&config.AppCode, &config.AppCode,
&config.Enabled, &config.Enabled,
@ -662,6 +731,7 @@ func (r *Repository) getRedPacketConfig(ctx context.Context, querier redPacketQu
&config.ExpireSeconds, &config.ExpireSeconds,
&config.DailySendLimit, &config.DailySendLimit,
&config.UpdatedByAdminID, &config.UpdatedByAdminID,
&config.RuleURL,
&config.CreatedAtMS, &config.CreatedAtMS,
&config.UpdatedAtMS, &config.UpdatedAtMS,
); err != nil { ); err != nil {
@ -670,6 +740,7 @@ func (r *Repository) getRedPacketConfig(ctx context.Context, querier redPacketQu
} }
return config, nil return config, nil
} }
config.ExpireSeconds = ledger.RedPacketExpireSeconds
counts, err := r.listRedPacketCountTiers(ctx, querier) counts, err := r.listRedPacketCountTiers(ctx, querier)
if err != nil { if err != nil {
return ledger.RedPacketConfig{}, err return ledger.RedPacketConfig{}, err
@ -869,19 +940,32 @@ func scanRedPacket(row redPacketScanner) (ledger.RedPacket, error) {
return packet, err return packet, err
} }
func (r *Repository) userClaimedRedPacket(ctx context.Context, tx *sql.Tx, packetID string, userID int64) (bool, error) { func (r *Repository) getUserRedPacketClaim(ctx context.Context, tx *sql.Tx, packetID string, userID int64) (ledger.RedPacketClaim, bool, error) {
var claimID string var claim ledger.RedPacketClaim
err := tx.QueryRowContext(ctx, ` err := tx.QueryRowContext(ctx, `
SELECT claim_id SELECT app_code, claim_id, command_id, packet_id, user_id, amount, wallet_transaction_id,
status, failure_reason, created_at_ms, updated_at_ms
FROM red_packet_claims FROM red_packet_claims
WHERE app_code = ? AND packet_id = ? AND user_id = ? WHERE app_code = ? AND packet_id = ? AND user_id = ?
LIMIT 1`, LIMIT 1`,
appcode.FromContext(ctx), packetID, userID, appcode.FromContext(ctx), packetID, userID,
).Scan(&claimID) ).Scan(
&claim.AppCode,
&claim.ClaimID,
&claim.CommandID,
&claim.PacketID,
&claim.UserID,
&claim.Amount,
&claim.WalletTransactionID,
&claim.Status,
&claim.FailureReason,
&claim.CreatedAtMS,
&claim.UpdatedAtMS,
)
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
return false, nil return ledger.RedPacketClaim{}, false, nil
} }
return err == nil, err return claim, err == nil, err
} }
func (r *Repository) lockOneRedPacketAllocation(ctx context.Context, tx *sql.Tx, packetID string) (int32, int64, error) { func (r *Repository) lockOneRedPacketAllocation(ctx context.Context, tx *sql.Tx, packetID string) (int32, int64, error) {
@ -944,6 +1028,50 @@ func (r *Repository) attachViewerClaims(ctx context.Context, packets []ledger.Re
return rows.Err() return rows.Err()
} }
func (r *Repository) attachRefunds(ctx context.Context, packets []ledger.RedPacket) error {
if len(packets) == 0 {
return nil
}
packetIDs := make([]string, 0, len(packets))
indexByID := make(map[string]int, len(packets))
for index := range packets {
packetIDs = append(packetIDs, packets[index].PacketID)
indexByID[packets[index].PacketID] = index
}
placeholders := strings.TrimRight(strings.Repeat("?,", len(packetIDs)), ",")
args := []any{appcode.FromContext(ctx)}
for _, packetID := range packetIDs {
args = append(args, packetID)
}
rows, err := r.db.QueryContext(ctx, `
SELECT packet_id, refund_amount, status, updated_at_ms
FROM red_packet_refunds
WHERE app_code = ? AND packet_id IN (`+placeholders+`)`,
args...,
)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var packetID string
var amount int64
var status string
var updatedAtMS int64
if err := rows.Scan(&packetID, &amount, &status, &updatedAtMS); err != nil {
return err
}
if index, ok := indexByID[packetID]; ok {
packets[index].RefundAmount = amount
packets[index].RefundStatus = status
if status == ledger.RedPacketStatusRefunded {
packets[index].RefundedAtMS = updatedAtMS
}
}
}
return rows.Err()
}
func (r *Repository) listRedPacketClaims(ctx context.Context, packetID string) ([]ledger.RedPacketClaim, error) { func (r *Repository) listRedPacketClaims(ctx context.Context, packetID string) ([]ledger.RedPacketClaim, error) {
rows, err := r.db.QueryContext(ctx, ` rows, err := r.db.QueryContext(ctx, `
SELECT app_code, claim_id, command_id, packet_id, user_id, amount, wallet_transaction_id, SELECT app_code, claim_id, command_id, packet_id, user_id, amount, wallet_transaction_id,
@ -1015,7 +1143,11 @@ func (r *Repository) redPacketClaimReceiptForTransaction(ctx context.Context, tx
if err := json.Unmarshal([]byte(raw), &metadata); err != nil { if err := json.Unmarshal([]byte(raw), &metadata); err != nil {
return ledger.RedPacketClaimReceipt{}, err return ledger.RedPacketClaimReceipt{}, err
} }
return ledger.RedPacketClaimReceipt{Claim: redPacketClaimFromMetadata(metadata), BalanceAfter: metadata.BalanceAfter}, nil packet, err := r.getRedPacket(ctx, tx, metadata.PacketID)
if err != nil {
return ledger.RedPacketClaimReceipt{Claim: redPacketClaimFromMetadata(metadata), BalanceAfter: metadata.BalanceAfter}, nil
}
return ledger.RedPacketClaimReceipt{Claim: redPacketClaimFromMetadata(metadata), Packet: packet, BalanceAfter: metadata.BalanceAfter}, nil
} }
func redPacketFromMetadata(metadata redPacketMetadata) ledger.RedPacket { func redPacketFromMetadata(metadata redPacketMetadata) ledger.RedPacket {

View File

@ -33,6 +33,7 @@ func (s *Server) UpdateRedPacketConfig(ctx context.Context, req *walletv1.Update
ExpireSeconds: req.GetExpireSeconds(), ExpireSeconds: req.GetExpireSeconds(),
DailySendLimit: req.GetDailySendLimit(), DailySendLimit: req.GetDailySendLimit(),
UpdatedByAdminID: req.GetOperatorUserId(), UpdatedByAdminID: req.GetOperatorUserId(),
RuleURL: req.GetRuleUrl(),
}) })
if err != nil { if err != nil {
return nil, xerr.ToGRPCError(err) return nil, xerr.ToGRPCError(err)
@ -80,6 +81,7 @@ func (s *Server) ClaimRedPacket(ctx context.Context, req *walletv1.ClaimRedPacke
Claim: redPacketClaimToProto(receipt.Claim), Claim: redPacketClaimToProto(receipt.Claim),
BalanceAfter: receipt.BalanceAfter, BalanceAfter: receipt.BalanceAfter,
ServerTimeMs: time.Now().UTC().UnixMilli(), ServerTimeMs: time.Now().UTC().UnixMilli(),
Packet: redPacketToProto(receipt.Packet),
}, nil }, nil
} }
@ -130,6 +132,19 @@ func (s *Server) ExpireRedPackets(ctx context.Context, req *walletv1.ExpireRedPa
}, nil }, nil
} }
func (s *Server) RetryRedPacketRefund(ctx context.Context, req *walletv1.RetryRedPacketRefundRequest) (*walletv1.RetryRedPacketRefundResponse, error) {
ctx = appcode.WithContext(ctx, req.GetAppCode())
receipt, err := s.svc.RetryRedPacketRefund(ctx, req.GetAppCode(), req.GetPacketId(), req.GetOperatorUserId())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &walletv1.RetryRedPacketRefundResponse{
Packet: redPacketToProto(receipt.Packet),
RefundedAmount: receipt.RefundedAmount,
ServerTimeMs: time.Now().UTC().UnixMilli(),
}, nil
}
func redPacketConfigToProto(config ledger.RedPacketConfig) *walletv1.RedPacketConfig { func redPacketConfigToProto(config ledger.RedPacketConfig) *walletv1.RedPacketConfig {
return &walletv1.RedPacketConfig{ return &walletv1.RedPacketConfig{
AppCode: config.AppCode, AppCode: config.AppCode,
@ -142,6 +157,7 @@ func redPacketConfigToProto(config ledger.RedPacketConfig) *walletv1.RedPacketCo
UpdatedByAdminId: config.UpdatedByAdminID, UpdatedByAdminId: config.UpdatedByAdminID,
CreatedAtMs: config.CreatedAtMS, CreatedAtMs: config.CreatedAtMS,
UpdatedAtMs: config.UpdatedAtMS, UpdatedAtMs: config.UpdatedAtMS,
RuleUrl: config.RuleURL,
} }
} }
@ -170,6 +186,9 @@ func redPacketToProto(packet ledger.RedPacket) *walletv1.RedPacket {
ClaimedByViewer: packet.ClaimedByViewer, ClaimedByViewer: packet.ClaimedByViewer,
ViewerClaimAmount: packet.ViewerClaimAmount, ViewerClaimAmount: packet.ViewerClaimAmount,
Claims: claims, Claims: claims,
RefundAmount: packet.RefundAmount,
RefundStatus: packet.RefundStatus,
RefundedAtMs: packet.RefundedAtMS,
} }
} }