refactor: extract platform media crates
This commit is contained in:
@@ -20,12 +20,14 @@ use shared_contracts::jump_hop::{
|
||||
};
|
||||
use shared_kernel::{build_prefixed_uuid_id, format_timestamp_micros};
|
||||
use spacetime_client::SpacetimeClientError;
|
||||
use std::{collections::BTreeMap, time::{SystemTime, UNIX_EPOCH}};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body,
|
||||
auth::{AuthenticatedAccessToken, RuntimePrincipal},
|
||||
http_error::AppError,
|
||||
generated_asset_sheets::{
|
||||
GeneratedAssetSheetPromptInput, build_generated_asset_sheet_prompt,
|
||||
slice_generated_asset_sheet,
|
||||
@@ -35,16 +37,18 @@ use crate::{
|
||||
adapter::{GeneratedImageAssetAdapterMetadata, GeneratedImageAssetPersistInput},
|
||||
normalize_generated_image_asset_mime,
|
||||
},
|
||||
http_error::AppError,
|
||||
openai_image_generation::{
|
||||
build_openai_image_http_client, create_openai_image_generation,
|
||||
require_openai_image_settings,
|
||||
},
|
||||
request_context::RequestContext,
|
||||
state::AppState,
|
||||
work_play_tracking::{record_work_play_start_after_success, WorkPlayTrackingDraft},
|
||||
work_play_tracking::{WorkPlayTrackingDraft, record_work_play_start_after_success},
|
||||
};
|
||||
|
||||
const JUMP_HOP_TILE_ITEM_NAMES: [&str; 6] = ["start", "normal", "target", "finish", "bonus", "accent"];
|
||||
const JUMP_HOP_TILE_ITEM_NAMES: [&str; 6] =
|
||||
["start", "normal", "target", "finish", "bonus", "accent"];
|
||||
|
||||
const JUMP_HOP_PROVIDER: &str = "jump-hop";
|
||||
const JUMP_HOP_CREATION_PROVIDER: &str = "jump-hop-creation";
|
||||
@@ -384,7 +388,10 @@ async fn maybe_generate_jump_hop_assets(
|
||||
}
|
||||
if payload.character_asset.is_some()
|
||||
&& payload.tile_atlas_asset.is_some()
|
||||
&& payload.tile_assets.as_ref().is_some_and(|assets| !assets.is_empty())
|
||||
&& payload
|
||||
.tile_assets
|
||||
.as_ref()
|
||||
.is_some_and(|assets| !assets.is_empty())
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
@@ -397,19 +404,18 @@ async fn maybe_generate_jump_hop_assets(
|
||||
.unwrap_or_else(|| build_prefixed_uuid_id("jump-hop-profile-"));
|
||||
payload.profile_id = Some(profile_id.clone());
|
||||
|
||||
let settings = require_openai_image_settings(state)
|
||||
.map_err(|error| jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error))?;
|
||||
let http_client = build_openai_image_http_client(&settings)
|
||||
.map_err(|error| jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error))?;
|
||||
let settings = require_openai_image_settings(state).map_err(|error| {
|
||||
jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error)
|
||||
})?;
|
||||
let http_client = build_openai_image_http_client(&settings).map_err(|error| {
|
||||
jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error)
|
||||
})?;
|
||||
|
||||
let character_prompt = payload
|
||||
.character_prompt
|
||||
.as_deref()
|
||||
.unwrap_or("俯视角可爱主角,透明背景");
|
||||
let tile_prompt = payload
|
||||
.tile_prompt
|
||||
.as_deref()
|
||||
.unwrap_or("等距立体地块图集");
|
||||
let tile_prompt = payload.tile_prompt.as_deref().unwrap_or("等距立体地块图集");
|
||||
|
||||
let character_generated = create_openai_image_generation(
|
||||
&http_client,
|
||||
@@ -423,16 +429,20 @@ async fn maybe_generate_jump_hop_assets(
|
||||
)
|
||||
.await
|
||||
.map_err(|error| jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error))?;
|
||||
let character_image = character_generated.images.into_iter().next().ok_or_else(|| {
|
||||
jump_hop_error_response(
|
||||
request_context,
|
||||
JUMP_HOP_CREATION_PROVIDER,
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "vector-engine",
|
||||
"message": "跳一跳角色资产生成成功但未返回图片。",
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
let character_image = character_generated
|
||||
.images
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| {
|
||||
jump_hop_error_response(
|
||||
request_context,
|
||||
JUMP_HOP_CREATION_PROVIDER,
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "vector-engine",
|
||||
"message": "跳一跳角色资产生成成功但未返回图片。",
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
let character_asset = persist_jump_hop_generated_image_asset(
|
||||
state,
|
||||
owner_user_id,
|
||||
@@ -449,7 +459,14 @@ async fn maybe_generate_jump_hop_assets(
|
||||
|
||||
let sheet_prompt = build_generated_asset_sheet_prompt(&GeneratedAssetSheetPromptInput {
|
||||
subject_text: tile_prompt,
|
||||
item_names: &vec!["start".to_string(), "normal".to_string(), "target".to_string(), "finish".to_string(), "bonus".to_string(), "accent".to_string()],
|
||||
item_names: &vec![
|
||||
"start".to_string(),
|
||||
"normal".to_string(),
|
||||
"target".to_string(),
|
||||
"finish".to_string(),
|
||||
"bonus".to_string(),
|
||||
"accent".to_string(),
|
||||
],
|
||||
grid_size: 3,
|
||||
item_name_prompt_template: Some("第{row_index}行:{item_name} 的 {view_count} 个不同视图"),
|
||||
special_prompt: Some("每个格子对应一个 tile 类型,供跳一跳地块裁切使用。"),
|
||||
@@ -479,7 +496,14 @@ async fn maybe_generate_jump_hop_assets(
|
||||
})?;
|
||||
let tile_slices = slice_generated_asset_sheet(
|
||||
&tile_image,
|
||||
&vec!["start".to_string(), "normal".to_string(), "target".to_string(), "finish".to_string(), "bonus".to_string(), "accent".to_string()],
|
||||
&vec![
|
||||
"start".to_string(),
|
||||
"normal".to_string(),
|
||||
"target".to_string(),
|
||||
"finish".to_string(),
|
||||
"bonus".to_string(),
|
||||
"accent".to_string(),
|
||||
],
|
||||
3,
|
||||
)
|
||||
.map_err(|error| jump_hop_error_response(request_context, JUMP_HOP_CREATION_PROVIDER, error))?;
|
||||
@@ -521,10 +545,11 @@ async fn maybe_generate_jump_hop_assets(
|
||||
payload.character_asset = Some(character_asset);
|
||||
payload.tile_atlas_asset = Some(tile_atlas_asset);
|
||||
payload.tile_assets = Some(tile_assets);
|
||||
payload.cover_composite = payload
|
||||
.cover_composite
|
||||
.clone()
|
||||
.or_else(|| Some(format!("/generated-jump-hop-assets/{profile_id}/cover-composite.png")));
|
||||
payload.cover_composite = payload.cover_composite.clone().or_else(|| {
|
||||
Some(format!(
|
||||
"/generated-jump-hop-assets/{profile_id}/cover-composite.png"
|
||||
))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -541,36 +566,37 @@ async fn persist_jump_hop_generated_image_asset(
|
||||
request_context: &RequestContext,
|
||||
) -> Result<JumpHopCharacterAsset, Response> {
|
||||
let image_format = normalize_generated_image_asset_mime(image.mime_type.as_str());
|
||||
let prepared = GeneratedImageAssetAdapter::prepare_put_object(GeneratedImageAssetPersistInput {
|
||||
prefix,
|
||||
path_segments: vec![profile_id.to_string(), slot.to_string()],
|
||||
file_stem: "image".to_string(),
|
||||
image: GeneratedImageAssetDataUrl {
|
||||
format: image_format,
|
||||
bytes: image.bytes,
|
||||
},
|
||||
access: OssObjectAccess::Private,
|
||||
metadata: GeneratedImageAssetAdapterMetadata {
|
||||
asset_kind: Some(format!("jump-hop-{slot}")),
|
||||
owner_user_id: Some(owner_user_id.to_string()),
|
||||
entity_kind: Some("jump_hop_work".to_string()),
|
||||
entity_id: Some(profile_id.to_string()),
|
||||
slot: Some(slot.to_string()),
|
||||
provider: Some("vector-engine".to_string()),
|
||||
task_id: None,
|
||||
},
|
||||
extra_metadata: BTreeMap::new(),
|
||||
})
|
||||
.map_err(|error| {
|
||||
jump_hop_error_response(
|
||||
request_context,
|
||||
JUMP_HOP_CREATION_PROVIDER,
|
||||
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({
|
||||
"provider": "generated-image-assets",
|
||||
"message": format!("准备跳一跳图片资产上传请求失败:{error:?}"),
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
let prepared =
|
||||
GeneratedImageAssetAdapter::prepare_put_object(GeneratedImageAssetPersistInput {
|
||||
prefix,
|
||||
path_segments: vec![profile_id.to_string(), slot.to_string()],
|
||||
file_stem: "image".to_string(),
|
||||
image: GeneratedImageAssetDataUrl {
|
||||
format: image_format,
|
||||
bytes: image.bytes,
|
||||
},
|
||||
access: OssObjectAccess::Private,
|
||||
metadata: GeneratedImageAssetAdapterMetadata {
|
||||
asset_kind: Some(format!("jump-hop-{slot}")),
|
||||
owner_user_id: Some(owner_user_id.to_string()),
|
||||
entity_kind: Some("jump_hop_work".to_string()),
|
||||
entity_id: Some(profile_id.to_string()),
|
||||
slot: Some(slot.to_string()),
|
||||
provider: Some("vector-engine".to_string()),
|
||||
task_id: None,
|
||||
},
|
||||
extra_metadata: BTreeMap::new(),
|
||||
})
|
||||
.map_err(|error| {
|
||||
jump_hop_error_response(
|
||||
request_context,
|
||||
JUMP_HOP_CREATION_PROVIDER,
|
||||
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({
|
||||
"provider": "generated-image-assets",
|
||||
"message": format!("准备跳一跳图片资产上传请求失败:{error:?}"),
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
let persisted_mime_type = prepared.format.mime_type.clone();
|
||||
let oss_client = state.oss_client().ok_or_else(|| {
|
||||
jump_hop_error_response(
|
||||
@@ -709,7 +735,6 @@ fn build_jump_hop_work_play_tracking_draft(
|
||||
WorkPlayTrackingDraft::runtime_principal("jump-hop", work_id, principal, source_route)
|
||||
}
|
||||
|
||||
|
||||
fn build_jump_hop_draft(payload: &JumpHopWorkspaceCreateRequest) -> JumpHopDraftResponse {
|
||||
JumpHopDraftResponse {
|
||||
template_id: JUMP_HOP_TEMPLATE_ID.to_string(),
|
||||
|
||||
Reference in New Issue
Block a user