package member import ( "biz-member-gateway/internal/domain" bizDomain "biz-member-gateway/internal/domain" "biz-member-gateway/internal/svc" "biz-member-gateway/internal/types" "context" "regexp" "strings" "time" "code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member" memberUC "code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/usecase" "code.30cm.net/digimon/library-go/errs" memberProto "code.30cm.net/digimon/proto-all/pkg/member" permissionProto "code.30cm.net/digimon/proto-all/pkg/permission" "github.com/golang/protobuf/proto" "github.com/zeromicro/go-zero/core/logx" ) type LoginLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } // NewLoginLogic 登入 func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic { return &LoginLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginTokenResp, err error) { var result memberUC.VerifyAuthResultResponse // Step 1 驗證進來的 Token platform := member.GetPlatformByPlatformCode(req.Platform) switch platform { case member.Digimon: switch member.GetAccountTypeByCode(req.AccountType) { case member.AccountTypePhone: phone, isPhone := normalizeTaiwanMobile(req.Account) if !isPhone { return nil, errs.InvalidFormat("phone number is invalid") } req.Account = phone case member.AccountTypeMail: if !isValidEmail(req.Account) { return nil, errs.InvalidFormat("email is invalid") } } // 原始平台驗證 res, err := l.svcCtx.MemberRPC.VerifyPlatformAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{ Account: proto.String(req.Account), Token: req.Token, }) if err != nil { return nil, err } result.Status = res.Status case member.Google: // 原始平台驗證 _, err := l.svcCtx.MemberRPC.VerifyGoogleAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{ Account: proto.String(req.Account), Token: req.Token, }) if err != nil { return nil, err } result.Status = true case member.Line: // 原始平台驗證 accessToken, err := l.svcCtx.MemberRPC.LineCodeToAccessToken(l.ctx, &memberProto.LineGetTokenReq{ Code: req.Account, }) if err != nil { return nil, err } // 換資料 userInfo, err := l.svcCtx.MemberRPC.LineGetProfileByAccessToken(l.ctx, &memberProto.LineGetUserInfoReq{ Token: accessToken.Token, }) if err != nil { return nil, err } result.Status = true req.Account = userInfo.UserId case member.PlatformNone: default: return nil, errs.InvalidFormat("invalid platform") } if !result.Status { return nil, errs.Unauthorized("failed to validate password ") } account, err := l.svcCtx.MemberRPC.GetUIDByAccount(l.ctx, &memberProto.GetUIDByAccountReq{ Account: req.Account, }) if err != nil { return nil, err } credentials := "client_credentials" // TODO 去拿這個使用者Role role := bizDomain.DefaultRole t, err := l.svcCtx.TokenRPC.NewToken(l.ctx, &permissionProto.AuthorizationReq{ GrantType: credentials, DeviceId: req.DeviceID, Scope: role, IsRefreshToken: true, Account: req.Account, Uid: account.GetUid(), Expires: proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.Expired).UnixNano()), // 指定到期的時間 RefreshExpire: proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.RefreshExpired).UnixNano()), // 指定到期的時間 Data: map[string]string{}, Role: role, }) if err != nil { return nil, err } return &types.LoginTokenResp{ UID: account.GetUid(), AccessToken: t.AccessToken, RefreshToken: t.RefreshToken, TokenType: domain.TokenTypeBearer, }, nil } // 標準化號碼並驗證是否為合法台灣手機號碼 func normalizeTaiwanMobile(phone string) (string, bool) { // 移除空格 phone = strings.ReplaceAll(phone, " ", "") // 移除 "+886" 並將剩餘部分標準化 if strings.HasPrefix(phone, "+886") { phone = strings.TrimPrefix(phone, "+886") if !strings.HasPrefix(phone, "0") { phone = "0" + phone } } // 正則表達式驗證標準化後的號碼 regex := regexp.MustCompile(`^(09\d{8})$`) if regex.MatchString(phone) { return phone, true } return "", false } // 驗證 Email 格式的函數 func isValidEmail(email string) bool { // 定義正則表達式 regex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) return regex.MatchString(email) }