feat: switch gateway to common grpc proto
This commit is contained in:
parent
5be5435bb0
commit
92edaabff7
@ -1,645 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.34.2
|
|
||||||
// protoc v5.29.2
|
|
||||||
// source: gateway.proto
|
|
||||||
|
|
||||||
package gatewaypb
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type LoginRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
LoginType string `protobuf:"bytes,1,opt,name=login_type,json=loginType,proto3" json:"login_type,omitempty"`
|
|
||||||
Account string `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"`
|
|
||||||
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
|
|
||||||
CountryCode string `protobuf:"bytes,4,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
|
||||||
VerifyCode string `protobuf:"bytes,5,opt,name=verify_code,json=verifyCode,proto3" json:"verify_code,omitempty"`
|
|
||||||
Provider string `protobuf:"bytes,6,opt,name=provider,proto3" json:"provider,omitempty"`
|
|
||||||
ProviderToken string `protobuf:"bytes,7,opt,name=provider_token,json=providerToken,proto3" json:"provider_token,omitempty"`
|
|
||||||
DeviceId string `protobuf:"bytes,8,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
|
|
||||||
Platform string `protobuf:"bytes,9,opt,name=platform,proto3" json:"platform,omitempty"`
|
|
||||||
AppVersion string `protobuf:"bytes,10,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) Reset() {
|
|
||||||
*x = LoginRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_gateway_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*LoginRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *LoginRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_gateway_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*LoginRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_gateway_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetLoginType() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.LoginType
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetAccount() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Account
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetPassword() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Password
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetCountryCode() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.CountryCode
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetVerifyCode() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.VerifyCode
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetProvider() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Provider
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetProviderToken() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ProviderToken
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetDeviceId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.DeviceId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetPlatform() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Platform
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginRequest) GetAppVersion() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.AppVersion
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserProfile struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
|
||||||
Nickname string `protobuf:"bytes,2,opt,name=nickname,proto3" json:"nickname,omitempty"`
|
|
||||||
AvatarUrl string `protobuf:"bytes,3,opt,name=avatar_url,json=avatarUrl,proto3" json:"avatar_url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProfile) Reset() {
|
|
||||||
*x = UserProfile{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_gateway_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProfile) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*UserProfile) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *UserProfile) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_gateway_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use UserProfile.ProtoReflect.Descriptor instead.
|
|
||||||
func (*UserProfile) Descriptor() ([]byte, []int) {
|
|
||||||
return file_gateway_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProfile) GetUserId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.UserId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProfile) GetNickname() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Nickname
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *UserProfile) GetAvatarUrl() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.AvatarUrl
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginResponse struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
|
||||||
AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
|
||||||
RefreshToken string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
|
|
||||||
ExpiresIn int64 `protobuf:"varint,4,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"`
|
|
||||||
IsNewUser bool `protobuf:"varint,5,opt,name=is_new_user,json=isNewUser,proto3" json:"is_new_user,omitempty"`
|
|
||||||
Profile *UserProfile `protobuf:"bytes,6,opt,name=profile,proto3" json:"profile,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) Reset() {
|
|
||||||
*x = LoginResponse{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_gateway_proto_msgTypes[2]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*LoginResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *LoginResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_gateway_proto_msgTypes[2]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*LoginResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_gateway_proto_rawDescGZIP(), []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) GetUserId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.UserId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) GetAccessToken() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.AccessToken
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) GetRefreshToken() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.RefreshToken
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) GetExpiresIn() int64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.ExpiresIn
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) GetIsNewUser() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.IsNewUser
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *LoginResponse) GetProfile() *UserProfile {
|
|
||||||
if x != nil {
|
|
||||||
return x.Profile
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type QueryOrderRequest struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
OrderNo string `protobuf:"bytes,1,opt,name=order_no,json=orderNo,proto3" json:"order_no,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderRequest) Reset() {
|
|
||||||
*x = QueryOrderRequest{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_gateway_proto_msgTypes[3]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*QueryOrderRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *QueryOrderRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_gateway_proto_msgTypes[3]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use QueryOrderRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*QueryOrderRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_gateway_proto_rawDescGZIP(), []int{3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderRequest) GetOrderNo() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.OrderNo
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type QueryOrderResponse struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
OrderNo string `protobuf:"bytes,1,opt,name=order_no,json=orderNo,proto3" json:"order_no,omitempty"`
|
|
||||||
UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
|
||||||
Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"`
|
|
||||||
Amount string `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount,omitempty"`
|
|
||||||
Currency string `protobuf:"bytes,5,opt,name=currency,proto3" json:"currency,omitempty"`
|
|
||||||
Subject string `protobuf:"bytes,6,opt,name=subject,proto3" json:"subject,omitempty"`
|
|
||||||
PayMethod string `protobuf:"bytes,7,opt,name=pay_method,json=payMethod,proto3" json:"pay_method,omitempty"`
|
|
||||||
PaidAt string `protobuf:"bytes,8,opt,name=paid_at,json=paidAt,proto3" json:"paid_at,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) Reset() {
|
|
||||||
*x = QueryOrderResponse{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_gateway_proto_msgTypes[4]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*QueryOrderResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_gateway_proto_msgTypes[4]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use QueryOrderResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*QueryOrderResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_gateway_proto_rawDescGZIP(), []int{4}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetOrderNo() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.OrderNo
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetUserId() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.UserId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetStatus() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Status
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetAmount() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Amount
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetCurrency() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Currency
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetSubject() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Subject
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetPayMethod() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.PayMethod
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *QueryOrderResponse) GetPaidAt() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.PaidAt
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_gateway_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_gateway_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x0d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
|
||||||
0x19, 0x63, 0x68, 0x61, 0x74, 0x61, 0x70, 0x70, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e,
|
|
||||||
0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x76, 0x31, 0x22, 0xc4, 0x02, 0x0a, 0x0c, 0x4c,
|
|
||||||
0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6c,
|
|
||||||
0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
|
||||||
0x09, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63,
|
|
||||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63,
|
|
||||||
0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
|
||||||
0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65,
|
|
||||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43,
|
|
||||||
0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x63, 0x6f,
|
|
||||||
0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79,
|
|
||||||
0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
|
||||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
|
||||||
0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x6b,
|
|
||||||
0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
|
|
||||||
0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63,
|
|
||||||
0x65, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69,
|
|
||||||
0x63, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d,
|
|
||||||
0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d,
|
|
||||||
0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
|
||||||
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
|
||||||
0x6e, 0x22, 0x61, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
|
|
||||||
0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63,
|
|
||||||
0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63,
|
|
||||||
0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f,
|
|
||||||
0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x76, 0x61, 0x74, 0x61,
|
|
||||||
0x72, 0x55, 0x72, 0x6c, 0x22, 0xf1, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65,
|
|
||||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69,
|
|
||||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12,
|
|
||||||
0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18,
|
|
||||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b,
|
|
||||||
0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f,
|
|
||||||
0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65,
|
|
||||||
0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72,
|
|
||||||
0x65, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x78, 0x70,
|
|
||||||
0x69, 0x72, 0x65, 0x73, 0x49, 0x6e, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x6e, 0x65, 0x77,
|
|
||||||
0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x4e,
|
|
||||||
0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
|
|
||||||
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x61, 0x70,
|
|
||||||
0x70, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
|
|
||||||
0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52,
|
|
||||||
0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x2e, 0x0a, 0x11, 0x51, 0x75, 0x65, 0x72,
|
|
||||||
0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a,
|
|
||||||
0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
|
||||||
0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x22, 0xe6, 0x01, 0x0a, 0x12, 0x51, 0x75, 0x65,
|
|
||||||
0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
|
||||||
0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73,
|
|
||||||
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65,
|
|
||||||
0x72, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20,
|
|
||||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61,
|
|
||||||
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f,
|
|
||||||
0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
|
|
||||||
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12,
|
|
||||||
0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
|
|
||||||
0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x79,
|
|
||||||
0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70,
|
|
||||||
0x61, 0x79, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x69, 0x64,
|
|
||||||
0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x69, 0x64, 0x41,
|
|
||||||
0x74, 0x32, 0x69, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 0x41, 0x70, 0x70, 0x55, 0x73, 0x65, 0x72,
|
|
||||||
0x12, 0x5a, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x27, 0x2e, 0x63, 0x68, 0x61, 0x74,
|
|
||||||
0x61, 0x70, 0x70, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
|
|
||||||
0x61, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
|
||||||
0x73, 0x74, 0x1a, 0x28, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x61, 0x70, 0x70, 0x67, 0x61, 0x74, 0x65,
|
|
||||||
0x77, 0x61, 0x79, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c,
|
|
||||||
0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x77, 0x0a, 0x0a,
|
|
||||||
0x43, 0x68, 0x61, 0x74, 0x41, 0x70, 0x70, 0x50, 0x61, 0x79, 0x12, 0x69, 0x0a, 0x0a, 0x51, 0x75,
|
|
||||||
0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x2c, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x61,
|
|
||||||
0x70, 0x70, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
|
|
||||||
0x79, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52,
|
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x61, 0x70, 0x70,
|
|
||||||
0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e,
|
|
||||||
0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73,
|
|
||||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x24, 0x5a, 0x22, 0x63, 0x68, 0x61, 0x74, 0x61, 0x70, 0x70,
|
|
||||||
0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
|
||||||
0x6f, 0x3b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
|
||||||
0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_gateway_proto_rawDescOnce sync.Once
|
|
||||||
file_gateway_proto_rawDescData = file_gateway_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_gateway_proto_rawDescGZIP() []byte {
|
|
||||||
file_gateway_proto_rawDescOnce.Do(func() {
|
|
||||||
file_gateway_proto_rawDescData = protoimpl.X.CompressGZIP(file_gateway_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_gateway_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
|
||||||
var file_gateway_proto_goTypes = []any{
|
|
||||||
(*LoginRequest)(nil), // 0: chatappgateway.gateway.v1.LoginRequest
|
|
||||||
(*UserProfile)(nil), // 1: chatappgateway.gateway.v1.UserProfile
|
|
||||||
(*LoginResponse)(nil), // 2: chatappgateway.gateway.v1.LoginResponse
|
|
||||||
(*QueryOrderRequest)(nil), // 3: chatappgateway.gateway.v1.QueryOrderRequest
|
|
||||||
(*QueryOrderResponse)(nil), // 4: chatappgateway.gateway.v1.QueryOrderResponse
|
|
||||||
}
|
|
||||||
var file_gateway_proto_depIdxs = []int32{
|
|
||||||
1, // 0: chatappgateway.gateway.v1.LoginResponse.profile:type_name -> chatappgateway.gateway.v1.UserProfile
|
|
||||||
0, // 1: chatappgateway.gateway.v1.ChatAppUser.Login:input_type -> chatappgateway.gateway.v1.LoginRequest
|
|
||||||
3, // 2: chatappgateway.gateway.v1.ChatAppPay.QueryOrder:input_type -> chatappgateway.gateway.v1.QueryOrderRequest
|
|
||||||
2, // 3: chatappgateway.gateway.v1.ChatAppUser.Login:output_type -> chatappgateway.gateway.v1.LoginResponse
|
|
||||||
4, // 4: chatappgateway.gateway.v1.ChatAppPay.QueryOrder:output_type -> chatappgateway.gateway.v1.QueryOrderResponse
|
|
||||||
3, // [3:5] is the sub-list for method output_type
|
|
||||||
1, // [1:3] is the sub-list for method input_type
|
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
|
||||||
0, // [0:1] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_gateway_proto_init() }
|
|
||||||
func file_gateway_proto_init() {
|
|
||||||
if File_gateway_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_gateway_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
|
||||||
switch v := v.(*LoginRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_gateway_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
|
||||||
switch v := v.(*UserProfile); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_gateway_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
|
||||||
switch v := v.(*LoginResponse); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_gateway_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
|
||||||
switch v := v.(*QueryOrderRequest); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_gateway_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
|
||||||
switch v := v.(*QueryOrderResponse); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_gateway_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 5,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 2,
|
|
||||||
},
|
|
||||||
GoTypes: file_gateway_proto_goTypes,
|
|
||||||
DependencyIndexes: file_gateway_proto_depIdxs,
|
|
||||||
MessageInfos: file_gateway_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_gateway_proto = out.File
|
|
||||||
file_gateway_proto_rawDesc = nil
|
|
||||||
file_gateway_proto_goTypes = nil
|
|
||||||
file_gateway_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package chatappgateway.gateway.v1;
|
|
||||||
|
|
||||||
option go_package = "chatappgateway/api/proto;gatewaypb";
|
|
||||||
|
|
||||||
service ChatAppUser {
|
|
||||||
rpc Login(LoginRequest) returns (LoginResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
service ChatAppPay {
|
|
||||||
rpc QueryOrder(QueryOrderRequest) returns (QueryOrderResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
message LoginRequest {
|
|
||||||
string login_type = 1;
|
|
||||||
string account = 2;
|
|
||||||
string password = 3;
|
|
||||||
string country_code = 4;
|
|
||||||
string verify_code = 5;
|
|
||||||
string provider = 6;
|
|
||||||
string provider_token = 7;
|
|
||||||
string device_id = 8;
|
|
||||||
string platform = 9;
|
|
||||||
string app_version = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UserProfile {
|
|
||||||
string user_id = 1;
|
|
||||||
string nickname = 2;
|
|
||||||
string avatar_url = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message LoginResponse {
|
|
||||||
string user_id = 1;
|
|
||||||
string access_token = 2;
|
|
||||||
string refresh_token = 3;
|
|
||||||
int64 expires_in = 4;
|
|
||||||
bool is_new_user = 5;
|
|
||||||
UserProfile profile = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
message QueryOrderRequest {
|
|
||||||
string order_no = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message QueryOrderResponse {
|
|
||||||
string order_no = 1;
|
|
||||||
string user_id = 2;
|
|
||||||
string status = 3;
|
|
||||||
string amount = 4;
|
|
||||||
string currency = 5;
|
|
||||||
string subject = 6;
|
|
||||||
string pay_method = 7;
|
|
||||||
string paid_at = 8;
|
|
||||||
}
|
|
||||||
@ -1,223 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// - protoc-gen-go-grpc v1.5.1
|
|
||||||
// - protoc v5.29.2
|
|
||||||
// source: gateway.proto
|
|
||||||
|
|
||||||
package gatewaypb
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "context"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
codes "google.golang.org/grpc/codes"
|
|
||||||
status "google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
// Requires gRPC-Go v1.64.0 or later.
|
|
||||||
const _ = grpc.SupportPackageIsVersion9
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChatAppUser_Login_FullMethodName = "/chatappgateway.gateway.v1.ChatAppUser/Login"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChatAppUserClient is the client API for ChatAppUser service.
|
|
||||||
//
|
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
|
||||||
type ChatAppUserClient interface {
|
|
||||||
Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type chatAppUserClient struct {
|
|
||||||
cc grpc.ClientConnInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewChatAppUserClient(cc grpc.ClientConnInterface) ChatAppUserClient {
|
|
||||||
return &chatAppUserClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *chatAppUserClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) {
|
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(LoginResponse)
|
|
||||||
err := c.cc.Invoke(ctx, ChatAppUser_Login_FullMethodName, in, out, cOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChatAppUserServer is the server API for ChatAppUser service.
|
|
||||||
// All implementations must embed UnimplementedChatAppUserServer
|
|
||||||
// for forward compatibility.
|
|
||||||
type ChatAppUserServer interface {
|
|
||||||
Login(context.Context, *LoginRequest) (*LoginResponse, error)
|
|
||||||
mustEmbedUnimplementedChatAppUserServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedChatAppUserServer must be embedded to have
|
|
||||||
// forward compatible implementations.
|
|
||||||
//
|
|
||||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
|
||||||
// pointer dereference when methods are called.
|
|
||||||
type UnimplementedChatAppUserServer struct{}
|
|
||||||
|
|
||||||
func (UnimplementedChatAppUserServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedChatAppUserServer) mustEmbedUnimplementedChatAppUserServer() {}
|
|
||||||
func (UnimplementedChatAppUserServer) testEmbeddedByValue() {}
|
|
||||||
|
|
||||||
// UnsafeChatAppUserServer may be embedded to opt out of forward compatibility for this service.
|
|
||||||
// Use of this interface is not recommended, as added methods to ChatAppUserServer will
|
|
||||||
// result in compilation errors.
|
|
||||||
type UnsafeChatAppUserServer interface {
|
|
||||||
mustEmbedUnimplementedChatAppUserServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterChatAppUserServer(s grpc.ServiceRegistrar, srv ChatAppUserServer) {
|
|
||||||
// If the following call pancis, it indicates UnimplementedChatAppUserServer was
|
|
||||||
// embedded by pointer and is nil. This will cause panics if an
|
|
||||||
// unimplemented method is ever invoked, so we test this at initialization
|
|
||||||
// time to prevent it from happening at runtime later due to I/O.
|
|
||||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
|
||||||
t.testEmbeddedByValue()
|
|
||||||
}
|
|
||||||
s.RegisterService(&ChatAppUser_ServiceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _ChatAppUser_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(LoginRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ChatAppUserServer).Login(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: ChatAppUser_Login_FullMethodName,
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ChatAppUserServer).Login(ctx, req.(*LoginRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChatAppUser_ServiceDesc is the grpc.ServiceDesc for ChatAppUser service.
|
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
|
||||||
// and not to be introspected or modified (even as a copy)
|
|
||||||
var ChatAppUser_ServiceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "chatappgateway.gateway.v1.ChatAppUser",
|
|
||||||
HandlerType: (*ChatAppUserServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "Login",
|
|
||||||
Handler: _ChatAppUser_Login_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "gateway.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChatAppPay_QueryOrder_FullMethodName = "/chatappgateway.gateway.v1.ChatAppPay/QueryOrder"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChatAppPayClient is the client API for ChatAppPay service.
|
|
||||||
//
|
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
|
||||||
type ChatAppPayClient interface {
|
|
||||||
QueryOrder(ctx context.Context, in *QueryOrderRequest, opts ...grpc.CallOption) (*QueryOrderResponse, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type chatAppPayClient struct {
|
|
||||||
cc grpc.ClientConnInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewChatAppPayClient(cc grpc.ClientConnInterface) ChatAppPayClient {
|
|
||||||
return &chatAppPayClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *chatAppPayClient) QueryOrder(ctx context.Context, in *QueryOrderRequest, opts ...grpc.CallOption) (*QueryOrderResponse, error) {
|
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(QueryOrderResponse)
|
|
||||||
err := c.cc.Invoke(ctx, ChatAppPay_QueryOrder_FullMethodName, in, out, cOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChatAppPayServer is the server API for ChatAppPay service.
|
|
||||||
// All implementations must embed UnimplementedChatAppPayServer
|
|
||||||
// for forward compatibility.
|
|
||||||
type ChatAppPayServer interface {
|
|
||||||
QueryOrder(context.Context, *QueryOrderRequest) (*QueryOrderResponse, error)
|
|
||||||
mustEmbedUnimplementedChatAppPayServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedChatAppPayServer must be embedded to have
|
|
||||||
// forward compatible implementations.
|
|
||||||
//
|
|
||||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
|
||||||
// pointer dereference when methods are called.
|
|
||||||
type UnimplementedChatAppPayServer struct{}
|
|
||||||
|
|
||||||
func (UnimplementedChatAppPayServer) QueryOrder(context.Context, *QueryOrderRequest) (*QueryOrderResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method QueryOrder not implemented")
|
|
||||||
}
|
|
||||||
func (UnimplementedChatAppPayServer) mustEmbedUnimplementedChatAppPayServer() {}
|
|
||||||
func (UnimplementedChatAppPayServer) testEmbeddedByValue() {}
|
|
||||||
|
|
||||||
// UnsafeChatAppPayServer may be embedded to opt out of forward compatibility for this service.
|
|
||||||
// Use of this interface is not recommended, as added methods to ChatAppPayServer will
|
|
||||||
// result in compilation errors.
|
|
||||||
type UnsafeChatAppPayServer interface {
|
|
||||||
mustEmbedUnimplementedChatAppPayServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterChatAppPayServer(s grpc.ServiceRegistrar, srv ChatAppPayServer) {
|
|
||||||
// If the following call pancis, it indicates UnimplementedChatAppPayServer was
|
|
||||||
// embedded by pointer and is nil. This will cause panics if an
|
|
||||||
// unimplemented method is ever invoked, so we test this at initialization
|
|
||||||
// time to prevent it from happening at runtime later due to I/O.
|
|
||||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
|
||||||
t.testEmbeddedByValue()
|
|
||||||
}
|
|
||||||
s.RegisterService(&ChatAppPay_ServiceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _ChatAppPay_QueryOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(QueryOrderRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ChatAppPayServer).QueryOrder(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: ChatAppPay_QueryOrder_FullMethodName,
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ChatAppPayServer).QueryOrder(ctx, req.(*QueryOrderRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChatAppPay_ServiceDesc is the grpc.ServiceDesc for ChatAppPay service.
|
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
|
||||||
// and not to be introspected or modified (even as a copy)
|
|
||||||
var ChatAppPay_ServiceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "chatappgateway.gateway.v1.ChatAppPay",
|
|
||||||
HandlerType: (*ChatAppPayServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "QueryOrder",
|
|
||||||
Handler: _ChatAppPay_QueryOrder_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "gateway.proto",
|
|
||||||
}
|
|
||||||
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
- `ChatAppGateway` 不是纯反向代理网关,而是 BFF / 聚合编排服务。
|
- `ChatAppGateway` 不是纯反向代理网关,而是 BFF / 聚合编排服务。
|
||||||
- 对客户端暴露 HTTP JSON API。
|
- 对客户端暴露 HTTP JSON API。
|
||||||
- 对内通过 gRPC 调用:
|
- 对内通过 `Common` 里的 proto 调用:
|
||||||
- `ChatAppUser.Login`
|
- `ChatAppUserService.Register`
|
||||||
- `ChatAppPay.QueryOrder`
|
- `ChatAppPayService.Pay`
|
||||||
- 自身不实现用户域认证规则,也不实现支付域核心规则,只做参数校验、字段归一化、协议适配、统一错误响应、日志与 trace。
|
- 自身不实现用户域认证规则,也不实现支付域核心规则,只做参数校验、字段归一化、协议适配、统一错误响应、日志与 trace。
|
||||||
|
|
||||||
## 目标能力
|
## 目标能力
|
||||||
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
1. `GET /health`
|
1. `GET /health`
|
||||||
2. `GET /ready`
|
2. `GET /ready`
|
||||||
3. `POST /api/v1/auth/login`
|
3. `POST /api/v1/users/register`
|
||||||
4. `GET /api/v1/pay/orders/{order_no}`
|
4. `POST /api/v1/pay`
|
||||||
|
|
||||||
## 配置要求
|
## 配置要求
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ grpc:
|
|||||||
|
|
||||||
## 登录接口要求
|
## 登录接口要求
|
||||||
|
|
||||||
`POST /api/v1/auth/login`
|
`POST /api/v1/users/register`
|
||||||
|
|
||||||
请求 DTO 至少包含:
|
请求 DTO 至少包含:
|
||||||
|
|
||||||
@ -77,16 +77,14 @@ grpc:
|
|||||||
|
|
||||||
网关职责:
|
网关职责:
|
||||||
|
|
||||||
- 只做模式校验和字段归一化
|
- 做基础参数校验和字段归一化
|
||||||
- 将请求映射到 `ChatAppUser.Login`
|
- 将请求映射到 `ChatAppUserService.Register`
|
||||||
- 接收用户服务返回后再统一包装给客户端
|
- 接收用户服务返回后再统一包装给客户端
|
||||||
|
|
||||||
`ChatAppUser.Login` 返回至少包含:
|
`ChatAppUserService.Register` 返回至少包含:
|
||||||
|
|
||||||
- `user_id`
|
- `user_id`
|
||||||
- `access_token`
|
- `access_token`
|
||||||
- `refresh_token`
|
|
||||||
- `expires_in`
|
|
||||||
- `is_new_user`
|
- `is_new_user`
|
||||||
- `profile`
|
- `profile`
|
||||||
|
|
||||||
@ -98,24 +96,25 @@ grpc:
|
|||||||
|
|
||||||
## 支付查询接口要求
|
## 支付查询接口要求
|
||||||
|
|
||||||
`GET /api/v1/pay/orders/{order_no}`
|
`POST /api/v1/pay`
|
||||||
|
|
||||||
网关职责:
|
网关职责:
|
||||||
|
|
||||||
- 校验 `order_no` 非空
|
- 校验 `order_no` 非空
|
||||||
- 调用 `ChatAppPay.QueryOrder`
|
- 调用 `ChatAppPayService.Pay`
|
||||||
- 把支付服务返回的订单结果转成统一 HTTP JSON 响应
|
- 把支付服务返回的订单结果转成统一 HTTP JSON 响应
|
||||||
|
|
||||||
返回字段至少包含:
|
返回字段至少包含:
|
||||||
|
|
||||||
|
- `payment_id`
|
||||||
- `order_no`
|
- `order_no`
|
||||||
- `user_id`
|
- `user_id`
|
||||||
- `status`
|
- `status`
|
||||||
- `amount`
|
- `amount`
|
||||||
- `currency`
|
- `currency`
|
||||||
- `subject`
|
|
||||||
- `pay_method`
|
- `pay_method`
|
||||||
- `paid_at`
|
- `subject`
|
||||||
|
- `created_at`
|
||||||
|
|
||||||
## 健康检查要求
|
## 健康检查要求
|
||||||
|
|
||||||
@ -148,7 +147,7 @@ grpc:
|
|||||||
## 错误码和状态码要求
|
## 错误码和状态码要求
|
||||||
|
|
||||||
- 参数错误:`400`
|
- 参数错误:`400`
|
||||||
- 登录失败或凭证错误:`401`
|
- 注册失败或凭证错误:`401`
|
||||||
- 订单不存在:`404`
|
- 订单不存在:`404`
|
||||||
- 下游 gRPC 超时:`504`
|
- 下游 gRPC 超时:`504`
|
||||||
- 下游 gRPC 不可用或内部异常:`502`
|
- 下游 gRPC 不可用或内部异常:`502`
|
||||||
|
|||||||
7
go.mod
7
go.mod
@ -3,14 +3,17 @@ module chatappgateway
|
|||||||
go 1.23.1
|
go 1.23.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
gitea.haiyihy.com/hy/chatappcommon v0.0.0
|
||||||
google.golang.org/grpc v1.67.3
|
google.golang.org/grpc v1.67.3
|
||||||
google.golang.org/protobuf v1.36.11
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/net v0.35.0 // indirect
|
golang.org/x/net v0.28.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace gitea.haiyihy.com/hy/chatappcommon => ../Common
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -1,7 +1,7 @@
|
|||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
gatewaypb "chatappgateway/api/proto"
|
|
||||||
"chatappgateway/internal/config"
|
"chatappgateway/internal/config"
|
||||||
"chatappgateway/internal/integration/paygrpc"
|
"chatappgateway/internal/integration/paygrpc"
|
||||||
"chatappgateway/internal/integration/usergrpc"
|
"chatappgateway/internal/integration/usergrpc"
|
||||||
"chatappgateway/internal/service/auth"
|
"chatappgateway/internal/service/auth"
|
||||||
"chatappgateway/internal/service/pay"
|
"chatappgateway/internal/service/pay"
|
||||||
httpserver "chatappgateway/internal/transport/http"
|
httpserver "chatappgateway/internal/transport/http"
|
||||||
|
commonpb "gitea.haiyihy.com/hy/chatappcommon/proto"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
@ -37,8 +37,8 @@ func New(ctx context.Context, cfg config.Config, logger *slog.Logger) (*Applicat
|
|||||||
return nil, fmt.Errorf("dial ChatAppPay: %w", err)
|
return nil, fmt.Errorf("dial ChatAppPay: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userClient := usergrpc.New(gatewaypb.NewChatAppUserClient(userConn), cfg.GRPC.User.Timeout)
|
userClient := usergrpc.New(commonpb.NewChatAppUserServiceClient(userConn), cfg.GRPC.User.Timeout)
|
||||||
payClient := paygrpc.New(gatewaypb.NewChatAppPayClient(payConn), cfg.GRPC.Pay.Timeout)
|
payClient := paygrpc.New(commonpb.NewChatAppPayServiceClient(payConn), cfg.GRPC.Pay.Timeout)
|
||||||
|
|
||||||
authService := auth.New(userClient)
|
authService := auth.New(userClient)
|
||||||
payService := pay.New(payClient)
|
payService := pay.New(payClient)
|
||||||
|
|||||||
@ -4,27 +4,27 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
gatewaypb "chatappgateway/api/proto"
|
commonpb "gitea.haiyihy.com/hy/chatappcommon/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client 封装支付服务 gRPC client,并统一超时控制。
|
// Client 封装支付服务 gRPC client,并统一超时控制。
|
||||||
type Client struct {
|
type Client struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
client gatewaypb.ChatAppPayClient
|
client commonpb.ChatAppPayServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// New 根据底层 gRPC client 构造支付服务调用器。
|
// New 根据底层 gRPC client 构造支付服务调用器。
|
||||||
func New(client gatewaypb.ChatAppPayClient, timeout time.Duration) *Client {
|
func New(client commonpb.ChatAppPayServiceClient, timeout time.Duration) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
client: client,
|
client: client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryOrder 调用支付服务最小订单查询接口。
|
// Pay 调用支付服务最小支付接口。
|
||||||
func (c *Client) QueryOrder(ctx context.Context, request *gatewaypb.QueryOrderRequest) (*gatewaypb.QueryOrderResponse, error) {
|
func (c *Client) Pay(ctx context.Context, request *commonpb.PayRequest) (*commonpb.PayResponse, error) {
|
||||||
callCtx, cancel := context.WithTimeout(ctx, c.timeout)
|
callCtx, cancel := context.WithTimeout(ctx, c.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
return c.client.QueryOrder(callCtx, request)
|
return c.client.Pay(callCtx, request)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,27 +4,27 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
gatewaypb "chatappgateway/api/proto"
|
commonpb "gitea.haiyihy.com/hy/chatappcommon/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client 封装用户服务 gRPC client,并统一超时控制。
|
// Client 封装用户服务 gRPC client,并统一超时控制。
|
||||||
type Client struct {
|
type Client struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
client gatewaypb.ChatAppUserClient
|
client commonpb.ChatAppUserServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// New 根据底层 gRPC client 构造用户服务调用器。
|
// New 根据底层 gRPC client 构造用户服务调用器。
|
||||||
func New(client gatewaypb.ChatAppUserClient, timeout time.Duration) *Client {
|
func New(client commonpb.ChatAppUserServiceClient, timeout time.Duration) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
client: client,
|
client: client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login 调用用户服务登录接口。
|
// Register 调用用户服务注册接口。
|
||||||
func (c *Client) Login(ctx context.Context, request *gatewaypb.LoginRequest) (*gatewaypb.LoginResponse, error) {
|
func (c *Client) Register(ctx context.Context, request *commonpb.RegisterRequest) (*commonpb.RegisterResponse, error) {
|
||||||
callCtx, cancel := context.WithTimeout(ctx, c.timeout)
|
callCtx, cancel := context.WithTimeout(ctx, c.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
return c.client.Login(callCtx, request)
|
return c.client.Register(callCtx, request)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,103 +4,76 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
gatewaypb "chatappgateway/api/proto"
|
|
||||||
"chatappgateway/internal/apperr"
|
"chatappgateway/internal/apperr"
|
||||||
|
commonpb "gitea.haiyihy.com/hy/chatappcommon/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client 定义用户服务 gRPC 客户端能力。
|
// Client 定义用户服务 gRPC 客户端能力。
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Login(ctx context.Context, request *gatewaypb.LoginRequest) (*gatewaypb.LoginResponse, error)
|
Register(ctx context.Context, request *commonpb.RegisterRequest) (*commonpb.RegisterResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service 负责登录参数校验、字段归一化和下游调用。
|
// Service 负责注册参数校验、字段归一化和下游调用。
|
||||||
type Service struct {
|
type Service struct {
|
||||||
client Client
|
client Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoginRequest 描述 HTTP 层的登录入参。
|
// RegisterRequest 描述 HTTP 层的注册入参。
|
||||||
type LoginRequest struct {
|
type RegisterRequest struct {
|
||||||
LoginType string `json:"login_type"`
|
Account string `json:"account"`
|
||||||
Account string `json:"account"`
|
Password string `json:"password"`
|
||||||
Password string `json:"password"`
|
CountryCode string `json:"country_code"`
|
||||||
CountryCode string `json:"country_code"`
|
VerifyCode string `json:"verify_code"`
|
||||||
VerifyCode string `json:"verify_code"`
|
Nickname string `json:"nickname"`
|
||||||
Provider string `json:"provider"`
|
DeviceID string `json:"device_id"`
|
||||||
ProviderToken string `json:"provider_token"`
|
Platform string `json:"platform"`
|
||||||
DeviceID string `json:"device_id"`
|
AppVersion string `json:"app_version"`
|
||||||
Platform string `json:"platform"`
|
|
||||||
AppVersion string `json:"app_version"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New 创建登录服务。
|
// New 创建注册服务。
|
||||||
func New(client Client) *Service {
|
func New(client Client) *Service {
|
||||||
return &Service{client: client}
|
return &Service{client: client}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login 校验客户端请求并转成 gRPC 请求。
|
// Register 校验客户端请求并转成 gRPC 请求。
|
||||||
func (s *Service) Login(ctx context.Context, request LoginRequest) (*gatewaypb.LoginResponse, error) {
|
func (s *Service) Register(ctx context.Context, request RegisterRequest) (*commonpb.RegisterResponse, error) {
|
||||||
normalized, err := normalize(request)
|
normalized, err := normalize(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.client.Login(ctx, &gatewaypb.LoginRequest{
|
return s.client.Register(ctx, &commonpb.RegisterRequest{
|
||||||
LoginType: normalized.LoginType,
|
Account: normalized.Account,
|
||||||
Account: normalized.Account,
|
Password: normalized.Password,
|
||||||
Password: normalized.Password,
|
CountryCode: normalized.CountryCode,
|
||||||
CountryCode: normalized.CountryCode,
|
VerifyCode: normalized.VerifyCode,
|
||||||
VerifyCode: normalized.VerifyCode,
|
Nickname: normalized.Nickname,
|
||||||
Provider: normalized.Provider,
|
DeviceId: normalized.DeviceID,
|
||||||
ProviderToken: normalized.ProviderToken,
|
Platform: normalized.Platform,
|
||||||
DeviceId: normalized.DeviceID,
|
AppVersion: normalized.AppVersion,
|
||||||
Platform: normalized.Platform,
|
|
||||||
AppVersion: normalized.AppVersion,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalize(request LoginRequest) (LoginRequest, error) {
|
func normalize(request RegisterRequest) (RegisterRequest, error) {
|
||||||
normalized := LoginRequest{
|
normalized := RegisterRequest{
|
||||||
LoginType: strings.ToLower(strings.TrimSpace(request.LoginType)),
|
Account: strings.TrimSpace(request.Account),
|
||||||
Account: strings.TrimSpace(request.Account),
|
Password: strings.TrimSpace(request.Password),
|
||||||
Password: request.Password,
|
CountryCode: strings.TrimSpace(request.CountryCode),
|
||||||
CountryCode: strings.TrimSpace(request.CountryCode),
|
VerifyCode: strings.TrimSpace(request.VerifyCode),
|
||||||
VerifyCode: strings.TrimSpace(request.VerifyCode),
|
Nickname: strings.TrimSpace(request.Nickname),
|
||||||
Provider: strings.ToLower(strings.TrimSpace(request.Provider)),
|
DeviceID: strings.TrimSpace(request.DeviceID),
|
||||||
ProviderToken: strings.TrimSpace(request.ProviderToken),
|
Platform: strings.TrimSpace(request.Platform),
|
||||||
DeviceID: strings.TrimSpace(request.DeviceID),
|
AppVersion: strings.TrimSpace(request.AppVersion),
|
||||||
Platform: strings.TrimSpace(request.Platform),
|
|
||||||
AppVersion: strings.TrimSpace(request.AppVersion),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch normalized.LoginType {
|
if normalized.Account == "" {
|
||||||
case "":
|
return RegisterRequest{}, apperr.New(400, "bad_request", "account is required")
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "login_type is required")
|
}
|
||||||
case "password":
|
if normalized.Password == "" {
|
||||||
if normalized.Account == "" {
|
return RegisterRequest{}, apperr.New(400, "bad_request", "password is required")
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "account is required")
|
}
|
||||||
}
|
if normalized.Nickname == "" {
|
||||||
if strings.TrimSpace(normalized.Password) == "" {
|
normalized.Nickname = normalized.Account
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "password is required")
|
|
||||||
}
|
|
||||||
case "sms_code":
|
|
||||||
if normalized.CountryCode == "" {
|
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "country_code is required")
|
|
||||||
}
|
|
||||||
if normalized.Account == "" {
|
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "account is required")
|
|
||||||
}
|
|
||||||
if normalized.VerifyCode == "" {
|
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "verify_code is required")
|
|
||||||
}
|
|
||||||
case "oauth":
|
|
||||||
if normalized.Provider == "" {
|
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "provider is required")
|
|
||||||
}
|
|
||||||
if normalized.ProviderToken == "" {
|
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "provider_token is required")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return LoginRequest{}, apperr.New(400, "bad_request", "login_type must be one of password, sms_code, oauth")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return normalized, nil
|
return normalized, nil
|
||||||
|
|||||||
@ -4,16 +4,26 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
gatewaypb "chatappgateway/api/proto"
|
|
||||||
"chatappgateway/internal/apperr"
|
"chatappgateway/internal/apperr"
|
||||||
|
commonpb "gitea.haiyihy.com/hy/chatappcommon/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client 定义支付服务 gRPC 客户端能力。
|
// Client 定义支付服务 gRPC 客户端能力。
|
||||||
type Client interface {
|
type Client interface {
|
||||||
QueryOrder(ctx context.Context, request *gatewaypb.QueryOrderRequest) (*gatewaypb.QueryOrderResponse, error)
|
Pay(ctx context.Context, request *commonpb.PayRequest) (*commonpb.PayResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service 负责订单号校验和下游支付查询。
|
// Request 描述客户端发起支付的请求。
|
||||||
|
type Request struct {
|
||||||
|
OrderNo string `json:"order_no"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
PayMethod string `json:"pay_method"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service 负责支付请求校验和下游支付调用。
|
||||||
type Service struct {
|
type Service struct {
|
||||||
client Client
|
client Client
|
||||||
}
|
}
|
||||||
@ -23,14 +33,33 @@ func New(client Client) *Service {
|
|||||||
return &Service{client: client}
|
return &Service{client: client}
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryOrder 校验订单号并调用支付服务。
|
// Pay 校验支付请求并调用支付服务。
|
||||||
func (s *Service) QueryOrder(ctx context.Context, orderNo string) (*gatewaypb.QueryOrderResponse, error) {
|
func (s *Service) Pay(ctx context.Context, request Request) (*commonpb.PayResponse, error) {
|
||||||
normalized := strings.TrimSpace(orderNo)
|
normalized := Request{
|
||||||
if normalized == "" {
|
OrderNo: strings.TrimSpace(request.OrderNo),
|
||||||
return nil, apperr.New(400, "bad_request", "order_no is required")
|
UserID: strings.TrimSpace(request.UserID),
|
||||||
|
Amount: strings.TrimSpace(request.Amount),
|
||||||
|
Currency: strings.TrimSpace(request.Currency),
|
||||||
|
PayMethod: strings.TrimSpace(request.PayMethod),
|
||||||
|
Subject: strings.TrimSpace(request.Subject),
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.client.QueryOrder(ctx, &gatewaypb.QueryOrderRequest{
|
if normalized.OrderNo == "" {
|
||||||
OrderNo: normalized,
|
return nil, apperr.New(400, "bad_request", "order_no is required")
|
||||||
|
}
|
||||||
|
if normalized.UserID == "" {
|
||||||
|
return nil, apperr.New(400, "bad_request", "user_id is required")
|
||||||
|
}
|
||||||
|
if normalized.Amount == "" {
|
||||||
|
return nil, apperr.New(400, "bad_request", "amount is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Pay(ctx, &commonpb.PayRequest{
|
||||||
|
OrderNo: normalized.OrderNo,
|
||||||
|
UserId: normalized.UserID,
|
||||||
|
Amount: normalized.Amount,
|
||||||
|
Currency: normalized.Currency,
|
||||||
|
PayMethod: normalized.PayMethod,
|
||||||
|
Subject: normalized.Subject,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,23 +14,22 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
gatewaypb "chatappgateway/api/proto"
|
|
||||||
"chatappgateway/internal/apperr"
|
"chatappgateway/internal/apperr"
|
||||||
"chatappgateway/internal/service/auth"
|
"chatappgateway/internal/service/auth"
|
||||||
|
payservice "chatappgateway/internal/service/pay"
|
||||||
|
commonpb "gitea.haiyihy.com/hy/chatappcommon/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const orderPrefix = "/api/v1/pay/orders/"
|
|
||||||
|
|
||||||
type requestIDKey struct{}
|
type requestIDKey struct{}
|
||||||
|
|
||||||
// AuthService 定义 HTTP 层依赖的登录业务能力。
|
// AuthService 定义 HTTP 层依赖的用户注册能力。
|
||||||
type AuthService interface {
|
type AuthService interface {
|
||||||
Login(ctx context.Context, request auth.LoginRequest) (*gatewaypb.LoginResponse, error)
|
Register(ctx context.Context, request auth.RegisterRequest) (*commonpb.RegisterResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayService 定义 HTTP 层依赖的支付查询能力。
|
// PayService 定义 HTTP 层依赖的支付下单能力。
|
||||||
type PayService interface {
|
type PayService interface {
|
||||||
QueryOrder(ctx context.Context, orderNo string) (*gatewaypb.QueryOrderResponse, error)
|
Pay(ctx context.Context, request payservice.Request) (*commonpb.PayResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadinessChecker 定义就绪检查能力,只有可接流量时才返回 nil。
|
// ReadinessChecker 定义就绪检查能力,只有可接流量时才返回 nil。
|
||||||
@ -104,9 +103,10 @@ func (s *Server) Run(ctx context.Context) error {
|
|||||||
func (s *Server) routes() http.Handler {
|
func (s *Server) routes() http.Handler {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/health", s.handleHealth)
|
mux.HandleFunc("/health", s.handleHealth)
|
||||||
|
mux.HandleFunc("/heath", s.handleHealth)
|
||||||
mux.HandleFunc("/ready", s.handleReady)
|
mux.HandleFunc("/ready", s.handleReady)
|
||||||
mux.HandleFunc("/api/v1/auth/login", s.handleLogin)
|
mux.HandleFunc("/api/v1/users/register", s.handleRegister)
|
||||||
mux.HandleFunc(orderPrefix, s.handleQueryOrder)
|
mux.HandleFunc("/api/v1/pay", s.handlePay)
|
||||||
mux.HandleFunc("/", s.handleNotFound)
|
mux.HandleFunc("/", s.handleNotFound)
|
||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
@ -139,13 +139,13 @@ func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
writeError(w, requestIDFromContext(r.Context()), apperr.New(http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed"))
|
writeError(w, requestIDFromContext(r.Context()), apperr.New(http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request auth.LoginRequest
|
var request auth.RegisterRequest
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
decoder.DisallowUnknownFields()
|
decoder.DisallowUnknownFields()
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
@ -153,7 +153,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.authService.Login(r.Context(), request)
|
response, err := s.authService.Register(r.Context(), request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, requestIDFromContext(r.Context()), err)
|
writeError(w, requestIDFromContext(r.Context()), err)
|
||||||
return
|
return
|
||||||
@ -162,19 +162,21 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
writeEnvelope(w, http.StatusOK, requestIDFromContext(r.Context()), response)
|
writeEnvelope(w, http.StatusOK, requestIDFromContext(r.Context()), response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleQueryOrder(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handlePay(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodGet {
|
if r.Method != http.MethodPost {
|
||||||
writeError(w, requestIDFromContext(r.Context()), apperr.New(http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed"))
|
writeError(w, requestIDFromContext(r.Context()), apperr.New(http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
orderNo, ok := extractOrderNo(r.URL.Path)
|
var request payservice.Request
|
||||||
if !ok {
|
decoder := json.NewDecoder(r.Body)
|
||||||
writeError(w, requestIDFromContext(r.Context()), apperr.New(http.StatusNotFound, "not_found", "order not found"))
|
decoder.DisallowUnknownFields()
|
||||||
|
if err := decoder.Decode(&request); err != nil {
|
||||||
|
writeError(w, requestIDFromContext(r.Context()), apperr.New(http.StatusBadRequest, "bad_request", "invalid json body"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.payService.QueryOrder(r.Context(), orderNo)
|
response, err := s.payService.Pay(r.Context(), request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, requestIDFromContext(r.Context()), err)
|
writeError(w, requestIDFromContext(r.Context()), err)
|
||||||
return
|
return
|
||||||
@ -234,17 +236,6 @@ func writeJSON(w http.ResponseWriter, statusCode int, payload any) {
|
|||||||
_ = json.NewEncoder(w).Encode(payload)
|
_ = json.NewEncoder(w).Encode(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractOrderNo(path string) (string, bool) {
|
|
||||||
if !strings.HasPrefix(path, orderPrefix) {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
orderNo := strings.TrimPrefix(path, orderPrefix)
|
|
||||||
if orderNo == "" || strings.Contains(orderNo, "/") {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
return orderNo, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestIDFromContext(ctx context.Context) string {
|
func requestIDFromContext(ctx context.Context) string {
|
||||||
requestID, _ := ctx.Value(requestIDKey{}).(string)
|
requestID, _ := ctx.Value(requestIDKey{}).(string)
|
||||||
return requestID
|
return requestID
|
||||||
|
|||||||
@ -15,12 +15,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
gatewaypb "chatappgateway/api/proto"
|
|
||||||
"chatappgateway/internal/integration/paygrpc"
|
"chatappgateway/internal/integration/paygrpc"
|
||||||
"chatappgateway/internal/integration/usergrpc"
|
"chatappgateway/internal/integration/usergrpc"
|
||||||
"chatappgateway/internal/service/auth"
|
"chatappgateway/internal/service/auth"
|
||||||
"chatappgateway/internal/service/pay"
|
payservice "chatappgateway/internal/service/pay"
|
||||||
httpserver "chatappgateway/internal/transport/http"
|
httpserver "chatappgateway/internal/transport/http"
|
||||||
|
commonpb "gitea.haiyihy.com/hy/chatappcommon/proto"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
@ -78,16 +78,16 @@ func TestReady(t *testing.T) {
|
|||||||
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
||||||
defer env.Close()
|
defer env.Close()
|
||||||
|
|
||||||
env.readinessChecker.err = errors.New("redis not ready")
|
env.readinessChecker.err = errors.New("chatappuser not ready")
|
||||||
|
|
||||||
resp, responseBody := doRequest(t, env.server.Client(), http.MethodGet, env.server.URL+"/ready", nil)
|
resp, responseBody := doRequest(t, env.server.Client(), http.MethodGet, env.server.URL+"/ready", nil)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusServiceUnavailable, "redis not ready")
|
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusServiceUnavailable, "chatappuser not ready")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoginValidation(t *testing.T) {
|
func TestRegisterValidation(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
||||||
@ -99,26 +99,21 @@ func TestLoginValidation(t *testing.T) {
|
|||||||
message string
|
message string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "password missing account",
|
name: "missing account",
|
||||||
body: `{"login_type":"password","password":"secret"}`,
|
body: `{"password":"secret"}`,
|
||||||
message: "account is required",
|
message: "account is required",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "sms_code missing verify_code",
|
name: "missing password",
|
||||||
body: `{"login_type":"sms_code","country_code":"+86","account":"13800138000"}`,
|
body: `{"account":"demo@example.com"}`,
|
||||||
message: "verify_code is required",
|
message: "password is required",
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "oauth missing provider_token",
|
|
||||||
body: `{"login_type":"oauth","provider":"google"}`,
|
|
||||||
message: "provider_token is required",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
testCase := testCase
|
testCase := testCase
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
resp, body := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/auth/login", strings.NewReader(testCase.body))
|
resp, body := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/users/register", strings.NewReader(testCase.body))
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusBadRequest {
|
if resp.StatusCode != http.StatusBadRequest {
|
||||||
@ -134,25 +129,19 @@ func TestLoginValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if env.userServer.CallCount() != 0 {
|
|
||||||
t.Fatalf("expected user service not to be called, got %d", env.userServer.CallCount())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoginDelegatesToUserGRPC(t *testing.T) {
|
func TestRegisterDelegatesToUserGRPC(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
||||||
defer env.Close()
|
defer env.Close()
|
||||||
|
|
||||||
env.userServer.response = &gatewaypb.LoginResponse{
|
env.userServer.response = &commonpb.RegisterResponse{
|
||||||
UserId: "u-100",
|
UserId: "u-100",
|
||||||
AccessToken: "access-token",
|
AccessToken: "access-token",
|
||||||
RefreshToken: "refresh-token",
|
IsNewUser: true,
|
||||||
ExpiresIn: 7200,
|
Profile: &commonpb.UserProfile{
|
||||||
IsNewUser: true,
|
|
||||||
Profile: &gatewaypb.UserProfile{
|
|
||||||
UserId: "u-100",
|
UserId: "u-100",
|
||||||
Nickname: "Neo",
|
Nickname: "Neo",
|
||||||
AvatarUrl: "https://example.com/avatar.png",
|
AvatarUrl: "https://example.com/avatar.png",
|
||||||
@ -160,22 +149,22 @@ func TestLoginDelegatesToUserGRPC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body := `{
|
body := `{
|
||||||
"login_type":" PASSWORD ",
|
|
||||||
"account":" demo@example.com ",
|
"account":" demo@example.com ",
|
||||||
"password":"secret",
|
"password":" secret ",
|
||||||
|
"nickname":" Neo ",
|
||||||
"device_id":" dev-1 ",
|
"device_id":" dev-1 ",
|
||||||
"platform":" ios ",
|
"platform":" ios ",
|
||||||
"app_version":" 1.0.0 "
|
"app_version":" 1.0.0 "
|
||||||
}`
|
}`
|
||||||
|
|
||||||
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/auth/login", strings.NewReader(body))
|
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/users/register", strings.NewReader(body))
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
t.Fatalf("unexpected status: %d body=%s", resp.StatusCode, string(responseBody))
|
t.Fatalf("unexpected status: %d body=%s", resp.StatusCode, string(responseBody))
|
||||||
}
|
}
|
||||||
|
|
||||||
var payload successResponse[gatewaypb.LoginResponse]
|
var payload successResponse[commonpb.RegisterResponse]
|
||||||
if err := json.Unmarshal(responseBody, &payload); err != nil {
|
if err := json.Unmarshal(responseBody, &payload); err != nil {
|
||||||
t.Fatalf("Unmarshal returned error: %v", err)
|
t.Fatalf("Unmarshal returned error: %v", err)
|
||||||
}
|
}
|
||||||
@ -187,45 +176,90 @@ func TestLoginDelegatesToUserGRPC(t *testing.T) {
|
|||||||
if request == nil {
|
if request == nil {
|
||||||
t.Fatal("expected request to be captured")
|
t.Fatal("expected request to be captured")
|
||||||
}
|
}
|
||||||
if request.LoginType != "password" {
|
|
||||||
t.Fatalf("unexpected login type: %s", request.LoginType)
|
|
||||||
}
|
|
||||||
if request.Account != "demo@example.com" {
|
if request.Account != "demo@example.com" {
|
||||||
t.Fatalf("unexpected account: %q", request.Account)
|
t.Fatalf("unexpected account: %q", request.Account)
|
||||||
}
|
}
|
||||||
if request.DeviceId != "dev-1" {
|
if request.Nickname != "Neo" {
|
||||||
t.Fatalf("unexpected device id: %q", request.DeviceId)
|
t.Fatalf("unexpected nickname: %q", request.Nickname)
|
||||||
}
|
|
||||||
if request.Platform != "ios" {
|
|
||||||
t.Fatalf("unexpected platform: %q", request.Platform)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQueryOrderDelegatesToPayGRPC(t *testing.T) {
|
func TestPayValidation(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
||||||
defer env.Close()
|
defer env.Close()
|
||||||
|
|
||||||
env.payServer.response = &gatewaypb.QueryOrderResponse{
|
testCases := []struct {
|
||||||
OrderNo: "order-001",
|
name string
|
||||||
UserId: "u-100",
|
body string
|
||||||
Status: "paid",
|
message string
|
||||||
Amount: "9.99",
|
}{
|
||||||
Currency: "USD",
|
{
|
||||||
Subject: "VIP",
|
name: "missing order_no",
|
||||||
PayMethod: "apple_pay",
|
body: `{"user_id":"u-1","amount":"9.99"}`,
|
||||||
PaidAt: "2026-04-04T12:00:00Z",
|
message: "order_no is required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing user_id",
|
||||||
|
body: `{"order_no":"ord-1","amount":"9.99"}`,
|
||||||
|
message: "user_id is required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing amount",
|
||||||
|
body: `{"order_no":"ord-1","user_id":"u-1"}`,
|
||||||
|
message: "amount is required",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, responseBody := doRequest(t, env.server.Client(), http.MethodGet, env.server.URL+"/api/v1/pay/orders/order-001", nil)
|
for _, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
resp, body := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/pay", strings.NewReader(testCase.body))
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Fatalf("unexpected status: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload errorResponse
|
||||||
|
if err := json.Unmarshal(body, &payload); err != nil {
|
||||||
|
t.Fatalf("Unmarshal returned error: %v", err)
|
||||||
|
}
|
||||||
|
if payload.Message != testCase.message {
|
||||||
|
t.Fatalf("unexpected message: %s", payload.Message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPayDelegatesToPayGRPC(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
||||||
|
defer env.Close()
|
||||||
|
|
||||||
|
env.payServer.response = &commonpb.PayResponse{
|
||||||
|
PaymentId: "pay-001",
|
||||||
|
OrderNo: "order-001",
|
||||||
|
UserId: "u-100",
|
||||||
|
Status: "processing",
|
||||||
|
Amount: "9.99",
|
||||||
|
Currency: "USD",
|
||||||
|
PayMethod: "apple_pay",
|
||||||
|
Subject: "vip",
|
||||||
|
CreatedAt: "2026-04-04T12:00:00Z",
|
||||||
|
}
|
||||||
|
|
||||||
|
body := `{"order_no":"order-001","user_id":"u-100","amount":"9.99","currency":"USD","pay_method":"apple_pay","subject":"vip"}`
|
||||||
|
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/pay", strings.NewReader(body))
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
t.Fatalf("unexpected status: %d body=%s", resp.StatusCode, string(responseBody))
|
t.Fatalf("unexpected status: %d body=%s", resp.StatusCode, string(responseBody))
|
||||||
}
|
}
|
||||||
|
|
||||||
var payload successResponse[gatewaypb.QueryOrderResponse]
|
var payload successResponse[commonpb.PayResponse]
|
||||||
if err := json.Unmarshal(responseBody, &payload); err != nil {
|
if err := json.Unmarshal(responseBody, &payload); err != nil {
|
||||||
t.Fatalf("Unmarshal returned error: %v", err)
|
t.Fatalf("Unmarshal returned error: %v", err)
|
||||||
}
|
}
|
||||||
@ -242,37 +276,25 @@ func TestQueryOrderDelegatesToPayGRPC(t *testing.T) {
|
|||||||
func TestErrorMapping(t *testing.T) {
|
func TestErrorMapping(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
t.Run("login unauthorized", func(t *testing.T) {
|
t.Run("register unauthorized", func(t *testing.T) {
|
||||||
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
||||||
defer env.Close()
|
defer env.Close()
|
||||||
|
|
||||||
env.userServer.err = status.Error(codes.Unauthenticated, "invalid credentials")
|
env.userServer.err = status.Error(codes.Unauthenticated, "register denied")
|
||||||
|
|
||||||
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/auth/login", strings.NewReader(`{"login_type":"password","account":"demo","password":"wrong"}`))
|
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/users/register", strings.NewReader(`{"account":"demo","password":"secret"}`))
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusUnauthorized, "invalid credentials")
|
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusUnauthorized, "register denied")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("pay not found", func(t *testing.T) {
|
t.Run("pay timeout", func(t *testing.T) {
|
||||||
env := newTestEnv(t, 200*time.Millisecond, 200*time.Millisecond)
|
env := newTestEnv(t, 200*time.Millisecond, 20*time.Millisecond)
|
||||||
defer env.Close()
|
defer env.Close()
|
||||||
|
|
||||||
env.payServer.err = status.Error(codes.NotFound, "order not found")
|
env.payServer.delay = 150 * time.Millisecond
|
||||||
|
|
||||||
resp, responseBody := doRequest(t, env.server.Client(), http.MethodGet, env.server.URL+"/api/v1/pay/orders/missing-order", nil)
|
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/pay", strings.NewReader(`{"order_no":"ord-1","user_id":"u-1","amount":"9.99"}`))
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusNotFound, "order not found")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("login timeout", func(t *testing.T) {
|
|
||||||
env := newTestEnv(t, 20*time.Millisecond, 200*time.Millisecond)
|
|
||||||
defer env.Close()
|
|
||||||
|
|
||||||
env.userServer.delay = 150 * time.Millisecond
|
|
||||||
|
|
||||||
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/auth/login", strings.NewReader(`{"login_type":"password","account":"demo","password":"secret"}`))
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusGatewayTimeout, "upstream request timeout")
|
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusGatewayTimeout, "upstream request timeout")
|
||||||
@ -284,7 +306,7 @@ func TestErrorMapping(t *testing.T) {
|
|||||||
|
|
||||||
env.payServer.err = status.Error(codes.Unavailable, "pay service unavailable")
|
env.payServer.err = status.Error(codes.Unavailable, "pay service unavailable")
|
||||||
|
|
||||||
resp, responseBody := doRequest(t, env.server.Client(), http.MethodGet, env.server.URL+"/api/v1/pay/orders/order-001", nil)
|
resp, responseBody := doRequest(t, env.server.Client(), http.MethodPost, env.server.URL+"/api/v1/pay", strings.NewReader(`{"order_no":"ord-1","user_id":"u-1","amount":"9.99"}`))
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusBadGateway, "pay service unavailable")
|
assertErrorResponse(t, resp.StatusCode, responseBody, http.StatusBadGateway, "pay service unavailable")
|
||||||
@ -320,37 +342,37 @@ func newTestEnv(t *testing.T, userTimeout time.Duration, payTimeout time.Duratio
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
userServer := &mockUserServer{
|
userServer := &mockUserServer{
|
||||||
response: &gatewaypb.LoginResponse{
|
response: &commonpb.RegisterResponse{
|
||||||
UserId: "default-user",
|
UserId: "default-user",
|
||||||
AccessToken: "default-access",
|
AccessToken: "default-access",
|
||||||
RefreshToken: "default-refresh",
|
IsNewUser: true,
|
||||||
ExpiresIn: 3600,
|
Profile: &commonpb.UserProfile{
|
||||||
Profile: &gatewaypb.UserProfile{
|
|
||||||
UserId: "default-user",
|
UserId: "default-user",
|
||||||
Nickname: "default",
|
Nickname: "default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
payServer := &mockPayServer{
|
payServer := &mockPayServer{
|
||||||
response: &gatewaypb.QueryOrderResponse{
|
response: &commonpb.PayResponse{
|
||||||
OrderNo: "default-order",
|
PaymentId: "default-pay",
|
||||||
UserId: "default-user",
|
OrderNo: "default-order",
|
||||||
Status: "pending",
|
UserId: "default-user",
|
||||||
Amount: "0",
|
Status: "processing",
|
||||||
Currency: "USD",
|
Amount: "9.99",
|
||||||
|
Currency: "USD",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
userConn, userClose := newBufConnClient(t, func(server *grpc.Server) {
|
userConn, userClose := newBufConnClient(t, func(server *grpc.Server) {
|
||||||
gatewaypb.RegisterChatAppUserServer(server, userServer)
|
commonpb.RegisterChatAppUserServiceServer(server, userServer)
|
||||||
})
|
})
|
||||||
payConn, payClose := newBufConnClient(t, func(server *grpc.Server) {
|
payConn, payClose := newBufConnClient(t, func(server *grpc.Server) {
|
||||||
gatewaypb.RegisterChatAppPayServer(server, payServer)
|
commonpb.RegisterChatAppPayServiceServer(server, payServer)
|
||||||
})
|
})
|
||||||
|
|
||||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||||
authService := auth.New(usergrpc.New(gatewaypb.NewChatAppUserClient(userConn), userTimeout))
|
authService := auth.New(usergrpc.New(commonpb.NewChatAppUserServiceClient(userConn), userTimeout))
|
||||||
payService := pay.New(paygrpc.New(gatewaypb.NewChatAppPayClient(payConn), payTimeout))
|
payService := payservice.New(paygrpc.New(commonpb.NewChatAppPayServiceClient(payConn), payTimeout))
|
||||||
readinessChecker := &mockReadinessChecker{}
|
readinessChecker := &mockReadinessChecker{}
|
||||||
handler := httpserver.New("chatappgateway", ":0", 2*time.Second, logger, authService, payService, readinessChecker).Handler()
|
handler := httpserver.New("chatappgateway", ":0", 2*time.Second, logger, authService, payService, readinessChecker).Handler()
|
||||||
|
|
||||||
@ -453,19 +475,17 @@ type errorResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockUserServer struct {
|
type mockUserServer struct {
|
||||||
gatewaypb.UnimplementedChatAppUserServer
|
commonpb.UnimplementedChatAppUserServiceServer
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
callCount int
|
lastReq *commonpb.RegisterRequest
|
||||||
lastReq *gatewaypb.LoginRequest
|
response *commonpb.RegisterResponse
|
||||||
response *gatewaypb.LoginResponse
|
err error
|
||||||
err error
|
delay time.Duration
|
||||||
delay time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mockUserServer) Login(ctx context.Context, request *gatewaypb.LoginRequest) (*gatewaypb.LoginResponse, error) {
|
func (s *mockUserServer) Register(ctx context.Context, request *commonpb.RegisterRequest) (*commonpb.RegisterResponse, error) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.callCount++
|
|
||||||
copied := *request
|
copied := *request
|
||||||
s.lastReq = &copied
|
s.lastReq = &copied
|
||||||
response := s.response
|
response := s.response
|
||||||
@ -487,13 +507,7 @@ func (s *mockUserServer) Login(ctx context.Context, request *gatewaypb.LoginRequ
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mockUserServer) CallCount() int {
|
func (s *mockUserServer) LastRequest() *commonpb.RegisterRequest {
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
return s.callCount
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *mockUserServer) LastRequest() *gatewaypb.LoginRequest {
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.lastReq == nil {
|
if s.lastReq == nil {
|
||||||
@ -504,16 +518,16 @@ func (s *mockUserServer) LastRequest() *gatewaypb.LoginRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockPayServer struct {
|
type mockPayServer struct {
|
||||||
gatewaypb.UnimplementedChatAppPayServer
|
commonpb.UnimplementedChatAppPayServiceServer
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
lastReq *gatewaypb.QueryOrderRequest
|
lastReq *commonpb.PayRequest
|
||||||
response *gatewaypb.QueryOrderResponse
|
response *commonpb.PayResponse
|
||||||
err error
|
err error
|
||||||
delay time.Duration
|
delay time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mockPayServer) QueryOrder(ctx context.Context, request *gatewaypb.QueryOrderRequest) (*gatewaypb.QueryOrderResponse, error) {
|
func (s *mockPayServer) Pay(ctx context.Context, request *commonpb.PayRequest) (*commonpb.PayResponse, error) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
copied := *request
|
copied := *request
|
||||||
s.lastReq = &copied
|
s.lastReq = &copied
|
||||||
@ -536,7 +550,7 @@ func (s *mockPayServer) QueryOrder(ctx context.Context, request *gatewaypb.Query
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mockPayServer) LastRequest() *gatewaypb.QueryOrderRequest {
|
func (s *mockPayServer) LastRequest() *commonpb.PayRequest {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.lastReq == nil {
|
if s.lastReq == nil {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user