83 KiB
Backend Endpoints Competence Map Association Map
Назначение: спецификация backend-контракта для страниц:
Все сервисы / Карта компетенций / Карта кластеров(/new-map);Все сервисы / Карта компетенций / Карта технопарков(/new-map/technoparks);Все сервисы / Карта компетенций / Карта ОЭЗ(/new-map/sez).
Документ нужен как задача для backend-разработки. Цель: реализовать endpoint-ы, которые возвращают все серверное состояние новых страниц карты по участию организаций ОПК в инновационных объединениях:
- значения фильтров;
- доступные разделы карты;
- доступные типы отображения карты;
- выбранные субъекты РФ или федеральные округа как фильтр карты;
- KPI-карточки;
- данные для раскраски и tooltip-ов карты субъектов РФ;
- данные для раскраски и tooltip-ов карты федеральных округов;
- список объединений, в числе участников которых есть организации ОПК;
- рейтинг субъектов РФ;
- счетчик и перечень организаций ОПК для табличного представления.
Документ является дополнением к
docs/backend-endpoints-competence-map-analytics-from-layouts.md.
Он не заменяет endpoint-ы аналитической панели /dashboard.
Принципы контракта
- snapshot страницы карты должен возвращаться одним page-level endpoint-ом;
- справочники фильтров должны возвращаться отдельным endpoint-ом;
- табличный перечень организаций должен возвращаться отдельным collection endpoint-ом, потому что это потенциально большой массив и он открывается отдельным состоянием страницы;
- контракт описывает серверное состояние страницы, а не конкретную визуальную реализацию UI;
- frontend может локально решать, показывать
association_typeкак route tabs или navigation links, какие иконки использовать, как отрисовывать SVG-карту и как скроллить таблицы; - backend должен возвращать все данные, необходимые для всех серверно значимых состояний страницы: фильтры, выбранные субъекты/округа, KPI, строки рейтинга, строки списка объединений, строки таблицы организаций, id сущностей и значения для drilldown;
- все значения должны возвращаться в raw-формате: number, string, boolean, null;
- проценты должны возвращаться числом
0..100, а не долей0..1; - даты должны возвращаться в ISO 8601 с timezone;
- каждый объект, который может использоваться для drilldown или ссылки, должен иметь стабильный
id; - все массивы должны возвращаться уже отсортированными, если для блока задан серверный порядок;
- все KPI должны иметь стабильный
code; 0должен возвращаться как валидное значение, а не какnull, строка или—;nullдопустим только для реально отсутствующего или неприменимого значения;- backend должен обеспечивать консистентность KPI, карты, рейтинга, списка объединений и таблицы организаций внутри одного набора фильтров.
Важное ограничение по числам в примерах:
- числовые значения в JSON-примерах взяты из референсных экранов и текущих mock-данных страницы;
- реальные значения должны быть рассчитаны по утвержденной backend-методике;
- если методика конкретного показателя не утверждена, ее нужно зафиксировать до реализации endpoint-а;
- если число на референсном экране противоречит смыслу поля, backend-контракт должен сохранять корректную семантику поля, а не повторять ошибочное или placeholder-значение макета.
REST и HTTP правила
Модель ресурсов
Endpoint-ы описывают серверные представления ресурсов, а не действия UI.
В рамках этих страниц есть три ресурса:
/api/v1/competence-map/association-map/filters/- справочники и доступные значения фильтров;/api/v1/competence-map/association-map/- snapshot карты для выбранных фильтров;/api/v1/competence-map/association-map/organizations/- табличный перечень организаций ОПК для выбранных фильтров.
Требования:
- не добавлять action-style endpoint-ы вида
/getMap,/loadMapFilters,/selectRegion; - не использовать frontend path
/new-mapв API path; association_typeявляется query-параметром одного ресурса карты, а не отдельным URI-сегментом;- выбранные на карте субъекты РФ и федеральные округа являются query-параметрами snapshot-а, а не отдельными action endpoint-ами;
- path должен оставаться стабильным в рамках
/api/v1; breaking changes требуют новой версии API.
HTTP методы
Для всех endpoint-ов используется только GET.
Причина:
- запросы только читают данные;
- запросы не меняют серверное состояние;
- результат полностью определяется URI, query params, правами пользователя и актуальностью данных.
GET request body не используется. Все параметры выбора представления передаются только через query params и headers.
POST для этих экранов не нужен. Его можно вводить только для отдельных будущих сценариев:
- создание export job;
- сохранение пользовательского набора фильтров;
- тяжелый search/report request, который не помещается в URL и имеет отдельный жизненный цикл.
Media type
Request:
Accept: application/json
Успешный response:
Content-Type: application/json; charset=utf-8
Content-Language: ru-RU
Error response:
Content-Type: application/problem+json; charset=utf-8
Query params
Для /association-map/filters/ и /association-map/ используется единый набор query params:
association_type:clusters | technoparks | sez, optional, defaultclusters;map_view_mode:regions | federal_districts, optional, defaultregions;federal_district_id: string, optional;region_id: string, optional;federal_district_ids: array of string, optional;region_ids: array of string, optional;industry_id: string, optional;integrated_structure_id: string, optional;organization_id: string, optional;competence_id: string, optional.
Для /association-map/organizations/ дополнительно используются query params:
limit: integer, optional, default500, maximum1000;offset: integer, optional, default0;sort_field: string, optional, defaultserial_number;sort_direction:asc | desc, optional, defaultasc.
Правила:
- если фильтр не выбран, параметр лучше не передавать;
- если frontend передал пустую строку, backend должен нормализовать ее в
nullили пустой массив вapplied_filters; - неизвестные query params должны возвращать
400 Bad Request, чтобы опечатки не игнорировались молча; - повтор одного и того же query param запрещен, кроме массивов
region_idsиfederal_district_ids; - массивы передаются в canonical form как repeated query params:
region_ids=RU-MOS®ion_ids=RU-MOW
federal_district_ids=cfo&federal_district_ids=pfo
- значения
idсравниваются как case-sensitive строки; - все примененные значения backend возвращает в
applied_filtersуже нормализованными; region_idи элементыregion_idsдля карты должны соответствовать SVG-id субъектов РФ в форматеRU-*, напримерRU-MOS,RU-MOW,RU-TA;federal_district_idи элементыfederal_district_idsдолжны соответствовать стабильным id федеральных округов, напримерcfo,pfo,ufo,sfo,dfo,nwfo,skfo,yufo;- если
region_idпередан вместе сfederal_district_id, backend должен проверить, что регион входит в указанный федеральный округ; - нельзя одновременно передавать
region_idиregion_ids; - нельзя одновременно передавать
federal_district_idиfederal_district_ids; - нельзя одновременно передавать
region_idsиfederal_district_ids; - нельзя одновременно передавать одиночные geography-фильтры (
region_id,federal_district_id) и multi-selection-фильтры карты (region_ids,federal_district_ids); - если фильтры валидны, но данных по ним нет, возвращается
200 OKс пустыми массивами; - если значение фильтра не существует или недоступно пользователю, возвращается ошибка валидации.
Canonical examples:
GET /api/v1/competence-map/association-map/filters/?association_type=clusters
GET /api/v1/competence-map/association-map/?association_type=clusters
GET /api/v1/competence-map/association-map/?association_type=technoparks&map_view_mode=regions®ion_ids=RU-MOS®ion_ids=RU-MOW
GET /api/v1/competence-map/association-map/?association_type=sez&map_view_mode=federal_districts&federal_district_ids=cfo&federal_district_ids=pfo
GET /api/v1/competence-map/association-map/organizations/?association_type=clusters®ion_ids=RU-MOS&limit=500&offset=0
Не использовать как canonical form:
GET /api/v1/competence-map/association-map/?association_type=clusters®ion_id=&federal_district_id=
GET /api/v1/competence-map/association-map/?association_type=clusters®ion_id=RU-MOS®ion_ids=RU-MOW
Первый request можно поддержать для совместимости, но backend все равно должен вернуть пустые значения как
null или [] в applied_filters.
Второй request должен возвращать 400 Bad Request, потому что одиночный фильтр и multi-selection фильтр
конфликтуют.
Headers и трассировка
Каждый response должен содержать:
X-Request-Id: <request id>
Требования:
- если frontend передал
X-Request-Id, backend должен использовать его или вернуть связанный id; - если id не передан, backend генерирует его сам;
- этот id должен попадать в backend logs, чтобы можно было связать ошибку UI и серверный запрос;
- технические debug-поля не добавляются в JSON payload, для них используются headers и логи.
Авторизация и права доступа
Endpoint-ы должны учитывать права текущего пользователя.
Правила:
401 Unauthorizedвозвращается, если пользователь не аутентифицирован;403 Forbiddenвозвращается, если пользователь аутентифицирован, но не имеет доступа к странице или разделу;- для
401 Unauthorizedbackend должен вернутьWWW-Authenticate, если это предусмотрено текущей схемой аутентификации; - если пользователь имеет доступ к странице, но часть справочников или данных ему недоступна, backend должен отфильтровать эти данные на сервере;
filtersне должен раскрывать id сущностей, к которым у пользователя нет доступа;disabled = trueможно использовать только для значений, которые пользователь видит, но которые неприменимы при текущих фильтрах;- значение фильтра, недоступное пользователю, обрабатывается как invalid filter value и не должно раскрывать, существует ли такая сущность в системе.
Кэширование и условные GET
Данные зависят от пользователя, прав доступа и фильтров, поэтому shared caching должен быть запрещен.
Recommended headers для успешных responses:
Cache-Control: private, max-age=60
Vary: Authorization, Accept
ETag: "<user-and-query-specific-etag>"
Last-Modified: <data_actual_at as HTTP-date>
Требования:
ETagдолжен учитывать пользователя, права доступа, endpoint path, query params и версию/актуальность данных;Last-Modifiedдолжен соответствовать актуальности данных, а не времени генерации response;- backend должен поддерживать
If-None-Matchи может вернуть304 Not Modified, если представление не изменилось; - при
304 Not Modifiedтело response не возвращается; - для персональных данных нельзя использовать
Cache-Control: public; - если backend пока не готов поддерживать validators, нужно явно вернуть
Cache-Control: no-store.
Актуальность и методика расчета
Response содержит два разных времени:
generated_at- когда backend сформировал текущий response;data_actual_at- на какой момент актуальны данные карты и таблиц.
Требования:
generated_atне использовать как дату актуальности данных;data_actual_atдолжен быть единым для snapshot-а карты и таблицы организаций при одинаковых фильтрах;- если разные блоки приходят из источников с разной актуальностью, backend должен либо привести их к единому согласованному snapshot-у, либо явно согласовать и задокументировать методику;
- версия методики расчета уникальности организаций, участий и принадлежности к регионам должна быть зафиксирована в backend-документации;
- если backend поддерживает версионирование методик, response может дополнительно возвращать
calculation_methodology_version; - изменение методики, которое меняет смысл существующих полей, считается breaking change для
/api/v1.
Большие справочники и массивы
Dashboard-массивы map.regions, map.federal_districts, association_list.rows и
region_ranking.rows возвращаются полностью.
Табличный перечень организаций может быть большим, поэтому он вынесен в отдельный endpoint
/association-map/organizations/ с limit и offset.
Требования к таблице организаций:
limitdefault500;limitmaximum1000;offsetdefault0;total_countвсегда возвращается независимо отlimit;unique_organizations_countвозвращается отдельно от количества строк, потому что одна организация может иметь несколько строк участия;- если текущий UI хочет показать таблицу без пагинации, frontend может запросить
limit=1000; - если
total_count > limit, frontend должен догружать следующие страницы или включить пагинацию/виртуализацию.
Для справочника organizations в /association-map/filters/ также возможен большой объем данных.
Если список может превышать безопасный размер для одного response, backend должен выбрать один из вариантов
до реализации:
- возвращать только доступные организации в пределах текущих фильтров, если объем гарантированно небольшой;
- добавить к
/association-map/filters/query paramsorganization_searchиorganization_limit; - вынести поиск организаций в отдельный endpoint справочника.
Если вводятся organization_search и organization_limit, правила должны быть такими:
organization_search: string, optional, минимум 2 символа после trim;organization_limit: integer, optional, default50, maximum100;- сортировка организаций: релевантность поиска, затем
labelпо возрастанию (ru-RU); - выбранная в
organization_idорганизация должна присутствовать вorganizations, даже если она не попала бы в первыеorganization_limitрезультатов.
Термины и правила подсчета
association_type
Тип инновационного объединения для карты.
Допустимые значения:
clusters: промышленные кластеры;technoparks: технопарки;sez: особые экономические зоны.
Для страниц карты значение all не используется.
Объединенный режим всех типов относится к аналитической панели /dashboard и описан в отдельном документе.
Соответствие frontend routes:
| Route | association_type |
|---|---|
/new-map |
clusters |
/new-map/technoparks |
technoparks |
/new-map/sez |
sez |
map_view_mode
Тип отображения карты.
Допустимые значения:
regions: карта субъектов РФ;federal_districts: карта федеральных округов.
map_view_mode сам по себе не меняет методику подсчета данных. Он определяет активное визуальное
представление и то, какие id frontend будет передавать при клике по карте.
region_id
Идентификатор субъекта РФ для карты.
Требования:
- должен совпадать с SVG-id субъекта РФ в компоненте карты;
- формат:
RU-*; - примеры:
RU-MOS,RU-MOW,RU-TA,RU-CHE; - если у backend-а есть внутренний id региона, backend должен уметь сопоставить его с
map_region_id.
federal_district_id
Идентификатор федерального округа.
Требования:
- стабильный string id;
- используется в фильтрах, карте федеральных округов и связке субъектов РФ с ФО;
- рекомендуемые значения:
cfo,nwfo,yufo,pfo,ufo,sfo,dfo,skfo.
region_ids и federal_district_ids
Множественный выбор на карте.
Назначение:
region_idsиспользуется, когда пользователь выбрал один или несколько субъектов РФ на карте;federal_district_idsиспользуется, когда пользователь выбрал один или несколько федеральных округов;- оба массива работают как фильтр данных страницы.
Правила:
- массивы могут быть пустыми;
- пустой массив означает, что multi-selection на карте не применен;
- если передан массив, соответствующий одиночный select-фильтр должен быть пустым;
region_idsиfederal_district_idsвзаимоисключающие;- порядок id в
applied_filtersдолжен сохранять порядок, нормализованный backend-ом.
associations_count
Количество инновационных объединений выбранного association_type после применения фильтров.
Примеры:
- для
clustersэто количество промышленных кластеров; - для
technoparksэто количество технопарков; - для
sezэто количество ОЭЗ.
opk_organizations_count
Количество уникальных организаций ОПК после применения фильтров.
Одна организация ОПК может входить в несколько объединений. Поэтому это поле не должно считаться как простая сумма строк таблицы организаций, если таблица возвращает строки участия.
opk_memberships_count
Количество связей организация ОПК x инновационное объединение.
Это поле может быть больше opk_organizations_count.
Если таблица организаций возвращает строки участия, total_count таблицы может совпадать с
opk_memberships_count, но не обязан совпадать с opk_organizations_count.
organization_directory.total_count
Количество строк в табличном перечне организаций после применения фильтров и до применения limit/offset.
Требования:
- значение используется для UI-счетчика
Найдено: N; - если таблица возвращает строки участия, а не уникальные организации, значение может быть больше
unique_organizations_count; - семантика строки таблицы должна быть зафиксирована в поле
row_granularity.
rank
Позиция строки в рейтинге.
Требования:
rankвозвращается backend-ом как integer>= 1;- если используется обычное порядковое ранжирование,
rankне должен дублироваться; - если бизнес-методика допускает одинаковые места при равных значениях, блок должен вернуть
ranking_method; - допустимые значения
ranking_method:ordinal,dense,competition; - если
ranking_methodне передан, frontend считает методикуordinal.
Полнота массивов
Если в блоке нет явного limit, массив rows, regions или federal_districts должен возвращаться полностью
для текущего состояния страницы и текущих фильтров.
Frontend может отображать часть данных в видимой области и скроллить контейнер локально, но backend не должен неявно обрезать массив до первых 5-10 строк только потому, что в макете виден короткий пример.
Нужно реализовать
competence-map association-map filters
GET /api/v1/competence-map/association-map/filters/
Описание:
- возвращает справочники для фильтров страниц карты;
- учитывает права пользователя;
- учитывает текущие выбранные фильтры, если они переданы в query params;
- возвращает серверно доступные значения
association_type; - возвращает доступные типы отображения карты;
- не возвращает KPI, рейтинг, список объединений и таблицу организаций;
- не управляет визуальным представлением фильтров: frontend может локально показывать типы объединений как route navigation, tabs или segmented control.
Request:
GET /api/v1/competence-map/association-map/filters/?association_type=clusters
Query params:
association_type:clusters | technoparks | sez, optional, defaultclusters;map_view_mode:regions | federal_districts, optional, defaultregions;federal_district_id: string, optional;region_id: string, optional;federal_district_ids: array of string, optional;region_ids: array of string, optional;industry_id: string, optional;integrated_structure_id: string, optional;organization_id: string, optional;competence_id: string, optional.
Response:
{
"generated_at": "2026-06-24T15:20:00+03:00",
"applied_filters": {
"association_type": "clusters",
"map_view_mode": "regions",
"federal_district_id": null,
"region_id": null,
"federal_district_ids": [],
"region_ids": [],
"industry_id": null,
"integrated_structure_id": null,
"organization_id": null,
"competence_id": null
},
"association_types": [
{
"id": "clusters",
"label": "Кластеры",
"route_path": "/new-map",
"associations_count": 124,
"disabled": false
},
{
"id": "technoparks",
"label": "Технопарки",
"route_path": "/new-map/technoparks",
"associations_count": 35,
"disabled": false
},
{
"id": "sez",
"label": "Особые экономические зоны",
"route_path": "/new-map/sez",
"associations_count": 17,
"disabled": false
}
],
"map_view_modes": [
{
"id": "regions",
"label": "Субъекты РФ",
"disabled": false
},
{
"id": "federal_districts",
"label": "Федеральные округа",
"disabled": false
}
],
"federal_districts": [
{
"id": "cfo",
"label": "Центральный ФО",
"region_ids": ["RU-BEL", "RU-MOS", "RU-MOW", "RU-YAR"],
"associations_count": 58,
"opk_organizations_count": 163,
"disabled": false
}
],
"regions": [
{
"id": "RU-MOS",
"map_region_id": "RU-MOS",
"federal_district_id": "cfo",
"label": "Московская область",
"associations_count": 17,
"opk_organizations_count": 68,
"disabled": false
}
],
"industries": [
{
"id": "radio-electronics",
"label": "Радиоэлектронная промышленность",
"opk_organizations_count": 180,
"disabled": false
}
],
"integrated_structures": [
{
"id": "rostec",
"label": "Госкорпорация Ростех",
"opk_organizations_count": 42,
"disabled": false
}
],
"organizations": [
{
"id": "org-concern-agat",
"label": "АО \"Концерн \"Моринсис - Агат\"",
"inn": "7700000000",
"ogrn": "1027700000000",
"association_memberships_count": 1,
"disabled": false
}
],
"competences": [
{
"id": "microelectronics",
"label": "Микроэлектроника",
"opk_organizations_count": 64,
"disabled": false
}
]
}
Требования к association-map/filters/
Поле generated_at
Требования:
- ISO 8601 с timezone;
- обязательное поле;
- единое время генерации для всего payload.
Блок applied_filters
Требования:
- возвращает фактически примененные фильтры;
- все отсутствующие одиночные значения возвращаются как
null; - все отсутствующие multi-selection значения возвращаются как
[]; association_typeвсегда заполнен;map_view_modeвсегда заполнен.
Массив association_types
Требования:
- содержит все доступные пользователю типы объединений для страниц карты;
idдолжен быть одним изclusters,technoparks,sez;labelобязательный;route_pathoptional, но если возвращается, должен соответствовать frontend route;associations_countinteger>= 0;disabledboolean.
Назначение:
- является серверным источником доступных разделов карты;
- порядок массива определяет рекомендуемый порядок навигации;
- при выборе раздела frontend передает выбранный
idкак query paramassociation_type; associations_countможет использоваться как серверное значение для badge/count рядом с разделом;disabled = trueозначает, что тип недоступен пользователю или неприменим при текущих фильтрах.
Массив map_view_modes
Требования:
- содержит
regionsиfederal_districts, если оба режима доступны пользователю; idобязательный;labelобязательный;disabledобязательный.
Массивы справочников
Для массивов federal_districts, regions, industries, integrated_structures, organizations,
competences:
idобязательный;labelобязательный;disabledобязательный;- count-поля должны быть integer
>= 0; - если по текущим фильтрам список пустой, возвращается пустой массив;
- выбранное значение должно присутствовать в справочнике, если пользователь имеет к нему доступ.
Для regions дополнительно:
map_region_idобязательный;federal_district_idобязательный;map_region_idдолжен совпадать с id региона в SVG-карте субъектов РФ.
Для federal_districts дополнительно:
region_idsобязательный;- каждый
region_ids[*]должен существовать в справочникеregions, если регион доступен пользователю.
competence-map association-map
GET /api/v1/competence-map/association-map/
Описание:
- возвращает основную аналитическую часть страницы карты для выбранного
association_type; - применяет все переданные фильтры;
- применяет выбранные на карте субъекты РФ или федеральные округа как фильтр;
- возвращает KPI, данные карты, список объединений, рейтинг субъектов РФ и summary таблицы организаций;
- не возвращает полный табличный перечень организаций, для него используется отдельный endpoint
/association-map/organizations/.
Request:
GET /api/v1/competence-map/association-map/?association_type=clusters
Query params:
association_type:clusters | technoparks | sez, optional, defaultclusters;map_view_mode:regions | federal_districts, optional, defaultregions;federal_district_id: string, optional;region_id: string, optional;federal_district_ids: array of string, optional;region_ids: array of string, optional;industry_id: string, optional;integrated_structure_id: string, optional;organization_id: string, optional;competence_id: string, optional.
Общий response envelope:
{
"generated_at": "2026-06-24T15:20:00+03:00",
"data_actual_at": "2026-06-24T00:00:00+03:00",
"association_type": "clusters",
"applied_filters": {
"association_type": "clusters",
"map_view_mode": "regions",
"federal_district_id": null,
"region_id": null,
"federal_district_ids": [],
"region_ids": [],
"industry_id": null,
"integrated_structure_id": null,
"organization_id": null,
"competence_id": null
},
"page": {},
"kpis": [],
"map": {},
"association_list": {},
"region_ranking": {},
"organization_directory": {}
}
Серверные состояния страницы по association_type
Страница имеет три серверных состояния. Они используют один и тот же endpoint
/api/v1/competence-map/association-map/.
| Frontend route | association_type |
Primary KPI | Список объединений | Рейтинг |
|---|---|---|---|---|
/new-map |
clusters |
clusters_count |
промышленные кластеры | субъекты РФ по участию организаций ОПК в кластерах |
/new-map/technoparks |
technoparks |
technoparks_count |
технопарки | субъекты РФ по участию организаций ОПК в технопарках |
/new-map/sez |
sez |
sez_count |
ОЭЗ | субъекты РФ по участию организаций ОПК в ОЭЗ |
Требования:
association_typeв корне response должен совпадать сapplied_filters.association_type;- frontend не должен делать отдельные endpoint-ы
/clusters,/technoparks,/sez; - при переходе между frontend routes меняется только
association_typeи повторно запрашиваются/association-map/filters/и/association-map/; map_view_modeменяет активное представление карты, но не требует отдельного backend resource.
Response для association_type=clusters
{
"generated_at": "2026-06-24T15:20:00+03:00",
"data_actual_at": "2026-06-24T00:00:00+03:00",
"association_type": "clusters",
"applied_filters": {
"association_type": "clusters",
"map_view_mode": "regions",
"federal_district_id": null,
"region_id": null,
"federal_district_ids": [],
"region_ids": [],
"industry_id": null,
"integrated_structure_id": null,
"organization_id": null,
"competence_id": null
},
"page": {
"title": "Участие организаций ОПК в кластерах",
"subtitle": "Российская Федерация",
"legend_label": "Присутствие одной или более организаций ОПК в кластере",
"empty_list_label": "По текущим фильтрам кластеры не найдены."
},
"kpis": [
{
"code": "clusters_count",
"label": "Количество кластеров",
"value": 124,
"value_type": "integer",
"numerator": 124,
"denominator": null
},
{
"code": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value": 517,
"value_type": "integer",
"numerator": 517,
"denominator": null
}
],
"map": {
"view_mode": "regions",
"selected_scope": null,
"selected_region_ids": [],
"selected_federal_district_ids": [],
"selected_chip_text": null,
"highlight_color": "#022456",
"regions": [
{
"region_id": "RU-MOS",
"map_region_id": "RU-MOS",
"region_name": "Московская область",
"federal_district_id": "cfo",
"associations_count": 17,
"opk_organizations_count": 68,
"has_data": true,
"is_highlighted": false,
"tooltip_label": "Московская область: 17 кластеров, 68 организаций ОПК"
},
{
"region_id": "RU-MOW",
"map_region_id": "RU-MOW",
"region_name": "Москва",
"federal_district_id": "cfo",
"associations_count": 11,
"opk_organizations_count": 34,
"has_data": true,
"is_highlighted": false,
"tooltip_label": "Москва: 11 кластеров, 34 организации ОПК"
}
],
"federal_districts": [
{
"federal_district_id": "cfo",
"federal_district_name": "Центральный ФО",
"region_ids": ["RU-MOS", "RU-MOW", "RU-YAR", "RU-BEL"],
"associations_count": 37,
"opk_organizations_count": 143,
"has_data": true,
"is_highlighted": false,
"tooltip_label": "Центральный ФО: 37 кластеров, 143 организации ОПК"
}
]
},
"association_list": {
"title": "Кластеры, в числе участников которых есть организации ОПК",
"row_metric_label": "Количество организаций ОПК",
"total_count": 124,
"rows": [
{
"association_id": "cluster-ru-mos-001",
"association_type": "clusters",
"association_name": "Радиоэлектронный кластер Подмосковья",
"region_id": "RU-MOS",
"region_name": "Московская область",
"federal_district_id": "cfo",
"opk_organizations_count": 9,
"opk_memberships_count": 9
}
]
},
"region_ranking": {
"title": "Рейтинг субъектов РФ по участию организаций ОПК в кластерах",
"sort": {
"field": "associations_count",
"direction": "desc"
},
"ranking_method": "ordinal",
"columns": [
{
"field": "associations_count",
"label": "Количество кластеров",
"value_type": "integer"
},
{
"field": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value_type": "integer"
}
],
"rows": [
{
"rank": 1,
"region_id": "RU-MOS",
"region_name": "Московская область",
"federal_district_id": "cfo",
"associations_count": 17,
"opk_organizations_count": 68,
"opk_memberships_count": 68
},
{
"rank": 2,
"region_id": "RU-MOW",
"region_name": "Москва",
"federal_district_id": "cfo",
"associations_count": 11,
"opk_organizations_count": 34,
"opk_memberships_count": 34
}
]
},
"organization_directory": {
"title": "Перечень организаций ОПК",
"endpoint": "/api/v1/competence-map/association-map/organizations/",
"row_granularity": "organization_association_membership",
"total_count": 517,
"unique_organizations_count": 517
}
}
Response для association_type=technoparks
{
"generated_at": "2026-06-24T15:20:00+03:00",
"data_actual_at": "2026-06-24T00:00:00+03:00",
"association_type": "technoparks",
"applied_filters": {
"association_type": "technoparks",
"map_view_mode": "regions",
"federal_district_id": null,
"region_id": null,
"federal_district_ids": [],
"region_ids": [],
"industry_id": null,
"integrated_structure_id": null,
"organization_id": null,
"competence_id": null
},
"page": {
"title": "Участие организаций ОПК в технопарках",
"subtitle": "Российская Федерация",
"legend_label": "Присутствие одной или более организаций ОПК в технопарке",
"empty_list_label": "По текущим фильтрам технопарки не найдены."
},
"kpis": [
{
"code": "technoparks_count",
"label": "Количество технопарков",
"value": 35,
"value_type": "integer",
"numerator": 35,
"denominator": null
},
{
"code": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value": 66,
"value_type": "integer",
"numerator": 66,
"denominator": null
}
],
"map": {
"view_mode": "regions",
"selected_scope": null,
"selected_region_ids": [],
"selected_federal_district_ids": [],
"selected_chip_text": null,
"highlight_color": "#022456",
"regions": [
{
"region_id": "RU-MOS",
"map_region_id": "RU-MOS",
"region_name": "Московская область",
"federal_district_id": "cfo",
"associations_count": 6,
"opk_organizations_count": 12,
"has_data": true,
"is_highlighted": false,
"tooltip_label": "Московская область: 6 технопарков, 12 организаций ОПК"
}
],
"federal_districts": [
{
"federal_district_id": "cfo",
"federal_district_name": "Центральный ФО",
"region_ids": ["RU-MOS", "RU-MOW", "RU-YAR", "RU-BEL"],
"associations_count": 14,
"opk_organizations_count": 28,
"has_data": true,
"is_highlighted": false,
"tooltip_label": "Центральный ФО: 14 технопарков, 28 организаций ОПК"
}
]
},
"association_list": {
"title": "Технопарки, в числе участников которых есть организации ОПК",
"row_metric_label": "Количество организаций ОПК",
"total_count": 35,
"rows": [
{
"association_id": "technopark-ru-mos-001",
"association_type": "technoparks",
"association_name": "Подмосковный технопарк высоких технологий",
"region_id": "RU-MOS",
"region_name": "Московская область",
"federal_district_id": "cfo",
"opk_organizations_count": 4,
"opk_memberships_count": 4
}
]
},
"region_ranking": {
"title": "Рейтинг субъектов РФ по участию организаций ОПК в технопарках",
"sort": {
"field": "associations_count",
"direction": "desc"
},
"ranking_method": "ordinal",
"columns": [
{
"field": "associations_count",
"label": "Количество технопарков",
"value_type": "integer"
},
{
"field": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value_type": "integer"
}
],
"rows": [
{
"rank": 1,
"region_id": "RU-MOS",
"region_name": "Московская область",
"federal_district_id": "cfo",
"associations_count": 6,
"opk_organizations_count": 12,
"opk_memberships_count": 12
}
]
},
"organization_directory": {
"title": "Перечень организаций ОПК",
"endpoint": "/api/v1/competence-map/association-map/organizations/",
"row_granularity": "organization_association_membership",
"total_count": 66,
"unique_organizations_count": 66
}
}
Response для association_type=sez
{
"generated_at": "2026-06-24T15:20:00+03:00",
"data_actual_at": "2026-06-24T00:00:00+03:00",
"association_type": "sez",
"applied_filters": {
"association_type": "sez",
"map_view_mode": "regions",
"federal_district_id": null,
"region_id": null,
"federal_district_ids": [],
"region_ids": [],
"industry_id": null,
"integrated_structure_id": null,
"organization_id": null,
"competence_id": null
},
"page": {
"title": "Участие организаций ОПК в особых экономических зонах",
"subtitle": "Российская Федерация",
"legend_label": "Присутствие одной или более организаций ОПК в особой экономической зоне",
"empty_list_label": "По текущим фильтрам особые экономические зоны не найдены."
},
"kpis": [
{
"code": "sez_count",
"label": "Количество ОЭЗ",
"value": 17,
"value_type": "integer",
"numerator": 17,
"denominator": null
},
{
"code": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value": 64,
"value_type": "integer",
"numerator": 64,
"denominator": null
}
],
"map": {
"view_mode": "regions",
"selected_scope": null,
"selected_region_ids": [],
"selected_federal_district_ids": [],
"selected_chip_text": null,
"highlight_color": "#022456",
"regions": [
{
"region_id": "RU-KGN",
"map_region_id": "RU-KGN",
"region_name": "Курганская область",
"federal_district_id": "ufo",
"associations_count": 9,
"opk_organizations_count": 9,
"has_data": true,
"is_highlighted": false,
"tooltip_label": "Курганская область: 9 ОЭЗ, 9 организаций ОПК"
}
],
"federal_districts": [
{
"federal_district_id": "ufo",
"federal_district_name": "Уральский ФО",
"region_ids": ["RU-CHE", "RU-KGN", "RU-SVE"],
"associations_count": 15,
"opk_organizations_count": 21,
"has_data": true,
"is_highlighted": false,
"tooltip_label": "Уральский ФО: 15 ОЭЗ, 21 организация ОПК"
}
]
},
"association_list": {
"title": "Особые экономические зоны, в числе участников которых есть организации ОПК",
"row_metric_label": "Количество организаций ОПК",
"total_count": 17,
"rows": [
{
"association_id": "sez-kurgan-4",
"association_type": "sez",
"association_name": "Особая экономическая зона \"Курганский 4\"",
"region_id": "RU-KGN",
"region_name": "Курганская область",
"federal_district_id": "ufo",
"opk_organizations_count": 9,
"opk_memberships_count": 9
}
]
},
"region_ranking": {
"title": "Рейтинг субъектов РФ по участию организаций ОПК в особых экономических зонах",
"sort": {
"field": "associations_count",
"direction": "desc"
},
"ranking_method": "ordinal",
"columns": [
{
"field": "associations_count",
"label": "Количество ОЭЗ",
"value_type": "integer"
},
{
"field": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value_type": "integer"
}
],
"rows": [
{
"rank": 1,
"region_id": "RU-KGN",
"region_name": "Курганская область",
"federal_district_id": "ufo",
"associations_count": 9,
"opk_organizations_count": 9,
"opk_memberships_count": 9
}
]
},
"organization_directory": {
"title": "Перечень организаций ОПК",
"endpoint": "/api/v1/competence-map/association-map/organizations/",
"row_granularity": "organization_association_membership",
"total_count": 64,
"unique_organizations_count": 64
}
}
competence-map association-map organizations
GET /api/v1/competence-map/association-map/organizations/
Описание:
- возвращает табличный перечень организаций ОПК для выбранного состояния карты;
- применяет те же фильтры, что
/association-map/; - используется для экрана, который открывается по ссылке
Перейти к перечню организаций; - возвращает строки таблицы, счетчик
Найдено, pagination metadata и нормализованные примененные фильтры.
Request:
GET /api/v1/competence-map/association-map/organizations/?association_type=clusters®ion_ids=RU-MOS&limit=500&offset=0
Query params:
association_type:clusters | technoparks | sez, optional, defaultclusters;map_view_mode:regions | federal_districts, optional, defaultregions;federal_district_id: string, optional;region_id: string, optional;federal_district_ids: array of string, optional;region_ids: array of string, optional;industry_id: string, optional;integrated_structure_id: string, optional;organization_id: string, optional;competence_id: string, optional;limit: integer, optional, default500, maximum1000;offset: integer, optional, default0;sort_field: string, optional, defaultserial_number;sort_direction:asc | desc, optional, defaultasc.
Response:
{
"generated_at": "2026-06-24T15:20:00+03:00",
"data_actual_at": "2026-06-24T00:00:00+03:00",
"association_type": "clusters",
"applied_filters": {
"association_type": "clusters",
"map_view_mode": "regions",
"federal_district_id": null,
"region_id": null,
"federal_district_ids": [],
"region_ids": ["RU-MOS"],
"industry_id": null,
"integrated_structure_id": null,
"organization_id": null,
"competence_id": null
},
"row_granularity": "organization_association_membership",
"total_count": 68,
"unique_organizations_count": 68,
"pagination": {
"limit": 500,
"offset": 0,
"has_next": false,
"has_previous": false
},
"sort": {
"field": "serial_number",
"direction": "asc"
},
"columns": [
{
"field": "serial_number",
"label": "№ п/п",
"value_type": "integer"
},
{
"field": "organization_name",
"label": "Наименование",
"value_type": "string"
},
{
"field": "director_full_name",
"label": "ФИО руководителя",
"value_type": "string"
},
{
"field": "official_emails",
"label": "Официальная почта",
"value_type": "string_array"
},
{
"field": "official_phones",
"label": "Официальный телефон",
"value_type": "string_array"
},
{
"field": "industry_name",
"label": "Отрасль",
"value_type": "string"
},
{
"field": "activity_type_name",
"label": "Вид деятельности",
"value_type": "string"
},
{
"field": "ownership_type_name",
"label": "Тип собственности",
"value_type": "string"
}
],
"rows": [
{
"row_id": "cluster-ru-mos-001:org-concern-agat",
"serial_number": 1,
"organization_id": "org-concern-agat",
"organization_name": "АО \"Концерн \"Моринсис - Агат\"",
"director_full_name": "Храмов М.Ю.",
"official_emails": ["info@concern-agat.ru", "gen_director@concern-agat.ru"],
"official_phones": ["603-90-05"],
"industry_id": "radio-electronics",
"industry_name": "Радиоэлектронная промышленность",
"activity_type_id": "research-organizations",
"activity_type_name": "Научные организации",
"ownership_type_id": "directly-or-indirectly-controlled",
"ownership_type_name": "Прямо или косвенно контролируемые",
"association_membership": {
"association_type": "clusters",
"association_id": "cluster-ru-mos-001",
"association_name": "Радиоэлектронный кластер Подмосковья",
"region_id": "RU-MOS",
"region_name": "Московская область",
"federal_district_id": "cfo"
}
}
]
}
Требования к association-map/organizations/
Поле row_granularity
Допустимые значения:
unique_organization;organization_association_membership.
Для текущего табличного представления рекомендуется organization_association_membership, потому что
одна организация может участвовать в нескольких объединениях или отображаться в разных отраслевых контекстах.
Требования:
- если
row_granularity = unique_organization,total_countдолжен быть равенunique_organizations_count; - если
row_granularity = organization_association_membership,total_countможет быть большеunique_organizations_count; row_idдолжен быть стабильным id строки, а не толькоorganization_id.
Поля организации
Требования:
organization_idобязательный;organization_nameобязательный;director_full_nameможет бытьnull, если данных нет;official_emailsвозвращается массивом строк, может быть пустым массивом;official_phonesвозвращается массивом строк, может быть пустым массивом;industry_idиindustry_nameобязательны, если строка имеет отраслевой контекст;activity_type_idиactivity_type_nameобязательны, если источник данных содержит вид деятельности;ownership_type_idиownership_type_nameобязательны, если источник данных содержит тип собственности;- неизвестные значения возвращаются как
nullили пустой массив, а не как строка-.
Блок association_membership
Требования:
- обязателен, если
row_granularity = organization_association_membership; association_typeдолжен совпадать с корневымassociation_type;association_idобязательный;association_nameобязательный;region_idдолжен быть совместим с SVG-картой;federal_district_idобязательный.
Требования к общим полям association-map response
Поле generated_at
Требования:
- ISO 8601 с timezone;
- обязательное;
- показывает время формирования response.
Поле data_actual_at
Требования:
- ISO 8601 с timezone;
- обязательное для
/association-map/и/association-map/organizations/; - показывает дату и время актуальности данных.
Поле association_type
Требования:
- обязательное;
- должно совпадать с фактически примененным режимом;
- допустимые значения:
clusters,technoparks,sez.
Блок applied_filters
Требования:
- обязательный;
- содержит нормализованные значения примененных фильтров;
- отсутствующие одиночные фильтры возвращаются как
null; - отсутствующие multi-selection фильтры возвращаются как
[]; association_typeиmap_view_modeвсегда заполнены.
Блок page
Поля:
title;subtitle;legend_label;empty_list_label.
Требования:
- все поля обязательны для
/association-map/; - значения должны соответствовать текущему
association_type; - frontend может использовать эти строки напрямую или локализовать их на своей стороне.
Массив kpis
Каждый объект массива - одна KPI-метрика.
Поля:
code: стабильный системный идентификатор метрики;label: человекочитаемое название метрики;value: raw numeric value;value_type:integer | percent | decimal;numerator: числитель, если применимо;denominator: знаменатель, если применимо.
Требования:
- все поля, кроме
numeratorиdenominator, обязательны; valueдолжен быть number, не string;0допустим как валидное значение;- для
value_type = percentзначение возвращается в диапазоне0..100; - порядок массива должен соответствовать порядку карточек на странице;
- для текущих страниц карты порядок: count выбранного типа объединения, затем количество организаций ОПК.
Блок map
Поля:
view_mode;selected_scope;selected_region_ids;selected_federal_district_ids;selected_chip_text;highlight_color;regions;federal_districts.
Требования:
view_modeдолжен совпадать сapplied_filters.map_view_mode;selected_scopeдолжен бытьregions,federal_districtsилиnull;- если применен
region_idилиregion_ids,selected_scope = regions; - если применен
federal_district_idилиfederal_district_ids,selected_scope = federal_districts; selected_region_idsиselected_federal_district_idsобязательны, даже если пустые;- selected ids описывают эффективный выбор географии, включая одиночные select-фильтры
region_id/federal_district_idи multi-selection фильтрыregion_ids/federal_district_ids; selected_chip_textможет бытьnull, если ничего не выбрано;highlight_coloroptional, но если возвращается, должен быть CSS-compatible color string;regionsдолжен включать все доступные пользователю субъекты РФ, а не только подсвеченные;federal_districtsдолжен включать все доступные пользователю федеральные округа, а не только подсвеченные.
map.regions
Каждый объект массива - один субъект РФ.
Поля:
region_id;map_region_id;region_name;federal_district_id;associations_count;opk_organizations_count;has_data;is_highlighted;tooltip_label.
Требования:
region_idобязательный;map_region_idобязательный;map_region_idдолжен совпадать с SVG-id региона;associations_countinteger>= 0;opk_organizations_countinteger>= 0;has_data = true, если по текущим фильтрам в регионе есть хотя бы одно объединение выбранного типа или хотя бы одна организация ОПК;is_highlighted = true, если регион должен быть подсвечен при текущих фильтрах/выборе;tooltip_labeloptional, frontend может построить подпись локально из raw-полей.
map.federal_districts
Каждый объект массива - один федеральный округ.
Поля:
federal_district_id;federal_district_name;region_ids;associations_count;opk_organizations_count;has_data;is_highlighted;tooltip_label.
Требования:
federal_district_idобязательный;region_idsобязательный;region_idsдолжен содержать id субъектов РФ, входящих в ФО и доступных пользователю;- count-поля должны быть агрегированы по регионам округа с учетом текущих фильтров;
is_highlighted = true, если округ должен быть подсвечен при текущих фильтрах/выборе.
Блок association_list
Поля:
title;row_metric_label;total_count;rows.
Требования:
titleдолжен соответствовать текущемуassociation_type;row_metric_labelдля текущего UI:Количество организаций ОПК;total_countinteger>= 0;rowsвозвращаются полностью для текущих фильтров;- строки должны быть отсортированы по
opk_organizations_countпо убыванию, затемassociation_nameпо возрастанию (ru-RU), если не задан другой backend-порядок.
Каждая строка:
association_idобязательный;association_typeобязательный;association_nameобязательный;region_idобязательный;region_nameобязательный;federal_district_idобязательный;opk_organizations_countinteger>= 0;opk_memberships_countinteger>= 0.
Блок region_ranking
Поля:
title;sort;ranking_method;columns;rows.
Требования:
titleобязательный;sort.fieldобязательный;sort.directionдолжен бытьasc | desc;ranking_methodoptional, defaultordinal;columnsобязательный, чтобы frontend не хардкодилКоличество кластеровдля страниц ОЭЗ/технопарков;rowsможет быть пустым массивом;rankвозвращается в каждой строке;region_idобязательный;region_nameобязательный;associations_countinteger>= 0;opk_organizations_countinteger>= 0.
Блок organization_directory
Поля:
title;endpoint;row_granularity;total_count;unique_organizations_count.
Требования:
- используется как summary для кнопки/ссылки перехода к таблице организаций;
endpointдолжен указывать на/api/v1/competence-map/association-map/organizations/;total_countдолжен совпадать сtotal_count, который вернет organizations endpoint при тех же фильтрах;unique_organizations_countдолжен совпадать сunique_organizations_count, который вернет organizations endpoint при тех же фильтрах.
Порядок массивов
association_types
Рекомендуемый порядок:
clusterstechnoparkssez
map_view_modes
Рекомендуемый порядок:
regionsfederal_districts
map.regions
Порядок сортировки:
federal_district_idв порядке справочника федеральных округов;region_nameпо возрастанию (ru-RU).
map.federal_districts
Порядок сортировки должен соответствовать порядку федеральных округов на backend-е. Если такого порядка нет, использовать:
cfonwfoyufoskfopfoufosfodfo
association_list.rows
Порядок сортировки:
opk_organizations_countпо убыванию;association_nameпо возрастанию (ru-RU);association_idпо возрастанию.
region_ranking.rows
Порядок сортировки по умолчанию:
associations_countпо убыванию;- при равенстве -
opk_organizations_countпо убыванию; - при равенстве -
region_nameпо возрастанию (ru-RU).
Если в блоке передан ranking_method = dense или competition, одинаковые rank допустимы
только для строк с равным основным показателем.
organizations.rows
Порядок сортировки по умолчанию:
serial_numberпо возрастанию.
serial_number должен соответствовать позиции строки после применения фильтров и сортировки,
а не позиции внутри текущей страницы limit/offset.
Проверки консистентности
Backend должен гарантировать:
association_typeв корне response совпадает сapplied_filters.association_type;map.view_modeсовпадает сapplied_filters.map_view_mode;kpis[*].valueсогласованы с соответствующими блоками карты и таблицы;- primary KPI count выбранного типа объединения равен
association_list.total_count; organization_directory.total_countравенtotal_countorganizations endpoint-а при тех же фильтрах;organization_directory.unique_organizations_countравенunique_organizations_countorganizations endpoint-а при тех же фильтрах;map.regions[*].region_idсуществует в справочникеregions;map.federal_districts[*].region_idsссылаются на существующиеmap.regions[*].region_id;region_ranking.rows[*].region_idсуществует вmap.regions;- если
selected_scope = regions,selected_region_idsне пустой иselected_federal_district_idsпустой; - если
selected_scope = federal_districts,selected_federal_district_idsне пустой иselected_region_idsпустой; - если
selected_scope = null, оба массива selected ids пустые; rankв рейтингах не дублируется, еслиranking_method = ordinal;- все
idстабильны между запросами; - все count-поля возвращаются как integer
>= 0; - пустые данные возвращаются пустыми массивами, а не отсутствующими полями.
Успешный пустой ответ
Если по фильтрам данных нет, возвращается 200 OK.
Пример:
{
"generated_at": "2026-06-24T15:20:00+03:00",
"data_actual_at": "2026-06-24T00:00:00+03:00",
"association_type": "clusters",
"applied_filters": {
"association_type": "clusters",
"map_view_mode": "regions",
"federal_district_id": null,
"region_id": "RU-NEN",
"federal_district_ids": [],
"region_ids": [],
"industry_id": null,
"integrated_structure_id": null,
"organization_id": null,
"competence_id": null
},
"page": {
"title": "Участие организаций ОПК в кластерах",
"subtitle": "Российская Федерация",
"legend_label": "Присутствие одной или более организаций ОПК в кластере",
"empty_list_label": "По текущим фильтрам кластеры не найдены."
},
"kpis": [
{
"code": "clusters_count",
"label": "Количество кластеров",
"value": 0,
"value_type": "integer",
"numerator": 0,
"denominator": null
},
{
"code": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value": 0,
"value_type": "integer",
"numerator": 0,
"denominator": null
}
],
"map": {
"view_mode": "regions",
"selected_scope": "regions",
"selected_region_ids": ["RU-NEN"],
"selected_federal_district_ids": [],
"selected_chip_text": "Ненецкий автономный округ: 0 кластеров",
"highlight_color": "#022456",
"regions": [
{
"region_id": "RU-NEN",
"map_region_id": "RU-NEN",
"region_name": "Ненецкий автономный округ",
"federal_district_id": "nwfo",
"associations_count": 0,
"opk_organizations_count": 0,
"has_data": false,
"is_highlighted": false,
"tooltip_label": "Ненецкий автономный округ: данных по текущим фильтрам нет"
}
],
"federal_districts": [
{
"federal_district_id": "nwfo",
"federal_district_name": "Северо-Западный ФО",
"region_ids": ["RU-NEN"],
"associations_count": 0,
"opk_organizations_count": 0,
"has_data": false,
"is_highlighted": false,
"tooltip_label": "Северо-Западный ФО: данных по текущим фильтрам нет"
}
]
},
"association_list": {
"title": "Кластеры, в числе участников которых есть организации ОПК",
"row_metric_label": "Количество организаций ОПК",
"total_count": 0,
"rows": []
},
"region_ranking": {
"title": "Рейтинг субъектов РФ по участию организаций ОПК в кластерах",
"sort": {
"field": "associations_count",
"direction": "desc"
},
"ranking_method": "ordinal",
"columns": [
{
"field": "associations_count",
"label": "Количество кластеров",
"value_type": "integer"
},
{
"field": "opk_organizations_count",
"label": "Количество организаций ОПК",
"value_type": "integer"
}
],
"rows": []
},
"organization_directory": {
"title": "Перечень организаций ОПК",
"endpoint": "/api/v1/competence-map/association-map/organizations/",
"row_granularity": "organization_association_membership",
"total_count": 0,
"unique_organizations_count": 0
}
}
Статус-коды и ошибки
Status code matrix
| Status | Когда возвращается |
|---|---|
200 OK |
Успешный ответ, включая валидные фильтры без данных. |
304 Not Modified |
Conditional GET по If-None-Match или If-Modified-Since, если представление не изменилось. |
400 Bad Request |
Некорректный query param, неизвестный query param, повтор параметра, неверный формат значения, конфликт фильтров. |
401 Unauthorized |
Пользователь не аутентифицирован. |
403 Forbidden |
Пользователь аутентифицирован, но не имеет доступа к странице или разделу. |
429 Too Many Requests |
Превышен rate limit. |
500 Internal Server Error |
Непредвиденная ошибка backend-а. |
503 Service Unavailable |
Временная недоступность сервиса или источника данных карты. |
Для collection/report endpoint-ов не использовать 404 Not Found, если path существует, но по валидным фильтрам
нет данных. В этом случае нужен 200 OK с пустыми массивами.
Problem Details
Все ошибки возвращаются в формате application/problem+json.
Обязательные поля:
type: стабильный URI типа ошибки;title: короткое человекочитаемое название;status: HTTP status code;detail: конкретное описание ошибки;instance: path и query текущего request-а;code: стабильный backend-код ошибки для frontend-логики;request_id: id запроса изX-Request-Id.
Frontend не должен парсить title или detail для бизнес-логики. Для логики используется только code
и дополнительные машинные поля.
Ошибка валидации query params
Для некорректного association_type:
HTTP status:
400 Bad Request
Response:
{
"type": "https://api.example.ru/problems/invalid-association-type",
"title": "Invalid association_type",
"status": 400,
"detail": "Unsupported association_type: all",
"instance": "/api/v1/competence-map/association-map/?association_type=all",
"code": "invalid_association_type",
"request_id": "req-01JYX4M2K7Y6WQ9RJ8T2V7VY5R",
"received_value": "all",
"allowed_values": ["clusters", "technoparks", "sez"]
}
Для несуществующего фильтра:
HTTP status:
400 Bad Request
Response:
{
"type": "https://api.example.ru/problems/invalid-filter-value",
"title": "Invalid filter value",
"status": 400,
"detail": "Unknown or unavailable filter value for region_id",
"instance": "/api/v1/competence-map/association-map/?association_type=clusters®ion_id=unknown",
"code": "invalid_filter_value",
"request_id": "req-01JYX4M2K7Y6WQ9RJ8T2V7VY5R",
"field": "region_id",
"received_value": "unknown"
}
Для конфликтующих фильтров:
HTTP status:
400 Bad Request
Response:
{
"type": "https://api.example.ru/problems/conflicting-filters",
"title": "Conflicting filters",
"status": 400,
"detail": "region_id cannot be used together with region_ids",
"instance": "/api/v1/competence-map/association-map/?association_type=clusters®ion_id=RU-MOS®ion_ids=RU-MOW",
"code": "conflicting_filters",
"request_id": "req-01JYX4M2K7Y6WQ9RJ8T2V7VY5R",
"fields": ["region_id", "region_ids"]
}
Для неизвестного query param:
HTTP status:
400 Bad Request
Response:
{
"type": "https://api.example.ru/problems/unknown-query-param",
"title": "Unknown query parameter",
"status": 400,
"detail": "Unknown query parameter: district",
"instance": "/api/v1/competence-map/association-map/?district=central",
"code": "unknown_query_param",
"request_id": "req-01JYX4M2K7Y6WQ9RJ8T2V7VY5R",
"field": "district",
"allowed_params": [
"association_type",
"map_view_mode",
"federal_district_id",
"region_id",
"federal_district_ids",
"region_ids",
"industry_id",
"integrated_structure_id",
"organization_id",
"competence_id",
"limit",
"offset",
"sort_field",
"sort_direction"
]
}
Ошибки доступа и инфраструктуры
Для 401, 403, 429, 500, 503 используется тот же формат application/problem+json.
Пример 403 Forbidden:
{
"type": "https://api.example.ru/problems/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "User does not have access to competence map association map",
"instance": "/api/v1/competence-map/association-map/",
"code": "forbidden",
"request_id": "req-01JYX4M2K7Y6WQ9RJ8T2V7VY5R"
}
Для 429 Too Many Requests backend должен вернуть header:
Retry-After: <seconds>
Ожидаемая схема запросов
При загрузке /new-map:
GET /api/v1/competence-map/association-map/filters/?association_type=clusters
GET /api/v1/competence-map/association-map/?association_type=clusters
При загрузке /new-map/technoparks:
GET /api/v1/competence-map/association-map/filters/?association_type=technoparks
GET /api/v1/competence-map/association-map/?association_type=technoparks
При загрузке /new-map/sez:
GET /api/v1/competence-map/association-map/filters/?association_type=sez
GET /api/v1/competence-map/association-map/?association_type=sez
При выборе нескольких субъектов РФ на карте:
GET /api/v1/competence-map/association-map/filters/?association_type=clusters&map_view_mode=regions®ion_ids=RU-MOS®ion_ids=RU-MOW
GET /api/v1/competence-map/association-map/?association_type=clusters&map_view_mode=regions®ion_ids=RU-MOS®ion_ids=RU-MOW
При выборе нескольких федеральных округов на карте:
GET /api/v1/competence-map/association-map/filters/?association_type=sez&map_view_mode=federal_districts&federal_district_ids=cfo&federal_district_ids=pfo
GET /api/v1/competence-map/association-map/?association_type=sez&map_view_mode=federal_districts&federal_district_ids=cfo&federal_district_ids=pfo
При переходе к перечню организаций:
GET /api/v1/competence-map/association-map/organizations/?association_type=clusters&map_view_mode=regions®ion_ids=RU-MOS®ion_ids=RU-MOW&limit=500&offset=0
Не требуется реализовывать отдельные endpoint-ы вида:
GET /api/v1/competence-map/association-map/clusters/
GET /api/v1/competence-map/association-map/technoparks/
GET /api/v1/competence-map/association-map/sez/
GET /api/v1/competence-map/association-map/select-region/
GET /api/v1/competence-map/association-map/select-federal-district/
Если в будущем появятся отдельные действия, например экспорт таблицы организаций или drilldown по строке,
для них можно добавить отдельные endpoint-ы. Для текущих состояний страниц из референсных экранов достаточно
/association-map/filters/, /association-map/ и /association-map/organizations/.