Обмен данными через Web Сервисы




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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='\

29 Comments

  1. Boyborodin

    Советую делать один параметр входящий типа string и исходящий такой же. В строку кидать текст JSON. В JSON можно запаковывать любое количество параметров, даже таблицы значений. Без муторной работы с XDTO, а сама запаковка и распаковка занимает несколько строк кода. Так же если при доработке придется добавлять входные/выходные параметры сам web-сервис можно уже не перенастраивать.

    Reply
  2. user5300

    (1) Хорошая идея! Спасибо за совет !)

    Reply
  3. AlX0id

    (1)

    Тут только один нюанс — в пользу использования того же XDTO — не будет автоматической проверки корректности того, что же вы запихали в этот JSON..

    В принципе, сам использую похожий способ — для работы с внешними источниками — на вход беру строки xml, кодированные в base64. Не очень красиво, зато внешним кодерам удобно и понятно.

    Reply
  4. Fox-trot
    каталог базы должен иметь полные права

    как тебя понимать, саид? (с)

    Reply
  5. user5300

    (4) Забыл…, если база файловая- каталог базы должен иметь полные права

    Reply
  6. Boyborodin

    (3)

    Запаковываем

      ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    СериализаторXDTO.ЗаписатьJSON(ЗаписьJSON, СтруктураДанных, НазначениеТипаXML.Явное);
    StringJS = ЗаписьJSON.Закрыть();

    Распаковываем

     ЧтениеJS = Новый ЧтениеJSON;
    ЧтениеJS.УстановитьСтроку(StringJS);
    СтруктураДанных = СериализаторXDTO.ПрочитатьJSON(ЧтениеJS);
    ЧтениеJS.Закрыть();

    JSON автоматически всё сериализует. Удобнее чем XML. Можно еще пожать, если данные объемные.

    Reply
  7. user5300

    (6) Кстати, а если структура баз отличается ? Допустим если выгрузить физ лица с ЗУП в УТ, у них же разные структуры… Реквизиты и тд., Ошибки не будет ?)

    Reply
  8. Boyborodin

    (7)

    Ошибка будет. Тем более такие обмены обычно делают в нетиповых конфигурациях. Для типовых есть типовые инструменты. В структуру лучше кидать примитивные параметры: дата, число, строка, или таблицу значений из примитивных данных.

    Для вашего примера:

    СтруктураДанных = Новый Структура;
    СтруктураДанных.Добавить(«КонецПериода», КонецПериода);
    СтруктураДанных.Добавить(«НачалоПериода», НачалоПериода);
    СтруктураДанных.Добавить(«Склад», Склад);

    И не забыть после успешного завершения процедуры вернуть какой-нибудь признак что всё прошло хорошо.

    Reply
  9. Fox-trot

    (5) может все же пользователь должен иметь полные права? тогда какой пользователь?

    Reply
  10. AlX0id

    (6)

    Я не про удобство — автоматического контроля состава данных внутри JSON нет.

    XML, кстати, я в итоге разбираю с помощью XDTO все равно.

    Reply
  11. user5300

    (9)

    Reply
  12. ellavs

    Довольно часто пользуемся подобным функционалом для обмена данными между базами (только используем HTTP-сервисы). Это в основном, когда мы не хотим пускать пользователей в основную базу, но им нужно поработать с данными. Есть другая конфигурация, например, не содержащая ПДн, куда доступ предоставлен бОльшему количеству пользователей, и в ней получаем данные из основной БД через сервис.

    Reply
  13. logos

    А зачем в этой ситуации вообще SOAP? Зачитывание wsdl, сериализация в ХЗ, обратная десериализация. Про json уже написали, так давайте последовательно советовать и http-сервис. Передаем в одном из полей метод, в другом данные. Сериализация/десериализация отлично делается методами ПрочитатьJSON, ЗаписатьJSON. И никакого XDTO вообще не нужно.

    Reply
  14. EMelihoff

    у меня наверное профессиональная деградация, при виде переменной «Хранилище» думал минут 5 причём тут хранилище ))

    Reply
  15. fr13

    Методы, параметры веб-сервиса на кириллице.

    Логин и пароль также.

    Если дергать такой веб-сервис не из платформы 1С, то будут проблемы.

    Reply
  16. user5300

    (15) Это пример обмена между базами ) Конечно можно все переделать на латиницу для работы с другими сервисами )

    Reply
  17. Ndochp

    (13)ХДТО нужен исключительно для автоматической проверки входящих данный на соответствие формату. К джейсону такое универсально на коленке не прикрутить.

    Reply
  18. logos

    (17) Какая в данном сценарии может быть проверка? Описывается получение произвольных данных запросом (базовых типов). Считаю, что использование SOAP имеет смысл только при работе с «ненадежным» клиентом, от которого можно получить «непредсказуемый» input. А когда пишешь свои сервисы — это избыточная перестраховка, сильно усложняющая разработку.

    Reply
  19. Ndochp

    (18) У меня 8 видов баз под контролем. И они периодически обновляются. Вот тебе и источник непредсказуемого инпута.

    Ты же не будешь правила писать для превращения документа в джейсон, правильно? ты сделаешь платформенную сериализацию.

    А на стороне приёмника уже сделаешь какой-то алгоритм разбора, если базы не совпадают.

    И вот, то что писали в «Реквизит» начали писать в «Реквизит подробно», а в «Реквизит» стали писать укрупненные типы.

    В случае SOAP алгоритм на принимающей стороне долбанется от новых реквизитов в которые переложили нужные тебе данные и ты полезешь разбираться. А в разборе джейсона ты проигнорируешь «Реквизит подробно», привычно заберешь все из «Реквизит», и получишь неверный документ.

    Reply
  20. logos

    (19) Давайте всё таки на Вы. Если «левая рука» не знает, что делает правая — никто не запрещает использовать SOAP. Однако, в контексте публикации автора, http сервис и JSON сериализатор дадут более аккуратный и более быстрый код.

    Reply
  21. Ndochp

    (20) Давайте на Вы.

    Левая рука — Вы. Правая — 1С. Обмена между самописными базами я за свою карьеру не встречал, обычно нетеленка обменивается с доработанной типовой, или две доработанных между собой. В нетленка — 1С непредсказуемость только на стороне приёмника, а вот в 1С-1С — с обеих сторон.

    У автора вообще контекста нет. У него есть инструмент и пример использования. Ясно, что код без проверок будет быстрее и компактнее. Как и подшипник без смазки крутится быстрее. Только вот в реальной жизни подшипники смазывают, а в коде делают проверки. И SOAP даёт для них автоматизированный инструмент, а Json оставляет все на откуп пользователю.

    В итоге в реальности на до исходить из своего контекста — передача ограниченного числа слабосвязанных объектов легко делается на Jsonе, кусочек базы данных — надежнее на XDTO.

    Reply
  22. logos

    (21) Левая рука — клиент, правая — сервер. Я не вижу, где тут код от ООО «1С-Софт», в случае, если мы пишем свой веб-сервис. Если Вы предоставляете сервер сторонним клиентам — я обеими руками за SOAP, когда ошибка в рассинхронизации протокола — это просто ошибка разработчика(ов) информационной системы. Впрочем, настаивать не буду, хотите SOAP — я не в праве Вам этого запретить.

    Reply
  23. Ndochp

    (22) Код от 1С вот тут: СериализаторXDTO.ЗаписатьJSON (http://v8.1c.ru/o7/201501json/index.htm) Соответственно в поле «Значение» будет то, что написала 1С.

    Reply
  24. logos

    (23) А я предлагаю пользоваться другим сериализатором:

    Глобальный контекст (Global context)

    ЗаписатьJSON (WriteJSON)

    Reply
  25. Ndochp

    (24)

    В формате JSON допускается записывать только значения следующих типов:

    Строка,

    Число,

    Булево,

    Дата (преобразованная в строку),

    Массив,

    ФиксированныйМассив,

    Структура,

    ФиксированнаяСтруктура,

    Соответствие,

    ФиксированноеСоответствие.

    Успехов в передаче документов.

    Reply
  26. logos

    (25) Передаются структуры/соответствия и массивы. К этим типам приводятся необходимые получателю объекты. Вы же не будете утверждать, что XDTO (xsd) у Вас волшебным образом самостоятельно заполняется? Платформенным сериализатором многое не передать. В любом случае придётся заполнять «объект xdto».

    Reply
  27. Сто27001

    (4) Если с русского перевести на русский, то «каталог базы должен иметь полные права» означает, что пользователь ОС от имени которого запущены IIS или Apache, должен иметь полные права на каталог в котором расположена публикуемая файловая база.

    Reply
  28. alexeypenzin

    Статья отличная, но хочется продолжения.

    На практике, в основном, нужно выгружать в базу приемник только измененные объекты. Не совсем понятно как это реализовать с помощью веб-сервисов. Как в данном обмене применить планы обмена? Когда и где очищать таблицу измененных объектов?

    Коротко вопрос: На клиенте получили данные, как на сервере узнать что клиент принял данные и на сервере удалить данный объект из таблицы измененных объектов?

    Reply
  29. user5300

    (28) Спасибо, статья для общего ознакомления )

    Выгрузку и загрузку объектов можно осуществить разными способами, в основном используется чтениезапись xml между нетиповыми конфигурациями , далее можно прикрутить планы обмена под свои задачи. Для обмена между типовыми — придется использовать расширения.

    Функции веб сервиса выполняются на сервере, в случае возникновении ошибки — система сообщит об этом ) можно также добавить функцию проверки после записи либо сразу, например:

    Функция ЗаписьДокумента(Параметры)
    попытка
    док = новыйДокумент;
    Док.Записать(Проведение);
    Ответ = «Создан новый док: + Док.Ссылка;
    Исключение
    Ответ = «ошибка: »  + ОписаниеОшибки();
    КонецПопытки
    Возврат Ответ;
    Конецфункции
    

    Показать

    //Пример оповещения пользователя…

    Возможно в будущем будет разбор более конкретной задачи с примерами и обработками …)

    Reply

Leave a Comment

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