Add task completion notification trigger

When a task is completed, send push notifications to all residence
users (except the person who completed it). Uses the existing
NotificationService.CreateAndSendNotification method.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-26 23:28:08 -06:00
parent 2b03f03604
commit 5576343175
2 changed files with 68 additions and 3 deletions

View File

@@ -74,6 +74,9 @@ func SetupRouter(deps *Dependencies) *gin.Engine {
contractorService := services.NewContractorService(contractorRepo, residenceRepo) contractorService := services.NewContractorService(contractorRepo, residenceRepo)
documentService := services.NewDocumentService(documentRepo, residenceRepo) documentService := services.NewDocumentService(documentRepo, residenceRepo)
notificationService := services.NewNotificationService(notificationRepo, gorushClient) notificationService := services.NewNotificationService(notificationRepo, gorushClient)
// Wire up notification service to task service (for task completion notifications)
taskService.SetNotificationService(notificationService)
subscriptionService := services.NewSubscriptionService(subscriptionRepo, residenceRepo, taskRepo, contractorRepo, documentRepo) subscriptionService := services.NewSubscriptionService(subscriptionRepo, residenceRepo, taskRepo, contractorRepo, documentRepo)
// Initialize middleware // Initialize middleware

View File

@@ -1,9 +1,12 @@
package services package services
import ( import (
"context"
"errors" "errors"
"fmt"
"time" "time"
"github.com/rs/zerolog/log"
"gorm.io/gorm" "gorm.io/gorm"
"github.com/treytartt/mycrib-api/internal/dto/requests" "github.com/treytartt/mycrib-api/internal/dto/requests"
@@ -23,8 +26,9 @@ var (
// TaskService handles task business logic // TaskService handles task business logic
type TaskService struct { type TaskService struct {
taskRepo *repositories.TaskRepository taskRepo *repositories.TaskRepository
residenceRepo *repositories.ResidenceRepository residenceRepo *repositories.ResidenceRepository
notificationService *NotificationService
} }
// NewTaskService creates a new task service // NewTaskService creates a new task service
@@ -35,6 +39,11 @@ func NewTaskService(taskRepo *repositories.TaskRepository, residenceRepo *reposi
} }
} }
// SetNotificationService sets the notification service (for breaking circular dependency)
func (s *TaskService) SetNotificationService(ns *NotificationService) {
s.notificationService = ns
}
// === Task CRUD === // === Task CRUD ===
// GetTask gets a task by ID with access check // GetTask gets a task by ID with access check
@@ -461,16 +470,69 @@ func (s *TaskService) CreateCompletion(req *requests.CreateTaskCompletionRequest
return nil, err return nil, err
} }
// Reload // Reload completion with user info
completion, err = s.taskRepo.FindCompletionByID(completion.ID) completion, err = s.taskRepo.FindCompletionByID(completion.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Send notification to residence owner and other users
s.sendTaskCompletedNotification(task, completion)
resp := responses.NewTaskCompletionResponse(completion) resp := responses.NewTaskCompletionResponse(completion)
return &resp, nil return &resp, nil
} }
// sendTaskCompletedNotification sends notifications when a task is completed
func (s *TaskService) sendTaskCompletedNotification(task *models.Task, completion *models.TaskCompletion) {
if s.notificationService == nil {
log.Debug().Msg("Notification service not configured, skipping task completion notification")
return
}
// Get all users with access to this residence
users, err := s.residenceRepo.GetResidenceUsers(task.ResidenceID)
if err != nil {
log.Error().Err(err).Uint("task_id", task.ID).Msg("Failed to get residence users for notification")
return
}
completedByName := "Someone"
if completion.CompletedBy.ID > 0 {
completedByName = completion.CompletedBy.GetFullName()
}
title := "Task Completed"
body := fmt.Sprintf("%s completed: %s", completedByName, task.Title)
data := map[string]interface{}{
"task_id": task.ID,
"residence_id": task.ResidenceID,
"completion_id": completion.ID,
}
// Notify all users except the one who completed the task
for _, user := range users {
if user.ID == completion.CompletedByID {
continue // Don't notify the person who completed it
}
go func(userID uint) {
ctx := context.Background()
if err := s.notificationService.CreateAndSendNotification(
ctx,
userID,
models.NotificationTaskCompleted,
title,
body,
data,
); err != nil {
log.Error().Err(err).Uint("user_id", userID).Uint("task_id", task.ID).Msg("Failed to send task completion notification")
}
}(user.ID)
}
}
// GetCompletion gets a task completion by ID // GetCompletion gets a task completion by ID
func (s *TaskService) GetCompletion(completionID, userID uint) (*responses.TaskCompletionResponse, error) { func (s *TaskService) GetCompletion(completionID, userID uint) (*responses.TaskCompletionResponse, error) {
completion, err := s.taskRepo.FindCompletionByID(completionID) completion, err := s.taskRepo.FindCompletionByID(completionID)