3D 结果序列化失败不再落「完成但 result 为空」的终态

- job_result_payload 改为返回 Result,失败按 500 上报服务端问题,去掉 result:null 兜底形状
- worker 持久化前用 ? 把失败交给既有失败链路:任务按失败终态处理并冲正扣费
- worker 测试夹具同步改为 expect,断言完成结果可序列化
This commit is contained in:
2026-09-23 12:15:02 +08:00
parent 83913785ae
commit 48ba8b3133
2 changed files with 17 additions and 12 deletions
@@ -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<Value, AppError> {
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 }))
}
@@ -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));