135 lines
8.1 KiB
Go
135 lines
8.1 KiB
Go
package xerr
|
||
|
||
import "google.golang.org/grpc/codes"
|
||
|
||
const (
|
||
httpStatusBadRequest = 400
|
||
httpStatusUnauthorized = 401
|
||
httpStatusForbidden = 403
|
||
httpStatusNotFound = 404
|
||
httpStatusConflict = 409
|
||
httpStatusInternalServerError = 500
|
||
httpStatusBadGateway = 502
|
||
)
|
||
|
||
// Spec 是错误码注册表中的一条稳定契约。
|
||
// GRPCCode 是服务间传输语义;PublicCode/PublicMessage 是 gateway 对 App 暴露的 envelope。
|
||
type Spec struct {
|
||
GRPCCode codes.Code
|
||
HTTPStatus int
|
||
PublicCode string
|
||
PublicMessage string
|
||
Retryable bool
|
||
}
|
||
|
||
var catalog = map[Code]Spec{
|
||
InvalidArgument: spec(codes.InvalidArgument, httpStatusBadRequest, InvalidArgument, "invalid argument"),
|
||
NotFound: spec(codes.NotFound, httpStatusNotFound, NotFound, "not found"),
|
||
Conflict: spec(codes.FailedPrecondition, httpStatusConflict, Conflict, "conflict"),
|
||
RoomClosed: spec(codes.FailedPrecondition, httpStatusConflict, RoomClosed, "room closed"),
|
||
|
||
Unauthorized: spec(codes.Unauthenticated, httpStatusUnauthorized, Unauthorized, "unauthorized"),
|
||
PermissionDenied: spec(codes.PermissionDenied, httpStatusForbidden, PermissionDenied, "permission denied"),
|
||
ProfileRequired: spec(codes.PermissionDenied, httpStatusForbidden, ProfileRequired, "profile required"),
|
||
Unavailable: {
|
||
GRPCCode: codes.Unavailable,
|
||
HTTPStatus: httpStatusBadGateway,
|
||
PublicCode: "UPSTREAM_ERROR",
|
||
PublicMessage: "upstream service error",
|
||
Retryable: true,
|
||
},
|
||
Internal: spec(codes.Internal, httpStatusInternalServerError, Internal, "internal error"),
|
||
|
||
AuthFailed: spec(codes.Unauthenticated, httpStatusUnauthorized, AuthFailed, "authentication failed"),
|
||
AuthLoginBlocked: spec(codes.PermissionDenied, httpStatusForbidden, AuthLoginBlocked, "login blocked"),
|
||
PasswordAlreadySet: spec(codes.AlreadyExists, httpStatusConflict, PasswordAlreadySet, "conflict"),
|
||
UserDisabled: spec(codes.PermissionDenied, httpStatusForbidden, UserDisabled, "permission denied"),
|
||
SessionExpired: spec(codes.Unauthenticated, httpStatusUnauthorized, SessionExpired, "unauthorized"),
|
||
SessionRevoked: spec(codes.Unauthenticated, httpStatusUnauthorized, SessionRevoked, "unauthorized"),
|
||
|
||
DisplayUserIDInvalid: spec(codes.InvalidArgument, httpStatusBadRequest, DisplayUserIDInvalid, "invalid argument"),
|
||
DisplayUserIDExists: spec(codes.AlreadyExists, httpStatusConflict, DisplayUserIDExists, "conflict"),
|
||
DisplayUserIDCooldown: spec(codes.FailedPrecondition, httpStatusConflict, DisplayUserIDCooldown, "conflict"),
|
||
DisplayUserIDNotFound: spec(codes.NotFound, httpStatusNotFound, DisplayUserIDNotFound, "not found"),
|
||
DisplayUserIDAllocateFailed: spec(codes.Internal, httpStatusInternalServerError, Internal, "internal error"),
|
||
DisplayUserIDPrettyNotAvailable: spec(codes.FailedPrecondition, httpStatusConflict, DisplayUserIDPrettyNotAvailable, "conflict"),
|
||
DisplayUserIDPrettyActive: spec(codes.FailedPrecondition, httpStatusConflict, DisplayUserIDPrettyActive, "conflict"),
|
||
DisplayUserIDLeaseExpired: spec(codes.FailedPrecondition, httpStatusConflict, DisplayUserIDLeaseExpired, "conflict"),
|
||
DisplayUserIDPaymentRequired: spec(codes.FailedPrecondition, httpStatusConflict, DisplayUserIDPaymentRequired, "conflict"),
|
||
|
||
CountryChangeCooldown: spec(codes.FailedPrecondition, httpStatusConflict, CountryChangeCooldown, "country change is cooling down"),
|
||
CountryNotFound: spec(codes.NotFound, httpStatusNotFound, CountryNotFound, "not found"),
|
||
RegionNotFound: spec(codes.NotFound, httpStatusNotFound, RegionNotFound, "not found"),
|
||
RegionCountryConflict: spec(codes.FailedPrecondition, httpStatusConflict, RegionCountryConflict, "conflict"),
|
||
RegionDisabled: spec(codes.FailedPrecondition, httpStatusConflict, RegionDisabled, "conflict"),
|
||
InvalidInviteCode: spec(codes.InvalidArgument, httpStatusBadRequest, InvalidInviteCode, "invalid argument"),
|
||
DeviceAlreadyRegistered: spec(
|
||
codes.FailedPrecondition,
|
||
httpStatusConflict,
|
||
DeviceAlreadyRegistered,
|
||
"conflict",
|
||
),
|
||
|
||
InsufficientBalance: spec(codes.FailedPrecondition, httpStatusConflict, InsufficientBalance, "insufficient balance"),
|
||
DuplicateBillingCommand: spec(codes.AlreadyExists, httpStatusConflict, DuplicateBillingCommand, "conflict"),
|
||
LedgerConflict: spec(codes.Aborted, httpStatusConflict, LedgerConflict, "conflict"),
|
||
CoinSellerNotActive: spec(codes.FailedPrecondition, httpStatusConflict, CoinSellerNotActive, "conflict"),
|
||
CoinSellerStockTypeInvalid: spec(codes.InvalidArgument, httpStatusBadRequest, CoinSellerStockTypeInvalid, "invalid argument"),
|
||
CoinSellerStockAmountInvalid: spec(codes.InvalidArgument, httpStatusBadRequest, CoinSellerStockAmountInvalid, "invalid argument"),
|
||
CoinSellerPaymentRefDuplicated: spec(codes.AlreadyExists, httpStatusConflict, CoinSellerPaymentRefDuplicated, "conflict"),
|
||
IdempotencyConflict: spec(codes.AlreadyExists, httpStatusConflict, IdempotencyConflict, "conflict"),
|
||
VIPLevelNotFound: spec(codes.NotFound, httpStatusNotFound, VIPLevelNotFound, "not found"),
|
||
VIPLevelDisabled: spec(codes.FailedPrecondition, httpStatusConflict, VIPLevelDisabled, "conflict"),
|
||
VIPDowngradeNotAllowed: spec(codes.FailedPrecondition, httpStatusConflict, VIPDowngradeNotAllowed, "conflict"),
|
||
VIPRechargeRequired: spec(codes.FailedPrecondition, httpStatusConflict, VIPRechargeRequired, "conflict"),
|
||
|
||
RedPacketDisabled: spec(codes.FailedPrecondition, httpStatusConflict, RedPacketDisabled, "conflict"),
|
||
RedPacketInvalidAmountTier: spec(codes.InvalidArgument, httpStatusBadRequest, RedPacketInvalidAmountTier, "invalid argument"),
|
||
RedPacketInvalidCountTier: spec(codes.InvalidArgument, httpStatusBadRequest, RedPacketInvalidCountTier, "invalid argument"),
|
||
RedPacketAmountTooSmall: spec(codes.InvalidArgument, httpStatusBadRequest, RedPacketAmountTooSmall, "invalid argument"),
|
||
RedPacketDailyLimitReached: spec(codes.ResourceExhausted, 429, RedPacketDailyLimitReached, "rate limited"),
|
||
RedPacketNotOpen: spec(codes.FailedPrecondition, httpStatusConflict, RedPacketNotOpen, "conflict"),
|
||
RedPacketExpired: spec(codes.FailedPrecondition, httpStatusConflict, RedPacketExpired, "conflict"),
|
||
RedPacketAlreadyClaimed: spec(codes.AlreadyExists, httpStatusConflict, RedPacketAlreadyClaimed, "conflict"),
|
||
RedPacketSoldOut: spec(codes.FailedPrecondition, httpStatusConflict, RedPacketSoldOut, "conflict"),
|
||
|
||
RuleNotActive: spec(codes.FailedPrecondition, httpStatusConflict, RuleNotActive, "conflict"),
|
||
EventAlreadyConsumed: spec(codes.AlreadyExists, httpStatusConflict, EventAlreadyConsumed, "conflict"),
|
||
RewardPending: spec(codes.FailedPrecondition, httpStatusConflict, RewardPending, "conflict"),
|
||
|
||
InvalidSection: spec(codes.InvalidArgument, httpStatusBadRequest, InvalidSection, "invalid argument"),
|
||
MessageNotFound: spec(codes.NotFound, httpStatusNotFound, MessageNotFound, "not found"),
|
||
MessageRecalled: spec(codes.NotFound, httpStatusNotFound, MessageRecalled, "not found"),
|
||
PageTokenInvalid: spec(codes.InvalidArgument, httpStatusBadRequest, PageTokenInvalid, "invalid argument"),
|
||
RequestConflict: spec(codes.FailedPrecondition, httpStatusConflict, RequestConflict, "conflict"),
|
||
ProducerEventConflict: spec(codes.FailedPrecondition, httpStatusConflict, ProducerEventConflict, "conflict"),
|
||
}
|
||
|
||
func spec(grpcCode codes.Code, httpStatus int, publicCode Code, publicMessage string) Spec {
|
||
return Spec{
|
||
GRPCCode: grpcCode,
|
||
HTTPStatus: httpStatus,
|
||
PublicCode: string(publicCode),
|
||
PublicMessage: publicMessage,
|
||
}
|
||
}
|
||
|
||
// SpecOf 返回业务码的注册表项。未知业务码降级为 INTERNAL_ERROR,避免泄漏未登记 reason。
|
||
func SpecOf(code Code) Spec {
|
||
if registered, ok := catalog[code]; ok {
|
||
return registered
|
||
}
|
||
|
||
return catalog[Internal]
|
||
}
|
||
|
||
// RegisteredCodes 返回当前注册表中全部业务码,供测试和文档生成使用。
|
||
func RegisteredCodes() []Code {
|
||
registered := make([]Code, 0, len(catalog))
|
||
for code := range catalog {
|
||
registered = append(registered, code)
|
||
}
|
||
|
||
return registered
|
||
}
|