95 lines
2.8 KiB
Go
95 lines
2.8 KiB
Go
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.DeleteAccount()
|
||
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
|
||
}
|