package service import ( "fmt" "time" "gorm.io/datatypes" "gorm.io/gorm" "github.com/sundynix/pets-be/internal/model" ) // RecordInput 健康记录入参 type RecordInput struct { Type string Icon string Title string Description string NumValue float64 Category string ImageURL string Extra datatypes.JSON OccurredAt *time.Time } // ListRecords 列出宠物的健康记录(可按 type 过滤) func (s *Service) ListRecords(userID, petID uint, recordType string) ([]model.HealthRecord, error) { if _, err := s.ownedPet(userID, petID); err != nil { return nil, err } q := s.db.Where("pet_id = ?", petID) if recordType != "" { q = q.Where("type = ?", recordType) } var records []model.HealthRecord err := q.Order("occurred_at desc, id desc").Find(&records).Error return records, err } // CreateRecord 新增健康记录;weight 类型同步更新宠物体重 func (s *Service) CreateRecord(userID, petID uint, in RecordInput) (*model.HealthRecord, error) { pet, err := s.ownedPet(userID, petID) if err != nil { return nil, err } occurred := time.Now() if in.OccurredAt != nil { occurred = *in.OccurredAt } rec := model.HealthRecord{ PetID: petID, UserID: userID, Type: in.Type, Icon: in.Icon, Title: in.Title, Description: in.Description, NumValue: in.NumValue, Category: in.Category, ImageURL: in.ImageURL, Extra: in.Extra, OccurredAt: occurred, } if err := s.db.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&rec).Error; err != nil { return err } if in.Type == model.RecordWeight && in.NumValue > 0 { weight := fmt.Sprintf("%gkg", in.NumValue) if err := tx.Model(&model.Pet{}).Where("id = ?", pet.ID).Update("weight", weight).Error; err != nil { return err } } return nil }); err != nil { return nil, err } return &rec, nil } // DeleteRecord 删除记录 func (s *Service) DeleteRecord(userID, recordID uint) error { res := s.db.Where("id = ? AND user_id = ?", recordID, userID).Delete(&model.HealthRecord{}) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return ErrNotFound } return nil } // WeightPoint 体重趋势点 type WeightPoint struct { Value float64 `json:"value"` OccurredAt time.Time `json:"occurred_at"` } // WeightTrend 最近 N 次体重(升序) func (s *Service) WeightTrend(userID, petID uint, limit int) ([]WeightPoint, error) { if _, err := s.ownedPet(userID, petID); err != nil { return nil, err } if limit <= 0 { limit = 7 } var records []model.HealthRecord if err := s.db.Where("pet_id = ? AND type = ?", petID, model.RecordWeight). Order("occurred_at desc").Limit(limit).Find(&records).Error; err != nil { return nil, err } points := make([]WeightPoint, 0, len(records)) for i := len(records) - 1; i >= 0; i-- { points = append(points, WeightPoint{Value: records[i].NumValue, OccurredAt: records[i].OccurredAt}) } return points, nil }