Найдите шаг, на котором появляется повтор
Одинаковые заявки на экране ещё не объясняют причину. Сопоставьте отправку формы, входящие события и действия интеграции по идентификаторам и времени. Проверьте также, нет ли двух подключений, обрабатывающих один источник.
| Ситуация | Что проверить |
|---|---|
| Форма отправлена повторно | Был ли второй запрос и сохранился ли идентификатор исходного обращения. Новая вкладка или перезагрузка могут начать отдельную отправку. |
| Webhook доставлен несколько раз | Относятся ли доставки к одному событию и одному объекту. Новый ID доставки сам по себе не означает новое действие. |
| Действие выполнено, подтверждение потеряно | Есть ли результат во внешней системе, хотя журнал показывает незавершённую обработку. Таймаут не доказывает отсутствие результата. |
Определите, какой результат должен быть один
Запишите ожидаемое действие: создать заявку, изменить статус или переместить папку сделки. У входящей доставки, самой заявки и результата во внешней системе могут быть разные ID. Согласуйте, по какому устойчивому признаку система отличает повтор от нового обращения.
В учебном образце используются ID доставки event_id, ID сделки deal_id и версия её состояния revision. Название папки не заменяет идентичность сделки: одинаковые названия разных сделок вызывают конфликт. Версия должна приходить из доверенного источника; порядок получения событий и местные часы не доказывают порядок изменений.
- До действия сохранить намерение, объект и ожидаемое состояние.
- После действия сохранить подтверждённый результат и его связь с исходным объектом.
- Разделять незавершённую обработку, подтверждённое действие, старую версию и конфликт.
Перед повтором проверьте фактическое состояние
Если подтверждение потеряно, сначала найдите результат во внешней системе по устойчивому ID или другому проверенному признаку владения. Сравните его с актуальным намерением. Когда нужное действие уже выполнено, можно подтвердить результат без повторного создания или перемещения. Если найдено противоречие, его нужно разобрать отдельно.
Наш учебный пример показывает сохранённый прогон Python-механизма с моделью внешнего диска. После симулированного сбоя папка уже перемещена, а журнал SQLite ещё не подтвердил операцию. Новый обработчик сверяет фактический путь и завершает запись без второго перемещения. Старый подтверждённый путь в журнале не заменяет такую проверку.
Это модель диска в памяти, не реальная CRM и не клиентский кейс. Для настоящего сервиса отдельно проверяют поиск результата, поведение чтения после записи и поддержку ключей идемпотентности. Если результат нельзя достоверно найти, автоматический повтор может создать дубль.
Шесть проверок перед приёмкой
Для каждого сценария заранее запишите вход, ожидаемый статус и число действий. На тестовых данных сверяйте журнал и состояние внешней системы. Учебный образец проверяет этот подход на модели; настоящая интеграция требует проверки своего API.
- Обычная обработка: нужный результат создан и подтверждён, его ID связан с исходной заявкой или сделкой.
- Повтор того же действия: одинаковые и разные ID доставки, включая одновременные доставки, не создают дополнительный результат по согласованному правилу идентичности.
- Ошибка до действия: результат отсутствует, намерение сохранено; контролируемый повтор выполняет действие и подтверждает его.
- Потеря ответа или сбой после действия: результат уже есть; восстановление проверяет его и подтверждает без второго эффекта.
- Старая версия: поздно доставленное событие не откатывает более новое состояние. Правило порядка версий проверено отдельно.
- Конфликт: одинаковый ID с другим содержимым или занятый чужим объектом путь приводят к понятной остановке без слияния и перезаписи.
Что безопасно прислать для диагностики
Для первого разбора достаточно короткого описания систем, ожидаемого действия и одного воспроизводимого сбоя. Укажите время с часовым поясом, обезличенные ID заявки и доставок, условный путь или статус и небольшой фрагмент журнала до и после повтора. Заменяйте один исходный ID одинаковым условным значением во всех строках, чтобы сохранить связи.
Уберите имена, контакты, содержание заявок, токены, ключи, cookies, заголовки авторизации и секреты из адресов. Не присылайте полный экспорт CRM или рабочие доступы в первом обращении. Если понадобится проверка подключения, позже согласуем ограниченный доступ к тестовой среде и необходимые права.
Как определить состав первого этапа
Первый этап можно ограничить одним источником, одним действием и согласованными сценариями сбоя. Результат диагностики — схема прохождения события, найденная или воспроизведённая причина, список изменений и критерии проверки. Реализация, развёртывание и сопровождение согласуются по результатам разбора и возможностям сервисов.
Отдельно фиксируют правила переименования, повторного открытия сделки, ручных правок, конфликтов, срок хранения журнала и ограничений повторов. Цена и срок зависят от этих границ и доступных материалов; до проверки они не определены.
В учебном прогоне нет реального API, очереди и автоматического расписания повторов. При пересоздании обработчика объект модели диска сохраняется; завершение всего Python-процесса теряет эту модель. Пример не подтверждает восстановление настоящего сервиса после рестарта и не гарантирует доставку ровно один раз через границу базы данных и API.
Вопросы и ответы
Достаточно заблокировать кнопку повторной отправки?
Это помогает на этапе формы, но не устраняет повторную доставку webhook и потерю подтверждения внешнего действия. Проверять нужно всю цепочку до сохранённого результата.
Можно просто удалить все одинаковые заявки?
Сначала нужно установить, какие записи относятся к одному обращению. Совпадающие имена или контакты могут встречаться в разных заявках. Массовое удаление без проверки связей и правил не входит в диагностику.
Демо уже подключено к CRM?
Нет. Страница показывает сохранённый прогон с моделью диска в памяти. Реальные CRM, API и клиентские данные не подключены; это учебный технический пример.
Примеры из портфолио
Посмотрите, какие интерфейсы и рабочие сценарии показаны в кейсах. Закрытые проекты обезличены; демонстрационные значения не являются результатами внедрения.