feat(alerts): core repo — filter params + markRead/softDelete/bulkAck/restore; drop AlertReadRepository

- listForInbox gains tri-state acked/read filter params
- countUnreadBySeverityForUser(envId, userId) → countUnreadBySeverity(envId, userId, groupIds, roleNames)
- new methods: markRead, bulkMarkRead, softDelete, bulkSoftDelete, bulkAck, restore
- delete AlertReadRepository — read is now global on alert_instances.read_at

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-21 17:38:10 +02:00
parent 6e8d890442
commit 55b2a00458
2 changed files with 37 additions and 25 deletions

View File

@@ -7,26 +7,27 @@ import java.util.Optional;
import java.util.UUID;
public interface AlertInstanceRepository {
AlertInstance save(AlertInstance instance); // upsert by id
AlertInstance save(AlertInstance instance);
Optional<AlertInstance> findById(UUID id);
Optional<AlertInstance> findOpenForRule(UUID ruleId); // state IN ('PENDING','FIRING','ACKNOWLEDGED')
/**
* Unfiltered inbox listing. Convenience overload that delegates to the filtered
* variant with {@code states}/{@code severities} set to {@code null} (no filter).
*/
/** Open instance for a rule: state IN ('PENDING','FIRING') AND deleted_at IS NULL. */
Optional<AlertInstance> findOpenForRule(UUID ruleId);
/** Unfiltered inbox listing — convenience overload. */
default List<AlertInstance> listForInbox(UUID environmentId,
List<String> userGroupIdFilter,
String userId,
List<String> userRoleNames,
int limit) {
return listForInbox(environmentId, userGroupIdFilter, userId, userRoleNames, null, null, limit);
return listForInbox(environmentId, userGroupIdFilter, userId, userRoleNames,
null, null, null, null, limit);
}
/**
* Inbox listing with optional state + severity filters. {@code null} or empty lists mean
* "no filter on that field". When both lists are non-empty the row must match at least one
* value from each list (AND between dimensions, OR within).
* Inbox listing with optional filters. {@code null} or empty lists mean no filter.
* {@code acked} and {@code read} are tri-state: {@code null} = no filter,
* {@code TRUE} = only acked/read, {@code FALSE} = only unacked/unread.
* Always excludes soft-deleted rows ({@code deleted_at IS NOT NULL}).
*/
List<AlertInstance> listForInbox(UUID environmentId,
List<String> userGroupIdFilter,
@@ -34,20 +35,40 @@ public interface AlertInstanceRepository {
List<String> userRoleNames,
List<AlertState> states,
List<AlertSeverity> severities,
Boolean acked,
Boolean read,
int limit);
/**
* Count unread alert instances for the user, grouped by severity.
* <p>
* Always returns a map with an entry for every {@link AlertSeverity} (value 0 if no rows),
* so callers never need null-checks. Total unread count is the sum of the values.
* Count unread alert instances visible to the user, grouped by severity.
* Visibility: targets user directly, or via one of the given groups/roles.
* "Unread" = {@code read_at IS NULL AND deleted_at IS NULL}.
*/
Map<AlertSeverity, Long> countUnreadBySeverityForUser(UUID environmentId, String userId);
Map<AlertSeverity, Long> countUnreadBySeverity(UUID environmentId,
String userId,
List<String> groupIds,
List<String> roleNames);
void ack(UUID id, String userId, Instant when);
void resolve(UUID id, Instant when);
void markSilenced(UUID id, boolean silenced);
void deleteResolvedBefore(Instant cutoff);
/** FIRING instances whose reNotify cadence has elapsed since last notification. */
/** Set {@code read_at = when} if currently null. Idempotent. */
void markRead(UUID id, Instant when);
/** Bulk variant — single UPDATE. */
void bulkMarkRead(List<UUID> ids, Instant when);
/** Set {@code deleted_at = when} if currently null. Idempotent. */
void softDelete(UUID id, Instant when);
/** Bulk variant — single UPDATE. */
void bulkSoftDelete(List<UUID> ids, Instant when);
/** Clear {@code deleted_at}. Undo for soft-delete. Idempotent. */
void restore(UUID id);
/** Bulk ack — single UPDATE. Each row gets {@code acked_at=when, acked_by=userId} if unacked. */
void bulkAck(List<UUID> ids, String userId, Instant when);
List<AlertInstance> listFiringDueForReNotify(Instant now);
}

View File

@@ -1,9 +0,0 @@
package com.cameleer.server.core.alerting;
import java.util.List;
import java.util.UUID;
public interface AlertReadRepository {
void markRead(String userId, UUID alertInstanceId);
void bulkMarkRead(String userId, List<UUID> alertInstanceIds);
}