biz-member-gateway/internal/logic/member/account_create_logic.go

243 lines
8.0 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 member
import (
bizDomain "biz-member-gateway/internal/domain"
"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
memberUC "code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/usecase"
tokenUC "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/usecase"
"time"
"code.30cm.net/digimon/library-go/errs"
"code.30cm.net/digimon/library-go/errs/code"
"context"
"github.com/golang/protobuf/proto"
"biz-member-gateway/internal/svc"
"biz-member-gateway/internal/types"
memberProto "code.30cm.net/digimon/proto-all/pkg/member"
permissionProto "code.30cm.net/digimon/proto-all/pkg/permission"
"github.com/zeromicro/go-zero/core/logx"
)
type AccountCreateLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewAccountCreateLogic 創建新會員
func NewAccountCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AccountCreateLogic {
return &AccountCreateLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// AccountCreate 建立新帳號 -> 業務邏輯
func (l *AccountCreateLogic) AccountCreate(req *types.CreateAccountRequest) (resp *types.LoginTokenResp, err error) {
platform := member.GetPlatformByPlatformCode(req.Platform)
if platform == member.PlatformNone {
// http 400
return nil, errs.InvalidFormat("platform not support")
}
// Step 1: 根據平台構建相關數據
createAccountReq, bindingUserReq, createUserInfoRequest, err := l.buildAccountRequests(req, platform)
if err != nil {
// 裡面有錯從 Grpc 已經轉換完成
return nil, err
}
// Step 2: 建立帳號
_, err = l.svcCtx.MemberRPC.CreateUserAccount(l.ctx, &memberProto.CreateLoginUserReq{
LoginId: createAccountReq.LoginID,
Token: createAccountReq.Token,
Platform: createAccountReq.Platform.ToInt64(),
})
if err != nil {
return nil, err
}
// Step 3: 綁定帳號並獲取 UID
account, err := l.svcCtx.MemberRPC.BindAccount(l.ctx, &memberProto.BindingUserReq{
LoginId: bindingUserReq.LoginID,
Type: int64(bindingUserReq.Type),
})
if err != nil {
return nil, err
}
// Step 4: 更新使用者資訊
_, err = l.svcCtx.MemberRPC.BindUserInfo(l.ctx, &memberProto.CreateUserInfoReq{
Uid: account.Uid,
AlarmType: memberProto.AlarmType(createUserInfoRequest.AlarmCategory),
Status: memberProto.MemberStatus(createUserInfoRequest.UserStatus),
Language: createUserInfoRequest.PreferredLanguage,
Currency: createUserInfoRequest.Currency,
Avatar: createUserInfoRequest.AvatarURL,
NickName: createUserInfoRequest.Nickname,
FullName: createUserInfoRequest.FullName,
Gender: createUserInfoRequest.GenderCode,
Birthdate: createUserInfoRequest.Birthdate,
PhoneNumber: createUserInfoRequest.PhoneNumber,
Email: createUserInfoRequest.Email,
Address: createUserInfoRequest.Address,
})
if err != nil {
return nil, err
}
// TODO 綁定角色
// Step 5: 生成 Token
t, err := l.generateToken(req, account.GetUid())
if err != nil {
return nil, err
}
return &types.LoginTokenResp{
UID: account.GetUid(),
AccessToken: t.AccessToken,
RefreshToken: t.RefreshToken,
TokenType: bizDomain.TokenTypeBearer,
}, nil
}
// 構建不同平台的請求數據
func (l *AccountCreateLogic) buildAccountRequests(req *types.CreateAccountRequest, platform member.Platform) (
memberUC.CreateLoginUserRequest, memberUC.BindingUser, memberUC.CreateUserInfoRequest, error) {
var createAccountReq memberUC.CreateLoginUserRequest
var bindingUserReq memberUC.BindingUser
var createUserInfoRequest memberUC.CreateUserInfoRequest
switch platform {
case member.Digimon:
// 驗證 Token 是否一致
if req.Token != req.TokenCheck {
return createAccountReq, bindingUserReq, createUserInfoRequest, errs.NewError(
code.CloudEPMember, code.InvalidRange, bizDomain.TokenNotTheSameAPIErrorCode,
"failed to verify check token",
)
}
createAccountReq = memberUC.CreateLoginUserRequest{
LoginID: req.Account,
Token: req.Token,
Platform: platform,
}
bindingUserReq = memberUC.BindingUser{
LoginID: req.Account,
Type: member.GetAccountTypeByCode(req.AccountType),
}
createUserInfoRequest = memberUC.CreateUserInfoRequest{
AlarmCategory: member.AlarmNoAlert,
UserStatus: member.AccountStatusUnverified,
PreferredLanguage: bizDomain.DefaultLang,
Currency: bizDomain.DefaultCurrency,
}
case member.Google:
googleToken, err := l.svcCtx.MemberRPC.VerifyGoogleAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{
Account: proto.String(req.Account),
Token: req.Token,
})
if err != nil {
return createAccountReq, bindingUserReq, createUserInfoRequest, err
}
createAccountReq = memberUC.CreateLoginUserRequest{
LoginID: *googleToken.Email,
Token: "",
Platform: platform,
}
bindingUserReq = memberUC.BindingUser{
LoginID: *googleToken.Email,
Type: member.GetAccountTypeByCode(req.AccountType),
}
createUserInfoRequest = memberUC.CreateUserInfoRequest{
AvatarURL: googleToken.Picture,
FullName: googleToken.Name,
Nickname: googleToken.Name,
Email: googleToken.Email,
AlarmCategory: member.AlarmNoAlert,
UserStatus: member.AccountStatusActive,
PreferredLanguage: bizDomain.DefaultLang,
Currency: bizDomain.DefaultCurrency,
}
case member.Line:
// 用 code 換取 line access token
lineAccessToken, err := l.svcCtx.MemberRPC.LineCodeToAccessToken(l.ctx, &memberProto.LineGetTokenReq{
Code: req.Account,
})
if err != nil {
return createAccountReq, bindingUserReq, createUserInfoRequest, err
}
// 用 access token 換取 line 使用者資料
userInfo, err := l.svcCtx.MemberRPC.LineGetProfileByAccessToken(l.ctx, &memberProto.LineGetUserInfoReq{
Token: lineAccessToken.Token,
})
if err != nil {
return createAccountReq, bindingUserReq, createUserInfoRequest, err
}
createAccountReq = memberUC.CreateLoginUserRequest{
LoginID: userInfo.UserId,
Token: "",
Platform: platform,
}
bindingUserReq = memberUC.BindingUser{
LoginID: userInfo.UserId,
Type: member.GetAccountTypeByCode(req.AccountType),
}
createUserInfoRequest = memberUC.CreateUserInfoRequest{
AvatarURL: proto.String(userInfo.PictureUrl),
FullName: proto.String(userInfo.DisplayName),
Nickname: proto.String(userInfo.DisplayName),
AlarmCategory: member.AlarmNoAlert,
UserStatus: member.AccountStatusActive,
PreferredLanguage: bizDomain.DefaultLang,
Currency: bizDomain.DefaultCurrency,
}
case member.PlatformNone:
default:
return createAccountReq, bindingUserReq, createUserInfoRequest, errs.InvalidFormat("invalid platform")
}
return createAccountReq, bindingUserReq, createUserInfoRequest, nil
}
// 生成 Token 這裡是註冊的,所以角色是固定的,不能讓平常註冊的使用者選擇角色
func (l *AccountCreateLogic) generateToken(req *types.CreateAccountRequest, uid string) (tokenUC.AccessTokenResponse, error) {
credentials := "client_credentials"
// 生成預設的Role -> 所有邏輯上的其他角色,應該都要從 user 開始後續再通過admin 做更改
// 第一個 admin 應該是直接插入資料庫的
t, err := l.svcCtx.TokenRPC.NewToken(l.ctx, &permissionProto.AuthorizationReq{
GrantType: credentials,
DeviceId: req.DeviceID,
Scope: bizDomain.DefaultRole,
IsRefreshToken: true,
Account: req.Account,
Uid: uid,
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: bizDomain.DefaultRole,
})
if err != nil {
return tokenUC.AccessTokenResponse{}, errs.FromGRPCError(err)
}
return tokenUC.AccessTokenResponse{
AccessToken: t.AccessToken,
ExpiresIn: t.ExpiresIn,
RefreshToken: t.RefreshToken,
}, nil
}