fix: INVITE USER

This commit is contained in:
lianghuanjie
2025-01-06 19:45:03 +08:00
parent af258d2207
commit c540744176
12 changed files with 344 additions and 39 deletions

View File

@@ -4,6 +4,7 @@ pwd ?= jMDqPQM^a6hsAR
table ?= table ?=
cache ?= cache ?=
database ?= "nova_home" database ?= "nova_home"
tag ?= dev
.PHONY: db .PHONY: db
# 链接数据库生成模型代码 # 链接数据库生成模型代码
@@ -37,11 +38,11 @@ build:
.PHONY: img .PHONY: img
# 构建Docker镜像 # 构建Docker镜像
img: img:
docker build --platform=amd64 -t harbor.phantom-u3d002.com/nova/nova-task:latest . docker build --platform=amd64 -t harbor.phantom-u3d002.com/nova/nova-task:${tag} .
.PHONY: push .PHONY: push
push: push:
docker push harbor.phantom-u3d002.com/nova/nova-task:latest docker push harbor.phantom-u3d002.com/nova/nova-task:${tag}
# 帮助信息 # 帮助信息

View File

@@ -6,7 +6,7 @@ syntax = "v1"
group: admin group: admin
) )
service novatask { service novatask {
@doc "每日钱包签到任务" @doc "给指定邮箱增加待发放奖励"
@handler AddEmailReward @handler AddEmailReward
post /email_reward (EmailReward) post /email_reward (EmailReward)
@@ -16,8 +16,9 @@ service novatask {
} }
type EmailReward { type EmailReward {
Email string `json:"email"` Email string `json:"email"` // 邮箱,多个邮箱分号隔开
RewardType string `json:"reward_type"` RewardType string `json:"reward_type"` // 奖励类型: points elite_points castile keys
Value float64 `json:"value"` Value float64 `json:"value"` // 数量
Remark string `json:"remark"` // 备注
} }

View File

@@ -39,12 +39,12 @@ type CarvResult {
type EmailKey { type EmailKey {
Email string `form:"email"` Email string `form:"email"`
ApiKey string `Header:"x-api-key"` ApiKey string `header:"x-api-key"`
} }
type UnlockChapterReq { type UnlockChapterReq {
Email string `form:"email"` Email string `form:"email"`
Chapter int `form:"chapter"` Chapter int `form:"chapter"`
ApiKey string `Header:"x-api-key"` ApiKey string `header:"x-api-key"`
} }

View File

@@ -2,6 +2,7 @@
"swagger": "2.0", "swagger": "2.0",
"info": { "info": {
"title": "", "title": "",
"description": "nova api",
"version": "" "version": ""
}, },
"schemes": [ "schemes": [
@@ -15,6 +16,187 @@
"application/json" "application/json"
], ],
"paths": { "paths": {
"/gapi/admin/email_reward": {
"get": {
"summary": "执行发放奖励操作",
"operationId": "SendEmailReward",
"responses": {
"200": {
"description": "A successful response.",
"schema": {}
}
},
"tags": [
"admin"
]
},
"post": {
"summary": "给指定邮箱增加待发放奖励",
"operationId": "AddEmailReward",
"responses": {
"200": {
"description": "A successful response.",
"schema": {}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/EmailReward"
}
}
],
"tags": [
"admin"
]
}
},
"/gapi/carv/bind_role": {
"get": {
"summary": "下载并绑定Castile游戏角色",
"operationId": "DownloadAndBindRole",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/CarvResult"
}
}
},
"parameters": [
{
"name": "x-api-key",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "email",
"in": "query",
"required": true,
"type": "string"
}
],
"tags": [
"carv"
],
"consumes": [
"multipart/form-data"
]
}
},
"/gapi/carv/bind_wallet": {
"get": {
"summary": "注册绑定钱包任务",
"operationId": "BindWallet",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/CarvResult"
}
}
},
"parameters": [
{
"name": "x-api-key",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "email",
"in": "query",
"required": true,
"type": "string"
}
],
"tags": [
"carv"
],
"consumes": [
"multipart/form-data"
]
}
},
"/gapi/carv/check_in_wallet": {
"get": {
"summary": "每日钱包签到任务",
"operationId": "WalletCheckIn",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/CarvResult"
}
}
},
"parameters": [
{
"name": "x-api-key",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "email",
"in": "query",
"required": true,
"type": "string"
}
],
"tags": [
"carv"
],
"consumes": [
"multipart/form-data"
]
}
},
"/gapi/carv/unlock_chapter": {
"get": {
"summary": "游戏主线解锁第x章节",
"operationId": "UnlockChapter",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/CarvResult"
}
}
},
"parameters": [
{
"name": "x-api-key",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "email",
"in": "query",
"required": true,
"type": "string"
},
{
"name": "chapter",
"in": "query",
"required": true,
"type": "integer",
"format": "int32"
}
],
"tags": [
"carv"
],
"consumes": [
"multipart/form-data"
]
}
},
"/gapi/task/v1/community": { "/gapi/task/v1/community": {
"get": { "get": {
"summary": "获取社区列表", "summary": "获取社区列表",
@@ -253,6 +435,22 @@
} }
}, },
"definitions": { "definitions": {
"CarvResult": {
"type": "object",
"properties": {
"result": {
"$ref": "#/definitions/Result"
},
"error": {
"$ref": "#/definitions/Error"
}
},
"title": "CarvResult",
"required": [
"result",
"error"
]
},
"Community": { "Community": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -294,6 +492,64 @@
"end_at" "end_at"
] ]
}, },
"EmailKey": {
"type": "object",
"properties": {
"email": {
"type": "string"
}
},
"title": "EmailKey",
"required": [
"email"
]
},
"EmailReward": {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": " 邮箱,多个邮箱分号隔开"
},
"reward_type": {
"type": "string",
"description": " 奖励类型: points elite_points castile keys"
},
"value": {
"type": "number",
"format": "double",
"description": " 数量"
},
"remark": {
"type": "string",
"description": " 备注"
}
},
"title": "EmailReward",
"required": [
"email",
"reward_type",
"value",
"remark"
]
},
"Error": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
},
"title": "Error",
"required": [
"code",
"message"
]
},
"GetCommunityListResp": { "GetCommunityListResp": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -362,6 +618,19 @@
"points" "points"
] ]
}, },
"Result": {
"type": "object",
"properties": {
"isValid": {
"type": "boolean",
"format": "boolean"
}
},
"title": "Result",
"required": [
"isValid"
]
},
"StakeNftList": { "StakeNftList": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -380,7 +649,6 @@
}, },
"title": "StakeNftList", "title": "StakeNftList",
"required": [ "required": [
"role_id",
"token_ids" "token_ids"
] ]
}, },
@@ -539,6 +807,23 @@
"token_id" "token_id"
] ]
}, },
"UnlockChapterReq": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"chapter": {
"type": "integer",
"format": "int32"
}
},
"title": "UnlockChapterReq",
"required": [
"email",
"chapter"
]
},
"UserNft": { "UserNft": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -20,7 +20,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
[]rest.Middleware{serverCtx.AdminSecretCheck}, []rest.Middleware{serverCtx.AdminSecretCheck},
[]rest.Route{ []rest.Route{
{ {
// 每日钱包签到任务 // 给指定邮箱增加待发放奖励
Method: http.MethodPost, Method: http.MethodPost,
Path: "/email_reward", Path: "/email_reward",
Handler: admin.AddEmailRewardHandler(serverCtx), Handler: admin.AddEmailRewardHandler(serverCtx),

View File

@@ -5,6 +5,7 @@ import (
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"nova_task/internal/model" "nova_task/internal/model"
"nova_task/internal/pkg/errs" "nova_task/internal/pkg/errs"
"strings"
"nova_task/internal/svc" "nova_task/internal/svc"
"nova_task/internal/types" "nova_task/internal/types"
@@ -28,14 +29,18 @@ func NewAddEmailRewardLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ad
} }
func (l *AddEmailRewardLogic) AddEmailReward(req *types.EmailReward) error { func (l *AddEmailRewardLogic) AddEmailReward(req *types.EmailReward) error {
_, err := l.svcCtx.EmailRewardModel.Insert(l.ctx, &model.NhEmailReward{ emails := strings.Split(req.Email, ";")
Email: req.Email, for _, email := range emails {
RewardType: req.RewardType, _, err := l.svcCtx.EmailRewardModel.Insert(l.ctx, &model.NhEmailReward{
Value: decimal.NewFromFloat(req.Value), Email: email,
}) RewardType: req.RewardType,
if err != nil { Value: decimal.NewFromFloat(req.Value),
l.Errorw("add email reward failed", logx.Field("err", err), logx.Field("email", req.Email)) Remark: req.Remark,
return errs.New(errs.ErrDatabaseOperate, err) })
if err != nil {
l.Errorw("add email reward failed", logx.Field("err", err), logx.Field("email", req.Email))
return errs.New(errs.ErrDatabaseOperate, err)
}
} }
return errs.Success() return errs.Success()

View File

@@ -35,6 +35,7 @@ func (l *SendEmailRewardLogic) SendEmailReward() error {
} }
if len(rewards) <= 0 { if len(rewards) <= 0 {
l.Infow("no email reward to send")
return errs.Success() return errs.Success()
} }
@@ -66,6 +67,8 @@ func (l *SendEmailRewardLogic) SendEmailReward() error {
} }
if err != nil { if err != nil {
l.Errorw("add asset failed", logx.Field("err", err), logx.Field("uid", u.Id), logx.Field("rewardType", rw.RewardType), logx.Field("value", rw.Value)) l.Errorw("add asset failed", logx.Field("err", err), logx.Field("uid", u.Id), logx.Field("rewardType", rw.RewardType), logx.Field("value", rw.Value))
} else {
l.Infow("add asset success", logx.Field("uid", u.Id), logx.Field("rewardType", rw.RewardType), logx.Field("value", rw.Value))
} }
_, err = l.svcCtx.TaskAssetRecordModel.Insert(ctx, &model.NhTaskAssetRecord{ _, err = l.svcCtx.TaskAssetRecordModel.Insert(ctx, &model.NhTaskAssetRecord{
Uid: int(u.Id), Uid: int(u.Id),
@@ -80,5 +83,5 @@ func (l *SendEmailRewardLogic) SendEmailReward() error {
} }
}) })
return nil return errs.Success()
} }

View File

@@ -72,6 +72,15 @@ func (l *VerifyTaskResultLogic) VerifyTaskResult(req *types.VerifyTaskResultReq)
if tw.TwitterId == "" { if tw.TwitterId == "" {
return &types.VerifyTaskResultResp{Finish: false}, nil return &types.VerifyTaskResultResp{Finish: false}, nil
} }
case model.TASKTYPE_INVITE_USER:
count, err := l.svcCtx.PromoteBindModel.UserInviteCount(l.ctx, uint(uid))
if err != nil {
l.Errorw("get user invite count failed", logx.Field("err", err), logx.Field("uid", uid))
return nil, errs.New(errs.ErrDatabaseOperate, err)
}
if count < cast.ToInt64(task.Param) {
return &types.VerifyTaskResultResp{Finish: false}, nil
}
case model.TASKTYPE_BIND_DISCORD: case model.TASKTYPE_BIND_DISCORD:
case model.TASKTYPE_DAILY_PAY: case model.TASKTYPE_DAILY_PAY:

View File

@@ -16,17 +16,17 @@ func NewAdminSecretCheckMiddleware(conf model.NhSystemConfigModel) *AdminSecretC
func (m *AdminSecretCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { func (m *AdminSecretCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if key, err := m.conf.GetAdminSecret(r.Context()); err != nil { key, err := m.conf.GetAdminSecret(r.Context())
if err != nil {
if !errors.Is(err, model.ErrNotFound) { if !errors.Is(err, model.ErrNotFound) {
http.Error(w, "system error", http.StatusInternalServerError) http.Error(w, "system error", http.StatusInternalServerError)
return return
} }
} else { }
apiKey := r.Header.Get("x-admin-secret") apiKey := r.Header.Get("x-admin-secret")
if apiKey != key { if apiKey == "" || apiKey != key {
http.Error(w, "Invalid API key", http.StatusUnauthorized) http.Error(w, "Invalid API key", http.StatusUnauthorized)
return return
}
} }
next(w, r) next(w, r)
} }

View File

@@ -16,17 +16,17 @@ func NewApiKeyCheckMiddleware(conf model.NhSystemConfigModel) *ApiKeyCheckMiddle
func (m *ApiKeyCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { func (m *ApiKeyCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if key, err := m.conf.GetCarvApiKey(r.Context()); err != nil { key, err := m.conf.GetCarvApiKey(r.Context())
if err != nil {
if !errors.Is(err, model.ErrNotFound) { if !errors.Is(err, model.ErrNotFound) {
http.Error(w, "system error", http.StatusInternalServerError) http.Error(w, "system error", http.StatusInternalServerError)
return return
} }
} else { }
apiKey := r.Header.Get("x-api-key") apiKey := r.Header.Get("x-api-key")
if apiKey != key { if apiKey == "" || apiKey != key {
http.Error(w, "Invalid API key", http.StatusUnauthorized) http.Error(w, "Invalid API key", http.StatusUnauthorized)
return return
}
} }
next(w, r) next(w, r)

View File

@@ -29,7 +29,7 @@ type (
) )
func (m *customNhSystemConfigModel) GetAdminSecret(ctx context.Context) (secret string, err error) { func (m *customNhSystemConfigModel) GetAdminSecret(ctx context.Context) (secret string, err error) {
cf, err := m.FindOneByName(ctx, consts.CarvApiKey) cf, err := m.FindOneByName(ctx, consts.AdminSecret)
if err != nil { if err != nil {
if !errors.Is(err, sqlx.ErrNotFound) { if !errors.Is(err, sqlx.ErrNotFound) {
return "", err return "", err

View File

@@ -19,13 +19,14 @@ type Community struct {
type EmailKey struct { type EmailKey struct {
Email string `form:"email"` Email string `form:"email"`
ApiKey string `Header:"x-api-key"` ApiKey string `header:"x-api-key"`
} }
type EmailReward struct { type EmailReward struct {
Email string `json:"email"` Email string `json:"email"` // 邮箱,多个邮箱分号隔开
RewardType string `json:"reward_type"` RewardType string `json:"reward_type"` // 奖励类型: points elite_points castile keys
Value float64 `json:"value"` Value float64 `json:"value"` // 数量
Remark string `json:"remark"` // 备注
} }
type Error struct { type Error struct {
@@ -101,7 +102,7 @@ type UnStakeNftReq struct {
type UnlockChapterReq struct { type UnlockChapterReq struct {
Email string `form:"email"` Email string `form:"email"`
Chapter int `form:"chapter"` Chapter int `form:"chapter"`
ApiKey string `Header:"x-api-key"` ApiKey string `header:"x-api-key"`
} }
type UserNft struct { type UserNft struct {