Загрузка данных
message_generator_queue_d_11 (Устройство) в статусе "Нестабильно": частое изменение статуса actionNames.28. 20.05.2026 15:30:00
import { Notification } from '@/shared/api/notifications/client';
import { t } from 'i18next';
import {
CRUD_ACTIONS,
ExtendedActionName,
SERVICE_STATUS_ACTIONS,
STATUS_CHANGE_ACTIONS,
} from './model';
import {
buildAggregatedMessage,
extractObjectName,
extractRepeatedStatus,
formatTimestamp,
} from './utils';
export const buildMessage = (notification: Notification): string => {
const actionName = (notification.action?.name?.trim() ??
'') as ExtendedActionName;
const entityType = notification.entity?.type;
const objectName = extractObjectName(notification);
const unknownUser = t('notifications:messageBuilder.unknownUser');
const system = t('notifications:messageBuilder.system');
let authorName = notification.actor?.fullName || unknownUser;
const timestamp = notification.createdAt
? formatTimestamp(notification.createdAt)
: '';
// Агрегированное уведомление
const attrs = notification.attrs as Record<string, unknown> | undefined;
if (attrs?.isAggregated === true) {
const eventCount =
typeof attrs.eventCount === 'number' ? attrs.eventCount : 0;
if (eventCount > 1) {
return buildAggregatedMessage(
actionName,
entityType,
objectName,
eventCount,
timestamp,
);
}
}
const objectType = t(
`notifications:messageBuilder.objectTypes.${entityType}`,
);
if (STATUS_CHANGE_ACTIONS.includes(actionName)) {
if (authorName === unknownUser) authorName = system;
return t('notifications:messageBuilder.statusChange', {
objectName,
objectType,
status: t(`notifications:actionNames.${actionName}`),
author: authorName,
timestamp,
});
}
if (SERVICE_STATUS_ACTIONS.includes(actionName)) {
return t('notifications:messageBuilder.serviceStatusChange', {
objectName,
status: t(`notifications:actionNames.${actionName}`),
author: authorName,
timestamp,
});
}
if (CRUD_ACTIONS.includes(actionName)) {
return t('notifications:messageBuilder.crudAction', {
objectName,
objectType,
action: t(`notifications:messageBuilder.actions.${actionName}`),
author: authorName,
timestamp,
});
}
switch (actionName) {
case 'due_today':
return t('notifications:messageBuilder.dueToday', {
objectName,
timestamp,
});
case 'due_tomorrow':
return t('notifications:messageBuilder.dueTomorrow', {
objectName,
timestamp,
});
case 'flapping':
return t('notifications:messageBuilder.flapping', {
objectName,
objectType,
repeatedStatus: extractRepeatedStatus(notification),
timestamp,
});
case 'flapping_resolved':
return t('notifications:messageBuilder.flappingResolved', {
objectName,
objectType,
author: authorName,
timestamp,
});
case 'service_status_warranty_expired':
return t('notifications:messageBuilder.warrantyExpired', {
objectName,
objectType,
system,
timestamp,
});
case 'service_status_warranty_will_expire_30day':
return t('notifications:messageBuilder.warrantyExpire30Day', {
objectName,
objectType,
system,
timestamp,
});
case 'service_status_warranty_will_expire_5day':
return t('notifications:messageBuilder.warrantyExpire5Day', {
objectName,
objectType,
system,
timestamp,
});
case 'service_status_warranty_will_expire_today':
return t('notifications:messageBuilder.warrantyExpireToday', {
objectName,
objectType,
system,
timestamp,
});
default:
return '';
}
};
import { ActionNames } from '@/shared/api/notifications/client';
export type ExtendedActionName =
| ActionNames
| 'status_warning'
| 'status_disabled'
| 'gve_failed'
| 'gve_success'
| 'gve_unknown'
| 'gve_missing_packets'
| 'flapping'
| 'flapping_resolved'
| 'service_status_warranty_expired'
| 'service_status_warranty_will_expire_30day'
| 'service_status_warranty_will_expire_5day'
| 'service_status_warranty_will_expire_today'
| 'archive';
export const STATUS_CHANGE_ACTIONS: ExtendedActionName[] = [
'status_disconnected',
'status_unknown',
'status_connected',
'status_warning',
'icmp_failed',
'tcp_failed',
'icmp_success',
'tcp_success',
'network_access_false',
'status_disabled',
'gve_failed',
'gve_missing_packets',
'gve_success',
'gve_unknown',
];
export const SERVICE_STATUS_ACTIONS: ExtendedActionName[] = [
'service_status_psi',
'service_status_in_service',
'service_status_exploitation',
];
export const CRUD_ACTIONS: ExtendedActionName[] = [
'create',
'update',
'delete',
'archive',
];
import { EntityType, Notification } from '@/shared/api/notifications/client';
import { t } from 'i18next';
import { ExtendedActionName, STATUS_CHANGE_ACTIONS } from './model';
export const formatTimestamp = (dateStr: string): string => {
const date = new Date(dateStr);
return date
.toLocaleString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZone: 'Europe/Moscow',
})
.replace(',', '');
};
export const extractObjectName = (notification: Notification): string => {
if (notification?.action?.name !== 'update') {
return notification?.entity?.name ?? '';
}
const attrs = notification.attrs as Record<string, unknown> | undefined;
if (attrs?.oldEntityName && typeof attrs.oldEntityName === 'string') {
return attrs.oldEntityName;
}
// старое условие тоже оставляем, т.к. в бд есть старые записи
if (attrs?.objectName && typeof attrs.objectName === 'string') {
return attrs.objectName;
}
return notification.entity?.id ?? '';
};
export const extractRepeatedStatus = (notification: Notification): string => {
const attrs = notification.attrs as Record<string, unknown> | undefined;
if (attrs?.status != null) {
const statusName = String(attrs.status);
return t(`notifications:actionNames.${statusName}`);
}
return '-';
};
export const buildAggregatedMessage = (
actionName: ExtendedActionName,
entityType: EntityType | undefined,
objectName: string,
eventCount: number,
timestamp: string,
): string => {
if (!STATUS_CHANGE_ACTIONS.includes(actionName)) return '';
return t('notifications:messageBuilder.statusChangeAggregated', {
objectName,
objectType: t(`notifications:messageBuilder.objectTypes.${entityType}`),
status: t(`notifications:actionNames.${actionName}`),
eventCount,
system: t('notifications:messageBuilder.system'),
timestamp,
});
};
{
"notifications": "Уведомления",
"criticalNotifications": "Критичные уведомления",
"notificationDetails": "Детали уведомления",
"detailsFields": {
"message": "Сообщение",
"createdAt": "Дата создания события",
"archivedAt": "Дата архивации события",
"actionPriority": "Тип события",
"actionName": "Событие",
"entity": "Сущность",
"actor": "Создатель события",
"reader": "Уведомление прочитано пользователем",
"readAt": "Дата прочтения",
"attrs": "Атрибуты события",
"locationPath": "Локация"
},
"actual": "Актуальные",
"archived": "Архив",
"buttons": {
"readAll": "Прочитать все",
"archiveAll": "В архив",
"read": "Прочитать",
"archive": "В архив",
"enableNotifications": "Включить уведомления",
"disableNotifications": "Выключить уведомления"
},
"filters": {
"byPriority": "По приоритету",
"byEntityType": "По объекту",
"byAction": "По событию",
"byDate": "По дате",
"searchPlaceholder": "Поиск по названию"
},
"tooltips": {
"archiveAll": "Перенести все уведомления в архив",
"details": "Детали",
"read": "Прочитать",
"archive": "В архив"
},
"priority": {
"critical": "Критический",
"important": "Требует внимания",
"normal": "Важный",
"info": "Информационный"
},
"actionNames": {
"create": "Создание",
"update": "Редактирование",
"delete": "Удаление",
"status_disconnected": "Отключено",
"status_unknown": "Неизвестно",
"status_connected": "Подключено",
"status_warning": "Внимание",
"status_missing_packets": "Потеря пакетов",
"service_status_psi": "Сервисный статус ПСИ",
"service_status_in_service": "Сервисный статус В сервисе",
"service_status_exploitation": "Сервисный статус Эксплуатация",
"icmp_failed": "ICMP не удался",
"tcp_failed": "TCP не удался",
"icmp_success": "ICMP успешен",
"tcp_success": "TCP успешен",
"icmp_unknown": "ICMP неизвестно",
"tcp_unknown": "TCP неизвестно",
"due_today": "Заявка истекает сегодня",
"due_tomorrow": "Заявка истекает завтра",
"network_access_false": "Отсутствие сетевого доступа",
"is_monitoring_true": "Только учёт включен",
"status_disabled": "Выключено",
"flapping": "Нестабильно",
"flapping_resolved": "Решено",
"archive": "В архиве",
"service_status_warranty_expired": "Гарантийный срок истёк",
"service_status_warranty_will_expire_today": "Гарантийный срок истекает сегодня",
"service_status_warranty_will_expire_5day": "Гарантийный срок истекает через 5 дней",
"service_status_warranty_will_expire_30day": "Гарантийный срок истекает через 30 дней",
"gve_failed": "UDP недоступен",
"gve_success": "UDP доступен",
"gve_unknown": "UDP неизвестно",
"gve_missing_packets": "UDP потеря пакетов"
},
"messageBuilder": {
"unknownUser": "Неизвестный пользователь",
"system": "Система",
"objectTypes": {
"controller": "Контроллер",
"padlet": "Панель SmartRoom",
"device": "Устройство",
"network-device": "ССУ",
"offline-device": "СНУ",
"location": "Локация",
"ticket": "Заявка",
"service-status": "Комната"
},
"actions": {
"create": "создание",
"update": "редактирование",
"delete": "удаление",
"archive": "архивация"
},
"statusChange": "{{objectName}} ({{objectType}}) - изменение статуса на {{status}}, {{author}} {{timestamp}}",
"statusChangeAggregated": "{{objectName}} ({{objectType}}) - изменение статуса на {{status}} произошло {{eventCount}} раз(а), {{system}} {{timestamp}}",
"serviceStatusChange": "Локация {{objectName}} : изменение сервисного статуса на {{status}}, пользователем {{author}}, {{timestamp}}",
"crudAction": "{{objectName}} ({{objectType}}) - {{action}} пользователем {{author}} {{timestamp}}",
"dueToday": "Срок выполнения заявки {{objectName}} истекает сегодня. {{timestamp}}",
"dueTomorrow": "Срок выполнения заявки {{objectName}} истекает завтра. {{timestamp}}",
"flapping": "{{objectName}} ({{objectType}}) в статусе \"Нестабильно\": частое изменение статуса {{repeatedStatus}}. {{timestamp}}",
"flappingResolved": "{{objectName}} ({{objectType}}): проблема с частым изменением статуса решена пользователем {{author}}, {{timestamp}}",
"warrantyExpired": "{{objectName}} ({{objectType}}) - Гарантийный срок истек, {{system}} {{timestamp}}",
"warrantyExpire30Day": "{{objectName}} ({{objectType}}) - Гарантийный срок истекает через 30 дней, {{system}} {{timestamp}}",
"warrantyExpire5Day": "{{objectName}} ({{objectType}}) - Гарантийный срок истекает через 5 дней, {{system}} {{timestamp}}",
"warrantyExpireToday": "{{objectName}} ({{objectType}}) - Гарантийный срок истекает сегодня, {{system}} {{timestamp}}"
},
"locationHasBeenDeleted": "Локация была удалена"
}
{
"notifications": [
{
"id": "06516318-897e-4951-8c7e-f2a3606c6ce1",
"entity": {
"id": "0e366339-d1a4-4c2c-95a8-9203d24f3d03",
"name": "message_generator_queue_d_11",
"type": "device"
},
"actor": {
"id": "00000000-0000-0000-0000-000000000000",
"fullName": ""
},
"reader": {
"id": "00000000-0000-0000-0000-000000000000",
"fullName": ""
},
"action": {
"id": 19,
"name": "flapping",
"priority": "important"
},
"isVip": null,
"attrs": {
"eventCount": 1,
"firstEventTime": "2026-05-20T12:30:00Z",
"isAggregated": true,
"lastEventTime": "2026-05-20T12:30:00Z",
"status": 28
},
"createdAt": "2026-05-20T15:30:00+03:00",
"readAt": null,
"archivedAt": null
},