From a20da52fc7f9410a22c3e2eab4ad1ac09138f89f Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Tue, 2 Sep 2025 18:09:19 +0300 Subject: [PATCH] =?utf8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5?= =?utf8?q?=D0=BC=20=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=84=D0=BE=D1=82?= =?utf8?q?=D0=BE=20=D0=B2=20=D1=84=D0=B8=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/controllers/MatrixErpController.php | 72 +++++++++++++++ erp24/services/FileService.php | 108 ++++++++++++++++++++++ erp24/services/MarketplaceService.php | 33 ++++++- 3 files changed, 209 insertions(+), 4 deletions(-) diff --git a/erp24/controllers/MatrixErpController.php b/erp24/controllers/MatrixErpController.php index c0742714..26fa85be 100644 --- a/erp24/controllers/MatrixErpController.php +++ b/erp24/controllers/MatrixErpController.php @@ -14,6 +14,7 @@ use Yii; use yii\base\DynamicModel; use yii\helpers\Json; use yii\web\UploadedFile; +use yii_app\records\Files; use yii_app\records\Images; use yii_app\records\MatrixErp; use yii_app\records\MatrixErpMedia; @@ -354,6 +355,7 @@ class MatrixErpController extends Controller $res['status'] = 'error'; $res['errors'][] = "Не удалось сохранить свойства для GUID {$matrix->guid}."; } + } } catch (\Throwable $e) { $res['status'] = 'error'; @@ -446,6 +448,50 @@ class MatrixErpController extends Controller return false; } + $seen = []; + $adminId = Yii::$app->user->id ?? null; + + if (!empty($matrixProduct['image_urls']) && is_array($matrixProduct['image_urls'])) { + $imageUrls = $matrixProduct['image_urls']; + foreach ($imageUrls as $imgUrl) { + $imgUrl = trim((string)$imgUrl); + if ($imgUrl === '' || isset($seen[$imgUrl])) { + continue; + } + $seen[$imgUrl] = true; + + try { + $info = FileService::saveFromUrlToUploads($imgUrl, $adminId); + if (!empty($info) && $info['fileType'] === 'image') { + $this->saveMatrixMediaForMatrixProperty($info, 'foto', $matrixProductProperty, $adminId); + } elseif (!empty($info)) { + // На случай, если по ссылке оказался не image — можно пропустить или тоже сохранять как 'foto' + Yii::warning("Ожидали image, но получили {$info['fileType']} для {$imgUrl}"); + } + } catch (\Throwable $e) { + Yii::error("Ошибка сохранения изображения {$imgUrl}: " . $e->getMessage()); + } + } + } + + + if (!empty($matrixProduct['video_url'])) { + $videoUrl = trim((string)$matrixProduct['video_url']); + if ($videoUrl !== '' && !isset($seen[$videoUrl])) { + $seen[$videoUrl] = true; + + try { + $info = FileService::saveFromUrlToUploads($videoUrl, $adminId); + if (!empty($info)) { + $name = ($info['fileType'] === 'video') ? 'video' : 'video'; + $this->saveMatrixMediaForMatrixProperty($info, $name, $matrixProductProperty, $adminId); + } + } catch (\Throwable $e) { + Yii::error("Ошибка сохранения видео {$videoUrl}: " . $e->getMessage()); + } + } + } + $matrixProductProperty->display_name = $matrixProduct['name']; $matrixProductProperty->description = $matrixProduct['description']; @@ -465,4 +511,30 @@ class MatrixErpController extends Controller return $matrixProductProperty->save(); } + + private function saveMatrixMediaForMatrixProperty(array $info, string $name, $matrixProp, $adminId) { + // Files + $file = new Files(); + $file->created_at = date("Y-m-d H:i:s"); + $file->entity_id = $matrixProp->id; + $file->entity = "matrix_media"; + $file->file_type = $info['fileType']; // 'image' или 'video' + $file->url = $info['target_base_file']; + if(!$file->save()) { + Yii::error("Ошибка сохранения в файлы " . json_encode($file->getErrors(), JSON_UNESCAPED_UNICODE)); + } + + // MatrixErpMedia + $mm = new MatrixErpMedia(); + $mm->guid = $matrixProp->guid; + $mm->created_admin_id = $adminId; + $mm->date = date("Y-m-d H:i:s"); + $mm->created_at = date("Y-m-d H:i:s"); + $mm->file_id = $file->id; + $mm->name = $name; // 'foto' или 'video' + + if(!$mm->save()) { + Yii::error("Ошибка сохранения в медиа " . json_encode($mm->getErrors(), JSON_UNESCAPED_UNICODE)); + } + } } \ No newline at end of file diff --git a/erp24/services/FileService.php b/erp24/services/FileService.php index e0dca70c..929384de 100755 --- a/erp24/services/FileService.php +++ b/erp24/services/FileService.php @@ -3,6 +3,7 @@ namespace yii_app\services; use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use Yii; use yii\helpers\ArrayHelper; use yii\helpers\FileHelper; @@ -351,4 +352,111 @@ class FileService return $uploaded; } + public static function saveFromUrlToUploads(string $url, int $adminId): ?array + { + if (!filter_var($url, FILTER_VALIDATE_URL)) { + Yii::error("Недопустимый URL: {$url}"); + return null; + } + + $client = new Client([ + 'headers' => [ + 'User-Agent' => 'Mozilla/5.0 (compatible; ERP24 Downloader)', + 'Accept' => '*/*', + ], + 'allow_redirects' => ['max' => 5], + 'connect_timeout' => 10, + 'timeout' => 30, + 'http_errors' => false, + 'verify' => true, // лучше включить SSL + ]); + + $tmp = tempnam(sys_get_temp_dir(), 'dl_'); + if ($tmp === false) { + Yii::error("Не удалось создать временный файл для {$url}"); + return null; + } + + try { + $response = $client->request('GET', $url, ['sink' => $tmp]); + } catch (GuzzleException $e) { + @unlink($tmp); + Yii::error("Ошибка сети при загрузке {$url}: " . $e->getMessage()); + return null; + } + + $status = $response->getStatusCode(); + if ($status >= 400) { + @unlink($tmp); + Yii::error("Ошибка загрузки {$url}: HTTP {$status}"); + return null; + } + + // Заголовки + $contentType = trim(explode(';', $response->getHeaderLine('Content-Type'))[0] ?? ''); + + $mimeMap = [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/webp' => 'webp', + 'image/gif' => 'gif', + 'video/mp4' => 'mp4', + 'video/webm' => 'webm', + 'video/quicktime' => 'mov', + 'video/x-msvideo' => 'avi', + 'video/mpeg' => 'mpeg', + ]; + + $pathExt = strtolower(pathinfo(parse_url($url, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION)); + $ext = $mimeMap[$contentType] ?? ($pathExt ?: 'bin'); + + $fileType = str_starts_with($contentType, 'image/') ? 'image' + : (str_starts_with($contentType, 'video/') ? 'video' : $ext); + + $basename = basename(parse_url($url, PHP_URL_PATH) ?? '') ?: 'file'; + $basename = preg_replace('/[^\pL\pN._-]+/u', '_', $basename) ?: 'file'; + + // Папки + $uploadDir = \Yii::getAlias('@uploads'); + $targetDir = $uploadDir . "/{$adminId}/" . date("Y/m/d") . "/"; + $targetBaseDir = "/uploads/{$adminId}/" . date("Y/m/d") . "/"; + + if (!is_dir($targetDir) && !mkdir($targetDir, 0777, true) && !is_dir($targetDir)) { + @unlink($tmp); + Yii::error("Не удалось создать директорию {$targetDir}"); + return null; + } + + if ($pathExt && str_ends_with(strtolower($basename), '.' . $pathExt)) { + $basename = substr($basename, 0, -(strlen($pathExt) + 1)); + } + + if (strlen($basename) > 100) { + $basename = substr($basename, 0, 100); + } + + $filename = date("His") . '_' . substr(md5($url . microtime(true)), 0, 8) . '_' . $basename . '.' . $ext; + $targetFile = $targetDir . $filename; + $targetBaseFile = $targetBaseDir . $filename; + + if (!@rename($tmp, $targetFile)) { + if (!@copy($tmp, $targetFile)) { + @unlink($tmp); + Yii::error("Не удалось сохранить файл {$url} в {$targetFile}"); + return null; + } + @unlink($tmp); + } + + return [ + 'fileType' => $fileType, + 'target_file' => $targetFile, + 'target_base_dir' => $targetBaseDir, + 'target_base_file' => $targetBaseFile, + 'mime' => $contentType, + 'ext' => $ext, + 'source_url' => $url, + ]; + } + } diff --git a/erp24/services/MarketplaceService.php b/erp24/services/MarketplaceService.php index 934f8268..5191fefb 100644 --- a/erp24/services/MarketplaceService.php +++ b/erp24/services/MarketplaceService.php @@ -476,7 +476,7 @@ class MarketplaceService $result[] = [ 'id' => $product->id, 'name' => $properties['displayName'], - 'pictures' => [$properties['imageUrl']], + 'pictures' => $properties['imageUrls'], 'price' => $price, 'oldprice' => MarketplaceService::getProductOldPrice($product->id), 'description' => MarketplaceService::getProductDescription($product->id), @@ -642,6 +642,7 @@ class MarketplaceService 'productUrl' => $product->product_url ?? self::getProductLinkByGuid($product->guid), 'flowwowCategory' => $product->flowwow_category, 'flowwowSubcategory' => $product->flowwow_subcategory, + 'imageUrls' => self::getProductImageUrls($product->guid), ]; } @@ -658,6 +659,24 @@ class MarketplaceService return null; } + public static function getProductImageUrls($guid) + { + $urls = []; + $data = MatrixErpMedia::find() + ->where(['guid' => $guid]) + ->all(); + foreach ($data as $media) { + if (!empty($data->file)) { + $resultData = FileService::getFile($data->file); + if (File::src($resultData, 'images') != null) { + $fileName = File::src($resultData, 'images'); + $urls[] = 'https://media.erp-flowers.ru/media/view-url?url=' . $fileName; + } + } + } + return $urls; + } + public static function getProductLinkByGuid($guid) { return 'https://media.erp-flowers.ru/media/view-card?guid=' . urlencode($guid); @@ -850,18 +869,24 @@ class MarketplaceService return null; } - return $product->lenghth ?? 20; + return $product->lenghth ?? null; } private static function getProductParams($productId) { - return [ + + $params = [ 'Ширина, См' => self::getProductWidth($productId), 'Высота, См' => self::getProductHeight($productId), - 'Длина, См' => self::getProductLength($productId), + ]; + if (self::getProductLength($productId)) { + array_push($params, ['Длина, См' => self::getProductLength($productId)]); + } + + return $params; } /** * Получает данные о заказах для указанных кампаний с учетом фильтров. -- 2.39.5