合并 master 并保留外部生成 worker 模式
合入 master 的生产健康巡检、JumpHop 和 SpacetimeDB 更新 保留外部生成 worker、队列/内联模式与 lease guard 口径 合并 Server-Provision 工具复用、health patrol 和外部生成 worker systemd 配置 补齐 SpacetimeDB 生成绑定并通过本地检查
This commit is contained in:
@@ -472,9 +472,9 @@ fn validate_jump_hop_runtime_ready(
|
||||
}
|
||||
validate_jump_hop_default_character_ready(work)?;
|
||||
validate_jump_hop_tile_atlas_asset_ready(&work.tile_atlas_asset, "tile_atlas_asset")?;
|
||||
if work.tile_assets.len() < 25 {
|
||||
if work.tile_assets.len() < 18 {
|
||||
return Err(SpacetimeClientError::validation_failed(
|
||||
"jump-hop runtime 需要 25 个地块资产",
|
||||
"jump-hop runtime 需要 18 个地块资产",
|
||||
));
|
||||
}
|
||||
for (index, asset) in work.tile_assets.iter().enumerate() {
|
||||
@@ -760,12 +760,12 @@ fn build_compile_input(
|
||||
draft.default_character = Some(default_jump_hop_default_character());
|
||||
let tile_atlas_asset = draft.tile_atlas_asset.clone().ok_or_else(|| {
|
||||
SpacetimeClientError::validation_failed(
|
||||
"jump-hop compile-draft 缺少真实地块图集资产,请先由 api-server 生成并持久化 asset_object",
|
||||
"jump-hop compile-draft 缺少真实地板贴图图集资产,请先由 api-server 生成并持久化 asset_object",
|
||||
)
|
||||
})?;
|
||||
let tile_assets = if draft.tile_assets.len() < 25 {
|
||||
let tile_assets = if draft.tile_assets.len() < 18 {
|
||||
return Err(SpacetimeClientError::validation_failed(
|
||||
"jump-hop compile-draft 需要 25 个真实地块资产,请先由 api-server 生成并持久化 asset_object",
|
||||
"jump-hop compile-draft 需要 18 个真实地块资产,请先由 api-server 生成并持久化 asset_object",
|
||||
));
|
||||
} else {
|
||||
draft.tile_assets.clone()
|
||||
@@ -877,7 +877,7 @@ fn default_draft() -> JumpHopDraftResponse {
|
||||
style_preset: JumpHopStylePreset::MinimalBlocks,
|
||||
default_character: Some(default_jump_hop_default_character()),
|
||||
character_prompt: "内置默认 3D 角色".to_string(),
|
||||
tile_prompt: "跳一跳主题的正面30度视角主题物体图集,物体本身作为跳跃落点".to_string(),
|
||||
tile_prompt: "跳一跳主题的3D立方体主题身份方块包装图集".to_string(),
|
||||
end_mood_prompt: None,
|
||||
character_asset: None,
|
||||
tile_atlas_asset: None,
|
||||
@@ -993,7 +993,7 @@ mod tests {
|
||||
const NOW_MICROS: i64 = 1_763_456_789_000_000;
|
||||
|
||||
#[test]
|
||||
fn jump_hop_action_compile_draft_builds_compile_input_with_25_tile_assets_and_builtin_character()
|
||||
fn jump_hop_action_compile_draft_builds_compile_input_with_18_tile_assets_and_builtin_character()
|
||||
{
|
||||
let session = session_with_draft(draft_without_character_asset());
|
||||
let payload = action(JumpHopActionType::CompileDraft);
|
||||
@@ -1027,9 +1027,9 @@ mod tests {
|
||||
.tile_assets_json
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("old-tile-25-object")
|
||||
.contains("old-tile-18-object")
|
||||
);
|
||||
assert_eq!(draft.tile_assets.len(), 25);
|
||||
assert_eq!(draft.tile_assets.len(), 18);
|
||||
assert_eq!(draft.generation_status, JumpHopGenerationStatus::Ready);
|
||||
}
|
||||
|
||||
@@ -1039,7 +1039,7 @@ mod tests {
|
||||
let mut payload = action(JumpHopActionType::RegenerateTiles);
|
||||
payload.tile_prompt = Some("新的地块提示词".to_string());
|
||||
payload.tile_atlas_asset = Some(tile_atlas_asset("new-tile-atlas", NOW_MICROS));
|
||||
payload.tile_assets = Some(tile_assets("new", 25));
|
||||
payload.tile_assets = Some(tile_assets("new", 18));
|
||||
|
||||
let (plan, _draft) =
|
||||
build_jump_hop_action_plan(&session, OWNER_USER_ID, &payload, NOW_MICROS)
|
||||
@@ -1081,7 +1081,7 @@ mod tests {
|
||||
.tile_assets_json
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("new-tile-25-object")
|
||||
.contains("new-tile-18-object")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1195,7 +1195,7 @@ mod tests {
|
||||
JumpHopDraftResponse {
|
||||
profile_id: None,
|
||||
tile_atlas_asset: Some(tile_atlas_asset("old-tile-atlas", 0)),
|
||||
tile_assets: tile_assets("old", 25),
|
||||
tile_assets: tile_assets("old", 18),
|
||||
..base_draft()
|
||||
}
|
||||
}
|
||||
@@ -1205,7 +1205,7 @@ mod tests {
|
||||
profile_id: Some(PROFILE_ID.to_string()),
|
||||
character_asset: Some(build_jump_hop_default_character_asset(PROFILE_ID, "旧主题")),
|
||||
tile_atlas_asset: Some(tile_atlas_asset("old-tile-atlas", 0)),
|
||||
tile_assets: tile_assets("old", 25),
|
||||
tile_assets: tile_assets("old", 18),
|
||||
path: Some(sample_jump_hop_path()),
|
||||
cover_composite: Some("/generated-jump-hop-assets/old-cover.png".to_string()),
|
||||
generation_status: JumpHopGenerationStatus::Ready,
|
||||
@@ -1242,13 +1242,14 @@ mod tests {
|
||||
index + 1
|
||||
),
|
||||
asset_object_id: format!("{prefix}-tile-{:02}-object", index + 1),
|
||||
source_atlas_cell: format!("row-{}-col-{}", index / 5 + 1, index % 5 + 1),
|
||||
atlas_row: Some(index as u32 / 5 + 1),
|
||||
atlas_col: Some(index as u32 % 5 + 1),
|
||||
source_atlas_cell: format!("row-{}-col-{}", index / 3 + 1, index % 3 + 1),
|
||||
atlas_row: Some(index as u32 / 3 + 1),
|
||||
atlas_col: Some(index as u32 % 3 + 1),
|
||||
visual_width: 256,
|
||||
visual_height: 192,
|
||||
top_surface_radius: 42.0,
|
||||
landing_radius: 34.0,
|
||||
face_assets: None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ use std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
sync::{Arc, Mutex},
|
||||
thread::JoinHandle,
|
||||
time::Duration,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use module_ai::{
|
||||
@@ -246,6 +246,7 @@ use tokio::{
|
||||
sync::{OwnedSemaphorePermit, RwLock, Semaphore, oneshot},
|
||||
time::timeout,
|
||||
};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::module_bindings::*;
|
||||
|
||||
@@ -258,6 +259,60 @@ pub struct SpacetimeClientConfig {
|
||||
pub procedure_timeout: Duration,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SpacetimeClientStage {
|
||||
Ready,
|
||||
PoolAcquire,
|
||||
ConnectBuild,
|
||||
ConnectHandshake,
|
||||
ReadModelSubscribe,
|
||||
ProcedureResult,
|
||||
ReducerResult,
|
||||
ReadCache,
|
||||
}
|
||||
|
||||
impl SpacetimeClientStage {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Ready => "ready",
|
||||
Self::PoolAcquire => "pool_acquire",
|
||||
Self::ConnectBuild => "connect_build",
|
||||
Self::ConnectHandshake => "connect_handshake",
|
||||
Self::ReadModelSubscribe => "read_model_subscribe",
|
||||
Self::ProcedureResult => "procedure_result",
|
||||
Self::ReducerResult => "reducer_result",
|
||||
Self::ReadCache => "read_cache",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SpacetimeClientHealthSnapshot {
|
||||
pub ok: bool,
|
||||
pub stage: SpacetimeClientStage,
|
||||
pub checked_at_micros: i64,
|
||||
pub elapsed_ms: u64,
|
||||
pub timeout_ms: u64,
|
||||
pub error: Option<String>,
|
||||
pub last_success_at_micros: Option<i64>,
|
||||
pub last_error: Option<String>,
|
||||
}
|
||||
|
||||
impl SpacetimeClientHealthSnapshot {
|
||||
pub fn healthy_for_test() -> Self {
|
||||
Self {
|
||||
ok: true,
|
||||
stage: SpacetimeClientStage::Ready,
|
||||
checked_at_micros: current_unix_micros(),
|
||||
elapsed_ms: 0,
|
||||
timeout_ms: 0,
|
||||
error: None,
|
||||
last_success_at_micros: Some(current_unix_micros()),
|
||||
last_error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AuthStoreSnapshotRecord {
|
||||
pub snapshot_json: Option<String>,
|
||||
@@ -275,6 +330,7 @@ pub struct AuthStoreSnapshotImportRecord {
|
||||
pub struct SpacetimeClient {
|
||||
config: SpacetimeClientConfig,
|
||||
pool: Arc<SpacetimeConnectionPool>,
|
||||
health_state: Arc<RwLock<SpacetimeClientHealthState>>,
|
||||
creation_entry_config_cache: Arc<RwLock<Option<CreationEntryConfigRecord>>>,
|
||||
custom_world_gallery_legacy_sync_attempted: Arc<AtomicBool>,
|
||||
}
|
||||
@@ -301,6 +357,24 @@ struct SpacetimeConnectionPool {
|
||||
permits: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct SpacetimeClientHealthState {
|
||||
last_success_at_micros: Option<i64>,
|
||||
last_error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SpacetimeStageError {
|
||||
stage: SpacetimeClientStage,
|
||||
error: SpacetimeClientError,
|
||||
}
|
||||
|
||||
impl SpacetimeStageError {
|
||||
fn new(stage: SpacetimeClientStage, error: SpacetimeClientError) -> Self {
|
||||
Self { stage, error }
|
||||
}
|
||||
}
|
||||
|
||||
struct PooledConnectionSlot {
|
||||
connection: Option<PooledConnection>,
|
||||
in_use: bool,
|
||||
@@ -346,6 +420,7 @@ impl SpacetimeClient {
|
||||
Self {
|
||||
config,
|
||||
pool,
|
||||
health_state: Arc::new(RwLock::new(SpacetimeClientHealthState::default())),
|
||||
creation_entry_config_cache: Arc::new(RwLock::new(None)),
|
||||
custom_world_gallery_legacy_sync_attempted: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
@@ -359,29 +434,58 @@ impl SpacetimeClient {
|
||||
where
|
||||
T: Send + 'static,
|
||||
{
|
||||
let started_at = Instant::now();
|
||||
let metrics_guard = telemetry::begin_procedure(procedure);
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
let result_sender = Arc::new(Mutex::new(Some(sender)));
|
||||
let final_result = match self.acquire_connection().await {
|
||||
let final_result = match self
|
||||
.acquire_connection_with_timeout(self.config.procedure_timeout)
|
||||
.await
|
||||
{
|
||||
Ok(lease) => {
|
||||
let result = if let Some(connection) = lease.connection.as_ref() {
|
||||
let (result, failed_stage) = if let Some(connection) = lease.connection.as_ref() {
|
||||
call(&connection.connection, result_sender.clone());
|
||||
match timeout(self.config.procedure_timeout, receiver).await {
|
||||
Ok(inner) => match inner {
|
||||
Ok(value) => value,
|
||||
Err(_) => Err(SpacetimeClientError::ConnectDropped),
|
||||
let stage = SpacetimeClientStage::ProcedureResult;
|
||||
(
|
||||
match timeout(self.config.procedure_timeout, receiver).await {
|
||||
Ok(inner) => match inner {
|
||||
Ok(value) => value,
|
||||
Err(_) => Err(SpacetimeClientError::ConnectDropped),
|
||||
},
|
||||
Err(_) => Err(Self::resolve_timeout_error(Some(connection), stage)),
|
||||
},
|
||||
Err(_) => Err(Self::resolve_timeout_error(Some(connection))),
|
||||
}
|
||||
stage,
|
||||
)
|
||||
} else {
|
||||
Err(SpacetimeClientError::Runtime(
|
||||
"SpacetimeDB 连接租约缺少连接".to_string(),
|
||||
))
|
||||
(
|
||||
Err(SpacetimeClientError::Runtime(
|
||||
"SpacetimeDB 连接租约缺少连接".to_string(),
|
||||
)),
|
||||
SpacetimeClientStage::ProcedureResult,
|
||||
)
|
||||
};
|
||||
self.release_connection(lease).await;
|
||||
if let Err(error) = &result {
|
||||
log_spacetime_client_failure(
|
||||
"procedure",
|
||||
procedure,
|
||||
failed_stage,
|
||||
started_at,
|
||||
error,
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
Err(error) => Err(error),
|
||||
Err(error) => {
|
||||
log_spacetime_client_failure(
|
||||
"procedure",
|
||||
procedure,
|
||||
error.stage,
|
||||
started_at,
|
||||
&error.error,
|
||||
);
|
||||
Err(error.error)
|
||||
}
|
||||
};
|
||||
|
||||
metrics_guard.finish(&final_result);
|
||||
@@ -393,29 +497,58 @@ impl SpacetimeClient {
|
||||
procedure: &'static str,
|
||||
call: impl FnOnce(&DbConnection, ReducerResultSender) + Send + 'static,
|
||||
) -> Result<(), SpacetimeClientError> {
|
||||
let started_at = Instant::now();
|
||||
let metrics_guard = telemetry::begin_procedure(procedure);
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
let result_sender = Arc::new(Mutex::new(Some(sender)));
|
||||
let final_result = match self.acquire_connection().await {
|
||||
let final_result = match self
|
||||
.acquire_connection_with_timeout(self.config.procedure_timeout)
|
||||
.await
|
||||
{
|
||||
Ok(lease) => {
|
||||
let result = if let Some(connection) = lease.connection.as_ref() {
|
||||
let (result, failed_stage) = if let Some(connection) = lease.connection.as_ref() {
|
||||
call(&connection.connection, result_sender.clone());
|
||||
match timeout(self.config.procedure_timeout, receiver).await {
|
||||
Ok(inner) => match inner {
|
||||
Ok(value) => value,
|
||||
Err(_) => Err(SpacetimeClientError::ConnectDropped),
|
||||
let stage = SpacetimeClientStage::ReducerResult;
|
||||
(
|
||||
match timeout(self.config.procedure_timeout, receiver).await {
|
||||
Ok(inner) => match inner {
|
||||
Ok(value) => value,
|
||||
Err(_) => Err(SpacetimeClientError::ConnectDropped),
|
||||
},
|
||||
Err(_) => Err(Self::resolve_timeout_error(Some(connection), stage)),
|
||||
},
|
||||
Err(_) => Err(Self::resolve_timeout_error(Some(connection))),
|
||||
}
|
||||
stage,
|
||||
)
|
||||
} else {
|
||||
Err(SpacetimeClientError::Runtime(
|
||||
"SpacetimeDB 连接租约缺少连接".to_string(),
|
||||
))
|
||||
(
|
||||
Err(SpacetimeClientError::Runtime(
|
||||
"SpacetimeDB 连接租约缺少连接".to_string(),
|
||||
)),
|
||||
SpacetimeClientStage::ReducerResult,
|
||||
)
|
||||
};
|
||||
self.release_connection(lease).await;
|
||||
if let Err(error) = &result {
|
||||
log_spacetime_client_failure(
|
||||
"reducer",
|
||||
procedure,
|
||||
failed_stage,
|
||||
started_at,
|
||||
error,
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
Err(error) => Err(error),
|
||||
Err(error) => {
|
||||
log_spacetime_client_failure(
|
||||
"reducer",
|
||||
procedure,
|
||||
error.stage,
|
||||
started_at,
|
||||
&error.error,
|
||||
);
|
||||
Err(error.error)
|
||||
}
|
||||
};
|
||||
|
||||
metrics_guard.finish(&final_result);
|
||||
@@ -430,11 +563,22 @@ impl SpacetimeClient {
|
||||
where
|
||||
T: Send + 'static,
|
||||
{
|
||||
let started_at = Instant::now();
|
||||
let metrics_guard = telemetry::begin_read(read_name);
|
||||
let lease = match self.acquire_connection().await {
|
||||
let lease = match self
|
||||
.acquire_connection_with_timeout(self.config.procedure_timeout)
|
||||
.await
|
||||
{
|
||||
Ok(lease) => lease,
|
||||
Err(error) => {
|
||||
let final_result = Err(error);
|
||||
log_spacetime_client_failure(
|
||||
"read",
|
||||
read_name,
|
||||
error.stage,
|
||||
started_at,
|
||||
&error.error,
|
||||
);
|
||||
let final_result = Err(error.error);
|
||||
metrics_guard.finish(&final_result);
|
||||
return final_result;
|
||||
}
|
||||
@@ -448,6 +592,15 @@ impl SpacetimeClient {
|
||||
};
|
||||
self.release_connection(lease).await;
|
||||
|
||||
if let Err(error) = &final_result {
|
||||
log_spacetime_client_failure(
|
||||
"read",
|
||||
read_name,
|
||||
SpacetimeClientStage::ReadCache,
|
||||
started_at,
|
||||
error,
|
||||
);
|
||||
}
|
||||
metrics_guard.finish(&final_result);
|
||||
final_result
|
||||
}
|
||||
@@ -460,14 +613,75 @@ impl SpacetimeClient {
|
||||
self.creation_entry_config_cache.read().await.clone()
|
||||
}
|
||||
|
||||
async fn acquire_connection(&self) -> Result<PooledConnectionLease, SpacetimeClientError> {
|
||||
let permit = timeout(
|
||||
self.config.procedure_timeout,
|
||||
self.pool.permits.clone().acquire_owned(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| SpacetimeClientError::Timeout)?
|
||||
.map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?;
|
||||
pub async fn health_check(&self, probe_timeout: Duration) -> SpacetimeClientHealthSnapshot {
|
||||
let timeout = if probe_timeout.is_zero() {
|
||||
DEFAULT_PROCEDURE_TIMEOUT
|
||||
} else {
|
||||
probe_timeout
|
||||
};
|
||||
let started_at = Instant::now();
|
||||
let checked_at_micros = current_unix_micros();
|
||||
let result = self.acquire_connection_with_timeout(timeout).await;
|
||||
match result {
|
||||
Ok(lease) => {
|
||||
self.release_connection(lease).await;
|
||||
let mut health_state = self.health_state.write().await;
|
||||
health_state.last_success_at_micros = Some(checked_at_micros);
|
||||
health_state.last_error = None;
|
||||
SpacetimeClientHealthSnapshot {
|
||||
ok: true,
|
||||
stage: SpacetimeClientStage::Ready,
|
||||
checked_at_micros,
|
||||
elapsed_ms: duration_millis_u64(started_at.elapsed()),
|
||||
timeout_ms: duration_millis_u64(timeout),
|
||||
error: None,
|
||||
last_success_at_micros: health_state.last_success_at_micros,
|
||||
last_error: health_state.last_error.clone(),
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
log_spacetime_client_failure(
|
||||
"health_check",
|
||||
"spacetime_connection",
|
||||
error.stage,
|
||||
started_at,
|
||||
&error.error,
|
||||
);
|
||||
let mut health_state = self.health_state.write().await;
|
||||
let error_message = error.error.to_string();
|
||||
health_state.last_error = Some(error_message.clone());
|
||||
SpacetimeClientHealthSnapshot {
|
||||
ok: false,
|
||||
stage: error.stage,
|
||||
checked_at_micros,
|
||||
elapsed_ms: duration_millis_u64(started_at.elapsed()),
|
||||
timeout_ms: duration_millis_u64(timeout),
|
||||
error: Some(error_message),
|
||||
last_success_at_micros: health_state.last_success_at_micros,
|
||||
last_error: health_state.last_error.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn acquire_connection_with_timeout(
|
||||
&self,
|
||||
operation_timeout: Duration,
|
||||
) -> Result<PooledConnectionLease, SpacetimeStageError> {
|
||||
let permit = timeout(operation_timeout, self.pool.permits.clone().acquire_owned())
|
||||
.await
|
||||
.map_err(|_| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::PoolAcquire,
|
||||
SpacetimeClientError::Timeout,
|
||||
)
|
||||
})?
|
||||
.map_err(|error| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::PoolAcquire,
|
||||
SpacetimeClientError::Runtime(error.to_string()),
|
||||
)
|
||||
})?;
|
||||
|
||||
loop {
|
||||
for (slot_index, slot) in self.pool.slots.iter().enumerate() {
|
||||
@@ -485,7 +699,7 @@ impl SpacetimeClient {
|
||||
let connection = if let Some(connection) = reusable_connection {
|
||||
connection
|
||||
} else {
|
||||
match self.build_pooled_connection().await {
|
||||
match self.build_pooled_connection(operation_timeout).await {
|
||||
Ok(connection) => connection,
|
||||
Err(error) => {
|
||||
let mut slot_guard = self.pool.slots[slot_index].lock().await;
|
||||
@@ -507,7 +721,10 @@ impl SpacetimeClient {
|
||||
}
|
||||
}
|
||||
|
||||
async fn build_pooled_connection(&self) -> Result<PooledConnection, SpacetimeClientError> {
|
||||
async fn build_pooled_connection(
|
||||
&self,
|
||||
operation_timeout: Duration,
|
||||
) -> Result<PooledConnection, SpacetimeStageError> {
|
||||
let config = self.config.clone();
|
||||
let broken = Arc::new(AtomicBool::new(false));
|
||||
let (sender, receiver) = oneshot::channel::<Result<(), SpacetimeClientError>>();
|
||||
@@ -515,7 +732,7 @@ impl SpacetimeClient {
|
||||
let broken_flag = broken.clone();
|
||||
let disconnect_sender = connect_sender.clone();
|
||||
let connection = timeout(
|
||||
self.config.procedure_timeout,
|
||||
operation_timeout,
|
||||
tokio::task::spawn_blocking(move || {
|
||||
DbConnection::builder()
|
||||
.with_uri(config.server_url)
|
||||
@@ -539,17 +756,41 @@ impl SpacetimeClient {
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| SpacetimeClientError::Timeout)?
|
||||
.map_err(|error| SpacetimeClientError::Runtime(error.to_string()))??;
|
||||
.map_err(|_| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::ConnectBuild,
|
||||
SpacetimeClientError::Timeout,
|
||||
)
|
||||
})?
|
||||
.map_err(|error| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::ConnectBuild,
|
||||
SpacetimeClientError::Runtime(error.to_string()),
|
||||
)
|
||||
})?
|
||||
.map_err(|error| SpacetimeStageError::new(SpacetimeClientStage::ConnectBuild, error))?;
|
||||
|
||||
let runner = connection.run_threaded();
|
||||
timeout(self.config.procedure_timeout, receiver)
|
||||
timeout(operation_timeout, receiver)
|
||||
.await
|
||||
.map_err(|_| SpacetimeClientError::Timeout)?
|
||||
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
|
||||
.map_err(|_| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::ConnectHandshake,
|
||||
SpacetimeClientError::Timeout,
|
||||
)
|
||||
})?
|
||||
.map_err(|_| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::ConnectHandshake,
|
||||
SpacetimeClientError::ConnectDropped,
|
||||
)
|
||||
})?
|
||||
.map_err(|error| {
|
||||
SpacetimeStageError::new(SpacetimeClientStage::ConnectHandshake, error)
|
||||
})?;
|
||||
|
||||
let read_model_subscriptions = self
|
||||
.subscribe_cached_read_models(&connection, broken.clone())
|
||||
.subscribe_cached_read_models(&connection, broken.clone(), operation_timeout)
|
||||
.await?;
|
||||
|
||||
Ok(PooledConnection {
|
||||
@@ -564,7 +805,8 @@ impl SpacetimeClient {
|
||||
&self,
|
||||
connection: &DbConnection,
|
||||
broken: Arc<AtomicBool>,
|
||||
) -> Result<Vec<SubscriptionHandle>, SpacetimeClientError> {
|
||||
operation_timeout: Duration,
|
||||
) -> Result<Vec<SubscriptionHandle>, SpacetimeStageError> {
|
||||
let mut subscriptions = Vec::new();
|
||||
for query in [
|
||||
"SELECT * FROM public_work_gallery_entry",
|
||||
@@ -581,7 +823,13 @@ impl SpacetimeClient {
|
||||
"SELECT * FROM big_fish_gallery_view",
|
||||
] {
|
||||
let subscription = self
|
||||
.subscribe_cached_read_model_query(connection, broken.clone(), query, true)
|
||||
.subscribe_cached_read_model_query(
|
||||
connection,
|
||||
broken.clone(),
|
||||
query,
|
||||
true,
|
||||
operation_timeout,
|
||||
)
|
||||
.await?;
|
||||
subscriptions.push(subscription);
|
||||
}
|
||||
@@ -602,7 +850,13 @@ impl SpacetimeClient {
|
||||
"SELECT * FROM asset_object",
|
||||
] {
|
||||
if let Ok(subscription) = self
|
||||
.subscribe_cached_read_model_query(connection, broken.clone(), query, false)
|
||||
.subscribe_cached_read_model_query(
|
||||
connection,
|
||||
broken.clone(),
|
||||
query,
|
||||
false,
|
||||
operation_timeout,
|
||||
)
|
||||
.await
|
||||
{
|
||||
subscriptions.push(subscription);
|
||||
@@ -618,7 +872,8 @@ impl SpacetimeClient {
|
||||
broken: Arc<AtomicBool>,
|
||||
query: &'static str,
|
||||
mark_broken_on_error: bool,
|
||||
) -> Result<SubscriptionHandle, SpacetimeClientError> {
|
||||
operation_timeout: Duration,
|
||||
) -> Result<SubscriptionHandle, SpacetimeStageError> {
|
||||
let (sender, receiver) = oneshot::channel::<Result<(), SpacetimeClientError>>();
|
||||
let applied_sender = Arc::new(Mutex::new(Some(sender)));
|
||||
let on_applied_sender = applied_sender.clone();
|
||||
@@ -640,10 +895,23 @@ impl SpacetimeClient {
|
||||
})
|
||||
.subscribe(query);
|
||||
|
||||
timeout(self.config.procedure_timeout, receiver)
|
||||
timeout(operation_timeout, receiver)
|
||||
.await
|
||||
.map_err(|_| SpacetimeClientError::Timeout)?
|
||||
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
|
||||
.map_err(|_| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::ReadModelSubscribe,
|
||||
SpacetimeClientError::Timeout,
|
||||
)
|
||||
})?
|
||||
.map_err(|_| {
|
||||
SpacetimeStageError::new(
|
||||
SpacetimeClientStage::ReadModelSubscribe,
|
||||
SpacetimeClientError::ConnectDropped,
|
||||
)
|
||||
})?
|
||||
.map_err(|error| {
|
||||
SpacetimeStageError::new(SpacetimeClientStage::ReadModelSubscribe, error)
|
||||
})?;
|
||||
|
||||
Ok(subscription)
|
||||
}
|
||||
@@ -663,7 +931,10 @@ impl SpacetimeClient {
|
||||
}
|
||||
|
||||
// 超时后必须统一归还租约;若连接已先一步断开则回传断线,否则标记坏连接并回传超时。
|
||||
fn resolve_timeout_error(connection: Option<&PooledConnection>) -> SpacetimeClientError {
|
||||
fn resolve_timeout_error(
|
||||
connection: Option<&PooledConnection>,
|
||||
_stage: SpacetimeClientStage,
|
||||
) -> SpacetimeClientError {
|
||||
if let Some(connection) = connection {
|
||||
if connection.is_broken() {
|
||||
return SpacetimeClientError::ConnectDropped;
|
||||
@@ -686,6 +957,27 @@ fn current_public_work_day() -> i64 {
|
||||
current_unix_micros().div_euclid(PUBLIC_WORK_PLAY_DAY_MICROS)
|
||||
}
|
||||
|
||||
fn duration_millis_u64(duration: Duration) -> u64 {
|
||||
duration.as_millis().min(u64::MAX as u128) as u64
|
||||
}
|
||||
|
||||
fn log_spacetime_client_failure(
|
||||
operation_kind: &'static str,
|
||||
operation_name: &'static str,
|
||||
stage: SpacetimeClientStage,
|
||||
started_at: Instant,
|
||||
error: &SpacetimeClientError,
|
||||
) {
|
||||
warn!(
|
||||
operation_kind,
|
||||
operation_name,
|
||||
spacetime_stage = stage.as_str(),
|
||||
elapsed_ms = duration_millis_u64(started_at.elapsed()),
|
||||
error = %error,
|
||||
"SpacetimeDB client operation failed"
|
||||
);
|
||||
}
|
||||
|
||||
fn public_work_recent_play_counts(
|
||||
connection: &DbConnection,
|
||||
source_type: &str,
|
||||
|
||||
@@ -8,9 +8,9 @@ pub use shared_contracts::jump_hop::{
|
||||
JumpHopRestartRunRequest, JumpHopRunResponse, JumpHopRunStatus,
|
||||
JumpHopRuntimeRunSnapshotResponse, JumpHopScoring, JumpHopSessionResponse,
|
||||
JumpHopSessionSnapshotResponse, JumpHopStartRunRequest, JumpHopStylePreset, JumpHopTileAsset,
|
||||
JumpHopTileType, JumpHopWorkDetailResponse, JumpHopWorkMutationResponse,
|
||||
JumpHopWorkProfileResponse, JumpHopWorkSummaryResponse, JumpHopWorksResponse,
|
||||
JumpHopWorkspaceCreateRequest,
|
||||
JumpHopTileFaceAsset, JumpHopTileFaceAssets, JumpHopTileFaceKey, JumpHopTileType,
|
||||
JumpHopWorkDetailResponse, JumpHopWorkMutationResponse, JumpHopWorkProfileResponse,
|
||||
JumpHopWorkSummaryResponse, JumpHopWorksResponse, JumpHopWorkspaceCreateRequest,
|
||||
};
|
||||
|
||||
pub(crate) fn map_jump_hop_agent_session_procedure_result(
|
||||
@@ -267,6 +267,33 @@ fn map_tile_asset(snapshot: JumpHopTileAssetSnapshot) -> JumpHopTileAsset {
|
||||
visual_height: snapshot.visual_height,
|
||||
top_surface_radius: snapshot.top_surface_radius,
|
||||
landing_radius: snapshot.landing_radius,
|
||||
face_assets: snapshot.face_assets.map(map_tile_face_assets),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_tile_face_assets(snapshot: JumpHopTileFaceAssetsSnapshot) -> JumpHopTileFaceAssets {
|
||||
JumpHopTileFaceAssets {
|
||||
top: map_tile_face_asset(snapshot.top),
|
||||
front: map_tile_face_asset(snapshot.front),
|
||||
right: map_tile_face_asset(snapshot.right),
|
||||
back: map_tile_face_asset(snapshot.back),
|
||||
left: map_tile_face_asset(snapshot.left),
|
||||
bottom: map_tile_face_asset(snapshot.bottom),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_tile_face_asset(snapshot: JumpHopTileFaceAssetSnapshot) -> JumpHopTileFaceAsset {
|
||||
JumpHopTileFaceAsset {
|
||||
face: parse_tile_face_key(&snapshot.face),
|
||||
asset_id: snapshot.asset_id,
|
||||
image_src: snapshot.image_src,
|
||||
image_object_key: snapshot.image_object_key,
|
||||
asset_object_id: snapshot.asset_object_id,
|
||||
generation_provider: snapshot.generation_provider,
|
||||
prompt: snapshot.prompt,
|
||||
width: snapshot.width,
|
||||
height: snapshot.height,
|
||||
source_atlas_cell: snapshot.source_atlas_cell,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +432,17 @@ fn parse_tile_type(value: &str) -> JumpHopTileType {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_tile_face_key(value: &str) -> JumpHopTileFaceKey {
|
||||
match value {
|
||||
"front" => JumpHopTileFaceKey::Front,
|
||||
"right" => JumpHopTileFaceKey::Right,
|
||||
"back" => JumpHopTileFaceKey::Back,
|
||||
"left" => JumpHopTileFaceKey::Left,
|
||||
"bottom" => JumpHopTileFaceKey::Bottom,
|
||||
_ => JumpHopTileFaceKey::Top,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_domain_tile_type(value: crate::module_bindings::JumpHopTileType) -> JumpHopTileType {
|
||||
match value {
|
||||
crate::module_bindings::JumpHopTileType::Start => JumpHopTileType::Start,
|
||||
|
||||
@@ -30,6 +30,17 @@ impl From<module_runtime::CreationEntryEventBannersAdminUpsertInput>
|
||||
}
|
||||
}
|
||||
|
||||
/// 将业务层公开作品互动配置保存输入转换为 SpacetimeDB 生成绑定类型。
|
||||
impl From<module_runtime::PublicWorkInteractionConfigAdminUpsertInput>
|
||||
for PublicWorkInteractionConfigAdminUpsertInput
|
||||
{
|
||||
fn from(input: module_runtime::PublicWorkInteractionConfigAdminUpsertInput) -> Self {
|
||||
Self {
|
||||
public_work_interactions_json: input.public_work_interactions_json,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<module_runtime::AdminWorkVisibilityListInput> for AdminWorkVisibilityListInput {
|
||||
fn from(input: module_runtime::AdminWorkVisibilityListInput) -> Self {
|
||||
Self {
|
||||
@@ -323,6 +334,7 @@ pub(crate) fn build_creation_entry_config_record_from_rows(
|
||||
})
|
||||
.collect(),
|
||||
updated_at_micros: header.updated_at.to_micros_since_unix_epoch(),
|
||||
public_work_interactions_json: header.public_work_interactions_json,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -376,6 +388,7 @@ fn map_creation_entry_config_snapshot(
|
||||
})
|
||||
.collect(),
|
||||
updated_at_micros: snapshot.updated_at_micros,
|
||||
public_work_interactions_json: snapshot.public_work_interactions_json,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,6 +445,7 @@ mod tests {
|
||||
event_starts_at_text: None,
|
||||
event_ends_at_text: None,
|
||||
event_banners_json: None,
|
||||
public_work_interactions_json: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,6 +528,7 @@ mod tests {
|
||||
unified_creation_spec_json: None,
|
||||
}],
|
||||
updated_at_micros: 1_000_000,
|
||||
public_work_interactions_json: None,
|
||||
});
|
||||
|
||||
let jump_hop = record
|
||||
|
||||
@@ -476,6 +476,8 @@ pub mod jump_hop_runtime_run_row_type;
|
||||
pub mod jump_hop_runtime_run_table;
|
||||
pub mod jump_hop_scoring_type;
|
||||
pub mod jump_hop_tile_asset_snapshot_type;
|
||||
pub mod jump_hop_tile_face_asset_snapshot_type;
|
||||
pub mod jump_hop_tile_face_assets_snapshot_type;
|
||||
pub mod jump_hop_tile_type_type;
|
||||
pub mod jump_hop_work_delete_input_type;
|
||||
pub mod jump_hop_work_get_input_type;
|
||||
@@ -603,6 +605,7 @@ pub mod public_work_detail_entry_table;
|
||||
pub mod public_work_detail_entry_type;
|
||||
pub mod public_work_gallery_entry_table;
|
||||
pub mod public_work_gallery_entry_type;
|
||||
pub mod public_work_interaction_config_admin_upsert_input_type;
|
||||
pub mod public_work_like_table;
|
||||
pub mod public_work_like_type;
|
||||
pub mod public_work_play_daily_stat_table;
|
||||
@@ -1036,6 +1039,7 @@ pub mod upsert_custom_world_profile_reducer;
|
||||
pub mod upsert_npc_state_and_return_procedure;
|
||||
pub mod upsert_npc_state_reducer;
|
||||
pub mod upsert_platform_browse_history_and_return_procedure;
|
||||
pub mod upsert_public_work_interaction_config_procedure;
|
||||
pub mod upsert_runtime_setting_and_return_procedure;
|
||||
pub mod upsert_runtime_snapshot_and_return_procedure;
|
||||
pub mod upsert_visual_novel_run_snapshot_procedure;
|
||||
@@ -1596,6 +1600,8 @@ pub use jump_hop_runtime_run_row_type::JumpHopRuntimeRunRow;
|
||||
pub use jump_hop_runtime_run_table::*;
|
||||
pub use jump_hop_scoring_type::JumpHopScoring;
|
||||
pub use jump_hop_tile_asset_snapshot_type::JumpHopTileAssetSnapshot;
|
||||
pub use jump_hop_tile_face_asset_snapshot_type::JumpHopTileFaceAssetSnapshot;
|
||||
pub use jump_hop_tile_face_assets_snapshot_type::JumpHopTileFaceAssetsSnapshot;
|
||||
pub use jump_hop_tile_type_type::JumpHopTileType;
|
||||
pub use jump_hop_work_delete_input_type::JumpHopWorkDeleteInput;
|
||||
pub use jump_hop_work_get_input_type::JumpHopWorkGetInput;
|
||||
@@ -1723,6 +1729,7 @@ pub use public_work_detail_entry_table::*;
|
||||
pub use public_work_detail_entry_type::PublicWorkDetailEntry;
|
||||
pub use public_work_gallery_entry_table::*;
|
||||
pub use public_work_gallery_entry_type::PublicWorkGalleryEntry;
|
||||
pub use public_work_interaction_config_admin_upsert_input_type::PublicWorkInteractionConfigAdminUpsertInput;
|
||||
pub use public_work_like_table::*;
|
||||
pub use public_work_like_type::PublicWorkLike;
|
||||
pub use public_work_play_daily_stat_table::*;
|
||||
@@ -2156,6 +2163,7 @@ pub use upsert_custom_world_profile_reducer::upsert_custom_world_profile;
|
||||
pub use upsert_npc_state_and_return_procedure::upsert_npc_state_and_return;
|
||||
pub use upsert_npc_state_reducer::upsert_npc_state;
|
||||
pub use upsert_platform_browse_history_and_return_procedure::upsert_platform_browse_history_and_return;
|
||||
pub use upsert_public_work_interaction_config_procedure::upsert_public_work_interaction_config;
|
||||
pub use upsert_runtime_setting_and_return_procedure::upsert_runtime_setting_and_return;
|
||||
pub use upsert_runtime_snapshot_and_return_procedure::upsert_runtime_snapshot_and_return;
|
||||
pub use upsert_visual_novel_run_snapshot_procedure::upsert_visual_novel_run_snapshot;
|
||||
|
||||
@@ -19,6 +19,7 @@ pub struct CreationEntryConfigSnapshot {
|
||||
pub event_banners_json: Option<String>,
|
||||
pub creation_types: Vec<CreationEntryTypeSnapshot>,
|
||||
pub updated_at_micros: i64,
|
||||
pub public_work_interactions_json: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for CreationEntryConfigSnapshot {
|
||||
|
||||
@@ -22,6 +22,7 @@ pub struct CreationEntryConfig {
|
||||
pub event_starts_at_text: Option<String>,
|
||||
pub event_ends_at_text: Option<String>,
|
||||
pub event_banners_json: Option<String>,
|
||||
pub public_work_interactions_json: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for CreationEntryConfig {
|
||||
@@ -47,6 +48,8 @@ pub struct CreationEntryConfigCols {
|
||||
pub event_starts_at_text: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
|
||||
pub event_ends_at_text: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
|
||||
pub event_banners_json: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
|
||||
pub public_work_interactions_json:
|
||||
__sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasCols for CreationEntryConfig {
|
||||
@@ -77,6 +80,10 @@ impl __sdk::__query_builder::HasCols for CreationEntryConfig {
|
||||
),
|
||||
event_ends_at_text: __sdk::__query_builder::Col::new(table_name, "event_ends_at_text"),
|
||||
event_banners_json: __sdk::__query_builder::Col::new(table_name, "event_banners_json"),
|
||||
public_work_interactions_json: __sdk::__query_builder::Col::new(
|
||||
table_name,
|
||||
"public_work_interactions_json",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::jump_hop_tile_face_assets_snapshot_type::JumpHopTileFaceAssetsSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct JumpHopTileAssetSnapshot {
|
||||
@@ -19,6 +21,7 @@ pub struct JumpHopTileAssetSnapshot {
|
||||
pub visual_height: u32,
|
||||
pub top_surface_radius: f32,
|
||||
pub landing_radius: f32,
|
||||
pub face_assets: Option<JumpHopTileFaceAssetsSnapshot>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for JumpHopTileAssetSnapshot {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct JumpHopTileFaceAssetSnapshot {
|
||||
pub face: String,
|
||||
pub asset_id: String,
|
||||
pub image_src: String,
|
||||
pub image_object_key: String,
|
||||
pub asset_object_id: String,
|
||||
pub generation_provider: String,
|
||||
pub prompt: String,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub source_atlas_cell: String,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for JumpHopTileFaceAssetSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::jump_hop_tile_face_asset_snapshot_type::JumpHopTileFaceAssetSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct JumpHopTileFaceAssetsSnapshot {
|
||||
pub top: JumpHopTileFaceAssetSnapshot,
|
||||
pub front: JumpHopTileFaceAssetSnapshot,
|
||||
pub right: JumpHopTileFaceAssetSnapshot,
|
||||
pub back: JumpHopTileFaceAssetSnapshot,
|
||||
pub left: JumpHopTileFaceAssetSnapshot,
|
||||
pub bottom: JumpHopTileFaceAssetSnapshot,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for JumpHopTileFaceAssetsSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct PublicWorkInteractionConfigAdminUpsertInput {
|
||||
pub public_work_interactions_json: String,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for PublicWorkInteractionConfigAdminUpsertInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::creation_entry_config_procedure_result_type::CreationEntryConfigProcedureResult;
|
||||
use super::public_work_interaction_config_admin_upsert_input_type::PublicWorkInteractionConfigAdminUpsertInput;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct UpsertPublicWorkInteractionConfigArgs {
|
||||
pub input: PublicWorkInteractionConfigAdminUpsertInput,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for UpsertPublicWorkInteractionConfigArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `upsert_public_work_interaction_config`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait upsert_public_work_interaction_config {
|
||||
fn upsert_public_work_interaction_config(
|
||||
&self,
|
||||
input: PublicWorkInteractionConfigAdminUpsertInput,
|
||||
) {
|
||||
self.upsert_public_work_interaction_config_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn upsert_public_work_interaction_config_then(
|
||||
&self,
|
||||
input: PublicWorkInteractionConfigAdminUpsertInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<CreationEntryConfigProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl upsert_public_work_interaction_config for super::RemoteProcedures {
|
||||
fn upsert_public_work_interaction_config_then(
|
||||
&self,
|
||||
input: PublicWorkInteractionConfigAdminUpsertInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<CreationEntryConfigProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, CreationEntryConfigProcedureResult>(
|
||||
"upsert_public_work_interaction_config",
|
||||
UpsertPublicWorkInteractionConfigArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -115,6 +115,34 @@ impl SpacetimeClient {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// 调用 SpacetimeDB procedure 保存公开作品互动配置并刷新缓存。
|
||||
pub async fn upsert_public_work_interaction_config(
|
||||
&self,
|
||||
input: module_runtime::PublicWorkInteractionConfigAdminUpsertInput,
|
||||
) -> Result<CreationEntryConfigRecord, SpacetimeClientError> {
|
||||
let procedure_input: PublicWorkInteractionConfigAdminUpsertInput = input.into();
|
||||
let config = self
|
||||
.call_after_connect(
|
||||
"upsert_public_work_interaction_config",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.upsert_public_work_interaction_config_then(
|
||||
procedure_input,
|
||||
move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_creation_entry_config_procedure_result);
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
self.cache_creation_entry_config(config.clone()).await;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub async fn admin_list_work_visibility(
|
||||
&self,
|
||||
admin_user_id: String,
|
||||
|
||||
Reference in New Issue
Block a user