From 937244a1a0369c654a8950f8eec452cd1c639ba9 Mon Sep 17 00:00:00 2001 From: jager Date: Thu, 20 Mar 2025 17:15:54 +0800 Subject: [PATCH] feat: bind tribally account --- doc/api/task.api | 8 +++ doc/swagger/nova.json | 35 ++++++++++ internal/consts/consts.go | 1 + internal/handler/routes.go | 6 ++ .../handler/task/bind_tribally_handler.go | 22 +++++++ internal/logic/task/bind_tribally_logic.go | 64 +++++++++++++++++++ internal/model/nh_system_config_model.go | 12 ++++ internal/pkg/errs/reason.go | 2 + internal/pkg/tribally/tribally.go | 48 ++++++++++++++ internal/types/types.go | 4 ++ 10 files changed, 202 insertions(+) create mode 100644 internal/handler/task/bind_tribally_handler.go create mode 100644 internal/logic/task/bind_tribally_logic.go create mode 100644 internal/pkg/tribally/tribally.go diff --git a/doc/api/task.api b/doc/api/task.api index abdc7a0..70d8e28 100644 --- a/doc/api/task.api +++ b/doc/api/task.api @@ -51,6 +51,10 @@ service novatask { @doc "赛季奖励数据" @handler PioneerReward get /pioneer_reward returns (PioneerReward) + + @doc "绑定Tribally账号" + @handler BindTribally + get /bind_tribally returns (BindTriballyReply) } type GetTaskListReq { @@ -168,3 +172,7 @@ type PioneerReward { Total float64 `json:"total"` // 总额 } +type BindTriballyReply { + AuthUrl string `json:"auth_url"` // 授权地址 +} + diff --git a/doc/swagger/nova.json b/doc/swagger/nova.json index 05906ba..2ae87de 100644 --- a/doc/swagger/nova.json +++ b/doc/swagger/nova.json @@ -437,6 +437,28 @@ ] } }, + "/gapi/task/v1/bind_tribally": { + "get": { + "summary": "绑定Tribally账号", + "operationId": "BindTribally", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/BindTriballyReply" + } + } + }, + "tags": [ + "task" + ], + "security": [ + { + "apiKey": [] + } + ] + } + }, "/gapi/task/v1/community": { "get": { "summary": "获取社区列表", @@ -732,6 +754,19 @@ } }, "definitions": { + "BindTriballyReply": { + "type": "object", + "properties": { + "auth_url": { + "type": "string", + "description": " 授权地址" + } + }, + "title": "BindTriballyReply", + "required": [ + "auth_url" + ] + }, "CarvResult": { "type": "object", "properties": { diff --git a/internal/consts/consts.go b/internal/consts/consts.go index ed7acd1..ac95eaf 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -13,6 +13,7 @@ const ( KgenApiKey = "kgen_api_key" AdminSecret = "admin_secret" NftHolderApiConf = "nft_holder_api_conf" + TriballyApiKey = "tribally_api_key" ) type AssetType string diff --git a/internal/handler/routes.go b/internal/handler/routes.go index 279074b..04bd556 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -145,6 +145,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { server.AddRoutes( []rest.Route{ + { + // 绑定Tribally账号 + Method: http.MethodGet, + Path: "/bind_tribally", + Handler: task.BindTriballyHandler(serverCtx), + }, { // 质押NFT Method: http.MethodPost, diff --git a/internal/handler/task/bind_tribally_handler.go b/internal/handler/task/bind_tribally_handler.go new file mode 100644 index 0000000..3b834bc --- /dev/null +++ b/internal/handler/task/bind_tribally_handler.go @@ -0,0 +1,22 @@ +package task + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "nova_task/internal/logic/task" + "nova_task/internal/svc" +) + +// 绑定Tribally账号 +func BindTriballyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + l := task.NewBindTriballyLogic(r.Context(), svcCtx) + resp, err := l.BindTribally() + if err != nil { + httpx.ErrorCtx(r.Context(), w, err) + } else { + httpx.OkJsonCtx(r.Context(), w, resp) + } + } +} diff --git a/internal/logic/task/bind_tribally_logic.go b/internal/logic/task/bind_tribally_logic.go new file mode 100644 index 0000000..858f20d --- /dev/null +++ b/internal/logic/task/bind_tribally_logic.go @@ -0,0 +1,64 @@ +package task + +import ( + "context" + "errors" + "nova_task/internal/model" + "nova_task/internal/pkg/errs" + "nova_task/internal/pkg/tribally" + "nova_task/internal/pkg/utils" + "nova_task/internal/svc" + "nova_task/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type BindTriballyLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// 绑定Tribally账号 + +func NewBindTriballyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BindTriballyLogic { + return &BindTriballyLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *BindTriballyLogic) BindTribally() (resp *types.BindTriballyReply, err error) { + uid := utils.GetUidUint(l.ctx) + apiKey, err := l.svcCtx.ConfigModel.GetTriballyApiKey(l.ctx) + if err != nil { + l.Errorw("get tribally api key failed", logx.Field("err", err)) + return nil, errs.New(errs.ErrDatabaseOperate, err) + } + if apiKey == "" { + return nil, errs.New(errs.ErrSystemConfig, "system config err") + } + u, err := l.svcCtx.UserModel.FindOne(l.ctx, uid) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errs.New(errs.ErrUserNotFound, "user not found") + } + l.Errorw("get user failed", logx.Field("err", err), logx.Field("uid", uid)) + return nil, errs.New(errs.ErrDatabaseOperate, err) + } + hasBind, err := l.svcCtx.RoleModel.AccountExist(l.ctx, u.Email) + if err != nil { + return nil, errs.New(errs.ErrDatabaseOperate, err) + } + if !hasBind { + return nil, errs.New(errs.ErrNotBindRole, "user not bind role") + } + authUrl, err := tribally.BindTribally(apiKey, u.Email) + if err != nil { + l.Errorw("bind tribally failed", logx.Field("err", err)) + return nil, errs.New(errs.ErrInternalServer, err) + } + + return &types.BindTriballyReply{AuthUrl: authUrl}, nil +} diff --git a/internal/model/nh_system_config_model.go b/internal/model/nh_system_config_model.go index 6ba2774..2bc2238 100755 --- a/internal/model/nh_system_config_model.go +++ b/internal/model/nh_system_config_model.go @@ -24,6 +24,7 @@ type ( GetApiKey(ctx context.Context, name string) (apiKey string, err error) GetAdminSecret(ctx context.Context) (secret string, err error) GetInviterId(ctx context.Context, name string) uint + GetTriballyApiKey(ctx context.Context) (apiKey string, err error) } customNhSystemConfigModel struct { @@ -31,6 +32,17 @@ type ( } ) +func (m *customNhSystemConfigModel) GetTriballyApiKey(ctx context.Context) (apiKey string, err error) { + cf, err := m.FindOneByName(ctx, consts.TriballyApiKey) + if err != nil { + if !errors.Is(err, sqlx.ErrNotFound) { + return "", err + } + return "", nil + } + return cf.Value, nil +} + func (m *customNhSystemConfigModel) GetInviterId(ctx context.Context, name string) uint { cf, err := m.FindOneByName(ctx, name) if err != nil { diff --git a/internal/pkg/errs/reason.go b/internal/pkg/errs/reason.go index ee2c7ab..9932d1b 100644 --- a/internal/pkg/errs/reason.go +++ b/internal/pkg/errs/reason.go @@ -16,6 +16,7 @@ const ( ErrEncodePassword Reason = 1003 // 密码加密错误 ErrGenerateUUid Reason = 1004 // 生成uuid错误 ErrGenerateToken Reason = 1005 // 生成token错误 + ErrSystemConfig Reason = 1006 // 系统配置错误 // ======= 业务层错误:20000~29999 ======= ErrUnknownLogicError Reason = 20000 // 未知的业务错误 @@ -30,4 +31,5 @@ const ( ErrInvalidApiKey Reason = 20009 // 无效的api key ErrRoleNotFound Reason = 20010 // 角色不存在 ErrInvalidParam Reason = 20011 // 无效的参数 + ErrNotBindRole Reason = 20012 // 未绑定角色 ) diff --git a/internal/pkg/tribally/tribally.go b/internal/pkg/tribally/tribally.go new file mode 100644 index 0000000..4442673 --- /dev/null +++ b/internal/pkg/tribally/tribally.go @@ -0,0 +1,48 @@ +package tribally + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" +) + +func BindTribally(apiKey, userId string) (string, error) { + url := "https://api.tribally.games/authenticate" + payload := strings.NewReader(fmt.Sprintf(`{"playerId": "%s"}`, userId)) + + req, err := http.NewRequest(http.MethodPost, url, payload) + if err != nil { + return "", err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Accept", "application/json") + req.Header.Add("x-api-key", apiKey) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + result := struct { + Error string `json:"error"` + Code string `json:"code"` + Data struct { + AuthURL string `json:"authUrl"` + } `json:"data"` + }{} + err = json.Unmarshal(body, &result) + if err != nil { + return "", err + } + if result.Error != "" { + return "", fmt.Errorf("error: %s, code: %s", result.Error, result.Code) + } + return result.Data.AuthURL, nil +} diff --git a/internal/types/types.go b/internal/types/types.go index 9363982..c52d5da 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -3,6 +3,10 @@ package types +type BindTriballyReply struct { + AuthUrl string `json:"auth_url"` // 授权地址 +} + type CarvResult struct { Result *Result `json:"result"` Error *Error `json:"error"`