feat(notify): per-channel delivery results + accurate notification metrics

Dispatcher.Send now returns []ChannelResult{Type, Err} alongside the
aggregated error, and scheduler.checkDomain increments
NotificationsTotal per channel type/status instead of a single
unconditional IncNotification("dispatch", newStatus) placeholder that
ignored per-channel delivery outcome.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
2026-07-04 15:56:15 +07:00
parent e9a100ab4a
commit f14916396c
4 changed files with 130 additions and 20 deletions
+10 -3
View File
@@ -49,7 +49,7 @@ type Checker interface {
// NotifySender delivers a status-change event to a project's notification
// channels. internal/notify.Dispatcher satisfies this.
type NotifySender interface {
Send(ctx context.Context, projectID uuid.UUID, ev notify.Event) error
Send(ctx context.Context, projectID uuid.UUID, ev notify.Event) ([]notify.ChannelResult, error)
}
// Scheduler drives periodic domain checks for every due project schedule.
@@ -172,10 +172,17 @@ func (s *Scheduler) checkDomain(ctx context.Context, projectID uuid.UUID, d stor
Summary: summarize(newStatus, cs, checkErr),
At: now,
}
if err := s.notifier.Send(ctx, projectID, ev); err != nil {
results, err := s.notifier.Send(ctx, projectID, ev)
if err != nil {
log.Printf("scheduler: notify send for project %s domain %s failed: %v", projectID, d.ID, err)
}
s.metrics.IncNotification("dispatch", newStatus)
for _, r := range results {
status := "sent"
if r.Err != nil {
status = "failed"
}
s.metrics.IncNotification(r.Type, status)
}
}
return newStatus