This repository has been archived on 2024-10-16. You can view files and clone it, but cannot push or open issues or pull requests.
skillparts/mirzaev/skillparts/system/controllers/SearchController.php

400 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace app\controllers;
use yii;
use yii\web\Controller;
use yii\web\Response;
use app\models\Account;
use app\models\Product;
use app\models\Supply;
use app\models\Search;
use app\models\connection\Dellin;
use app\models\Settings;
use Exception;
class SearchController extends Controller
{
/**
* @todo Сессию привязать к аккаунту и проверку по нему делать, иначе её можно просто сбрасывать
*/
public function actionIndex(): array|string
{
// Инициализация параметров
$auth_only = false;
if ($auth_only && yii::$app->user->isGuest) {
// Если активирован режим "Поиск только аутентифицированным" и запрос пришел не от аутентифицированного
// Запись кода ответа: 401 (необходима авторизация)
yii::$app->response->statusCode = 401;
// Переход к концу обработки
goto skip_search;
}
// Инициализация
$query = yii::$app->request->post('request') ?? yii::$app->request->get('q');
if (yii::$app->request->post('type') === 'product' || yii::$app->request->get('type') === 'product') {
// Поиск по продуктам
if (yii::$app->request->post('history')) {
// Запрошена история
yii::$app->response->format = Response::FORMAT_JSON;
return [
'panel' => $this->renderPartial('/search/panel', ['history' => true]),
'_csrf' => yii::$app->request->getCsrfToken()
];
}
// Инициализация сессии
$session = yii::$app->session;
// Инициализация ответа
$response = null;
// Инициализация параметров
$timer = 0;
// Период пропуска запросов (в секундах)
$period = 3;
$keep_connect = false;
$sanction = false;
$sanction_condition = ($session['last_request'] + $period - time()) < $period;
$sanction_time = 2;
$query_min = 2;
$query_max = 20;
if (isset($session['last_request'])) {
// Данные о времени последнего запроса не найдены
if ($sanction && $sanction_condition) {
// Наказание за повторный запрос при условии задержки
$session['last_request'] += $sanction_time;
}
} else {
// Это первый запрос
// Инициализация
$session['last_request'] = time();
// Пропуск проверок
goto first_request;
}
// Метка: "Повтор обработки поиска" (для создания непрерывного соединения)
keep_connect_wait:
// Запись времени последнего запроса и вычисление об истечении таймера
$timer = $session['last_request'] + $period - time();
if ($timer > 0) {
// Время блокировки не истекло
// Запись кода ответа: 202 (запрос не обработан)
yii::$app->response->statusCode = 202;
$return = [
'timer' => $timer,
'panel' => $this->renderPartial('/search/loading'),
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// Запрос
// Очистка времени последнего запроса
unset($session['last_request']);
// Метка: "Первый запрос"
first_request:
if (strlen($query) < $query_min) {
// Выход за ограничения длины с недостатком
// Пропуск поиска
goto skip_query;
} else if (strlen($query) > $query_max) {
// Выход за ограничения длины с превышением
// Пропуск поиска
goto skip_query;
}
// Запись в историю
Search::write($query);
$limit = yii::$app->request->isPost ? 10 : 20;
if ($response = Product::searchByPartialCatn($query, $limit, [
'_key' => '_key',
'catn' => 'catn',
'name' => 'name',
// Баг с названием DESC
'dscr' => 'dscr',
'catg' => 'catg',
'imgs' => 'imgs',
'prod' => 'prod',
'dmns' => 'dmns',
])) {
// Данные найдены по поиску в полях Каталожного номера
foreach ($response as &$row) {
// Перебор продуктов
// Поиск поставок привязанных к продуктам
$connections = Supply::searchByEdge(
from: 'product',
to: 'supply',
edge: 'supply_edge_product',
limit: 11,
direction: 'OUTBOUND',
subquery_where: [
['product._key' => $row['_key']],
['supply.catn == product.catn'],
['supply_edge_product.type' => 'connect']
],
where: 'supply._id == supply_edge_product[0]._from',
select: '{supply, supply_edge_product}'
);
// Инициализация буфера
$buffer_connections = [];
if (count($connections) === 11) {
// Если в базе данных хранится много поставок
// Инициализация
$row['overload'] = true;
}
foreach ($connections as &$connection) {
// Перебор поставок
// Инициализация аккаунта
$connection['account'] = Account::searchBySupplyId($connection['supply_edge_product'][0]['_from']);
// Инициализация продукта
$connection['product'] = Product::searchBySupplyId($connection['supply_edge_product'][0]['_from']);
try {
// Инициализация данных геолокации
try {
$from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? Settings::search()->delivery_from_default ?? 36;
} catch (Exception $e) {
$from = (int) Settings::search()->delivery_from_default ?? 36;
}
try {
$to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36;
} catch (Exception $e) {
$to = 36;
}
if (
($buffer_connection = $connection['product']['bffr']["$from-$to"] ?? false)
&& time() < $buffer_connection['expires']
) {
// Найдены данные доставки в буфере
// и срок хранения не превышен, информация актуальна
// Запись в буфер вывода
$connection['delivery'] = $buffer_connection['data'];
$connection['delivery']['type'] = 'auto';
} else {
// Инициализация инстанции продукта в базе данных
$product = Product::searchByCatn($connection['product']['catn']);
// Инициализация доставки Dellin (автоматическая)
$product->bffr = [
"$from-$to" => [
'data' => $connection['delivery'] = Dellin::calcDeliveryAdvanced(
$from,
$to,
(int) ($connection['product']['wght'] ?? 0),
(int) ($connection['product']['dmns']['x'] ?? 0),
(int) ($connection['product']['dmns']['y'] ?? 0),
(int) ($connection['product']['dmns']['z'] ?? 0)
),
'expires' => time() + 86400
]
] + ($product->bffr ?? []);
$connection['delivery']['type'] = 'auto';
// Отправка в базу данных
$product->update();
}
} catch (Exception $e) {
$connection['delivery']['error'] = true;
// var_dump($e->getMessage());
// var_dump($e->getTrace());
// var_dump($e->getFile());
// var_dump(json_decode($e->getMessage(), true)['errors']);
// die;
}
// Инициализация цены (цена поставки + цена доставки + наша наценка)
$connection['cost'] = ($cost['ЦенаЗаЕдиницу'] ?? $connection['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);
// Инициализация версии для рассчета доставки по воздуху
$buffer_delivery_avia = $connection;
try {
// Инициализация данных геолокации
try {
$from = (int) $buffer_delivery_avia['account']['opts']['delivery_from_terminal'] ?? Settings::search()->delivery_from_default ?? 36;
} catch (Exception $e) {
$from = (int) Settings::search()->delivery_from_default ?? 36;
}
try {
$to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36;
} catch (Exception $e) {
$to = 36;
}
if (
($buffer_connection = $buffer_delivery_avia['product']['bffr']["$from-$to-avia"] ?? false)
&& time() < $buffer_connection['expires']
) {
// Найдены данные доставки в буфере
// и срок хранения не превышен, информация актуальна
// Запись в буфер вывода
$buffer_delivery_avia['delivery'] = $buffer_connection['data'];
$buffer_delivery_avia['delivery']['type'] = 'avia';
} else {
// Инициализация инстанции продукта в базе данных
$product = Product::searchByCatn($buffer_delivery_avia['product']['catn']);
// Инициализация доставки Dellin (автоматическая)
$product->bffr = [
"$from-$to-avia" => [
'data' => $buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced(
$from,
$to,
(int) ($buffer_delivery_avia['product']['wght'] ?? 0),
(int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0),
(int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0),
(int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0),
avia: true
),
'expires' => time() + 86400
]
] + ($product->bffr ?? []);
$buffer_delivery_avia['delivery']['type'] = 'avia';
// Отправка в базу данных
$product->update();
}
} catch (Exception $e) {
$buffer_delivery_avia['delivery']['error'] = true;
// var_dump($e->getMessage());
// var_dump($e->getTrace());
// var_dump($e->getFile());
// var_dump(json_decode($e->getMessage(), true)['errors']);
// die;
}
if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) {
// Если рассчиталась доставка самолётом
// Инициализация цены (цена поставки + цена доставки + наша наценка)
$buffer_delivery_avia['cost'] = ($cost['ЦенаЗаЕдиницу'] ?? $buffer_delivery_avia['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0);
// Запись в буфер
$buffer_connections[] = $buffer_delivery_avia;
}
}
// Запись обработанных данных
$row['supplies'] = array_merge($connections, $buffer_connections);
}
// Запись ответа
$return = [
'panel' => $this->renderPartial('/search/panel', compact('response')),
'_csrf' => yii::$app->request->getCsrfToken()
];
if ((int) yii::$app->request->post('advanced')) {
// Полноценный поиск
// Запись ответа
$return['main'] = $this->renderPartial('/search/index', compact('response'));
$return['hide'] = 1;
$return['redirect'] = '/search?type=product&q=' . $query;
}
} else {
// Данные не найдены
// Запись кода ответа: 404 (запрашиваемые данные не найдены)
yii::$app->response->statusCode = 404;
}
}
// Метка: "Пропуск запроса" (для пропуска самого поиска и возврата ответа)
skip_query:
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return $return ?? [
'panel' => $this->renderPartial('/search/panel'),
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// GET-запрос
if (empty($return['main']) && $keep_connect && $timer > 0) {
// Режим непрерывного соединения
// Ожидание
sleep($timer);
// Повтор обработки
goto keep_connect_wait;
}
$advanced = true;
return $this->render('/search/index', compact('response', 'timer', 'advanced'));
}
}
// Метка: "Пропуск обработки"
skip_search:
if (yii::$app->request->isPost) {
// POST-запрос
yii::$app->response->format = Response::FORMAT_JSON;
return [
'panel' => $this->renderPartial('/search/panel'),
'hide' => (int) $auth_only,
'_csrf' => yii::$app->request->getCsrfToken()
];
} else {
// GET-запрос
return $this->render('/search/index');
}
}
}