From 04654bf7601e36a1545d8b35032bcdce3400ab17 Mon Sep 17 00:00:00 2001 From: kvtodev Date: Mon, 13 Jul 2026 14:21:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=A9=E5=B1=95=E5=A4=96=E9=83=A8=E7=94=9F?= =?UTF-8?q?=E6=88=90=E4=BB=BB=E5=8A=A1=E7=BB=93=E6=9E=9C=E4=B8=8E=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E5=85=A5=E9=98=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持调用方提供任务 ID 和去重键,复用既有外部任务队列。 保存并投影编辑器生成结果,供任务消费者恢复展示。 --- .../src/contracts/externalGeneration.ts | 1 + .../api-server/src/editor_generation_queue.rs | 32 +++++++++++++++- .../api-server/src/external_generation.rs | 5 +++ .../src/external_generation_worker.rs | 38 ++++++++++++++----- server-rs/crates/api-server/src/jump_hop.rs | 1 + server-rs/crates/api-server/src/puzzle.rs | 1 + .../crates/api-server/src/puzzle_clear.rs | 1 + .../crates/api-server/src/wooden_fish.rs | 1 + .../src/external_generation.rs | 2 + 9 files changed, 72 insertions(+), 10 deletions(-) diff --git a/packages/shared/src/contracts/externalGeneration.ts b/packages/shared/src/contracts/externalGeneration.ts index 24919afec..308ca0c8a 100644 --- a/packages/shared/src/contracts/externalGeneration.ts +++ b/packages/shared/src/contracts/externalGeneration.ts @@ -23,6 +23,7 @@ export interface ExternalGenerationJobStatusRecord { progress: number; error?: string | null; updatedAtMicros: number; + result?: unknown; } export interface ExternalGenerationJobStatusResponse { diff --git a/server-rs/crates/api-server/src/editor_generation_queue.rs b/server-rs/crates/api-server/src/editor_generation_queue.rs index 3d6a24b52..ca0d118d2 100644 --- a/server-rs/crates/api-server/src/editor_generation_queue.rs +++ b/server-rs/crates/api-server/src/editor_generation_queue.rs @@ -46,6 +46,35 @@ where T: Serialize, { let job_id = build_prefixed_uuid_id("task-"); + enqueue_editor_generation_job_with_identity( + state, + owner_user_id, + job_kind, + source_entity_id, + request_label, + price_mud_points, + payload, + job_id.clone(), + format!("editor-canvas:{job_kind}:{job_id}"), + ) + .await +} + +#[allow(clippy::too_many_arguments)] +pub(crate) async fn enqueue_editor_generation_job_with_identity( + state: &AppState, + owner_user_id: &str, + job_kind: &str, + source_entity_id: impl Into, + request_label: impl Into, + price_mud_points: u64, + payload: &T, + job_id: String, + dedupe_key: String, +) -> Result +where + T: Serialize, +{ let request_payload_json = serde_json::to_string(payload).map_err(|error| { AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ "provider": EDITOR_GENERATION_QUEUE_PROVIDER, @@ -56,7 +85,7 @@ where state .spacetime_client() .enqueue_external_generation_job(ExternalGenerationJobEnqueueRecordInput { - dedupe_key: format!("editor-canvas:{job_kind}:{job_id}"), + dedupe_key, job_id, job_kind: job_kind.to_string(), owner_user_id: owner_user_id.to_string(), @@ -89,6 +118,7 @@ pub(crate) fn editor_generation_queue_state( progress: 8, error: job.last_error_message, updated_at_micros: job.updated_at_micros, + result: None, } } diff --git a/server-rs/crates/api-server/src/external_generation.rs b/server-rs/crates/api-server/src/external_generation.rs index 0757cbc5a..f381c2479 100644 --- a/server-rs/crates/api-server/src/external_generation.rs +++ b/server-rs/crates/api-server/src/external_generation.rs @@ -178,6 +178,11 @@ fn map_external_generation_job_status( progress, error: job.last_error_message.clone(), updated_at_micros: job.updated_at_micros, + result: job + .result_payload_json + .as_deref() + .and_then(|payload| serde_json::from_str::(payload).ok()) + .and_then(|payload| payload.get("response").cloned()), } } diff --git a/server-rs/crates/api-server/src/external_generation_worker.rs b/server-rs/crates/api-server/src/external_generation_worker.rs index 993a75ad9..2bb0bbead 100644 --- a/server-rs/crates/api-server/src/external_generation_worker.rs +++ b/server-rs/crates/api-server/src/external_generation_worker.rs @@ -671,7 +671,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(error) => { let message = error.body_text(); fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -699,7 +701,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(error) => { let message = error.body_text(); fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -728,7 +732,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(error) => { let message = error.body_text(); fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -756,7 +762,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(error) => { let message = error.body_text(); fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -784,7 +792,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(error) => { let message = error.body_text(); fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -813,7 +823,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(response) => { let message = response_error_message(response).await; fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -842,7 +854,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(response) => { let message = response_error_message(response).await; fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -871,7 +885,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(response) => { let message = response_error_message(response).await; fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -900,7 +916,9 @@ async fn process_external_generation_job_once( ) .await { - Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await, + Ok(result) => { + complete_editor_generation_job(&state, &worker_id, &job, result.0).await + } Err(response) => { let message = response_error_message(response).await; fail_job(&state, &worker_id, &job, message.clone()).await?; @@ -1005,6 +1023,7 @@ async fn complete_editor_generation_job( state: &AppState, worker_id: &str, job: &ExternalGenerationJobRecord, + result: serde_json::Value, ) -> Result<(), String> { complete_job( state, @@ -1014,6 +1033,7 @@ async fn complete_editor_generation_job( json!({ "sourceModule": job.source_module.clone(), "sourceEntityId": job.source_entity_id.clone(), + "response": result, }) .to_string(), ), diff --git a/server-rs/crates/api-server/src/jump_hop.rs b/server-rs/crates/api-server/src/jump_hop.rs index a689909d0..70fef9b71 100644 --- a/server-rs/crates/api-server/src/jump_hop.rs +++ b/server-rs/crates/api-server/src/jump_hop.rs @@ -354,6 +354,7 @@ fn map_jump_hop_queue_job_status( progress: 8, error: job.last_error_message, updated_at_micros: job.updated_at_micros, + result: None, } } diff --git a/server-rs/crates/api-server/src/puzzle.rs b/server-rs/crates/api-server/src/puzzle.rs index 13c042dde..472cb5b8e 100644 --- a/server-rs/crates/api-server/src/puzzle.rs +++ b/server-rs/crates/api-server/src/puzzle.rs @@ -262,6 +262,7 @@ fn map_puzzle_queue_job_status( .unwrap_or(100), error: job.last_error_message.clone(), updated_at_micros: job.updated_at_micros, + result: None, } } diff --git a/server-rs/crates/api-server/src/puzzle_clear.rs b/server-rs/crates/api-server/src/puzzle_clear.rs index 77f50d6db..d09fca18e 100644 --- a/server-rs/crates/api-server/src/puzzle_clear.rs +++ b/server-rs/crates/api-server/src/puzzle_clear.rs @@ -331,6 +331,7 @@ fn map_puzzle_clear_queue_job_status( progress: 8, error: job.last_error_message, updated_at_micros: job.updated_at_micros, + result: None, } } diff --git a/server-rs/crates/api-server/src/wooden_fish.rs b/server-rs/crates/api-server/src/wooden_fish.rs index ba6214920..d3902829a 100644 --- a/server-rs/crates/api-server/src/wooden_fish.rs +++ b/server-rs/crates/api-server/src/wooden_fish.rs @@ -340,6 +340,7 @@ fn map_wooden_fish_queue_job_status( progress: 8, error: job.last_error_message, updated_at_micros: job.updated_at_micros, + result: None, } } diff --git a/server-rs/crates/shared-contracts/src/external_generation.rs b/server-rs/crates/shared-contracts/src/external_generation.rs index 004574c81..a9bddeaaa 100644 --- a/server-rs/crates/shared-contracts/src/external_generation.rs +++ b/server-rs/crates/shared-contracts/src/external_generation.rs @@ -34,6 +34,8 @@ pub struct ExternalGenerationJobStatusRecord { pub progress: u8, pub error: Option, pub updated_at_micros: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub result: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]