fix(notification): dedupe due/overdue events to prevent resend on scan

This commit is contained in:
2026-03-03 01:00:00 +08:00
parent a258923892
commit 58e228b0dc

View File

@@ -476,8 +476,7 @@ func (s *Service) PublishTaskEvent(ctx context.Context, eventType string, task T
traceID = eventID traceID = eventID
} }
if eventType == "task.overdue" { if eventType == "task.overdue" {
bucket := time.Now().UTC().Unix() / int64(s.cfg.OverdueThrottle/time.Second) eventID = fmt.Sprintf("task-overdue:%d:%s", task.ID, task.DueAt)
eventID = fmt.Sprintf("task-overdue:%d:%d", task.ID, bucket)
traceID = eventID traceID = eventID
} }
payload := map[string]any{ payload := map[string]any{
@@ -681,14 +680,40 @@ func (s *Service) scheduleDueEvents(ctx context.Context) {
continue continue
} }
if dueAt.After(now) && dueAt.Before(now.Add(s.cfg.DueSoonWindow)) { if dueAt.After(now) && dueAt.Before(now.Add(s.cfg.DueSoonWindow)) {
if s.hasExistingTaskEvent(ctx, task.ID, "task.due_soon", task.DueAt) {
continue
}
_ = s.PublishTaskEvent(ctx, "task.due_soon", task) _ = s.PublishTaskEvent(ctx, "task.due_soon", task)
} }
if dueAt.Before(now) { if dueAt.Before(now) {
if s.hasExistingTaskEvent(ctx, task.ID, "task.overdue", task.DueAt) {
continue
}
_ = s.PublishTaskEvent(ctx, "task.overdue", task) _ = s.PublishTaskEvent(ctx, "task.overdue", task)
} }
} }
} }
func (s *Service) hasExistingTaskEvent(ctx context.Context, taskID int64, eventType, dueAt string) bool {
var exists bool
err := s.pool.QueryRow(ctx, `
SELECT EXISTS (
SELECT 1
FROM notification_events
WHERE event_type = $1
AND payload->>'task_id' = $2
AND payload->>'due_at' = $3
)`,
eventType,
strconv.FormatInt(taskID, 10),
dueAt,
).Scan(&exists)
if err != nil {
return false
}
return exists
}
func (s *Service) runDailySummaryScheduler(ctx context.Context) { func (s *Service) runDailySummaryScheduler(ctx context.Context) {
ticker := time.NewTicker(s.cfg.SchedulerTick) ticker := time.NewTicker(s.cfg.SchedulerTick)
defer ticker.Stop() defer ticker.Stop()