123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- <?php
- namespace app\common\service;
- use think\Config;
- use think\Log;
- /**
- * AI测量服务类
- */
- class AiMeasurementService
- {
- /**
- * 调用第三方AI接口分析身体照片
- */
- public static function analyzBodyPhotos($photos, $gender, $height = null, $weight = null)
- {
- try {
- // 获取第三方API配置
- $config = Config::get('ai_measurement');
-
- // 验证照片
- $validatedPhotos = self::validatePhotos($photos);
- if (!$validatedPhotos) {
- throw new \Exception('照片验证失败,请确保上传正面、侧面、背面三张照片');
- }
- // 准备请求数据
- $requestData = [
- 'photos' => $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;
- }
- }
|