Перейти к основному содержимому

Семья

Канонический DDL находится в ../database-schema.md. SQL ниже является концептуальным примером feature-модели и не используется для миграций.

Зачем нужно

Документ описывает семейные группы, student_profile, приглашения взрослых, родительский режим, delegated sessions и авторизацию ребёнка на отдельном устройстве.

1. Что такое семья в MVP

Семья (family_group) — access-контур, который объединяет взрослых пользователей и учебные субъекты детей (student_profile). Семья не является организацией, учебной группой, CRM-карточкой или ограничительным детским режимом.

1.1. Канонические правила

  • В одной семье может быть несколько взрослых: папа, мама, бабушка, дедушка и другие доверенные взрослые.
  • В одной семье может быть несколько детей.
  • Любой active adult семьи имеет одинаковые семейные права по отношению ко всем student_profile этой семьи.
  • Adult membership даёт права только по отношению к детям семьи.
  • Взрослый не получает доступ к прогрессу, календарю, профилю, уведомлениям, покупкам и учебному контексту другого взрослого.
  • Взрослый пользователь сам остаётся обычным пользователем: может учиться, покупать продукты для себя, иметь собственный прогресс, календарь и enrollments.
  • student_profile является учебным субъектом ребёнка.
  • Если student_profile связан с user, linked user ребёнка сохраняет собственные права пользователя в MVP.
  • Семейная группа добавляет взрослым права над детьми, но не отнимает self-права у детского аккаунта.
  • Редкие и конфликтные операции переноса, отвязки, выхода взрослого, удаления семьи и удаления взрослого участника в MVP выполняются вручную администраторами.

1.2. Роли и субъекты

СущностьНазначение
family_groupсемейный access-контур
user_family_group с role = adultсвязь взрослого пользователя с семьёй
student_profileучебный субъект ребёнка внутри семьи
linked_user_idoptional ссылка на user ребёнка для самостоятельного входа

Linked user ребёнка не становится отдельным user_family_group member. Source of truth ребёнка в семейной модели — student_profile.

2. Student profiles и linked user

2.1. Создание

Любой active adult семьи может создать student_profile:

  • без user: ребёнок доступен взрослым через родительский режим и явно разрешённые delegated flows;
  • с привязкой существующего user ребёнка;
  • с созданием нового user для самостоятельного входа ребёнка.

Фейковые email не используются. Если самостоятельный вход не нужен, user не создаётся.

2.2. Собственные права linked user ребёнка

Если student_profile связан с user, этот user остаётся самостоятельным аккаунтом и в MVP может:

  • смотреть свой прогресс;
  • смотреть свой календарь;
  • редактировать свой профиль и свои настройки в рамках обычных self-permissions;
  • покупать продукты для себя;
  • записываться на программы и занятия;
  • учиться под своим аккаунтом;
  • пользоваться обычными auth/security сценариями своего аккаунта.

Взрослые могут редактировать профиль ребёнка из семейного контура, но это не отменяет собственные права linked user ребёнка, пока отдельным решением не будет принят ограничительный детский режим.

2.3. Student profile без linked user

student_profile без linked_user_id не может создать обычную user_session ребёнка. Такой профиль доступен:

  • взрослым в родительском режиме;
  • через delegated session взрослого, если конкретный продуктовый домен явно разрешает такое действие.

Для авторизации ребёнка на отдельном устройстве сначала нужно создать или привязать user ребёнка.

3. Режимы работы

Семейная модель различает четыре режима. Их нельзя подменять друг другом.

РежимActorSubjectЧто происходит
Свой аккаунттекущий userтекущий user или его собственный учебный субъектпользователь действует от своего имени
Родительский режим управления ребёнкомadult userstudent_profile ребёнкавзрослый редактирует профиль, покупает, записывает, настраивает уведомления, смотрит прогресс и календарь ребёнка
Сублогин / delegated sessionadult userstudent_profile ребёнкавзрослый входит в детский контекст, но audit хранит adult actor
Авторизация ребёнка на устройствеadult user подтверждаетlinked user / student_profile ребёнкавзрослый подтверждает вход ребёнка на другом устройстве и не переходит в детский контекст

4. Родительский режим

В родительском режиме взрослый действует из своего аккаунта и не становится ребёнком.

В MVP взрослый может по любому ребёнку своей семьи:

  • читать профиль, календарь и прогресс;
  • видеть занятия, домашние задания и ключевые учебные статусы;
  • редактировать семейно-управляемые поля student_profile;
  • покупать продукт для ребёнка;
  • записывать ребёнка на программу или занятие, если это разрешено доменом-владельцем продукта;
  • настраивать получение уведомлений по событиям ребёнка.

В MVP взрослый видит календарь и прогресс конкретного ребёнка. Единый семейный календарь не входит в MVP и проектируется отдельно, если будет подтверждён как продуктовая функция.

4.1. Уведомления по ребёнку

Взрослый участник семьи может настраивать получение уведомлений по любому ребёнку своей семьи. Детальная модель уведомлений, каналов, правил и шаблонов принадлежит platform notifications. Family Group предоставляет family context, subject student_profile и проверку, что получатель является active adult той же семьи.

5. Делегированный контекст

Любой active adult семьи может открыть детский режим для student_profile, если продуктовый домен допускает действие через delegated context.

5.1. Как работает

  1. Взрослый вызывает POST /api/v2/identity/families/{familyId}/delegated-sessions.
  2. В request передаётся обязательный studentProfileId.
  3. Система проверяет active adult membership и принадлежность student_profile этой семье.
  4. Система создаёт delegated_session.
  5. Access token или actor context сохраняет sub = actorUserId взрослого и actorContext.studentProfileId = studentProfileId.
  6. Downstream-домен видит child delegated context и subject student_profile.
  7. Audit фиксирует actorUserId, subjectStudentProfileId, optional subjectUserId, familyGroupId и delegatedSessionId.
  8. Взрослый завершает delegated session через DELETE /api/v2/identity/families/{familyId}/delegated-sessions/{sessionId}.

5.2. Security

  • sub в JWT не меняется и всегда содержит ID взрослого.
  • Delegated session доступна только для student_profile текущей семьи.
  • Попытка открыть контекст другого взрослого запрещена.
  • service access settings не являются precondition для delegated session в MVP.
  • Audit: identity.delegated_session.started, identity.delegated_session.ended.

6. Авторизация ребёнка на устройстве

child_device_authorization — отдельный flow для входа ребёнка на другом устройстве без передачи ребёнку пароля взрослого. Это не delegated session.

6.1. Канонический flow

  1. На устройстве ребёнка начинается вход в детский аккаунт.
  2. Система показывает QR-код или короткий одноразовый код.
  3. Взрослый входит в свой аккаунт и подтверждает запрос.
  4. Система проверяет, что взрослый является active adult в той же family_group.
  5. Система проверяет, что student_profile принадлежит этой семье.
  6. Если у ребёнка есть linked_user_id, на устройстве ребёнка создаётся user_session ребёнка.
  7. В audit пишется, кто подтвердил вход, для какого ребёнка, на каком устройстве и с каким сроком действия.
  8. Взрослый не переходит в детский контекст.

Для student_profile без linked_user_id device authorization в MVP невозможна без предварительного создания или привязки user. Такой профиль остаётся доступен через родительский режим и явно разрешённый delegated context.

6.2. Сущность

child_device_authorizations хранит запрос входа ребёнка на устройстве.

ПолеНазначение
idидентификатор запроса
family_group_idсемья
student_profile_idучебный субъект ребёнка
child_user_idlinked user ребёнка, если есть
approved_by_user_idвзрослый, подтвердивший вход
device_fingerprint_hash или device_binding_idустройство ребёнка
request_code_hash или qr_token_hashодноразовый код / QR-token
statuscreated, approved, completed, expired, revoked
requested_atкогда создан запрос
approved_atкогда подтверждён
completed_atкогда создана сессия ребёнка
expires_atсрок действия запроса
revoked_atкогда отозван

7. Приглашение взрослого

Взрослого можно пригласить ссылкой или коротким кодом. Перед отправкой приглашения и перед принятием кода UI обязан показывать смысл доступа:

Приглашённый взрослый после вступления получит родительский доступ ко всем детским профилям этой семьи. Он не получит доступ к прогрессу, календарю, профилю и учебному контексту других взрослых.

Создание и принятие приглашения пишутся в audit. Команда может добавить step-up confirmation для приглашения взрослого без изменения канонической модели.

8. Покупки и продуктовые назначения

Семейный доступ не означает, что покупатель и учащийся совпадают.

  • Покупатель — actor / purchaser / payer.
  • Получатель продукта — student_profile, linked user или future family_group, если продукт семейный.
  • Покупатель и получатель могут совпадать, но не обязаны совпадать.
  • Взрослый может купить продукт для ребёнка.
  • Ребёнок с linked user может купить продукт себе.
  • Будущие семейные продукты могут назначаться на family_group.

Детальная модель order, invoice, payment и entitlement принадлежит CRM/billing. Identity поставляет family context и identifiers.

9. Post-MVP / Admin-only

Следующие сценарии не входят в пользовательский MVP Family Group:

  • PIN для семейного или детского режима;
  • вход ребёнка через родительские контакты;
  • обязательный parental control;
  • service-level allow/deny для детей;
  • service access gating как обязательный слой доступа;
  • self-service удаление семьи;
  • self-service выход взрослого из семьи;
  • self-service удаление взрослого участника;
  • автоматический перенос или отвязка ребёнка между семьями;
  • каскадное архивирование детей при удалении семьи.

В MVP операции, которые могут привести к потере доступа или конфликту взрослых, выполняются администратором вручную.

Если после MVP понадобится управление доступностью сервисов для самого ребёнка, его нужно проектировать как family-wide настройки student_service_settings / student_service_access_settings, а не как per-adult право.

10. Entity

10.1. family_groups

CREATE TABLE family_groups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
avatar_url VARCHAR(500),
created_by UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

10.2. user_family_groups

CREATE TABLE user_family_groups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
family_group_id UUID NOT NULL REFERENCES family_groups(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role VARCHAR(20) NOT NULL DEFAULT 'adult',
status VARCHAR(20) NOT NULL DEFAULT 'active',
label VARCHAR(100), -- parent, friend, grandparent, guardian, other
invited_by UUID REFERENCES users(id) ON DELETE SET NULL,
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
removed_at TIMESTAMPTZ,
UNIQUE(user_id, family_group_id),
CHECK (role = 'adult')
);

10.3. student_profiles

CREATE TABLE student_profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
family_group_id UUID NOT NULL REFERENCES family_groups(id) ON DELETE CASCADE,
linked_user_id UUID UNIQUE REFERENCES users(id) ON DELETE SET NULL,
display_name VARCHAR(160) NOT NULL,
birth_date DATE,
grade_level VARCHAR(40),
status VARCHAR(40) NOT NULL DEFAULT 'active',
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

10.4. delegated_sessions

CREATE TABLE delegated_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
actor_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
subject_student_profile_id UUID NOT NULL REFERENCES student_profiles(id) ON DELETE CASCADE,
subject_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
family_group_id UUID NOT NULL REFERENCES family_groups(id) ON DELETE CASCADE,
service_key VARCHAR(80),
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
ended_at TIMESTAMPTZ,
end_reason VARCHAR(80)
);

10.5. child_device_authorizations

CREATE TABLE child_device_authorizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
family_group_id UUID NOT NULL REFERENCES family_groups(id) ON DELETE CASCADE,
student_profile_id UUID NOT NULL REFERENCES student_profiles(id) ON DELETE CASCADE,
child_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
approved_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
device_binding_id UUID REFERENCES device_bindings(id) ON DELETE SET NULL,
device_fingerprint_hash TEXT,
request_code_hash TEXT,
qr_token_hash TEXT,
status VARCHAR(40) NOT NULL DEFAULT 'created',
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
approved_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ NOT NULL
);

11. API endpoints

HTTPПутьAuthНазначение
GET/api/v2/identity/familiesJWTсемьи пользователя
POST/api/v2/identity/familiesJWTсоздать семью
GET/api/v2/identity/families/{familyId}JWT adultдетали семьи
PATCH/api/v2/identity/families/{familyId}JWT adult + step-up где нужнопереименовать или изменить базовые настройки
POST/api/v2/identity/families/{familyId}/invitationsJWT adultпригласить взрослого
POST/api/v2/identity/family-invitation-tokens/{token}/acceptJWTпринять приглашение
POST/api/v2/identity/families/{familyId}/student-profilesJWT adultсоздать student_profile
PATCH/api/v2/identity/families/{familyId}/student-profiles/{studentProfileId}JWT adultизменить семейно-управляемые поля ребёнка
POST/api/v2/identity/families/{familyId}/student-profiles/{studentProfileId}/link-userJWT adultпривязать или создать user ребёнка
POST/api/v2/identity/families/{familyId}/delegated-sessionsJWT adultоткрыть delegated session по studentProfileId
DELETE/api/v2/identity/families/{familyId}/delegated-sessions/{sessionId}JWT adultзавершить delegated session
POST/api/v2/identity/families/{familyId}/student-profiles/{studentProfileId}/device-authorizationspre-auth / child device flowсоздать запрос входа ребёнка
POST/api/v2/identity/families/{familyId}/device-authorizations/{authorizationId}/approveJWT adultподтвердить вход ребёнка
POST/api/v2/identity/families/{familyId}/device-authorizations/{authorizationId}/completevalid authorization tokenзавершить вход и создать сессию ребёнка
POST/api/v2/identity/families/{familyId}/device-authorizations/{authorizationId}/revokeJWT adult или adminотозвать запрос

12. UX/UI

12.1. FamilyPage

Страница семьи показывает:

  • взрослых участников;
  • student_profile детей;
  • pending invitations;
  • linked user status по каждому ребёнку;
  • активные delegated sessions текущего взрослого;
  • pending/completed device authorization requests, если этот экран используется для подтверждения входа ребёнка.

12.2. Действия active adult

  • создать student_profile;
  • пригласить взрослого с явным предупреждением о доступе ко всем детям семьи;
  • редактировать базовые поля student_profile;
  • привязать или создать user ребёнка;
  • открыть delegated session;
  • завершить свою delegated session;
  • подтвердить или отозвать pending device authorization;
  • настроить получение уведомлений по ребёнку через platform notifications.

В пользовательском MVP FamilyPage не содержит действий удаления семьи, выхода взрослого, удаления другого взрослого, переноса ребёнка, PIN и service access gating.

12.3. Dashboard — блок Family

На дашборде блок family показывает:

  • список взрослых и детей;
  • кнопку добавления ребёнка;
  • кнопку приглашения взрослого;
  • переход в профиль конкретного ребёнка;
  • действие delegated session там, где downstream-домен разрешает детский контекст.

12.4. Модалки

МодалкаТриггерСодержимое
InviteFamilyAdultModalПригласить взрослогоКонтакт/ссылка/код и предупреждение о доступе ко всем детям
CreateStudentProfileModalСоздать профиль ребёнкаИмя, дата рождения/класс, optional linked user
LinkChildUserModalПривязать userСоздать user или связать существующий аккаунт ребёнка
StartDelegatedSessionModalОткрыть детский контекстПодтверждение subject studentProfileId и service/domain context
ApproveChildDeviceAuthorizationModalЗапрос входа ребёнкаКод/устройство, ребёнок, срок действия, кнопки approve/revoke