]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
[ERP-50-J] Добавление проверки камер origin/feature_filippov_ERP-50-J_
authorAleksey Filippov <Aleksey.Filippov@erp-flowers.ru>
Tue, 3 Feb 2026 18:37:46 +0000 (21:37 +0300)
committerAleksey Filippov <Aleksey.Filippov@erp-flowers.ru>
Tue, 3 Feb 2026 18:37:46 +0000 (21:37 +0300)
.gitignore
erp24/controllers/DiagnosticController.php
erp24/views/diagnostic/index.php

index 74ddd0c7bfbd6e6fcba146dffeb7c9773cdef252..bf51f52605afc1dde0944ac56527ad2d2a5d29f1 100644 (file)
@@ -45,3 +45,4 @@ hive-mind-prompt-*.txt
 
 # Auto Claude data directory
 .auto-claude/
+.env.jira
index 644f028ff7047a63aad9b0b8e80ffb019c8ca7d7..cf018dc0d7fcfa3d03c78e66690f4b786eada1ad 100644 (file)
@@ -514,6 +514,171 @@ class DiagnosticController extends Controller
         }
     }
 
+    /**
+     * Тестирование подключения к камере Дом.ру
+     *
+     * @param int $id Номер камеры (1-6)
+     */
+    public function actionTestCamera(int $id = 1): Response
+    {
+        Yii::$app->response->format = Response::FORMAT_JSON;
+
+        if ($id < 1 || $id > 6) {
+            return $this->asJson([
+                'success' => false,
+                'message' => 'Неверный номер камеры',
+                'hint' => 'Номер камеры должен быть от 1 до 6',
+            ]);
+        }
+
+        $login = getenv("CAMERA_{$id}_LOGIN") ?: '';
+        $password = getenv("CAMERA_{$id}_PASSWORD") ?: '';
+
+        if (empty($login) || empty($password)) {
+            return $this->asJson([
+                'success' => false,
+                'message' => "Камера {$id}: credentials не настроены",
+                'hint' => "Установите CAMERA_{$id}_LOGIN и CAMERA_{$id}_PASSWORD в .env файле",
+                'data' => [
+                    'camera_id' => $id,
+                    'login_set' => !empty($login),
+                    'password_set' => !empty($password),
+                ],
+            ]);
+        }
+
+        try {
+            $client = new Client(['timeout' => 15]);
+            $startTime = microtime(true);
+
+            // Авторизация в API Дом.ру
+            $response = $client->post('https://vs.domru.ru/api/Login', [
+                'form_params' => [
+                    'Login' => $login,
+                    'Password' => $password,
+                ],
+                'verify' => false,
+            ]);
+
+            $data = json_decode($response->getBody()->getContents(), true);
+            $sessionId = $data['SessionID'] ?? null;
+
+            if (empty($sessionId)) {
+                return $this->asJson([
+                    'success' => false,
+                    'message' => "Камера {$id}: ошибка авторизации",
+                    'error' => $data['Error'] ?? 'Не получен SessionID',
+                    'data' => [
+                        'camera_id' => $id,
+                        'login' => $this->maskValue("CAMERA_{$id}_LOGIN", $login),
+                    ],
+                ]);
+            }
+
+            // Получение списка камер
+            $camerasResponse = $client->post('https://vs.domru.ru/api/GetCameras', [
+                'form_params' => [
+                    'SessionID' => $sessionId,
+                ],
+                'verify' => false,
+            ]);
+
+            $camerasData = json_decode($camerasResponse->getBody()->getContents(), true);
+            $duration = round((microtime(true) - $startTime) * 1000, 2);
+
+            $camerasList = [];
+            if (is_array($camerasData)) {
+                foreach ($camerasData as $camera) {
+                    $camerasList[] = [
+                        'name' => $camera['Name'] ?? 'N/A',
+                        'account_object' => $camera['AccountObjectName'] ?? 'N/A',
+                        'camera_id' => $camera['CameraID'] ?? 'N/A',
+                    ];
+                }
+            }
+
+            return $this->asJson([
+                'success' => true,
+                'message' => "Камера {$id}: подключение успешно",
+                'data' => [
+                    'camera_id' => $id,
+                    'login' => $this->maskValue("CAMERA_{$id}_LOGIN", $login),
+                    'session_id' => substr($sessionId, 0, 8) . '...',
+                    'cameras_count' => count($camerasList),
+                    'cameras' => $camerasList,
+                    'duration_ms' => $duration,
+                ],
+            ]);
+        } catch (GuzzleException $e) {
+            return $this->asJson([
+                'success' => false,
+                'message' => "Камера {$id}: ошибка подключения",
+                'error' => $e->getMessage(),
+                'data' => [
+                    'camera_id' => $id,
+                    'login' => $this->maskValue("CAMERA_{$id}_LOGIN", $login),
+                ],
+            ]);
+        }
+    }
+
+    /**
+     * Тестирование всех настроенных камер Дом.ру
+     */
+    public function actionTestAllCameras(): Response
+    {
+        Yii::$app->response->format = Response::FORMAT_JSON;
+
+        $results = [];
+        $successCount = 0;
+        $failCount = 0;
+        $skippedCount = 0;
+
+        for ($id = 1; $id <= 6; $id++) {
+            $login = getenv("CAMERA_{$id}_LOGIN") ?: '';
+            $password = getenv("CAMERA_{$id}_PASSWORD") ?: '';
+
+            if (empty($login) || empty($password)) {
+                $results["camera_{$id}"] = [
+                    'success' => null,
+                    'message' => 'Не настроена',
+                    'skipped' => true,
+                ];
+                $skippedCount++;
+                continue;
+            }
+
+            // Вызываем тест для каждой камеры
+            $testResult = $this->actionTestCamera($id);
+            $testData = json_decode($testResult->content, true);
+            $results["camera_{$id}"] = $testData;
+
+            if ($testData['success'] ?? false) {
+                $successCount++;
+            } else {
+                $failCount++;
+            }
+
+            // Небольшая пауза между запросами
+            usleep(500000); // 0.5 секунды
+        }
+
+        return $this->asJson([
+            'success' => $failCount === 0 && $successCount > 0,
+            'message' => $successCount > 0
+                ? "Камеры: {$successCount} успешно, {$failCount} ошибок, {$skippedCount} пропущено"
+                : 'Нет настроенных камер',
+            'summary' => [
+                'total' => 6,
+                'configured' => $successCount + $failCount,
+                'passed' => $successCount,
+                'failed' => $failCount,
+                'skipped' => $skippedCount,
+            ],
+            'results' => $results,
+        ]);
+    }
+
     /**
      * Полная проверка всех переменных окружения
      */
@@ -627,7 +792,7 @@ class DiagnosticController extends Controller
         ];
 
         $cameras = [];
-        for ($i = 1; $i <= 5; $i++) {
+        for ($i = 1; $i <= 6; $i++) {
             $cameras["CAMERA_{$i}_LOGIN"] = "Логин камеры $i";
             $cameras["CAMERA_{$i}_PASSWORD"] = "Пароль камеры $i";
         }
@@ -673,7 +838,7 @@ class DiagnosticController extends Controller
      */
     private function getConnectionsStatus(): array
     {
-        return [
+        $connections = [
             'postgresql' => [
                 'name' => 'PostgreSQL (основная БД)',
                 'configured' => Yii::$app->has('db'),
@@ -703,6 +868,18 @@ class DiagnosticController extends Controller
                 'configured' => Yii::$app->has('queue'),
             ],
         ];
+
+        // Добавляем статус камер
+        for ($i = 1; $i <= 6; $i++) {
+            $loginSet = !empty(getenv("CAMERA_{$i}_LOGIN"));
+            $passwordSet = !empty(getenv("CAMERA_{$i}_PASSWORD"));
+            $connections["camera_{$i}"] = [
+                'name' => "Камера Дом.ру #{$i}",
+                'configured' => $loginSet && $passwordSet,
+            ];
+        }
+
+        return $connections;
     }
 
     /**
index d94f4b77b98334a5d6457af4275f9f3282d83b74..4fb25b1a4c02d3b3c02b4963e1689ac3212dc044 100644 (file)
@@ -583,6 +583,49 @@ $this->params['breadcrumbs'][] = $this->title;
         </div>
     </div>
 
+    <!-- Cameras Tests -->
+    <div class="section-card">
+        <div class="section-header">
+            <h3>📹 Камеры Дом.ру</h3>
+            <button class="btn-test btn-test-success" onclick="testAllCameras()">
+                🔄 Тестировать все
+            </button>
+        </div>
+        <div class="section-body" style="padding: 0;">
+            <?php for ($i = 1; $i <= 6; $i++): ?>
+                <?php
+                    $loginSet = !empty(getenv("CAMERA_{$i}_LOGIN"));
+                    $passwordSet = !empty(getenv("CAMERA_{$i}_PASSWORD"));
+                    $configured = $loginSet && $passwordSet;
+                ?>
+                <div class="test-item">
+                    <div class="test-icon" style="background: <?= $configured ? '#28a745' : '#6c757d' ?>; color: white;">
+                        <?= $i ?>
+                    </div>
+                    <div class="test-info">
+                        <div class="test-name">Камера <?= $i ?></div>
+                        <div class="test-status" id="camera-<?= $i ?>-status">
+                            <?php if ($configured): ?>
+                                <span class="status-badge info">Сконфигурирована</span>
+                            <?php else: ?>
+                                <span class="status-badge warning">Не настроена</span>
+                            <?php endif; ?>
+                        </div>
+                    </div>
+                    <div class="test-actions">
+                        <button class="btn-test btn-test-primary" onclick="testCamera(<?= $i ?>)" <?= !$configured ? 'disabled' : '' ?>>
+                            Тестировать
+                        </button>
+                    </div>
+                </div>
+                <div class="result-container" id="camera-<?= $i ?>-result"></div>
+            <?php endfor; ?>
+
+            <!-- Cameras Summary -->
+            <div class="result-container" id="cameras-summary-result" style="margin: 15px;"></div>
+        </div>
+    </div>
+
     <!-- Environment Variables -->
     <div class="section-card">
         <div class="section-header">
@@ -705,7 +748,9 @@ $this->params['breadcrumbs'][] = $this->title;
         'telegram-salebot': '<?= \yii\helpers\Url::to(['/diagnostic/test-telegram-salebot']) ?>',
         'queue': '<?= \yii\helpers\Url::to(['/diagnostic/test-queue']) ?>',
         'send-message': '<?= \yii\helpers\Url::to(['/diagnostic/send-test-message']) ?>',
-        'run-all': '<?= \yii\helpers\Url::to(['/diagnostic/run-all-tests']) ?>'
+        'run-all': '<?= \yii\helpers\Url::to(['/diagnostic/run-all-tests']) ?>',
+        'camera': '<?= \yii\helpers\Url::to(['/diagnostic/test-camera']) ?>',
+        'all-cameras': '<?= \yii\helpers\Url::to(['/diagnostic/test-all-cameras']) ?>'
     };
 
     async function testConnection(type) {
@@ -845,6 +890,99 @@ $this->params['breadcrumbs'][] = $this->title;
         }
     }
 
+    async function testCamera(cameraId) {
+        const resultContainer = document.getElementById('camera-' + cameraId + '-result');
+        const statusContainer = document.getElementById('camera-' + cameraId + '-status');
+
+        resultContainer.innerHTML = '<div class="loading"></div> Тестирование камеры ' + cameraId + '...';
+        resultContainer.classList.add('show');
+
+        try {
+            const response = await fetch(endpoints['camera'] + '?id=' + cameraId);
+            const data = await response.json();
+
+            let statusBadge = data.success
+                ? '<span class="status-badge success">✓ Подключена</span>'
+                : '<span class="status-badge danger">✗ Ошибка</span>';
+
+            if (data.skipped) {
+                statusBadge = '<span class="status-badge warning">Не настроена</span>';
+            }
+
+            statusContainer.innerHTML = statusBadge;
+
+            let resultHtml = `<strong>${data.message}</strong><br>`;
+            if (data.data) {
+                // Форматируем вывод камер
+                if (data.data.cameras && data.data.cameras.length > 0) {
+                    resultHtml += '<div style="margin-top: 10px;"><strong>Камеры в аккаунте:</strong><ul style="margin: 5px 0; padding-left: 20px;">';
+                    data.data.cameras.forEach(cam => {
+                        resultHtml += `<li>${cam.name} (${cam.account_object})</li>`;
+                    });
+                    resultHtml += '</ul></div>';
+
+                    // Показываем остальные данные без массива камер
+                    const displayData = {...data.data};
+                    delete displayData.cameras;
+                    resultHtml += '<pre>' + JSON.stringify(displayData, null, 2) + '</pre>';
+                } else {
+                    resultHtml += '<pre>' + JSON.stringify(data.data, null, 2) + '</pre>';
+                }
+            }
+            if (data.error) {
+                resultHtml += '<pre style="color: #dc3545;">' + data.error + '</pre>';
+            }
+            if (data.hint) {
+                resultHtml += '<p style="color: #856404; margin-top: 10px;">💡 ' + data.hint + '</p>';
+            }
+
+            resultContainer.innerHTML = resultHtml;
+        } catch (error) {
+            resultContainer.innerHTML = '<pre style="color: #dc3545;">Ошибка запроса: ' + error.message + '</pre>';
+            statusContainer.innerHTML = '<span class="status-badge danger">✗ Ошибка</span>';
+        }
+    }
+
+    async function testAllCameras() {
+        const summaryContainer = document.getElementById('cameras-summary-result');
+        summaryContainer.innerHTML = '<div class="loading"></div> Тестирование всех камер...';
+        summaryContainer.classList.add('show');
+
+        // Тестируем каждую камеру по очереди
+        for (let i = 1; i <= 6; i++) {
+            const loginSet = document.querySelector(`#camera-${i}-status .status-badge`)?.textContent?.includes('Сконфигурирована');
+            if (loginSet || document.querySelector(`button[onclick="testCamera(${i})"]`)?.disabled === false) {
+                await testCamera(i);
+                await new Promise(resolve => setTimeout(resolve, 300));
+            }
+        }
+
+        // Получаем общую сводку
+        try {
+            const response = await fetch(endpoints['all-cameras']);
+            const data = await response.json();
+
+            let summaryHtml = '<h4 style="margin-top: 0;">📊 Сводка по камерам</h4>';
+
+            if (data.summary) {
+                const { passed, failed, skipped, configured } = data.summary;
+                summaryHtml += `
+                    <div style="display: flex; gap: 20px; flex-wrap: wrap;">
+                        <div><strong style="color: #28a745;">${passed}</strong> успешно</div>
+                        <div><strong style="color: #dc3545;">${failed}</strong> ошибок</div>
+                        <div><strong style="color: #6c757d;">${skipped}</strong> пропущено</div>
+                        <div><strong style="color: #17a2b8;">${configured}</strong> настроено</div>
+                    </div>
+                `;
+            }
+
+            summaryHtml += `<p style="margin-top: 10px;">${data.message}</p>`;
+            summaryContainer.innerHTML = summaryHtml;
+        } catch (error) {
+            summaryContainer.innerHTML = '<pre style="color: #dc3545;">Ошибка: ' + error.message + '</pre>';
+        }
+    }
+
     // Загрузка реальных названий ботов при открытии страницы
     async function loadBotNames() {
         // Загрузка Dev бота