diff --git a/server-rs/crates/api-server/src/tripo3d/job.rs b/server-rs/crates/api-server/src/tripo3d/job.rs index 9baf4892d..cb2717784 100644 --- a/server-rs/crates/api-server/src/tripo3d/job.rs +++ b/server-rs/crates/api-server/src/tripo3d/job.rs @@ -79,12 +79,17 @@ impl Model3dJobRequest { } /// 审计与落库用的模型版本线上取值;取值只由契约枚举的 serde rename 定义。 - pub(crate) fn model_version(&self) -> String { + /// + /// 该值会持久化进资源 / 素材记录的 `model` 字段,因此走严格的 + /// [`shared_contracts::model3d::wire_str`]:契约枚举的序列化形态坏掉时要显式失败, + /// 而不是把 `Debug` 名(如 `H31`)当线上取值落库。 + pub(crate) fn model_version(&self) -> Result { let model = match self { Self::TextToModel(request) => request.generation.model, Self::ImageToModel(request) => request.generation.model, }; - shared_contracts::model3d::wire_str_lossy(&model) + shared_contracts::model3d::wire_str(&model) + .map_err(|error| format!("模型版本无法序列化成契约取值:{error}")) } } @@ -133,6 +138,33 @@ mod tests { ); } + #[test] + fn model_version_uses_the_contract_wire_value() { + let payload = json!({ + "generation": { + "prompt": "一只木箱", + "model": "v3.1-20260211", + "texture": true, + "pbr": true, + "textureQuality": "standard", + "geometryQuality": "standard", + "quad": false, + "smartLowPoly": false, + "generateParts": false + }, + "target": { "kind": "projectResource", "projectId": "project-1" } + }) + .to_string(); + + let request = + parse_model3d_job_request(MODEL3D_TEXT_TO_MODEL_JOB_KIND, &payload).expect("应可解析"); + + assert_eq!( + request.model_version().expect("契约枚举应能取到 wire 值"), + "v3.1-20260211" + ); + } + #[test] fn job_request_is_parsed_after_stripping_platform_fingerprint() { let payload = json!({ diff --git a/server-rs/crates/api-server/src/tripo3d/worker.rs b/server-rs/crates/api-server/src/tripo3d/worker.rs index 9086d9f6f..662822a0e 100644 --- a/server-rs/crates/api-server/src/tripo3d/worker.rs +++ b/server-rs/crates/api-server/src/tripo3d/worker.rs @@ -234,7 +234,13 @@ async fn persist_result( let preview_src = editor_media_src_from_object_key(preview.object_key.as_str()); let completed_at_micros = current_utc_micros(); let audit_prompt = request.audit_prompt(); - let model_version = request.model_version(); + // 落库值必须是契约 wire 取值;取不到就按服务端问题失败,不写入 Debug 兜底名。 + let model_version = request.model_version().map_err(|error| { + AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ + "provider": TRIPO_PROVIDER, + "message": format!("落库 3D 生成结果时:{error}。"), + })) + })?; let (project_id, folder_id, label, canvas_completion) = match request.target() { Model3dGenerationTarget::ProjectResource { project_id,