package handler import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/user-management-system/internal/auth" "github.com/user-management-system/internal/domain" "github.com/user-management-system/internal/service" ) // UserHandler handles user management requests type UserHandler struct { userService *service.UserService } // NewUserHandler creates a new UserHandler func NewUserHandler(userService *service.UserService) *UserHandler { return &UserHandler{userService: userService} } func (h *UserHandler) CreateUser(c *gin.Context) { var req struct { Username string `json:"username" binding:"required"` Email string `json:"email"` Password string `json:"password"` Nickname string `json:"nickname"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } user := &domain.User{ Username: req.Username, Email: domain.StrPtr(req.Email), Nickname: req.Nickname, Status: domain.UserStatusActive, } if req.Password != "" { hashed, err := auth.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to hash password"}) return } user.Password = hashed } if err := h.userService.Create(c.Request.Context(), user); err != nil { handleError(c, err) return } c.JSON(http.StatusCreated, gin.H{ "code": 0, "message": "success", "data": toUserResponse(user), }) } func (h *UserHandler) ListUsers(c *gin.Context) { cursor := c.Query("cursor") sizeStr := c.DefaultQuery("size", "") // Use cursor-based pagination when cursor is provided if cursor != "" || sizeStr != "" { var req service.ListCursorRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } result, err := h.userService.ListCursor(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": result, }) return } // Fallback to legacy offset-based pagination offset, _ := strconv.ParseInt(c.DefaultQuery("offset", "0"), 10, 64) limit, _ := strconv.ParseInt(c.DefaultQuery("limit", "20"), 10, 64) users, total, err := h.userService.List(c.Request.Context(), int(offset), int(limit)) if err != nil { handleError(c, err) return } userResponses := make([]*UserResponse, len(users)) for i, u := range users { userResponses[i] = toUserResponse(u) } c.JSON(http.StatusOK, gin.H{ "users": userResponses, "total": total, "offset": offset, "limit": limit, }) } func (h *UserHandler) GetUser(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } user, err := h.userService.GetByID(c.Request.Context(), id) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, toUserResponse(user)) } func (h *UserHandler) UpdateUser(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } var req struct { Email *string `json:"email"` Nickname *string `json:"nickname"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } user, err := h.userService.GetByID(c.Request.Context(), id) if err != nil { handleError(c, err) return } if req.Email != nil { user.Email = req.Email } if req.Nickname != nil { user.Nickname = *req.Nickname } if err := h.userService.Update(c.Request.Context(), user); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, toUserResponse(user)) } func (h *UserHandler) DeleteUser(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } if err := h.userService.Delete(c.Request.Context(), id); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"message": "user deleted"}) } func (h *UserHandler) UpdatePassword(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } var req struct { OldPassword string `json:"old_password" binding:"required"` NewPassword string `json:"new_password" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.userService.ChangePassword(c.Request.Context(), id, req.OldPassword, req.NewPassword); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"message": "密码修改成功"}) } func (h *UserHandler) UpdateUserStatus(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"}) return } var req struct { Status string `json:"status" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var status domain.UserStatus switch req.Status { case "active", "1": status = domain.UserStatusActive case "inactive", "0": status = domain.UserStatusInactive case "locked", "2": status = domain.UserStatusLocked case "disabled", "3": status = domain.UserStatusDisabled default: c.JSON(http.StatusBadRequest, gin.H{"error": "invalid status"}) return } if err := h.userService.UpdateStatus(c.Request.Context(), id, status); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"message": "status updated"}) } func (h *UserHandler) GetUserRoles(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } roles, err := h.userService.GetUserRoles(c.Request.Context(), id) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": roles, }) } func (h *UserHandler) AssignRoles(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } var req struct { RoleIDs []int64 `json:"role_ids" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } if err := h.userService.AssignRoles(c.Request.Context(), id, req.RoleIDs); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "角色分配成功"}) } func (h *UserHandler) BatchUpdateStatus(c *gin.Context) { var req service.BatchUpdateStatusRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } count, err := h.userService.BatchUpdateStatus(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "批量更新失败"}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "更新成功", "data": gin.H{"count": count}}) } func (h *UserHandler) BatchDelete(c *gin.Context) { var req service.BatchDeleteRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } count, err := h.userService.BatchDelete(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "批量删除失败"}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "删除成功", "data": gin.H{"count": count}}) } func (h *UserHandler) UploadAvatar(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "avatar upload not implemented"}) } func (h *UserHandler) ListAdmins(c *gin.Context) { admins, err := h.userService.ListAdmins(c.Request.Context()) if err != nil { handleError(c, err) return } adminResponses := make([]*UserResponse, len(admins)) for i, u := range admins { adminResponses[i] = toUserResponse(u) } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success", "data": adminResponses}) } func (h *UserHandler) CreateAdmin(c *gin.Context) { var req struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` Email string `json:"email"` Nickname string `json:"nickname"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } adminReq := &service.CreateAdminRequest{ Username: req.Username, Password: req.Password, Email: req.Email, Nickname: req.Nickname, } admin, err := h.userService.CreateAdmin(c.Request.Context(), adminReq) if err != nil { handleError(c, err) return } c.JSON(http.StatusCreated, gin.H{"code": 0, "message": "管理员创建成功", "data": toUserResponse(admin)}) } func (h *UserHandler) DeleteAdmin(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } if err := h.userService.DeleteAdmin(c.Request.Context(), id); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "管理员已移除"}) } type UserResponse struct { ID int64 `json:"id"` Username string `json:"username"` Email string `json:"email,omitempty"` Nickname string `json:"nickname,omitempty"` Status string `json:"status"` } func toUserResponse(u *domain.User) *UserResponse { email := "" if u.Email != nil { email = *u.Email } return &UserResponse{ ID: u.ID, Username: u.Username, Email: email, Nickname: u.Nickname, Status: strconv.FormatInt(int64(u.Status), 10), } }