-- 187_gisogd_permits.sql -- Forgejo #2367 PR-1 (Schema) — реестр разрешений на строительство (РНС) и ввод -- в эксплуатацию (РВЭ) Свердловской области из ОТКРЫТОГО API ГИСОГД-СО. -- -- Одна таблица gisogd_permits: карточки документов раздела 13 («Сведения о -- документах в деле о земельном участке») региональной ГИС органов гос. власти -- Свердловской области (gisogd66.midural.ru). Открытый JSON-API без авторизации. -- -- Источник (карточки/списки) отдаёт данные без гео-блокировки (bare curl 200), но -- код лоадера всё равно держим устойчивым к отсутствию полей (spatialUnit / geometry -- у части документов отсутствуют — hasGeometry=false). -- -- Две схемы источника: -- agate_ekbgo — ЕКБ, самая свежая выгрузка; -- agate_sverdregion — регион-агрегат, отстаёт ~неделю. -- Один документ может встретиться в ОБЕИХ схемах → бизнес-ключ = (doc_group, doc_num); -- при конфликте предпочитаем запись с более поздним date_reg (лоадер, PR-2). -- -- doc_group: -- 'RS' — Разрешение на строительство (группа источника DocRS); -- 'RV' — Разрешение на ввод в эксплуатацию (группа источника DocRV). -- -- cad_nums — распарсенный spatialUnit (может быть НЕСКОЛЬКО кадастровых номеров -- через запятую, может отсутствовать → пустой массив). geom — из infosetGeoJson -- (GeoJSON Polygon WGS84 lon/lat, лоадер приводит к MultiPolygon), NULL когда -- hasGeometry=false. source_schema/source_key фиксируют, откуда прилетела запись -- (для инкрементальной сверки по (source_schema, source_key, date_reg)). -- -- Порядок применения: миграция ПЕРЕД деплоем кода (schema-first, sql.md). -- Таблица НОВАЯ → deploy.yml гоняет всё в одной транзакции; CREATE INDEX -- CONCURRENTLY здесь не нужен (и запрещён внутри транзакции). BEGIN; -- ── gisogd_permits ──────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS gisogd_permits ( id BIGSERIAL PRIMARY KEY, doc_group TEXT NOT NULL, -- 'RS' (РНС) | 'RV' (РВЭ) — см. CHECK doc_num TEXT NOT NULL, -- № документа, напр. «66-45-08-2026» (strip'нут) doc_name TEXT, -- name карточки, напр. «Разрешение на строительство № … от …» date_doc DATE, -- dateDoc — дата выдачи документа date_reg DATE, -- dateReg — дата регистрации в реестре (для conflict-резолва) approved_organization TEXT, -- approvedOrganization — выдавший орган source_schema TEXT NOT NULL, -- 'agate_ekbgo' | 'agate_sverdregion' source_key TEXT NOT NULL, -- key карточки в схеме источника (для инкрементальной сверки) cad_nums TEXT[] NOT NULL DEFAULT '{}', -- распарсенный spatialUnit ('{}' если нет) geom GEOMETRY(MultiPolygon, 4326), -- из infosetGeoJson; NULL если hasGeometry=false fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Только 'RS'/'RV' допустимы (защита от опечаток в лоадере). CONSTRAINT chk_gisogd_permits_doc_group CHECK (doc_group IN ('RS', 'RV')), -- Бизнес-ключ: один документ (группа + номер) хранится ОДНОЙ строкой, даже если -- прилетел из обеих схем. Лоадер UPSERT-ит по нему, предпочитая поздний date_reg. UNIQUE (doc_group, doc_num) ); -- Пространственный индекс — §-запросы «РНС/РВЭ рядом с участком». CREATE INDEX IF NOT EXISTS gist_gisogd_permits_geom ON gisogd_permits USING GIST (geom); -- GIN по массиву кадастровых номеров — матч «разрешение на этом участке». CREATE INDEX IF NOT EXISTS gin_gisogd_permits_cad_nums ON gisogd_permits USING GIN (cad_nums); -- Дата документа — фильтры/сортировки по свежести разрешений. CREATE INDEX IF NOT EXISTS idx_gisogd_permits_date_doc ON gisogd_permits (date_doc DESC); COMMENT ON TABLE gisogd_permits IS 'Реестр разрешений на строительство (РНС) и ввод в эксплуатацию (РВЭ) ' 'Свердловской области из открытого API ГИСОГД-СО (gisogd66.midural.ru, раздел 13). ' 'Бизнес-ключ (doc_group, doc_num); при конфликте двух схем источника ' '(agate_ekbgo / agate_sverdregion) предпочитается поздний date_reg. Refresh: weekly ' '/ on-demand. Forgejo #2367.'; COMMENT ON COLUMN gisogd_permits.doc_group IS '''RS'' = разрешение на строительство (группа источника DocRS), ' '''RV'' = разрешение на ввод в эксплуатацию (группа источника DocRV).'; COMMENT ON COLUMN gisogd_permits.cad_nums IS 'Распарсенный spatialUnit карточки: список кадастровых номеров (несколько через ' 'запятую в источнике). Пустой массив ''{}'' когда spatialUnit отсутствует или все ' 'значения не прошли валидацию формата кадастрового номера.'; COMMENT ON COLUMN gisogd_permits.geom IS 'MultiPolygon (WGS84) из infosetGeoJson карточки (Polygon приведён к MultiPolygon ' 'лоадером). NULL когда hasGeometry=false / infosetGeoJson отсутствует.'; COMMENT ON COLUMN gisogd_permits.source_schema IS 'Схема источника, из которой прилетела запись: ''agate_ekbgo'' (ЕКБ, свежая) или ' '''agate_sverdregion'' (регион-агрегат, отстаёт ~неделю).'; COMMENT ON COLUMN gisogd_permits.source_key IS 'key карточки в схеме источника. Вместе с (source_schema, date_reg) используется ' 'лоадером для инкрементальной сверки: карточки тянутся только для новых/изменённых.'; COMMIT;