243 lines
8.0 KiB
Go
243 lines
8.0 KiB
Go
|
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
|
|||
|
}
|