hi-server/internal/logic/public/user/verifyEmailLogic.go
shanshanzhong fde3210a88
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m41s
feat(用户): 实现邮箱绑定功能并返回登录凭证
修改绑定邮箱接口返回登录凭证,优化用户数据迁移流程
添加用户缓存清理逻辑,确保设备绑定后数据一致性
完善邮箱验证和绑定逻辑的注释和错误处理
2025-10-23 10:07:59 -07:00

101 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package user
import (
"context"
"encoding/json"
"fmt"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
// VerifyEmailLogic 邮箱验证逻辑结构体
// 用于处理用户邮箱验证码验证的业务逻辑
type VerifyEmailLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewVerifyEmailLogic 创建邮箱验证逻辑实例
// 用于初始化邮箱验证处理器
func NewVerifyEmailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VerifyEmailLogic {
return &VerifyEmailLogic{
Logger: logger.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// CacheKeyPayload Redis缓存中验证码的数据结构
// 用于存储验证码和最后发送时间
type CacheKeyPayload struct {
Code string `json:"code"` // 验证码
LastAt int64 `json:"lastAt"` // 最后发送时间戳
}
// VerifyEmail 验证邮箱验证码
// 该方法用于验证用户输入的邮箱验证码是否正确,并将邮箱标记为已验证状态
func (l *VerifyEmailLogic) VerifyEmail(req *types.VerifyEmailRequest) error {
// 构建Redis缓存键格式认证码缓存前缀:安全标识:邮箱地址
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, constant.Security, req.Email)
// 从Redis中获取验证码缓存数据
value, err := l.svcCtx.Redis.Get(l.ctx, cacheKey).Result()
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
// 解析缓存中的验证码数据
var payload CacheKeyPayload
err = json.Unmarshal([]byte(value), &payload)
if err != nil {
l.Errorw("Redis Error", logger.Field("error", err.Error()), logger.Field("cacheKey", cacheKey))
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
// 验证用户输入的验证码是否与缓存中的验证码匹配
if payload.Code != req.Code {
return errors.Wrapf(xerr.NewErrCode(xerr.VerifyCodeError), "code error")
}
// 验证成功后删除Redis中的验证码缓存一次性使用
l.svcCtx.Redis.Del(l.ctx, cacheKey)
// 从上下文中获取当前用户信息
u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
if !ok {
logger.Error("current user is not found in context")
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
}
// 根据邮箱地址查找用户的邮箱认证方式记录
method, err := l.svcCtx.UserModel.FindUserAuthMethodByOpenID(l.ctx, "email", req.Email)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindUserAuthMethodByOpenID error")
}
// 验证邮箱认证记录是否属于当前用户(安全检查)
if method.UserId != u.Id {
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "invalid access")
}
// 将邮箱标记为已验证状态
method.Verified = true
// 更新数据库中的认证方式记录
err = l.svcCtx.UserModel.UpdateUserAuthMethods(l.ctx, method)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "UpdateUserAuthMethods error")
}
return nil
}