1
This commit is contained in:
@@ -11,7 +11,7 @@ use module_puzzle::{
|
||||
PuzzleRunGetInput, PuzzleRunNextLevelInput, PuzzleRunProcedureResult, PuzzleRunSnapshot,
|
||||
PuzzleRunStartInput, PuzzleRunSwapInput, PuzzleRuntimeLevelStatus, PuzzleSelectCoverImageInput,
|
||||
PuzzleWorkDeleteInput, PuzzleWorkGetInput, PuzzleWorkProcedureResult, PuzzleWorkProfile,
|
||||
PuzzleWorkUpsertInput, PuzzleWorksListInput, PuzzleWorksProcedureResult,
|
||||
PuzzleWorkRemixInput, PuzzleWorkUpsertInput, PuzzleWorksListInput, PuzzleWorksProcedureResult,
|
||||
apply_publish_overrides_to_draft, apply_selected_candidate, build_result_preview,
|
||||
compile_result_draft, create_work_profile, infer_anchor_pack, normalize_theme_tags,
|
||||
publish_work_profile, resolve_puzzle_grid_size, select_next_profile, start_run, swap_pieces,
|
||||
@@ -77,6 +77,8 @@ pub struct PuzzleWorkProfileRow {
|
||||
cover_asset_id: Option<String>,
|
||||
publication_status: PuzzlePublicationStatus,
|
||||
play_count: u32,
|
||||
remix_count: u32,
|
||||
like_count: u32,
|
||||
anchor_pack_json: String,
|
||||
publish_ready: bool,
|
||||
created_at: Timestamp,
|
||||
@@ -387,6 +389,25 @@ pub fn get_puzzle_gallery_detail(
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn remix_puzzle_work(
|
||||
ctx: &mut ProcedureContext,
|
||||
input: PuzzleWorkRemixInput,
|
||||
) -> PuzzleAgentSessionProcedureResult {
|
||||
match ctx.try_with_tx(|tx| remix_puzzle_work_tx(tx, input.clone())) {
|
||||
Ok(session) => PuzzleAgentSessionProcedureResult {
|
||||
ok: true,
|
||||
session_json: Some(serialize_json(&session)),
|
||||
error_message: None,
|
||||
},
|
||||
Err(message) => PuzzleAgentSessionProcedureResult {
|
||||
ok: false,
|
||||
session_json: None,
|
||||
error_message: Some(message),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn start_puzzle_run(
|
||||
ctx: &mut ProcedureContext,
|
||||
@@ -931,6 +952,8 @@ fn update_puzzle_work_tx(
|
||||
cover_asset_id: input.cover_asset_id,
|
||||
publication_status: row.publication_status,
|
||||
play_count: row.play_count,
|
||||
remix_count: row.remix_count,
|
||||
like_count: row.like_count,
|
||||
anchor_pack_json: row.anchor_pack_json.clone(),
|
||||
publish_ready: row.publish_ready,
|
||||
created_at: row.created_at,
|
||||
@@ -1033,6 +1056,140 @@ fn get_puzzle_gallery_detail_tx(
|
||||
build_puzzle_work_profile_from_row(&row)
|
||||
}
|
||||
|
||||
fn remix_puzzle_work_tx(
|
||||
ctx: &TxContext,
|
||||
input: PuzzleWorkRemixInput,
|
||||
) -> Result<PuzzleAgentSessionSnapshot, String> {
|
||||
let source_profile_id = input.source_profile_id.trim();
|
||||
let target_owner_user_id = input.target_owner_user_id.trim();
|
||||
let target_session_id = input.target_session_id.trim();
|
||||
let target_profile_id = input.target_profile_id.trim();
|
||||
let target_work_id = input.target_work_id.trim();
|
||||
if source_profile_id.is_empty()
|
||||
|| target_owner_user_id.is_empty()
|
||||
|| target_session_id.is_empty()
|
||||
|| target_profile_id.is_empty()
|
||||
|| target_work_id.is_empty()
|
||||
{
|
||||
return Err("拼图 remix 参数不能为空".to_string());
|
||||
}
|
||||
if input.author_display_name.trim().is_empty() {
|
||||
return Err("拼图 remix 作者名不能为空".to_string());
|
||||
}
|
||||
ensure_session_missing(ctx, target_session_id)?;
|
||||
ensure_message_missing(ctx, input.welcome_message_id.trim())?;
|
||||
if ctx
|
||||
.db
|
||||
.puzzle_work_profile()
|
||||
.profile_id()
|
||||
.find(&target_profile_id.to_string())
|
||||
.is_some()
|
||||
{
|
||||
return Err("拼图 remix 目标作品已存在".to_string());
|
||||
}
|
||||
|
||||
let source = ctx
|
||||
.db
|
||||
.puzzle_work_profile()
|
||||
.profile_id()
|
||||
.find(&source_profile_id.to_string())
|
||||
.filter(|row| row.publication_status == PuzzlePublicationStatus::Published)
|
||||
.ok_or_else(|| "拼图已发布源作品不存在".to_string())?;
|
||||
let source_profile = build_puzzle_work_profile_from_row(&source)?;
|
||||
let remixed_at = Timestamp::from_micros_since_unix_epoch(input.remixed_at_micros);
|
||||
|
||||
replace_puzzle_work_profile(
|
||||
ctx,
|
||||
&source,
|
||||
PuzzleWorkProfileRow {
|
||||
profile_id: source.profile_id.clone(),
|
||||
work_id: source.work_id.clone(),
|
||||
owner_user_id: source.owner_user_id.clone(),
|
||||
source_session_id: source.source_session_id.clone(),
|
||||
author_display_name: source.author_display_name.clone(),
|
||||
level_name: source.level_name.clone(),
|
||||
summary: source.summary.clone(),
|
||||
theme_tags_json: source.theme_tags_json.clone(),
|
||||
cover_image_src: source.cover_image_src.clone(),
|
||||
cover_asset_id: source.cover_asset_id.clone(),
|
||||
publication_status: source.publication_status,
|
||||
play_count: source.play_count,
|
||||
remix_count: source.remix_count.saturating_add(1),
|
||||
like_count: source.like_count,
|
||||
anchor_pack_json: source.anchor_pack_json.clone(),
|
||||
publish_ready: source.publish_ready,
|
||||
created_at: source.created_at,
|
||||
updated_at: remixed_at,
|
||||
published_at: source.published_at,
|
||||
},
|
||||
);
|
||||
|
||||
let draft = PuzzleResultDraft {
|
||||
level_name: source_profile.level_name.clone(),
|
||||
summary: source_profile.summary.clone(),
|
||||
theme_tags: source_profile.theme_tags.clone(),
|
||||
forbidden_directives: Vec::new(),
|
||||
creator_intent: None,
|
||||
anchor_pack: source_profile.anchor_pack.clone(),
|
||||
candidates: Vec::new(),
|
||||
selected_candidate_id: None,
|
||||
cover_image_src: source_profile.cover_image_src.clone(),
|
||||
cover_asset_id: source_profile.cover_asset_id.clone(),
|
||||
generation_status: "ready".to_string(),
|
||||
};
|
||||
ctx.db.puzzle_agent_session().insert(PuzzleAgentSessionRow {
|
||||
session_id: target_session_id.to_string(),
|
||||
owner_user_id: target_owner_user_id.to_string(),
|
||||
seed_text: source_profile.summary.clone(),
|
||||
current_turn: 1,
|
||||
progress_percent: 88,
|
||||
stage: PuzzleAgentStage::DraftReady,
|
||||
anchor_pack_json: serialize_json(&source_profile.anchor_pack),
|
||||
draft_json: Some(serialize_json(&draft)),
|
||||
last_assistant_reply: Some("已从公开作品 Remix 出新的拼图草稿。".to_string()),
|
||||
published_profile_id: None,
|
||||
created_at: remixed_at,
|
||||
updated_at: remixed_at,
|
||||
});
|
||||
ctx.db.puzzle_agent_message().insert(PuzzleAgentMessageRow {
|
||||
message_id: input.welcome_message_id,
|
||||
session_id: target_session_id.to_string(),
|
||||
role: PuzzleAgentMessageRole::Assistant,
|
||||
kind: PuzzleAgentMessageKind::Summary,
|
||||
text: "已复制公开作品为你的草稿。".to_string(),
|
||||
created_at: remixed_at,
|
||||
});
|
||||
ctx.db.puzzle_work_profile().insert(PuzzleWorkProfileRow {
|
||||
profile_id: target_profile_id.to_string(),
|
||||
work_id: target_work_id.to_string(),
|
||||
owner_user_id: target_owner_user_id.to_string(),
|
||||
source_session_id: Some(target_session_id.to_string()),
|
||||
author_display_name: input.author_display_name.trim().to_string(),
|
||||
level_name: source_profile.level_name,
|
||||
summary: source_profile.summary,
|
||||
theme_tags_json: serialize_json(&source_profile.theme_tags),
|
||||
cover_image_src: source_profile.cover_image_src,
|
||||
cover_asset_id: source_profile.cover_asset_id,
|
||||
publication_status: PuzzlePublicationStatus::Draft,
|
||||
play_count: 0,
|
||||
remix_count: 0,
|
||||
like_count: 0,
|
||||
anchor_pack_json: serialize_json(&source_profile.anchor_pack),
|
||||
publish_ready: true,
|
||||
created_at: remixed_at,
|
||||
updated_at: remixed_at,
|
||||
published_at: None,
|
||||
});
|
||||
|
||||
get_puzzle_agent_session_tx(
|
||||
ctx,
|
||||
PuzzleAgentSessionGetInput {
|
||||
session_id: target_session_id.to_string(),
|
||||
owner_user_id: target_owner_user_id.to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn start_puzzle_run_tx(
|
||||
ctx: &TxContext,
|
||||
input: PuzzleRunStartInput,
|
||||
@@ -1308,6 +1465,8 @@ fn build_puzzle_work_profile_from_row(
|
||||
.published_at
|
||||
.map(|value| value.to_micros_since_unix_epoch()),
|
||||
play_count: row.play_count,
|
||||
remix_count: row.remix_count,
|
||||
like_count: row.like_count,
|
||||
publish_ready: row.publish_ready,
|
||||
anchor_pack: deserialize_anchor_pack(&row.anchor_pack_json)?,
|
||||
})
|
||||
@@ -1507,6 +1666,8 @@ fn upsert_puzzle_work_profile(ctx: &TxContext, profile: PuzzleWorkProfile) -> Re
|
||||
// 二次编辑发布同一个 profile 时,作品内容可以覆盖,但历史游玩数属于
|
||||
// 广场消费数据,不能因为重新发布被清零。
|
||||
play_count: existing.play_count.max(profile.play_count),
|
||||
remix_count: existing.remix_count.max(profile.remix_count),
|
||||
like_count: existing.like_count.max(profile.like_count),
|
||||
anchor_pack_json: serialize_json(&profile.anchor_pack),
|
||||
publish_ready: profile.publish_ready,
|
||||
created_at: existing.created_at,
|
||||
@@ -1532,6 +1693,8 @@ fn upsert_puzzle_work_profile(ctx: &TxContext, profile: PuzzleWorkProfile) -> Re
|
||||
cover_asset_id: profile.cover_asset_id,
|
||||
publication_status: profile.publication_status,
|
||||
play_count: profile.play_count,
|
||||
remix_count: profile.remix_count,
|
||||
like_count: profile.like_count,
|
||||
anchor_pack_json: serialize_json(&profile.anchor_pack),
|
||||
publish_ready: profile.publish_ready,
|
||||
created_at: Timestamp::from_micros_since_unix_epoch(profile.updated_at_micros),
|
||||
@@ -1620,6 +1783,8 @@ fn increment_puzzle_profile_play_count(
|
||||
cover_asset_id: row.cover_asset_id.clone(),
|
||||
publication_status: row.publication_status,
|
||||
play_count: row.play_count.saturating_add(1),
|
||||
remix_count: row.remix_count,
|
||||
like_count: row.like_count,
|
||||
anchor_pack_json: row.anchor_pack_json.clone(),
|
||||
publish_ready: row.publish_ready,
|
||||
created_at: row.created_at,
|
||||
|
||||
Reference in New Issue
Block a user