gendesign/data/sql/187_gisogd_permits.sql
bot-backend 1b96ec1baf
Some checks failed
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Failing after 1m56s
CI / backend-tests (pull_request) Successful in 14m37s
feat(scrapers): реестр РНС/РВЭ ГИСОГД-СО из открытого API (#2367 PR-1)
Ранний сигнал future-supply: разрешения на строительство появляются в
ГИСОГД до проектных деклараций ЕИСЖС. Старый minstroy-путь мёртв (WAF),
портал gisogd66.midural.ru отдаёт открытый JSON-API без авторизации.

Миграция 187_gisogd_permits (RS+RV, UNIQUE(doc_group,doc_num), GIST geom
4326, GIN cad_nums) + инкрементальный httpx-лоадер (схемы agate_ekbgo +
agate_sverdregion, сверка по (source_key,date_reg), карточки только для
новых/изменённых, SAVEPOINT per-row, WHERE-гейт предпочтения позднего
date_reg) + Celery weekly (вт 06:30 МСК, limit 3600/3300) + on-demand
admin-триггер + freshness-реестр (data-table, 14/45).

Refs #2367
2026-07-04 07:58:59 +05:00

99 lines
7.5 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 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;