182 lines
5.1 KiB
Go
182 lines
5.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"go-server/internal/models"
|
|
"go-server/internal/repository"
|
|
"time"
|
|
)
|
|
|
|
type TodoService struct {
|
|
todoRepo *repository.TodoRepository
|
|
}
|
|
|
|
func NewTodoService(todoRepo *repository.TodoRepository) *TodoService {
|
|
return &TodoService{
|
|
todoRepo: todoRepo,
|
|
}
|
|
}
|
|
|
|
// CreateTodoRequest represents the request to create a new todo
|
|
type CreateTodoRequest struct {
|
|
Title string `json:"title" validate:"required,min=1,max=200"`
|
|
Description string `json:"description" validate:"max=1000"`
|
|
Color string `json:"color" validate:"required,hexcolor"`
|
|
}
|
|
|
|
// UpdateTodoRequest represents the request to update a todo
|
|
type UpdateTodoRequest struct {
|
|
Title string `json:"title" validate:"required,min=1,max=200"`
|
|
Description string `json:"description" validate:"max=1000"`
|
|
Color string `json:"color" validate:"required,hexcolor"`
|
|
}
|
|
|
|
// CompleteTodoRequest represents the request to complete a todo
|
|
type CompleteTodoRequest struct {
|
|
Description string `json:"description" validate:"max=1000"`
|
|
}
|
|
|
|
// CreateTodo creates a new todo for a user
|
|
func (s *TodoService) CreateTodo(ctx context.Context, userID models.ULID, req CreateTodoRequest) (*models.Todo, error) {
|
|
todo := &models.Todo{
|
|
UserID: userID,
|
|
Title: req.Title,
|
|
Description: req.Description,
|
|
Color: req.Color,
|
|
IsActive: true,
|
|
}
|
|
|
|
err := s.todoRepo.CreateTodo(ctx, todo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return todo, nil
|
|
}
|
|
|
|
// GetTodosWithStats gets all todos for a user with completion statistics
|
|
func (s *TodoService) GetTodosWithStats(ctx context.Context, userID models.ULID) ([]models.TodoWithStats, error) {
|
|
return s.todoRepo.GetTodosWithStats(ctx, userID)
|
|
}
|
|
|
|
// GetTodoByID gets a todo by ID (with user verification)
|
|
func (s *TodoService) GetTodoByID(ctx context.Context, todoID, userID models.ULID) (*models.Todo, error) {
|
|
return s.todoRepo.GetTodoByID(ctx, todoID, userID)
|
|
}
|
|
|
|
// UpdateTodo updates a todo
|
|
func (s *TodoService) UpdateTodo(ctx context.Context, todoID, userID models.ULID, req UpdateTodoRequest) (*models.Todo, error) {
|
|
todo, err := s.todoRepo.GetTodoByID(ctx, todoID, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
todo.Title = req.Title
|
|
todo.Description = req.Description
|
|
todo.Color = req.Color
|
|
|
|
err = s.todoRepo.UpdateTodo(ctx, todo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return todo, nil
|
|
}
|
|
|
|
// DeleteTodo deletes (deactivates) a todo
|
|
func (s *TodoService) DeleteTodo(ctx context.Context, todoID, userID models.ULID) error {
|
|
return s.todoRepo.DeleteTodo(ctx, todoID, userID)
|
|
}
|
|
|
|
// CompleteTodo marks a todo as completed for today
|
|
func (s *TodoService) CompleteTodo(ctx context.Context, todoID, userID models.ULID, req CompleteTodoRequest) error {
|
|
// First check if the todo exists and belongs to the user
|
|
todo, err := s.todoRepo.GetTodoByID(ctx, todoID, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check if already completed today
|
|
completedToday, err := s.todoRepo.CheckTodoCompletedToday(ctx, todoID, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if completedToday {
|
|
return errors.New("todo already completed today")
|
|
}
|
|
|
|
// Create completion record
|
|
completion := &models.TodoCompletion{
|
|
TodoID: todo.ID,
|
|
UserID: userID,
|
|
CompletedAt: time.Now(),
|
|
Description: req.Description,
|
|
}
|
|
|
|
return s.todoRepo.CompleteTodo(ctx, completion)
|
|
}
|
|
|
|
// GetTodaysSummary gets today's todo summary
|
|
func (s *TodoService) GetTodaysSummary(ctx context.Context, userID models.ULID) (*models.DailyTodoSummary, error) {
|
|
todos, err := s.todoRepo.GetTodosWithStats(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
completedCount := 0
|
|
for _, todo := range todos {
|
|
if todo.CompletedToday {
|
|
completedCount++
|
|
}
|
|
}
|
|
|
|
summary := &models.DailyTodoSummary{
|
|
Date: time.Now(),
|
|
Todos: todos,
|
|
CompletedCount: completedCount,
|
|
TotalCount: len(todos),
|
|
}
|
|
|
|
return summary, nil
|
|
}
|
|
|
|
// GetActivityLog gets the user's activity log with pagination
|
|
func (s *TodoService) GetActivityLog(ctx context.Context, userID models.ULID, limit, offset int) ([]models.TodoCompletion, error) {
|
|
if limit <= 0 {
|
|
limit = 50 // Default limit
|
|
}
|
|
if limit > 200 {
|
|
limit = 200 // Max limit
|
|
}
|
|
|
|
return s.todoRepo.GetActivityLog(ctx, userID, limit, offset)
|
|
}
|
|
|
|
// GetActivityLogByDate gets activity log for a specific date
|
|
func (s *TodoService) GetActivityLogByDate(ctx context.Context, userID models.ULID, date time.Time) ([]models.TodoCompletion, error) {
|
|
return s.todoRepo.GetActivityLogByDate(ctx, userID, date)
|
|
}
|
|
|
|
// GetWeeklySummary gets a summary of the past 7 days
|
|
func (s *TodoService) GetWeeklySummary(ctx context.Context, userID models.ULID) (map[string][]models.TodoCompletion, error) {
|
|
endDate := time.Now().Add(24 * time.Hour) // Include today
|
|
startDate := endDate.Add(-7 * 24 * time.Hour) // 7 days ago
|
|
|
|
completions, err := s.todoRepo.GetCompletionsByDateRange(ctx, userID, startDate, endDate)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Group by date
|
|
dailyCompletions := make(map[string][]models.TodoCompletion)
|
|
for _, completion := range completions {
|
|
dateKey := completion.CompletedAt.Format("2006-01-02")
|
|
dailyCompletions[dateKey] = append(dailyCompletions[dateKey], completion)
|
|
}
|
|
|
|
return dailyCompletions, nil
|
|
}
|
|
|