From ba90f229043e9dd65c6b5c9cf347a931dc4073be Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Mon, 9 Mar 2026 15:42:24 +0100 Subject: [PATCH] feat: Add endpoint for optimizing products based on clicks, including client identification and response examples in documentation --- .vscode/ftp-kr.sync.cache.json | 10 ++- api.php | 86 +++++++++++++++++++++ docs/api-public-product-management.md | 104 ++++++++++++++++++++++++-- 3 files changed, 193 insertions(+), 7 deletions(-) diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index b53600c..11d43a9 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -9,8 +9,8 @@ }, "api.php": { "type": "-", - "size": 5529, - "lmtime": 1772790264251, + "size": 10947, + "lmtime": 1772982639016, "modified": false }, "autoload": { @@ -341,6 +341,12 @@ "size": 657, "lmtime": 1772115859276, "modified": false + }, + "api-public-product-management.md": { + "type": "-", + "size": 6206, + "lmtime": 1772982690134, + "modified": false } }, "feeds": { diff --git a/api.php b/api.php index 74db4bf..251de82 100644 --- a/api.php +++ b/api.php @@ -340,6 +340,92 @@ if ( \S::get( 'action' ) == 'product_google_category_get' ) ] ); } +// Pobranie 10 produktow do optymalizacji (wg klikniec, bez nowego tytulu lub kategorii Google) +if ( \S::get( 'action' ) == 'products_to_optimize' ) +{ + api_validate_api_key( $mdb ); + + $client_id = trim( (string) \S::get( 'client_id' ) ); + $google_ads_id = trim( (string) \S::get( 'google_ads_id' ) ); + $merchant_id = trim( (string) \S::get( 'merchant_id' ) ); + $limit = (int) \S::get( 'limit' ); + + if ( $limit <= 0 || $limit > 100 ) + { + $limit = 10; + } + + // Resolve client_id from whichever parameter was provided + $resolved_client_id = null; + + if ( $client_id !== '' ) + { + $resolved_client_id = (int) $mdb -> get( 'clients', 'id', [ 'id' => (int) $client_id ] ); + } + elseif ( $google_ads_id !== '' ) + { + $google_ads_id_clean = str_replace( '-', '', $google_ads_id ); + $resolved_client_id = (int) $mdb -> query( + 'SELECT id FROM clients WHERE REPLACE( google_ads_customer_id, \'-\', \'\' ) = :gid LIMIT 1', + [ ':gid' => $google_ads_id_clean ] + ) -> fetchColumn(); + } + elseif ( $merchant_id !== '' ) + { + $merchant_id_clean = str_replace( '-', '', $merchant_id ); + $resolved_client_id = (int) $mdb -> query( + 'SELECT id FROM clients WHERE REPLACE( google_merchant_account_id, \'-\', \'\' ) = :mid LIMIT 1', + [ ':mid' => $merchant_id_clean ] + ) -> fetchColumn(); + } + else + { + api_json_response( [ 'result' => 'error', 'message' => 'Missing required param: client_id, google_ads_id or merchant_id' ], 422 ); + } + + if ( !$resolved_client_id ) + { + api_json_response( [ 'result' => 'error', 'message' => 'Client not found' ], 404 ); + } + + $products = $mdb -> query( + 'SELECT p.id, + p.offer_id, + p.name AS original_name, + p.title AS custom_title, + p.google_product_category, + p.product_url, + SUM( pa.clicks_30 ) AS clicks_30, + SUM( pa.clicks_all_time ) AS clicks_all_time, + SUM( pa.impressions_30 ) AS impressions_30, + SUM( pa.cost_30 ) AS cost_30, + SUM( pa.conversions_30 ) AS conversions_30, + SUM( pa.conversion_value_30 ) AS conversion_value_30 + FROM products AS p + INNER JOIN products_aggregate AS pa ON pa.product_id = p.id + WHERE p.client_id = :client_id + AND ( + TRIM( COALESCE( p.title, \'\' ) ) = \'\' + OR TRIM( COALESCE( p.google_product_category, \'\' ) ) = \'\' + ) + GROUP BY p.id + HAVING clicks_all_time > 0 + ORDER BY clicks_all_time DESC + LIMIT :limit', + [ + ':client_id' => $resolved_client_id, + ':limit' => $limit + ] + ) -> fetchAll( \PDO::FETCH_ASSOC ); + + api_json_response( [ + 'result' => 'ok', + 'client_id' => $resolved_client_id, + 'count' => count( $products ), + 'products' => $products + ] ); +} + // Open Page Rank - zapis if ( \S::get( 'action' ) == 'domain_opr_save' ) { diff --git a/docs/api-public-product-management.md b/docs/api-public-product-management.md index 8a58370..11bd6bd 100644 --- a/docs/api-public-product-management.md +++ b/docs/api-public-product-management.md @@ -212,7 +212,99 @@ Jesli kategoria nie jest ustawiona: } ``` -### 4.5 Ustawienie custom_label_4 (istniejacy endpoint) +### 4.5 Pobranie produktow do optymalizacji + +- `action=products_to_optimize` +- Cel: zwraca liste produktow posortowanych wg klikniec (malejaco), ktore nie maja ustawionego custom tytulu (`title`) lub kategorii Google (`google_product_category`) + +Identyfikacja klienta (wymagany dokladnie jeden z trzech): +- `client_id` (int) - lokalny identyfikator klienta w adsPRO +- `google_ads_id` (string) - ID konta Google Ads (z myslnikami lub bez, np. `123-456-7890` lub `1234567890`) +- `merchant_id` (string) - ID konta Google Merchant Center (z myslnikami lub bez) + +Parametry dodatkowe: +- `api_key` (string, wymagany) +- `limit` (int, opcjonalny) - liczba produktow do zwrocenia (domyslnie 10, max 100) + +Logika: +- Wybiera produkty, ktorych `title` jest pusty/NULL **lub** `google_product_category` jest pusty/NULL +- Pomija produkty z 0 klikniec (all-time) +- Sortuje po `clicks_all_time` malejaco (produkty z najwiekszym ruchem na gorze) +- Agreguje dane z `products_aggregate` (suma klikniec ze wszystkich kampanii/grup reklam) + +Przyklad z `client_id`: + +```bash +curl -G "https://example.com/api.php" \ + --data-urlencode "action=products_to_optimize" \ + --data-urlencode "api_key=YOUR_API_KEY" \ + --data-urlencode "client_id=12" \ + --data-urlencode "limit=10" +``` + +Przyklad z `google_ads_id`: + +```bash +curl -G "https://example.com/api.php" \ + --data-urlencode "action=products_to_optimize" \ + --data-urlencode "api_key=YOUR_API_KEY" \ + --data-urlencode "google_ads_id=123-456-7890" +``` + +Przyklad z `merchant_id`: + +```bash +curl -G "https://example.com/api.php" \ + --data-urlencode "action=products_to_optimize" \ + --data-urlencode "api_key=YOUR_API_KEY" \ + --data-urlencode "merchant_id=987654321" +``` + +Przyklad odpowiedzi: + +```json +{ + "result": "ok", + "client_id": 12, + "count": 3, + "products": [ + { + "id": 987, + "offer_id": "SKU-123", + "original_name": "Buty sportowe Nike Air", + "custom_title": null, + "google_product_category": null, + "product_url": "https://example.com/buty-nike-air", + "clicks_30": 45, + "clicks_all_time": 312, + "impressions_30": 1200, + "cost_30": "89.500000", + "conversions_30": "3.000000", + "conversion_value_30": "450.000000" + }, + { + "id": 654, + "offer_id": "SKU-456", + "original_name": "Koszulka Adidas", + "custom_title": "Koszulka sportowa Adidas Originals", + "google_product_category": null, + "product_url": "https://example.com/koszulka-adidas", + "clicks_30": 22, + "clicks_all_time": 198, + "impressions_30": 800, + "cost_30": "45.200000", + "conversions_30": "1.000000", + "conversion_value_30": "120.000000" + } + ] +} +``` + +Bledy specyficzne: +- Brak parametru identyfikujacego klienta: `422` z `"Missing required param: client_id, google_ads_id or merchant_id"` +- Klient nie znaleziony: `404` z `"Client not found"` + +### 4.6 Ustawienie custom_label_4 (istniejacy endpoint) - `action=product_custom_label_4_set` - Cel: zapisuje `products.custom_label_4` @@ -270,9 +362,11 @@ Przy integracji AI zawsze ustawiaj jawnie `action` i weryfikuj, czy odpowiedz to ## 7. Minimalny scenariusz end-to-end -1. Ustaw tytul (`product_title_set`) -2. Potwierdz zmiane (`product_title_changed_check`) -3. Ustaw kategorie (`product_google_category_set`) -4. Odczytaj kategorie (`product_google_category_get`) +1. Pobierz liste produktow do optymalizacji (`products_to_optimize`) +2. Dla kazdego produktu z listy: + a. Jesli `custom_title` jest `null` - ustaw tytul (`product_title_set`) + b. Potwierdz zmiane tytulu (`product_title_changed_check`) + c. Jesli `google_product_category` jest `null` - ustaw kategorie (`product_google_category_set`) + d. Odczytaj kategorie (`product_google_category_get`) To daje prosty, deterministyczny przeplyw dla automatyzacji AI.