diff --git a/server-rs/crates/api-server/src/runtime_profile.rs b/server-rs/crates/api-server/src/runtime_profile.rs index 3627ab371..287cae8a6 100644 --- a/server-rs/crates/api-server/src/runtime_profile.rs +++ b/server-rs/crates/api-server/src/runtime_profile.rs @@ -1511,6 +1511,8 @@ fn is_runtime_profile_domain_error(message: &str) -> bool { fn is_runtime_profile_membership_domain_error(message: &str) -> bool { message.starts_with("会员升级被拒绝: ") || message.starts_with("会员档位") + || message.starts_with("会员商品 product_id 无法解析: ") + || message.starts_with("充值商品类型无效:会员商品已迁移到会员档位目录") || matches!( message, "当前不是会员,请直接选择套餐购买" @@ -2610,6 +2612,24 @@ mod tests { ); } + #[test] + fn membership_business_rejections_map_to_bad_request() { + // 中文注释:会员商品迁档位目录后的业务拒绝必须按 400 返回;漏掉前缀匹配会落到 502, + // 让后台与下单端把可读的业务提示当成基础设施故障。目录缺行属于部署配置错误,保持 502。 + for message in [ + "会员升级被拒绝: cycle_kind_change_unsupported", + "会员商品 product_id 无法解析: membership-pro-yearly", + "充值商品类型无效:会员商品已迁移到会员档位目录", + ] { + let error = map_runtime_profile_client_error(SpacetimeClientError::Procedure( + message.to_string(), + )); + + assert_eq!(error.status_code(), StatusCode::BAD_REQUEST, "{message}"); + assert_eq!(error.message(), message); + } + } + #[test] fn admin_profile_code_time_parser_accepts_optional_rfc3339_values() { assert_eq!(