package handlers import ( "strconv" "time" "yuyue/database" "yuyue/middleware" "yuyue/models" "yuyue/utils" "github.com/gofiber/fiber/v2" ) type TimeSlotRequest struct { Date string `json:"date" validate:"required"` StartTime string `json:"start_time" validate:"required"` EndTime string `json:"end_time" validate:"required"` MaxPeople int `json:"max_people" validate:"required,min=1"` IsActive bool `json:"is_active"` } func CreateTimeSlot(c *fiber.Ctx) error { // 检查是否为管理员 if middleware.GetUserRole(c) != models.RoleAdmin { return utils.Unauthorized(c, "需要管理员权限") } var req TimeSlotRequest if err := c.BodyParser(&req); err != nil { return utils.BadRequest(c, "请求数据格式错误") } // 解析日期时间 date, err := time.Parse("2006-01-02", req.Date) if err != nil { return utils.BadRequest(c, "日期格式错误,应为 YYYY-MM-DD") } startTime, err := time.Parse("15:04", req.StartTime) if err != nil { return utils.BadRequest(c, "开始时间格式错误,应为 HH:MM") } endTime, err := time.Parse("15:04", req.EndTime) if err != nil { return utils.BadRequest(c, "结束时间格式错误,应为 HH:MM") } // 组合完整的时间 fullStartTime := time.Date(date.Year(), date.Month(), date.Day(), startTime.Hour(), startTime.Minute(), 0, 0, date.Location()) fullEndTime := time.Date(date.Year(), date.Month(), date.Day(), endTime.Hour(), endTime.Minute(), 0, 0, date.Location()) // 检查时间合理性 if fullStartTime.After(fullEndTime) || fullStartTime.Equal(fullEndTime) { return utils.BadRequest(c, "开始时间必须早于结束时间") } timeSlot := models.TimeSlot{ Date: date, StartTime: fullStartTime, EndTime: fullEndTime, MaxPeople: req.MaxPeople, CurrentPeople: 0, IsActive: req.IsActive, } if err := database.GetDB().Create(&timeSlot).Error; err != nil { return utils.InternalServerError(c, "时间槽创建失败") } return utils.Success(c, timeSlot) } func GetTimeSlots(c *fiber.Ctx) error { var timeSlots []models.TimeSlot query := database.GetDB().Model(&models.TimeSlot{}) // 可选的查询参数 date := c.Query("date") if date != "" { query = query.Where("date = ? ", date) } else { // 2. 如果没传日期,才默认显示“今天及以后”的数据 today := time.Now() sevenDaysLater := today.AddDate(0, 0, 14) query = query.Where("date >= ? and date <= ?", today.Format("2006-01-02"), sevenDaysLater.Format("2006-01-02")) } //isActive := c.Query("is_active") //if isActive != "" { // active, err := strconv.ParseBool(isActive) // if err == nil { // query = query.Where("is_active = ?", active) // } //} //// //// 只显示未来的时段 now := time.Now().Format("2006-01-02") query = query.Where("date >= ? and is_active = true", now) // 按日期和时间排序 query = query.Order("date ASC, start_time ASC") if err := query.Find(&timeSlots).Error; err != nil { return utils.InternalServerError(c, "获取时间槽列表失败") } return utils.Success(c, timeSlots) } func GetTimeSlot(c *fiber.Ctx) error { id, err := strconv.ParseUint(c.Params("id"), 10, 32) if err != nil { return utils.BadRequest(c, "无效的时间槽ID") } var timeSlot models.TimeSlot if err := database.GetDB().First(&timeSlot, uint(id)).Error; err != nil { return utils.NotFound(c, "时间槽不存在") } return utils.Success(c, timeSlot) } func UpdateTimeSlot(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") } var req TimeSlotRequest if err := c.BodyParser(&req); err != nil { return utils.BadRequest(c, "请求数据格式错误") } var timeSlot models.TimeSlot if err := database.GetDB().First(&timeSlot, uint(id)).Error; err != nil { return utils.NotFound(c, "时间槽不存在") } // 解析日期时间 date, err := time.Parse("2006-01-02", req.Date) if err != nil { return utils.BadRequest(c, "日期格式错误,应为 YYYY-MM-DD") } startTime, err := time.Parse("15:04", req.StartTime) if err != nil { return utils.BadRequest(c, "开始时间格式错误,应为 HH:MM") } endTime, err := time.Parse("15:04", req.EndTime) if err != nil { return utils.BadRequest(c, "结束时间格式错误,应为 HH:MM") } // 组合完整的时间 fullStartTime := time.Date(date.Year(), date.Month(), date.Day(), startTime.Hour(), startTime.Minute(), 0, 0, date.Location()) fullEndTime := time.Date(date.Year(), date.Month(), date.Day(), endTime.Hour(), endTime.Minute(), 0, 0, date.Location()) // 检查时间合理性 if fullStartTime.After(fullEndTime) || fullStartTime.Equal(fullEndTime) { return utils.BadRequest(c, "开始时间必须早于结束时间") } // 检查是否会影响现有预约 if req.MaxPeople < timeSlot.CurrentPeople { return utils.BadRequest(c, "最大人数不能小于当前已预约人数") } timeSlot.Date = date timeSlot.StartTime = fullStartTime timeSlot.EndTime = fullEndTime timeSlot.MaxPeople = req.MaxPeople timeSlot.IsActive = req.IsActive if err := database.GetDB().Save(&timeSlot).Error; err != nil { return utils.InternalServerError(c, "更新时间槽失败") } return utils.Success(c, timeSlot) } func DeleteTimeSlot(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") } // 检查是否有关联的预约 var appointmentCount int64 database.GetDB().Model(&models.Appointment{}).Where("time_slot_id = ?", uint(id)).Count(&appointmentCount) if appointmentCount > 0 { return utils.BadRequest(c, "该时间槽已有预约,无法删除") } if err := database.GetDB().Delete(&models.TimeSlot{}, uint(id)).Error; err != nil { return utils.InternalServerError(c, "删除时间槽失败") } return utils.Success(c, fiber.Map{"message": "时间槽删除成功"}) }