diff --git a/docs/tech-design/01-data-model.md b/docs/tech-design/01-data-model.md index fc93461..d55435b 100644 --- a/docs/tech-design/01-data-model.md +++ b/docs/tech-design/01-data-model.md @@ -74,7 +74,7 @@ ## 4. Пользователи и доступ -### 4.1. `users` — аккаунты +### 4.1. `users` — аккаунты (в т.ч. члены организации) | Поле | Тип | Ограничения | Описание | |---|---|---|---| @@ -83,13 +83,28 @@ | messenger_type | messenger_type | NOT NULL | | | messenger_id | text | NOT NULL | внешний id в мессенджере | | phone | text | | опционально | -| region_id | bigint | FK → regions, NULL | привязка к региону | -| status | user_status | NOT NULL, default `active` | | +| 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)`. -### 4.2. `user_roles` — роли пользователей (с областью действия) +**Решение:** отдельная таблица `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` — роли пользователей (с областью действия) | Поле | Тип | Ограничения | |---|---|---| @@ -102,7 +117,7 @@ **Уникальность:** `UNIQUE (user_id, role_id, region_id)`. -### 4.3. `login_tokens` — одноразовые ссылки входа +### 4.4. `login_tokens` — одноразовые ссылки входа | Поле | Тип | Ограничения | Описание | |---|---|---|---| @@ -118,7 +133,7 @@ **Правило:** повторное использование → `status = used`, ответ «Ссылка уже была использована…» (п. 4.1.5 ТЗ). -### 4.4. `sessions` — сессии +### 4.5. `sessions` — сессии | Поле | Тип | Ограничения | |---|---|---| @@ -239,18 +254,7 @@ ## 7. Члены организации -### 7.1. `members` - -| Поле | Тип | Ограничения | Описание | -|---|---|---|---| -| id | bigint | PK | | -| user_id | bigint | FK → users, NULL | связь с аккаунтом | -| region_id | bigint | FK → regions | | -| full_name | text | NOT NULL | | -| type | member_type | NOT NULL, default `member` | член / резидент | -| status | text | NOT NULL, default `active` | | -| joined_at | date | | | -| created_at / updated_at | timestamptz | NOT NULL | | +**Решение:** отдельная таблица `members` не используется. Членство выражается через поля `users.member_type` (`member` / `resident`) и `users.status`, а привязка к регионам — через `user_regions` (раздел 4). Учёт численности — агрегат по `users` с фильтром по региону и типу. --- @@ -505,15 +509,15 @@ ```mermaid erDiagram FEDERAL_DISTRICTS ||--o{ REGIONS : contains - REGIONS ||--o{ USERS : "привязка" REGIONS ||--|| REGION_CARDS : has REGIONS ||--o{ REGION_LINKS : has - REGIONS ||--o{ MEMBERS : 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 @@ -550,19 +554,19 @@ erDiagram | artifacts | `(region_id)`, `(task_id)`, `(goal_id)` | привязки | | broadcast_recipients | `(broadcast_id, status)` | прогресс рассылки | | audit_log | `(actor_user_id, created_at)`, `(entity_type, entity_id)` | аудит | -| members | `(region_id, type)` | численность | +| user_regions | `(region_id)`, `(user_id)` | численность, привязки | **Материализованные представления** `mv_region_metrics`, `mv_district_metrics` обновляются Celery-задачей после каждой загрузки выгрузки и по расписанию. --- -## 19. Открытые вопросы +## 19. Принятые решения -1. **Члены vs пользователи** — нужна ли отдельная сущность `members` или достаточно `users` со статусом? (влияет на учёт численности) -2. **История срезов** — хранить все срезы (`history`) или только `point0` + `current`? -3. **Резиденты** — отдельная таблица или `member_type`? -4. **Мультирегиональность пользователя** — может ли пользователь быть привязан к нескольким регионам? -5. **Хранение артефактов** — MinIO на том же диске (единый диск сервера). +1. **Члены vs пользователи** — отдельная таблица `members` не используется; членство через `users.member_type` + `users.status`. +2. **История срезов** — хранятся **все** срезы (`snapshot_kind = history`), не только `point0` + `current`. +3. **Резиденты** — через `member_type` (`member` / `resident`), без отдельной таблицы. +4. **Мультирегиональность** — пользователь может быть привязан к нескольким регионам через `user_regions`. +5. **Хранение артефактов** — MinIO на едином диске сервера (отдельного диска нет). ---