Альтернативный способ передачи произвольных параметров между процедурами/сеансами/фоновыми заданиями




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2025-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

23 Comments

  1. Goleff74
    Процедура УстановитьЗначениеДопПараметраПроцедурИФункций(ИмяПараметра, ЗначениеПараметра) Экспорт
    
    НаборЗаписей = РегистрыСведений.ДополнительныеПараметрыПроцедурИФункций.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.ИмяПараметра.Установить(ИмяПараметра);
    
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.ИмяПараметра = ИмяПараметра;
    НоваяЗапись.ЗначениеПараметра = ЗначениеПараметра;
    
    НаборЗаписей.Записать();
    
    КонецПроцедуры

    Показать

    Так, пожалуй, красивее будет 🙂

    Reply
  2. PerlAmutor

    Я подобную задачу решал иначе. В БСП есть справочник ДополнительныеОтчетыИОбработки, в каждой обработке можно хранить любые собственные настройки. Соответственно сначала туда записываются параметры, а потом туда же возвращается результат.

    Reply
  3. surikateg

    Чем плохо СохранитьЗначение(<Имя>, <Значение>) , ВосстановитьЗначение(<Имя>) ? Нет привязки к постоянно изменяющемуся бсп.

    Reply
  4. Skin123

    (1). Да, точно красивее:) Спасибо!

    Reply
  5. Skin123

    (2) Интересная задумка

    Reply
  6. KilloN

    (3) СохранитьЗначение(<Имя>, <Значение>) , ВосстановитьЗначение(<Имя>) только для толстого клиента.

    Лучше использовать ХранилищеОбщихНастроек.Сохранить, Загрузить, Удалить

    Reply
  7. PerlAmutor

    (6) Пока не пробовал. В качестве минуса наверное можно выделить то, что эти настройки засоряют базу, т.к. при удалении обработки данные там остаются.

    Reply
  8. KilloN

    (7) Да все верно, поэтому нужно вызывать Удалить, или сразу после загрузки, или перед завершением сеанса через функцию ХранилищеОбщихНастроек.ПолучитьСписок(…) очищаем все настройки

    Reply
  9. Altair777

    Это однопользовательский вариант? Так имя параметра быстрее всего одинаковое для документов одного вида, то коллизии будут возникать с вероятностью чуть менее, чем полностью 🙂

    Reply
  10. Skin123

    (9) Хм, а ведь и правда, в случае, если два пользователя решат одновременно заполнить два разных документа, то коллизия неизбежна. По идее тут нужно еще одно измерение создавать, которое будет отвечать за уникальность значения данного параметра. Подумаю на эту тему, спасибо.

    Reply
  11. Altair777

    (10) достаточно ссылки на документ (?)

    Reply
  12. Skin123

    (8), с хранилищем общих настроек хорошая идея, но они же вроде сохраняются для определенного пользователя? В таком случае, как узнать в фоновом задании настройки какого пользователя читать?

    Reply
  13. Skin123

    (11) В общем случае ссылки на документ будет достаточно, ну или как вариант уникального идентификатора, чтобы не привязываться к типу объекта. Однако, в моем случае в процедуре, в которую нужно было передать дополнительный параметр не было ссылки на заполняемый объект, поэтому тут и могут возникнуть сложности в определении значения параметра

    Reply
  14. sergathome

    В общем случае метод годится только для случая, когда им пользуется только один сеанс. То есть не годится даже для регламентных заданий ;)) ИД сеанса нужно в измерения добавлять, как минимум. И передавать этот ИД в фоновое задание стандартным способом. Выигрыш только в том, что можно одним параметром передать сразу много, но что мешает передать структуру сразу ?

    Вобсчем поржал. Спасибо.

    Reply
  15. KilloN

    (10)

    Reply
  16. KilloN

    (9)

    Это однопользовательский вариант? Так имя параметра быстрее всего одинаковое для документов одного вида, то коллизии будут возникать с вероятностью чуть менее, чем полностью 🙂

    Не будут, тк. хранилище работает, если не указывать конкретного пользователя, для каждого пользователяотдельно!

    Reply
  17. Altair777

    (16) а разве в публикации говорилось о хранилище?!

    Не морочьте людям голову

    Reply
  18. Goleff74

    (13)

    Почему проблемы?

    Ключ = Строка(Новый УникальныйИдентификатор)

    Параметр по этому ключу в РС. И фоновое задание запускать с этим ключом.

    Reply
  19. VmvLer

    Относительно фоновых заданий хранилище имеет только обратную связь, т.е.

    при запуске фонового задания в него можно передать адрес хранилища и после завершения фонового задания в точке его

    запуска получить значения из хранилища.

    Прямая связь не работает, т.е. поместить в хранилище значение, передать в фоновое адрес и в задании получить значение по этому адресу невозможно.

    Именно поэтому возникают задачи как в (0)

    Я считаю, что с броским названием тс перегнул, ибо

    создать регистр хранения в конфигурации — это банальное решение в лоб не претендующее на рациональную идею и альтернативу. это костыль

    Reply
  20. Goleff74

    Ну, можно поизвращаться.

    На клиентском компутере:

    1. Получаем новый УИД.

    2. Блокируем по нему исключительно запись в РС.

    3. Запускаем фоновое задание, в которое передаем-таки этот уид (только в функцию самого задания).

    4. В бесконечном цикле (можно с паузой) опрашиваем фоновое задание на предмет сообщений пользователю.

    5. Как только появляется сообщение в РС фигачим по номеру в этом собщении необходимый параметр и снимаем блокировку с записи РС из п.2.

    В функции ФЗ:

    1. Получаем номер сеанса.

    2. Создаем сообщение пользователю, в которое добавляем этот номер сеанса.

    3. Делаем запрос к РС по УИДу преданному в качестве параметра ФЗ.

    4. Ждем на блокировке…

    5. Выполняем свой код. В функции, где нужен параметр с клиента обращаемся в РС по НомерСеансаИнформационнойБазы().

    6. Профит

    PS Ну, и не забываем, где надо, транзакции всунуть, чтоб блокировка отработала.

    PPS Но лучше, каеш, параметр протолкнуть по функциям 🙂

    Reply
  21. VmvLer

    чтобы не изобретать велосипед советую использовать БСП

    ДлительныеОперации.ЗапуститьВыполнениеВФоне()

    Reply
  22. DarkAn

    (12) Вот тут и тут я как раз этим и пользуюсь 🙂

    Reply
  23. ybatiaev

    Добрый день!

    Немного поздновато, но всё же.

    У меня архиватор баз, где все необходимые процедуры выполняется в фоне для одного «робота», а другие пользователи просто читаю что-то.

    Вот «пухнет» база, я так понял что постоянная перезапись данных идёт.

    Сделано через хранилище данных. Весь результат «робот» складывает в хранилище, к нему все подключаются и читают данные, которые отображаются на главной форме любого пользователя (ЗначениеВДанныеФормы()) с определённой для клиента периодичностью. Тут получается что нет никаких справочников, регистров и т.п.

    Можно сделать через параметры сеанса, где данные тоже в виде хранилища. Вот данные хранилища будет оценивать «робот» и «распихивать» по таблицам (справочники, регистры). А у других пользователей на форму просто будет выведены стандартные динамические списки.

    Кто сталкивался? Как правильнее сделать? Главный вопрос, на который я не нашёл ответа — при перезаписи хранилища каким образом данные заносятся в ТЧ СУБД. К примеру, старое хранилище с ТЧ помечается не актуальным, а новая информация ДОПИСЫВАЕТСЯ. Или в то же физическое пространство перезаписывается информация?

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *