feat: complete published registry contracts and gated SRO ingestion
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 3m55s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 3m43s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m45s
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 3m55s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 3m43s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m45s
This commit is contained in:
139
docs/sro-membership-source.md
Normal file
139
docs/sro-membership-source.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Проверка членства в СРО: эксплуатация
|
||||
|
||||
Источник `sro_membership_check`, группа/тип `sro_membership`, карточка
|
||||
`sro-membership-check`. Upstream — сайт `reestr-sro.ru`; внутренний справочник
|
||||
организаций используется для точной привязки UID и ОКПО. Другие внешние реестры
|
||||
загрузчик не опрашивает.
|
||||
|
||||
## Доступ и пределы запросов
|
||||
|
||||
По умолчанию сбор закрыт. Для открытия необходимы одновременно
|
||||
`SRO_UPSTREAM_ACCESS_APPROVED=true` и непустой
|
||||
`SRO_UPSTREAM_APPROVAL_REFERENCE` — ссылка/номер документированного разрешения
|
||||
владельца сайта на автоматизированные query/search запросы. Согласование разработки
|
||||
внутри проекта не заменяет разрешение владельца upstream. Условия разрешения
|
||||
должны быть совместимы с настройками загрузчика; более строгие пределы задаются
|
||||
перед включением. Секреты в approval reference хранить нельзя.
|
||||
|
||||
Оба ручных entrypoint проверяют этот gate до enqueue; task и HTTP клиент повторяют
|
||||
проверку перед внешним IO. Закрытый gate возвращает typed
|
||||
`409 upstream_access_not_approved`. Если разрешение отозвано после постановки,
|
||||
job завершается с ошибкой, без успешной пустой публикации.
|
||||
|
||||
| Настройка | По умолчанию | Значение |
|
||||
| --- | --- | --- |
|
||||
| `SRO_REQUEST_INTERVAL_SECONDS` | `3` | Минимум 3 секунды между запросами, включая redirects/retries; можно увеличить. |
|
||||
| `SRO_HTTP_MAX_RESPONSE_BYTES` | `2097152` | Предел распакованного тела одного ответа. |
|
||||
| `SRO_HTTP_TIMEOUT_SECONDS` | `30` | Read timeout; connect timeout 10 секунд. |
|
||||
| `SRO_MAX_REQUESTS_PER_RUN` | `10000` | Общий предел запросов одного запуска, включая redirects/retries/sitemaps. |
|
||||
|
||||
HTTP клиент делает не более трёх попыток при транспортной ошибке и ответах
|
||||
429/500/502/503/504. `Retry-After` учитывается как секунды или HTTP date, ожидание
|
||||
свыше 300 секунд завершает запуск ошибкой. Цепочка redirects ограничена пятью
|
||||
ответами; каждый адрес проверяется до обращения. Разрешён только HTTPS без
|
||||
credentials/нестандартного порта, на `reestr-sro.ru` и его поддоменах. Переменные
|
||||
окружения HTTP proxy автоматически не используются. HTML/query с идентификаторами
|
||||
не выводится в application logs.
|
||||
|
||||
## Публикация и обновление
|
||||
|
||||
Celery task: `parsers.sro_membership_check.refresh`;
|
||||
Python entrypoint: `apps.parsers.tasks_registry_snapshots.parse_sro_membership`,
|
||||
keyword args `requested_by_id=None`, `mode="incremental"|"full"`.
|
||||
|
||||
Проверяются только организации текущего канонического справочника. Инкрементальный
|
||||
запуск выбирает новые организации и те, у которых изменились name/full_name,
|
||||
ИНН/ОГРН/ОКПО. Изменение определяется fingerprint; у модели Organization нет
|
||||
универсального `updated_at`. Полный запуск проверяет весь справочник.
|
||||
|
||||
Lookup использует точный ОГРН, при его отсутствии/неподходящем формате — ИНН
|
||||
с проверкой нормализованного имени. Если нет пригодного идентификатора, запуск
|
||||
отклоняется с `sro_candidate_identifier_missing`, без удаления прежних записей.
|
||||
Ответ дополнительно сопоставляется по ИНН+ОГРН через внутренний индекс. Отсутствие
|
||||
ОКПО, конфликт идентификаторов, неоднозначная организация, неизвестный статус,
|
||||
неполные обязательные поля или missing/unsafe SRO URL отправляют строку в quarantine.
|
||||
Новые организации из внешнего ответа не создаются.
|
||||
|
||||
Одна запись соответствует паре canonical organization UID + SRO ID. Record UID
|
||||
стабилен между загрузками, включая переход `active` → `excluded`. SRO ID/URL
|
||||
берётся из same-site ссылки или точного ID, разрешённого по same-site sitemap;
|
||||
по одному похожему названию URL не угадывается. Для даты допуска читается same-site
|
||||
страница members по ИНН. Неизвестная дата остаётся `null` с причиной
|
||||
`not_found`, `sro_page_unresolved` или `parse_error`. Наблюдаемая дата реестра
|
||||
не подменяется датой загрузки.
|
||||
|
||||
Raw ответы сохраняются в приватном ZIP artifact с manifest URL→файл; отдельные
|
||||
staged rows содержат source fields, нормализованный payload и причину quarantine.
|
||||
Для SRO используется отдельный `FileSystemStorage`: `PARSER_PRIVATE_ARTIFACT_ROOT`
|
||||
по умолчанию равен `<repository-root>/private-parser-artifacts`, вне `MEDIA_ROOT`.
|
||||
Путь обязан быть абсолютным и не находиться внутри `MEDIA_ROOT`, в том числе после
|
||||
разрешения symlink. Ошибка конфигурации запрещает файловую операцию. Приватный storage
|
||||
не выдаёт публичный URL; файлы создаются с правами `0600`, каталоги `0700`.
|
||||
Остальные источники сохраняют прежний `default_storage`.
|
||||
|
||||
В контейнерном окружении задайте `PARSER_PRIVATE_ARTIFACT_ROOT=/app/private-parser-artifacts`
|
||||
и подключите к web/worker один постоянный volume по этому пути. Каталог должен быть
|
||||
доступен пользователю приложения для записи и не должен монтироваться в public media,
|
||||
static или reverse-proxy document root. Существующая retention-команда удаляет private
|
||||
файлы через тот же `artifact.file.delete()`; удаление/пересоздание контейнера не должно
|
||||
удалять volume. Миграция `0037` меняет storage в состоянии модели без перемещения данных.
|
||||
До этой версии живой SRO сбор не запускался. Если оператор ранее создал SRO artifacts
|
||||
в старом MEDIA_ROOT вручную, их требуется отдельно перенести в private root с сохранением
|
||||
относительных имён и убрать публичные копии до открытия источника; автоматического
|
||||
fallback на public raw нет.
|
||||
|
||||
Публикация записей, checkpoint организаций, load log и terminal job выполняется
|
||||
в одной транзакции. Инкрементальная публикация заменяет данные только успешно
|
||||
проверенного набора организаций; записи остальных организаций сохраняются.
|
||||
Transport/schema failure или отменённая job откатывает публикацию и checkpoints.
|
||||
Допустимый пустой ответ удаляет старые членства проверенной организации.
|
||||
Любая семантически некорректная строка остаётся в quarantine и отклоняет весь
|
||||
запуск с `sro_incomplete_membership_scan`. Это сохраняет прежние членства и
|
||||
checkpoints: неизвестный статус или неразрешённая ссылка не доказывают, что
|
||||
членство исчезло. Организация остаётся кандидатом следующего incremental запуска.
|
||||
|
||||
Если incremental не нашёл изменившихся организаций, batch имеет нулевую дельту
|
||||
и наследует source registry date/version последней успешной публикации; ссылка
|
||||
на неё сохраняется в `metadata.base_snapshot_artifact_id`. Данные не переписываются.
|
||||
|
||||
После commit безопасно ставится существующая задача обновления общих export
|
||||
artifacts. Ошибка брокера после commit не превращает успешную публикацию в failure;
|
||||
готовность выгрузки отражает собственный контракт export service.
|
||||
|
||||
## Счётчики и диагностика
|
||||
|
||||
`artifact.published_count`, `result.published_records_count` и `load_log.records_count`
|
||||
содержат размер текущего опубликованного набора источника. Для incremental это
|
||||
общий результат, включая неизменённые организации. `metadata.snapshot_records_count`
|
||||
и `snapshot_organizations_count` дают такие же общие record/distinct organization
|
||||
counts; `batch_published_records_count` и `updated_records_count` — число опубликованных
|
||||
строк текущей дельты. `parsed_count`, quarantine и candidate/queried counts относятся
|
||||
к текущей проверке; складывать их с общим published count нельзя.
|
||||
|
||||
Источник публикует весь собственный справочник; карточка/list/dashboard для трёх
|
||||
новых источников используют весь published scope. Coverage ОПК показывается
|
||||
отдельно. МСП и бюджет продолжают полную замену: новый scoped аргумент публикации
|
||||
по умолчанию `None` сохраняет их поведение.
|
||||
|
||||
Для расследования используют job status/error, artifact status,
|
||||
`metadata.rejection_reason`, `rejection_reasons` и staged disposition/reason.
|
||||
Они содержат ограниченные коды ошибок, не raw HTML. Partial raw ZIP сохраняется
|
||||
при ошибке уже начатого сбора; предыдущая публикация остаётся доступна.
|
||||
Приватные raw artifacts удерживаются минимум 90 дней и минимум 10 последних
|
||||
успешных SRO batches; неуспешные batches не вытесняют эти десять успешных.
|
||||
|
||||
## Расписания и граница проверки
|
||||
|
||||
Миграция `parsers.0036_sro_disabled_schedules` создаёт **выключенные** schedules:
|
||||
daily incremental в 04:00 Europe/Moscow; monthly full 16-го числа в 04:00
|
||||
Europe/Moscow — после наблюдаемого обновления реестра 15-го. Перед включением
|
||||
нужно подтвердить фактическую периодичность/условия разрешения. Расписание само
|
||||
не открывает gate; оба механизма по умолчанию выключены.
|
||||
|
||||
На 14.09.2026 query/search и живой SRO snapshot не запускались. Основная страница
|
||||
без query доступна через read-only web; свежий robots.txt получить не удалось.
|
||||
Указанные в source-first спецификации `Crawl-delay: 3` и query/search disallow
|
||||
учтены консервативно. DOM selector/mapping проверены синтетическими fixtures формы
|
||||
из `docs/source-integration/sources/sro-membership-check/backend-api.md`, а не
|
||||
полным live scan. После разрешения владельца обязательна ограниченная проверка
|
||||
фактической разметки перед первой полной загрузкой.
|
||||
Reference in New Issue
Block a user