feat: add puzzle clear template runtime

This commit is contained in:
2026-06-03 22:11:46 +08:00
parent 6e74cf5add
commit 1b5e098225
148 changed files with 19588 additions and 241 deletions

View File

@@ -19,6 +19,7 @@ pub mod match3d_runtime;
pub mod match3d_works;
pub mod public_work;
pub mod puzzle_agent;
pub mod puzzle_clear;
pub mod puzzle_creative_template;
pub mod puzzle_gallery;
pub mod puzzle_runtime;

View File

@@ -0,0 +1,313 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PuzzleClearGenerationStatus {
Draft,
Generating,
Ready,
Failed,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PuzzleClearActionType {
CompileDraft,
RegenerateAtlas,
UpdateWorkMeta,
UpdateBoardBackground,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PuzzleClearRunStatus {
Playing,
LevelFailed,
LevelCleared,
Finished,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearImageAsset {
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,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearPatternGroup {
pub group_id: String,
pub shape: String,
pub width: u32,
pub height: u32,
pub atlas_x: u32,
pub atlas_y: u32,
pub atlas_width: u32,
pub atlas_height: u32,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearCardAsset {
pub card_id: String,
pub group_id: String,
pub shape: String,
pub orientation: String,
pub part_x: u32,
pub part_y: u32,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub source_atlas_cell: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkspaceCreateRequest {
pub template_id: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
#[serde(default)]
pub board_background_prompt: String,
pub generate_board_background: bool,
pub board_background_asset: Option<PuzzleClearImageAsset>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearActionRequest {
pub action_type: PuzzleClearActionType,
pub profile_id: Option<String>,
pub work_title: Option<String>,
pub work_description: Option<String>,
pub theme_prompt: Option<String>,
#[serde(default)]
pub board_background_prompt: Option<String>,
pub generate_board_background: Option<bool>,
pub board_background_asset: Option<PuzzleClearImageAsset>,
pub atlas_asset: Option<PuzzleClearImageAsset>,
pub pattern_groups: Option<Vec<PuzzleClearPatternGroup>>,
pub card_assets: Option<Vec<PuzzleClearCardAsset>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearDraftResponse {
pub template_id: String,
pub template_name: String,
pub profile_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
#[serde(default)]
pub board_background_prompt: String,
pub generate_board_background: bool,
pub board_background_asset: Option<PuzzleClearImageAsset>,
pub card_back_image_src: Option<String>,
pub atlas_asset: Option<PuzzleClearImageAsset>,
pub pattern_groups: Vec<PuzzleClearPatternGroup>,
pub card_assets: Vec<PuzzleClearCardAsset>,
pub generation_status: PuzzleClearGenerationStatus,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearSessionSnapshotResponse {
pub session_id: String,
pub owner_user_id: String,
pub status: PuzzleClearGenerationStatus,
pub draft: Option<PuzzleClearDraftResponse>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearSessionResponse {
pub session: PuzzleClearSessionSnapshotResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearActionResponse {
pub action_type: PuzzleClearActionType,
pub session: PuzzleClearSessionSnapshotResponse,
pub work: Option<PuzzleClearWorkProfileResponse>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkSummaryResponse {
pub runtime_kind: String,
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub source_session_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub cover_image_src: Option<String>,
pub publication_status: String,
pub play_count: u32,
pub updated_at: String,
pub published_at: Option<String>,
pub publish_ready: bool,
pub generation_status: PuzzleClearGenerationStatus,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkProfileResponse {
pub summary: PuzzleClearWorkSummaryResponse,
pub draft: PuzzleClearDraftResponse,
pub board_background_asset: Option<PuzzleClearImageAsset>,
pub atlas_asset: PuzzleClearImageAsset,
pub pattern_groups: Vec<PuzzleClearPatternGroup>,
pub card_assets: Vec<PuzzleClearCardAsset>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorksResponse {
pub items: Vec<PuzzleClearWorkSummaryResponse>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkDetailResponse {
pub item: PuzzleClearWorkProfileResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkMutationResponse {
pub item: PuzzleClearWorkProfileResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearBoardCell {
pub row: u32,
pub col: u32,
pub card: Option<PuzzleClearCardAsset>,
pub locked_group_id: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearBoardSnapshot {
pub rows: u32,
pub cols: u32,
pub cells: Vec<PuzzleClearBoardCell>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearRuntimeSnapshotResponse {
pub run_id: String,
pub profile_id: String,
pub owner_user_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime_mode: Option<String>,
pub status: PuzzleClearRunStatus,
pub level_index: u32,
pub clears_done: u32,
pub target_clears: u32,
pub level_duration_seconds: u32,
pub level_started_at_ms: u64,
pub board: PuzzleClearBoardSnapshot,
pub ready_columns: Vec<Vec<PuzzleClearCardAsset>>,
pub started_at_ms: u64,
pub finished_at_ms: Option<u64>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearRunResponse {
pub run: PuzzleClearRuntimeSnapshotResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearStartRunRequest {
pub profile_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearSwapRequest {
pub from_row: u32,
pub from_col: u32,
pub to_row: u32,
pub to_col: u32,
pub client_action_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearRetryLevelRequest {
pub client_action_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearNextLevelRequest {
pub client_action_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearTimeUpRequest {
pub client_action_id: String,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn workspace_create_request_uses_camel_case() {
let payload = PuzzleClearWorkspaceCreateRequest {
template_id: "puzzle-clear".to_string(),
work_title: "花园拼消消".to_string(),
work_description: "轻松消除".to_string(),
theme_prompt: "春日花园".to_string(),
board_background_prompt: "樱花庭院".to_string(),
generate_board_background: true,
board_background_asset: None,
};
let value = serde_json::to_value(payload).expect("request should serialize");
assert_eq!(value["templateId"], json!("puzzle-clear"));
assert_eq!(value["themePrompt"], json!("春日花园"));
assert_eq!(value["boardBackgroundPrompt"], json!("樱花庭院"));
assert_eq!(value["generateBoardBackground"], json!(true));
}
#[test]
fn runtime_swap_request_uses_camel_case() {
let payload = PuzzleClearSwapRequest {
from_row: 1,
from_col: 2,
to_row: 1,
to_col: 3,
client_action_id: "swap-1".to_string(),
};
let value = serde_json::to_value(payload).expect("request should serialize");
assert_eq!(value["fromRow"], json!(1));
assert_eq!(value["toCol"], json!(3));
assert_eq!(value["clientActionId"], json!("swap-1"));
}
}