diff --git a/server-rs/crates/api-server/src/asset_billing.rs b/server-rs/crates/api-server/src/asset_billing.rs index c16ded945..741665a1e 100644 --- a/server-rs/crates/api-server/src/asset_billing.rs +++ b/server-rs/crates/api-server/src/asset_billing.rs @@ -271,7 +271,7 @@ pub(crate) fn should_skip_asset_operation_billing_for_connectivity( error: &SpacetimeClientError, ) -> bool { match error { - SpacetimeClientError::ConnectDropped | SpacetimeClientError::Timeout => true, + SpacetimeClientError::ConnectDropped | SpacetimeClientError::Timeout(_) => true, SpacetimeClientError::Build(message) | SpacetimeClientError::Procedure(message) | SpacetimeClientError::Runtime(message) => { diff --git a/server-rs/crates/api-server/src/big_fish.rs b/server-rs/crates/api-server/src/big_fish.rs index e89691173..bbd974c84 100644 --- a/server-rs/crates/api-server/src/big_fish.rs +++ b/server-rs/crates/api-server/src/big_fish.rs @@ -935,12 +935,17 @@ fn map_big_fish_client_error(error: SpacetimeClientError) -> AppError { StatusCode::BAD_REQUEST } SpacetimeClientError::Runtime(_) => StatusCode::BAD_REQUEST, - SpacetimeClientError::Timeout => StatusCode::GATEWAY_TIMEOUT, + SpacetimeClientError::Timeout(_) => StatusCode::GATEWAY_TIMEOUT, _ => StatusCode::BAD_GATEWAY, }; let message = match &error { - SpacetimeClientError::Timeout => "SpacetimeDB 会话读取超时,请稍后重试。".to_string(), + SpacetimeClientError::Timeout(stage) => { + format!( + "SpacetimeDB 会话读取超时({}),请稍后重试。", + stage.as_str() + ) + } SpacetimeClientError::ConnectDropped => { "SpacetimeDB 会话连接已断开,请稍后重试。".to_string() } @@ -983,6 +988,8 @@ fn current_timestamp_micros_to_string(value: i64) -> String { #[cfg(test)] mod tests { + use spacetime_client::SpacetimeClientStage; + use super::*; #[test] @@ -1001,7 +1008,7 @@ mod tests { #[test] fn big_fish_gallery_keeps_timeout_errors_visible() { assert!(!should_soft_fallback_big_fish_gallery( - &SpacetimeClientError::Timeout + &SpacetimeClientError::Timeout(SpacetimeClientStage::ProcedureResult) )); } } diff --git a/server-rs/crates/api-server/src/big_fish/mappers.rs b/server-rs/crates/api-server/src/big_fish/mappers.rs index ec8bcb029..406d7ceb7 100644 --- a/server-rs/crates/api-server/src/big_fish/mappers.rs +++ b/server-rs/crates/api-server/src/big_fish/mappers.rs @@ -1,4 +1,5 @@ use super::*; +use spacetime_client::SpacetimeClientStage; pub(super) fn map_big_fish_session_response( session: BigFishSessionRecord, @@ -237,7 +238,7 @@ pub(super) async fn load_big_fish_session_with_retry( .await { Ok(session) => return Ok(session), - Err(error @ SpacetimeClientError::Timeout) + Err(error @ SpacetimeClientError::Timeout(_)) | Err(error @ SpacetimeClientError::ConnectDropped) => { last_retryable_error = Some(error); if attempt == 0 { @@ -249,7 +250,11 @@ pub(super) async fn load_big_fish_session_with_retry( } } - Err(last_retryable_error.unwrap_or(SpacetimeClientError::Timeout)) + Err( + last_retryable_error.unwrap_or(SpacetimeClientError::Timeout( + SpacetimeClientStage::ProcedureResult, + )), + ) } pub(super) fn map_record_anchor_pack_to_domain( diff --git a/server-rs/crates/api-server/src/public_work.rs b/server-rs/crates/api-server/src/public_work.rs index d46059feb..4c8a8650e 100644 --- a/server-rs/crates/api-server/src/public_work.rs +++ b/server-rs/crates/api-server/src/public_work.rs @@ -133,7 +133,7 @@ fn map_public_work_client_error(error: SpacetimeClientError) -> AppError { { StatusCode::NOT_FOUND } - SpacetimeClientError::Timeout => StatusCode::GATEWAY_TIMEOUT, + SpacetimeClientError::Timeout(_) => StatusCode::GATEWAY_TIMEOUT, SpacetimeClientError::ConnectDropped => StatusCode::SERVICE_UNAVAILABLE, _ => StatusCode::BAD_GATEWAY, }; diff --git a/server-rs/crates/api-server/src/puzzle/tags.rs b/server-rs/crates/api-server/src/puzzle/tags.rs index c6ce0693b..78509b157 100644 --- a/server-rs/crates/api-server/src/puzzle/tags.rs +++ b/server-rs/crates/api-server/src/puzzle/tags.rs @@ -382,7 +382,7 @@ pub(super) fn puzzle_bad_request( pub(super) fn map_puzzle_client_error(error: SpacetimeClientError) -> AppError { let status = match &error { SpacetimeClientError::ConnectDropped => StatusCode::SERVICE_UNAVAILABLE, - SpacetimeClientError::Timeout => StatusCode::GATEWAY_TIMEOUT, + SpacetimeClientError::Timeout(_) => StatusCode::GATEWAY_TIMEOUT, error if should_skip_asset_operation_billing_for_connectivity(error) => { StatusCode::SERVICE_UNAVAILABLE } @@ -450,7 +450,7 @@ pub(super) fn map_puzzle_compile_error(error: SpacetimeClientError) -> AppError StatusCode::SERVICE_UNAVAILABLE } else if matches!( error, - SpacetimeClientError::ConnectDropped | SpacetimeClientError::Timeout + SpacetimeClientError::ConnectDropped | SpacetimeClientError::Timeout(_) ) || should_skip_asset_operation_billing_for_connectivity(&error) { StatusCode::SERVICE_UNAVAILABLE @@ -485,7 +485,7 @@ pub(super) fn map_puzzle_compile_error(error: SpacetimeClientError) -> AppError StatusCode::BAD_GATEWAY } SpacetimeClientError::ConnectDropped => StatusCode::SERVICE_UNAVAILABLE, - SpacetimeClientError::Timeout => StatusCode::GATEWAY_TIMEOUT, + SpacetimeClientError::Timeout(_) => StatusCode::GATEWAY_TIMEOUT, SpacetimeClientError::Runtime(_) => StatusCode::BAD_REQUEST, _ => StatusCode::BAD_GATEWAY, } diff --git a/server-rs/crates/api-server/src/state.rs b/server-rs/crates/api-server/src/state.rs index 8798c8916..1752ceceb 100644 --- a/server-rs/crates/api-server/src/state.rs +++ b/server-rs/crates/api-server/src/state.rs @@ -1604,6 +1604,7 @@ fn is_missing_creation_entry_config_procedure(error: &SpacetimeClientError) -> b #[cfg(test)] mod tests { use module_ai::{AiTaskKind, generate_ai_task_id}; + use spacetime_client::SpacetimeClientStage; use super::*; @@ -1618,7 +1619,7 @@ mod tests { &SpacetimeClientError::Procedure("No such procedure".to_string()), )); assert!(!is_missing_creation_entry_config_procedure( - &SpacetimeClientError::Timeout, + &SpacetimeClientError::Timeout(SpacetimeClientStage::ProcedureResult), )); } diff --git a/server-rs/crates/api-server/src/visual_novel.rs b/server-rs/crates/api-server/src/visual_novel.rs index d97ffea09..7f7469c99 100644 --- a/server-rs/crates/api-server/src/visual_novel.rs +++ b/server-rs/crates/api-server/src/visual_novel.rs @@ -1455,7 +1455,7 @@ fn map_spacetime_error(error: SpacetimeClientError) -> AppError { { StatusCode::FORBIDDEN } - SpacetimeClientError::Timeout => StatusCode::GATEWAY_TIMEOUT, + SpacetimeClientError::Timeout(_) => StatusCode::GATEWAY_TIMEOUT, SpacetimeClientError::ConnectDropped => StatusCode::SERVICE_UNAVAILABLE, _ => StatusCode::BAD_GATEWAY, }; diff --git a/server-rs/crates/spacetime-client/src/lib.rs b/server-rs/crates/spacetime-client/src/lib.rs index 34110353d..01215ad45 100644 --- a/server-rs/crates/spacetime-client/src/lib.rs +++ b/server-rs/crates/spacetime-client/src/lib.rs @@ -360,7 +360,7 @@ pub enum SpacetimeClientError { ConnectDropped, Procedure(String), Runtime(String), - Timeout, + Timeout(SpacetimeClientStage), } const DEFAULT_PROCEDURE_TIMEOUT: Duration = Duration::from_secs(30); @@ -711,7 +711,7 @@ impl SpacetimeClient { .map_err(|_| { SpacetimeStageError::new( SpacetimeClientStage::PoolAcquire, - SpacetimeClientError::Timeout, + SpacetimeClientError::Timeout(SpacetimeClientStage::PoolAcquire), ) })? .map_err(|error| { @@ -804,7 +804,7 @@ impl SpacetimeClient { .map_err(|_| { SpacetimeStageError::new( SpacetimeClientStage::ConnectBuild, - SpacetimeClientError::Timeout, + SpacetimeClientError::Timeout(SpacetimeClientStage::ConnectBuild), ) })? .map_err(|error| { @@ -821,7 +821,7 @@ impl SpacetimeClient { .map_err(|_| { SpacetimeStageError::new( SpacetimeClientStage::ConnectHandshake, - SpacetimeClientError::Timeout, + SpacetimeClientError::Timeout(SpacetimeClientStage::ConnectHandshake), ) })? .map_err(|_| { @@ -945,7 +945,7 @@ impl SpacetimeClient { .map_err(|_| { SpacetimeStageError::new( SpacetimeClientStage::ReadModelSubscribe, - SpacetimeClientError::Timeout, + SpacetimeClientError::Timeout(SpacetimeClientStage::ReadModelSubscribe), ) })? .map_err(|_| { @@ -970,7 +970,7 @@ impl SpacetimeClient { // 超时后必须统一归还租约;若连接已先一步断开则回传断线,否则标记坏连接并回传超时。 fn resolve_timeout_error( connection: Option<&PooledConnection>, - _stage: SpacetimeClientStage, + stage: SpacetimeClientStage, ) -> SpacetimeClientError { if let Some(connection) = connection { if connection.is_broken() { @@ -979,7 +979,7 @@ impl SpacetimeClient { connection.mark_broken(); } - SpacetimeClientError::Timeout + SpacetimeClientError::Timeout(stage) } } @@ -1127,7 +1127,9 @@ impl fmt::Display for SpacetimeClientError { f.write_str(message) } Self::ConnectDropped => f.write_str("SpacetimeDB 连接在返回结果前已断开"), - Self::Timeout => f.write_str("SpacetimeDB procedure 调用超时"), + Self::Timeout(stage) => { + f.write_str(&format!("SpacetimeDB {} 调用超时", stage.as_str())) + } } } } @@ -1241,6 +1243,6 @@ mod tests { Err(error) => error, }; assert_eq!(error.stage, SpacetimeClientStage::PoolAcquire); - assert!(matches!(error.error, SpacetimeClientError::Timeout)); + assert!(matches!(error.error, SpacetimeClientError::Timeout(_))); } } diff --git a/src/components/square-hole-result/SquareHoleResultView.tsx b/src/components/square-hole-result/SquareHoleResultView.tsx index b91e3eb20..0178b4999 100644 --- a/src/components/square-hole-result/SquareHoleResultView.tsx +++ b/src/components/square-hole-result/SquareHoleResultView.tsx @@ -9,13 +9,7 @@ import { Send, Trash2, } from 'lucide-react'; -import { - type ChangeEvent, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { type ChangeEvent, useEffect, useMemo, useRef, useState } from 'react'; import type { SquareHoleResultDraft } from '../../../packages/shared/src/contracts/squareHoleAgent'; import type { @@ -24,6 +18,11 @@ import type { SquareHoleShapeOption, SquareHoleWorkProfile, } from '../../../packages/shared/src/contracts/squareHoleWorks'; +import { + canUseNativeHostCapability, + type HostFileImportImageResult, + importHostImageFile, +} from '../../services/host-bridge/hostBridge'; import { publishSquareHoleWork, regenerateSquareHoleWorkImage, @@ -32,14 +31,9 @@ import { type SquareHoleImageAssetKind, updateSquareHoleWork, } from '../../services/square-hole-works'; -import { - canUseNativeHostCapability, - type HostFileImportImageResult, - importHostImageFile, -} from '../../services/host-bridge/hostBridge'; import { PlatformActionButton } from '../common/PlatformActionButton'; -import { PlatformBackActionButton } from '../common/PlatformBackActionButton'; import { PlatformAssetPickerGrid } from '../common/PlatformAssetPickerCard'; +import { PlatformBackActionButton } from '../common/PlatformBackActionButton'; import { PlatformFieldLabel } from '../common/PlatformFieldLabel'; import { PlatformIconButton } from '../common/PlatformIconButton'; import { PlatformMediaFrame } from '../common/PlatformMediaFrame'; @@ -47,11 +41,11 @@ import { PlatformPillBadge } from '../common/PlatformPillBadge'; import { PlatformStatGrid } from '../common/PlatformStatGrid'; import { PlatformStatusMessage } from '../common/PlatformStatusMessage'; import { PlatformSubpanel } from '../common/PlatformSubpanel'; -import { PlatformToolModalShell } from '../common/PlatformToolModalShell'; import { PlatformSelectField, PlatformTextField, } from '../common/PlatformTextField'; +import { PlatformToolModalShell } from '../common/PlatformToolModalShell'; type SquareHoleResultViewProps = { profile: SquareHoleWorkProfile; @@ -792,6 +786,9 @@ export function SquareHoleResultView({ setEditState((current) => applyImageSlotSrc(current, slot, dataUrl)); setLocalError(null); } catch (caughtError) { + if (slot.kind === 'background') { + return; + } setLocalError( caughtError instanceof Error ? caughtError.message : '图片读取失败。', ); @@ -823,6 +820,9 @@ export function SquareHoleResultView({ ); setLocalError(null); } catch (caughtError) { + if (slot.kind === 'background') { + return; + } setLocalError( caughtError instanceof Error ? caughtError.message : '图片读取失败。', );