Всем привет
Хочу поделиться своим решением, которое немного требует доработки, но будет полезным в нашем комьюнити
Я первый раз решил программировать на sh с применением логических условий. До этого я просто пулял очередность команд без каких либо проверок
Задача
Реализовать импорт файла в базу данных
Архитектура
Так как было согласовано не делать всякие морды, а в итоге у нас считаются деньги - надо сделать максимально точно.
Было выбрано решение: CSV файл загружается через FTP, cron на регламенте запускает bash скрипт который импортирует файл в PostgreSQL
CRON-> .sh -> .sql
Наш bash скрипт
#!/bin/sh
cd /home/admin/ftp/jeventpayments/
f=`find -maxdepth 1 -name \*.csv -or -name \*.CSV`
if [[ ${f[*]} == '' ]]; then
exit 1
fi
for file in $f
do
declare -A A=([ref]=ref [action_id]=action_id [order_id]=order_id [payment]=payment [paid]=paid [status]=status [partner]=partner)
# Имя промежуточного файла
date=$(date '+%Y%m%d%H%M%S')
logfile=`echo ${file} | sed -e 's/.csv$//' | sed -e 's/.CSV$//'`
logfile=${logfile}_${date}.log
errfile=`echo ${file} | sed -e 's/.csv$//' | sed -e 's/.CSV$//'`
errfile=${errfile}_${date}.csv
ERR=errors/${errfile##*/}
LOG=logs/${logfile##*/}
ERRLOG=logs/ERROR_${logfile##*/}
touch ${LOG}
echo $(date '+%Y-%m-%d %H:%M:%S') Processing ${file} >> ${LOG}
unset i
unset IFS
unset arr
unset col
for i in `cat ${file}`; do
headers=${i}
IFS=','
arr=($i)
echo $(date '+%Y-%m-%d %H:%M:%S') Проверка атрибутов >> ${LOG}
echo $(date '+%Y-%m-%d %H:%M:%S') Обязательные атрибуты ${A[*]} >> ${LOG}
for col in "${arr[@]}"; do
#[[ ${A[*]} =~ ${col} ]] && echo 'yes' ${col} ${file} || echo 'no' ${col} ${file}
if [[ ${A[*]} =~ ${col} ]]; then
echo $(date '+%Y-%m-%d %H:%M:%S') Aтрибут ${col} найден >> ${LOG}
unset A[${col}]
fi
done
unset IFS
break;
done
if [[ ${#A[*]} >0 ]]; then
echo $(date '+%Y-%m-%d %H:%M:%S') ОШИБКА ОСТАЛИСЬ НЕ ЗАПОЛНЕННЫЕ АТРИБУТЫ >> ${LOG}
echo $(date '+%Y-%m-%d %H:%M:%S') Не хватает атрибутов: ${A[*]} >> ${LOG}
echo $(date '+%Y-%m-%d %H:%M:%S') Перемещение файла в папку ${ERR} >> ${LOG}
mv ${file} ${ERR}
echo $(date '+%Y-%m-%d %H:%M:%S') End ${file} >> ${LOG}
mv ${LOG} ${ERRLOG}
break;
fi
echo $(date '+%Y-%m-%d %H:%M:%S') Проверка атрибутов прошла успешно. Переходим к импорту файла >> ${LOG}
#IMPORT CSV TO DATABASE
filepath=`pwd`/${file}
headers=${headers// /,}
resultfile=/tmp/importfile2jeventpayments_$(date +%s)
psql -v v1="'$filepath'" -v v2="$headers" -d crm -U admin -f /home/admin/cron/bash/sql/importfile2jeventpayments.sql > ${resultfile} 2>&1
error=`cat ${resultfile} | grep 'ERROR'`
if [ ! -z "$error" ]
#ОШИБКА ИМПОРТА
then
echo $(date '+%Y-%m-%d %H:%M:%S') ОШИБКА ИМПОРТА ФАЙЛА В БАЗУ ДАННЫХ >> ${LOG}
echo \################################### >> ${LOG}
echo `cat ${resultfile}` >> ${LOG}
echo \################################### >> ${LOG}
echo $(date '+%Y-%m-%d %H:%M:%S') Перемещение файла в папку ${ERR} >> ${LOG}
mv ${file} ${ERR}
echo $(date '+%Y-%m-%d %H:%M:%S') End ${file} >> ${LOG}
mv ${LOG} ${ERRLOG}
else
echo $(date '+%Y-%m-%d %H:%M:%S') ФАЙЛ УСПЕШНО ИМПОРТИРОВАН В БАЗУ ДАННЫХ >> ${LOG}
echo $(date '+%Y-%m-%d %H:%M:%S') Удаляем файл ${file} >> ${LOG}
echo $(date '+%Y-%m-%d %H:%M:%S') End ${file} >> ${LOG}
rm -f ${file}
fi
rm -f ${resultfile}
done
Наш sql файл
importfile2jeventpayments.sql
create temporary table t1 as
select :v2
from contact.jeventpayments
where 1=2
;
COPY t1
FROM :v1 DELIMITER ',' CSV HEADER FORCE NOT NULL ref, action_id, order_id
;
insert into contact.jeventpayments
(:v2)
select :v2
from t1
ON CONFLICT (ref,action_id,order_id)
DO UPDATE SET
status =EXCLUDED.status,
payment = EXCLUDED.payment,
paid = EXCLUDED.paid,
date_update = current_timestamp
;
В вкратце мы проверяем необходимые атрибуты, импортируем в БД, проверяем в /tmp файле наличие ошибки, если она есть - отправляем его содержимое в Log файл, исходный csv отправляем в errors/
Если ошибок нет - csv просто удаляем, в Log пишем успех
Проблема
- Изначально таблицу я получаю в Excel от партнеров.
- Правлю все поля, редактирую. Сохраняю как csv.
- После этого в sublime text я делаю замену разделителей с
;
на,
- Если в этот момент я сохраню файл - он не импортируется. Скрипт отругается, что не нашел последнее поле в файле
- Если же я создам новый файл, вставлю в него данные, и сохраню с указанием расширения csv - такой файл будет обработан успешно.
Мне кажется что excel добавляет в конце полей невидимые символы-байты, которые понимает bash, но в текстовом редакторе они не видны. (Не помню как эти символы называются, какой-то дополнительный байт)
Если мы с вами поправим эту ошибку - будет готовое решение для других пользователей, с анализом полей и анализом успешности импорта в БД