Шаблонизатор текста

Назначение

ru.bgcrm.service.variables.TextTemplateProcessor заменяет конструкции {{ ключ }} строковыми значениями контекстных переменных. Реализация DefaultTextTemplateProcessor зарегистрирована в глобальном ServerContext.

val processor = ServerContext.lookup(TextTemplateProcessor::class.java)

val result = processor.processMessage(
    "Процесс #{{ process:id }}: {{ process:title }}",
    processId,
    Process.OBJECT_TYPE
)

Для объекта 127 с названием «Подключение» результат будет:

Процесс #127: Подключение

Список штатных ключей приведён в каталоге контекстных переменных.

Синтаксис

Переменная состоит из двух открывающих и двух закрывающих фигурных скобок:

{{process:title}}
{{ process:title }}

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

Вход Результат Правило

{{ process:id }}

Значение process:id

Пробелы вокруг ключа удаляются

{{unknown}}

Пустая строка

Неизвестные ключи не считаются ошибкой

{{}}

Пустая строка

Пустой ключ обрабатывается как неизвестный

{{process:id

Без изменения

Незакрытая конструкция не распознаётся

{process:id}

Без изменения

Одиночные скобки не распознаются

Ключ с переводом строки

Без изменения

Ключ не может занимать несколько строк

Подстановка выполняется один раз. Если значение само содержит {{другой:ключ}}, вложенная конструкция повторно не обрабатывается.

Источник контекста

Интерфейс предоставляет две перегрузки:

String processMessage(String message, int objectId, String objectType);

String processMessage(
    String message,
    Map<String, Supplier<String>> context
);

Первая перегрузка строит обычный контекст через ContextVariableBuilderFactory. Она не добавляет связанные сущности автоматически.

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

val context = CombinedContextVariableBuildHelper()
    .buildContext(processId, Process.OBJECT_TYPE)

val message = processor.processMessage(
    "{{ process:title }} — {{ customer:title }}",
    context
)

Порядок обработки

  1. Пустой или null-текст сразу превращается в пустую строку.

  2. В самом шаблоне раскрываются Java escape-последовательности, например \n, \t и Unicode.

  3. Регулярное выражение находит конструкции {{…​}}.

  4. Ключ обрезается по краям и ищется в карте.

  5. Для найденного ключа вызывается Supplier.get(); null превращается в пустую строку.

  6. Неизвестный ключ заменяется пустой строкой.

Escape-последовательности раскрываются до подстановки. Поэтому обратные слеши внутри значений остаются как есть:

Шаблон: Путь:\n{{ path }}
Значение path: D:\users\ivan

Результат:
Путь:
D:\users\ivan

Знаки $ и \ в значениях подставляются буквально и не интерпретируются как управляющие символы регулярного выражения.

Практические рекомендации

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

  • Не используйте отсутствие текста как проверку существования переменной: опечатка и настоящее пустое значение выглядят одинаково.

  • Если требуется контрагент или договор процесса, передавайте комбинированный контекст явно.

  • Не помещайте в Supplier запись данных и другие побочные эффекты. Одинаковая переменная в одном шаблоне может встретиться несколько раз, и поставщик будет вызван для каждого вхождения.

  • Исключения из билдера или Supplier не перехватываются шаблонизатором и прерывают обработку.

Пример с собственным контекстом

val context = mapOf(
    "order:id" to Supplier { "42" },
    "order:amount" to Supplier { "1 500 ₽" }
)

val text = processor.processMessage(
    "Заказ #{{ order:id }}, сумма {{ order:amount }}",
    context
)

Результат: Заказ #42, сумма 1 500 ₽.