package service import ( "context" "secunda-test/internal/domain" ) type TaskService struct { tasks TaskRepository teams TeamRepository cache TaskCache } func NewTaskService(tasks TaskRepository, teams TeamRepository, cache TaskCache) *TaskService { return &TaskService{tasks: tasks, teams: teams, cache: cache} } func (s *TaskService) Create(ctx context.Context, userID int64, task domain.Task) (domain.Task, error) { if task.TeamID == 0 || task.Title == "" { return domain.Task{}, ErrBadRequest } if _, ok, err := s.teams.MemberRole(ctx, task.TeamID, userID); err != nil { return domain.Task{}, err } else if !ok { return domain.Task{}, ErrForbidden } if task.AssigneeID != nil { if _, ok, err := s.teams.MemberRole(ctx, task.TeamID, *task.AssigneeID); err != nil { return domain.Task{}, err } else if !ok { return domain.Task{}, ErrBadRequest } } task.CreatedBy = userID created, err := s.tasks.Create(ctx, task) if err == nil { _ = s.cache.DeleteTeam(ctx, task.TeamID) } return created, err } func (s *TaskService) List(ctx context.Context, userID int64, filter domain.TaskFilter) ([]domain.Task, error) { if filter.TeamID == 0 { return nil, ErrBadRequest } if _, ok, err := s.teams.MemberRole(ctx, filter.TeamID, userID); err != nil { return nil, err } else if !ok { return nil, ErrForbidden } if filter.Page < 1 { filter.Page = 1 } if filter.PageSize <= 0 || filter.PageSize > 100 { filter.PageSize = 20 } if cached, ok, err := s.cache.GetTasks(ctx, filter); err == nil && ok { return cached, nil } tasks, err := s.tasks.List(ctx, filter) if err == nil { _ = s.cache.SetTasks(ctx, filter, tasks) } return tasks, err } func (s *TaskService) Update(ctx context.Context, userID, taskID int64, patch domain.Task) (domain.Task, error) { current, err := s.tasks.Get(ctx, taskID) if err != nil { return domain.Task{}, err } role, ok, err := s.teams.MemberRole(ctx, current.TeamID, userID) if err != nil { return domain.Task{}, err } if !ok || (role == domain.RoleMember && current.CreatedBy != userID && (current.AssigneeID == nil || *current.AssigneeID != userID)) { return domain.Task{}, ErrForbidden } if patch.Title != "" { current.Title = patch.Title } if patch.Description != "" { current.Description = patch.Description } if patch.Status != "" { current.Status = patch.Status } if patch.AssigneeID != nil { if _, ok, err := s.teams.MemberRole(ctx, current.TeamID, *patch.AssigneeID); err != nil { return domain.Task{}, err } else if !ok { return domain.Task{}, ErrBadRequest } current.AssigneeID = patch.AssigneeID } updated, err := s.tasks.Update(ctx, current, userID) if err == nil { _ = s.cache.DeleteTeam(ctx, current.TeamID) } return updated, err } func (s *TaskService) History(ctx context.Context, userID, taskID int64) ([]domain.TaskHistory, error) { task, err := s.tasks.Get(ctx, taskID) if err != nil { return nil, err } if _, ok, err := s.teams.MemberRole(ctx, task.TeamID, userID); err != nil { return nil, err } else if !ok { return nil, ErrForbidden } return s.tasks.History(ctx, taskID) } func (s *TaskService) TeamSummary(ctx context.Context) ([]domain.TeamSummary, error) { return s.tasks.TeamSummary(ctx) } func (s *TaskService) TopCreators(ctx context.Context) ([]domain.TopCreator, error) { return s.tasks.TopCreators(ctx) } func (s *TaskService) InvalidAssignees(ctx context.Context) ([]domain.Task, error) { return s.tasks.InvalidAssignees(ctx) }