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

243 lines
8.0 KiB
Go
Raw Normal View History

2025-03-12 13:46:41 +00:00
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
}