Перейти к основному содержимому

Support Service

Назначение

support («Служба заботы») — greenfield PIN-внутренний back-office-контекст для контроля качества и поддержки. Владеет: обращениями пользователей (тикеты) с очередью/маршрутизацией и перепиской, базой знаний/FAQ, шаблонными ответами и арбитражными делами (конфликты/жалобы). Источник требований — ТехЗадание/Служба заботы.html (разделы 1–9 матрицы) + Список фич §11; роль в ролевой модели — technical-assignment.md, glossary «Служба заботы» (glossary.md). Уровни доступа: наблюдатель / оператор / супервизор.

Покрывает матрицу 4.3–4.7 (тикеты, шаблоны, база знаний, уведомления о событиях), 6.1–6.5 (арбитраж), 8.1/8.4 (дашборд обращений). Разделы 2.x (сквозной мониторинг), 3.x (ручные вмешательства), 5.x (документы/юр.этапы), 7.3/7.4 (вебинары/прогресс), 9.x (справочники/внешний Service Desk) — НЕ owned здесь: реализуются как platform-scope доступ к чужим сервисам через permissions либо отложены (см. Ответственность И Границы / Ограничения).

Metadata

ПолеЗначение
Статусdraft
Owning teamPIN Platform (Care squad)
DomainПоддержка/контроль качества
Data classificationMixed (PII заявителей: ФИО/телефон/email реплики; тексты обращений)
Runtime criticalitymedium (не в денежной воронке, но SLA поддержки)

Ownership

ArtifactOwner/Rule
PostgreSQL schemasupport
API areaspublic (заявитель) / support (наблюдатель/оператор/супервизор); admin — вне контекста (эскалация в админ-панель, ADR-0053)
Owned entitiesTicketEntity (+child TicketMessageEntity), TicketQueueEntity, KnowledgeBaseArticleEntity, TicketTemplateEntity, ArbitrageCaseEntity (+child ArbitrageNoteEntity)
Produced eventspin.support.ticket-created.v1, pin.support.ticket-message-posted.v1, pin.support.ticket-status-changed.v1, pin.support.arbitrage-resolved.v1
Consumed eventspin.identity.users.v1 (display-реплика заявителя)
Search indexesElasticsearch-проекции (Talent.Common.Domain.Search, ADR-0059): support-tickets — внутренний поиск тикетов по номеру/теме/заявителю (search/tickets.md); support-kb-articles — публичный поиск базы знаний по заголовку/тексту/тегам (search/kb.md). Арбитраж — низкий объём: PostgreSQL ILIKE по номеру/заголовку, без отдельного индекса (ADR-0056)
Analytics factsнет (дашборд обращений 8.1/8.4 — SQL-query по своим таблицам; ClickHouse не используется, объём мал при 1–10k активных, ADR-0056; ClickHouse только dev-stats, ADR-0057)

Ответственность И Границы

  • Делает: приём входящих обращений (форма обратной связи 4.4) в единую очередь; маршрутизация по очередям (TicketQueue) и категориям; управление тикетами — взятие в работу, назначение/переназначение оператора, приоритет, смена статуса New/InWork/Resolved/Closed, реопен (4.5); переписка по тикету (публичные ответы заявителю + внутренние заметки операторов, 4.1 в рамках тикета); шаблонные ответы (4.3); база знаний/FAQ с полнотекстовым поиском (Elasticsearch) и аудиторией Public/Authenticated/Internal (4.7, 7.1, 7.2); арбитраж конфликтов/ жалоб — регистрация (6.1), расследование с привязкой к аудиту/чатам/истории (6.2), вынесение решения (6.3), информирование сторон событием (6.4), эскалация администратору (6.5); эскалация тикетов супервизору/ администратору; дашборд обращений и анализ тематик (8.1, 8.4); SLA-мониторинг просрочек первого ответа/ решения (4.6) фоновым worker-ом.
  • Не делает (границы):
    • Сквозной просмотр пользователей/проектов/сделок/финансов (2.x) — support читает чужие сервисы (identity/chessboard/crm/rewards) их API в platform-scope, как Менеджер ПИН; копий не хранит.
    • Ручные вмешательства (3.x: правка профиля/роли/блокировка — identity; смена статуса сделки/ переназначение клиента/сделки — crm; ручной резерв/освобождение объекта недвижимости — chessboard; корректировка комиссий — rewards; модерация постов — news; массовые/системные уведомления — notifications) выполняются в owning-сервисах через выданные support platform-scope permissions; support фиксирует факт/ссылку в ArbitrageNoteEntity/аудите, но чужие таблицы не пишет и permissions не дублирует.
    • Чаты сделок/групповые чаты (4.1/4.2) — владелец messenger (ADR-0055); support подключается к чату через messenger в platform-scope; TicketMessageEntityотдельная переписка тикета, не чат сделки.
    • Документы/юр.этапы (5.x) — crm/profile/storage (support вызывает их decide/request-document ручки в scope). Хранение бинарей вложений — storage/CDN (только FileId в метаданных).
    • Централизованный аудит-лог для расследований — стемпинг IAuditableEntity/таймлайны в каждом сервисе + лог-хранилище (ELK); support только читает/ссылается.
  • Source of truth: тикеты, очереди, переписка тикетов, база знаний, шаблоны, арбитражные дела — support.
  • Реплики/проекции: RequesterName/RequesterPhone/RequesterEmail — display-реплика из identity (консюмер user-upserted, version-guard); связи RelatedDealId/RelatedPropertyObjectId/RelatedCompanyId/ ArbitrageCaseId — логические id без загрузки чужих агрегатов.

Зависимости

ЗависимостьТипЗачем НужнаFailure mode
PostgreSQL (support)dbвсе агрегаты (source of truth), outboxнедоступность = недоступность сервиса
Elasticsearchsearchsearch-проекции support-tickets/support-kb-articles (ADR-0059, Talent.Common.Domain.Search)недоступность = поиск тикетов/базы знаний недоступен; PostgreSQL source of truth и команды работают
Kafkaqueueoutbox-исходящие + консюмер identityдеградация: события копятся в outbox, реплика заявителя отстаёт
identityservice/eventsactor/permissions (JWT, Scoped-RBAC platform-scope), lookup пользователей/компаний для карточки тикета; консюмер user-upserted для репликинедоступность = 401/503 на командах, реплика eventual
crm / chessboard / rewards / profile / newsserviceсквозной просмотр (2.x) и ручные вмешательства (3.x/5.x) через platform-scope API этих сервисоввмешательство/просмотр недоступны, own-функции тикетов работают
messengerserviceподключение оператора к чатам сделок (4.1) и внутренние чаты (4.2) в platform-scopeчат недоступен, тикеты работают
notificationsconsumer наших событийоповещение операторов/заявителя/сторон конфликтапри отставании уведомления запаздывают (eventual)
storage/CDNexternalпресайн вложений тикетов/KBзагрузка/скачивание вложений недоступны

Проекты

src/services/support/
Pin.Support.Api/ # области public / support (admin — вне контекста, ADR-0053)
Pin.Support.Application/ # сценарии операций (команды/запросы) над обращениями, очередями, базой знаний, арбитражем
Pin.Support.Domain/ # сущности, инварианты, доменные события
Pin.Support.Infrastructure/ # БД support, исходящие интеграционные события (outbox), Elasticsearch-проекции (поиск)
Pin.Support.Background/ # worker SLA-мониторинга + консюмер реплики пользователей + проекторы/rebuild поиска
Pin.Support.Migrator/ # миграции + SEED (дефолтные очереди, шаблоны/KB-категории; каталог permissions -> identity SEED)
Pin.Support.Contracts/ # контракты интеграционных событий pin.support.*

Основные Сущности

EntityНазначениеSource Of TruthДокумент
TicketEntityОбращение: статус/приоритет/очередь/assignee/связьsupportdomain/ticket.md
TicketMessageEntityПереписка тикета (Public/Internal), append-onlysupportdomain/ticket-message.md
TicketQueueEntityОчередь/маршрутизация обращенийsupportdomain/ticket-queue.md
KnowledgeBaseArticleEntityБаза знаний/FAQ, поиск (Elasticsearch), аудиторияsupportdomain/knowledge-base-article.md
TicketTemplateEntityШаблонные ответыsupportdomain/ticket-template.md
ArbitrageCaseEntity (+ArbitrageNoteEntity)Арбитраж конфликтов/жалоб, решение, заметки/аудит-ссылкиsupportdomain/arbitrage-case.md

ER-обзор

API

Карта областей (детальные документы — api/<area>/<method>.md):

  • Public (/api/public/support/...): подать тикет (create-ticket), мои тикеты (list-my-tickets, get-my-ticket), ответ по своему тикету (add-ticket-message), база знаний (search-kb-articles — поиск Elasticsearch, get-kb-article). Заявитель = любой авторизованный пользователь; чтение public-KB допускает Anonymous.
  • Support (/api/support/...):
    • тикеты: list-tickets (фильтры статус/очередь/assignee/приоритет/категория/заявитель/даты), get-ticket, create-ticket-on-behalf, take-ticket, assign-ticket, change-ticket-status, update-ticket (приоритет/категория/очередь), escalate-ticket, reply-ticket (Public/Internal + apply template);
    • очереди: list/create/update/archive-queue;
    • база знаний: list/create/update/publish/archive-kb-article;
    • шаблоны: list/create/update/archive-template;
    • арбитраж: list/get/register/add-note/resolve/escalate-arbitrage-case;
    • дашборд: get-support-dashboard.
  • Admin: вне контекста — приём эскалаций админ-панелью (ADR-0053).

Events И Consumers

TypeNameTopicНазначениеLink
ProducedTicketCreatedEventpin.support.ticket-created.v1новый тикет; консюмер notifications (оповестить дежурных операторов/очередь)events/ticket-created.md
ProducedTicketMessagePostedEventpin.support.ticket-message-posted.v1публичный ответ оператора (reply-ticket, Visibility = Public); notifications уведомляют заявителя о новом ответе (4.1/4.5)events/ticket-message-posted.md
ProducedTicketStatusChangedEventpin.support.ticket-status-changed.v1смена статуса (взятие/решение/закрытие/реопен); notifications информируют заявителя (4.5/4.6)events/ticket-status-changed.md
ProducedArbitrageResolvedEventpin.support.arbitrage-resolved.v1решение по конфликту вынесено/эскалировано; notifications информируют стороны (6.4)events/arbitrage-resolved.md
Consumedpin.identity.users.v1identitydisplay-реплика заявителя (ФИО/телефон/email) на тикетах/арбитраже (fan-out по RequesterUserId), version-guardconsumers/identity-user-upserted.md

Все produced-события несут ContractVersion и публикуются через транзакционный outbox; консюмер идемпотентен по идентификатору сообщения (MessageId) и защищён version-guard.

Кросс-сервисные контракты и окна согласованности

Что support ожидает от соседей (входные гарантии) и что предоставляет (исходящие гарантии). Ключ партиционирования исходящих событий — AggregateId (TicketId/ArbitrageCaseId): гарантирует порядок событий по одному обращению/делу; между разными агрегатами порядок не гарантируется.

НаправлениеКонтрактКлюч / версияГарантии и окно согласованности
Ожидает от identitypin.identity.users.v1 — display-реплика заявителя (RequesterUserId, FullName, Phone, Email, монотонная IdentityRequesterVersion)key UserId; version-guardРеплика eventual: применяется, только если версия события ≥ сохранённой; отставание консюмера → карточки показывают устаревшее/пустое ФИО до догоняющего события. Отсутствие реплики на момент создания тикета — штатно (поля null), не блокирует
Ожидает от identity (sync)внутренняя проверка оператора при assign-ticket (существование, активность, support.tickets.manage)синхронный вызовНедоступность → IDENTITY_UNAVAILABLE (503, retry); own-функции тикетов не зависят от этого вызова
Предоставляетpin.support.ticket-created.v1 — новое обращение (TicketId, Number, QueueId, Category, Priority, RequesterUserId, Channel)key TicketId; ContractVersion = 1At-least-once через outbox; консюмер notifications идемпотентен по MessageId. Заявитель видит тикет сразу (read-your-writes), дежурный оператор — после доставки события (секунды)
Предоставляетpin.support.ticket-message-posted.v1 — публичный ответ оператораkey TicketId; ContractVersion = 1Уведомление заявителю о новом ответе — после доставки (окно eventual); в промежутке заявитель видит ответ при следующем открытии карточки
Предоставляетpin.support.ticket-status-changed.v1 — смена статуса (взятие/решение/закрытие/реопен)key TicketId; ContractVersion = 1Порядок статусных событий по одному тикету сохранён (общий ключ). Уведомление заявителя/оператора — eventual; статус в карточке меняется сразу после коммита
Предоставляетpin.support.arbitrage-resolved.v1 — решение/эскалация по конфликтуkey ArbitrageCaseId; ContractVersion = 1Стороны конфликта уведомляются только по этому событию (не по заметкам расследования); окно eventual

Внутренние справочники (очереди, шаблоны, база знаний) интеграционных Kafka-событий не публикуют — их изменения видны через search-проекции (support-tickets/support-kb-articles, свежесть < 1 мин, ADR-0059) и прямые чтения source of truth; внешние контексты о них не знают.

Runtime И Эксплуатация

ОбластьТребование
Health checks/health/live, /health/ready (PostgreSQL + Kafka), /health/startup (миграции применены)
SLO/SLAкоманды p95 < 500 мс; списки/поиск p95 < 1 с; SLA-worker — точность цикла ≤ 1 мин; свежесть search-проекций < 1 мин
Background jobsticket-sla-monitor (цикл 1 мин; выборка просроченных обращений под блокировку; помечает SlaBreached, эмитит уведомление о просрочке первого ответа/решения) — идемпотентен (ADR-0056)
DashboardGrafana pin-support (ops) + продуктовый дашборд обращений (get-support-dashboard, SQL)
Alertsлаг консюмера identity > 5 мин; лаг search-проекций (support-tickets/support-kb-articles) > 5 мин; outbox backlog > 1000; рост SlaBreached/просрочек; ошибки worker-а
Runbooksdocs/06-services/support/runbooks/ (будущий путь по конвенции)
Feature flagsвнешний Service Desk (Jira/Zendesk, 9.2) — выключенный модуль (отложено)

Безопасность

  • Actor types: Заявитель (любой авторизованный пользователь / Anonymous для public-KB), Служба заботы — Наблюдатель / Оператор / Супервизор (все platform-scope), Админ-панель (ADR-0053, приём эскалаций), система (worker/consumer).
  • Permission model: Scoped-RBAC (identity/domain/permission.md), platform-scope + record-level (AssigneeUserId/QueueId). Каталог support-permissions (SEED в identity): support.tickets.view (наблюдатель), support.tickets.manage (оператор: take/reply/status/update/create-on-behalf), support.tickets.assign (супервизор), support.tickets.escalate (супервизор), support.queues.manage (супервизор), support.kb.view/support.kb.manage, support.templates.manage, support.arbitrage.view/support.arbitrage.manage/support.arbitrage.decide (супервизор), support.dashboard.view. Уровни доступа = профили permissions: Наблюдатель = *.view (+ cross-service *.view для 2.x); Оператор = + tickets/kb/templates/arbitrage .manage (+ выданные crm/identity/ chessboard интервенционные perms для 3.x); Супервизор = + assign/escalate/queues/arbitrage.decide (+ финансовые/блокировка 3.6/3.8, эскалация 6.5).
  • Cross-service интервенции (3.x/5.x): permissions живут и проверяются в owning-сервисах (crm.*/identity.*/chessboard.*/rewards.*/news.*/notifications.*); support-роли назначаются в platform-scope; support их НЕ определяет и НЕ дублирует (описано как зависимость).
  • Tenant isolation (ADR-0052): support — PIN-внутренний single-tenant platform-tool; owned-сущности несут TenantId = PIN CompanyId (для SDK ITenantedEntity), доступ — по platform-scope permissions, не по агентство/застройщик-tenant. Контекст заявителя — явные колонки RequesterCompanyId/RequesterRole (для фильтра/маршрутизации), не tenant.
  • Sensitive/PII: ФИО/телефон/email заявителя (PII, реплика; маска в логах +7***1234, И***); тексты обращений/заметок — Internal; вложения — метаданные здесь, бинарь в storage. Внутренние заметки (Visibility=Internal) не видны заявителю (public-API исключает).
  • Audit: IAuditableEntity + Version на всех агрегатах; переписка тикета и заметки арбитража — append-only (модерация — soft-delete); арбитражное решение фиксируется иммутабельно (ArbitrageDecision owned + DecidedByUserId).

Известные Ограничения И Assumptions

  • ClickHouse/facts не используются: объём тикетов мал (1–10k активных, ADR-0056); дашборд/анализ — SQL по своим таблицам; экспорт отчётов в Excel/PDF (8.5) — отложен (report-export, будущее).
  • Внешний Service Desk (Jira/Zendesk 9.2), вебинары/трансляции (7.3) и трекинг прогресса обучения (7.4) — вне первой версии; база знаний покрывает 7.1/7.2.
  • Ревизионная история KB (полный snapshot по образцу news) — отложена; версия контента через Version + аудит.
  • Отчёты «проблемные сделки» (8.2) и «активность пользователей» (8.3) — читаются из crm/user-activity их API в platform-scope, не дублируются в support.

Принятые решения контекста

#ВопросРешение
1Тенантность supportsingle-tenant platform-tool, TenantId = PIN CompanyId, доступ platform-scope (ADR-0052) — не агентство/застройщик-tenant
2Ручные вмешательства и сквозной просмотр (2.x/3.x/5.x)не owned; выполняются в owning-сервисах через выданные permissions; support фиксирует ссылку в арбитраже/аудите
3Переписка тикета vs чат сделкиTicketMessageEntity — свой тред тикета; чаты сделок/групповые — messenger (ADR-0055), подключение в platform-scope
4Арбитраж «через аудит»ArbitrageNoteEntity.AuditRefs (jsonb) ссылается на аудит-записи чужих сервисов/лог-хранилище; support не владеет аудит-логом
5Поиск и таймерыполнотекстовый поиск тикетов/базы знаний — Elasticsearch-проекции (ADR-0059, Talent.Common.Domain.Search; PostgreSQL — source of truth); арбитраж — ILIKE (низкий объём); SLA/просрочки — polling-worker (ADR-0056); без Redis/ClickHouse
6Эскалация администратору (6.5)статус Escalated + arbitrage-resolved (Outcome=EscalatedToAdmin) / для тикетов — флаг + переназначение; приём — админ-панель (ADR-0053)

Связанные Документы