From 48ba8b3133c0c16f0feb78291336641725b26422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 23 Sep 2026 12:15:02 +0800 Subject: [PATCH] =?UTF-8?q?3D=20=E7=BB=93=E6=9E=9C=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E5=A4=B1=E8=B4=A5=E4=B8=8D=E5=86=8D=E8=90=BD=E3=80=8C?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BD=86=20result=20=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=E3=80=8D=E7=9A=84=E7=BB=88=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - job_result_payload 改为返回 Result,失败按 500 上报服务端问题,去掉 result:null 兜底形状 - worker 持久化前用 ? 把失败交给既有失败链路:任务按失败终态处理并冲正扣费 - worker 测试夹具同步改为 expect,断言完成结果可序列化 --- .../crates/api-server/src/tripo3d/result.rs | 25 +++++++++++-------- .../crates/api-server/src/tripo3d/worker.rs | 4 +-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/server-rs/crates/api-server/src/tripo3d/result.rs b/server-rs/crates/api-server/src/tripo3d/result.rs index c93f1b754..63a38fbbc 100644 --- a/server-rs/crates/api-server/src/tripo3d/result.rs +++ b/server-rs/crates/api-server/src/tripo3d/result.rs @@ -3,13 +3,16 @@ //! 结果按端点使用严格 tagged enum,只带正式资源引用与已落地对象元数据; //! provider task ID、带签名的临时地址与 SDK 类型都不进入结果。 +use axum::http::StatusCode; use serde_json::{Value, json}; use shared_contracts::model3d::Model3dGenerationResult; use shared_contracts::model3d::common::{Model3dGeneratedArtifact, Model3dGenerationTargetRef}; use shared_contracts::model3d::image_to_model::Model3dImageToModelResult; use shared_contracts::model3d::text_to_model::Model3dTextToModelResult; -use super::{job::Model3dJobKind, storage::StoredModel3dArtifact}; +use crate::http_error::AppError; + +use super::{job::Model3dJobKind, provider::TRIPO_PROVIDER, storage::StoredModel3dArtifact}; pub(crate) fn artifact_metadata(stored: &StoredModel3dArtifact) -> Model3dGeneratedArtifact { Model3dGeneratedArtifact { @@ -47,13 +50,15 @@ pub(crate) fn build_result( } /// 队列终态结果载荷:外层固定 `result` 字段,值就是严格类型化的完成结果。 -pub(crate) fn job_result_payload(result: Model3dGenerationResult) -> Value { - match serde_json::to_value(result) { - Ok(value) => json!({ "result": value }), - // 结果由字符串与数字组成,序列化失败属于实现错误;仍返回可对账的稳定形状。 - Err(error) => json!({ - "result": null, - "resultSerializeError": error.to_string(), - }), - } +/// +/// 序列化失败按服务端问题上报,由 worker 把任务标记为失败 —— 不能落一条 +/// 「完成但 `result: null`」的终态,消费方会当成成功却反序列化不出结果。 +pub(crate) fn job_result_payload(result: Model3dGenerationResult) -> Result { + let value = serde_json::to_value(result).map_err(|error| { + AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ + "provider": TRIPO_PROVIDER, + "message": format!("序列化 3D 生成结果失败:{error}"), + })) + })?; + Ok(json!({ "result": value })) } diff --git a/server-rs/crates/api-server/src/tripo3d/worker.rs b/server-rs/crates/api-server/src/tripo3d/worker.rs index 662822a0e..d73b479de 100644 --- a/server-rs/crates/api-server/src/tripo3d/worker.rs +++ b/server-rs/crates/api-server/src/tripo3d/worker.rs @@ -365,7 +365,7 @@ async fn persist_result( _ => EditorCanvasLayoutPlan::None, }; let job_result_payload_json = - editor_generation_job_result_payload_json(caller, job_result_payload(result)); + editor_generation_job_result_payload_json(caller, job_result_payload(result)?); persist_editor_generation_result_atomically( state, caller, @@ -499,7 +499,7 @@ mod tests { &model, &preview, ); - let payload = job_result_payload(result); + let payload = job_result_payload(result).expect("完成结果应可序列化"); let result = &payload["result"]; assert_eq!(result["kind"], json!(expected_kind));