修复问题:阿里云未配置/禁用会被记录成可重试的外部 503 失败。

This commit is contained in:
2026-07-09 13:53:12 +00:00
parent 1aaf075724
commit b342f1eecb
4 changed files with 75 additions and 35 deletions
@@ -17,12 +17,9 @@ pub(crate) async fn segment_image_with_aliyun_matting(
image: &DownloadedOpenAiImage,
log_label: &str,
) -> Result<DownloadedOpenAiImage, AppError> {
let matting_client = state.matting_client().ok_or_else(|| {
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_details(json!({
"provider": "aliyun-matting",
"message": "阿里云抠图客户端未配置或未启用。",
}))
})?;
let matting_client = state
.matting_client()
.ok_or_else(aliyun_matting_unconfigured_error)?;
let file_name = format!("{log_label}.png");
let started_at = std::time::Instant::now();
@@ -81,6 +78,14 @@ fn aliyun_matting_error_is_transport(message: &str) -> bool {
aliyun_matting_error_http_status(message).is_none()
}
fn aliyun_matting_unconfigured_error() -> AppError {
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_details(json!({
"provider": "aliyun-matting",
"message": "阿里云抠图客户端未配置或未启用。",
"externalCallAttempted": false,
}))
}
#[cfg(test)]
mod tests {
use super::*;
@@ -122,4 +127,11 @@ mod tests {
"通用抠图接口返回失败(HTTP 429Code=Throttled):QPS exceeded"
));
}
#[test]
fn aliyun_matting_unconfigured_error_marks_no_external_call_attempt() {
let error = aliyun_matting_unconfigured_error();
assert!(!crate::external_api_audit::matting_failure_external_call_attempted(&error));
}
}
@@ -2238,21 +2238,23 @@ async fn remove_editor_character_animation_frame_backgrounds(
error_details = ?error.details(),
"editor_animation_frame_aliyun_matting_fallback_to_local"
);
crate::external_api_audit::record_matting_external_api_failure(
state,
audit,
"aliyun-matting",
state.config.aliyun_matting_endpoint.clone(),
"editor-character-animation-frame-matting",
"aliyun_segment",
crate::external_api_audit::matting_failure_audit_status_code(&error),
crate::external_api_audit::matting_failure_audit_timeout(&error),
crate::external_api_audit::matting_failure_audit_latency_ms(&error),
error.message().to_string(),
crate::external_api_audit::matting_failure_audit_raw_excerpt(&error)
.or_else(|| Some(format!("frame_index={frame_index}"))),
)
.await;
if crate::external_api_audit::matting_failure_external_call_attempted(&error) {
crate::external_api_audit::record_matting_external_api_failure(
state,
audit,
"aliyun-matting",
state.config.aliyun_matting_endpoint.clone(),
"editor-character-animation-frame-matting",
"aliyun_segment",
crate::external_api_audit::matting_failure_audit_status_code(&error),
crate::external_api_audit::matting_failure_audit_timeout(&error),
crate::external_api_audit::matting_failure_audit_latency_ms(&error),
error.message().to_string(),
crate::external_api_audit::matting_failure_audit_raw_excerpt(&error)
.or_else(|| Some(format!("frame_index={frame_index}"))),
)
.await;
}
remove_editor_generated_green_screen_background(&image, screen_color)?
}
};
@@ -2304,20 +2304,22 @@ async fn fallback_editor_screen_background_removal(
error_details = ?error.details(),
"editor_aliyun_matting_fallback_to_local_screen_background_removal"
);
crate::external_api_audit::record_matting_external_api_failure(
state,
audit,
"aliyun-matting",
state.config.aliyun_matting_endpoint.clone(),
"editor-screen-background-removal",
"aliyun_segment",
crate::external_api_audit::matting_failure_audit_status_code(&error),
crate::external_api_audit::matting_failure_audit_timeout(&error),
crate::external_api_audit::matting_failure_audit_latency_ms(&error),
error.message().to_string(),
crate::external_api_audit::matting_failure_audit_raw_excerpt(&error),
)
.await;
if crate::external_api_audit::matting_failure_external_call_attempted(&error) {
crate::external_api_audit::record_matting_external_api_failure(
state,
audit,
"aliyun-matting",
state.config.aliyun_matting_endpoint.clone(),
"editor-screen-background-removal",
"aliyun_segment",
crate::external_api_audit::matting_failure_audit_status_code(&error),
crate::external_api_audit::matting_failure_audit_timeout(&error),
crate::external_api_audit::matting_failure_audit_latency_ms(&error),
error.message().to_string(),
crate::external_api_audit::matting_failure_audit_raw_excerpt(&error),
)
.await;
}
remove_editor_generated_green_screen_background(image, screen_color)
}
}
@@ -248,6 +248,14 @@ pub(crate) fn matting_failure_audit_latency_ms(error: &AppError) -> Option<u64>
.and_then(Value::as_u64)
}
pub(crate) fn matting_failure_external_call_attempted(error: &AppError) -> bool {
error
.details()
.and_then(|details| details.get("externalCallAttempted"))
.and_then(Value::as_bool)
.unwrap_or(true)
}
pub(crate) fn matting_failure_audit_raw_excerpt(error: &AppError) -> Option<String> {
error
.details()
@@ -741,4 +749,20 @@ mod tests {
assert_eq!(tracking.metadata["retryable"], false);
assert_eq!(tracking.metadata["latencyMs"], 67);
}
#[test]
fn matting_failure_external_call_attempted_defaults_to_true() {
let attempted = AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": "aliyun-matting",
"message": "真实上游失败",
}));
let skipped = AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_details(json!({
"provider": "aliyun-matting",
"message": "阿里云抠图客户端未配置或未启用。",
"externalCallAttempted": false,
}));
assert!(matting_failure_external_call_attempted(&attempted));
assert!(!matting_failure_external_call_attempted(&skipped));
}
}