Чтобы одну заявку не обработали дважды, дайте обращению устойчивый идентификатор, проверяйте его текущее состояние на сервере и сохраняйте решение так, чтобы два одновременных действия не могли завершить одну операцию повторно. Эта статья для владельца бота или формы и разработчика, который принимает заявки клиентов. Разберём повторную доставку сообщения, двойное нажатие и работу двух сотрудников. Итогом будет набор правил и тестов для Вашего процесса, а не обещание готовой защиты в любом боте.

Администратор проверяет обращение на рабочем месте, сгенерированная иллюстрация

Различите повтор доставки и новую заявку

Одинаковый текст не всегда означает одну операцию. Клиент может дважды спросить о похожей услуге, создать новое обращение после закрытия прежнего или уточнить действующий заказ. Если автоматически удалять всё похожее, можно потерять настоящую заявку. Сначала определите, что в Вашем процессе считается самостоятельным обращением.

Отдельный случай — система повторно доставила то же событие. Например, обработчик не успел вернуть подтверждение, хотя часть работы уже выполнил. Получатель снова видит доставку и не должен заново создавать заявку только потому, что запрос пришёл ещё раз. Технический повтор нужно отличать от нового действия человека.

В Telegram Bot API у объекта Update есть update_id, который помогает распознавать повторные обновления. В описании setWebhook также указано, что после неуспешной доставки запрос повторяется. Это свойства конкретного API. Идентификатор обновления полезен для входящего события, но сам по себе не определяет, какую бизнес-заявку изменяет нажатая кнопка.

СитуацияЧто сравниватьОжидаемое поведение
Повтор доставки событияИсточник и идентификатор событияНе создавать второй результат того же события
Повтор нажатия решенияЗаявку, действие и актуальное состояниеПоказать уже сохранённый исход
Два сотрудника открыли карточкуСостояние непосредственно при записиПрименить только допустимый переход
Клиент прислал похожий запросСмысл обращения и правила процессаНе удалять автоматически только по сходству

Составьте такую таблицу для своих каналов: сайт, бот, почта или внешняя система. Не вводите одно общее правило «один телефон — одна заявка». Один человек может обращаться по разным задачам. Контакт помогает обсуждать связь обращений, но обычно недостаточен как единственное основание запрета.

Назначьте идентификаторы нужным объектам

Для устойчивой обработки полезно различать идентификатор заявки, входящего события и попытки действия. Заявка обозначает предмет работы. Событие обозначает полученную доставку. Попытка относится к конкретной операции, которую пользователь или система просит выполнить. Их не обязательно показывать человеку целиком, но разработчик должен понимать назначение каждого.

Присваивайте номер заявки при её создании и сохраняйте его вместе с данными. При открытии карточки из истории передавайте ссылку именно на эту заявку, а не на её положение в очереди. «Третья карточка» изменится после обработки соседних записей; постоянный номер останется связан с нужным обращением.

Если данные поступают из нескольких источников, учитывайте источник идентификатора. Одинаковые числовые значения двух разных систем не обязаны обозначать один объект. Для проверки технических повторов можно хранить пару «источник и номер события». Конкретный формат определяется интеграцией; не нужно менять весь учёт только ради удобного внешнего вида номера.

Не используйте в качестве такого ключа произвольный текст, имя клиента или текущее время без проверки уникальности. Текст может измениться, имена совпадают, точность времени ограничена. Идентификатор должен позволять найти запись после перезапуска и однозначно сопоставить попытку с операцией, которую собирались выполнить.

Отдельно опишите объединение похожих обращений. Если сотрудник помечает запись дублем другой заявки, сохраняйте связь и причину, а не уничтожайте исходник незаметно. Это бизнес-решение. Его нельзя смешивать с автоматическим пропуском повторной сетевой доставки, где смысл обращения не менялся.

Проверяйте переход состояния в момент записи

Скрыть кнопку после нажатия полезно для интерфейса, но недостаточно для защиты. У сотрудника может остаться старая карточка, другой человек откроет ту же запись, а запрос повторится после задержки. Окончательное решение должно проверяться там, где сохраняются данные, а не только на экране пользователя.

Определите допустимые переходы. Например, из «ожидает проверки» можно перейти в «одобрена» или «отклонена». Из уже завершённого состояния обычная кнопка проверки не должна повторять прежнее действие. Если процесс допускает пересмотр, сделайте его отдельной операцией с понятными правами и причиной, а не побочным эффектом повторного нажатия.

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

В документации PostgreSQL об ограничениях описаны уникальные ограничения для столбца или сочетания столбцов. Они могут быть частью защиты от повторного создания записи. Однако уникальность ключа не заменяет правила перехода состояния и не предотвращает автоматически повторный вызов внешней системы.

Конкретный механизм зависит от хранилища и архитектуры: ограничения, транзакции, условное изменение или другой согласованный способ. Важно проверить итог при конкуренции, а не только наличие слова «блокировка» в описании. Попросите разработчика показать, какая из двух одновременных операций применится и какой ответ получит вторая.

Два администратора сверяют одно обращение рядом со своими рабочими местами, сгенерированная иллюстрация
Две открытые карточки не должны давать два независимых решения одной операции

Определите ответ на повторную попытку

Повтор уже выполненной операции должен давать понятный исход. Можно сообщить: «По этой заявке уже сохранено решение» и показать доступные пользователю сведения. Не отвечайте второй раз «одобрено», если ничего нового не произошло: сотрудник может принять это за повторное выполнение следующего шага.

Идемпотентность означает, что повтор одной и той же операции не создаёт дополнительного эффекта. Это не запрет любых повторов и не гарантия, что запрос всегда завершится успешно. Система всё ещё должна объяснять ошибку, незавершённую обработку или конфликт с другим действием.

В документации Stripe об идемпотентных запросах описан ключ повторяемой операции и проверка параметров. Это полезный пример API-подхода, а не инструкция подключать оплату к Вашему боту. Правила хранения ключей и ответов конкретного сервиса нельзя автоматически переносить на все заявки.

Повтор с тем же ключом, но другим содержанием требует отдельной проверки. Если сотрудник сначала отправил «одобрить», а затем «отклонить», это не две одинаковые попытки. Не возвращайте старый успешный ответ как будто новое действие выполнено. Система должна обнаружить несовместимость и направить пользователя к актуальному состоянию.

Для зависшей операции заранее определите путь восстановления. Метка «в обработке» не должна навсегда блокировать заявку после аварийного завершения процесса. Но автоматически снимать её по таймеру и повторять внешний эффект тоже небезопасно. Сначала выясняют, был ли эффект выполнен, затем выбирают повтор, завершение или ручную проверку.

Не путайте сохранение решения и доставку уведомления

Решение может сохраниться, а сообщение клиенту не отправиться. Если в ответ на такую ошибку заново одобрять заявку, можно повторить связанные действия. Разделите состояние основной операции и состояние уведомления: решение принято, уведомление ожидает отправки, отправлено либо требует проверки. Названия выбирайте по реальному процессу.

Обратная ситуация тоже возможна: внешняя система выполнила действие, но подтверждение не дошло. Не обещайте, что локальный флаг всегда исключит повтор снаружи. Для заказа, платежа или публикации требуется проверить, поддерживает ли получатель устойчивый ключ операции либо способ узнать фактический результат. Без этого может понадобиться ручное восстановление.

На стадии проекта перечислите все эффекты одного решения: изменение статуса, отправка сообщения, создание заказа у партнёра или публикация. Для каждого задайте правило повторной попытки и проверяемое подтверждение. Если нужного подтверждения нет, покажите неопределённость оператору; не подменяйте её словом «успех».

Общую цепочку данных между системами разбирает статья о сокращении переноса данных между таблицами и чатами. Здесь задача уже: сохранить однократность конкретного решения, даже когда доставка и ответ приходят с задержкой.

Не записывайте в журнал лишние содержательные данные только ради диагностики. Для многих проверок достаточно номера заявки, типа действия, времени, исхода и идентификатора попытки. Состав журнала, права доступа и срок хранения нужно определить для своего процесса. Это отдельная задача от защиты перехода состояния.

Проверьте повторы на безопасной учебной заявке

Используйте тестовое окружение и вымышленные данные, не создающие настоящих заказов. Сначала выполните обычное действие и убедитесь, что результат сохранён. Затем повторите его с теми же параметрами. Проверьте не только надпись на экране, но и число созданных записей, решений и внешних действий, доступных для контроля.

Следующий тест — два сотрудника открывают одну ожидающую заявку и почти одновременно выбирают разные решения. Ожидаемый исход нужно определить заранее: одна допустимая операция применяется, вторая получает актуальное состояние или конфликт. Если итог зависит только от того, чья запись окажется последней, защиты от конкуренции недостаточно.

После этого проверьте старую карточку и перезапуск. Откройте кнопку из истории уже завершённой заявки. Перезапустите обработчик и повторите прежнее входящее событие на учебном стенде. Сохранённое решение и ключ повторной операции не должны исчезать только из-за очистки оперативной памяти.

Отдельно испытайте сбой между основным решением и уведомлением. Убедитесь, что повтор отправки не повторяет саму обработку заявки. Если тестируется внешнее действие, используйте доступный безопасный режим получателя. Не отправляйте реальные клиентские сообщения ради проверки этого руководства.

Сохраняйте результат каждого теста: исходное состояние, выполненные действия, ожидаемый и фактический итог. При ошибке укажите, какая операция повторилась и в какой момент. Фраза «дубли иногда появляются» почти не помогает воспроизвести проблему; точная последовательность позволяет проверить исправление.

Разработчик проверяет сценарий с ноутбуком и телефоном, экраны обращены к нему, сгенерированная иллюстрация
Проверяйте повтор, конкурирующее действие и восстановление после сбоя отдельно

Согласуйте правила до доработки бота

В описании демонстрационного бота администратора СВК показана очередь учебных заявок и сохранение решений. Страница прямо называет проект локальным демо, а не готовой системой СВК. Это пример интерфейса процесса; он не подтверждает, что все перечисленные в этой статье механизмы распределённой обработки уже реализованы в Вашем приложении.

Для постановки доработки подготовьте одну схему заявки: откуда она приходит, какой номер получает, кто принимает решение и какие действия должны произойти после него. Добавьте тесты выше и отдельно назовите результат повторной попытки. Так можно обсуждать конкретное поведение вместо общего пожелания «убрать дубли».

Чек-лист ниже сохраняет отметки в браузере, но не проверяет сервер и не блокирует заявки. Если хотите разобрать свой процесс, Умная форма позволяет описать задачу. Передайте обезличенную последовательность действий; не присылайте токены бота, доступы к базе и настоящие клиентские обращения.

Вопросы о повторной обработке

Достаточно ли отключить кнопку после нажатия

Нет. Это помогает пользователю, но не защищает от старой карточки, другого сотрудника или повторной сетевой доставки. Нужна проверка операции на стороне сохранения данных.

Можно ли считать дублем заявку с тем же телефоном

Только по согласованным правилам конкретного процесса и после проверки смысла. Один контакт может относиться к разным заказам. Совпадение телефона само по себе не доказывает технический повтор.

Что делать, если сотрудник ошибочно отклонил заявку

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

Нужно ли показывать пользователю технический ключ

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

Почему после тайм-аута нельзя сразу создавать новую заявку

Истечение ожидания не доказывает отсутствие результата. Сначала нужно проверить прежнюю операцию. Иначе новая попытка может создать вторую запись или повторить внешнее действие.

Защита от повторов устраняет любые одинаковые обращения

Нет. Она ограничивает повторный эффект одной операции. Сходные по содержанию обращения разных людей и новые заказы требуют отдельных правил поиска и разбора дублей.