app/Customize/Controller/Admin/Product/SpreadsheetImportController.php line 132

Open in your IDE?
  1. <?php
  2. namespace Customize\Controller\Admin\Product;
  3. use Customize\Service\ScraperService;
  4. use Eccube\Controller\AbstractController;
  5. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  6. use Symfony\Component\HttpFoundation\JsonResponse;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Component\Routing\Annotation\Route;
  9. /**
  10.  * Admin controller for launching scraper processes and monitoring progress.
  11.  */
  12. class SpreadsheetImportController extends AbstractController
  13. {
  14.     /** @var ScraperService */
  15.     private $scraperService;
  16.     /** @var array */
  17.     private static $categoryLabels = [
  18.         'cp' => 'カーポート',
  19.         'cy' => 'サイクルポート',
  20.         'cg' => 'カーゲート',
  21.         'sh' => 'ガレージシャッター',
  22.         'wd' => 'ウッドデッキ',
  23.         'rs' => '立水栓・ガーデンシンク',
  24.         'gf' => 'ガーデンファニチャー',
  25.         'tf' => '人工芝・芝生',
  26.         'fe' => 'フェンス・柵・塀',
  27.         'ts' => '手すり',
  28.         'mp' => '門扉',
  29.         'fu' => 'ポスト・門柱・宅配ボックス',
  30.         'tr' => 'テラス屋根',
  31.         'tg' => 'テラス囲い・サンルーム',
  32.         'br' => 'バルコニー屋根',
  33.         'bl' => 'バルコニー・ベランダ',
  34.         'aw' => 'オーニング・日よけ',
  35.         'pg' => 'パーゴラ',
  36.         'mo' => '物置・収納・屋外倉庫',
  37.         'sy' => 'ストックヤード',
  38.         'gs' => 'ゴミステーション',
  39.         'wh' => '倉庫・ガレージ',
  40.         'um' => '二重窓(内窓)',
  41.         'ws' => '窓シャッター',
  42.         'mk' => '面格子・窓格子',
  43.         'fj' => '風除室・玄関フード',
  44.         'rd' => '玄関ドア',
  45.         'st' => '石材',
  46.         'sl' => '照明',
  47.         'dy' => 'DIY',
  48.     ];
  49.     public function __construct(ScraperService $scraperService)
  50.     {
  51.         $this->scraperService $scraperService;
  52.     }
  53.     /**
  54.      * Main page with buttons for product and price scraping.
  55.      *
  56.      * @Route("/%eccube_admin_route%/product/scraper", name="admin_product_scraper", methods={"GET"})
  57.      * @Template("@admin/Product/scraper.twig")
  58.      */
  59.     public function index()
  60.     {
  61.         return [
  62.             'categories'  => self::$categoryLabels,
  63.             'runningJobs' => $this->scraperService->getRunningJobs(),
  64.         ];
  65.     }
  66.     /**
  67.      * Start product scraper (basic data).
  68.      *
  69.      * @Route("/%eccube_admin_route%/product/scraper/start_product", name="admin_product_scraper_start_product", methods={"POST"})
  70.      */
  71.     public function startProduct(Request $request): JsonResponse
  72.     {
  73.         if (!$this->isTokenValid()) {
  74.             return $this->json(['error' => 'Invalid CSRF token'], 403);
  75.         }
  76.         if ($this->scraperService->isProductLocked()) {
  77.             return $this->json(['error' => '基本データ取込は既に実行中です'], 409);
  78.         }
  79.         $result $this->scraperService->startProductScraper();
  80.         if ($result) {
  81.             return $this->json(['status' => 'started''message' => '基本データ取込を開始しました']);
  82.         }
  83.         return $this->json(['error' => '開始できませんでした'], 500);
  84.     }
  85.     /**
  86.      * Start price scraper for a specific category.
  87.      *
  88.      * @Route("/%eccube_admin_route%/product/scraper/start_price", name="admin_product_scraper_start_price", methods={"POST"})
  89.      */
  90.     public function startPrice(Request $request): JsonResponse
  91.     {
  92.         if (!$this->isTokenValid()) {
  93.             return $this->json(['error' => 'Invalid CSRF token'], 403);
  94.         }
  95.         $category $request->request->get('category''');
  96.         if (!$category || !isset(self::$categoryLabels[$category])) {
  97.             return $this->json(['error' => '無効なカテゴリです'], 400);
  98.         }
  99.         if ($this->scraperService->isPriceLocked($category)) {
  100.             $label self::$categoryLabels[$category];
  101.             return $this->json(['error' => "{$label} の価格マトリクス取込は既に実行中です"], 409);
  102.         }
  103.         $maker $request->request->get('maker');
  104.         $result $this->scraperService->startPriceScraper($category$maker ?: null);
  105.         if ($result) {
  106.             $label self::$categoryLabels[$category];
  107.             return $this->json(['status' => 'started''message' => "{$label} の価格マトリクス取込を開始しました"]);
  108.         }
  109.         return $this->json(['error' => '開始できませんでした'], 500);
  110.     }
  111.     /**
  112.      * Get current status of all scrapers (AJAX polling endpoint).
  113.      *
  114.      * @Route("/%eccube_admin_route%/product/scraper/status", name="admin_product_scraper_status", methods={"GET"})
  115.      */
  116.     public function status(): JsonResponse
  117.     {
  118.         $product $this->scraperService->getProductProgress();
  119.         $productLocked $this->scraperService->isProductLocked();
  120.         // Collect price statuses for all categories
  121.         $priceStatuses = [];
  122.         foreach (self::$categoryLabels as $cat => $label) {
  123.             $locked $this->scraperService->isPriceLocked($cat);
  124.             $progress $this->scraperService->getPriceProgress($cat);
  125.             if ($locked || $progress) {
  126.                 $priceStatuses[$cat] = [
  127.                     'locked'   => $locked,
  128.                     'progress' => $progress,
  129.                 ];
  130.             }
  131.         }
  132.         return $this->json([
  133.             'product' => [
  134.                 'locked'   => $productLocked,
  135.                 'progress' => $product,
  136.             ],
  137.             'price' => $priceStatuses,
  138.         ]);
  139.     }
  140.     /**
  141.      * Get execution log for a scraper (for debugging).
  142.      *
  143.      * @Route("/%eccube_admin_route%/product/scraper/log", name="admin_product_scraper_log", methods={"GET"})
  144.      */
  145.     public function log(Request $request): JsonResponse
  146.     {
  147.         $type $request->query->get('type''product');
  148.         $category $request->query->get('category''');
  149.         if ($type === 'product') {
  150.             return $this->json([
  151.                 'log'   => $this->scraperService->getProductLog(200),
  152.                 'debug' => $this->scraperService->getDebugInfo(),
  153.             ]);
  154.         }
  155.         if ($type === 'price' && $category) {
  156.             return $this->json([
  157.                 'log'   => $this->scraperService->getPriceLog($category200),
  158.                 'debug' => $this->scraperService->getDebugInfo(),
  159.             ]);
  160.         }
  161.         return $this->json(['error' => 'invalid type/category'], 400);
  162.     }
  163.     /**
  164.      * Cancel a running scraper.
  165.      *
  166.      * @Route("/%eccube_admin_route%/product/scraper/cancel", name="admin_product_scraper_cancel", methods={"POST"})
  167.      */
  168.     public function cancel(Request $request): JsonResponse
  169.     {
  170.         if (!$this->isTokenValid()) {
  171.             return $this->json(['error' => 'Invalid CSRF token'], 403);
  172.         }
  173.         $type $request->request->get('type''');
  174.         $category $request->request->get('category''');
  175.         if ($type === 'product') {
  176.             $this->scraperService->cancelProduct();
  177.             return $this->json(['status' => 'cancelled''message' => '基本データ取込をキャンセルしました']);
  178.         }
  179.         if ($type === 'price' && $category) {
  180.             $this->scraperService->cancelPrice($category);
  181.             $label self::$categoryLabels[$category] ?? $category;
  182.             return $this->json(['status' => 'cancelled''message' => "{$label} の価格マトリクス取込をキャンセルしました"]);
  183.         }
  184.         return $this->json(['error' => '無効なリクエスト'], 400);
  185.     }
  186. }