Files
opora/docs/tech-design/01-data-model.md
T

20 KiB
Raw Blame History

Технический проект. Документ 1: Модель данных

Проект: АС «Платформа ОПОРА РОССИИ» СУБД: PostgreSQL 16 Дата: 26.09.2026 Статус: черновик к согласованию


1. Общие принципы

  • Единое хранилище для бота и сайта (п. 4.1.1 ТЗ).
  • Первичные ключи — bigint (identity) либо uuid для внешне адресуемых сущностей (токены, сессии).
  • Все таблицы содержат created_at / updated_at (timestamptz).
  • Мягкое удаление (deleted_at) — для сущностей, где нужна история (карточки, задачи, артефакты).
  • Перечисления — через enum-типы PostgreSQL или text + CHECK (выбор на этапе реализации).
  • Денормализованные агрегаты (прирост, рейтинг) — материализованные представления, пересчитываемые Celery-задачей.
  • JSONB — для гибких полей (фильтры аудитории, payload аудита).

2. Перечисления (enum)

Enum Значения
messenger_type telegram, max
user_status active, blocked, pending
role_code admin, coordinator, region_head, member, deputy
login_token_status active, used, expired, revoked
snapshot_kind point0, current, history
member_type member, resident
task_status new, in_progress, review, done
task_priority low, medium, high
request_type join, access, event, support
request_status new, in_progress, closed
broadcast_type info, education, rating
broadcast_status draft, scheduled, sending, sent, failed
recipient_status pending, sent, failed
actor_type user, assistant, system
link_type community, chat

3. Справочники

3.1. federal_districts — федеральные округа (8)

Поле Тип Ограничения Описание
id bigint PK
name text NOT NULL, UNIQUE «ЮФО», «ЦФО»…
code text UNIQUE краткий код
created_at timestamptz NOT NULL

3.2. regions — регионы (89)

Поле Тип Ограничения Описание
id bigint PK
name text NOT NULL, UNIQUE «Донецкая Народная Республика»
federal_district_id bigint FK → federal_districts округ
status text NOT NULL, default active
created_at / updated_at timestamptz NOT NULL

3.3. roles — роли

Поле Тип Ограничения
id bigint PK
code role_code NOT NULL, UNIQUE
name text NOT NULL

4. Пользователи и доступ

4.1. users — аккаунты (в т.ч. члены организации)

Поле Тип Ограничения Описание
id bigint PK
full_name text ФИО
messenger_type messenger_type NOT NULL
messenger_id text NOT NULL внешний id в мессенджере
phone text опционально
status user_status NOT NULL, default active статус аккаунта
member_type member_type NULL member / resident; NULL — не член
joined_at date NULL дата вступления
created_at / updated_at timestamptz NOT NULL

Уникальность: UNIQUE (messenger_type, messenger_id).

Решение: отдельная таблица members не используется — членство выражается через member_type и status пользователя (учёт численности — агрегат по users).

4.2. user_regions — привязка пользователя к регионам (мультирегиональность)

Поле Тип Ограничения
id bigint PK
user_id bigint FK → users
region_id bigint FK → regions
is_primary boolean NOT NULL, default false
created_at timestamptz NOT NULL

Уникальность: UNIQUE (user_id, region_id). Один пользователь может быть привязан к нескольким регионам; is_primary — основной.

4.3. user_roles — роли пользователей (с областью действия)

Поле Тип Ограничения
id bigint PK
user_id bigint FK → users
role_id bigint FK → roles
region_id bigint FK → regions, NULL (область: своё отделение)
granted_by bigint FK → users, NULL
granted_at timestamptz NOT NULL

Уникальность: UNIQUE (user_id, role_id, region_id).

4.4. login_tokens — одноразовые ссылки входа

Поле Тип Ограничения Описание
id uuid PK
token text NOT NULL, UNIQUE случайный, достаточной длины
user_id bigint FK → users, NULL может быть выдан до регистрации
region_id bigint FK → regions, NULL
issued_by bigint FK → users (координатор)
status login_token_status NOT NULL, default active
expires_at timestamptz NOT NULL
used_at timestamptz NULL
created_at timestamptz NOT NULL

Правило: повторное использование → status = used, ответ «Ссылка уже была использована…» (п. 4.1.5 ТЗ).

4.5. sessions — сессии

Поле Тип Ограничения
id uuid PK
user_id bigint FK → users
token_hash text NOT NULL, UNIQUE
messenger_type messenger_type NOT NULL
created_at timestamptz NOT NULL
expires_at timestamptz NOT NULL
revoked_at timestamptz NULL

5. Цели и метрики

5.1. goals — цели

Поле Тип Ограничения Описание
id bigint PK
name text NOT NULL «Рост базы членов»
description text
target_value numeric «+1096»
unit text «чел.»
is_active boolean NOT NULL, default true
created_at / updated_at timestamptz NOT NULL

5.2. snapshots — срезы

Поле Тип Ограничения Описание
id bigint PK
goal_id bigint FK → goals
snapshot_date date NOT NULL дата среза
kind snapshot_kind NOT NULL point0 / current / history
source text upload / manual
created_at timestamptz NOT NULL

Правило: для цели ровно один point0; current — последний по дате.

5.3. region_goal_values — значения по регионам

Поле Тип Ограничения
id bigint PK
snapshot_id bigint FK → snapshots
region_id bigint FK → regions
value numeric NOT NULL
created_at timestamptz NOT NULL

Уникальность: UNIQUE (snapshot_id, region_id).

5.4. data_uploads — загрузки выгрузок

Поле Тип Ограничения
id bigint PK
goal_id bigint FK → goals
file_key text NOT NULL (MinIO)
uploaded_by bigint FK → users
status text uploaded / processed / failed
processed_at timestamptz NULL
created_at timestamptz NOT NULL

5.5. Материализованное представление mv_region_metrics

Расчёт (п. 4.3.1 ТЗ):

  • point0_value — значение среза point0;
  • current_value — значение последнего current;
  • growth = current_value − point0_value;
  • has_dynamics = growth > 0;
  • freshness_days = now() − snapshot_date (для правила «не старше 7 дней»).

Поля: region_id, goal_id, point0_value, current_value, growth, has_dynamics, snapshot_date, freshness_days, updated_at.

5.6. mv_district_metrics

Агрегат по округу: district_id, goal_id, growth_sum, regions_with_dynamics, regions_total.


6. Карточка региона

6.1. region_cards

Поле Тип Ограничения
region_id bigint PK, FK → regions
population integer численность
head_user_id bigint FK → users, NULL
updated_at timestamptz NOT NULL
Поле Тип Ограничения
id bigint PK
region_id bigint FK → regions
type link_type NOT NULL
title text
url text NOT NULL
created_at / updated_at timestamptz NOT NULL

6.3. region_card_history — история изменений

Поле Тип Ограничения
id bigint PK
region_id bigint FK → regions
field text NOT NULL
old_value text
new_value text
changed_by bigint FK → users
changed_at timestamptz NOT NULL

7. Члены организации

Решение: отдельная таблица members не используется. Членство выражается через поля users.member_type (member / resident) и users.status, а привязка к регионам — через user_regions (раздел 4). Учёт численности — агрегат по users с фильтром по региону и типу.


8. Артефакты

8.1. artifacts

Поле Тип Ограничения Описание
id bigint PK
title text
file_key text NOT NULL ключ в MinIO
file_name text NOT NULL
mime_type text
size_bytes bigint до 50 МБ (п. 4.2.8.1)
region_id bigint FK → regions, NULL привязка
goal_id bigint FK → goals, NULL привязка
task_id bigint FK → tasks, NULL привязка
uploaded_by bigint FK → users
deleted_at timestamptz NULL мягкое удаление
created_at timestamptz NOT NULL

9. Задачи (трекер)

9.1. tasks

Поле Тип Ограничения
id bigint PK
title text NOT NULL
description text
due_date date
priority task_priority NOT NULL, default medium
status task_status NOT NULL, default new
region_id bigint FK → regions, NULL
assignee_user_id bigint FK → users, NULL
assignee_role role_code NULL
created_by bigint FK → users
deleted_at timestamptz NULL
created_at / updated_at timestamptz NOT NULL

9.2. task_comments

Поле Тип Ограничения
id bigint PK
task_id bigint FK → tasks
author_id bigint FK → users
body text NOT NULL
created_at timestamptz NOT NULL

10. База знаний

10.1. knowledge_categories

Поле Тип Ограничения
id bigint PK
name text NOT NULL, UNIQUE

10.2. knowledge_articles

Поле Тип Ограничения
id bigint PK
title text NOT NULL
body text NOT NULL
category_id bigint FK → knowledge_categories, NULL
created_by bigint FK → users
created_at / updated_at timestamptz NOT NULL

11. Календарь событий

11.1. events

Поле Тип Ограничения
id bigint PK
title text NOT NULL
description text
starts_at timestamptz NOT NULL
ends_at timestamptz
location text
created_by bigint FK → users
created_at / updated_at timestamptz NOT NULL

11.2. event_reminders

Поле Тип Ограничения
id bigint PK
event_id bigint FK → events
user_id bigint FK → users
remind_at timestamptz NOT NULL
sent_at timestamptz NULL

12. Запросы (заявки)

12.1. requests

Поле Тип Ограничения
id bigint PK
type request_type NOT NULL
user_id bigint FK → users
region_id bigint FK → regions, NULL
subject text
body text
status request_status NOT NULL, default new
assigned_to bigint FK → users, NULL
created_at / updated_at timestamptz NOT NULL

12.2. request_comments

Поле Тип Ограничения
id bigint PK
request_id bigint FK → requests
author_id bigint FK → users
body text NOT NULL
created_at timestamptz NOT NULL

13. Советы от регионов

13.1. advices

Поле Тип Ограничения
id bigint PK
region_id bigint FK → regions
goal_id bigint FK → goals, NULL
author_user_id bigint FK → users
body text NOT NULL
status text published / hidden
created_at timestamptz NOT NULL

14. Рассылки

14.1. broadcast_templates

Поле Тип Ограничения
id bigint PK
code text NOT NULL, UNIQUE
name text NOT NULL
type broadcast_type NOT NULL
body_template text NOT NULL (с плейсхолдерами)
created_at / updated_at timestamptz NOT NULL

14.2. broadcasts

Поле Тип Ограничения
id bigint PK
template_id bigint FK → broadcast_templates, NULL
type broadcast_type NOT NULL
subject text
body text
audience_filter jsonb регион/округ/роль/подписка
status broadcast_status NOT NULL, default draft
scheduled_at timestamptz NULL
started_at / finished_at timestamptz NULL
created_by bigint FK → users
created_at timestamptz NOT NULL

14.3. broadcast_recipients

Поле Тип Ограничения
id bigint PK
broadcast_id bigint FK → broadcasts
user_id bigint FK → users
status recipient_status NOT NULL, default pending
sent_at timestamptz NULL
error text NULL

Уникальность: UNIQUE (broadcast_id, user_id).


15. История платформы

15.1. platform_history

Поле Тип Ограничения
id bigint PK
title text NOT NULL
body text
event_date date
is_public boolean NOT NULL, default true
created_by bigint FK → users
created_at timestamptz NOT NULL

16. Аудит и ассистент

16.1. audit_log

Поле Тип Ограничения
id bigint PK
actor_user_id bigint FK → users, NULL
actor_type actor_type NOT NULL
action text NOT NULL
entity_type text
entity_id text
payload jsonb
ip inet
created_at timestamptz NOT NULL

16.2. assistant_configs

Поле Тип Ограничения
id bigint PK
owner_user_id bigint FK → users, UNIQUE
enabled boolean NOT NULL, default false
permissions jsonb разрешённые действия
created_at / updated_at timestamptz NOT NULL

16.3. assistant_actions

Поле Тип Ограничения
id bigint PK
owner_user_id bigint FK → users
action text NOT NULL
entity_type text
entity_id text
payload jsonb
status text ok / failed
created_at timestamptz NOT NULL

17. ERD (основные связи)

erDiagram
    FEDERAL_DISTRICTS ||--o{ REGIONS : contains
    REGIONS ||--|| REGION_CARDS : has
    REGIONS ||--o{ REGION_LINKS : has
    REGIONS ||--o{ REGION_GOAL_VALUES : has
    REGIONS ||--o{ TASKS : has
    REGIONS ||--o{ ARTIFACTS : has
    REGIONS ||--o{ ADVICES : has

    USERS ||--o{ USER_REGIONS : "привязан к"
    REGIONS ||--o{ USER_REGIONS : includes
    USERS ||--o{ USER_ROLES : has
    ROLES ||--o{ USER_ROLES : grants
    USERS ||--o{ LOGIN_TOKENS : receives
    USERS ||--o{ SESSIONS : has
    USERS ||--|| ASSISTANT_CONFIGS : owns
    USERS ||--o{ ASSISTANT_ACTIONS : performs

    GOALS ||--o{ SNAPSHOTS : has
    SNAPSHOTS ||--o{ REGION_GOAL_VALUES : contains
    GOALS ||--o{ DATA_UPLOADS : receives

    TASKS ||--o{ TASK_COMMENTS : has
    TASKS ||--o{ ARTIFACTS : links

    KNOWLEDGE_CATEGORIES ||--o{ KNOWLEDGE_ARTICLES : groups
    EVENTS ||--o{ EVENT_REMINDERS : triggers
    REQUESTS ||--o{ REQUEST_COMMENTS : has

    BROADCAST_TEMPLATES ||--o{ BROADCASTS : based_on
    BROADCASTS ||--o{ BROADCAST_RECIPIENTS : sends

18. Индексы и производительность

Таблица Индекс Назначение
users (messenger_type, messenger_id) UNIQUE поиск при входе
login_tokens (token) UNIQUE, (status, expires_at) проверка ссылки
region_goal_values (snapshot_id, region_id) UNIQUE целостность
region_goal_values (region_id) выборки по региону
tasks (region_id, status), (assignee_user_id, status) фильтры трекера
artifacts (region_id), (task_id), (goal_id) привязки
broadcast_recipients (broadcast_id, status) прогресс рассылки
audit_log (actor_user_id, created_at), (entity_type, entity_id) аудит
user_regions (region_id), (user_id) численность, привязки

Материализованные представления mv_region_metrics, mv_district_metrics обновляются Celery-задачей после каждой загрузки выгрузки и по расписанию.


19. Принятые решения

  1. Члены vs пользователи — отдельная таблица members не используется; членство через users.member_type + users.status.
  2. История срезов — хранятся все срезы (snapshot_kind = history), не только point0 + current.
  3. Резиденты — через member_type (member / resident), без отдельной таблицы.
  4. Мультирегиональность — пользователь может быть привязан к нескольким регионам через user_regions.
  5. Хранение артефактов — MinIO на едином диске сервера (отдельного диска нет).

20. Следующие документы

  • Документ 2: контракты API (REST).
  • Документ 3: макеты экранов (mobile-first).