2025-06-30 19:21:56 +08:00
|
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-07-02 16:17:59 +08:00
|
|
|
|
"time"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
|
|
|
|
|
|
"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 {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
userService interfaces.UserService
|
|
|
|
|
|
smsCodeService *services.SMSCodeService
|
|
|
|
|
|
response interfaces.ResponseBuilder
|
|
|
|
|
|
validator interfaces.RequestValidator
|
|
|
|
|
|
logger *zap.Logger
|
|
|
|
|
|
jwtAuth *middleware.JWTAuthMiddleware
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewUserHandler 创建用户处理器
|
|
|
|
|
|
func NewUserHandler(
|
2025-07-02 16:17:59 +08:00
|
|
|
|
userService interfaces.UserService,
|
|
|
|
|
|
smsCodeService *services.SMSCodeService,
|
2025-06-30 19:21:56 +08:00
|
|
|
|
response interfaces.ResponseBuilder,
|
|
|
|
|
|
validator interfaces.RequestValidator,
|
|
|
|
|
|
logger *zap.Logger,
|
|
|
|
|
|
jwtAuth *middleware.JWTAuthMiddleware,
|
|
|
|
|
|
) *UserHandler {
|
|
|
|
|
|
return &UserHandler{
|
2025-07-02 16:17:59 +08:00
|
|
|
|
userService: userService,
|
|
|
|
|
|
smsCodeService: smsCodeService,
|
|
|
|
|
|
response: response,
|
|
|
|
|
|
validator: validator,
|
|
|
|
|
|
logger: logger,
|
|
|
|
|
|
jwtAuth: jwtAuth,
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 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
|
2025-06-30 19:21:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证请求体
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &req); err != nil {
|
|
|
|
|
|
return // 响应已在验证器中处理
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 获取客户端信息
|
|
|
|
|
|
clientIP := c.ClientIP()
|
|
|
|
|
|
userAgent := c.GetHeader("User-Agent")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 发送验证码
|
|
|
|
|
|
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())
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 构建响应
|
|
|
|
|
|
response := &dto.SendCodeResponse{
|
|
|
|
|
|
Message: "验证码发送成功",
|
|
|
|
|
|
ExpiresAt: time.Now().Add(5 * time.Minute), // 5分钟过期
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Success(c, response, "验证码发送成功")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 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
|
2025-06-30 19:21:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证请求体
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &req); err != nil {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
return // 响应已在验证器中处理
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 注册用户
|
|
|
|
|
|
user, err := h.userService.Register(c.Request.Context(), &req)
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if err != nil {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.logger.Error("注册用户失败", zap.Error(err))
|
2025-06-30 19:21:56 +08:00
|
|
|
|
h.response.BadRequest(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 返回响应
|
|
|
|
|
|
response := dto.FromEntity(user)
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Created(c, response, "用户注册成功")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 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
|
2025-06-30 19:21:56 +08:00
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 验证请求体
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &req); err != nil {
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 用户登录
|
|
|
|
|
|
user, err := h.userService.LoginWithPassword(c.Request.Context(), &req)
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if err != nil {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.logger.Error("密码登录失败", zap.Error(err))
|
|
|
|
|
|
h.response.Unauthorized(c, "用户名或密码错误")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 生成JWT token
|
|
|
|
|
|
accessToken, err := h.jwtAuth.GenerateToken(user.ID, user.Phone, user.Phone)
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if err != nil {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.logger.Error("生成令牌失败", zap.Error(err))
|
|
|
|
|
|
h.response.InternalError(c, "生成访问令牌失败")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 构建登录响应
|
|
|
|
|
|
loginResponse := &dto.LoginResponse{
|
|
|
|
|
|
User: dto.FromEntity(user),
|
|
|
|
|
|
AccessToken: accessToken,
|
|
|
|
|
|
TokenType: "Bearer",
|
|
|
|
|
|
ExpiresIn: 86400, // 24小时,从配置获取
|
|
|
|
|
|
LoginMethod: "password",
|
|
|
|
|
|
}
|
2025-06-30 19:21:56 +08:00
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Success(c, loginResponse, "登录成功")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 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
|
2025-06-30 19:21:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证请求体
|
|
|
|
|
|
if err := h.validator.BindAndValidate(c, &req); err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 用户登录
|
2025-07-02 16:17:59 +08:00
|
|
|
|
user, err := h.userService.LoginWithSMS(c.Request.Context(), &req)
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if err != nil {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.logger.Error("短信登录失败", zap.Error(err))
|
|
|
|
|
|
h.response.Unauthorized(c, err.Error())
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成JWT token
|
2025-07-02 16:17:59 +08:00
|
|
|
|
accessToken, err := h.jwtAuth.GenerateToken(user.ID, user.Phone, user.Phone)
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if err != nil {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.logger.Error("生成令牌失败", zap.Error(err))
|
|
|
|
|
|
h.response.InternalError(c, "生成访问令牌失败")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建登录响应
|
|
|
|
|
|
loginResponse := &dto.LoginResponse{
|
|
|
|
|
|
User: dto.FromEntity(user),
|
|
|
|
|
|
AccessToken: accessToken,
|
|
|
|
|
|
TokenType: "Bearer",
|
|
|
|
|
|
ExpiresIn: 86400, // 24小时,从配置获取
|
2025-07-02 16:17:59 +08:00
|
|
|
|
LoginMethod: "sms",
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Success(c, loginResponse, "登录成功")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProfile 获取当前用户信息
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// @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]
|
2025-06-30 19:21:56 +08:00
|
|
|
|
func (h *UserHandler) GetProfile(c *gin.Context) {
|
|
|
|
|
|
userID := h.getCurrentUserID(c)
|
|
|
|
|
|
if userID == "" {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Unauthorized(c, "用户未认证")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户信息
|
|
|
|
|
|
user, err := h.userService.GetByID(c.Request.Context(), userID)
|
|
|
|
|
|
if err != nil {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.logger.Error("获取用户资料失败", zap.Error(err))
|
|
|
|
|
|
h.response.NotFound(c, "用户不存在")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// 返回用户信息
|
2025-06-30 19:21:56 +08:00
|
|
|
|
response := dto.FromEntity(user)
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Success(c, response, "获取用户资料成功")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ChangePassword 修改密码
|
2025-07-02 16:17:59 +08:00
|
|
|
|
// @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]
|
2025-06-30 19:21:56 +08:00
|
|
|
|
func (h *UserHandler) ChangePassword(c *gin.Context) {
|
|
|
|
|
|
userID := h.getCurrentUserID(c)
|
|
|
|
|
|
if userID == "" {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Unauthorized(c, "用户未认证")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
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 {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.logger.Error("修改密码失败", zap.Error(err))
|
2025-06-30 19:21:56 +08:00
|
|
|
|
h.response.BadRequest(c, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 16:17:59 +08:00
|
|
|
|
h.response.Success(c, nil, "密码修改成功")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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 ""
|
|
|
|
|
|
}
|