Files
sundynix-pets/pets-be/internal/httpapi/pets.go
T

150 lines
4.4 KiB
Go

package httpapi
import (
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"github.com/sundynix/pets-be/internal/model"
)
type petInput struct {
Name string `json:"name" binding:"required"`
Species string `json:"species"`
Breed string `json:"breed"`
Gender string `json:"gender"`
BirthDate string `json:"birth_date"`
WeightKg float64 `json:"weight_kg"`
AvatarFileID *string `json:"avatar_file_id"`
Coat string `json:"coat"`
Allergies string `json:"allergies"`
CareNotes string `json:"care_notes"`
}
func (a *API) listPets(c *gin.Context) {
var pets []model.Pet
if err := a.db.Where("user_id = ? AND archived = false", actor(c)).Order("created_at ASC").Find(&pets).Error; err != nil {
fail(c, http.StatusInternalServerError, "读取宠物失败")
return
}
items := make([]gin.H, 0, len(pets))
for _, pet := range pets {
items = append(items, a.petView(pet))
}
ok(c, items)
}
func (a *API) getPet(c *gin.Context) {
pet, err := a.ownedPet(actor(c), c.Param("id"))
if err != nil {
fail(c, http.StatusNotFound, "宠物不存在")
return
}
ok(c, a.petView(pet))
}
func (a *API) createPet(c *gin.Context) {
var in petInput
if c.ShouldBindJSON(&in) != nil {
fail(c, http.StatusBadRequest, "宠物资料无效")
return
}
pet, valid := a.buildPet(c, in)
if !valid {
return
}
pet.UserID = actor(c)
if err := a.db.Create(&pet).Error; err != nil {
fail(c, http.StatusInternalServerError, "创建宠物失败")
return
}
created(c, a.petView(pet))
}
func (a *API) updatePet(c *gin.Context) {
pet, err := a.ownedPet(actor(c), c.Param("id"))
if err != nil {
fail(c, http.StatusNotFound, "宠物不存在")
return
}
var in petInput
if c.ShouldBindJSON(&in) != nil {
fail(c, http.StatusBadRequest, "宠物资料无效")
return
}
updated, valid := a.buildPet(c, in)
if !valid {
return
}
pet.Name, pet.Species, pet.Breed, pet.Gender = updated.Name, updated.Species, updated.Breed, updated.Gender
pet.BirthDate, pet.WeightKg, pet.AvatarFileID = updated.BirthDate, updated.WeightKg, updated.AvatarFileID
pet.Coat, pet.Allergies, pet.CareNotes = updated.Coat, updated.Allergies, updated.CareNotes
if err := a.db.Save(&pet).Error; err != nil {
fail(c, http.StatusInternalServerError, "更新宠物失败")
return
}
ok(c, a.petView(pet))
}
func (a *API) buildPet(c *gin.Context, in petInput) (model.Pet, bool) {
pet := model.Pet{}
in.Name = strings.TrimSpace(in.Name)
if in.Name == "" || len([]rune(in.Name)) > 64 || in.WeightKg < 0 || in.WeightKg > 200 {
fail(c, http.StatusBadRequest, "名字或体重无效")
return pet, false
}
if in.Species == "" {
in.Species = "dog"
}
if in.Species != "dog" && in.Species != "cat" && in.Species != "other" {
fail(c, http.StatusBadRequest, "宠物类型无效")
return pet, false
}
if in.AvatarFileID != nil {
var count int64
if !validID(*in.AvatarFileID) || a.db.Model(&model.File{}).Where("id = ?", *in.AvatarFileID).Count(&count).Error != nil || count == 0 {
fail(c, http.StatusBadRequest, "头像文件无效")
return pet, false
}
}
var birthday *time.Time
if in.BirthDate != "" {
d, err := parseDay(in.BirthDate)
if err != nil || d.After(time.Now()) {
fail(c, http.StatusBadRequest, "生日无效")
return pet, false
}
birthday = &d
}
pet = model.Pet{Name: in.Name, Species: in.Species, Breed: strings.TrimSpace(in.Breed),
Gender: in.Gender, BirthDate: birthday, WeightKg: in.WeightKg,
AvatarFileID: in.AvatarFileID, Coat: in.Coat, Allergies: in.Allergies, CareNotes: in.CareNotes}
return pet, true
}
func (a *API) archivePet(c *gin.Context) {
pet, err := a.ownedPet(actor(c), c.Param("id"))
if err != nil {
fail(c, http.StatusNotFound, "宠物不存在")
return
}
var active int64
if err := a.db.Table("sundynix_walk_pet AS wp").Joins("JOIN sundynix_walk AS w ON w.id = wp.walk_id").
Where("wp.pet_id = ? AND w.status IN ?", pet.ID, []string{"active", "paused"}).Count(&active).Error; err != nil {
fail(c, http.StatusInternalServerError, "检查遛狗状态失败")
return
}
if active > 0 {
fail(c, http.StatusConflict, "这只宠物正在遛狗,结束后再归档")
return
}
if err := a.db.Model(&model.Pet{}).Where("id = ? AND user_id = ?", pet.ID, actor(c)).Update("archived", true).Error; err != nil && err != gorm.ErrRecordNotFound {
fail(c, http.StatusInternalServerError, "归档失败")
return
}
ok(c, gin.H{"archived": true})
}