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

Последующая проверка помогает обнаружить проблемы после выполнения действия. Предварительное согласование, напротив, приостанавливает выполнение до принятия решения. Требовать разрешение целесообразно, когда потенциальное воздействие, сложность отката действия или неопределённость превышают уровень, приемлемый для автоматизации.
Оценивайте каждую операцию с помощью конкретных вопросов: может ли она изменить данные, которые трудно восстановить? Затрагивает ли она деньги, права, доступ или обязательства перед клиентами? Есть ли проверяемое правило, позволяющее выполнить её автономно? Какой ущерб может причинить ошибка и сколько времени есть на ответ? Рутинную, обратимую и ограниченную операцию можно выполнять автоматически, сохраняя запись для последующей проверки. Исключительное действие или действие с серьёзными последствиями может требовать предварительного разрешения.
Не применяйте человеческий контроль ко всему по умолчанию. Переполненная очередь приводит к задержкам и провоцирует формальное согласование без вдумчивой проверки. Установите пороговые значения и исключения, а также отслеживайте число ожидающих решений, время ожидания, отказы и истечения срока, чтобы выявлять правила, которые стоит скорректировать. Решение должно основываться на реальном риске, а не только на технической возможности выполнить операцию.
Определите, что предлагается и что можно согласовать
Проверяющему нужно понимать последствия операции, а не разбираться во внутреннем объекте PHP. Покажите текущее и предлагаемое значения, причину, источник данных, существенные последствия и любые ограничения. Если решение зависит от правила, предоставьте информацию, необходимую для его применения. Скрывайте или защищайте персональные данные, которые не нужны для проверки.
Разделяйте команду, содержащую предложение, и разрешение. Предложение описывает действие и его параметры; разрешение позволяет выполнить именно это предложение. Оно не должно предоставлять общие полномочия или позволять согласующему незаметно менять параметры. Если нужны изменения, проверяющий может запросить их; система создаёт обновлённое предложение, которое должно пройти соответствующие правила согласования.
Применяйте принцип минимальных привилегий: ограничьте круг тех, кто может создавать, согласовывать, отклонять или отменять предложения, и проверяйте эти разрешения на сервере при каждом переходе состояния. Если того требует риск, запретите автору предложения согласовывать собственную операцию. Это разделение нужно реализовать в логике разрешений, а не полагаться только на скрытие кнопок в интерфейсе.
Моделируйте состояния и переходы явно
Представьте процесс в виде конечного автомата. В начальный набор состояний могут входить pending, approved, executing, rejected, changes_requested, expired, cancelled и executed. Ожидающее предложение можно согласовать, отклонить, отменить или перевести в состояние истёкшего срока; согласованное можно передать на выполнение, только если оно всё ещё действительно; выполнение может завершиться успешно либо перейти в состояние, из которого возможно восстановление, если установлено, что эффект не наступил. Выполненное предложение нельзя согласовать повторно.
Храните текущее состояние вместе с неизменяемой историей решений и переходов. Записывайте идентификатор предложения, предыдущее и новое состояния, участника, дату и время, причину и ссылку на версию проверенных данных. Не заменяйте историю при обновлении записи: она необходима для аудита произошедшего и диагностики сбоев.
В PHP сосредоточьте переходы в доменном сервисе или эквивалентном компоненте. Не позволяйте разным контроллерам напрямую менять состояние с помощью универсальных обновлений. Проверяйте переход и разрешения внутри транзакции, когда это уместно, и отклоняйте действия, несовместимые с текущим состоянием. Такая структура уменьшает число ошибок конкурентного доступа и упрощает тестирование правил без привязки к интерфейсу.
Не допускайте устаревших согласований и повторного выполнения
Данные могут измениться, пока предложение ожидает решения. Человек не должен согласовывать условие, которое уже не соответствует выполняемой операции. При создании предложения сохраните версию, дату обновления или контрольный отпечаток соответствующих полей. При согласовании снова сравните их с текущим состоянием.
Одной проверки при согласовании недостаточно: данные могут измениться и до применения операции. Непосредственно перед выполнением повторно сравните текущую версию или контрольный отпечаток с согласованными. Если значения различаются, остановите процесс, аннулируйте разрешение для этого предложения и запросите новое решение на основании обновлённых данных. С учётом риска можно показать различия и потребовать явного подтверждения, но нельзя автоматически использовать прежнее согласование.
Срок действия ограничивает период, в течение которого решение считается действительным. После его истечения переведите предложение в состояние expired и потребуйте нового разрешения для продолжения. При каждой повторной попытке проверяйте, что разрешение всё ещё действительно; никогда не используйте истёкшее разрешение для возобновления или повторения выполнения.
Согласование должно относиться к идентифицируемому предложению и не быть повторно используемым сигналом. Чтобы два параллельно работающих обработчика не выполнили одно и то же предложение, атомарно закрепляйте за собой или блокируйте его переход из approved в executing: получить предложение может только один обработчик, если оно по-прежнему согласовано и действительно. В рамках этой локальной защиты также проверяйте идемпотентность и записывайте уникальный идентификатор операции до продолжения. Это предотвращает дублирование внутри системы, однако транзакция базы данных сама по себе не гарантирует, что внешний API применит эффект только один раз.
Если действие выполняется в другом сервисе, используйте ключ идемпотентности, который этот сервис принимает и обрабатывает идемпотентно, если такая возможность доступна. Записывайте идентификатор корреляции, номер попытки и ответы. Если ответ потерян или результат неизвестен, не повторяйте действие вслепую: запросите удалённое состояние по этому идентификатору или сверьте результат с надёжными данными. Если невозможно подтвердить, наступил ли эффект, остановите новые автоматические попытки и передайте случай оператору. Если достоверно известно, что сбой произошёл до отправки запроса, повторная попытка может быть допустима при условии повторной проверки состояния и разрешения.
Если обработчик завершился со сбоем и оставил предложение в состоянии executing, не помечайте его автоматически как выполненное и не отправляйте запрос повторно без диагностики. Процесс восстановления должен определить, наступил ли эффект, используя локальные записи и при необходимости запрашивая удалённый сервис. Если подтверждено, что эффект не наступил, предложение можно вернуть в состояние, допускающее выполнение, только после повторной проверки срока действия, данных и разрешения; если результат по-прежнему неизвестен, предложение нужно оставить заблокированным и передать на эскалацию.
Спроектируйте очередь для операторов и ручной резервный процесс
Очередь должна позволять находить ожидающие предложения по сроку нахождения в очереди, уровню воздействия, ответственному и дате истечения, а также показывать контекст, на котором основано решение. Объясняйте, почему случай заблокирован и что следует сделать: подождать, запросить изменения, отменить или передать на эскалацию. Интерфейс также должен ясно описывать, что произойдёт при согласовании, а не просто предлагать кнопки принятия решения.
Определите резервный процесс на случай сбоя зависимости, например сервиса уведомлений или интеграции, необходимой для выполнения действия. Предложение можно оставить ожидающим и предусмотреть контролируемую процедуру, позволяющую уполномоченному человеку рассмотреть случай в доступной операционной системе. Ручной процесс должен использовать те же проверки, фиксировать участника и причину, предотвращать параллельное выполнение и сверять результат после восстановления интеграции.
Не превращайте технический сбой в подразумеваемое согласование. Если невозможно проверить личность, разрешения или необходимые данные, система должна безопасно отказать: приостановить процесс, сообщить о проблеме и передать случай на эскалацию. Определите, кто может разблокировать процесс, как документируется это вмешательство и какие задачи нужно проверить после восстановления сервиса.
Тестируйте процесс и контролируйте его работу

Тестируйте доменные правила и полные сценарии: согласование, отклонение, запрос изменений, истечение срока, отмену и повторные попытки. Добавьте тесты разрешений, чтобы подтвердить, что человек без нужных полномочий не может изменить состояние, и тесты конкурентного доступа, гарантирующие, что два одновременных решения не приводят к двум выполнениям.
Проверьте сценарии, в которых данные меняются во время ожидания или непосредственно перед выполнением, удалённое выполнение завершается сбоем после принятия запроса, а ответ теряется, хотя эффект уже наступил. Убедитесь, что атомарное закрепление позволяет выполнить предложение только одному обработчику, повторные попытки отклоняют истёкшие разрешения, а каждое ручное вмешательство оставляет достаточную запись. В production отслеживайте количество и срок ожидания предложений, истечения срока, ошибки выполнения и случаи, требующие сверки.
Хорошо спроектированный процесс сохраняет человеческий контроль там, где он действительно нужен, и не полагается на память команд в вопросах безопасности. Явные состояния, разделённые разрешения, актуальные проверенные данные, идемпотентное выполнение и понятный порядок действий при сбоях превращают неформальное согласование в проверяемый процесс.



