241 lines
6.8 KiB
Go
241 lines
6.8 KiB
Go
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)
|
|
}
|