gifts2017

Веб-сервисы, что они и как их применить в реальных условиях

Опубликовал Дмитрий Шерстобитов (DitriX) в раздел Программирование - Практика программирования

Уже не раз я сталкивался с разного рода реализациями обмена при помощи веб-сервисов разными людьми, под разные задачи и т.д. Однако, все эти решения содержали ряд упущений, проблем, и не совсем верных подходов. По сему я решил написать "краткий" гайд о том, как и почему мы докатились до использования веб сервисов.

Веб-сервисы, что они и как их применить в реальных условиях.

Уже не раз я сталкивался с разного рода реализациями веб сервисов разными людьми, под разные задачи и т.д. Однако, все эти решения содержали ряд упущений, проблем, и не совсем верных подходов. По сему я решил написать краткий гайд о том, как и почему мы докатились до использования веб сервисов.

Ну пожалуй начнем немного с теории и того, как было раньше.

Была 1 центральная база (далее ЦБ) и был 1 интернет магазин (далее ИМ), и нам, как и всем смертным было необходимо выгружать туда остатки, цены и прочий хлам.

Ну пожалуй следует начать с того, как мы выгружали номенклатуру и ее описание, изначально мы просто раз в какой то период – выгружали файлик xml на ftp, этот период выбирал кто угодно, сайт его автоматом подхватывал и обновлял. Но, так как мы легких путей не ищем, было принято решение добавить в номенклатуру реквизит ДатаИзменения, и тогда выгружалась только та номенклатура, которая была изменена начиная с указанного периода.

Далее, мы раз в день выгружали полные остатки товара в файл и отправляли на фтп, и каждый час, стоял регламент, который выгружал такой же файлик, но только уже не остатков, а оборотов.

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

Далее открыли другие партнерские ИМ и с ними тоже надо было как то и что то делать.

В тот момент я уже поднатаскался в веб сервисах 1С и технологии соап в целом, и мы РЕШИЛИСЬ!

Ну во первых у нас возник вопрос – как выгрузить остатки, он решился довольно таки быстро, на стороне сайта подняли соап сервер и я к нему подключаясь через 1С слал строку, но строку не простую, а великолепную :).

Код этой злобной функции приведен ниже.

Однако, мы пока еще немного зайдем в глубь, и так, цель выгрузок для ИМ1 и ИМ2 – была разной, т.е. для ИМ1 выгружались все остатки по складам, а в случае ИМ2 – остатки нужны были в целом, без разбиения по складам.

Поэтому, для того что бы не плодить кучу функций, был добавлен параметр Номер, если Номер = 0, то это выгрузка была для ИМ1, если Номер = 1, то для ИМ2.

И та

//Выгрузка регламентная остатков, эта процедура вызывалась регламентом раз в сутки и отправляла данные в ИМ.
Процедура ВыгрузкаОстатковДляИнтернетМагазина() Экспорт
//Тут мы определяем цикл, т.е. функция выполняется столько раз, сколько ИМ магазинов у нас есть.
    Для Номер = 0 По 1 Цикл    
        Запрос = Новый Запрос;
        Если Номер = 0 Тогда
            Запрос.Текст =
            "ВЫБРАТЬ
            |    Склады.Ссылка
            |ИЗ
            |    Справочник.Склады КАК Склады
            |ГДЕ
            |    Склады.СтатусСклада = ЗНАЧЕНИЕ(Перечисление.СтатусСклада.Розничный)"
;
            Результат = Запрос.Выполнить();
            
            ВыборкаДетальныеЗаписи = Результат.Выбрать();
//Определяем переменную, которую будет отправлять на сервер
            хмл = "";
//Для случая ИМ1 мы выполняем отдельный запрос и отправку для каждого склада
            Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
                Попытка
//Пытаемся получить хмл строку для данного склада
                    хмл = ПолучитьХМЛ(ВыборкаДетальныеЗаписи.Ссылка, Номер);
                    Если хмл = Неопределено Тогда
                        
                    Иначе
//Если все успешно, то получаем структуру подключения, куда передаем номер ИМ и имя функции.
                        СтруктураДанных = ПолучитьСоединение("Prod_1c",Номер);
//Получили строку, теперь надо отправить данные по SOAP, тут передается три параметра на сервер
//1 – сама строка хмл
//2 – код склада
//3 – тип остатков, если это начальные остатки, то он равен «0», если это оборот – то «1»                        
                        Ответ = СтруктураДанных.ВСПрокси. Prod_1c(ФабрикаXDTO.Создать(СтруктураДанных.ТипыXDTO[0], хмл),
                        ФабрикаXDTO.Создать(СтруктураДанных.ТипыXDTO[1], СокрЛП(ВыборкаДетальныеЗаписи.Ссылка.Код)),
                        ФабрикаXDTO.Создать(СтруктураДанных.ТипыXDTO[2], "0"));
//Тут стоить понимать, что посылая данные, мы получаем и ответ, ответ может быть разным, для нас в одних случаях ответ должен быть «Success» или «Error», в других случаях, мы просто возвращали количество строк в таблице. Т.е. я например выгрузил 1000 строк в хмл, сайт отпарсил и отвечает мне, если ответ != 1000, то это считалось ошибкой, хотя не всегда, например был новый товар, которого в ИМ еще нету, или не должно быть, тогда это просто записывается в журнал и дальше просматривается в ручную.                        
                        Если Ответ = "Success" Тогда
                            //ЗаписьЖурналаРегистрации("Отправка остатков со склада, кол. символов = " + СтрДлина(хмл), УровеньЖурналаРегистрации.Информация, ,ВыборкаДетальныеЗаписи.Ссылка , Ответ);        
                        Иначе
                            ЗаписьЖурналаРегистрации("Отправка остатков со склада, кол. символов = " + СтрДлина(хмл), УровеньЖурналаРегистрации.Ошибка, ,ВыборкаДетальныеЗаписи.Ссылка , Ответ);
                        КонецЕсли;
                    КонецЕсли;
                Исключение
                    ТекстОшибки = ОписаниеОшибки();
                    ЗаписьЖурналаРегистрации("Отправка остатков со склада, кол. символов = " + СтрДлина(хмл), УровеньЖурналаРегистрации.Ошибка, ,ВыборкаДетальныеЗаписи.Ссылка , ТекстОшибки);        
                КонецПопытки;
                хмл = "";
            КонецЦикла;
            
        ИначеЕсли Номер = 1 Тогда
//В этом случае, цикла по складам не было, мы просто выгружаем остатки
            хмл = "";
            Попытка
                хмл = ПолучитьХМЛ("", Номер);
                Если хмл = Неопределено Тогда
                    
                Иначе
                    СтруктураДанных = ПолучитьСоединение("Prod_1c",Номер);
                    
                    Ответ = СтруктураДанных.ВСПрокси. Prod_1c(ФабрикаXDTO.Создать(СтруктураДанных.ТипыXDTO[0], хмл),
                    ФабрикаXDTO.Создать(СтруктураДанных.ТипыXDTO[1], ""),
                    ФабрикаXDTO.Создать(СтруктураДанных.ТипыXDTO[2], "0"));
                    
                    Если Ответ = "Success" Тогда
                        //ЗаписьЖурналаРегистрации("Отправка остатков со склада, кол. символов = " + СтрДлина(хмл), УровеньЖурналаРегистрации.Информация, ,ВыборкаДетальныеЗаписи.Ссылка , Ответ);        
                    Иначе
                        ЗаписьЖурналаРегистрации("Отправка остатков со склада, кол. символов = " + СтрДлина(хмл), УровеньЖурналаРегистрации.Ошибка, ,"ИМ" , Ответ);
                    КонецЕсли;
                КонецЕсли;
            Исключение
                ТекстОшибки = ОписаниеОшибки();
                ЗаписьЖурналаРегистрации("Отправка остатков со склада, кол. символов = " + СтрДлина(хмл), УровеньЖурналаРегистрации.Ошибка, ,"ИМ", ТекстОшибки);        
            КонецПопытки;
            хмл = "";
        КонецЕсли;
        
    КонецЦикла;
КонецПроцедуры

//Собственно сама функция для получения хмл
Функция ПолучитьХМЛ(Склад,Номер)    
//тут для каждого случая, мы создаем свою таблицу
    Если Номер = 0 Тогда
        Запрос = Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ
        |    ТоварыНаСкладахОстатки.Номенклатура.Код КАК nom_id,
        |    ТоварыНаСкладахОстатки.ХарактеристикаНоменклатуры.Наименование КАК size_name,
        |    ТоварыНаСкладахОстатки.КоличествоОстаток КАК count
        |ИЗ
        |    РегистрНакопления.ТоварыНаСкладах.Остатки(
        |            ,
        |            Склад = &Склад
        |                И Номенклатура.ВидНоменклатуры = &ВидНоменклатуры) КАК ТоварыНаСкладахОстатки"
);
        Запрос.УстановитьПараметр("Склад", Склад);
        Запрос.УстановитьПараметр("ВидНоменклатуры", Справочники.ВидыНоменклатуры.НайтиПоКоду("000000001"));
    ИначеЕсли Номер = 1 Тогда
        Запрос = Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ
        |    ТоварыНаСкладахОстатки.Номенклатура.Код КАК nom_id,
        |    ТоварыНаСкладахОстатки.ХарактеристикаНоменклатуры.Наименование КАК size_name,
        |    ТоварыНаСкладахОстатки.КоличествоОстаток КАК count
        |ИЗ
        |    РегистрНакопления.ТоварыНаСкладах.Остатки(
        |            ,
        |            Склад.родитель = &Склад
        |                И (Склад.СтатусСклада = ЗНАЧЕНИЕ(Перечисление.СтатусСклада.Розничный)
        |                    ИЛИ Склад.СтатусСклада = ЗНАЧЕНИЕ(Перечисление.СтатусСклада.Склад))) КАК ТоварыНаСкладахОстатки"
);
        Запрос.УстановитьПараметр("Склад", Справочники.Склады.НайтиПоКоду("00001"));
        
    КонецЕсли;
    Результат = Запрос.Выполнить().Выгрузить();
    Если Результат.Количество() = 0 Тогда
        Возврат Неопределено;
    КонецЕсли;
//Тут обратите внимание, я не создаю файл хмл,  я не занимаюсь собственной наработкой для создания хмл структуры, я использую сериализатор 1С
    ДеревоВОбъектеXDTO = СериализаторXDTO.ЗаписатьXDTO(Результат);
    МойXML = Новый ЗаписьXML;
    МойXML.УстановитьСтроку();
    
    ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
    ФабрикаXDTO.ЗаписатьXML(МойXML, ДеревоВОбъектеXDTO);
    
    xml = МойXML.Закрыть();
//Вот и все, теперь в xml находится хмл структура, в виде строки, которую я и возвращаю в первую функцию, и передаю потом на сервер сайта ИМ.
//А вот очень интересная функция, УбратьЛишнее, она чистит хмл структуру созданную 1С, убирая оттуда не нужные данные и символы, таким образом мы сжимаем файл в 3 – 5 раз, а на парсинг это никак не влияет.
    xml = УбратьЛишнее(xml);
    
    Возврат xml
    
КонецФункции

//Чистка хмл на лишний мусор
//А вот и сама функция
Функция УбратьЛишнее(xml)
    xml = СтрЗаменить(xml,"xsi:type=""xs:decimal""", "");
    xml = СтрЗаменить(xml,"xsi:type=""xs:string""", "");
    xml = СтрЗаменить(xml,"xsi:type=""xs:boolean""", "");
    xml = СтрЗаменить(xml,"xsi:type=""xs:dateTime""", "");
    xml = СтрЗаменить(xml,"T00:00:00", "");
    xml = СтрЗаменить(xml,"0001-01-01", "");
    xml = СтрЗаменить(xml,"<Value ", "<Vl");
    xml = СтрЗаменить(xml,"", "");
    xml = СтрЗаменить(xml,"false", "0");
    xml = СтрЗаменить(xml,"true", "1");
    xml = СтрЗаменить(xml,"xsi:type=""Null""", "");  
//Для красоты 1С рисует структуру при помощи табуляции, это удобно читать, то для парсинга – это мусор, поэтому мы убираем все лишние табуляции, что позволяет сократить объем данных в 2 раза
    xml = СтрЗаменить(xml,"    ","");
    Возврат xml
КонецФункции

//Куда же без функции подключения к соап серверу?
//Сюда мы передаем имя операции, которую нам надо вернуть в структуре и номер ИМ
Функция ПолучитьСоединение(Имя, НомерСоединения)
    СтруктураДанных = Новый Структура;
    Если НомерСоединения = 0 Тогда
        Пользователь = " Пользователь 1";
        Пароль = " Пароль 1";
        Адрес = "http:// 127.0.0.1/sync";
        URIПространстваИменСервиса = "URI";
        ИмяСервиса = " ИмяСервиса ";
        ИмяТочкиПодключения = " ИмяСервиса Soap";
    ИначеЕсли НомерСоединения = 1 Тогда
        Пользователь = " Пользователь 2";
        Пароль = " Пароль 2";
        Адрес = "http:// 127.0.0.1:12345/product";
        URIПространстваИменСервиса = "URI";
        ИмяСервиса = " ИмяСервиса ";
        ИмяТочкиПодключения = " ИмяСервиса Soap";
    ИначеЕсли НомерСоединения = 2 Тогда
        Пользователь = " Пользователь 3";
        Пароль = " Пароль 3";
        //Адрес = "http://127.0.0.1/IM/ws/SendUsers.1cws?wsdl";
        URIПространстваИменСервиса = "URI";
        ИмяСервиса = " ИмяСервиса ";
        ИмяТочкиПодключения = " ИмяСервиса Soap";
    КонецЕсли;
    
    ВСОпределение = Новый WSОпределения(Адрес, Пользователь, Пароль);
    
    ВСПрокси = Новый WSПрокси(ВСОпределение, URIПространстваИменСервиса, ИмяСервиса, ИмяТочкиПодключения) ;
    ВСПрокси.Пользователь = Пользователь;
    ВСПрокси.Пароль       = Пароль;
    ВСервис     = ВСОпределение.Сервисы.Получить(0);
    ВТочкаВхода = ВСервис.ТочкиПодключения.Получить(0);
    ВОперация     = ПоискФункции(ВТочкаВхода.Интерфейс.Операции, Имя);        
    
    ТипыXDTO = Новый Массив;
    Для Каждого Параметр Из ВОперация.Параметры Цикл
        _ТипXDTO = ФабрикаXDTO.Тип(Параметр.Тип.URIПространстваИмен, Параметр.Тип.Имя);
        ТипыXDTO.Добавить(_ТипXDTO);
    КонецЦикла;
    // ТипыXDTO – это массив параметров, в нашем случае он будет равен 3. Я специально не привязываюсь к имени параметров, так как тогда, нужно будет под каждую операцию писать свою функцию, а мне влом.
    СтруктураДанных.Вставить("ВСОпределение", ВСОпределение);
    СтруктураДанных.Вставить("ВСПрокси", ВСПрокси);
    СтруктураДанных.Вставить("ВСервис", ВСервис);
    СтруктураДанных.Вставить("ВТочкаВхода", ВТочкаВхода);
    СтруктураДанных.Вставить("ВОперация", ВОперация);
    СтруктураДанных.Вставить("ТипыXDTO", ТипыXDTO);
    
    Возврат СтруктураДанных;

//Тут по сути ничего менять не надо, кроме данных авторизации
КонецФункции

//Найдем нужную функцию
//Ну тут все просто, после того как вы получили коллекцию операций, мы ищем нужную нам операцию и возвращаем ее. Сделана для экономии пространства
Функция ПоискФункции(Операции, Имя)
    Для Каждого Операция ИЗ Операции Цикл
        Если Операция.Имя = Имя Тогда
            Возврат Операция
        КонецЕсли;
    КонецЦикла;
КонецФункции

 

В итоге – на сайт придет строка такого формата:

Изначально идет описание колонок ТЗ, где указывается имя колонки, длина символов (максимальная), тип данных и др.

Далее – последовательно идут теги row, это строка таблицы, и теги Vl, их порядок соответствует порядку колонок в заголовке. Если бы мы не сделали сжатие, то данных там было бы на порядок больше, но они были бы бесполезны.

 

<ValueTable xmlns="http://v8.1c.ru/8.1/data/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <column>
        <Name>nom_id>
        <ValueType>
        <Type>Null>
        <Type>xs:decimal>
        <NumberQualifiers>
        <Digits>11>
        <FractionDigits>0>
        <AllowedSign>Nonnegative>
        >
        >
        <Title>nom_id>
        <Width>11>
    >
    <column>
        <Name>size_name>
        <ValueType>
        <Type>xs:string>
        <Type>Null>
        <StringQualifiers>
        <Length>100>
        <AllowedLength>Variable>
        >
        >
        <Title>size_name>
        <Width>100>
    >
    <column>
        <Name>count>
        <ValueType>
        <Type>Null>
        <Type>xs:decimal>
        <NumberQualifiers>
        <Digits>38>
        <FractionDigits>0>
        <AllowedSign>Any>
        >
        >
        <Title>count>
        <Width>38>
    >
    <row>
        <Vl>95761>
        <Vl>40>
        <Vl>2>
    >
    <row>
        <Vl>95761>
        <Vl>41>
        <Vl>1>
    >
    <row>
        <Vl>95761>
        <Vl>42>
        <Vl>1>
    >
    <row>
        <Vl>95761>
        <Vl>44>
        <Vl>2>
    >
    <row>
        <Vl>95762>
        <Vl>40>
        <Vl>1>
    >
>
 
Часть xml тегов съел сайт, но тут главное суть :).

Теперь можно передавать данные на сайт, минуя всякие там фтп, файлы и прочую лабуду. Для пущей безопасности – у нас обмен данными идет внутри тунеля, так что просто так, со стороны, туда ничего не отправишь. Скорость обмена – выше всех похвал, таблица на 10 000 строк передается около 30 секунд, это с учетом времени запроса, формирования хмл, создания подключения, отправки данных, парсинг данных на стороне ИМ и получение ответа от сайта.

 

Ну как? Интересно? А может пойдем еще дальше? Мы же программисты 1С, а не веба? А может веб программист не хочет этого делать, такое тоже бывает, но вот у вас, допустим, есть франчайзы, и у вас общая дисконтная система, ну бывает же такое? Бывает. А база у них – своя, а вот дисконтов хочется видеть у себя, или вот вы сделали документ перемещения на них, а теперь надо что бы он попал в их базу, бывает? А для того что бы он выгрузился к ним в базу, нужно что бы у них была вся номенклатура? Ну без этого никак, а вдруг они сделают загрузят документ у себя, через файлик, а потом что то изменят, и начинается сверка, а там полная лажа. Тоже бывает.

 

И так, давайте поставим сами себе задачу – СДЕЛАТЬ ВСЕ КРАСИВО.

Ну вначале давайте определимся с тем, что есть красиво?

В нашем случае, это есть последовательная передача данных с базы в базу, логично?

И так, если мы хотим выгрузить документ перемещения в базу франчайзи, нам надо быть уверенными в том, что как минимум у них стоит 1С и включен сервер. Проверили? Ок. Следующая задача – убедиться что у них есть вся номенклатура, логично? А как мы можем быть в этом уверенными?

 

Тут стоит отметить, что я специально исключаю вредительство, т.е. если мы раз им выгрузили номенлатуру, то она никуда потом не девается :).

 

Но, давайте посмотрим на то, как и что делается в нашей базе.

Изначально, до создания документа – создается номенклатура, хотя надо предусмотреть вариант, когда создался документ, потом мы создали номенклатуру, а потом ее добавили в тот документ :). Но те кто с этим работает постоянно, знают как выкрутиться из этой ситуации.

 

И так, рисуется определенный алгоритм, а именно, вначале мы создаем номенклатуру, потом документ.

 

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

 

И так, выходом в моем случае – было создание регистра сведений Синхронизация, с вот такими вот полями:

Объект – это составной тип данных, в нашем случае указываем в нем Номенклатуру, ДокументПеремещения

ДатаДобавления - Дата

ДатаПопытки - Дата

ОписаниеОшибки - Строка

НомерСайта – число, хотите делайте строкой, дело ваше.

 

Тут все очень просто, это регистр сведений без подчинения, непериодический.

 

Далее, мы делаем подписку на события документа перемещения и номенклатуры.

Подписка работает в случае записи.

В подписке вот такой код:

 

Процедура ЗаписатьОбъектВОчередь(Объект) Экспорт
    Для Номер = 0 По 1 Цикл
        
        Нов = РегистрыСведений.СинхронизацияССайтом.СоздатьМенеджерЗаписи();
        Нов.Объект = Объект;
        Нов.ДатаДобавления = ТекущаяДата();
        Нов.НомерСайта = Номер;
        Попытка    
            Нов.Записать();
            //ЗаписьЖурналаРегистрации("Поставить в очередь", УровеньЖурналаРегистрации.Информация, ,Объект ,);        
        Исключение
            ЗаписьЖурналаРегистрации("Поставить в очередь", УровеньЖурналаРегистрации.Ошибка, ,Объект ,);        
        КонецПопытки;
        
    КонецЦикла;
КонецПроцедуры

 

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

 

И так, теперь в случае создания/изменения номенклатуры или документа, он попадет в этот регистр.

При чем в той же последовательности.

И так, вроде все идет хорошо, теперь же что то надо делать с этим регистром?

А делаем мы вот что, создаем регламент, у меня периодичность стоит 5 минут.

 

Процедура ПрочитатьОчередь() Экспорт
    Выборка = РегистрыСведений.СинхронизацияССайтом.Выбрать(,"ДатаДобавления");
    Успешно = Ложь;
    Пока Выборка.Следующий() Цикл
        Успешно = Ложь;
        Попытка
            Если ТипЗнч(Выборка.Объект) = Тип("СправочникСсылка.Номенклатура") Тогда
                Успешно = ВыгрузкаНоменклатуры(Выборка.Объект, Выборка.НомерСайта);
            ИначеЕсли ТипЗнч(Выборка.Объект) = Тип("ДокументСсылка.ПеремещениеТоваров") Тогда
                Успешно = ВыгрузкаСкидокДляИнтернетМагазина(Выборка.Объект, Выборка.НомерСайта);    
Иначе
            Успешно = Истина;
            КонецЕсли;
            
            Если Успешно Тогда
                Выборка.ПолучитьМенеджерЗаписи().Удалить();
            Иначе
                
                Запись = Выборка.ПолучитьМенеджерЗаписи();
                Запись.Прочитать();
                Запись.ДатаПопытки = ТекущаяДата();
                Запись.Записать();                
            КонецЕсли;
        Исключение
            
            ЗаписьЖурналаРегистрации("Отправка данных", УровеньЖурналаРегистрации.Ошибка, , Выборка.Объект, ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

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

 

Давайте отработаем ситуацию, когда у нас в очереди 2 Номенлатуры и 1 Документ с ними.

Допустим – первая прошла успешно и удалилась из регистра, а вот вторая, не прошла, например не удалось создать ее в базе приемнике, тогда эта запись не удалится, и документ не создается, так как не может найти эту номенлатуру, как только решат с ней проблему – трубы прорвет и все выгрузиться, но остальная очередь не пострадает.

 

Тут можно долго мудрить, у меня например в случае ошибки приходит сообщение, и запись помечается – как запись ошибочная и она не долбится постоянно в базу приемник, пока я не сниму эту метку.

Так же для выравнивания партионного учета, у нас есть специальный пользователь – если он проводит документ, то в регистр ничего не записывается.

На стороне базы приемника – есть система логов, которая логирует, когда и какой документ изменился и что в нем поменяли.

 

Тут можно думать долго и нудно, и каждый найдет свой путь просвещения.

 

Однако, на такой нотке было бы закончить совсем не весело, по этому я осмелюсь напомнить о вот таком куске кода:

ДеревоВОбъектеXDTO = СериализаторXDTO.ЗаписатьXDTO(Результат);
МойXML = Новый ЗаписьXML;
МойXML.УстановитьСтроку();
        
ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
ФабрикаXDTO.ЗаписатьXML(МойXML, ДеревоВОбъектеXDTO);
        
хмл = МойXML.Закрыть();
хмл = СтрЗаменить(хмл,"    ","");
 

 

Помните? Ну тот, который создает сам хмл структуру и она потом уходит на сайт. А давайте посмотрим, что можно придумать, если эта структура уходит в другую базу 1С?

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

А вот эта операция вернет вам таблицу значений, т.е. больше ничего парсить не надо, вы сериализовали таблицу товаров документа, передали в базу приемник и там же – десериализовали. И дальше работаете с обычной ТЗ.

Однако! Обратите внимание, что я вырезаль только табуляцию, так как остальные параметры нужны 1С для десериализации!

Правда если вы синхронизацию с базами делаете не через GUID, то в этом случае, я например, сериализую таблицу, но вместо ссылки на номенклатуру передаю код, в базе приемнике – я добавляю к ТЗ колонку Номенклатура, заполняю ее по коду, и просто загружаю в ТЗ документа, так как название колонок в таблицах совпадают.

 

Надеюсь это кому то помогло!

Тему о поднятии веб сервисов – я не рассматриваю, так как она уже достаточно разжевана и в моих и в других статьях.

Если есть вопросы – добро пожаловать в комментарии.

Очень приветствуется злобная критика. Я еще учусь :)

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Василий Казьмин (awk) 12.11.12 18:12
Причины почему поставил минус.
1. Непонятно, что и зачем выгружаем.
2. Запросы в цикле.
3. Смысл статьи непонятен.
3.1 Почему веб сервис лучше файлов - из статьи непонятно.
3.2 Зачем такие сложности если есть планы обмена - то же непонятно из статьи.
4. Смешаны гетерогенная среда с гомогенной.
2. Дмитрий Шерстобитов (DitriX) 12.11.12 19:21
(1)читаем статью :)
1.
Ну пожалуй следует начать с того, как мы выгружали номенклатуру и ее описание, изначально мы просто раз в какой то период – выгружали файлик xml на ftp, этот период выбирал кто угодно, сайт его автоматом подхватывал и обновлял. Но, так как мы легких путей не ищем, было принято решение добавить в номенклатуру реквизит ДатаИзменения, и тогда выгружалась только та номенклатура, которая была изменена начиная с указанного периода.

И таких примеров много, по сути всю статью надо копировать.
2.Эммм, что то я не понял вопроса, где вам там цикл не пришелся по вкусу?
Я таким образом решил проблему создания двух отдельных функций.
Если вы про цикл по складам, то это было обусловленно тем, что если один раз слать общий запрос - и вытаскивать результат, то массив данных был оооочень большой. И сайт мог не успеть быстро обработать все данные. Поэтому были разделены склады запросом. Можно было бы выполнить общий запрос, поместить в ВТ и работать с ней, или выгрузить в ТЗ, добавить индексы и там уже отбирать, но результат тестирования показал, что время затраченное на такой цикл, в сравнении с другими подходами, составлял менее 1%, поэтому, для удобства редактирования, был оставлен этот подход.
3. Из статьи этого и не должно быть понятно. Тут каждый делает так, как ему по душе, я привел аналог обмена без файлов.
3.1 Кроме этого, один из главных плюсов - мгновенный обмен данными любого количества. Мне интересно как вы построите обмен данными в онлайне при помощи файлов?
3.2 Да ну, настройте-ка план обмена между двумя абсолютно отличающимися базами, причем эти базы не есть дочерние, ну и подключите туда несколько ИМ, и это все в режиме онлайна. Если вы это сделаете - я вам сделаю низкий поклон.
Однако, объем данных значительный (около 100 000 позиций + размеры и прочее), в базах работает около 100 пользователей.
4. Это и была цель, показать - как можно с помощью веб сервисов решить проблему объединения кучи различных баз (1С - база ИМ), а так же преимущества, при объединении сходных баз (1С - 1С)

Надеюсь - я ответил на ваши вопросы, и вы можете изменить свое мнение о моей статье :)
3. Василий Казьмин (awk) 12.11.12 23:31
(2) DitriX,
1. "И таких примеров много.." И разбросаны по тексту, что не понять к чему что и почему.
2. "цикл не пришелся по вкусу?" Запрос не должен быть в цикле - точка. Это стандарт. В твоем случае делается запрос и обход выборки по группировкам.
Тут каждый делает так, как ему по душе,
Я обычно исходя из задач.
один из главных плюсов - мгновенный обмен данными любого количества
Не верю. Телепорт что ли?
Мне интересно как вы построите обмен данными в онлайне при помощи файлов?
Что есть онлайн?
настройте-ка план обмена между двумя абсолютно отличающимися базами
УТ и БП или ЗУП и БП подойдут? Если подойдут, жду поклона :D
Однако, объем данных значительный (около 100 000 позиций + размеры и прочее), в базах работает около 100 пользователей.
Это к чему?
Это и была цель, показать - как можно...
А получилась каша.

Да я и не сильно против. Сделать оглавление, подчистить код и будет вполне плюс.
4. А Р (p1l1gr1m) 13.11.12 02:07
Поставил плюс, однако верно сказал (3) awk - запрос нельзя выполнять в цикле, в данном случае нужно было выполнить запрос по массиву складов и далее, например, перебирать его по группировкам по складам. В Вашем случае запрос в цикле может и не сильно проигрывал в производительности, но в общем случае, чем больше итераций выполнения запроса в цикле, тем хуже его производительность, поэтому надо делать изначально правильно.
Далее, все то, для чего Вы создаете регистр для синхронизации, код создающий/удаляющий оттуда записи и т.д, делается с помощью планов обмена.
Но что касается части работы с соап - однозначный плюс.
5. Андрей Скляров (coder1cv8) 13.11.12 10:55
Автор, Вы не обижайтесь, правильно выше говорят, Ваши решения очень ...мягко говоря... спорные. Это не пример красивого решения, это набор каких-то личных велосипедов с квадратными колесами местами. Нет планов обмена, методология хромает на обе ноги, вся суть и привлекательность SOAP сервисов в объектном подходе, а Вы данные XML-строкой передаете (если я правильно понял).
6. Никита Коротаев (bforce) 13.11.12 11:40
Интересно, народ вообще в коде разбирался?
Где там запрос в цикле? Цикл состоит из 2-х итераций, причем на каждой из них выполняется своя ветка. Такое ощущение, что просмотрели статью бегло.

Автор предложил вполне жизнеспособное решение. Мы сами используем обмен с 2 сайтами и одним приложением для работы в оффлайн. Для всего используем планы обмена, однако, скорость обмена просто ужасающая. Попробуем у себя переписать также (можно даже к плану обмена) и проверить результат.

ИМХО, в рамках этой статьи можно обойтись без плана обмена. Сама идея понятна и проста без всяких излишеств.
7. Василий Казьмин (awk) 13.11.12 12:32
(6) bforce,
Где там запрос в цикле?

/Для случая ИМ1 мы выполняем отдельный запрос и отправку для каждого склада
            Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
                Попытка
//Пытаемся получить хмл строку для данного склада
                    хмл = ПолучитьХМЛ(ВыборкаДетальныеЗаписи.Ссылка, Номер);

.....

//Собственно сама функция для получения хмл
Функция ПолучитьХМЛ(Склад,Номер)    
//тут для каждого случая, мы создаем свою таблицу
    Если Номер = 0 Тогда
        Запрос = Новый Запрос("ВЫБРАТЬ РАЗРЕШЕННЫЕ

...Показать Скрыть

ощущение, что просмотрели статью бегло.
8. Дмитрий Шерстобитов (DitriX) 13.11.12 12:37
(3) 1. Чухча не писатель, так что звиняйте.
2. Спасибо что столько внимания уделили моему правописанию.
Не верю. Телепорт что ли?

Нет, но нет необходимости подключать какие то отладчики ожидания.
Если этот вопрос решать файлами, то необходимо выгружать файл и на сервере потом ставит обработчик ожидания файла.
Однако, вы не получите ответ от сервера, например "Код номенклатуры не найден". А он просто проглотит ваш файл и не подавится. Потом в приемнике - вы может и буду видеть ошибку, но не в базе отправителе.
Вот это и называется мгновенно - я инициирую событие тогда, когда мне это нужно и получаю ответ от сервера.
Что есть онлайн?

В моем понимании - это есть практически мгновенный обмен данными, без посредников (файлов синхронизации и т.п.), когда на любой момент времени - в базах находится актуальная информация (ну и +/- время чтения очереди, если выставленно, у меня это где как, есть и 1 минута)

УТ и БП или ЗУП и БП подойдут? Если подойдут, жду поклона :D

Не вырывайте из контекста, я сказал:
Да ну, настройте-ка план обмена между двумя абсолютно отличающимися базами, причем эти базы не есть дочерние, ну и подключите туда несколько ИМ, и это все в режиме онлайна. Если вы это сделаете - я вам сделаю низкий поклон.
Однако, объем данных значительный (около 100 000 позиций + размеры и прочее), в базах работает около 100 пользователей.

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

А получилась каша.

"Мало ли что я там варила" (из анекдота)
9. Дмитрий Шерстобитов (DitriX) 13.11.12 12:45
(4) такой подход был выбран из за удобства чтения/изменения/доработки.
Может это и велосипед, но правил он никаких не нарушает - я всегда вижу очередь которая стоит к сайту, я могу ее быстро редактировать, я могу применить стандартные обработки для работы с регистрами сведений - а вот для планов обмена таких мало, если вообще есть, а свои писать - кто платить будет :)

(5) вы абсолютно правы, но этот подход значительно быстрее и проще. Создание xdto пакетов для передачи строковых данных - бессмысленно, а если мы работаем с другой базой 1С, то я указал в конце - как можно элементарно сериализовать данные.
Этот вариант более гибкий, в противном случае - на каждый сайт и на каждую функцию - необходимо будет создавать свои пакеты. А это весьма раздует код.

Мой вариант - универсальный. Во всяком случае после добавления нескольких баз - я практически не менял код, за исключением вида выгружаемых таблиц.
10. Дмитрий Шерстобитов (DitriX) 13.11.12 12:55
Внимание! Мораль статьи такова - не делайте запросы в цикле. :)
Если честно, то интересные люди откликнулись на статью. И хотелось бы узнать ваши подходы к решению аналогичных ситуаций, и проблем, с которыми вы столкнулись.

Вот я, например, не люблю планы обмена, как то не сложилось. Я всегда стараюсь работать в онлайне.
Из-за обменов очень много проблем, + эти файлы выгружать, иногда по 300 метров, это ужас.
И мне очень сильно не хватало ответа от сервера, что он успешно загрузил данные и т.д.

Да, SOAP я использую как контейнер для передачи данных, хотя иногда и по назначению, но это реже, но таким образом он решает все мои проблемы, а чего еще нужно? :)

У нас вот есть в базе Чат, и есть несколько франчей, чо своими базами, и есть партнеры, которые вообще ни как не пересекаются с нами по 1С. Но вот чат - работает по SOAP и всегда в режиме онлайна.
Согласитесь, реализовать такое файлами - убожество.
11. Олег Филиппов (comol) 13.11.12 13:06
"Уж сколько раз твердили миру"... не для обмена остатками каждые 30 секунд web сервисы предназначены... Если хотите действительно красиво и "полный online" это нужно делать по-другому... не файлами конечно, слишком просто, но и не web сервисами.. которые по сути представляют RPC через Web интерфейс. А вы тут "гвозди микроскопом"...
12. Василий Казьмин (awk) 13.11.12 13:11
(8) DitriX,

Скорость:

Нет, но нет необходимости подключать какие то отладчики ожидания.
Если этот вопрос решать файлами, то необходимо выгружать файл и на сервере потом ставит обработчик ожидания файла.
Однако, вы не получите ответ от сервера, например "Код номенклатуры не найден". А он просто проглотит ваш файл и не подавится. Потом в приемнике - вы может и буду видеть ошибку, но не в базе отправителе.
Вот это и называется мгновенно - я инициирую событие тогда, когда мне это нужно и получаю ответ от сервера.


1. Обработчик ожидания - это не проблема для алгоритма, т.к. O(x) = C. -
2. Ответ я не получу только когда не предусмотрю ответ. То же не проблема. -

Вид транспорта влияет на скорость обмена, не так сильно как выборка из базы данных (при достаточно большом объеме передаваемых данных).

Онлайн:

В моем понимании - это есть практически мгновенный обмен данными, без посредников (файлов синхронизации и т.п.), когда на любой момент времени - в базах находится актуальная информация (ну и +/- время чтения очереди, если выставленно, у меня это где как, есть и 1 минута)


Что бы в базах была актуальная информация применяется механизм распределенных транзакций.

Планы обмена:

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


План обмена занимается регистрацией изменений. Что регистрировать - дело ваше. Скорость костыля(даже хорошего) будет <= скорости встроенного механизма плана обмена.
13. Василий Казьмин (awk) 13.11.12 13:15
(10) DitriX,
интересные люди откликнулись на статью
Ибо тема интересная. 99% статей на сайте как сделать яйцам -вид сбоку.
хотелось бы узнать ваши подходы к решению аналогичных ситуаций, и проблем

1. Выявление необходимой информации для обмена
2. Классификация информации по критичности задержек при передачи
3. Оценка объема передаваемой информации
4. Выбор метода передачи
5. Выбор транспорта для передачи
6. Реализация модели
7. Если модель достаточна - завершаем иначе переход к 4 пункту.
14. Дмитрий Шерстобитов (DitriX) 13.11.12 13:19
(11) буду признателен за описание вашего решения.
(12)
План обмена занимается регистрацией изменений. Что регистрировать - дело ваше. Скорость костыля(даже хорошего) будет <= скорости встроенного механизма плана обмена.

Согласен, не подумал, спасибо за совет.
(13) тогда я надеюсь на еще более глубокий анализ моих костылей со стороны пользователей, думаю это будет полезно не только мне. Ибо не считаю себя уникальным человеком, который реализует уникальный решения :)
15. Дмитрий Шерстобитов (DitriX) 13.11.12 13:23
(13) из вашей практики - когда и почему вы выбрали именно этот подход?
Очень интересно, прям сгораю от нетерпения узнать :) Буду признателен за развернутый ответ.
16. Василий Казьмин (awk) 13.11.12 13:44
(15) DitriX,
Почему?

Потому, что был стабильный канал связи. И руководство шло на признание отсутствия связи форс-мажором. Без этого вся прелесть SOAP теряется.

Когда?

1. Когда надо было реализовать распределенные транзакции. (самый простой способ)
2. Когда нужно было сделать API для управления системой.
3. Когда нужно было сделать API для доступа к данным из вне. (REST предпочтительней)
17. Дмитрий Шерстобитов (DitriX) 13.11.12 13:54
18. Олег Филиппов (comol) 13.11.12 15:17
(14) DitriX, План обмена, прямая запись в БД, двустороняя репликация, сверка изменений. Если уж заморочиться то так.
19. Олег Филиппов (comol) 13.11.12 15:19
(16) awk, Респект. Ведь есть же оказывается люди которые понимают что такое SOAP и зачем оно нужно :)
20. Евгений Шабалин (xzorkiix) 13.11.12 16:33
21. Дмитрий Шерстобитов (DitriX) 13.11.12 16:50
(20) прочитайте внимательней комментарии к теме, тут было решение без оных.
При явной необходимости - не проблема добавить, но это уже другая тема
22. muha muhaha (fr.myha) 14.11.12 10:39
Много разных споров, но спасибо за эту статью. Для меня тема актуальна.
23. Дмитрий Наветный (Кебабыч) 14.11.12 15:23
А мне статья понравилась. А отправка XDTO пакетами может быть и оправданной - у меня была проблема, когда soap запрос сформированный 1С корректно не парсировался на другом конце, а при отправке пакетами я его могу скорректировать, что автор в статье и сделал, выбросив "мусор" из xml.
24. Дмитрий Шерстобитов (DitriX) 15.11.12 00:04
(23) кстати говоря - если на обратной стороне 1с, и все типы колонок простые, то можно в приемнике прописать что бы он приводил хмл к начальному виду и десериализовал. Таким образом мы получаем что то вроде архиватора. Кроме этого - так можно передавать и файл. Ну например у вас есть оооочень большой хмл, метров на 100, вы его архивируете и передаете через соап, на другой стороне - разархивируете и парсите. Или десириализуете.

Жаль что 1с не может заархивировать строку, без создания файла.
25. Вова Касьянов (tarikss) 31.03.13 23:36
Статья нормальная, мне пришлась по вкусу!
26. Александр Хомяк (logarifm) 03.09.13 13:48
На основе данной статьи и при помощи автора удалось создать мобильную торговлю, которая какраз-таки использует обмен через веб-сервис!!!

Данные реально быстро уходят и достаточно Сериализовать и десириализовать.

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

К комменту (1) и т.д. по тексту, ну для начала надо разобраться в теме, а не искать изъяны в коде.

Еще раз автору спасибо. Из своего хочу добавить, что удалось еще оптимизировать пакеты используя элементарное ХранилищеЗначений с сжатием максимальным 9.
Designer1C; +1 Ответить 1
27. Дмитрий Шерстобитов (DitriX) 03.09.13 14:03
(26) спасибо за отзыв, но Хранилище - работает только в режиме 1С-1С, а в статье приведен пример обмена 1С-веб сайт.
28. Александр Зорин (Manticor) 25.09.13 15:40
DitriX, скажите, а если делать реализацию двустороннего обмена при помощи сервисов - много кода нужно для этого добавлять???
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа