package handler import ( "net/http" "github.com/gin-gonic/gin" "github.com/user-management-system/internal/service" ) // SMSHandler handles SMS requests type SMSHandler struct { authService *service.AuthService smsCodeService *service.SMSCodeService } // NewSMSHandler creates a new SMSHandler (stub, no SMS configured) func NewSMSHandler() *SMSHandler { return &SMSHandler{} } // NewSMSHandlerWithService creates a SMSHandler backed by real AuthService + SMSCodeService func NewSMSHandlerWithService(authService *service.AuthService, smsCodeService *service.SMSCodeService) *SMSHandler { return &SMSHandler{ authService: authService, smsCodeService: smsCodeService, } } // SendCode 发送短信验证码(用于注册/登录) func (h *SMSHandler) SendCode(c *gin.Context) { if h.smsCodeService == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"error": "SMS service not configured"}) return } var req service.SendCodeRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } resp, err := h.smsCodeService.SendCode(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, resp) } // LoginByCode 短信验证码登录(带设备信息以支持设备信任链路) func (h *SMSHandler) LoginByCode(c *gin.Context) { if h.authService == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"error": "SMS login not configured"}) return } var req struct { Phone string `json:"phone" binding:"required"` Code string `json:"code" binding:"required"` DeviceID string `json:"device_id"` DeviceName string `json:"device_name"` DeviceBrowser string `json:"device_browser"` DeviceOS string `json:"device_os"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } clientIP := c.ClientIP() resp, err := h.authService.LoginByCode(c.Request.Context(), req.Phone, req.Code, clientIP) if err != nil { handleError(c, err) return } // 自动注册/更新设备记录(不阻塞主流程) // 注意:必须用独立的 background context,不能用 c.Request.Context()(gin 回收后会取消) if req.DeviceID != "" && resp != nil && resp.User != nil { loginReq := &service.LoginRequest{ DeviceID: req.DeviceID, DeviceName: req.DeviceName, DeviceBrowser: req.DeviceBrowser, DeviceOS: req.DeviceOS, } userID := resp.User.ID go func() { devCtx, cancel := newBackgroundCtx(5) defer cancel() h.authService.BestEffortRegisterDevicePublic(devCtx, userID, loginReq) }() } c.JSON(http.StatusOK, resp) }