已发布作品可二次编辑

This commit is contained in:
2026-04-26 15:32:36 +08:00
parent 7aabbcc10c
commit de2c49005f
13 changed files with 154 additions and 77 deletions

View File

@@ -47,13 +47,14 @@ use spacetime_client::{
PuzzleAgentMessageRecord, PuzzleAgentMessageSubmitRecordInput,
PuzzleAgentSessionCreateRecordInput, PuzzleAgentSessionRecord,
PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord, PuzzleAnchorPackRecord,
PuzzleCreatorIntentRecord, PuzzleGeneratedImageCandidateRecord,
PuzzleGeneratedImagesSaveRecordInput, PuzzlePublishRecordInput, PuzzleResultDraftRecord,
PuzzleResultPreviewBlockerRecord, PuzzleResultPreviewFindingRecord, PuzzleResultPreviewRecord,
PuzzleBoardRecord, PuzzleCellPositionRecord, PuzzleMergedGroupRecord, PuzzlePieceStateRecord,
PuzzleRunDragRecordInput, PuzzleRunRecord, PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput,
PuzzleRuntimeLevelRecord, PuzzleSelectCoverImageRecordInput, PuzzleWorkProfileRecord,
PuzzleWorkUpsertRecordInput, SpacetimeClientError,
PuzzleBoardRecord, PuzzleCellPositionRecord, PuzzleCreatorIntentRecord,
PuzzleGeneratedImageCandidateRecord, PuzzleGeneratedImagesSaveRecordInput,
PuzzleMergedGroupRecord, PuzzlePieceStateRecord, PuzzlePublishRecordInput,
PuzzleResultDraftRecord, PuzzleResultPreviewBlockerRecord, PuzzleResultPreviewFindingRecord,
PuzzleResultPreviewRecord, PuzzleRunDragRecordInput, PuzzleRunRecord,
PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput, PuzzleRuntimeLevelRecord,
PuzzleSelectCoverImageRecordInput, PuzzleWorkProfileRecord, PuzzleWorkUpsertRecordInput,
SpacetimeClientError,
};
use std::convert::Infallible;
use tokio::time::sleep;
@@ -1639,7 +1640,10 @@ async fn generate_puzzle_image_candidates(
let mut items = Vec::with_capacity(generated.images.len());
for (index, image) in generated.images.into_iter().enumerate() {
let candidate_id = format!("{session_id}-candidate-{}", candidate_start_index + index + 1);
let candidate_id = format!(
"{session_id}-candidate-{}",
candidate_start_index + index + 1
);
let asset = persist_puzzle_generated_asset(
state,
owner_user_id,
@@ -1690,10 +1694,12 @@ async fn build_local_next_puzzle_run(
}))
})?;
if current_level.status != "cleared" {
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
"provider": PUZZLE_RUNTIME_PROVIDER,
"message": "current level is not cleared",
})));
return Err(
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
"provider": PUZZLE_RUNTIME_PROVIDER,
"message": "current level is not cleared",
})),
);
}
if let Some(gallery_item) = resolve_gallery_next_puzzle_work(state, &run).await? {
@@ -1702,10 +1708,12 @@ async fn build_local_next_puzzle_run(
let source_session_id = payload.source_session_id.unwrap_or_default();
if source_session_id.trim().is_empty() {
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
"provider": PUZZLE_RUNTIME_PROVIDER,
"message": "sourceSessionId is required when gallery has no next puzzle work",
})));
return Err(
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
"provider": PUZZLE_RUNTIME_PROVIDER,
"message": "sourceSessionId is required when gallery has no next puzzle work",
})),
);
}
let session = state
.spacetime_client()
@@ -1767,14 +1775,23 @@ async fn build_local_next_puzzle_run(
let candidate = updated_session
.draft
.as_ref()
.and_then(|draft| draft.candidates.iter().find(|candidate| !candidate.image_src.is_empty()))
.and_then(|draft| {
draft
.candidates
.iter()
.find(|candidate| !candidate.image_src.is_empty())
})
.ok_or_else(|| {
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": PUZZLE_RUNTIME_PROVIDER,
"message": "现场生成后没有可用候选图",
}))
})?;
Ok(build_next_run_from_candidate(run, &updated_session, candidate))
Ok(build_next_run_from_candidate(
run,
&updated_session,
candidate,
))
}
async fn resolve_gallery_next_puzzle_work(
@@ -1788,7 +1805,10 @@ async fn resolve_gallery_next_puzzle_work(
.map_err(map_puzzle_client_error)?;
Ok(items.into_iter().find(|item| {
item.publication_status == "published"
&& item.cover_image_src.as_ref().is_some_and(|value| !value.is_empty())
&& item
.cover_image_src
.as_ref()
.is_some_and(|value| !value.is_empty())
&& !run.played_profile_ids.contains(&item.profile_id)
}))
}
@@ -1836,7 +1856,9 @@ fn build_next_run_from_candidate(
.map(|draft| format!("{} · 候选 {}", draft.level_name, level_index))
.unwrap_or_else(|| format!("候选拼图 {level_index}")),
"当前草稿".to_string(),
draft.map(|draft| draft.theme_tags.clone()).unwrap_or_default(),
draft
.map(|draft| draft.theme_tags.clone())
.unwrap_or_default(),
Some(candidate.image_src.clone()),
)
}
@@ -1893,13 +1915,14 @@ fn build_local_puzzle_board(grid_size: u32) -> PuzzleBoardRecord {
}
let pieces = (0..total)
.map(|index| {
let current = positions
.get(index as usize)
.cloned()
.unwrap_or(PuzzleCellPositionRecord {
row: index / grid_size,
col: index % grid_size,
});
let current =
positions
.get(index as usize)
.cloned()
.unwrap_or(PuzzleCellPositionRecord {
row: index / grid_size,
col: index % grid_size,
});
PuzzlePieceStateRecord {
piece_id: format!("piece-{index}"),
correct_row: index / grid_size,