package handlers import ( "strconv" "time" "yuyue/database" "yuyue/middleware" "yuyue/models" "yuyue/utils" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) type AppointmentRequest struct { TimeSlotID uint `json:"time_slot_id" validate:"required"` PeopleCount int `json:"people_count" validate:"required,min=1"` Notes string `json:"notes"` } type AppointmentQuery struct { Status string `query:"status"` Date string `query:"date"` } func CreateAppointment(c *fiber.Ctx) error { userID := middleware.GetUserID(c) var req AppointmentRequest if err := c.BodyParser(&req); err != nil { return utils.BadRequest(c, "请求数据格式错误") } // 获取时间槽信息 var timeSlot models.TimeSlot if err := database.GetDB().First(&timeSlot, req.TimeSlotID).Error; err != nil { return utils.NotFound(c, "时间槽不存在") } // 检查时间槽是否激活 if !timeSlot.IsActive { return utils.BadRequest(c, "该时间段暂不开放预约") } // 检查时间槽是否已过期 now := time.Now() if timeSlot.Date.Before(now.Truncate(24 * time.Hour)) { return utils.BadRequest(c, "不能预约过去的时间") } // 检查人数限制 if req.PeopleCount > timeSlot.MaxPeople { return utils.BadRequest(c, "预约人数超过该时间段的最大限制") } // 检查剩余容量 if timeSlot.CurrentPeople+req.PeopleCount > timeSlot.MaxPeople { remaining := timeSlot.MaxPeople - timeSlot.CurrentPeople return utils.BadRequest(c, "该时间段剩余容量不足,最多还可预约"+strconv.Itoa(remaining)+"人") } // 检查用户是否已在此时间段预约 var existingAppointment models.Appointment err := database.GetDB().Where("user_id = ? AND time_slot_id = ? AND status != ?", userID, req.TimeSlotID, models.AppointmentCancelled).First(&existingAppointment).Error if err == nil { return utils.BadRequest(c, "您已在此时间段有预约") } appointment := models.Appointment{ UserID: userID, TimeSlotID: req.TimeSlotID, PeopleCount: req.PeopleCount, Status: models.AppointmentPending, Notes: req.Notes, } if err := database.GetDB().Create(&appointment).Error; err != nil { return utils.InternalServerError(c, "创建预约失败") } // 更新时间槽的当前人数 timeSlot.CurrentPeople += req.PeopleCount if err := database.GetDB().Save(&timeSlot).Error; err != nil { // 如果更新失败,回滚预约 database.GetDB().Delete(&appointment) return utils.InternalServerError(c, "更新时间槽人数失败") } // 预加载关联数据 database.GetDB().Preload("User").Preload("TimeSlot").First(&appointment, appointment.ID) return utils.Success(c, appointment) } func GetAppointments(c *fiber.Ctx) error { userID := middleware.GetUserID(c) role := middleware.GetUserRole(c) var appointments []models.Appointment query := database.GetDB().Model(&models.Appointment{}) // 管理员可以看到所有预约,普通用户只能看到自己的预约 if role != models.RoleAdmin { query = query.Where("user_id = ?", userID) } // 可选的查询参数 status := c.Query("status") if status != "" { query = query.Where("status = ?", status) } date := c.Query("date") if date != "" { query = query.Joins("JOIN time_slots ON appointments.time_slot_id = time_slots.id"). Where("time_slots.date = ?", date) } // 按创建时间降序排列 query = query.Order("created_at DESC") if err := query.Preload("User").Preload("TimeSlot").Find(&appointments).Error; err != nil { return utils.InternalServerError(c, "获取预约列表失败") } return utils.Success(c, appointments) } func GetAppointment(c *fiber.Ctx) error { userID := middleware.GetUserID(c) role := middleware.GetUserRole(c) id, err := strconv.ParseUint(c.Params("id"), 10, 32) if err != nil { return utils.BadRequest(c, "无效的预约ID") } var appointment models.Appointment query := database.GetDB().Preload("User").Preload("TimeSlot") // 管理员可以查看所有预约,普通用户只能查看自己的预约 if role != models.RoleAdmin { query = query.Where("id = ? AND user_id = ?", uint(id), userID) } else { query = query.Where("id = ?", uint(id)) } if err := query.First(&appointment).Error; err != nil { return utils.NotFound(c, "预约不存在") } return utils.Success(c, appointment) } func UpdateAppointmentStatus(c *fiber.Ctx) error { // 检查是否为管理员 if middleware.GetUserRole(c) != models.RoleAdmin { return utils.Unauthorized(c, "需要管理员权限") } id, err := strconv.ParseUint(c.Params("id"), 10, 32) if err != nil { return utils.BadRequest(c, "无效的预约ID") } type StatusUpdate struct { Status string `json:"status" validate:"required,oneof=pending confirmed cancelled completed"` } var req StatusUpdate if err := c.BodyParser(&req); err != nil { return utils.BadRequest(c, "请求数据格式错误") } var appointment models.Appointment if err := database.GetDB().Preload("TimeSlot").First(&appointment, uint(id)).Error; err != nil { return utils.NotFound(c, "预约不存在") } oldStatus := appointment.Status appointment.Status = req.Status if err := database.GetDB().Save(&appointment).Error; err != nil { return utils.InternalServerError(c, "更新预约状态失败") } // 如果是从已确认状态变为取消状态,需要释放人数 if oldStatus == models.AppointmentConfirmed && req.Status == models.AppointmentCancelled { timeSlot := appointment.TimeSlot timeSlot.CurrentPeople -= appointment.PeopleCount database.GetDB().Save(&timeSlot) } // 预加载关联数据 database.GetDB().Preload("User").Preload("TimeSlot").First(&appointment, appointment.ID) return utils.Success(c, appointment) } func CancelAppointment(c *fiber.Ctx) error { userID := middleware.GetUserID(c) id, err := strconv.ParseUint(c.Params("id"), 10, 32) if err != nil { return utils.BadRequest(c, "无效的预约ID") } var appointment models.Appointment if err := database.GetDB().Preload("TimeSlot").Where("id = ? AND user_id = ?", uint(id), userID).First(&appointment).Error; err != nil { return utils.NotFound(c, "预约不存在或无权取消") } // 检查预约状态 if appointment.Status == models.AppointmentCancelled || appointment.Status == models.AppointmentCompleted { return utils.BadRequest(c, "无法取消已完成或已取消的预约") } appointment.Status = models.AppointmentCancelled // 释放时间槽的人数 timeSlot := appointment.TimeSlot timeSlot.CurrentPeople -= appointment.PeopleCount if err := database.GetDB().Transaction(func(tx *gorm.DB) error { if err := tx.Save(&appointment).Error; err != nil { return err } if err := tx.Save(&timeSlot).Error; err != nil { return err } return nil }); err != nil { return utils.InternalServerError(c, "取消预约失败") } return utils.Success(c, appointment) }