gendesign/_wt-cadval/data/sql/145_insight.sql
bot-backend 542ff1c9ca docs(tradein/domclick): два комментария описывали пул, которого нет с миграции 253
Оба места утверждали, что у Домклика один выделенный резидентный прокси и
пула нет. Это перестало быть правдой ещё в #2800 (миграция 253 сняла
резервацию узла), но текст остался — и именно на него опирался тикет #3189,
поставленный под «калибровку» ограничения, которого не существует.
Свип к тому же ходит через пул давно (serp.py:359), а бэкфилл подключён к
нему в PR #3222.

Заодно докстринг называл не тот ограничитель: свип кладёт не счётчик
провалов lease, а break по первому DomClickBlockedError (#2854) — до
ротации дело не доходит ни при каком счётчике, отсюда buckets_completed=0.

Только комментарии, поведение не меняется. Миграция 175 уже применена, а
_schema_migrations трекает по имени файла без checksum — правка текста
её не перезапустит.
2026-08-29 18:57:25 +03:00

113 lines
7.1 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.

-- 145_insight.sql
-- #948 Part A (ТЗ §7.13/§8.9): manual-entry "insight" — аналитик пишет свободную
-- заметку/intel про локацию или участок, с пометкой «непублично» (is_confidential).
--
-- Access control: backend rbac_guard (app/main.py) хард-блокирует ТОЛЬКО
-- /api/v1/admin/*. На /api/v1/insights pilot отсекается FRONTEND-овым RouteGuard
-- (allowed_paths из /me) + Caddy, НЕ бэкендом; до эндпоинта доходят analyst+admin.
-- is_confidential — хранимая маркировка (per-record backend-gate НЕТ; будущий gate
-- публичной выдачи), сейчас просто хранимый+фильтруемый флаг (политика #962: analyst
-- видит конфиденциальные данные). created_by фиксирует автора (из X-Authenticated-User).
--
-- Location ref: пока plain TEXT district + опциональный cad_num (ссылка на участок).
-- Part B (#948, §8.2) промоутит district в Location FK — НЕ в этом таске.
--
-- Audit (§19, интеграция с #962): WRITE-запросы (POST/PUT/DELETE) аудируются
-- middleware app/core/audit_middleware.py с action='insight_write'. Поэтому ниже
-- (блок 4) аддитивно расширяем CHECK audit_log.action, чтобы 'insight_write'
-- проходил констрейнт. Это idempotent ALTER (DROP IF EXISTS + ADD) — НЕ правит
-- 144_audit_log.sql (тот уже применён на проде/смёржен; правка применённой
-- миграции не переиграется через _schema_migrations).
--
-- Deploy: auto-applied deploy.yml через _schema_migrations (ровно один раз NN=145).
-- Idempotent: CREATE TABLE/INDEX IF NOT EXISTS + guarded CHECK.
BEGIN;
-- ── 1. Таблица insight ────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS insight (
id BIGSERIAL PRIMARY KEY,
created_by TEXT NOT NULL, -- автор, из X-Authenticated-User
title TEXT NOT NULL, -- заголовок заметки
body TEXT NOT NULL, -- тело заметки (free-form intel)
category TEXT, -- competition|permitting|demand|risk|other
is_confidential BOOLEAN NOT NULL DEFAULT FALSE, -- «непублично» (§7.13)
district TEXT, -- location ref (Part B → Location FK)
cad_num TEXT, -- опциональная ссылка на участок
lon DOUBLE PRECISION CHECK (lon IS NULL OR lon BETWEEN -180 AND 180),
lat DOUBLE PRECISION CHECK (lat IS NULL OR lat BETWEEN -90 AND 90),
-- Опциональный map-pin: GENERATED из lon/lat (как user_custom_pois.geom #254).
-- NULL если координаты не заданы (обе должны быть заданы вместе).
geom GEOMETRY(POINT, 4326) GENERATED ALWAYS AS (
CASE
WHEN lon IS NOT NULL AND lat IS NOT NULL
THEN ST_SetSRID(ST_MakePoint(lon, lat), 4326)
END
) STORED,
source TEXT, -- источник intel (опционально)
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
COMMENT ON TABLE insight IS
'Manual-entry аналитические заметки/intel про локацию или участок (#948 Part A, '
'ТЗ §7.13/§8.9). is_confidential = маркировка «непублично». created_by = автор '
'из X-Authenticated-User. district — plain TEXT (Part B промоутит в Location FK).';
COMMENT ON COLUMN insight.is_confidential IS
'«Непублично» (§7.13): маркировка чувствительной заметки. Сейчас хранимый+'
'фильтруемый флаг; будущий gate публичной выдачи.';
COMMENT ON COLUMN insight.category IS
'Свободная категория (рекоменд.: competition|permitting|demand|risk|other).';
COMMENT ON COLUMN insight.geom IS
'Опциональный map-pin, GENERATED из lon/lat. NULL если координаты не заданы.';
-- ── 2. CHECK на category (guarded — idempotent при ре-apply) ──────────────────
-- Мягкий enum: разрешаем рекомендованные значения + NULL. Расширять список —
-- через новую миграцию (DROP IF EXISTS + ADD), не правкой этой.
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'ck_insight_category'
) THEN
ALTER TABLE insight
ADD CONSTRAINT ck_insight_category
CHECK (
category IS NULL
OR category IN ('competition', 'permitting', 'demand', 'risk', 'other')
);
END IF;
END
$$;
-- ── 3. Индексы: фильтры list-эндпоинта ────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_insight_district
ON insight (district);
CREATE INDEX IF NOT EXISTS idx_insight_cad_num
ON insight (cad_num);
CREATE INDEX IF NOT EXISTS idx_insight_is_confidential
ON insight (is_confidential);
CREATE INDEX IF NOT EXISTS idx_insight_created_by
ON insight (created_by);
-- Опциональный geo-lookup (как user_custom_pois_geom_gist #254).
CREATE INDEX IF NOT EXISTS idx_insight_geom_gist
ON insight USING GIST (geom);
-- ── 4. Расширяем CHECK audit_log.action → +'insight_write' (§19, #962) ─────────
-- Middleware аудирует WRITE insight'ов как action='insight_write'. 144_audit_log
-- разрешал только analyze|forecast|export|other; добавляем 'insight_write'.
-- Idempotent: DROP IF EXISTS + ADD пере-создаёт констрейнт с расширенным набором
-- (повторный apply этой миграции даст тот же результат). Таблица audit_log создана
-- в 144 (она в HEAD/проде до этой миграции) — но guard'имся to_regclass на случай
-- среды, где 144 ещё не накатан, чтобы 145 не падал.
DO $$
BEGIN
IF to_regclass('public.audit_log') IS NOT NULL THEN
ALTER TABLE audit_log DROP CONSTRAINT IF EXISTS ck_audit_log_action;
ALTER TABLE audit_log
ADD CONSTRAINT ck_audit_log_action
CHECK (action IN ('analyze', 'forecast', 'export', 'insight_write', 'other'));
END IF;
END
$$;
COMMIT;