Импорт данных в систему учета из .excel таблицы (Symfony)

170 000 руб. за проект
25 октября 2024, 15:32 • 12 откликов • 86 просмотров
ВНИМАНИЕ : только фреймворк Symfony
оставляйте свой TG для связи!

Надо получить механизм, который позволяет
 вгрузить к нам данные (создать и/ли обновить сущности) заданного типа
 до загрузки получить предпросмотр, чтобы юзер смог указать наглядно "что куда грузим"
 в зависимости от условий и выбранных данных, доуказать какие-то опции "как обрабатывать"
 когда всего хватает - обработать импорт
Как сделано у другой системы (flow), для референса.

Там хорошо и правильно. Очень рекомендую посмотреть другие системы
 выбираешь "импорт" - например товары
 грузишь файл, excel-источник
 бек его читает, собирает первые NN строк со столбцами, выводит в ответе предпросмотра
 юзер в окне предпросмотра видит колонки с примером данных, выбирает им "заголовок", т.е куда грузить. По умолчанию не выбраны
 бек получает выбранные колонки/типы данных, и отдает в ответе "какие еще опции надо указать"
 юзер указывает (на фронте) доп параметры, которые бек запросил на этапе предпросмотра. Например "как обрабатывать остатки", создавать ли оприходование итп
 юзер аппрувит, шлёт данные. Бек ставит задачу по обработке данных, со всеми указанными параметрами - отдает фронту "запустили обработку, вон ее ID"
 обработка задачи выполняет то, что от нее просили, и финалится по готовности

Как бы я сделал, с учетом нашей имеющейся машинерии
У нас есть миграшки. Всё чем они занимаются - берут входящие параметры (любые), обрабатывают эти параметры, ставят себе в миграшку задачи, которые уже обрабатывают и вливают данные в зависимости от того, что попросили. Мне кажется - вполне наш вариант.

Чего нет: нам надо получать файлик, выдавать предпросмотр, дальше (до постановки задачи в обработку) понимать, чего от нас хотят и чего не хватает, запрашивать у фронта необходимые параметры (доп.опции и доп.обработки), и когда всего хватает - ставить и запускать миграшки. Миграшка умеет писать лог, чего реально происходило и как обработалось - окай.

 Я бы сделал объект ImportTask, и у него type=excel (мало ли потом захотят еще там с 1C, YML как у МСклада итд итп)
 Фронт запрашивает метод ImportTask/create entity=product (например), мы ее создаем пустую: фронт знает айдишник, может ее дальше доконфигурить
 Фронт грузит к ней файлик (возможно, больше одного раза) - т.е метод upload
 Мы по type=excel и entity=product понимаем, какие поля объекта существуют, и читаем первые 20 строк файла. Говорим OK - файлик получили (вдруг нет, или формат не тот)
 Отдаем а) строки предпросмотра б) набор возможных полей (метод configure для таски)
 В зависимости от уже загруженных данных в ImportTask, определяем, чего не хватает. Эта логика зависит от уже выбранных данных! так что имеет смысл для importTask написать ряд проверок, которые посмотрят на данные задачи, и что-то запишут в ее requirements + выдадут какой-то лог (например, "ваш файл ваще говно склизское")
 Я бы сделал отдельный метод ImportTask/configure, в который фронт шлет тупо набор данных, собранных из формы. Прямо json. Тут тебе и выбранные поля, и опции входной обработки (не создавать товары, только обновлять / выбраны остатки, создавать приход, какого контрагента итд итп). Мы данные получаем, прогоняем обработчики по type+entity, и отвечаем на configure: вот снова предпросмотр, вот такие requirements, вот такой ответ валидации, вот такой набор справочных строк (например, сколько данных там вообще в файле нашли подходящих)
 Для всей ImportTask я бы ввел статус, если он не ready - значит юзеру надо что-то дозаполнить
 Фронт, когда видит дополнительные requirements, показывает юзеру доп.формы и доп.блоки, которых изначально не было, и которые у юзера дособерут нужные данные
 Цикл повторяется: юзер дозаполняет или меняет поля (или даже грузит другой файл), мы снова всё проверяем, и так пока все наши проверки не пройдет, а для фронта ImportTask не станет ready
 Когда у importTask status=ready, фронт вызывает метод execute, который по полностью заполненному importTask создает миграшку, и отвечает ok + ID созданной миграшки
 Метод execute внутри бека создает MigrationTask - нужного типа для type=excel и entity=product - и передает ей входящий набор данных целиком
 Созданная MigrationTaskImportExcelProduct (например) забирает уже полностью готовые данные, и создает Subtask нужного типа, сколько их там понадобится. Одна создает Products, вторая создает оприходование, третья заполняет кастомные поля товаров например, четвертая баркоды итп.
Где здесь узкие места
 процесс конфигурации задачи может быть достаточно просторным (цикл вызовов /configure и дозаполнения данных). Может вообще хз когда завершиться. Я выбрал файл, вот предпросмотр. Я указываю что вот этот столбец - это остатки, сохраняюсь.

Бек смотрит "ага, у нас используются остатки - ставим в requirements что-то типа [stocks]".
Фронт это получает, выводит компонент с формой про оприходование.
Юзер на него смотрит, чот сложно, убирает выбор поля остатков для столбца.
Мы снова получаем данные configure - ага, остатки больше не хотят, в requirements stocks убираем (может там что-то еще осталось другое). Прогоняем остальные проверки. Итп.
Юзер внезапно понял что вообще мудак, и загрузил другой файл. Ловим другой файл, обновляем importTask, снова проверки, снова ответ.
Когда-то весь пакет данных станет ready.
 фронту придется иметь набор подключаемых компонент для различных requirements. Мы не можем с бека сказать "вот такие контролы нам тут нарисуй", это придется понимать фронту (и у них блоки под остатки, под прочее = статически уже есть в коде, но включаются фронтом на странице в зависимости от ответа)
 таски мигратора. Именно им придется читать файлик и собственно проводить операции над данными. Файлик на этом этапе валяется у нас на харде, условия обработки - в миграшке
 читалка Excel. Тут надо найти подходящую, Леха нашел https://github.com/openspout/openspout , читает она норм. Читать придется и на предпросмотре (importTask type=excel), и на обработке (миграшки).
 обработчики для requirements. Как можно заметить, они очень узко-специализированные, и их несколько.
 базовые данные конфига. Можем сделать отдельный requirements:[product] например, для type=product.

И там уже с фронта прилетит набор опций: создавать/только обновлять товары, искать по id/по sku, итд.
Список данных нам на этом этапе - пофиг. Нам надо только собрать их по списку, убедиться что всё нужное заполнено, записать это в конфиг, и потом передать в миграшку - пускай уже она там разбирается, что конкретно наконфигурили.

Для importTask
 id, created, account_id, modified, всякие стандартные поля
 type, entity - что грузим и из чего (type=excel, entity=product)
 source: имя загруженного файла, который уже у нас на диске
 status: статус задачи на импорт, мне кажется [need_source, need_config, ready, executed] как минимум
 requirements[], чтобы фронту однозначно показывать, какие блоки формы выводить
 fields[] со списком всех возможных полей, чтобы фронт знал что выводить в формах (их можно тоже обработчиком заполнять из product - см ниже про configure)
 fields_map[] со списком столбец_юзера = наше_поле. Выбраны для импорта могут быть далеко не все столбцы!
 preview: json кеш прочитанных N строк со столбцами из файла, чтобы на диск не бегать
 config: json набор произвольных данных ключ-значение
 validation[] набор строк, которые мы заполним, когда проверим данные своими
 migration_class чтобы понимать, какую миграшку создавать (по type=excel и entity=product)
 migration_id чтобы понимать, что мы этому конфигу на execute завели миграцию, и вон она там по id живет

Цикл проверки configure, еще разок
 вытаскиваем все подходящие обработчики по type=excel & entity=product
 гоняем им всем метод check у нас на беке
 каждый обработчик выполняет свою проверку, может читать всё что есть в importTask, и писать свои результаты проверки - выставлять/снимать requirements, дописывать в validation[] результаты проверки ("укажи контрагента, сцк!"), смотреть в fields[] (что у нас например остатки хотят)
 если source нет, задача в статус need_source
 если validation[] не пустой, задача в статус need_config
 а если всего хватает, то status=ready, ждем финального execute когда юзер нажмет кнопку

Что надо сейчас
 собрать эту машинерию на примере type=excel и entity=product. Остальные пока не трогаем, начнем с малого
 поддержать как минимум один requirement, когда нам указывают работу с остатками
 сделать загрузку upload, цикл configure, доведение importTask до ready
 написать класс таска миграции (пока тупо один, excel+product), и к нему сабтаски: залив товаров, обработка остатков
 чтобы не ждать фронт, проверить api-вызовами, что наша часть работает: importTask создается, к ней можно залить файл, к ней можно получить предпросмотр, к ней можно указать данные конфига, ответ попросит requirements:[stocks], и если доуказать все данные - importTask станет ready
 по заполненной importTask status=ready запустить execute, создастся и запустится миграшка с сабтасками
Пока на детали самой миграшки и сабтасок можно плотно не заморачиваться (они могут быть вообще пустые). Потом задачами допилим.
Сейчас главное механизму с importTask собрать и проверить.
Эту задачу можно и нужно бить на сабтаски, и даже отдельные коммиты. Вот один метод, вот второй, вот добавили обработчики, вот фиксы, вот проверили requirements, вот миграшка, вот execute итд итп. Слона лучше жрать по частям.