diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java index 73f6e0a861..b4a967109b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java @@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.COMMENT_DELETED; + @Service @AllArgsConstructor public class DefaultTbAlarmCommentService extends AbstractTbEntityService implements TbAlarmCommentService { @@ -59,9 +61,10 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem if (alarmComment.getType() == AlarmCommentType.OTHER) { alarmComment.setType(AlarmCommentType.SYSTEM); alarmComment.setUserId(null); - alarmComment.setComment(JacksonUtil.newObjectNode().put("text", - String.format("User %s deleted his comment", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))); + alarmComment.setComment(JacksonUtil.newObjectNode() + .put("text", String.format(COMMENT_DELETED.getText(), user.getTitle())) + .put("subtype", COMMENT_DELETED.name()) + .put("userName", user.getTitle())); AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.saveAlarmComment(alarm.getTenantId(), alarmComment)); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), ActionType.DELETED_COMMENT, user, savedAlarmComment); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index 5a1dee3bb5..8dafe4725f 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmCommentSubType; import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -39,9 +40,17 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.UUID; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.ACKED_BY_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.ASSIGNED_TO_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.CLEARED_BY_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.UNASSIGNED_BY_USER; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.UNASSIGNED_FROM_DELETED_USER; + @Service @AllArgsConstructor @Slf4j @@ -102,8 +111,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - String systemComment = String.format("Alarm was acknowledged by user %s", user.getTitle()); - addSystemAlarmComment(alarmInfo, user, "ACK", systemComment); + addSystemAlarmComment(alarmInfo, user, ACKED_BY_USER,"userName", user.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user); } else { @@ -125,8 +133,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isCleared()) { - String systemComment = String.format("Alarm was cleared by user %s", user.getTitle()); - addSystemAlarmComment(alarmInfo, user, "CLEAR", systemComment); + addSystemAlarmComment(alarmInfo, user, CLEARED_BY_USER, "userName", user.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user); } else { @@ -144,8 +151,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { AlarmAssignee assignee = alarmInfo.getAssignee(); - String systemComment = String.format("Alarm was assigned by user %s to user %s", user.getTitle(), assignee.getTitle()); - addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId()); + addSystemAlarmComment(alarmInfo, user, ASSIGNED_TO_USER, "userName", user.getTitle(), "assigneeName", assignee.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user); } else { @@ -162,8 +168,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - String systemComment = String.format("Alarm was unassigned by user %s", user.getTitle()); - addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment); + addSystemAlarmComment(alarmInfo, user, UNASSIGNED_BY_USER, "userName", user.getTitle()); logEntityActionService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user); } else { @@ -182,8 +187,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb continue; } if (result.isModified()) { - String comment = String.format("Alarm was unassigned because user %s - was deleted", userTitle); - addSystemAlarmComment(result.getAlarm(), null, "ASSIGN", comment); + addSystemAlarmComment(result.getAlarm(), null, UNASSIGNED_FROM_DELETED_USER, "userName", userTitle); logEntityActionService.logEntityAction(result.getAlarm().getTenantId(), result.getAlarm().getOriginator(), result.getAlarm(), result.getAlarm().getCustomerId(), ActionType.ALARM_UNASSIGNED, null); } } @@ -214,20 +218,24 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb return ts > 0 ? ts : System.currentTimeMillis(); } - private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText) { - addSystemAlarmComment(alarm, user, subType, commentText, null); + private void addSystemAlarmComment(Alarm alarm, User user, AlarmCommentSubType subType, String param, String value) { + Map params = new LinkedHashMap<>(1); + params.put(param, value); + addSystemAlarmComment(alarm, user, subType, params); + } + + private void addSystemAlarmComment(Alarm alarm, User user, AlarmCommentSubType subType, String param, String value, String param2, String value2) { + Map params = new LinkedHashMap<>(2); + params.put(param, value); + params.put(param2, value2); + addSystemAlarmComment(alarm, user, subType, params); } - private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText, UserId assigneeId) { + private void addSystemAlarmComment(Alarm alarm, User user, AlarmCommentSubType subType, Map params) { ObjectNode commentNode = JacksonUtil.newObjectNode(); - commentNode.put("text", commentText) - .put("subtype", subType); - if (user != null) { - commentNode.put("userId", user.getId().getId().toString()); - } - if (assigneeId != null) { - commentNode.put("assigneeId", assigneeId.getId().toString()); - } + commentNode.put("text", String.format(subType.getText(), params.values().toArray())) + .put("subtype", subType.name()); + params.forEach(commentNode::put); AlarmComment alarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index b68f604460..1226fabf10 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -61,6 +61,8 @@ import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.Collection; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.SEVERITY_CHANGED; + /** * Created by ashvayka on 27.03.18. */ @@ -251,8 +253,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService AlarmComment.AlarmCommentBuilder alarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", - String.format("Alarm severity was updated from %s to %s", result.getOldSeverity(), alarm.getSeverity()))); + .comment(JacksonUtil.newObjectNode() + .put("text", String.format(SEVERITY_CHANGED.getText(), result.getOldSeverity(), alarm.getSeverity())) + .put("subtype", SEVERITY_CHANGED.name()) + .put("oldSeverity", result.getOldSeverity().name()) + .put("newSeverity", alarm.getSeverity().name())); if (request != null && request.getUserId() != null) { alarmComment.userId(request.getUserId()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java index 0dcc037b98..cfa74fa873 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java @@ -48,6 +48,7 @@ import java.util.List; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.alarm.AlarmCommentSubType.COMMENT_DELETED; @Slf4j @ContextConfiguration(classes = {AlarmCommentControllerTest.Config.class}) @@ -207,8 +208,10 @@ public class AlarmCommentControllerTest extends AbstractControllerTest { AlarmComment expectedAlarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment", - CUSTOMER_USER_EMAIL))) + .comment(JacksonUtil.newObjectNode() + .put("text", String.format(COMMENT_DELETED.getText(), CUSTOMER_USER_EMAIL)) + .put("subtype", COMMENT_DELETED.name()) + .put("userName", CUSTOMER_USER_EMAIL)) .build(); testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment); } @@ -226,8 +229,10 @@ public class AlarmCommentControllerTest extends AbstractControllerTest { AlarmComment expectedAlarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("User %s deleted his comment", - TENANT_ADMIN_EMAIL))) + .comment(JacksonUtil.newObjectNode() + .put("text", String.format(COMMENT_DELETED.getText(), TENANT_ADMIN_EMAIL)) + .put("subtype", COMMENT_DELETED.name()) + .put("userName", TENANT_ADMIN_EMAIL)) .build(); testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED_COMMENT, 1, expectedAlarmComment); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentSubType.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentSubType.java new file mode 100644 index 0000000000..08bdf71e37 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentSubType.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.alarm; + +import lombok.Getter; + +public enum AlarmCommentSubType { + + ACKED_BY_USER("Alarm was acknowledged by user %s"), + CLEARED_BY_USER("Alarm was cleared by user %s"), + ASSIGNED_TO_USER("Alarm was assigned by user %s to user %s"), + UNASSIGNED_BY_USER("Alarm was unassigned by user %s"), + UNASSIGNED_FROM_DELETED_USER("Alarm was unassigned because user %s - was deleted"), + COMMENT_DELETED("User %s deleted his comment"), + SEVERITY_CHANGED("Alarm severity was updated from %s to %s"); + + @Getter + private final String text; + + AlarmCommentSubType(String text) { + this.text = text; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts index cc99ccf411..800fdf2371 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts @@ -27,7 +27,13 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MAX_SAFE_PAGE_SIZE, PageLink } from '@shared/models/page/page-link'; import { DateAgoPipe } from '@shared/pipe/date-ago.pipe'; import { map } from 'rxjs/operators'; -import { AlarmComment, AlarmCommentType, getUserDisplayName } from '@shared/models/alarm.models'; +import { + AlarmComment, + AlarmCommentInfo, + AlarmCommentType, + AlarmMessage, + getUserDisplayName +} from '@shared/models/alarm.models'; import { UtilsService } from '@core/services/utils.service'; import { EntityType } from '@shared/models/entity-type.models'; import { DatePipe } from '@angular/common'; @@ -121,7 +127,7 @@ export class AlarmCommentComponent implements OnInit { const displayDataElement = {} as AlarmCommentsDisplayData; displayDataElement.createdTime = this.datePipe.transform(alarmComment.createdTime, 'yyyy-MM-dd HH:mm:ss'); displayDataElement.createdDateAgo = this.dateAgoPipe.transform(alarmComment.createdTime); - displayDataElement.commentText = alarmComment.comment.text; + displayDataElement.commentText = this.parseSystemComment(alarmComment); displayDataElement.isSystemComment = alarmComment.type === AlarmCommentType.SYSTEM; if (alarmComment.type === AlarmCommentType.OTHER) { displayDataElement.commentId = alarmComment.id.id; @@ -144,6 +150,15 @@ export class AlarmCommentComponent implements OnInit { ); } + private parseSystemComment(alarm: AlarmCommentInfo): string { + const subTypeKey = alarm.comment?.subtype; + if (subTypeKey && AlarmMessage[subTypeKey]) { + const translationKey = AlarmMessage[subTypeKey]; + return this.translate.instant(translationKey, alarm.comment); + } + return alarm.comment.text; + } + changeSortDirection() { const currentDirection = this.alarmCommentSortOrder.direction; this.alarmCommentSortOrder.direction = currentDirection === Direction.DESC ? Direction.ASC : Direction.DESC; diff --git a/ui-ngx/src/app/shared/models/alarm.models.ts b/ui-ngx/src/app/shared/models/alarm.models.ts index 590e866465..5e1e5def25 100644 --- a/ui-ngx/src/app/shared/models/alarm.models.ts +++ b/ui-ngx/src/app/shared/models/alarm.models.ts @@ -120,12 +120,27 @@ export enum AlarmCommentType { OTHER = 'OTHER' } +export enum AlarmMessage { + ACKED_BY_USER = "alarm.system-comments.acked-by-user", + CLEARED_BY_USER = "alarm.system-comments.cleared-by-user", + ASSIGNED_TO_USER = "alarm.system-comments.assigned-to-user", + UNASSIGNED_BY_USER = "alarm.system-comments.unassigned-to-user", + UNASSIGNED_FROM_DELETED_USER = "alarm.system-comments.unassigned-from-deleted-user", + COMMENT_DELETED = "alarm.system-comments.comment-deleted", + SEVERITY_CHANGED = "alarm.system-comments.severity-changed", +} + export interface AlarmComment extends BaseData { alarmId: AlarmId; userId?: UserId; type: AlarmCommentType; comment: { text: string; + subtype?: keyof typeof AlarmMessage; + userName?: string; + assigneeName?: string; + oldSeverity?: AlarmSeverity; + newSeverity?: AlarmSeverity; edited?: boolean; editedOn?: number; }; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 0ae52ea1d5..60e279d7e6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -657,7 +657,16 @@ "alarm-type": "Alarm type", "enter-alarm-type": "Enter alarm type", "no-alarm-types-matching": "No alarm types matching '{{entitySubtype}}' were found.", - "alarm-type-list-empty": "No alarm types selected." + "alarm-type-list-empty": "No alarm types selected.", + "system-comments": { + "acked-by-user": "Alarm was acknowledged by user {{userName}}", + "cleared-by-user": "Alarm was cleared by user {{userName}}", + "assigned-to-user": "Alarm was assigned by user {{userName}} to user {{assigneeName}}", + "unassigned-to-user": "Alarm was unassigned by user {{userName}}", + "unassigned-from-deleted-user": "Alarm was unassigned because user {{userName}} - was deleted", + "comment-deleted": "User {{userName}} deleted his comment", + "severity-changed": "Alarm severity was updated from {{oldSeverity}} to {{newSeverity}}" + } }, "alarm-activity": { "add": "Add a comment...",