use serde::{Deserialize, Serialize}; use crate::puzzle_agent::{PuzzleAnchorPackResponse, PuzzleDraftLevelResponse}; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PutPuzzleWorkRequest { pub work_title: String, pub work_description: String, pub level_name: String, pub summary: String, pub theme_tags: Vec, #[serde(default)] pub cover_image_src: Option, #[serde(default)] pub cover_asset_id: Option, #[serde(default)] pub levels: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PuzzleWorkSummaryResponse { pub work_id: String, pub profile_id: String, pub owner_user_id: String, #[serde(default)] pub source_session_id: Option, pub author_display_name: String, pub work_title: String, pub work_description: String, pub level_name: String, pub summary: String, pub theme_tags: Vec, #[serde(default)] pub cover_image_src: Option, #[serde(default)] pub cover_asset_id: Option, pub publication_status: String, pub updated_at: String, #[serde(default)] pub published_at: Option, pub play_count: u32, #[serde(default)] pub remix_count: u32, #[serde(default)] pub like_count: u32, #[serde(default)] pub recent_play_count_7d: u32, #[serde(default)] pub point_incentive_total_half_points: u64, #[serde(default)] pub point_incentive_claimed_points: u64, #[serde(default)] pub point_incentive_total_points: f64, #[serde(default)] pub point_incentive_claimable_points: u64, pub publish_ready: bool, #[serde(default)] pub levels: Vec, } #[cfg(test)] mod tests { use super::*; #[test] fn puzzle_work_summary_response_uses_point_incentive_fields() { let payload = serde_json::to_value(PuzzleWorkSummaryResponse { work_id: "work-1".to_string(), profile_id: "profile-1".to_string(), owner_user_id: "user-1".to_string(), source_session_id: None, author_display_name: "作者".to_string(), work_title: "作品".to_string(), work_description: "描述".to_string(), level_name: "第一关".to_string(), summary: "画面".to_string(), theme_tags: vec!["拼图".to_string(), "夜色".to_string(), "灯光".to_string()], cover_image_src: None, cover_asset_id: None, publication_status: "published".to_string(), updated_at: "2026-05-01T00:00:00Z".to_string(), published_at: Some("2026-05-01T00:00:00Z".to_string()), play_count: 1, remix_count: 0, like_count: 0, recent_play_count_7d: 1, point_incentive_total_half_points: 3, point_incentive_claimed_points: 1, point_incentive_total_points: 1.5, point_incentive_claimable_points: 0, publish_ready: true, levels: Vec::new(), }) .expect("payload should serialize"); assert_eq!(payload["pointIncentiveTotalHalfPoints"], 3); assert_eq!(payload["pointIncentiveClaimedPoints"], 1); assert_eq!(payload["pointIncentiveTotalPoints"], 1.5); assert_eq!(payload["pointIncentiveClaimablePoints"], 0); } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PuzzleWorkProfileResponse { #[serde(flatten)] pub summary: PuzzleWorkSummaryResponse, pub anchor_pack: PuzzleAnchorPackResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PuzzleWorksResponse { pub items: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PuzzleWorkDetailResponse { pub item: PuzzleWorkProfileResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PuzzleWorkMutationResponse { pub item: PuzzleWorkProfileResponse, }