package controllers import ( "hr_receiver/config" "hr_receiver/models" "hr_receiver/util" "net/http" "github.com/gin-gonic/gin" ) type LoginRequest struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` } type RegisterRequest struct { Username string `json:"username" form:"username"` Password string `json:"password" form:"password"` Email *string `json:"email" form:"email"` Phone *string `json:"phone" form:"phone"` Role models.UserRole `json:"role" form:"role"` FlavorType models.UserFlavorType `json:"flavorType" form:"flavorType"` RegionIDs []uint32 `json:"regionIds" form:"regionIds"` } type AuthResponse struct { Token string `json:"token"` User models.User `json:"user"` } // @Summary 用户注册 // @Description 注册新用户,返回JWT Token // @Tags 认证 // @Accept json // @Produce json // @Param request body SwagRegisterRequest true "注册信息" // @Success 201 {object} SwagAPIResponse "注册成功" // @Failure 400 {object} SwagAPIResponse "请求参数错误" // @Failure 409 {object} SwagAPIResponse "用户名已存在" // @Router /register [post] func Register(c *gin.Context) { var req RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 检查用户名是否已存在 var existingUser models.User if result := config.DB.Where("username = ?", req.Username).First(&existingUser); result.Error == nil { c.JSON(http.StatusConflict, gin.H{"error": "Username already exists"}) return } // 创建新用户 user := models.User{ Username: req.Username, Email: req.Email, Phone: req.Phone, Password: req.Password, // BeforeCreate钩子会自动加密 Role: req.Role, FlavorType: req.FlavorType, Regions: buildUserRegionBindings(req.RegionIDs), } if result := config.DB.Create(&user); result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"}) return } // 生成Token token, err := util.GenerateToken(&user) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"}) return } c.JSON(http.StatusCreated, AuthResponse{ Token: token, User: user, }) } // @Summary 用户登录 // @Description 用户名密码登录,返回JWT Token和用户信息 // @Tags 认证 // @Accept json // @Produce json // @Param request body SwagLoginRequest true "登录信息" // @Success 200 {object} SwagAPIResponse "登录成功" // @Failure 400 {object} SwagAPIResponse "请求参数错误" // @Failure 401 {object} SwagAPIResponse "用户名或密码错误" // @Failure 403 {object} SwagAPIResponse "用户已禁用" // @Router /login [post] func Login(c *gin.Context) { var req LoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 查找用户 var user models.User result := config.DB.Preload("Regions").Where("username = ?", req.Username).First(&user) if result.Error != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"}) return } // 验证密码 if !user.CheckPassword(req.Password) { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"}) return } if !user.IsActive { c.JSON(http.StatusForbidden, gin.H{"error": "User is disabled"}) return } // 生成JWT Token token, err := util.GenerateToken(&user) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"}) return } c.JSON(http.StatusOK, AuthResponse{ Token: token, User: user, }) } // GetProfile 获取用户信息(需要认证) func GetProfile(c *gin.Context) { userID, exists := c.Get("userID") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"}) return } var user models.User if result := config.DB.Preload("Regions").First(&user, userID); result.Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } c.JSON(http.StatusOK, user) } func buildUserRegionBindings(regionIDs []uint32) []models.UserRegionBinding { if len(regionIDs) == 0 { return nil } seen := make(map[uint32]struct{}, len(regionIDs)) regions := make([]models.UserRegionBinding, 0, len(regionIDs)) for _, regionID := range regionIDs { if regionID == 0 { continue } if _, exists := seen[regionID]; exists { continue } seen[regionID] = struct{}{} regions = append(regions, models.UserRegionBinding{RegionID: regionID}) } return regions }