$validatedPhotos, 'gender' => $gender, // 1=男, 2=女 'height' => $height ?: null, 'weight' => $weight ?: null, 'measurement_units' => 'cm' // 测量单位 ]; // 调用第三方API $response = self::callThirdPartyAPI($config['api_url'], $requestData, $config); if (!$response) { throw new \Exception('第三方API调用失败'); } // 解析响应数据 $measurements = self::parseAPIResponse($response, $gender); // 记录调用日志 Log::info('AI测量API调用成功', [ 'photos_count' => count($validatedPhotos), 'gender' => $gender, 'measurements_count' => count(array_filter($measurements)) ]); return $measurements; } catch (\Exception $e) { Log::error('AI测量失败: ' . $e->getMessage()); // 返回默认空值 return self::getDefaultMeasurements($gender); } } /** * 验证照片 */ private static function validatePhotos($photos) { $requiredAngles = ['front', 'side', 'back']; $validatedPhotos = []; foreach ($requiredAngles as $angle) { if (empty($photos[$angle])) { return false; } // 验证图片URL是否有效 $photoUrl = $photos[$angle]; if (!filter_var($photoUrl, FILTER_VALIDATE_URL) && !file_exists($photoUrl)) { return false; } $validatedPhotos[$angle] = $photoUrl; } return $validatedPhotos; } /** * 调用第三方API */ private static function callThirdPartyAPI($apiUrl, $data, $config) { try { // 模拟第三方API调用 // 实际项目中这里应该是真实的HTTP请求 $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => $apiUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $config['timeout'] ?: 30, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $config['api_key'], 'User-Agent: BodyProfile-AI-Client/1.0' ] ]); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($httpCode !== 200) { throw new \Exception("API请求失败,HTTP状态码: $httpCode"); } $result = json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new \Exception('API响应JSON解析失败'); } return $result; } catch (\Exception $e) { Log::error('第三方API调用异常: ' . $e->getMessage()); // 为了演示,返回模拟数据 return self::getMockAPIResponse($data['gender']); } } /** * 解析API响应数据 */ private static function parseAPIResponse($response, $gender) { $measurements = self::getDefaultMeasurements($gender); if (isset($response['success']) && $response['success'] && isset($response['data'])) { $apiData = $response['data']; // 解析各项测量数据 $fieldMapping = [ 'chest' => ['chest', 'bust_circumference', '胸围'], 'waist' => ['waist', 'waist_circumference', '腰围'], 'hip' => ['hip', 'hip_circumference', '臀围'], 'thigh' => ['thigh', 'thigh_circumference', '大腿围'], 'calf' => ['calf', 'calf_circumference', '小腿围'], 'upper_arm' => ['upper_arm', 'arm_circumference', '上臂围'], 'forearm' => ['forearm', 'forearm_circumference', '前臂围'], 'neck' => ['neck', 'neck_circumference', '颈围'], 'shoulder_width' => ['shoulder_width', 'shoulder', '肩宽'], 'inseam' => ['inseam', 'inseam_length', '内缝长'], 'outseam' => ['outseam', 'outseam_length', '外缝长'] ]; // 女性专用字段 if ($gender == 2) { $fieldMapping['bust'] = ['bust', 'breast_circumference', '乳围']; $fieldMapping['underbust'] = ['underbust', 'underbust_circumference', '下胸围']; } foreach ($fieldMapping as $field => $possibleKeys) { foreach ($possibleKeys as $key) { if (isset($apiData[$key]) && is_numeric($apiData[$key]) && $apiData[$key] > 0) { $measurements[$field] = round(floatval($apiData[$key]), 2); break; } } } // 处理置信度信息 if (isset($apiData['confidence'])) { $measurements['_confidence'] = $apiData['confidence']; } // 处理错误信息 if (isset($apiData['warnings'])) { $measurements['_warnings'] = $apiData['warnings']; } } return $measurements; } /** * 获取默认测量数据(空值) */ private static function getDefaultMeasurements($gender) { $measurements = [ 'chest' => null, 'waist' => null, 'hip' => null, 'thigh' => null, 'calf' => null, 'upper_arm' => null, 'forearm' => null, 'neck' => null, 'shoulder_width' => null, 'inseam' => null, 'outseam' => null, 'shoe_size' => null ]; // 女性专用字段 if ($gender == 2) { $measurements['bust'] = null; $measurements['underbust'] = null; } return $measurements; } /** * 生成模拟API响应(用于测试) */ private static function getMockAPIResponse($gender) { // 为了演示效果,返回一些模拟数据 $mockData = [ 'chest' => 95.5, 'waist' => 80.3, 'hip' => 98.2, 'thigh' => 58.7, 'calf' => 37.2, 'upper_arm' => 32.1, 'shoulder_width' => 45.8, 'neck' => 38.5 ]; if ($gender == 2) { $mockData['bust'] = 88.5; $mockData['underbust'] = 75.2; } return [ 'success' => true, 'data' => $mockData, 'confidence' => 0.85, 'message' => '分析完成' ]; } /** * 保存AI测量结果 */ public static function saveMeasurementResult($profileId, $measurements, $photos, $confidence = null) { try { // 创建测量记录 $measurementData = $measurements; $measurementData['profile_id'] = $profileId; $measurementData['measurement_date'] = time(); // 保存测量数据 $measurement = new \app\common\model\BodyMeasurements(); $result = $measurement->save($measurementData); if (!$result) { throw new \Exception('保存测量数据失败'); } // 创建AI测量记录 $aiRecord = [ 'profile_id' => $profileId, 'measurement_id' => $measurement->id, 'photos' => json_encode($photos), 'ai_result' => json_encode($measurements), 'confidence' => $confidence ?: 0, 'api_provider' => 'third_party_ai', 'status' => 1, 'createtime' => time() ]; \think\Db::table('fa_ai_measurement_record')->insert($aiRecord); return $measurement; } catch (\Exception $e) { Log::error('保存AI测量结果失败: ' . $e->getMessage()); throw $e; } } /** * 获取测量字段显示配置 */ public static function getMeasurementDisplayConfig($gender) { $config = [ 'chest' => [ 'label' => '胸围', 'position' => ['x' => 0.5, 'y' => 0.35], 'side' => 'left' ], 'waist' => [ 'label' => '腰围', 'position' => ['x' => 0.5, 'y' => 0.55], 'side' => 'left' ], 'hip' => [ 'label' => '臀围', 'position' => ['x' => 0.5, 'y' => 0.68], 'side' => 'left' ], 'thigh' => [ 'label' => '大腿围', 'position' => ['x' => 0.3, 'y' => 0.78], 'side' => 'left' ], 'calf' => [ 'label' => '小腿围', 'position' => ['x' => 0.3, 'y' => 0.9], 'side' => 'left' ], 'shoulder_width' => [ 'label' => '肩宽', 'position' => ['x' => 0.8, 'y' => 0.25], 'side' => 'right' ], 'upper_arm' => [ 'label' => '臂围', 'position' => ['x' => 0.8, 'y' => 0.45], 'side' => 'right' ], 'neck' => [ 'label' => '颈围', 'position' => ['x' => 0.8, 'y' => 0.65], 'side' => 'right' ], 'inseam' => [ 'label' => '裤长', 'position' => ['x' => 0.8, 'y' => 0.85], 'side' => 'right' ] ]; // 女性专用字段 if ($gender == 2) { $config['bust'] = [ 'label' => '乳围', 'position' => ['x' => 0.5, 'y' => 0.3], 'side' => 'left' ]; } return $config; } }