上游瞬时状态不再回落成 400

- 408 / 425 / 429 与 platform-tripo 的 is_retryable 口径对齐,分别回 504 / 502 / 429
- 补上这三档与客户端 4xx 的分支测试
This commit is contained in:
2026-09-22 15:15:27 +08:00
parent e18c6ee502
commit cda7724cae
@@ -48,25 +48,62 @@ pub(crate) fn map_pricing_store_error(error: EditorGenerationPricingError) -> Ap
}))
}
/// provider 错误归一:参数类回 400,其余按“上游不可用/上游拒绝”分流。
/// provider 错误归一:参数类回 400,瞬时状态按可重试语义上报,其余按“上游不可用/上游拒绝”分流。
pub(crate) fn map_provider_error(error: TripoError) -> AppError {
let (status, reason) = match &error {
TripoError::InvalidParameters { .. } | TripoError::SdkInvalidArgument(_) => {
(StatusCode::BAD_REQUEST, "tripo-invalid-parameters")
}
TripoError::Api { status, .. } if matches!(status, Some(400..=499)) => {
(StatusCode::BAD_REQUEST, "tripo-api-rejected")
}
TripoError::TaskFailure { .. } => (StatusCode::BAD_GATEWAY, "tripo-task-failed"),
TripoError::OutputSchema { .. } => (StatusCode::BAD_GATEWAY, "tripo-output-schema"),
TripoError::Request { .. } | TripoError::Sdk { .. } | TripoError::Api { .. } => {
(StatusCode::BAD_GATEWAY, "tripo-upstream-error")
}
let (status, reason, message) = match &error {
TripoError::InvalidParameters { .. } | TripoError::SdkInvalidArgument(_) => (
StatusCode::BAD_REQUEST,
"tripo-invalid-parameters",
error.to_string(),
),
// 408 / 425 / 429 与 platform-tripo 的 is_retryable 口径一致,属于瞬时状态:
// 混进 400 会让客户端把限流 / 上游超时误读成自己参数写错了。
TripoError::Api {
status: Some(429), ..
} => (
StatusCode::TOO_MANY_REQUESTS,
"tripo-api-rate-limited",
"3D 生成服务当前限流,请稍后重试。".to_string(),
),
TripoError::Api {
status: Some(408), ..
} => (
StatusCode::GATEWAY_TIMEOUT,
"tripo-api-timeout",
"3D 生成服务响应超时,请稍后重试。".to_string(),
),
TripoError::Api {
status: Some(425), ..
} => (
StatusCode::BAD_GATEWAY,
"tripo-api-retry-later",
"3D 生成服务暂时无法处理该请求,请稍后重试。".to_string(),
),
TripoError::Api { status, .. } if matches!(status, Some(400..=499)) => (
StatusCode::BAD_REQUEST,
"tripo-api-rejected",
error.to_string(),
),
TripoError::TaskFailure { .. } => (
StatusCode::BAD_GATEWAY,
"tripo-task-failed",
error.to_string(),
),
TripoError::OutputSchema { .. } => (
StatusCode::BAD_GATEWAY,
"tripo-output-schema",
error.to_string(),
),
TripoError::Request { .. } | TripoError::Sdk { .. } | TripoError::Api { .. } => (
StatusCode::BAD_GATEWAY,
"tripo-upstream-error",
error.to_string(),
),
};
AppError::from_status(status).with_details(json!({
"provider": TRIPO_PROVIDER,
"reason": reason,
"message": error.to_string(),
"message": message,
}))
}
@@ -100,4 +137,33 @@ mod tests {
});
assert_eq!(error.status_code(), StatusCode::BAD_GATEWAY);
}
/// 408 / 425 / 429 是 platform-tripo 认定的瞬时状态,不能按「参数有问题」回 400。
#[test]
fn provider_transient_4xx_is_not_reported_as_bad_request() {
for (status, expected) in [
(429u16, StatusCode::TOO_MANY_REQUESTS),
(408, StatusCode::GATEWAY_TIMEOUT),
(425, StatusCode::BAD_GATEWAY),
] {
let error = map_provider_error(TripoError::Api {
code: 1001,
message: Some("transient".to_string()),
suggestion: None,
status: Some(status),
});
assert_eq!(error.status_code(), expected, "status {status}");
}
}
#[test]
fn provider_client_side_4xx_stays_bad_request() {
let error = map_provider_error(TripoError::Api {
code: 1002,
message: Some("bad parameter".to_string()),
suggestion: None,
status: Some(400),
});
assert_eq!(error.status_code(), StatusCode::BAD_REQUEST);
}
}