1
This commit is contained in:
@@ -37,8 +37,9 @@ use shared_contracts::assets::{
|
||||
CharacterAnimationImportVideoResponse, CharacterAnimationPublishRequest,
|
||||
CharacterAnimationPublishResponse, CharacterAnimationStrategy,
|
||||
CharacterAnimationTemplatePayload, CharacterAnimationTemplatesResponse,
|
||||
CharacterAssetJobStatusPayload, CharacterAssetJobStatusText, CharacterVisualDraftPayload,
|
||||
CharacterWorkflowCacheGetResponse, CharacterWorkflowCachePayload,
|
||||
CharacterAssetJobStatusPayload, CharacterAssetJobStatusText,
|
||||
CharacterRoleAssetWorkflowResolveRequest, CharacterRoleAssetWorkflowResponse,
|
||||
CharacterVisualDraftPayload, CharacterWorkflowCacheGetResponse, CharacterWorkflowCachePayload,
|
||||
CharacterWorkflowCacheSaveRequest, CharacterWorkflowCacheSaveResponse,
|
||||
};
|
||||
use spacetime_client::SpacetimeClientError;
|
||||
@@ -49,6 +50,9 @@ use crate::{
|
||||
build_character_animation_prompt, build_fallback_moderation_safe_animation_prompt,
|
||||
},
|
||||
http_error::AppError,
|
||||
prompt::role_asset_studio::{
|
||||
build_role_asset_workflow, normalize_animation_prompt_text_by_key,
|
||||
},
|
||||
request_context::RequestContext,
|
||||
state::AppState,
|
||||
};
|
||||
@@ -646,6 +650,92 @@ pub async fn save_character_workflow_cache(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn resolve_role_asset_workflow(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
AxumPath(character_id): AxumPath<String>,
|
||||
payload: Result<Json<CharacterRoleAssetWorkflowResolveRequest>, JsonRejection>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let character_id = normalize_required_text(character_id.as_str(), "");
|
||||
if character_id.is_empty() {
|
||||
return Err(character_animation_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "role-asset-workflow",
|
||||
"message": "characterId is required.",
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
let Json(payload) = payload.map_err(|error| {
|
||||
character_animation_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "role-asset-workflow",
|
||||
"message": error.body_text(),
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
|
||||
let cache_scope_id = trim_optional_text(payload.cache_scope_id.as_deref());
|
||||
let cache = load_workflow_cache(&state, character_id.as_str(), cache_scope_id.as_deref())
|
||||
.await
|
||||
.map_err(|error| character_animation_error_response(&request_context, error))?;
|
||||
let workflow = build_role_asset_workflow(payload.role, cache.as_ref());
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
CharacterRoleAssetWorkflowResponse {
|
||||
ok: true,
|
||||
cache,
|
||||
workflow,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn put_role_asset_workflow(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
AxumPath(character_id): AxumPath<String>,
|
||||
payload: Result<Json<CharacterWorkflowCacheSaveRequest>, JsonRejection>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
let character_id = normalize_required_text(character_id.as_str(), "");
|
||||
if character_id.is_empty() {
|
||||
return Err(character_animation_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "role-asset-workflow",
|
||||
"message": "characterId is required.",
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
let Json(mut payload) = payload.map_err(|error| {
|
||||
character_animation_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "role-asset-workflow",
|
||||
"message": error.body_text(),
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
payload.character_id = character_id;
|
||||
|
||||
let cache = normalize_workflow_cache_payload(payload, current_utc_iso_text());
|
||||
save_workflow_cache(&state, cache.clone())
|
||||
.await
|
||||
.map_err(|error| character_animation_error_response(&request_context, error))?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
CharacterWorkflowCacheSaveResponse {
|
||||
ok: true,
|
||||
cache,
|
||||
save_message: "角色资产工坊缓存已更新到 OSS。".to_string(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn create_animation_task(
|
||||
state: &AppState,
|
||||
task_id: &str,
|
||||
@@ -1634,6 +1724,9 @@ fn normalize_workflow_cache_payload(
|
||||
cache_scope_id,
|
||||
visual_prompt_text: clamp_prompt_seed_text(payload.visual_prompt_text.as_deref()),
|
||||
animation_prompt_text: clamp_prompt_seed_text(payload.animation_prompt_text.as_deref()),
|
||||
animation_prompt_text_by_key: normalize_animation_prompt_text_by_key(
|
||||
payload.animation_prompt_text_by_key,
|
||||
),
|
||||
visual_drafts: normalize_visual_drafts(character_id.as_str(), payload.visual_drafts),
|
||||
selected_visual_draft_id: trim_optional_text(payload.selected_visual_draft_id.as_deref())
|
||||
.unwrap_or_default(),
|
||||
@@ -3354,6 +3447,10 @@ mod tests {
|
||||
cache_scope_id: None,
|
||||
visual_prompt_text: Some("主形象".to_string()),
|
||||
animation_prompt_text: Some("待机".to_string()),
|
||||
animation_prompt_text_by_key: BTreeMap::from([(
|
||||
"run".to_string(),
|
||||
"奔跑".to_string(),
|
||||
)]),
|
||||
visual_drafts: vec![CharacterVisualDraftPayload {
|
||||
id: "".to_string(),
|
||||
label: "".to_string(),
|
||||
@@ -3373,6 +3470,7 @@ mod tests {
|
||||
|
||||
assert_eq!(cache.character_id, "hero");
|
||||
assert_eq!(cache.selected_animation, "idle");
|
||||
assert_eq!(cache.animation_prompt_text_by_key["run"], "奔跑");
|
||||
assert_eq!(cache.visual_drafts[0].id, "hero-draft-1");
|
||||
assert_eq!(cache.visual_drafts[0].width, 1024);
|
||||
assert_eq!(cache.image_src, None);
|
||||
|
||||
Reference in New Issue
Block a user