package user import ( "context" "encoding/json" "fmt" "net/http" "strings" "time" "github.com/gin-gonic/gin" "github.com/perfect-panel/server/internal/config" "github.com/perfect-panel/server/internal/logic/public/user" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/pkg/constant" "github.com/perfect-panel/server/pkg/result" ) // DeleteAccountHandler 注销账号处理器 // 根据当前token删除所有关联设备,然后根据各自设备ID重新新建账号 // 新增:需携带邮箱验证码,验证通过后执行注销 type deleteAccountReq struct { Email string `json:"email" binding:"required,email"` // 用户邮箱 Code string `json:"code" binding:"required"` // 邮箱验证码 } func DeleteAccountHandler(serverCtx *svc.ServiceContext) gin.HandlerFunc { return func(c *gin.Context) { var req deleteAccountReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()}) return } // 统一处理邮箱格式:转小写并去空格,与发送验证码逻辑保持一致 req.Email = strings.ToLower(strings.TrimSpace(req.Email)) // 校验邮箱验证码 if err := verifyEmailCode(c.Request.Context(), serverCtx, req.Email, req.Code); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } l := user.NewDeleteAccountLogic(c.Request.Context(), serverCtx) resp, err := l.DeleteAccountAll() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } result.HttpResult(c, resp, err) } } // CacheKeyPayload 验证码缓存结构 type CacheKeyPayload struct { Code string `json:"code"` LastAt int64 `json:"lastAt"` } // verifyEmailCode 校验邮箱验证码 // 支持 DeleteAccount 和 Security 两种场景的验证码 func verifyEmailCode(ctx context.Context, serverCtx *svc.ServiceContext, email string, code string) error { // 尝试多种场景的验证码 scenes := []string{constant.DeleteAccount.String(), constant.Security.String()} var verified bool var cacheKeyUsed string var payload CacheKeyPayload for _, scene := range scenes { cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, scene, email) value, err := serverCtx.Redis.Get(ctx, cacheKey).Result() if err != nil || value == "" { continue } if err := json.Unmarshal([]byte(value), &payload); err != nil { continue } // 检查验证码是否匹配且未过期 if payload.Code == code && time.Now().Unix()-payload.LastAt <= serverCtx.Config.VerifyCode.ExpireTime { verified = true cacheKeyUsed = cacheKey break } } if !verified { return fmt.Errorf("verification code error or expired") } // 验证成功后删除缓存 serverCtx.Redis.Del(ctx, cacheKeyUsed) return nil }