19 KiB
Backend Issues Competence Map Analytics Current Data
Назначение: список проблем текущей реализации backend-данных для страницы
Все сервисы / Карта компетенций / Аналитическая панель.
Документ нужен как задача для backend-разработки.
Цель: привести фактический response тестового backend-а к контракту из
docs/info/backend-endpoints-competence-map-analytics-from-layouts.md и openapi.json.
Тестовый endpoint:
GET https://anal-back.dev.nii-ecos.ru/api/v1/competence-map/analytics/
Проблемы ниже относятся к page-level endpoint-у:
GET /api/v1/competence-map/analytics/?association_type=all
GET /api/v1/competence-map/analytics/?association_type=sez
GET /api/v1/competence-map/analytics/?association_type=technoparks
GET /api/v1/competence-map/analytics/?association_type=clusters
Новые endpoint-ы для этих исправлений не нужны. Исправлять нужно наполнение существующего response.
Общие требования к исправлению
- backend должен возвращать реальные бизнес-названия сущностей, а не технические placeholder-строки;
- frontend не должен подменять
Субъект РФ 11,Отрасль 24и подобные значения локальными маппингами; - если сущность имеет
id, backend должен резолвить человекочитаемое имя на сервере; - если данные по применимому блоку есть в источниках, backend должен вернуть строки блока, а не пустой массив;
- если показатель применим для выбранного
association_type, backend должен вернуть его в контрактном поле; 0является валидным числом и должен возвращаться как0, а не какnull, пустая строка или отсутствие поля;- пустой массив допустим только когда данных действительно нет после применения фильтров;
nullдопустим только когда блок или показатель неприменим к выбранному состоянию страницы.
Проблема 1. Placeholder-названия субъектов РФ и отраслей
Где видно
На странице в блоках рейтинга и топа отраслей отображаются значения вида:
Субъект РФ 11
Субъект РФ 59
Субъект РФ 65
Отрасль 24
Отрасль 127
Отрасль 31
Это не реальные названия субъектов РФ и отраслей. Такие значения нельзя отдавать в production response.
Затронутые поля
region_ranking.rows[]:
{
"rank": 1,
"region_id": "11",
"region_name": "Субъект РФ 11"
}
Должно быть:
{
"rank": 1,
"region_id": "11",
"region_name": "<реальное название субъекта РФ из справочника>"
}
top_industries.rows[]:
{
"industry_id": "24",
"industry_name": "Отрасль 24"
}
Должно быть:
{
"industry_id": "24",
"industry_name": "<реальное название отрасли из справочника>"
}
Требования
Для region_ranking.rows[]:
region_idдолжен оставаться стабильным backend-id субъекта РФ;region_nameдолжен быть реальным названием субъекта РФ;region_nameне должен генерироваться какСубъект РФ ${id};- если субъект не найден в справочнике, это ошибка данных, которую нужно логировать на backend-е;
- frontend должен получать уже готовое отображаемое имя.
Для top_industries.rows[]:
industry_idдолжен оставаться стабильным backend-id отрасли;industry_nameдолжен быть реальным названием отрасли;industry_nameне должен генерироваться какОтрасль ${id};- если отрасль не найдена в справочнике, это ошибка данных, которую нужно логировать на backend-е;
- frontend должен получать уже готовое отображаемое имя.
Где исправлять
Исправление должно затронуть все состояния страницы:
association_type=all
association_type=sez
association_type=technoparks
association_type=clusters
Поля:
region_ranking.rows[].region_name;top_industries.rows[].industry_name;- при необходимости тот же источник справочников для
/analytics/filters/, если там используются те же placeholder-значения вregions[].labelилиindustries[].label.
Acceptance criteria
Response не должен содержать placeholder-паттерны:
region_ranking.rows[].region_name != "Субъект РФ <id>"
top_industries.rows[].industry_name != "Отрасль <id>"
Для каждого association_type нужно проверить:
GET /api/v1/competence-map/analytics/?association_type=all
GET /api/v1/competence-map/analytics/?association_type=sez
GET /api/v1/competence-map/analytics/?association_type=technoparks
GET /api/v1/competence-map/analytics/?association_type=clusters
И убедиться, что:
- все строки
region_ranking.rows[]имеют заполненныйregion_name; - все строки
top_industries.rows[]имеют заполненныйindustry_name; - значения являются реальными названиями, а не техническими fallback-строками.
Проблема 2. Не приходят строки таблицы приоритетных направлений ОЭЗ
Где видно
В состоянии association_type=sez блок
Типы ОЭЗ и приоритетные направления деятельности отображает donut с типами ОЭЗ, но таблица
Приоритетные направления деятельности ОЭЗ пустая и показывает Нет данных.
При этом по контракту этот блок должен состоять из двух частей:
main_block.sez_types- типы ОЭЗ;main_block.priority_directions- таблица приоритетных направлений ОЭЗ.
Затронутый endpoint
GET /api/v1/competence-map/analytics/?association_type=sez
Затронутые поля
{
"association_type": "sez",
"main_block": {
"type": "sez_types_and_priority_directions",
"sez_types": {},
"priority_directions": {
"title": "Приоритетные направления деятельности ОЭЗ",
"sort": {
"field": "sez_count",
"direction": "desc"
},
"ranking_method": "ordinal",
"rows": []
}
}
}
Сейчас rows фактически пустой или не содержит нужных данных.
Требуемый формат
main_block.priority_directions.rows[] должен возвращать строки в формате:
{
"rank": 1,
"direction_id": "ict",
"direction_name": "Информационно-коммуникационные технологии",
"sez_count": 4
}
Полный блок должен сохранять структуру:
{
"priority_directions": {
"title": "Приоритетные направления деятельности ОЭЗ",
"sort": {
"field": "sez_count",
"direction": "desc"
},
"ranking_method": "ordinal",
"rows": [
{
"rank": 1,
"direction_id": "ict",
"direction_name": "Информационно-коммуникационные технологии",
"sez_count": 4
},
{
"rank": 2,
"direction_id": "aircraft-maintenance",
"direction_name": "Обслуживание и ремонт воздушных судов",
"sez_count": 3
}
]
}
}
Требования
main_block.typeдляassociation_type=sezдолжен бытьsez_types_and_priority_directions;main_block.priority_directionsдолжен присутствовать вместе сmain_block.sez_types;priority_directions.rowsдолжен быть массивом;- строки должны быть отсортированы на backend-е согласно
sort; rankдолжен быть рассчитан backend-ом и начинаться с1;direction_idдолжен быть стабильным id направления;direction_nameдолжен быть реальным названием направления, а не placeholder;sez_countдолжен быть числом>= 0;- пустой
rows: []допустим только если после применения фильтров действительно нет ни одного направления.
Acceptance criteria
Для запроса без дополнительных фильтров:
GET /api/v1/competence-map/analytics/?association_type=sez
Ожидается:
main_block.priority_directions.rows.length > 0;- каждая строка имеет
rank,direction_id,direction_name,sez_count; direction_nameзаполнен реальным названием;sez_countявляется числом;- таблица на frontend-е перестает показывать
Нет данных, если данные в источнике есть.
Проблема 3. Не приходят KPI-карточки численности сотрудников для ОЭЗ и технопарков
Где видно
На вкладках ОЭЗ и Технопарки есть блок Численность сотрудников.
Сейчас карточки отображаются с прочерками, потому что backend не возвращает данные в employee_stats.
Этот блок является серверным состоянием страницы: frontend не может рассчитать эти значения локально.
Затронутые endpoint-ы
GET /api/v1/competence-map/analytics/?association_type=sez
GET /api/v1/competence-map/analytics/?association_type=technoparks
ОЭЗ. Требуемые поля
Для association_type=sez backend должен возвращать:
{
"association_type": "sez",
"employee_stats": {
"items": [
{
"code": "opk_resident_employees_count",
"label": "Численность сотрудников организаций ОПК-резидентов ОЭЗ",
"value": 1678,
"value_type": "integer",
"numerator": 1678,
"denominator": null
},
{
"code": "sez_employees_count",
"label": "Численность сотрудников ОЭЗ",
"value": 5235,
"value_type": "integer",
"numerator": 5235,
"denominator": null
}
]
}
}
Обязательные метрики для ОЭЗ:
code |
Назначение | value_type |
|---|---|---|
opk_resident_employees_count |
численность сотрудников организаций ОПК-резидентов ОЭЗ | integer |
sez_employees_count |
общая численность сотрудников ОЭЗ | integer |
Технопарки. Требуемые поля
Для association_type=technoparks backend должен возвращать:
{
"association_type": "technoparks",
"employee_stats": {
"items": [
{
"code": "opk_resident_employees_count",
"label": "Численность сотрудников организаций ОПК-резидентов технопарков",
"value": 1678,
"value_type": "integer",
"numerator": 1678,
"denominator": null
},
{
"code": "opk_resident_employees_share_percent",
"label": "Доля сотрудников организаций ОПК-резидентов технопарков",
"value": 35.0,
"value_type": "percent",
"numerator": 1678,
"denominator": 4794
}
]
}
}
Обязательные метрики для технопарков:
code |
Назначение | value_type |
|---|---|---|
opk_resident_employees_count |
численность сотрудников организаций ОПК-резидентов технопарков | integer |
opk_resident_employees_share_percent |
доля сотрудников организаций ОПК-резидентов технопарков | percent |
Требования
employee_statsдляassociation_type=sezдолжен быть объектом, а неnull;employee_statsдляassociation_type=technoparksдолжен быть объектом, а неnull;employee_stats.itemsдолжен содержать все обязательные метрики для выбранного типа;items[].codeдолжен быть стабильным;items[].labelдолжен быть готовым отображаемым названием;items[].valueдолжен быть числом;- для процентных метрик
numeratorиdenominatorобязательны, если знаменатель известен; denominatorу процентной метрики не должен быть0;0нужно возвращать как0, если показатель реально равен нулю;- не возвращать
employee_stats: nullдля применимого блока только потому, что расчет ещё не реализован.
Когда employee_stats может быть null
employee_stats: null допустим для состояний, где блок сотрудников не применяется:
association_type=all
association_type=clusters
Для sez и technoparks блок применим и должен быть заполнен.
Acceptance criteria
Для запроса:
GET /api/v1/competence-map/analytics/?association_type=sez
Ожидается:
employee_stats !== null;employee_stats.items.length >= 2;- есть item с
code = "opk_resident_employees_count"; - есть item с
code = "sez_employees_count"; - оба
valueявляются числами.
Для запроса:
GET /api/v1/competence-map/analytics/?association_type=technoparks
Ожидается:
employee_stats !== null;employee_stats.items.length >= 2;- есть item с
code = "opk_resident_employees_count"; - есть item с
code = "opk_resident_employees_share_percent"; - оба
valueявляются числами; - для
opk_resident_employees_share_percentзаполненыnumeratorиdenominator.
Сводка исправлений по backend response
| Проблема | Endpoint | Поля | Что нужно сделать |
|---|---|---|---|
| Placeholder субъектов РФ | /analytics/ для всех association_type |
region_ranking.rows[].region_name |
возвращать реальные названия субъектов РФ |
| Placeholder отраслей | /analytics/ для всех association_type |
top_industries.rows[].industry_name |
возвращать реальные названия отраслей |
| Пустая таблица направлений ОЭЗ | /analytics/?association_type=sez |
main_block.priority_directions.rows[] |
вернуть строки направлений ОЭЗ |
| Нет карточек сотрудников ОЭЗ | /analytics/?association_type=sez |
employee_stats.items[] |
вернуть две KPI-метрики сотрудников |
| Нет карточек сотрудников технопарков | /analytics/?association_type=technoparks |
employee_stats.items[] |
вернуть две KPI-метрики сотрудников |
Минимальный backend checklist
Перед отдачей исправления нужно проверить:
GET /api/v1/competence-map/analytics/?association_type=all
GET /api/v1/competence-map/analytics/?association_type=sez
GET /api/v1/competence-map/analytics/?association_type=technoparks
GET /api/v1/competence-map/analytics/?association_type=clusters
Проверки:
region_ranking.rows[].region_nameне содержитСубъект РФ;top_industries.rows[].industry_nameне содержитОтрасль;- для
association_type=sezзаполненmain_block.priority_directions.rows; - для
association_type=sezзаполненemployee_stats.items; - для
association_type=technoparksзаполненemployee_stats.items; - все числовые поля возвращаются числами, а не строками;
- пустые массивы используются только когда данных действительно нет по выбранным фильтрам;
- response соответствует
openapi.jsonи контрактуdocs/info/backend-endpoints-competence-map-analytics-from-layouts.md.