Files
Genarrative/server-rs/crates/api-server/src/custom_world_ai/assets.rs

123 lines
4.3 KiB
Rust

use super::opening_cg::{
map_asset_binding_prepare_error, map_asset_object_prepare_error,
map_custom_world_asset_oss_error, map_custom_world_asset_spacetime_error,
map_custom_world_generated_image_asset_error,
};
use super::*;
pub(super) async fn persist_custom_world_asset(
state: &AppState,
owner_user_id: &str,
upload: PreparedAssetUpload,
mut response: GeneratedAssetResponse,
) -> Result<GeneratedAssetResponse, AppError> {
let oss_client = state.oss_client().ok_or_else(|| {
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_details(json!({
"provider": "aliyun-oss",
"reason": "OSS 未完成环境变量配置",
}))
})?;
let http_client = reqwest::Client::new();
let PreparedAssetUpload {
prefix,
path_segments,
file_name,
content_type,
body,
asset_kind,
entity_kind,
entity_id,
profile_id,
slot,
source_job_id,
} = upload;
let file_stem = file_name
.rsplit_once('.')
.map(|(stem, _)| stem)
.unwrap_or(file_name.as_str())
.to_string();
let prepared =
GeneratedImageAssetAdapter::prepare_put_object(GeneratedImageAssetPersistInput {
prefix,
path_segments,
file_stem,
image: GeneratedImageAssetDataUrl {
format: normalize_generated_image_asset_mime(content_type.as_str()),
bytes: body,
},
access: OssObjectAccess::Private,
metadata: GeneratedImageAssetAdapterMetadata {
asset_kind: Some(asset_kind.to_string()),
owner_user_id: Some(owner_user_id.to_string()),
entity_kind: Some(entity_kind.to_string()),
entity_id: Some(entity_id.clone()),
slot: Some(slot.to_string()),
provider: None,
task_id: source_job_id.clone(),
},
extra_metadata: profile_id
.as_ref()
.map(|profile_id| BTreeMap::from([("profile_id".to_string(), profile_id.clone())]))
.unwrap_or_default(),
})
.map_err(map_custom_world_generated_image_asset_error)?;
let persisted_mime_type = prepared.format.mime_type.clone();
let put_result = oss_client
.put_object(&http_client, prepared.request)
.await
.map_err(map_custom_world_asset_oss_error)?;
// custom world 图片链正式改为 OSS 真值确认,不再把 put_object 返回值直接当成唯一对象真相。
let head = oss_client
.head_object(
&http_client,
OssHeadObjectRequest {
object_key: put_result.object_key.clone(),
},
)
.await
.map_err(map_custom_world_asset_oss_error)?;
let now_micros = current_utc_micros();
let asset_object = state
.spacetime_client()
.confirm_asset_object(
build_asset_object_upsert_input(
generate_asset_object_id(now_micros),
head.bucket,
head.object_key,
AssetObjectAccessPolicy::Private,
head.content_type.or(Some(persisted_mime_type)),
head.content_length,
head.etag,
asset_kind.to_string(),
source_job_id,
Some(owner_user_id.to_string()),
profile_id.clone(),
Some(entity_id.clone()),
now_micros,
)
.map_err(map_asset_object_prepare_error)?,
)
.await
.map_err(map_custom_world_asset_spacetime_error)?;
state
.spacetime_client()
.bind_asset_object_to_entity(
build_asset_entity_binding_input(
generate_asset_binding_id(now_micros),
asset_object.asset_object_id,
entity_kind.to_string(),
entity_id.clone(),
slot.to_string(),
asset_kind.to_string(),
Some(owner_user_id.to_string()),
profile_id,
now_micros,
)
.map_err(map_asset_binding_prepare_error)?,
)
.await
.map_err(map_custom_world_asset_spacetime_error)?;
response.image_src = put_result.legacy_public_path;
Ok(response)
}