← Все проекты уровня 13
Уровень 13 · Сервис для нескольких организацийВариант B

Helpdesk

Тикеты нескольких компаний с клиентской видимостью сообщений, SLA и рабочими календарями.

Техническое задание

Tenant, роли и тикеты

Создайте REST helpdesk для нескольких компаний. Локальный seed создаёт customer, agent, team_lead и tenant_admin в двух tenant; фиксированные токены читаются только из Authorization header, actor и tenant определяются по токену. Каждые customer, team, agent, ticket, message и SLA calendar имеют tenant_id. Роли customer, agent, team_lead и tenant_admin определяют действия: customer создаёт тикет и читает свои тикеты и публичные сообщения; agent читает тикеты назначенной команды, меняет статус и пишет customer-visible или internal сообщение; team_lead управляет назначением, приоритетом и командным календарём; tenant_admin настраивает участников, SLA и календарь компании. Любая выборка, счётчик и вложенный маршрут фильтруются по проверенному tenant и доступу пользователя. Чужой или скрытый ресурс отвечает одинаковым 404.

Ticket содержит subject 1–160 символов, priority low/normal/high/urgent, status open/pending/resolved/closed, requester, team, assignee, created_at и revision. Сообщение содержит body 1–5000 символов, visibility public/internal, автора и время; HTML хранится как обычный текст и не исполняется. Customer не может запросить internal сообщения, даже задав visibility в query. Правила переходов: open -> pending/resolved; pending -> open/resolved; resolved -> closed/open; closed терминален. Новое публичное сообщение клиента переводит resolved в open, но не меняет closed. PATCH тикета требует expected_revision; при несовпадении возвращается 409 stale_revision с текущим номером. GET/POST /api/v1/tickets, GET/PATCH /tickets/{id}, GET/POST /tickets/{id}/messages и GET /api/v1/reports/sla реализуют эти действия.

SLA и календарь

Календарь задаётся IANA timezone, рабочими днями и локальными интервалами. SLA first_response равен 4 рабочим часам для urgent, 8 для high и 24 для normal/low. Решение о выполнении — время первого публичного сообщения агента. Пауза ticket pending останавливает SLA, возврат в open продлевает deadline на точную длительность паузы. Остаток рассчитывается по снимку календаря, который действовал при открытии тикета. Выходные и DST учитываются согласно timezone tenant; несуществующее локальное время пропускается, неоднозначный час использует более ранний offset. Изменение календаря действует только для тикетов, открытых после изменения; deadline не пересчитывается по новому календарю. GET ticket показывает UTC deadline и исходную timezone.

Напоминание о просрочке создаётся один раз на ticket и SLA deadline. Исполнитель (worker) сохраняет контрольную позицию и запись outbox в PostgreSQL; локальная имитация почтового получателя принимает ключ идемпотентности. Максимум 200 просрочек за партию; после перезапуска исполнитель повторяет незавершённую партию. Внешняя доставка может повториться, exactly-once не обещается. Все SQL операции имеют context timeout 3 секунды.

Структура программы

Предметные правила задают видимость сообщений, переходы тикета, календарь и паузу SLA; HTTP и SQL отдельно отвечают за транспорт и хранение. main.go только загружает конфигурацию, связывает зависимости и управляет запуском. Пакеты без циклов, общих utils и интерфейсов без потребителя. README показывает зависимости; тесты ядра без HTTP/БД, интеграционные отдельно.

Аудит и приёмка

Изменения статуса, назначения, priority, календаря, участников и видимости сообщения атомарно создают audit row с actor, tenant, объектом и временем. PostgreSQL, ручной SQL и миграции обязательны. Тестовая база имеет две компании, разных команд и клиентов; тесты просят customer прочитать internal сообщение, чужой ticket, отчёт и счётчик. Другие проверки покрывают все переходы, stale revision, business-hours расчёт через DST, pause/resume SLA, смену календаря и остановку исполнителя до и после deadline. Перезапуск не теряет долг и не создаёт второй внутренний факт напоминания. Критерии готовности: customer никогда не видит internal текст; неправильный tenant не раскрывается; дедлайн для фиксированных часов соответствует календарю; аудит и изменение тикета фиксируются одной транзакцией.

Критерии готовности

Ожидаемый результат

Проверяемый результат

  • README показывает границы и направление зависимостей; main.go только связывает конфигурацию и запуск; модульные тесты ядра обходятся без HTTP и БД, интеграционные тесты отделены.
  • Helpdesk запускается локально с PostgreSQL; customer, agent, team_lead и tenant_admin имеют ровно указанные действия.
  • Запросы тикетов, сообщений, отчётов и счётчиков изолированы по tenant; internal сообщения недоступны customer.
  • Условная ревизия возвращает 409 при stale write и не теряет последнее изменение.
  • SLA учитывает приоритет, рабочие интервалы, pending, DST и неизменяемый UTC deadline после изменения календаря.
  • Детерминированный исполнитель переживает остановку, сохраняет контрольную позицию, пишет одно outbox событие на deadline и использует имитацию получателя.
  • Тесты проверяют две компании, все переходы, роли, приватность, DST, аудит и повтор после перезапуска.
  • Контракт маршрутов, правила календаря, типы сообщений и команды локального запуска задокументированы.
  • Счётчики и отчёты используют те же ограничения организации и роли, что и списки тикетов.