package service import ( "time" "gorm.io/gorm" "github.com/sundynix/pets-be/internal/model" ) func dayStart(t time.Time) time.Time { return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) } // ensureDayTasks 惰性生成某天的任务:今天/未来若为空,则从「最近一次有任务的那天」顺延复制, // 无历史则用养护模板。过去的日子不补(保留历史真实)。 func (s *Service) ensureDayTasks(pet *model.Pet, day time.Time) { start := dayStart(day) if start.Before(dayStart(time.Now())) { return } end := start.AddDate(0, 0, 1) var cnt int64 s.db.Model(&model.DailyTask{}).Where("pet_id = ? AND task_date >= ? AND task_date < ?", pet.ID, start, end).Count(&cnt) if cnt > 0 { return } var defs []model.DailyTask var latest model.DailyTask if err := s.db.Where("pet_id = ? AND task_date < ?", pet.ID, start).Order("task_date desc, id desc").First(&latest).Error; err == nil { ls := dayStart(latest.TaskDate) var prev []model.DailyTask s.db.Where("pet_id = ? AND task_date >= ? AND task_date < ?", pet.ID, ls, ls.AddDate(0, 0, 1)).Order("id asc").Find(&prev) for _, t := range prev { defs = append(defs, model.DailyTask{ PetID: pet.ID, UserID: pet.UserID, TaskDate: start, Title: t.Title, Description: t.Description, Priority: t.Priority, SheetType: t.SheetType, }) } } else { for _, it := range s.careTemplateItems(SpeciesOf(pet.Type), pet.Stage) { if it.Kind == model.CareKindTask { defs = append(defs, model.DailyTask{ PetID: pet.ID, UserID: pet.UserID, TaskDate: start, Title: it.Title, Description: it.Description, Priority: it.Priority, SheetType: it.SheetType, }) } } } if len(defs) > 0 { s.db.Create(&defs) } } // ListTasks 列出宠物某天任务(date 为空则取今天);今天/未来为空会惰性生成 func (s *Service) ListTasks(userID, petID string, date *time.Time) ([]model.DailyTask, error) { pet, err := s.ownedPet(userID, petID) if err != nil { return nil, err } target := time.Now() if date != nil { target = *date } s.ensureDayTasks(pet, target) start := dayStart(target) end := start.AddDate(0, 0, 1) var tasks []model.DailyTask err = s.db.Where("pet_id = ? AND task_date >= ? AND task_date < ?", petID, start, end). Order("id asc").Find(&tasks).Error return tasks, err } // TaskInput 增/改任务入参 type TaskInput struct { Title string Description string Priority string SheetType string Date *time.Time // 仅新增用,默认今天 } // CreateTask 为某天新增一条任务(默认今天) func (s *Service) CreateTask(userID, petID string, in TaskInput) (*model.DailyTask, error) { if _, err := s.ownedPet(userID, petID); err != nil { return nil, err } day := time.Now() if in.Date != nil { day = *in.Date } t := model.DailyTask{ PetID: petID, UserID: userID, TaskDate: dayStart(day), Title: in.Title, Description: in.Description, Priority: in.Priority, SheetType: in.SheetType, } if err := s.db.Create(&t).Error; err != nil { return nil, err } return &t, nil } // UpdateTask 修改任务文案/优先级/关联弹层 func (s *Service) UpdateTask(userID, taskID string, in TaskInput) (*model.DailyTask, error) { var t model.DailyTask if err := s.db.Where("id = ? AND user_id = ?", taskID, userID).First(&t).Error; err != nil { return nil, ErrNotFound } if err := s.db.Model(&model.DailyTask{}).Where("id = ?", taskID).Updates(map[string]any{ "title": in.Title, "description": in.Description, "priority": in.Priority, "sheet_type": in.SheetType, }).Error; err != nil { return nil, err } return s.UpdateTaskReload(taskID) } // UpdateTaskReload 重新读取任务 func (s *Service) UpdateTaskReload(taskID string) (*model.DailyTask, error) { var t model.DailyTask if err := s.db.First(&t, "id = ?", taskID).Error; err != nil { return nil, ErrNotFound } return &t, nil } // DeleteTask 删除任务 func (s *Service) DeleteTask(userID, taskID string) error { res := s.db.Where("id = ? AND user_id = ?", taskID, userID).Delete(&model.DailyTask{}) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return ErrNotFound } return nil } // ToggleTask 切换任务完成状态 func (s *Service) ToggleTask(userID, taskID string) (*model.DailyTask, error) { var task model.DailyTask if err := s.db.Where("id = ? AND user_id = ?", taskID, userID).First(&task).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, ErrNotFound } return nil, err } task.Done = !task.Done if task.Done { now := time.Now() task.CompletedAt = &now } else { task.CompletedAt = nil } if err := s.db.Save(&task).Error; err != nil { return nil, err } return &task, nil } // CompleteAllTasks 完成宠物今日全部任务 func (s *Service) CompleteAllTasks(userID, petID string) ([]model.DailyTask, error) { if _, err := s.ownedPet(userID, petID); err != nil { return nil, err } now := time.Now() start := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) end := start.AddDate(0, 0, 1) if err := s.db.Model(&model.DailyTask{}). Where("pet_id = ? AND task_date >= ? AND task_date < ?", petID, start, end). Updates(map[string]any{"done": true, "completed_at": now}).Error; err != nil { return nil, err } return s.ListTasks(userID, petID, &now) }