package xerr import ( "errors" "fmt" ) // Code 是服务内部稳定错误原因。 // 它对应 google.rpc.ErrorInfo.reason,而不是 HTTP code 或 gRPC code。 type Code string const ( // InvalidArgument 表示请求字段、格式或领域约束不合法。 InvalidArgument Code = "INVALID_ARGUMENT" // NotFound 表示目标实体不存在。 NotFound Code = "NOT_FOUND" // Conflict 表示唯一键、版本、幂等或状态冲突。 Conflict Code = "CONFLICT" // RoomClosed 表示房间已被后台关闭,客户端不能再进入。 RoomClosed Code = "ROOM_CLOSED" // Unauthorized 表示身份不存在或认证失败。 Unauthorized Code = "UNAUTHORIZED" // PermissionDenied 表示已认证但没有执行权限。 PermissionDenied Code = "PERMISSION_DENIED" // ProfileRequired 表示已登录用户尚未完成 App 最小注册资料,不能进入门禁保护业务。 ProfileRequired Code = "PROFILE_REQUIRED" // Unavailable 表示依赖或当前服务临时不可用。 Unavailable Code = "UNAVAILABLE" // Internal 表示不能安全暴露更细原因的内部失败。 Internal Code = "INTERNAL_ERROR" // AuthFailed 统一短号不存在、未设置密码、密码错误和三方凭证拒绝,避免身份枚举。 AuthFailed Code = "AUTH_FAILED" // AuthLoginBlocked 表示登录入口命中地区、语言、时区或 IP 风控策略。 AuthLoginBlocked Code = "AUTH_LOGIN_BLOCKED" // PasswordAlreadySet 表示用户已经设置过密码,不能重复初始化密码身份。 PasswordAlreadySet Code = "PASSWORD_ALREADY_SET" // UserDisabled 表示用户被禁用或封禁。 UserDisabled Code = "USER_DISABLED" // SessionExpired 表示 refresh token 或会话已过期。 SessionExpired Code = "SESSION_EXPIRED" // SessionRevoked 表示 refresh token 或会话已被主动失效。 SessionRevoked Code = "SESSION_REVOKED" // DisplayUserIDInvalid 表示短号格式不符合 user-service 规则。 DisplayUserIDInvalid Code = "DISPLAY_USER_ID_INVALID" // DisplayUserIDExists 表示短号已被 active 用户占用。 DisplayUserIDExists Code = "DISPLAY_USER_ID_EXISTS" // DisplayUserIDCooldown 表示用户仍处在短号修改冷却期。 DisplayUserIDCooldown Code = "DISPLAY_USER_ID_COOLDOWN" // DisplayUserIDNotFound 表示 active 短号不存在。 DisplayUserIDNotFound Code = "DISPLAY_USER_ID_NOT_FOUND" // DisplayUserIDAllocateFailed 表示默认短号分配重试耗尽。 DisplayUserIDAllocateFailed Code = "DISPLAY_USER_ID_ALLOCATE_FAILED" // DisplayUserIDPrettyNotAvailable 表示靓号不可申请、已被租用或被预留。 DisplayUserIDPrettyNotAvailable Code = "DISPLAY_USER_ID_PRETTY_NOT_AVAILABLE" // DisplayUserIDPrettyActive 表示用户已有 active 靓号租约。 DisplayUserIDPrettyActive Code = "DISPLAY_USER_ID_PRETTY_ACTIVE" // DisplayUserIDLeaseExpired 表示靓号租约已经过期或不能再被当前操作使用。 DisplayUserIDLeaseExpired Code = "DISPLAY_USER_ID_LEASE_EXPIRED" // DisplayUserIDPaymentRequired 表示申请靓号缺少有效支付凭证。 DisplayUserIDPaymentRequired Code = "DISPLAY_USER_ID_PAYMENT_REQUIRED" // CountryChangeCooldown 表示用户仍处在国家修改冷却期。 CountryChangeCooldown Code = "COUNTRY_CHANGE_COOLDOWN" // CountryNotFound 表示国家主数据不存在或已经停用。 CountryNotFound Code = "COUNTRY_NOT_FOUND" // RegionNotFound 表示区域主数据不存在。 RegionNotFound Code = "REGION_NOT_FOUND" // RegionCountryConflict 表示国家已经归属其他 active 区域。 RegionCountryConflict Code = "REGION_COUNTRY_CONFLICT" // RegionDisabled 表示区域已经停用,不能继续配置国家。 RegionDisabled Code = "REGION_DISABLED" // RegionMismatch 表示当前动作要求双方同一区域,但用户、组织或上级身份的实时区域不一致。 RegionMismatch Code = "REGION_MISMATCH" // InvalidInviteCode 表示邀请码不存在、停用、跨 App、邀请人不可用或用户尝试自邀。 InvalidInviteCode Code = "INVALID_INVITE_CODE" // DeviceAlreadyRegistered 表示同一注册设备的账号数量已经达到注册上限,不能继续创建新账号。 DeviceAlreadyRegistered Code = "DEVICE_ALREADY_REGISTERED" // InsufficientBalance 表示钱包余额不足。 InsufficientBalance Code = "INSUFFICIENT_BALANCE" // DuplicateBillingCommand 表示账务幂等键命中已处理命令。 DuplicateBillingCommand Code = "DUPLICATE_BILLING_COMMAND" // LedgerConflict 表示账本并发写入或余额版本冲突。 LedgerConflict Code = "LEDGER_CONFLICT" // CoinSellerNotActive 表示目标用户不存在 active 币商身份。 CoinSellerNotActive Code = "COIN_SELLER_NOT_ACTIVE" // CoinSellerStockTypeInvalid 表示币商库存入账类型不属于支持范围。 CoinSellerStockTypeInvalid Code = "COIN_SELLER_STOCK_TYPE_INVALID" // CoinSellerStockAmountInvalid 表示币商库存金币数或线下付款金额不合法。 CoinSellerStockAmountInvalid Code = "COIN_SELLER_STOCK_AMOUNT_INVALID" // CoinSellerPaymentRefDuplicated 表示同一 USDT 收款凭证已经入账。 CoinSellerPaymentRefDuplicated Code = "COIN_SELLER_PAYMENT_REF_DUPLICATED" // IdempotencyConflict 表示 command_id 相同但业务 payload 不一致。 IdempotencyConflict Code = "IDEMPOTENCY_CONFLICT" // VIPLevelNotFound 表示目标 VIP 等级不存在。 VIPLevelNotFound Code = "VIP_LEVEL_NOT_FOUND" // VIPLevelDisabled 表示目标 VIP 等级当前不可购买。 VIPLevelDisabled Code = "VIP_LEVEL_DISABLED" // VIPDowngradeNotAllowed 表示用户当前有效 VIP 等级高于本次购买目标等级。 VIPDowngradeNotAllowed Code = "VIP_DOWNGRADE_NOT_ALLOWED" // VIPRechargeRequired 表示目标 VIP 等级需要用户先达到累计充值门槛。 VIPRechargeRequired Code = "VIP_RECHARGE_REQUIRED" // RedPacketDisabled 表示红包功能未启用。 RedPacketDisabled Code = "RED_PACKET_DISABLED" // RedPacketInvalidAmountTier 表示红包金额不在后台配置档位内。 RedPacketInvalidAmountTier Code = "INVALID_AMOUNT_TIER" // RedPacketInvalidCountTier 表示红包数量不在后台配置档位内。 RedPacketInvalidCountTier Code = "INVALID_COUNT_TIER" // RedPacketAmountTooSmall 表示红包总金额小于红包份数,无法保证每份至少 1 金币。 RedPacketAmountTooSmall Code = "RED_PACKET_AMOUNT_TOO_SMALL" // RedPacketDailyLimitReached 表示用户达到 UTC 当日发送次数上限。 RedPacketDailyLimitReached Code = "DAILY_SEND_LIMIT_REACHED" // RedPacketNotOpen 表示延迟红包尚未到打开时间。 RedPacketNotOpen Code = "RED_PACKET_NOT_OPEN" // RedPacketExpired 表示红包已经过期。 RedPacketExpired Code = "RED_PACKET_EXPIRED" // RedPacketAlreadyClaimed 表示当前用户已领取过该红包。 RedPacketAlreadyClaimed Code = "RED_PACKET_ALREADY_CLAIMED" // RedPacketSoldOut 表示红包已经被抢完。 RedPacketSoldOut Code = "RED_PACKET_SOLD_OUT" // RuleNotActive 表示活动规则当前不可用。 RuleNotActive Code = "RULE_NOT_ACTIVE" // EventAlreadyConsumed 表示活动事件消费幂等命中。 EventAlreadyConsumed Code = "EVENT_ALREADY_CONSUMED" // RewardPending 表示奖励仍在处理中,不能重复结算。 RewardPending Code = "REWARD_PENDING" // InvalidSection 表示 App 消息分区不属于 system/activity。 InvalidSection Code = "INVALID_SECTION" // MessageNotFound 表示消息不存在、已删除、已过期、已撤回或不属于当前用户。 MessageNotFound Code = "MESSAGE_NOT_FOUND" // MessageRecalled 表示消息已撤回,不能继续展示或操作。 MessageRecalled Code = "MESSAGE_RECALLED" // PageTokenInvalid 表示分页游标不能解析或不属于当前查询。 PageTokenInvalid Code = "PAGE_TOKEN_INVALID" // RequestConflict 表示同 command_id 的 fanout 请求 payload 冲突。 RequestConflict Code = "REQUEST_CONFLICT" // ProducerEventConflict 表示同 producer_event_id 的入箱请求 payload 冲突。 ProducerEventConflict Code = "PRODUCER_EVENT_CONFLICT" ) // Error 是 domain/service 层统一返回的业务错误。 // 该类型不依赖任何传输层包,避免业务代码直接绑定 gRPC status。 type Error struct { Code Code Message string } // Error 实现标准 error 接口。 func (e *Error) Error() string { return fmt.Sprintf("%s: %s", e.Code, e.Message) } // Is 让 errors.Is 可以按稳定 reason 判断业务错误。 func (e *Error) Is(target error) bool { typed, ok := target.(*Error) return ok && e.Code == typed.Code } // New 构造带稳定 reason 的业务错误。 func New(code Code, message string) error { return &Error{ Code: code, Message: message, } } // As 提取当前仓库约定的业务错误。 func As(err error) (*Error, bool) { if err == nil { return nil, false } var typed *Error if errors.As(err, &typed) { return typed, true } return nil, false } // CodeOf 安全提取错误 reason。 func CodeOf(err error) Code { if typed, ok := As(err); ok { return typed.Code } if err == nil { return "" } return Internal } // MessageOf 安全提取可返回给内部调用方的短错误描述。 func MessageOf(err error) string { if typed, ok := As(err); ok { return typed.Message } if err == nil { return "" } return "internal error" }