Генератор номеров для PSI WMS




Принцип обмена данными из 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='\

23 Comments

  1. BorovikSV

    (0)

    В диапазоне целых чисел от 1 до 100 возникали до 8 подряд одинаковых номеров

    А не пробовали сделать диапазон от 1 до 1 000 000, а затем получить остаток от деления от 100? по природе генераторов случайных чисел это должно вам помочь

    СлучайноеЧисло = ГСЧ.СлучайноеЧисло(1, 1000000) % 100;
    
    Reply
  2. m-rv

    Молодец, давно тебе говорил: опубликуй эту идею

    Reply
  3. BorovikSV

    (0)

    Практические испытания (10 000 полученных значений) показывают то, что штатный генератор распределяет значения куда лучше, чем ваша реализация основанная на UUID.

    см. во вложении диаграмму.

    Верхняя диаграмма использует:

    ГСЧ.СлучайноеЧисло(0,99);

    Нижняя диаграмма использует:

    ПолучитьСлучайноеЧисло(0,99);

    Обратите внимание на нижней диаграмме граничные значения (0 и 99). Тут ваш алгоритм вообще плохо себя ведет. Я уже не говорю о том что использование UUID получается на порядки медленней.

    Reply
  4. TODD22
    В диапазоне целых чисел от 1 до 100 возникали до 8 подряд одинаковых номеров.

    Может потому что в одну секунду их генерировал? На сколько помню генератор завязан на системное время.

    Reply
  5. v3rter

    Посмотрите тут https://habrahabr.ru/post/132217/ начиная с текста «Получение псевдослучайных чисел на основе полиномиального счетчика», возможно этот метод тоже подойдет.

    Reply
  6. IgorNastenko

    (3) У меня дополнительные требования были к генератору, кроме нормального распределения, должно быть еще, что бы в один момент времени не было повторяющихся номеров, это выявится если ГСЧ.СлучайноеЧисло(0,99) запустить в несколько потоков.

    Reply
  7. IgorNastenko

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

    Reply
  8. IgorNastenko

    (2)Да случайно у себя на неё наткнулся, подумал, пора)

    Reply
  9. BorovikSV

    (6)

    это выявится если ГСЧ.СлучайноеЧисло(0,99) запустить в несколько потоков.

    так вы попробуйте при создании генератора инициализировать его случайным образом (идентификатор сеанса например)

    ГСЧ = новый ГенераторСлучайныхЧисел(НомерСоединенияИнформационнойБазы());
    Reply
  10. slawa

    Зачем после возврата из рекурсивного вызова (Уник = ПолучитьСлучайноеЧисло(Мин, Макс);) опять приводится к интревалу?

    Сразу его вернуть нельзя «Возврат ПолучитьСлучайноеЧисло(Мин, Макс);» ?

    Reply
  11. IgorNastenko

    (10)Чтобы избежать случая, где GUID будет состоять из недостаточного количества цифр (составляющие GUID будут больше 9), в данном случае это не произойдет, но если этот генератор нужен будет для использования с длинной числа > 16 то лучше сделать эту проверку.

    Reply
  12. bulpi

    ЭЭЭЭ…. А на фига все это ? Я тоже общаюсь с wms, и мне хватает префиксов в номерах документов, чтобы получить уникальный номер Ж)

    Reply
  13. v3rter

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

    Reply
  14. IgorNastenko

    (12) Согласен что нумератор надо использовать из документа. Такое вот наследство было, и хорошо что вообще не в константе не хранили, а переделывать нельзя было, во первых, ответственная часть, во-вторых, этот элемент использовался в интеграции с другими системами. Если что то не так пойдет то много процессов бы встало, а так если отделаться малой кровью и избавиться от проблемы блокировки, использовалось такое решение.

    Reply
  15. v3rter

    Я бы попробовал хранить счетчик(и) во внешней базе MS SQL, используя хранимую функцию, возвращающую и автоинкрементирующую нужный счетчик в момент обращения.

    Reply
  16. IgorNastenko

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

    Reply
  17. slawa

    (11)

    Зачем нужен рекурсивный вызов — понятно. (для перегенерации случ.числа при получении гуид с малым количеством цифр)

    Не понял зачем полученный из рекурсии результат опять приводится к интервалу. Ведь он и так должен вернуться в нужном диапазоне.

    Reply
  18. v3rter

    Интересно, если использовать все цифры гуида распределение ухудшается или нет?

       УникЧисло16=0;
    Для Позиция = 1 По СтрДлина(Уник) Цикл
    УникЧисло16=УникЧисло*16+Найти(«123456789abcdef»,Сред(Уник, Позиция ,1));
    КонецЦикла;

    Reply
  19. slawa

    Взято тут: http://www.forum.mista.ru/topic.php?id=196865

    Не проверял

    В конце:

    //преобразуем его в случайное число из заданного интервала, округляем до целого
    
    //ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс);
    
    // Делаем правильно вот так, т.к. по предыдущей формуле крайние значения выпадают в 2 раза реже.
    
    ЧислоИзИнтервала = Цел(0.5 + (Макс — Мин + 1) * Случ) + Мин — 1;
    Если ЧислоИзИнтервала = Мин — 1 Тогда
    ЧислоИзИнтервала = Макс;
    КонецЕсли;
    
    

    Показать

    Reply
  20. v3rter

    Чисто академический интерес — Новый УникальныйИдентификатор несёт в себе дату-время как в https://helpf.pro/faq/view/1099.html ?

    Как по GUID определить время и дату создания ссылки?

    Код 1C v 8.
     Функция ДатаСозданияСсылки(Ссылка)
    ГУИД = Ссылка.УникальныйИдентификатор();
    Строка16 = Сред(ГУИД, 16, 3) + Сред(ГУИД, 10, 4) + Сред(ГУИД, 1, 8);
    Разрядность = СтрДлина(Строка16);
    ЧислоСек = 0;
    Для Позиция = 1 По Разрядность Цикл
    ЧислоСек = ЧислоСек + Найти(«123456789abcdef»,Сред(Строка16,Позиция,1))*Pow(16,Разрядность — Позиция);
    КонецЦикла;
    ЧислоСек = ЧислоСек / 10000000;
    Возврат Дата(1582, 10, 15, 04, 00, 00) + ЧислоСек;
    КонецФункции   

    Показать

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

    Reply
  21. v3rter

    (20) Проверил. Нет. Не несет.

    Reply
  22. IgorNastenko

    (18)Давно конечно эксперементировал, но все эти генераторы давали достаточно хорошее «нормальное» распределение, если не брать создание в 1 момент времени, да и с 16-ричными числами в 1с работать как то не удобно.

    Reply
  23. IgorNastenko

    (21)Вроде он должен нести в себе дату время + счетчик. Может только в какой нибудь другой зависимости.

    Reply

Leave a Comment

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