OTACKРозробка цифрових продуктів
№ ТЗ-081 · Додаток А
Невід'ємна частина ТЗ-081
Проєкт документа
Додаток А до технічного завдання

Модель даних:
таблиці та поля БД вузла і хаба

PostgreSQL 17 · вузол: +PostGIS · хаб: +pgvector · іменування snake_case · усі таблиці мають created_at/updated_at, якщо не вказано інше

1 Загальні конвенції

2 База міського вузла

2.1 Користувачі та доступ

users — інтерв'юери та координатори міста
ПолеТипОпис
idbigserial PK
rolevarcharinterviewer | coordinator
full_namevarchar(160)ПІБ
phonevarchar(20)контактний, унікальний
loginvarchar(60)унікальний
password_hashvarcharbcrypt/argon2
totp_secretvarchar, encryptedсекрет 2FA
photo_pathvarchar, nullфото профілю (Д-5 — єдине фото в системі)
statusvarcharactive | suspended | dismissed
voice_ref_recorded_attimestamptz, nullеталон голосу записано і передано на хаб; null = інтерв'ю недоступні
integrity_indexnumeric(5,2), nullкешоване значення індексу (джерело істини — хаб)
index_breakdownjsonb, nullспрощений розбір для кабінету

Обліковий запис координатора міста створюється і керується хабом (ТЗ, п. 2.3): запрошення, заміна, скидання пароля і сесії, деактивація. Облікові дані передаються на вузол захищеним каналом обміну; всі дії журналюються.

devices — зареєстровані пристрої
ПолеТипОпис
idbigserial PK
user_idFK users
fingerprintvarchar(128)відбиток браузера/пристрою
platform / user_agentvarcharОС, браузер
statusvarcharactive | revoked; активний — один на інтерв'юера
first_seen_at / last_seen_attimestamptz
sessions
ПолеТипОпис
idbigserial PK
user_id / device_idFK
token_hashvarchar
ipinet
started_at / last_activity_at / expires_attimestamptzстрок 30 днів
revoked_at / revoke_reasontimestamptz / varchar, nullparallel_login | coordinator | expired
session_events — сигнали передачі акаунта · парт.
ПолеТипОпис
idbigserial PK
user_idFK users
typevarcharlogin | login_denied_parallel | device_change | session_revoked
device_fingerprint / ipvarchar / inet
geogeometry(Point), nullде відбулася спроба (якщо надано)
detailsjsonb
access_log — доступ до записів · парт. · без updated_at
ПолеТипОпис
idbigserial PK
actor_type / actor_idvarchar / bigintnode_user | hub_user (через обмін)
actionvarcharlisten | download | delete
interview_uuiduuid
ipinet, null

2.2 Анкети

questionnaires
ПолеТипОпис
idbigserial PK
titlevarchar(200)
statusvarchardraft | queued | active | completed | archived; активна — лише одна на місто (частковий унікальний індекс)
deadlinetimestamptz, nullдедлайн збору активної анкети
queue_positionint, nullпозиція в черзі міста (для queued)
quota_targetint, nullквота інтерв'ю анкети
created_byFK users

У міста одночасно активна лише одна анкета; решта очікують у черзі за queue_position. Наступна анкета стартує автоматично після настання deadline активної або виконання quota_target; перехід статусів журналюється. Статистика анкети (агрегати відповідей, когортні зрізи за is_cohort-питаннями) доступна і під час збору, і після завершення — координатору вузла та хабу.

questionnaire_versions — незмінні після публікації
ПолеТипОпис
idbigserial PK
questionnaire_idFK
versionintінкремент у межах анкети
published_attimestamptz, nullnull = чернетка
published_byFK users, null
questions
ПолеТипОпис
idbigserial PK
version_idFK questionnaire_versions
positionintпорядок
typevarcharsingle | multi | scale | number | date | text
texttextформулювання
hinttext, nullпідказка інтерв'юеру
requiredbool
is_cohortboolкогортне питання (стать, вікова група тощо) — відповіді формують когортні зрізи статистики анкети
configjsonbмежі шкали/числа, маски
logicjsonb, nullумови показу (залежність від відповідей)
question_options
ПолеТипОпис
idbigserial PK
question_idFK questions
positionint
labelvarchar(300)
is_otherboolваріант «інше» з вільним полем

2.3 Завдання, зміни, інтерв'ю

districts — райони роботи
ПолеТипОпис
idbigserial PK
namevarchar(160)
kindvarcharpolygon | radius
geomgeometry(Polygon), nullдля polygon
center / radius_mgeometry(Point) / int, nullдля radius
created_byFK users
assignments — завдання
ПолеТипОпис
idbigserial PK
district_idFK districts
questionnaire_version_idFKконкретна версія анкети
date_from / date_todateперіод
quotaint, nullцільова кількість інтерв'ю
statusvarcharplanned | active | closed
created_byFK users
assignment_interviewers — призначення (pivot)
ПолеТипОпис
assignment_id / user_idFK, разом PK
assigned_by / assigned_atFK users / timestamptz
revoked_attimestamptz, nullперепризначення
shifts — зміни
ПолеТипОпис
idbigserial PK
user_id / assignment_idFK
started_at / closed_attimestamptz
close_typevarcharmanual | auto_midnight
interviews_countintкеш
manifest_uuiduuid, nullпосилання на відправлений маніфест
statusvarcharopen | closed | manifested | acked
interviews · парт.
ПолеТипОпис
uuiduuid PKглобальний ідентифікатор (той самий на хабі)
shift_id / user_id / assignment_idFK
questionnaire_version_idFK
respondent_consentboolпозначка згоди (обов'язкова для старту)
started_at / finished_attimestamptz
duration_secint
geo_start / geo_finishgeometry(Point)точки старту і фінішу
geo_start_acc / geo_finish_accintточність, м
geo_start_in_zone / geo_finish_in_zoneboolрезультат радіус-чеку
audio_uuiduuid, nullFK media_files; null = скринінг вимкнено
statusvarcharrecorded | uploaded | signals_done | manifested | diarized | skipped_screening_off | marked | accepted | rejected | annulled
abort_reasonvarchar, nullcall | minimized | screen_lock | manual
prelim_score / final_scorenumeric(5,2), nullбал вузла / бал хаба
answers · парт.
ПолеТипОпис
idbigserial PK
interview_uuiduuid FK
question_idFK questions
valuejsonbзначення будь-якого типу питання (обрані опції, число, текст)
answered_attimestamptzдля аналізу темпу заповнення
media_files — аудіо · парт.
ПолеТипОпис
uuiduuid PK
interview_uuiduuid FK
pathvarcharшлях на диску вузла
size_bytes / duration_secbigint / int
codecvarcharopus/webm
sha256char(64)контроль цілісності
uploaded_attimestamptz
diarized_attimestamptz, nullack хаба; старт 3-денного відліку
holdboolфлагований — не видаляти до вердикта (Д-10)
delete_afterdate, nulldiarized_at + 3 доби
deleted_attimestamptz, nullфакт видалення (журналюється)

2.4 Сигнали, скринінг, обмін, облік

signals — локальні сигнали вузла · парт.
ПолеТипОпис
idbigserial PK
interview_uuiduuid FK, nullnull для сигналів рівня інтерв'юера (темп, сесії)
user_idFK users
typevarcharsilence_ratio | vad_ratio | volume_low | clipping | fingerprint_dup | geo_out_start | geo_out_finish | speed_anomaly | cluster_point | tempo_high | tempo_low | parallel_session | record_abort
value_numnumeric, nullнормоване значення
detailsjsonbсирі дані розрахунку
computed_attimestamptz
screening_states — стан скринінгу (Д-12)
ПолеТипОпис
user_idFK users, PK
audio_enabledboolfalse = довірений, аудіо не пишеться
changed_byFK usersкоординатор
reason_codevarchartrusted_by_stats | control_reenable | forced_by_hub | manual
reason_commenttext, null
stats_snapshotjsonbстатистична довідка на момент рішення (перевірених інтерв'ю, індекс, маркери)
changed_attimestamptz

Історія змін — таблиця screening_history з тими самими полями + id; кожна зміна стану реплікується на хаб.

outbox — черга обміну з хабом
ПолеТипОпис
uuiduuid PKідемпотентність доставки
typevarcharmanifest | screening_state | appeal | voice_reference | ack
payloadjsonb
statusvarcharpending | sent | acked | failed
attempts / last_attempt_at / last_errorint / timestamptz / textретраї з бекофом
hub_markers — маркери, отримані з хаба · парт.
ПолеТипОпис
idbigserial PK
interview_uuiduuid FK
marker_type / severityvarchar / smallintтип і вага (довідник хаба)
payloadjsonbпояснення для координатора
received_attimestamptz
verdicts — вердикти, отримані з хаба
ПолеТипОпис
interview_uuiduuid PK
verdictvarcharaccepted | rejected
reason_code / commentvarchar / text, null
received_attimestamptzвизначає період обліку коригувань
appeals — апеляції інтерв'юерів
ПолеТипОпис
idbigserial PK
interview_uuid / user_iduuid FK / FK users
messagetext
statusvarcharopen | in_review | upheld | declined
resolved_attimestamptz, nullрішення приходить пушем із хаба
payable_periods — розрахункові періоди
ПолеТипОпис
idbigserial PK
period_start / period_enddate
statusvarcharopen | closed; закритий незмінний
closed_by / closed_atFK users / timestamptz, null
payable_lines — кількісний підсумок «до оплати»
ПолеТипОпис
idbigserial PK
period_id / user_idFK
accepted_count / rejected_countintза період
correction_countint± коригування за вердикти після закриття минулих періодів
payable_countintaccepted + correction
detailsjsonbперелік interview_uuid коригувань
settings — конфігурація вузла (key/value jsonb)

Ключі: пороги темпу, ліміти офлайн-черги, retention (3 доби), адреса хаба, версія протоколу. Аналогічна таблиця на хабі.

3 База обласного хаба

3.1 Реєстр вузлів і користувачі

nodes — міські вузли області
ПолеТипОпис
idbigserial PK
namevarchar(120)місто
wg_addressinetадреса у WireGuard-мережі
wg_public_keyvarchar
api_token_hashvarcharservice-токен вузла, ротований
protocol_versionsmallintхаб приймає N і N−1
statusvarcharonline | offline | disabled
last_seen_at / registered_attimestamptz
healthjsonbдиск, черги, відставання (з моніторингу)
hub_users
ПолеТипОпис
idbigserial PK
rolevarcharadmin | operator | auditor
full_name / login / password_hash / totp_secretvarcharяк у вузла
statusvarcharactive | suspended

Таблиці devices, sessions, access_log хаба — ідентичні вузловим (розд. 2.1) і тут не повторюються.

node_coordinators — реєстр координаторів вузлів (керується хабом, ТЗ п. 2.3)
ПолеТипОпис
idbigserial PK
node_idFK nodes
coordinator_refbigint, nullid користувача-координатора на вузлі (після активації)
full_name / phonevarchar
account_statevarcharinvited (запрошення надіслано) | active | replaced | deactivated
invited_at / activated_at / deactivated_attimestamptz, null
managed_byFK hub_usersхто виконав останню дію

Дії хаба над координаторами (створення облікового запису, запрошення, заміна, скидання пароля і сесії, деактивація) передаються на вузол командами обміну захищеним каналом і журналюються з обох боків.

3.2 Обмін і реєстр інтерв'ю

manifests · парт.
ПолеТипОпис
uuiduuid PKз вузла, ідемпотентність
node_idFK nodes
shift_ref / interviewer_refbigintid зміни та інтерв'юера на вузлі
interviews_countint
payloadjsonbметадані і сигнали вузла за кожним інтерв'ю
received_at / processed_attimestamptz
statusvarcharreceived | queued | done
interview_registry — реєстр інтерв'ю області · парт.
ПолеТипОпис
uuiduuid PK= uuid інтерв'ю на вузлі
node_id / manifest_uuidFK
interviewer_refbigintid інтерв'юера на вузлі
metajsonbтривалість, гео-флаги, темп, сигнали вузла
audio_statevarcharpending | fetching | diarized | skipped_screening_off | failed
scorenumeric(5,2), nullпідсумковий бал інтерв'ю
score_breakdownjsonb, nullсигнал → значення → внесок
audit_statevarcharnone | queued | in_review | accepted | rejected
fetch_queue — черга забирання аудіо
ПолеТипОпис
idbigserial PK
interview_uuiduuid FK registry
prioritysmallintвище — раніше (за сигналами вузла)
statusvarcharqueued | fetching | done | failed
attempts / last_errorint / text

3.3 Діаризація і голосові відбитки

diarization_results · парт.
ПолеТипОпис
interview_uuiduuid PK
voices_countsmallintу проаналізованому вікні
window_secsmallintфактична довжина вікна (30–90)
net_second_voice_secnumeric(5,1)чистої речі другого голосу
second_voice_confirmedbool, nullnull = не набралося мовлення
similarity_to_interviewernumeric(4,3), nullдругий голос ≈ інтерв'юер → фейк «на два голоси»
gpu_msintметрика продуктивності
detailsjsonbсегменти, впевненості
voiceprints — відбитки · TTL 90 днів (Д-11)
ПолеТипОпис
idbigserial PK
interview_uuiduuid FK, nullnull для еталона інтерв'юера
node_id / interviewer_refFK / bigint
kindvarcharrespondent | interviewer_reference
embeddingvector(256)pgvector, необоротний
expires_attimestamptzcreated_at + 90 днів; еталони — без строку

Еталон (kind = interviewer_reference) рахується з контрольного фрагмента ~30 с, який інтерв'юер начитує при першому вході в PWA (ТЗ, п. 3.1); сирий аудіозапис еталона після розрахунку відбитка видаляється. Повторний запис — через координатора (нова версія еталона замінює стару).

voice_matches — збіги відбитків
ПолеТипОпис
idbigserial PK
voiceprint_id / matched_idFK voiceprintsпара
similaritynumeric(4,3)косинусна близькість
scopevarcharsame_interviewer | same_city | oblast

3.4 Скоринг, аудит, нагляд

signal_weights — ваги і пороги (історія в signal_weights_history)
ПолеТипОпис
keyvarchar PKтип сигналу (довідник розд. 2.4 + голосові)
group_namevarcharгео / темп / голос / аудіо / сесії / стабільність / вердикти
weightnumeric(4,3)
thresholdsjsonbнормування значення
activebool
effective_from / updated_bytimestamptz / FK hub_usersзастосування «від дати», минуле не переписується
integrity_indexes — індекс доброчесності (знімки в integrity_index_history)
ПолеТипОпис
node_id / interviewer_refразом PK
valuenumeric(5,2)0–100
breakdownjsonbгрупи → внески
interviews_windowintкількість інтерв'ю у ковзному періоді
computed_attimestamptz
audit_queue
ПолеТипОпис
idbigserial PK
interview_uuiduuid FK registry
reasonvarcharmarker | random | appeal | control
prioritysmallint
statusvarcharpending | in_review | done | expired_audio
assigned_to / taken_atFK hub_users / timestamptz, nullзакріплення за аудитором
audio_deadlinetimestamptz, nullкінець 3-денного вікна прослуховування
hub_verdicts · парт.
ПолеТипОпис
interview_uuiduuid PK
auditor_idFK hub_users
verdictvarcharaccepted | rejected
reason_code / commentvarchar / text, nullдовідник причин відхилення
listened_secintскільки реально прослухано (контроль якості аудиту)
pushed_attimestamptz, nullдоставлено вузлу
hub_appeals — розгляд апеляцій (дзеркало вузлових + рішення)
ПолеТипОпис
uuiduuid PKз вузла
interview_uuid / node_idFK
messagetext
statusvarcharopen | in_review | upheld | declined
resolved_by / resolution_commentFK hub_users / text, null
screening_registry — стан скринінгу по області (Д-12)
ПолеТипОпис
node_id / interviewer_refразом PK
audio_enabledbool
reason_code / stats_snapshotvarchar / jsonbреплікація з вузла
assessedvarchar, nullоцінка хаба: justified | no_stats | bad_stats
changed_at / synced_attimestamptz
coordinator_markers — маркери координаторам міст (Д-10, Д-12)
ПолеТипОпис
idbigserial PK
node_id / coordinator_refFK / bigint, null
typevarcharscreening_no_stats | screening_bad_stats | audit_queue_lag | verdict_anomaly
severitysmallint
payloadjsonbобґрунтування, цифри
statusvarcharopen | acknowledged | closed
analytics_daily — агрегати для дашбордів
ПолеТипОпис
idbigserial PK
scope / scope_idvarchar / bigintoblast | node | interviewer
datedate
metricsjsonbінтерв'ю, перевірено, відхилено, середній бал, збої, черга

4 Довідники

OTACK · розробка цифрових продуктів
Додаток А до ТЗ-081. Схема уточнюється на етапі 1 без зміни складу сутностей.
Пов'язані документи:
ТЗ-081 · Додаток Б · СЗ-081