№ ТЗ-081 · Додаток А
Невід'ємна частина ТЗ-081 Проєкт документа
Додаток А до технічного завдання
Модель даних: таблиці та поля БД вузла і хаба
PostgreSQL 17 · вузол: +PostGIS · хаб: +pgvector · іменування snake_case · усі таблиці мають created_at/updated_at, якщо не вказано інше
1 Загальні конвенції
Первинні ключі: id BIGSERIAL; інтерв'ю та повідомлення обміну — uuid (глобальна ідемпотентність);
грошових полів у системі немає (облік «до оплати» — лише кількість);
таблиці з позначкою парт. партиціонуються за місяцями (created_at);
enum-поля реалізуються як VARCHAR + CHECK-констрейнт; значення наведені в описі;
гео-поля: geometry(Point,4326) / geometry(Polygon,4326) (PostGIS, лише вузол);
час — timestamptz, зона вузла — локальна для міста, зона хаба — Києва.
2 База міського вузла
2.1 Користувачі та доступ
users — інтерв'юери та координатори міста
Поле
Тип
Опис
id
bigserial PK
role
varchar
interviewer | coordinator
full_name
varchar(160)
ПІБ
phone
varchar(20)
контактний, унікальний
login
varchar(60)
унікальний
password_hash
varchar
bcrypt/argon2
totp_secret
varchar, encrypted
секрет 2FA
photo_path
varchar, null
фото профілю (Д-5 — єдине фото в системі)
status
varchar
active | suspended | dismissed
voice_ref_recorded_at
timestamptz, null
еталон голосу записано і передано на хаб; null = інтерв'ю недоступні
integrity_index
numeric(5,2), null
кешоване значення індексу (джерело істини — хаб)
index_breakdown
jsonb, null
спрощений розбір для кабінету
Обліковий запис координатора міста створюється і керується хабом (ТЗ, п. 2.3): запрошення, заміна, скидання пароля і сесії, деактивація. Облікові дані передаються на вузол захищеним каналом обміну; всі дії журналюються.
access_log — доступ до записів · парт. · без updated_at
Поле
Тип
Опис
id
bigserial PK
actor_type / actor_id
varchar / bigint
node_user | hub_user (через обмін)
action
varchar
listen | download | delete
interview_uuid
uuid
ip
inet, null
2.2 Анкети
questionnaires
Поле
Тип
Опис
id
bigserial PK
title
varchar(200)
status
varchar
draft | queued | active | completed | archived; активна — лише одна на місто (частковий унікальний індекс)
deadline
timestamptz, null
дедлайн збору активної анкети
queue_position
int, null
позиція в черзі міста (для queued)
quota_target
int, null
квота інтерв'ю анкети
created_by
FK users
У міста одночасно активна лише одна анкета; решта очікують у черзі за queue_position. Наступна анкета стартує автоматично після настання deadline активної або виконання quota_target; перехід статусів журналюється. Статистика анкети (агрегати відповідей, когортні зрізи за is_cohort-питаннями) доступна і під час збору, і після завершення — координатору вузла та хабу.
questionnaire_versions — незмінні після публікації
Поле
Тип
Опис
id
bigserial PK
questionnaire_id
FK
version
int
інкремент у межах анкети
published_at
timestamptz, null
null = чернетка
published_by
FK users, null
questions
Поле
Тип
Опис
id
bigserial PK
version_id
FK questionnaire_versions
position
int
порядок
type
varchar
single | multi | scale | number | date | text
text
text
формулювання
hint
text, null
підказка інтерв'юеру
required
bool
is_cohort
bool
когортне питання (стать, вікова група тощо) — відповіді формують когортні зрізи статистики анкети
± коригування за вердикти після закриття минулих періодів
payable_count
int
accepted + correction
details
jsonb
перелік interview_uuid коригувань
settings — конфігурація вузла (key/value jsonb)
Ключі: пороги темпу, ліміти офлайн-черги, retention (3 доби), адреса хаба, версія протоколу. Аналогічна таблиця на хабі.
3 База обласного хаба
3.1 Реєстр вузлів і користувачі
nodes — міські вузли області
Поле
Тип
Опис
id
bigserial PK
name
varchar(120)
місто
wg_address
inet
адреса у WireGuard-мережі
wg_public_key
varchar
api_token_hash
varchar
service-токен вузла, ротований
protocol_version
smallint
хаб приймає N і N−1
status
varchar
online | offline | disabled
last_seen_at / registered_at
timestamptz
health
jsonb
диск, черги, відставання (з моніторингу)
hub_users
Поле
Тип
Опис
id
bigserial PK
role
varchar
admin | operator | auditor
full_name / login / password_hash / totp_secret
varchar
як у вузла
status
varchar
active | suspended
Таблиці devices, sessions, access_log хаба — ідентичні вузловим (розд. 2.1) і тут не повторюються.
node_coordinators — реєстр координаторів вузлів (керується хабом, ТЗ п. 2.3)
Поле
Тип
Опис
id
bigserial PK
node_id
FK nodes
coordinator_ref
bigint, null
id користувача-координатора на вузлі (після активації)
full_name / phone
varchar
account_state
varchar
invited (запрошення надіслано) | active | replaced | deactivated
invited_at / activated_at / deactivated_at
timestamptz, null
managed_by
FK hub_users
хто виконав останню дію
Дії хаба над координаторами (створення облікового запису, запрошення, заміна, скидання пароля і сесії, деактивація) передаються на вузол командами обміну захищеним каналом і журналюються з обох боків.
3.2 Обмін і реєстр інтерв'ю
manifests · парт.
Поле
Тип
Опис
uuid
uuid PK
з вузла, ідемпотентність
node_id
FK nodes
shift_ref / interviewer_ref
bigint
id зміни та інтерв'юера на вузлі
interviews_count
int
payload
jsonb
метадані і сигнали вузла за кожним інтерв'ю
received_at / processed_at
timestamptz
status
varchar
received | queued | done
interview_registry — реєстр інтерв'ю області · парт.
Еталон (kind = interviewer_reference) рахується з контрольного фрагмента ~30 с, який інтерв'юер начитує при першому вході в PWA (ТЗ, п. 3.1); сирий аудіозапис еталона після розрахунку відбитка видаляється. Повторний запис — через координатора (нова версія еталона замінює стару).
voice_matches — збіги відбитків
Поле
Тип
Опис
id
bigserial PK
voiceprint_id / matched_id
FK voiceprints
пара
similarity
numeric(4,3)
косинусна близькість
scope
varchar
same_interviewer | same_city | oblast
3.4 Скоринг, аудит, нагляд
signal_weights — ваги і пороги (історія в signal_weights_history)
інтерв'ю, перевірено, відхилено, середній бал, збої, черга
4 Довідники
Причини відхилення інтерв'ю (вердикти): немає другого голосу; голос збігається з інтерв'юером; повторюваний респондент; поза районом; фабрикація темпу; дубль запису; порушення згоди; інше (коментар обов'язковий);
Підстави зміни скринінгу: довірений за статистикою; контрольне увімкнення; примусово хабом; вручну (коментар);
Типи маркерів — збігаються з типами сигналів (розд. 2.4 + голосові: no_second_voice, self_dialogue, repeat_respondent_interviewer, repeat_respondent_city, repeat_respondent_oblast, second_voice_unconfirmed).
OTACK · розробка цифрових продуктів Додаток А до ТЗ-081. Схема уточнюється на етапі 1 без зміни складу сутностей.