119 lines
5.1 KiB
PL/PgSQL
119 lines
5.1 KiB
PL/PgSQL
-- ============================================================
|
|
-- Родословное дерево семьи — SQL-схема (PostgreSQL 15+)
|
|
-- ============================================================
|
|
-- Кодировка UTF-8. Запуск: psql -U postgres -d family_tree -f schema.sql
|
|
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- быстрый поиск по имени/фамилии
|
|
|
|
-- ---------- Пользователи и роли ----------
|
|
CREATE TYPE user_role AS ENUM ('admin', 'editor', 'viewer');
|
|
|
|
CREATE TABLE users (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
email TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
display_name TEXT NOT NULL,
|
|
role user_role NOT NULL DEFAULT 'viewer',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- ---------- Персоны ----------
|
|
CREATE TYPE gender_t AS ENUM ('male', 'female', 'other');
|
|
|
|
CREATE TABLE persons (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
first_name TEXT NOT NULL,
|
|
last_name TEXT,
|
|
middle_name TEXT,
|
|
gender gender_t NOT NULL DEFAULT 'other',
|
|
birth_date DATE,
|
|
death_date DATE,
|
|
birth_place TEXT,
|
|
photo_url TEXT,
|
|
bio TEXT,
|
|
contacts TEXT,
|
|
notes TEXT,
|
|
context TEXT, -- произвольный дополнительный контекст
|
|
resource_url TEXT, -- ссылка на подробный внешний ресурс
|
|
created_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX idx_persons_last_name ON persons USING gin (last_name gin_trgm_ops);
|
|
CREATE INDEX idx_persons_first_name ON persons USING gin (first_name gin_trgm_ops);
|
|
|
|
-- ---------- Связь родитель → ребёнок (вкл. приёмные) ----------
|
|
CREATE TYPE parent_kind AS ENUM ('biological', 'adoptive', 'step', 'guardian');
|
|
|
|
CREATE TABLE parent_child (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
parent_id UUID NOT NULL REFERENCES persons(id) ON DELETE CASCADE,
|
|
child_id UUID NOT NULL REFERENCES persons(id) ON DELETE CASCADE,
|
|
kind parent_kind NOT NULL DEFAULT 'biological',
|
|
UNIQUE (parent_id, child_id),
|
|
CHECK (parent_id <> child_id)
|
|
);
|
|
CREATE INDEX idx_pc_parent ON parent_child(parent_id);
|
|
CREATE INDEX idx_pc_child ON parent_child(child_id);
|
|
|
|
-- ---------- Браки / партнёрства (несколько браков поддерживаются) ----------
|
|
CREATE TYPE union_status AS ENUM ('married', 'divorced', 'partner', 'widowed');
|
|
|
|
CREATE TABLE unions (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
partner_a UUID NOT NULL REFERENCES persons(id) ON DELETE CASCADE,
|
|
partner_b UUID NOT NULL REFERENCES persons(id) ON DELETE CASCADE,
|
|
status union_status NOT NULL DEFAULT 'married',
|
|
start_date DATE,
|
|
end_date DATE,
|
|
UNIQUE (partner_a, partner_b),
|
|
CHECK (partner_a <> partner_b)
|
|
);
|
|
|
|
-- ---------- Галерея фотографий ----------
|
|
CREATE TABLE media (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
person_id UUID NOT NULL REFERENCES persons(id) ON DELETE CASCADE,
|
|
url TEXT NOT NULL,
|
|
caption TEXT,
|
|
sort_order INT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX idx_media_person ON media(person_id);
|
|
|
|
-- ---------- История изменений (аудит) ----------
|
|
CREATE TABLE change_log (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
entity TEXT NOT NULL, -- 'person' | 'union' | 'parent_child' | ...
|
|
entity_id UUID,
|
|
action TEXT NOT NULL, -- 'create' | 'update' | 'delete'
|
|
diff JSONB, -- было/стало
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX idx_log_entity ON change_log(entity, entity_id);
|
|
|
|
-- ---------- Авто-обновление updated_at ----------
|
|
CREATE OR REPLACE FUNCTION touch_updated_at() RETURNS trigger AS $$
|
|
BEGIN NEW.updated_at = now(); RETURN NEW; END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_persons_touch BEFORE UPDATE ON persons
|
|
FOR EACH ROW EXECUTE FUNCTION touch_updated_at();
|
|
CREATE TRIGGER trg_users_touch BEFORE UPDATE ON users
|
|
FOR EACH ROW EXECUTE FUNCTION touch_updated_at();
|
|
|
|
-- ---------- Удобное представление дерева ----------
|
|
CREATE VIEW person_relations AS
|
|
SELECT p.id,
|
|
p.first_name, p.last_name, p.middle_name, p.gender,
|
|
p.birth_date, p.death_date,
|
|
(SELECT array_agg(pc.parent_id) FROM parent_child pc WHERE pc.child_id = p.id) AS parent_ids,
|
|
(SELECT array_agg(pc.child_id) FROM parent_child pc WHERE pc.parent_id = p.id) AS child_ids,
|
|
(SELECT array_agg(CASE WHEN u.partner_a = p.id THEN u.partner_b ELSE u.partner_a END)
|
|
FROM unions u WHERE u.partner_a = p.id OR u.partner_b = p.id) AS spouse_ids
|
|
FROM persons p;
|