Необходимо ежедневно выгружать файлы платежей, полученные от платежных систем и загружать их в биллинговую платформу, для последующей обработки.
Задача не сложная, но есть несколько очень важных критериев, а именно:
- Биллинговая система поддерживается версионность файлов, поэтому если платежный документ пришел повторно (корректированный) его тоже нужно загрузить.
- Необходимо вести полное логирование всех действий
- Помимо автоматизированных систем с файлами платежей работает оператор, создает их копии и уже затем обрабатывает
- Платежи поступают через ППО базирующее на ОС Windows, биллинговая система — Red Hat
Исходные данные
- Windows Server 2008
- Red Hat Linux Server
На сервере Windows делаем доступ на чтение к каталогу, куда поступают платежи, специально созданному пользователю.
Создаем сервер, Debian Linux, который будет отвечать за доставку платежей на сервер Red Hat Linux, установим на него базу данных MySQL
Подготовка к реализации задачи
- Монтируем удаленный каталог с платежами Windows Server на Debian Linux, например через samba. В каталог /home/pays/share
- Создадим необходимые каталоги /home/pays/cgi, /home/pays/cgi/cache, /home/pays/cgi/tosend
- Создадим рабочие скрипты cdmd5.sh, cfd.sh, ftd.sh, sfd.sh в каталоге /home/pays/cgi (скрипты должны быть исполняемыми)
- Добавим базу данных history в сервер MySQL и создадим в ней 2 таблицы
Таблицы в базе данных MySQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
mysql> DESCRIBE history; +----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+----------------+ | id | int(255) | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | size | varchar(255) | NO | | NULL | | | datetime | varchar(255) | NO | | NULL | | | status | int(1) | NO | | NULL | | +----------+--------------+------+-----+---------+----------------+ 5 rows in set (0.01 sec) mysql> DROP TABLE IF EXISTS `history`; CREATE TABLE `history` ( `id` int(255) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `size` varchar(255) NOT NULL, `datetime` varchar(255) NOT NULL, `status` int(1) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; mysql> DESCRIBE md5sum; +----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+----------------+ | id | int(255) | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | size | varchar(255) | NO | | NULL | | | datetime | varchar(255) | NO | | NULL | | | sum | varchar(255) | NO | | NULL | | +----------+--------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) mysql> DROP TABLE IF EXISTS `md5sum`; CREATE TABLE `md5sum` ( `id` int(255) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `size` varchar(255) NOT NULL, `datetime` varchar(255) NOT NULL, `sum` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
Настройка скриптов
Создадим задачу в планировщике cron
1 2 3 4 |
2 0 * * 1,2,3,4,5 root /home/pays/cgi/ftd.sh */5 * * * 1,2,3,4,5 root /home/pays/cgi/cfd.sh */9 * * * 1,2,3,4,5 root /home/pays/cgi/sfd.sh 5 0 * * 7 root /home/pays/cgi/cdmd5.sh |
Файлы, которые необходимо отправлять имеют вид 10ГГММДД.txt и US_10ГГММДД.txt, пример 10160513.txt
Этот скрипт запускается по будням в 00:02 и создает записи в базе данных с именами файлов, которые нужно будет найти и отправить биллинговой системе
1 |
nano /home/pays/cgi/ftd.sh |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#!/bin/bash # First script # Write filenames to table # Run workday at 00:02 # VARIABLES dusername="root" // -- пользователь БД dpassword="password" // -- пароль пользователя БД ddb="history" // -- имя базы данных dtable="history" // -- имя таблицы dhost="localhost" // -- адрес сервера ddate=$(date +"%Y-%m-%d-%H:%M") // -- дата в формате ГГГГ-ММ-ДД-ЧЧ:ММ ddatefile=$(date +"%y%m%d") // -- дата в формате ггммдд LOGFILEP="/var/log/put.log" // -- файл логов # MASKS // -- маски файлов которые нужно искать USmask="US_10$ddatefile.txt" noUSmask="10$ddatefile.txt" # FUNCTIONS // -- функция записывает в БД имена файлов, дату добавления со статусом 0 говорит о том, файл не был найден и отправлен function checkrecord() { for i in $USmask $noUSmask do mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --execute="INSERT INTO $ddb.$dtable (id,name,size,datetime,status) VALUES( NULL,'$i','0','$ddate','0' )"; echo "Созана запись на поиск файла $i"; done } # LOGGING // -- функция пишет логи function write_log() { while read text do LOGTIME=`date "+%Y-%m-%d %H:%M:%S"` # If log file is not defined, just echo the output if [ "$LOG_FILE" == "" ]; then echo $LOGTIME": $text"; else LOG=$LOG_FILE.`date +%Y%m%d` touch $LOG if [ ! -f $LOG ]; then echo "ERROR!! Cannot create log file $LOG. Exiting."; exit 1; fi echo $LOGTIME": $text" | tee -a $LOG; fi done } # LOGGING // -- функция проверяет размер логов и очищает его, предыдущие - бэкапит function check_log() { MAXSIZE="10240" LOGSIZE=`du $LOGFILEP | awk '{print $1}'` if (( "$LOGSIZE" > "$MAXSIZE" )); then gzip $LOGFILEP mv $LOGFILEP.gz /var/log/put_$ddate.log.gz touch $LOGFILEP fi } check_log checkrecord | write_log | tee -a $LOGFILEP |
Данный скрипт запускается по будням каждые 5 минут и ищет файлы, имена которых были созданы в таблице history предыдущим скриптом, также скрипт сверяет контрольную сумму функцией md5sum и записывает её в таблицу md5sum, чтобы избежать отправку дубликатов
1 |
nano /home/pays/cgi/cfd.sh |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
#!/bin/bash # Second script # Select filenames from table & search in directory # Run workday every 5 minets # VARIABLES dusername="root" // -- пользователь БД dpassword="password" // -- пароль пользователя БД ddb="history" // -- имя БД dtable="history" // -- таблица для поиска файлов dsum="md5sum" // -- таблица хранит контрольные сумму найденых и отправленых файлов dhost="localhost" ddate=$(date +"%Y-%m-%d-%H:%M") // -- searchdir="/home/sberpay/share/" // -- каталог происка storedir="/home/sberpay/cgi/tosend/" // -- каталог хранит файла предназначенные на отправку биллинговой системе cachedir="/home/sberpay/cgi/cache/" // -- КЭШ, на всякий случай, храним файлы у себя LOGFILEP="/var/log/put.log" // -- файл логов # FUNCTIONS // -- функция проверяет и записывает контрольные суммы в таблицу function checksum() { # Select sended filenames from table filename=`mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --silent --skip-column-names --execute="SELECT name FROM $dtable WHERE status = '1'";` for i in $filename do # Search md5 hash files in directory findfile=`find $searchdir -name "$i" -exec md5sum '{}' ';' | sort | cut -c -32` findfiles=`find $searchdir -name "$i" -exec echo {} \;` FILESIZE=$(stat -c%s "$findfiles") fileupdate=`mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --execute="INSERT INTO $ddb.$dsum (id,name,size,datetime,sum) VALUES( NULL,'$i','$FILESIZE','$ddate','$findfile' )";` echo "[DUPLICATE] Контрольная сумма файла $i добавлена. "; done } function dupl() // -- функция ищет дубликаты, сверяя контрольные суммы записи в таблице и файла в каталоге { # Search duplicate filename=`mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --silent --skip-column-names --execute="SELECT name FROM $dtable WHERE status = '1'";` for i in $filename do finddupl=`mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --silent --skip-column-names --execute="SELECT sum FROM $dsum WHERE name = '$i' ORDER BY id DESC LIMIT 1";` findfile=`find $searchdir -name "$i" -exec md5sum '{}' ';' | sort | cut -c -32` if [ "$finddupl" == "$findfile" ]; then echo "[DUPLICATE] Верная контрольная сумма файла $i. " else echo "[DUPLICATE] Не верная контрольная сумма. Файл $i помечен для отправки. " setdupl=`mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --silent --skip-column-names --execute="UPDATE $dtable SET status = '0' WHERE name = '$i'";` fi done } function findfiles() // -- функция поиска файлов в каталоге { # Select filenames from table filename=`mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --silent --skip-column-names --execute="SELECT name FROM $dtable WHERE status = '0'";` for i in $filename do # Search files in directory findfile=`find $searchdir -name "$i" -exec cp {} $storedir \; -print` cachefile=`find $searchdir -name "$i" -exec cp {} $cachedir \; -print` # Check size files // -- функция получения размера файла fsize=${#findfile} if [ "$fsize" -eq 0 ]; then echo "[INFO] Файл $i не найден. "; else FILESIZE=$(stat -c%s "$findfile") fileupdate=`mysql -u "$dusername" --password="$dpassword" --database="$ddb" -h"$dhost" --execute="UPDATE $dtable SET status = '1', datetime = '$ddate', size = '$FILESIZE' WHERE name = '$i'";` fi done } # LOGGING // -- логирования function write_log() { while read text do LOGTIME=`date "+%Y-%m-%d %H:%M:%S"` # If log file is not defined, just echo the output if [ "$LOG_FILE" == "" ]; then echo $LOGTIME": $text"; else LOG=$LOG_FILE.`date +%Y%m%d` touch $LOG if [ ! -f $LOG ]; then echo "ERROR!! Cannot create log file $LOG. Exiting."; exit 1; fi echo $LOGTIME": $text" | tee -a $LOG; fi done } # LOGGIN // -- функция очистки логов function check_log() { MAXSIZE="10240" LOGSIZE=`du $LOGFILEP | awk '{print $1}'` if (( "$LOGSIZE" > "$MAXSIZE" )); then gzip $LOGFILEP mv $LOGFILEP.gz /var/log/put_$ddate.log.gz touch $LOGFILEP fi } check_log checksum | write_log | tee -a $LOGFILEP sleep 5 dupl | write_log | tee -a $LOGFILEP sleep 5 findfiles | write_log | tee -a $LOGFILEP |
Данный скрипт только отправляет данные на сервер через sFTP, при отправке используется SSH ключ, запускается по будням каждые 9 минут
1 |
nano /home/pays/cgi/sfd.sh |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#!/bin/bash # Treeth script # Send all files in diretory to billing # Run worday every 9 minets # VARIABLES storedir="/home/sberpay/cgi/tosend/" // -- каталог, файлы из которого нужно отправить SERVER_USER="pays" // -- удаленный пользователь SERVER_IP="192.168.0.10" // -- IP удаленного сервера LOGFILEP="/var/log/put.log" // -- файл логово // -- поиск и отправка файлов if [ "$(ls -A $storedir)" ]; then # Puts files HOST="-i /home/pays/.ssh/sert.key $SERVER_USER@$SERVER_IP" putfile=`sftp $HOST <<EOF cd in put $storedir* ls bye EOF` # Del files delfinding=`find $storedir -type f -delete -print` else putfile="[INFO] Нет файлов для отправки" delfinding="[INFO] Очистка временного каталога" fi // -- функция логирования function write_log() { while read text do LOGTIME=`date "+%Y-%m-%d %H:%M:%S"` # If log file is not defined, just echo the output if [ "$LOG_FILE" == "" ]; then echo $LOGTIME": $text"; else LOG=$LOG_FILE.`date +%Y%m%d` touch $LOG if [ ! -f $LOG ]; then echo "ERROR!! Cannot create log file $LOG. Exiting."; exit 1; fi echo $LOGTIME": $text" | tee -a $LOG; fi done } // -- функция очистки логов function check_log() { MAXSIZE="10240" LOGSIZE=`du $LOGFILEP | awk '{print $1}'` if (( "$LOGSIZE" > "$MAXSIZE" )); then gzip $LOGFILEP mv $LOGFILEP.gz /var/log/put_$ddate.log.gz touch $LOGFILEP fi } check_log echo $putfile | write_log | tee -a $LOGFILEP echo $delfinding | write_log | tee -a $LOGFILEP |