Документы и цепочка
Создайте REST-сервис согласования документов. Документ имеет owner, tenant, название и состояние draft, submitted, approved, rejected или needs_changes. Каждая отправка создаёт неизменяемый version с номером, digest, временем и автором; содержимое существующей версии изменить нельзя. Цепочка approval_steps содержит от 1 до 10 последовательных обязательных шагов; каждый задаёт approver, deadline UTC и решение pending/approved/rejected/changes_requested; optional steps отсутствуют. Внутренний текст документа может быть локальной строкой до 1 MiB; файлы и внешнее хранилище не требуются. Локальные seed-пользователи owner, approver и admin имеют фиксированные роли и токены из Authorization header. Только owner создаёт версии и отправляет черновик. Администратор назначает согласующих до отправки. Согласующий видит только назначенные ему версии и может дать одно решение для текущего активного шага.
REST-контракт и правила
POST /api/v1/documents создаёт черновик; PUT /documents/{id}/draft меняет текущий черновик; POST /documents/{id}/submit фиксирует версию и активирует первый шаг; GET /documents/{id} показывает доступное пользователю состояние; POST /documents/{id}/decisions принимает decision и comment; POST /documents/{id}/resubmit создаёт новую версию после changes_requested или rejected согласно разрешению owner. Только один шаг активен одновременно. После approved шаг продвигается к следующему; когда все шаги approved, документ становится approved. Любой rejected завершает эту версию как rejected. changes_requested ставит её на паузу и возвращает owner. Rejected version неизменяема; повторное рассмотрение всегда создаёт новую version и новую цепочку. Approver передаёт expected_version и expected_step_id. Если текущая версия или шаг изменились, API отвечает 409 stale_decision и не записывает решение. Дедлайн не завершает процесс автоматически: исполнитель помечает шаг overdue и создаёт уведомление владельцу и администратору. Просроченный согласующий всё ещё может ответить, если шаг остаётся активным.
Надёжность и аудит
PostgreSQL, миграции и SQL без ORM. Решение, audit row и переход состояния фиксируются одной транзакцией. Уникальное ограничение допускает одно решение для пары version_id/step_id/approver_id. Повторный запрос с тем же decision ID возвращает прежний результат, иной текст или решение конфликтует. Исполнитель (worker) использует право обработки на 30 секунд, партии по 100 шагов и контрольную позицию; после перезапуска повторная проверка не дублирует уведомление. Никакие входящие webhooks не нужны; локальный fake notification sink показывает попытки. Содержание документов, комментарии и решения остаются в audit согласно сроку хранения; удаление tenant выполняется только администратором и логируется отдельно.
Структура программы
Предметный пакет задаёт неизменяемые версии, цепочку решений и сроки; хранилище фиксирует переходы, исполнитель формирует напоминания, HTTP проверяет назначенного согласующего. main.go только загружает конфигурацию, связывает зависимости и управляет запуском. Пакеты без циклов, общих utils и интерфейсов без потребителя. README показывает зависимости; тесты ядра без HTTP/БД, интеграционные отдельно.
Приёмка
Офлайн-тесты проверяют цепочку из трёх согласующих, неверную последовательность, отклонение, возврат на доработку, resubmit новой версии, устаревшее решение, два конкурентных решения и рестарт исполнитель по дедлайну. Проверяется, что digest старой версии не меняется и новая версия не переиспользует её решение. Критерии готовности: одновременно активен максимум один шаг; решение устаревшего шага не меняет состояние; уведомление overdue имеет уникальный ключ; все изменения и audit атомарны. SQL deadline 3 секунды, HTTP ошибки базы безопасные.