package auth import ( "backend/internal/domain" "backend/internal/utils" errs "backend/pkg/library/errors" "backend/pkg/member/domain/member" "backend/pkg/member/domain/usecase" "context" "fmt" "backend/internal/svc" "backend/internal/types" "github.com/zeromicro/go-zero/core/logx" ) type RequestPasswordResetLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewRequestPasswordResetLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RequestPasswordResetLogic { return &RequestPasswordResetLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // RequestPasswordReset 請求發送密碼重設驗證碼 aka 忘記密碼 func (l *RequestPasswordResetLogic) RequestPasswordReset(req *types.RequestPasswordResetReq) (resp *types.RespOK, err error) { // 驗證並標準化帳號 acc, err := l.validateAndNormalizeAccount(req.AccountType, req.Identifier) if err != nil { return nil, err } // 檢查發送冷卻時間 rk := domain.GenerateVerifyCodeRedisKey.With(fmt.Sprintf("%s:%d", acc, member.GenerateCodeTypeForgetPassword)).ToString() if err := l.checkVerifyCodeCooldown(rk); err != nil { return nil, err } // 確認帳號是否註冊並檢查平台限制 if err := l.checkAccountAndPlatform(acc); err != nil { return nil, err } // 生成驗證碼 vcode, err := l.svcCtx.AccountUC.GenerateRefreshCode(l.ctx, usecase.GenerateRefreshCodeRequest{ LoginID: acc, CodeType: member.GenerateCodeTypeForgetPassword, }) if err != nil { return nil, err } // 獲取用戶資訊並確認綁定帳號 account, err := l.svcCtx.AccountUC.GetUIDByAccount(l.ctx, usecase.GetUIDByAccountRequest{Account: acc}) if err != nil { return nil, errs.ResNotFoundError(fmt.Sprintf("account not found:%s", acc)) } info, err := l.svcCtx.AccountUC.GetUserInfo(l.ctx, usecase.GetUserInfoRequest{UID: account.UID}) if err != nil { return nil, err } // 發送驗證碼 fmt.Println("======= send", vcode.Data.VerifyCode, &info) //nickname := getEmailShowName(&info) //if err := l.sendVerificationCode(req.AccountType, acc, &info, vcode.Data.VerifyCode, nickname); err != nil { // return nil, err //} // 設置 Redis 鍵 l.setRedisKeyWithExpiry(rk, vcode.Data.VerifyCode, 60) return &types.RespOK{}, nil } // validateAndNormalizeAccount 驗證並標準化帳號 func (l *RequestPasswordResetLogic) validateAndNormalizeAccount(accountType, account string) (string, error) { switch member.GetAccountTypeByCode(accountType) { case member.AccountTypePhone: phone, isPhone := utils.NormalizeTaiwanMobile(account) if !isPhone { return "", errs.InputInvalidFormatError("phone number is invalid") } return phone, nil case member.AccountTypeMail: if !utils.IsValidEmail(account) { return "", errs.InputInvalidFormatError("email is invalid") } return account, nil case member.AccountTypeNone, member.AccountTypeDefine: default: } return "", errs.InputInvalidFormatError("unsupported account type") } // checkVerifyCodeCooldown 檢查是否已在限制時間內發送過驗證碼 func (l *RequestPasswordResetLogic) checkVerifyCodeCooldown(rk string) error { if cachedCode, err := l.svcCtx.Redis.GetCtx(l.ctx, rk); err != nil || cachedCode != "" { return errs.SysTooManyRequestError("verification code already sent, please wait 3min for system to send again") } return nil } // checkAccountAndPlatform 檢查帳號是否註冊及平台限制 func (l *RequestPasswordResetLogic) checkAccountAndPlatform(acc string) error { accountInfo, err := l.svcCtx.AccountUC.GetUserAccountInfo(l.ctx, usecase.GetUIDByAccountRequest{Account: acc}) if err != nil { return err } if accountInfo.Data.Platform != member.Digimon { return errs.InputInvalidFormatError( "failed to send verify code since platform not correct") } return nil } // setRedisKeyWithExpiry 設置 Redis 鍵 func (l *RequestPasswordResetLogic) setRedisKeyWithExpiry(rk, verifyCode string, expiry int) { if status, err := l.svcCtx.Redis.SetnxExCtx(l.ctx, rk, verifyCode, expiry); err != nil || !status { _ = errs.DBErrorErrorL(l.svcCtx.Logger, []errs.LogField{ {Key: "redisKey", Val: rk}, {Key: "error", Val: err.Error()}, }, "failed to set redis expire").Wrap(err) } }