fix: 修复火山云 API 响应格式解析问题

问题修复:
-  修复响应结构不匹配导致的解析失败
-  更新数据结构以匹配火山云 API 的实际响应格式
-  添加调试日志以便排查 API 响应问题

技术改进:
- 重构响应数据结构,支持火山云嵌套格式 (ResponseMetadata + Result)
- 更新前端类型定义和数据访问路径
- 添加详细的响应解析错误信息
- 保持向后兼容性

响应格式变更:
- 原格式: { code, message, data, ... }
- 新格式: { ResponseMetadata: {...}, Result: { code, message, data, ... } }

现在 OmniHuman 主体识别功能应该可以正确解析火山云 API 响应了!
This commit is contained in:
imeepos 2025-08-05 18:36:46 +08:00
parent 4ce50b15d7
commit d58499b564
3 changed files with 60 additions and 15 deletions

View File

@ -92,9 +92,33 @@ pub struct RealmanAvatarPictureCreateRoleOmniRequest {
pub image_url: String, pub image_url: String,
} }
/// OmniHuman 主体识别响应 /// 火山云 API 响应元数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VolcanoResponseMetadata {
#[serde(rename = "Action")]
pub action: String,
#[serde(rename = "Region")]
pub region: String,
#[serde(rename = "RequestId")]
pub request_id: String,
#[serde(rename = "Service")]
pub service: String,
#[serde(rename = "Version")]
pub version: String,
}
/// OmniHuman 主体识别响应 (火山云格式)
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RealmanAvatarPictureCreateRoleOmniResponse { pub struct RealmanAvatarPictureCreateRoleOmniResponse {
#[serde(rename = "ResponseMetadata")]
pub response_metadata: VolcanoResponseMetadata,
#[serde(rename = "Result")]
pub result: RealmanAvatarPictureCreateRoleOmniResult,
}
/// OmniHuman 主体识别结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RealmanAvatarPictureCreateRoleOmniResult {
pub code: i32, pub code: i32,
pub message: String, pub message: String,
pub data: Option<RealmanAvatarPictureCreateRoleOmniData>, pub data: Option<RealmanAvatarPictureCreateRoleOmniData>,
@ -561,10 +585,16 @@ impl VolcanoVideoService {
return Err(anyhow!("OmniHuman 主体识别 API 调用失败: {} - {}", status, error_text)); return Err(anyhow!("OmniHuman 主体识别 API 调用失败: {} - {}", status, error_text));
} }
let api_response: RealmanAvatarPictureCreateRoleOmniResponse = response.json().await?; // 先获取响应文本进行调试
let response_text = response.text().await?;
info!("OmniHuman API 原始响应: {}", response_text);
if api_response.code != 10000 { // 尝试解析响应
return Err(anyhow!("OmniHuman 主体识别 API 返回错误: {} - {}", api_response.code, api_response.message)); let api_response: RealmanAvatarPictureCreateRoleOmniResponse = serde_json::from_str(&response_text)
.map_err(|e| anyhow!("解析响应失败: {} - 响应内容: {}", e, response_text))?;
if api_response.result.code != 10000 {
return Err(anyhow!("OmniHuman 主体识别 API 返回错误: {} - {}", api_response.result.code, api_response.result.message));
} }
info!("OmniHuman 主体识别任务提交成功: {:?}", api_response); info!("OmniHuman 主体识别任务提交成功: {:?}", api_response);

View File

@ -90,11 +90,11 @@ const OmniHumanDetectionTool: React.FC = () => {
setUploadProgress(100); setUploadProgress(100);
setResult(response); setResult(response);
if (response.code === 10000) { if (response.Result.code === 10000) {
success('主体识别任务提交成功'); success('主体识别任务提交成功');
} else { } else {
setErrorMessage(`识别失败: ${response.message}`); setErrorMessage(`识别失败: ${response.Result.message}`);
error(`识别失败: ${response.message}`); error(`识别失败: ${response.Result.message}`);
} }
} catch (err) { } catch (err) {
const errMsg = err instanceof Error ? err.message : '未知错误'; const errMsg = err instanceof Error ? err.message : '未知错误';
@ -221,17 +221,17 @@ const OmniHumanDetectionTool: React.FC = () => {
<span className="text-green-800 font-medium"></span> <span className="text-green-800 font-medium"></span>
</div> </div>
<div className="text-sm text-green-700"> <div className="text-sm text-green-700">
<p>ID: {result.data?.task_id}</p> <p>ID: {result.Result.data?.task_id}</p>
<p>ID: {result.request_id}</p> <p>ID: {result.Result.request_id}</p>
<p>: {result.time_elapsed}</p> <p>: {result.Result.time_elapsed}</p>
</div> </div>
</div> </div>
{result.data?.image_urls && result.data.image_urls.length > 0 && ( {result.Result.data?.image_urls && result.Result.data.image_urls.length > 0 && (
<div> <div>
<h4 className="font-medium text-gray-900 mb-2"></h4> <h4 className="font-medium text-gray-900 mb-2"></h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{result.data.image_urls.map((url, index) => ( {result.Result.data.image_urls.map((url, index) => (
<div key={index} className="border border-gray-200 rounded-lg p-4 bg-gray-50"> <div key={index} className="border border-gray-200 rounded-lg p-4 bg-gray-50">
<img <img
src={url} src={url}
@ -254,11 +254,11 @@ const OmniHumanDetectionTool: React.FC = () => {
</div> </div>
)} )}
{result.data?.resp_data && ( {result.Result.data?.resp_data && (
<div> <div>
<h4 className="font-medium text-gray-900 mb-2"></h4> <h4 className="font-medium text-gray-900 mb-2"></h4>
<div className="p-3 bg-gray-50 border border-gray-200 rounded text-sm font-mono"> <div className="p-3 bg-gray-50 border border-gray-200 rounded text-sm font-mono">
{result.data.resp_data} {result.Result.data.resp_data}
</div> </div>
</div> </div>
)} )}

View File

@ -412,7 +412,15 @@ export interface RealmanAvatarPictureCreateRoleOmniData {
algorithm_base_resp?: any; algorithm_base_resp?: any;
} }
export interface RealmanAvatarPictureCreateRoleOmniResponse { export interface VolcanoResponseMetadata {
Action: string;
Region: string;
RequestId: string;
Service: string;
Version: string;
}
export interface RealmanAvatarPictureCreateRoleOmniResult {
/** 业务侧错误码 */ /** 业务侧错误码 */
code: number; code: number;
/** 描述信息 */ /** 描述信息 */
@ -426,3 +434,10 @@ export interface RealmanAvatarPictureCreateRoleOmniResponse {
/** 内部服务耗时 */ /** 内部服务耗时 */
time_elapsed: string; time_elapsed: string;
} }
export interface RealmanAvatarPictureCreateRoleOmniResponse {
/** 火山云响应元数据 */
ResponseMetadata: VolcanoResponseMetadata;
/** 实际结果数据 */
Result: RealmanAvatarPictureCreateRoleOmniResult;
}