惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
N
News and Events Feed by Topic
D
DataBreaches.Net
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Engineering at Meta
Engineering at Meta
T
Tailwind CSS Blog
博客园_首页
Microsoft Azure Blog
Microsoft Azure Blog
Y
Y Combinator Blog
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
月光博客
月光博客
A
About on SuperTechFans
I
InfoQ
S
Securelist
Last Week in AI
Last Week in AI
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
腾讯CDC
大猫的无限游戏
大猫的无限游戏
S
Security @ Cisco Blogs
博客园 - 三生石上(FineUI控件)
Simon Willison's Weblog
Simon Willison's Weblog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
美团技术团队
aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
罗磊的独立博客
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Latest news
Latest news
Recent Announcements
Recent Announcements
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LINUX DO - 热门话题
Security Latest
Security Latest
TaoSecurity Blog
TaoSecurity Blog
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как мы перестали размножать BIRT‑шаблоны и собрали блочную платформу self‑service генерации страховых полисов в PDF
ElisBuh (Аль · 2026-05-07 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение7 мин

Охват и читатели661

Кейс

Я Javaразработчик в АльфаСтрахование, в команде Авиа Блока. Мы занимаемся страхованием авиапассажиров, и одна из наших прикладных задач — генерация страховых полисов в PDF.

Исторически печать у нас была построена на Eclipse BIRT. Если коротко, BIRT — это движок отчётности и рендеринга документов, а файл .rptdesign — его шаблон в XMLформате: в нём описаны структура документа, таблицы, параметры, стили и скрипты.

Мы внедрили подход, где итоговый документ собирается не из одной «монолитной» .rptdesign, а из базового шаблона, переиспользуемых блоков и версионированных наборов значений. Поверх BIRT появился backend-слой, который отвечает за композицию документа, каскадное версионирование, импорт/экспорт конфигурации между средами и безопасный рендер итогового PDF через BIRT runtime.

Ключевой фокус был не в ускорении рендеринга, а в user experience: изменения в структуре и контенте должны выполнять нетехнические пользователи, а продукт — контролировать качество через управляемый процесс публикации (черновики → product review → публикация версии → откат при необходимости).

В чём была реальная боль

Всё начиналось с обычной просьбы: поправить текст в полисе. Но за такой правкой быстро обнаруживалась проблема: один и тот же документ существовал в нескольких .rptdesign, у партнёров были свои вариации, а любое изменение снова шло через разработчика.

Хотим:

  • Исправить формулировку;

  • Поменять логотип партнёра;

  • Вынести адрес из статичного текста в параметр;

  • Переставить местами два смысловых блока.

Получаем:

  • Разработчик открывает .rptdesign;

  • Правит XML/таблицы/скрипты;

  • Проверяет, что соседние продукты не сломались;

  • Выкатывает новую версию;

  • Через пару недель повторяет всё снова, но уже для другого партнёра.

Пока документов мало — терпимо. Когда продуктов и партнёров становится много, система расползается: одни и те же куски появляются в десятках копий, версия документа перестаёт быть понятной сущностью, а бизнес оказывается зависим от IT даже в простых изменениях.

Почему одного BIRT оказалось недостаточно

BIRT хорошо решает задачу рендеринга: берёт .rptdesign, подставляет параметры и отдаёт PDF. Но BIRT сам по себе не знает о наших бизнес‑инвариантах — правилах, которые важны именно для страховых документов:

  • новые документы должны печататься по новой версии, а старые — по той версии, по которой были выпущены;

  • один и тот же логотип, текст оферты или юридическая вставка должны переиспользоваться между разными документами, но управляться независимо.

То есть движок рендеринга уже был — не хватало предметного слоя поверх него.

Правильная единица изменения

Ключевой вопрос был простой: «что именно является единицей изменения?» Не документ целиком, потому что в реальности меняются локальные части:

  • шапка;

  • таблица условий;

  • логотип;

  • юридическая вставка;

  • порядок блоков;

  • набор значений для конкретного партнёра.

После этого архитектура начала перестраиваться практически сама.

Модель: от трёх слоёв к четырём

Изначально удобно было объяснять модель тремя уровнями: макет → блок → данные блока. Но в живом проекте модель доросла до четырёх уровней.

Макет (layout)

Макет отвечает за сборку документа для связки партнёр + продукт + версия и перестаёт быть «файлом на диске», превращаясь в предметную сущность с конфигурацией:

  • какие наборы данных подключены;

  • в каком порядке выводятся блоки;

  • используется ли режим onlyBlocks;

  • какой у макета статус.

Блок (block)

Блок — переиспользуемый фрагмент документа: шапка, маршрут, таблица рисков, оферта, юридический текст, подписи. Технически блок хранит BIRT-совместимый XML-фрагмент, который можно встроить в итоговый документ.

Важно, что сам блок отвечает только за отдельный кусок документа. А вот за композицию документа целиком отвечает уже layout: именно он хранит, какие блоки участвуют в сборке и в каком порядке они идут. Для бизнеса это не «техническая деталь», а часть продуктовой логики: в зависимости от продукта, партнёра или сценария один и тот же набор блоков может идти в разной последовательности.

Данные блока (blockData)

BlockData — конкретная версия настроек блока: какой логотип подставить, какой текст показать, какие ссылки использовать, какие значения применить для конкретного партнёра/продукта. Этот слой отделяет структуру блока от наполнения.

Справочники и версии значений

Со временем выяснилось, что даже blockData разрастается, если хранить всё как свободный JSON. Поэтому появились:

  • DataReference — описание параметра (что это за значение).

  • DataReferenceValue — конкретная версия значения (какое именно значение в этой версии).

В более приземлённом виде это работает так:

  • у блока HEADER есть параметр logo;

  • DataReference описывает сам параметр: «это логотип партнёра, картинка, обязательное поле»;

  • DataReferenceValue хранит уже конкретные версии значения: например, logo-v1.png и logo-v2.png;

  • blockData не дублирует эти файлы и строки внутри себя, а ссылается на нужные версии значений.

Так модель становится заметно понятнее. blockData отвечает на вопрос «что нужно этому блоку в этой конфигурации», а DataReference и DataReferenceValue — «какие именно значения доступны и какая версия сейчас выбрана».

Это даёт управляемость: система знает, где используется значение, может каскадно обновлять зависимости и позволяет переиспользовать один и тот же логотип, текст или ссылку в нескольких конфигурациях без копипаста.

Пример конфигурации

Ниже — иллюстрация идеи (не точная схема хранения из production): структура документа, состав блоков и конкретные значения живут раздельно.

{
  "layout": {
    "partnerCode": "PARTNER_A",
    "productCode": "TRAVEL",
    "version": 2,
    "blockNameOrder": ["HEADER", "TERMS", "FOOTER"],
    "onlyBlocks": true
  },
  "blockData": {
    "blockName": "HEADER",
    "version": 2,
    "values": {
      "logo": "partner-a/logo-v2.png",
      "offerUrl": "https://example.org/offer",
      "companyName": "Partner A"
    }
  }
}

Два кейса, ради которых всё и затевалось

Кейс 1. Исправить опечатку без большой миграции

Если это безопасная правка текста, её не нужно тащить через полную пересборку всего документа. Если изменение должно повлиять только на новые документы — делаем новую версию на нужном уровне модели; если структурное — оно идёт по каскаду.

Кейс 2. Новый логотип только для новых продаж

Бизнес хочет, чтобы новые документы печатались с новым логотипом, а старые — как раньше, без размножения полных копий шаблонов под каждого партнёра. Решение — поднять версию только того уровня, где реально произошло изменение (например, blockData), не создавая новый «монолитный» шаблон документа.

Почему ключевым оказался каскад версий

Самая опасная ситуация — частично обновлённая конфигурация: макет уже новый, блок ещё старый, значения уже обновлены, и поведение системы можно объяснить только ручным разбором связей.

Поэтому разные уровни версионируются независимо, но согласованно, а публикация становится полноценным процессом со статусами и правилами переходов.

Гибридный период и зачем нужен onlyBlocks

Переход к «конструктору» не происходит мгновенно: какое-то время сосуществуют старые и новые поколения. У нас параллельно жили три сценария:

  • Полностью старый макет.

  • Старый макет с точечным внедрением блоков.

  • Новый макет, который собирается только из блоков.

В этом контексте onlyBlocks — не просто оптимизация, а режим, который позволяет реально работать с блочной композицией и ускоряет цикл «поправил → посмотрел».

Как документ собирается на backend

На практике «конструктор PDF» — это серверный pipeline:

  1. Получаем запрос на генерацию документа.

  2. Берём исходный .rptdesign (из кэша или Object Storage).

  3. Определяем, какой layout и какие версии blockData должны участвовать в сборке.

  4. Находим в шаблоне точки вставки для блоков.

  5. Подтягиваем XML нужных блоков и значения для них.

  6. Собираем итоговый .rptdesign и при необходимости кэшируем подготовленную версию.

  7. Передаём результат в BIRT runtime.

  8. Возвращаем готовый PDF.

Условный псевдокод:

report = loadBaseTemplate(partnerCode, productCode, version)
anchors = findBlockAnchors(report)
blocks  = loadBlocksWithValues(layoutVersion, blockDataVersions)
preparedReport = injectBlocks(report, anchors, blocks)
pdf = birtRuntime.render(preparedReport)
return pdf

Управляемая публикация: как бизнес меняет документ безопасно

Если бизнес может свободно менять структуру, риски смещаются из «код-ошибок» в «ошибки конфигурации/контента». Чтобы удержать систему управляемой, мы ввели процесс и ограничения.

Процесс

  • Draft — бизнес создаёт черновик конфигурации и контента, собирает структуру, проверяет предпросмотр (в т.ч. onlyBlocks).

  • Product review — продукт ревьюит визуальный результат и корректность (бренд / читабельность / логика документа).

  • Publish — публикация создаёт версию, которая становится доступна для прод-генерации.

Guardrails

Так как обязательных блоков нет, важны автоматические проверки:

  • Валидация входной конфигурации: корректность blockNameOrder, допустимость комбинаций версий, валидность ссылок/изображений и типов данных.

  • Проверка совместимости версий layout / block / blockData на этапе публикации (матрица соответствий версий).

  • Предсказуемый предпросмотр: бизнес видит итоговый PDF до публикации и не выпускает «сломанный» документ.

Импорт/экспорт и безопасность

Как только появляются версии, блоки, связанные значения, изображения и зависимости, становится критичным переносить конфигурацию между средами (DEV → TEST → PROD) без ручной сборки.

Поэтому в платформе появляется импорт/экспорт:

  • блоки едут вместе со связанными сущностями;

  • значения и изображения не теряются;

  • связи сохраняются;

  • макеты можно переносить отдельно.

Безопасность тоже перестаёт быть факультативной. Когда в системе есть XML, архивы и пользовательские файлы, нужны:

  • безопасный XMLпарсинг (защита от XXE);

  • контроль путей внутри архивов (zip slip);

  • валидация загружаемых файлов;

  • контроль целостности зависимостей при импорте.

Что измеряем, если цель — user-friendly, а не скорость

Если задача — делегировать изменения бизнесу, ключевые метрики должны быть продуктовыми:

Метрика

Что показывает

Lead time изменения

От идеи/правки до опубликованной версии

Доля self-service изменений

Процент правок, сделанных бизнесом без участия разработки (по событиям draft / review / publish)

Количество откатов

И причины: ошибка данных, визуальная ошибка, неверный порядок блоков, несовместимость версий

Правки после review

Индикатор качества интерфейса и guardrails

Заключение

Мы не отказались от BIRT и не переписывали рендеринг с нуля. Мы добавили предметный backend-слой, который превращает набор шаблонов в платформу: документ стал композицией версий макета, блока, данных блока и справочников значений, плюс правила публикации и переноса между средами.

Такой подход делает генерацию страховых полисов управляемой: изменения можно выпускать безопасно и предсказуемо, не превращая каждый новый вариант документа в очередную копию.

Если у вас тоже есть BIRT, Jasper или любая другая шаблонная платформа — и вы уже чувствуете, что модель «один кейс — один шаблон» больше не работает — возможно, вам нужен не новый рендерер, а нормальный слой композиции, версионирования и публикации поверх текущего движка.