123 lines
4.3 KiB
Rust
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)
|
|
}
|