feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
2026-04-02 11:19:50 +08:00
|
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2026-04-07 12:08:16 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/user-management-system/internal/service"
|
feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
2026-04-02 11:19:50 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// SMSHandler handles SMS requests
|
2026-04-07 12:08:16 +08:00
|
|
|
|
type SMSHandler struct {
|
|
|
|
|
|
authService *service.AuthService
|
|
|
|
|
|
smsCodeService *service.SMSCodeService
|
|
|
|
|
|
}
|
feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
2026-04-02 11:19:50 +08:00
|
|
|
|
|
2026-04-11 22:49:13 +08:00
|
|
|
|
// SMSLoginRequest 短信登录请求
|
|
|
|
|
|
type SMSLoginRequest 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"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
test: add comprehensive test coverage and improve code quality
- Add new test files for auth, service, and handler modules
- Improve test organization and coverage
- Refactor code for better maintainability
- Add captcha, settings, stats, and theme handler tests
- Add auth module tests (CAS, OAuth, password, SSO, state)
- Add service layer tests for auth, export, permissions, roles
- All Go tests pass (exit code 0)
- All frontend tests pass (325 tests in 59 files)
2026-04-17 20:43:50 +08:00
|
|
|
|
// NewSMSHandler creates a SMSHandler backed by AuthService + SMSCodeService.
|
|
|
|
|
|
// If both services are nil, the handler will return 503 for all requests.
|
|
|
|
|
|
func NewSMSHandler(authService *service.AuthService, smsCodeService *service.SMSCodeService) *SMSHandler {
|
2026-04-07 12:08:16 +08:00
|
|
|
|
return &SMSHandler{
|
|
|
|
|
|
authService: authService,
|
|
|
|
|
|
smsCodeService: smsCodeService,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-11 22:49:13 +08:00
|
|
|
|
// SendCode 发送短信验证码
|
|
|
|
|
|
// @Summary 发送短信验证码
|
|
|
|
|
|
// @Description 向指定手机号发送短信验证码(用于注册或登录)
|
|
|
|
|
|
// @Tags 短信验证
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body service.SendCodeRequest true "发送验证码请求"
|
|
|
|
|
|
// @Success 200 {object} Response "发送成功"
|
|
|
|
|
|
// @Failure 400 {object} Response "请求参数错误"
|
|
|
|
|
|
// @Failure 503 {object} Response "短信服务未配置"
|
|
|
|
|
|
// @Router /api/v1/sms/send [post]
|
feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
2026-04-02 11:19:50 +08:00
|
|
|
|
func (h *SMSHandler) SendCode(c *gin.Context) {
|
2026-04-07 12:08:16 +08:00
|
|
|
|
if h.smsCodeService == nil {
|
fix: unify handler response format in multiple handlers
- captcha_handler.go: Fix GenerateCaptcha/VerifyCaptcha to use {code, message, data}
- password_reset_handler.go: Fix all error responses to use {code, message}
- settings_handler.go: Add missing "code" and "message" fields
- sms_handler.go: Fix error responses to use {code, message}
- sso_handler.go: Fix all error responses to use {code, message, data}
- stats_handler.go: Add missing "message" field in success responses
- theme_handler.go: Fix error responses to use {code, message}
- totp_handler.go: Fix all responses to use {code, message, data}
Standardize all JSON responses to {code: 0, message: "success", data: ...} for success
and {code: XXX, message: "..."} for errors.
2026-04-11 13:06:58 +08:00
|
|
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{"code": 503, "message": "SMS service not configured"})
|
2026-04-07 12:08:16 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var req service.SendCodeRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
fix: unify handler response format in multiple handlers
- captcha_handler.go: Fix GenerateCaptcha/VerifyCaptcha to use {code, message, data}
- password_reset_handler.go: Fix all error responses to use {code, message}
- settings_handler.go: Add missing "code" and "message" fields
- sms_handler.go: Fix error responses to use {code, message}
- sso_handler.go: Fix all error responses to use {code, message, data}
- stats_handler.go: Add missing "message" field in success responses
- theme_handler.go: Fix error responses to use {code, message}
- totp_handler.go: Fix all responses to use {code, message, data}
Standardize all JSON responses to {code: 0, message: "success", data: ...} for success
and {code: XXX, message: "..."} for errors.
2026-04-11 13:06:58 +08:00
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()})
|
2026-04-07 12:08:16 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resp, err := h.smsCodeService.SendCode(c.Request.Context(), &req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
handleError(c, err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-08 20:06:54 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": resp,
|
|
|
|
|
|
})
|
feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
2026-04-02 11:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-11 22:49:13 +08:00
|
|
|
|
// LoginByCode 短信验证码登录
|
|
|
|
|
|
// @Summary 短信验证码登录
|
|
|
|
|
|
// @Description 使用手机号和短信验证码登录(带设备信息以支持设备信任链路)
|
|
|
|
|
|
// @Tags 短信验证
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body SMSLoginRequest true "登录请求"
|
|
|
|
|
|
// @Success 200 {object} Response "登录成功"
|
|
|
|
|
|
// @Failure 400 {object} Response "请求参数错误"
|
|
|
|
|
|
// @Failure 401 {object} Response "验证码错误"
|
|
|
|
|
|
// @Failure 503 {object} Response "短信登录未配置"
|
|
|
|
|
|
// @Router /api/v1/sms/login [post]
|
feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
2026-04-02 11:19:50 +08:00
|
|
|
|
func (h *SMSHandler) LoginByCode(c *gin.Context) {
|
2026-04-07 12:08:16 +08:00
|
|
|
|
if h.authService == nil {
|
fix: unify handler response format in multiple handlers
- captcha_handler.go: Fix GenerateCaptcha/VerifyCaptcha to use {code, message, data}
- password_reset_handler.go: Fix all error responses to use {code, message}
- settings_handler.go: Add missing "code" and "message" fields
- sms_handler.go: Fix error responses to use {code, message}
- sso_handler.go: Fix all error responses to use {code, message, data}
- stats_handler.go: Add missing "message" field in success responses
- theme_handler.go: Fix error responses to use {code, message}
- totp_handler.go: Fix all responses to use {code, message, data}
Standardize all JSON responses to {code: 0, message: "success", data: ...} for success
and {code: XXX, message: "..."} for errors.
2026-04-11 13:06:58 +08:00
|
|
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{"code": 503, "message": "SMS login not configured"})
|
2026-04-07 12:08:16 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-11 22:49:13 +08:00
|
|
|
|
var req SMSLoginRequest
|
2026-04-07 12:08:16 +08:00
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
fix: unify handler response format in multiple handlers
- captcha_handler.go: Fix GenerateCaptcha/VerifyCaptcha to use {code, message, data}
- password_reset_handler.go: Fix all error responses to use {code, message}
- settings_handler.go: Add missing "code" and "message" fields
- sms_handler.go: Fix error responses to use {code, message}
- sso_handler.go: Fix all error responses to use {code, message, data}
- stats_handler.go: Add missing "message" field in success responses
- theme_handler.go: Fix error responses to use {code, message}
- totp_handler.go: Fix all responses to use {code, message, data}
Standardize all JSON responses to {code: 0, message: "success", data: ...} for success
and {code: XXX, message: "..."} for errors.
2026-04-11 13:06:58 +08:00
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()})
|
2026-04-07 12:08:16 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}()
|
|
|
|
|
|
}
|
2026-04-23 07:14:12 +08:00
|
|
|
|
setSessionCookies(c, h.authService, resp.RefreshToken)
|
2026-04-07 12:08:16 +08:00
|
|
|
|
|
2026-04-08 20:06:54 +08:00
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": resp,
|
|
|
|
|
|
})
|
feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
2026-04-02 11:19:50 +08:00
|
|
|
|
}
|