Compare commits

..

No commits in common. "880f151feb7009bfb7f14e82669a5df0aa864cd6" and "11e0ec2f753067a85f748cb3cc1c07ed1fc4eec7" have entirely different histories.

48 changed files with 503 additions and 3351 deletions

File diff suppressed because it is too large Load Diff

View File

@ -360,35 +360,6 @@ message ListFriendApplicationsResponse {
int64 total = 2; int64 total = 2;
} }
// UserReport App
message UserReport {
string report_id = 1;
int64 reporter_user_id = 2;
string target_type = 3;
int64 user_id = 4;
string room_id = 5;
string report_type = 6;
string reason = 7;
repeated string image_urls = 8;
string status = 9;
int64 created_at_ms = 10;
}
// SubmitReportRequest user_id room_id
message SubmitReportRequest {
RequestMeta meta = 1;
int64 reporter_user_id = 2;
int64 user_id = 3;
string room_id = 4;
string report_type = 5;
string reason = 6;
repeated string image_urls = 7;
}
message SubmitReportResponse {
UserReport report = 1;
}
// BatchGetUsersRequest // BatchGetUsersRequest
message BatchGetUsersRequest { message BatchGetUsersRequest {
RequestMeta meta = 1; RequestMeta meta = 1;
@ -745,7 +716,6 @@ service UserSocialService {
rpc DeleteFriend(DeleteFriendRequest) returns (DeleteFriendResponse); rpc DeleteFriend(DeleteFriendRequest) returns (DeleteFriendResponse);
rpc ListFriends(ListFriendsRequest) returns (ListFriendsResponse); rpc ListFriends(ListFriendsRequest) returns (ListFriendsResponse);
rpc ListFriendApplications(ListFriendApplicationsRequest) returns (ListFriendApplicationsResponse); rpc ListFriendApplications(ListFriendApplicationsRequest) returns (ListFriendApplicationsResponse);
rpc SubmitReport(SubmitReportRequest) returns (SubmitReportResponse);
} }
// UserCronService cron-service user-service owner // UserCronService cron-service user-service owner

View File

@ -477,7 +477,6 @@ const (
UserSocialService_DeleteFriend_FullMethodName = "/hyapp.user.v1.UserSocialService/DeleteFriend" UserSocialService_DeleteFriend_FullMethodName = "/hyapp.user.v1.UserSocialService/DeleteFriend"
UserSocialService_ListFriends_FullMethodName = "/hyapp.user.v1.UserSocialService/ListFriends" UserSocialService_ListFriends_FullMethodName = "/hyapp.user.v1.UserSocialService/ListFriends"
UserSocialService_ListFriendApplications_FullMethodName = "/hyapp.user.v1.UserSocialService/ListFriendApplications" UserSocialService_ListFriendApplications_FullMethodName = "/hyapp.user.v1.UserSocialService/ListFriendApplications"
UserSocialService_SubmitReport_FullMethodName = "/hyapp.user.v1.UserSocialService/SubmitReport"
) )
// UserSocialServiceClient is the client API for UserSocialService service. // UserSocialServiceClient is the client API for UserSocialService service.
@ -496,7 +495,6 @@ type UserSocialServiceClient interface {
DeleteFriend(ctx context.Context, in *DeleteFriendRequest, opts ...grpc.CallOption) (*DeleteFriendResponse, error) DeleteFriend(ctx context.Context, in *DeleteFriendRequest, opts ...grpc.CallOption) (*DeleteFriendResponse, error)
ListFriends(ctx context.Context, in *ListFriendsRequest, opts ...grpc.CallOption) (*ListFriendsResponse, error) ListFriends(ctx context.Context, in *ListFriendsRequest, opts ...grpc.CallOption) (*ListFriendsResponse, error)
ListFriendApplications(ctx context.Context, in *ListFriendApplicationsRequest, opts ...grpc.CallOption) (*ListFriendApplicationsResponse, error) ListFriendApplications(ctx context.Context, in *ListFriendApplicationsRequest, opts ...grpc.CallOption) (*ListFriendApplicationsResponse, error)
SubmitReport(ctx context.Context, in *SubmitReportRequest, opts ...grpc.CallOption) (*SubmitReportResponse, error)
} }
type userSocialServiceClient struct { type userSocialServiceClient struct {
@ -607,16 +605,6 @@ func (c *userSocialServiceClient) ListFriendApplications(ctx context.Context, in
return out, nil return out, nil
} }
func (c *userSocialServiceClient) SubmitReport(ctx context.Context, in *SubmitReportRequest, opts ...grpc.CallOption) (*SubmitReportResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SubmitReportResponse)
err := c.cc.Invoke(ctx, UserSocialService_SubmitReport_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserSocialServiceServer is the server API for UserSocialService service. // UserSocialServiceServer is the server API for UserSocialService service.
// All implementations must embed UnimplementedUserSocialServiceServer // All implementations must embed UnimplementedUserSocialServiceServer
// for forward compatibility. // for forward compatibility.
@ -633,7 +621,6 @@ type UserSocialServiceServer interface {
DeleteFriend(context.Context, *DeleteFriendRequest) (*DeleteFriendResponse, error) DeleteFriend(context.Context, *DeleteFriendRequest) (*DeleteFriendResponse, error)
ListFriends(context.Context, *ListFriendsRequest) (*ListFriendsResponse, error) ListFriends(context.Context, *ListFriendsRequest) (*ListFriendsResponse, error)
ListFriendApplications(context.Context, *ListFriendApplicationsRequest) (*ListFriendApplicationsResponse, error) ListFriendApplications(context.Context, *ListFriendApplicationsRequest) (*ListFriendApplicationsResponse, error)
SubmitReport(context.Context, *SubmitReportRequest) (*SubmitReportResponse, error)
mustEmbedUnimplementedUserSocialServiceServer() mustEmbedUnimplementedUserSocialServiceServer()
} }
@ -674,9 +661,6 @@ func (UnimplementedUserSocialServiceServer) ListFriends(context.Context, *ListFr
func (UnimplementedUserSocialServiceServer) ListFriendApplications(context.Context, *ListFriendApplicationsRequest) (*ListFriendApplicationsResponse, error) { func (UnimplementedUserSocialServiceServer) ListFriendApplications(context.Context, *ListFriendApplicationsRequest) (*ListFriendApplicationsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListFriendApplications not implemented") return nil, status.Error(codes.Unimplemented, "method ListFriendApplications not implemented")
} }
func (UnimplementedUserSocialServiceServer) SubmitReport(context.Context, *SubmitReportRequest) (*SubmitReportResponse, error) {
return nil, status.Error(codes.Unimplemented, "method SubmitReport not implemented")
}
func (UnimplementedUserSocialServiceServer) mustEmbedUnimplementedUserSocialServiceServer() {} func (UnimplementedUserSocialServiceServer) mustEmbedUnimplementedUserSocialServiceServer() {}
func (UnimplementedUserSocialServiceServer) testEmbeddedByValue() {} func (UnimplementedUserSocialServiceServer) testEmbeddedByValue() {}
@ -878,24 +862,6 @@ func _UserSocialService_ListFriendApplications_Handler(srv interface{}, ctx cont
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _UserSocialService_SubmitReport_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SubmitReportRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserSocialServiceServer).SubmitReport(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserSocialService_SubmitReport_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserSocialServiceServer).SubmitReport(ctx, req.(*SubmitReportRequest))
}
return interceptor(ctx, in, info, handler)
}
// UserSocialService_ServiceDesc is the grpc.ServiceDesc for UserSocialService service. // UserSocialService_ServiceDesc is the grpc.ServiceDesc for UserSocialService 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)
@ -943,10 +909,6 @@ var UserSocialService_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListFriendApplications", MethodName: "ListFriendApplications",
Handler: _UserSocialService_ListFriendApplications_Handler, Handler: _UserSocialService_ListFriendApplications_Handler,
}, },
{
MethodName: "SubmitReport",
Handler: _UserSocialService_SubmitReport_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "proto/user/v1/user.proto", Metadata: "proto/user/v1/user.proto",

View File

@ -10676,7 +10676,6 @@ 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
} }
@ -10781,13 +10780,6 @@ 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"`
@ -10933,9 +10925,6 @@ 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
} }
@ -11103,27 +11092,6 @@ 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"`
@ -11239,7 +11207,6 @@ 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
} }
@ -11337,13 +11304,6 @@ 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"`
@ -11645,7 +11605,6 @@ 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
} }
@ -11701,13 +11660,6 @@ 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"`
@ -12148,134 +12100,6 @@ 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 = "" +
@ -13390,7 +13214,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\"\x9f\x03\n" + "\x11idempotent_replay\x18\x03 \x01(\bR\x10idempotentReplay\"\x84\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" +
@ -13403,8 +13227,7 @@ 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\x12\x19\n" + " \x01(\x03R\vupdatedAtMs\"\xee\x02\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" +
@ -13418,7 +13241,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\"\xa0\x06\n" + "\rupdated_at_ms\x18\v \x01(\x03R\vupdatedAtMs\"\xb0\x05\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" +
@ -13442,17 +13265,14 @@ 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\x12#\n" + "\x06claims\x18\x13 \x03(\v2\x1f.hyapp.wallet.v1.RedPacketClaimR\x06claims\"U\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\"\xfe\x02\n" + "\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xe3\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" +
@ -13464,9 +13284,7 @@ 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\x12\x19\n" + "\x10operator_user_id\x18\t \x01(\x03R\x0eoperatorUserId\"\x7f\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" +
@ -13494,12 +13312,11 @@ 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\"\xce\x01\n" + "\auser_id\x18\x05 \x01(\x03R\x06userId\"\x9a\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\x122\n" + "\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs\"\xf9\x02\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" +
@ -13539,17 +13356,7 @@ const file_proto_wallet_v1_wallet_proto_rawDesc = "" +
"\x18ExpireRedPacketsResponse\x12#\n" + "\x18ExpireRedPacketsResponse\x12#\n" +
"\rexpired_count\x18\x01 \x01(\x05R\fexpiredCount\x12'\n" + "\rexpired_count\x18\x01 \x01(\x05R\fexpiredCount\x12'\n" +
"\x0frefunded_amount\x18\x02 \x01(\x03R\x0erefundedAmount\x12$\n" + "\x0frefunded_amount\x18\x02 \x01(\x03R\x0erefundedAmount\x12$\n" +
"\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs\"\x9e\x01\n" + "\x0eserver_time_ms\x18\x03 \x01(\x03R\fserverTimeMs2\xf01\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" +
@ -13609,8 +13416,7 @@ 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.ExpireRedPacketsResponse\x12s\n" + "\x10ExpireRedPackets\x12(.hyapp.wallet.v1.ExpireRedPacketsRequest\x1a).hyapp.wallet.v1.ExpireRedPacketsResponseB*Z(hyapp.local/api/proto/wallet/v1;walletv1b\x06proto3"
"\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
@ -13624,7 +13430,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, 139) var file_proto_wallet_v1_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 137)
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
@ -13763,8 +13569,6 @@ 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
@ -13830,135 +13634,131 @@ 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.ClaimRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket 122, // 63: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket
122, // 64: hyapp.wallet.v1.ListRedPacketsResponse.packets:type_name -> hyapp.wallet.v1.RedPacket 122, // 64: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket
122, // 65: hyapp.wallet.v1.GetRedPacketResponse.packet:type_name -> hyapp.wallet.v1.RedPacket 0, // 65: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest
122, // 66: hyapp.wallet.v1.RetryRedPacketRefundResponse.packet:type_name -> hyapp.wallet.v1.RedPacket 3, // 66: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest
0, // 67: hyapp.wallet.v1.WalletService.DebitGift:input_type -> hyapp.wallet.v1.DebitGiftRequest 5, // 67: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest
3, // 68: hyapp.wallet.v1.WalletService.GetBalances:input_type -> hyapp.wallet.v1.GetBalancesRequest 7, // 68: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest
5, // 69: hyapp.wallet.v1.WalletService.AdminCreditAsset:input_type -> hyapp.wallet.v1.AdminCreditAssetRequest 9, // 69: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest
7, // 70: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:input_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockRequest 21, // 70: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest
9, // 71: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:input_type -> hyapp.wallet.v1.TransferCoinFromSellerRequest 23, // 71: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest
21, // 72: hyapp.wallet.v1.WalletService.ListResources:input_type -> hyapp.wallet.v1.ListResourcesRequest 25, // 72: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest
23, // 73: hyapp.wallet.v1.WalletService.GetResource:input_type -> hyapp.wallet.v1.GetResourceRequest 26, // 73: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest
25, // 74: hyapp.wallet.v1.WalletService.CreateResource:input_type -> hyapp.wallet.v1.CreateResourceRequest 27, // 74: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest
26, // 75: hyapp.wallet.v1.WalletService.UpdateResource:input_type -> hyapp.wallet.v1.UpdateResourceRequest 29, // 75: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest
27, // 76: hyapp.wallet.v1.WalletService.SetResourceStatus:input_type -> hyapp.wallet.v1.SetResourceStatusRequest 31, // 76: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest
29, // 77: hyapp.wallet.v1.WalletService.ListResourceGroups:input_type -> hyapp.wallet.v1.ListResourceGroupsRequest 33, // 77: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest
31, // 78: hyapp.wallet.v1.WalletService.GetResourceGroup:input_type -> hyapp.wallet.v1.GetResourceGroupRequest 34, // 78: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest
33, // 79: hyapp.wallet.v1.WalletService.CreateResourceGroup:input_type -> hyapp.wallet.v1.CreateResourceGroupRequest 35, // 79: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest
34, // 80: hyapp.wallet.v1.WalletService.UpdateResourceGroup:input_type -> hyapp.wallet.v1.UpdateResourceGroupRequest 37, // 80: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest
35, // 81: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:input_type -> hyapp.wallet.v1.SetResourceGroupStatusRequest 39, // 81: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest
37, // 82: hyapp.wallet.v1.WalletService.ListGiftConfigs:input_type -> hyapp.wallet.v1.ListGiftConfigsRequest 43, // 82: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest
39, // 83: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:input_type -> hyapp.wallet.v1.ListGiftTypeConfigsRequest 44, // 83: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest
43, // 84: hyapp.wallet.v1.WalletService.CreateGiftConfig:input_type -> hyapp.wallet.v1.CreateGiftConfigRequest 45, // 84: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest
44, // 85: hyapp.wallet.v1.WalletService.UpdateGiftConfig:input_type -> hyapp.wallet.v1.UpdateGiftConfigRequest 41, // 85: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest
45, // 86: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:input_type -> hyapp.wallet.v1.SetGiftConfigStatusRequest 47, // 86: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest
41, // 87: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:input_type -> hyapp.wallet.v1.UpsertGiftTypeConfigRequest 48, // 87: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest
47, // 88: hyapp.wallet.v1.WalletService.GrantResource:input_type -> hyapp.wallet.v1.GrantResourceRequest 50, // 88: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest
48, // 89: hyapp.wallet.v1.WalletService.GrantResourceGroup:input_type -> hyapp.wallet.v1.GrantResourceGroupRequest 52, // 89: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest
50, // 90: hyapp.wallet.v1.WalletService.ListUserResources:input_type -> hyapp.wallet.v1.ListUserResourcesRequest 54, // 90: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest
52, // 91: hyapp.wallet.v1.WalletService.EquipUserResource:input_type -> hyapp.wallet.v1.EquipUserResourceRequest 57, // 91: hyapp.wallet.v1.WalletService.ListResourceShopItems:input_type -> hyapp.wallet.v1.ListResourceShopItemsRequest
54, // 92: hyapp.wallet.v1.WalletService.ListResourceGrants:input_type -> hyapp.wallet.v1.ListResourceGrantsRequest 59, // 92: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:input_type -> hyapp.wallet.v1.UpsertResourceShopItemsRequest
57, // 93: hyapp.wallet.v1.WalletService.ListResourceShopItems:input_type -> hyapp.wallet.v1.ListResourceShopItemsRequest 61, // 93: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:input_type -> hyapp.wallet.v1.SetResourceShopItemStatusRequest
59, // 94: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:input_type -> hyapp.wallet.v1.UpsertResourceShopItemsRequest 63, // 94: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:input_type -> hyapp.wallet.v1.PurchaseResourceShopItemRequest
61, // 95: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:input_type -> hyapp.wallet.v1.SetResourceShopItemStatusRequest 66, // 95: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest
63, // 96: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:input_type -> hyapp.wallet.v1.PurchaseResourceShopItemRequest 69, // 96: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest
66, // 97: hyapp.wallet.v1.WalletService.ListRechargeBills:input_type -> hyapp.wallet.v1.ListRechargeBillsRequest 72, // 97: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest
69, // 98: hyapp.wallet.v1.WalletService.GetWalletOverview:input_type -> hyapp.wallet.v1.GetWalletOverviewRequest 75, // 98: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest
72, // 99: hyapp.wallet.v1.WalletService.GetWalletValueSummary:input_type -> hyapp.wallet.v1.GetWalletValueSummaryRequest 78, // 99: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest
75, // 100: hyapp.wallet.v1.WalletService.GetUserGiftWall:input_type -> hyapp.wallet.v1.GetUserGiftWallRequest 80, // 100: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest
78, // 101: hyapp.wallet.v1.WalletService.ListRechargeProducts:input_type -> hyapp.wallet.v1.ListRechargeProductsRequest 82, // 101: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest
80, // 102: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:input_type -> hyapp.wallet.v1.ConfirmGooglePaymentRequest 84, // 102: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest
82, // 103: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:input_type -> hyapp.wallet.v1.ListAdminRechargeProductsRequest 85, // 103: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest
84, // 104: hyapp.wallet.v1.WalletService.CreateRechargeProduct:input_type -> hyapp.wallet.v1.CreateRechargeProductRequest 86, // 104: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest
85, // 105: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:input_type -> hyapp.wallet.v1.UpdateRechargeProductRequest 90, // 105: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest
86, // 106: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:input_type -> hyapp.wallet.v1.DeleteRechargeProductRequest 93, // 106: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest
90, // 107: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:input_type -> hyapp.wallet.v1.GetDiamondExchangeConfigRequest 96, // 107: hyapp.wallet.v1.WalletService.ApplyWithdrawal:input_type -> hyapp.wallet.v1.ApplyWithdrawalRequest
93, // 108: hyapp.wallet.v1.WalletService.ListWalletTransactions:input_type -> hyapp.wallet.v1.ListWalletTransactionsRequest 101, // 108: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest
96, // 109: hyapp.wallet.v1.WalletService.ApplyWithdrawal:input_type -> hyapp.wallet.v1.ApplyWithdrawalRequest 103, // 109: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest
101, // 110: hyapp.wallet.v1.WalletService.ListVipPackages:input_type -> hyapp.wallet.v1.ListVipPackagesRequest 105, // 110: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest
103, // 111: hyapp.wallet.v1.WalletService.GetMyVip:input_type -> hyapp.wallet.v1.GetMyVipRequest 107, // 111: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest
105, // 112: hyapp.wallet.v1.WalletService.PurchaseVip:input_type -> hyapp.wallet.v1.PurchaseVipRequest 110, // 112: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest
107, // 113: hyapp.wallet.v1.WalletService.GrantVip:input_type -> hyapp.wallet.v1.GrantVipRequest 112, // 113: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest
110, // 114: hyapp.wallet.v1.WalletService.ListAdminVipLevels:input_type -> hyapp.wallet.v1.ListAdminVipLevelsRequest 114, // 114: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest
112, // 115: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:input_type -> hyapp.wallet.v1.UpdateAdminVipLevelsRequest 116, // 115: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:input_type -> hyapp.wallet.v1.CreditLuckyGiftRewardRequest
114, // 116: hyapp.wallet.v1.WalletService.CreditTaskReward:input_type -> hyapp.wallet.v1.CreditTaskRewardRequest 118, // 116: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest
116, // 117: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:input_type -> hyapp.wallet.v1.CreditLuckyGiftRewardRequest 123, // 117: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest
118, // 118: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:input_type -> hyapp.wallet.v1.ApplyGameCoinChangeRequest 125, // 118: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest
123, // 119: hyapp.wallet.v1.WalletService.GetRedPacketConfig:input_type -> hyapp.wallet.v1.GetRedPacketConfigRequest 127, // 119: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest
125, // 120: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:input_type -> hyapp.wallet.v1.UpdateRedPacketConfigRequest 129, // 120: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest
127, // 121: hyapp.wallet.v1.WalletService.CreateRedPacket:input_type -> hyapp.wallet.v1.CreateRedPacketRequest 131, // 121: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest
129, // 122: hyapp.wallet.v1.WalletService.ClaimRedPacket:input_type -> hyapp.wallet.v1.ClaimRedPacketRequest 133, // 122: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest
131, // 123: hyapp.wallet.v1.WalletService.ListRedPackets:input_type -> hyapp.wallet.v1.ListRedPacketsRequest 135, // 123: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest
133, // 124: hyapp.wallet.v1.WalletService.GetRedPacket:input_type -> hyapp.wallet.v1.GetRedPacketRequest 1, // 124: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse
135, // 125: hyapp.wallet.v1.WalletService.ExpireRedPackets:input_type -> hyapp.wallet.v1.ExpireRedPacketsRequest 4, // 125: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse
137, // 126: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:input_type -> hyapp.wallet.v1.RetryRedPacketRefundRequest 6, // 126: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse
1, // 127: hyapp.wallet.v1.WalletService.DebitGift:output_type -> hyapp.wallet.v1.DebitGiftResponse 8, // 127: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse
4, // 128: hyapp.wallet.v1.WalletService.GetBalances:output_type -> hyapp.wallet.v1.GetBalancesResponse 10, // 128: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse
6, // 129: hyapp.wallet.v1.WalletService.AdminCreditAsset:output_type -> hyapp.wallet.v1.AdminCreditAssetResponse 22, // 129: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse
8, // 130: hyapp.wallet.v1.WalletService.AdminCreditCoinSellerStock:output_type -> hyapp.wallet.v1.AdminCreditCoinSellerStockResponse 24, // 130: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse
10, // 131: hyapp.wallet.v1.WalletService.TransferCoinFromSeller:output_type -> hyapp.wallet.v1.TransferCoinFromSellerResponse 28, // 131: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse
22, // 132: hyapp.wallet.v1.WalletService.ListResources:output_type -> hyapp.wallet.v1.ListResourcesResponse 28, // 132: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse
24, // 133: hyapp.wallet.v1.WalletService.GetResource:output_type -> hyapp.wallet.v1.GetResourceResponse 28, // 133: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse
28, // 134: hyapp.wallet.v1.WalletService.CreateResource:output_type -> hyapp.wallet.v1.ResourceResponse 30, // 134: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse
28, // 135: hyapp.wallet.v1.WalletService.UpdateResource:output_type -> hyapp.wallet.v1.ResourceResponse 32, // 135: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse
28, // 136: hyapp.wallet.v1.WalletService.SetResourceStatus:output_type -> hyapp.wallet.v1.ResourceResponse 36, // 136: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse
30, // 137: hyapp.wallet.v1.WalletService.ListResourceGroups:output_type -> hyapp.wallet.v1.ListResourceGroupsResponse 36, // 137: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse
32, // 138: hyapp.wallet.v1.WalletService.GetResourceGroup:output_type -> hyapp.wallet.v1.GetResourceGroupResponse 36, // 138: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse
36, // 139: hyapp.wallet.v1.WalletService.CreateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse 38, // 139: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse
36, // 140: hyapp.wallet.v1.WalletService.UpdateResourceGroup:output_type -> hyapp.wallet.v1.ResourceGroupResponse 40, // 140: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse
36, // 141: hyapp.wallet.v1.WalletService.SetResourceGroupStatus:output_type -> hyapp.wallet.v1.ResourceGroupResponse 46, // 141: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse
38, // 142: hyapp.wallet.v1.WalletService.ListGiftConfigs:output_type -> hyapp.wallet.v1.ListGiftConfigsResponse 46, // 142: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse
40, // 143: hyapp.wallet.v1.WalletService.ListGiftTypeConfigs:output_type -> hyapp.wallet.v1.ListGiftTypeConfigsResponse 46, // 143: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse
46, // 144: hyapp.wallet.v1.WalletService.CreateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse 42, // 144: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse
46, // 145: hyapp.wallet.v1.WalletService.UpdateGiftConfig:output_type -> hyapp.wallet.v1.GiftConfigResponse 49, // 145: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse
46, // 146: hyapp.wallet.v1.WalletService.SetGiftConfigStatus:output_type -> hyapp.wallet.v1.GiftConfigResponse 49, // 146: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse
42, // 147: hyapp.wallet.v1.WalletService.UpsertGiftTypeConfig:output_type -> hyapp.wallet.v1.GiftTypeConfigResponse 51, // 147: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse
49, // 148: hyapp.wallet.v1.WalletService.GrantResource:output_type -> hyapp.wallet.v1.ResourceGrantResponse 53, // 148: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse
49, // 149: hyapp.wallet.v1.WalletService.GrantResourceGroup:output_type -> hyapp.wallet.v1.ResourceGrantResponse 55, // 149: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse
51, // 150: hyapp.wallet.v1.WalletService.ListUserResources:output_type -> hyapp.wallet.v1.ListUserResourcesResponse 58, // 150: hyapp.wallet.v1.WalletService.ListResourceShopItems:output_type -> hyapp.wallet.v1.ListResourceShopItemsResponse
53, // 151: hyapp.wallet.v1.WalletService.EquipUserResource:output_type -> hyapp.wallet.v1.EquipUserResourceResponse 60, // 151: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:output_type -> hyapp.wallet.v1.UpsertResourceShopItemsResponse
55, // 152: hyapp.wallet.v1.WalletService.ListResourceGrants:output_type -> hyapp.wallet.v1.ListResourceGrantsResponse 62, // 152: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:output_type -> hyapp.wallet.v1.ResourceShopItemResponse
58, // 153: hyapp.wallet.v1.WalletService.ListResourceShopItems:output_type -> hyapp.wallet.v1.ListResourceShopItemsResponse 64, // 153: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:output_type -> hyapp.wallet.v1.PurchaseResourceShopItemResponse
60, // 154: hyapp.wallet.v1.WalletService.UpsertResourceShopItems:output_type -> hyapp.wallet.v1.UpsertResourceShopItemsResponse 67, // 154: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse
62, // 155: hyapp.wallet.v1.WalletService.SetResourceShopItemStatus:output_type -> hyapp.wallet.v1.ResourceShopItemResponse 70, // 155: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse
64, // 156: hyapp.wallet.v1.WalletService.PurchaseResourceShopItem:output_type -> hyapp.wallet.v1.PurchaseResourceShopItemResponse 73, // 156: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse
67, // 157: hyapp.wallet.v1.WalletService.ListRechargeBills:output_type -> hyapp.wallet.v1.ListRechargeBillsResponse 76, // 157: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse
70, // 158: hyapp.wallet.v1.WalletService.GetWalletOverview:output_type -> hyapp.wallet.v1.GetWalletOverviewResponse 79, // 158: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse
73, // 159: hyapp.wallet.v1.WalletService.GetWalletValueSummary:output_type -> hyapp.wallet.v1.GetWalletValueSummaryResponse 81, // 159: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse
76, // 160: hyapp.wallet.v1.WalletService.GetUserGiftWall:output_type -> hyapp.wallet.v1.GetUserGiftWallResponse 83, // 160: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse
79, // 161: hyapp.wallet.v1.WalletService.ListRechargeProducts:output_type -> hyapp.wallet.v1.ListRechargeProductsResponse 87, // 161: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse
81, // 162: hyapp.wallet.v1.WalletService.ConfirmGooglePayment:output_type -> hyapp.wallet.v1.ConfirmGooglePaymentResponse 87, // 162: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse
83, // 163: hyapp.wallet.v1.WalletService.ListAdminRechargeProducts:output_type -> hyapp.wallet.v1.ListAdminRechargeProductsResponse 88, // 163: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse
87, // 164: hyapp.wallet.v1.WalletService.CreateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse 91, // 164: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse
87, // 165: hyapp.wallet.v1.WalletService.UpdateRechargeProduct:output_type -> hyapp.wallet.v1.RechargeProductResponse 94, // 165: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse
88, // 166: hyapp.wallet.v1.WalletService.DeleteRechargeProduct:output_type -> hyapp.wallet.v1.DeleteRechargeProductResponse 97, // 166: hyapp.wallet.v1.WalletService.ApplyWithdrawal:output_type -> hyapp.wallet.v1.ApplyWithdrawalResponse
91, // 167: hyapp.wallet.v1.WalletService.GetDiamondExchangeConfig:output_type -> hyapp.wallet.v1.GetDiamondExchangeConfigResponse 102, // 167: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse
94, // 168: hyapp.wallet.v1.WalletService.ListWalletTransactions:output_type -> hyapp.wallet.v1.ListWalletTransactionsResponse 104, // 168: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse
97, // 169: hyapp.wallet.v1.WalletService.ApplyWithdrawal:output_type -> hyapp.wallet.v1.ApplyWithdrawalResponse 106, // 169: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse
102, // 170: hyapp.wallet.v1.WalletService.ListVipPackages:output_type -> hyapp.wallet.v1.ListVipPackagesResponse 108, // 170: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse
104, // 171: hyapp.wallet.v1.WalletService.GetMyVip:output_type -> hyapp.wallet.v1.GetMyVipResponse 111, // 171: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse
106, // 172: hyapp.wallet.v1.WalletService.PurchaseVip:output_type -> hyapp.wallet.v1.PurchaseVipResponse 113, // 172: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse
108, // 173: hyapp.wallet.v1.WalletService.GrantVip:output_type -> hyapp.wallet.v1.GrantVipResponse 115, // 173: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse
111, // 174: hyapp.wallet.v1.WalletService.ListAdminVipLevels:output_type -> hyapp.wallet.v1.ListAdminVipLevelsResponse 117, // 174: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:output_type -> hyapp.wallet.v1.CreditLuckyGiftRewardResponse
113, // 175: hyapp.wallet.v1.WalletService.UpdateAdminVipLevels:output_type -> hyapp.wallet.v1.UpdateAdminVipLevelsResponse 119, // 175: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse
115, // 176: hyapp.wallet.v1.WalletService.CreditTaskReward:output_type -> hyapp.wallet.v1.CreditTaskRewardResponse 124, // 176: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse
117, // 177: hyapp.wallet.v1.WalletService.CreditLuckyGiftReward:output_type -> hyapp.wallet.v1.CreditLuckyGiftRewardResponse 126, // 177: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse
119, // 178: hyapp.wallet.v1.WalletService.ApplyGameCoinChange:output_type -> hyapp.wallet.v1.ApplyGameCoinChangeResponse 128, // 178: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse
124, // 179: hyapp.wallet.v1.WalletService.GetRedPacketConfig:output_type -> hyapp.wallet.v1.GetRedPacketConfigResponse 130, // 179: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse
126, // 180: hyapp.wallet.v1.WalletService.UpdateRedPacketConfig:output_type -> hyapp.wallet.v1.UpdateRedPacketConfigResponse 132, // 180: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse
128, // 181: hyapp.wallet.v1.WalletService.CreateRedPacket:output_type -> hyapp.wallet.v1.CreateRedPacketResponse 134, // 181: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse
130, // 182: hyapp.wallet.v1.WalletService.ClaimRedPacket:output_type -> hyapp.wallet.v1.ClaimRedPacketResponse 136, // 182: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse
132, // 183: hyapp.wallet.v1.WalletService.ListRedPackets:output_type -> hyapp.wallet.v1.ListRedPacketsResponse 124, // [124:183] is the sub-list for method output_type
134, // 184: hyapp.wallet.v1.WalletService.GetRedPacket:output_type -> hyapp.wallet.v1.GetRedPacketResponse 65, // [65:124] is the sub-list for method input_type
136, // 185: hyapp.wallet.v1.WalletService.ExpireRedPackets:output_type -> hyapp.wallet.v1.ExpireRedPacketsResponse 65, // [65:65] is the sub-list for extension type_name
138, // 186: hyapp.wallet.v1.WalletService.RetryRedPacketRefund:output_type -> hyapp.wallet.v1.RetryRedPacketRefundResponse 65, // [65:65] is the sub-list for extension extendee
127, // [127:187] is the sub-list for method output_type 0, // [0:65] is the sub-list for field type_name
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() }
@ -13974,7 +13774,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: 139, NumMessages: 137,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -1189,7 +1189,6 @@ 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 {
@ -1226,9 +1225,6 @@ 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 {
@ -1251,7 +1247,6 @@ 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 {
@ -1289,7 +1284,6 @@ 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 {
@ -1338,19 +1332,6 @@ 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);
@ -1412,5 +1393,4 @@ 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,7 +78,6 @@ 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.
@ -146,7 +145,6 @@ 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 {
@ -747,16 +745,6 @@ 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.
@ -822,7 +810,6 @@ 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()
} }
@ -1010,9 +997,6 @@ 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() {}
@ -2096,24 +2080,6 @@ 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)
@ -2357,10 +2323,6 @@ 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

@ -1,272 +0,0 @@
# 用户举报 Flutter 对接
本文描述 Flutter App 对接用户举报和房间举报的 HTTP 契约。通用文件上传接口用于上传举报图片并拿到 URL提交举报接口只接收 JSON。
## 基础地址
本地开发地址:
```text
http://127.0.0.1:13000
```
线上环境使用当前 App 配置里的 gateway base URL。下面所有路径都拼在 gateway base URL 后。
## 请求头
| Header | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `Authorization` | 是 | `Bearer <access_token>` | 登录 access token举报发起人只从 token 读取Flutter 不传举报发起人 ID。 |
| `X-App-Code` | 否 | `lalu` | App 编码;登录态接口最终以 token 内的 `app_code` 为准。 |
| `Content-Type` | 提交举报必填 | `application/json` | 提交举报使用 JSON。 |
| `Content-Type` | 上传图片必填 | `multipart/form-data` | 上传举报图片使用 multipart。 |
统一响应 envelope
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_abc",
"data": {}
}
```
Flutter 只在 `code == "OK"` 时读取 `data`。失败时记录 `request_id`,用于后端排查。
## 调用顺序
```text
1. 用户在举报弹窗选择举报类型。
2. 如有举报图,逐张调用 POST /api/v1/files/upload收集每次返回的 data.url。
3. 调用 POST /api/v1/reports 提交举报。
4. 提交成功后关闭弹窗,并用 data.report_id 写本地日志。
```
## 上传举报图片
上传接口只负责把文件写入对象存储并返回 URL不创建举报单。
```http
POST /api/v1/files/upload
Authorization: Bearer <access_token>
X-App-Code: lalu
Content-Type: multipart/form-data
file=<binary>
```
请求字段:
| 字段 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- |
| `file` | 是 | multipart file | 单文件最大 20MB。多图逐张上传Flutter 把每张图返回的 `data.url` 放进提交举报接口的 `image_urls`。 |
成功响应:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_upload_report_image",
"data": {
"url": "https://cdn.example.com/app/files/10001/20260529/file_xxx.jpg",
"object_key": "app/files/10001/20260529/file_xxx.jpg",
"content_type": "image/jpeg",
"size_bytes": 345678
}
}
```
Flutter 提交举报时只需要使用 `data.url`
## 提交举报
```http
POST /api/v1/reports
Authorization: Bearer <access_token>
X-App-Code: lalu
Content-Type: application/json
{
"user_id": "10002",
"report_type": "pornography",
"reason": "用户在房间内发布违规内容",
"image_urls": [
"https://cdn.example.com/app/files/10001/20260529/file_xxx.jpg"
]
}
```
举报房间示例:
```http
POST /api/v1/reports
Authorization: Bearer <access_token>
X-App-Code: lalu
Content-Type: application/json
{
"room_id": "lalu_abc123",
"report_type": "fraud",
"reason": "房间引导线下交易",
"image_urls": []
}
```
请求字段:
| 字段 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- |
| `user_id` | 条件必填 | string | 被举报用户 ID。和 `room_id` 二选一,只能传一个。 |
| `room_id` | 条件必填 | string | 被举报房间 ID。和 `user_id` 二选一,只能传一个。 |
| `report_type` | 是 | string | 举报类型,取值见下方枚举。 |
| `reason` | 否 | string | 举报理由Flutter 可允许用户不填。 |
| `image_urls` | 否 | string[] | 举报图 URL 数组;无图可传 `[]` 或不传。 |
目标校验规则:
| 场景 | 结果 |
| --- | --- |
| 只传 `user_id` | 举报用户。 |
| 只传 `room_id` | 举报房间。 |
| `user_id``room_id` 都不传 | `400 INVALID_ARGUMENT`。 |
| `user_id``room_id` 同时传 | `400 INVALID_ARGUMENT`Flutter 需要拆成一次明确的用户举报或房间举报。 |
举报发起人不在请求体里传。gateway 必须从 `Authorization` 对应的 access token 读取当前登录用户,避免客户端伪造举报发起人。
## 举报类型
| UI 文案 | `report_type` |
| --- | --- |
| Politics | `politics` |
| Pornography | `pornography` |
| Graphic Violence | `graphic_violence` |
| Verbal Abuse | `verbal_abuse` |
| Fraud | `fraud` |
| Illegal Content | `illegal_content` |
| Minors | `minors` |
| In-Person Transactions | `in_person_transactions` |
| Other | `other` |
## 返回体模拟数据
举报用户成功:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_report_user",
"data": {
"report_id": "rpt_10001_1778000000000",
"target_type": "user",
"user_id": "10002",
"room_id": "",
"report_type": "pornography",
"status": "submitted",
"created_at_ms": 1778000000000
}
}
```
举报房间成功:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_report_room",
"data": {
"report_id": "rpt_10001_1778000001000",
"target_type": "room",
"user_id": "",
"room_id": "lalu_abc123",
"report_type": "fraud",
"status": "submitted",
"created_at_ms": 1778000001000
}
}
```
字段说明:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `report_id` | string | 举报单 IDFlutter 可用于本地日志和客服排查。 |
| `target_type` | string | `user``room`,由服务端按本次传入目标生成。 |
| `user_id` | string | 被举报用户 ID房间举报返回空字符串。 |
| `room_id` | string | 被举报房间 ID用户举报返回空字符串。 |
| `report_type` | string | 本次提交的举报类型。 |
| `status` | string | 首次提交固定返回 `submitted`。 |
| `created_at_ms` | int64 | 举报创建时间Unix epoch milliseconds。 |
## 错误处理
失败返回仍使用统一 envelope
```json
{
"code": "INVALID_ARGUMENT",
"message": "invalid argument",
"request_id": "req_abc"
}
```
| HTTP 状态码 | `code` | Flutter 处理 |
| --- | --- | --- |
| `400` | `INVALID_JSON` | JSON 格式错误,检查本地序列化逻辑。 |
| `400` | `INVALID_ARGUMENT` | `user_id/room_id` 二选一不满足、`report_type` 非法、图片 URL 格式非法或字段超长。 |
| `401` | `UNAUTHORIZED` | token 缺失、无效、过期或会话失效,重新登录。 |
| `403` | `PROFILE_REQUIRED` | 用户资料未完成,跳转资料补全流程。 |
| `404` | `NOT_FOUND` | 被举报用户不存在,刷新页面状态。 |
| `502` | `UPSTREAM_ERROR` | 内部服务暂不可用,可提示稍后重试。 |
## Flutter 数据模型建议
```dart
class SubmitReportRequest {
SubmitReportRequest({
this.userId,
this.roomId,
required this.reportType,
this.reason,
this.imageUrls = const [],
});
final String? userId;
final String? roomId;
final String reportType;
final String? reason;
final List<String> imageUrls;
Map<String, dynamic> toJson() => {
if (userId != null && userId!.isNotEmpty) 'user_id': userId,
if (roomId != null && roomId!.isNotEmpty) 'room_id': roomId,
'report_type': reportType,
if (reason != null && reason!.trim().isNotEmpty) 'reason': reason!.trim(),
if (imageUrls.isNotEmpty) 'image_urls': imageUrls,
};
}
```
提交前本地校验:
```dart
void validateReportTarget({String? userId, String? roomId}) {
final hasUser = userId != null && userId.trim().isNotEmpty;
final hasRoom = roomId != null && roomId.trim().isNotEmpty;
if (hasUser == hasRoom) {
throw ArgumentError('user_id and room_id must be exactly one');
}
}
```
## 实现边界
- Flutter 不传举报发起人 ID服务端只认 token 中的当前用户。
- `user_id` 表示被举报用户,不是举报发起人。
- `room_id` 表示被举报房间,不要求同时传房间内某个用户。
- `user_id``room_id` 必须二选一,不能同时提交一个混合举报。
- 举报图片先上传再提交 URL提交举报接口不接收 multipart 文件。
- `message` 不作为业务分支依据Flutter 只按 `code` 和 HTTP 状态处理。

View File

@ -47,7 +47,6 @@ import (
redpacketmodule "hyapp-admin-server/internal/modules/redpacket" redpacketmodule "hyapp-admin-server/internal/modules/redpacket"
regionblockmodule "hyapp-admin-server/internal/modules/regionblock" regionblockmodule "hyapp-admin-server/internal/modules/regionblock"
registrationrewardmodule "hyapp-admin-server/internal/modules/registrationreward" registrationrewardmodule "hyapp-admin-server/internal/modules/registrationreward"
reportmodule "hyapp-admin-server/internal/modules/report"
resourcemodule "hyapp-admin-server/internal/modules/resource" resourcemodule "hyapp-admin-server/internal/modules/resource"
roomadminmodule "hyapp-admin-server/internal/modules/roomadmin" roomadminmodule "hyapp-admin-server/internal/modules/roomadmin"
roomtreasuremodule "hyapp-admin-server/internal/modules/roomtreasure" roomtreasuremodule "hyapp-admin-server/internal/modules/roomtreasure"
@ -228,7 +227,6 @@ func main() {
Payment: paymentmodule.New(walletclient.NewGRPC(walletConn), auditHandler), Payment: paymentmodule.New(walletclient.NewGRPC(walletConn), auditHandler),
RBAC: rbacmodule.New(store, auditHandler), RBAC: rbacmodule.New(store, auditHandler),
RedPacket: redpacketmodule.New(walletclient.NewGRPC(walletConn), auditHandler), RedPacket: redpacketmodule.New(walletclient.NewGRPC(walletConn), auditHandler),
Report: reportmodule.New(userDB, roomClient),
RegistrationReward: registrationrewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler), RegistrationReward: registrationrewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler),
RegionBlock: regionblockmodule.New(userDB, auditHandler), RegionBlock: regionblockmodule.New(userDB, auditHandler),
Resource: resourcemodule.New(walletclient.NewGRPC(walletConn), store, userDB, auditHandler), Resource: resourcemodule.New(walletclient.NewGRPC(walletConn), store, userDB, auditHandler),

View File

@ -46,7 +46,6 @@ 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 {
@ -200,7 +199,3 @@ 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,16 +26,10 @@ 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 {
@ -46,7 +40,6 @@ 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"`
@ -69,17 +62,9 @@ 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"`
@ -119,36 +104,15 @@ 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: countTiers, CountTiers: req.CountTiers,
AmountTiers: amountTiers, AmountTiers: req.AmountTiers,
DelayedOpenSeconds: delaySeconds, DelayedOpenSeconds: req.DelayedOpenSeconds,
ExpireSeconds: 86400, ExpireSeconds: req.ExpireSeconds,
DailySendLimit: dailyLimit, DailySendLimit: req.DailySendLimit,
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())
@ -172,16 +136,10 @@ 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: firstInt64Query(c, "senderUserId", "sender_user_id"), SenderUserId: parseInt64Query(c, "sender_user_id"),
RegionId: parseInt64Query(c, "region_id"), RegionId: parseInt64Query(c, "region_id"),
StartAtMs: firstInt64Query(c, "startTime", "start_at_ms"), StartAtMs: parseInt64Query(c, "start_at_ms"),
EndAtMs: firstInt64Query(c, "endTime", "end_at_ms"), EndAtMs: parseInt64Query(c, "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 {
@ -217,41 +175,6 @@ 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{}
@ -264,7 +187,6 @@ 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(),
@ -296,9 +218,6 @@ 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,
} }
} }
@ -322,15 +241,6 @@ 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,9 +10,8 @@ func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
if h == nil { if h == nil {
return return
} }
protected.GET("/resident-activity/voice-room-red-packet/config", middleware.RequirePermission("red-packet:view"), h.GetConfig) protected.GET("/admin/activity/red-packets/config", middleware.RequirePermission("red-packet:view"), h.GetConfig)
protected.POST("/resident-activity/voice-room-red-packet/config", middleware.RequirePermission("red-packet:update"), h.UpdateConfig) protected.PUT("/admin/activity/red-packets/config", middleware.RequirePermission("red-packet:update"), h.UpdateConfig)
protected.GET("/resident-activity/voice-room-red-packet/records", middleware.RequirePermission("red-packet:view"), h.ListPackets) protected.GET("/admin/activity/red-packets", middleware.RequirePermission("red-packet:view"), h.ListPackets)
protected.GET("/resident-activity/voice-room-red-packet/records/:packet_id", middleware.RequirePermission("red-packet:view"), h.GetPacket) protected.GET("/admin/activity/red-packets/: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

@ -1,83 +0,0 @@
package report
import (
"database/sql"
"strconv"
"strings"
"hyapp-admin-server/internal/appctx"
"hyapp-admin-server/internal/integration/roomclient"
"hyapp-admin-server/internal/modules/shared"
"hyapp-admin-server/internal/response"
"github.com/gin-gonic/gin"
)
type Handler struct {
service *Service
}
func New(userDB *sql.DB, roomClient roomclient.Client) *Handler {
return &Handler{service: NewService(userDB, roomClient)}
}
func (h *Handler) ListReports(c *gin.Context) {
query, ok := parseListQuery(c)
if !ok {
return
}
items, total, err := h.service.ListReports(c.Request.Context(), appctx.FromContext(c.Request.Context()), query)
if err != nil {
response.ServerError(c, "获取举报列表失败")
return
}
response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total})
}
func parseListQuery(c *gin.Context) (listQuery, bool) {
options := shared.ListOptions(c)
startAtMS, ok := optionalInt64(c, "start_at_ms", "startAtMs")
if !ok {
response.BadRequest(c, "开始时间不正确")
return listQuery{}, false
}
endAtMS, ok := optionalInt64(c, "end_at_ms", "endAtMs")
if !ok {
response.BadRequest(c, "结束时间不正确")
return listQuery{}, false
}
if startAtMS > 0 && endAtMS > 0 && startAtMS >= endAtMS {
response.BadRequest(c, "时间区间不正确")
return listQuery{}, false
}
return normalizeListQuery(listQuery{
Page: options.Page,
PageSize: options.PageSize,
Keyword: options.Keyword,
TargetType: firstQuery(c, "target_type", "targetType"),
ReportType: firstQuery(c, "report_type", "reportType"),
StartAtMS: startAtMS,
EndAtMS: endAtMS,
}), true
}
func optionalInt64(c *gin.Context, keys ...string) (int64, bool) {
value := strings.TrimSpace(firstQuery(c, keys...))
if value == "" {
return 0, true
}
parsed, err := strconv.ParseInt(value, 10, 64)
if err != nil || parsed < 0 {
return 0, false
}
return parsed, true
}
func firstQuery(c *gin.Context, keys ...string) string {
for _, key := range keys {
if value := strings.TrimSpace(c.Query(key)); value != "" {
return value
}
}
return ""
}

View File

@ -1,11 +0,0 @@
package report
type listQuery struct {
Page int
PageSize int
Keyword string
TargetType string
ReportType string
StartAtMS int64
EndAtMS int64
}

View File

@ -1,38 +0,0 @@
package report
type reportUserDTO struct {
Avatar string `json:"avatar"`
DefaultDisplayUserID string `json:"defaultDisplayUserId"`
DisplayUserID string `json:"displayUserId"`
UserID string `json:"userId"`
Username string `json:"username"`
}
type reportRoomDTO struct {
CoverURL string `json:"coverUrl"`
RoomID string `json:"roomId"`
RoomShortID string `json:"roomShortId"`
Title string `json:"title"`
}
type reportTargetDTO struct {
Type string `json:"type"`
Room *reportRoomDTO `json:"room,omitempty"`
User *reportUserDTO `json:"user,omitempty"`
}
type reportDTO struct {
CreatedAtMS int64 `json:"createdAtMs"`
ImageURLs []string `json:"imageUrls"`
Reason string `json:"reason"`
ReportID string `json:"reportId"`
ReporterUserID string `json:"reporterUserId"`
ReportType string `json:"reportType"`
RequestID string `json:"requestId"`
RoomID string `json:"roomId"`
Status string `json:"status"`
Target reportTargetDTO `json:"target"`
TargetType string `json:"targetType"`
UpdatedAtMS int64 `json:"updatedAtMs"`
UserID string `json:"userId"`
}

View File

@ -1,15 +0,0 @@
package report
import (
"hyapp-admin-server/internal/middleware"
"github.com/gin-gonic/gin"
)
func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
if h == nil {
return
}
protected.GET("/admin/operations/reports", middleware.RequirePermission("report:view"), h.ListReports)
}

View File

@ -1,274 +0,0 @@
package report
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"strconv"
"strings"
"hyapp-admin-server/internal/integration/roomclient"
)
const (
targetTypeUser = "user"
targetTypeRoom = "room"
)
type Service struct {
roomClient roomclient.Client
userDB *sql.DB
}
func NewService(userDB *sql.DB, roomClient roomclient.Client) *Service {
return &Service{userDB: userDB, roomClient: roomClient}
}
func (s *Service) ListReports(ctx context.Context, appCode string, query listQuery) ([]reportDTO, int64, error) {
query = normalizeListQuery(query)
if s == nil || s.userDB == nil {
return nil, 0, fmt.Errorf("user mysql is not configured")
}
whereSQL, args := reportWhere(appCode, query)
var total int64
if err := s.userDB.QueryRowContext(ctx, `
SELECT COUNT(*)
FROM user_reports r
LEFT JOIN users u ON u.app_code = r.app_code AND u.user_id = r.target_user_id
`+whereSQL,
args...,
).Scan(&total); err != nil {
return nil, 0, err
}
rows, err := s.userDB.QueryContext(ctx, `
SELECT r.report_id, r.reporter_user_id, r.target_type, r.target_user_id, r.room_id,
r.report_type, r.reason, CAST(r.image_urls_json AS CHAR), r.status, r.request_id,
r.created_at_ms, r.updated_at_ms,
COALESCE(u.current_display_user_id, ''), COALESCE(u.default_display_user_id, ''),
COALESCE(u.username, ''), COALESCE(u.avatar, '')
FROM user_reports r
LEFT JOIN users u ON u.app_code = r.app_code AND u.user_id = r.target_user_id
`+whereSQL+`
ORDER BY r.created_at_ms DESC, r.report_id DESC
LIMIT ? OFFSET ?`,
append(args, query.PageSize, offset(query.Page, query.PageSize))...,
)
if err != nil {
return nil, 0, err
}
defer rows.Close()
items := make([]reportDTO, 0, query.PageSize)
for rows.Next() {
var item reportDTO
var reporterUserID int64
var targetUserID sql.NullInt64
var imageURLsJSON string
var displayUserID string
var defaultDisplayUserID string
var username string
var avatar string
if err := rows.Scan(
&item.ReportID,
&reporterUserID,
&item.TargetType,
&targetUserID,
&item.RoomID,
&item.ReportType,
&item.Reason,
&imageURLsJSON,
&item.Status,
&item.RequestID,
&item.CreatedAtMS,
&item.UpdatedAtMS,
&displayUserID,
&defaultDisplayUserID,
&username,
&avatar,
); err != nil {
return nil, 0, err
}
imageURLs, err := parseImageURLs(imageURLsJSON)
if err != nil {
return nil, 0, err
}
item.ReporterUserID = formatID(reporterUserID)
item.ImageURLs = imageURLs
item.Target = reportTargetDTO{Type: item.TargetType}
if targetUserID.Valid && targetUserID.Int64 > 0 {
item.UserID = formatID(targetUserID.Int64)
item.Target.User = &reportUserDTO{
Avatar: avatar,
DefaultDisplayUserID: defaultDisplayUserID,
DisplayUserID: displayUserID,
UserID: item.UserID,
Username: username,
}
}
if item.TargetType == targetTypeRoom {
item.Target.Room = &reportRoomDTO{RoomID: item.RoomID}
}
items = append(items, item)
}
if err := rows.Err(); err != nil {
return nil, 0, err
}
if err := s.fillRoomTargets(ctx, items); err != nil {
return nil, 0, err
}
return items, total, nil
}
func normalizeListQuery(query listQuery) listQuery {
if query.Page < 1 {
query.Page = 1
}
if query.PageSize < 1 {
query.PageSize = 20
}
if query.PageSize > 100 {
query.PageSize = 100
}
query.Keyword = strings.TrimSpace(query.Keyword)
query.TargetType = normalizeTargetType(query.TargetType)
query.ReportType = strings.ToLower(strings.TrimSpace(query.ReportType))
if query.StartAtMS < 0 {
query.StartAtMS = 0
}
if query.EndAtMS < 0 {
query.EndAtMS = 0
}
return query
}
func normalizeTargetType(value string) string {
switch strings.ToLower(strings.TrimSpace(value)) {
case targetTypeUser, "用户":
return targetTypeUser
case targetTypeRoom, "房间":
return targetTypeRoom
default:
return ""
}
}
func reportWhere(appCode string, query listQuery) (string, []any) {
conditions := []string{"r.app_code = ?"}
args := []any{appCode}
if query.TargetType != "" {
conditions = append(conditions, "r.target_type = ?")
args = append(args, query.TargetType)
}
if query.ReportType != "" {
conditions = append(conditions, "r.report_type = ?")
args = append(args, query.ReportType)
}
if query.StartAtMS > 0 {
conditions = append(conditions, "r.created_at_ms >= ?")
args = append(args, query.StartAtMS)
}
if query.EndAtMS > 0 {
conditions = append(conditions, "r.created_at_ms < ?")
args = append(args, query.EndAtMS)
}
if query.Keyword != "" {
like := "%" + query.Keyword + "%"
conditions = append(conditions, `(r.report_id LIKE ? OR r.room_id LIKE ? OR CAST(r.target_user_id AS CHAR) LIKE ? OR CAST(r.reporter_user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.default_display_user_id LIKE ? OR u.username LIKE ?)`)
args = append(args, like, like, like, like, like, like, like)
}
return "WHERE " + strings.Join(conditions, " AND "), args
}
func parseImageURLs(raw string) ([]string, error) {
if strings.TrimSpace(raw) == "" {
return []string{}, nil
}
var urls []string
if err := json.Unmarshal([]byte(raw), &urls); err != nil {
return nil, err
}
if urls == nil {
return []string{}, nil
}
return urls, nil
}
func (s *Service) fillRoomTargets(ctx context.Context, items []reportDTO) error {
if s == nil || s.roomClient == nil || len(items) == 0 {
return nil
}
roomIDs := make([]string, 0, len(items))
for _, item := range items {
if item.TargetType == targetTypeRoom && strings.TrimSpace(item.RoomID) != "" {
roomIDs = append(roomIDs, item.RoomID)
}
}
rooms, err := s.queryRooms(ctx, uniqueStrings(roomIDs))
if err != nil {
return err
}
for index := range items {
if items[index].TargetType != targetTypeRoom {
continue
}
room := reportRoomDTO{RoomID: items[index].RoomID}
if found, ok := rooms[items[index].RoomID]; ok {
room = found
}
items[index].Target.Room = &room
}
return nil
}
func (s *Service) queryRooms(ctx context.Context, roomIDs []string) (map[string]reportRoomDTO, error) {
rooms := make(map[string]reportRoomDTO, len(roomIDs))
for _, roomID := range roomIDs {
room, err := s.roomClient.GetRoom(ctx, roomclient.GetRoomRequest{RoomID: roomID})
if err != nil {
// 举报列表是治理入口,房间被删或房间服务短暂不可读时仍保留举报事实本身。
if ctx.Err() != nil {
return nil, ctx.Err()
}
continue
}
rooms[roomID] = reportRoomDTO{
CoverURL: room.CoverURL,
RoomID: room.RoomID,
RoomShortID: room.RoomShortID,
Title: room.Title,
}
}
return rooms, nil
}
func formatID(value int64) string {
if value <= 0 {
return ""
}
return strconv.FormatInt(value, 10)
}
func uniqueStrings(values []string) []string {
seen := make(map[string]struct{}, len(values))
unique := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
unique = append(unique, value)
}
return unique
}
func offset(page int, pageSize int) int {
return (page - 1) * pageSize
}

View File

@ -1,38 +0,0 @@
package report
import "testing"
func TestNormalizeListQueryCapsPageSizeAndFilters(t *testing.T) {
query := normalizeListQuery(listQuery{Page: -1, PageSize: 1000, Keyword: " abc ", TargetType: "用户", ReportType: " Fraud "})
if query.Page != 1 || query.PageSize != 100 || query.Keyword != "abc" || query.TargetType != targetTypeUser || query.ReportType != "fraud" {
t.Fatalf("normalized query mismatch: %+v", query)
}
}
func TestReportWhereUsesStableFilters(t *testing.T) {
query := listQuery{
Keyword: "10001",
TargetType: targetTypeRoom,
ReportType: "fraud",
StartAtMS: 100,
EndAtMS: 200,
}
where, args := reportWhere("lalu", query)
want := "WHERE r.app_code = ? AND r.target_type = ? AND r.report_type = ? AND r.created_at_ms >= ? AND r.created_at_ms < ? AND (r.report_id LIKE ? OR r.room_id LIKE ? OR CAST(r.target_user_id AS CHAR) LIKE ? OR CAST(r.reporter_user_id AS CHAR) LIKE ? OR u.current_display_user_id LIKE ? OR u.default_display_user_id LIKE ? OR u.username LIKE ?)"
if where != want {
t.Fatalf("where mismatch:\nwant %s\n got %s", want, where)
}
if len(args) != 12 || args[0] != "lalu" || args[1] != targetTypeRoom || args[2] != "fraud" || args[3] != int64(100) || args[4] != int64(200) || args[5] != "%10001%" || args[11] != "%10001%" {
t.Fatalf("args mismatch: %#v", args)
}
}
func TestParseImageURLsNormalizesEmptyJSON(t *testing.T) {
urls, err := parseImageURLs("null")
if err != nil {
t.Fatalf("parse images failed: %v", err)
}
if len(urls) != 0 {
t.Fatalf("empty images mismatch: %#v", urls)
}
}

View File

@ -76,7 +76,6 @@ var defaultPermissions = []model.Permission{
{Name: "金币流水查看", Code: "coin-ledger:view", Kind: "menu"}, {Name: "金币流水查看", Code: "coin-ledger:view", Kind: "menu"},
{Name: "金币增减查看", Code: "coin-adjustment:view", Kind: "menu"}, {Name: "金币增减查看", Code: "coin-adjustment:view", Kind: "menu"},
{Name: "金币增减创建", Code: "coin-adjustment:create", Kind: "button"}, {Name: "金币增减创建", Code: "coin-adjustment:create", Kind: "button"},
{Name: "举报列表查看", Code: "report:view", Kind: "menu"},
{Name: "支付账单查看", Code: "payment-bill:view", Kind: "menu"}, {Name: "支付账单查看", Code: "payment-bill:view", Kind: "menu"},
{Name: "内购配置查看", Code: "payment-product:view", Kind: "menu"}, {Name: "内购配置查看", Code: "payment-product:view", Kind: "menu"},
{Name: "内购配置创建", Code: "payment-product:create", Kind: "button"}, {Name: "内购配置创建", Code: "payment-product:create", Kind: "button"},
@ -239,7 +238,6 @@ func (s *Store) seedMenus() error {
{ParentID: &operationsID, Title: "金币流水", Code: "operation-coin-ledger", Path: "/operations/coin-ledger", Icon: "receipt", PermissionCode: "coin-ledger:view", Sort: 68, Visible: true}, {ParentID: &operationsID, Title: "金币流水", Code: "operation-coin-ledger", Path: "/operations/coin-ledger", Icon: "receipt", PermissionCode: "coin-ledger:view", Sort: 68, Visible: true},
{ParentID: &operationsID, Title: "金币增减", Code: "operation-coin-adjustment", Path: "/operations/coin-adjustments", Icon: "wallet", PermissionCode: "coin-adjustment:view", Sort: 69, Visible: true}, {ParentID: &operationsID, Title: "金币增减", Code: "operation-coin-adjustment", Path: "/operations/coin-adjustments", Icon: "wallet", PermissionCode: "coin-adjustment:view", Sort: 69, Visible: true},
{ParentID: &operationsID, Title: "幸运礼物", Code: "lucky-gift", Path: "/operations/lucky-gift", Icon: "redeem", PermissionCode: "lucky-gift:view", Sort: 70, Visible: true}, {ParentID: &operationsID, Title: "幸运礼物", Code: "lucky-gift", Path: "/operations/lucky-gift", Icon: "redeem", PermissionCode: "lucky-gift:view", Sort: 70, Visible: true},
{ParentID: &operationsID, Title: "举报列表", Code: "operation-reports", Path: "/operations/reports", Icon: "flag", PermissionCode: "report:view", Sort: 71, Visible: true},
{ParentID: &paymentID, Title: "账单列表", Code: "payment-bill-list", Path: "/payment/bills", Icon: "receipt", PermissionCode: "payment-bill:view", Sort: 68, Visible: true}, {ParentID: &paymentID, Title: "账单列表", Code: "payment-bill-list", Path: "/payment/bills", Icon: "receipt", PermissionCode: "payment-bill:view", Sort: 68, Visible: true},
{ParentID: &activityID, Title: "每日任务", Code: "daily-task-list", Path: "/activities/daily-tasks", Icon: "task", PermissionCode: "daily-task:view", Sort: 69, Visible: true}, {ParentID: &activityID, Title: "每日任务", Code: "daily-task-list", Path: "/activities/daily-tasks", Icon: "task", PermissionCode: "daily-task:view", Sort: 69, Visible: true},
{ParentID: &activityID, Title: "注册奖励", Code: "registration-reward", Path: "/activities/registration-reward", Icon: "gift", PermissionCode: "registration-reward:view", Sort: 70, Visible: true}, {ParentID: &activityID, Title: "注册奖励", Code: "registration-reward", Path: "/activities/registration-reward", Icon: "gift", PermissionCode: "registration-reward:view", Sort: 70, Visible: true},
@ -484,7 +482,7 @@ func defaultRolePermissionCodes(code string) []string {
"agency:view", "agency:create", "agency:status", "agency:view", "agency:create", "agency:status",
"bd:view", "bd:create", "bd:update", "bd:view", "bd:create", "bd:update",
"coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit",
"coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete",
"lucky-gift:view", "lucky-gift:update", "lucky-gift:view", "lucky-gift:update",
"game:view", "game:create", "game:update", "game:status", "game:delete", "game:view", "game:create", "game:update", "game:status", "game:delete",
"daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status",
@ -498,7 +496,7 @@ func defaultRolePermissionCodes(code string) []string {
"upload:create", "upload:create",
} }
case "auditor": case "auditor":
return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-ledger:view", "coin-adjustment:view", "report:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view", "role:view", "permission:view", "job:view"} return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-ledger:view", "coin-adjustment:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view", "role:view", "permission:view", "job:view"}
case "readonly": case "readonly":
return []string{ return []string{
"overview:view", "overview:view",
@ -525,7 +523,6 @@ func defaultRolePermissionCodes(code string) []string {
"coin-seller:view", "coin-seller:view",
"coin-ledger:view", "coin-ledger:view",
"coin-adjustment:view", "coin-adjustment:view",
"report:view",
"lucky-gift:view", "lucky-gift:view",
"payment-bill:view", "payment-bill:view",
"payment-product:view", "payment-product:view",
@ -567,7 +564,7 @@ func defaultRolePermissionMigrationCodes(code string) []string {
"country:view", "country:create", "country:update", "country:status", "country:view", "country:create", "country:update", "country:status",
"region:view", "region:create", "region:update", "region:status", "region:view", "region:create", "region:update", "region:status",
"coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit",
"coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete",
"lucky-gift:view", "lucky-gift:update", "lucky-gift:view", "lucky-gift:update",
"game:view", "game:create", "game:update", "game:status", "game:delete", "game:view", "game:create", "game:update", "game:status", "game:delete",
"daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status",
@ -578,7 +575,7 @@ func defaultRolePermissionMigrationCodes(code string) []string {
"vip-config:view", "vip-config:update", "vip-config:grant", "vip-config:view", "vip-config:update", "vip-config:grant",
} }
case "auditor", "readonly": case "auditor", "readonly":
return []string{"level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-seller:view", "coin-ledger:view", "coin-adjustment:view", "report:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view"} return []string{"level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-seller:view", "coin-ledger:view", "coin-adjustment:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view"}
default: default:
return nil return nil
} }

View File

@ -27,7 +27,6 @@ import (
"hyapp-admin-server/internal/modules/redpacket" "hyapp-admin-server/internal/modules/redpacket"
"hyapp-admin-server/internal/modules/regionblock" "hyapp-admin-server/internal/modules/regionblock"
"hyapp-admin-server/internal/modules/registrationreward" "hyapp-admin-server/internal/modules/registrationreward"
reportmodule "hyapp-admin-server/internal/modules/report"
resourcemodule "hyapp-admin-server/internal/modules/resource" resourcemodule "hyapp-admin-server/internal/modules/resource"
"hyapp-admin-server/internal/modules/roomadmin" "hyapp-admin-server/internal/modules/roomadmin"
"hyapp-admin-server/internal/modules/roomtreasure" "hyapp-admin-server/internal/modules/roomtreasure"
@ -65,7 +64,6 @@ type Handlers struct {
Payment *payment.Handler Payment *payment.Handler
RBAC *rbac.Handler RBAC *rbac.Handler
RedPacket *redpacket.Handler RedPacket *redpacket.Handler
Report *reportmodule.Handler
RegistrationReward *registrationreward.Handler RegistrationReward *registrationreward.Handler
RegionBlock *regionblock.Handler RegionBlock *regionblock.Handler
Resource *resourcemodule.Handler Resource *resourcemodule.Handler
@ -95,7 +93,6 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine {
menu.RegisterRoutes(protected, h.Menu) menu.RegisterRoutes(protected, h.Menu)
rbac.RegisterRoutes(protected, h.RBAC) rbac.RegisterRoutes(protected, h.RBAC)
redpacket.RegisterRoutes(protected, h.RedPacket) redpacket.RegisterRoutes(protected, h.RedPacket)
reportmodule.RegisterRoutes(protected, h.Report)
resourcemodule.RegisterRoutes(protected, h.Resource) resourcemodule.RegisterRoutes(protected, h.Resource)
adminuser.RegisterRoutes(protected, h.AdminUser) adminuser.RegisterRoutes(protected, h.AdminUser)
appuser.RegisterRoutes(protected, h.AppUser) appuser.RegisterRoutes(protected, h.AppUser)

View File

@ -1,43 +0,0 @@
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED);
INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES
('举报列表查看', 'report:view', 'menu', '', @now_ms, @now_ms)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
kind = VALUES(kind),
description = VALUES(description),
updated_at_ms = @now_ms;
INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms) VALUES
(NULL, '运营管理', 'operations', '', 'operations', '', 68, TRUE, @now_ms, @now_ms)
ON DUPLICATE KEY UPDATE
title = VALUES(title),
path = VALUES(path),
icon = VALUES(icon),
permission_code = VALUES(permission_code),
sort = VALUES(sort),
visible = VALUES(visible),
updated_at_ms = @now_ms;
INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms)
SELECT parent.id, '举报列表', 'operation-reports', '/operations/reports', 'flag', 'report:view', 71, TRUE, @now_ms, @now_ms
FROM admin_menus parent
WHERE parent.code = 'operations'
ON DUPLICATE KEY UPDATE
parent_id = VALUES(parent_id),
title = VALUES(title),
path = VALUES(path),
icon = VALUES(icon),
permission_code = VALUES(permission_code),
sort = VALUES(sort),
visible = VALUES(visible),
updated_at_ms = @now_ms;
INSERT IGNORE INTO admin_role_permissions (role_id, permission_id)
SELECT admin_role.id, admin_permission.id
FROM admin_roles admin_role
JOIN admin_permissions admin_permission
WHERE admin_role.code IN ('platform-admin', 'ops-admin', 'auditor', 'readonly')
AND admin_permission.code = 'report:view';

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.ConsumeRedPacketWalletEvent(appcode.WithContext(ctx, event.AppCode), event) return broadcastSvc.ConsumeRedPacketCreatedEvent(appcode.WithContext(ctx, event.AppCode), event)
}); err != nil { }); err != nil {
_ = consumer.Shutdown() _ = consumer.Shutdown()
shutdownConsumers(mqConsumers) shutdownConsumers(mqConsumers)
@ -436,32 +436,25 @@ func firstRechargeEventFromWalletMessage(body []byte) (firstrechargedomain.Recha
}, true, nil }, true, nil
} }
func redPacketEventFromWalletMessage(body []byte) (broadcastdomain.RedPacketWalletEvent, bool, error) { func redPacketEventFromWalletMessage(body []byte) (broadcastdomain.RedPacketCreatedEvent, bool, error) {
message, err := walletmq.DecodeWalletOutboxMessage(body) message, err := walletmq.DecodeWalletOutboxMessage(body)
if err != nil { if err != nil {
return broadcastdomain.RedPacketWalletEvent{}, false, err return broadcastdomain.RedPacketCreatedEvent{}, false, err
} }
if message.EventType != "WalletRedPacketCreated" && message.EventType != "WalletRedPacketClaimed" && message.EventType != "WalletRedPacketRefunded" { if message.EventType != "WalletRedPacketCreated" {
return broadcastdomain.RedPacketWalletEvent{}, false, nil return broadcastdomain.RedPacketCreatedEvent{}, false, nil
} }
packetID, packetType, roomID, regionID, status, openAtMS, expiresAtMS := redPacketFieldsFromWalletPayload(message.PayloadJSON, message.EventType) regionID := redPacketRegionIDFromWalletPayload(message.PayloadJSON)
if packetID == "" { if regionID <= 0 {
return broadcastdomain.RedPacketWalletEvent{}, false, nil return broadcastdomain.RedPacketCreatedEvent{}, false, nil
} }
return broadcastdomain.RedPacketWalletEvent{ return broadcastdomain.RedPacketCreatedEvent{
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
} }
@ -487,41 +480,19 @@ func rechargeFactFromWalletPayload(payload string) (int64, string) {
return sequence, rechargeType return sequence, rechargeType
} }
func redPacketFieldsFromWalletPayload(payload string, eventType string) (string, string, string, int64, string, int64, int64) { func redPacketRegionIDFromWalletPayload(payload string) 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, "", 0, 0 return 0
} }
packetID := stringFromDecoded(decoded, "packet_id") switch value := decoded["region_id"].(type) {
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:
parsed, _ := value.Int64() regionID, _ := value.Int64()
return parsed return regionID
default: default:
return 0 return 0
} }

View File

@ -7,8 +7,6 @@ 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"
@ -76,23 +74,13 @@ type ConsumeRoomEventResult struct {
BroadcastCreated bool BroadcastCreated bool
} }
// RedPacketWalletEvent 是 wallet-service 已提交的红包资金事实。 // RedPacketCreatedEvent 是 wallet-service 已提交的红包创建事实。
type RedPacketWalletEvent struct { type RedPacketCreatedEvent 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,110 +2,24 @@ 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"
) )
// ConsumeRedPacketWalletEvent converts one MQ-delivered wallet red-packet fact into IM outbox records. // ConsumeRedPacketCreatedEvent converts one MQ-delivered wallet red-packet fact into a broadcast outbox.
func (s *Service) ConsumeRedPacketWalletEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error { func (s *Service) ConsumeRedPacketCreatedEvent(ctx context.Context, event broadcastdomain.RedPacketCreatedEvent) error {
if s == nil { if s == nil {
return nil return nil
} }
ctx = appcode.WithContext(ctx, event.AppCode) if event.RegionID <= 0 {
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
} }
if err := s.publishRedPacketRoomEvent(ctx, event, "CREATED", event.CreatedAtMS); err != nil { _, err := s.PublishRegionBroadcast(appcode.WithContext(ctx, event.AppCode), PublishInput{
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: redPacketIMPayload(event, eventName, "ROOM"), PayloadJSON: event.PayloadJSON,
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,7 +65,6 @@ type PublishInput struct {
BroadcastType string BroadcastType string
RegionID int64 RegionID int64
PayloadJSON string PayloadJSON string
DeliverAtMS int64
} }
// Service 拥有播报群建群补偿、播报入队和 outbox 投递决策。 // Service 拥有播报群建群补偿、播报入队和 outbox 投递决策。
@ -147,14 +146,6 @@ 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) {
@ -218,13 +209,9 @@ 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

@ -50,7 +50,6 @@ type UserSocialClient interface {
DeleteFriend(ctx context.Context, req *userv1.DeleteFriendRequest) (*userv1.DeleteFriendResponse, error) DeleteFriend(ctx context.Context, req *userv1.DeleteFriendRequest) (*userv1.DeleteFriendResponse, error)
ListFriends(ctx context.Context, req *userv1.ListFriendsRequest) (*userv1.ListFriendsResponse, error) ListFriends(ctx context.Context, req *userv1.ListFriendsRequest) (*userv1.ListFriendsResponse, error)
ListFriendApplications(ctx context.Context, req *userv1.ListFriendApplicationsRequest) (*userv1.ListFriendApplicationsResponse, error) ListFriendApplications(ctx context.Context, req *userv1.ListFriendApplicationsRequest) (*userv1.ListFriendApplicationsResponse, error)
SubmitReport(ctx context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error)
} }
// UserDeviceClient 抽象 gateway 对 user-service 设备 push token 的依赖。 // UserDeviceClient 抽象 gateway 对 user-service 设备 push token 的依赖。
@ -282,10 +281,6 @@ func (c *grpcUserSocialClient) ListFriendApplications(ctx context.Context, req *
return c.client.ListFriendApplications(ctx, req) return c.client.ListFriendApplications(ctx, req)
} }
func (c *grpcUserSocialClient) SubmitReport(ctx context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error) {
return c.client.SubmitReport(ctx, req)
}
func (c *grpcUserDeviceClient) BindPushToken(ctx context.Context, req *userv1.BindPushTokenRequest) (*userv1.BindPushTokenResponse, error) { func (c *grpcUserDeviceClient) BindPushToken(ctx context.Context, req *userv1.BindPushTokenRequest) (*userv1.BindPushTokenResponse, error) {
return c.client.BindPushToken(ctx, req) return c.client.BindPushToken(ctx, req)
} }

View File

@ -84,7 +84,6 @@ type UserHandlers struct {
ListMyResources http.HandlerFunc ListMyResources http.HandlerFunc
EquipMyResource http.HandlerFunc EquipMyResource http.HandlerFunc
UserSocialAction http.HandlerFunc UserSocialAction http.HandlerFunc
SubmitReport http.HandlerFunc
} }
type ManagerHandlers struct { type ManagerHandlers struct {
@ -176,14 +175,6 @@ 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 {
@ -303,7 +294,6 @@ func (r routes) registerUserRoutes() {
r.profile("/users/me", "", h.GetMyProfile) r.profile("/users/me", "", h.GetMyProfile)
r.profile("/users/me/resources", "", h.ListMyResources) r.profile("/users/me/resources", "", h.ListMyResources)
r.profile("/users/me/resources/{resource_id}/equip", "", h.EquipMyResource) r.profile("/users/me/resources/{resource_id}/equip", "", h.EquipMyResource)
r.profile("/reports", http.MethodPost, h.SubmitReport)
r.profile("/users/{user_id}/{action...}", "", h.UserSocialAction) r.profile("/users/{user_id}/{action...}", "", h.UserSocialAction)
} }
@ -404,15 +394,6 @@ 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,7 +39,6 @@ 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

@ -6,7 +6,6 @@ import (
"hyapp/services/gateway-service/internal/transport/http/httpkit" "hyapp/services/gateway-service/internal/transport/http/httpkit"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings"
"testing" "testing"
userv1 "hyapp.local/api/proto/user/v1" userv1 "hyapp.local/api/proto/user/v1"
@ -24,7 +23,6 @@ type fakeUserSocialClient struct {
lastDelete *userv1.DeleteFriendRequest lastDelete *userv1.DeleteFriendRequest
lastFriends *userv1.ListFriendsRequest lastFriends *userv1.ListFriendsRequest
lastFriendApps *userv1.ListFriendApplicationsRequest lastFriendApps *userv1.ListFriendApplicationsRequest
lastReport *userv1.SubmitReportRequest
} }
func (f *fakeUserSocialClient) RecordProfileVisit(_ context.Context, req *userv1.RecordProfileVisitRequest) (*userv1.RecordProfileVisitResponse, error) { func (f *fakeUserSocialClient) RecordProfileVisit(_ context.Context, req *userv1.RecordProfileVisitRequest) (*userv1.RecordProfileVisitResponse, error) {
@ -77,26 +75,6 @@ func (f *fakeUserSocialClient) ListFriendApplications(_ context.Context, req *us
return &userv1.ListFriendApplicationsResponse{Applications: []*userv1.FriendApplication{{RequesterUserId: 10002, TargetUserId: req.GetUserId(), Status: "pending", CreatedAtMs: 9000, UpdatedAtMs: 9000}}, Total: 1}, nil return &userv1.ListFriendApplicationsResponse{Applications: []*userv1.FriendApplication{{RequesterUserId: 10002, TargetUserId: req.GetUserId(), Status: "pending", CreatedAtMs: 9000, UpdatedAtMs: 9000}}, Total: 1}, nil
} }
func (f *fakeUserSocialClient) SubmitReport(_ context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error) {
f.lastReport = req
targetType := "user"
if req.GetRoomId() != "" {
targetType = "room"
}
return &userv1.SubmitReportResponse{Report: &userv1.UserReport{
ReportId: "rpt-test",
ReporterUserId: req.GetReporterUserId(),
TargetType: targetType,
UserId: req.GetUserId(),
RoomId: req.GetRoomId(),
ReportType: req.GetReportType(),
Reason: req.GetReason(),
ImageUrls: req.GetImageUrls(),
Status: "submitted",
CreatedAtMs: 1778000000000,
}}, nil
}
func TestFriendApplicationFlowRoutesUseBackendThenIMOwnedReminder(t *testing.T) { func TestFriendApplicationFlowRoutesUseBackendThenIMOwnedReminder(t *testing.T) {
socialClient := &fakeUserSocialClient{} socialClient := &fakeUserSocialClient{}
handler := NewHandler(&fakeRoomClient{}) handler := NewHandler(&fakeRoomClient{})
@ -126,28 +104,6 @@ func TestFriendApplicationFlowRoutesUseBackendThenIMOwnedReminder(t *testing.T)
} }
} }
func TestSubmitReportRoutePassesAuthenticatedReporterAndSingleTarget(t *testing.T) {
socialClient := &fakeUserSocialClient{}
handler := NewHandler(&fakeRoomClient{})
handler.SetUserSocialClient(socialClient)
router := handler.Routes(auth.NewVerifier("secret"))
request := httptest.NewRequest(http.MethodPost, "/api/v1/reports", strings.NewReader(`{"user_id":"10002","report_type":"pornography","reason":"bad","image_urls":["https://cdn.example.com/a.png"]}`))
request.Header.Set("Authorization", "Bearer "+signGatewayTokenWithProfile(t, "secret", 10001, true))
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
if response.Code != http.StatusOK {
t.Fatalf("submit report status = %d body=%s", response.Code, response.Body.String())
}
if socialClient.lastReport == nil || socialClient.lastReport.GetReporterUserId() != 10001 || socialClient.lastReport.GetUserId() != 10002 || socialClient.lastReport.GetRoomId() != "" || socialClient.lastReport.GetReportType() != "pornography" {
t.Fatalf("submit report request mismatch: %+v", socialClient.lastReport)
}
var envelope httpkit.ResponseEnvelope
if err := json.Unmarshal(response.Body.Bytes(), &envelope); err != nil || envelope.Code != httpkit.CodeOK {
t.Fatalf("invalid envelope: %+v err=%v", envelope, err)
}
}
func TestSocialListRoutesPassPagingAndDirection(t *testing.T) { func TestSocialListRoutesPassPagingAndDirection(t *testing.T) {
socialClient := &fakeUserSocialClient{} socialClient := &fakeUserSocialClient{}
handler := NewHandler(&fakeRoomClient{}) handler := NewHandler(&fakeRoomClient{})

View File

@ -61,7 +61,6 @@ func (h *Handler) UserHandlers() httproutes.UserHandlers {
ListMyFriendApplications: h.listMyFriendApplications, ListMyFriendApplications: h.listMyFriendApplications,
GetMyProfile: h.getMyProfile, GetMyProfile: h.getMyProfile,
UserSocialAction: h.userSocialAction, UserSocialAction: h.userSocialAction,
SubmitReport: h.submitReport,
} }
} }

View File

@ -1,94 +0,0 @@
package userapi
import (
"encoding/json"
"net/http"
"strconv"
"strings"
userv1 "hyapp.local/api/proto/user/v1"
"hyapp/services/gateway-service/internal/auth"
"hyapp/services/gateway-service/internal/transport/http/httpkit"
)
type submitReportData struct {
ReportID string `json:"report_id"`
TargetType string `json:"target_type"`
UserID string `json:"user_id"`
RoomID string `json:"room_id"`
ReportType string `json:"report_type"`
Status string `json:"status"`
CreatedAtMs int64 `json:"created_at_ms"`
}
// submitReport 只把当前登录用户和举报目标透传到 user-service举报发起人不能来自客户端 body。
func (h *Handler) submitReport(writer http.ResponseWriter, request *http.Request) {
if h.userSocialClient == nil {
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
return
}
var body struct {
UserID json.RawMessage `json:"user_id"`
RoomID string `json:"room_id"`
ReportType string `json:"report_type"`
Reason string `json:"reason"`
ImageURLs []string `json:"image_urls"`
}
if !httpkit.Decode(writer, request, &body) {
return
}
userID, ok := optionalReportUserID(body.UserID)
if !ok {
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
return
}
resp, err := h.userSocialClient.SubmitReport(request.Context(), &userv1.SubmitReportRequest{
Meta: httpkit.UserMeta(request, ""),
ReporterUserId: auth.UserIDFromContext(request.Context()),
UserId: userID,
RoomId: body.RoomID,
ReportType: body.ReportType,
Reason: body.Reason,
ImageUrls: body.ImageURLs,
})
if err != nil {
httpkit.WriteRPCError(writer, request, err)
return
}
httpkit.WriteOK(writer, request, reportData(resp.GetReport()))
}
func optionalReportUserID(raw json.RawMessage) (int64, bool) {
if len(raw) == 0 || string(raw) == "null" {
return 0, true
}
var text string
if err := json.Unmarshal(raw, &text); err == nil {
text = strings.TrimSpace(text)
if text == "" {
return 0, true
}
value, err := strconv.ParseInt(text, 10, 64)
return value, err == nil && value > 0
}
var value int64
if err := json.Unmarshal(raw, &value); err != nil {
return 0, false
}
return value, value > 0
}
func reportData(report *userv1.UserReport) submitReportData {
if report == nil {
return submitReportData{}
}
return submitReportData{
ReportID: report.GetReportId(),
TargetType: report.GetTargetType(),
UserID: httpkit.UserIDString(report.GetUserId()),
RoomID: report.GetRoomId(),
ReportType: report.GetReportType(),
Status: report.GetStatus(),
CreatedAtMs: report.GetCreatedAtMs(),
}
}

View File

@ -11,7 +11,6 @@ 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
@ -20,7 +19,6 @@ 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
@ -30,7 +28,6 @@ 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,
@ -55,14 +52,6 @@ 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,18 +1,14 @@
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"
) )
@ -26,7 +22,6 @@ 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"`
} }
@ -118,377 +113,6 @@ 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")
@ -681,7 +305,6 @@ 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(),
} }
@ -753,165 +376,3 @@ 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

@ -146,28 +146,6 @@ CREATE TABLE IF NOT EXISTS user_status_change_logs (
KEY idx_user_status_logs_request (app_code, request_id) KEY idx_user_status_logs_request (app_code, request_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户状态变更日志表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户状态变更日志表';
CREATE TABLE IF NOT EXISTS user_reports (
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
report_id VARCHAR(96) NOT NULL COMMENT '举报单业务 ID',
reporter_user_id BIGINT NOT NULL COMMENT '举报发起人用户 ID',
target_type VARCHAR(16) NOT NULL COMMENT '举报目标类型user/room',
target_user_id BIGINT NULL COMMENT '被举报用户 ID',
room_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '被举报房间 ID',
report_type VARCHAR(64) NOT NULL COMMENT '举报类型',
reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '举报理由',
image_urls_json JSON NOT NULL COMMENT '举报图片 URL 数组',
status VARCHAR(32) NOT NULL COMMENT '处理状态',
request_id VARCHAR(96) NOT NULL DEFAULT '' COMMENT '链路请求 ID用于排查问题',
created_at_ms BIGINT NOT NULL COMMENT '创建时间UTC epoch ms',
updated_at_ms BIGINT NOT NULL COMMENT '更新时间UTC epoch ms',
PRIMARY KEY (app_code, report_id),
KEY idx_user_reports_reporter (app_code, reporter_user_id, created_at_ms),
KEY idx_user_reports_target_user (app_code, target_user_id, created_at_ms),
KEY idx_user_reports_room (app_code, room_id, created_at_ms),
KEY idx_user_reports_status (app_code, status, created_at_ms),
KEY idx_user_reports_request (app_code, request_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户举报表';
CREATE TABLE IF NOT EXISTS display_user_id_sequences ( CREATE TABLE IF NOT EXISTS display_user_id_sequences (
app_code VARCHAR(32) NOT NULL PRIMARY KEY COMMENT '应用编码,用于多租户隔离', app_code VARCHAR(32) NOT NULL PRIMARY KEY COMMENT '应用编码,用于多租户隔离',
next_value BIGINT NOT NULL COMMENT '下一个值', next_value BIGINT NOT NULL COMMENT '下一个值',

View File

@ -192,12 +192,6 @@ const (
FriendApplicationStatusPending = "pending" FriendApplicationStatusPending = "pending"
// FriendApplicationStatusAccepted 表示好友申请已同意。 // FriendApplicationStatusAccepted 表示好友申请已同意。
FriendApplicationStatusAccepted = "accepted" FriendApplicationStatusAccepted = "accepted"
// ReportTargetUser 表示举报目标是单个用户。
ReportTargetUser = "user"
// ReportTargetRoom 表示举报目标是单个房间。
ReportTargetRoom = "room"
// ReportStatusSubmitted 表示举报单已提交,等待后台治理流程处理。
ReportStatusSubmitted = "submitted"
) )
// ProfileVisitRecord 是访问某个用户主页的去重记录。 // ProfileVisitRecord 是访问某个用户主页的去重记录。
@ -231,36 +225,6 @@ type FriendApplication struct {
UpdatedAtMs int64 UpdatedAtMs int64
} }
// Report 是 App 用户提交的治理举报事实。
type Report struct {
// AppCode 是举报所属 App后台治理只在同 App 范围内处理。
AppCode string
// ReportID 是举报单不可变业务 ID。
ReportID string
// ReporterUserID 是举报发起人,只能来自已认证 token。
ReporterUserID int64
// TargetType 区分 user/room避免靠空字段猜测举报对象。
TargetType string
// UserID 是被举报用户 ID房间举报时为 0。
UserID int64
// RoomID 是被举报房间 ID用户举报时为空。
RoomID string
// ReportType 是客户端选择的稳定举报类型枚举。
ReportType string
// Reason 是用户补充的举报理由。
Reason string
// ImageURLs 是用户上传后的举报图片 URL 快照。
ImageURLs []string
// Status 是举报单当前处理状态。
Status string
// RequestID 是提交举报时的 gateway 链路追踪 ID。
RequestID string
// CreatedAtMs 是举报创建时间UTC epoch ms。
CreatedAtMs int64
// UpdatedAtMs 是举报最后更新时间UTC epoch ms。
UpdatedAtMs int64
}
// BusinessUserLookupItem 是带业务场景权限裁剪后的用户查询结果。 // BusinessUserLookupItem 是带业务场景权限裁剪后的用户查询结果。
// 它只包含选择目标用户所需字段,不能扩展手机号、登录身份或钱包资产。 // 它只包含选择目标用户所需字段,不能扩展手机号、登录身份或钱包资产。
type BusinessUserLookupItem struct { type BusinessUserLookupItem struct {

View File

@ -105,7 +105,6 @@ type fakeModerationRepository struct {
user userdomain.User user userdomain.User
sessionIDs []string sessionIDs []string
lastCommand UserStatusCommand lastCommand UserStatusCommand
lastReport userdomain.Report
} }
func (r *fakeModerationRepository) SetUserStatus(_ context.Context, command UserStatusCommand) (UserStatusPersistenceResult, error) { func (r *fakeModerationRepository) SetUserStatus(_ context.Context, command UserStatusCommand) (UserStatusPersistenceResult, error) {
@ -162,11 +161,6 @@ func (r *fakeModerationRepository) ListFriendApplications(context.Context, int64
return nil, 0, nil return nil, 0, nil
} }
func (r *fakeModerationRepository) SubmitReport(_ context.Context, report userdomain.Report) (userdomain.Report, error) {
r.lastReport = report
return report, nil
}
func (r *fakeModerationRepository) BusinessUserLookup(context.Context, string, bool, int32) ([]userdomain.BusinessUserLookupItem, error) { func (r *fakeModerationRepository) BusinessUserLookup(context.Context, string, bool, int32) ([]userdomain.BusinessUserLookupItem, error) {
return nil, nil return nil, nil
} }

View File

@ -1,117 +0,0 @@
package user
import (
"context"
"net/url"
"strings"
"unicode/utf8"
"hyapp/pkg/idgen"
"hyapp/pkg/roomid"
"hyapp/pkg/xerr"
userdomain "hyapp/services/user-service/internal/domain/user"
)
const (
maxReportReasonRunes = 512
maxReportImages = 9
maxReportImageURLLen = 1024
)
var validReportTypes = map[string]struct{}{
"politics": {},
"pornography": {},
"graphic_violence": {},
"verbal_abuse": {},
"fraud": {},
"illegal_content": {},
"minors": {},
"in_person_transactions": {},
"other": {},
}
// SubmitReport 校验 App 举报目标和证据 URL并把不可变举报事实交给 user repository 持久化。
func (s *Service) SubmitReport(ctx context.Context, reporterUserID int64, userID int64, roomID string, reportType string, reason string, imageURLs []string, requestID string) (userdomain.Report, error) {
if s.userRepository == nil {
return userdomain.Report{}, xerr.New(xerr.Unavailable, "user repository is not configured")
}
reporterUserID, userID, roomID, reportType, reason, imageURLs, targetType, err := normalizeReportInput(reporterUserID, userID, roomID, reportType, reason, imageURLs)
if err != nil {
return userdomain.Report{}, err
}
nowMs := s.now().UnixMilli()
report := userdomain.Report{
ReportID: idgen.New("rpt"),
ReporterUserID: reporterUserID,
TargetType: targetType,
UserID: userID,
RoomID: roomID,
ReportType: reportType,
Reason: reason,
ImageURLs: imageURLs,
Status: userdomain.ReportStatusSubmitted,
RequestID: strings.TrimSpace(requestID),
CreatedAtMs: nowMs,
UpdatedAtMs: nowMs,
}
return s.userRepository.SubmitReport(ctx, report)
}
func normalizeReportInput(reporterUserID int64, userID int64, roomID string, reportType string, reason string, imageURLs []string) (int64, int64, string, string, string, []string, string, error) {
roomID = strings.TrimSpace(roomID)
reportType = strings.ToLower(strings.TrimSpace(reportType))
reason = strings.TrimSpace(reason)
if reporterUserID <= 0 {
return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "reporter_user_id is required")
}
hasUser := userID > 0
hasRoom := roomID != ""
if hasUser == hasRoom {
return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "user_id and room_id must be exactly one")
}
if hasUser && userID == reporterUserID {
return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "cannot report self")
}
targetType := userdomain.ReportTargetUser
if hasRoom {
if !roomid.ValidStringID(roomID) {
return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "room_id is invalid")
}
targetType = userdomain.ReportTargetRoom
}
if _, ok := validReportTypes[reportType]; !ok {
return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "report_type is invalid")
}
if utf8.RuneCountInString(reason) > maxReportReasonRunes {
return 0, 0, "", "", "", nil, "", xerr.New(xerr.InvalidArgument, "reason is too long")
}
normalizedImages, err := normalizeReportImageURLs(imageURLs)
if err != nil {
return 0, 0, "", "", "", nil, "", err
}
return reporterUserID, userID, roomID, reportType, reason, normalizedImages, targetType, nil
}
func normalizeReportImageURLs(imageURLs []string) ([]string, error) {
if len(imageURLs) > maxReportImages {
return nil, xerr.New(xerr.InvalidArgument, "too many report images")
}
normalized := make([]string, 0, len(imageURLs))
for _, raw := range imageURLs {
value := strings.TrimSpace(raw)
if value == "" {
continue
}
if len(value) > maxReportImageURLLen {
return nil, xerr.New(xerr.InvalidArgument, "image url is too long")
}
parsed, err := url.Parse(value)
if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") {
return nil, xerr.New(xerr.InvalidArgument, "image url is invalid")
}
normalized = append(normalized, value)
}
return normalized, nil
}

View File

@ -1,47 +0,0 @@
package user
import (
"context"
"testing"
"time"
"hyapp/pkg/appcode"
userdomain "hyapp/services/user-service/internal/domain/user"
)
func TestSubmitReportAcceptsExactlyOneTarget(t *testing.T) {
repository := &fakeModerationRepository{}
svc := New(repository, WithClock(func() time.Time { return time.UnixMilli(1_778_000_000_000).UTC() }))
ctx := appcode.WithContext(context.Background(), "lalu")
report, err := svc.SubmitReport(ctx, 10001, 10002, "", " Pornography ", " reason ", []string{" https://cdn.example.com/report.png "}, "req-report")
if err != nil {
t.Fatalf("SubmitReport failed: %v", err)
}
if report.ReportID == "" || report.TargetType != userdomain.ReportTargetUser || report.ReportType != "pornography" || report.Reason != "reason" || len(report.ImageURLs) != 1 {
t.Fatalf("report normalization mismatch: %+v", report)
}
if repository.lastReport.ReporterUserID != 10001 || repository.lastReport.UserID != 10002 || repository.lastReport.RequestID != "req-report" || repository.lastReport.CreatedAtMs != 1_778_000_000_000 {
t.Fatalf("repository report mismatch: %+v", repository.lastReport)
}
}
func TestSubmitReportRejectsMissingOrMixedTarget(t *testing.T) {
svc := New(&fakeModerationRepository{})
ctx := appcode.WithContext(context.Background(), "lalu")
tests := []struct {
name string
userID int64
roomID string
}{
{name: "missing target"},
{name: "mixed target", userID: 10002, roomID: "room_1"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if _, err := svc.SubmitReport(ctx, 10001, test.userID, test.roomID, "fraud", "", nil, "req-report"); err == nil {
t.Fatalf("SubmitReport must reject target combination user=%d room=%q", test.userID, test.roomID)
}
})
}
}

View File

@ -37,8 +37,6 @@ type UserRepository interface {
ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error)
// ListFriendApplications 分页读取好友申请。 // ListFriendApplications 分页读取好友申请。
ListFriendApplications(ctx context.Context, userID int64, direction string, page int32, pageSize int32) ([]userdomain.FriendApplication, int64, error) ListFriendApplications(ctx context.Context, userID int64, direction string, page int32, pageSize int32) ([]userdomain.FriendApplication, int64, error)
// SubmitReport 持久化 App 用户提交的治理举报单。
SubmitReport(ctx context.Context, report userdomain.Report) (userdomain.Report, error)
// BusinessUserLookup 按已授权业务场景查询目标用户,返回字段必须保持最小化。 // BusinessUserLookup 按已授权业务场景查询目标用户,返回字段必须保持最小化。
BusinessUserLookup(ctx context.Context, keyword string, numericKeyword bool, pageSize int32) ([]userdomain.BusinessUserLookupItem, error) BusinessUserLookup(ctx context.Context, keyword string, numericKeyword bool, pageSize int32) ([]userdomain.BusinessUserLookupItem, error)
// BatchGetUsers 批量查询用户主状态,缺失用户不应返回占位对象。 // BatchGetUsers 批量查询用户主状态,缺失用户不应返回占位对象。

View File

@ -1,51 +0,0 @@
package user
import (
"context"
"encoding/json"
"hyapp/pkg/appcode"
userdomain "hyapp/services/user-service/internal/domain/user"
)
// SubmitReport 持久化举报事实。用户目标会校验同 App 存在;房间目标只记录 room_id不回查 room-service 明细表。
func (r *Repository) SubmitReport(ctx context.Context, report userdomain.Report) (userdomain.Report, error) {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return userdomain.Report{}, err
}
defer tx.Rollback()
appCode := appcode.FromContext(ctx)
if err := ensureSocialUser(ctx, tx, appCode, report.ReporterUserID); err != nil {
return userdomain.Report{}, err
}
if report.UserID > 0 {
if err := ensureSocialUser(ctx, tx, appCode, report.UserID); err != nil {
return userdomain.Report{}, err
}
}
imageURLsJSON, err := json.Marshal(report.ImageURLs)
if err != nil {
return userdomain.Report{}, err
}
if _, err := tx.ExecContext(ctx, `
INSERT INTO user_reports (
app_code, report_id, reporter_user_id, target_type, target_user_id, room_id,
report_type, reason, image_urls_json, status, request_id, created_at_ms, updated_at_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
appCode, report.ReportID, report.ReporterUserID, report.TargetType, nullableReportUserID(report.UserID), report.RoomID,
report.ReportType, report.Reason, string(imageURLsJSON), report.Status, report.RequestID, report.CreatedAtMs, report.UpdatedAtMs,
); err != nil {
return userdomain.Report{}, err
}
report.AppCode = appCode
return report, tx.Commit()
}
func nullableReportUserID(userID int64) any {
if userID <= 0 {
return nil
}
return userID
}

View File

@ -111,10 +111,6 @@ func (r *Repository) ListFriendApplications(ctx context.Context, userID int64, d
return r.Repository.UserRepository().ListFriendApplications(ctx, userID, direction, page, pageSize) return r.Repository.UserRepository().ListFriendApplications(ctx, userID, direction, page, pageSize)
} }
func (r *Repository) SubmitReport(ctx context.Context, report userdomain.Report) (userdomain.Report, error) {
return r.Repository.UserRepository().SubmitReport(ctx, report)
}
// BusinessUserLookup 让测试 wrapper 继续满足业务场景用户查询接口。 // BusinessUserLookup 让测试 wrapper 继续满足业务场景用户查询接口。
func (r *Repository) BusinessUserLookup(ctx context.Context, keyword string, numericKeyword bool, pageSize int32) ([]userdomain.BusinessUserLookupItem, error) { func (r *Repository) BusinessUserLookup(ctx context.Context, keyword string, numericKeyword bool, pageSize int32) ([]userdomain.BusinessUserLookupItem, error) {
return r.Repository.UserRepository().BusinessUserLookup(ctx, keyword, numericKeyword, pageSize) return r.Repository.UserRepository().BusinessUserLookup(ctx, keyword, numericKeyword, pageSize)

View File

@ -176,21 +176,6 @@ func toProtoFriendApplication(application userdomain.FriendApplication) *userv1.
} }
} }
func toProtoUserReport(report userdomain.Report) *userv1.UserReport {
return &userv1.UserReport{
ReportId: report.ReportID,
ReporterUserId: report.ReporterUserID,
TargetType: report.TargetType,
UserId: report.UserID,
RoomId: report.RoomID,
ReportType: report.ReportType,
Reason: report.Reason,
ImageUrls: append([]string{}, report.ImageURLs...),
Status: report.Status,
CreatedAtMs: report.CreatedAtMs,
}
}
// toProtoCountry 把国家领域对象转换为管理 RPC 响应。 // toProtoCountry 把国家领域对象转换为管理 RPC 响应。
func toProtoCountry(country userdomain.Country) *userv1.Country { func toProtoCountry(country userdomain.Country) *userv1.Country {
return &userv1.Country{ return &userv1.Country{

View File

@ -408,16 +408,6 @@ func (s *Server) ListFriendApplications(ctx context.Context, req *userv1.ListFri
return resp, nil return resp, nil
} }
// SubmitReport 记录 App 用户提交的用户或房间举报。
func (s *Server) SubmitReport(ctx context.Context, req *userv1.SubmitReportRequest) (*userv1.SubmitReportResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
report, err := s.userSvc.SubmitReport(ctx, req.GetReporterUserId(), req.GetUserId(), req.GetRoomId(), req.GetReportType(), req.GetReason(), req.GetImageUrls(), req.GetMeta().GetRequestId())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
return &userv1.SubmitReportResponse{Report: toProtoUserReport(report)}, nil
}
// BatchGetUsers 批量返回用户主状态,缺失用户不填充占位对象。 // BatchGetUsers 批量返回用户主状态,缺失用户不填充占位对象。
func (s *Server) BatchGetUsers(ctx context.Context, req *userv1.BatchGetUsersRequest) (*userv1.BatchGetUsersResponse, error) { func (s *Server) BatchGetUsers(ctx context.Context, req *userv1.BatchGetUsersRequest) (*userv1.BatchGetUsersResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta()) ctx = contextWithApp(ctx, req.GetMeta())

View File

@ -666,9 +666,8 @@ 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 86400 COMMENT '红包过期秒数,房内红包固定 24 小时', expire_seconds INT NOT NULL DEFAULT 300 COMMENT '红包过期秒数',
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,9 +80,6 @@ const (
RedPacketStatusRefunded = "refunded" RedPacketStatusRefunded = "refunded"
// RedPacketClaimStatusClaimed 表示红包份额领取成功。 // RedPacketClaimStatusClaimed 表示红包份额领取成功。
RedPacketClaimStatusClaimed = "claimed" RedPacketClaimStatusClaimed = "claimed"
// RedPacketExpireSeconds 固定房内红包 24 小时过期退款。
RedPacketExpireSeconds int32 = 24 * 60 * 60
) )
// DebitGiftCommand 是 room-service 送礼扣费的账务命令。 // DebitGiftCommand 是 room-service 送礼扣费的账务命令。
@ -609,7 +606,6 @@ 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
} }
@ -635,9 +631,6 @@ type RedPacket struct {
ClaimedByViewer bool ClaimedByViewer bool
ViewerClaimAmount int64 ViewerClaimAmount int64
Claims []RedPacketClaim Claims []RedPacketClaim
RefundAmount int64
RefundStatus string
RefundedAtMS int64
} }
// RedPacketClaim 是单个用户抢红包的到账事实。 // RedPacketClaim 是单个用户抢红包的到账事实。
@ -680,15 +673,9 @@ 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
@ -809,12 +796,6 @@ 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,23 +28,18 @@ 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.DailySendLimit < 0 { if config.DelayedOpenSeconds < 0 || config.ExpireSeconds <= 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 >= ledger.RedPacketExpireSeconds { if config.DelayedOpenSeconds >= config.ExpireSeconds {
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)
} }
@ -132,19 +127,6 @@ 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,7 +32,6 @@ 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,12 +58,6 @@ 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"`
@ -75,11 +69,7 @@ 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"`
@ -120,23 +110,21 @@ 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, rule_url, 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, 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,
ledger.RedPacketExpireSeconds, config.ExpireSeconds,
config.DailySendLimit, config.DailySendLimit,
strings.TrimSpace(config.RuleURL),
config.UpdatedByAdminID, config.UpdatedByAdminID,
nowMS, nowMS,
nowMS, nowMS,
@ -214,7 +202,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 ledger.RedPacketExpireSeconds <= 0 || (command.PacketType == ledger.RedPacketTypeDelayed && ledger.RedPacketExpireSeconds <= config.DelayedOpenSeconds) { if config.ExpireSeconds <= 0 || (command.PacketType == ledger.RedPacketTypeDelayed && config.ExpireSeconds <= 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 {
@ -241,7 +229,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(ledger.RedPacketExpireSeconds)*1000 expiresAtMS := nowMS + int64(config.ExpireSeconds)*1000
balanceAfter := sender.AvailableAmount - command.TotalAmount balanceAfter := sender.AvailableAmount - command.TotalAmount
metadata := redPacketMetadata{ metadata := redPacketMetadata{
AppCode: command.AppCode, AppCode: command.AppCode,
@ -334,11 +322,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 existingClaim, claimed, err := r.getUserRedPacketClaim(ctx, tx, command.PacketID, command.UserID); err != nil || claimed { if claimed, err := r.userClaimedRedPacket(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{Claim: existingClaim, Packet: packet}, nil return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.RedPacketAlreadyClaimed, "red packet already claimed")
} }
allocationNo, amount, err := r.lockOneRedPacketAllocation(ctx, tx, command.PacketID) allocationNo, amount, err := r.lockOneRedPacketAllocation(ctx, tx, command.PacketID)
if err != nil { if err != nil {
@ -362,11 +350,6 @@ 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,
@ -393,14 +376,7 @@ 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) {
existingClaim, claimed, lookupErr := r.getUserRedPacketClaim(ctx, tx, command.PacketID, command.UserID) return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.RedPacketAlreadyClaimed, "red packet already claimed")
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
} }
@ -419,7 +395,6 @@ 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 = ?
@ -437,11 +412,7 @@ 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
} }
packet.RemainingAmount = newRemainingAmount return ledger.RedPacketClaimReceipt{Claim: redPacketClaimFromMetadata(metadata), BalanceAfter: balanceAfter}, nil
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) {
@ -487,9 +458,6 @@ 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
@ -512,15 +480,6 @@ 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 {
@ -582,30 +541,6 @@ 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 {
@ -653,11 +588,7 @@ 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,
@ -718,12 +649,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, COALESCE(rule_url, ''), created_at_ms, updated_at_ms updated_by_admin_id, 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: ledger.RedPacketExpireSeconds} config := ledger.RedPacketConfig{AppCode: appcode.FromContext(ctx), ExpireSeconds: 300}
if err := row.Scan( if err := row.Scan(
&config.AppCode, &config.AppCode,
&config.Enabled, &config.Enabled,
@ -731,7 +662,6 @@ 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 {
@ -740,7 +670,6 @@ 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
@ -940,32 +869,19 @@ func scanRedPacket(row redPacketScanner) (ledger.RedPacket, error) {
return packet, err return packet, err
} }
func (r *Repository) getUserRedPacketClaim(ctx context.Context, tx *sql.Tx, packetID string, userID int64) (ledger.RedPacketClaim, bool, error) { func (r *Repository) userClaimedRedPacket(ctx context.Context, tx *sql.Tx, packetID string, userID int64) (bool, error) {
var claim ledger.RedPacketClaim var claimID string
err := tx.QueryRowContext(ctx, ` err := tx.QueryRowContext(ctx, `
SELECT app_code, claim_id, command_id, packet_id, user_id, amount, wallet_transaction_id, SELECT claim_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( ).Scan(&claimID)
&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 ledger.RedPacketClaim{}, false, nil return false, nil
} }
return claim, err == nil, err return 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) {
@ -1028,50 +944,6 @@ 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,
@ -1143,11 +1015,7 @@ 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
} }
packet, err := r.getRedPacket(ctx, tx, metadata.PacketID) return ledger.RedPacketClaimReceipt{Claim: redPacketClaimFromMetadata(metadata), BalanceAfter: metadata.BalanceAfter}, nil
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,7 +33,6 @@ 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)
@ -81,7 +80,6 @@ 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
} }
@ -132,19 +130,6 @@ 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,
@ -157,7 +142,6 @@ 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,
} }
} }
@ -186,9 +170,6 @@ 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,
} }
} }