package handlers import ( "time" "github.com/gin-gonic/gin" "go.uber.org/zap" "tyapi-server/internal/domains/user/dto" "tyapi-server/internal/domains/user/services" "tyapi-server/internal/shared/interfaces" "tyapi-server/internal/shared/middleware" ) // UserHandler 用户HTTP处理器 type UserHandler struct { userService interfaces.UserService smsCodeService *services.SMSCodeService response interfaces.ResponseBuilder validator interfaces.RequestValidator logger *zap.Logger jwtAuth *middleware.JWTAuthMiddleware } // NewUserHandler 创建用户处理器 func NewUserHandler( userService interfaces.UserService, smsCodeService *services.SMSCodeService, response interfaces.ResponseBuilder, validator interfaces.RequestValidator, logger *zap.Logger, jwtAuth *middleware.JWTAuthMiddleware, ) *UserHandler { return &UserHandler{ userService: userService, smsCodeService: smsCodeService, response: response, validator: validator, logger: logger, jwtAuth: jwtAuth, } } // SendCode 发送验证码 // @Summary 发送短信验证码 // @Description 向指定手机号发送验证码,支持注册、登录、修改密码等场景 // @Tags 用户认证 // @Accept json // @Produce json // @Param request body dto.SendCodeRequest true "发送验证码请求" // @Success 200 {object} dto.SendCodeResponse "验证码发送成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 429 {object} map[string]interface{} "请求频率限制" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /users/send-code [post] func (h *UserHandler) SendCode(c *gin.Context) { var req dto.SendCodeRequest // 验证请求体 if err := h.validator.BindAndValidate(c, &req); err != nil { return // 响应已在验证器中处理 } // 获取客户端信息 clientIP := c.ClientIP() userAgent := c.GetHeader("User-Agent") // 发送验证码 if err := h.smsCodeService.SendCode(c.Request.Context(), req.Phone, req.Scene, clientIP, userAgent); err != nil { h.logger.Error("发送验证码失败", zap.String("phone", req.Phone), zap.String("scene", string(req.Scene)), zap.Error(err)) h.response.BadRequest(c, err.Error()) return } // 构建响应 response := &dto.SendCodeResponse{ Message: "验证码发送成功", ExpiresAt: time.Now().Add(5 * time.Minute), // 5分钟过期 } h.response.Success(c, response, "验证码发送成功") } // Register 用户注册 // @Summary 用户注册 // @Description 使用手机号、密码和验证码进行用户注册,需要确认密码 // @Tags 用户认证 // @Accept json // @Produce json // @Param request body dto.RegisterRequest true "用户注册请求" // @Success 201 {object} dto.UserResponse "注册成功" // @Failure 400 {object} map[string]interface{} "请求参数错误或验证码无效" // @Failure 409 {object} map[string]interface{} "手机号已存在" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /users/register [post] func (h *UserHandler) Register(c *gin.Context) { var req dto.RegisterRequest // 验证请求体 if err := h.validator.BindAndValidate(c, &req); err != nil { return // 响应已在验证器中处理 } // 注册用户 user, err := h.userService.Register(c.Request.Context(), &req) if err != nil { h.logger.Error("注册用户失败", zap.Error(err)) h.response.BadRequest(c, err.Error()) return } // 返回响应 response := dto.FromEntity(user) h.response.Created(c, response, "用户注册成功") } // LoginWithPassword 密码登录 // @Summary 用户密码登录 // @Description 使用手机号和密码进行用户登录,返回JWT令牌 // @Tags 用户认证 // @Accept json // @Produce json // @Param request body dto.LoginWithPasswordRequest true "密码登录请求" // @Success 200 {object} dto.LoginResponse "登录成功" // @Failure 400 {object} map[string]interface{} "请求参数错误" // @Failure 401 {object} map[string]interface{} "认证失败" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /users/login-password [post] func (h *UserHandler) LoginWithPassword(c *gin.Context) { var req dto.LoginWithPasswordRequest // 验证请求体 if err := h.validator.BindAndValidate(c, &req); err != nil { return } // 用户登录 user, err := h.userService.LoginWithPassword(c.Request.Context(), &req) if err != nil { h.logger.Error("密码登录失败", zap.Error(err)) h.response.Unauthorized(c, "用户名或密码错误") return } // 生成JWT token accessToken, err := h.jwtAuth.GenerateToken(user.ID, user.Phone, user.Phone) if err != nil { h.logger.Error("生成令牌失败", zap.Error(err)) h.response.InternalError(c, "生成访问令牌失败") return } // 构建登录响应 loginResponse := &dto.LoginResponse{ User: dto.FromEntity(user), AccessToken: accessToken, TokenType: "Bearer", ExpiresIn: 86400, // 24小时,从配置获取 LoginMethod: "password", } h.response.Success(c, loginResponse, "登录成功") } // LoginWithSMS 短信验证码登录 // @Summary 用户短信验证码登录 // @Description 使用手机号和短信验证码进行用户登录,返回JWT令牌 // @Tags 用户认证 // @Accept json // @Produce json // @Param request body dto.LoginWithSMSRequest true "短信登录请求" // @Success 200 {object} dto.LoginResponse "登录成功" // @Failure 400 {object} map[string]interface{} "请求参数错误或验证码无效" // @Failure 401 {object} map[string]interface{} "认证失败" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /users/login-sms [post] func (h *UserHandler) LoginWithSMS(c *gin.Context) { var req dto.LoginWithSMSRequest // 验证请求体 if err := h.validator.BindAndValidate(c, &req); err != nil { return } // 用户登录 user, err := h.userService.LoginWithSMS(c.Request.Context(), &req) if err != nil { h.logger.Error("短信登录失败", zap.Error(err)) h.response.Unauthorized(c, err.Error()) return } // 生成JWT token accessToken, err := h.jwtAuth.GenerateToken(user.ID, user.Phone, user.Phone) if err != nil { h.logger.Error("生成令牌失败", zap.Error(err)) h.response.InternalError(c, "生成访问令牌失败") return } // 构建登录响应 loginResponse := &dto.LoginResponse{ User: dto.FromEntity(user), AccessToken: accessToken, TokenType: "Bearer", ExpiresIn: 86400, // 24小时,从配置获取 LoginMethod: "sms", } h.response.Success(c, loginResponse, "登录成功") } // GetProfile 获取当前用户信息 // @Summary 获取当前用户信息 // @Description 根据JWT令牌获取当前登录用户的详细信息 // @Tags 用户管理 // @Accept json // @Produce json // @Security Bearer // @Success 200 {object} dto.UserResponse "用户信息" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 404 {object} map[string]interface{} "用户不存在" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /users/me [get] func (h *UserHandler) GetProfile(c *gin.Context) { userID := h.getCurrentUserID(c) if userID == "" { h.response.Unauthorized(c, "用户未认证") return } // 获取用户信息 user, err := h.userService.GetByID(c.Request.Context(), userID) if err != nil { h.logger.Error("获取用户资料失败", zap.Error(err)) h.response.NotFound(c, "用户不存在") return } // 返回用户信息 response := dto.FromEntity(user) h.response.Success(c, response, "获取用户资料成功") } // ChangePassword 修改密码 // @Summary 修改密码 // @Description 使用旧密码、新密码确认和验证码修改当前用户的密码 // @Tags 用户管理 // @Accept json // @Produce json // @Security Bearer // @Param request body dto.ChangePasswordRequest true "修改密码请求" // @Success 200 {object} map[string]interface{} "密码修改成功" // @Failure 400 {object} map[string]interface{} "请求参数错误或验证码无效" // @Failure 401 {object} map[string]interface{} "未认证" // @Failure 500 {object} map[string]interface{} "服务器内部错误" // @Router /users/me/password [put] func (h *UserHandler) ChangePassword(c *gin.Context) { userID := h.getCurrentUserID(c) if userID == "" { h.response.Unauthorized(c, "用户未认证") return } var req dto.ChangePasswordRequest // 验证请求体 if err := h.validator.BindAndValidate(c, &req); err != nil { return } // 修改密码 if err := h.userService.ChangePassword(c.Request.Context(), userID, &req); err != nil { h.logger.Error("修改密码失败", zap.Error(err)) h.response.BadRequest(c, err.Error()) return } h.response.Success(c, nil, "密码修改成功") } // getCurrentUserID 获取当前用户ID func (h *UserHandler) getCurrentUserID(c *gin.Context) string { if userID, exists := c.Get("user_id"); exists { if id, ok := userID.(string); ok { return id } } return "" }