Files
anal-front/docs/info/backend-issues-competence-map-analytics-current-data.md
Gleb Korotkiy 67ec34d619
Some checks failed
CI/CD Pipeline / Quality Gate (push) Failing after 2m37s
CI/CD Pipeline / Build and Deploy Dev via Compose (push) Has been skipped
refactor: align frontend with FSD architecture
2026-06-22 12:33:33 +03:00

19 KiB
Raw Permalink Blame History

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.