]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Настройки вотсап
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 6 Mar 2025 14:45:03 +0000 (17:45 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 6 Mar 2025 14:45:03 +0000 (17:45 +0300)
erp24/commands/CronController.php
erp24/migrations/m250305_121956_add_column_whatsapp_settings_to_users_message_management_table.php [new file with mode: 0644]
erp24/records/UsersMessageManagement.php
erp24/services/WhatsAppService.php
erp24/views/users-message-management/index.php

index 7f1c12dbc8effad72fac8ac00bf16fdf6b1b26cb..a95b366a7cbf6a6457f0752f500388c3ce193df9 100644 (file)
@@ -841,20 +841,59 @@ class CronController extends Controller
             }
         } else {
             $this->stdout(
-                "Нет данных для отправки второго сообщения в телеграм
+                "Нет данных для отправки сообщения в Whatsapp
                  на {$kogortDate} для целевой даты {$targetDate}.\n",
                 BaseConsole::FG_RED
             );
         }
 
         $this->stdout(
-            "Отправка второго сообщения в телеграм для корорты (ватсап)
+            "Отправка сообщения ватсап для корорты
              на {$kogortDate} для целевой даты {$targetDate}.\n",
             BaseConsole::FG_GREEN
         );
         return ExitCode::OK;
     }
 
+    public function actionGetWhatsappMessageHistory()
+    {
+        $this->stdout("Получаем историю сообщений...\n", BaseConsole::FG_GREEN);
+        date_default_timezone_set('Europe/Moscow');
+        $currentDate = date('Y-m-d');
+
+        $params = [
+            "offset"       => 0,
+            "limit"        => 1000,
+            "channelTypes" => ["WHATSAPP"],
+            "direction"    => "OUT",
+            "dateFrom"     => $currentDate . "T00:00:00Z",
+            "dateTo"       => $currentDate . "T23:59:59Z",
+            "sort"         => [
+                [
+                    "property"  => "messageId",
+                    "direction" => "DESC"
+                ]
+            ],
+            "subjectId"    => 11374
+        ];
+
+        $done = WhatsAppService::processMessagesHistoryAndUpdateStatuses($params);
+
+        if ($done) {
+            $this->stdout(
+                "Сохранение статусов успешно завершено.\n",
+                BaseConsole::FG_GREEN
+            );
+            return ExitCode::OK;
+        } else {
+            $this->stdout(
+                "Сохранение статусов не удалось.\n",
+                BaseConsole::FG_RED
+            );
+            return ExitCode::DATAERR;
+        }
+    }
+
     public function actionGenerateCallKogorts()
     {
         $messagesSettings = UsersMessageManagement::find()->one();
diff --git a/erp24/migrations/m250305_121956_add_column_whatsapp_settings_to_users_message_management_table.php b/erp24/migrations/m250305_121956_add_column_whatsapp_settings_to_users_message_management_table.php
new file mode 100644 (file)
index 0000000..cf3ceeb
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+
+use yii\db\Migration;
+
+class m250305_121956_add_column_whatsapp_settings_to_users_message_management_table extends Migration
+{
+    const TABLE_NAME = 'erp24.users_message_management';
+    /**
+     * {@inheritdoc}
+     */
+    public function safeUp()
+    {
+        if ($this->db->schema->getTableSchema(self::TABLE_NAME) === null) {
+            return;
+        }
+
+        if ($this->db->schema->getTableSchema(self::TABLE_NAME)->getColumn('channel_name') === null) {
+            $this->addColumn(
+                self::TABLE_NAME,
+                'channel_name',
+                $this->string()->null()->comment('Имя канала')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'channel_id',
+                $this->string()->null()->comment('Идентификатор канала - подпись')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'cascade_name',
+                $this->string()->null()->comment('Имя каскада')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'channel_limit',
+                $this->integer()->null()->comment('Идентификатор шаблона')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'cascade_id',
+                $this->integer()->null()->comment('ID каскада')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'subject_id',
+                $this->integer()->null()->comment('Идентификатор подписи')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'template_name',
+                $this->string()->null()->comment('Имя шаблона')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'template_id',
+                $this->integer()->null()->comment('Идентификатор шаблона')
+            );
+            $this->addColumn(
+                self::TABLE_NAME,
+                'callback_status_url',
+                $this->string()->null()->comment('URL приема колбеков статусов сообщений')
+            );
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function safeDown()
+    {
+        if ($this->db->schema->getTableSchema(self::TABLE_NAME) === null) {
+            return;
+        }
+
+        if ($this->db->schema->getTableSchema(self::TABLE_NAME)->getColumn('channel_name') !== null) {
+            $this->dropColumn(self::TABLE_NAME, 'channel_name');
+            $this->dropColumn(self::TABLE_NAME, 'channel_id');
+            $this->dropColumn(self::TABLE_NAME, 'channel_limit');
+            $this->dropColumn(self::TABLE_NAME, 'cascade_name');
+            $this->dropColumn(self::TABLE_NAME, 'cascade_id');
+            $this->dropColumn(self::TABLE_NAME, 'subject_id');
+            $this->dropColumn(self::TABLE_NAME, 'template_name');
+            $this->dropColumn(self::TABLE_NAME, 'template_id');
+            $this->dropColumn(self::TABLE_NAME, 'callback_status_url');
+        }
+    }
+
+    /*
+    // Use up()/down() to run migration code without a transaction.
+    public function up()
+    {
+
+    }
+
+    public function down()
+    {
+        echo "m250305_121956_add_column_whatsapp_settings_to_users_message_management_table cannot be reverted.\n";
+
+        return false;
+    }
+    */
+}
index a8b69e79e6f6ea5635d82ac44b48be5d5a6beb88..542dfc6838e63481fd92502563ffd204178012ac 100644 (file)
@@ -32,6 +32,7 @@ use Yii;
  * @property string|null $date_end Дата и время завершения события
  * @property string $test_phones_list Список тестовых телефонов
  * @property string $test_phones_active Активность тестового списка
+ * @property string $channel_name Имя канала
  */
 class UsersMessageManagement extends \yii\db\ActiveRecord
 {
@@ -69,16 +70,17 @@ class UsersMessageManagement extends \yii\db\ActiveRecord
             [['bonus', 'day_before_step1', 'day_before_step2', 'day_before_step3', 'date_start', 'offer_1', 'offer_2', 'offer_whatsapp', 'offer_text', 'date_last_scenario', 'created_at', 'created_by', 'updated_at', 'updated_by', 'hold'], 'required'],
             [['bonus'], 'number'],
             [['day_before_step1', 'day_before_step2', 'day_before_step3', 'created_by', 'updated_by',
-                'hold', 'hold_active', 'day_before_step1_active', 'day_before_step2_active', 'day_before_step3_active', 'active'], 'default', 'value' => null],
-            [['day_before_step1', 'day_before_step2', 'day_before_step3', 'created_by', 'updated_by',
-                'hold', 'hold_active', 'day_before_step1_active', 'day_before_step2_active', 'day_before_step3_active', 'active', 'test_phones_active'], 'integer'],
-            [['date_start', 'date_last_scenario', 'created_at', 'updated_at', 'date_end', 'test_phones_list'], 'safe'],
-            [['offer_1', 'offer_2', 'offer_3', 'offer_whatsapp', 'offer_text', 'date_end', 'test_phones_list'], 'string'],
+                'hold', 'hold_active', 'day_before_step1_active', 'day_before_step2_active', 'day_before_step3_active', 'active',
+                'cascade_id', 'subject_id', 'template_id', 'channel_limit', 'test_phones_active'], 'integer'],
+            [['date_start', 'date_last_scenario', 'created_at', 'updated_at', 'date_end'], 'safe'],
+            [['offer_1', 'offer_2', 'offer_3', 'offer_whatsapp', 'offer_text', 'test_phones_list',
+                'channel_name', 'channel_id', 'cascade_name', 'template_name', 'callback_status_url'], 'string'],
             [['offer_1', 'offer_2'], 'string', 'max' => 10000, 'tooLong' => '{attribute} должно содержать не более 10000 символов'],
             [['offer_whatsapp', 'offer_text'], 'string', 'max' => 900, 'tooLong' => '{attribute} должно содержать не более 900 символов'],
         ];
     }
 
+
     public function attributes()
     {
         return array_merge(parent::attributes(), ['bonus_action']);
@@ -116,6 +118,15 @@ class UsersMessageManagement extends \yii\db\ActiveRecord
             'bonus_action' => 'Срок действия бонуса',
             'test_phones_list' => 'Список тестовых телефонов',
             'test_phones_active' => 'Активность тестовой рассылки',
+            'channel_name' => 'Имя канала',
+            'channel_id' => 'Идентификатор канала - подпись',
+            'channel_limit' => 'Суточный лимит сообщений',
+            'cascade_name' => 'Имя каскада',
+            'cascade_id' => 'ID каскада',
+            'subject_id' => 'Идентификатор подписи',
+            'template_name' => 'Имя шаблона',
+            'template_id' => 'Идентификатор шаблона',
+            'callback_status_url' => 'URL приема колбеков статусов сообщений',
         ];
     }
 
@@ -133,9 +144,18 @@ class UsersMessageManagement extends \yii\db\ActiveRecord
     {
         $bonusActivity = $this->getBonusAction();
         $step1 = $this->day_before_step1;
+        $step2 = $this->day_before_step2;
+
         $startDate = date('d.m.Y', strtotime("-$step1 days", strtotime($targetDate)));
         $validDate = date('d.m.Y', strtotime("+$bonusActivity days", strtotime($startDate)));
+
         $message = str_replace('[NumberOfBonuses]', $this->bonus, $message);
-        return str_replace('[ValidityOfBonuses]', $validDate, $message);
+        $message = str_replace('[ValidityOfBonuses]', $validDate, $message);
+        $message = str_replace('[StepTwoDaysBeforeTarget]', $step2, $message);
+
+        // Нормализуем переводы строк: заменяем \r\n на \n
+        $message = str_replace("\r\n", "\n", $message);
+
+        return $message;
     }
 }
index fe9d024245cb26a2a3e465b4f4ae078144839046..14f277cc91dfc4332037107fe2d165b54fa76bc1 100644 (file)
@@ -3,14 +3,13 @@
 namespace yii_app\services;
 
 use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
 use GuzzleHttp\Exception\RequestException;
 use Psr\Http\Message\ResponseInterface;
 use Yii;
-use yii\base\Arrayable;
 use yii\base\Exception;
 use yii_app\helpers\DataHelper;
 use yii_app\records\UsersMessageManagement;
+use yii_app\records\UsersWhatsappMessage;
 
 /**
  * Сервис для отправки сообщений WhatsApp через API.
@@ -134,7 +133,6 @@ class WhatsAppService
      */
     public function sendMessage($requestId, $phone, $message, $isTest = false, $startTime = null, $ttl = null)
     {
-
         if (!$message) {
             Yii::error("Текст сообщения для WhatsApp не передан.");
             return null;
@@ -151,22 +149,18 @@ class WhatsAppService
 
         $buttons =  [
             'rows' => [
-                [
                     'buttons' => [
                         [
                             'text'       => 'Наш сайт',
                             'url'        => 'https://bazacvetov24.ru',
-                            'urlPostfix' => '',
                             'type'       => 'URL'
                         ],
                         [
                             'text'       => '1000 руб. забрать',
                             'url'        => 'https://bazacvetov24.ru/akcii',
-                            'urlPostfix' => '',
                             'type'       => 'URL'
                         ]
                     ]
-                ]
             ]
         ];
         // Формируем содержимое WhatsApp-сообщения
@@ -176,8 +170,8 @@ class WhatsAppService
         ];
 
         if (!$isTest) {
-            $whatsappContent['keyboard'] = $buttons;
-            $whatsappContent['messageMatcherId'] = self::getMessageMatcherIdBySubjectId($subjectId, 'kogort_message') ?? 120669;
+           // $whatsappContent['keyboard'] = $buttons;
+            $whatsappContent['messageMatcherId'] = self::getMessageMatcherIdBySubjectId($subjectId, 'kogort_message_3') ?? 121254;
         } else {
             $whatsappContent['messageMatcherId'] = self::getMessageMatcherIdBySubjectId($subjectId, 'podrobnosti') ?? 117216;
         }
@@ -267,9 +261,8 @@ class WhatsAppService
                 return null;
             } else {
                 self::handleErrorResponse($response);
-                return null;
             }
-        } catch (RequestException $e) {
+        } catch (Exception $e) {
             Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
             return null;
         }
@@ -307,9 +300,8 @@ class WhatsAppService
                 return null;
             } else {
                 self::handleErrorResponse($response);
-                return null;
             }
-        } catch (RequestException $e) {
+        } catch (Exception $e) {
             Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
             return null;
         }
@@ -355,9 +347,8 @@ class WhatsAppService
                 return null;
             } else {
                 self::handleErrorResponse($response);
-                return null;
             }
-        } catch (RequestException $e) {
+        } catch (Exception $e) {
             Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
             return null;
         }
@@ -376,13 +367,122 @@ class WhatsAppService
         $errorDetail = $data['detail'] ?? '';
 
         $errorMessages = [
+            // Ошибки из других методов API:
             'error-subject-unknown' => 'Указанное имя подписи отсутствует.',
-            'error-syntax' => 'Неверно указан тип канала.',
+            'error-syntax'          => 'Неверно указан тип канала.',
+            // Дополнительные ошибки для метода messages/history:
+            'Api-key not found'                     => 'Указан неверный API-ключ.',
+            'not-valid-request'                     => 'Указано пустое значение параметра address.',
+            'limit-not-valid'                       => 'Указано значение больше 1000 в параметре limit.',
+            'date-range-not-valid'                  => 'Диапазон значений между параметрами dateFrom и dateTo превышает 366 дней.',
+            'message-matcher-subject-not-found'     => 'Указан неверный идентификатор в значении параметра subjectId.',
         ];
 
         $errorMessage = $errorMessages[$errorKey] ??
             "Ошибка: " . $response->getStatusCode() . ". " . $errorKey . ". " . $errorDetail;
+
         Yii::error($errorMessage);
+        throw new Exception($errorMessage);
+    }
+
+    /**
+     * Получает историю исходящих сообщений по каналу WHATSAPP.
+     *
+     * @param array $params Массив параметров запроса, например:
+     * [
+     *    "offset" => 0,
+     *    "limit" => 1000,
+     *    "channelTypes" => ["WHATSAPP"],
+     *    "direction" => "OUT",
+     *    "dateFrom" => "2025-03-05T00:00:00Z",
+     *    "dateTo" => "2025-03-05T23:59:59Z",
+     *    "sort" => [
+     *         [
+     *             "property" => "messageId",
+     *             "direction" => "DESC"
+     *         ]
+     *    ],
+     *    "subjectId" => 11374
+     * ]
+     *
+     * @return array Массив сообщений, если запрос выполнен успешно.
+     *
+     * @throws \Exception При ошибке выполнения запроса.
+     */
+    public static function getMessagesHistoryByDate($params)
+    {
+        $apiKey = Yii::$app->params['WHATSAPP_API_KEY'];
+        $client = new Client();
+        $url = self::$apiBaseUrl . '/messages/history';
+
+        try {
+            $response = $client->request('POST', $url, [
+                'headers' => [
+                    'Content-Type' => 'application/json',
+                    'X-API-KEY'    => $apiKey,
+                ],
+                'json' => $params,
+            ]);
+
+            if ($response->getStatusCode() == 200) {
+                $data = json_decode($response->getBody(), true, 512);
+                Yii::error("Контент 200: " . json_encode($data, JSON_PRETTY_PRINT));
+                return $data['content'];
+            } else {
+                Yii::error("Контент: " . json_encode($response, JSON_PRETTY_PRINT));
+                self::handleErrorResponse($response);
+            }
+        } catch (Exception $e) {
+            Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
+            return [];
+        }
+    }
+
+    public static function processMessagesHistoryAndUpdateStatuses($params)
+    {
+        $messagesHistory = self::getMessagesHistoryByDate($params);
+        if (!is_array($messagesHistory)) {
+            Yii::error("Некорректный формат истории сообщений");
+            return false;
+        }
+
+        foreach ($messagesHistory as $message) {
+            if (
+                isset($message['address']) &&
+                isset($message['sentOrReceivedAt']) &&
+                isset($message['deliveryStatus'])
+            ) {
+                $phone = $message['address'];
+                try {
+                    $dateTime = new \DateTime($message['sentOrReceivedAt']);
+                } catch (\Exception $e) {
+                    Yii::error("Ошибка преобразования даты из сообщения: " . $e->getMessage());
+                    continue;
+                }
+                $dateTime->modify('+3 hours');
+                $formattedTime = $dateTime->format('Y-m-d H:i');
+
+                $record = UsersWhatsappMessage::find()
+                    ->where(['phone' => $phone])
+                    ->andWhere("TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI') = :sentTime", [':sentTime' => $formattedTime])
+                    ->one();
+
+                if ($record) {
+                    $record->status = $message['deliveryStatus'];
+                    if (!$record->save(false)) {
+                        Yii::error("Не удалось сохранить статус для сообщения с телефоном {$phone} и временем {$formattedTime}");
+                    } else {
+                        Yii::warning("Статус для сообщения с телефоном {$phone} успешно обновлён на {$message['deliveryStatus']}");
+                    }
+                } else {
+                    Yii::warning("Запись с телефоном {$phone} и временем {$formattedTime} не найдена.");
+                }
+            } else {
+                Yii::warning("Некорректный формат сообщения: " . json_encode($message));
+            }
+        }
+
+        return true;
     }
 
-}
\ No newline at end of file
+}
index 0e26694092819033262a4a231069dbe0be64f560..8c53a3879bf8a5a3daf54e98054ef2b46903555e 100644 (file)
@@ -26,6 +26,17 @@ $this->registerCss('
 
 ?>
 
+<style>
+    [data-bs-toggle="collapse"][aria-expanded="true"] .collapse-arrow {
+        transform: rotate(90deg);
+    }
+    .collapse-arrow {
+        transition: transform 0.3s ease;
+        display: inline-block;
+        margin-right: 8px;
+    }
+</style>
+
 <div class="usersMessageManagementIndex m-5">
 
     <?php if (Yii::$app->session->hasFlash('error')): ?>
@@ -152,7 +163,7 @@ $this->registerCss('
                 </div>
                     <div class="row">
                     <div class="col-4 mt-2">
-                        Второй этап
+                        Второй этап [StepTwoDaysBeforeTarget]
                     </div>
                     <div class="col-8">
                         <div class="d-inline-block">
@@ -258,7 +269,28 @@ $this->registerCss('
                         <?= $form->field($model, 'offer_whatsapp')
                             ->textarea(['rows' => 7])
                             ->hint('Используйте [NumberOfBonuses] для отображения количества бонусов.
-                             <br>Для даты окончания действия бонусов - [ValidityOfBonuses].') ?>
+                             <br>Для даты окончания действия бонусов - [ValidityOfBonuses].
+                             <br>Для дней до окончания акции [StepTwoDaysBeforeTarget]') ?>
+                        <!-- Блок настроек WhatsApp -->
+                        <div class="card mt-4">
+                            <div class="" data-bs-toggle="collapse" data-bs-target="#whatsAppSettings" aria-expanded="false" aria-controls="whatsAppSettings" style="cursor:pointer;">
+
+                                <p class="mb-0"> <span class="collapse-arrow">&#9654;</span> Настройки вотсап</p>
+                            </div>
+                            <div id="whatsAppSettings" class="collapse">
+                                <div class="card-body">
+                                    <?= $form->field($model, 'channel_name')->textInput()->label('Имя канала') ?>
+                                    <?= $form->field($model, 'channel_id')->textInput()->label('Идентификатор канала - подпись') ?>
+                                    <?= $form->field($model, 'channel_limit')->textInput(['type' => 'number'])->label('Суточный лимит сообщений') ?>
+                                    <?= $form->field($model, 'cascade_name')->textInput()->label('Имя каскада') ?>
+                                    <?= $form->field($model, 'cascade_id')->textInput(['type' => 'number'])->label('ID каскада') ?>
+                                    <?= $form->field($model, 'subject_id')->textInput(['type' => 'number'])->label('Идентификатор подписи') ?>
+                                    <?= $form->field($model, 'template_name')->textInput()->label('Имя шаблона') ?>
+                                    <?= $form->field($model, 'template_id')->textInput(['type' => 'number'])->label('Идентификатор шаблона') ?>
+                                    <?= $form->field($model, 'callback_status_url')->textInput()->label('URL приема колбеков статусов сообщений') ?>
+                                </div>
+                            </div>
+                        </div>
                     </div>
                     <div class="col-6">
                         <?= $form->field($model, 'offer_text')