4.1.6.2. Наполнение секций истории¶
Added in version 1.28.0.
Секции “Часто используемое” и “Недавно закрытые” Навигационного хаба наполняет прикладной слой: он перехватывает действия пользователя, накапливает историю и уведомляет сервер о её изменении. Подключение самих секций через разметку описано на странице Подключение Хаба к приложению.
4.1.6.2.1. Модель ответственности¶
Платформа и прикладной слой делят обязанности так:
Платформа отображает секции, хранит пользовательские настройки секции “Закрепленное”, читает данные секций истории из прикладных выборок (
selectionNameконтролов<tiles>с ролямиpopularиlatest) и обновляет клиент сессии, в которой опубликовано событие истории.Прикладной слой перехватывает действия пользователя (выполнение операций, закрытие выборок), накапливает и агрегирует историю (например, в таблицах решения), поддерживает актуальность данных выборок истории и публикует событие в топик
NavHubHistoryEventsTopicпосле каждого изменения.
Выбор способа накопления и агрегации истории — зона ответственности прикладного
департамента; ориентиром служит существующая прикладная библиотека телеметрии
ru.bitec.app.btk.telemetry.Btk_TelemetryLib.
4.1.6.2.2. Перехват закрытия выборок¶
Факт использования выборки надёжнее регистрировать по событию закрытия
(afterClose): к этому моменту выборка гарантированно открывалась без ошибок и
пользователь взаимодействовал с ней. Перехват по событию открытия (beforeOpen)
реализуется идентично.
Закрытие перехватывается переопределением метода afterClose
(ru.bitec.app.gtk.gl.DefaultRep#afterClose) в выборке. Чтобы не дублировать
переопределение в каждой выборке, создаётся промежуточный класс:
abstract class NavigationHubEntityAvi extends EntityAvi {
trait NavigationHubEntityAviDefault extends EntityAviDefault {
override protected def afterClose(): Unit = {
super.afterClose()
// Зарегистрировать закрытие выборки в истории текущего пользователя.
// ...
}
}
}
AVI-классы выборок, участвующих в истории, наследуются от промежуточного класса:
@AvmFile(name = "Module_Document.avm.xml")
class Module_DocumentAvi extends Module_DocumentDvi with NavigationHubEntityAvi {
trait Default extends DviDefault with NavigationHubEntityAviDefault
trait List extends Default with DviList
trait Card extends Default with DviCard
}
Такой подход заодно определяет область охвата: выборки, не подмешавшие
NavigationHubEntityAviDefault, в истории не регистрируются.
4.1.6.2.3. Параметры открытия в истории¶
Чтобы история различала объекты одного класса — например, карточки разных договоров, —
вместе с именем выборки при закрытии сохраняются параметры, с которыми открыта форма.
В выборке тайлов истории они отдаются колонкой параметров открытия (атрибут
actionParamsAttr фрейма <tiles>, см. Подключение Хаба к приложению),
и клик по тайлу воспроизводит открытие.
Источник.
selection.form.params(ru.bitec.app.gtk.gl.Form#params) — параметры, переданные форме при открытии: из URI-ссылки на выборку, изopenFormExприкладного кода или из тайла Хаба. Значения в карте не обязательно строки; в JSON они записываются строкой, числом илиtrue/false. Ключи со значениемnullпри сериализации отбрасываются, вложенные объекты и массивы не записываются: тайл с такими значениями не выводится.Что в карту не попадает. Фильтры, выставленные пользователем в открытой форме, и переход к другой записи внутри карточки. Если для истории важна запись, на которой пользователь закончил работу, её ключ берётся из текущей строки выборки и записывается под именем параметра ключа карточки (в прикладных библиотеках обычно
idItem#). Для списков, открытых без параметров, карта пуста — колонка параметров остаётся пустой.Служебные ключи. Ключи, не влияющие на то, какой объект откроется (
CREATINGMODE,DEFAULTEDITOR, размеры окна), в историю не записываются, иначе одна карточка даст несколько записей.EDITINGTYPEсо значениемEditсохраняется. Карточки, открытые на создание или копирование, в историю не записываются: повторное открытие создало бы ещё один объект.Подпись тайла. Для тайла записи подпись формируется из объекта (номер, наименование), а не из заголовка выборки, и пересчитывается при чтении выборки истории.
Ключ агрегации. Сервер сравнивает параметры тайлов без учёта порядка ключей. Чтобы одна карточка не расходилась в истории на несколько записей, ключ агрегации строится из имени выборки и параметров одним методом для обеих секций — например, с сортировкой ключей перед сериализацией.
Размер колонки. Колонка параметров должна вмещать сериализованную карту целиком: обрезанный JSON не разбирается, и тайл не выводится. Если в параметрах бывают фильтры или макросы, колонку заводят текстового типа без ограничения длины.
4.1.6.2.4. Перехват выполнения операций¶
Выполнение операций перехватывается обработчиком событий уровня приложения. В классе
главной выборки приложения (какой класс является главным — определяется по
src/main/resources/META-INF/applications.xml прикладного решения) переопределяется
метод onRestoreState (ru.bitec.app.btk.gl.ApplicationAvi.ApplicationAviDefault#onRestoreState)
и регистрируется обработчик на все операции:
override def onRestoreState(bundle: KeyBundle): Unit = {
super.onRestoreState(bundle)
val thisRep = selection.baseRep.unwrap(classOf[InternalRep])
val idUser = Btk_UserApi().getCurrentUserID()
val sSelectionName = selection.name + "#" + selection.representation
val isOperProcessing = (sOperName: String) => {
(sOperName.toUpperCase === "closeFormOk".toUpperCase).isFalse &&
(sOperName.toUpperCase === "closeFormCancel".toUpperCase).isFalse &&
!session.isClosed &&
selection.coreRep != null
// Допустимы дополнительные проверки.
}
thisRep.handlers.addBeforeEvent(RepEvent.allOperations, spOperName => {
val sOperName = spOperName.toLowerCase
if (isOperProcessing(sOperName)) {
// Сохранить параметры выполненной операции в историю.
// ...
}
})
}
Служебные операции (в примере — операции закрытия форм) исключаются из регистрации
фильтром isOperProcessing.
4.1.6.2.5. Событие изменения истории¶
После внесения изменений в историю (или пересчёта агрегатов) прикладной код публикует
событие в шину сообщений. Контракт события задаёт интерфейс
ru.bitec.gtk.core.gl.workcenter.navhub.NavHubHistoryEventsTopic (модуль
gtk-core — доступен и прикладному слою):
Константа |
Значение |
Назначение |
|---|---|---|
|
|
Имя выделенного топика событий истории |
|
|
Изменился список недавно закрытых — обновить секцию “Недавно закрытые” |
|
|
Изменилась статистика использования — обновить секцию “Часто используемое” |
Публикация выполняется асинхронно, ответ не предусмотрен:
import ru.bitec.gtk.core.gl.workcenter.navhub.NavHubHistoryEventsTopic
application.eventBus.post(
NavHubHistoryEventsTopic.TOPIC_NAME,
NavHubHistoryEventsTopic.LASTCLOSED_EVENT_NAME,
Array.empty
)
Событие — только сигнал: получив его, сервер заново читает прикладные выборки истории соответствующей секции (см. Подключение Хаба к приложению) и передаёт обновлённое состояние Хаба на открытые клиенты. Аргументы события сервер не разбирает, поэтому к моменту публикации данные выборок истории уже должны отражать новое состояние. События с другими именами в этом топике сервер игнорирует.
See also