红包字段返回
This commit is contained in:
parent
10bcfadbc4
commit
db5d6de329
@ -152,6 +152,9 @@ Content-Type: application/json
|
||||
"roomId": "9001",
|
||||
"regionId": 310,
|
||||
"senderUserId": 10001,
|
||||
"senderAccount": "10001",
|
||||
"senderNickname": "Tom",
|
||||
"senderAvatar": "https://example.com/avatar1.png",
|
||||
"packetMode": "DELAYED",
|
||||
"totalAmount": 15000,
|
||||
"totalCount": 5,
|
||||
@ -262,6 +265,12 @@ Content-Type: application/json
|
||||
},
|
||||
"packet": {
|
||||
"packetNo": "VRRP2026052712340001",
|
||||
"roomId": "9001",
|
||||
"regionId": 310,
|
||||
"senderUserId": 10001,
|
||||
"senderAccount": "10001",
|
||||
"senderNickname": "Tom",
|
||||
"senderAvatar": "https://example.com/avatar1.png",
|
||||
"remainingAmount": 12000,
|
||||
"remainingCount": 4,
|
||||
"status": "ACTIVE",
|
||||
@ -276,6 +285,7 @@ Content-Type: application/json
|
||||
|
||||
处理规则:
|
||||
|
||||
- 领取成功响应里的 `packet` 结构和详情接口一致,示例只保留弹窗刷新需要的关键字段。
|
||||
- Flutter 只提交 `packetNo`。当前服务端按 `packetNo + 当前用户` 保证同一用户重复领取返回同一结果。
|
||||
- 如果返回 `voice_room_red_packet_room_presence_required`,提示用户需要在房间内操作,并刷新房间在线态。
|
||||
- 如果返回 `voice_room_red_packet_not_started`,展示倒计时,不要重试抢。
|
||||
@ -301,6 +311,9 @@ GET /api/v1/app/voice-room/red-packet/detail?packetNo=VRRP2026052712340001
|
||||
"roomId": "9001",
|
||||
"regionId": 310,
|
||||
"senderUserId": 10001,
|
||||
"senderAccount": "10001",
|
||||
"senderNickname": "Tom",
|
||||
"senderAvatar": "https://example.com/avatar1.png",
|
||||
"packetMode": "DELAYED",
|
||||
"totalAmount": 15000,
|
||||
"totalCount": 5,
|
||||
@ -322,7 +335,7 @@ GET /api/v1/app/voice-room/red-packet/detail?packetNo=VRRP2026052712340001
|
||||
}
|
||||
```
|
||||
|
||||
详情接口里的 `claims` 不作为二级领取记录页的资料来源。需要头像昵称账号时使用 `claim-records`,因为该接口会按当前页批量补用户资料。
|
||||
详情和领取成功响应里的 `packet` 会补发送者 `senderAccount/senderNickname/senderAvatar`。详情接口里的 `claims` 不作为二级领取记录页的资料来源。需要领取用户头像昵称账号时使用 `claim-records`,因为该接口会按当前页批量补用户资料。
|
||||
|
||||
## 9. 领取记录
|
||||
|
||||
@ -555,9 +568,9 @@ class VoiceRoomRedPacket {
|
||||
final String packetMode;
|
||||
final String status;
|
||||
final String senderUserId;
|
||||
final String account;
|
||||
final String nickname;
|
||||
final String avatar;
|
||||
final String senderAccount;
|
||||
final String senderNickname;
|
||||
final String senderAvatar;
|
||||
final int totalAmount;
|
||||
final int totalCount;
|
||||
final int remainingAmount;
|
||||
@ -572,6 +585,7 @@ class VoiceRoomRedPacket {
|
||||
解析建议:
|
||||
|
||||
- 对 `senderUserId/userId` 这类 ID,兼容 number 和 string,统一转 string。
|
||||
- HTTP 发送成功、详情、领取成功里的 `packet` 使用 `senderAccount/senderNickname/senderAvatar`;IM `CREATED/OPEN_DUE` 使用 `actualAccount/userNickname/userAvatar`,落本地模型时归一到发送者字段。
|
||||
- 对金额和时间,兼容 number 和 string,统一转 int。
|
||||
- `CREATED` IM 里的 `remainAmount/remainCount` 对应 HTTP 里的 `remainingAmount/remainingCount`。
|
||||
- 如果 IM 和 HTTP 字段冲突,以 HTTP 最新查询结果为准。
|
||||
|
||||
@ -313,6 +313,7 @@ type fakeUserProfileClient struct {
|
||||
countryErr error
|
||||
regionID int64
|
||||
regionByUserID map[int64]int64
|
||||
usersByID map[int64]*userv1.User
|
||||
}
|
||||
|
||||
type fakeTencentIMAccountImporter struct {
|
||||
@ -457,8 +458,12 @@ type fakeWalletClient struct {
|
||||
lastGrantResource *walletv1.GrantResourceRequest
|
||||
grantResourceResp *walletv1.ResourceGrantResponse
|
||||
grantResourceErr error
|
||||
lastListRedPackets *walletv1.ListRedPacketsRequest
|
||||
listRedPacketsResp *walletv1.ListRedPacketsResponse
|
||||
lastGetRedPacket *walletv1.GetRedPacketRequest
|
||||
getRedPacketResp *walletv1.GetRedPacketResponse
|
||||
lastClaimRedPacket *walletv1.ClaimRedPacketRequest
|
||||
claimRedPacketResp *walletv1.ClaimRedPacketResponse
|
||||
}
|
||||
|
||||
type fakeMessageInboxClient struct {
|
||||
@ -573,6 +578,11 @@ func (f *fakeUserProfileClient) GetUser(_ context.Context, req *userv1.GetUserRe
|
||||
if f.getErr != nil {
|
||||
return nil, f.getErr
|
||||
}
|
||||
if f.usersByID != nil {
|
||||
if user := f.usersByID[req.GetUserId()]; user != nil {
|
||||
return &userv1.GetUserResponse{User: user}, nil
|
||||
}
|
||||
}
|
||||
regionID := f.regionID
|
||||
if f.regionByUserID != nil {
|
||||
if mapped := f.regionByUserID[req.GetUserId()]; mapped != 0 {
|
||||
@ -1380,11 +1390,22 @@ func (f *fakeWalletClient) CreateRedPacket(context.Context, *walletv1.CreateRedP
|
||||
return &walletv1.CreateRedPacketResponse{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeWalletClient) ClaimRedPacket(context.Context, *walletv1.ClaimRedPacketRequest) (*walletv1.ClaimRedPacketResponse, error) {
|
||||
func (f *fakeWalletClient) ClaimRedPacket(_ context.Context, req *walletv1.ClaimRedPacketRequest) (*walletv1.ClaimRedPacketResponse, error) {
|
||||
f.lastClaimRedPacket = req
|
||||
if f.err != nil {
|
||||
return nil, f.err
|
||||
}
|
||||
if f.claimRedPacketResp != nil {
|
||||
return f.claimRedPacketResp, nil
|
||||
}
|
||||
return &walletv1.ClaimRedPacketResponse{}, nil
|
||||
}
|
||||
|
||||
func (f *fakeWalletClient) ListRedPackets(context.Context, *walletv1.ListRedPacketsRequest) (*walletv1.ListRedPacketsResponse, error) {
|
||||
func (f *fakeWalletClient) ListRedPackets(_ context.Context, req *walletv1.ListRedPacketsRequest) (*walletv1.ListRedPacketsResponse, error) {
|
||||
f.lastListRedPackets = req
|
||||
if f.listRedPacketsResp != nil {
|
||||
return f.listRedPacketsResp, nil
|
||||
}
|
||||
return &walletv1.ListRedPacketsResponse{}, nil
|
||||
}
|
||||
|
||||
@ -4146,6 +4167,169 @@ func TestVoiceRoomRedPacketClaimRecordsIncludesUserProfiles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoiceRoomRedPacketDetailIncludesSenderProfile(t *testing.T) {
|
||||
walletClient := &fakeWalletClient{getRedPacketResp: &walletv1.GetRedPacketResponse{
|
||||
ServerTimeMs: 3000,
|
||||
Packet: &walletv1.RedPacket{
|
||||
PacketId: "packet-1",
|
||||
RoomId: "room-1",
|
||||
RegionId: 1001,
|
||||
SenderUserId: 10002,
|
||||
PacketType: "normal",
|
||||
TotalAmount: 1000,
|
||||
PacketCount: 5,
|
||||
RemainingCount: 3,
|
||||
Status: "active",
|
||||
},
|
||||
}}
|
||||
profileClient := &fakeUserProfileClient{usersByID: map[int64]*userv1.User{
|
||||
10002: &userv1.User{UserId: 10002, DisplayUserId: "880002", Username: "sender-name", Avatar: "https://cdn.example/sender.png", RegionId: 1001},
|
||||
}}
|
||||
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient)
|
||||
handler.SetWalletClient(walletClient)
|
||||
router := handler.Routes(auth.NewVerifier("secret"))
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/v1/app/voice-room/red-packet/detail?packetNo=packet-1", nil)
|
||||
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||
request.Header.Set("X-Request-ID", "req-red-packet-detail")
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
if profileClient.lastGet == nil || profileClient.lastGet.GetUserId() != 10002 {
|
||||
t.Fatalf("detail must fetch sender profile: %+v", profileClient.lastGet)
|
||||
}
|
||||
var response httpkit.ResponseEnvelope
|
||||
if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("decode red-packet detail failed: %v", err)
|
||||
}
|
||||
data, ok := response.Data.(map[string]any)
|
||||
packet, packetOK := data["packet"].(map[string]any)
|
||||
if response.Code != httpkit.CodeOK || !ok || !packetOK {
|
||||
t.Fatalf("detail response mismatch: %+v", response)
|
||||
}
|
||||
if packet["senderAccount"] != "880002" || packet["senderNickname"] != "sender-name" || packet["senderAvatar"] != "https://cdn.example/sender.png" {
|
||||
t.Fatalf("detail packet should include sender display fields: %+v", packet)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoiceRoomRedPacketClaimIncludesSenderProfile(t *testing.T) {
|
||||
packetBeforeClaim := &walletv1.RedPacket{
|
||||
PacketId: "packet-1",
|
||||
RoomId: "room-1",
|
||||
RegionId: 1001,
|
||||
SenderUserId: 10002,
|
||||
PacketType: "normal",
|
||||
TotalAmount: 1000,
|
||||
PacketCount: 5,
|
||||
RemainingCount: 3,
|
||||
Status: "active",
|
||||
}
|
||||
walletClient := &fakeWalletClient{
|
||||
getRedPacketResp: &walletv1.GetRedPacketResponse{Packet: packetBeforeClaim},
|
||||
claimRedPacketResp: &walletv1.ClaimRedPacketResponse{
|
||||
ServerTimeMs: 4000,
|
||||
BalanceAfter: 700,
|
||||
Packet: &walletv1.RedPacket{
|
||||
PacketId: "packet-1",
|
||||
RoomId: "room-1",
|
||||
RegionId: 1001,
|
||||
SenderUserId: 10002,
|
||||
PacketType: "normal",
|
||||
TotalAmount: 1000,
|
||||
PacketCount: 5,
|
||||
RemainingCount: 2,
|
||||
Status: "active",
|
||||
},
|
||||
Claim: &walletv1.RedPacketClaim{ClaimId: "claim-1", PacketId: "packet-1", UserId: 42, Amount: 200, Status: "claimed", CreatedAtMs: 3500},
|
||||
},
|
||||
}
|
||||
profileClient := &fakeUserProfileClient{usersByID: map[int64]*userv1.User{
|
||||
42: &userv1.User{UserId: 42, DisplayUserId: "880042", Username: "claimer-name", Avatar: "https://cdn.example/claimer.png", RegionId: 1001},
|
||||
10002: &userv1.User{UserId: 10002, DisplayUserId: "880002", Username: "sender-name", Avatar: "https://cdn.example/sender.png", RegionId: 1001},
|
||||
}}
|
||||
guardClient := &fakeRoomGuardClient{presenceResp: &roomv1.VerifyRoomPresenceResponse{Present: true, RoomVersion: 9}}
|
||||
handler := NewHandlerWithConfig(&fakeRoomClient{}, guardClient, nil, nil, TencentIMConfig{}, profileClient)
|
||||
handler.SetWalletClient(walletClient)
|
||||
router := handler.Routes(auth.NewVerifier("secret"))
|
||||
request := httptest.NewRequest(http.MethodPost, "/api/v1/app/voice-room/red-packet/claim", bytes.NewReader([]byte(`{"packetNo":"packet-1"}`)))
|
||||
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||
request.Header.Set("X-Request-ID", "req-red-packet-claim")
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
if guardClient.lastPresence == nil || guardClient.lastPresence.GetRoomId() != "room-1" || guardClient.lastPresence.GetUserId() != 42 {
|
||||
t.Fatalf("claim must verify room presence before wallet mutation: %+v", guardClient.lastPresence)
|
||||
}
|
||||
if len(profileClient.getRequests) != 2 || profileClient.getRequests[0].GetUserId() != 42 || profileClient.getRequests[1].GetUserId() != 10002 {
|
||||
t.Fatalf("claim must fetch claimer then sender profiles before response: %+v", profileClient.getRequests)
|
||||
}
|
||||
if walletClient.lastClaimRedPacket == nil || walletClient.lastClaimRedPacket.GetPacketId() != "packet-1" || walletClient.lastClaimRedPacket.GetUserId() != 42 {
|
||||
t.Fatalf("claim request mismatch: %+v", walletClient.lastClaimRedPacket)
|
||||
}
|
||||
var response httpkit.ResponseEnvelope
|
||||
if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("decode red-packet claim failed: %v", err)
|
||||
}
|
||||
data, ok := response.Data.(map[string]any)
|
||||
packet, packetOK := data["packet"].(map[string]any)
|
||||
claim, claimOK := data["claim"].(map[string]any)
|
||||
if response.Code != httpkit.CodeOK || !ok || !packetOK || !claimOK {
|
||||
t.Fatalf("claim response mismatch: %+v", response)
|
||||
}
|
||||
if packet["senderAccount"] != "880002" || packet["senderNickname"] != "sender-name" || packet["senderAvatar"] != "https://cdn.example/sender.png" {
|
||||
t.Fatalf("claim packet should include sender display fields: %+v", packet)
|
||||
}
|
||||
if claim["account"] != "880042" || claim["nickname"] != "claimer-name" || claim["avatar"] != "https://cdn.example/claimer.png" {
|
||||
t.Fatalf("claim should keep claimant display fields: %+v", claim)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoiceRoomActiveRedPacketRouteIsRegistered(t *testing.T) {
|
||||
walletClient := &fakeWalletClient{listRedPacketsResp: &walletv1.ListRedPacketsResponse{
|
||||
ServerTimeMs: 4000,
|
||||
Packets: []*walletv1.RedPacket{
|
||||
{PacketId: "packet-active", RoomId: "room-1", RegionId: 1001, PacketType: "normal", Status: "active"},
|
||||
{PacketId: "packet-finished", RoomId: "room-1", RegionId: 1001, PacketType: "normal", Status: "finished"},
|
||||
},
|
||||
}}
|
||||
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, &fakeUserProfileClient{regionID: 1001})
|
||||
handler.SetWalletClient(walletClient)
|
||||
router := handler.Routes(auth.NewVerifier("secret"))
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/v1/app/voice-room/red-packet/active?roomId=room-1", nil)
|
||||
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||
request.Header.Set("X-Request-ID", "req-red-packet-active")
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
if walletClient.lastListRedPackets == nil || walletClient.lastListRedPackets.GetRoomId() != "room-1" || walletClient.lastListRedPackets.GetViewerUserId() != 42 {
|
||||
t.Fatalf("active red-packet request mismatch: %+v", walletClient.lastListRedPackets)
|
||||
}
|
||||
var response httpkit.ResponseEnvelope
|
||||
if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("decode active red-packet response failed: %v", err)
|
||||
}
|
||||
data, ok := response.Data.(map[string]any)
|
||||
packets, packetsOK := data["packets"].([]any)
|
||||
if response.Code != httpkit.CodeOK || !ok || !packetsOK || len(packets) != 1 {
|
||||
t.Fatalf("active red-packet response mismatch: %+v", response)
|
||||
}
|
||||
first, ok := packets[0].(map[string]any)
|
||||
if !ok || first["packetNo"] != "packet-active" || first["status"] != "ACTIVE" {
|
||||
t.Fatalf("active packet item mismatch: %+v", first)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMyBalancesUsesAuthenticatedUserIDAndDefaultsCoin(t *testing.T) {
|
||||
walletClient := &fakeWalletClient{resp: &walletv1.GetBalancesResponse{
|
||||
Balances: []*walletv1.AssetBalance{{AssetType: "COIN", AvailableAmount: 120, FrozenAmount: 3, Version: 2}},
|
||||
|
||||
@ -265,7 +265,7 @@ func (h *Handler) sendVoiceRoomRedPacket(writer http.ResponseWriter, request *ht
|
||||
return
|
||||
}
|
||||
httpkit.WriteOK(writer, request, map[string]any{
|
||||
"packet": voiceRoomPacketFromProto(resp.GetPacket()),
|
||||
"packet": voiceRoomPacketFromProtoWithSender(resp.GetPacket(), user),
|
||||
"balanceAfter": resp.GetBalanceAfter(),
|
||||
"serverTime": resp.GetServerTimeMs(),
|
||||
})
|
||||
@ -346,6 +346,11 @@ func (h *Handler) claimVoiceRoomRedPacket(writer http.ResponseWriter, request *h
|
||||
httpkit.WriteError(writer, request, http.StatusForbidden, "voice_room_region_not_match", "permission denied")
|
||||
return
|
||||
}
|
||||
sender, err := h.redPacketSenderProfile(request, packet, user)
|
||||
if err != nil {
|
||||
httpkit.WriteRPCError(writer, request, err)
|
||||
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()),
|
||||
@ -359,14 +364,14 @@ func (h *Handler) claimVoiceRoomRedPacket(writer http.ResponseWriter, request *h
|
||||
}
|
||||
httpkit.WriteOK(writer, request, map[string]any{
|
||||
"claim": voiceRoomClaimFromProtoWithUser(resp.GetClaim(), user),
|
||||
"packet": voiceRoomPacketFromProto(resp.GetPacket()),
|
||||
"packet": voiceRoomPacketFromProtoWithSender(resp.GetPacket(), sender),
|
||||
"balanceAfter": resp.GetBalanceAfter(),
|
||||
"serverTime": resp.GetServerTimeMs(),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) getVoiceRoomRedPacket(writer http.ResponseWriter, request *http.Request) {
|
||||
if h.walletClient == nil {
|
||||
if h.walletClient == nil || h.userProfileClient == nil {
|
||||
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
||||
return
|
||||
}
|
||||
@ -388,8 +393,13 @@ func (h *Handler) getVoiceRoomRedPacket(writer http.ResponseWriter, request *htt
|
||||
writeRedPacketRPCError(writer, request, err)
|
||||
return
|
||||
}
|
||||
sender, err := h.redPacketSenderProfile(request, resp.GetPacket(), nil)
|
||||
if err != nil {
|
||||
httpkit.WriteRPCError(writer, request, err)
|
||||
return
|
||||
}
|
||||
httpkit.WriteOK(writer, request, map[string]any{
|
||||
"packet": voiceRoomPacketFromProto(resp.GetPacket()),
|
||||
"packet": voiceRoomPacketFromProtoWithSender(resp.GetPacket(), sender),
|
||||
"serverTime": resp.GetServerTimeMs(),
|
||||
})
|
||||
}
|
||||
@ -484,6 +494,25 @@ func (h *Handler) redPacketClaimProfiles(request *http.Request, claims []*wallet
|
||||
return resp.GetUsers(), nil
|
||||
}
|
||||
|
||||
func (h *Handler) redPacketSenderProfile(request *http.Request, packet *walletv1.RedPacket, currentUser *userv1.User) (*userv1.User, error) {
|
||||
senderUserID := packet.GetSenderUserId()
|
||||
if senderUserID <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// 发送者资料属于 user-service;claim 流程复用当前用户资料,避免同一用户发抢自己的红包时重复 RPC。
|
||||
if currentUser != nil && currentUser.GetUserId() == senderUserID {
|
||||
return currentUser, nil
|
||||
}
|
||||
resp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
|
||||
Meta: httpkit.UserMeta(request, ""),
|
||||
UserId: senderUserID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.GetUser(), nil
|
||||
}
|
||||
|
||||
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")
|
||||
@ -938,6 +967,29 @@ func voiceRoomPacketFromProto(packet *walletv1.RedPacket) map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
func voiceRoomPacketFromProtoWithSender(packet *walletv1.RedPacket, sender *userv1.User) map[string]any {
|
||||
data := voiceRoomPacketFromProto(packet)
|
||||
if packet == nil {
|
||||
return data
|
||||
}
|
||||
account := ""
|
||||
nickname := ""
|
||||
avatar := ""
|
||||
if sender != nil {
|
||||
account = sender.GetDisplayUserId()
|
||||
nickname = sender.GetUsername()
|
||||
avatar = sender.GetAvatar()
|
||||
}
|
||||
if account == "" && packet.GetSenderUserId() > 0 {
|
||||
account = strconv.FormatInt(packet.GetSenderUserId(), 10)
|
||||
}
|
||||
// wallet 只拥有红包和账务事实;HTTP 展示字段在 gateway 出口由 user-service 资料补齐。
|
||||
data["senderAccount"] = account
|
||||
data["senderNickname"] = nickname
|
||||
data["senderAvatar"] = avatar
|
||||
return data
|
||||
}
|
||||
|
||||
func voiceRoomClaimFromProto(claim *walletv1.RedPacketClaim) map[string]any {
|
||||
return voiceRoomClaimFromProtoWithUser(claim, nil)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user