refactor: modularize api server assets and handlers
This commit is contained in:
490
server-rs/crates/api-server/src/match3d/mappers.rs
Normal file
490
server-rs/crates/api-server/src/match3d/mappers.rs
Normal file
@@ -0,0 +1,490 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) fn map_match3d_agent_session_response(
|
||||
session: Match3DAgentSessionRecord,
|
||||
) -> Match3DAgentSessionSnapshotResponse {
|
||||
Match3DAgentSessionSnapshotResponse {
|
||||
session_id: session.session_id,
|
||||
current_turn: session.current_turn,
|
||||
progress_percent: session.progress_percent,
|
||||
stage: session.stage.clone(),
|
||||
anchor_pack: map_match3d_anchor_pack_response_for_turn(
|
||||
session.anchor_pack,
|
||||
session.current_turn,
|
||||
session.stage.as_str(),
|
||||
),
|
||||
config: session.config.map(map_match3d_config_response),
|
||||
draft: session.draft.map(map_match3d_draft_response),
|
||||
messages: session
|
||||
.messages
|
||||
.into_iter()
|
||||
.map(map_match3d_message_response)
|
||||
.collect(),
|
||||
last_assistant_reply: session.last_assistant_reply,
|
||||
published_profile_id: session.published_profile_id,
|
||||
updated_at: session.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_agent_session_response_with_assets(
|
||||
session: Match3DAgentSessionRecord,
|
||||
generated_item_assets: &[Match3DGeneratedItemAsset],
|
||||
) -> Match3DAgentSessionSnapshotResponse {
|
||||
let mut response = map_match3d_agent_session_response(session);
|
||||
if let Some(draft) = response.draft.as_mut() {
|
||||
draft.generated_item_assets = generated_item_assets
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(map_match3d_generated_item_asset_for_agent)
|
||||
.collect();
|
||||
if draft
|
||||
.cover_image_src
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.unwrap_or_default()
|
||||
.is_empty()
|
||||
{
|
||||
draft.cover_image_src = resolve_match3d_default_cover_image_src(generated_item_assets);
|
||||
}
|
||||
let background_asset = find_match3d_generated_background_asset(generated_item_assets);
|
||||
apply_match3d_background_asset_to_agent_draft(draft, background_asset);
|
||||
}
|
||||
response
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_anchor_pack_response_for_turn(
|
||||
anchor: Match3DAnchorPackRecord,
|
||||
current_turn: u32,
|
||||
stage: &str,
|
||||
) -> Match3DAnchorPackResponse {
|
||||
let is_ready = matches!(
|
||||
stage,
|
||||
"ReadyToCompile"
|
||||
| "ready_to_compile"
|
||||
| "DraftCompiled"
|
||||
| "draft_compiled"
|
||||
| "draft_ready"
|
||||
| "ReadyToPublish"
|
||||
| "ready_to_publish"
|
||||
| "Published"
|
||||
| "published"
|
||||
);
|
||||
let collected_count = if is_ready { 3 } else { current_turn.min(3) };
|
||||
|
||||
Match3DAnchorPackResponse {
|
||||
theme: map_match3d_anchor_item_response_for_collected(anchor.theme, collected_count >= 1),
|
||||
clear_count: map_match3d_anchor_item_response_for_collected(
|
||||
anchor.clear_count,
|
||||
collected_count >= 2,
|
||||
),
|
||||
difficulty: map_match3d_anchor_item_response_for_collected(
|
||||
anchor.difficulty,
|
||||
collected_count >= 3,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_anchor_item_response(anchor: Match3DAnchorItemRecord) -> Match3DAnchorItemResponse {
|
||||
Match3DAnchorItemResponse {
|
||||
key: anchor.key,
|
||||
label: anchor.label,
|
||||
value: anchor.value,
|
||||
status: anchor.status,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_anchor_item_response_for_collected(
|
||||
anchor: Match3DAnchorItemRecord,
|
||||
collected: bool,
|
||||
) -> Match3DAnchorItemResponse {
|
||||
if collected {
|
||||
return map_match3d_anchor_item_response(anchor);
|
||||
}
|
||||
|
||||
Match3DAnchorItemResponse {
|
||||
key: anchor.key,
|
||||
label: anchor.label,
|
||||
value: String::new(),
|
||||
status: "missing".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_config_response(config: Match3DCreatorConfigRecord) -> Match3DCreatorConfigResponse {
|
||||
Match3DCreatorConfigResponse {
|
||||
theme_text: config.theme_text,
|
||||
reference_image_src: config.reference_image_src,
|
||||
clear_count: config.clear_count,
|
||||
difficulty: config.difficulty,
|
||||
asset_style_id: config.asset_style_id,
|
||||
asset_style_label: config.asset_style_label,
|
||||
asset_style_prompt: config.asset_style_prompt,
|
||||
generate_click_sound: config.generate_click_sound,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_draft_response(draft: Match3DResultDraftRecord) -> Match3DResultDraftResponse {
|
||||
Match3DResultDraftResponse {
|
||||
profile_id: draft.profile_id,
|
||||
game_name: draft.game_name,
|
||||
theme_text: draft.theme_text,
|
||||
summary_text: Some(draft.summary_text.clone()),
|
||||
summary: draft.summary_text,
|
||||
tags: draft.tags,
|
||||
cover_image_src: draft.cover_image_src,
|
||||
reference_image_src: draft.reference_image_src,
|
||||
clear_count: draft.clear_count,
|
||||
difficulty: draft.difficulty,
|
||||
total_item_count: draft.total_item_count,
|
||||
publish_ready: draft.publish_ready,
|
||||
blockers: draft.blockers,
|
||||
background_prompt: None,
|
||||
background_image_src: None,
|
||||
background_image_object_key: None,
|
||||
generated_background_asset: None,
|
||||
generated_item_assets: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_generated_item_asset_for_agent(
|
||||
asset: Match3DGeneratedItemAsset,
|
||||
) -> Match3DAgentGeneratedItemAssetResponse {
|
||||
Match3DAgentGeneratedItemAssetResponse {
|
||||
item_id: asset.item_id,
|
||||
item_name: asset.item_name,
|
||||
image_src: asset.image_src,
|
||||
image_object_key: asset.image_object_key,
|
||||
image_views: asset
|
||||
.image_views
|
||||
.into_iter()
|
||||
.map(map_match3d_image_view_for_agent)
|
||||
.collect(),
|
||||
model_src: asset.model_src,
|
||||
model_object_key: asset.model_object_key,
|
||||
model_file_name: asset.model_file_name,
|
||||
task_uuid: asset.task_uuid,
|
||||
subscription_key: asset.subscription_key,
|
||||
sound_prompt: asset.sound_prompt,
|
||||
background_music_title: asset.background_music_title,
|
||||
background_music_style: asset.background_music_style,
|
||||
background_music_prompt: asset.background_music_prompt,
|
||||
background_music: asset.background_music,
|
||||
click_sound: asset.click_sound,
|
||||
background_asset: asset
|
||||
.background_asset
|
||||
.map(map_match3d_background_asset_for_agent),
|
||||
status: asset.status,
|
||||
error: asset.error,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_generated_item_asset_for_work(
|
||||
asset: Match3DGeneratedItemAssetJson,
|
||||
) -> shared_contracts::match3d_works::Match3DGeneratedItemAssetResponse {
|
||||
shared_contracts::match3d_works::Match3DGeneratedItemAssetResponse {
|
||||
item_id: asset.item_id,
|
||||
item_name: asset.item_name,
|
||||
image_src: asset.image_src,
|
||||
image_object_key: asset.image_object_key,
|
||||
image_views: asset
|
||||
.image_views
|
||||
.into_iter()
|
||||
.map(map_match3d_image_view_for_work)
|
||||
.collect(),
|
||||
model_src: asset.model_src,
|
||||
model_object_key: asset.model_object_key,
|
||||
model_file_name: asset.model_file_name,
|
||||
task_uuid: asset.task_uuid,
|
||||
subscription_key: asset.subscription_key,
|
||||
sound_prompt: asset.sound_prompt,
|
||||
background_music_title: asset.background_music_title,
|
||||
background_music_style: asset.background_music_style,
|
||||
background_music_prompt: asset.background_music_prompt,
|
||||
background_music: asset.background_music,
|
||||
click_sound: asset.click_sound,
|
||||
background_asset: asset
|
||||
.background_asset
|
||||
.map(map_match3d_background_asset_for_work),
|
||||
status: asset.status,
|
||||
error: asset.error,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_image_view_for_agent(
|
||||
view: Match3DGeneratedItemImageView,
|
||||
) -> shared_contracts::match3d_agent::Match3DGeneratedItemImageViewResponse {
|
||||
shared_contracts::match3d_agent::Match3DGeneratedItemImageViewResponse {
|
||||
view_id: view.view_id,
|
||||
view_index: view.view_index,
|
||||
image_src: view.image_src,
|
||||
image_object_key: view.image_object_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_image_view_for_work(
|
||||
view: Match3DGeneratedItemImageView,
|
||||
) -> shared_contracts::match3d_works::Match3DGeneratedItemImageViewResponse {
|
||||
shared_contracts::match3d_works::Match3DGeneratedItemImageViewResponse {
|
||||
view_id: view.view_id,
|
||||
view_index: view.view_index,
|
||||
image_src: view.image_src,
|
||||
image_object_key: view.image_object_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_image_view_from_work(
|
||||
view: shared_contracts::match3d_works::Match3DGeneratedItemImageViewResponse,
|
||||
) -> Match3DGeneratedItemImageView {
|
||||
Match3DGeneratedItemImageView {
|
||||
view_id: view.view_id,
|
||||
view_index: view.view_index,
|
||||
image_src: view.image_src,
|
||||
image_object_key: view.image_object_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_background_asset_for_agent(
|
||||
asset: Match3DGeneratedBackgroundAsset,
|
||||
) -> shared_contracts::match3d_agent::Match3DGeneratedBackgroundAssetResponse {
|
||||
shared_contracts::match3d_agent::Match3DGeneratedBackgroundAssetResponse {
|
||||
prompt: asset.prompt,
|
||||
image_src: asset.image_src,
|
||||
image_object_key: asset.image_object_key,
|
||||
container_prompt: asset.container_prompt,
|
||||
container_image_src: asset.container_image_src,
|
||||
container_image_object_key: asset.container_image_object_key,
|
||||
status: asset.status,
|
||||
error: asset.error,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_background_asset_for_work(
|
||||
asset: Match3DGeneratedBackgroundAsset,
|
||||
) -> shared_contracts::match3d_works::Match3DGeneratedBackgroundAssetResponse {
|
||||
shared_contracts::match3d_works::Match3DGeneratedBackgroundAssetResponse {
|
||||
prompt: asset.prompt,
|
||||
image_src: asset.image_src,
|
||||
image_object_key: asset.image_object_key,
|
||||
container_prompt: asset.container_prompt,
|
||||
container_image_src: asset.container_image_src,
|
||||
container_image_object_key: asset.container_image_object_key,
|
||||
status: asset.status,
|
||||
error: asset.error,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn find_match3d_generated_background_asset(
|
||||
assets: &[Match3DGeneratedItemAsset],
|
||||
) -> Option<Match3DGeneratedBackgroundAsset> {
|
||||
assets
|
||||
.iter()
|
||||
.find_map(|asset| asset.background_asset.clone())
|
||||
}
|
||||
|
||||
pub(super) fn resolve_match3d_default_cover_image_src(assets: &[Match3DGeneratedItemAsset]) -> Option<String> {
|
||||
find_match3d_generated_background_asset(assets).and_then(|asset| {
|
||||
asset
|
||||
.container_image_src
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
.or_else(|| {
|
||||
asset
|
||||
.container_image_object_key
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
})
|
||||
.or_else(|| {
|
||||
asset
|
||||
.image_src
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
})
|
||||
.or_else(|| {
|
||||
asset
|
||||
.image_object_key
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn find_match3d_generated_background_asset_json(
|
||||
assets: &[Match3DGeneratedItemAssetJson],
|
||||
) -> Option<Match3DGeneratedBackgroundAsset> {
|
||||
assets
|
||||
.iter()
|
||||
.find_map(|asset| asset.background_asset.clone())
|
||||
}
|
||||
|
||||
pub(super) fn apply_match3d_background_asset_to_agent_draft(
|
||||
draft: &mut Match3DResultDraftResponse,
|
||||
background_asset: Option<Match3DGeneratedBackgroundAsset>,
|
||||
) {
|
||||
if let Some(asset) = background_asset {
|
||||
draft.background_prompt = Some(asset.prompt.clone());
|
||||
draft.background_image_src = asset.image_src.clone();
|
||||
draft.background_image_object_key = asset.image_object_key.clone();
|
||||
draft.generated_background_asset = Some(map_match3d_background_asset_for_agent(asset));
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_message_response(message: Match3DAgentMessageRecord) -> Match3DAgentMessageResponse {
|
||||
Match3DAgentMessageResponse {
|
||||
id: message.message_id,
|
||||
role: message.role,
|
||||
kind: message.kind,
|
||||
text: message.text,
|
||||
created_at: message.created_at,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_work_summary_response(item: Match3DWorkProfileRecord) -> Match3DWorkSummaryResponse {
|
||||
let generated_item_asset_json =
|
||||
parse_match3d_generated_item_assets(item.generated_item_assets_json.as_deref());
|
||||
let background_asset = find_match3d_generated_background_asset_json(&generated_item_asset_json);
|
||||
let generated_background_asset = background_asset
|
||||
.clone()
|
||||
.map(map_match3d_background_asset_for_work);
|
||||
let generated_item_assets = generated_item_asset_json
|
||||
.into_iter()
|
||||
.map(map_match3d_generated_item_asset_for_work)
|
||||
.collect();
|
||||
Match3DWorkSummaryResponse {
|
||||
work_id: item.work_id,
|
||||
profile_id: item.profile_id,
|
||||
owner_user_id: item.owner_user_id,
|
||||
source_session_id: item.source_session_id,
|
||||
game_name: item.game_name,
|
||||
theme_text: item.theme_text,
|
||||
summary: item.summary,
|
||||
tags: item.tags,
|
||||
cover_image_src: item.cover_image_src,
|
||||
reference_image_src: item.reference_image_src,
|
||||
clear_count: item.clear_count,
|
||||
difficulty: item.difficulty,
|
||||
publication_status: item.publication_status,
|
||||
play_count: item.play_count,
|
||||
updated_at: item.updated_at,
|
||||
published_at: item.published_at,
|
||||
publish_ready: item.publish_ready,
|
||||
background_prompt: background_asset.as_ref().map(|asset| asset.prompt.clone()),
|
||||
background_image_src: background_asset
|
||||
.as_ref()
|
||||
.and_then(|asset| asset.image_src.clone()),
|
||||
background_image_object_key: background_asset
|
||||
.as_ref()
|
||||
.and_then(|asset| asset.image_object_key.clone()),
|
||||
generated_background_asset,
|
||||
generated_item_assets,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn match3d_bad_gateway(message: impl Into<String>) -> AppError {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": MATCH3D_AGENT_PROVIDER,
|
||||
"message": message.into(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) fn match3d_background_music_missing_error(message: impl Into<String>) -> AppError {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": MATCH3D_AGENT_PROVIDER,
|
||||
"message": message.into(),
|
||||
"missingAssets": ["背景音乐"],
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) fn require_match3d_background_music_title(
|
||||
plan: &Match3DGeneratedBackgroundMusicPlan,
|
||||
) -> Result<String, AppError> {
|
||||
let title = normalize_match3d_audio_title(plan.title.as_str());
|
||||
if title.is_empty() {
|
||||
return Err(match3d_background_music_missing_error(
|
||||
"抓大鹅草稿背景音乐名称为空,无法完成背景音乐生成",
|
||||
));
|
||||
}
|
||||
Ok(title)
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_work_profile_response(item: Match3DWorkProfileRecord) -> Match3DWorkProfileResponse {
|
||||
Match3DWorkProfileResponse {
|
||||
summary: map_match3d_work_summary_response(item),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_run_response(run: Match3DRunRecord) -> Match3DRunSnapshotResponse {
|
||||
Match3DRunSnapshotResponse {
|
||||
run_id: run.run_id,
|
||||
profile_id: run.profile_id,
|
||||
owner_user_id: run.owner_user_id,
|
||||
status: normalize_match3d_run_status(run.status.as_str()).to_string(),
|
||||
snapshot_version: run.snapshot_version,
|
||||
started_at_ms: run.started_at_ms,
|
||||
duration_limit_ms: run.duration_limit_ms,
|
||||
server_now_ms: run.server_now_ms,
|
||||
remaining_ms: run.remaining_ms,
|
||||
clear_count: run.clear_count,
|
||||
total_item_count: run.total_item_count,
|
||||
cleared_item_count: run.cleared_item_count,
|
||||
items: run
|
||||
.items
|
||||
.into_iter()
|
||||
.map(map_match3d_item_response)
|
||||
.collect(),
|
||||
tray_slots: run
|
||||
.tray_slots
|
||||
.into_iter()
|
||||
.map(map_match3d_tray_slot_response)
|
||||
.collect(),
|
||||
failure_reason: run
|
||||
.failure_reason
|
||||
.map(|reason| normalize_match3d_failure_reason(reason.as_str()).to_string()),
|
||||
last_confirmed_action_id: run.last_confirmed_action_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_item_response(item: Match3DItemSnapshotRecord) -> Match3DItemSnapshotResponse {
|
||||
Match3DItemSnapshotResponse {
|
||||
item_instance_id: item.item_instance_id,
|
||||
item_type_id: item.item_type_id,
|
||||
visual_key: item.visual_key,
|
||||
x: item.x,
|
||||
y: item.y,
|
||||
radius: item.radius,
|
||||
layer: item.layer,
|
||||
state: normalize_match3d_item_state(item.state.as_str()).to_string(),
|
||||
clickable: item.clickable,
|
||||
tray_slot_index: item.tray_slot_index,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_tray_slot_response(slot: Match3DTraySlotRecord) -> Match3DTraySlotResponse {
|
||||
Match3DTraySlotResponse {
|
||||
slot_index: slot.slot_index,
|
||||
item_instance_id: slot.item_instance_id,
|
||||
item_type_id: slot.item_type_id,
|
||||
visual_key: slot.visual_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn map_match3d_click_confirmation_response(
|
||||
confirmation: Match3DClickConfirmationRecord,
|
||||
) -> Match3DClickConfirmationResponse {
|
||||
Match3DClickConfirmationResponse {
|
||||
accepted: confirmation.accepted,
|
||||
reject_reason: confirmation
|
||||
.reject_reason
|
||||
.map(|reason| normalize_match3d_click_reject_reason(reason.as_str()).to_string()),
|
||||
entered_slot_index: confirmation.entered_slot_index,
|
||||
cleared_item_instance_ids: confirmation.cleared_item_instance_ids,
|
||||
run: map_match3d_run_response(confirmation.run),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user