From 433ffb4a15c5aa52a8d9724ee504e13697384bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 22 Sep 2026 16:40:48 +0800 Subject: [PATCH] =?UTF-8?q?=E8=90=BD=E5=BA=93=E7=9A=84=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=94=B9=E7=94=A8=E4=B8=A5=E6=A0=BC=20wire?= =?UTF-8?q?=20=E5=8F=96=E5=80=BC=20-=20Model3dJobRequest::model=5Fversion?= =?UTF-8?q?=20=E6=94=B9=E4=B8=BA=E8=BF=94=E5=9B=9E=20Result=20=E5=B9=B6?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20wire=5Fstr=20-=20worker=20=E8=90=BD?= =?UTF-8?q?=E5=BA=93=E5=89=8D=E5=8F=96=E4=B8=8D=E5=88=B0=E5=A5=91=E7=BA=A6?= =?UTF-8?q?=E5=8F=96=E5=80=BC=E5=8D=B3=E6=8C=89=E6=9C=8D=E5=8A=A1=E7=AB=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E5=A4=B1=E8=B4=A5=EF=BC=8C=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=86=99=20Debug=20=E5=85=9C=E5=BA=95=E5=90=8D=20-=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=94=A8=E4=BE=8B=E6=96=AD=E8=A8=80=E8=90=BD=E5=BA=93?= =?UTF-8?q?=E5=8F=96=E5=80=BC=E4=B8=BA=E5=A5=91=E7=BA=A6=E7=BA=BF=E4=B8=8A?= =?UTF-8?q?=E5=80=BC=20v3.1-20260211?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crates/api-server/src/tripo3d/job.rs | 36 +++++++++++++++++-- .../crates/api-server/src/tripo3d/worker.rs | 8 ++++- 2 files changed, 41 insertions(+), 3 deletions(-) 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,